From 36c1b57b2ecf3c61ac93f5f07bd29b6f21e226ed Mon Sep 17 00:00:00 2001 From: Mingyu Wang <25181214217@stu.xidian.edu.cn> Date: Sat, 23 May 2026 21:52:10 +0800 Subject: [PATCH 0001/2758] fs/fcntl: fix SOFTIRQ-unsafe lock order in fasync signaling commit 00633c4683828acd5256fa8d5163f440d74bbe71 upstream. A SOFTIRQ-safe to SOFTIRQ-unsafe lock order deadlock can occur in send_sigio() and send_sigurg() when a process group receives a signal. When FASYNC is configured for a process group (PIDTYPE_PGID), both functions use read_lock(&tasklist_lock) to traverse the task list. However, they are frequently called from softirq context: - send_sigio() via input_inject_event -> kill_fasync - send_sigurg() via tcp_check_urg -> sk_send_sigurg (NET_RX_SOFTIRQ) The deadlock is caused by the rwlock writer fairness mechanism: 1. CPU 0 (process context) holds read_lock(&tasklist_lock) in do_wait(). 2. CPU 1 (process context) attempts write_lock(&tasklist_lock) in fork() or exit() and spins, which blocks all new readers. 3. CPU 0 is interrupted by a softirq (e.g., TCP URG packet reception). 4. The softirq calls send_sigurg() and attempts to acquire read_lock(&tasklist_lock), deadlocking because CPU 1 is waiting. Since PID hashing and do_each_pid_task() traversals are already RCU-protected, the read_lock on tasklist_lock is no longer strictly required for safe traversal. Fix this by replacing tasklist_lock with rcu_read_lock(), aligning the process group signaling path with the single-PID path. This also mitigates a potential remote denial of service vector via TCP URG packets. Lockdep splat: ===================================================== WARNING: SOFTIRQ-safe -> SOFTIRQ-unsafe lock order detected [...] Chain exists of: &dev->event_lock --> &f_owner->lock --> tasklist_lock Possible interrupt unsafe locking scenario: CPU0 CPU1 ---- ---- lock(tasklist_lock); local_irq_disable(); lock(&dev->event_lock); lock(&f_owner->lock); lock(&dev->event_lock); *** DEADLOCK *** Reviewed-by: Jeff Layton Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> Link: https://patch.msgid.link/20260523135210.590928-1-w15303746062@163.com Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/fcntl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/fcntl.c b/fs/fcntl.c index beab8080badf6..92d643a141964 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -929,11 +929,11 @@ void send_sigio(struct fown_struct *fown, int fd, int band) send_sigio_to_task(p, fown, fd, band, type); rcu_read_unlock(); } else { - read_lock(&tasklist_lock); + rcu_read_lock(); do_each_pid_task(pid, type, p) { send_sigio_to_task(p, fown, fd, band, type); } while_each_pid_task(pid, type, p); - read_unlock(&tasklist_lock); + rcu_read_unlock(); } out_unlock_fown: read_unlock_irqrestore(&fown->lock, flags); @@ -975,11 +975,11 @@ int send_sigurg(struct file *file) send_sigurg_to_task(p, fown, type); rcu_read_unlock(); } else { - read_lock(&tasklist_lock); + rcu_read_lock(); do_each_pid_task(pid, type, p) { send_sigurg_to_task(p, fown, type); } while_each_pid_task(pid, type, p); - read_unlock(&tasklist_lock); + rcu_read_unlock(); } out_unlock_fown: read_unlock_irqrestore(&fown->lock, flags); From 89b7ef06258f895aadfce0ae11c24b9694e19b42 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 24 Apr 2026 17:31:26 +0200 Subject: [PATCH 0002/2758] driver core: faux: fix root device registration commit 580a795105dae2ef1622df72a27a8fb0605e2f6b upstream. A recent change made the faux bus root device be allocated dynamically but failed to provide a release function to free the memory when the last reference is dropped (on theoretical failure to register the device or bus). Fix this by using root_device_register() instead of open coding. Also add the missing sanity check when registering faux devices to avoid use-after-free if the bus failed to register (which would previously have triggered a bunch of use-after-free warnings). Fixes: 61b76d07d2b4 ("driver core: faux: stop using static struct device") Cc: stable@vger.kernel.org # 7.0 Cc: Greg Kroah-Hartman Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260424153127.2647405-2-johan@kernel.org Signed-off-by: Danilo Krummrich Signed-off-by: Greg Kroah-Hartman --- drivers/base/faux.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/drivers/base/faux.c b/drivers/base/faux.c index fb3e42f213624..3d1d1eafb4733 100644 --- a/drivers/base/faux.c +++ b/drivers/base/faux.c @@ -133,6 +133,9 @@ struct faux_device *faux_device_create_with_groups(const char *name, struct device *dev; int ret; + if (!faux_bus_root) + return NULL; + faux_obj = kzalloc_obj(*faux_obj); if (!faux_obj) return NULL; @@ -232,19 +235,12 @@ EXPORT_SYMBOL_GPL(faux_device_destroy); int __init faux_bus_init(void) { + struct device *root; int ret; - faux_bus_root = kzalloc_obj(*faux_bus_root); - if (!faux_bus_root) - return -ENOMEM; - - dev_set_name(faux_bus_root, "faux"); - - ret = device_register(faux_bus_root); - if (ret) { - put_device(faux_bus_root); - return ret; - } + root = root_device_register("faux"); + if (IS_ERR(root)) + return PTR_ERR(root); ret = bus_register(&faux_bus_type); if (ret) @@ -254,12 +250,14 @@ int __init faux_bus_init(void) if (ret) goto error_driver; + faux_bus_root = root; + return ret; error_driver: bus_unregister(&faux_bus_type); error_bus: - device_unregister(faux_bus_root); + root_device_unregister(root); return ret; } From cb5dcab1993a9aeffac50b52a21e1230ad8bf773 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 30 Apr 2026 11:17:18 +0200 Subject: [PATCH 0003/2758] driver core: reject devices with unregistered buses commit 36f35b8df6972167102a1c3d4361e0afb6a84534 upstream. Trying to register a device on a bus which has not yet been registered used to trigger a NULL-pointer dereference, but since the const bus structure rework registration instead succeeds without the device being added to the bus. This specifically means that the device will never bind to a driver and that the bus sysfs attributes are not created (i.e. as if the device had no bus). Reject devices with unregistered buses to catch any callers that get the ordering wrong and to handle bus registration failures more gracefully. Fixes: 5221b82d46f2 ("driver core: bus: bus_add/probe/remove_device() cleanups") Cc: stable@vger.kernel.org # 6.3 Cc: Greg Kroah-Hartman Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260430091718.230228-1-johan@kernel.org Signed-off-by: Danilo Krummrich Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 8b6722ff8590d..d17bd91490ee4 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -544,10 +544,10 @@ static const struct attribute_group driver_override_dev_group = { */ int bus_add_device(struct device *dev) { - struct subsys_private *sp = bus_to_subsys(dev->bus); + struct subsys_private *sp; int error; - if (!sp) { + if (!dev->bus) { /* * This is a normal operation for many devices that do not * have a bus assigned to them, just say that all went @@ -556,6 +556,13 @@ int bus_add_device(struct device *dev) return 0; } + sp = bus_to_subsys(dev->bus); + if (!sp) { + pr_err("%s: cannot add device '%s' to unregistered bus '%s'\n", + __func__, dev_name(dev), dev->bus->name); + return -EINVAL; + } + /* * Reference in sp is now incremented and will be dropped when * the device is removed from the bus From 83d5c3cf358845ce62b920d0b14957c0b8eb2941 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Tue, 16 Jun 2026 13:19:53 +0100 Subject: [PATCH 0004/2758] arm64: cputype: Add C1-Ultra definitions commit 60349e64a6c65f9f0aa118af711b3c7e137f07ff upstream. Add cputype definitions for C1-Ultra. These will be used for errata detection in subsequent patches. These values can be found in the C1-Ultra TRM: https://developer.arm.com/documentation/108014/0100/ ... in section A.5.1 ("MIDR_EL1, Main ID Register"). Signed-off-by: Mark Rutland Cc: Catalin Marinas Cc: Will Deacon Signed-off-by: Will Deacon [Mark: backport to v7.1.y] Signed-off-by: Mark Rutland Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/cputype.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/include/asm/cputype.h b/arch/arm64/include/asm/cputype.h index 7b518e81dd15b..3e223a7781866 100644 --- a/arch/arm64/include/asm/cputype.h +++ b/arch/arm64/include/asm/cputype.h @@ -97,6 +97,7 @@ #define ARM_CPU_PART_CORTEX_X925 0xD85 #define ARM_CPU_PART_CORTEX_A725 0xD87 #define ARM_CPU_PART_CORTEX_A720AE 0xD89 +#define ARM_CPU_PART_C1_ULTRA 0xD8C #define ARM_CPU_PART_NEOVERSE_N3 0xD8E #define ARM_CPU_PART_C1_PRO 0xD8B @@ -189,6 +190,7 @@ #define MIDR_CORTEX_X925 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X925) #define MIDR_CORTEX_A725 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A725) #define MIDR_CORTEX_A720AE MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A720AE) +#define MIDR_C1_ULTRA MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_C1_ULTRA) #define MIDR_NEOVERSE_N3 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_N3) #define MIDR_C1_PRO MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_C1_PRO) #define MIDR_THUNDERX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX) From f93c6eee26895755cccbc3aeaa5984100e3adee6 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Tue, 16 Jun 2026 13:19:54 +0100 Subject: [PATCH 0005/2758] arm64: cputype: Add C1-Premium definitions commit d28413bfc5a255957241f1df5d7fd0c2cd74fe18 upstream. Add cputype definitions for C1-Premium. These will be used for errata detection in subsequent patches. These values can be found in the C1-Premium TRM: https://developer.arm.com/documentation/109416/0100/ ... in section A.5.1 ("MIDR_EL1, Main ID Register"). Signed-off-by: Mark Rutland Cc: Catalin Marinas Cc: Will Deacon Signed-off-by: Will Deacon [Mark: backport to v7.1.y] Signed-off-by: Mark Rutland Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/cputype.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/include/asm/cputype.h b/arch/arm64/include/asm/cputype.h index 3e223a7781866..1b9f0cda1336d 100644 --- a/arch/arm64/include/asm/cputype.h +++ b/arch/arm64/include/asm/cputype.h @@ -100,6 +100,7 @@ #define ARM_CPU_PART_C1_ULTRA 0xD8C #define ARM_CPU_PART_NEOVERSE_N3 0xD8E #define ARM_CPU_PART_C1_PRO 0xD8B +#define ARM_CPU_PART_C1_PREMIUM 0xD90 #define APM_CPU_PART_XGENE 0x000 #define APM_CPU_VAR_POTENZA 0x00 @@ -193,6 +194,7 @@ #define MIDR_C1_ULTRA MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_C1_ULTRA) #define MIDR_NEOVERSE_N3 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_N3) #define MIDR_C1_PRO MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_C1_PRO) +#define MIDR_C1_PREMIUM MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_C1_PREMIUM) #define MIDR_THUNDERX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX) #define MIDR_THUNDERX_81XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX_81XX) #define MIDR_THUNDERX_83XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX_83XX) From 1268c64e2bcb6e968152990e87bd10c440fcc9c0 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Tue, 16 Jun 2026 13:19:55 +0100 Subject: [PATCH 0006/2758] arm64: errata: Mitigate TLBI errata on various Arm CPUs commit cfd391e74134db664feb499d43af286380b10ba8 upstream. A number of CPUs developed by Arm suffer from errata whereby a broadcast TLBI;DSB sequence may complete before the global observation of writes which are translated by an affected TLB entry. These errata ONLY affect the completion of memory accesses which have been translated by an invalidated TLB entry, and these errata DO NOT affect the actual invalidation of TLB entries. TLB entries are removed correctly. This issue has been assigned CVE ID CVE-2025-10263. To mitigate this issue, Arm recommends that software follows any affected TLBI;DSB sequence with an additional TLBI;DSB, which will ensure that all memory write effects affected by the first TLBI have been globally observed. The additional TLBI can use any operation that is broadcast to affected CPUs, and the additional DSB can use any option that is sufficient to complete the additional TLBI. The ARM64_WORKAROUND_REPEAT_TLBI workaround is sufficient to mitigate the issue. Enable this workaround for affected CPUs, and update the silicon errata documentation accordingly. Note that due to the manner in which Arm develops IP and tracks errata, some CPUs share a common erratum number. Signed-off-by: Mark Rutland Cc: Catalin Marinas Cc: Will Deacon Signed-off-by: Will Deacon [Mark: backport to v7.1.y] Signed-off-by: Mark Rutland Signed-off-by: Greg Kroah-Hartman --- Documentation/arch/arm64/silicon-errata.rst | 42 +++++++++++++++++++++ arch/arm64/Kconfig | 36 ++++++++++++++++++ arch/arm64/kernel/cpu_errata.c | 32 +++++++++++++++- 3 files changed, 108 insertions(+), 2 deletions(-) diff --git a/Documentation/arch/arm64/silicon-errata.rst b/Documentation/arch/arm64/silicon-errata.rst index 211119ce7adc7..a01e916ede17d 100644 --- a/Documentation/arch/arm64/silicon-errata.rst +++ b/Documentation/arch/arm64/silicon-errata.rst @@ -128,16 +128,28 @@ stable kernels. +----------------+-----------------+-----------------+-----------------------------+ | ARM | Cortex-A76 | #3324349 | ARM64_ERRATUM_3194386 | +----------------+-----------------+-----------------+-----------------------------+ +| ARM | Cortex-A76 | #4193800 | ARM64_ERRATUM_4118414 | ++----------------+-----------------+-----------------+-----------------------------+ +| ARM | Cortex-A76AE | #4193801 | ARM64_ERRATUM_4118414 | ++----------------+-----------------+-----------------+-----------------------------+ | ARM | Cortex-A77 | #1491015 | N/A | +----------------+-----------------+-----------------+-----------------------------+ | ARM | Cortex-A77 | #1508412 | ARM64_ERRATUM_1508412 | +----------------+-----------------+-----------------+-----------------------------+ | ARM | Cortex-A77 | #3324348 | ARM64_ERRATUM_3194386 | +----------------+-----------------+-----------------+-----------------------------+ +| ARM | Cortex-A77 | #4193798 | ARM64_ERRATUM_4118414 | ++----------------+-----------------+-----------------+-----------------------------+ | ARM | Cortex-A78 | #3324344 | ARM64_ERRATUM_3194386 | +----------------+-----------------+-----------------+-----------------------------+ +| ARM | Cortex-A78 | #4193791 | ARM64_ERRATUM_4118414 | ++----------------+-----------------+-----------------+-----------------------------+ +| ARM | Cortex-A78AE | #4193793 | ARM64_ERRATUM_4118414 | ++----------------+-----------------+-----------------+-----------------------------+ | ARM | Cortex-A78C | #3324346,3324347| ARM64_ERRATUM_3194386 | +----------------+-----------------+-----------------+-----------------------------+ +| ARM | Cortex-A78C | #4193794 | ARM64_ERRATUM_4118414 | ++----------------+-----------------+-----------------+-----------------------------+ | ARM | Cortex-A710 | #2119858 | ARM64_ERRATUM_2119858 | +----------------+-----------------+-----------------+-----------------------------+ | ARM | Cortex-A710 | #2054223 | ARM64_ERRATUM_2054223 | @@ -146,6 +158,8 @@ stable kernels. +----------------+-----------------+-----------------+-----------------------------+ | ARM | Cortex-A710 | #3324338 | ARM64_ERRATUM_3194386 | +----------------+-----------------+-----------------+-----------------------------+ +| ARM | Cortex-A710 | #4193788 | ARM64_ERRATUM_4118414 | ++----------------+-----------------+-----------------+-----------------------------+ | ARM | Cortex-A715 | #2645198 | ARM64_ERRATUM_2645198 | +----------------+-----------------+-----------------+-----------------------------+ | ARM | Cortex-A715 | #3456084 | ARM64_ERRATUM_3194386 | @@ -158,20 +172,32 @@ stable kernels. +----------------+-----------------+-----------------+-----------------------------+ | ARM | Cortex-X1 | #3324344 | ARM64_ERRATUM_3194386 | +----------------+-----------------+-----------------+-----------------------------+ +| ARM | Cortex-X1 | #4193791 | ARM64_ERRATUM_4118414 | ++----------------+-----------------+-----------------+-----------------------------+ | ARM | Cortex-X1C | #3324346 | ARM64_ERRATUM_3194386 | +----------------+-----------------+-----------------+-----------------------------+ +| ARM | Cortex-X1C | #4193792 | ARM64_ERRATUM_4118414 | ++----------------+-----------------+-----------------+-----------------------------+ | ARM | Cortex-X2 | #2119858 | ARM64_ERRATUM_2119858 | +----------------+-----------------+-----------------+-----------------------------+ | ARM | Cortex-X2 | #2224489 | ARM64_ERRATUM_2224489 | +----------------+-----------------+-----------------+-----------------------------+ | ARM | Cortex-X2 | #3324338 | ARM64_ERRATUM_3194386 | +----------------+-----------------+-----------------+-----------------------------+ +| ARM | Cortex-X2 | #4193788 | ARM64_ERRATUM_4118414 | ++----------------+-----------------+-----------------+-----------------------------+ | ARM | Cortex-X3 | #3324335 | ARM64_ERRATUM_3194386 | +----------------+-----------------+-----------------+-----------------------------+ +| ARM | Cortex-X3 | #4193786 | ARM64_ERRATUM_4118414 | ++----------------+-----------------+-----------------+-----------------------------+ | ARM | Cortex-X4 | #3194386 | ARM64_ERRATUM_3194386 | +----------------+-----------------+-----------------+-----------------------------+ +| ARM | Cortex-X4 | #4118414 | ARM64_ERRATUM_4118414 | ++----------------+-----------------+-----------------+-----------------------------+ | ARM | Cortex-X925 | #3324334 | ARM64_ERRATUM_3194386 | +----------------+-----------------+-----------------+-----------------------------+ +| ARM | Cortex-X925 | #4193781 | ARM64_ERRATUM_4118414 | ++----------------+-----------------+-----------------+-----------------------------+ | ARM | Neoverse-N1 | #1188873,1418040| ARM64_ERRATUM_1418040 | +----------------+-----------------+-----------------+-----------------------------+ | ARM | Neoverse-N1 | #1349291 | N/A | @@ -182,6 +208,8 @@ stable kernels. +----------------+-----------------+-----------------+-----------------------------+ | ARM | Neoverse-N1 | #3324349 | ARM64_ERRATUM_3194386 | +----------------+-----------------+-----------------+-----------------------------+ +| ARM | Neoverse-N1 | #4193800 | ARM64_ERRATUM_4118414 | ++----------------+-----------------+-----------------+-----------------------------+ | ARM | Neoverse-N2 | #2139208 | ARM64_ERRATUM_2139208 | +----------------+-----------------+-----------------+-----------------------------+ | ARM | Neoverse-N2 | #2067961 | ARM64_ERRATUM_2067961 | @@ -190,20 +218,34 @@ stable kernels. +----------------+-----------------+-----------------+-----------------------------+ | ARM | Neoverse-N2 | #3324339 | ARM64_ERRATUM_3194386 | +----------------+-----------------+-----------------+-----------------------------+ +| ARM | Neoverse-N2 | #4193789 | ARM64_ERRATUM_4118414 | ++----------------+-----------------+-----------------+-----------------------------+ | ARM | Neoverse-N3 | #3456111 | ARM64_ERRATUM_3194386 | +----------------+-----------------+-----------------+-----------------------------+ | ARM | Neoverse-V1 | #1619801 | N/A | +----------------+-----------------+-----------------+-----------------------------+ | ARM | Neoverse-V1 | #3324341 | ARM64_ERRATUM_3194386 | +----------------+-----------------+-----------------+-----------------------------+ +| ARM | Neoverse-V1 | #4193790 | ARM64_ERRATUM_4118414 | ++----------------+-----------------+-----------------+-----------------------------+ | ARM | Neoverse-V2 | #3324336 | ARM64_ERRATUM_3194386 | +----------------+-----------------+-----------------+-----------------------------+ +| ARM | Neoverse-V2 | #4193787 | ARM64_ERRATUM_4118414 | ++----------------+-----------------+-----------------+-----------------------------+ | ARM | Neoverse-V3 | #3312417 | ARM64_ERRATUM_3194386 | +----------------+-----------------+-----------------+-----------------------------+ +| ARM | Neoverse-V3 | #4193784 | ARM64_ERRATUM_4118414 | ++----------------+-----------------+-----------------+-----------------------------+ | ARM | Neoverse-V3AE | #3312417 | ARM64_ERRATUM_3194386 | +----------------+-----------------+-----------------+-----------------------------+ +| ARM | Neoverse-V3AE | #4193784 | ARM64_ERRATUM_4118414 | ++----------------+-----------------+-----------------+-----------------------------+ +| ARM | C1-Premium | #4193780 | ARM64_ERRATUM_4118414 | ++----------------+-----------------+-----------------+-----------------------------+ | ARM | C1-Pro | #4193714 | ARM64_ERRATUM_4193714 | +----------------+-----------------+-----------------+-----------------------------+ +| ARM | C1-Ultra | #4193780 | ARM64_ERRATUM_4118414 | ++----------------+-----------------+-----------------+-----------------------------+ | ARM | MMU-500 | #841119,826419 | ARM_SMMU_MMU_500_CPRE_ERRATA| | | | #562869,1047329 | | +----------------+-----------------+-----------------+-----------------------------+ diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index fe60738e5943b..48233b54c4820 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1154,6 +1154,42 @@ config ARM64_ERRATUM_4193714 If unsure, say Y. +config ARM64_ERRATUM_4118414 + bool "Cortex-*/Neoverse-*/C1-*: Completion of affected memory accesses might not be guaranteed by completion of a TLBI" + default y + select ARM64_WORKAROUND_REPEAT_TLBI + help + This option adds a workaround for the following errata: + + * ARM C1-Premium erratum 4193780 + * ARM C1-Ultra erratum 4193780 + * ARM Cortex-A76 erratum 4193800 + * ARM Cortex-A76AE erratum 4193801 + * ARM Cortex-A77 erratum 4193798 + * ARM Cortex-A78 erratum 4193791 + * ARM Cortex-A78AE erratum 4193793 + * ARM Cortex-A78C erratum 4193794 + * ARM Cortex-A710 erratum 4193788 + * ARM Cortex-X1 erratum 4193791 + * ARM Cortex-X1C erratum 4193792 + * ARM Cortex-X2 erratum 4193788 + * ARM Cortex-X3 erratum 4193786 + * ARM Cortex-X4 erratum 4118414 + * ARM Cortex-X925 erratum 4193781 + * ARM Neoverse-N1 erratum 4193800 + * ARM Neoverse-N2 erratum 4193789 + * ARM Neoverse-V1 erratum 4193790 + * ARM Neoverse-V2 erratum 4193787 + * ARM Neoverse-V3 erratum 4193784 + * ARM Neoverse-V3AE erratum 4193784 + + On affected cores, some memory accesses might not be completed by + broadcast TLB invalidation. + + This issue is also known as CVE-2025-10263. + + If unsure, say Y. + config CAVIUM_ERRATUM_22375 bool "Cavium erratum 22375, 24313" default y diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c index 5377e4c2eba2b..fe6fe5de495b0 100644 --- a/arch/arm64/kernel/cpu_errata.c +++ b/arch/arm64/kernel/cpu_errata.c @@ -340,7 +340,35 @@ static const struct arm64_cpu_capabilities arm64_repeat_tlbi_list[] = { ERRATA_MIDR_RANGE(MIDR_CORTEX_A510, 0, 0, 1, 1), }, #endif - {}, +#ifdef CONFIG_ARM64_ERRATUM_4118414 + { + ERRATA_MIDR_RANGE_LIST(((const struct midr_range[]) { + MIDR_ALL_VERSIONS(MIDR_C1_PREMIUM), + MIDR_ALL_VERSIONS(MIDR_C1_ULTRA), + MIDR_ALL_VERSIONS(MIDR_CORTEX_A76), + MIDR_ALL_VERSIONS(MIDR_CORTEX_A76AE), + MIDR_ALL_VERSIONS(MIDR_CORTEX_A77), + MIDR_ALL_VERSIONS(MIDR_CORTEX_A78), + MIDR_ALL_VERSIONS(MIDR_CORTEX_A78AE), + MIDR_ALL_VERSIONS(MIDR_CORTEX_A78C), + MIDR_ALL_VERSIONS(MIDR_CORTEX_A710), + MIDR_ALL_VERSIONS(MIDR_CORTEX_X1), + MIDR_ALL_VERSIONS(MIDR_CORTEX_X1C), + MIDR_ALL_VERSIONS(MIDR_CORTEX_X2), + MIDR_ALL_VERSIONS(MIDR_CORTEX_X3), + MIDR_ALL_VERSIONS(MIDR_CORTEX_X4), + MIDR_ALL_VERSIONS(MIDR_CORTEX_X925), + MIDR_ALL_VERSIONS(MIDR_NEOVERSE_N1), + MIDR_ALL_VERSIONS(MIDR_NEOVERSE_N2), + MIDR_ALL_VERSIONS(MIDR_NEOVERSE_V1), + MIDR_ALL_VERSIONS(MIDR_NEOVERSE_V2), + MIDR_ALL_VERSIONS(MIDR_NEOVERSE_V3), + MIDR_ALL_VERSIONS(MIDR_NEOVERSE_V3AE), + {} + })), + }, +#endif + {} }; #endif @@ -693,7 +721,7 @@ const struct arm64_cpu_capabilities arm64_errata[] = { #endif #ifdef CONFIG_ARM64_WORKAROUND_REPEAT_TLBI { - .desc = "Qualcomm erratum 1009, or ARM erratum 1286807, 2441009", + .desc = "Broken broadcast TLBI completion", .capability = ARM64_WORKAROUND_REPEAT_TLBI, .type = ARM64_CPUCAP_LOCAL_CPU_ERRATUM, .matches = cpucap_multi_entry_cap_matches, From 845c842eb29284626869bbf5c7d3043a8425712c Mon Sep 17 00:00:00 2001 From: Shanker Donthineni Date: Tue, 16 Jun 2026 13:19:56 +0100 Subject: [PATCH 0007/2758] arm64: errata: Mitigate TLBI errata on NVIDIA Olympus CPU commit ec7216f92e4ebd485b1c6dc6aa3f6064b71a5768 upstream. NVIDIA Olympus cores are affected by the TLBI completion issue tracked as CVE-2025-10263. The existing ARM64_ERRATUM_4118414 handling already uses ARM64_WORKAROUND_REPEAT_TLBI to issue an additional broadcast TLBI;DSB sequence and ensure affected memory write effects are globally observed. Add MIDR_NVIDIA_OLYMPUS to the repeat-TLBI match list so the same mitigation is enabled on affected Olympus systems. Also document the NVIDIA Olympus erratum in the arm64 silicon errata table and list it in the Kconfig help text. Signed-off-by: Shanker Donthineni Cc: Catalin Marinas Cc: Will Deacon Cc: Mark Rutland Acked-by: Mark Rutland Signed-off-by: Will Deacon [Mark: backport to v7.1.y] Signed-off-by: Mark Rutland Signed-off-by: Greg Kroah-Hartman --- Documentation/arch/arm64/silicon-errata.rst | 2 ++ arch/arm64/Kconfig | 3 ++- arch/arm64/kernel/cpu_errata.c | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Documentation/arch/arm64/silicon-errata.rst b/Documentation/arch/arm64/silicon-errata.rst index a01e916ede17d..ad09bbb10da80 100644 --- a/Documentation/arch/arm64/silicon-errata.rst +++ b/Documentation/arch/arm64/silicon-errata.rst @@ -298,6 +298,8 @@ stable kernels. +----------------+-----------------+-----------------+-----------------------------+ | NVIDIA | Carmel Core | N/A | NVIDIA_CARMEL_CNP_ERRATUM | +----------------+-----------------+-----------------+-----------------------------+ +| NVIDIA | Olympus core | T410-OLY-1029 | ARM64_ERRATUM_4118414 | ++----------------+-----------------+-----------------+-----------------------------+ | NVIDIA | T241 GICv3/4.x | T241-FABRIC-4 | N/A | +----------------+-----------------+-----------------+-----------------------------+ | NVIDIA | T241 MPAM | T241-MPAM-1 | N/A | diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 48233b54c4820..1069ae9535822 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1155,7 +1155,7 @@ config ARM64_ERRATUM_4193714 If unsure, say Y. config ARM64_ERRATUM_4118414 - bool "Cortex-*/Neoverse-*/C1-*: Completion of affected memory accesses might not be guaranteed by completion of a TLBI" + bool "Various: Completion of affected memory accesses might not be guaranteed by completion of a TLBI" default y select ARM64_WORKAROUND_REPEAT_TLBI help @@ -1182,6 +1182,7 @@ config ARM64_ERRATUM_4118414 * ARM Neoverse-V2 erratum 4193787 * ARM Neoverse-V3 erratum 4193784 * ARM Neoverse-V3AE erratum 4193784 + * NVIDIA Olympus erratum T410-OLY-1029 On affected cores, some memory accesses might not be completed by broadcast TLB invalidation. diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c index fe6fe5de495b0..d597896b0f7f3 100644 --- a/arch/arm64/kernel/cpu_errata.c +++ b/arch/arm64/kernel/cpu_errata.c @@ -364,6 +364,7 @@ static const struct arm64_cpu_capabilities arm64_repeat_tlbi_list[] = { MIDR_ALL_VERSIONS(MIDR_NEOVERSE_V2), MIDR_ALL_VERSIONS(MIDR_NEOVERSE_V3), MIDR_ALL_VERSIONS(MIDR_NEOVERSE_V3AE), + MIDR_ALL_VERSIONS(MIDR_NVIDIA_OLYMPUS), {} })), }, From fc10eff5a2a8b600448096a34ffbb21586bac88c Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 16 Jun 2026 13:19:57 +0100 Subject: [PATCH 0008/2758] arm64: errata: Mitigate TLBI errata on Microsoft Azure Cobalt 100 CPU commit 1940e70a8144bf75e6df26bf6f600862ea7f7ea1 upstream. Commit fb091ff39479 ("arm64: Subscribe Microsoft Azure Cobalt 100 to ARM Neoverse N2 errata") states that Microsoft Azure Cobalt 100 CPU "is a Microsoft implemented CPU based on r0p0 of the ARM Neoverse N2 CPU, and therefore suffers from all the same errata.". So enable the workaround for the latest broadcast TLB invalidation bug on these parts. Signed-off-by: Will Deacon [Mark: backport to v7.1.y] Signed-off-by: Mark Rutland Signed-off-by: Greg Kroah-Hartman --- Documentation/arch/arm64/silicon-errata.rst | 2 ++ arch/arm64/Kconfig | 1 + arch/arm64/kernel/cpu_errata.c | 1 + 3 files changed, 4 insertions(+) diff --git a/Documentation/arch/arm64/silicon-errata.rst b/Documentation/arch/arm64/silicon-errata.rst index ad09bbb10da80..ad04d1cdc0f05 100644 --- a/Documentation/arch/arm64/silicon-errata.rst +++ b/Documentation/arch/arm64/silicon-errata.rst @@ -367,3 +367,5 @@ stable kernels. +----------------+-----------------+-----------------+-----------------------------+ | Microsoft | Azure Cobalt 100| #3324339 | ARM64_ERRATUM_3194386 | +----------------+-----------------+-----------------+-----------------------------+ +| Microsoft | Azure Cobalt 100| #4193789 | ARM64_ERRATUM_4118414 | ++----------------+-----------------+-----------------+-----------------------------+ diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 1069ae9535822..10c69474f2761 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1182,6 +1182,7 @@ config ARM64_ERRATUM_4118414 * ARM Neoverse-V2 erratum 4193787 * ARM Neoverse-V3 erratum 4193784 * ARM Neoverse-V3AE erratum 4193784 + * Microsoft Azure Cobalt 100 4193789 * NVIDIA Olympus erratum T410-OLY-1029 On affected cores, some memory accesses might not be completed by diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c index d597896b0f7f3..4b0d5d9328972 100644 --- a/arch/arm64/kernel/cpu_errata.c +++ b/arch/arm64/kernel/cpu_errata.c @@ -365,6 +365,7 @@ static const struct arm64_cpu_capabilities arm64_repeat_tlbi_list[] = { MIDR_ALL_VERSIONS(MIDR_NEOVERSE_V3), MIDR_ALL_VERSIONS(MIDR_NEOVERSE_V3AE), MIDR_ALL_VERSIONS(MIDR_NVIDIA_OLYMPUS), + MIDR_ALL_VERSIONS(MIDR_MICROSOFT_AZURE_COBALT_100), {} })), }, From b15b03d6913a4bd05b394bdd6271d5ed471cbb3a Mon Sep 17 00:00:00 2001 From: Rafael Passos Date: Tue, 2 Jun 2026 00:05:19 -0300 Subject: [PATCH 0009/2758] HID: Input: Add battery list cleanup with devm action commit 426e5846eba75feaf1c9c6c119cb153610192da1 upstream. The batteries list (hdev->batteries) is not cleaned up during hidinput_disconnect(), but struct hid_battery entries are allocated with devm_kzalloc. When a driver is unbound (e.g. during devicereprobe), devm frees those entries while their list_head nodesremain dangling in hdev->batteries, which persists across rebinds. Link: https://lore.kernel.org/all/20260602011949.2825852-1-rafael@rcpassos.me/ Fixes: 4a58ae85c3f9 ("HID: input: Add support for multiple batteries per device") Signed-off-by: Rafael Passos Acked-by: Lucas Zampieri Signed-off-by: Benjamin Tissoires Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-input.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index d73cfa2e73d33..c7b8c4ff7a338 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -519,6 +519,13 @@ static struct hid_battery *hidinput_find_battery(struct hid_device *dev, return NULL; } +static void hidinput_cleanup_battery(void *res) +{ + struct hid_battery *bat = res; + + list_del(&bat->list); +} + static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type, struct hid_field *field, bool is_percentage) { @@ -610,6 +617,12 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type, power_supply_powers(bat->ps, &dev->dev); list_add_tail(&bat->list, &dev->batteries); + + error = devm_add_action_or_reset(&dev->dev, + hidinput_cleanup_battery, bat); + if (error) + return error; + return 0; err_free_name: From ce3fb4b97163bfbb56c745a80b7c7a2b17c5ab31 Mon Sep 17 00:00:00 2001 From: Honglei Huang Date: Fri, 29 May 2026 10:23:17 +0800 Subject: [PATCH 0010/2758] drm/amdgpu: drop retry loop in amdgpu_hmm_range_get_pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 342981fff32802a819d6fc7cf3c9fedf9f3d9d60 upstream. Since commit c08972f55594 ("drm/amdgpu: fix amdgpu_hmm_range_get_pages") moved mmu_interval_read_begin() out of the per-chunk loop, the captured notifier_seq is no longer refreshed across retries. As a result, the existing -EBUSY retry path can never make progress: hmm_range_fault() returns -EBUSY only when mmu_interval_check_retry(notifier, notifier_seq) reports that the sequence is stale. Once the sequence has advanced, the stored seq will never match again, so every subsequent call within the same invocation returns -EBUSY immediately. The "goto retry" therefore degenerates into a busy spin that simply burns CPU for the full HMM_RANGE_DEFAULT_TIMEOUT (~1s) window before finally bailing out with -EAGAIN. This is pure latency with no chance of recovery, and it actively hurts the KFD userptr stack: the caller ends up blocked for a second while holding mmap_lock, only to return -EAGAIN to the restore worker (or to userspace) which would have re-driven the operation immediately anyway. Drop the retry/timeout entirely and let -EBUSY propagate straight to out_free_pfns, where it is already translated to -EAGAIN. Recovery is handled at a higher level: the KFD restore_userptr_worker reschedules itself, and the userptr ioctl path returns -EAGAIN to userspace. No functional regression: the previous behaviour on -EBUSY was already to fail with -EAGAIN after a 1s stall; we just skip the stall. Reviewed-by: Christian König Signed-off-by: Honglei Huang Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c index e452444b33b08..99bc9ad67d5b8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c @@ -174,7 +174,6 @@ int amdgpu_hmm_range_get_pages(struct mmu_interval_notifier *notifier, const u64 max_bytes = SZ_2G; struct hmm_range *hmm_range = &range->hmm_range; - unsigned long timeout; unsigned long *pfns; unsigned long end; int r; @@ -201,15 +200,9 @@ int amdgpu_hmm_range_get_pages(struct mmu_interval_notifier *notifier, pr_debug("hmm range: start = 0x%lx, end = 0x%lx", hmm_range->start, hmm_range->end); - timeout = jiffies + msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT); - -retry: r = hmm_range_fault(hmm_range); - if (unlikely(r)) { - if (r == -EBUSY && !time_after(jiffies, timeout)) - goto retry; + if (unlikely(r)) goto out_free_pfns; - } if (hmm_range->end == end) break; From c9acdc466e9aa96352f658b9276aa8a45b8e817d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 19 Jun 2026 13:50:58 +0200 Subject: [PATCH 0011/2758] Linux 7.1.1 Link: https://lore.kernel.org/r/20260616145523.335696673@linuxfoundation.org Tested-by: Brett A C Sheffield Tested-by: Salvatore Bonaccorso Tested-by: Barry K. Nathan Tested-by: Takeshi Ogasawara Tested-by: Miguel Ojeda Tested-by: Mark Brown Tested-by: Shuah Khan Tested-by: Peter Schneider Tested-by: Luna Jernberg Tested-by: Ron Economos Signed-off-by: Greg Kroah-Hartman --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 408f070d49b8e..39baa3275447f 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 7 PATCHLEVEL = 1 -SUBLEVEL = 0 +SUBLEVEL = 1 EXTRAVERSION = NAME = Baby Opossum Posse From 7e00cafa33b50b191ab652411f78773cba1c0878 Mon Sep 17 00:00:00 2001 From: Gabriel Krisman Bertazi Date: Wed, 17 Jun 2026 13:49:47 -0400 Subject: [PATCH 0012/2758] io_uring/net: Avoid msghdr on op_connect/op_bind async data [ Upstream commit 3979840cd858f30f43ea9f4e7f7f1f56de82d698 ] This fixes a memory leak due to the lack of the cleanup hook for the iovec. The stable backport differs from upstream by dropping the io_connect_bpf_populate hunk, which didn't exist at the time and by fixing the merge conflict due to the introduction of io_bind_file_create. Both IORING_OP_CONNECT and IORING_OP_BIND reuse the msghdr object just to store the sockaddr. Beyond allocating a much larger object than needed, msghdr can also wrap an iovec, which will be recycled unnecessarily. This uses the sockaddr directly. Cc: stable@vger.kernel.org Signed-off-by: Gabriel Krisman Bertazi Link: https://patch.msgid.link/20260602215327.1885109-2-krisman@suse.de Signed-off-by: Jens Axboe Signed-off-by: Gabriel Krisman Bertazi Signed-off-by: Sasha Levin --- io_uring/net.c | 36 ++++++++++++++++++------------------ io_uring/opdef.c | 4 ++-- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/io_uring/net.c b/io_uring/net.c index ee848eb65ec99..bf60cd393b11a 100644 --- a/io_uring/net.c +++ b/io_uring/net.c @@ -1733,7 +1733,7 @@ int io_socket(struct io_kiocb *req, unsigned int issue_flags) int io_connect_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { struct io_connect *conn = io_kiocb_to_cmd(req, struct io_connect); - struct io_async_msghdr *io; + struct sockaddr_storage *addr; if (sqe->len || sqe->buf_index || sqe->rw_flags || sqe->splice_fd_in) return -EINVAL; @@ -1742,17 +1742,17 @@ int io_connect_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) conn->addr_len = READ_ONCE(sqe->addr2); conn->in_progress = conn->seen_econnaborted = false; - io = io_msg_alloc_async(req); - if (unlikely(!io)) + addr = io_uring_alloc_async_data(NULL, req); + if (unlikely(!addr)) return -ENOMEM; - return move_addr_to_kernel(conn->addr, conn->addr_len, &io->addr); + return move_addr_to_kernel(conn->addr, conn->addr_len, addr); } int io_connect(struct io_kiocb *req, unsigned int issue_flags) { struct io_connect *connect = io_kiocb_to_cmd(req, struct io_connect); - struct io_async_msghdr *io = req->async_data; + struct sockaddr_storage *addr = req->async_data; unsigned file_flags; int ret; bool force_nonblock = issue_flags & IO_URING_F_NONBLOCK; @@ -1766,8 +1766,7 @@ int io_connect(struct io_kiocb *req, unsigned int issue_flags) file_flags = force_nonblock ? O_NONBLOCK : 0; - ret = __sys_connect_file(req->file, &io->addr, connect->addr_len, - file_flags); + ret = __sys_connect_file(req->file, addr, connect->addr_len, file_flags); if ((ret == -EAGAIN || ret == -EINPROGRESS || ret == -ECONNABORTED) && force_nonblock) { if (ret == -EINPROGRESS) { @@ -1796,7 +1795,6 @@ int io_connect(struct io_kiocb *req, unsigned int issue_flags) out: if (ret < 0) req_set_fail(req); - io_req_msg_cleanup(req, issue_flags); io_req_set_res(req, ret, 0); return IOU_COMPLETE; } @@ -1806,15 +1804,15 @@ int io_connect(struct io_kiocb *req, unsigned int issue_flags) * which in turn end up in mnt_want_write() which will grab the fs * percpu start write sem. This can trigger a lockdep warning. */ -static int io_bind_file_create(const struct io_async_msghdr *io, int addr_len) +static int io_bind_file_create(const struct sockaddr_storage *addr, int addr_len) { const struct sockaddr_un *sun; - if (io->addr.ss_family != AF_UNIX) + if (addr->ss_family != AF_UNIX) return 0; if (addr_len <= offsetof(struct sockaddr_un, sun_path)) return 0; - sun = (const struct sockaddr_un *) &io->addr; + sun = (const struct sockaddr_un *) addr; return sun->sun_path[0] != '\0'; } @@ -1822,7 +1820,7 @@ int io_bind_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { struct io_bind *bind = io_kiocb_to_cmd(req, struct io_bind); struct sockaddr __user *uaddr; - struct io_async_msghdr *io; + struct sockaddr_storage *addr; int ret; if (sqe->len || sqe->buf_index || sqe->rw_flags || sqe->splice_fd_in) @@ -1831,21 +1829,23 @@ int io_bind_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) uaddr = u64_to_user_ptr(READ_ONCE(sqe->addr)); bind->addr_len = READ_ONCE(sqe->addr2); - io = io_msg_alloc_async(req); - if (unlikely(!io)) + addr = io_uring_alloc_async_data(NULL, req); + if (unlikely(!addr)) return -ENOMEM; - ret = move_addr_to_kernel(uaddr, bind->addr_len, &io->addr); + + ret = move_addr_to_kernel(uaddr, bind->addr_len, addr); if (unlikely(ret)) return ret; - if (io_bind_file_create(io, bind->addr_len)) + if (io_bind_file_create(addr, bind->addr_len)) req->flags |= REQ_F_FORCE_ASYNC; return 0; } + int io_bind(struct io_kiocb *req, unsigned int issue_flags) { struct io_bind *bind = io_kiocb_to_cmd(req, struct io_bind); - struct io_async_msghdr *io = req->async_data; + struct sockaddr_storage *addr = req->async_data; struct socket *sock; int ret; @@ -1853,7 +1853,7 @@ int io_bind(struct io_kiocb *req, unsigned int issue_flags) if (unlikely(!sock)) return -ENOTSOCK; - ret = __sys_bind_socket(sock, &io->addr, bind->addr_len); + ret = __sys_bind_socket(sock, addr, bind->addr_len); if (ret < 0) req_set_fail(req); io_req_set_res(req, ret, 0); diff --git a/io_uring/opdef.c b/io_uring/opdef.c index c3ef52b708113..34cd320a427b8 100644 --- a/io_uring/opdef.c +++ b/io_uring/opdef.c @@ -203,7 +203,7 @@ const struct io_issue_def io_issue_defs[] = { .unbound_nonreg_file = 1, .pollout = 1, #if defined(CONFIG_NET) - .async_size = sizeof(struct io_async_msghdr), + .async_size = sizeof(struct sockaddr_storage), .prep = io_connect_prep, .issue = io_connect, #else @@ -503,7 +503,7 @@ const struct io_issue_def io_issue_defs[] = { .needs_file = 1, .prep = io_bind_prep, .issue = io_bind, - .async_size = sizeof(struct io_async_msghdr), + .async_size = sizeof(struct sockaddr_storage), #else .prep = io_eopnotsupp_prep, #endif From e28db6ac4792d065ab32565fd9f0a2361c3d4666 Mon Sep 17 00:00:00 2001 From: Joanne Koong Date: Mon, 18 May 2026 22:28:06 -0700 Subject: [PATCH 0013/2758] fuse: re-lock request before replacing page cache folio commit a078484921052d0badd827fcc2770b5cfc1d4120 upstream. fuse_try_move_folio() unlocks the request on entry but does not re-lock it on the success path. This means fuse_chan_abort() can end the request and free the fuse_io_args (eg fuse_readpages_end()) while the subsequent copy chain logic after fuse_try_move_folio() accesses the fuse_io_args, leading to use-after-free issues. Fix this by calling lock_request() before replace_page_cache_folio(). This ensures the request is locked on the success path which will prevent the fuse_io_args from being freed while the later copying logic runs, and also ensures that the ap->folios[i]->mapping is never null since ap->folios[i] will always point to the newfolio after replace_page_cache_folio(). Fixes: ce534fb05292 ("fuse: allow splice to move pages") Cc: stable@vger.kernel.org Reported-by: Lei Lu Signed-off-by: Joanne Koong Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index c105aaf9ff5d7..71f9fcefc0ebe 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1037,6 +1037,10 @@ static int fuse_try_move_folio(struct fuse_copy_state *cs, struct folio **foliop if (WARN_ON(folio_test_mlocked(oldfolio))) goto out_fallback_unlock; + err = lock_request(cs->req); + if (err) + goto out_fallback_unlock; + replace_page_cache_folio(oldfolio, newfolio); folio_get(newfolio); @@ -1050,20 +1054,7 @@ static int fuse_try_move_folio(struct fuse_copy_state *cs, struct folio **foliop */ pipe_buf_release(cs->pipe, buf); - err = 0; - spin_lock(&cs->req->waitq.lock); - if (test_bit(FR_ABORTED, &cs->req->flags)) - err = -ENOENT; - else - *foliop = newfolio; - spin_unlock(&cs->req->waitq.lock); - - if (err) { - folio_unlock(newfolio); - folio_put(newfolio); - goto out_put_old; - } - + *foliop = newfolio; folio_unlock(oldfolio); /* Drop ref for ap->pages[] array */ folio_put(oldfolio); From 613257f919066b8b9c1cd48f78c31b8eb31b2c80 Mon Sep 17 00:00:00 2001 From: Yang Erkun Date: Wed, 13 May 2026 10:42:52 +0800 Subject: [PATCH 0014/2758] Revert "NFSD: Defer sub-object cleanup in export put callbacks" commit 516403d4d85607fdef3ca41d4a56b54e5566fa9a upstream. This reverts commit 48db892356d6cb80f6942885545de4a6dd8d2a29. Commit 48db892356d6 ("NFSD: Defer sub-object cleanup in export put callbacks") moved path_put() and auth_domain_put() out of svc_export_put() and expkey_put() and behind queue_rcu_work() to close a claimed use-after-free in e_show() and c_show() against ex_path and ex_client->name. Discussion in [1] shows neither the diagnosis nor the remedy survives review. The downstream teardown of both sub-objects is already RCU-deferred. auth_domain_put() reaches svcauth_unix_domain_release(), which frees the unix_domain and its ->name through call_rcu(). path_put() reaches dentry_free(), which frees the dentry through call_rcu(), and prepend_path() is already structured to tolerate concurrent dentry teardown. A reader in cache_seq_start_rcu() therefore observes both sub-objects through the next grace period regardless of whether svc_export_put() runs synchronously, so the synchronous form was never unsafe. The crash signature in the report cited by commit 48db892356d6 ("NFSD: Defer sub-object cleanup in export put callbacks") has a different root cause: a /proc/net/rpc cache file held open across network-namespace exit lets cache_destroy_net() free cd->hash_table while a reader is still walking it. The correct fix pins cd->net for the open fd's lifetime and does not require any deferral inside svc_export_put(). Meanwhile, deferring path_put() out of svc_export_put() reintroduces the regression that commit 69d803c40ede ("nfsd: Revert "nfsd: release svc_expkey/svc_export with rcu_work"") repaired: after "exportfs -r" drops the last cache reference, the mount reference held through ex_path lingers in the workqueue, so a subsequent umount fails with EBUSY. Restore the synchronous path_put() and auth_domain_put() in svc_export_put() and expkey_put() and the call_rcu()/kfree_rcu() free of the containing structures. The unrelated fix for ex_uuid/ex_stats from commit 2530766492ec ("nfsd: fix UAF when access ex_uuid or ex_stats") is preserved. Link: https://lore.kernel.org/all/10019b42-4589-4f9f-8d5b-d8197db1ce3c@huawei.com/ [1] Fixes: 48db892356d6 ("NFSD: Defer sub-object cleanup in export put callbacks") Cc: stable@vger.kernel.org Reviewed-by: Jeff Layton Tested-by: Alexandr Alexandrov Signed-off-by: Yang Erkun Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/export.c | 63 +++++++----------------------------------------- fs/nfsd/export.h | 7 ++---- fs/nfsd/nfsctl.c | 8 +----- 3 files changed, 12 insertions(+), 66 deletions(-) diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 665153f1720e0..0baa58d1dbfcd 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -36,30 +36,19 @@ * second map contains a reference to the entry in the first map. */ -static struct workqueue_struct *nfsd_export_wq; - #define EXPKEY_HASHBITS 8 #define EXPKEY_HASHMAX (1 << EXPKEY_HASHBITS) #define EXPKEY_HASHMASK (EXPKEY_HASHMAX -1) -static void expkey_release(struct work_struct *work) +static void expkey_put(struct kref *ref) { - struct svc_expkey *key = container_of(to_rcu_work(work), - struct svc_expkey, ek_rwork); + struct svc_expkey *key = container_of(ref, struct svc_expkey, h.ref); if (test_bit(CACHE_VALID, &key->h.flags) && !test_bit(CACHE_NEGATIVE, &key->h.flags)) path_put(&key->ek_path); auth_domain_put(key->ek_client); - kfree(key); -} - -static void expkey_put(struct kref *ref) -{ - struct svc_expkey *key = container_of(ref, struct svc_expkey, h.ref); - - INIT_RCU_WORK(&key->ek_rwork, expkey_release); - queue_rcu_work(nfsd_export_wq, &key->ek_rwork); + kfree_rcu(key, ek_rcu); } static int expkey_upcall(struct cache_detail *cd, struct cache_head *h) @@ -364,13 +353,11 @@ static void export_stats_destroy(struct export_stats *stats) EXP_STATS_COUNTERS_NUM); } -static void svc_export_release(struct work_struct *work) +static void svc_export_release(struct rcu_head *rcu_head) { - struct svc_export *exp = container_of(to_rcu_work(work), - struct svc_export, ex_rwork); + struct svc_export *exp = container_of(rcu_head, struct svc_export, + ex_rcu); - path_put(&exp->ex_path); - auth_domain_put(exp->ex_client); nfsd4_fslocs_free(&exp->ex_fslocs); export_stats_destroy(exp->ex_stats); kfree(exp->ex_stats); @@ -382,8 +369,9 @@ static void svc_export_put(struct kref *ref) { struct svc_export *exp = container_of(ref, struct svc_export, h.ref); - INIT_RCU_WORK(&exp->ex_rwork, svc_export_release); - queue_rcu_work(nfsd_export_wq, &exp->ex_rwork); + path_put(&exp->ex_path); + auth_domain_put(exp->ex_client); + call_rcu(&exp->ex_rcu, svc_export_release); } static int svc_export_upcall(struct cache_detail *cd, struct cache_head *h) @@ -1492,36 +1480,6 @@ const struct seq_operations nfs_exports_op = { .show = e_show, }; -/** - * nfsd_export_wq_init - allocate the export release workqueue - * - * Called once at module load. The workqueue runs deferred svc_export and - * svc_expkey release work scheduled by queue_rcu_work() in the cache put - * callbacks. - * - * Return values: - * %0: workqueue allocated - * %-ENOMEM: allocation failed - */ -int nfsd_export_wq_init(void) -{ - nfsd_export_wq = alloc_workqueue("nfsd_export", WQ_UNBOUND, 0); - if (!nfsd_export_wq) - return -ENOMEM; - return 0; -} - -/** - * nfsd_export_wq_shutdown - drain and free the export release workqueue - * - * Called once at module unload. Per-namespace teardown in - * nfsd_export_shutdown() has already drained all deferred work. - */ -void nfsd_export_wq_shutdown(void) -{ - destroy_workqueue(nfsd_export_wq); -} - /* * Initialize the exports module. */ @@ -1583,9 +1541,6 @@ nfsd_export_shutdown(struct net *net) cache_unregister_net(nn->svc_expkey_cache, net); cache_unregister_net(nn->svc_export_cache, net); - /* Drain deferred export and expkey release work. */ - rcu_barrier(); - flush_workqueue(nfsd_export_wq); cache_destroy_net(nn->svc_expkey_cache, net); cache_destroy_net(nn->svc_export_cache, net); svcauth_unix_purge(net); diff --git a/fs/nfsd/export.h b/fs/nfsd/export.h index b053993745749..d2b09cd761453 100644 --- a/fs/nfsd/export.h +++ b/fs/nfsd/export.h @@ -7,7 +7,6 @@ #include #include -#include #include #include @@ -76,7 +75,7 @@ struct svc_export { u32 ex_layout_types; struct nfsd4_deviceid_map *ex_devid_map; struct cache_detail *cd; - struct rcu_work ex_rwork; + struct rcu_head ex_rcu; unsigned long ex_xprtsec_modes; struct export_stats *ex_stats; }; @@ -93,7 +92,7 @@ struct svc_expkey { u32 ek_fsid[6]; struct path ek_path; - struct rcu_work ek_rwork; + struct rcu_head ek_rcu; }; #define EX_ISSYNC(exp) (!((exp)->ex_flags & NFSEXP_ASYNC)) @@ -111,8 +110,6 @@ __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp, /* * Function declarations */ -int nfsd_export_wq_init(void); -void nfsd_export_wq_shutdown(void); int nfsd_export_init(struct net *); void nfsd_export_shutdown(struct net *); void nfsd_export_flush(struct net *); diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 04e3954d54bd9..7cb8f95067453 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -2320,12 +2320,9 @@ static int __init init_nfsd(void) if (retval) goto out_free_pnfs; nfsd_lockd_init(); /* lockd->nfsd callbacks */ - retval = nfsd_export_wq_init(); - if (retval) - goto out_free_lockd; retval = register_pernet_subsys(&nfsd_net_ops); if (retval < 0) - goto out_free_export_wq; + goto out_free_lockd; retval = register_cld_notifier(); if (retval) goto out_free_subsys; @@ -2354,8 +2351,6 @@ static int __init init_nfsd(void) unregister_cld_notifier(); out_free_subsys: unregister_pernet_subsys(&nfsd_net_ops); -out_free_export_wq: - nfsd_export_wq_shutdown(); out_free_lockd: nfsd_lockd_shutdown(); nfsd_drc_slab_free(); @@ -2376,7 +2371,6 @@ static void __exit exit_nfsd(void) nfsd4_destroy_laundry_wq(); unregister_cld_notifier(); unregister_pernet_subsys(&nfsd_net_ops); - nfsd_export_wq_shutdown(); nfsd_drc_slab_free(); nfsd_lockd_shutdown(); nfsd4_free_slabs(); From cefe535a60a2e00e09f4b2689b0c8ffc6912459a Mon Sep 17 00:00:00 2001 From: Mingyu Wang <25181214217@stu.xidian.edu.cn> Date: Mon, 4 May 2026 15:48:23 +0800 Subject: [PATCH 0015/2758] agp/amd64: Fix broken error propagation in agp_amd64_probe() commit b08472db93b1ccff84a7adec5779d47f0e9d3a30 upstream. A NULL pointer dereference was observed in the AMD64 AGP driver when running in a virtualized environment (e.g. qemu/kvm) without a physical AMD northbridge. The crash occurs in amd64_fetch_size() when attempting to dereference the pointer returned by node_to_amd_nb(0). The root cause of this crash is broken error propagation in agp_amd64_probe(): When no AMD northbridges are found, cache_nbs() correctly returns -ENODEV. However, the probe function erroneously checks the return value against exactly -1, rather than < 0. As a result, the hardware absence error is masked, allowing the driver to improperly proceed with initialization. It eventually calls agp_add_bridge(), which invokes amd64_fetch_size(). Since the hardware does not exist, node_to_amd_nb(0) returns NULL, leading to a General Protection Fault (GPF) when accessing its ->misc member. Fix the issue by correcting the error check in agp_amd64_probe() to abort properly when cache_nbs() returns any negative error code. This prevents the driver from erroneously proceeding without hardware, thereby avoiding the subsequent NULL pointer dereference at its source. Fixes: a32073bffc65 ("[PATCH] x86_64: Clean and enhance up K8 northbridge access code") Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> Signed-off-by: Lukas Wunner Reviewed-by: Lukas Wunner Cc: stable@vger.kernel.org # v2.6.18+ Link: https://patch.msgid.link/20260504074823.99377-1-w15303746062@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/char/agp/amd64-agp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c index 2505df1f4e691..6741270e0a982 100644 --- a/drivers/char/agp/amd64-agp.c +++ b/drivers/char/agp/amd64-agp.c @@ -546,7 +546,7 @@ static int agp_amd64_probe(struct pci_dev *pdev, /* Fill in the mode register */ pci_read_config_dword(pdev, bridge->capndx+PCI_AGP_STATUS, &bridge->mode); - if (cache_nbs(pdev, cap_ptr) == -1) { + if (cache_nbs(pdev, cap_ptr) < 0) { agp_put_bridge(bridge); return -ENODEV; } From e545936e06f1c7173ab41a5f33a77ff43ced3a8d Mon Sep 17 00:00:00 2001 From: Sam Daly Date: Thu, 14 May 2026 18:23:21 +0200 Subject: [PATCH 0016/2758] iio: light: veml6075: add bounds check to veml6075_it_ms index commit 307dc4240bd41852d9e0912921e298160db1c109 upstream. veml6075_it_ms has 5 elements but VEML6075_CONF_IT can yield values 0-7. If it returns a value >= 5, this causes an out-of-bounds array access. Add a bounds check and return -EINVAL if the index is out of range. The problem values are reserved so should never be read from the register. Hence this is hardening against fault device, missprogramming or bus corruption. Assisted-by: gkh_clanker_2000 Cc: stable Signed-off-by: Sam Daly Signed-off-by: Greg Kroah-Hartman Reviewed-by: Javier Carrasco Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/light/veml6075.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/iio/light/veml6075.c b/drivers/iio/light/veml6075.c index edbb434070543..f7eb159e5cb40 100644 --- a/drivers/iio/light/veml6075.c +++ b/drivers/iio/light/veml6075.c @@ -100,7 +100,7 @@ static const struct iio_chan_spec veml6075_channels[] = { static int veml6075_request_measurement(struct veml6075_data *data) { - int ret, conf, int_time; + int ret, conf, int_time, int_index; ret = regmap_read(data->regmap, VEML6075_CMD_CONF, &conf); if (ret < 0) @@ -117,7 +117,11 @@ static int veml6075_request_measurement(struct veml6075_data *data) * time for all possible configurations. Using a 1.50 factor simplifies * operations and ensures reliability under all circumstances. */ - int_time = veml6075_it_ms[FIELD_GET(VEML6075_CONF_IT, conf)]; + int_index = FIELD_GET(VEML6075_CONF_IT, conf); + if (int_index >= ARRAY_SIZE(veml6075_it_ms)) + return -EINVAL; + + int_time = veml6075_it_ms[int_index]; msleep(int_time + (int_time / 2)); /* shutdown again, data registers are still accessible */ From abd776ded3e256889610595290f6ca46cb6e91ab Mon Sep 17 00:00:00 2001 From: Sam Daly Date: Thu, 14 May 2026 18:23:20 +0200 Subject: [PATCH 0017/2758] iio: adc: ti-ads1298: add bounds check to pga_settings index MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 95e8a48d7a85d4226934020e57815a3316d3a14b upstream. ads1298_pga_settings has 7 elements but ADS1298_MASK_CH_PGA can yield values 0-7. If it yields a value >= 7, this causes an out-of-bounds array access. Add a bounds check and return -EINVAL if the index is out of range. Note that the remaining value b111 is reserved so should not be seen in a correctly functioning system. Assisted-by: gkh_clanker_2000 Cc: stable Cc: Jonathan Cameron Cc: David Lechner Cc: "Nuno Sá" Cc: Andy Shevchenko Signed-off-by: Sam Daly Signed-off-by: Greg Kroah-Hartman Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/ti-ads1298.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/ti-ads1298.c b/drivers/iio/adc/ti-ads1298.c index ae30b47e45142..731792f06993a 100644 --- a/drivers/iio/adc/ti-ads1298.c +++ b/drivers/iio/adc/ti-ads1298.c @@ -279,6 +279,7 @@ static const u8 ads1298_pga_settings[] = { 6, 1, 2, 3, 4, 8, 12 }; static int ads1298_get_scale(struct ads1298_private *priv, int channel, int *val, int *val2) { + unsigned int pga_idx; int ret; unsigned int regval; u8 gain; @@ -302,7 +303,11 @@ static int ads1298_get_scale(struct ads1298_private *priv, if (ret) return ret; - gain = ads1298_pga_settings[FIELD_GET(ADS1298_MASK_CH_PGA, regval)]; + pga_idx = FIELD_GET(ADS1298_MASK_CH_PGA, regval); + if (pga_idx >= ARRAY_SIZE(ads1298_pga_settings)) + return -EINVAL; + + gain = ads1298_pga_settings[pga_idx]; *val /= gain; /* Full scale is VREF / gain */ *val2 = ADS1298_BITS_PER_SAMPLE - 1; /* Signed, hence the -1 */ From 3ae49dd04dbb11fb73f17f58a982dba128abe83a Mon Sep 17 00:00:00 2001 From: Giovanni Cabiddu Date: Mon, 11 May 2026 11:04:08 +0100 Subject: [PATCH 0018/2758] crypto: qat - remove unused character device and IOCTLs commit d237230728c567297f2f98b425d63156ab2ed17f upstream. The QAT driver exposes a character device (qat_adf_ctl) with IOCTLs for device configuration, start, stop, status query and enumeration. These IOCTLs are not part of any public uAPI header and have no known in-tree or out-of-tree users. Device lifecycle is already managed via sysfs. The ioctl interface also increases the attack surface and is the subject of a number of bug reports. Remove the character device, the IOCTL definitions, and the related data structures (adf_dev_status_info, adf_user_cfg_key_val, adf_user_cfg_section, adf_user_cfg_ctl_data). Drop the now-unused adf_cfg_user.h header and strip adf_ctl_drv.c down to the minimal module_init/module_exit hooks for workqueue, AER, and crypto/compression algorithm registration. Clean up leftover dead code that was only reachable from the removed IOCTL paths: adf_cfg_del_all(), adf_devmgr_verify_id(), adf_devmgr_get_num_dev(), adf_devmgr_get_dev_by_id(), adf_get_vf_real_id() and the unused ADF_CFG macros. Additionally, drop the entry associated to QAT IOCTLs in ioctl-number.rst. Cc: stable@vger.kernel.org Fixes: d8cba25d2c68 ("crypto: qat - Intel(R) QAT driver framework") Reported-by: Zhi Wang Reported-by: Bin Yu Reported-by: MingYu Wang Closes: https://lore.kernel.org/all/61d6d499.ab89.19b9b7f3186.Coremail.wangzhi_xd@stu.xidian.edu.cn/ Link: https://lore.kernel.org/all/20260508034841.256794-1-w15303746062@163.com/ Link: https://lore.kernel.org/all/20260508023542.256299-1-w15303746062@163.com/ Link: https://lore.kernel.org/all/20260504025120.98242-1-w15303746062@163.com/ Signed-off-by: Giovanni Cabiddu Reviewed-by: Ahsan Atta Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- .../userspace-api/ioctl/ioctl-number.rst | 1 - drivers/crypto/intel/qat/qat_common/adf_cfg.c | 10 - drivers/crypto/intel/qat/qat_common/adf_cfg.h | 1 - .../intel/qat/qat_common/adf_cfg_common.h | 32 -- .../intel/qat/qat_common/adf_cfg_user.h | 38 -- .../intel/qat/qat_common/adf_common_drv.h | 3 - .../crypto/intel/qat/qat_common/adf_ctl_drv.c | 404 +----------------- .../crypto/intel/qat/qat_common/adf_dev_mgr.c | 70 --- 8 files changed, 1 insertion(+), 558 deletions(-) delete mode 100644 drivers/crypto/intel/qat/qat_common/adf_cfg_user.h diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst index 331223761ffff..29a08bc059dd7 100644 --- a/Documentation/userspace-api/ioctl/ioctl-number.rst +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst @@ -229,7 +229,6 @@ Code Seq# Include File Comments 'a' all linux/atm*.h, linux/sonet.h ATM on linux -'a' 00-0F drivers/crypto/qat/qat_common/adf_cfg_common.h conflict! qat driver 'b' 00-FF conflict! bit3 vme host bridge 'b' 00-0F linux/dma-buf.h conflict! diff --git a/drivers/crypto/intel/qat/qat_common/adf_cfg.c b/drivers/crypto/intel/qat/qat_common/adf_cfg.c index c202209f17d5e..ea5d72d5090c0 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_cfg.c +++ b/drivers/crypto/intel/qat/qat_common/adf_cfg.c @@ -103,16 +103,6 @@ static void adf_cfg_section_del_all(struct list_head *head); static void adf_cfg_section_del_all_except(struct list_head *head, const char *section_name); -void adf_cfg_del_all(struct adf_accel_dev *accel_dev) -{ - struct adf_cfg_device_data *dev_cfg_data = accel_dev->cfg; - - down_write(&dev_cfg_data->lock); - adf_cfg_section_del_all(&dev_cfg_data->sec_list); - up_write(&dev_cfg_data->lock); - clear_bit(ADF_STATUS_CONFIGURED, &accel_dev->status); -} - void adf_cfg_del_all_except(struct adf_accel_dev *accel_dev, const char *section_name) { diff --git a/drivers/crypto/intel/qat/qat_common/adf_cfg.h b/drivers/crypto/intel/qat/qat_common/adf_cfg.h index 2afa6f0d15c51..108032b392425 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_cfg.h +++ b/drivers/crypto/intel/qat/qat_common/adf_cfg.h @@ -34,7 +34,6 @@ void adf_cfg_dev_remove(struct adf_accel_dev *accel_dev); void adf_cfg_dev_dbgfs_add(struct adf_accel_dev *accel_dev); void adf_cfg_dev_dbgfs_rm(struct adf_accel_dev *accel_dev); int adf_cfg_section_add(struct adf_accel_dev *accel_dev, const char *name); -void adf_cfg_del_all(struct adf_accel_dev *accel_dev); void adf_cfg_del_all_except(struct adf_accel_dev *accel_dev, const char *section_name); int adf_cfg_add_key_value_param(struct adf_accel_dev *accel_dev, diff --git a/drivers/crypto/intel/qat/qat_common/adf_cfg_common.h b/drivers/crypto/intel/qat/qat_common/adf_cfg_common.h index 81e9e9d7eccd2..d63f4dcccbb5d 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_cfg_common.h +++ b/drivers/crypto/intel/qat/qat_common/adf_cfg_common.h @@ -4,18 +4,11 @@ #define ADF_CFG_COMMON_H_ #include -#include #define ADF_CFG_MAX_STR_LEN 64 #define ADF_CFG_MAX_KEY_LEN_IN_BYTES ADF_CFG_MAX_STR_LEN #define ADF_CFG_MAX_VAL_LEN_IN_BYTES ADF_CFG_MAX_STR_LEN #define ADF_CFG_MAX_SECTION_LEN_IN_BYTES ADF_CFG_MAX_STR_LEN -#define ADF_CFG_BASE_DEC 10 -#define ADF_CFG_BASE_HEX 16 -#define ADF_CFG_ALL_DEVICES 0xFE -#define ADF_CFG_NO_DEVICE 0xFF -#define ADF_CFG_AFFINITY_WHATEVER 0xFF -#define MAX_DEVICE_NAME_SIZE 32 #define ADF_MAX_DEVICES (32 * 32) #define ADF_DEVS_ARRAY_SIZE BITS_TO_LONGS(ADF_MAX_DEVICES) @@ -51,29 +44,4 @@ enum adf_device_type { DEV_420XX, DEV_6XXX, }; - -struct adf_dev_status_info { - enum adf_device_type type; - __u32 accel_id; - __u32 instance_id; - __u8 num_ae; - __u8 num_accel; - __u8 num_logical_accel; - __u8 banks_per_accel; - __u8 state; - __u8 bus; - __u8 dev; - __u8 fun; - char name[MAX_DEVICE_NAME_SIZE]; -}; - -#define ADF_CTL_IOC_MAGIC 'a' -#define IOCTL_CONFIG_SYS_RESOURCE_PARAMETERS _IOW(ADF_CTL_IOC_MAGIC, 0, \ - struct adf_user_cfg_ctl_data) -#define IOCTL_STOP_ACCEL_DEV _IOW(ADF_CTL_IOC_MAGIC, 1, \ - struct adf_user_cfg_ctl_data) -#define IOCTL_START_ACCEL_DEV _IOW(ADF_CTL_IOC_MAGIC, 2, \ - struct adf_user_cfg_ctl_data) -#define IOCTL_STATUS_ACCEL_DEV _IOW(ADF_CTL_IOC_MAGIC, 3, __u32) -#define IOCTL_GET_NUM_DEVICES _IOW(ADF_CTL_IOC_MAGIC, 4, __s32) #endif diff --git a/drivers/crypto/intel/qat/qat_common/adf_cfg_user.h b/drivers/crypto/intel/qat/qat_common/adf_cfg_user.h deleted file mode 100644 index 421f4fb8b4dd2..0000000000000 --- a/drivers/crypto/intel/qat/qat_common/adf_cfg_user.h +++ /dev/null @@ -1,38 +0,0 @@ -/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) */ -/* Copyright(c) 2014 - 2020 Intel Corporation */ -#ifndef ADF_CFG_USER_H_ -#define ADF_CFG_USER_H_ - -#include "adf_cfg_common.h" -#include "adf_cfg_strings.h" - -struct adf_user_cfg_key_val { - char key[ADF_CFG_MAX_KEY_LEN_IN_BYTES]; - char val[ADF_CFG_MAX_VAL_LEN_IN_BYTES]; - union { - struct adf_user_cfg_key_val *next; - __u64 padding3; - }; - enum adf_cfg_val_type type; -} __packed; - -struct adf_user_cfg_section { - char name[ADF_CFG_MAX_SECTION_LEN_IN_BYTES]; - union { - struct adf_user_cfg_key_val *params; - __u64 padding1; - }; - union { - struct adf_user_cfg_section *next; - __u64 padding3; - }; -} __packed; - -struct adf_user_cfg_ctl_data { - union { - struct adf_user_cfg_section *config_section; - __u64 padding; - }; - __u8 device_id; -} __packed; -#endif diff --git a/drivers/crypto/intel/qat/qat_common/adf_common_drv.h b/drivers/crypto/intel/qat/qat_common/adf_common_drv.h index 7b8b295ac4590..8ff35dd840f3f 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_common_drv.h +++ b/drivers/crypto/intel/qat/qat_common/adf_common_drv.h @@ -68,10 +68,7 @@ int adf_devmgr_add_dev(struct adf_accel_dev *accel_dev, void adf_devmgr_rm_dev(struct adf_accel_dev *accel_dev, struct adf_accel_dev *pf); struct list_head *adf_devmgr_get_head(void); -struct adf_accel_dev *adf_devmgr_get_dev_by_id(u32 id); struct adf_accel_dev *adf_devmgr_pci_to_accel_dev(struct pci_dev *pci_dev); -int adf_devmgr_verify_id(u32 id); -void adf_devmgr_get_num_dev(u32 *num); int adf_devmgr_in_reset(struct adf_accel_dev *accel_dev); int adf_dev_started(struct adf_accel_dev *accel_dev); int adf_dev_restarting_notify(struct adf_accel_dev *accel_dev); diff --git a/drivers/crypto/intel/qat/qat_common/adf_ctl_drv.c b/drivers/crypto/intel/qat/qat_common/adf_ctl_drv.c index c2e6f0cb7480b..f01f2946de6e8 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_ctl_drv.c +++ b/drivers/crypto/intel/qat/qat_common/adf_ctl_drv.c @@ -2,410 +2,13 @@ /* Copyright(c) 2014 - 2020 Intel Corporation */ #include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include "adf_accel_devices.h" #include "adf_common_drv.h" -#include "adf_cfg.h" -#include "adf_cfg_common.h" -#include "adf_cfg_user.h" - -#define ADF_CFG_MAX_SECTION 512 -#define ADF_CFG_MAX_KEY_VAL 256 - -#define DEVICE_NAME "qat_adf_ctl" - -static DEFINE_MUTEX(adf_ctl_lock); -static long adf_ctl_ioctl(struct file *fp, unsigned int cmd, unsigned long arg); - -static const struct file_operations adf_ctl_ops = { - .owner = THIS_MODULE, - .unlocked_ioctl = adf_ctl_ioctl, - .compat_ioctl = compat_ptr_ioctl, -}; - -static const struct class adf_ctl_class = { - .name = DEVICE_NAME, -}; - -struct adf_ctl_drv_info { - unsigned int major; - struct cdev drv_cdev; -}; - -static struct adf_ctl_drv_info adf_ctl_drv; - -static void adf_chr_drv_destroy(void) -{ - device_destroy(&adf_ctl_class, MKDEV(adf_ctl_drv.major, 0)); - cdev_del(&adf_ctl_drv.drv_cdev); - class_unregister(&adf_ctl_class); - unregister_chrdev_region(MKDEV(adf_ctl_drv.major, 0), 1); -} - -static int adf_chr_drv_create(void) -{ - dev_t dev_id; - struct device *drv_device; - int ret; - - if (alloc_chrdev_region(&dev_id, 0, 1, DEVICE_NAME)) { - pr_err("QAT: unable to allocate chrdev region\n"); - return -EFAULT; - } - - ret = class_register(&adf_ctl_class); - if (ret) - goto err_chrdev_unreg; - - adf_ctl_drv.major = MAJOR(dev_id); - cdev_init(&adf_ctl_drv.drv_cdev, &adf_ctl_ops); - if (cdev_add(&adf_ctl_drv.drv_cdev, dev_id, 1)) { - pr_err("QAT: cdev add failed\n"); - goto err_class_destr; - } - - drv_device = device_create(&adf_ctl_class, NULL, - MKDEV(adf_ctl_drv.major, 0), - NULL, DEVICE_NAME); - if (IS_ERR(drv_device)) { - pr_err("QAT: failed to create device\n"); - goto err_cdev_del; - } - return 0; -err_cdev_del: - cdev_del(&adf_ctl_drv.drv_cdev); -err_class_destr: - class_unregister(&adf_ctl_class); -err_chrdev_unreg: - unregister_chrdev_region(dev_id, 1); - return -EFAULT; -} - -static struct adf_user_cfg_ctl_data *adf_ctl_alloc_resources(unsigned long arg) -{ - struct adf_user_cfg_ctl_data *cfg_data; - - cfg_data = memdup_user((void __user *)arg, sizeof(*cfg_data)); - if (IS_ERR(cfg_data)) - pr_err("QAT: failed to copy from user cfg_data.\n"); - return cfg_data; -} - -static int adf_add_key_value_data(struct adf_accel_dev *accel_dev, - const char *section, - const struct adf_user_cfg_key_val *key_val) -{ - if (key_val->type == ADF_HEX) { - long *ptr = (long *)key_val->val; - long val = *ptr; - - if (adf_cfg_add_key_value_param(accel_dev, section, - key_val->key, (void *)val, - key_val->type)) { - dev_err(&GET_DEV(accel_dev), - "failed to add hex keyvalue.\n"); - return -EFAULT; - } - } else { - if (adf_cfg_add_key_value_param(accel_dev, section, - key_val->key, key_val->val, - key_val->type)) { - dev_err(&GET_DEV(accel_dev), - "failed to add keyvalue.\n"); - return -EFAULT; - } - } - return 0; -} - -static int adf_copy_key_value_data(struct adf_accel_dev *accel_dev, - struct adf_user_cfg_ctl_data *ctl_data) -{ - struct adf_user_cfg_key_val key_val; - struct adf_user_cfg_key_val *params_head; - struct adf_user_cfg_section section, *section_head; - int i, j; - - section_head = ctl_data->config_section; - - for (i = 0; section_head && i < ADF_CFG_MAX_SECTION; i++) { - if (copy_from_user(§ion, (void __user *)section_head, - sizeof(*section_head))) { - dev_err(&GET_DEV(accel_dev), - "failed to copy section info\n"); - goto out_err; - } - - if (adf_cfg_section_add(accel_dev, section.name)) { - dev_err(&GET_DEV(accel_dev), - "failed to add section.\n"); - goto out_err; - } - - params_head = section.params; - - for (j = 0; params_head && j < ADF_CFG_MAX_KEY_VAL; j++) { - if (copy_from_user(&key_val, (void __user *)params_head, - sizeof(key_val))) { - dev_err(&GET_DEV(accel_dev), - "Failed to copy keyvalue.\n"); - goto out_err; - } - if (adf_add_key_value_data(accel_dev, section.name, - &key_val)) { - goto out_err; - } - params_head = key_val.next; - } - section_head = section.next; - } - return 0; -out_err: - adf_cfg_del_all(accel_dev); - return -EFAULT; -} - -static int adf_ctl_ioctl_dev_config(struct file *fp, unsigned int cmd, - unsigned long arg) -{ - struct adf_user_cfg_ctl_data *ctl_data; - struct adf_accel_dev *accel_dev; - int ret = 0; - - ctl_data = adf_ctl_alloc_resources(arg); - if (IS_ERR(ctl_data)) - return PTR_ERR(ctl_data); - - accel_dev = adf_devmgr_get_dev_by_id(ctl_data->device_id); - if (!accel_dev) { - ret = -EFAULT; - goto out; - } - - if (adf_dev_started(accel_dev)) { - ret = -EFAULT; - goto out; - } - - if (adf_copy_key_value_data(accel_dev, ctl_data)) { - ret = -EFAULT; - goto out; - } - set_bit(ADF_STATUS_CONFIGURED, &accel_dev->status); -out: - kfree(ctl_data); - return ret; -} - -static int adf_ctl_is_device_in_use(int id) -{ - struct adf_accel_dev *dev; - - list_for_each_entry(dev, adf_devmgr_get_head(), list) { - if (id == dev->accel_id || id == ADF_CFG_ALL_DEVICES) { - if (adf_devmgr_in_reset(dev) || adf_dev_in_use(dev)) { - dev_info(&GET_DEV(dev), - "device qat_dev%d is busy\n", - dev->accel_id); - return -EBUSY; - } - } - } - return 0; -} - -static void adf_ctl_stop_devices(u32 id) -{ - struct adf_accel_dev *accel_dev; - - list_for_each_entry(accel_dev, adf_devmgr_get_head(), list) { - if (id == accel_dev->accel_id || id == ADF_CFG_ALL_DEVICES) { - if (!adf_dev_started(accel_dev)) - continue; - - /* First stop all VFs */ - if (!accel_dev->is_vf) - continue; - - adf_dev_down(accel_dev); - } - } - - list_for_each_entry(accel_dev, adf_devmgr_get_head(), list) { - if (id == accel_dev->accel_id || id == ADF_CFG_ALL_DEVICES) { - if (!adf_dev_started(accel_dev)) - continue; - - adf_dev_down(accel_dev); - } - } -} - -static int adf_ctl_ioctl_dev_stop(struct file *fp, unsigned int cmd, - unsigned long arg) -{ - int ret; - struct adf_user_cfg_ctl_data *ctl_data; - - ctl_data = adf_ctl_alloc_resources(arg); - if (IS_ERR(ctl_data)) - return PTR_ERR(ctl_data); - - if (adf_devmgr_verify_id(ctl_data->device_id)) { - pr_err("QAT: Device %d not found\n", ctl_data->device_id); - ret = -ENODEV; - goto out; - } - - ret = adf_ctl_is_device_in_use(ctl_data->device_id); - if (ret) - goto out; - - if (ctl_data->device_id == ADF_CFG_ALL_DEVICES) - pr_info("QAT: Stopping all acceleration devices.\n"); - else - pr_info("QAT: Stopping acceleration device qat_dev%d.\n", - ctl_data->device_id); - - adf_ctl_stop_devices(ctl_data->device_id); - -out: - kfree(ctl_data); - return ret; -} - -static int adf_ctl_ioctl_dev_start(struct file *fp, unsigned int cmd, - unsigned long arg) -{ - int ret; - struct adf_user_cfg_ctl_data *ctl_data; - struct adf_accel_dev *accel_dev; - - ctl_data = adf_ctl_alloc_resources(arg); - if (IS_ERR(ctl_data)) - return PTR_ERR(ctl_data); - - ret = -ENODEV; - accel_dev = adf_devmgr_get_dev_by_id(ctl_data->device_id); - if (!accel_dev) - goto out; - - dev_info(&GET_DEV(accel_dev), - "Starting acceleration device qat_dev%d.\n", - ctl_data->device_id); - - ret = adf_dev_up(accel_dev, false); - - if (ret) { - dev_err(&GET_DEV(accel_dev), "Failed to start qat_dev%d\n", - ctl_data->device_id); - adf_dev_down(accel_dev); - } -out: - kfree(ctl_data); - return ret; -} - -static int adf_ctl_ioctl_get_num_devices(struct file *fp, unsigned int cmd, - unsigned long arg) -{ - u32 num_devices = 0; - - adf_devmgr_get_num_dev(&num_devices); - if (copy_to_user((void __user *)arg, &num_devices, sizeof(num_devices))) - return -EFAULT; - - return 0; -} - -static int adf_ctl_ioctl_get_status(struct file *fp, unsigned int cmd, - unsigned long arg) -{ - struct adf_hw_device_data *hw_data; - struct adf_dev_status_info dev_info; - struct adf_accel_dev *accel_dev; - - if (copy_from_user(&dev_info, (void __user *)arg, - sizeof(struct adf_dev_status_info))) { - pr_err("QAT: failed to copy from user.\n"); - return -EFAULT; - } - - accel_dev = adf_devmgr_get_dev_by_id(dev_info.accel_id); - if (!accel_dev) - return -ENODEV; - - hw_data = accel_dev->hw_device; - dev_info.state = adf_dev_started(accel_dev) ? DEV_UP : DEV_DOWN; - dev_info.num_ae = hw_data->get_num_aes(hw_data); - dev_info.num_accel = hw_data->get_num_accels(hw_data); - dev_info.num_logical_accel = hw_data->num_logical_accel; - dev_info.banks_per_accel = hw_data->num_banks - / hw_data->num_logical_accel; - strscpy(dev_info.name, hw_data->dev_class->name, sizeof(dev_info.name)); - dev_info.instance_id = hw_data->instance_id; - dev_info.type = hw_data->dev_class->type; - dev_info.bus = accel_to_pci_dev(accel_dev)->bus->number; - dev_info.dev = PCI_SLOT(accel_to_pci_dev(accel_dev)->devfn); - dev_info.fun = PCI_FUNC(accel_to_pci_dev(accel_dev)->devfn); - - if (copy_to_user((void __user *)arg, &dev_info, - sizeof(struct adf_dev_status_info))) { - dev_err(&GET_DEV(accel_dev), "failed to copy status.\n"); - return -EFAULT; - } - return 0; -} - -static long adf_ctl_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) -{ - int ret; - - if (mutex_lock_interruptible(&adf_ctl_lock)) - return -EFAULT; - - switch (cmd) { - case IOCTL_CONFIG_SYS_RESOURCE_PARAMETERS: - ret = adf_ctl_ioctl_dev_config(fp, cmd, arg); - break; - - case IOCTL_STOP_ACCEL_DEV: - ret = adf_ctl_ioctl_dev_stop(fp, cmd, arg); - break; - - case IOCTL_START_ACCEL_DEV: - ret = adf_ctl_ioctl_dev_start(fp, cmd, arg); - break; - - case IOCTL_GET_NUM_DEVICES: - ret = adf_ctl_ioctl_get_num_devices(fp, cmd, arg); - break; - - case IOCTL_STATUS_ACCEL_DEV: - ret = adf_ctl_ioctl_get_status(fp, cmd, arg); - break; - default: - pr_err_ratelimited("QAT: Invalid ioctl %d\n", cmd); - ret = -EFAULT; - break; - } - mutex_unlock(&adf_ctl_lock); - return ret; -} static int __init adf_register_ctl_device_driver(void) { - if (adf_chr_drv_create()) - goto err_chr_dev; - if (adf_init_misc_wq()) goto err_misc_wq; @@ -437,15 +40,11 @@ static int __init adf_register_ctl_device_driver(void) err_aer: adf_exit_misc_wq(); err_misc_wq: - adf_chr_drv_destroy(); -err_chr_dev: - mutex_destroy(&adf_ctl_lock); return -EFAULT; } static void __exit adf_unregister_ctl_device_driver(void) { - adf_chr_drv_destroy(); adf_exit_misc_wq(); adf_exit_aer(); adf_exit_vf_wq(); @@ -453,7 +52,6 @@ static void __exit adf_unregister_ctl_device_driver(void) qat_crypto_unregister(); qat_compression_unregister(); adf_clean_vf_map(false); - mutex_destroy(&adf_ctl_lock); } module_init(adf_register_ctl_device_driver); diff --git a/drivers/crypto/intel/qat/qat_common/adf_dev_mgr.c b/drivers/crypto/intel/qat/qat_common/adf_dev_mgr.c index e050de16ab5d0..161863841a527 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_dev_mgr.c +++ b/drivers/crypto/intel/qat/qat_common/adf_dev_mgr.c @@ -45,19 +45,6 @@ static struct vf_id_map *adf_find_vf(u32 bdf) return NULL; } -static int adf_get_vf_real_id(u32 fake) -{ - struct list_head *itr; - - list_for_each(itr, &vfs_table) { - struct vf_id_map *ptr = - list_entry(itr, struct vf_id_map, list); - if (ptr->fake_id == fake) - return ptr->id; - } - return -1; -} - /** * adf_clean_vf_map() - Cleans VF id mappings * @vf: flag indicating whether mappings is cleaned @@ -304,63 +291,6 @@ struct adf_accel_dev *adf_devmgr_pci_to_accel_dev(struct pci_dev *pci_dev) } EXPORT_SYMBOL_GPL(adf_devmgr_pci_to_accel_dev); -struct adf_accel_dev *adf_devmgr_get_dev_by_id(u32 id) -{ - struct list_head *itr; - int real_id; - - mutex_lock(&table_lock); - real_id = adf_get_vf_real_id(id); - if (real_id < 0) - goto unlock; - - id = real_id; - - list_for_each(itr, &accel_table) { - struct adf_accel_dev *ptr = - list_entry(itr, struct adf_accel_dev, list); - if (ptr->accel_id == id) { - mutex_unlock(&table_lock); - return ptr; - } - } -unlock: - mutex_unlock(&table_lock); - return NULL; -} - -int adf_devmgr_verify_id(u32 id) -{ - if (id == ADF_CFG_ALL_DEVICES) - return 0; - - if (adf_devmgr_get_dev_by_id(id)) - return 0; - - return -ENODEV; -} - -static int adf_get_num_dettached_vfs(void) -{ - struct list_head *itr; - int vfs = 0; - - mutex_lock(&table_lock); - list_for_each(itr, &vfs_table) { - struct vf_id_map *ptr = - list_entry(itr, struct vf_id_map, list); - if (ptr->bdf != ~0 && !ptr->attached) - vfs++; - } - mutex_unlock(&table_lock); - return vfs; -} - -void adf_devmgr_get_num_dev(u32 *num) -{ - *num = num_devices - adf_get_num_dettached_vfs(); -} - /** * adf_dev_in_use() - Check whether accel_dev is currently in use * @accel_dev: Pointer to acceleration device. From 09a43e81279b8da15526da09877134b8bcf618b0 Mon Sep 17 00:00:00 2001 From: Yi Yang Date: Thu, 4 Jun 2026 06:07:34 +0000 Subject: [PATCH 0019/2758] vc_screen: fix null-ptr-deref in vcs_notifier() during concurrent vcs_write commit a287620312dc6dcb9a093417a0e589bf30fcf38a upstream. A KASAN null-ptr-deref was observed in vcs_notifier(): BUG: KASAN: null-ptr-deref in vcs_notifier+0x98/0x130 Read of size 2 at addr qmp_cmd_name: qmp_capabilities, arguments: {} The issue is a race condition in vcs_write(). When the console_lock is temporarily dropped (to copy data from userspace), the vc_data pointer obtained from vcs_vc() may become stale. After re-acquiring the lock, vcs_vc() is called again to re-validate the pointer. If the vc has been deallocated in the meantime, vcs_vc() returns NULL, and the while loop breaks (with written > 0). However, after the loop, vcs_scr_updated(vc) is still called with the now-NULL vc pointer, leading to a null pointer dereference in the notifier chain (vcs_notifier dereferences param->vc). Fix this by adding a NULL check for vc before calling vcs_scr_updated(). Fixes: 8fb9ea65c9d1 ("vc_screen: reload load of struct vc_data pointer in vcs_write() to avoid UAF") Cc: stable@vger.kernel.org Signed-off-by: Yi Yang Reviewed-by: Jiri Slaby Link: https://patch.msgid.link/20260604060734.2914976-1-yiyang13@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vc_screen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c index 4d2d46c95fefd..7d40eacc21b3e 100644 --- a/drivers/tty/vt/vc_screen.c +++ b/drivers/tty/vt/vc_screen.c @@ -686,7 +686,7 @@ vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) } *ppos += written; ret = written; - if (written) + if (written && vc) vcs_scr_updated(vc); return ret; From ee6754f583a9892c113996f684427f2b72975cb9 Mon Sep 17 00:00:00 2001 From: Viken Dadhaniya Date: Thu, 28 May 2026 22:48:07 +0530 Subject: [PATCH 0020/2758] serial: qcom_geni: Fix RX DMA stall when SE_DMA_RX_LEN_IN is zero commit b93062b6d8a1b2d9bad235cac25558a909819026 upstream. In qcom_geni_serial_handle_rx_dma(), geni_se_rx_dma_unprep() clears port->rx_dma_addr before SE_DMA_RX_LEN_IN is read. If the register is zero, for example when the RX stale counter fires on an idle line, the handler returns without calling geni_se_rx_dma_prep(). The next RX DMA interrupt then hits the !port->rx_dma_addr guard and returns immediately, so the RX DMA buffer is never rearmed and later input is lost. Keep the handler on the rearm path when rx_in is zero. Warn about the unexpected zero-length DMA completion, skip received-data handling, and always call geni_se_rx_dma_prep(). Fixes: 2aaa43c70778 ("tty: serial: qcom-geni-serial: add support for serial engine DMA") Cc: stable@vger.kernel.org Reviewed-by: Bartosz Golaszewski Signed-off-by: Viken Dadhaniya Link: https://patch.msgid.link/20260528-serial-rx-0-byte-fix-v2-1-b4195cfe342f@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/qcom_geni_serial.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 17da115b1e78b..3b0f836a98222 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -905,12 +905,9 @@ static void qcom_geni_serial_handle_rx_dma(struct uart_port *uport, bool drop) port->rx_dma_addr = 0; rx_in = readl(uport->membase + SE_DMA_RX_LEN_IN); - if (!rx_in) { - dev_warn(uport->dev, "serial engine reports 0 RX bytes in!\n"); - return; - } - - if (!drop) + if (!rx_in) + dev_warn_ratelimited(uport->dev, "serial engine reports 0 RX bytes in!\n"); + else if (!drop) handle_rx_uart(uport, rx_in); ret = geni_se_rx_dma_prep(&port->se, port->rx_buf, From 778b9dda4b24005a27bcd9c35c110bf8d7f259ca Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Thu, 14 May 2026 19:37:45 +0500 Subject: [PATCH 0021/2758] serial: 8250_dw: unregister 8250 port if clk_notifier_register() fails commit 10fc708b4de7f86002d2d735a2dbf3b5b7f65692 upstream. dw8250_probe() registers the 8250 port via serial8250_register_8250_port() and then, if the device has a clock, registers a clock notifier. If clk_notifier_register() fails, probe returns the error but leaves the 8250 port registered. The matching serial8250_unregister_port() lives in dw8250_remove(), which is not called when probe fails, so the port slot stays occupied until the device is rebound or the system is rebooted. The devm-allocated driver data is freed while the port still references it (via the saved private_data and serial_in/serial_out callbacks), so any access to that port slot before a rebind is a use-after-free hazard. Unregister the port on the clk_notifier_register() error path. Fixes: cc816969d7b5 ("serial: 8250_dw: Fix common clocks usage race condition") Cc: stable@vger.kernel.org Signed-off-by: Stepan Ionichev Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260514143746.23671-2-sozdayvek@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 2af0c4d0ad823..035b46e136666 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -850,8 +850,10 @@ static int dw8250_probe(struct platform_device *pdev) */ if (data->clk) { err = clk_notifier_register(data->clk, &data->clk_notifier); - if (err) + if (err) { + serial8250_unregister_port(data->data.line); return dev_err_probe(dev, err, "Failed to set the clock notifier\n"); + } queue_work(system_dfl_wq, &data->clk_work); } From 059ac6252a63edf1cea79bf30bd860a8c264b62c Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Thu, 14 May 2026 02:26:57 -0700 Subject: [PATCH 0022/2758] drivers/base/memory: set mem->altmap after successful device registration commit a2b8d7827f48ee54a686cb80e4a1d0ff954ec42a upstream. If __add_memory_block() fails at xa_store() (under memory pressure for example), device_unregister() is called, which eventually triggers memory_block_release() with mem->altmap still set, causing a WARN_ON(mem->altmap). This was triggered by modifying virtio-mem driver. Fix this by delaying the assignment of mem->altmap until after __add_memory_block() has succeeded. Link: https://lore.kernel.org/20260514092657.3057141-1-georgi.djakov@oss.qualcomm.com Fixes: 1a8c64e11043 ("mm/memory_hotplug: embed vmem_altmap details in memory block") Signed-off-by: Georgi Djakov Acked-by: Oscar Salvador (SUSE) Cc: Vishal Verma Cc: Mike Rapoport Cc: Richard Cheng Cc: David Hildenbrand Cc: Georgi Djakov Cc: Greg Kroah-Hartman Cc: "Rafael J. Wysocki" Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/base/memory.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 6981b55d582ac..843f85d3e3f9b 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -807,7 +807,6 @@ static int add_memory_block(unsigned long block_id, int nid, unsigned long state mem->start_section_nr = block_id * sections_per_block; mem->state = state; mem->nid = nid; - mem->altmap = altmap; INIT_LIST_HEAD(&mem->group_next); #ifndef CONFIG_NUMA @@ -825,6 +824,8 @@ static int add_memory_block(unsigned long block_id, int nid, unsigned long state if (ret) return ret; + mem->altmap = altmap; + if (group) { mem->group = group; list_add(&mem->group_next, &group->memory_blocks); From 5f983b864d3d473ac533b2f4f44a1bbe5dcbccf4 Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Thu, 11 Jun 2026 22:59:19 +0900 Subject: [PATCH 0023/2758] ksmbd: reject non-VALID session in compound request branch commit 609ca17d869d04ba249e32cdcbf13c0b1c66f43c upstream. smb2_check_user_session() takes a shortcut for any operation that is not the first in a COMPOUND request: it reuses work->sess (the session bound by the first operation) and validates only the SessionId, then returns "valid". It never re-checks work->sess->state == SMB2_SESSION_VALID, and a SessionId of 0xFFFFFFFFFFFFFFFF (ULLONG_MAX, the MS-SMB2 related-operation value) skips even the id comparison. The standalone path (ksmbd_session_lookup_all() plus the SESSION_SETUP state machine) does enforce the VALID state; the compound branch bypasses all of it. A SESSION_SETUP carrying only an NTLM Type-1 (NtLmNegotiate) blob publishes a fresh SMB2_SESSION_IN_PROGRESS session whose sess->user is still NULL (->user is assigned later, by ntlm_authenticate()). Used as operation 1 of a COMPOUND with operation 2 = TREE_CONNECT (related, SessionId=ULLONG_MAX, \\host\IPC$), the tree-connect then runs on that IN_PROGRESS session and reaches ksmbd_ipc_tree_connect_request(), which dereferences user_name(sess->user) with sess->user == NULL (transport_ipc.c:687/701/704) -> remote NULL-pointer dereference and a kernel Oops that wedges the ksmbd worker for all clients. Reject any non-first compound operation that lands on a session which is not SMB2_SESSION_VALID, mirroring the validity the standalone lookup path enforces. SESSION_SETUP itself legitimately runs on an IN_PROGRESS session, but it is never carried as a non-first compound operation, so multi-leg authentication is unaffected by this check. Fixes: 5005bcb42191 ("ksmbd: validate session id and tree id in the compound request") Cc: stable@vger.kernel.org Signed-off-by: Gil Portnoy Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smb2pdu.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 3eb3b1711acb3..95d0ea045cfb0 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -613,6 +613,11 @@ int smb2_check_user_session(struct ksmbd_work *work) sess_id, work->sess->id); return -EINVAL; } + if (work->sess->state != SMB2_SESSION_VALID) { + pr_err("compound request on a non-valid session (state %d)\n", + work->sess->state); + return -EINVAL; + } return 1; } From 232e4b313ea342672edf8947e067c0de4328405b Mon Sep 17 00:00:00 2001 From: Ruslan Valiyev Date: Tue, 17 Mar 2026 17:05:44 +0000 Subject: [PATCH 0024/2758] media: vidtv: fix NULL pointer dereference in vidtv_mux_push_si commit 7d8bf3d8f91073f4db347ed3aa6302b56107499c upstream. syzbot reported a general protection fault in vidtv_psi_ts_psi_write_into [1]. vidtv_mux_get_pid_ctx() can return NULL, but vidtv_mux_push_si() does not check for this before dereferencing the returned pointer to access the continuity counter. This leads to a general protection fault when accessing a near-NULL address. The root cause is that vidtv_mux_pid_ctx_init() does not check the return value of vidtv_mux_create_pid_ctx_once() for PMT section PIDs. If the allocation fails, the PID context is never created, but init returns success. The subsequent vidtv_mux_push_si() call then gets NULL from vidtv_mux_get_pid_ctx() and crashes. Fix both the root cause (add error check in vidtv_mux_pid_ctx_init for PMT PIDs) and add defensive NULL checks in vidtv_mux_push_si for all vidtv_mux_get_pid_ctx() calls. [1] Oops: general protection fault, probably for non-canonical address 0xdffffc0000000000: 0000 [#1] SMP KASAN PTI KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] Workqueue: events vidtv_mux_tick RIP: 0010:vidtv_psi_ts_psi_write_into+0x54a/0xbc0 drivers/media/test-drivers/vidtv/vidtv_psi.c:197 Call Trace: vidtv_psi_table_header_write_into drivers/media/test-drivers/vidtv/vidtv_psi.c:799 [inline] vidtv_psi_pmt_write_into+0x3b2/0xa70 drivers/media/test-drivers/vidtv/vidtv_psi.c:1231 vidtv_mux_push_si+0x932/0xe80 drivers/media/test-drivers/vidtv/vidtv_mux.c:196 vidtv_mux_tick+0xe9b/0x1480 drivers/media/test-drivers/vidtv/vidtv_mux.c:408 Fixes: f90cf6079bf67 ("media: vidtv: add a bridge driver") Cc: stable@vger.kernel.org Reported-by: syzbot+814c351d094f4f1a1b86@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=814c351d094f4f1a1b86 Signed-off-by: Ruslan Valiyev Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/test-drivers/vidtv/vidtv_mux.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/media/test-drivers/vidtv/vidtv_mux.c b/drivers/media/test-drivers/vidtv/vidtv_mux.c index f0134e38a1fb8..ea2ea53102f9f 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_mux.c +++ b/drivers/media/test-drivers/vidtv/vidtv_mux.c @@ -101,7 +101,8 @@ static int vidtv_mux_pid_ctx_init(struct vidtv_mux *m) /* add a ctx for all PMT sections */ while (p) { pid = vidtv_psi_get_pat_program_pid(p); - vidtv_mux_create_pid_ctx_once(m, pid); + if (!vidtv_mux_create_pid_ctx_once(m, pid)) + goto free; p = p->next; } @@ -170,6 +171,9 @@ static u32 vidtv_mux_push_si(struct vidtv_mux *m) nit_ctx = vidtv_mux_get_pid_ctx(m, VIDTV_NIT_PID); eit_ctx = vidtv_mux_get_pid_ctx(m, VIDTV_EIT_PID); + if (!pat_ctx || !sdt_ctx || !nit_ctx || !eit_ctx) + return 0; + pat_args.offset = m->mux_buf_offset; pat_args.continuity_counter = &pat_ctx->cc; @@ -186,6 +190,8 @@ static u32 vidtv_mux_push_si(struct vidtv_mux *m) } pmt_ctx = vidtv_mux_get_pid_ctx(m, pmt_pid); + if (!pmt_ctx) + continue; pmt_args.offset = m->mux_buf_offset; pmt_args.pmt = m->si.pmt_secs[i]; From e09412a714bcd49375198427bb4aa005037a9d6f Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 28 May 2026 10:58:24 +0200 Subject: [PATCH 0025/2758] virtiofs: fix UAF on submount umount MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 06b41351779e9289e8785694ade9042ae85e41ea upstream. iput() called from fuse_release_end() can Oops if the super block has already been destroyed. Normally this is prevented by waiting for num_waiting to go down to zero before commencing with super block shutdown. This only works, however, for the last submount instance, as the wait counter is per connection, not per superblock. Revert to using synchronous release requests for the auto_submounts case, which is virtiofs only at this time. Reported-by: Aurélien Bombo Reported-by: Zhihao Cheng Cc: Greg Kurz Closes: https://github.com/kata-containers/kata-containers/issues/12589 Fixes: 26e5c67deb2e ("fuse: fix livelock in synchronous file put from fuseblk workers") Cc: stable@vger.kernel.org Reviewed-by: Greg Kurz Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/file.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index f94f3dc082c6b..b31d0dffaf4c3 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -380,8 +380,14 @@ void fuse_file_release(struct inode *inode, struct fuse_file *ff, * aio and closes the fd before the aio completes. Since aio takes its * own ref to the file, the IO completion has to drop the ref, which is * how the fuse server can end up closing its clients' files. + * + * Exception is virtio-fs, which is not affected by the above (server is + * on host, cannot close open files in guest). Virtio-fs needs sync + * release, because the num_waiting mechanism to wait for all requests + * before commencing with fs shutdown doesn't work if submounts are + * used. */ - fuse_file_put(ff, false); + fuse_file_put(ff, ff->fm->fc->auto_submounts); } void fuse_release_common(struct file *file, bool isdir) From 03e2778d1f11de9260543f969e9e888a1c2bf830 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 27 Jun 2026 11:08:18 +0100 Subject: [PATCH 0026/2758] Linux 7.1.2 Link: https://lore.kernel.org/r/20260625125613.243729608@linuxfoundation.org Tested-by: Florian Fainelli Tested-by: Ronald Warsow Tested-by: Brett A C Sheffield Tested-by: Justin M. Forbes Tested-by: Peter Schneider Tested-by: Shuah Khan Tested-by: Ron Economos Tested-by: Miguel Ojeda Tested-by: Dileep Malepu Tested-by: Takeshi Ogasawara Tested-by: Mark Brown Tested-by: Jeffrin Jose T Signed-off-by: Greg Kroah-Hartman --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 39baa3275447f..78662694be586 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 7 PATCHLEVEL = 1 -SUBLEVEL = 1 +SUBLEVEL = 2 EXTRAVERSION = NAME = Baby Opossum Posse From 1ae7d5a6db6c190ce183e3098ca0e0846e14d462 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 26 Jun 2026 13:22:32 +0200 Subject: [PATCH 0027/2758] KVM: x86: Fix shadow paging use-after-free due to unexpected role commit 81ccda30b4e83d8f5cc4fd50503c44e3a33abfeb upstream. Commit 0cb2af2ea66ad ("KVM: x86: Fix shadow paging use-after-free due to unexpected GFN") fixed a shadow paging mismatch between stored and computed GFNs; the bug could be triggered by changing a PDE mapping from outside the guest, and then deleting a memslot. The rmap_remove() call would miss entries created after the PDE change because the GFN of the leaf SPTE does not match the GFN of the struct kvm_mmu_page. A similar hole however remains if the modified PDE points to a non-leaf page. In this case the gfn can be made to match, but the role does not match: the original large 2MB page creates a kvm_mmu_page with direct=1, while the new 4KB needs a kvm_mmu_page with direct=0. However, kvm_mmu_get_child_sp() does not compare the role, and therefore reuses the page. The next step is installing a leaf (4KB) SPTE on the new path which records an rmap entry under the gfn resolved by the walk. But when that child is zapped its parent kvm_mmu_page has direct=1 and kvm_mmu_page_get_gfn() computes the gfn for the 4KB page as sp->gfn + index instead of using sp->shadowed_translation[] (or sp->gfns[] in older kernels). It therefore fails to remove the recorded entry. When the memslot is dropped the shadow page is freed but the rmap entry survives, as in the scenario that was already fixed. Code that later walks that gfn (dirty logging, MMU notifier invalidation, and so on) dereferences an sptep that lies in the freed page, causing the use-after-free. Fixes: 2032a93d66fa ("KVM: MMU: Don't allocate gfns page for direct mmu pages") Reported-by: Hyunwoo Kim Signed-off-by: Paolo Bonzini Signed-off-by: Sasha Levin --- arch/x86/kvm/mmu/mmu.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index f0144ae8d891d..bb204d3c66b7e 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -2453,13 +2453,15 @@ static struct kvm_mmu_page *kvm_mmu_get_child_sp(struct kvm_vcpu *vcpu, u64 *sptep, gfn_t gfn, bool direct, unsigned int access) { - union kvm_mmu_page_role role; + union kvm_mmu_page_role role = kvm_mmu_child_role(sptep, direct, access); - if (is_shadow_present_pte(*sptep) && !is_large_pte(*sptep) && - spte_to_child_sp(*sptep) && spte_to_child_sp(*sptep)->gfn == gfn) + if (is_shadow_present_pte(*sptep) && + !is_large_pte(*sptep) && + spte_to_child_sp(*sptep) && + spte_to_child_sp(*sptep)->gfn == gfn && + spte_to_child_sp(*sptep)->role.word == role.word) return ERR_PTR(-EEXIST); - role = kvm_mmu_child_role(sptep, direct, access); return kvm_mmu_get_shadow_page(vcpu, gfn, role); } From d7f6ffe690780ae1fa34a71416b9b19e8d2aa1c5 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:16 +0200 Subject: [PATCH 0028/2758] batman-adv: tp_meter: keep unacked list in ascending ordered commit 5aa8651527ea0b610e7a09fb3b8204c1398b9525 upstream. When batadv_tp_handle_out_of_order inserts a new entry in the list of unacked (out of order) packets, it searches from the entry with the newest sequence number towards oldest sequence number. If an entry is found which is older than the newly entry, the new entry has to be added after the found one to keep the ascending order. But for this operation list_add_tail() was used. But this function adds an entry _before_ another one. As result, the list would contain a lot of swapped sequence numbers. The consumer of this list (batadv_tp_ack_unordered()) would then fail to correctly ack packets. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/tp_meter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 0fc4ca78e84eb..e8941f753a969 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -1325,7 +1325,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, * one is attached _after_ it. In this way the list is kept in * ascending order */ - list_add_tail(&new->list, &un->list); + list_add(&new->list, &un->list); added = true; break; } From fd46e54c06011622d61e7593fcd8125049eba1f0 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:17 +0200 Subject: [PATCH 0029/2758] batman-adv: tp_meter: initialize dup_acks explicitly commit b2b68b32a715e0328662801576974aa37b942b00 upstream. When an ack with a sequence number equal to the last_acked is received, the dup_acks counter is increased to decide whether fast retransmit should be performed. Only when the sequence numbers are not equal, the dup_acks is set to the initial value (0). But if the initial packet would have the sequence number BATADV_TP_FIRST_SEQ, dup_acks would not be initialized and atomic_inc would operate on an undefined starting value. It is therefore required to have it explicitly initialized during the start of the sender session. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/tp_meter.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index e8941f753a969..0325b951ff8a8 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -1045,6 +1045,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, tp_vars->icmp_uid = icmp_uid; tp_vars->last_sent = BATADV_TP_FIRST_SEQ; + atomic_set(&tp_vars->dup_acks, 0); atomic_set(&tp_vars->last_acked, BATADV_TP_FIRST_SEQ); tp_vars->fast_recovery = false; tp_vars->recover = BATADV_TP_FIRST_SEQ; From fa54b5d133cd673ed06b5c964d6c2f178a8fbb7b Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:18 +0200 Subject: [PATCH 0030/2758] batman-adv: tp_meter: initialize dec_cwnd explicitly commit febfb1b86224489535312296ecfa3d4bf467f339 upstream. When batadv_tp_update_cwnd() is called, dec_cwnd is increased. But dec_cwnd is only initialixed (to 0) when a duplicate Ack was received or when cwnd is below the ss_threshold. Just initialize the cwnd during the initialization to avoid any potential access of uninitialized data. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/tp_meter.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 0325b951ff8a8..2ff7aa5ed19fb 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -1055,6 +1055,8 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, * mesh_interface, hence its MTU */ tp_vars->cwnd = BATADV_TP_PLEN * 3; + tp_vars->dec_cwnd = 0; + /* at the beginning initialise the SS threshold to the biggest possible * window size, hence the AWND size */ From f1be6ca7c183ee5d707d6c3cd2b87dfa5b3c51ee Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:19 +0200 Subject: [PATCH 0031/2758] batman-adv: tp_meter: avoid window underflow commit 765947b81fb54b6ebb0bc1cfe55c0fa399e002b8 upstream. In batadv_tp_avail(), win_left is calculated with 32-bit unsigned arithmetic: win_left = win_limit - tp_vars->last_sent; During Fast Recovery, cwnd is inflated and last_sent advances rapidly. When Fast Recovery ends, cwnd drops abruptly back to ss_threshold. If the newly shrunk win_limit is less than last_sent, the unsigned subtraction will underflow, wrapping to a massive positive value. Instead of returning that the window is full (unavailable), it returns that the sender can continue sending. To handle this situation, it must be checked whether the windows end sequence number (win_limit) has to be compared with the last sent sequence number. If it would be before the last sent sequence number, then more acks are needed before the transmission can be started again. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/tp_meter.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 2ff7aa5ed19fb..d3b8f02ca08b1 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -817,10 +817,15 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, static bool batadv_tp_avail(struct batadv_tp_vars *tp_vars, size_t payload_len) { + u32 last_sent = READ_ONCE(tp_vars->last_sent); u32 win_left, win_limit; win_limit = atomic_read(&tp_vars->last_acked) + tp_vars->cwnd; - win_left = win_limit - tp_vars->last_sent; + + if (batadv_seq_before(last_sent, win_limit)) + win_left = win_limit - last_sent; + else + win_left = 0; return win_left >= payload_len; } From 585616dab0aa9c45bc11b2c8082ca78533bc00e9 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:20 +0200 Subject: [PATCH 0032/2758] batman-adv: tp_meter: avoid divide-by-zero for dec_cwnd commit 33ccd52f3cc9ed46ce395199f89aa3234dc83314 upstream. The cwnd is always MSS <= cwnd <= 0x20000000. But the calculation in batadv_tp_update_cwnd() assumes unsigned 32 bit arithmetics. ((mss * 8) ** 2) / (cwnd * 8) In case cwnd is actually 0x20000000, it will be shifted by 3 bit to the left end up at 0x100000000 or U32_MAX + 1. It will therefore wrap around and be 0 - resulting in: ((mss * 8) ** 2) / 0 This is of course invalid and cannot be calculated. The calculation should must be simplified to avoid this overflow: (mss ** 2) * 8 / cwnd It will keep the precision enhancement from the scaling (by 8) but avoid the overflow in the divisor. In theory, there could still be an overflow in the dividend. It is at the moment fixed to BATADV_TP_PLEN in batadv_tp_recv_ack() - so it is not an imminent problem. But allowing it to use the whole u32 bit range, would mean that it can still use up to 67 bits. To keep this calculation safe for 32 bit arithmetic, mss must never use more than floor((32 - 3) / 2) bits - or in other words: must never be larger than 16383. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/tp_meter.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index d3b8f02ca08b1..e4f76c141af3e 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -154,9 +154,12 @@ static void batadv_tp_update_cwnd(struct batadv_tp_vars *tp_vars, u32 mss) return; } + /* prevent overflow in (mss * mss) << 3 */ + mss = min_t(u32, mss, (1U << 14) - 1); + /* increment CWND at least of 1 (section 3.1 of RFC5681) */ tp_vars->dec_cwnd += max_t(u32, 1U << 3, - ((mss * mss) << 6) / (tp_vars->cwnd << 3)); + ((mss * mss) << 3) / tp_vars->cwnd); if (tp_vars->dec_cwnd < (mss << 3)) { spin_unlock_bh(&tp_vars->cwnd_lock); return; From 47ca1ecb85b9051695fe25bd0dd7418cddd8b223 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:21 +0200 Subject: [PATCH 0033/2758] batman-adv: tp_meter: fix fast recovery precondition commit 2b0d08f08ed3b2174f05c43089ec65f3543a025b upstream. The fast recovery precondition checks if the recover (initialized to BATADV_TP_FIRST_SEQ) is bigger than the received ack. But since recover is only updated when this check is successful, it will never enter the fast recovery mode. According to RFC6582 Section 3.2 step 2, the check should actually be different: > When the third duplicate ACK is received, the TCP sender first > checks the value of recover to see if the Cumulative > Acknowledgment field covers more than recover The precondition must therefore check if recover is smaller than the received ack - basically swapping the operands of the current check. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/tp_meter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index e4f76c141af3e..77bc69573a562 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -733,7 +733,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, if (atomic_read(&tp_vars->dup_acks) != 3) goto out; - if (recv_ack >= tp_vars->recover) + if (tp_vars->recover >= recv_ack) goto out; /* if this is the third duplicate ACK do Fast Retransmit */ From 1d8b344e8dfc8ca694f8978207c6a1038782d67b Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:22 +0200 Subject: [PATCH 0034/2758] batman-adv: tp_meter: handle seqno wrap-around for fast recovery detection commit f54c85ed42a1b27a516cf2a4728f5a612b799e07 upstream. The recover variable and the last_sent sequence number are initialized on purpose as a really high value which will wrap-around after the first 2000 bytes. The fast recovery precondition must therefore not use simple integer comparisons but use helpers which are aware of the sequence number wrap-arounds. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/tp_meter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 77bc69573a562..0801dcf93540a 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -733,7 +733,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, if (atomic_read(&tp_vars->dup_acks) != 3) goto out; - if (tp_vars->recover >= recv_ack) + if (!batadv_seq_before(tp_vars->recover, recv_ack)) goto out; /* if this is the third duplicate ACK do Fast Retransmit */ From bafe4928d321d1addff75f36872fe10b677515b5 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:23 +0200 Subject: [PATCH 0035/2758] batman-adv: tp_meter: add only finished tp_vars to lists commit 15ccbf685222274f5add1387af58c2a41a95f81e upstream. When the receiver variables (aka "session") are initialized, then they are added to the list of sessions before the timer is set up. A RCU protected reader could therefore find the entry and run mod_setup before batadv_tp_init_recv() finished the timer initialization. The same is true for batadv_tp_start(), which must first initialize the finish_work and the test_length to avoid a similar problem. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/tp_meter.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 0801dcf93540a..7bfad65c862e9 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -1096,21 +1096,21 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, tp_vars->prerandom_offset = 0; spin_lock_init(&tp_vars->prerandom_lock); - kref_get(&tp_vars->refcount); - hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); - spin_unlock_bh(&bat_priv->tp_list_lock); - tp_vars->test_length = test_length; if (!tp_vars->test_length) tp_vars->test_length = BATADV_TP_DEF_TEST_LENGTH; + /* init work item for finished tp tests */ + INIT_DELAYED_WORK(&tp_vars->finish_work, batadv_tp_sender_finish); + + kref_get(&tp_vars->refcount); + hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); + spin_unlock_bh(&bat_priv->tp_list_lock); + batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Meter: starting throughput meter towards %pM (length=%ums)\n", dst, test_length); - /* init work item for finished tp tests */ - INIT_DELAYED_WORK(&tp_vars->finish_work, batadv_tp_sender_finish); - /* start tp kthread. This way the write() call issued from userspace can * happily return and avoid to block */ @@ -1430,10 +1430,10 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, INIT_LIST_HEAD(&tp_vars->unacked_list); kref_get(&tp_vars->refcount); - hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); + timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); kref_get(&tp_vars->refcount); - timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); + hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); batadv_tp_reset_receiver_timer(tp_vars); From 7f58e114c1f328f5636277d95fa65bf0514ec067 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:24 +0200 Subject: [PATCH 0036/2758] batman-adv: bla: annotate lasttime access with READ/WRITE_ONCE commit 98b0fb191c878a64cbaebfe231d96d57576acf8c upstream. The lasttime field for claim, backbone_gw, and loopdetect tracks the jiffies value of the most recent activity and is used to detect timeouts. These accesses are not consistently protected by a lock, so READ_ONCE/WRITE_ONCE must be used to prevent data races caused by compiler optimizations. Cc: stable@kernel.org Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/bridge_loop_avoidance.c | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index ffe854018bd3a..7f643de42579d 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -512,7 +512,7 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig, return NULL; entry->vid = vid; - entry->lasttime = jiffies; + WRITE_ONCE(entry->lasttime, jiffies); entry->crc = BATADV_BLA_CRC_INIT; entry->bat_priv = bat_priv; spin_lock_init(&entry->crc_lock); @@ -580,7 +580,7 @@ batadv_bla_update_own_backbone_gw(struct batadv_priv *bat_priv, if (unlikely(!backbone_gw)) return; - backbone_gw->lasttime = jiffies; + WRITE_ONCE(backbone_gw->lasttime, jiffies); batadv_backbone_gw_put(backbone_gw); } @@ -714,7 +714,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, ether_addr_copy(claim->addr, mac); spin_lock_init(&claim->backbone_lock); claim->vid = vid; - claim->lasttime = jiffies; + WRITE_ONCE(claim->lasttime, jiffies); kref_get(&backbone_gw->refcount); claim->backbone_gw = backbone_gw; kref_init(&claim->refcount); @@ -736,7 +736,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, return; } } else { - claim->lasttime = jiffies; + WRITE_ONCE(claim->lasttime, jiffies); if (claim->backbone_gw == backbone_gw) /* no need to register a new backbone */ goto claim_free_ref; @@ -769,7 +769,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, spin_lock_bh(&backbone_gw->crc_lock); backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN); spin_unlock_bh(&backbone_gw->crc_lock); - backbone_gw->lasttime = jiffies; + WRITE_ONCE(backbone_gw->lasttime, jiffies); claim_free_ref: batadv_claim_put(claim); @@ -858,7 +858,7 @@ static bool batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr, return true; /* handle as ANNOUNCE frame */ - backbone_gw->lasttime = jiffies; + WRITE_ONCE(backbone_gw->lasttime, jiffies); crc = ntohs(*((__force __be16 *)(&an_addr[4]))); batadv_dbg(BATADV_DBG_BLA, bat_priv, @@ -1253,7 +1253,7 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now) head, hash_entry) { if (now) goto purge_now; - if (!batadv_has_timed_out(backbone_gw->lasttime, + if (!batadv_has_timed_out(READ_ONCE(backbone_gw->lasttime), BATADV_BLA_BACKBONE_TIMEOUT)) continue; @@ -1334,7 +1334,7 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv, primary_if->net_dev->dev_addr)) goto skip; - if (!batadv_has_timed_out(claim->lasttime, + if (!batadv_has_timed_out(READ_ONCE(claim->lasttime), BATADV_BLA_CLAIM_TIMEOUT)) goto skip; @@ -1494,7 +1494,7 @@ static void batadv_bla_periodic_work(struct work_struct *work) eth_random_addr(bat_priv->bla.loopdetect_addr); bat_priv->bla.loopdetect_addr[0] = 0xba; bat_priv->bla.loopdetect_addr[1] = 0xbe; - bat_priv->bla.loopdetect_lasttime = jiffies; + WRITE_ONCE(bat_priv->bla.loopdetect_lasttime, jiffies); atomic_set(&bat_priv->bla.loopdetect_next, BATADV_BLA_LOOPDETECT_PERIODS); @@ -1515,7 +1515,7 @@ static void batadv_bla_periodic_work(struct work_struct *work) primary_if->net_dev->dev_addr)) continue; - backbone_gw->lasttime = jiffies; + WRITE_ONCE(backbone_gw->lasttime, jiffies); batadv_bla_send_announce(bat_priv, backbone_gw); if (send_loopdetect) @@ -1899,7 +1899,7 @@ batadv_bla_loopdetect_check(struct batadv_priv *bat_priv, struct sk_buff *skb, /* If the packet came too late, don't forward it on the mesh * but don't consider that as loop. It might be a coincidence. */ - if (batadv_has_timed_out(bat_priv->bla.loopdetect_lasttime, + if (batadv_has_timed_out(READ_ONCE(bat_priv->bla.loopdetect_lasttime), BATADV_BLA_LOOPDETECT_TIMEOUT)) return true; @@ -2014,7 +2014,7 @@ bool batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb, if (own_claim) { /* ... allow it in any case */ - claim->lasttime = jiffies; + WRITE_ONCE(claim->lasttime, jiffies); goto allow; } @@ -2116,7 +2116,7 @@ bool batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb, /* if yes, the client has roamed and we have * to unclaim it. */ - if (batadv_has_timed_out(claim->lasttime, 100)) { + if (batadv_has_timed_out(READ_ONCE(claim->lasttime), 100)) { /* only unclaim if the last claim entry is * older than 100 ms to make sure we really * have a roaming client here. @@ -2361,7 +2361,7 @@ batadv_bla_backbone_dump_entry(struct sk_buff *msg, u32 portid, backbone_crc = backbone_gw->crc; spin_unlock_bh(&backbone_gw->crc_lock); - msecs = jiffies_to_msecs(jiffies - backbone_gw->lasttime); + msecs = jiffies_to_msecs(jiffies - READ_ONCE(backbone_gw->lasttime)); if (is_own) if (nla_put_flag(msg, BATADV_ATTR_BLA_OWN)) { From b4284cac3095775539d65bcccb076bb9200b7cca Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:25 +0200 Subject: [PATCH 0037/2758] batman-adv: prevent ELP transmission interval underflow commit 5e50d4b8ae3ea622122d3c6a38d7f6fe68dfddca upstream. batadv_v_elp_start_timer() enqeues a delayed work. The time when it starts is randomly chosen between (elp_interval - BATADV_JITTER) and (elp_interval + BATADV_JITTER). The configured elp_interval must therefore be larger or equal to BATADV_JITTER to avoid that it causes an underflow of the unsigned integer. If this would happen, then a "fast" ELP interval would turn into a "day long" delay. At the same time, it must not be larger than the maximum value the variable can store. Cc: stable@kernel.org Fixes: a10800829040 ("batman-adv: Add elp_interval hardif genl configuration") [ Context ] Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/netlink.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c index 78c651f634cd3..1d144d8cc0928 100644 --- a/net/batman-adv/netlink.c +++ b/net/batman-adv/netlink.c @@ -917,9 +917,15 @@ static int batadv_netlink_set_hardif(struct sk_buff *skb, #ifdef CONFIG_BATMAN_ADV_BATMAN_V if (info->attrs[BATADV_ATTR_ELP_INTERVAL]) { + u32 elp_interval; + attr = info->attrs[BATADV_ATTR_ELP_INTERVAL]; + elp_interval = nla_get_u32(attr); + + elp_interval = min_t(u32, elp_interval, INT_MAX); + elp_interval = max_t(u32, elp_interval, BATADV_JITTER); - atomic_set(&hard_iface->bat_v.elp_interval, nla_get_u32(attr)); + atomic_set(&hard_iface->bat_v.elp_interval, elp_interval); } if (info->attrs[BATADV_ATTR_THROUGHPUT_OVERRIDE]) { From 26ac02e6ae5dc2c17217f8019402e7c9a51cbd83 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:26 +0200 Subject: [PATCH 0038/2758] batman-adv: tp_meter: initialize last_recv_time during init commit 811cb00fa8cdc3f0a7f6eefc000a6888367c8c8f upstream. The last_recv_time is the most important indicator for a receiver session to figure out whether a session timed out or not. But this information was only initialized after the session was added to the tp_receiver_list and after the timer was started. In the worst case, the timer (function) could have tried to access this information before the actual initialization was reached. Like rest of the variables of the tp_meter receiver session, this field has to be filled out before any other (parallel running) context has the chance to access it. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") [ Context ] Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/tp_meter.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 7bfad65c862e9..397b945635356 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -1403,8 +1403,10 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig, icmp->session, BATADV_TP_RECEIVER); - if (tp_vars) + if (tp_vars) { + tp_vars->last_recv_time = jiffies; goto out_unlock; + } if (!atomic_add_unless(&bat_priv->tp_num, 1, BATADV_TP_MAX_NUM)) { batadv_dbg(BATADV_DBG_TP_METER, bat_priv, @@ -1432,6 +1434,8 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, kref_get(&tp_vars->refcount); timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); + tp_vars->last_recv_time = jiffies; + kref_get(&tp_vars->refcount); hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); @@ -1480,9 +1484,9 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, icmp->orig); goto out; } - } - tp_vars->last_recv_time = jiffies; + tp_vars->last_recv_time = jiffies; + } /* if the packet is a duplicate, it may be the case that an ACK has been * lost. Resend the ACK From c14d3619a1f73ee32abab780221e5332fcc98873 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:27 +0200 Subject: [PATCH 0039/2758] batman-adv: gw: don't deselect gateway with active hardif commit df97a7107b16375a10a36d7a63e9b4291a8ac680 upstream. The batadv_hardif_cnt() was previously checking if there is an batadv_hard_iface->mesh_iface which is has the same mesh_iface. And since batadv_hardif_disable_interface() was resetting the batadv_hard_iface->mesh_iface after this check, it had to verify whether *1* interface was still part of the mesh_iface before it started the gateway deselection. But after batadv_hardif_cnt() is now checking the lower interfaces of mesh_iface and batadv_hardif_disable_interface() already removed the interface via netdev_upper_dev_unlink() earlier in this function, the check must now make sure that *0* interfaces can be found by batadv_hardif_cnt() before selected gateway must be deselected. Otherwise the deselection would already happen one batadv_hard_iface too early. Because a 0 hardif count from batadv_hardif_cnt() is equal to an empty list, it is possible to replace the counting with a simple list_empty(). Cc: stable@kernel.org Fixes: 7dc284702bcd ("batman-adv: store hard_iface as iflink private data") Reviewed-by: Nora Schiffer Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/hard-interface.c | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index d6732c34aeafc..c7dbd24ea1998 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -786,30 +786,6 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface, return ret; } -/** - * batadv_hardif_cnt() - get number of interfaces enslaved to mesh interface - * @mesh_iface: mesh interface to check - * - * This function is only using RCU for locking - the result can therefore be - * off when another function is modifying the list at the same time. The - * caller can use the rtnl_lock to make sure that the count is accurate. - * - * Return: number of connected/enslaved hard interfaces - */ -static size_t batadv_hardif_cnt(struct net_device *mesh_iface) -{ - struct batadv_hard_iface *hard_iface; - struct list_head *iter; - size_t count = 0; - - rcu_read_lock(); - netdev_for_each_lower_private_rcu(mesh_iface, hard_iface, iter) - count++; - rcu_read_unlock(); - - return count; -} - /** * batadv_hardif_disable_interface() - Remove hard interface from mesh interface * @hard_iface: hard interface to be removed @@ -850,8 +826,8 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface) netdev_upper_dev_unlink(hard_iface->net_dev, hard_iface->mesh_iface); batadv_hardif_recalc_extra_skbroom(hard_iface->mesh_iface); - /* nobody uses this interface anymore */ - if (batadv_hardif_cnt(hard_iface->mesh_iface) <= 1) + /* nobody uses this mesh interface anymore */ + if (list_empty(&hard_iface->mesh_iface->adj_list.lower)) batadv_gw_check_client_stop(bat_priv); hard_iface->mesh_iface = NULL; From 4f121f393811d7c80fd22444a8e55d1564299177 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:28 +0200 Subject: [PATCH 0040/2758] batman-adv: ensure bcast is writable before modifying TTL commit 4cd6d3a4b96a8576f1fed8f9f9f17c2dc2978e0c upstream. Before batman-adv is allowed to write to an skb, it either has to have its own copy of the skb or used skb_cow() to ensure that the data part is not shared. The old implementation used a shared queue and created copies before attempting to write to it. But with the new implementation, the broadcast packet is already modified when it gets received. Potentially writing to shared buffers in this process. Adding a skb_cow() right before this operation avoids this and can at the same time prepare it for the modifications required to rebroadcast the packet. Cc: stable@kernel.org Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/routing.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 12c16f81cc51d..0672dc30bed3b 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -1191,6 +1191,12 @@ int batadv_recv_bcast_packet(struct sk_buff *skb, if (batadv_is_my_mac(bat_priv, bcast_packet->orig)) goto free_skb; + /* create a copy of the skb, if needed, to modify it. */ + if (skb_cow(skb, ETH_HLEN) < 0) + goto free_skb; + + bcast_packet = (struct batadv_bcast_packet *)skb->data; + if (bcast_packet->ttl-- < 2) goto free_skb; From 09927ad14a5dc9954d3f4cda2a79287b1aebbc20 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:29 +0200 Subject: [PATCH 0041/2758] batman-adv: fix (m|b)cast csum after decrementing TTL commit e728bbdf32660c8f32b8f5e8d09427a2c131ad60 upstream. The broadcast and multicast packets can be received at the same time by the local system and forwarded to other nodes. Both are simply decrementing the TTL at the beginning of the receive path - independent of chosen paths (receive/forward). But such a modification of the data conflicts with the hw csum. This is not a problem when the packet is directly forwarded but can cause errors in the local receive path. Such a problem can then trigger a "hw csum failure". The receiver path must therefore ensure that the csum is fixed for each modification of the payload before batadv_interface_rx() is reached. Since all batman-adv packet types with a ttl have it as u8 at offset 2, a helper can be used for all of them. But it is only used at the moment for batadv_bcast_packet and batadv_mcast_packet because they are the only ones which deliver the packet locally but unconditionally modify the TTL. Cc: stable@kernel.org Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed") Fixes: 07afe1ba288c ("batman-adv: mcast: implement multicast packet reception and forwarding") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/routing.c | 58 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 0672dc30bed3b..cdcea90db6123 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -8,6 +8,7 @@ #include "main.h" #include +#include #include #include #include @@ -204,6 +205,59 @@ bool batadv_check_management_packet(struct sk_buff *skb, return true; } +/** + * batadv_skb_decrement_ttl() - decrement ttl in a batman-adv header, csum-safe + * @skb: the received packet with @skb->data pointing to the batman-adv header + * + * Supports the following packet types, all of which carry the TTL at offset 2: + * + * - batadv_ogm_packet + * - batadv_ogm2_packet + * - batadv_icmp_header + * - batadv_icmp_packet + * - batadv_icmp_tp_packet + * - batadv_icmp_packet_rr + * - batadv_unicast_packet + * - batadv_frag_packet + * - batadv_bcast_packet + * - batadv_mcast_packet + * - batadv_coded_packet + * - batadv_unicast_tvlv_packet + * + * Return: true if the packet may be forwarded (ttl decremented), + * false if it must be dropped (ttl would expire) + */ +static bool batadv_skb_decrement_ttl(struct sk_buff *skb) +{ + static const size_t ttl_offset = 2; + u8 *ttl_pos; + + BUILD_BUG_ON(offsetof(struct batadv_ogm_packet, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_ogm2_packet, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_icmp_header, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_icmp_packet, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_icmp_tp_packet, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_icmp_packet_rr, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_unicast_packet, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_frag_packet, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_bcast_packet, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_mcast_packet, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_coded_packet, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_unicast_tvlv_packet, ttl) != ttl_offset); + + ttl_pos = skb->data + ttl_offset; + + /* would expire on this hop -> drop, leave header + csum untouched */ + if (*ttl_pos < 2) + return false; + + skb_postpull_rcsum(skb, ttl_pos, 1); + (*ttl_pos)--; + skb_postpush_rcsum(skb, ttl_pos, 1); + + return true; +} + /** * batadv_recv_my_icmp_packet() - receive an icmp packet locally * @bat_priv: the bat priv with all the mesh interface information @@ -1197,7 +1251,7 @@ int batadv_recv_bcast_packet(struct sk_buff *skb, bcast_packet = (struct batadv_bcast_packet *)skb->data; - if (bcast_packet->ttl-- < 2) + if (!batadv_skb_decrement_ttl(skb)) goto free_skb; orig_node = batadv_orig_hash_find(bat_priv, bcast_packet->orig); @@ -1304,7 +1358,7 @@ int batadv_recv_mcast_packet(struct sk_buff *skb, goto free_skb; mcast_packet = (struct batadv_mcast_packet *)skb->data; - if (mcast_packet->ttl-- < 2) + if (!batadv_skb_decrement_ttl(skb)) goto free_skb; tvlv_buff = (unsigned char *)(skb->data + hdr_size); From cc97b6311190c901389b8612001fba3f2a61b3b1 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:30 +0200 Subject: [PATCH 0042/2758] batman-adv: frag: ensure fragment is writable before modifying TTL commit b7293c6e8c15b2db77809b25cf8389e35331b27a upstream. Before batman-adv is allowed to write to an skb, it either has to have its own copy of the skb or use skb_cow() to ensure that the data part is not shared. But batadv_frag_skb_fwd() modifies the TTL even when it is shared. Adding a skb_cow() right before this operation avoids this and can at the same time prepare it for the modifications required to forward the fragment. Cc: stable@kernel.org Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/fragmentation.c | 15 ++++++++++++++- net/batman-adv/fragmentation.h | 3 ++- net/batman-adv/routing.c | 3 +-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c index e9553db423491..5c2a7629e4e09 100644 --- a/net/batman-adv/fragmentation.c +++ b/net/batman-adv/fragmentation.c @@ -384,6 +384,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb, * @skb: skb to forward * @recv_if: interface that the skb is received on * @orig_node_src: originator that the skb is received from + * @rx_result: set to NET_RX_SUCCESS when the fragment was forwarded and + * NET_RX_DROP when it was dropped; only valid when true is returned * * Look up the next-hop of the fragments payload and check if the merged packet * will exceed the MTU towards the next-hop. If so, the fragment is forwarded @@ -393,7 +395,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb, */ bool batadv_frag_skb_fwd(struct sk_buff *skb, struct batadv_hard_iface *recv_if, - struct batadv_orig_node *orig_node_src) + struct batadv_orig_node *orig_node_src, + int *rx_result) { struct batadv_priv *bat_priv = netdev_priv(recv_if->mesh_iface); struct batadv_neigh_node *neigh_node = NULL; @@ -412,12 +415,22 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb, */ total_size = ntohs(packet->total_size); if (total_size > neigh_node->if_incoming->net_dev->mtu) { + if (skb_cow(skb, ETH_HLEN) < 0) { + kfree_skb(skb); + *rx_result = NET_RX_DROP; + ret = true; + goto out; + } + + packet = (struct batadv_frag_packet *)skb->data; + batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_FWD); batadv_add_counter(bat_priv, BATADV_CNT_FRAG_FWD_BYTES, skb->len + ETH_HLEN); packet->ttl--; batadv_send_unicast_skb(skb, neigh_node); + *rx_result = NET_RX_SUCCESS; ret = true; } diff --git a/net/batman-adv/fragmentation.h b/net/batman-adv/fragmentation.h index dbf0871f87030..51e281027ab63 100644 --- a/net/batman-adv/fragmentation.h +++ b/net/batman-adv/fragmentation.h @@ -19,7 +19,8 @@ void batadv_frag_purge_orig(struct batadv_orig_node *orig, bool (*check_cb)(struct batadv_frag_table_entry *)); bool batadv_frag_skb_fwd(struct sk_buff *skb, struct batadv_hard_iface *recv_if, - struct batadv_orig_node *orig_node_src); + struct batadv_orig_node *orig_node_src, + int *rx_result); bool batadv_frag_skb_buffer(struct sk_buff **skb, struct batadv_orig_node *orig_node); int batadv_frag_send_packet(struct sk_buff *skb, diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index cdcea90db6123..4483f8d9c7583 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -1168,10 +1168,9 @@ int batadv_recv_frag_packet(struct sk_buff *skb, /* Route the fragment if it is not for us and too big to be merged. */ if (!batadv_is_my_mac(bat_priv, frag_packet->dest) && - batadv_frag_skb_fwd(skb, recv_if, orig_node_src)) { + batadv_frag_skb_fwd(skb, recv_if, orig_node_src, &ret)) { /* skb was consumed */ skb = NULL; - ret = NET_RX_SUCCESS; goto put_orig_node; } From 5d8e321654272a91a571e1d943a75483d9a75fa4 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:31 +0200 Subject: [PATCH 0043/2758] batman-adv: frag: avoid underflow of TTL commit 493d9d2528e1a09b090e4b37f0f553def7bd5ce9 upstream. Packets with a TTL are using it to limit the amount of time this packet can be forwarded. But for batadv_frag_packet, the TTL was always only reduced but it was never evaluated. It could even underflow without any effect. Check the TTL in batadv_frag_skb_fwd() before attempting to prepare it for forwarding. This keeps it in sync with the not fragmented unicast packet. Cc: stable@kernel.org Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/fragmentation.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c index 5c2a7629e4e09..9a5927ecc4746 100644 --- a/net/batman-adv/fragmentation.c +++ b/net/batman-adv/fragmentation.c @@ -415,6 +415,13 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb, */ total_size = ntohs(packet->total_size); if (total_size > neigh_node->if_incoming->net_dev->mtu) { + if (packet->ttl < 2) { + kfree_skb(skb); + *rx_result = NET_RX_DROP; + ret = true; + goto out; + } + if (skb_cow(skb, ETH_HLEN) < 0) { kfree_skb(skb); *rx_result = NET_RX_DROP; From 86ab6b6fb5b82163bf6c45780bb72150021d7349 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:32 +0200 Subject: [PATCH 0044/2758] batman-adv: v: prevent OGM aggregation on disabled hardif commit d11c00b95b2a3b3934007fc003dccc6fdcc061ad upstream. When an interface gets disabled, the worker is correctly disabled by batadv_hardif_disable_interface() -> ... -> batadv_v_ogm_iface_disable(). In this process, the skb aggr_list is also freed. But batadv_v_ogm_send_meshif() can still queue new skbs (via batadv_v_ogm_queue_on_if()) to the aggr_list. This will only stop after all cores can no longer find the RCU protected list of hard interfaces. These queued skbs will never be freed or consumed by batadv_v_ogm_aggr_work. The batadv_v_ogm_iface_disable() function must block batadv_v_ogm_queue_on_if() to avoid leak of skbs. Cc: stable@kernel.org Fixes: f89255a02f1d ("batman-adv: BATMAN_V: introduce per hard-iface OGMv2 queues") [ Context ] Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/bat_v.c | 1 + net/batman-adv/bat_v_ogm.c | 12 ++++++++++++ net/batman-adv/types.h | 6 ++++++ 3 files changed, 19 insertions(+) diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c index de94447142642..17d2a1ccdce67 100644 --- a/net/batman-adv/bat_v.c +++ b/net/batman-adv/bat_v.c @@ -817,6 +817,7 @@ void batadv_v_hardif_init(struct batadv_hard_iface *hard_iface) hard_iface->bat_v.aggr_len = 0; skb_queue_head_init(&hard_iface->bat_v.aggr_list); + hard_iface->bat_v.aggr_list_enabled = false; INIT_DELAYED_WORK(&hard_iface->bat_v.aggr_wq, batadv_v_ogm_aggr_work); } diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c index d66ca77b1aaa3..6852bf5da8c55 100644 --- a/net/batman-adv/bat_v_ogm.c +++ b/net/batman-adv/bat_v_ogm.c @@ -252,11 +252,18 @@ static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv, } spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); + if (!hard_iface->bat_v.aggr_list_enabled) { + kfree_skb(skb); + goto unlock; + } + if (!batadv_v_ogm_queue_left(skb, hard_iface)) batadv_v_ogm_aggr_send(bat_priv, hard_iface); hard_iface->bat_v.aggr_len += batadv_v_ogm_len(skb); __skb_queue_tail(&hard_iface->bat_v.aggr_list, skb); + +unlock: spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); } @@ -417,6 +424,10 @@ int batadv_v_ogm_iface_enable(struct batadv_hard_iface *hard_iface) { struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface); + spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); + hard_iface->bat_v.aggr_list_enabled = true; + spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); + batadv_v_ogm_start_queue_timer(hard_iface); batadv_v_ogm_start_timer(bat_priv); @@ -432,6 +443,7 @@ void batadv_v_ogm_iface_disable(struct batadv_hard_iface *hard_iface) cancel_delayed_work_sync(&hard_iface->bat_v.aggr_wq); spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); + hard_iface->bat_v.aggr_list_enabled = false; batadv_v_ogm_aggr_list_free(hard_iface); spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); } diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index a01ee46d97f34..765ba71fe8c69 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -130,6 +130,12 @@ struct batadv_hard_iface_bat_v { /** @aggr_list: queue for to be aggregated OGM packets */ struct sk_buff_head aggr_list; + /** + * @aggr_list_enabled: aggr_list is active and new skbs can be + * enqueued. Protected by aggr_list.lock after initialization + */ + bool aggr_list_enabled:1; + /** @aggr_len: size of the OGM aggregate (excluding ethernet header) */ unsigned int aggr_len; From 1fb8762600a393d1caccd63be5d07e1756982d68 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:33 +0200 Subject: [PATCH 0045/2758] batman-adv: tp_meter: restrict number of unacked list entries commit e7c775110e1858e5a7471a23a9c9658c0af9df89 upstream. When the unacked_list is unbound, an attacker could send messages with small lengths and appropriated seqno + gaps to force the receiver to allocate more and more unacked_list entries. And the end either causing an out-of-memory situation or increase the management overhead for the (large) list that significant portions of CPU cycles are wasted in searching through the list. When limiting the list to a specific number, it is important to still correctly add a new entry to the list. But if the list became larger than the limit, the last entry of the list (with the highest seqno) must be dropped to still allow the earlier seqnos to finish and therefore to continue the process. Otherwise, the process might get stuck with too high seqnos which are not handled by batadv_tp_ack_unordered(). Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") [ Switch to pre-splitted tp_vars structure names ] Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/tp_meter.c | 23 ++++++++++++++++++++++- net/batman-adv/types.h | 3 +++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 397b945635356..4f8af909893ab 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -87,6 +87,11 @@ #define BATADV_TP_PLEN (BATADV_TP_PACKET_LEN - ETH_HLEN - \ sizeof(struct batadv_unicast_packet)) +/** + * BATADV_TP_MAX_UNACKED - maximum number of packets a receiver didn't yet ack + */ +#define BATADV_TP_MAX_UNACKED 100 + static u8 batadv_tp_prerandom[4096] __read_mostly; /** @@ -1195,6 +1200,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t) list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) { list_del(&un->list); kfree(un); + tp_vars->unacked_count--; } spin_unlock_bh(&tp_vars->unacked_lock); @@ -1308,6 +1314,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, /* if the list is empty immediately attach this new object */ if (list_empty(&tp_vars->unacked_list)) { list_add(&new->list, &tp_vars->unacked_list); + tp_vars->unacked_count++; goto out; } @@ -1338,12 +1345,24 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, */ list_add(&new->list, &un->list); added = true; + tp_vars->unacked_count++; break; } /* received packet with smallest seqno out of order; add it to front */ - if (!added) + if (!added) { list_add(&new->list, &tp_vars->unacked_list); + tp_vars->unacked_count++; + } + + /* remove the last (biggest) unacked seqno when list is too large */ + if (tp_vars->unacked_count > BATADV_TP_MAX_UNACKED) { + un = list_last_entry(&tp_vars->unacked_list, + struct batadv_tp_unacked, list); + list_del(&un->list); + kfree(un); + tp_vars->unacked_count--; + } out: spin_unlock_bh(&tp_vars->unacked_lock); @@ -1380,6 +1399,7 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) list_del(&un->list); kfree(un); + tp_vars->unacked_count--; } spin_unlock_bh(&tp_vars->unacked_lock); } @@ -1430,6 +1450,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, spin_lock_init(&tp_vars->unacked_lock); INIT_LIST_HEAD(&tp_vars->unacked_list); + tp_vars->unacked_count = 0; kref_get(&tp_vars->refcount); timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 765ba71fe8c69..4f9f1820e3a92 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1426,6 +1426,9 @@ struct batadv_tp_vars { /** @unacked_lock: protect unacked_list */ spinlock_t unacked_lock; + /** @unacked_count: number of unacked entries */ + size_t unacked_count; + /** @last_recv_time: time (jiffies) a msg was received */ unsigned long last_recv_time; From 7c5f5f680dfccf58fb14218bfdc8a446764670fc Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:34 +0200 Subject: [PATCH 0046/2758] batman-adv: tp_meter: annotate last_recv_time access with READ/WRITE_ONCE commit d67c728f07fca2ee6ffdc6dd4421cf2e8691f4d1 upstream. The last_recv_time field for batadv_tp_receiver tracks the jiffies value of the most recent activity and is used to detect timeouts. These accesses are not consistently protected by a lock, so READ_ONCE/WRITE_ONCE must be used to prevent data races caused by compiler optimizations. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/tp_meter.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 4f8af909893ab..e23b75846ebe1 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -1183,7 +1183,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t) bat_priv = tp_vars->bat_priv; /* if there is recent activity rearm the timer */ - if (!batadv_has_timed_out(tp_vars->last_recv_time, + if (!batadv_has_timed_out(READ_ONCE(tp_vars->last_recv_time), BATADV_TP_RECV_TIMEOUT)) { /* reset the receiver shutdown timer */ batadv_tp_reset_receiver_timer(tp_vars); @@ -1424,7 +1424,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig, icmp->session, BATADV_TP_RECEIVER); if (tp_vars) { - tp_vars->last_recv_time = jiffies; + WRITE_ONCE(tp_vars->last_recv_time, jiffies); goto out_unlock; } @@ -1455,7 +1455,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, kref_get(&tp_vars->refcount); timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); - tp_vars->last_recv_time = jiffies; + WRITE_ONCE(tp_vars->last_recv_time, jiffies); kref_get(&tp_vars->refcount); hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); @@ -1506,7 +1506,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, goto out; } - tp_vars->last_recv_time = jiffies; + WRITE_ONCE(tp_vars->last_recv_time, jiffies); } /* if the packet is a duplicate, it may be the case that an ACK has been From aa9fe4cb1acb145d40625b4902961d67c45cfeb9 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:35 +0200 Subject: [PATCH 0047/2758] batman-adv: tp_meter: prevent parallel modifications of last_recv commit 6dde0cfcb36e4d5b3de35b75696937478441eed4 upstream. When last_recv is updated to store the last receive sequence number, it is assuming that nothing is modifying in parallel while: * check for outdated packets is done * out of order check is performed (and packets are stored in out-of-order queue) * the out-of-order queue was searched for closed gaps * sequence number for next ack is calculated Nothing of that was actually protected. It could therefore happen that the last_recv was updated multiple times in parallel and the final sequence number was calculated with deltas which had no connection to the sequence number they were added to. Lock this whole region with the same lock which was already used to protect the unacked (out-of-order) list. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") [ Switch to pre-splitted tp_vars structure names ] Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/tp_meter.c | 22 +++++++++++++--------- net/batman-adv/types.h | 2 +- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index e23b75846ebe1..6a23050f03d89 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -1294,6 +1294,7 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, */ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, const struct sk_buff *skb) + __must_hold(&tp_vars->unacked_lock) { const struct batadv_icmp_tp_packet *icmp; struct batadv_tp_unacked *un, *new; @@ -1310,12 +1311,11 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, payload_len = skb->len - sizeof(struct batadv_unicast_packet); new->len = payload_len; - spin_lock_bh(&tp_vars->unacked_lock); /* if the list is empty immediately attach this new object */ if (list_empty(&tp_vars->unacked_list)) { list_add(&new->list, &tp_vars->unacked_list); tp_vars->unacked_count++; - goto out; + return true; } /* otherwise loop over the list and either drop the packet because this @@ -1364,9 +1364,6 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, tp_vars->unacked_count--; } -out: - spin_unlock_bh(&tp_vars->unacked_lock); - return true; } @@ -1376,6 +1373,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, * @tp_vars: the private data of the current TP meter session */ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) + __must_hold(&tp_vars->unacked_lock) { struct batadv_tp_unacked *un, *safe; u32 to_ack; @@ -1383,7 +1381,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) /* go through the unacked packet list and possibly ACK them as * well */ - spin_lock_bh(&tp_vars->unacked_lock); list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) { /* the list is ordered, therefore it is possible to stop as soon * there is a gap between the last acked seqno and the seqno of @@ -1401,7 +1398,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) kfree(un); tp_vars->unacked_count--; } - spin_unlock_bh(&tp_vars->unacked_lock); } /** @@ -1481,6 +1477,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, const struct batadv_icmp_tp_packet *icmp; struct batadv_tp_vars *tp_vars; size_t packet_size; + u32 to_ack; u32 seqno; icmp = (struct batadv_icmp_tp_packet *)skb->data; @@ -1509,6 +1506,8 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, WRITE_ONCE(tp_vars->last_recv_time, jiffies); } + spin_lock_bh(&tp_vars->unacked_lock); + /* if the packet is a duplicate, it may be the case that an ACK has been * lost. Resend the ACK */ @@ -1520,8 +1519,10 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, /* exit immediately (and do not send any ACK) if the packet has * not been enqueued correctly */ - if (!batadv_tp_handle_out_of_order(tp_vars, skb)) + if (!batadv_tp_handle_out_of_order(tp_vars, skb)) { + spin_unlock_bh(&tp_vars->unacked_lock); goto out; + } /* send a duplicate ACK */ goto send_ack; @@ -1535,11 +1536,14 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, batadv_tp_ack_unordered(tp_vars); send_ack: + to_ack = tp_vars->last_recv; + spin_unlock_bh(&tp_vars->unacked_lock); + /* send the ACK. If the received packet was out of order, the ACK that * is going to be sent is a duplicate (the sender will count them and * possibly enter Fast Retransmit as soon as it has reached 3) */ - batadv_tp_send_ack(bat_priv, icmp->orig, tp_vars->last_recv, + batadv_tp_send_ack(bat_priv, icmp->orig, to_ack, icmp->timestamp, icmp->session, icmp->uid); out: batadv_tp_vars_put(tp_vars); diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 4f9f1820e3a92..3f940a201cdf8 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1423,7 +1423,7 @@ struct batadv_tp_vars { /** @unacked_list: list of unacked packets (meta-info only) */ struct list_head unacked_list; - /** @unacked_lock: protect unacked_list */ + /** @unacked_lock: protect unacked_list + &batadv_tp_receiver.last_recv */ spinlock_t unacked_lock; /** @unacked_count: number of unacked entries */ From 39aadfa351607c02bd4834b05f3ad14dcd5edbf6 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:36 +0200 Subject: [PATCH 0048/2758] batman-adv: tp_meter: handle overlapping packets commit cbde75c38b21f022891525078622587ad557b7c1 upstream. If the size of the packets would change during the transmission, it could happen that some retries of packets are overlapping. In this case, precise comparisons of sequence numbers by the receiver would be wrong. It is then necessary to check if the start sequence number to the end sequence number ("seqno + length") would contain a new range. If this is the case then this is enough to accept this packet. In all other cases, the packet still has to be dropped (and not acked). Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") [ Switch to pre-splitted tp_vars structure names ] Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/tp_meter.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 6a23050f03d89..dbaae33db0f1f 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -1284,7 +1284,8 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, /** * batadv_tp_handle_out_of_order() - store an out of order packet * @tp_vars: the private data of the current TP meter session - * @skb: the buffer containing the received packet + * @seqno: sequence number of new received packet + * @payload_len: length of the received packet * * Store the out of order packet in the unacked list for late processing. This * packets are kept in this list so that they can be ACKed at once as soon as @@ -1293,22 +1294,17 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, * Return: true if the packed has been successfully processed, false otherwise */ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, - const struct sk_buff *skb) + u32 seqno, u32 payload_len) __must_hold(&tp_vars->unacked_lock) { - const struct batadv_icmp_tp_packet *icmp; struct batadv_tp_unacked *un, *new; - u32 payload_len; bool added = false; new = kmalloc_obj(*new, GFP_ATOMIC); if (unlikely(!new)) return false; - icmp = (struct batadv_icmp_tp_packet *)skb->data; - - new->seqno = ntohl(icmp->seqno); - payload_len = skb->len - sizeof(struct batadv_unicast_packet); + new->seqno = seqno; new->len = payload_len; /* if the list is empty immediately attach this new object */ @@ -1476,7 +1472,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, { const struct batadv_icmp_tp_packet *icmp; struct batadv_tp_vars *tp_vars; - size_t packet_size; + u32 payload_len; u32 to_ack; u32 seqno; @@ -1511,15 +1507,17 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, /* if the packet is a duplicate, it may be the case that an ACK has been * lost. Resend the ACK */ - if (batadv_seq_before(seqno, tp_vars->last_recv)) + payload_len = skb->len - sizeof(struct batadv_unicast_packet); + to_ack = seqno + payload_len; + if (batadv_seq_before(to_ack, tp_vars->last_recv)) goto send_ack; /* if the packet is out of order enqueue it */ - if (ntohl(icmp->seqno) != tp_vars->last_recv) { + if (batadv_seq_before(tp_vars->last_recv, seqno)) { /* exit immediately (and do not send any ACK) if the packet has * not been enqueued correctly */ - if (!batadv_tp_handle_out_of_order(tp_vars, skb)) { + if (!batadv_tp_handle_out_of_order(tp_vars, seqno, payload_len)) { spin_unlock_bh(&tp_vars->unacked_lock); goto out; } @@ -1529,8 +1527,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, } /* if everything was fine count the ACKed bytes */ - packet_size = skb->len - sizeof(struct batadv_unicast_packet); - tp_vars->last_recv += packet_size; + tp_vars->last_recv = to_ack; /* check if this ordered message filled a gap.... */ batadv_tp_ack_unordered(tp_vars); From 6ae315914113c1c6d3a53b2e0f4958e60c962301 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:37 +0200 Subject: [PATCH 0049/2758] batman-adv: tt: don't merge change entries with different VIDs commit f08e06c2d5c3e2434e7c773f2213f4a7dce6bc1e upstream. batadv_tt_local_event() merges/cancels events for the same client which would conflict or be duplicates. The matching of the queued events only compares the MAC address - the VLAN ID stored in each event is ignored. If a MAC would now appear on multiple VID, the two ADD change events (for VID 1 and VID 2) would be merged to a single vid event. The remote can therefore not calculate the correct TT table and desync. A full translation table exchange is required to recover from this state. A check of VID is therefore necessary to avoid such wrong merges/cancels. Cc: stable@kernel.org Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/translation-table.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 9f6e67771ffa8..acd8af4446671 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -446,6 +446,9 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv, if (!batadv_compare_eth(entry->change.addr, common->addr)) continue; + if (entry->change.vid != tt_change_node->change.vid) + continue; + del_op_entry = entry->change.flags & BATADV_TT_CLIENT_DEL; if (del_op_requested != del_op_entry) { /* DEL+ADD in the same orig interval have no effect and From f91d579a085ba99a242fca9c4595ed08a35fcd43 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:38 +0200 Subject: [PATCH 0050/2758] batman-adv: tt: track roam count per VID commit 12407d5f61c2653a64f2ff4b22f3c267f8420ef1 upstream. batadv_tt_check_roam_count() is supposed to track roaming of a TT entry. But TT entries are for a MAC + VID. The VID was completely missed and thus leads to incorrect detection of ROAM counts when a client MAC exists in multiple VLANs. Cc: stable@kernel.org Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/translation-table.c | 9 +++++++-- net/batman-adv/types.h | 3 +++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index acd8af4446671..83dfd804a143a 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -3442,6 +3442,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv) * batadv_tt_check_roam_count() - check if a client has roamed too frequently * @bat_priv: the bat priv with all the mesh interface information * @client: mac address of the roaming client + * @vid: VLAN identifier * * This function checks whether the client already reached the * maximum number of possible roaming phases. In this case the ROAMING_ADV @@ -3449,7 +3450,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv) * * Return: true if the ROAMING_ADV can be sent, false otherwise */ -static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) +static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client, u16 vid) { struct batadv_tt_roam_node *tt_roam_node; bool ret = false; @@ -3462,6 +3463,9 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) if (!batadv_compare_eth(tt_roam_node->addr, client)) continue; + if (tt_roam_node->vid != vid) + continue; + if (batadv_has_timed_out(tt_roam_node->first_time, BATADV_ROAMING_MAX_TIME)) continue; @@ -3483,6 +3487,7 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) atomic_set(&tt_roam_node->counter, BATADV_ROAMING_MAX_COUNT - 1); ether_addr_copy(tt_roam_node->addr, client); + tt_roam_node->vid = vid; list_add(&tt_roam_node->list, &bat_priv->tt.roam_list); ret = true; @@ -3519,7 +3524,7 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, u8 *client, /* before going on we have to check whether the client has * already roamed to us too many times */ - if (!batadv_tt_check_roam_count(bat_priv, client)) + if (!batadv_tt_check_roam_count(bat_priv, client, vid)) goto out; batadv_dbg(BATADV_DBG_TT, bat_priv, diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 3f940a201cdf8..4dce996f01cce 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1912,6 +1912,9 @@ struct batadv_tt_roam_node { /** @addr: mac address of the client in the roaming phase */ u8 addr[ETH_ALEN]; + /** @vid: VLAN identifier */ + u16 vid; + /** * @counter: number of allowed roaming events per client within a single * OGM interval (changes are committed with each OGM) From 3e4555177235758a855f5509946a215e4a52a924 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:39 +0200 Subject: [PATCH 0051/2758] batman-adv: dat: prevent false sharing between VLANs commit 20d7658b74169f86d4ac01b9185b3eadddf71f28 upstream. The local hash of DAT entries is supposed to be VLAN (VID) aware. But the adding to the hash and the search in the hash were not checking the VID information of the hash entries. The entries would therefore only be correctly separated when batadv_hash_dat() didn't select the same buckets for different VIDs. Cc: stable@kernel.org Fixes: be1db4f6615b ("batman-adv: make the Distributed ARP Table vlan aware") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/distributed-arp-table.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index 0a8bd95e2f99e..86fb5de5022ac 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -214,10 +214,13 @@ static void batadv_dat_purge(struct work_struct *work) */ static bool batadv_compare_dat(const struct hlist_node *node, const void *data2) { - const void *data1 = container_of(node, struct batadv_dat_entry, - hash_entry); + const struct batadv_dat_entry *entry1; + const struct batadv_dat_entry *entry2; - return memcmp(data1, data2, sizeof(__be32)) == 0; + entry1 = container_of(node, struct batadv_dat_entry, hash_entry); + entry2 = data2; + + return entry1->ip == entry2->ip && entry1->vid == entry2->vid; } /** @@ -344,6 +347,9 @@ batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip, if (dat_entry->ip != ip) continue; + if (dat_entry->vid != vid) + continue; + if (!kref_get_unless_zero(&dat_entry->refcount)) continue; From 56910cfd31160b78f0a85724b138917325dbb2bf Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:40 +0200 Subject: [PATCH 0052/2758] batman-adv: tvlv: enforce 2-byte alignment commit 32a6799255525d6ea4da0f7e9e0e521ad9560a46 upstream. The fields of an aggregated OGM(v2) are accessed assuming (at least) 2-byte alignment, so a following OGM must start at an even offset. As the header length is even, an odd tvlv_len would misalign it and trigger unaligned accesses on strict-alignment architectures. Such a misaligned TVLV/OGM/OGMv2 is not created by a normal participant in the mesh. Therefore, reject such malformed packets. Cc: stable@kernel.org Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/bat_iv_ogm.c | 11 ++++++++++- net/batman-adv/bat_v_ogm.c | 11 ++++++++++- net/batman-adv/routing.c | 6 ++++++ net/batman-adv/tvlv.c | 6 ++++++ 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index b8b1b997960a9..6e79f69c2fede 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -311,14 +311,23 @@ batadv_iv_ogm_aggr_packet(int buff_pos, int packet_len, const struct batadv_ogm_packet *ogm_packet) { int next_buff_pos = 0; + u16 tvlv_len; /* check if there is enough space for the header */ next_buff_pos += buff_pos + sizeof(*ogm_packet); if (next_buff_pos > packet_len) return false; + tvlv_len = ntohs(ogm_packet->tvlv_len); + + /* the fields of an aggregated OGM are accessed assuming (at least) + * 2-byte alignment, so a following OGM must start at an even offset. + */ + if (tvlv_len & 1) + return false; + /* check if there is enough space for the optional TVLV */ - next_buff_pos += ntohs(ogm_packet->tvlv_len); + next_buff_pos += tvlv_len; return next_buff_pos <= packet_len; } diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c index 6852bf5da8c55..1f9b2d2b4831c 100644 --- a/net/batman-adv/bat_v_ogm.c +++ b/net/batman-adv/bat_v_ogm.c @@ -849,14 +849,23 @@ batadv_v_ogm_aggr_packet(int buff_pos, int packet_len, const struct batadv_ogm2_packet *ogm2_packet) { int next_buff_pos = 0; + u16 tvlv_len; /* check if there is enough space for the header */ next_buff_pos += buff_pos + sizeof(*ogm2_packet); if (next_buff_pos > packet_len) return false; + tvlv_len = ntohs(ogm2_packet->tvlv_len); + + /* the fields of an aggregated OGMv2 are accessed assuming (at least) + * 2-byte alignment, so a following OGMv2 must start at an even offset. + */ + if (tvlv_len & 1) + return false; + /* check if there is enough space for the optional TVLV */ - next_buff_pos += ntohs(ogm2_packet->tvlv_len); + next_buff_pos += tvlv_len; return next_buff_pos <= packet_len; } diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 4483f8d9c7583..41951c7a1c50b 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -1366,6 +1366,12 @@ int batadv_recv_mcast_packet(struct sk_buff *skb, if (tvlv_buff_len > skb->len - hdr_size) goto free_skb; + /* the fields of an multicast payload are accessed assuming (at least) + * 2-byte alignment, so a following packet must start at an even offset. + */ + if (tvlv_buff_len & 1) + goto free_skb; + ret = batadv_tvlv_containers_process(bat_priv, BATADV_MCAST, NULL, skb, tvlv_buff, tvlv_buff_len); if (ret >= 0) { diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c index cc6ac580c6208..baadf97f98e5f 100644 --- a/net/batman-adv/tvlv.c +++ b/net/batman-adv/tvlv.c @@ -464,6 +464,12 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, if (tvlv_value_cont_len > tvlv_value_len) break; + /* the next tvlv header is accessed assuming (at least) 2-byte + * alignment, so it must start at an even offset. + */ + if (tvlv_value_cont_len & 1) + break; + tvlv_handler = batadv_tvlv_handler_get(bat_priv, tvlv_hdr->type, tvlv_hdr->version); From d25df4f62eea1be05d534a35d21ad8f90457e2a4 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:41 +0200 Subject: [PATCH 0053/2758] batman-adv: tvlv: avoid race of cifsnotfound handler state commit edb557b2ba38fea2c5eb710cf366c797e187218c upstream. TVLV handlers can have the flag BATADV_TVLV_HANDLER_OGM_CIFNOTFND set to signal that the OGM handler should be called (with NULL for data) when the specific TVLV container was not found in the OGM. This is used by: * DAT * GW * Multicast (OGM + Tracker) The state whether the handler was executed was stored in the struct batadv_tvlv_handler. But the TVLV processing is started without any lock. Multiple parallel contexts processing TVLVs would therefore overwrite each others BATADV_TVLV_HANDLER_OGM_CALLED flag in the shared batadv_tvlv_handler. Drop the shared BATADV_TVLV_HANDLER_OGM_CALLED flag and instead determine, per TVLV buffer, whether a matching container was present by scanning the packet's buffer. Cc: stable@kernel.org Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/tvlv.c | 63 ++++++++++++++++++++++++++++++++++++++---- net/batman-adv/types.h | 7 ----- 2 files changed, 57 insertions(+), 13 deletions(-) diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c index baadf97f98e5f..afb61a46d7a67 100644 --- a/net/batman-adv/tvlv.c +++ b/net/batman-adv/tvlv.c @@ -398,7 +398,6 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv, tvlv_handler->ogm_handler(bat_priv, orig_node, BATADV_NO_FLAGS, tvlv_value, tvlv_value_len); - tvlv_handler->flags |= BATADV_TVLV_HANDLER_OGM_CALLED; break; case BATADV_UNICAST_TVLV: if (!skb) @@ -430,6 +429,48 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv, return NET_RX_SUCCESS; } +/** + * batadv_tvlv_containers_contain() - check if a tvlv buffer holds a container + * @tvlv_value: tvlv content + * @tvlv_value_len: tvlv content length + * @type: tvlv container type to look for + * @version: tvlv container version to look for + * + * Return: true if a container of the given type and version is present in the + * tvlv buffer, false otherwise. + */ +static bool batadv_tvlv_containers_contain(void *tvlv_value, + u16 tvlv_value_len, u8 type, + u8 version) +{ + struct batadv_tvlv_hdr *tvlv_hdr; + u16 tvlv_value_cont_len; + + while (tvlv_value_len >= sizeof(*tvlv_hdr)) { + tvlv_hdr = tvlv_value; + tvlv_value_cont_len = ntohs(tvlv_hdr->len); + tvlv_value = tvlv_hdr + 1; + tvlv_value_len -= sizeof(*tvlv_hdr); + + if (tvlv_value_cont_len > tvlv_value_len) + break; + + /* the next tvlv header is accessed assuming (at least) 2-byte + * alignment, so it must start at an even offset. + */ + if (tvlv_value_cont_len & 1) + break; + + if (tvlv_hdr->type == type && tvlv_hdr->version == version) + return true; + + tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len; + tvlv_value_len -= tvlv_value_cont_len; + } + + return false; +} + /** * batadv_tvlv_containers_process() - parse the given tvlv buffer to call the * appropriate handlers @@ -449,7 +490,9 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, struct sk_buff *skb, void *tvlv_value, u16 tvlv_value_len) { + u16 tvlv_value_start_len = tvlv_value_len; struct batadv_tvlv_handler *tvlv_handler; + void *tvlv_value_start = tvlv_value; struct batadv_tvlv_hdr *tvlv_hdr; u16 tvlv_value_cont_len; u8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND; @@ -493,12 +536,20 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, if (!tvlv_handler->ogm_handler) continue; - if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) && - !(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CALLED)) - tvlv_handler->ogm_handler(bat_priv, orig_node, - cifnotfound, NULL, 0); + if (!(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND)) + continue; - tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_OGM_CALLED; + /* if the corresponding container was present then the handler + * was already called from the loop above + */ + if (batadv_tvlv_containers_contain(tvlv_value_start, + tvlv_value_start_len, + tvlv_handler->type, + tvlv_handler->version)) + continue; + + tvlv_handler->ogm_handler(bat_priv, orig_node, + cifnotfound, NULL, 0); } rcu_read_unlock(); diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 4dce996f01cce..30c870cacf437 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -2245,13 +2245,6 @@ enum batadv_tvlv_handler_flags { * will call this handler even if its type was not found (with no data) */ BATADV_TVLV_HANDLER_OGM_CIFNOTFND = BIT(1), - - /** - * @BATADV_TVLV_HANDLER_OGM_CALLED: interval tvlv handling flag - the - * API marks a handler as being called, so it won't be called if the - * BATADV_TVLV_HANDLER_OGM_CIFNOTFND flag was set - */ - BATADV_TVLV_HANDLER_OGM_CALLED = BIT(2), }; #endif /* _NET_BATMAN_ADV_TYPES_H_ */ From e9eacf19281ea2498b36291b56c9606118c2d74e Mon Sep 17 00:00:00 2001 From: Wongi Lee Date: Tue, 16 Jun 2026 22:46:17 +0900 Subject: [PATCH 0054/2758] ipv6: account for fraggap on the paged allocation path commit 736b380e28d0480c7bc3e022f1950f31fe53a7c5 upstream. In __ip6_append_data(), when the paged-allocation branch is taken (MSG_MORE / NETIF_F_SG / large fraglen), alloclen and pagedlen are computed as alloclen = fragheaderlen + transhdrlen; pagedlen = datalen - transhdrlen; datalen already includes fraggap (datalen = length + fraggap). When fraggap is non-zero, this is not the first skb and transhdrlen is zero. The fraggap bytes carried over from the previous skb are copied just past the fragment headers in the new skb's linear area. The linear area is therefore undersized by fraggap bytes while pagedlen is overstated by the same amount, and the copy writes past skb->end into the trailing skb_shared_info. An unprivileged user can trigger this via a UDPv6 socket using MSG_MORE together with MSG_SPLICE_PAGES. The bad accounting was introduced by commit 773ba4fe9104 ("ipv6: avoid partial copy for zc"). Before commit ce650a166335 ("udp6: Fix __ip6_append_data()'s handling of MSG_SPLICE_PAGES"), the negative copy value caused -EINVAL to be returned. That later commit allowed MSG_SPLICE_PAGES to proceed in this case, making the corruption triggerable. The non-paged branch sets alloclen to fraglen, which already accounts for fraggap because datalen does. Bring the paged branch in line by adding fraggap to alloclen and subtracting it from pagedlen. After this adjustment, copy no longer collapses to -fraggap on the paged path, so remove the stale comment describing that old arithmetic. Since a negative copy is no longer expected for a valid MSG_SPLICE_PAGES case, remove the MSG_SPLICE_PAGES exception from the negative copy check. Fixes: 773ba4fe9104 ("ipv6: avoid partial copy for zc") Signed-off-by: Jungwoo Lee Signed-off-by: Wongi Lee Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/ajFTqRljatR17fFy@DESKTOP-19IMU7U.localdomain Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv6/ip6_output.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index c14adcdd43960..b5d9c57fe439f 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1668,8 +1668,8 @@ static int __ip6_append_data(struct sock *sk, !(rt->dst.dev->features & NETIF_F_SG))) alloclen = fraglen; else { - alloclen = fragheaderlen + transhdrlen; - pagedlen = datalen - transhdrlen; + alloclen = fragheaderlen + transhdrlen + fraggap; + pagedlen = datalen - transhdrlen - fraggap; } alloclen += alloc_extra; @@ -1684,10 +1684,7 @@ static int __ip6_append_data(struct sock *sk, fraglen = datalen + fragheaderlen; copy = datalen - transhdrlen - fraggap - pagedlen; - /* [!] NOTE: copy may be negative if pagedlen>0 - * because then the equation may reduces to -fraggap. - */ - if (copy < 0 && !(flags & MSG_SPLICE_PAGES)) { + if (copy < 0) { err = -EINVAL; goto error; } From c04d9ece23deb9e26c19f9ca215e98b3295aa1bb Mon Sep 17 00:00:00 2001 From: Wongi Lee Date: Tue, 16 Jun 2026 22:38:29 +0900 Subject: [PATCH 0055/2758] ipv4: account for fraggap on the paged allocation path [ Upstream commit eca856950f7cb1a221e02b99d758409f2c5cec42 ] In __ip_append_data(), when the paged-allocation branch is taken, alloclen and pagedlen are computed as alloclen = fragheaderlen + transhdrlen; pagedlen = datalen - transhdrlen; datalen already includes fraggap, but the fraggap bytes carried over from the previous skb are copied into the new skb's linear area at offset transhdrlen by the subsequent skb_copy_and_csum_bits(). The linear area is therefore undersized by fraggap bytes while pagedlen is overstated by the same amount. The non-paged branch sets alloclen to fraglen, which already accounts for fraggap because datalen does. Bring the paged branch in line by adding fraggap to alloclen and subtracting it from pagedlen. After this adjustment, copy no longer collapses to -fraggap on the paged path, so remove the stale comment describing that old arithmetic. Fixes: 8eb77cc73977 ("ipv4: avoid partial copy for zc") Signed-off-by: Jungwoo Lee Signed-off-by: Wongi Lee Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/ajFR1eLAIs42TN3g@DESKTOP-19IMU7U.localdomain Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv4/ip_output.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 5bcd73cbdb41c..ec790bad1679a 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -1117,8 +1117,8 @@ static int __ip_append_data(struct sock *sk, !(rt->dst.dev->features & NETIF_F_SG))) alloclen = fraglen; else { - alloclen = fragheaderlen + transhdrlen; - pagedlen = datalen - transhdrlen; + alloclen = fragheaderlen + transhdrlen + fraggap; + pagedlen = datalen - transhdrlen - fraggap; } alloclen += alloc_extra; @@ -1165,9 +1165,6 @@ static int __ip_append_data(struct sock *sk, } copy = datalen - transhdrlen - fraggap - pagedlen; - /* [!] NOTE: copy will be negative if pagedlen>0 - * because then the equation reduces to -fraggap. - */ if (copy > 0 && INDIRECT_CALL_1(getfrag, ip_generic_getfrag, from, data + transhdrlen, offset, From 293a84fa40b3a1b3471c0545722724bc10973f76 Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Wed, 10 Jun 2026 12:31:01 +0200 Subject: [PATCH 0056/2758] ntfs3: reject direct userspace writes to reserved $LX* xattrs commit 5b08dccecf825cbf905f348bc6ccb497507e28e2 upstream. NTFS3 uses $LXUID, $LXGID, $LXMOD and $LXDEV as internal WSL permission metadata and reloads them into i_uid, i_gid and i_mode from ntfs_get_wsl_perm(). Because the empty-prefix xattr handler also lets file owners call setxattr() on these names directly, an unprivileged writer on a writable ntfs3 mount can plant root ownership and S_ISUID on their own file and gain euid 0 after inode reload. Reject direct userspace writes to the reserved $LX* names. Internal ntfs3 metadata updates are unchanged because ntfs_save_wsl_perm() writes them via ntfs_set_ea() directly. Signed-off-by: Zhen Yan [almaz.alexandrovich@paragon-software.com: added an additional check for non privileged users] Signed-off-by: Konstantin Komarov Signed-off-by: Greg Kroah-Hartman --- fs/ntfs3/xattr.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/fs/ntfs3/xattr.c b/fs/ntfs3/xattr.c index 9eeac0ab2b714..372cf2f2e89b3 100644 --- a/fs/ntfs3/xattr.c +++ b/fs/ntfs3/xattr.c @@ -851,6 +851,12 @@ static int ntfs_getxattr(const struct xattr_handler *handler, struct dentry *de, return err; } +static bool ntfs_is_reserved_lxattr(const char *name) +{ + return !strcmp(name, "$LXUID") || !strcmp(name, "$LXGID") || + !strcmp(name, "$LXMOD") || !strcmp(name, "$LXDEV"); +} + /* * ntfs_setxattr - inode_operations::setxattr */ @@ -955,6 +961,12 @@ static noinline int ntfs_setxattr(const struct xattr_handler *handler, goto out; } + /* Do not allow non privileged users to change $LXUID/$LXGID... */ + if (ntfs_is_reserved_lxattr(name) && !capable(CAP_SYS_ADMIN)) { + err = -EPERM; + goto out; + } + /* Deal with NTFS extended attribute. */ err = ntfs_set_ea(inode, name, strlen(name), value, size, flags, 0, NULL); From 55e014aaec650ede08b693ba59c8d0443f13f11c Mon Sep 17 00:00:00 2001 From: Jiajia Liu Date: Thu, 28 May 2026 11:38:14 +0800 Subject: [PATCH 0057/2758] wifi: mt76: add wcid publish check in mt76_sta_add commit 20b126920a259df4d7dcae19fcfe2c57a74d6b2e upstream. Since mt7925_mac_sta_add publishes wcid, add publish check in mt76_sta_add to avoid reinitializing the wcid->poll_list. Found dev->sta_poll_list corruption when using mt7925 and 7.1-rc4. According to the corruption information, prev->next was changed to itself. wlan0: disconnect from AP 90:fb:5d:94:8b:e3 for new auth to 90:fb:5d:94:8b:e2 wlan0: authenticate with 90:fb:5d:94:8b:e2 (local address=84:9e:56:9c:7e:6b) wlan0: send auth to 90:fb:5d:94:8b:e2 (try 1/3) slab kmalloc-8k start ffff8c80958a6000 pointer offset 4160 size 8192 list_add corruption. prev->next should be next (ffff8c808a7488f8), but was ffff8c80958a7040. (prev=ffff8c80958a7040). mt76_wcid_add_poll+0x95/0xd0 [mt76] mt7925_mac_add_txs.part.0+0xa5/0xe0 [mt7925_common] mt7925_rx_check+0xa7/0xc0 [mt7925_common] mt76_dma_rx_poll+0x50d/0x790 [mt76] mt792x_poll_rx+0x52/0xe0 [mt792x_lib] Signed-off-by: Jiajia Liu Link: https://patch.msgid.link/20260528033814.46418-1-liujiajia@kylinos.cn Signed-off-by: Felix Fietkau Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/mediatek/mt76/mac80211.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index 4ae5e4715a9c5..b78b4cd206e06 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -1576,6 +1576,7 @@ mt76_sta_add(struct mt76_phy *phy, struct ieee80211_vif *vif, { struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv; struct mt76_dev *dev = phy->dev; + struct mt76_wcid *published; int ret; int i; @@ -1595,11 +1596,19 @@ mt76_sta_add(struct mt76_phy *phy, struct ieee80211_vif *vif, mtxq->wcid = wcid->idx; } - ewma_signal_init(&wcid->rssi); - rcu_assign_pointer(dev->wcid[wcid->idx], wcid); + published = rcu_dereference_protected(dev->wcid[wcid->idx], + lockdep_is_held(&dev->mutex)); + if (published != wcid) { + WARN_ON_ONCE(published); + ewma_signal_init(&wcid->rssi); + rcu_assign_pointer(dev->wcid[wcid->idx], wcid); + mt76_wcid_init(wcid, phy->band_idx); + } else { + wcid->phy_idx = phy->band_idx; + } + phy->num_sta++; - mt76_wcid_init(wcid, phy->band_idx); out: mutex_unlock(&dev->mutex); From 86d531337ea1ba02d9f2bc830d07c683d9bfaade Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Tue, 26 May 2026 20:37:26 +0200 Subject: [PATCH 0058/2758] mac802154: llsec: add skb_cow_data() before in-place crypto commit 84a04eb5b210643bd67aab81ff805d32f62aa865 upstream. llsec_do_encrypt_unauth(), llsec_do_encrypt_auth(), llsec_do_decrypt_unauth(), and llsec_do_decrypt_auth() all perform in-place cryptographic transformations on skb data. They build a scatterlist with sg_init_one() pointing into the skb's linear data area and then pass the same scatterlist as both src and dst to the crypto API (e.g. crypto_skcipher_encrypt/decrypt, crypto_aead_encrypt/decrypt). On the RX path, __ieee802154_rx_handle_packet() clones the received skb before handing it to each subscriber via ieee802154_subif_frame(). The cloned skb shares the same underlying data buffer via reference counting. When llsec_do_decrypt() subsequently modifies this shared buffer in place, it corrupts data that other clones -- potentially belonging to other sockets or subsystems -- still reference. On the TX path, similar data sharing can occur when an skb's head has been cloned (skb_cloned() returns true). The fix is to call skb_cow_data() before performing any in-place crypto operation. skb_cow_data() ensures that the skb's data area is not shared: if the skb head is cloned or the data spans multiple fragments, it copies the data into a private buffer that can be safely modified in place. This is the same pattern used by: - ESP (net/ipv4/esp4.c, net/ipv6/esp6.c) - MACsec (drivers/net/macsec.c) - WireGuard (drivers/net/wireguard/receive.c) - TIPC (net/tipc/crypto.c) Without this guard, in-place crypto on shared skb data leads to: - Silent data corruption of other skb clones - Use-after-free when the crypto API scatterwalk writes through a page that has already been freed by another clone's kfree_skb() - Kernel crashes under concurrent 802.15.4 traffic with security enabled (KASAN/KMSAN reports slab-use-after-free) Found by 0sec (https://0sec.ai) using automated source analysis. Fixes: 4c14a2fb5d14 ("mac802154: add llsec decryption method") Fixes: 03556e4d0dbb ("mac802154: add llsec encryption method") Cc: stable@vger.kernel.org Reported-by: Doruk Tan Ozturk Closes: https://lore.kernel.org/linux-wpan/20260525161806.96158-1-doruk@0sec.ai/ Reviewed-by: Alexander Lobakin Signed-off-by: Doruk Tan Ozturk Closes: Link: https://lore.kernel.org/20260526183726.56100-1-doruk@0sec.ai Signed-off-by: Stefan Schmidt Signed-off-by: Greg Kroah-Hartman --- net/mac802154/llsec.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/net/mac802154/llsec.c b/net/mac802154/llsec.c index e8512578398e5..5e7cc11fab3a4 100644 --- a/net/mac802154/llsec.c +++ b/net/mac802154/llsec.c @@ -710,6 +710,7 @@ int mac802154_llsec_encrypt(struct mac802154_llsec *sec, struct sk_buff *skb) { struct ieee802154_hdr hdr; int rc, authlen, hlen; + struct sk_buff *trailer; struct mac802154_llsec_key *key; u32 frame_ctr; @@ -769,6 +770,12 @@ int mac802154_llsec_encrypt(struct mac802154_llsec *sec, struct sk_buff *skb) skb->mac_len = ieee802154_hdr_push(skb, &hdr); skb_reset_mac_header(skb); + rc = skb_cow_data(skb, 0, &trailer); + if (rc < 0) { + llsec_key_put(key); + return rc; + } + rc = llsec_do_encrypt(skb, sec, &hdr, key); llsec_key_put(key); @@ -908,6 +915,13 @@ llsec_do_decrypt(struct sk_buff *skb, const struct mac802154_llsec *sec, const struct ieee802154_hdr *hdr, struct mac802154_llsec_key *key, __le64 dev_addr) { + struct sk_buff *trailer; + int err; + + err = skb_cow_data(skb, 0, &trailer); + if (err < 0) + return err; + if (hdr->sec.level == IEEE802154_SCF_SECLEVEL_ENC) return llsec_do_decrypt_unauth(skb, sec, hdr, key, dev_addr); else From 21ed9540a8e1906dfcbc1bb82ba9b4de4fa4bd6d Mon Sep 17 00:00:00 2001 From: Yiming Qian Date: Wed, 10 Jun 2026 06:21:36 +0000 Subject: [PATCH 0059/2758] net: skmsg: preserve sg.copy across SG transforms commit 406e8a651a7b854c41fecd5117bb282b3a6c2c6b upstream. The sk_msg sg.copy bitmap is part of the scatterlist entry ownership state. A set bit tells sk_msg_compute_data_pointers() not to expose the entry through writable BPF ctx->data. This protects entries backed by pages that are not private to the sk_msg, such as splice-backed file page-cache pages. Several sk_msg transform paths move, copy, split, or compact msg->sg.data[] entries without moving the matching sg.copy bit. This can make an externally backed entry arrive at a new slot with a clear copy bit. A later SK_MSG verdict can then expose sg_virt(sge) as writable ctx->data and BPF stores can modify the original page cache. Keep sg.copy synchronized with sg.data[] whenever entries are transferred, shifted, split, or copied into a new sk_msg. Clear the bit when an entry is replaced by a newly allocated private page or freed. This covers the BPF pull/push/pop helpers, sk_msg_shift_left/right(), sk_msg_xfer(), and tls_split_open_record(), including the partial tail entry created during TLS open-record splitting. Fixes: d3b18ad31f93 ("tls: add bpf support to sk_msg handling") Cc: stable@vger.kernel.org Reported-by: Yiming Qian Reported-by: Keenan Dong Signed-off-by: Yiming Qian Link: https://patch.msgid.link/20260610062137.49075-1-yimingqian591@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- include/linux/skmsg.h | 15 +++++++++++---- net/core/filter.c | 27 +++++++++++++++++++++++++++ net/core/skmsg.c | 2 ++ net/tls/tls_sw.c | 4 ++++ 4 files changed, 44 insertions(+), 4 deletions(-) diff --git a/include/linux/skmsg.h b/include/linux/skmsg.h index 19f4f253b4f90..937823856de59 100644 --- a/include/linux/skmsg.h +++ b/include/linux/skmsg.h @@ -4,6 +4,7 @@ #ifndef _LINUX_SKMSG_H #define _LINUX_SKMSG_H +#include #include #include #include @@ -199,11 +200,14 @@ static inline void sk_msg_xfer(struct sk_msg *dst, struct sk_msg *src, int which, u32 size) { dst->sg.data[which] = src->sg.data[which]; + __assign_bit(which, dst->sg.copy, test_bit(which, src->sg.copy)); dst->sg.data[which].length = size; dst->sg.size += size; src->sg.size -= size; src->sg.data[which].length -= size; src->sg.data[which].offset += size; + if (!src->sg.data[which].length) + __clear_bit(which, src->sg.copy); } static inline void sk_msg_xfer_full(struct sk_msg *dst, struct sk_msg *src) @@ -273,16 +277,19 @@ static inline void sk_msg_page_add(struct sk_msg *msg, struct page *page, static inline void sk_msg_sg_copy(struct sk_msg *msg, u32 i, bool copy_state) { do { - if (copy_state) - __set_bit(i, msg->sg.copy); - else - __clear_bit(i, msg->sg.copy); + __assign_bit(i, msg->sg.copy, copy_state); sk_msg_iter_var_next(i); if (i == msg->sg.end) break; } while (1); } +static inline void sk_msg_sg_copy_assign(struct sk_msg *dst, u32 dst_i, + const struct sk_msg *src, u32 src_i) +{ + __assign_bit(dst_i, dst->sg.copy, test_bit(src_i, src->sg.copy)); +} + static inline void sk_msg_sg_copy_set(struct sk_msg *msg, u32 start) { sk_msg_sg_copy(msg, start, true); diff --git a/net/core/filter.c b/net/core/filter.c index 80439767e0eea..40037413dd4ec 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2733,11 +2733,13 @@ BPF_CALL_4(bpf_msg_pull_data, struct sk_msg *, msg, u32, start, poffset += len; sge->length = 0; put_page(sg_page(sge)); + __clear_bit(i, msg->sg.copy); sk_msg_iter_var_next(i); } while (i != last_sge); sg_set_page(&msg->sg.data[first_sge], page, copy, 0); + __clear_bit(first_sge, msg->sg.copy); /* To repair sg ring we need to shift entries. If we only * had a single entry though we can just replace it and @@ -2763,9 +2765,11 @@ BPF_CALL_4(bpf_msg_pull_data, struct sk_msg *, msg, u32, start, break; msg->sg.data[i] = msg->sg.data[move_from]; + sk_msg_sg_copy_assign(msg, i, msg, move_from); msg->sg.data[move_from].length = 0; msg->sg.data[move_from].page_link = 0; msg->sg.data[move_from].offset = 0; + __clear_bit(move_from, msg->sg.copy); sk_msg_iter_var_next(i); } while (1); @@ -2794,6 +2798,7 @@ BPF_CALL_4(bpf_msg_push_data, struct sk_msg *, msg, u32, start, { struct scatterlist sge, nsge, nnsge, rsge = {0}, *psge; u32 new, i = 0, l = 0, space, copy = 0, offset = 0; + bool sge_copy, nsge_copy, nnsge_copy, rsge_copy = false; u8 *raw, *to, *from; struct page *page; @@ -2866,6 +2871,7 @@ BPF_CALL_4(bpf_msg_push_data, struct sk_msg *, msg, u32, start, sk_msg_iter_var_prev(i); psge = sk_msg_elem(msg, i); rsge = sk_msg_elem_cpy(msg, i); + rsge_copy = test_bit(i, msg->sg.copy); psge->length = start - offset; rsge.length -= psge->length; @@ -2890,24 +2896,32 @@ BPF_CALL_4(bpf_msg_push_data, struct sk_msg *, msg, u32, start, /* Shift one or two slots as needed */ sge = sk_msg_elem_cpy(msg, new); + sge_copy = test_bit(new, msg->sg.copy); sg_unmark_end(&sge); nsge = sk_msg_elem_cpy(msg, i); + nsge_copy = test_bit(i, msg->sg.copy); if (rsge.length) { sk_msg_iter_var_next(i); nnsge = sk_msg_elem_cpy(msg, i); + nnsge_copy = test_bit(i, msg->sg.copy); sk_msg_iter_next(msg, end); } while (i != msg->sg.end) { msg->sg.data[i] = sge; + __assign_bit(i, msg->sg.copy, sge_copy); sge = nsge; + sge_copy = nsge_copy; sk_msg_iter_var_next(i); if (rsge.length) { nsge = nnsge; + nsge_copy = nnsge_copy; nnsge = sk_msg_elem_cpy(msg, i); + nnsge_copy = test_bit(i, msg->sg.copy); } else { nsge = sk_msg_elem_cpy(msg, i); + nsge_copy = test_bit(i, msg->sg.copy); } } @@ -2921,6 +2935,7 @@ BPF_CALL_4(bpf_msg_push_data, struct sk_msg *, msg, u32, start, get_page(sg_page(&rsge)); sk_msg_iter_var_next(new); msg->sg.data[new] = rsge; + __assign_bit(new, msg->sg.copy, rsge_copy); } sk_msg_reset_curr(msg); @@ -2948,25 +2963,33 @@ static void sk_msg_shift_left(struct sk_msg *msg, int i) prev = i; sk_msg_iter_var_next(i); msg->sg.data[prev] = msg->sg.data[i]; + sk_msg_sg_copy_assign(msg, prev, msg, i); } while (i != msg->sg.end); sk_msg_iter_prev(msg, end); + __clear_bit(msg->sg.end, msg->sg.copy); } static void sk_msg_shift_right(struct sk_msg *msg, int i) { struct scatterlist tmp, sge; + bool tmp_copy, sge_copy; sk_msg_iter_next(msg, end); sge = sk_msg_elem_cpy(msg, i); + sge_copy = test_bit(i, msg->sg.copy); sk_msg_iter_var_next(i); tmp = sk_msg_elem_cpy(msg, i); + tmp_copy = test_bit(i, msg->sg.copy); while (i != msg->sg.end) { msg->sg.data[i] = sge; + __assign_bit(i, msg->sg.copy, sge_copy); sk_msg_iter_var_next(i); sge = tmp; + sge_copy = tmp_copy; tmp = sk_msg_elem_cpy(msg, i); + tmp_copy = test_bit(i, msg->sg.copy); } } @@ -3026,6 +3049,8 @@ BPF_CALL_4(bpf_msg_pop_data, struct sk_msg *, msg, u32, start, struct scatterlist *nsge, *sge = sk_msg_elem(msg, i); int a = start - offset; int b = sge->length - pop - a; + u32 sge_i = i; + bool sge_copy = test_bit(i, msg->sg.copy); sk_msg_iter_var_next(i); @@ -3038,6 +3063,7 @@ BPF_CALL_4(bpf_msg_pop_data, struct sk_msg *, msg, u32, start, sg_set_page(nsge, sg_page(sge), b, sge->offset + pop + a); + __assign_bit(i, msg->sg.copy, sge_copy); } else { struct page *page, *orig; u8 *to, *from; @@ -3054,6 +3080,7 @@ BPF_CALL_4(bpf_msg_pop_data, struct sk_msg *, msg, u32, start, memcpy(to, from, a); memcpy(to + a, from + a + pop, b); sg_set_page(sge, page, a + b, 0); + __clear_bit(sge_i, msg->sg.copy); put_page(orig); } pop = 0; diff --git a/net/core/skmsg.c b/net/core/skmsg.c index e1850caf1a71a..30c3b9a2681c4 100644 --- a/net/core/skmsg.c +++ b/net/core/skmsg.c @@ -66,6 +66,7 @@ int sk_msg_alloc(struct sock *sk, struct sk_msg *msg, int len, sge = &msg->sg.data[msg->sg.end]; sg_unmark_end(sge); sg_set_page(sge, pfrag->page, use, orig_offset); + __clear_bit(msg->sg.end, msg->sg.copy); get_page(pfrag->page); sk_msg_iter_next(msg, end); } @@ -186,6 +187,7 @@ static int sk_msg_free_elem(struct sock *sk, struct sk_msg *msg, u32 i, sk_mem_uncharge(sk, len); put_page(sg_page(sge)); } + __clear_bit(i, msg->sg.copy); memset(sge, 0, sizeof(*sge)); return len; } diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index 964ebc268ee46..a47f6a1e2c77d 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -623,6 +623,7 @@ static int tls_split_open_record(struct sock *sk, struct tls_rec *from, struct scatterlist *sge, *osge, *nsge; u32 orig_size = msg_opl->sg.size; struct scatterlist tmp = { }; + u32 tmp_i = 0; struct sk_msg *msg_npl; struct tls_rec *new; int ret; @@ -644,6 +645,7 @@ static int tls_split_open_record(struct sock *sk, struct tls_rec *from, if (sge->length > apply) { u32 len = sge->length - apply; + tmp_i = i; get_page(sg_page(sge)); sg_set_page(&tmp, sg_page(sge), len, sge->offset + apply); @@ -675,6 +677,7 @@ static int tls_split_open_record(struct sock *sk, struct tls_rec *from, nsge = sk_msg_elem(msg_npl, j); if (tmp.length) { memcpy(nsge, &tmp, sizeof(*nsge)); + sk_msg_sg_copy_assign(msg_npl, j, msg_opl, tmp_i); sk_msg_iter_var_next(j); nsge = sk_msg_elem(msg_npl, j); } @@ -682,6 +685,7 @@ static int tls_split_open_record(struct sock *sk, struct tls_rec *from, osge = sk_msg_elem(msg_opl, i); while (osge->length) { memcpy(nsge, osge, sizeof(*nsge)); + sk_msg_sg_copy_assign(msg_npl, j, msg_opl, i); sg_unmark_end(nsge); sk_msg_iter_var_next(i); sk_msg_iter_var_next(j); From 47b5d3d506609b08b2e1f7c14f0b681a1953d572 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Fri, 12 Jun 2026 16:59:35 +0800 Subject: [PATCH 0060/2758] net: ip_gre: require CAP_NET_ADMIN in the device netns for changelink commit 8165f7ff57d9667d2bb477ef6af83ede7fed4ad7 upstream. A tunnel changelink() operates on at most two netns, dev_net(dev) and the tunnel link netns t->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in t->net can rewrite a tunnel that lives in t->net. Add rtnl_dev_link_net_capable() next to rtnl_get_net_ns_capable() in net/core/rtnetlink.c. It requires CAP_NET_ADMIN in the link netns and is skipped when the link netns is dev_net(dev), where the rtnl path already checked it. The other patches in this series use the same helper. Gate ipgre_changelink() and erspan_changelink() with it, at the top of the op before any attribute is parsed, because the parsers update live tunnel fields first. ipgre_netlink_parms() sets t->collect_md before ip_tunnel_changelink() runs. Commit 8b484efd5cb4 ("ip6: vti: Use ip6_tnl.net in vti6_siocdevprivate().") added the same check on the ioctl path. This adds it on RTM_NEWLINK. Reported-by: Xiao Liang Closes: https://lore.kernel.org/netdev/CABAhCOSzP1vaThGV35_VnsRCb=87_CPjPVsTHbq905k8A+BuUg@mail.gmail.com/ Fixes: b57708add314 ("gre: add x-netns support") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260612085941.3158249-2-maoyixie.tju@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- include/net/rtnetlink.h | 2 ++ net/core/rtnetlink.c | 8 ++++++++ net/ipv4/ip_gre.c | 6 ++++++ 3 files changed, 16 insertions(+) diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h index ec65a8cebb992..2bff41aacc987 100644 --- a/include/net/rtnetlink.h +++ b/include/net/rtnetlink.h @@ -256,6 +256,8 @@ int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm, int rtnl_nla_parse_ifinfomsg(struct nlattr **tb, const struct nlattr *nla_peer, struct netlink_ext_ack *exterr); struct net *rtnl_get_net_ns_capable(struct sock *sk, int netnsid); +bool rtnl_dev_link_net_capable(const struct net_device *dev, + const struct net *link_net); #define MODULE_ALIAS_RTNL_LINK(kind) MODULE_ALIAS("rtnl-link-" kind) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 511c25bf6f2a2..3be5f264f5ad9 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2421,6 +2421,14 @@ struct net *rtnl_get_net_ns_capable(struct sock *sk, int netnsid) } EXPORT_SYMBOL_GPL(rtnl_get_net_ns_capable); +bool rtnl_dev_link_net_capable(const struct net_device *dev, + const struct net *link_net) +{ + return net_eq(link_net, dev_net(dev)) || + ns_capable(link_net->user_ns, CAP_NET_ADMIN); +} +EXPORT_SYMBOL_GPL(rtnl_dev_link_net_capable); + static int rtnl_valid_dump_ifinfo_req(const struct nlmsghdr *nlh, bool strict_check, struct nlattr **tb, struct netlink_ext_ack *extack) diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 169e2921a851d..0ebed1438f6c5 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -1457,6 +1457,9 @@ static int ipgre_changelink(struct net_device *dev, struct nlattr *tb[], __u32 fwmark = t->fwmark; int err; + if (!rtnl_dev_link_net_capable(dev, t->net)) + return -EPERM; + err = ipgre_newlink_encap_setup(dev, data); if (err) return err; @@ -1486,6 +1489,9 @@ static int erspan_changelink(struct net_device *dev, struct nlattr *tb[], __u32 fwmark = t->fwmark; int err; + if (!rtnl_dev_link_net_capable(dev, t->net)) + return -EPERM; + err = ipgre_newlink_encap_setup(dev, data); if (err) return err; From cbad530277b510135a668e7b03622d2b55fbae1e Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Thu, 4 Jun 2026 17:12:08 +0200 Subject: [PATCH 0061/2758] PCI/P2PDMA: Add Intel QAT, DSA, IAA devices to whitelist commit 0ba76b19fd4c7256787eab0283c759b18eb76876 upstream. The first device on a PCI root bus determines whether the host bridge is whitelisted for P2PDMA. All Intel Xeon chips since Ice Lake (ICX, 2021) expose a device with ID 0x09a2 as first device. It is loosely associated with the IOMMU. All these Xeon chips support P2PDMA, so since the addition of the device with commit feaea1fe8b36 ("PCI/P2PDMA: Add Intel 3rd Gen Intel Xeon Scalable Processors to whitelist"), P2PDMA has been allowed on all new Xeons without the need to amend the whitelist: Xeons with Performance Cores: Sapphire Rapids (SPR, 2023) Emerald Rapids (EMR, 2023) Granite Rapids (GNR, 2024) Diamond Rapids (DMR, 2026) Xeons with Efficiency Cores: Sierra Forest (SRF, 2024) Clearwater Forest (CWF, 2026) However these Xeons also expose accelerators as first device on a root bus of its own: QuickAssist Technology (QAT, crypto & compression accelerator) Data Streaming Accelerator (DSA, dma engine) In-Memory Analytics Accelerator (IAA, compression accelerator) Whitelist them for P2PDMA as well. Move their Device ID macros from the accelerator drivers to for reuse by P2PDMA code. Unfortunately the Device IDs vary across Xeon generations as additional features were added to the accelerators. This currently necessitates an amendment for each new Xeon chip. For future chips, this need shall be avoided by an ongoing effort to extend ACPI HMAT with PCIe P2PDMA characteristics (latency, bandwidth, ordering constraints). The PCI core will be able look up in this BIOS-provided ACPI table whether P2PDMA is supported, instead of relying on a whitelist that needs to be amended continuously. Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas Acked-by: Vinicius Costa Gomes Acked-by: Giovanni Cabiddu # QAT Cc: stable@vger.kernel.org Link: https://patch.msgid.link/6aac4922b5fe7070b11874427a9285e42ddd05a4.1780585518.git.lukas@wunner.de Signed-off-by: Greg Kroah-Hartman --- .../crypto/intel/qat/qat_common/adf_accel_devices.h | 5 ----- drivers/dma/idxd/registers.h | 3 --- drivers/pci/p2pdma.c | 10 ++++++++++ include/linux/pci_ids.h | 8 ++++++++ 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h b/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h index 03a4e96902080..cbd1d1eda5a34 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h +++ b/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h @@ -28,15 +28,10 @@ #define ADF_4XXX_DEVICE_NAME "4xxx" #define ADF_420XX_DEVICE_NAME "420xx" #define ADF_6XXX_DEVICE_NAME "6xxx" -#define PCI_DEVICE_ID_INTEL_QAT_4XXX 0x4940 #define PCI_DEVICE_ID_INTEL_QAT_4XXXIOV 0x4941 -#define PCI_DEVICE_ID_INTEL_QAT_401XX 0x4942 #define PCI_DEVICE_ID_INTEL_QAT_401XXIOV 0x4943 -#define PCI_DEVICE_ID_INTEL_QAT_402XX 0x4944 #define PCI_DEVICE_ID_INTEL_QAT_402XXIOV 0x4945 -#define PCI_DEVICE_ID_INTEL_QAT_420XX 0x4946 #define PCI_DEVICE_ID_INTEL_QAT_420XXIOV 0x4947 -#define PCI_DEVICE_ID_INTEL_QAT_6XXX 0x4948 #define PCI_DEVICE_ID_INTEL_QAT_6XXX_IOV 0x4949 #define ADF_DEVICE_FUSECTL_OFFSET 0x40 diff --git a/drivers/dma/idxd/registers.h b/drivers/dma/idxd/registers.h index f95411363ea9a..1dce26d4da835 100644 --- a/drivers/dma/idxd/registers.h +++ b/drivers/dma/idxd/registers.h @@ -10,9 +10,6 @@ #endif /* PCI Config */ -#define PCI_DEVICE_ID_INTEL_DSA_GNRD 0x11fb -#define PCI_DEVICE_ID_INTEL_DSA_DMR 0x1212 -#define PCI_DEVICE_ID_INTEL_IAA_DMR 0x1216 #define PCI_DEVICE_ID_INTEL_IAA_PTL 0xb02d #define PCI_DEVICE_ID_INTEL_IAA_WCL 0xfd2d diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index 7c898542af8d5..206e3feb9636e 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -548,6 +548,16 @@ static const struct pci_p2pdma_whitelist_entry { {PCI_VENDOR_ID_INTEL, 0x2033, 0}, {PCI_VENDOR_ID_INTEL, 0x2020, 0}, {PCI_VENDOR_ID_INTEL, 0x09a2, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DSA_SPR0, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IAX_SPR0, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DSA_GNRD, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DSA_DMR, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IAA_DMR, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QAT_4XXX, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QAT_401XX, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QAT_402XX, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QAT_420XX, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QAT_6XXX, 0}, /* Google SoCs. */ {PCI_VENDOR_ID_GOOGLE, PCI_ANY_ID, 0}, {} diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 24cb42f66e4b6..1c9d40e09107d 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2732,6 +2732,9 @@ #define PCI_DEVICE_ID_INTEL_82815_MC 0x1130 #define PCI_DEVICE_ID_INTEL_82815_CGC 0x1132 #define PCI_DEVICE_ID_INTEL_SST_TNG 0x119a +#define PCI_DEVICE_ID_INTEL_DSA_GNRD 0x11fb +#define PCI_DEVICE_ID_INTEL_DSA_DMR 0x1212 +#define PCI_DEVICE_ID_INTEL_IAA_DMR 0x1216 #define PCI_DEVICE_ID_INTEL_82092AA_0 0x1221 #define PCI_DEVICE_ID_INTEL_82437 0x122d #define PCI_DEVICE_ID_INTEL_82371FB_0 0x122e @@ -3052,6 +3055,11 @@ #define PCI_DEVICE_ID_INTEL_5400_FBD1 0x4036 #define PCI_DEVICE_ID_INTEL_HDA_TGL_H 0x43c8 #define PCI_DEVICE_ID_INTEL_HDA_DG1 0x490d +#define PCI_DEVICE_ID_INTEL_QAT_4XXX 0x4940 +#define PCI_DEVICE_ID_INTEL_QAT_401XX 0x4942 +#define PCI_DEVICE_ID_INTEL_QAT_402XX 0x4944 +#define PCI_DEVICE_ID_INTEL_QAT_420XX 0x4946 +#define PCI_DEVICE_ID_INTEL_QAT_6XXX 0x4948 #define PCI_DEVICE_ID_INTEL_HDA_EHL_0 0x4b55 #define PCI_DEVICE_ID_INTEL_HDA_EHL_3 0x4b58 #define PCI_DEVICE_ID_INTEL_HDA_WCL 0x4d28 From 45ebb934ea50b436ce49b2f159f090dab0d7fa28 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Mon, 22 Jun 2026 15:57:38 -0500 Subject: [PATCH 0062/2758] apparmor: mediate the implicit connect of TCP fast open sendmsg commit 4d587cd8a72155089a627130bbd4716ec0856e21 upstream. sendmsg()/sendto() with MSG_FASTOPEN is a combination of connect(2) and write(2): it opens the connection in the SYN. apparmor_socket_sendmsg() only checks AA_MAY_SEND, so a profile that grants send but denies connect lets a confined task open an outbound TCP/MPTCP connection that connect(2) would have refused, bypassing connect mediation. Mediate the implicit connect when MSG_FASTOPEN is set and a destination is supplied. Add it to apparmor_socket_sendmsg() (not the shared aa_sock_msg_perm() helper, which recvmsg also uses) and call aa_sk_perm() directly, mirroring the selinux and tomoyo fixes. sk_is_tcp() does not cover MPTCP fast open, so the SOCK_STREAM/IPPROTO_MPTCP arm is explicit. Fixes: cf60af03ca4e ("net-tcp: Fast Open client - sendmsg(MSG_FASTOPEN)") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Signed-off-by: John Johansen Signed-off-by: Greg Kroah-Hartman --- security/apparmor/lsm.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 3491e9f601943..e01efdf50efa2 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -1422,7 +1422,21 @@ static int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock, static int apparmor_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size) { - return aa_sock_msg_perm(OP_SENDMSG, AA_MAY_SEND, sock, msg, size); + int error = aa_sock_msg_perm(OP_SENDMSG, AA_MAY_SEND, sock, msg, size); + + if (error) + return error; + + /* TCP fast open carries connect() semantics in sendmsg(); mediate + * the implicit connect so it cannot bypass the connect permission. + */ + if ((msg->msg_flags & MSG_FASTOPEN) && msg->msg_name && + (sk_is_tcp(sock->sk) || + (sk_is_inet(sock->sk) && sock->sk->sk_type == SOCK_STREAM && + sock->sk->sk_protocol == IPPROTO_MPTCP))) + error = aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk); + + return error; } static int apparmor_socket_recvmsg(struct socket *sock, From 5e34fa9f6f7cd688ae153fff13139a5cf2d42339 Mon Sep 17 00:00:00 2001 From: Ruslan Valiyev Date: Tue, 26 May 2026 00:04:46 +0200 Subject: [PATCH 0063/2758] apparmor: fix use-after-free in rawdata dedup loop commit 6f060496d03e4dc560a40f73770bd08335cb7a27 upstream. aa_replace_profiles() walks ns->rawdata_list to dedup the incoming policy blob against entries already attached to existing profiles. Per the kernel-doc on struct aa_loaddata, list membership does not hold a reference: profiles hold pcount, and when the last pcount drops, do_ploaddata_rmfs() is queued on a workqueue that takes ns->lock and removes the entry. Between dropping the last pcount and the workqueue running, an entry remains on the list with pcount == 0. aa_get_profile_loaddata() is an unconditional kref_get() on pcount, so when the dedup loop hits such an entry, refcount hardening reports refcount_t: addition on 0; use-after-free. inside aa_replace_profiles(), and the poisoned counter then trips "saturated" and "underflow" warnings on the subsequent uses of the same loaddata. Before commit a0b7091c4de4 ("apparmor: fix race on rawdata dereference") the dedup path used a get_unless_zero-style helper on a single counter, so the existing "if (tmp)" guard was meaningful. The split-refcount refactor introduced aa_get_profile_loaddata(), which has plain kref_get() semantics, and the guard quietly became a no-op. Introduce aa_get_profile_loaddata_not0(), matching the existing _not0 convention used by aa_get_profile_not0(), and use it for the rawdata_list dedup lookup so dying entries are skipped. Reproduced on x86_64 with v7.1-rc5 in QEMU+KVM running Ubuntu 24.04 + stress-ng 0.17.06: stress-ng --apparmor 1 --klog-check --timeout 60s Without this patch the three refcount_t warnings fire within a few seconds. With it the same 60 s run is clean. Coverage is a smoke-test only; a longer soak with CONFIG_KASAN, CONFIG_KCSAN and CONFIG_PROVE_LOCKING would be welcome from anyone with the cycles. Fixes: a0b7091c4de4 ("apparmor: fix race on rawdata dereference") Reported-by: Colin Ian King Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221513 Cc: stable@vger.kernel.org Signed-off-by: Ruslan Valiyev Signed-off-by: John Johansen Signed-off-by: Greg Kroah-Hartman --- security/apparmor/include/policy_unpack.h | 19 +++++++++++++++++++ security/apparmor/policy.c | 8 ++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h index e5a95dc4da1f7..b9de0fdf9ee52 100644 --- a/security/apparmor/include/policy_unpack.h +++ b/security/apparmor/include/policy_unpack.h @@ -163,6 +163,25 @@ aa_get_profile_loaddata(struct aa_loaddata *data) return data; } +/** + * aa_get_profile_loaddata_not0 - get a profile reference count if not zero + * @data: reference to get a count on + * + * Like aa_get_profile_loaddata(), but safe to call on an entry that may + * be on a list (e.g. ns->rawdata_list) where the last pcount has already + * dropped and the deferred cleanup has not yet run. + * + * Returns: pointer to reference, or %NULL if @data is NULL or its + * profile refcount has already reached zero. + */ +static inline struct aa_loaddata * +aa_get_profile_loaddata_not0(struct aa_loaddata *data) +{ + if (data && kref_get_unless_zero(&data->pcount)) + return data; + return NULL; +} + void __aa_loaddata_update(struct aa_loaddata *data, long revision); bool aa_rawdata_eq(struct aa_loaddata *l, struct aa_loaddata *r); void aa_loaddata_kref(struct kref *kref); diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index b6a5eb4021dbd..e103cce6f4af5 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1223,8 +1223,12 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, if (aa_rawdata_eq(rawdata_ent, udata)) { struct aa_loaddata *tmp; - tmp = aa_get_profile_loaddata(rawdata_ent); - /* check we didn't fail the race */ + /* + * Entries remain on rawdata_list with + * pcount == 0 until do_ploaddata_rmfs() + * runs; only take a live profile ref. + */ + tmp = aa_get_profile_loaddata_not0(rawdata_ent); if (tmp) { aa_put_profile_loaddata(udata); udata = tmp; From 9764a786ba98db58f0725913c369e721253aba33 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 4 Mar 2026 11:05:27 +0900 Subject: [PATCH 0064/2758] NTB: epf: Avoid pci_iounmap() with offset when PEER_SPAD and CONFIG share BAR commit d876153680e3d721d385e554def919bce3d18c74 upstream. When BAR_PEER_SPAD and BAR_CONFIG share one PCI BAR, the module teardown path ends up calling pci_iounmap() on the same iomem with some offset, which is unnecessary and triggers a kernel warning like the following: Trying to vunmap() nonexistent vm area (0000000069a5ffe8) WARNING: mm/vmalloc.c:3470 at vunmap+0x58/0x68, CPU#5: modprobe/2937 [...] Call trace: vunmap+0x58/0x68 (P) iounmap+0x34/0x48 pci_iounmap+0x2c/0x40 ntb_epf_pci_remove+0x44/0x80 [ntb_hw_epf] pci_device_remove+0x48/0xf8 device_remove+0x50/0x88 device_release_driver_internal+0x1c8/0x228 driver_detach+0x50/0xb0 bus_remove_driver+0x74/0x100 driver_unregister+0x34/0x68 pci_unregister_driver+0x34/0xa0 ntb_epf_pci_driver_exit+0x14/0xfe0 [ntb_hw_epf] [...] Fix it by unmapping only when PEER_SPAD and CONFIG use difference bars. Cc: stable@vger.kernel.org Fixes: e75d5ae8ab88 ("NTB: epf: Allow more flexibility in the memory BAR map method") Reviewed-by: Frank Li Signed-off-by: Koichiro Den Reviewed-by: Dave Jiang Signed-off-by: Jon Mason Signed-off-by: Greg Kroah-Hartman --- drivers/ntb/hw/epf/ntb_hw_epf.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c index d3ecf25a51625..9935da48a52ea 100644 --- a/drivers/ntb/hw/epf/ntb_hw_epf.c +++ b/drivers/ntb/hw/epf/ntb_hw_epf.c @@ -646,7 +646,8 @@ static void ntb_epf_deinit_pci(struct ntb_epf_dev *ndev) struct pci_dev *pdev = ndev->ntb.pdev; pci_iounmap(pdev, ndev->ctrl_reg); - pci_iounmap(pdev, ndev->peer_spad_reg); + if (ndev->barno_map[BAR_PEER_SPAD] != ndev->barno_map[BAR_CONFIG]) + pci_iounmap(pdev, ndev->peer_spad_reg); pci_iounmap(pdev, ndev->db_reg); pci_release_regions(pdev); From 70f1e000b88cfa8ca3fd7f4d082647fc089a7769 Mon Sep 17 00:00:00 2001 From: Ian Bridges Date: Thu, 25 Jun 2026 23:50:48 -0500 Subject: [PATCH 0065/2758] fbdev: fix use-after-free in store_modes() commit 2c1c805c65fb7dc7524e20376d6987721e73a0b1 upstream. store_modes() replaces a framebuffer's modelist with modes from userspace. On success it frees the old modelist with fb_destroy_modelist(). Two fields still point into that freed list. One pointer is fb_display[i].mode, the mode a console is using. fbcon_new_modelist() moves these pointers to the new list. It only does so for consoles still mapped to the framebuffer. An unmapped console is skipped and keeps its stale pointer. Unbinding fbcon, for example, sets con2fb_map[i] to -1 but leaves fb_display[i].mode set. An FBIOPUT_VSCREENINFO ioctl with FB_ACTIVATE_INV_MODE later reaches fbcon_mode_deleted(). That function reads the stale fb_display[i].mode through fb_mode_is_equal(). The read is a use-after-free. The other pointer is fb_info->mode, the current mode. It is set through the mode sysfs attribute. store_modes() does not update fb_info->mode, so it is left pointing into the freed list. show_mode(), the attribute's read handler, dereferences the stale fb_info->mode through mode_string(). The read is a use-after-free. Clear both pointers before freeing the list. Commit a1f305893074 ("fbcon: Set fb_display[i]->mode to NULL when the mode is released") added the helper fbcon_delete_modelist(). It clears every fb_display[i].mode that points into a given list. So far it is called only from the unregister path. Call it from store_modes() too, and set fb_info->mode to NULL. Reported-by: syzbot+81c7c6b52649fd07299d@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=81c7c6b52649fd07299d Cc: stable@vger.kernel.org Link: https://lore.kernel.org/all/ajjoDhAi2y4ArSlz@dev/ Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Ian Bridges Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/core/fbsysfs.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/video/fbdev/core/fbsysfs.c b/drivers/video/fbdev/core/fbsysfs.c index baa2bae0fb5b3..fe8bd33e64ab1 100644 --- a/drivers/video/fbdev/core/fbsysfs.c +++ b/drivers/video/fbdev/core/fbsysfs.c @@ -11,6 +11,7 @@ #include #include "fb_internal.h" +#include "fbcon.h" static int activate(struct fb_info *fb_info, struct fb_var_screeninfo *var) { @@ -111,8 +112,15 @@ static ssize_t store_modes(struct device *device, if (fb_new_modelist(fb_info)) { fb_destroy_modelist(&fb_info->modelist); list_splice(&old_list, &fb_info->modelist); - } else + } else { + /* + * fb_display[i].mode and fb_info->mode both point into the old + * list. Clear them before it is freed. + */ + fbcon_delete_modelist(&old_list); + fb_info->mode = NULL; fb_destroy_modelist(&old_list); + } unlock_fb_info(fb_info); console_unlock(); From 43e40c7a7b26d89d4bc268346962999d814ed8e3 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 18 Jun 2026 11:06:51 -0700 Subject: [PATCH 0066/2758] fscrypt: Fix key setup in edge case with multiple data unit sizes commit dd015b566d505d698386103e9c80b739c7336eb8 upstream. The addition of support for customizable data unit sizes introduced an edge case where a file's contents can be en/decrypted with the wrong data unit size. It occurs when there are multiple v2 policies that: - Have *different* data unit sizes, via the log2_data_unit_size field - Share the same master_key_identifier, contents_encryption_mode, and either FSCRYPT_POLICY_FLAG_DIRECT_KEY, FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32, or FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 - Are being used on the same filesystem, which also must be mounted with the "inlinecrypt" mount option. Fortunately this edge case doesn't actually occur in practice. I just found it via code review. But it needs to be fixed regardless. The bug is caused by the data unit size not being fully considered when blk_crypto_keys are cached in mk_direct_keys, mk_iv_ino_lblk_32_keys, and mk_iv_ino_lblk_64_keys. They're differentiated only by master key, encryption mode, and flag. However, each one actually has a data unit size too. Only the first data unit size that is cached is used. To fix this, start using the data unit size to differentiate the cached keys. For several reasons, including avoiding increasing the size of struct fscrypt_master_key, just replace all three arrays with a single linked list instead of changing them into two-dimensional arrays. This works well when considering that in practice at most 2 entries are used across all three arrays, so it was already mostly wasted space. For simplicity, make the list also take over the publish/subscribe of the prepared key itself. That is, create separate list nodes for blk_crypto_keys vs crypto_skciphers, and add nodes to the list only when their key is actually prepared. (Note that the legacy fscrypt_direct_keys table in fs/crypto/keysetup_v1.c already works this way.) This eliminates the need for the additional memory barriers when reading and writing the fields of struct fscrypt_prepared_key. Note that I technically should have included the data unit size in the HKDF info string as well. But it's too late to change that. Fixes: 5b1188847180 ("fscrypt: support crypto data unit size less than filesystem block size") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260618180652.52742-1-ebiggers@kernel.org Signed-off-by: Eric Biggers Signed-off-by: Greg Kroah-Hartman --- fs/crypto/fscrypt_private.h | 52 +++++++++------- fs/crypto/inline_crypt.c | 8 +-- fs/crypto/keyring.c | 23 ++++--- fs/crypto/keysetup.c | 118 ++++++++++++++++++++++-------------- 4 files changed, 120 insertions(+), 81 deletions(-) diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 8d3c278a7591e..4263cac24b329 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -236,7 +236,7 @@ struct fscrypt_symlink_data { * @tfm: crypto API transform object * @blk_key: key for blk-crypto * - * Normally only one of the fields will be non-NULL. + * Only one of the fields is non-NULL. */ struct fscrypt_prepared_key { struct crypto_sync_skcipher *tfm; @@ -245,6 +245,15 @@ struct fscrypt_prepared_key { #endif }; +/* An entry in the linked list ->mk_mode_keys */ +struct fscrypt_mode_key { + struct fscrypt_prepared_key key; + struct list_head link; + u8 hkdf_context; + u8 mode_num; + u8 data_unit_bits; +}; + /* * fscrypt_inode_info - the "encryption key" for an inode * @@ -430,20 +439,12 @@ int fscrypt_derive_sw_secret(struct super_block *sb, * @prep_key, depending on which encryption implementation the file will use. */ static inline bool -fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key, +fscrypt_is_key_prepared(const struct fscrypt_prepared_key *prep_key, const struct fscrypt_inode_info *ci) { - /* - * The two smp_load_acquire()'s here pair with the smp_store_release()'s - * in fscrypt_prepare_inline_crypt_key() and fscrypt_prepare_key(). - * I.e., in some cases (namely, if this prep_key is a per-mode - * encryption key) another task can publish blk_key or tfm concurrently, - * executing a RELEASE barrier. We need to use smp_load_acquire() here - * to safely ACQUIRE the memory the other task published. - */ if (fscrypt_using_inline_encryption(ci)) - return smp_load_acquire(&prep_key->blk_key) != NULL; - return smp_load_acquire(&prep_key->tfm) != NULL; + return prep_key->blk_key != NULL; + return prep_key->tfm != NULL; } #else /* CONFIG_FS_ENCRYPTION_INLINE_CRYPT */ @@ -486,10 +487,10 @@ fscrypt_derive_sw_secret(struct super_block *sb, } static inline bool -fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key, +fscrypt_is_key_prepared(const struct fscrypt_prepared_key *prep_key, const struct fscrypt_inode_info *ci) { - return smp_load_acquire(&prep_key->tfm) != NULL; + return prep_key->tfm != NULL; } #endif /* !CONFIG_FS_ENCRYPTION_INLINE_CRYPT */ @@ -577,8 +578,8 @@ struct fscrypt_master_key { /* * Active and structural reference counts. An active ref guarantees * that the struct continues to exist, continues to be in the keyring - * ->s_master_keys, and that any embedded subkeys (e.g. - * ->mk_direct_keys) that have been prepared continue to exist. + * ->s_master_keys, and that any non-file-scoped subkeys (e.g. + * ->mk_mode_keys) that have been prepared continue to exist. * A structural ref only guarantees that the struct continues to exist. * * There is one active ref associated with ->mk_present being true, and @@ -632,12 +633,21 @@ struct fscrypt_master_key { spinlock_t mk_decrypted_inodes_lock; /* - * Per-mode encryption keys for the various types of encryption policies - * that use them. Allocated and derived on-demand. + * A list of 'struct fscrypt_mode_key' for the (hkdf_context, mode_num, + * data_unit_bits, inlinecrypt) combinations that are in use for this + * master key, for hkdf_context in [HKDF_CONTEXT_DIRECT_KEY, + * HKDF_CONTEXT_IV_INO_LBLK_32_KEY, HKDF_CONTEXT_IV_INO_LBLK_64_KEY]. + * + * This is a linked list and not a hash table because in practice + * there's just a single encryption policy per master key, using + * _at most_ 2 nodes in this list. Per-file keys don't use this at all. + * + * This list is append-only until the master key is fully removed, at + * which time the list is cleared. Before then, + * fscrypt_mode_key_setup_mutex synchronizes appends, and searches use + * the RCU read lock together with ->mk_sem held for read. */ - struct fscrypt_prepared_key mk_direct_keys[FSCRYPT_MODE_MAX + 1]; - struct fscrypt_prepared_key mk_iv_ino_lblk_64_keys[FSCRYPT_MODE_MAX + 1]; - struct fscrypt_prepared_key mk_iv_ino_lblk_32_keys[FSCRYPT_MODE_MAX + 1]; + struct list_head mk_mode_keys; /* Hash key for inode numbers. Initialized only when needed. */ siphash_key_t mk_ino_hash_key; diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index 37d42d357925e..47324062fee51 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -198,13 +198,7 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, goto fail; } - /* - * Pairs with the smp_load_acquire() in fscrypt_is_key_prepared(). - * I.e., here we publish ->blk_key with a RELEASE barrier so that - * concurrent tasks can ACQUIRE it. Note that this concurrency is only - * possible for per-mode keys, not for per-file keys. - */ - smp_store_release(&prep_key->blk_key, blk_key); + prep_key->blk_key = blk_key; return 0; fail: diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c index be8e6e8011f21..5fe0d985a58d5 100644 --- a/fs/crypto/keyring.c +++ b/fs/crypto/keyring.c @@ -87,14 +87,14 @@ void fscrypt_put_master_key(struct fscrypt_master_key *mk) void fscrypt_put_master_key_activeref(struct super_block *sb, struct fscrypt_master_key *mk) { - size_t i; + struct fscrypt_mode_key *node, *tmp; if (!refcount_dec_and_test(&mk->mk_active_refs)) return; /* * No active references left, so complete the full removal of this * fscrypt_master_key struct by removing it from the keyring and - * destroying any subkeys embedded in it. + * destroying any non-file-scoped subkeys. */ if (WARN_ON_ONCE(!sb->s_master_keys)) @@ -110,13 +110,16 @@ void fscrypt_put_master_key_activeref(struct super_block *sb, WARN_ON_ONCE(mk->mk_present); WARN_ON_ONCE(!list_empty(&mk->mk_decrypted_inodes)); - for (i = 0; i <= FSCRYPT_MODE_MAX; i++) { - fscrypt_destroy_prepared_key( - sb, &mk->mk_direct_keys[i]); - fscrypt_destroy_prepared_key( - sb, &mk->mk_iv_ino_lblk_64_keys[i]); - fscrypt_destroy_prepared_key( - sb, &mk->mk_iv_ino_lblk_32_keys[i]); + /* + * Destroy any non-file-scoped subkeys. Since ->mk_active_refs == 0, + * they're no longer referenced by any inodes. Nor can key setup run + * and use them again. So they're no longer needed. (This implies no + * concurrent readers, so we don't need list_del_rcu() for example.) + */ + list_for_each_entry_safe(node, tmp, &mk->mk_mode_keys, link) { + fscrypt_destroy_prepared_key(sb, &node->key); + list_del(&node->link); + kfree(node); } memzero_explicit(&mk->mk_ino_hash_key, sizeof(mk->mk_ino_hash_key)); @@ -445,6 +448,8 @@ static int add_new_master_key(struct super_block *sb, INIT_LIST_HEAD(&mk->mk_decrypted_inodes); spin_lock_init(&mk->mk_decrypted_inodes_lock); + INIT_LIST_HEAD(&mk->mk_mode_keys); + if (mk_spec->type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) { err = allocate_master_key_users_keyring(mk); if (err) diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c index ce327bfdada46..f905f9f94bdd5 100644 --- a/fs/crypto/keysetup.c +++ b/fs/crypto/keysetup.c @@ -163,13 +163,7 @@ int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key, tfm = fscrypt_allocate_skcipher(ci->ci_mode, raw_key, ci->ci_inode); if (IS_ERR(tfm)) return PTR_ERR(tfm); - /* - * Pairs with the smp_load_acquire() in fscrypt_is_key_prepared(). - * I.e., here we publish ->tfm with a RELEASE barrier so that - * concurrent tasks can ACQUIRE it. Note that this concurrency is only - * possible for per-mode keys, not for per-file keys. - */ - smp_store_release(&prep_key->tfm, tfm); + prep_key->tfm = tfm; return 0; } @@ -190,9 +184,37 @@ int fscrypt_set_per_file_enc_key(struct fscrypt_inode_info *ci, return fscrypt_prepare_key(&ci->ci_enc_key, raw_key, ci); } +/* + * Find the fscrypt_prepared_key (if any) for a particular (mk, hkdf_context, + * mode_num, data_unit_bits, inlinecrypt) combination. + * + * The caller must hold ->mk_sem for reading and ->mk_present must be true, + * ensuring that ->mk_mode_keys is still append-only. + */ +static struct fscrypt_prepared_key * +fscrypt_find_mode_key(struct fscrypt_master_key *mk, u8 hkdf_context, + u8 mode_num, const struct fscrypt_inode_info *ci) +{ + struct fscrypt_mode_key *node; + + /* + * The RCU read lock here is used only to synchronize with concurrent + * list_add_tail_rcu(). Concurrent deletions are impossible here, so + * returning a pointer to a node without taking any refcount is safe. + */ + guard(rcu)(); + list_for_each_entry_rcu(node, &mk->mk_mode_keys, link) { + if (node->hkdf_context == hkdf_context && + node->mode_num == mode_num && + node->data_unit_bits == ci->ci_data_unit_bits && + fscrypt_is_key_prepared(&node->key, ci)) + return &node->key; + } + return NULL; +} + static int setup_per_mode_enc_key(struct fscrypt_inode_info *ci, struct fscrypt_master_key *mk, - struct fscrypt_prepared_key *keys, u8 hkdf_context, bool include_fs_uuid) { const struct inode *inode = ci->ci_inode; @@ -200,7 +222,8 @@ static int setup_per_mode_enc_key(struct fscrypt_inode_info *ci, struct fscrypt_mode *mode = ci->ci_mode; const u8 mode_num = mode - fscrypt_modes; struct fscrypt_prepared_key *prep_key; - u8 mode_key[FSCRYPT_MAX_RAW_KEY_SIZE]; + struct fscrypt_mode_key *new_node; + u8 raw_mode_key[FSCRYPT_MAX_RAW_KEY_SIZE]; u8 hkdf_info[sizeof(mode_num) + sizeof(sb->s_uuid)]; unsigned int hkdf_infolen = 0; bool use_hw_wrapped_key = false; @@ -223,48 +246,56 @@ static int setup_per_mode_enc_key(struct fscrypt_inode_info *ci, use_hw_wrapped_key = true; } - prep_key = &keys[mode_num]; - if (fscrypt_is_key_prepared(prep_key, ci)) { + prep_key = fscrypt_find_mode_key(mk, hkdf_context, mode_num, ci); + if (prep_key) { ci->ci_enc_key = *prep_key; return 0; } - mutex_lock(&fscrypt_mode_key_setup_mutex); + guard(mutex)(&fscrypt_mode_key_setup_mutex); - if (fscrypt_is_key_prepared(prep_key, ci)) - goto done_unlock; + prep_key = fscrypt_find_mode_key(mk, hkdf_context, mode_num, ci); + if (prep_key) { + ci->ci_enc_key = *prep_key; + return 0; + } + + new_node = kzalloc_obj(*new_node); + if (!new_node) + return -ENOMEM; + new_node->hkdf_context = hkdf_context; + new_node->mode_num = mode_num; + new_node->data_unit_bits = ci->ci_data_unit_bits; + prep_key = &new_node->key; if (use_hw_wrapped_key) { err = fscrypt_prepare_inline_crypt_key(prep_key, mk->mk_secret.bytes, mk->mk_secret.size, true, ci); - if (err) - goto out_unlock; - goto done_unlock; + } else { + static_assert(sizeof(mode_num) == 1); + static_assert(sizeof(sb->s_uuid) == 16); + static_assert(sizeof(hkdf_info) == 17); + hkdf_info[hkdf_infolen++] = mode_num; + if (include_fs_uuid) { + memcpy(&hkdf_info[hkdf_infolen], &sb->s_uuid, + sizeof(sb->s_uuid)); + hkdf_infolen += sizeof(sb->s_uuid); + } + fscrypt_hkdf_expand(&mk->mk_secret.hkdf, hkdf_context, + hkdf_info, hkdf_infolen, raw_mode_key, + mode->keysize); + err = fscrypt_prepare_key(prep_key, raw_mode_key, ci); + memzero_explicit(raw_mode_key, mode->keysize); } - - BUILD_BUG_ON(sizeof(mode_num) != 1); - BUILD_BUG_ON(sizeof(sb->s_uuid) != 16); - BUILD_BUG_ON(sizeof(hkdf_info) != 17); - hkdf_info[hkdf_infolen++] = mode_num; - if (include_fs_uuid) { - memcpy(&hkdf_info[hkdf_infolen], &sb->s_uuid, - sizeof(sb->s_uuid)); - hkdf_infolen += sizeof(sb->s_uuid); + if (err) { + kfree(new_node); + return err; } - fscrypt_hkdf_expand(&mk->mk_secret.hkdf, hkdf_context, hkdf_info, - hkdf_infolen, mode_key, mode->keysize); - err = fscrypt_prepare_key(prep_key, mode_key, ci); - memzero_explicit(mode_key, mode->keysize); - if (err) - goto out_unlock; -done_unlock: + list_add_tail_rcu(&new_node->link, &mk->mk_mode_keys); ci->ci_enc_key = *prep_key; - err = 0; -out_unlock: - mutex_unlock(&fscrypt_mode_key_setup_mutex); - return err; + return 0; } /* @@ -311,8 +342,8 @@ static int fscrypt_setup_iv_ino_lblk_32_key(struct fscrypt_inode_info *ci, { int err; - err = setup_per_mode_enc_key(ci, mk, mk->mk_iv_ino_lblk_32_keys, - HKDF_CONTEXT_IV_INO_LBLK_32_KEY, true); + err = setup_per_mode_enc_key(ci, mk, HKDF_CONTEXT_IV_INO_LBLK_32_KEY, + true); if (err) return err; @@ -364,8 +395,8 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_inode_info *ci, * encryption key. This ensures that the master key is * consistently used only for HKDF, avoiding key reuse issues. */ - err = setup_per_mode_enc_key(ci, mk, mk->mk_direct_keys, - HKDF_CONTEXT_DIRECT_KEY, false); + err = setup_per_mode_enc_key(ci, mk, HKDF_CONTEXT_DIRECT_KEY, + false); } else if (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) { /* @@ -374,9 +405,8 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_inode_info *ci, * the IVs. This format is optimized for use with inline * encryption hardware compliant with the UFS standard. */ - err = setup_per_mode_enc_key(ci, mk, mk->mk_iv_ino_lblk_64_keys, - HKDF_CONTEXT_IV_INO_LBLK_64_KEY, - true); + err = setup_per_mode_enc_key( + ci, mk, HKDF_CONTEXT_IV_INO_LBLK_64_KEY, true); } else if (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) { err = fscrypt_setup_iv_ino_lblk_32_key(ci, mk); From 77bba61a20f1b3d206f4f90e10a7bb3cd90b9619 Mon Sep 17 00:00:00 2001 From: Usama Arif Date: Tue, 16 Jun 2026 07:15:17 -0700 Subject: [PATCH 0067/2758] kernel/fork: clear PF_BLOCK_TS in copy_process() commit fd38b75c4b43295b10d69772a46d1c74dbd6fc81 upstream. PF_BLOCK_TS is only set in blk_time_get_ns() when current->plug is non-NULL, and blk_finish_plug() clears it via __blk_flush_plug() before NULLing the plug pointer. copy_process() breaks the invariant by inheriting PF_BLOCK_TS from the parent while resetting the child's plug to NULL. Clear PF_BLOCK_TS alongside that assignment so callers can rely on "PF_BLOCK_TS set implies current->plug != NULL" and dereference current->plug unguarded. Fixes: 06b23f92af87 ("block: update cached timestamp post schedule/preemption") Cc: stable@vger.kernel.org Signed-off-by: Usama Arif Link: https://patch.msgid.link/20260616141604.328820-2-usama.arif@linux.dev Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- kernel/fork.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/fork.c b/kernel/fork.c index 8ac38beae360b..145bcc4c8e125 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -2314,6 +2314,7 @@ __latent_entropy struct task_struct *copy_process( #ifdef CONFIG_BLOCK p->plug = NULL; + p->flags &= ~PF_BLOCK_TS; #endif futex_init_task(p); From dcb7416212e6bad951b0706d7fe4446a92fd966f Mon Sep 17 00:00:00 2001 From: Usama Arif Date: Tue, 16 Jun 2026 07:15:18 -0700 Subject: [PATCH 0068/2758] block: invalidate cached plug timestamp after task switch commit fad156c2af227f42ca796cbb20ddc354a6dd9932 upstream. blk_time_get_ns() caches ktime_get_ns() in current->plug->cur_ktime and marks the task with PF_BLOCK_TS. That cache is only valid while the task keeps running; if the task is switched out, wall-clock time advances and the cached value must not be reused when the task runs again. The existing invalidation covers explicit plug flushes through __blk_flush_plug(), and the schedule() / rtmutex paths through sched_update_worker(). It does not cover in-kernel preemption paths such as preempt_schedule(), preempt_schedule_notrace(), and preempt_schedule_irq(), which enter __schedule(SM_PREEMPT) directly and return without calling sched_update_worker(). As a result, a task preempted while holding a plug with PF_BLOCK_TS set can reuse a stale plug->cur_ktime after it is scheduled back in. blk-iocost then consumes that stale timestamp through ioc_now(), producing stale vnow values for throttle decisions, and through ioc_rqos_done(), inflating on-queue time and feeding false missed-QoS samples into vrate adjustment. Move the schedule-side invalidation to finish_task_switch(), which runs for the scheduled-in task after every actual context switch regardless of which schedule entry point was used. Keep __blk_flush_plug() as the explicit flush/finish-plug invalidation path, and remove only the PF_BLOCK_TS handling from sched_update_worker(). Fixes: 06b23f92af87 ("block: update cached timestamp post schedule/preemption") Cc: stable@vger.kernel.org Signed-off-by: Usama Arif Link: https://patch.msgid.link/20260616141604.328820-3-usama.arif@linux.dev Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- include/linux/blkdev.h | 16 ++++++---------- kernel/sched/core.c | 12 ++++++++---- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 890128cdea1ce..dda80803740fb 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1214,16 +1214,12 @@ static inline void blk_flush_plug(struct blk_plug *plug, bool async) __blk_flush_plug(plug, async); } -/* - * tsk == current here - */ -static inline void blk_plug_invalidate_ts(struct task_struct *tsk) +static __always_inline void blk_plug_invalidate_ts(void) { - struct blk_plug *plug = tsk->plug; - - if (plug) - plug->cur_ktime = 0; - current->flags &= ~PF_BLOCK_TS; + if (unlikely(current->flags & PF_BLOCK_TS)) { + current->plug->cur_ktime = 0; + current->flags &= ~PF_BLOCK_TS; + } } int blkdev_issue_flush(struct block_device *bdev); @@ -1249,7 +1245,7 @@ static inline void blk_flush_plug(struct blk_plug *plug, bool async) { } -static inline void blk_plug_invalidate_ts(struct task_struct *tsk) +static inline void blk_plug_invalidate_ts(void) { } diff --git a/kernel/sched/core.c b/kernel/sched/core.c index b8871449d3c69..3314ead931273 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -5252,6 +5252,12 @@ static struct rq *finish_task_switch(struct task_struct *prev) */ kmap_local_sched_in(); + /* + * Any cached block-layer timestamp (plug->cur_ktime) is stale now, + * invalidate it. + */ + blk_plug_invalidate_ts(); + fire_sched_in_preempt_notifiers(current); /* * When switching through a kernel thread, the loop in @@ -7251,12 +7257,10 @@ static inline void sched_submit_work(struct task_struct *tsk) static void sched_update_worker(struct task_struct *tsk) { - if (tsk->flags & (PF_WQ_WORKER | PF_IO_WORKER | PF_BLOCK_TS)) { - if (tsk->flags & PF_BLOCK_TS) - blk_plug_invalidate_ts(tsk); + if (tsk->flags & (PF_WQ_WORKER | PF_IO_WORKER)) { if (tsk->flags & PF_WQ_WORKER) wq_worker_running(tsk); - else if (tsk->flags & PF_IO_WORKER) + else io_wq_worker_running(tsk); } } From 8ead1735811967a50dfe8b0ee0a2f19c5ba4380b Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 4 Jun 2026 17:11:56 +0200 Subject: [PATCH 0069/2758] KVM: arm64: Omit tag sync on stage-2 mappings of the zero page commit 2986a625740599fe6e7635b0586fed2a95bcd1f7 upstream. Commit f620d66af316 ("arm64: mte: Do not flag the zero page as PG_mte_tagged") removed the PG_mte_tagged flag from the zero page, but missed a KVM code path that may set this flag on the zero page when it is used in a stage-2 CoW mapping of anonymous memory. So disregard the zero page explicitly in sanitise_mte_tags(). Fixes: f620d66af316 ("arm64: mte: Do not flag the zero page as PG_mte_tagged") Cc: stable@vger.kernel.org # 5.10.x Suggested-by: Catalin Marinas Signed-off-by: Ard Biesheuvel Reviewed-by: Catalin Marinas Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/mmu.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c index 4da9281312eb8..6e95514d5a76e 100644 --- a/arch/arm64/kvm/mmu.c +++ b/arch/arm64/kvm/mmu.c @@ -1479,6 +1479,11 @@ static void sanitise_mte_tags(struct kvm *kvm, kvm_pfn_t pfn, if (!kvm_has_mte(kvm)) return; + if (is_zero_pfn(pfn)) { + WARN_ON_ONCE(nr_pages != 1); + return; + } + if (folio_test_hugetlb(folio)) { /* Hugetlb has MTE flags set on head page only */ if (folio_try_hugetlb_mte_tagging(folio)) { From 450ee7ff510aae3cba1380547c63a6b6d004370d Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 26 May 2026 12:18:41 +0200 Subject: [PATCH 0070/2758] err.h: use __always_inline on all error pointer helpers commit 94bfc7f3b0c7c33331ba4ff6cc64ff309dfcbce8 upstream. While testing randconfig builds on s390, I came across a link failure with CONFIG_DMA_SHARED_BUFFER disabled: ERROR: modpost: "dma_buf_put" [drivers/iommu/iommufd/iommufd.ko] undefined! The problem here is that IS_ERR() is not inlined and dead code elimination fails as a consequence. The err.h helpers all turn into a trivial assignment of a bit mask and should never result in a function call, so force them to always be inline. This should generally result in better object code aside from avoiding the link failure above. Link: https://lore.kernel.org/20260526101851.2495110-1-arnd@kernel.org Signed-off-by: Arnd Bergmann Reviewed-by: Alexander Lobakin Reviewed-by: Nathan Chancellor Tested-by: Tamir Duberstein Cc: Alexander Gordeev Cc: Andriy Shevchenko Cc: Ansuel Smith Cc: Bjorn Andersson Cc: Heiko Carstens Cc: Vasily Gorbik Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- include/linux/err.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/linux/err.h b/include/linux/err.h index 8c37be0620abf..d3e38d5b3a985 100644 --- a/include/linux/err.h +++ b/include/linux/err.h @@ -36,7 +36,7 @@ * * Return: A pointer with @error encoded within its value. */ -static inline void * __must_check ERR_PTR(long error) +static __always_inline void * __must_check ERR_PTR(long error) { return (void *) error; } @@ -60,7 +60,7 @@ static inline void * __must_check ERR_PTR(long error) * @ptr: An error pointer. * Return: The error code within @ptr. */ -static inline long __must_check PTR_ERR(__force const void *ptr) +static __always_inline long __must_check PTR_ERR(__force const void *ptr) { return (long) ptr; } @@ -73,7 +73,7 @@ static inline long __must_check PTR_ERR(__force const void *ptr) * @ptr: The pointer to check. * Return: true if @ptr is an error pointer, false otherwise. */ -static inline bool __must_check IS_ERR(__force const void *ptr) +static __always_inline bool __must_check IS_ERR(__force const void *ptr) { return IS_ERR_VALUE((unsigned long)ptr); } @@ -87,7 +87,7 @@ static inline bool __must_check IS_ERR(__force const void *ptr) * * Like IS_ERR(), but also returns true for a null pointer. */ -static inline bool __must_check IS_ERR_OR_NULL(__force const void *ptr) +static __always_inline bool __must_check IS_ERR_OR_NULL(__force const void *ptr) { return unlikely(!ptr) || IS_ERR_VALUE((unsigned long)ptr); } @@ -99,7 +99,7 @@ static inline bool __must_check IS_ERR_OR_NULL(__force const void *ptr) * Explicitly cast an error-valued pointer to another pointer type in such a * way as to make it clear that's what's going on. */ -static inline void * __must_check ERR_CAST(__force const void *ptr) +static __always_inline void * __must_check ERR_CAST(__force const void *ptr) { /* cast away the const */ return (void *) ptr; @@ -122,7 +122,7 @@ static inline void * __must_check ERR_CAST(__force const void *ptr) * * Return: The error code within @ptr if it is an error pointer; 0 otherwise. */ -static inline int __must_check PTR_ERR_OR_ZERO(__force const void *ptr) +static __always_inline int __must_check PTR_ERR_OR_ZERO(__force const void *ptr) { if (IS_ERR(ptr)) return PTR_ERR(ptr); From 5b959c1dbb4522b9e3ac4e26ad638b8784869841 Mon Sep 17 00:00:00 2001 From: Konstantin Khorenko Date: Mon, 11 May 2026 12:50:52 +0200 Subject: [PATCH 0071/2758] gcov: use atomic counter updates to fix concurrent access crashes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 56cb9b7d96b28a1173a510ab25354b6599ad3a33 upstream. GCC's GCOV instrumentation can merge global branch counters with loop induction variables as an optimization. In inflate_fast(), the inner copy loops get transformed so that the GCOV counter value is loaded multiple times to compute the loop base address, start index, and end bound. Since GCOV counters are global (not per-CPU), concurrent execution on different CPUs causes the counter to change between loads, producing inconsistent values and out-of-bounds memory writes. The crash manifests during IPComp (IP Payload Compression) processing when inflate_fast() runs concurrently on multiple CPUs: BUG: unable to handle page fault for address: ffffd0a3c0902ffa RIP: inflate_fast+1431 Call Trace: zlib_inflate __deflate_decompress crypto_comp_decompress ipcomp_decompress [xfrm_ipcomp] ipcomp_input [xfrm_ipcomp] xfrm_input At the crash point, the compiler generated three loads from the same global GCOV counter (__gcov0.inflate_fast+216) to compute base, start, and end for an indexed loop. Another CPU modified the counter between loads, making the values inconsistent - the write went 3.4 MB past a 65 KB buffer. Add -fprofile-update=prefer-atomic to CFLAGS_GCOV at the global level in the top-level Makefile, guarded by a try-run compile test. The test compiles a minimal program with and without -fprofile-update=prefer-atomic using the full KBUILD_CFLAGS, then compares undefined symbols in the resulting object files. If prefer-atomic introduces new undefined references (such as __atomic_fetch_add_8 on i386 or __aarch64_ldadd8_relax on arm64 with outline-atomics), the flag is not added -- the kernel does not link against libatomic. On architectures where GCC inlines 64-bit atomic counter updates (x86_64, s390, ...) the test passes and the flag is enabled, preventing the compiler from merging counters with loop induction variables and fixing the observed concurrent-access crash. On architectures where the flag would introduce libatomic dependencies, it is silently omitted and behaviour is no worse than before this patch. Move the CFLAGS_GCOV block from its original position (before the arch Makefile include) to after the core KBUILD_CFLAGS assignments but before the scripts/Makefile.gcc-plugins include. This placement ensures the try-run test sees arch-specific flags (-m32, -march=, -mno-outline-atomics) while avoiding GCC plugin flags (-fplugin=) that would break the test on clean builds when plugin shared objects do not yet exist. Link: https://lore.kernel.org/20260511105052.417187-2-khorenko@virtuozzo.com Signed-off-by: Konstantin Khorenko Tested-by: Arnd Bergmann Tested-by: Peter Oberparleiter Reviewed-by: Peter Oberparleiter Cc: Masahiro Yamada Cc: Miguel Ojeda Cc: Mikhail Zaslonko Cc: Nathan Chancellor Cc: Pavel Tikhomirov Cc: Thomas Weißschuh Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- Makefile | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 78662694be586..68d8b4fc2c675 100644 --- a/Makefile +++ b/Makefile @@ -827,12 +827,6 @@ endif # KBUILD_EXTMOD # Defaults to vmlinux, but the arch makefile usually adds further targets all: vmlinux -CFLAGS_GCOV := -fprofile-arcs -ftest-coverage -ifdef CONFIG_CC_IS_GCC -CFLAGS_GCOV += -fno-tree-loop-im -endif -export CFLAGS_GCOV - # The arch Makefiles can override CC_FLAGS_FTRACE. We may also append it later. ifdef CONFIG_FUNCTION_TRACER CC_FLAGS_FTRACE := -pg @@ -1150,6 +1144,27 @@ endif # Ensure compilers do not transform certain loops into calls to wcslen() KBUILD_CFLAGS += -fno-builtin-wcslen +CFLAGS_GCOV := -fprofile-arcs -ftest-coverage +ifdef CONFIG_CC_IS_GCC +CFLAGS_GCOV += -fno-tree-loop-im +# Use atomic counter updates to avoid concurrent-access crashes in GCOV. +# Only enable if -fprofile-update=prefer-atomic does not introduce new +# undefined symbols (e.g. libatomic calls that the kernel cannot link). +CFLAGS_GCOV += $(call try-run,\ + echo 'long long x; void f(void){x++;}' | \ + $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -w -fprofile-arcs \ + -ftest-coverage -x c - -c -o "$$TMP.base" && \ + echo 'long long x; void f(void){x++;}' | \ + $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -w -fprofile-arcs \ + -ftest-coverage -fprofile-update=prefer-atomic \ + -x c - -c -o "$$TMP" && \ + $(NM) "$$TMP.base" | grep ' U ' > "$$TMP.ubase" || true ; \ + $(NM) "$$TMP" | grep ' U ' > "$$TMP.utest" || true ; \ + cmp -s "$$TMP.ubase" "$$TMP.utest",\ + -fprofile-update=prefer-atomic) +endif +export CFLAGS_GCOV + # change __FILE__ to the relative path to the source directory ifdef building_out_of_srctree KBUILD_CPPFLAGS += -fmacro-prefix-map=$(srcroot)/= From 670fc6a311ed321522b7fff92cf0fc376b4f6e78 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Mon, 1 Jun 2026 23:11:54 +0300 Subject: [PATCH 0072/2758] KEYS: fix overflow in keyctl_pkey_params_get_2() commit cb481e59ea6cae3b7796ac1d7a22b6b24c3f3c0b upstream. The length for the internal output buffer is calculated incorrectly, which can result overflow when a too small buffer is provided. Fix the bug by allocating internal output with the size of the maximum length of the cryptographic primitive instead of caller provided size. Link: https://lore.kernel.org/keyrings/20260531024914.3712130-1-jarkko@kernel.org/ Cc: stable@vger.kernel.org # v4.20+ Fixes: 00d60fd3b932 ("KEYS: Provide keyctls to drive the new key type ops for asymmetric keys [ver #2]") Reported-by: Alessandro Groppo Tested-by: Alessandro Groppo Signed-off-by: Jarkko Sakkinen Signed-off-by: Greg Kroah-Hartman --- security/keys/keyctl_pkey.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/security/keys/keyctl_pkey.c b/security/keys/keyctl_pkey.c index 97bc27bbf0797..ba150ee2d4a37 100644 --- a/security/keys/keyctl_pkey.c +++ b/security/keys/keyctl_pkey.c @@ -138,28 +138,35 @@ static int keyctl_pkey_params_get_2(const struct keyctl_pkey_params __user *_par if (uparams.in_len > info.max_dec_size || uparams.out_len > info.max_enc_size) return -EINVAL; + + params->out_len = info.max_enc_size; break; case KEYCTL_PKEY_DECRYPT: if (uparams.in_len > info.max_enc_size || uparams.out_len > info.max_dec_size) return -EINVAL; + + params->out_len = info.max_dec_size; break; case KEYCTL_PKEY_SIGN: if (uparams.in_len > info.max_data_size || uparams.out_len > info.max_sig_size) return -EINVAL; + + params->out_len = info.max_sig_size; break; case KEYCTL_PKEY_VERIFY: if (uparams.in_len > info.max_data_size || uparams.in2_len > info.max_sig_size) return -EINVAL; + + params->out_len = info.max_sig_size; break; default: BUG(); } params->in_len = uparams.in_len; - params->out_len = uparams.out_len; /* Note: same as in2_len */ return 0; } From 83c0a1cb296d955d5f4d1f0bd8a769ba8ed8c29f Mon Sep 17 00:00:00 2001 From: Shaomin Chen Date: Wed, 10 Jun 2026 13:10:05 +0300 Subject: [PATCH 0073/2758] keys: Pin request_key_auth payload in instantiate paths commit fd15b457a86939c38aa12116adabd8ff686c5e51 upstream. A: request_key() B: KEYCTL_INSTANTIATE_IOV ================ ========================= create auth key store rka in auth key wait for helper get auth key load rka from auth key copy user payload sleep on #PF helper completed detach and free rka destroy auth key wake up use rka->target_key **USE-AFTER-FREE** Give request_key_auth payloads a refcount. Take a payload reference while authkey->sem stabilizes the payload and revocation state. Hold that reference across the instantiate and reject paths. Drop the auth key owning reference from revoke and destroy. [jarkko: Replaced the first two paragraphs of text with an actual concurrency scenario.] Cc: stable@vger.kernel.org # v5.10+ Fixes: b5f545c880a2 ("[PATCH] keys: Permit running process to instantiate keys") Reported-by: Shaomin Chen Closes: https://lore.kernel.org/r/20260519144403.436694-1-eeesssooo020@gmail.com Signed-off-by: Shaomin Chen Signed-off-by: Jarkko Sakkinen Signed-off-by: Greg Kroah-Hartman --- include/keys/request_key_auth-type.h | 2 ++ security/keys/internal.h | 2 ++ security/keys/keyctl.c | 24 +++++++++++++++----- security/keys/request_key_auth.c | 33 ++++++++++++++++++++++++++-- 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/include/keys/request_key_auth-type.h b/include/keys/request_key_auth-type.h index 36b89a933310f..01e42ee5f4099 100644 --- a/include/keys/request_key_auth-type.h +++ b/include/keys/request_key_auth-type.h @@ -9,12 +9,14 @@ #define _KEYS_REQUEST_KEY_AUTH_TYPE_H #include +#include /* * Authorisation record for request_key(). */ struct request_key_auth { struct rcu_head rcu; + refcount_t usage; struct key *target_key; struct key *dest_keyring; const struct cred *cred; diff --git a/security/keys/internal.h b/security/keys/internal.h index 2cffa6dc82557..b7b622bc36a13 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -208,6 +208,8 @@ extern struct key *request_key_auth_new(struct key *target, const void *callout_info, size_t callout_len, struct key *dest_keyring); +struct request_key_auth *request_key_auth_get(struct key *authkey); +void request_key_auth_put(struct request_key_auth *rka); extern struct key *key_get_instantiation_authkey(key_serial_t target_id); diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index ef855d69c97a9..d14ace88e529c 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1197,9 +1197,13 @@ static long keyctl_instantiate_key_common(key_serial_t id, if (!instkey) goto error; - rka = instkey->payload.data[0]; - if (rka->target_key->serial != id) + rka = request_key_auth_get(instkey); + if (!rka) { + ret = -EKEYREVOKED; goto error; + } + if (rka->target_key->serial != id) + goto error_put_rka; /* pull the payload in if one was supplied */ payload = NULL; @@ -1208,7 +1212,7 @@ static long keyctl_instantiate_key_common(key_serial_t id, ret = -ENOMEM; payload = kvmalloc(plen, GFP_KERNEL); if (!payload) - goto error; + goto error_put_rka; ret = -EFAULT; if (!copy_from_iter_full(payload, plen, from)) @@ -1234,6 +1238,8 @@ static long keyctl_instantiate_key_common(key_serial_t id, error2: kvfree_sensitive(payload, plen); +error_put_rka: + request_key_auth_put(rka); error: return ret; } @@ -1358,15 +1364,19 @@ long keyctl_reject_key(key_serial_t id, unsigned timeout, unsigned error, if (!instkey) goto error; - rka = instkey->payload.data[0]; - if (rka->target_key->serial != id) + rka = request_key_auth_get(instkey); + if (!rka) { + ret = -EKEYREVOKED; goto error; + } + if (rka->target_key->serial != id) + goto error_put_rka; /* find the destination keyring if present (which must also be * writable) */ ret = get_instantiation_keyring(ringid, rka, &dest_keyring); if (ret < 0) - goto error; + goto error_put_rka; /* instantiate the key and link it into a keyring */ ret = key_reject_and_link(rka->target_key, timeout, error, @@ -1379,6 +1389,8 @@ long keyctl_reject_key(key_serial_t id, unsigned timeout, unsigned error, if (ret == 0) keyctl_change_reqkey_auth(NULL); +error_put_rka: + request_key_auth_put(rka); error: return ret; } diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index a7d7538c1f705..282e09d8fa46c 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -23,6 +23,7 @@ static void request_key_auth_describe(const struct key *, struct seq_file *); static void request_key_auth_revoke(struct key *); static void request_key_auth_destroy(struct key *); static long request_key_auth_read(const struct key *, char *, size_t); +static void request_key_auth_rcu_disposal(struct rcu_head *); /* * The request-key authorisation key type definition. @@ -115,6 +116,31 @@ static void free_request_key_auth(struct request_key_auth *rka) kfree(rka); } +/* + * Take a reference to the request-key authorisation payload so callers can + * drop authkey->sem before doing operations that may sleep. + */ +struct request_key_auth *request_key_auth_get(struct key *authkey) +{ + struct request_key_auth *rka; + + down_read(&authkey->sem); + rka = dereference_key_locked(authkey); + if (rka && !test_bit(KEY_FLAG_REVOKED, &authkey->flags)) + refcount_inc(&rka->usage); + else + rka = NULL; + up_read(&authkey->sem); + + return rka; +} + +void request_key_auth_put(struct request_key_auth *rka) +{ + if (rka && refcount_dec_and_test(&rka->usage)) + call_rcu(&rka->rcu, request_key_auth_rcu_disposal); +} + /* * Dispose of the request_key_auth record under RCU conditions */ @@ -136,8 +162,10 @@ static void request_key_auth_revoke(struct key *key) struct request_key_auth *rka = dereference_key_locked(key); kenter("{%d}", key->serial); + if (!rka) + return; rcu_assign_keypointer(key, NULL); - call_rcu(&rka->rcu, request_key_auth_rcu_disposal); + request_key_auth_put(rka); } /* @@ -150,7 +178,7 @@ static void request_key_auth_destroy(struct key *key) kenter("{%d}", key->serial); if (rka) { rcu_assign_keypointer(key, NULL); - call_rcu(&rka->rcu, request_key_auth_rcu_disposal); + request_key_auth_put(rka); } } @@ -174,6 +202,7 @@ struct key *request_key_auth_new(struct key *target, const char *op, rka = kzalloc_obj(*rka); if (!rka) goto error; + refcount_set(&rka->usage, 1); rka->callout_info = kmemdup(callout_info, callout_len, GFP_KERNEL); if (!rka->callout_info) goto error_free_rka; From 19ad7bfbd7f8f1c7f65cbf4aaef91e8ffbecaf81 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Wed, 13 May 2026 11:14:16 +0300 Subject: [PATCH 0074/2758] userfaultfd: ensure mremap_userfaultfd_fail() releases mmap_changing commit 0496a59745b0723ea74274db16fd5c8b1379b9a9 upstream. Sashiko says: mremap_userfaultfd_prep() increments ctx->mmap_changing to stall concurrent operations, but mremap_userfaultfd_fail() does not decrement it before dropping the context reference. If an mremap operation fails, ctx->mmap_changing remains elevated. This will causes subsequent userfaultfd operations like a UFFDIO_COPY to fail with -EAGAIN. Decrement ctx->mmap_changing in mremap_userfaultfd_fail(). Link: https://sashiko.dev/#/patchset/20260430113512.115938-1-rppt@kernel.org Link: https://lore.kernel.org/20260513081416.495963-1-rppt@kernel.org Fixes: df2cc96e7701 ("userfaultfd: prevent non-cooperative events vs mcopy_atomic races") Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: David Hildenbrand (Arm) Cc: Al Viro Cc: Christian Brauner Cc: Jan Kara Cc: Peter Xu Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/userfaultfd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c index 4b53dc4a32664..390e4b7d9cb9f 100644 --- a/fs/userfaultfd.c +++ b/fs/userfaultfd.c @@ -786,6 +786,8 @@ void mremap_userfaultfd_fail(struct vm_userfaultfd_ctx *vm_ctx) if (!ctx) return; + atomic_dec(&ctx->mmap_changing); + VM_WARN_ON_ONCE(atomic_read(&ctx->mmap_changing) < 0); userfaultfd_ctx_put(ctx); } From 5db89515fc28412eb9dc9972c9218a20276ae261 Mon Sep 17 00:00:00 2001 From: "Kiryl Shutsemau (Meta)" Date: Fri, 29 May 2026 18:23:30 +0100 Subject: [PATCH 0075/2758] userfaultfd: build __VMA_UFFD_FLAGS from config-gated masks commit cc7a9f6e57c4f71e8e1fee3274b1ae8770f2a743 upstream. The VMA flags bitmap is a single word today: NUM_VMA_FLAG_BITS is BITS_PER_LONG, so on 32-bit vma_flags_t holds only 32 bits. (The bitmap type exists so this can grow past BITS_PER_LONG later; until it does, anything declared above the first word is out of range on 32-bit.) The bit enum nevertheless declares some bits unconditionally above BITS_PER_LONG -- VMA_UFFD_MINOR_BIT is 41, with VM_UFFD_MINOR == VM_NONE on 32-bit so no VMA actually carries the bit. __VMA_UFFD_FLAGS feeds VMA_UFFD_MINOR_BIT to mk_vma_flags() unconditionally. On 32-bit that becomes __set_bit(41, &one_long), a write one word past the end of the single-word bitmap. The compiler folds the out-of-bounds store with wraparound (1UL << (41 % 32) == bit 9) into the first word; bit 9 is already in __VMA_UFFD_FLAGS so the mask happens to come out right today, but it is an out-of-bounds write all the same, and any high-numbered bit whose mod-BITS_PER_LONG position is otherwise unused would silently OR an extra bit into the mask. Rather than feed bit numbers that may not exist on the current build to mk_vma_flags(), build the mask from whole per-mode masks that collapse to EMPTY_VMA_FLAGS when their feature is unavailable. Add mk_vma_flags_from_masks() for that, and define VMA_UFFD_MISSING / _WP / _MINOR alongside the VM_UFFD_* flags, gating VMA_UFFD_MINOR on the same config as VM_UFFD_MINOR (which implies 64BIT, where bit 41 fits). An out-of-range bit is then never materialised, on any arch, and the in-range fast path stays a compile-time constant. Link: https://lore.kernel.org/20260529172331.356655-7-kas@kernel.org Fixes: 9ea35a25d51b ("mm: introduce VMA flags bitmap type") Signed-off-by: Kiryl Shutsemau Reported-by: Sashiko AI review Suggested-by: Lorenzo Stoakes Reviewed-by: Lorenzo Stoakes Assisted-by: Claude:claude-opus-4-8 Cc: David Hildenbrand Cc: Michal Hocko Cc: Mike Rapoport Cc: Peter Xu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Balbir Singh Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- include/linux/mm.h | 39 +++++++++++++++++++++++++++++++++++ include/linux/userfaultfd_k.h | 4 ++-- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index fc2acedf0b763..80fce25159302 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -496,6 +496,21 @@ enum { #else #define VM_UFFD_MINOR VM_NONE #endif + +/* + * vma_flags_t masks for the userfaultfd VMA flags. VMA_UFFD_MINOR is gated on + * the same config as VM_UFFD_MINOR -- which implies 64BIT, where the bit fits + * -- so an out-of-range bit is never fed to mk_vma_flags() on a build whose + * bitmap cannot hold it. + */ +#define VMA_UFFD_MISSING mk_vma_flags(VMA_UFFD_MISSING_BIT) +#define VMA_UFFD_WP mk_vma_flags(VMA_UFFD_WP_BIT) +#ifdef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR +#define VMA_UFFD_MINOR mk_vma_flags(VMA_UFFD_MINOR_BIT) +#else +#define VMA_UFFD_MINOR EMPTY_VMA_FLAGS +#endif + #ifdef CONFIG_64BIT #define VM_ALLOW_ANY_UNCACHED INIT_VM_FLAG(ALLOW_ANY_UNCACHED) #define VM_SEALED INIT_VM_FLAG(SEALED) @@ -1238,6 +1253,30 @@ static __always_inline void vma_flags_set_mask(vma_flags_t *flags, #define vma_flags_set(flags, ...) \ vma_flags_set_mask(flags, mk_vma_flags(__VA_ARGS__)) +static __always_inline vma_flags_t __mk_vma_flags_from_masks(size_t count, + const vma_flags_t *masks) +{ + vma_flags_t flags = EMPTY_VMA_FLAGS; + size_t i; + + for (i = 0; i < count; i++) + vma_flags_set_mask(&flags, masks[i]); + return flags; +} + +/* + * Combine pre-computed vma_flags_t masks into one value, e.g.: + * + * vma_flags_t flags = mk_vma_flags_from_masks(VMA_UFFD_WP, VMA_UFFD_MINOR); + * + * Unlike mk_vma_flags(), which takes bit numbers, this takes whole masks -- + * each of which may be EMPTY_VMA_FLAGS when its feature is unavailable -- so a + * bit that does not exist on the current build is never materialised. + */ +#define mk_vma_flags_from_masks(...) \ + __mk_vma_flags_from_masks(COUNT_ARGS(__VA_ARGS__), \ + (const vma_flags_t []){__VA_ARGS__}) + /* Clear all of the to-clear flags in flags, non-atomically. */ static __always_inline void vma_flags_clear_mask(vma_flags_t *flags, vma_flags_t to_clear) diff --git a/include/linux/userfaultfd_k.h b/include/linux/userfaultfd_k.h index d2920f98ab869..8d2bfae6b4e67 100644 --- a/include/linux/userfaultfd_k.h +++ b/include/linux/userfaultfd_k.h @@ -23,8 +23,8 @@ /* The set of all possible UFFD-related VM flags. */ #define __VM_UFFD_FLAGS (VM_UFFD_MISSING | VM_UFFD_WP | VM_UFFD_MINOR) -#define __VMA_UFFD_FLAGS mk_vma_flags(VMA_UFFD_MISSING_BIT, VMA_UFFD_WP_BIT, \ - VMA_UFFD_MINOR_BIT) +#define __VMA_UFFD_FLAGS mk_vma_flags_from_masks(VMA_UFFD_MISSING, VMA_UFFD_WP, \ + VMA_UFFD_MINOR) /* * CAREFUL: Check include/uapi/asm-generic/fcntl.h when defining From f10e6d5a35c432c5df974f5148916ef977bfe83a Mon Sep 17 00:00:00 2001 From: Zenm Chen Date: Tue, 7 Apr 2026 23:44:30 +0800 Subject: [PATCH 0076/2758] wifi: mt76: mt76x2u: Add support for ELECOM WDC-867SU3S commit f4ce0664e9f0387873b181777891741c33e19465 upstream. Add the ID 056e:400a to the table to support an additional MT7612U adapter: ELECOM WDC-867SU3S. Compile tested only. Cc: stable@vger.kernel.org # 5.10.x Signed-off-by: Zenm Chen Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260407154430.9184-1-zenmchen@gmail.com Signed-off-by: Felix Fietkau Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/mediatek/mt76/mt76x2/usb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c index 01cb3b2830f35..459c4044f0d10 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c @@ -16,6 +16,7 @@ static const struct usb_device_id mt76x2u_device_table[] = { { USB_DEVICE(0x0e8d, 0x7612) }, /* Aukey USBAC1200 - Alfa AWUS036ACM */ { USB_DEVICE(0x057c, 0x8503) }, /* Avm FRITZ!WLAN AC860 */ { USB_DEVICE(0x7392, 0xb711) }, /* Edimax EW 7722 UAC */ + { USB_DEVICE(0x056e, 0x400a) }, /* ELECOM WDC-867SU3S */ { USB_DEVICE(0x0e8d, 0x7632) }, /* HC-M7662BU1 */ { USB_DEVICE(0x0471, 0x2126) }, /* LiteOn WN4516R module, nonstandard USB connector */ { USB_DEVICE(0x0471, 0x7600) }, /* LiteOn WN4519R module, nonstandard USB connector */ From 84139c1ab36825119b75eaff747232d9ebfa78ad Mon Sep 17 00:00:00 2001 From: ElXreno Date: Wed, 6 May 2026 04:39:16 +0300 Subject: [PATCH 0077/2758] wifi: mt76: mt7925: don't disable AP BSS when removing TDLS peer commit 37d65384aa6f9cbe45f4052b13b378af1aab3e95 upstream. On a STATION vif, removing a TDLS peer takes the mt7925_mac_sta_remove -> mt7925_mac_sta_remove_links path. The first loop in that function calls mt7925_mcu_add_bss_info(..., enable=false) for every link of the station being removed. For a non-MLO STATION vif there is exactly one link, link 0, whose bss_conf is the AP's. TDLS peers do not have their own bss_conf - they share the AP's BSS. The result is that every TDLS peer teardown sends a BSS_INFO_UPDATE with enable=0 for the AP's BSS to the firmware, which wipes the AP-side rate-control context. The connection stays associated and TX from the host still works at the negotiated rate, but the AP's downlink to us collapses to the lowest mandatory OFDM rate (HE-MCS 0 / 6 Mbit/s OFDM) and only slowly recovers as rate adaptation re-learns under sustained traffic. With brief or bursty traffic the link can stay at 6-72 Mbit/s indefinitely, requiring a manual reconnect. mt7925_mac_link_sta_remove() already guards its own mt7925_mcu_add_bss_info(..., false) call with "vif->type == NL80211_IFTYPE_STATION && !link_sta->sta->tdls". Add the equivalent guard at the top of the cleanup loop in mt7925_mac_sta_remove_links(), above the link_sta / link_conf / mlink / mconf lookups, so TDLS peer teardown skips the loop body entirely without doing the per-link work that would just be thrown away. Verified on mt7925e by triggering Samsung-S938B auto-TDLS via iperf3 and watching iw rx bitrate after teardown: Before: rx bitrate collapses to 6.0-72.0 Mbit/s, oscillates 17/72/ 137/288/432 Mbit/s for 30+ seconds, no full recovery without a manual reassoc. After: rx bitrate stays at 1200.9 Mbit/s HE-MCS 11 NSS 2 80 MHz across the entire TDLS lifecycle. bpftrace confirms a single mt7925_mcu_add_bss_info(enable=0) call per teardown before the fix; zero such calls after. Fixes: 3878b4333602 ("wifi: mt76: mt7925: update mt7925_mac_link_sta_[add, assoc, remove] for MLO") Cc: stable@vger.kernel.org Signed-off-by: ElXreno Assisted-by: Claude:claude-opus-4-7 bpftrace Link: https://patch.msgid.link/20260506-mt7925-tdls-fixes-v2-2-46aa826ba8bb@gmail.com Signed-off-by: Felix Fietkau Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/mediatek/mt76/mt7925/main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 73d3722739d0a..79cc30a3b29ef 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -1265,6 +1265,9 @@ mt7925_mac_sta_remove_links(struct mt792x_dev *dev, struct ieee80211_vif *vif, if (vif->type == NL80211_IFTYPE_AP) break; + if (vif->type == NL80211_IFTYPE_STATION && sta->tdls) + continue; + link_sta = mt792x_sta_to_link_sta(vif, sta, link_id); if (!link_sta) continue; From 051f954b94479d72222c9fbc82a3eef4777bca01 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Mon, 20 Apr 2026 13:01:29 +0200 Subject: [PATCH 0078/2758] wifi: ath11k: fix warning when unbinding commit 8b7a26b6681922a38cd5a7829ace61f8e54df9b7 upstream. If there is an error during some initialization related to firmware, the buffers dp->tx_ring[i].tx_status are released. However this is released again when the device is unbinded (ath11k_pci), and we get: WARNING: CPU: 0 PID: 6231 at mm/slub.c:4368 free_large_kmalloc+0x57/0x90 Call Trace: free_large_kmalloc ath11k_dp_free ath11k_core_deinit ath11k_pci_remove ... The issue is always reproducible from a VM because the MSI addressing initialization is failing. In order to fix the issue, just set the buffers to NULL after releasing in order to avoid the double free. Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices") Cc: stable@vger.kernel.org Signed-off-by: Jose Ignacio Tornos Martinez Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260420110130.509670-1-jtornosm@redhat.com Signed-off-by: Jeff Johnson Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath11k/dp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c index bbb86f1651419..5a50b623bd07b 100644 --- a/drivers/net/wireless/ath/ath11k/dp.c +++ b/drivers/net/wireless/ath/ath11k/dp.c @@ -1040,6 +1040,7 @@ void ath11k_dp_free(struct ath11k_base *ab) idr_destroy(&dp->tx_ring[i].txbuf_idr); spin_unlock_bh(&dp->tx_ring[i].tx_idr_lock); kfree(dp->tx_ring[i].tx_status); + dp->tx_ring[i].tx_status = NULL; } /* Deinit any SOC level resource */ From 4b75e6180f46704e2591c6b3d86241773699edeb Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Wed, 29 Apr 2026 15:02:48 +0300 Subject: [PATCH 0079/2758] wifi: rtl8xxxu: Detect the maximum supported channel width commit ef771eabc79d5f21b63689cca0e0fa5493fa0a8a upstream. Some devices malfunction when connected to a network with 40 MHz channel width, because they don't support that. RTL8188FU, RTL8192FU, and RTL8710BU (RTL8188GU) have a way to signal this (and some other capabilities) to the driver. Get this information from the hardware and advertise 40 MHz support only when the hardware can handle it. We assume the other chips can always handle it. RTL8710BU needs a different way to retrieve this information, which will be implemented some other time. Fixes: dbf9b7bb0edf ("wifi: rtl8xxxu: Enable 40 MHz width by default") Cc: stable@vger.kernel.org Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221394 Signed-off-by: Bitterblue Smith Reviewed-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/c57de68e-5d57-4c26-898f-8a284bb25381@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/realtek/rtl8xxxu/8188e.c | 1 + drivers/net/wireless/realtek/rtl8xxxu/8188f.c | 1 + drivers/net/wireless/realtek/rtl8xxxu/8192c.c | 1 + drivers/net/wireless/realtek/rtl8xxxu/8192e.c | 1 + drivers/net/wireless/realtek/rtl8xxxu/8192f.c | 1 + drivers/net/wireless/realtek/rtl8xxxu/8710b.c | 1 + drivers/net/wireless/realtek/rtl8xxxu/8723a.c | 1 + drivers/net/wireless/realtek/rtl8xxxu/8723b.c | 1 + drivers/net/wireless/realtek/rtl8xxxu/core.c | 64 +++++++++++++++++-- drivers/net/wireless/realtek/rtl8xxxu/regs.h | 2 + .../net/wireless/realtek/rtl8xxxu/rtl8xxxu.h | 7 ++ 11 files changed, 76 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8188e.c b/drivers/net/wireless/realtek/rtl8xxxu/8188e.c index 766a7a7c7d281..fea5aec9ced99 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8188e.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8188e.c @@ -1866,6 +1866,7 @@ struct rtl8xxxu_fileops rtl8188eu_fops = { .has_tx_report = 1, .init_reg_pkt_life_time = 1, .gen2_thermal_meter = 1, + .hw_feature_report = 0, .max_sec_cam_num = 32, .adda_1t_init = 0x0b1b25a0, .adda_1t_path_on = 0x0bdb25a0, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8188f.c b/drivers/net/wireless/realtek/rtl8xxxu/8188f.c index 3abf14d7044f3..5556aad1a7ad3 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8188f.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8188f.c @@ -1745,6 +1745,7 @@ struct rtl8xxxu_fileops rtl8188fu_fops = { .init_reg_rxfltmap = 1, .init_reg_pkt_life_time = 1, .init_reg_hmtfr = 1, + .hw_feature_report = 1, .ampdu_max_time = 0x70, .ustime_tsf_edca = 0x28, .max_aggr_num = 0x0c14, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8192c.c b/drivers/net/wireless/realtek/rtl8xxxu/8192c.c index 444872131c663..508322a8f3108 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8192c.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8192c.c @@ -723,6 +723,7 @@ struct rtl8xxxu_fileops rtl8192cu_fops = { .tx_desc_size = sizeof(struct rtl8xxxu_txdesc32), .rx_desc_size = sizeof(struct rtl8xxxu_rxdesc16), .supports_ap = 1, + .hw_feature_report = 0, .max_macid_num = 32, .max_sec_cam_num = 32, .adda_1t_init = 0x0b1b25a0, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8192e.c b/drivers/net/wireless/realtek/rtl8xxxu/8192e.c index 8e123bbfc6659..690ace3031503 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8192e.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8192e.c @@ -1751,6 +1751,7 @@ struct rtl8xxxu_fileops rtl8192eu_fops = { .has_s0s1 = 0, .gen2_thermal_meter = 1, .needs_full_init = 1, + .hw_feature_report = 0, .supports_ap = 1, .max_macid_num = 128, .max_sec_cam_num = 64, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8192f.c b/drivers/net/wireless/realtek/rtl8xxxu/8192f.c index cd2156b7a57ab..5f076da4b5506 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8192f.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8192f.c @@ -2074,6 +2074,7 @@ struct rtl8xxxu_fileops rtl8192fu_fops = { .init_reg_rxfltmap = 1, .init_reg_pkt_life_time = 1, .init_reg_hmtfr = 1, + .hw_feature_report = 1, .ampdu_max_time = 0x5e, .ustime_tsf_edca = 0x50, .max_aggr_num = 0x1f1f, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8710b.c b/drivers/net/wireless/realtek/rtl8xxxu/8710b.c index 11c63c320eaeb..a87e692bfc812 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8710b.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8710b.c @@ -1852,6 +1852,7 @@ struct rtl8xxxu_fileops rtl8710bu_fops = { .init_reg_rxfltmap = 1, .init_reg_pkt_life_time = 1, .init_reg_hmtfr = 1, + .hw_feature_report = 0, /* TODO, it's different */ .ampdu_max_time = 0x5e, /* * The RTL8710BU vendor driver uses 0x50 here and it works fine, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8723a.c b/drivers/net/wireless/realtek/rtl8xxxu/8723a.c index 4f4493d0bfc2d..18e038b82f490 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8723a.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8723a.c @@ -632,6 +632,7 @@ struct rtl8xxxu_fileops rtl8723au_fops = { .rx_agg_buf_size = 16000, .tx_desc_size = sizeof(struct rtl8xxxu_txdesc32), .rx_desc_size = sizeof(struct rtl8xxxu_rxdesc16), + .hw_feature_report = 0, .max_sec_cam_num = 32, .adda_1t_init = 0x0b1b25a0, .adda_1t_path_on = 0x0bdb25a0, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8723b.c b/drivers/net/wireless/realtek/rtl8xxxu/8723b.c index cc2e60b06f647..e314ef991b388 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8723b.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8723b.c @@ -1746,6 +1746,7 @@ struct rtl8xxxu_fileops rtl8723bu_fops = { .gen2_thermal_meter = 1, .needs_full_init = 1, .init_reg_hmtfr = 1, + .hw_feature_report = 0, .ampdu_max_time = 0x5e, .ustime_tsf_edca = 0x50, .max_aggr_num = 0x0c14, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c index f20fade0c0990..40af324f5f0a3 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c @@ -14,6 +14,7 @@ */ #include +#include #include "regs.h" #include "rtl8xxxu.h" @@ -3915,6 +3916,46 @@ static inline u8 rtl8xxxu_get_macid(struct rtl8xxxu_priv *priv, return sta_info->macid; } +static void rtl8xxxu_request_hw_feature(struct rtl8xxxu_priv *priv) +{ + if (!priv->fops->hw_feature_report) + return; + + rtl8xxxu_write8(priv, REG_C2HEVT_MSG_NORMAL, C2H_HW_FEATURE_DUMP); +} + +static int rtl8xxxu_dump_hw_feature(struct rtl8xxxu_priv *priv) +{ + static const u8 bw_map[8] = { 0, 0, 160, 5, 10, 20, 40, 80 }; + struct rtl8xxxu_hw_feature *hw_feature = &priv->hw_feature; + u8 feature[13]; + int i, ret; + u8 id, bw; + + if (!priv->fops->hw_feature_report) { + hw_feature->max_bw = 40; + return 0; + } + + ret = read_poll_timeout(rtl8xxxu_read8, id, + id == C2H_HW_FEATURE_REPORT, + 10000, 800000, false, + priv, REG_C2HEVT_MSG_NORMAL); + if (ret) + return ret; + + for (i = 0; i < ARRAY_SIZE(feature); i++) + feature[i] = rtl8xxxu_read8(priv, REG_C2HEVT_MSG_NORMAL + 2 + i); + + rtl8xxxu_write8(priv, REG_C2HEVT_MSG_NORMAL, 0); + + bw = u8_get_bits(feature[6], GENMASK(2, 0)); + + hw_feature->max_bw = bw_map[bw]; + + return 0; +} + static int rtl8xxxu_init_device(struct ieee80211_hw *hw) { struct rtl8xxxu_priv *priv = hw->priv; @@ -3961,6 +4002,8 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) */ rtl8xxxu_write16(priv, REG_TRXFF_BNDY + 2, fops->trxff_boundary); + rtl8xxxu_request_hw_feature(priv); + for (int retry = 5; retry >= 0 ; retry--) { ret = rtl8xxxu_download_firmware(priv); dev_dbg(dev, "%s: download_firmware %i\n", __func__, ret); @@ -3976,6 +4019,12 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) if (ret) goto exit; + ret = rtl8xxxu_dump_hw_feature(priv); + if (ret) { + dev_err(dev, "failed to dump hw feature\n"); + goto exit; + } + if (fops->phy_init_antenna_selection) fops->phy_init_antenna_selection(priv); @@ -7835,15 +7884,20 @@ static int rtl8xxxu_probe(struct usb_interface *interface, sband->ht_cap.ht_supported = true; sband->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; sband->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16; - sband->ht_cap.cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 | - IEEE80211_HT_CAP_SUP_WIDTH_20_40; + sband->ht_cap.cap = IEEE80211_HT_CAP_SGI_20; + + if (priv->hw_feature.max_bw >= 40) { + sband->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; + sband->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + } else { + dev_info(&udev->dev, "hardware doesn't support HT40\n"); + } + memset(&sband->ht_cap.mcs, 0, sizeof(sband->ht_cap.mcs)); sband->ht_cap.mcs.rx_mask[0] = 0xff; sband->ht_cap.mcs.rx_mask[4] = 0x01; - if (priv->rf_paths > 1) { + if (priv->rf_paths > 1) sband->ht_cap.mcs.rx_mask[1] = 0xff; - sband->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; - } sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; hw->wiphy->bands[NL80211_BAND_2GHZ] = sband; diff --git a/drivers/net/wireless/realtek/rtl8xxxu/regs.h b/drivers/net/wireless/realtek/rtl8xxxu/regs.h index 0741db8d08bff..eafaef1438b97 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/regs.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/regs.h @@ -447,6 +447,8 @@ /* 8188EU */ #define REG_32K_CTRL 0x0194 #define REG_C2HEVT_MSG_NORMAL 0x01a0 +#define C2H_HW_FEATURE_REPORT 0x19 +#define C2H_HW_FEATURE_DUMP 0xfd /* 8192EU/8723BU/8812 */ #define REG_C2HEVT_CMD_ID_8723B 0x01ae #define REG_C2HEVT_CLEAR 0x01af diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h index 9fb2583ffffc3..eeb18eb0e4c0f 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h @@ -1789,11 +1789,17 @@ struct rtl8xxxu_cfo_tracking { #define RTL8XXXU_BC_MC_MACID1 1 #define RTL8XXXU_MAX_SEC_CAM_NUM 64 +struct rtl8xxxu_hw_feature { + u8 max_bw; +}; + struct rtl8xxxu_priv { struct ieee80211_hw *hw; struct usb_device *udev; struct rtl8xxxu_fileops *fops; + struct rtl8xxxu_hw_feature hw_feature; + spinlock_t tx_urb_lock; struct list_head tx_urb_free_list; int tx_urb_free_count; @@ -2009,6 +2015,7 @@ struct rtl8xxxu_fileops { u8 init_reg_pkt_life_time:1; u8 init_reg_hmtfr:1; u8 supports_concurrent:1; + u8 hw_feature_report:1; u8 ampdu_max_time; u8 ustime_tsf_edca; u16 max_aggr_num; From 2a42951e935fdf53d14bb07a80202459b12f0480 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sat, 25 Apr 2026 22:32:58 +0300 Subject: [PATCH 0080/2758] wifi: rtlwifi: rtl8821ae: Fix C2H bit location in RX descriptor commit 83d38df6929118c3f996b9e3351c2d5014073d87 upstream. Bit 28 of double word 2 in the RX descriptor indicates if the packet is a normal 802.11 frame, or a message from the wifi firmware to the driver (Card 2 Host). Commit f5678bfe1cdc ("rtlwifi: rtl8821ae: Replace local bit manipulation macros") mistakenly made the driver look for this bit in double word 1, causing packet loss and Bluetooth coexistence problems. Fixes: f5678bfe1cdc ("rtlwifi: rtl8821ae: Replace local bit manipulation macros") Cc: Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/04da7398-cedb-425a-a810-5772ab10139d@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h index 1155365348f32..d5de09d75f451 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h @@ -291,7 +291,7 @@ static inline int get_rx_desc_paggr(__le32 *__pdesc) static inline int get_rx_status_desc_rpt_sel(__le32 *__pdesc) { - return le32_get_bits(*(__pdesc + 1), BIT(28)); + return le32_get_bits(*(__pdesc + 2), BIT(28)); } static inline int get_rx_desc_rxmcs(__le32 *__pdesc) From a68c04f4ee6a73cd8be44df32f00e1bcb41ec158 Mon Sep 17 00:00:00 2001 From: Luka Gejak Date: Mon, 18 May 2026 16:23:10 +0200 Subject: [PATCH 0081/2758] wifi: rtw88: increase TX report timeout to fix race condition commit c80788f7c5aed8d420366b821f867a8a353d83a5 upstream. The driver expects the firmware to report TX status within 500ms. However, a timeout can be triggered when the hardware performs background scans while under TX load. During these scans, the firmware stays off-channel for periods exceeding 500ms, delaying the delivery of TX reports back to the driver. When this occurs, the purge timer fires prematurely and drops the tracking skbs from the queue. This results in the host stack interpreting the missing status as packet loss, leading to TCP window collapse. In testing with iperf3, this causes throughput to drop from ~90 Mbps to near-zero for approximately 2 seconds until the connection recovers. Increase RTW_TX_PROBE_TIMEOUT to 2500ms for RTL8723DU. This duration is sufficient to accommodate off-channel dwell time during full background scans, ensuring the purge timer only trips during genuine firmware lockups and preventing unnecessary TCP retransmission cycles. Fixes: a82dfd33d123 ("wifi: rtw88: Add common USB chip support") Cc: stable@vger.kernel.org Acked-by: Ping-Ke Shih Tested-by: Luka Gejak Signed-off-by: Luka Gejak Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260518142311.10328-1-luka.gejak@linux.dev Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/realtek/rtw88/tx.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw88/tx.c b/drivers/net/wireless/realtek/rtw88/tx.c index 3106edb84fb47..a6e43314a4e95 100644 --- a/drivers/net/wireless/realtek/rtw88/tx.c +++ b/drivers/net/wireless/realtek/rtw88/tx.c @@ -196,6 +196,7 @@ void rtw_tx_report_purge_timer(struct timer_list *t) void rtw_tx_report_enqueue(struct rtw_dev *rtwdev, struct sk_buff *skb, u8 sn) { struct rtw_tx_report *tx_report = &rtwdev->tx_report; + unsigned long timeout = RTW_TX_PROBE_TIMEOUT; unsigned long flags; u8 *drv_data; @@ -207,7 +208,11 @@ void rtw_tx_report_enqueue(struct rtw_dev *rtwdev, struct sk_buff *skb, u8 sn) __skb_queue_tail(&tx_report->queue, skb); spin_unlock_irqrestore(&tx_report->q_lock, flags); - mod_timer(&tx_report->purge_timer, jiffies + RTW_TX_PROBE_TIMEOUT); + if (rtwdev->chip->id == RTW_CHIP_TYPE_8723D && + rtwdev->hci.type == RTW_HCI_TYPE_USB) + timeout = msecs_to_jiffies(2500); + + mod_timer(&tx_report->purge_timer, jiffies + timeout); } EXPORT_SYMBOL(rtw_tx_report_enqueue); From 8206d173d18ef5a077423119f4e9a93cb3a6f4eb Mon Sep 17 00:00:00 2001 From: Luka Gejak Date: Mon, 18 May 2026 16:23:11 +0200 Subject: [PATCH 0082/2758] wifi: rtw88: usb: fix memory leaks on USB write failures commit 6b964941bbfe6e0f18b1a5e008486dbb62df440a upstream. When rtw_usb_write_port() fails to submit a USB Request Block (URB) (e.g., due to device disconnect or ENOMEM), the completion callback is never executed. Currently, the driver ignores the return value of rtw_usb_write_port() in rtw_usb_write_data() and rtw_usb_tx_agg_skb(). Because these functions rely on the completion callback to free the socket buffers (skbs) and the transaction control block (txcb), a submission failure results in: 1. A memory leak of the allocated skb in rtw_usb_write_data(). 2. A memory leak of the txcb structure and all aggregated skbs in rtw_usb_tx_agg_skb(). Fix this by checking the return value of rtw_usb_write_port(). If it fails, explicitly free the skb in rtw_usb_write_data(), and properly purge the tx_ack_queue and free the txcb in rtw_usb_tx_agg_skb(). The issue was discovered in practice during device disconnect/reconnect scenarios and memory pressure conditions. Tested by verifying normal TX operation continues after the fix without regressions. Fixes: a82dfd33d123 ("wifi: rtw88: Add common USB chip support") Cc: stable@vger.kernel.org Acked-by: Ping-Ke Shih Tested-by: Luka Gejak Signed-off-by: Luka Gejak Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260518142311.10328-2-luka.gejak@linux.dev Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/realtek/rtw88/usb.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c index 718940ebba316..1a0bdbf52cb0d 100644 --- a/drivers/net/wireless/realtek/rtw88/usb.c +++ b/drivers/net/wireless/realtek/rtw88/usb.c @@ -399,6 +399,7 @@ static bool rtw_usb_tx_agg_skb(struct rtw_usb *rtwusb, struct sk_buff_head *list int agg_num = 0; unsigned int align_next = 0; u8 qsel; + int ret; if (skb_queue_empty(list)) return false; @@ -456,7 +457,13 @@ static bool rtw_usb_tx_agg_skb(struct rtw_usb *rtwusb, struct sk_buff_head *list tx_desc = (struct rtw_tx_desc *)skb_head->data; qsel = le32_get_bits(tx_desc->w1, RTW_TX_DESC_W1_QSEL); - rtw_usb_write_port(rtwdev, qsel, skb_head, rtw_usb_write_port_tx_complete, txcb); + ret = rtw_usb_write_port(rtwdev, qsel, skb_head, + rtw_usb_write_port_tx_complete, txcb); + if (ret) { + ieee80211_purge_tx_queue(rtwdev->hw, &txcb->tx_ack_queue); + kfree(txcb); + return false; + } return true; } @@ -518,8 +525,10 @@ static int rtw_usb_write_data(struct rtw_dev *rtwdev, ret = rtw_usb_write_port(rtwdev, qsel, skb, rtw_usb_write_port_complete, skb); - if (unlikely(ret)) + if (unlikely(ret)) { rtw_err(rtwdev, "failed to do USB write, ret=%d\n", ret); + dev_kfree_skb_any(skb); + } return ret; } From 032e49805099cdd88cede42209357c67fb6270df Mon Sep 17 00:00:00 2001 From: Junjie Cao Date: Thu, 12 Feb 2026 20:50:34 +0800 Subject: [PATCH 0083/2758] wifi: iwlwifi: mvm: fix race condition in PTP removal commit 65150c9cc3e06ab54bc4e8134a47f6f5d095a4e3 upstream. iwl_mvm_ptp_remove() calls cancel_delayed_work_sync() only after ptp_clock_unregister() and clearing ptp_data state (ptp_clock, ptp_clock_info, last_gp2). This creates a race where the delayed work iwl_mvm_ptp_work() can execute between ptp_clock_unregister() and cancel_delayed_work_sync(), observing partially cleared PTP state. Move cancel_delayed_work_sync() before ptp_clock_unregister() to ensure the delayed work is fully stopped before any PTP cleanup begins. Cc: stable@vger.kernel.org Reviewed-by: Simon Horman Reviewed-by: Vadim Fedorenko Signed-off-by: Junjie Cao Link: https://patch.msgid.link/20260212125035.1345718-1-junjie.cao@intel.com Signed-off-by: Miri Korenblit Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/intel/iwlwifi/mvm/ptp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c b/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c index f7b620136c857..e05e531b09f0b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c @@ -325,11 +325,11 @@ void iwl_mvm_ptp_remove(struct iwl_mvm *mvm) mvm->ptp_data.ptp_clock_info.name, ptp_clock_index(mvm->ptp_data.ptp_clock)); + cancel_delayed_work_sync(&mvm->ptp_data.dwork); ptp_clock_unregister(mvm->ptp_data.ptp_clock); mvm->ptp_data.ptp_clock = NULL; memset(&mvm->ptp_data.ptp_clock_info, 0, sizeof(mvm->ptp_data.ptp_clock_info)); mvm->ptp_data.last_gp2 = 0; - cancel_delayed_work_sync(&mvm->ptp_data.dwork); } } From 9579781cd16d92b387d5277134caae61b4352b1d Mon Sep 17 00:00:00 2001 From: Junjie Cao Date: Thu, 12 Feb 2026 20:50:35 +0800 Subject: [PATCH 0084/2758] wifi: iwlwifi: mld: fix race condition in PTP removal commit e1fc08598aa34b28359831e768076f56632720c1 upstream. iwl_mld_ptp_remove() calls cancel_delayed_work_sync() only after ptp_clock_unregister() and clearing ptp_data state (ptp_clock, last_gp2, wrap_counter). This creates a race where the delayed work iwl_mld_ptp_work() can execute between ptp_clock_unregister() and cancel_delayed_work_sync(), observing partially cleared PTP state. Move cancel_delayed_work_sync() before ptp_clock_unregister() to ensure the delayed work is fully stopped before any PTP cleanup begins. Cc: stable@vger.kernel.org Reviewed-by: Simon Horman Reviewed-by: Vadim Fedorenko Signed-off-by: Junjie Cao Link: https://patch.msgid.link/20260212125035.1345718-2-junjie.cao@intel.com Signed-off-by: Miri Korenblit Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/intel/iwlwifi/mld/ptp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ptp.c b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c index c65f4b56a3270..20ae338e5696d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/ptp.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c @@ -321,10 +321,10 @@ void iwl_mld_ptp_remove(struct iwl_mld *mld) mld->ptp_data.ptp_clock_info.name, ptp_clock_index(mld->ptp_data.ptp_clock)); + cancel_delayed_work_sync(&mld->ptp_data.dwork); ptp_clock_unregister(mld->ptp_data.ptp_clock); mld->ptp_data.ptp_clock = NULL; mld->ptp_data.last_gp2 = 0; mld->ptp_data.wrap_counter = 0; - cancel_delayed_work_sync(&mld->ptp_data.dwork); } } From fe7f339f63c9dc4ca546ed7ac38ba4bb3a99dcfc Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Thu, 2 Apr 2026 14:48:07 +0800 Subject: [PATCH 0085/2758] wifi: iwlwifi: mld: validate sta_mask before ffs() in BA session handlers commit f056fc2b927448d37eca6b6cacc3d1b0f67b20d2 upstream. Three BA session handlers use ffs(ba_data->sta_mask) - 1 to derive a station ID without checking that sta_mask is non-zero. When sta_mask is zero, ffs() returns 0 and the subtraction wraps to 0xFFFFFFFF, causing an out-of-bounds access on fw_id_to_link_sta[]. Add WARN_ON_ONCE(!ba_data->sta_mask) guards before each ffs() call, consistent with the existing check in iwl_mld_ampdu_rx_start(). Reported-by: Yuhao Jiang Cc: stable@vger.kernel.org Signed-off-by: Junrui Luo Link: https://patch.msgid.link/SYBPR01MB788115C6CE873271A9A15A25AF51A@SYBPR01MB7881.ausprd01.prod.outlook.com Signed-off-by: Miri Korenblit Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/intel/iwlwifi/mld/agg.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/agg.c b/drivers/net/wireless/intel/iwlwifi/mld/agg.c index 3bf36f8f68742..e3627ad0321c8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/agg.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/agg.c @@ -64,6 +64,9 @@ static void iwl_mld_release_frames_from_notif(struct iwl_mld *mld, } /* pick any STA ID to find the pointer */ + if (WARN_ON_ONCE(!ba_data->sta_mask)) + goto out_unlock; + sta_id = ffs(ba_data->sta_mask) - 1; link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]); if (WARN_ON_ONCE(IS_ERR_OR_NULL(link_sta) || !link_sta->sta)) @@ -166,6 +169,9 @@ void iwl_mld_del_ba(struct iwl_mld *mld, int queue, goto out_unlock; /* pick any STA ID to find the pointer */ + if (WARN_ON_ONCE(!ba_data->sta_mask)) + goto out_unlock; + sta_id = ffs(ba_data->sta_mask) - 1; link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]); if (WARN_ON_ONCE(IS_ERR_OR_NULL(link_sta) || !link_sta->sta)) @@ -347,6 +353,9 @@ static void iwl_mld_rx_agg_session_expired(struct timer_list *t) } /* timer expired, pick any STA ID to find the pointer */ + if (WARN_ON_ONCE(!ba_data->sta_mask)) + goto unlock; + sta_id = ffs(ba_data->sta_mask) - 1; link_sta = rcu_dereference(ba_data->mld->fw_id_to_link_sta[sta_id]); From 48c92559e7b66fdc3cbc74f6e152e66ec0150a0a Mon Sep 17 00:00:00 2001 From: Wenjie Qi Date: Wed, 20 May 2026 17:52:04 +0800 Subject: [PATCH 0086/2758] f2fs: fix missing read bio submission on large folio error commit 74c8d2ec95c59a5651ecd975c466998af1961fd4 upstream. f2fs_read_data_large_folio() can keep a read bio across multiple readahead folios. If a later folio hits an error before any of its blocks are added to the bio, folio_in_bio is false and the current error path returns immediately after ending that folio. This can leave the bio accumulated for earlier folios unsubmitted. Those folios then never receive read completion, and readers can wait indefinitely on the locked folios. Route errors through the common out path so any pending bio is submitted before returning. Stop consuming more readahead folios once an error is seen, and only wait on and clear the current folio when it was actually added to the bio. Cc: stable@kernel.org Fixes: a5d8b9d94e18 ("f2fs: fix to unlock folio in f2fs_read_data_large_folio()") Signed-off-by: Wenjie Qi Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/data.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 8d4f1e75dee3e..836edb7899dec 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -2495,7 +2495,7 @@ static int f2fs_read_data_large_folio(struct inode *inode, unsigned nrpages; struct f2fs_folio_state *ffs; int ret = 0; - bool folio_in_bio; + bool folio_in_bio = false; if (!IS_IMMUTABLE(inode) || f2fs_compressed_file(inode)) { if (folio) @@ -2611,18 +2611,17 @@ static int f2fs_read_data_large_folio(struct inode *inode, } trace_f2fs_read_folio(folio, DATA); err_out: - if (!folio_in_bio) { + if (!folio_in_bio) folio_end_read(folio, !ret); - if (ret) - return ret; - } + if (ret) + goto out; if (rac) { folio = readahead_folio(rac); goto next_folio; } out: f2fs_submit_read_bio(F2FS_I_SB(inode), bio, DATA); - if (ret) { + if (ret && folio_in_bio) { /* Wait bios and clear uptodate. */ folio_lock(folio); folio_clear_uptodate(folio); From 8a2d8a34ef0bb669321ba411df5ee57975919e88 Mon Sep 17 00:00:00 2001 From: Wenjie Qi Date: Wed, 20 May 2026 20:07:05 +0800 Subject: [PATCH 0087/2758] f2fs: pass correct iostat type for single node writes commit fcb05c26c2a67953b420739b85f49386efc9b6c0 upstream. f2fs_write_single_node_folio() takes an io_type argument, but still passes FS_GC_NODE_IO to __write_node_folio() unconditionally. This was harmless while the helper was only used by f2fs_move_node_folio(), whose caller passes FS_GC_NODE_IO. However, commit fe9b8b30b971 ("f2fs: fix inline data not being written to disk in writeback path") made f2fs_inline_data_fiemap() call the helper with FS_NODE_IO for FIEMAP_FLAG_SYNC. Honor the caller supplied io_type so inline-data FIEMAP sync writeback is accounted as normal node IO instead of GC node IO, while the GC path continues to pass FS_GC_NODE_IO explicitly. Cc: stable@kernel.org Fixes: fe9b8b30b971 ("f2fs: fix inline data not being written to disk in writeback path") Signed-off-by: Wenjie Qi Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/node.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 4e5bd9e4cfc32..6a9e77c87d1ec 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1875,7 +1875,7 @@ int f2fs_write_single_node_folio(struct folio *node_folio, int sync_mode, } if (!__write_node_folio(node_folio, false, false, NULL, - &wbc, false, FS_GC_NODE_IO, NULL)) + &wbc, false, io_type, NULL)) err = -EAGAIN; goto release_folio; out_folio: From f5b8b3dd6e8593262726147fb6bac4526b0cb0fb Mon Sep 17 00:00:00 2001 From: Wenjie Qi Date: Wed, 10 Jun 2026 22:37:35 +0800 Subject: [PATCH 0088/2758] f2fs: reject setattr size changes on large folio files commit 242d30bfc0a84b8b5de0a88821b53c9ad7fd31c4 upstream. F2FS large folios are only enabled for immutable non-compressed files. Writable open and writable mmap reject such mappings, but truncate(2) through f2fs_setattr() misses the same guard. If FS_IMMUTABLE_FL is cleared while the inode is still cached, the mapping can keep large-folio support and ATTR_SIZE can change i_size. Reject size changes in that state. Cc: stable@kernel.org Fixes: 05e65c14ea59 ("f2fs: support large folio for immutable non-compressed case") Signed-off-by: Wenjie Qi Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/file.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index fb12c5c9affda..6d750e9d6f328 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1098,6 +1098,8 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, return -EPERM; if ((attr->ia_valid & ATTR_SIZE)) { + if (mapping_large_folio_support(inode->i_mapping)) + return -EOPNOTSUPP; if (!f2fs_is_compress_backend_ready(inode) || IS_DEVICE_ALIASING(inode)) return -EOPNOTSUPP; From 0cc21c1ffe15b4156b0bf744f32fd1faef0b7c73 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 22 May 2026 15:53:29 +0800 Subject: [PATCH 0089/2758] f2fs: fix to do sanity check on f2fs_get_node_folio_ra() commit 8712353ed80f87271d732297567dcdbe4b84e8c7 upstream. kernel BUG at fs/f2fs/file.c:845! Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI CPU: 0 UID: 0 PID: 5336 Comm: syz.0.0 Not tainted syzkaller #0 PREEMPT(full) Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 RIP: 0010:f2fs_do_truncate_blocks+0x1115/0x1140 fs/f2fs/file.c:845 Code: fc fc 90 0f 0b e8 8b 9d 9a fd 90 0f 0b e8 83 9d 9a fd 48 89 df 48 c7 c6 60 d1 1a 8c e8 54 f1 fc fc 90 0f 0b e8 6c 9d 9a fd 90 <0f> 0b e8 64 9d 9a fd 90 0f 0b 90 e9 93 fd ff ff e8 56 9d 9a fd 90 RSP: 0018:ffffc9000e4474c0 EFLAGS: 00010283 RAX: ffffffff842b1d34 RBX: 0000000000000003 RCX: 0000000000100000 RDX: ffffc9000f03a000 RSI: 0000000000035503 RDI: 0000000000035504 RBP: ffffc9000e447608 R08: ffff8880123b0000 R09: 0000000000000002 R10: 00000000fffffffe R11: 0000000000000002 R12: 0000000000000001 R13: 0000000000000000 R14: 1ffff92001c88ea0 R15: 00000000ffff039c FS: 00007f7e02ee36c0(0000) GS:ffff88808c887000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007ff0305c4000 CR3: 0000000012d4c000 CR4: 0000000000352ef0 Call Trace: f2fs_truncate_blocks+0x10a/0x300 fs/f2fs/file.c:882 f2fs_truncate+0x471/0x7c0 fs/f2fs/file.c:940 f2fs_evict_inode+0xa3f/0x1ac0 fs/f2fs/inode.c:907 evict+0x61e/0xb10 fs/inode.c:841 f2fs_fill_super+0x5f43/0x78f0 fs/f2fs/super.c:5224 get_tree_bdev_flags+0x431/0x4f0 fs/super.c:1694 vfs_get_tree+0x92/0x2a0 fs/super.c:1754 fc_mount fs/namespace.c:1193 [inline] do_new_mount_fc fs/namespace.c:3758 [inline] do_new_mount+0x341/0xd30 fs/namespace.c:3834 do_mount fs/namespace.c:4167 [inline] __do_sys_mount fs/namespace.c:4383 [inline] __se_sys_mount+0x31d/0x420 fs/namespace.c:4360 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x15f/0xf80 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f count = ADDRS_PER_PAGE(dn.node_folio, inode); count -= dn.ofs_in_node; f2fs_bug_on(sbi, count < 0); The fuzz test will trigger above bug_on in f2fs. The root cause should be: in the corrupted inode, there is a direct node which has the same ino and nid in its footer, so in f2fs_do_truncate_blocks(), after f2fs_get_dnode_of_data() finds such dnode: 1) ADDRS_PER_PAGE(dn.node_folio, inode) will return 923 2) once dn.ofs_in_node points to addr[923, 1017] Then it will trigger the system panic. Let's introduce NODE_TYPE_NON_IXNODE to indicate current node should not be an inode or xattr node, and then use it in below path to detect inconsistent node chain in inode mapping table: - f2fs_do_truncate_blocks - f2fs_get_dnode_of_data - f2fs_get_node_folio_ra - __get_node_folio - f2fs_sanity_check_node_footer - case NODE_TYPE_NON_IXNODE -> check whether it is inode|xnode Cc: stable@kernel.org Reported-by: syzbot+2488d8d751b27f7ce268@syzkaller.appspotmail.com Closes: https://lore.kernel.org/all/69fa3697.170a0220.59368.0018.GAE@google.com Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/f2fs.h | 1 + fs/f2fs/node.c | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 91f506e7c9cfb..069595fc5e1a0 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1583,6 +1583,7 @@ enum node_type { NODE_TYPE_INODE, NODE_TYPE_XATTR, NODE_TYPE_NON_INODE, + NODE_TYPE_NON_IXNODE, /* non inode and xnode */ }; /* a threshold of maximum elapsed time in critical region to print tracepoint */ diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 6a9e77c87d1ec..01d860e7e169e 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1541,6 +1541,10 @@ int f2fs_sanity_check_node_footer(struct f2fs_sb_info *sbi, if (is_inode) goto out_err; break; + case NODE_TYPE_NON_IXNODE: + if (is_inode || is_xnode) + goto out_err; + break; default: break; } @@ -1634,7 +1638,7 @@ static struct folio *f2fs_get_node_folio_ra(struct folio *parent, int start) struct f2fs_sb_info *sbi = F2FS_F_SB(parent); nid_t nid = get_nid(parent, start, false); - return __get_node_folio(sbi, nid, parent, start, NODE_TYPE_REGULAR); + return __get_node_folio(sbi, nid, parent, start, NODE_TYPE_NON_IXNODE); } static void flush_inline_data(struct f2fs_sb_info *sbi, nid_t ino) From 2e12381d4495dc8b0ff042c6856022b2e359835c Mon Sep 17 00:00:00 2001 From: Wenjie Qi Date: Tue, 26 May 2026 13:35:57 +0800 Subject: [PATCH 0090/2758] f2fs: validate orphan inode entry count commit 846c499a65816d13f1186e3090e825e8bb8bcb8b upstream. f2fs_recover_orphan_inodes() trusts the orphan block entry_count when replaying orphan inodes from the checkpoint pack. A corrupted entry_count larger than F2FS_ORPHANS_PER_BLOCK makes the recovery loop read past the ino[] array and interpret footer or following data as inode numbers. On a crafted image, mounting an unpatched kernel can drive orphan recovery into f2fs_bug_on() and panic the kernel. Validate entry_count before consuming entries so corrupted checkpoint data fails the mount with -EFSCORRUPTED and requests fsck instead. Set ERROR_INCONSISTENT_ORPHAN as well, so the corruption reason can be recorded in the superblock s_errors[] field. This gives fsck a persistent hint even though mount-time orphan recovery failure may leave no chance to persist SBI_NEED_FSCK through a checkpoint. Cc: stable@kernel.org Fixes: 127e670abfa7 ("f2fs: add checkpoint operations") Signed-off-by: Wenjie Qi Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/checkpoint.c | 14 +++++++++++++- include/linux/f2fs_fs.h | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index c00a6b6ebcbd8..064f5b5374237 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -943,6 +943,7 @@ int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi) for (i = 0; i < orphan_blocks; i++) { struct folio *folio; struct f2fs_orphan_block *orphan_blk; + unsigned int entry_count; folio = f2fs_get_meta_folio(sbi, start_blk + i); if (IS_ERR(folio)) { @@ -951,7 +952,18 @@ int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi) } orphan_blk = folio_address(folio); - for (j = 0; j < le32_to_cpu(orphan_blk->entry_count); j++) { + entry_count = le32_to_cpu(orphan_blk->entry_count); + if (entry_count > F2FS_ORPHANS_PER_BLOCK) { + f2fs_err(sbi, "invalid orphan inode entry count %u", + entry_count); + set_sbi_flag(sbi, SBI_NEED_FSCK); + f2fs_handle_error(sbi, ERROR_INCONSISTENT_ORPHAN); + err = -EFSCORRUPTED; + f2fs_folio_put(folio, true); + goto out; + } + + for (j = 0; j < entry_count; j++) { nid_t ino = le32_to_cpu(orphan_blk->ino[j]); err = recover_orphan_inode(sbi, ino); diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 829a59399dacf..bb2b6cd5d5070 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -107,6 +107,7 @@ enum f2fs_error { ERROR_CORRUPTED_XATTR, ERROR_INVALID_NODE_REFERENCE, ERROR_INCONSISTENT_NAT, + ERROR_INCONSISTENT_ORPHAN, ERROR_MAX, }; From 0969926d987bbde9a1aa49da317582ba37095805 Mon Sep 17 00:00:00 2001 From: Wenjie Qi Date: Thu, 21 May 2026 11:16:18 +0800 Subject: [PATCH 0091/2758] f2fs: validate compress cache inode only when enabled commit 5073c66a96a9c23c0c2533ed4ed06e42f9021208 upstream. F2FS_COMPRESS_INO() uses NM_I(sbi)->max_nid as the synthetic inode number for the compressed page cache inode. That inode only exists when the compress_cache mount option is enabled. When compress_cache is disabled, max_nid is outside the valid inode range. A corrupted directory entry that points to ino == max_nid should therefore be rejected by f2fs_check_nid_range(). However, is_meta_ino() currently treats F2FS_COMPRESS_INO() as a meta inode unconditionally, so f2fs_iget() bypasses do_read_inode() and its nid range check, and instantiates a fake internal inode instead. Gate the compressed cache inode case on COMPRESS_CACHE, matching f2fs_init_compress_inode(). With compress_cache disabled, ino == max_nid now follows the normal inode path and is rejected as an out-of-range nid. Cc: stable@kernel.org Fixes: 6ce19aff0b8c ("f2fs: compress: add compress_inode to cache compressed blocks") Signed-off-by: Wenjie Qi Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/inode.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index c6dcda447882f..4c59af4c98bca 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -561,8 +561,13 @@ static int do_read_inode(struct inode *inode) static bool is_meta_ino(struct f2fs_sb_info *sbi, unsigned int ino) { - return ino == F2FS_NODE_INO(sbi) || ino == F2FS_META_INO(sbi) || - ino == F2FS_COMPRESS_INO(sbi); + if (ino == F2FS_NODE_INO(sbi) || ino == F2FS_META_INO(sbi)) + return true; +#ifdef CONFIG_F2FS_FS_COMPRESSION + if (test_opt(sbi, COMPRESS_CACHE) && ino == F2FS_COMPRESS_INO(sbi)) + return true; +#endif + return false; } struct inode *f2fs_iget(struct super_block *sb, unsigned long ino) From a805fec35c201c59643ddcde713bce4051c8ee27 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 21 May 2026 10:15:05 +0800 Subject: [PATCH 0092/2758] f2fs: atomic: fix UAF issue on f2fs_inode_info.atomic_inode commit e0288584baa5dc41df4a829a023c4c1b33fe53d7 upstream. - ioctl(F2FS_IOC_GARBAGE_COLLECT_RANGE) - shrink - f2fs_gc - gc_data_segment - ra_data_block(cow_inode) - mapping = F2FS_I(inode)->atomic_inode->i_mapping : f2fs_is_cow_file(cow_inode) is true - f2fs_evict_inode(atomic_inode) - clear_inode_flag(fi->cow_inode, FI_COW_FILE) - F2FS_I(fi->cow_inode)->atomic_inode = NULL ... - truncate_inode_pages_final(atomic_inode) - f2fs_grab_cache_folio(mapping) : create folio in atomic_inode->mapping - clear_inode(atomic_inode) - BUG_ON(atomic_inode->i_data.nrpages) We need to add a reference on fi->atomic_inode before using its mapping field during garbage collection, otherwise, it will cause UAF issue. Cc: stable@kernel.org Cc: Daeho Jeong Cc: Sunmin Jeong Fixes: 3db1de0e582c ("f2fs: change the current atomic write way") Fixes: f18d00769336 ("f2fs: use meta inode for GC of COW file") Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/gc.c | 50 +++++++++++++++++++++++++++++++++++++++++-------- fs/f2fs/inode.c | 11 ++++++++--- 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index ba93010924c06..74004c2351dd9 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -1220,8 +1220,8 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, static int ra_data_block(struct inode *inode, pgoff_t index) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - struct address_space *mapping = f2fs_is_cow_file(inode) ? - F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping; + struct address_space *mapping = inode->i_mapping; + struct inode *atomic_inode = NULL; struct dnode_of_data dn; struct folio *folio, *efolio; struct f2fs_io_info fio = { @@ -1236,9 +1236,22 @@ static int ra_data_block(struct inode *inode, pgoff_t index) }; int err = 0; + f2fs_down_read(&F2FS_I(inode)->i_sem); + if (f2fs_is_cow_file(inode)) { + atomic_inode = igrab(F2FS_I(inode)->atomic_inode); + if (!atomic_inode) { + f2fs_up_read(&F2FS_I(inode)->i_sem); + return -EBUSY; + } + mapping = atomic_inode->i_mapping; + } + f2fs_up_read(&F2FS_I(inode)->i_sem); + folio = f2fs_grab_cache_folio(mapping, index, true); - if (IS_ERR(folio)) - return PTR_ERR(folio); + if (IS_ERR(folio)) { + err = PTR_ERR(folio); + goto out_iput; + } if (f2fs_lookup_read_extent_cache_block(inode, index, &dn.data_blkaddr)) { @@ -1299,11 +1312,16 @@ static int ra_data_block(struct inode *inode, pgoff_t index) f2fs_update_iostat(sbi, inode, FS_DATA_READ_IO, F2FS_BLKSIZE); f2fs_update_iostat(sbi, NULL, FS_GDATA_READ_IO, F2FS_BLKSIZE); + if (atomic_inode) + iput(atomic_inode); return 0; put_encrypted_page: f2fs_put_page(fio.encrypted_page, true); put_folio: f2fs_folio_put(folio, true); +out_iput: + if (atomic_inode) + iput(atomic_inode); return err; } @@ -1314,8 +1332,8 @@ static int ra_data_block(struct inode *inode, pgoff_t index) static int move_data_block(struct inode *inode, block_t bidx, int gc_type, unsigned int segno, int off) { - struct address_space *mapping = f2fs_is_cow_file(inode) ? - F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping; + struct address_space *mapping = inode->i_mapping; + struct inode *atomic_inode = NULL; struct f2fs_io_info fio = { .sbi = F2FS_I_SB(inode), .ino = inode->i_ino, @@ -1337,10 +1355,23 @@ static int move_data_block(struct inode *inode, block_t bidx, (fio.sbi->gc_mode != GC_URGENT_HIGH) ? CURSEG_ALL_DATA_ATGC : CURSEG_COLD_DATA; + f2fs_down_read(&F2FS_I(inode)->i_sem); + if (f2fs_is_cow_file(inode)) { + atomic_inode = igrab(F2FS_I(inode)->atomic_inode); + if (!atomic_inode) { + f2fs_up_read(&F2FS_I(inode)->i_sem); + return -EBUSY; + } + mapping = atomic_inode->i_mapping; + } + f2fs_up_read(&F2FS_I(inode)->i_sem); + /* do not read out */ folio = f2fs_grab_cache_folio(mapping, bidx, false); - if (IS_ERR(folio)) - return PTR_ERR(folio); + if (IS_ERR(folio)) { + err = PTR_ERR(folio); + goto out_iput; + } if (!check_valid_map(F2FS_I_SB(inode), segno, off)) { err = -ENOENT; @@ -1473,6 +1504,9 @@ static int move_data_block(struct inode *inode, block_t bidx, folio_unlock(folio); folio_end_dropbehind(folio); folio_put(folio); +out_iput: + if (atomic_inode) + iput(atomic_inode); return err; } diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 4c59af4c98bca..e47bce07bc750 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -863,10 +863,15 @@ void f2fs_evict_inode(struct inode *inode) f2fs_abort_atomic_write(inode, true); if (fi->cow_inode && f2fs_is_cow_file(fi->cow_inode)) { - clear_inode_flag(fi->cow_inode, FI_COW_FILE); - F2FS_I(fi->cow_inode)->atomic_inode = NULL; - iput(fi->cow_inode); + struct inode *cow_inode = fi->cow_inode; + + f2fs_down_write(&F2FS_I(cow_inode)->i_sem); + clear_inode_flag(cow_inode, FI_COW_FILE); + F2FS_I(cow_inode)->atomic_inode = NULL; fi->cow_inode = NULL; + f2fs_up_write(&F2FS_I(cow_inode)->i_sem); + + iput(cow_inode); } trace_f2fs_evict_inode(inode); From 536c7e7482e0a1df0bbfb6d40eb48e1529254a21 Mon Sep 17 00:00:00 2001 From: Sunmin Jeong Date: Mon, 22 Jun 2026 14:28:17 +0900 Subject: [PATCH 0093/2758] f2fs: fix to round down start offset of fallocate for pin file commit 4275b59673eb60b02eec3997816c83f1f4b909c4 upstream. Currently, the length of fallocate for pin file is section-aligned to keep allocated sections from being selected as victims of GC. However, for the case that the start offset of fallocate is not aligned in section, the allocated sections can't be fully utilized. It's because a new section is allocated by f2fs_allocate_pinning_section() after using blks_per_sec blocks regardless of the start offset. As a result, several unexpected dirty segments may be created, including blocks assigned to the pinned file. To address this issue, let's round down the start offset of fallocate to the length of section. The reproducing scenario is as below chunk=$(((2<<20)+4096)) # 2MB + 4KB touch test f2fs_io pinfile set test f2fs_io fallocate 0 0 $chunk test f2fs_io fallocate 0 $chunk $chunk test f2fs_io fallocate 0 $((chunk*2)) $chunk test f2fs_io fiemap 0 $((chunk*3)) test Fiemap: offset = 0 len = 12288 logical addr. physical addr. length flags 0 0000000000000000 000000068c600000 0000000000400000 00001088 1 0000000000400000 000000003d400000 0000000000001000 00001088 2 0000000000401000 00000003eb200000 0000000000200000 00001088 3 0000000000601000 00000005e4200000 0000000000001000 00001088 4 0000000000602000 0000000605400000 0000000000200000 00001089 Cc: stable@vger.kernel.org Fixes: f5a53edcf01e ("f2fs: support aligned pinned file") Reviewed-by: Yunji Kang Reviewed-by: Yeongjin Gil Reviewed-by: Sungjong Seo Signed-off-by: Sunmin Jeong Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/file.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 6d750e9d6f328..ade4fde2ef0eb 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1916,8 +1916,15 @@ static int f2fs_expand_inode_data(struct inode *inode, loff_t offset, if (f2fs_is_pinned_file(inode)) { block_t sec_blks = CAP_BLKS_PER_SEC(sbi); - block_t sec_len = roundup(map.m_len, sec_blks); + block_t sec_len; + if (map.m_lblk % sec_blks) { + map.m_lblk = rounddown(map.m_lblk, sec_blks); + map.m_len = pg_end - map.m_lblk; + if (off_end) + map.m_len++; + } + sec_len = roundup(map.m_len, sec_blks); map.m_len = sec_blks; next_alloc: f2fs_down_write(&sbi->pin_sem); From 16bc237ce3c483b75575abea53cfb639745311ed Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Thu, 11 Jun 2026 23:00:36 -0500 Subject: [PATCH 0094/2758] f2fs: bound i_inline_xattr_size for non-inline-xattr inodes commit 378acf3cf19b6af6cba55e8dd1154c4e1504bae8 upstream. When the flexible_inline_xattr feature is enabled, do_read_inode() loads the on-disk i_inline_xattr_size unconditionally: if (f2fs_sb_has_flexible_inline_xattr(sbi)) fi->i_inline_xattr_size = le16_to_cpu(ri->i_inline_xattr_size); but sanity_check_inode() only range-checks it when the inode also has the FI_INLINE_XATTR flag set. An inode that carries an inline dentry or inline data but not FI_INLINE_XATTR -- the normal layout for an inline directory -- therefore keeps a fully attacker-controlled i_inline_xattr_size from a crafted image. get_inline_xattr_addrs() returns that value with no flag gating, so it feeds the inode geometry: MAX_INLINE_DATA() = 4 * (CUR_ADDRS_PER_INODE - i_inline_xattr_size - 1) NR_INLINE_DENTRY() = MAX_INLINE_DATA() * BITS_PER_BYTE / (...) addrs_per_page() = CUR_ADDRS_PER_INODE - i_inline_xattr_size A large i_inline_xattr_size drives MAX_INLINE_DATA() and NR_INLINE_DENTRY() negative, so make_dentry_ptr_inline() sets d->max (int) to a negative value. The inline directory walk then compares an unsigned long bit_pos against that negative d->max, which is promoted to a huge unsigned bound, and reads far past the inline area: while (bit_pos < d->max) /* fs/f2fs/dir.c */ ... test_bit_le(bit_pos, d->bitmap) / d->dentry[bit_pos] ... Mounting a crafted image and reading such a directory triggers an out-of-bounds read in f2fs_fill_dentries(); the same underflow also corrupts ADDRS_PER_INODE for regular files. Validate i_inline_xattr_size against MAX_INLINE_XATTR_SIZE whenever the flexible_inline_xattr feature is enabled -- i.e. whenever the value is loaded from disk and consumed -- and keep the lower MIN_INLINE_XATTR_SIZE bound gated on inodes that actually carry an inline xattr, so legitimate inodes with i_inline_xattr_size == 0 are still accepted. Cc: stable@vger.kernel.org Fixes: 6afc662e68b5 ("f2fs: support flexible inline xattr size") Signed-off-by: Bryam Vargas Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/inode.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index e47bce07bc750..5f0cf81e49f08 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -324,9 +324,9 @@ static bool sanity_check_inode(struct inode *inode, struct folio *node_folio) } if (f2fs_sb_has_flexible_inline_xattr(sbi) && - f2fs_has_inline_xattr(inode) && - (fi->i_inline_xattr_size < MIN_INLINE_XATTR_SIZE || - fi->i_inline_xattr_size > MAX_INLINE_XATTR_SIZE)) { + (fi->i_inline_xattr_size > MAX_INLINE_XATTR_SIZE || + (f2fs_has_inline_xattr(inode) && + fi->i_inline_xattr_size < MIN_INLINE_XATTR_SIZE))) { f2fs_warn(sbi, "%s: inode (ino=%llx) has corrupted i_inline_xattr_size: %d, min: %zu, max: %lu", __func__, inode->i_ino, fi->i_inline_xattr_size, MIN_INLINE_XATTR_SIZE, MAX_INLINE_XATTR_SIZE); From 5d8a39649947a4e86c8fbc682d7fc0041b8d109a Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Mon, 15 Jun 2026 15:19:54 +0800 Subject: [PATCH 0095/2758] f2fs: validate ACL entry sizes in f2fs_acl_from_disk() commit c4810ada31e80cbe4011467c4f3b1e93f94134f3 upstream. f2fs_acl_count() only validates the aggregate ACL xattr length. A malformed ACL can still place ACL_USER or ACL_GROUP in a slot that only contains struct f2fs_acl_entry_short bytes, and f2fs_acl_from_disk() then reads entry->e_id before verifying that a full entry fits. Require a short entry before reading e_tag and e_perm, and require a full entry before reading e_id for ACL_USER and ACL_GROUP. Return -EFSCORRUPTED from these new truncated-entry checks, while keeping the pre-existing -EINVAL paths unchanged. Validation reproduced this kernel report: KASAN slab-out-of-bounds in __f2fs_get_acl+0x6fb/0x7e0 RIP: 0033:0x7f4b835ea7aa The buggy address belongs to the object at ffff888114589960 which belongs to the cache kmalloc-8 of size 8 The buggy address is located 0 bytes to the right of allocated 8-byte region [ffff888114589960, ffff888114589968) Read of size 4 Call trace: dump_stack_lvl+0x66/0xa0 (?:?) print_report+0xce/0x630 (?:?) __f2fs_get_acl+0x6fb/0x7e0 (fs/f2fs/acl.c:169) srso_alias_return_thunk+0x5/0xfbef5 (?:?) __virt_addr_valid+0x224/0x430 (?:?) kasan_report+0xe0/0x110 (?:?) __f2fs_get_acl+0x5/0x7e0 (fs/f2fs/acl.c:169) __get_acl+0x281/0x380 (?:?) vfs_get_acl+0x10b/0x190 (?:?) do_get_acl+0x2a/0x410 (?:?) do_get_acl+0x9/0x410 (?:?) do_getxattr+0xe8/0x260 (?:?) filename_getxattr+0xd1/0x140 (?:?) do_getname+0x2d/0x2d0 (?:?) path_getxattrat+0x16c/0x200 (?:?) lock_release+0xc8/0x290 (?:?) cgroup_update_frozen+0x9d/0x320 (?:?) lockdep_hardirqs_on_prepare+0xea/0x1a0 (?:?) trace_hardirqs_on+0x1a/0x170 (?:?) _raw_spin_unlock_irq+0x28/0x50 (?:?) do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f (?:?) Cc: stable@kernel.org Fixes: af48b85b8cd3 ("f2fs: add xattr and acl functionalities") Assisted-by: Codex:gpt-5.5 Signed-off-by: Zhang Cen Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/acl.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c index fa8d81a30fb91..d3253549173e6 100644 --- a/fs/f2fs/acl.c +++ b/fs/f2fs/acl.c @@ -47,6 +47,7 @@ static inline int f2fs_acl_count(size_t size) static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size) { int i, count; + int err = -EINVAL; struct posix_acl *acl; struct f2fs_acl_header *hdr = (struct f2fs_acl_header *)value; struct f2fs_acl_entry *entry = (struct f2fs_acl_entry *)(hdr + 1); @@ -70,8 +71,11 @@ static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size) for (i = 0; i < count; i++) { - if ((char *)entry > end) + if (unlikely((char *)entry + + sizeof(struct f2fs_acl_entry_short) > end)) { + err = -EFSCORRUPTED; goto fail; + } acl->a_entries[i].e_tag = le16_to_cpu(entry->e_tag); acl->a_entries[i].e_perm = le16_to_cpu(entry->e_perm); @@ -86,6 +90,11 @@ static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size) break; case ACL_USER: + if (unlikely((char *)entry + + sizeof(struct f2fs_acl_entry) > end)) { + err = -EFSCORRUPTED; + goto fail; + } acl->a_entries[i].e_uid = make_kuid(&init_user_ns, le32_to_cpu(entry->e_id)); @@ -93,6 +102,11 @@ static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size) sizeof(struct f2fs_acl_entry)); break; case ACL_GROUP: + if (unlikely((char *)entry + + sizeof(struct f2fs_acl_entry) > end)) { + err = -EFSCORRUPTED; + goto fail; + } acl->a_entries[i].e_gid = make_kgid(&init_user_ns, le32_to_cpu(entry->e_id)); @@ -108,7 +122,7 @@ static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size) return acl; fail: posix_acl_release(acl); - return ERR_PTR(-EINVAL); + return ERR_PTR(err); } static void *f2fs_acl_to_disk(struct f2fs_sb_info *sbi, From 6e035dae44154af4dd7bdb8ef7a1118c0b5f17b6 Mon Sep 17 00:00:00 2001 From: Zhaoyang Huang Date: Mon, 8 Jun 2026 17:09:39 +0800 Subject: [PATCH 0096/2758] Revert "f2fs: remove non-uptodate folio from the page cache in move_data_block" commit ccaba785821970f422c47770331c7e3271763f17 upstream. This reverts commit 9609dd704725a40cd63d915f2ab6c44248a44598. The kernel panics are keeping to be reported especially when the f2fs partition get almost full. By investigation, we find that the reason is one f2fs page got freed to buddy without being deleted from LRU and the root cause is the race happened in [2] which is enrolled by this commit. There are 3 race processes in this scenario, please find below for their main activities. The changed code in move_data_block() lets the GC path evict the tail-end folio from the page cache through folio_end_dropbehind(). Once folio_unmap_invalidate() removes the folio from mapping->i_pages, the page-cache references for all pages in the folio are dropped. The folio is then kept alive only by temporary external references, which allows a later split to operate on a folio whose subpages are no longer protected by page-cache references. After the page-cache references are gone, split_folio_to_order() can split the big folio into individual pages and put the resulting subpages back on the LRU. For tail pages beyond EOF, split removes them from the page cache and drops their page-cache references. A tail page can then remain on the LRU with PG_lru set while holding only the split caller's temporary reference. When free_folio_and_swap_cache() drops that final reference, the page enters the final folio_put() release path. In parallel, folio_isolate_lru() can observe the same tail page with a non-zero refcount and PG_lru set. It clears PG_lru before taking its own reference. If this races with the final folio_put() from the split path, __folio_put() sees PG_lru already cleared and skips lruvec_del_folio(). The page is then freed back to the allocator while its lru links are still present in the LRU list. A later LRU operation on a neighboring page detects the stale link and reports list corruption. [1] [ 22.486082] list_del corruption. next->prev should be fffffffec10e0ac8, but was dead000000000122. (next=fffffffec10e0a88) [ 22.486130] ------------[ cut here ]------------ [ 22.486134] kernel BUG at lib/list_debug.c:67! [ 22.486141] Internal error: Oops - BUG: 00000000f2000800 [#1] SMP [ 22.488502] Tainted: [W]=WARN, [O]=OOT_MODULE [ 22.488506] Hardware name: Spreadtrum UMS9230 1H10 SoC (DT) [ 22.488511] pstate: 604000c5 (nZCv daIF +PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 22.488517] pc : __list_del_entry_valid_or_report+0x14c/0x154 [ 22.488531] lr : __list_del_entry_valid_or_report+0x14c/0x154 [ 22.488539] sp : ffffffc08006b830 [ 22.488542] x29: ffffffc08006b868 x28: 0000000000003020 x27: 0000000000000000 [ 22.488553] x26: 0000000000000000 x25: 0000000000000004 x24: fffffffec10e0ac0 [ 22.488564] x23: 00000000000000e8 x22: 0000000000000024 x21: dead000000000122 [ 22.488574] x20: fffffffec10e0a88 x19: fffffffec10e0ac8 x18: ffffffc080061060 [ 22.488585] x17: 20747562202c3863 x16: 6130653031636566 x15: 0000000000000058 [ 22.488595] x14: 0000000000000004 x13: ffffff80f91e0000 x12: 0000000000000003 [ 22.488605] x11: 0000000000000003 x10: 0000000000000001 x9 : ffe85721f0e25f00 [ 22.488615] x8 : ffe85721f0e25f00 x7 : 0000000000000000 x6 : 6c65645f7473696c [ 22.488625] x5 : ffffffed39b23026 x4 : 0000000000000000 x3 : 0000000000000010 [ 22.488636] x2 : 0000000000000000 x1 : 0000000000000000 x0 : 000000000000006d [ 22.488647] Call trace: [ 22.488651] __list_del_entry_valid_or_report+0x14c/0x154 (P) [ 22.488661] __folio_put+0x2bc/0x434 [ 22.488670] folio_put+0x28/0x58 [ 22.488678] do_garbage_collect+0x1a34/0x2584 [ 22.488689] f2fs_gc+0x230/0x9b4 [ 22.488697] f2fs_fallocate+0xb90/0xdf4 [ 22.488706] vfs_fallocate+0x1b4/0x2bc [ 22.488716] __arm64_sys_fallocate+0x44/0x78 [ 22.488725] invoke_syscall+0x58/0xe4 [ 22.488732] do_el0_svc+0x48/0xdc [ 22.488739] el0_svc+0x3c/0x98 [ 22.488747] el0t_64_sync_handler+0x20/0x130 [ 22.488754] el0t_64_sync+0x1c4/0x1c8 [2] CPU0 (f2fs GC) CPU1 (split_folio_to_order) CPU2 (folio_isolate_lru) F: pagecache refs = n F: extra refs = GC + split F: PG_lru set move_data_block() folio = f2fs_grab_cache_folio(F) ... __folio_set_dropbehind(F) folio_unlock(F) folio_end_dropbehind(F) folio_unmap_invalidate(F) __filemap_remove_folio(F) folio_put_refs(F, n) folio_put(F) split_folio_to_order(F) folio_ref_freeze(F, 1) ... lru_add_split_folio(T) list_add_tail(&T->lru, &F->lru) folio_set_lru(T) __filemap_remove_folio(T) folio_put_refs(T, 1) /* T refcount == 1, PageLRU set */ folio_isolate_lru(T) folio_test_clear_lru(T) free_folio_and_swap_cache(T) folio_put(T) /* refcount: 1 -> 0 */ __folio_put(T) __page_cache_release(T) folio_test_lru(T) == false /* skip lruvec_del_folio(T) */ free_frozen_pages(T) folio_get(T) lruvec_del_folio(T) later: list_del(adjacent->lru) next == &T->lru next->prev == LIST_POISON / PCP freelist BUG Cc: stable@vger.kernel.org Fixes: 9609dd704725 ("f2fs: remove non-uptodate folio from the page cache in move_data_block") Signed-off-by: Zhaoyang Huang Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/gc.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 74004c2351dd9..11228349b0035 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -1499,11 +1499,7 @@ static int move_data_block(struct inode *inode, block_t bidx, put_out: f2fs_put_dnode(&dn); out: - if (!folio_test_uptodate(folio)) - __folio_set_dropbehind(folio); - folio_unlock(folio); - folio_end_dropbehind(folio); - folio_put(folio); + f2fs_folio_put(folio, true); out_iput: if (atomic_inode) iput(atomic_inode); From edf12cbeeeabe799bd2ee21fdb5c336cce6fbad7 Mon Sep 17 00:00:00 2001 From: Yongpeng Yang Date: Mon, 27 Apr 2026 21:10:51 +0800 Subject: [PATCH 0097/2758] f2fs: fix incorrect FI_NO_EXTENT handling in __destroy_extent_node() commit 1f70ddb28a3c71df124da5fa4040c808116d6bb9 upstream. When __destroy_extent_node() sets the inode flag FI_NO_EXTENT, it does not reset the length of the largest extent to 0 and update the inode folio. Since modifications to the extent tree are disallowed afterward, the cached largest extent may become stale. This can trigger the following error in xfstests generic/388: F2FS-fs (dm-0): sanity_check_extent_cache: inode (ino=1761) extent info [220057, 57, 6] is incorrect, run fsck to fix In the f2fs_drop_inode path, __destroy_extent_node() does not need to guarantee that et->node_cnt is 0, because concurrency with writeback is expected in this path, and writeback may update the extent cache. This patch reverts commit ed78aeebef05 ("f2fs: fix node_cnt race between extent node destroy and writeback"), and remove the unnecessary zero check of et->node_cnt. Fixes: ed78aeebef05 ("f2fs: fix node_cnt race between extent node destroy and writeback") Cc: stable@vger.kernel.org Reported-by: Chao Yu Suggested-by: Chao Yu Signed-off-by: Yongpeng Yang Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/extent_cache.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index d2e006420f040..61f6b97143663 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -119,10 +119,9 @@ static bool __may_extent_tree(struct inode *inode, enum extent_type type) if (!__init_may_extent_tree(inode, type)) return false; - if (is_inode_flag_set(inode, FI_NO_EXTENT)) - return false; - if (type == EX_READ) { + if (is_inode_flag_set(inode, FI_NO_EXTENT)) + return false; if (is_inode_flag_set(inode, FI_COMPRESSED_FILE) && !f2fs_sb_has_readonly(F2FS_I_SB(inode))) return false; @@ -645,14 +644,10 @@ static unsigned int __destroy_extent_node(struct inode *inode, while (atomic_read(&et->node_cnt)) { write_lock(&et->lock); - if (!is_inode_flag_set(inode, FI_NO_EXTENT)) - set_inode_flag(inode, FI_NO_EXTENT); node_cnt += __free_extent_tree(sbi, et, nr_shrink); write_unlock(&et->lock); } - f2fs_bug_on(sbi, atomic_read(&et->node_cnt)); - return node_cnt; } @@ -691,12 +686,12 @@ static void __update_extent_tree_range(struct inode *inode, write_lock(&et->lock); - if (is_inode_flag_set(inode, FI_NO_EXTENT)) { - write_unlock(&et->lock); - return; - } - if (type == EX_READ) { + if (is_inode_flag_set(inode, FI_NO_EXTENT)) { + write_unlock(&et->lock); + return; + } + prev = et->largest; dei.len = 0; From d52dbbcad61d9f420d2471864f8f62202fd5fe71 Mon Sep 17 00:00:00 2001 From: Wenjie Qi Date: Wed, 27 May 2026 20:06:28 +0800 Subject: [PATCH 0098/2758] f2fs: keep atomic write retry from zeroing original data commit 6d874b65aadce56ac78f76129dbcfc2599b638f8 upstream. A partial atomic write reserves a block in the COW inode before reading the original data page for the untouched bytes in that page. If that read fails, write_begin returns an error but leaves the COW inode entry as NEW_ADDR. A retry of the same partial write then finds the COW entry, treats it as existing COW data, and f2fs_write_begin() zeroes the whole folio because blkaddr is NEW_ADDR. If the retry is committed, the bytes outside the retried write range are committed as zeroes instead of preserving the original file contents. Only use the COW inode as the read source when it already has a real data block. If the COW entry is still NEW_ADDR, treat it as a reservation to reuse: keep reading the old data from the original inode and avoid reserving or accounting the same atomic block again. Cc: stable@kernel.org Fixes: 3db1de0e582c ("f2fs: change the current atomic write way") Signed-off-by: Wenjie Qi Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/data.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 836edb7899dec..380afd56cc8bc 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -3828,6 +3828,7 @@ static int prepare_atomic_write_begin(struct f2fs_sb_info *sbi, pgoff_t index = folio->index; int err = 0; block_t ori_blk_addr = NULL_ADDR; + bool cow_has_reserved_block = false; /* If pos is beyond the end of file, reserve a new block in COW inode */ if ((pos & PAGE_MASK) >= i_size_read(inode)) @@ -3837,9 +3838,11 @@ static int prepare_atomic_write_begin(struct f2fs_sb_info *sbi, err = __find_data_block(cow_inode, index, blk_addr); if (err) { return err; - } else if (*blk_addr != NULL_ADDR) { + } else if (__is_valid_data_blkaddr(*blk_addr)) { *use_cow = true; return 0; + } else if (*blk_addr == NEW_ADDR) { + cow_has_reserved_block = true; } if (is_inode_flag_set(inode, FI_ATOMIC_REPLACE)) @@ -3852,10 +3855,13 @@ static int prepare_atomic_write_begin(struct f2fs_sb_info *sbi, reserve_block: /* Finally, we should reserve a new block in COW inode for the update */ - err = __reserve_data_block(cow_inode, index, blk_addr, node_changed); - if (err) - return err; - inc_atomic_write_cnt(inode); + if (!cow_has_reserved_block) { + err = __reserve_data_block(cow_inode, index, blk_addr, + node_changed); + if (err) + return err; + inc_atomic_write_cnt(inode); + } if (ori_blk_addr != NULL_ADDR) *blk_addr = ori_blk_addr; From a92332f32a8d31a7eee47b1dc1d751cb3319908f Mon Sep 17 00:00:00 2001 From: Mikhail Lobanov Date: Mon, 15 Jun 2026 14:36:13 +0300 Subject: [PATCH 0099/2758] f2fs: read COW data with the original inode during atomic write commit a41075acde0124d2f8a5f563068a5d63e8ffd57b upstream. When updating an atomic-write file, f2fs_write_begin() may read the previously written data back from the COW inode: prepare_atomic_write_begin() locates the block in the COW inode and sets use_cow, and the read bio is then built with the COW inode: f2fs_submit_page_read(use_cow ? F2FS_I(inode)->cow_inode : inode, ...); and f2fs_grab_read_bio() decides whether to schedule fs-layer decryption (STEP_DECRYPT) for the bio based on that inode via fscrypt_inode_uses_fs_layer_crypto(). However, the folio being filled belongs to the original inode (folio->mapping->host == inode), and the data stored in the COW block was encrypted (or left as plaintext) using the original inode's context, not the COW inode's -- see f2fs_encrypt_one_page(), which keys off fio->page->mapping->host. fscrypt_decrypt_pagecache_blocks() likewise operates on folio->mapping->host. The COW inode is created as a tmpfile in the parent directory and inherits its encryption policy from there. With test_dummy_encryption the newly created COW inode gets the dummy policy and becomes encrypted, while a pre-existing regular file -- created before the policy applied, e.g. already present in the on-disk image -- stays unencrypted. The read path then sets STEP_DECRYPT based on the encrypted COW inode and calls fscrypt_decrypt_pagecache_blocks() on a folio whose host (the unencrypted original inode) has a NULL ->i_crypt_info, dereferencing it: Oops: general protection fault, probably for non-canonical address ... KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f] RIP: 0010:fscrypt_decrypt_pagecache_blocks+0xa0/0x310 Workqueue: f2fs_post_read_wq f2fs_post_read_work Call Trace: fscrypt_decrypt_bio+0x1eb/0x340 f2fs_post_read_work+0xba/0x140 process_one_work+0x91c/0x1a40 worker_thread+0x677/0xe90 kthread+0x2bc/0x3a0 The COW inode is only needed to locate the on-disk block, and that block address is already resolved into @blkaddr by prepare_atomic_write_begin() via __find_data_block(cow_inode, ...); f2fs_submit_page_read() then reads from that physical @blkaddr directly, so the inode argument only selects the post-read crypto context, not which block is fetched. Reading with @inode therefore returns the same (latest, not-yet-committed) COW data, while making both the fs-layer decryption decision and the inline crypto path use the correct (original inode's) key. With the COW inode no longer used at the read site, the use_cow flag has no remaining consumer; drop it from f2fs_write_begin() and prepare_atomic_write_begin(). Fixes: 591fc34e1f98 ("f2fs: use cow inode data when updating atomic write") Cc: stable@vger.kernel.org Signed-off-by: Mikhail Lobanov Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/data.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 380afd56cc8bc..e24b74859427c 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -3821,7 +3821,7 @@ static int __reserve_data_block(struct inode *inode, pgoff_t index, static int prepare_atomic_write_begin(struct f2fs_sb_info *sbi, struct folio *folio, loff_t pos, unsigned int len, - block_t *blk_addr, bool *node_changed, bool *use_cow) + block_t *blk_addr, bool *node_changed) { struct inode *inode = folio->mapping->host; struct inode *cow_inode = F2FS_I(inode)->cow_inode; @@ -3836,14 +3836,14 @@ static int prepare_atomic_write_begin(struct f2fs_sb_info *sbi, /* Look for the block in COW inode first */ err = __find_data_block(cow_inode, index, blk_addr); - if (err) { + if (err) return err; - } else if (__is_valid_data_blkaddr(*blk_addr)) { - *use_cow = true; + + if (__is_valid_data_blkaddr(*blk_addr)) return 0; - } else if (*blk_addr == NEW_ADDR) { + + if (*blk_addr == NEW_ADDR) cow_has_reserved_block = true; - } if (is_inode_flag_set(inode, FI_ATOMIC_REPLACE)) goto reserve_block; @@ -3878,7 +3878,6 @@ static int f2fs_write_begin(const struct kiocb *iocb, struct folio *folio; pgoff_t index = pos >> PAGE_SHIFT; bool need_balance = false; - bool use_cow = false; block_t blkaddr = NULL_ADDR; int err = 0; @@ -3941,7 +3940,7 @@ static int f2fs_write_begin(const struct kiocb *iocb, if (f2fs_is_atomic_file(inode)) err = prepare_atomic_write_begin(sbi, folio, pos, len, - &blkaddr, &need_balance, &use_cow); + &blkaddr, &need_balance); else err = prepare_write_begin(sbi, folio, pos, len, &blkaddr, &need_balance); @@ -3981,8 +3980,15 @@ static int f2fs_write_begin(const struct kiocb *iocb, err = -EFSCORRUPTED; goto put_folio; } - f2fs_submit_page_read(use_cow ? F2FS_I(inode)->cow_inode : - inode, + /* + * Although the block may be stored in the COW inode, the folio + * belongs to @inode and its data was encrypted (or not) using + * @inode's context (see f2fs_encrypt_one_page()). Read with + * @inode so the post-read decryption decision matches the + * folio's owner; otherwise an unencrypted @inode whose COW inode + * is encrypted hits a NULL ->i_crypt_info on decryption. + */ + f2fs_submit_page_read(inode, NULL, /* can't write to fsverity files */ folio, blkaddr, 0, true); From 717f721eb67d2dacd3ed5f7495aef2f442e84ce4 Mon Sep 17 00:00:00 2001 From: Denis Arefev Date: Thu, 21 May 2026 10:28:56 +0300 Subject: [PATCH 0100/2758] block: Avoid mounting the bdev pseudo-filesystem in userspace commit f73aa66dffcb8e61e78f01b56163ec16a15d06d2 upstream. The bdev pseudo-filesystem is an internal kernel filesystem with which userspace should not interfere. Unregister it so that userspace cannot even attempt to mount it. This fixes a bug [1] that occurs when attempting to access files, because the system call move_mount() uses pointers declared in the inode_operations structure, which for the bdev pseudo-filesystem are always equal to 0. `inode->i_op = &empty_iops;` [1] BUG: kernel NULL pointer dereference, address: 0000000000000000 #PF: supervisor instruction fetch in kernel mode #PF: error_code(0x0010) - not-present page PGD 23380067 P4D 23380067 PUD 23381067 PMD 0 Oops: 0010 [#1] PREEMPT SMP KASAN NOPTI CPU: 2 PID: 17125 Comm: syz-executor.0 Not tainted 6.1.155-syzkaller-00350-g84221fde2681 #0 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014 RIP: 0010:0x0 Call Trace: lookup_open.isra.0+0x700/0x1180 fs/namei.c:3460 open_last_lookups fs/namei.c:3550 [inline] path_openat+0x953/0x2700 fs/namei.c:3780 do_filp_open+0x1c5/0x410 fs/namei.c:3810 do_sys_openat2+0x171/0x4d0 fs/open.c:1318 do_sys_open fs/open.c:1334 [inline] __do_sys_openat fs/open.c:1350 [inline] __se_sys_openat fs/open.c:1345 [inline] __x64_sys_openat+0x13c/0x1f0 fs/open.c:1345 do_syscall_x64 arch/x86/entry/common.c:51 [inline] do_syscall_64+0x35/0x80 arch/x86/entry/common.c:81 entry_SYSCALL_64_after_hwframe+0x6e/0xd8 Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Link: https://lore.kernel.org/all/20131010004732.GJ13318@ZenIV.linux.org.uk/T/# Cc: stable@vger.kernel.org Signed-off-by: Denis Arefev Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260521072857.5078-1-arefev@swemel.ru Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/bdev.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/block/bdev.c b/block/bdev.c index bb0ffa3bb4dfb..107ac9eaac7f1 100644 --- a/block/bdev.c +++ b/block/bdev.c @@ -446,15 +446,10 @@ EXPORT_SYMBOL_GPL(blockdev_superblock); void __init bdev_cache_init(void) { - int err; - bdev_cachep = kmem_cache_create("bdev_cache", sizeof(struct bdev_inode), 0, (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT| SLAB_ACCOUNT|SLAB_PANIC), init_once); - err = register_filesystem(&bd_type); - if (err) - panic("Cannot register bdev pseudo-fs"); blockdev_mnt = kern_mount(&bd_type); if (IS_ERR(blockdev_mnt)) panic("Cannot create bdev pseudo-fs"); From 70df4de46577fab5e25418f014583155a147c902 Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Wed, 3 Jun 2026 18:53:16 +0800 Subject: [PATCH 0101/2758] bpf: use kvfree() for replaced sysctl write buffer commit 4c21b5927d4364bfe7365f2700da5fea0ed0d004 upstream. proc_sys_call_handler() allocates its temporary sysctl buffer with kvzalloc() and passes it to __cgroup_bpf_run_filter_sysctl(). Since kvzalloc() may fall back to vmalloc() for large allocations, freeing that buffer with kfree() is wrong and can corrupt memory. Use kvfree() to safely handle both kmalloc and kvzalloc()/vmalloc allocations. The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1-rc5. Reproduced the bug based on v7.1-rc4 in a QEMU x86_64 guest booted with KASAN and CONFIG_FAILSLAB enabled. To exercise the replacement path, the test tree also included the accompanying fix for the stale ret == 1 check in __cgroup_bpf_run_filter_sysctl(). The reproducer confines failslab injections to the proc_sys_call_handler() range, uses stacktrace-depth=32, and injects fail-nth=1 while writing 8191 bytes to /proc/sys/kernel/domainname from a task in the target cgroup. Under that setup, fail-nth=1 triggered the fault: BUG: unable to handle page fault for address: ffffeb0200024d48 #PF: supervisor read access in kernel mode #PF: error_code(0x0000) - not-present page PGD 0 P4D 0 Oops: Oops: 0000 SMP KASAN NOPTI CPU: 2 UID: 0 PID: 209 Comm: repro_proc_sys_ Not tainted 7.1.0-rc4-00686-g97625979a5d4 PREEMPT(lazy) Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.15.0-1 04/01/2014 RIP: 0010:kfree+0x6e/0x510 ... Call Trace: ? __cgroup_bpf_run_filter_sysctl+0x626/0xc30 __cgroup_bpf_run_filter_sysctl+0x74d/0xc30 ? __pfx___cgroup_bpf_run_filter_sysctl+0x10/0x10 ? srso_return_thunk+0x5/0x5f ? __kvmalloc_node_noprof+0x345/0x870 ? proc_sys_call_handler+0x250/0x480 ? srso_return_thunk+0x5/0x5f proc_sys_call_handler+0x3a2/0x480 ? __pfx_proc_sys_call_handler+0x10/0x10 ? srso_return_thunk+0x5/0x5f ? selinux_file_permission+0x39f/0x500 ? srso_return_thunk+0x5/0x5f ? lock_is_held_type+0x9e/0x120 vfs_write+0x98e/0x1000 ... With this fix applied on top of the same test setup, rerunning the reproducer with fail-nth=1 yields no corresponding Oops reports. Fixes: 4508943794ef ("proc: use kvzalloc for our kernel buffer") Cc: stable@vger.kernel.org Reviewed-by: Emil Tsalapatis Reviewed-by: Jiayuan Chen Acked-by: Yonghong Song Signed-off-by: Zilin Guan Signed-off-by: Dawei Feng Link: https://lore.kernel.org/r/20260603105317.944304-3-dawei.feng@seu.edu.cn Signed-off-by: Alexei Starovoitov Signed-off-by: Greg Kroah-Hartman --- kernel/bpf/cgroup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index 876f6a81a9b61..faadcfb9b5e5b 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -1936,7 +1936,7 @@ int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head, kfree(ctx.cur_val); if (ret == 1 && ctx.new_updated) { - kfree(*buf); + kvfree(*buf); *buf = ctx.new_val; *pcount = ctx.new_len; } else { From 07c245bc39f94481fd75ff1ed54f7ab97111f3dd Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Wed, 6 May 2026 23:42:27 +0100 Subject: [PATCH 0102/2758] MIPS: DEC: Prevent initial console buffer from landing in XKPHYS commit 7fb13fd35110ebe95eb053faf79d018f51144d85 upstream. In 64-bit configurations calling the initial console output handler from a kernel thread other than the initial one will result in a situation where the stack has been placed in the XKPHYS 64-bit memory segment and consequently so has been the buffer allocated there that is used as the argument corresponding to the `%s' output conversion specifier for the firmware's printf() entry point. This 64-bit address will then be truncated by 32-bit firmware, resulting in an attempt to access the wrong memory location, which in turn will cause all kinds of unpredictable behaviour, such as a kernel crash: Console: colour dummy device 160x64 Calibrating delay loop... 49.36 BogoMIPS (lpj=192512) pid_max: default: 32768 minimum: 301 CPU 0 Unable to handle kernel paging request at virtual address 000000000203bd00, epc == ffffffffbfc08364, ra == ffffffffbfc08800 Oops[#1]: CPU: 0 PID: 0 Comm: swapper Not tainted 5.18.0-rc2-00254-gfb649bda6f56-dirty #121 $ 0 : 0000000000000000 0000000000000001 0000000000000023 ffffffff80684ba0 $ 4 : 000000000203bd00 ffffffffbfc0f3b4 ffffffffffffffff 0000000000000073 $ 8 : 0a303d7469000000 0000000000000000 0000000000000073 ffffffffbfc0f473 $12 : 0000000000000002 0000000000000000 ffffffff80684c1c 0000000000000000 $16 : 0000000000000000 ffffffff80596dc9 0000000000000000 ffffffffbfc09240 $20 : ffffffff80684c40 ffffffffbfc0f400 000000000000002d 000000000000002b $24 : ffffffffffffffbf 000000000203bd00 $28 : ffffffff805f0000 ffffffff80684b58 0000000000000030 ffffffffbfc08800 Hi : 0000000000000000 Lo : 0000000000000aa8 epc : ffffffffbfc08364 0xffffffffbfc08364 ra : ffffffffbfc08800 0xffffffffbfc08800 Status: 140120e2 KX SX UX KERNEL EXL Cause : 00000008 (ExcCode 02) BadVA : 000000000203bd00 PrId : 00000430 (R4000SC) Modules linked in: Process swapper (pid: 0, threadinfo=(____ptrval____), task=(____ptrval____), tls=0000000000000000) Stack : 0000000000000000 0000000000000000 0000000000000000 0000004d0000004d 80684cc0806a2a40 80596dc80000004d 8061000000000000 bfc0850c80684c38 0000000000000000 000000000203bd00 0000000000000000 0000000000000000 0000000000000000 00000000bfc0f3b4 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000002500000000 0000000000000000 0000000000000000 802c1a7400000000 0203bd0080596dc8 0203bd4d69000000 6c61632000000018 5f746567646e6172 6c616320625f6d6f 5f736e5f6d6f7266 206361323778302b 303d74696e726320 806a0a38806b0000 806a0a38806b0000 00000000806b0000 80683c58806b0000 ... Call Trace: Code: a082ffff 03e00008 00601021 <80820000> 00001821 10400005 24840001 80820000 24630001 ---[ end trace 0000000000000000 ]--- Kernel panic - not syncing: Fatal exception in interrupt KN04 V2.1k (PC: 0xa0026768, SP: 0x806848e8) >> In this case the pointer in $4 was truncated from 0x980000000203bd00 to 0x000000000203bd00. This may happen when no final console driver has been enabled in the configuration and consequently the initial console continues being used late into bootstrap or with an upcoming change that will switch the zs driver to use a platform device, which in turn will make the console handover happen only after other kernel threads have already been started. Fix the issue by making the buffer static and initdata, and therefore placed in the CKSEG0 32-bit compatibility segment, observing that the console output handler is called with the console lock held, implying no need for this code to be reentrant. Add an assertion to verify the buffer actually has been placed in a compatibility segment. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Maciej W. Rozycki Cc: stable@vger.kernel.org # v2.6.12+ Signed-off-by: Thomas Bogendoerfer Signed-off-by: Greg Kroah-Hartman --- arch/mips/dec/prom/console.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/mips/dec/prom/console.c b/arch/mips/dec/prom/console.c index 31a8441d84314..b4f0dba3fa20e 100644 --- a/arch/mips/dec/prom/console.c +++ b/arch/mips/dec/prom/console.c @@ -2,8 +2,9 @@ /* * DECstation PROM-based early console support. * - * Copyright (C) 2004, 2007 Maciej W. Rozycki + * Copyright (C) 2004, 2007, 2026 Maciej W. Rozycki */ +#include #include #include #include @@ -14,9 +15,11 @@ static void __init prom_console_write(struct console *con, const char *s, unsigned int c) { - char buf[81]; + static char buf[81] __initdata = { 0 }; unsigned int chunk = sizeof(buf) - 1; + BUG_ON((long)buf != (int)(long)buf); + while (c > 0) { if (chunk > c) chunk = c; From 708b97e792945d3e4653939fd3405d71a61ad065 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 22 Apr 2026 11:58:44 -0400 Subject: [PATCH 0103/2758] exfat: fix potential use-after-free in exfat_find_dir_entry() commit 3f5f8ee9917cc2b9076ac533492d8a200edcabb8 upstream. In exfat_find_dir_entry(), the buffer_head obtained from exfat_get_dentry() is released with brelse(bh) before the fall-through TYPE_EXTEND branch reads the directory entry through ep (which points into bh->b_data): brelse(bh); if (entry_type == TYPE_EXTEND) { ... len = exfat_extract_uni_name(ep, entry_uniname); ... } After brelse() drops our reference, nothing guarantees that the underlying page backing bh->b_data remains valid for the subsequent exfat_extract_uni_name() read. This is the same pattern fixed in commit fc961522ddbd ("exfat: Fix potential use after free in exfat_load_upcase_table()"). Move brelse(bh) so it runs after ep is no longer dereferenced on each branch. Confirmed on QEMU x86_64 with CONFIG_KASAN=y + CONFIG_DEBUG_PAGEALLOC=y + CONFIG_PAGE_POISONING=y on linux-next, using a crafted exFAT image (long filename with same-hash collisions forcing the TYPE_EXTEND path). With a debug-only invalidate_bdev() inserted between brelse(bh) and the ep read to make the stale-deref window deterministic, the unpatched kernel faults: BUG: KASAN: use-after-free in exfat_find_dir_entry+0x133b/0x15a0 BUG: unable to handle page fault for address: ffff88801a5fa0c2 Oops: 0000 [#1] SMP DEBUG_PAGEALLOC KASAN NOPTI RIP: 0010:exfat_find_dir_entry+0x1188/0x15a0 With this patch applied, the same instrumented harness completes cleanly under the same sanitizer stack. I have not reproduced a crash on an uninstrumented kernel under ordinary reclaim; the instrumented A/B establishes the lifetime violation and that the patch closes it, not an unaided triggerability claim. Fixes: ca06197382bd ("exfat: add directory operations") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/exfat/dir.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c index ac008ccaa97de..561fd23492183 100644 --- a/fs/exfat/dir.c +++ b/fs/exfat/dir.c @@ -1027,12 +1027,12 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, continue; } - brelse(bh); if (entry_type == TYPE_EXTEND) { unsigned short entry_uniname[16], unichar; if (step != DIRENT_STEP_NAME || name_len >= MAX_NAME_LENGTH) { + brelse(bh); step = DIRENT_STEP_FILE; continue; } @@ -1043,6 +1043,7 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, uniname += EXFAT_FILE_NAME_LEN; len = exfat_extract_uni_name(ep, entry_uniname); + brelse(bh); name_len += len; unichar = *(uniname+len); @@ -1061,6 +1062,7 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, continue; } + brelse(bh); if (entry_type & (TYPE_CRITICAL_SEC | TYPE_BENIGN_SEC)) { if (step == DIRENT_STEP_SECD) { From b2ae3245ea44dccaa9af676b6747476951883318 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 29 Apr 2026 09:34:01 -0700 Subject: [PATCH 0104/2758] KVM: x86/mmu: Ensure hugepage is in by slot before checking max mapping level commit ef057cbf825e03b63f6edf5980f96abf3c53089d upstream. When recovering hugepages in the shadow MMU, verify that the base gfn of the shadow page is actually contained within the target memslot, *before* querying the max mapping level given the shadow page's gfn. Failure to pre-check the validity of the gfn can lead to an out-of-bounds access to the slot's lpage_info (which typically manifests as a host #PF because the lpage_info is vmalloc'd) if the guest creates a hugepage mapping (in its PTEs) that extends "below" the bounds of a memslot. When faulting in memory for a guest, and the size of the guest mapping is greater than KVM's (current) max mapping, then KVM will create a "direct" shadow page (direct in that there are no gPTEs to shadow, and so the target gfn is a direct calculation given the base gfn of the shadow page). The hugepage recovery flow looks for such direct shadow pages, as forcing 4KiB mappings when dirty logging generates the guest > host mapping size case. When the 4KiB restriction is lifted, then KVM can replace the shadow page with a hugepage. But if KVM originally used a smaller mapping than the guest because the range of memory covered by the guest hugepage exceeds the bounds of a memslot, then KVM will link a direct shadow page with a gfn that is outside the bounds of the memslot being used to fault in memory. The rmap entry added for the leaf mapping is correct and within bounds, but the gfn of the leaf SPTE's parent shadow page will be out of bounds. BUG: unable to handle page fault for address: ffffc90000806ffc #PF: supervisor read access in kernel mode #PF: error_code(0x0000) - not-present page PGD 100000067 P4D 100000067 PUD 1002a7067 PMD 10612f067 PTE 0 Oops: Oops: 0000 [#1] SMP CPU: 13 UID: 1000 PID: 757 Comm: mmu_stress_test Not tainted 7.1.0-rc1-48ce1e26eace-x86_pir_to_irr_comments-vm #341 PREEMPT Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015 RIP: 0010:kvm_mmu_max_mapping_level+0x79/0x2b0 [kvm] Call Trace: kvm_mmu_recover_huge_pages+0x21b/0x320 [kvm] kvm_set_memslot+0x1ee/0x590 [kvm] kvm_set_memory_region.part.0+0x3a1/0x4d0 [kvm] kvm_vm_ioctl+0x9bf/0x15d0 [kvm] __x64_sys_ioctl+0x8a/0xd0 do_syscall_64+0xb7/0xbb0 entry_SYSCALL_64_after_hwframe+0x4b/0x53 RIP: 0033:0x7f21c0f1a9bf Don't bother pre-checking the bounds of the potential hugepage, i.e. don't check that e.g. sp->gfn + KVM_PAGES_PER_HPAGE(sp->role.level + 1) is also within the memslot, as the checks performed by kvm_mmu_max_mapping_level() are a superset of the basic bounds checks. I.e. pre-checking the full range would be a dubious micro-optimization. Fixes: 9eba50f8d7fc ("KVM: x86/mmu: Consult max mapping level when zapping collapsible SPTEs") Cc: stable@vger.kernel.org Cc: David Matlack Cc: James Houghton Cc: Alexander Bulekov Cc: Fred Griffoul Cc: Alexander Graf Cc: David Woodhouse Cc: Filippo Sironi Cc: Ivan Orlov Signed-off-by: Sean Christopherson Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/mmu/mmu.c | 18 ++++++++++++------ include/linux/kvm_host.h | 7 ++++++- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index bb204d3c66b7e..0c2e35e3ffbdb 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -7288,13 +7288,19 @@ static bool kvm_mmu_zap_collapsible_spte(struct kvm *kvm, sp = sptep_to_sp(sptep); /* - * We cannot do huge page mapping for indirect shadow pages, - * which are found on the last rmap (level = 1) when not using - * tdp; such shadow pages are synced with the page table in - * the guest, and the guest page table is using 4K page size - * mapping if the indirect sp has level = 1. + * Direct shadow page can be replaced by a hugepage if the host + * mapping level allows it and the memslot maps all of the host + * hugepage. Note! If the memslot maps only part of the + * hugepage, sp->gfn may be below slot->base_gfn, and querying + * the max mapping level would cause an out-of-bounds lpage_info + * access. So the gfn bounds check *must* be done first. + * + * Indirect shadow pages are created when the guest page tables + * are using 4K pages. Since the host mapping is always + * constrained by the page size in the guest, indirect shadow + * pages are never collapsible. */ - if (sp->role.direct && + if (sp->role.direct && is_gfn_in_memslot(slot, sp->gfn) && sp->role.level < kvm_mmu_max_mapping_level(kvm, NULL, slot, sp->gfn)) { kvm_zap_one_rmap_spte(kvm, rmap_head, sptep); diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 4c14aee1fb063..0135c1c6ff060 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -1815,6 +1815,11 @@ void kvm_unregister_irq_ack_notifier(struct kvm *kvm, struct kvm_irq_ack_notifier *kian); bool kvm_arch_irqfd_allowed(struct kvm *kvm, struct kvm_irqfd *args); +static inline bool is_gfn_in_memslot(const struct kvm_memory_slot *slot, gfn_t gfn) +{ + return gfn >= slot->base_gfn && gfn < slot->base_gfn + slot->npages; +} + /* * Returns a pointer to the memslot if it contains gfn. * Otherwise returns NULL. @@ -1825,7 +1830,7 @@ try_get_memslot(struct kvm_memory_slot *slot, gfn_t gfn) if (!slot) return NULL; - if (gfn >= slot->base_gfn && gfn < slot->base_gfn + slot->npages) + if (is_gfn_in_memslot(slot, gfn)) return slot; else return NULL; From 5c87b47374682f69686068ad0a7779365a527b1c Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 12 Jun 2026 15:52:41 -0700 Subject: [PATCH 0105/2758] KVM: Replace guest-triggerable BUG_ON() in ioeventfd datamatch with get_unaligned() commit f1edbed787ba67988ed34e0132ca128b052b6ce8 upstream. Drop a BUG_ON() that has been reachable since it was first added, way back in 2009, and instead use get_unaligned() to perform potentially-unaligned accesses. For a given store, KVM x86's emulator tracks the entire value in the destination operand, x86_emulate_ctxt.dst. If the destination is memory, and the target splits multiple pages and/or is emulated MMIO, then KVM handles each fragment independently. E.g. on a page split starting at page offset 0xffc, KVM writes 4 bytes to the first page, then the remaining bytes to the second page, using ctxt->dst as the source for both (with appropriate offsets). If the destination splits a page *and* hits emulated MMIO on the second page, then KVM will complete the write to the first page, then emulate the MMIO access to the second page. If there is a datamatch-enabled ioeventfd at offset 0 of the second page, then KVM will process the remainder of the store as a potential ioeventfd signal. Putting it all together, if the guest emits a store that splits a page starting at page offset N, and the second page has a datamatch-enabled ioeventfd at offset 0, then KVM will check for datamatch using &dst.valptr[N] as the source. Due to dst (and thus dst.valptr) being 32-byte aligned, if N is not aligned to @len, the BUG_ON() fires. E.g. with a 16-byte store at page offset 0xffc, to an ioeventfd of len 8, all initial checks in ioeventfd_in_range() will succeed, and the BUG_ON() fires due to @val being 4-byte aligned, but not 8-byte aligned. ------------[ cut here ]------------ kernel BUG at arch/x86/kvm/../../../virt/kvm/eventfd.c:783! Oops: invalid opcode: 0000 [#1] SMP CPU: 0 UID: 1000 PID: 615 Comm: repro Not tainted 7.1.0-rc2-ff238429d1ea #365 PREEMPT Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015 RIP: 0010:ioeventfd_write+0x6c/0x70 [kvm] Call Trace: __kvm_io_bus_write+0x85/0xb0 [kvm] kvm_io_bus_write+0x53/0x80 [kvm] vcpu_mmio_write+0x66/0xf0 [kvm] emulator_read_write_onepage+0x12a/0x540 [kvm] emulator_read_write+0x109/0x2b0 [kvm] x86_emulate_insn+0x4f8/0xfb0 [kvm] x86_emulate_instruction+0x181/0x790 [kvm] kvm_mmu_page_fault+0x313/0x630 [kvm] vmx_handle_exit+0x18a/0x590 [kvm_intel] kvm_arch_vcpu_ioctl_run+0xc81/0x1c90 [kvm] kvm_vcpu_ioctl+0x2d5/0x970 [kvm] __x64_sys_ioctl+0x8a/0xd0 do_syscall_64+0xb7/0x890 entry_SYSCALL_64_after_hwframe+0x4b/0x53 RIP: 0033:0x7f19c931a9bf Modules linked in: kvm_intel kvm irqbypass ---[ end trace 0000000000000000 ]--- In a perfect world, the fix would be to simply delete the BUG_ON(), as KVM x86 doesn't perform alignment checks on "normal" memory accesses at CPL0. Sadly, C99 ruins all the fun; while the x86 architecture plays nice, dereferencing an unaligned pointer directly is undefined behavior in C, e.g. triggers splats when running with CONFIG_UBSAN_ALIGNMENT=y. Fixes: d34e6b175e61 ("KVM: add ioeventfd support") Cc: stable@vger.kernel.org Signed-off-by: Sean Christopherson Message-ID: <20260612225241.678509-1-seanjc@google.com> Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- virt/kvm/eventfd.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/virt/kvm/eventfd.c b/virt/kvm/eventfd.c index 0e8b8a2c5b791..93ad2ebc963f9 100644 --- a/virt/kvm/eventfd.c +++ b/virt/kvm/eventfd.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -779,21 +780,18 @@ ioeventfd_in_range(struct _ioeventfd *p, gpa_t addr, int len, const void *val) return true; /* otherwise, we have to actually compare the data */ - - BUG_ON(!IS_ALIGNED((unsigned long)val, len)); - switch (len) { case 1: - _val = *(u8 *)val; + _val = get_unaligned((u8 *)val); break; case 2: - _val = *(u16 *)val; + _val = get_unaligned((u16 *)val); break; case 4: - _val = *(u32 *)val; + _val = get_unaligned((u32 *)val); break; case 8: - _val = *(u64 *)val; + _val = get_unaligned((u64 *)val); break; default: return false; From 833033e6e55acf11304ff7bbbdf18351d139c281 Mon Sep 17 00:00:00 2001 From: Sam James Date: Mon, 25 May 2026 08:56:19 +0100 Subject: [PATCH 0106/2758] crypto: nx - fix nx_crypto_ctx_exit argument commit 4e67f504ee9ded15e256b64f4fde150e917381d7 upstream. nx_crypto_ctx_shash_exit calls nx_crypto_ctx_exit with crypto_shash_ctx(...) but crypto_shash_ctx gives a nx_crypto_ctx *, not a crypto_tfm *. Fix the type in nx_crypto_ctx_exit and drop the bogus crypto_tfm_ctx call. This fixes the following oops: BUG: Unable to handle kernel data access at 0xc0403effffffffc8 Faulting instruction address: 0xc000000000396cb4 Oops: Kernel access of bad area, sig: 11 [#15] Call Trace: nx_crypto_ctx_shash_exit+0x24/0x60 crypto_shash_exit_tfm+0x28/0x40 crypto_destroy_tfm+0x98/0x140 crypto_exit_ahash_using_shash+0x20/0x40 crypto_destroy_tfm+0x98/0x140 hash_release+0x1c/0x30 alg_sock_destruct+0x38/0x60 __sk_destruct+0x48/0x2b0 af_alg_release+0x58/0xb0 __sock_release+0x68/0x150 sock_close+0x20/0x40 __fput+0x110/0x3a0 sys_close+0x48/0xa0 system_call_exception+0x140/0x2d0 system_call_common+0xf4/0x258 .. which came from hardlink(1) opportunistically using AF_ALG. The same problem exists with nx_crypto_ctx_skcipher_exit getting a context it wasn't expecting, but apparently nobody hit that for years. Cc: Eric Biggers Cc: stable@vger.kernel.org Fixes: bfd9efddf990 ("crypto: nx - convert AES-ECB to skcipher API") Fixes: 9420e628e7d8 ("crypto: nx - Use API partial block handling") Acked-by: Breno Leitao Reviewed-by: Eric Biggers Reported-by: Calvin Buckley Tested-by: Calvin Buckley Suggested-by: Brad Spengler Signed-off-by: Sam James Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/nx/nx.c | 6 ++---- drivers/crypto/nx/nx.h | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/crypto/nx/nx.c b/drivers/crypto/nx/nx.c index 78135fb13f5c2..1b7509e2ce443 100644 --- a/drivers/crypto/nx/nx.c +++ b/drivers/crypto/nx/nx.c @@ -714,15 +714,13 @@ int nx_crypto_ctx_aes_xcbc_init(struct crypto_shash *tfm) /** * nx_crypto_ctx_exit - destroy a crypto api context * - * @tfm: the crypto transform pointer for the context + * @nx_ctx: the crypto api context * * As crypto API contexts are destroyed, this exit hook is called to free the * memory associated with it. */ -void nx_crypto_ctx_exit(struct crypto_tfm *tfm) +void nx_crypto_ctx_exit(struct nx_crypto_ctx *nx_ctx) { - struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(tfm); - kfree_sensitive(nx_ctx->kmem); nx_ctx->csbcpb = NULL; nx_ctx->csbcpb_aead = NULL; diff --git a/drivers/crypto/nx/nx.h b/drivers/crypto/nx/nx.h index 36974f08490a9..6dfabfbf8192c 100644 --- a/drivers/crypto/nx/nx.h +++ b/drivers/crypto/nx/nx.h @@ -153,7 +153,7 @@ int nx_crypto_ctx_aes_ctr_init(struct crypto_skcipher *tfm); int nx_crypto_ctx_aes_cbc_init(struct crypto_skcipher *tfm); int nx_crypto_ctx_aes_ecb_init(struct crypto_skcipher *tfm); int nx_crypto_ctx_sha_init(struct crypto_shash *tfm); -void nx_crypto_ctx_exit(struct crypto_tfm *tfm); +void nx_crypto_ctx_exit(struct nx_crypto_ctx *nx_ctx); void nx_crypto_ctx_skcipher_exit(struct crypto_skcipher *tfm); void nx_crypto_ctx_aead_exit(struct crypto_aead *tfm); void nx_crypto_ctx_shash_exit(struct crypto_shash *tfm); From 9d0d5ba20cad661f7f287d4c66d2c19022ce2fd0 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Fri, 1 May 2026 11:02:03 +0000 Subject: [PATCH 0107/2758] gfs2: fix use-after-free in gfs2_qd_dealloc commit f9c9ec2c319f843b70ecdf939d48b52d189bc081 upstream. gfs2_qd_dealloc(), called as an RCU callback from gfs2_qd_dispose(), accesses the superblock object sdp through qd->qd_sbd after freeing qd. It does so to decrement sd_quota_count and wake up sd_kill_wait. However, by the time the RCU callback runs, gfs2_put_super() may have already freed sdp via free_sbd(). This can happen when gfs2_quota_cleanup() is called during unmount: it disposes of quota objects via call_rcu() and then waits on sd_kill_wait with a 60-second timeout. If the timeout expires, or if gfs2_gl_hash_clear() triggers additional qd_put() calls that schedule more RCU callbacks after the wait completes, gfs2_put_super() will proceed to free the superblock while RCU callbacks referencing it are still pending. Add an rcu_barrier() before free_sbd() in gfs2_put_super() to ensure all pending RCU callbacks (including gfs2_qd_dealloc) have completed before the superblock is freed. Fixes: a475c5dd16e5 ("gfs2: Free quota data objects synchronously") Reported-by: syzbot+42a37bf8045847d8f9d2@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=42a37bf8045847d8f9d2 Tested-by: syzbot+42a37bf8045847d8f9d2@syzkaller.appspotmail.com Cc: stable@vger.kernel.org Signed-off-by: Tristan Madani Signed-off-by: Andreas Gruenbacher Signed-off-by: Greg Kroah-Hartman --- fs/gfs2/super.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index a2ea121331f18..4d854556b5299 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -643,6 +643,7 @@ static void gfs2_put_super(struct super_block *sb) gfs2_delete_debugfs_file(sdp); gfs2_sys_fs_del(sdp); + rcu_barrier(); free_sbd(sdp); } From 73569a44fca2992f0ca4a4c0104069741b9873a0 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Tue, 16 Jun 2026 15:10:49 +0000 Subject: [PATCH 0108/2758] pwrseq: core: fix use-after-free in pwrseq_debugfs_seq_next() commit 257595adf9dac15ae1edd9d07753fbc576a7583d upstream. pwrseq_debugfs_seq_next() declares 'next' with __free(put_device), which causes put_device() to be called on the returned pointer when the variable goes out of scope. This results in a use-after-free since the seq_file framework receives a pointer whose reference has already been dropped. Simply removing __free(put_device) would fix the UAF but would leak the reference acquired by bus_find_next_device(), as stop() only calls up_read(&pwrseq_sem) and never releases the device reference. Fix this by making the reference counting consistent across all seq_file callbacks, matching the standard pattern used by PCI and SCSI: - start(): use get_device() so it returns a referenced pointer. - next(): explicitly put_device(curr) to release the previous device's reference (no NULL check needed - the seq_file framework only calls next() while the previous return was non-NULL). - stop(): put_device(data) to release the last iterated device's reference, with a NULL guard since stop() may be called with NULL when start() returned NULL or next() reached end-of-sequence. Cc: stable@vger.kernel.org Fixes: 249ebf3f65f8 ("power: sequencing: implement the pwrseq core") Signed-off-by: Wentao Liang Link: https://patch.msgid.link/20260616151049.1705503-1-vulab@iscas.ac.cn Signed-off-by: Bartosz Golaszewski Signed-off-by: Greg Kroah-Hartman --- drivers/power/sequencing/core.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/power/sequencing/core.c b/drivers/power/sequencing/core.c index 4dff71be11b60..9b512a055b5a4 100644 --- a/drivers/power/sequencing/core.c +++ b/drivers/power/sequencing/core.c @@ -989,8 +989,9 @@ static void *pwrseq_debugfs_seq_start(struct seq_file *seq, loff_t *pos) ctx.index = *pos; /* - * We're holding the lock for the entire printout so no need to fiddle - * with device reference count. + * Hold the lock for the entire printout to prevent device removal. + * Reference counts are managed by start()/next()/stop() as required + * by the seq_file contract. */ down_read(&pwrseq_sem); @@ -998,7 +999,7 @@ static void *pwrseq_debugfs_seq_start(struct seq_file *seq, loff_t *pos) if (!ctx.index) return NULL; - return ctx.dev; + return get_device(ctx.dev); } static void *pwrseq_debugfs_seq_next(struct seq_file *seq, void *data, @@ -1008,8 +1009,9 @@ static void *pwrseq_debugfs_seq_next(struct seq_file *seq, void *data, ++*pos; - struct device *next __free(put_device) = - bus_find_next_device(&pwrseq_bus, curr); + struct device *next = bus_find_next_device(&pwrseq_bus, curr); + + put_device(curr); return next; } @@ -1058,6 +1060,8 @@ static int pwrseq_debugfs_seq_show(struct seq_file *seq, void *data) static void pwrseq_debugfs_seq_stop(struct seq_file *seq, void *data) { + if (data) + put_device(data); up_read(&pwrseq_sem); } From a594debfd4e7ec39413647458907f689ef57fd2f Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Wed, 17 Jun 2026 02:05:18 +0000 Subject: [PATCH 0109/2758] hdlc_ppp: sync per-proto timers before freeing hdlc state commit c78a4e41ab5ead6193ad8a2dd92e8906bae659fa upstream. Each PPP control protocol (LCP/IPCP/IPV6CP) embedded in struct ppp registers a timer via timer_setup(). That struct ppp is the hdlc->state allocation, which detach_hdlc_protocol() frees with kfree() in both teardown paths: unregister_hdlc_device() and the re-attach inside attach_hdlc_protocol(). The ppp proto never registered a .detach callback, so detach_hdlc_protocol() performs no timer synchronization before the kfree(). The only cancel, timer_delete(&proto->timer) in ppp_cp_event(), is partial (it does not wait for a running callback) and only runs on the ->CLOSED transition; ppp_stop()/ppp_close() do not sync either. A ppp_timer callback already executing (blocked on ppp->lock) survives the kfree and then dereferences proto->state / ppp->lock in freed memory, leading to a use-after-free. Fix this by adding a .detach helper that calls timer_shutdown_sync() on every per-proto timer. detach_hdlc_protocol() invokes proto->detach(dev) before kfree(hdlc->state), so timer_shutdown_sync() now runs on both free paths. timer_shutdown_sync() is used instead of timer_delete_sync() because the keepalive path re-arms the timer through add_timer()/mod_timer() and shutdown blocks any re-activation during teardown. Initialize the per-protocol timers in ppp_ioctl() when the protocol is attached, and remove the now-redundant timer_setup() from ppp_start(), so that the timers are initialized exactly once at attach time and ppp_timer_release() never operates on uninitialized timer_list structures. attach_hdlc_protocol() uses kmalloc() (not kzalloc), so struct ppp's protos[i].timer is uninitialized garbage until the first timer_setup(); without this init-at-attach, attaching the PPP protocol without ever bringing the device up would leave timer_shutdown_sync() operating on uninitialized memory in .detach. Moving the init out of ppp_start() (which only runs on NETDEV_UP) into the attach path makes the initialization unconditional and avoids initializing the same timer_list twice. This bug was found by static analysis. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Fan Wu Link: https://patch.msgid.link/20260617020518.116319-1-fanwu01@zju.edu.cn Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/wan/hdlc_ppp.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/net/wan/hdlc_ppp.c b/drivers/net/wan/hdlc_ppp.c index 159295c4bd6d8..302ed27944e7c 100644 --- a/drivers/net/wan/hdlc_ppp.c +++ b/drivers/net/wan/hdlc_ppp.c @@ -619,7 +619,6 @@ static void ppp_start(struct net_device *dev) struct proto *proto = &ppp->protos[i]; proto->dev = dev; - timer_setup(&proto->timer, ppp_timer, 0); proto->state = CLOSED; } ppp->protos[IDX_LCP].pid = PID_LCP; @@ -639,6 +638,15 @@ static void ppp_close(struct net_device *dev) ppp_tx_flush(); } +static void ppp_timer_release(struct net_device *dev) +{ + struct ppp *ppp = get_ppp(dev); + int i; + + for (i = 0; i < IDX_COUNT; i++) + timer_shutdown_sync(&ppp->protos[i].timer); +} + static struct hdlc_proto proto = { .start = ppp_start, .stop = ppp_stop, @@ -647,6 +655,7 @@ static struct hdlc_proto proto = { .ioctl = ppp_ioctl, .netif_rx = ppp_rx, .module = THIS_MODULE, + .detach = ppp_timer_release, }; static const struct header_ops ppp_header_ops = { @@ -657,7 +666,7 @@ static int ppp_ioctl(struct net_device *dev, struct if_settings *ifs) { hdlc_device *hdlc = dev_to_hdlc(dev); struct ppp *ppp; - int result; + int i, result; switch (ifs->type) { case IF_GET_PROTO: @@ -685,6 +694,8 @@ static int ppp_ioctl(struct net_device *dev, struct if_settings *ifs) return result; ppp = get_ppp(dev); + for (i = 0; i < IDX_COUNT; i++) + timer_setup(&ppp->protos[i].timer, ppp_timer, 0); spin_lock_init(&ppp->lock); ppp->req_timeout = 2; ppp->cr_retries = 10; From afebe44facc48a61761e885bbb7f0380d4a603ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Koutn=C3=BD?= Date: Thu, 5 Feb 2026 23:54:23 +0800 Subject: [PATCH 0110/2758] blk-cgroup: fix UAF in __blkcg_rstat_flush() commit 0ab5ee5a1badb58cbb2242617cb01a4972b1f2a2 upstream. When multiple blkgs in the same blkcg are released concurrently, a use-after-free can occur. The race happens when one blkg's __blkcg_rstat_flush() removes another blkg's iostat entries via llist_del_all(). The second blkg sees an empty list and proceeds to free itself while the first is still iterating over its entries. Move the flush from __blkg_release() (RCU callback) to blkg_release() (before call_rcu). This ensures the RCU grace period waits for any concurrent flush's rcu_read_lock() section to complete before freeing. Cc: stable@vger.kernel.org Cc: Jay Shin Cc: Tejun Heo Cc: Waiman Long Fixes: 20cb1c2fb756 ("blk-cgroup: Flush stats before releasing blkcg_gq") Reported-by: coregee2000@gmail.com Closes: https://lore.kernel.org/linux-block/CAHPqNmwT9oRpem3J3erS_W0uSQND47LGGSBsNxP8E6uSUish1w@mail.gmail.com/ Signed-off-by: Ming Lei Tested-by: Jose Fernandez (Anthropic) Link: https://patch.msgid.link/20260205155425.342084-1-ming.lei@redhat.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/blk-cgroup.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index bc63bd220865d..51a9c35313256 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -164,20 +164,10 @@ static void blkg_free(struct blkcg_gq *blkg) static void __blkg_release(struct rcu_head *rcu) { struct blkcg_gq *blkg = container_of(rcu, struct blkcg_gq, rcu_head); - struct blkcg *blkcg = blkg->blkcg; - int cpu; #ifdef CONFIG_BLK_CGROUP_PUNT_BIO WARN_ON(!bio_list_empty(&blkg->async_bios)); #endif - /* - * Flush all the non-empty percpu lockless lists before releasing - * us, given these stat belongs to us. - * - * blkg_stat_lock is for serializing blkg stat update - */ - for_each_possible_cpu(cpu) - __blkcg_rstat_flush(blkcg, cpu); /* release the blkcg and parent blkg refs this blkg has been holding */ css_put(&blkg->blkcg->css); @@ -195,6 +185,17 @@ static void __blkg_release(struct rcu_head *rcu) static void blkg_release(struct percpu_ref *ref) { struct blkcg_gq *blkg = container_of(ref, struct blkcg_gq, refcnt); + struct blkcg *blkcg = blkg->blkcg; + int cpu; + + /* + * Flush all the non-empty percpu lockless lists before releasing + * us, given these stat belongs to us. + * + * blkg_stat_lock is for serializing blkg stat update + */ + for_each_possible_cpu(cpu) + __blkcg_rstat_flush(blkcg, cpu); call_rcu(&blkg->rcu_head, __blkg_release); } From 1eea5e1820a2f5164d706bd1277bc97ff31ce32d Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Wed, 17 Jun 2026 09:58:18 +0200 Subject: [PATCH 0111/2758] tipc: fix slab-use-after-free Read in tipc_aead_decrypt_done commit bda3348872a2ef0d19f2df6aa8cb5025adce2f20 upstream. tipc_aead_decrypt() goes straight from tipc_bearer_hold(b) to crypto_aead_decrypt(req) without taking a reference on the netns, unlike the encrypt path. When crypto_aead_decrypt() is offloaded asynchronously (e.g. the SIMD aead wrapper queuing to cryptd), the cryptd worker runs tipc_aead_decrypt_done() later. If the bearer's netns is torn down in the meantime, cleanup_net() -> tipc_exit_net() -> tipc_crypto_stop() frees the per-netns tipc_crypto, and the completion then reads it: tipc_aead_decrypt_done() dereferences aead->crypto->stats and aead->crypto->net, and tipc_crypto_rcv_complete() dereferences aead->crypto->aead[] and the node table -- reading freed memory. Decoded KASAN splat (v7.1-rc7, CONFIG_KASAN_INLINE + TIPC + TIPC_CRYPTO): BUG: KASAN: slab-use-after-free in tipc_aead_decrypt_done (net/tipc/crypto.c:999) Read of size 8 at addr ffff8881056258a8 by task kworker/u16:2/51 Workqueue: events_unbound Call Trace: tipc_aead_decrypt_done (net/tipc/crypto.c:999) process_one_work (kernel/workqueue.c:3314) worker_thread (kernel/workqueue.c:3397 kernel/workqueue.c:3478) kthread (kernel/kthread.c:436) ret_from_fork (arch/x86/kernel/process.c:158) ret_from_fork_asm (arch/x86/entry/entry_64.S:245) Allocated by task 169: __kasan_kmalloc (mm/kasan/common.c:398 mm/kasan/common.c:415) tipc_crypto_start (net/tipc/crypto.c:1502) tipc_init_net (net/tipc/core.c:72) ops_init (net/core/net_namespace.c:137) setup_net (net/core/net_namespace.c:446) copy_net_ns (net/core/net_namespace.c:579) create_new_namespaces (kernel/nsproxy.c:132) __x64_sys_unshare (kernel/fork.c:3316) do_syscall_64 (arch/x86/entry/syscall_64.c:63) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) Freed by task 8: kfree (mm/slub.c:6566) tipc_exit_net (net/tipc/core.c:119) cleanup_net (net/core/net_namespace.c:704) process_one_work (kernel/workqueue.c:3314) kthread (kernel/kthread.c:436) This is the same class of bug that commit e279024617134 ("net/tipc: fix slab-use-after-free Read in tipc_aead_encrypt_done") fixed for the encrypt side. The encrypt path takes maybe_get_net(aead->crypto->net) before crypto_aead_encrypt() and drops it with put_net() on the synchronous return paths and in tipc_aead_encrypt_done(); the -EINPROGRESS/-EBUSY return keeps the reference for the async callback to release. The decrypt path was left without the equivalent guard. Mirror the encrypt-side fix on the decrypt path: take a net reference before crypto_aead_decrypt() (failing with -ENODEV and the matching bearer put if it cannot be acquired), keep it across the -EINPROGRESS/-EBUSY async return, and drop it with put_net() on the synchronous success/error return and at the end of tipc_aead_decrypt_done(). Reproduced under KASAN on v7.1-rc7: a UDP bearer with a cluster key is flooded with crafted encrypted frames from an unknown peer (driving the cluster-key decrypt path) while the bearer's netns is repeatedly torn down. The completion must run asynchronously to outlive tipc_crypto_stop(); on x86 the stock aesni gcm(aes) now decrypts synchronously, so the async path was exercised via cryptd offload. The unguarded aead->crypto dereference in tipc_aead_decrypt_done() is the unpatched upstream path; tipc_aead_decrypt() still lacks maybe_get_net(aead->crypto->net), so the completion can outlive the free on any config where crypto_aead_decrypt() goes async. Found by 0sec automated security-research tooling (https://0sec.ai). Fixes: fc1b6d6de220 ("tipc: introduce TIPC encryption & authentication") Cc: stable@vger.kernel.org Signed-off-by: Doruk Tan Ozturk Reviewed-by: Alexander Lobakin Reviewed-by: Tung Nguyen Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260617075818.37431-1-doruk@0sec.ai Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/tipc/crypto.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/net/tipc/crypto.c b/net/tipc/crypto.c index 6d3b6b89b1d17..16f1ed1f6b1b5 100644 --- a/net/tipc/crypto.c +++ b/net/tipc/crypto.c @@ -941,12 +941,20 @@ static int tipc_aead_decrypt(struct net *net, struct tipc_aead *aead, goto exit; } + /* Get net to avoid freed tipc_crypto when delete namespace */ + if (!maybe_get_net(net)) { + tipc_bearer_put(b); + rc = -ENODEV; + goto exit; + } + /* Now, do decrypt */ rc = crypto_aead_decrypt(req); if (rc == -EINPROGRESS || rc == -EBUSY) return rc; tipc_bearer_put(b); + put_net(net); exit: kfree(ctx); @@ -984,6 +992,7 @@ static void tipc_aead_decrypt_done(void *data, int err) } tipc_bearer_put(b); + put_net(net); } static inline int tipc_ehdr_size(struct tipc_ehdr *ehdr) From 0833b2b84c2fc1387f8165f0cbf6a02d67f647a5 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 25 Jun 2026 13:03:49 +0800 Subject: [PATCH 0112/2758] LoongArch: Report dying CPU to RCU in stop_this_cpu() commit f2539c56c74691e7a88af6372ba2b48c06ed2fe4 upstream. This is a port of MIPS commit 9f3f3bdc6d9dac1 ("MIPS: smp: report dying CPU to RCU in stop_this_cpu()"). smp_send_stop() parks all secondary CPUs in stop_this_cpu(). And the function marks the CPU offline for the scheduler via set_cpu_online(false) but never informs RCU, so RCU keeps expecting a quiescent state from CPUs that are now spinning forever with interrupts disabled. As long as nothing waits for an RCU grace period after smp_send_stop() this is harmless, which is why it went unnoticed. However, since commit 91840be8f710370 ("irq_work: Fix use-after-free in irq_work_single() on PREEMPT_RT"), irq_work_sync() calls synchronize_rcu() on architectures without an irq_work self-IPI, i.e. where arch_irq_work_has_interrupt() returns false. Any irq_work_sync() issued in the reboot/shutdown/halt path after smp_send_stop() then blocks on a grace period that can never complete, hanging the reboot: WARNING: CPU: 0 PID: 15 at kernel/irq_work.c:144 irq_work_queue_on ... rcu: INFO: rcu_sched detected stalls on CPUs/tasks: rcu: Offline CPU 1 blocking current GP. rcu: Offline CPU 2 blocking current GP. rcu: Offline CPU 3 blocking current GP. This issue needs some hacks to reproduce, and it was not noticed on LoongArch because arch_irq_work_has_interrupt() usually returns true. Call rcutree_report_cpu_dead() once interrupts are disabled, mirroring the generic CPU-hotplug offline path, so RCU stops waiting on the parked CPUs and grace periods can still complete. LoongArch shuts down all CPUs here without going through the CPU-hotplug mechanism, so this report is not otherwise issued. Cc: Fixes: 91840be8f710 ("irq_work: Fix use-after-free in irq_work_single() on PREEMPT_RT") Reviewed-by: Guo Ren Signed-off-by: Huacai Chen Signed-off-by: Greg Kroah-Hartman --- arch/loongarch/kernel/smp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/loongarch/kernel/smp.c b/arch/loongarch/kernel/smp.c index 64a048f1b8803..b077b2aa1ab37 100644 --- a/arch/loongarch/kernel/smp.c +++ b/arch/loongarch/kernel/smp.c @@ -705,6 +705,7 @@ static void stop_this_cpu(void *dummy) set_cpu_online(smp_processor_id(), false); calculate_cpu_foreign_map(); local_irq_disable(); + rcutree_report_cpu_dead(); while (true); } From 9645aaf689aff57427ece3b9fa47d5b5399417f4 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Mon, 18 May 2026 13:10:36 +0000 Subject: [PATCH 0113/2758] pNFS: Fix use-after-free in pnfs_update_layout() commit 13e198a90ca4050f4bee8a3f23680389a6563ccc upstream. When hitting the NFS_LAYOUT_RETURN branch in pnfs_update_layout(), the code calls pnfs_prepare_to_retry_layoutget(lo). If it succeeds, pnfs_put_layout_hdr(lo) is called before trace_pnfs_update_layout(), which still references 'lo'. This results in a use-after-free when the tracepoint accesses lo's fields. Fix this by moving the tracepoint call before pnfs_put_layout_hdr(lo). Fixes: 2c8d5fc37fe2 ("pNFS: Stricter ordering of layoutget and layoutreturn") Cc: stable@vger.kernel.org Signed-off-by: Wentao Liang Signed-off-by: Anna Schumaker Signed-off-by: Greg Kroah-Hartman --- fs/nfs/pnfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index 743467e9ba202..aee523134c0f3 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -2229,11 +2229,11 @@ pnfs_update_layout(struct inode *ino, dprintk("%s wait for layoutreturn\n", __func__); lseg = ERR_PTR(pnfs_prepare_to_retry_layoutget(lo)); if (!IS_ERR(lseg)) { - pnfs_put_layout_hdr(lo); dprintk("%s retrying\n", __func__); trace_pnfs_update_layout(ino, pos, count, iomode, lo, lseg, PNFS_UPDATE_LAYOUT_RETRY); + pnfs_put_layout_hdr(lo); goto lookup_again; } trace_pnfs_update_layout(ino, pos, count, iomode, lo, lseg, From 8d32856fb72ba976d9c87ba405fd17e80419934c Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Tue, 16 Jun 2026 16:38:17 -0400 Subject: [PATCH 0114/2758] sched/mmcid: Fix OOB clear_bit when CID is MM_CID_UNSET in fixup path commit de3ab9bd3133899efb92e4cd05ba4203e58fc0a3 upstream. In mm_cid_fixup_cpus_to_tasks(), when rq->curr has the target mm and mm_cid.active is set, the CID is checked with cid_in_transit() before setting the transition bit. In per-CPU mode a newly forked or exec'd task can be running with mm_cid.cid == MM_CID_UNSET because CIDs are assigned lazily on schedule-in. With cid_in_transit() the guard passes for MM_CID_UNSET (no transit bit), converts it to MM_CID_UNSET | MM_CID_TRANSIT and stores it back; later mm_cid_schedout() feeds this to clear_bit() with MM_CID_UNSET as the bit number, triggering an out-of-bounds write. Symptoms: this is genuine memory corruption, but a bounded out-of-bounds write, not an arbitrary one. MM_CID_UNSET is the fixed sentinel BIT(31), so once the bad value reaches mm_cid_schedout() the cid_from_transit_cid() strip leaves MM_CID_UNSET, which fails the "cid < max_cids" convergence test and falls into mm_drop_cid() -> clear_bit(MM_CID_UNSET, mm_cidmask(mm)). The cid bitmap is embedded in the mm_struct slab object (after cpu_bitmap and mm_cpus_allowed) and is only num_possible_cpus() bits wide, so clearing bit 31 is a deterministic OOB bit-clear at a fixed offset of 2^31 / 8 == 256 MiB past the bitmap base. The address is not attacker-influenced (fixed sentinel -> fixed offset) and the op only clears a single bit; what sits 256 MiB further along the direct map is whatever kernel object happens to live there, so this corrupts one bit of unpredictable kernel memory -- it is not an arbitrary-address or arbitrary-value write. It triggers only in per-CPU CID mode, when a CPU is running an active task of the target mm whose cid is still MM_CID_UNSET -- the fork()/execve() window before that task's next schedule-in assigns it a real CID -- and a per-CPU -> per-task fixup walks over it (the mode fallback driven by a thread exit, sched_mm_cid_exit(), or by the deferred max_cids recompute in mm_cid_work_fn()). In practice syzkaller surfaced it as a KASAN use-after-free reported in __schedule -> mm_cid_switch_to, where the offending clear_bit() is inlined via mm_cid_schedout() -> mm_drop_cid(). Guard the transition-bit assignment against MM_CID_UNSET, in addition to the existing cid_in_transit() check, so the bit is only set on a genuine task-owned CID. A CPU-owned (MM_CID_ONCPU) CID of a running active task is handled by the cid_on_cpu(pcp->cid) branch above and never reaches this path, so excluding MM_CID_UNSET (and the already-transitioning case) is sufficient. Fixes: fbd0e71dc370 ("sched/mmcid: Provide CID ownership mode fixup functions") Signed-off-by: Rik van Riel Signed-off-by: Thomas Gleixner Assisted-by: Claude:claude-opus-4-8 syzkaller Reviewed-by: Mathieu Desnoyers Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260616203818.1516263-1-riel@surriel.com Signed-off-by: Greg Kroah-Hartman --- kernel/sched/core.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 3314ead931273..091ee8d2b17a8 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -10880,8 +10880,19 @@ static void mm_cid_fixup_cpus_to_tasks(struct mm_struct *mm) } else if (rq->curr->mm == mm && rq->curr->mm_cid.active) { unsigned int cid = rq->curr->mm_cid.cid; - /* Ensure it has the transition bit set */ - if (!cid_in_transit(cid)) { + /* + * Set the transition bit only on a genuine task-owned + * CID. A running active task can legitimately have + * MM_CID_UNSET here: in per-CPU mode CIDs are assigned + * lazily on schedule-in, so the fork()/execve() window + * leaves the task active with no owned CID. Setting the + * transition bit on MM_CID_UNSET would later feed + * clear_bit() an out-of-bounds bit number via + * mm_cid_schedout(), so exclude it. A CPU-owned + * (MM_CID_ONCPU) CID is handled by the cid_on_cpu() + * branch above and never reaches here. + */ + if (cid != MM_CID_UNSET && !cid_in_transit(cid)) { cid = cid_to_transit_cid(cid); rq->curr->mm_cid.cid = cid; pcp->cid = cid; From 0405a65e4ebd9eac13a765f9f02ac05851ca5421 Mon Sep 17 00:00:00 2001 From: Qingshuang Fu Date: Thu, 18 Jun 2026 10:13:52 +0800 Subject: [PATCH 0115/2758] irqchip/imgpdc: Fix resource leak, add missing chained handler cleanup on remove commit 37738fdf2ab1e504d1c63ce5bc0aeb6452d8f057 upstream. The driver allocates domain generic chips using irq_alloc_domain_generic_chips() during probe and sets up chained handlers using irq_set_chained_handler_and_data(). However, on driver removal, the generic chips are not freed and the chained handlers are not removed. The generic chips remain on the global gc_list and may later be accessed by generic interrupt chip suspend, resume, or shutdown callbacks after the driver has been removed, potentially resulting in a use-after-free and kernel crash. The chained handlers that were installed in probe for peripheral and syswake interrupts are also left dangling, which can lead to spurious interrupts accessing freed memory. Fix these issues by: - Setting IRQ_DOMAIN_FLAG_DESTROY_GC flag in domain->flags, so the core code automatically removes generic chips when irq_domain_remove() is called - Clearing all chained handlers with NULL in pdc_intc_remove() Fixes: b6ef9161e43a ("irq-imgpdc: add ImgTec PDC irqchip driver") Signed-off-by: Qingshuang Fu Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260618021352.661773-1-fffsqian@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/irqchip/irq-imgpdc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/irqchip/irq-imgpdc.c b/drivers/irqchip/irq-imgpdc.c index e9ef2f5a7207e..4feef4ab5fecc 100644 --- a/drivers/irqchip/irq-imgpdc.c +++ b/drivers/irqchip/irq-imgpdc.c @@ -378,6 +378,7 @@ static int pdc_intc_probe(struct platform_device *pdev) dev_err(&pdev->dev, "cannot add IRQ domain\n"); return -ENOMEM; } + priv->domain->flags |= IRQ_DOMAIN_FLAG_DESTROY_GC; /* * Set up 2 generic irq chips with 2 chip types. @@ -465,6 +466,11 @@ static void pdc_intc_remove(struct platform_device *pdev) { struct pdc_intc_priv *priv = platform_get_drvdata(pdev); + for (unsigned int i = 0; i < priv->nr_perips; ++i) + irq_set_chained_handler_and_data(priv->perip_irqs[i], NULL, NULL); + + irq_set_chained_handler_and_data(priv->syswake_irq, NULL, NULL); + irq_domain_remove(priv->domain); } From 5e098e40e8bac43ed58645c10d5fad781966efe4 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Wed, 8 Apr 2026 15:45:34 +0000 Subject: [PATCH 0116/2758] fpga: region: fix use-after-free in child_regions_with_firmware() commit 54f3c5643ec523a04b6ec0e7c19eb10f5ebebdd3 upstream. Move of_node_put(child_region) after the error print to avoid accessing freed memory when pr_err() references child_region. Fixes: 0fa20cdfcc1f ("fpga: fpga-region: device tree control for FPGA") Cc: stable@vger.kernel.org Signed-off-by: Wentao Liang [ Yilun: Fix the Fixes tag ] Reviewed-by: Xu Yilun Link: https://lore.kernel.org/r/20260408154534.404327-1-vulab@iscas.ac.cn Signed-off-by: Xu Yilun Signed-off-by: Greg Kroah-Hartman --- drivers/fpga/of-fpga-region.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/fpga/of-fpga-region.c b/drivers/fpga/of-fpga-region.c index caa091224dc54..9107a5b461d35 100644 --- a/drivers/fpga/of-fpga-region.c +++ b/drivers/fpga/of-fpga-region.c @@ -168,11 +168,10 @@ static int child_regions_with_firmware(struct device_node *overlay) fpga_region_of_match); } - of_node_put(child_region); - if (ret) pr_err("firmware-name not allowed in child FPGA region: %pOF", child_region); + of_node_put(child_region); return ret; } From ff268cd9ccbce6472a0658791b417bf11c31ee39 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Mon, 1 Jun 2026 14:32:47 -0400 Subject: [PATCH 0117/2758] rpmsg: char: Fix use-after-free on probe error path commit 1ff3f528e67d20e2b1483dcaba899dc7832b2e6b upstream. rpmsg_chrdev_probe() stores the newly allocated eptdev in the default endpoint's priv pointer before calling rpmsg_chrdev_eptdev_add(). If rpmsg_chrdev_eptdev_add() then fails, its error path frees eptdev while the default endpoint may still dispatch callbacks with the stale priv pointer. Avoid publishing eptdev through the default endpoint until rpmsg_chrdev_eptdev_add() succeeds. Messages received before the priv pointer is published should be ignored by rpmsg_ept_cb(). Flow-control updates can hit rpmsg_ept_flow_cb() in the same window, so make both callbacks return success when priv is NULL. Fixes: bc69d1066569 ("rpmsg: char: Introduce the "rpmsg-raw" channel") Signed-off-by: Yuho Choi Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20260601183247.1962010-1-dbgh9129@gmail.com Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/rpmsg/rpmsg_char.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c index ca9cf8858a5ef..bff5aefee2126 100644 --- a/drivers/rpmsg/rpmsg_char.c +++ b/drivers/rpmsg/rpmsg_char.c @@ -104,6 +104,9 @@ static int rpmsg_ept_cb(struct rpmsg_device *rpdev, void *buf, int len, struct rpmsg_eptdev *eptdev = priv; struct sk_buff *skb; + if (!eptdev) + return 0; + skb = alloc_skb(len, GFP_ATOMIC); if (!skb) return -ENOMEM; @@ -124,6 +127,9 @@ static int rpmsg_ept_flow_cb(struct rpmsg_device *rpdev, void *priv, bool enable { struct rpmsg_eptdev *eptdev = priv; + if (!eptdev) + return 0; + eptdev->remote_flow_restricted = enable; eptdev->remote_flow_updated = true; @@ -490,6 +496,7 @@ static int rpmsg_chrdev_probe(struct rpmsg_device *rpdev) struct rpmsg_channel_info chinfo; struct rpmsg_eptdev *eptdev; struct device *dev = &rpdev->dev; + int ret; memcpy(chinfo.name, rpdev->id.name, RPMSG_NAME_SIZE); chinfo.src = rpdev->src; @@ -502,13 +509,17 @@ static int rpmsg_chrdev_probe(struct rpmsg_device *rpdev) /* Set the default_ept to the rpmsg device endpoint */ eptdev->default_ept = rpdev->ept; + ret = rpmsg_chrdev_eptdev_add(eptdev, chinfo); + + if (ret) + return ret; /* * The rpmsg_ept_cb uses *priv parameter to get its rpmsg_eptdev context. - * Storedit in default_ept *priv field. + * Stored it in default_ept *priv field. */ eptdev->default_ept->priv = eptdev; - return rpmsg_chrdev_eptdev_add(eptdev, chinfo); + return 0; } static void rpmsg_chrdev_remove(struct rpmsg_device *rpdev) From 99c21e7263248c3f084756bfae08163cc5d6c62f Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Sun, 24 May 2026 19:12:48 +0800 Subject: [PATCH 0118/2758] ocfs2: reject oversized group bitmap descriptors commit 9bd541e09dffff27e5bec0f9f45b0228173a5375 upstream. ocfs2_validate_gd_parent() only bounds bg_bits against the parent allocator's chain geometry. A malicious descriptor can still claim a bg_size/bg_bits pair that exceeds the bitmap bytes that physically fit in the group descriptor block, so later bitmap scans and bit updates can run past bg_bitmap. Add a physical-cap check based on ocfs2_group_bitmap_size() for the parent allocator type and reject descriptors whose bg_size or bg_bits exceed that capacity. Keep the existing chain geometry check so both the on-disk bitmap layout and the allocator metadata must agree before the descriptor is used. Validation reproduced this kernel report: KASAN use-after-free in _find_next_bit+0x7f/0xc0 Read of size 8 Call trace: dump_stack_lvl+0x66/0xa0 (?:?) print_report+0xd0/0x630 (?:?) _find_next_bit+0x7f/0xc0 (?:?) srso_alias_return_thunk+0x5/0xfbef5 (?:?) __virt_addr_valid+0x188/0x2f0 (?:?) kasan_report+0xe4/0x120 (?:?) ocfs2_find_max_contig_free_bits+0x35/0x70 (fs/ocfs2/suballoc.c:1375) ocfs2_block_group_set_bits+0x472/0x4b0 (fs/ocfs2/suballoc.c:1457) ocfs2_cluster_group_search+0x16b/0x440 (fs/ocfs2/suballoc.c:86) ocfs2_bg_discontig_fix_result+0x1ef/0x230 (fs/ocfs2/suballoc.c:1786) ocfs2_search_chain+0x8f8/0x10a0 (fs/ocfs2/suballoc.c:1886) get_page_from_freelist+0x70e/0x2370 (?:?) lock_release+0xc6/0x290 (?:?) do_raw_spin_unlock+0x9a/0x100 (?:?) kasan_unpoison+0x27/0x60 (?:?) __bfs+0x147/0x240 (?:?) get_page_from_freelist+0x83d/0x2370 (?:?) ocfs2_claim_suballoc_bits+0x38c/0xe70 (fs/ocfs2/suballoc.c:96) sched_domains_numa_masks_clear+0x70/0xd0 (?:?) check_irq_usage+0xe8/0xb70 (?:?) __ocfs2_claim_clusters+0x18d/0x4c0 (fs/ocfs2/suballoc.c:2497) check_path+0x24/0x50 (?:?) rcu_is_watching+0x20/0x50 (?:?) check_prev_add+0xfd/0xd00 (?:?) ocfs2_add_clusters_in_btree+0x17d/0x810 (fs/ocfs2/suballoc.c:?) __folio_batch_add_and_move+0x1f5/0x3d0 (?:?) ocfs2_add_inode_data+0xd9/0x120 (fs/ocfs2/suballoc.c:?) filemap_add_folio+0x105/0x1f0 (?:?) ocfs2_write_begin_nolock+0x29f7/0x2f80 (fs/ocfs2/suballoc.c:3043) ocfs2_read_inode_block+0xb5/0x110 (fs/ocfs2/suballoc.c:?) down_write+0xf5/0x180 (?:?) ocfs2_write_begin+0x180/0x240 (fs/ocfs2/suballoc.c:?) __mark_inode_dirty+0x758/0x9a0 (?:?) inode_to_bdi+0x41/0x90 (?:?) balance_dirty_pages_ratelimited_flags+0xf8/0x1d0 (?:?) generic_perform_write+0x252/0x440 (?:?) mnt_put_write_access_file+0x16/0x70 (?:?) file_update_time_flags+0xe4/0x200 (?:?) ocfs2_file_write_iter+0x80a/0x1320 (fs/ocfs2/suballoc.c:?) lock_acquire+0x184/0x2f0 (?:?) ksys_write+0xd2/0x170 (?:?) apparmor_file_permission+0xf5/0x310 (?:?) read_zero+0x8d/0x140 (?:?) lock_is_held_type+0x8f/0x100 (?:?) Link: https://lore.kernel.org/20260524111248.1429884-1-rollkingzzc@gmail.com Fixes: ccd979bdbce9 ("[PATCH] OCFS2: The Second Oracle Cluster Filesystem") Assisted-by: Codex:gpt-5.5 Signed-off-by: Zhang Cen Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/ocfs2/suballoc.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c index d284e0e372525..a4a2b87a45fe3 100644 --- a/fs/ocfs2/suballoc.c +++ b/fs/ocfs2/suballoc.c @@ -231,8 +231,16 @@ static int ocfs2_validate_gd_parent(struct super_block *sb, int resize) { unsigned int max_bits; + unsigned int max_bitmap_bits; + unsigned int max_bitmap_size; + int suballocator; struct ocfs2_group_desc *gd = (struct ocfs2_group_desc *)bh->b_data; + suballocator = le64_to_cpu(di->i_blkno) != OCFS2_SB(sb)->bitmap_blkno; + max_bitmap_size = ocfs2_group_bitmap_size(sb, suballocator, + OCFS2_SB(sb)->s_feature_incompat); + max_bitmap_bits = max_bitmap_size * 8; + if (di->i_blkno != gd->bg_parent_dinode) { do_error("Group descriptor #%llu has bad parent pointer (%llu, expected %llu)\n", (unsigned long long)bh->b_blocknr, @@ -240,6 +248,20 @@ static int ocfs2_validate_gd_parent(struct super_block *sb, (unsigned long long)le64_to_cpu(di->i_blkno)); } + if (le16_to_cpu(gd->bg_size) > max_bitmap_size) { + do_error("Group descriptor #%llu has bitmap size %u but physical max of %u\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(gd->bg_size), + max_bitmap_size); + } + + if (le16_to_cpu(gd->bg_bits) > max_bitmap_bits) { + do_error("Group descriptor #%llu has bit count %u but physical max of %u\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(gd->bg_bits), + max_bitmap_bits); + } + max_bits = le16_to_cpu(di->id2.i_chain.cl_cpg) * le16_to_cpu(di->id2.i_chain.cl_bpc); if (le16_to_cpu(gd->bg_bits) > max_bits) { do_error("Group descriptor #%llu has bit count of %u\n", From a7656d368265d085ac9bb85ab31b0cdb72ad8c38 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Thu, 28 May 2026 13:39:16 +0800 Subject: [PATCH 0119/2758] 9p: avoid putting oldfid in p9_client_walk() error path commit 1a3860d46e3eb47dbd60339783cdad7904486b9f upstream. When p9_client_walk() is called with clone set to false, fid aliases oldfid. If the walk subsequently fails after the request has been sent, the error path jumps to clunk_fid, which currently calls p9_fid_put(fid) unconditionally. This drops a reference to oldfid even though ownership of oldfid remains with the caller. If this is the last reference, oldfid can be clunked and destroyed while the caller still expects it to be valid. A later use or put of oldfid can then trigger a use-after-free or refcount underflow. Fix this by only putting fid in the clunk_fid error path when it does not alias oldfid, matching the existing guard in the error path below. This can be triggered when a multi-component walk is split into multiple p9_client_walk() calls and a later non-cloning walk fails. A reproducer and refcount warning logs are available on request. Fixes: b48dbb998d70 ("9p fid refcount: add p9_fid_get/put wrappers") Cc: stable@vger.kernel.org Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Assisted-by: GLM 5.1 Signed-off-by: Yizhou Zhao Message-ID: <20260528053918.53550-1-zhaoyz24@mails.tsinghua.edu.cn> Signed-off-by: Dominique Martinet Signed-off-by: Greg Kroah-Hartman --- net/9p/client.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/9p/client.c b/net/9p/client.c index f0dcf252af7e1..4b942d02c29cf 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -1092,7 +1092,8 @@ struct p9_fid *p9_client_walk(struct p9_fid *oldfid, uint16_t nwname, clunk_fid: kfree(wqids); - p9_fid_put(fid); + if (fid != oldfid) + p9_fid_put(fid); fid = NULL; error: From 9fef09df42df55ab819b285ea892e0fc1b95a9c4 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Mon, 8 Jun 2026 09:37:29 +0000 Subject: [PATCH 0120/2758] MIPS: smp: report dying CPU to RCU in stop_this_cpu() commit 9f3f3bdc6d9dac1a5a8262ee7ad0f2ff1527a7e7 upstream. smp_send_stop() parks all secondary CPUs in stop_this_cpu(). The function marks the CPU offline for the scheduler via set_cpu_online(false) but never informs RCU, so RCU keeps expecting a quiescent state from CPUs that are now spinning forever with interrupts disabled. As long as nothing waits for an RCU grace period after smp_send_stop() this is harmless, which is why it went unnoticed. Since commit 91840be8f710 ("irq_work: Fix use-after-free in irq_work_single() on PREEMPT_RT") however, irq_work_sync() calls synchronize_rcu() on architectures without an irq_work self-IPI, i.e. where arch_irq_work_has_interrupt() returns false. That is the asm-generic default used by MIPS. Any irq_work_sync() issued in the reboot/shutdown path after smp_send_stop() then blocks on a grace period that can never complete, hanging the reboot: WARNING: CPU: 0 PID: 15 at kernel/irq_work.c:144 irq_work_queue_on ... rcu: INFO: rcu_sched detected stalls on CPUs/tasks: rcu: Offline CPU 1 blocking current GP. rcu: Offline CPU 2 blocking current GP. rcu: Offline CPU 3 blocking current GP. This issue was noticed on several Realtek MIPS switch SoCs (MIPS interAptiv) and came up during kernel bump downstream in OpenWrt from 6.18.33 to 6.18.34, after the backport of the patch to the 6.18 stable branch. The patch also has been backported all the way back to 6.1. Call rcutree_report_cpu_dead() once interrupts are disabled, mirroring the generic CPU-hotplug offline path, so RCU stops waiting on the parked CPUs and grace periods can still complete. MIPS shuts down all CPUs here without going through the CPU-hotplug mechanism, so this report is not otherwise issued. Reporting a dying CPU to RCU outside the regular hotplug offline path is not unprecedented: arm64 does the same in cpu_die_early(). There it is an exception for a CPU that was coming online and is aborting bringup, rather than the default shutdown action as on MIPS. Fixes: 91840be8f710 ("irq_work: Fix use-after-free in irq_work_single() on PREEMPT_RT") CC: stable@vger.kernel.org Signed-off-by: Jonas Jelonek Signed-off-by: Thomas Bogendoerfer Signed-off-by: Greg Kroah-Hartman --- arch/mips/kernel/smp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/mips/kernel/smp.c b/arch/mips/kernel/smp.c index 4868e79f3b30e..0f28b4a62e72e 100644 --- a/arch/mips/kernel/smp.c +++ b/arch/mips/kernel/smp.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -422,6 +423,7 @@ static void stop_this_cpu(void *dummy) set_cpu_online(smp_processor_id(), false); calculate_cpu_foreign_map(); local_irq_disable(); + rcutree_report_cpu_dead(); while (1); } From f636cf6a1e7b7f40d48d8d08bd5f152aa61dd130 Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Sat, 6 Jun 2026 23:44:52 +0900 Subject: [PATCH 0121/2758] KVM: x86: hyper-v: Bound the bank index when querying sparse banks commit 4721f8160f17554b003e8928bb61e6c9b2fe92a3 upstream. When checking if a VP ID is included in a sparse bank set, explicitly check that the ID can actually be contained in a sparse bank (the TLFS allows for a maximum of 64 banks of 64 vCPUs each). When handling a paravirtual TLB flush for L2, the VP ID is copied verbatim from the enlightened VMCS, without any bounds check, i.e. isn't guaranteed to be under the limit of 4096. Failure to check the bounds of the VP ID leads to an out-of-bounds read when testing the sparse bank, and super strictly speaking could lead to KVM performing an unnecessary TLB flush for an L2 vCPU. ================================================================== BUG: KASAN: use-after-free in hv_is_vp_in_sparse_set+0x85/0x100 [kvm] Read of size 8 at addr ffff88811ba5f598 by task hyperv_evmcs/2802 CPU: 12 UID: 1000 PID: 2802 Comm: hyperv_evmcs Not tainted 7.1.0-rc2 #7 PREEMPT Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015 Call Trace: dump_stack_lvl+0x51/0x60 print_report+0xcb/0x5d0 kasan_report+0xb4/0xe0 kasan_check_range+0x35/0x1b0 hv_is_vp_in_sparse_set+0x85/0x100 [kvm] kvm_hv_flush_tlb+0xe9e/0x16c0 [kvm] kvm_hv_hypercall+0xe6b/0x1e60 [kvm] vmx_handle_exit+0x485/0x1b60 [kvm_intel] kvm_arch_vcpu_ioctl_run+0x22e3/0x5070 [kvm] kvm_vcpu_ioctl+0x5d0/0x10c0 [kvm] __x64_sys_ioctl+0x129/0x1a0 do_syscall_64+0xb9/0xcf0 entry_SYSCALL_64_after_hwframe+0x4b/0x53 RIP: 0033:0x7f0e62d1a9bf The buggy address belongs to the physical page: page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffffffffffffffff pfn:0x11ba5f flags: 0x4000000000000000(zone=1) raw: 4000000000000000 0000000000000000 00000000ffffffff 0000000000000000 raw: ffffffffffffffff 0000000000000000 00000000ffffffff 0000000000000000 page dumped because: kasan: bad access detected Memory state around the buggy address: ffff88811ba5f480: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ffff88811ba5f500: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff >ffff88811ba5f580: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ^ ffff88811ba5f600: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ffff88811ba5f680: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ================================================================== Disabling lock debugging due to kernel taint Opportunistically add a compile time assertion to ensure the maximum number of sparse banks exactly matches the number of possible bits in the passed in mask. Cc: stable@vger.kernel.org Fixes: c58a318f6090 ("KVM: x86: hyper-v: L2 TLB flush") Signed-off-by: Hyunwoo Kim Reviewed-by: Vitaly Kuznetsov Link: https://patch.msgid.link/aiQyZIJtO-2Aj_xN@v4bel [sean: add KASAN splat, drop comment, add assert, massage changelog] Signed-off-by: Sean Christopherson Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/hyperv.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c index 4438ecac9a89b..ad59c22979bde 100644 --- a/arch/x86/kvm/hyperv.c +++ b/arch/x86/kvm/hyperv.c @@ -1839,6 +1839,11 @@ static bool hv_is_vp_in_sparse_set(u32 vp_id, u64 valid_bank_mask, u64 sparse_ba int valid_bit_nr = vp_id / HV_VCPUS_PER_SPARSE_BANK; unsigned long sbank; + BUILD_BUG_ON(BITS_PER_TYPE(valid_bank_mask) != HV_MAX_SPARSE_VCPU_BANKS); + + if (valid_bit_nr >= HV_MAX_SPARSE_VCPU_BANKS) + return false; + if (!test_bit(valid_bit_nr, (unsigned long *)&valid_bank_mask)) return false; From 2753a097d1fe24c4351c608048612c74108aa89f Mon Sep 17 00:00:00 2001 From: Ashutosh Desai Date: Fri, 1 May 2026 13:35:32 -0700 Subject: [PATCH 0122/2758] KVM: SVM: Fix page overflow in sev_dbg_crypt() for ENCRYPT path commit 78ee2d50185a037b3d2452a97f3dad69c3f7f389 upstream. In sev_dbg_crypt(), the per-iteration transfer length is bounded by the source page offset (PAGE_SIZE - s_off) but not by the destination page offset (PAGE_SIZE - d_off). When d_off > s_off, the encrypt path (__sev_dbg_encrypt_user) performs a read-modify-write using a single-page intermediate buffer (dst_tpage): 1. __sev_dbg_decrypt() expands the size to round_up(len + (d_off & 15), 16) before issuing the PSP command. If len + (d_off & 15) > PAGE_SIZE, the PSP writes beyond the end of the 4096-byte dst_tpage allocation. 2. The subsequent memcpy()/copy_from_user() into page_address(dst_tpage) + (d_off & 15) of 'len' bytes overflows by up to 15 bytes under the same condition. Trigger example: s_off = 0, d_off = 1, debug.len = PAGE_SIZE - the PSP is instructed to write round_up(4097, 16) = 4112 bytes to a 4096-byte buffer. Fix by also bounding len by (PAGE_SIZE - d_off), the same check that sev_send_update_data() already performs for its single-page guest region. ================================================================== BUG: KASAN: slab-use-after-free in sev_dbg_crypt+0x993/0xd10 [kvm_amd] Write of size 4095 at addr ff110062293bb009 by task sev_dbg_test/228214 CPU: 96 UID: 0 PID: 228214 Comm: sev_dbg_test Tainted: G U W 7.0.0-smp--5ce9b0c48211-dbg #156 PREEMPTLAZY Tainted: [U]=USER, [W]=WARN Hardware name: Google Astoria/astoria, BIOS 0.20250817.1-0 08/25/2025 Call Trace: dump_stack_lvl+0x54/0x70 print_report+0xbc/0x260 kasan_report+0xa2/0xd0 kasan_check_range+0x25f/0x2c0 __asan_memcpy+0x40/0x70 sev_dbg_crypt+0x993/0xd10 [kvm_amd] sev_mem_enc_ioctl+0x33c/0x450 [kvm_amd] kvm_vm_ioctl+0x65d/0x6d0 [kvm] __se_sys_ioctl+0xb2/0x100 do_syscall_64+0xe8/0x870 entry_SYSCALL_64_after_hwframe+0x4b/0x53 The buggy address belongs to the physical page: page: refcount:1 mapcount:0 mapping:0000000000000000 index:0x7fe72b6a0 pfn:0x62293bb memcg:ff11000112827d82 flags: 0x1400000000000000(node=1|zone=1) raw: 1400000000000000 0000000000000000 dead000000000122 0000000000000000 raw: 00000007fe72b6a0 0000000000000000 00000001ffffffff ff11000112827d82 page dumped because: kasan: bad access detected Memory state around the buggy address: ff110062293bbf00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff110062293bbf80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >ff110062293bc000: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc ^ ff110062293bc080: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc ff110062293bc100: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc ================================================================== Disabling lock debugging due to kernel taint Fixes: 24f41fb23a39 ("KVM: SVM: Add support for SEV DEBUG_DECRYPT command") Fixes: 7d1594f5d94b ("KVM: SVM: Add support for SEV DEBUG_ENCRYPT command") Cc: stable@vger.kernel.org Signed-off-by: Ashutosh Desai [sean: add sample KASAN splat, Fixes, and stable@] Link: https://patch.msgid.link/20260501203537.2120074-2-seanjc@google.com Signed-off-by: Sean Christopherson Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/svm/sev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 6c6a6d663e296..29c3d557b78ba 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -1396,6 +1396,7 @@ static int sev_dbg_crypt(struct kvm *kvm, struct kvm_sev_cmd *argp, bool dec) s_off = vaddr & ~PAGE_MASK; d_off = dst_vaddr & ~PAGE_MASK; len = min_t(size_t, (PAGE_SIZE - s_off), size); + len = min_t(size_t, len, PAGE_SIZE - d_off); if (dec) ret = __sev_dbg_decrypt_user(kvm, From d109e72f3fbccb540473285d17d7519584f7f76e Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Tue, 7 Apr 2026 07:30:25 +0000 Subject: [PATCH 0123/2758] power: reset: linkstation-poweroff: fix use-after-free in the linkstation_poweroff_init() commit 8eec545cde69e46e9a1d2b7d915ce4f5df85b3bd upstream. Move of_node_put(dn) after the of_match_node() call, which still needs the node pointer. The node reference is correctly released after use. Fixes: e2f471efe1d6 ("power: reset: linkstation-poweroff: prepare for new devices") Cc: stable@vger.kernel.org Signed-off-by: Wentao Liang Link: https://patch.msgid.link/20260407073025.271865-1-vulab@iscas.ac.cn Signed-off-by: Sebastian Reichel Signed-off-by: Greg Kroah-Hartman --- drivers/power/reset/linkstation-poweroff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/reset/linkstation-poweroff.c b/drivers/power/reset/linkstation-poweroff.c index 02f5fdb8ffc4c..e56d75bfcc43f 100644 --- a/drivers/power/reset/linkstation-poweroff.c +++ b/drivers/power/reset/linkstation-poweroff.c @@ -163,10 +163,10 @@ static int __init linkstation_poweroff_init(void) dn = of_find_matching_node(NULL, ls_poweroff_of_match); if (!dn) return -ENODEV; - of_node_put(dn); match = of_match_node(ls_poweroff_of_match, dn); cfg = match->data; + of_node_put(dn); dn = of_find_node_by_name(NULL, cfg->mdio_node_name); if (!dn) From d6d6051fd15a3f57262e9091d5f50fb9622ba09f Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Tue, 3 Mar 2026 13:29:45 +0800 Subject: [PATCH 0124/2758] riscv: mm: Extract helper mark_new_valid_map() commit 9ee25d0a70ff4494b4e1d266b962d0a574ef318a upstream. In preparation of a future patch using the same mechanism for non-vmalloc addresses, extract the mark_new_valid_map() helper from flush_cache_vmap(). No functional change intended. Cc: stable@vger.kernel.org Signed-off-by: Vivian Wang Link: https://patch.msgid.link/20260303-handle-kfence-protect-spurious-fault-v2-1-f80d8354d79d@iscas.ac.cn Signed-off-by: Paul Walmsley Signed-off-by: Greg Kroah-Hartman --- arch/riscv/include/asm/cacheflush.h | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/arch/riscv/include/asm/cacheflush.h b/arch/riscv/include/asm/cacheflush.h index 0092513c3376c..b1a2ac6657926 100644 --- a/arch/riscv/include/asm/cacheflush.h +++ b/arch/riscv/include/asm/cacheflush.h @@ -43,20 +43,23 @@ do { \ #ifdef CONFIG_64BIT extern u64 new_vmalloc[NR_CPUS / sizeof(u64) + 1]; extern char _end[]; +static inline void mark_new_valid_map(void) +{ + int i; + + /* + * We don't care if concurrently a cpu resets this value since + * the only place this can happen is in handle_exception() where + * an sfence.vma is emitted. + */ + for (i = 0; i < ARRAY_SIZE(new_vmalloc); ++i) + new_vmalloc[i] = -1ULL; +} #define flush_cache_vmap flush_cache_vmap static inline void flush_cache_vmap(unsigned long start, unsigned long end) { - if (is_vmalloc_or_module_addr((void *)start)) { - int i; - - /* - * We don't care if concurrently a cpu resets this value since - * the only place this can happen is in handle_exception() where - * an sfence.vma is emitted. - */ - for (i = 0; i < ARRAY_SIZE(new_vmalloc); ++i) - new_vmalloc[i] = -1ULL; - } + if (is_vmalloc_or_module_addr((void *)start)) + mark_new_valid_map(); } #define flush_cache_vmap_early(start, end) local_flush_tlb_kernel_range(start, end) #endif From 7f7a9d6cb0ed5fdb66a699a8ad27639f524ee4a6 Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Tue, 3 Mar 2026 13:29:46 +0800 Subject: [PATCH 0125/2758] riscv: kfence: Call mark_new_valid_map() for kfence_unprotect() commit 8d6c8c40e733b3fcaf92fed0a078bba2f6941a3b upstream. In kfence_protect_page(), which kfence_unprotect() calls, we cannot send IPIs to other CPUs to ask them to flush TLB. This may lead to those CPUs spuriously faulting on a recently allocated kfence object despite it being valid, leading to false positive use-after-free reports. Fix this by calling mark_new_valid_map() so that the page fault handling code path notices the spurious fault and flushes TLB then retries the access. Update the comment in handle_exception to indicate that new_valid_map_cpus_check also handles kfence_unprotect() spurious faults. Note that kfence_protect() has the same stale TLB entries problem, but that leads to false negatives, which is fine with kfence. Cc: stable@vger.kernel.org Reported-by: Yanko Kaneti Fixes: b3431a8bb336 ("riscv: Fix IPIs usage in kfence_protect_page()") Signed-off-by: Vivian Wang Link: https://patch.msgid.link/20260303-handle-kfence-protect-spurious-fault-v2-2-f80d8354d79d@iscas.ac.cn Signed-off-by: Paul Walmsley Signed-off-by: Greg Kroah-Hartman --- arch/riscv/include/asm/kfence.h | 7 +++++-- arch/riscv/kernel/entry.S | 6 ++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/arch/riscv/include/asm/kfence.h b/arch/riscv/include/asm/kfence.h index d08bf7fb3aee6..29cb3a6ee113d 100644 --- a/arch/riscv/include/asm/kfence.h +++ b/arch/riscv/include/asm/kfence.h @@ -6,6 +6,7 @@ #include #include #include +#include #include static inline bool arch_kfence_init_pool(void) @@ -17,10 +18,12 @@ static inline bool kfence_protect_page(unsigned long addr, bool protect) { pte_t *pte = virt_to_kpte(addr); - if (protect) + if (protect) { set_pte(pte, __pte(pte_val(ptep_get(pte)) & ~_PAGE_PRESENT)); - else + } else { set_pte(pte, __pte(pte_val(ptep_get(pte)) | _PAGE_PRESENT)); + mark_new_valid_map(); + } preempt_disable(); local_flush_tlb_kernel_range(addr, addr + PAGE_SIZE); diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index d011fb51c59a0..6c14ed7a56135 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -136,8 +136,10 @@ SYM_CODE_START(handle_exception) #ifdef CONFIG_64BIT /* - * The RISC-V kernel does not eagerly emit a sfence.vma after each - * new vmalloc mapping, which may result in exceptions: + * The RISC-V kernel does not flush TLBs on all CPUS after each new + * vmalloc mapping or kfence_unprotect(), which may result in + * exceptions: + * * - if the uarch caches invalid entries, the new mapping would not be * observed by the page table walker and an invalidation is needed. * - if the uarch does not cache invalid entries, a reordered access From acd744019460bad22e43d4569a502f9c88d331ae Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Tue, 2 Jun 2026 13:53:24 +0900 Subject: [PATCH 0126/2758] ntfs: serialize volume label accesses commit e9e50ce4f13dc721014af622613409455c734942 upstream. Protect vol->volume_label with a mutex and snaphost the label before copy_to_user. This prevent a use-after-free when FS_IOC_SETFSLABEL replaces the vol->volume_label and FS_IOC_GETTSLABEL reads it concurrently. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/file.c | 17 +++++++++++++---- fs/ntfs/super.c | 19 +++++++++++++------ fs/ntfs/volume.h | 2 ++ 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c index e8bea22b81a75..264cf8404385c 100644 --- a/fs/ntfs/file.c +++ b/fs/ntfs/file.c @@ -707,12 +707,21 @@ static int ntfs_ioctl_get_volume_label(struct file *filp, unsigned long arg) { struct ntfs_volume *vol = NTFS_SB(file_inode(filp)->i_sb); char __user *buf = (char __user *)arg; + char label[FSLABEL_MAX]; + ssize_t len; + mutex_lock(&vol->volume_label_lock); if (!vol->volume_label) { - if (copy_to_user(buf, "", 1)) - return -EFAULT; - } else if (copy_to_user(buf, vol->volume_label, - MIN(FSLABEL_MAX, strlen(vol->volume_label) + 1))) + label[0] = '\0'; + len = 0; + } else { + len = strscpy(label, vol->volume_label, sizeof(label)); + if (len == -E2BIG) + len = FSLABEL_MAX - 1; + } + mutex_unlock(&vol->volume_label_lock); + + if (copy_to_user(buf, label, len + 1)) return -EFAULT; return 0; } diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 9e321cc2febe7..cb880cb6e3886 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -460,17 +460,23 @@ int ntfs_write_volume_label(struct ntfs_volume *vol, char *label) ret = ntfs_resident_attr_record_add(vol_ni, AT_VOLUME_NAME, AT_UNNAMED, 0, (u8 *)uname, uname_len * sizeof(__le16), 0); out: - mutex_unlock(&vol_ni->mrec_lock); - kvfree(uname); - if (ret >= 0) { - kfree(vol->volume_label); + char *old_label; + + mutex_lock(&vol->volume_label_lock); + old_label = vol->volume_label; vol->volume_label = new_label; + mutex_unlock(&vol->volume_label_lock); + + kfree(old_label); mark_inode_dirty_sync(vol->vol_ino); ret = 0; - } else { - kfree(new_label); } + mutex_unlock(&vol_ni->mrec_lock); + kvfree(uname); + + if (ret < 0) + kfree(new_label); return ret; } @@ -2631,6 +2637,7 @@ static int ntfs_init_fs_context(struct fs_context *fc) NVolSetCaseSensitive(vol); init_rwsem(&vol->mftbmp_lock); init_rwsem(&vol->lcnbmp_lock); + mutex_init(&vol->volume_label_lock); fc->s_fs_info = vol; fc->ops = &ntfs_context_ops; diff --git a/fs/ntfs/volume.h b/fs/ntfs/volume.h index af41427ec622c..99e642f475f78 100644 --- a/fs/ntfs/volume.h +++ b/fs/ntfs/volume.h @@ -72,6 +72,7 @@ * @vol_flags: Volume flags. * @major_ver: Ntfs major version of volume. * @minor_ver: Ntfs minor version of volume. + * @volume_label_lock: protects @volume_label. * @volume_label: volume label. * @root_ino: The VFS inode of the root directory. * @secure_ino: The VFS inode of $Secure (NTFS3.0+ only, otherwise NULL). @@ -133,6 +134,7 @@ struct ntfs_volume { struct inode *logfile_ino; struct inode *lcnbmp_ino; struct rw_semaphore lcnbmp_lock; + struct mutex volume_label_lock; struct inode *vol_ino; __le16 vol_flags; u8 major_ver; From 88913059c77e171f44ba829282d42dde0d458811 Mon Sep 17 00:00:00 2001 From: Ian Bridges Date: Wed, 24 Jun 2026 23:13:12 -0500 Subject: [PATCH 0127/2758] fbdev: Fix fb_new_modelist to prevent null-ptr-deref in fb_videomode_to_var commit 7f08fc10fa3d3366dc3af723970bd03d7d6d10e3 upstream. info->var, a framebuffer's current mode, is expected to have a matching entry in info->modelist. var_to_display() relies on this and treats a failed fb_match_mode() as "This should not happen". fb_set_var() keeps it true by adding the mode to the list on every change, and do_register_framebuffer() does the same at registration. store_modes() replaces the modelist from userspace. fb_new_modelist() validates the new modes but does not check that info->var still has a match. It relies on fbcon_new_modelist() to re-point consoles, but that only handles consoles mapped to the framebuffer. With fbcon unbound there are none, so info->var is left describing a mode that is no longer in the list. A later console takeover runs var_to_display(), where fb_match_mode() returns NULL and leaves fb_display[i].mode NULL. fbcon_switch() passes it to display_to_var(), and fb_videomode_to_var() dereferences the NULL mode. Keep the current mode in the list in fb_new_modelist(), the same way fb_set_var() does. Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Ian Bridges Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/core/fbmem.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c index 30f2b59c47bf9..0929deb952185 100644 --- a/drivers/video/fbdev/core/fbmem.c +++ b/drivers/video/fbdev/core/fbmem.c @@ -734,6 +734,18 @@ int fb_new_modelist(struct fb_info *info) if (list_empty(&info->modelist)) return 1; + /* + * The new modelist may not contain the current mode (info->var), and + * fbcon_new_modelist() below only re-points consoles mapped to this + * framebuffer. Add the current mode here so info->var keeps a match + * even when fbcon is unbound. + */ + if (!fb_match_mode(&info->var, &info->modelist)) { + fb_var_to_videomode(&mode, &info->var); + if (fb_add_videomode(&mode, &info->modelist)) + return 1; + } + fbcon_new_modelist(info); return 0; From 39815715cbcfabb16fc8c5f4a23deeda20f5df62 Mon Sep 17 00:00:00 2001 From: Mingyu Wang <25181214217@stu.xidian.edu.cn> Date: Fri, 26 Jun 2026 00:03:06 +0800 Subject: [PATCH 0128/2758] fbdev: fbcon: fix out-of-bounds read in err_out of fbcon_do_set_font() commit 8fdc8c2057eea08d40ce2c8eed41ff9e451c65c2 upstream. When fbcon_do_set_font() fails (e.g., due to a memory allocation failure inside vc_resize() under heavy memory pressure), it jumps to the `err_out` label to roll back the console state. However, the current rollback logic forgets to restore the `hi_font` state, leading to a severe state machine corruption. Earlier in the function, `set_vc_hi_font()` might be called to change `vc->vc_hi_font_mask` and mutate the screen buffer. If `vc_resize()` subsequently fails, the `err_out` path restores `vc_font.charcount` but entirely skips rolling back the `vc_hi_font_mask` and the screen buffer. This mismatch leaves the terminal in a desynchronized state. Because `vc_hi_font_mask` remains set, the VT subsystem will still accept character indices greater than 255 from userspace and write them to the screen buffer. Subsequent rendering calls (e.g., `fbcon_putcs()`) will then use these inflated indices to access the reverted, 256-character font array, leading to a deterministic out-of-bounds read and potential kernel memory disclosure. Fix this by adding the missing rollback logic for the `hi_font` mask and screen buffer in the error path. Fixes: a5a923038d70 ("fbdev: fbcon: Properly revert changes when vc_resize() failed") Cc: stable@vger.kernel.org Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> Reviewed-by: Thomas Zimmermann Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/core/fbcon.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index b0e3e765360d1..3f4a850d0e5b3 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -2407,6 +2407,7 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, int charcount, int resize, ret, old_width, old_height, old_charcount; font_data_t *old_fontdata = p->fontdata; const u8 *old_data = vc->vc_font.data; + unsigned short old_hi_font_mask = vc->vc_hi_font_mask; font_data_get(data); @@ -2453,6 +2454,12 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, int charcount, vc->vc_font.height = old_height; vc->vc_font.charcount = old_charcount; + /* Restore the hi_font state and screen buffer */ + if (old_hi_font_mask && !vc->vc_hi_font_mask) + set_vc_hi_font(vc, true); + else if (!old_hi_font_mask && vc->vc_hi_font_mask) + set_vc_hi_font(vc, false); + font_data_put(data); return ret; From 6eb6ebcc8590007ad59ddccc8b5f9201655b33f8 Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Tue, 2 Jun 2026 16:54:21 +0800 Subject: [PATCH 0129/2758] fbdev: omap2: fix use-after-free in omapfb_mmap commit 7958e67375aa111522086286bba13cfc0816ce8d upstream. omapfb_mmap() has a race condition with OMAPFB_SETUP_PLANE ioctl that can lead to use-after-free: The fb_mmap() entry point holds mm_lock but not lock (fb_info->lock), while ioctl handlers like OMAPFB_SETUP_PLANE hold lock but not mm_lock. This allows concurrent execution. In omapfb_mmap(): 1. rg = omapfb_get_mem_region(ofbi->region); // Get old region ref 2. start = omapfb_get_region_paddr(ofbi); // Read from NEW region 3. len = fix->smem_len; // Read from NEW region 4. vm_iomap_memory(vma, start, len); // Map NEW region memory 5. atomic_inc(&rg->map_count); // Increment OLD region! Concurrently, OMAPFB_SETUP_PLANE can: - Reassign ofbi->region = new_rg - Update fix->smem_len - OMAPFB_SETUP_MEM then checks NEW region's map_count (0!) and frees it This leaves userspace with a mapping to freed physical memory. The fix is to read all required values (start, len) from the same region reference (rg) that will have its map_count incremented, preventing the region from being freed while still mapped. Cc: stable@vger.kernel.org Signed-off-by: Hongling Zeng Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/omap2/omapfb/omapfb-main.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/video/fbdev/omap2/omapfb/omapfb-main.c b/drivers/video/fbdev/omap2/omapfb/omapfb-main.c index d70deb6a91508..046892682fc62 100644 --- a/drivers/video/fbdev/omap2/omapfb/omapfb-main.c +++ b/drivers/video/fbdev/omap2/omapfb/omapfb-main.c @@ -1099,7 +1099,11 @@ static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) rg = omapfb_get_mem_region(ofbi->region); - start = omapfb_get_region_paddr(ofbi); + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) + start = rg->vrfb.paddr[0]; + else + start = rg->paddr; + len = fix->smem_len; DBG("user mmap region start %lx, len %d, off %lx\n", start, len, @@ -1109,6 +1113,8 @@ static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) vma->vm_ops = &mmap_user_ops; vma->vm_private_data = rg; + atomic_inc(&rg->map_count); + r = vm_iomap_memory(vma, start, len); if (r) goto error; @@ -1121,6 +1127,7 @@ static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) return 0; error: + atomic_dec(&rg->map_count); omapfb_put_mem_region(rg); return r; From 13b6f0cdd5cd5e60f682ec43134ab0e2024bd356 Mon Sep 17 00:00:00 2001 From: Tuo Li Date: Wed, 10 Jun 2026 10:50:14 +0800 Subject: [PATCH 0130/2758] fbdev: modedb: fix a possible UAF in fb_find_mode() commit 85b6256469cebdac395e7447147e06b2e151014f upstream. If mode_option is NULL, it is assigned from mode_option_buf: if (!mode_option) { fb_get_options(NULL, &mode_option_buf); mode_option = mode_option_buf; } Later, name is assigned from mode_option: const char *name = mode_option; However, mode_option_buf is freed before name is no longer used: kfree(mode_option_buf); while name is still accessed by: if ((name_matches(db[i], name, namelen) || Since name aliases mode_option_buf, this may result in a use-after-free. Fix this by extending the lifetime of mode_option_buf until the end of the function by using scope-based resource management for cleanup. Signed-off-by: Tuo Li Cc: stable@vger.kernel.org # v6.5+ Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/core/modedb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/video/fbdev/core/modedb.c b/drivers/video/fbdev/core/modedb.c index 703d0b7aec322..b6926764a99cd 100644 --- a/drivers/video/fbdev/core/modedb.c +++ b/drivers/video/fbdev/core/modedb.c @@ -626,7 +626,7 @@ int fb_find_mode(struct fb_var_screeninfo *var, const struct fb_videomode *default_mode, unsigned int default_bpp) { - char *mode_option_buf = NULL; + char *mode_option_buf __free(kfree) = NULL; int i; /* Set up defaults */ @@ -724,7 +724,6 @@ int fb_find_mode(struct fb_var_screeninfo *var, res_specified = 1; } done: - kfree(mode_option_buf); if (cvt) { struct fb_videomode cvt_mode; int ret; From 7e58653d43526ad390056a38696cfcb898d28424 Mon Sep 17 00:00:00 2001 From: Steffen Persvold Date: Fri, 12 Jun 2026 18:40:41 +0200 Subject: [PATCH 0131/2758] fbdev: modedb: Fix misaligned fields in the 1920x1080-60 mode commit d894c48a57d78206e4df9c90d4acfaf39394806a upstream. The 1920x1080@60 modedb entry has one too many initializers before its sync field: a stray "0" occupies the sync slot, which shifts the remaining values by one field. The entry therefore decodes as sync = 0, vmode = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT (0x3, i.e. FB_VMODE_INTERLACED | FB_VMODE_DOUBLE), and flag = FB_VMODE_NONINTERLACED, instead of the intended sync = positive H/V, vmode = non-interlaced. fb_find_mode() then returns a 1920x1080 mode flagged as interlaced + doublescan with active-low syncs. Drivers that honour var->vmode and var->sync when programming display timing enable doublescan and the wrong sync polarity, corrupting the output. Drop the stray initializer so sync and vmode hold their intended values (positive H/V sync, non-interlaced), matching the adjacent 1920x1200 entry. Fixes: c8902258b2b8 ("fbdev: modedb: Add 1920x1080 at 60 Hz video mode") Cc: stable@vger.kernel.org Signed-off-by: Steffen Persvold Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/core/modedb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/fbdev/core/modedb.c b/drivers/video/fbdev/core/modedb.c index b6926764a99cd..e1fd9298a702c 100644 --- a/drivers/video/fbdev/core/modedb.c +++ b/drivers/video/fbdev/core/modedb.c @@ -259,7 +259,7 @@ static const struct fb_videomode modedb[] = { FB_VMODE_DOUBLE }, /* 1920x1080 @ 60 Hz, 67.3 kHz hsync */ - { NULL, 60, 1920, 1080, 6734, 148, 88, 36, 4, 44, 5, 0, + { NULL, 60, 1920, 1080, 6734, 148, 88, 36, 4, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, From a4c8094bbf4c6fa68b17e3b16f6a0a1b7a14f3e0 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 16:37:12 +0200 Subject: [PATCH 0132/2758] i2c: core: fix adapter registration race commit ba14d7cf2fe7284610a29854bdff22b2537d3ce6 upstream. Adapters can be looked up based on their id using i2c_get_adapter() which takes a reference to the embedded struct device. Make sure that the adapter (including its struct device) has been initialised before adding it to the IDR to avoid accessing uninitialised data which could, for example, lead to NULL-pointer dereferences or use-after-free. Note that the i2c-dev chardev, which is registered from a bus notifier, currently uses i2c_get_adapter() so the adapter needs to be added to the IDR before registration. Fixes: 6e13e6418418 ("i2c: Add i2c_add_numbered_adapter()") Cc: stable@vger.kernel.org # 2.6.22 Signed-off-by: Johan Hovold Signed-off-by: Wolfram Sang Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/i2c-core-base.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index a2132d70fb360..4039d1175c4c4 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -1569,6 +1569,10 @@ static int i2c_register_adapter(struct i2c_adapter *adap) pm_suspend_ignore_children(&adap->dev, true); pm_runtime_enable(&adap->dev); + mutex_lock(&core_lock); + idr_replace(&i2c_adapter_idr, adap, adap->nr); + mutex_unlock(&core_lock); + res = device_add(&adap->dev); if (res) { pr_err("adapter '%s': can't register device (%d)\n", adap->name, res); @@ -1627,7 +1631,7 @@ static int __i2c_add_numbered_adapter(struct i2c_adapter *adap) int id; mutex_lock(&core_lock); - id = idr_alloc(&i2c_adapter_idr, adap, adap->nr, adap->nr + 1, GFP_KERNEL); + id = idr_alloc(&i2c_adapter_idr, NULL, adap->nr, adap->nr + 1, GFP_KERNEL); mutex_unlock(&core_lock); if (WARN(id < 0, "couldn't get idr")) return id == -ENOSPC ? -EBUSY : id; @@ -1661,7 +1665,7 @@ int i2c_add_adapter(struct i2c_adapter *adapter) } mutex_lock(&core_lock); - id = idr_alloc(&i2c_adapter_idr, adapter, + id = idr_alloc(&i2c_adapter_idr, NULL, __i2c_first_dynamic_bus_num, 0, GFP_KERNEL); mutex_unlock(&core_lock); if (WARN(id < 0, "couldn't get idr")) From 83c2b7797742339bb768f83935f7ca33950db138 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Mon, 18 May 2026 13:16:36 -0700 Subject: [PATCH 0133/2758] nfsd: release layout stid on setlease failure commit 30d55c8aabb261bc3f427d6b9aae7ef6206063f9 upstream. nfs4_alloc_stid() publishes the new stid into cl->cl_stateids via idr_alloc_cyclic() under cl_lock before returning to nfsd4_alloc_layout_stateid(). When nfsd4_layout_setlease() then fails, the error path frees the layout stateid directly with kmem_cache_free() without ever calling idr_remove(), leaving the IDR slot pointing at freed slab memory. Any subsequent IDR walker (states_show, client teardown) dereferences the dangling pointer. The correct teardown for an IDR-published stid is nfs4_put_stid(), which removes the IDR slot under cl_lock, dispatches sc_free (nfsd4_free_layout_stateid) to release ls->ls_file via nfsd4_close_layout(), and drops the nfs4_file reference in its tail. A second issue blocks that switch: nfsd4_free_layout_stateid() unconditionally inspects ls->ls_fence_work via delayed_work_pending() under ls_lock, but INIT_DELAYED_WORK(&ls->ls_fence_work, ...) currently runs only after the setlease call. On the setlease-failure path the destructor would touch an uninitialized delayed_work. nfsd4_alloc_layout_stateid() nfs4_alloc_stid() /* idr_alloc_cyclic under cl_lock */ nfsd4_layout_setlease() /* fails */ nfs4_put_stid() nfsd4_free_layout_stateid() delayed_work_pending(&ls->ls_fence_work) /* needs INIT */ nfsd4_close_layout() /* nfsd_file_put(ls->ls_file) */ put_nfs4_file() Fix by hoisting the ls_fenced / ls_fence_delay / INIT_DELAYED_WORK initialization above the nfsd4_layout_setlease() call, and replace the manual nfsd_file_put + put_nfs4_file + kmem_cache_free cleanup with a single nfs4_put_stid(stp). Fixes: c5c707f96fc9 ("nfsd: implement pNFS layout recalls") Cc: stable@vger.kernel.org Assisted-by: kres (claude-opus-4-7) Signed-off-by: Chris Mason Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4layouts.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c index 69e41105efdd5..8e2929cffa7c6 100644 --- a/fs/nfsd/nfs4layouts.c +++ b/fs/nfsd/nfs4layouts.c @@ -264,10 +264,12 @@ nfsd4_alloc_layout_stateid(struct nfsd4_compound_state *cstate, ls->ls_file = find_any_file(fp); BUG_ON(!ls->ls_file); + ls->ls_fenced = false; + ls->ls_fence_delay = 0; + INIT_DELAYED_WORK(&ls->ls_fence_work, nfsd4_layout_fence_worker); + if (nfsd4_layout_setlease(ls)) { - nfsd_file_put(ls->ls_file); - put_nfs4_file(fp); - kmem_cache_free(nfs4_layout_stateid_cache, ls); + nfs4_put_stid(stp); return NULL; } @@ -280,10 +282,6 @@ nfsd4_alloc_layout_stateid(struct nfsd4_compound_state *cstate, list_add(&ls->ls_perfile, &fp->fi_lo_states); spin_unlock(&fp->fi_lock); - ls->ls_fenced = false; - ls->ls_fence_delay = 0; - INIT_DELAYED_WORK(&ls->ls_fence_work, nfsd4_layout_fence_worker); - trace_nfsd_layoutstate_alloc(&ls->ls_stid.sc_stateid); return ls; } From 46eb17d45be69d28c7a23ea03283b207426a8232 Mon Sep 17 00:00:00 2001 From: Guannan Wang Date: Thu, 21 May 2026 16:03:32 +0800 Subject: [PATCH 0134/2758] NFSD: Fix SECINFO_NO_NAME decode error cleanup commit 9e18e83b8846a5c3fe13fc8a464b4865d33996c6 upstream. nfsd4_decode_secinfo_no_name() currently initializes sin_exp after decoding sin_style. If the XDR stream is truncated, the decoder returns nfserr_bad_xdr before sin_exp is initialized. Since commit 3fdc54646234 ("NFSD: Reduce amount of struct nfsd4_compoundargs that needs clearing"), the inline iops array is not cleared between RPC calls. A failed SECINFO_NO_NAME decode can therefore leave sin_exp holding stale union contents from a previous operation. The error response path still invokes nfsd4_secinfo_no_name_release(), which calls exp_put() on a non-NULL sin_exp. Initialize sin_exp before the first failable decode step, matching nfsd4_decode_secinfo(). Fixes: 3fdc54646234 ("NFSD: Reduce amount of struct nfsd4_compoundargs that needs clearing") Cc: stable@vger.kernel.org Signed-off-by: Guannan Wang Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4xdr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 2a0946c630e1d..2da83053fa106 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -2008,10 +2008,11 @@ static __be32 nfsd4_decode_secinfo_no_name(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) { struct nfsd4_secinfo_no_name *sin = &u->secinfo_no_name; + + sin->sin_exp = NULL; if (xdr_stream_decode_u32(argp->xdr, &sin->sin_style) < 0) return nfserr_bad_xdr; - sin->sin_exp = NULL; return nfs_ok; } From a5b42c1e4ff2befaa6b96f7cbf32174751eba083 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 21 May 2026 13:51:43 -0400 Subject: [PATCH 0135/2758] nfsd: fix posix_acl leak on SETACL decode failure commit 0853ac544c590880d797b04daa33fcb72b6be0e1 upstream. nfsaclsvc_decode_setaclargs() and nfs3svc_decode_setaclargs() each call nfs_stream_decode_acl() twice, first for NFS_ACL and then for NFS_DFACL. Each successful call transfers ownership of a freshly allocated posix_acl into argp->acl_access or argp->acl_default. If the first call succeeds but the second fails, the decoder returns false and argp->acl_access is left dangling. ACLPROC2_SETACL.pc_release was wired to nfssvc_release_attrstat and ACLPROC3_SETACL.pc_release was wired to nfs3svc_release_fhandle. Both only call fh_put() and have no knowledge of the ACL fields on argp. The posix_acl_release() pairs sat at the out: labels inside nfsacld_proc_setacl() and nfsd3_proc_setacl(), but svc_process() skips pc_func when pc_decode returns false, so that cleanup is unreachable on decode failure: svc_process_common() pc_decode() /* decode_setaclargs: false */ /* pc_func skipped */ pc_release() /* fh_put only -- ACLs leaked */ The orphaned posix_acl is leaked for the lifetime of the server. Fix by adding nfsaclsvc_release_setacl() and nfs3svc_release_setacl(), which release both argp->acl_access and argp->acl_default in addition to fh_put(), and wiring them as pc_release for their respective SETACL procedures. pc_release runs on every path svc_process() takes after decode, including decode failure, so the posix_acl_release() pairs are removed from the proc functions' out: labels to keep ownership in one place. This matches the existing release_getacl() pattern used by the sibling GETACL procedures. Fixes: a257cdd0e217 ("[PATCH] NFSD: Add server support for NFSv3 ACLs.") Cc: stable@vger.kernel.org Assisted-by: kres:claude-opus-4-7 Signed-off-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs2acl.c | 17 ++++++++++++----- fs/nfsd/nfs3acl.c | 17 ++++++++++++----- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/fs/nfsd/nfs2acl.c b/fs/nfsd/nfs2acl.c index 0ac538c761800..76305b86c1a95 100644 --- a/fs/nfsd/nfs2acl.c +++ b/fs/nfsd/nfs2acl.c @@ -131,10 +131,7 @@ static __be32 nfsacld_proc_setacl(struct svc_rqst *rqstp) resp->status = fh_getattr(fh, &resp->stat); out: - /* argp->acl_{access,default} may have been allocated in - nfssvc_decode_setaclargs. */ - posix_acl_release(argp->acl_access); - posix_acl_release(argp->acl_default); + /* argp->acl_{access,default} are released in nfsaclsvc_release_setacl. */ return rpc_success; out_drop_lock: @@ -310,6 +307,16 @@ static void nfsaclsvc_release_access(struct svc_rqst *rqstp) fh_put(&resp->fh); } +static void nfsaclsvc_release_setacl(struct svc_rqst *rqstp) +{ + struct nfsd3_setaclargs *argp = rqstp->rq_argp; + struct nfsd_attrstat *resp = rqstp->rq_resp; + + fh_put(&resp->fh); + posix_acl_release(argp->acl_access); + posix_acl_release(argp->acl_default); +} + #define ST 1 /* status*/ #define AT 21 /* attributes */ #define pAT (1+AT) /* post attributes - conditional */ @@ -343,7 +350,7 @@ static const struct svc_procedure nfsd_acl_procedures2[5] = { .pc_func = nfsacld_proc_setacl, .pc_decode = nfsaclsvc_decode_setaclargs, .pc_encode = nfssvc_encode_attrstatres, - .pc_release = nfssvc_release_attrstat, + .pc_release = nfsaclsvc_release_setacl, .pc_argsize = sizeof(struct nfsd3_setaclargs), .pc_argzero = sizeof(struct nfsd3_setaclargs), .pc_ressize = sizeof(struct nfsd_attrstat), diff --git a/fs/nfsd/nfs3acl.c b/fs/nfsd/nfs3acl.c index 7b5433bd30197..e87731380be85 100644 --- a/fs/nfsd/nfs3acl.c +++ b/fs/nfsd/nfs3acl.c @@ -118,10 +118,7 @@ static __be32 nfsd3_proc_setacl(struct svc_rqst *rqstp) out_errno: resp->status = nfserrno(error); out: - /* argp->acl_{access,default} may have been allocated in - nfs3svc_decode_setaclargs. */ - posix_acl_release(argp->acl_access); - posix_acl_release(argp->acl_default); + /* argp->acl_{access,default} are released in nfs3svc_release_setacl. */ return rpc_success; } @@ -223,6 +220,16 @@ static void nfs3svc_release_getacl(struct svc_rqst *rqstp) posix_acl_release(resp->acl_default); } +static void nfs3svc_release_setacl(struct svc_rqst *rqstp) +{ + struct nfsd3_setaclargs *argp = rqstp->rq_argp; + struct nfsd3_attrstat *resp = rqstp->rq_resp; + + fh_put(&resp->fh); + posix_acl_release(argp->acl_access); + posix_acl_release(argp->acl_default); +} + #define ST 1 /* status*/ #define AT 21 /* attributes */ #define pAT (1+AT) /* post attributes - conditional */ @@ -256,7 +263,7 @@ static const struct svc_procedure nfsd_acl_procedures3[3] = { .pc_func = nfsd3_proc_setacl, .pc_decode = nfs3svc_decode_setaclargs, .pc_encode = nfs3svc_encode_setaclres, - .pc_release = nfs3svc_release_fhandle, + .pc_release = nfs3svc_release_setacl, .pc_argsize = sizeof(struct nfsd3_setaclargs), .pc_argzero = sizeof(struct nfsd3_setaclargs), .pc_ressize = sizeof(struct nfsd3_attrstat), From 80866c84137e624045e364da321a3c422fd7e964 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 21 May 2026 09:25:40 -0400 Subject: [PATCH 0136/2758] nfsd: fix inverted cp_ttl check in async copy reaper commit 0150459b05490b88b7e7378a31550a9e07b5517c upstream. nfsd4_async_copy_reaper() is supposed to keep completed async copy state around for NFSD_COPY_INITIAL_TTL (10) laundromat ticks so that OFFLOAD_STATUS can report the result, then reap the state once the countdown expires. The TTL predicate is inverted: `if (--copy->cp_ttl)` is true while ticks remain and false when the counter reaches zero. This causes the copy to be reaped on the very first tick (cp_ttl goes from 10 to 9, which is non-zero) instead of after all 10 ticks elapse. Once reaped, OFFLOAD_STATUS returns NFS4ERR_BAD_STATEID because the copy state has already been freed. Fix by negating the test so that cleanup runs when the TTL expires. Fixes: aa0ebd21df9c ("NFSD: Add nfsd4_copy time-to-live") Cc: stable@vger.kernel.org Reported-by: Chris Mason Assisted-by: kres:claude-opus-4-6 Signed-off-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4proc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index ab39ec8854405..972a6eefce7c0 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -1470,7 +1470,7 @@ void nfsd4_async_copy_reaper(struct nfsd_net *nn) list_for_each_safe(pos, next, &clp->async_copies) { copy = list_entry(pos, struct nfsd4_copy, copies); if (test_bit(NFSD4_COPY_F_OFFLOAD_DONE, ©->cp_flags)) { - if (--copy->cp_ttl) { + if (!--copy->cp_ttl) { list_del_init(©->copies); list_add(©->copies, &reaplist); } From 18cf006a08babec0bbac2a3784f8f28e56e47490 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 21 May 2026 12:37:33 -0400 Subject: [PATCH 0137/2758] nfsd: fix posix_acl leak and ignored error in nfsd4_create_file commit 24c975bbdd564d7d0ad90294bfa69729830345de upstream. nfsd4_create_file() has two bugs in its ACL handling: The return value of nfsd4_acl_to_attr() is silently discarded. When the NFSv4-to-POSIX ACL conversion fails (e.g., -EINVAL for unsupported ACE types), the file is created without any ACL and the client receives NFS4_OK. This violates RFC 7530/8881 which require the server to reject unsupported attributes on CREATE. When start_creating() fails after ACL attributes have been populated in attrs (either via nfsd4_acl_to_attr or via ownership transfer from open->op_dpacl/op_pacl), the function jumps to out_write which skips nfsd_attrs_free(). The posix_acl allocations are leaked. A client can trigger this repeatedly with OPEN(CREATE), ACL attributes, and an invalid filename (e.g., longer than NAME_MAX). Fix both by capturing the nfsd4_acl_to_attr() return value and by changing the early error paths to jump to out instead of out_write. Initialize child to ERR_PTR(-EINVAL) so that end_creating() is safe to call even if start_creating() was never reached. Reported-by: Chris Mason Fixes: 7ab96df840e6 ("VFS/nfsd/cachefiles/ovl: add start_creating() and end_creating()") Cc: stable@vger.kernel.org Assisted-by: kres:claude-opus-4-6 Signed-off-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4proc.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 972a6eefce7c0..3428eaf971f52 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -253,7 +253,7 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, .na_iattr = iap, .na_seclabel = &open->op_label, }; - struct dentry *parent, *child; + struct dentry *parent, *child = ERR_PTR(-EINVAL); __u32 v_mtime, v_atime; struct inode *inode; __be32 status; @@ -277,10 +277,14 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, if (open->op_acl) { if (open->op_dpacl || open->op_pacl) { status = nfserr_inval; - goto out_write; + goto out; + } + if (is_create_with_attrs(open)) { + status = nfsd4_acl_to_attr(NF4REG, open->op_acl, + &attrs); + if (status) + goto out; } - if (is_create_with_attrs(open)) - nfsd4_acl_to_attr(NF4REG, open->op_acl, &attrs); } else if (is_create_with_attrs(open)) { /* The dpacl and pacl will get released by nfsd_attrs_free(). */ attrs.na_dpacl = open->op_dpacl; @@ -293,7 +297,7 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, &QSTR_LEN(open->op_fname, open->op_fnamelen)); if (IS_ERR(child)) { status = nfserrno(PTR_ERR(child)); - goto out_write; + goto out; } if (d_really_is_negative(child)) { @@ -407,7 +411,6 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, out: end_creating(child); nfsd_attrs_free(&attrs); -out_write: fh_drop_write(fhp); return status; } From ff3ecd17db74a6a97e6b19d920420382ddf6bca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Wo=C5=BAniak?= Date: Thu, 21 May 2026 17:46:56 +0200 Subject: [PATCH 0138/2758] nfsd: check get_user() return when reading princhashlen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit e186fa1c057f5eccb22afb1e83e34c0627085868 upstream. In __cld_pipe_inprogress_downcall(), the get_user() that reads princhashlen from the userspace cld_msg_v2 buffer does not check its return value. A failing copy leaves princhashlen with uninitialised stack contents, which are then used to drive memdup_user() and stored as princhash.len on the resulting reclaim record. The other get_user() calls in this function all check the return; only this one is missed, which is most likely a copy-paste oversight from when v2 upcalls were introduced. Mirror the existing pattern used a few lines above for namelen. namecopy is declared with __free(kfree) so the early return cleans up the already-allocated buffer automatically. Fixes: 6ee95d1c8991 ("nfsd: add support for upcall version 2") Cc: stable@vger.kernel.org Signed-off-by: Dominik Woźniak Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4recover.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index b338473d6e52d..6ea25a52d2f4e 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c @@ -718,7 +718,8 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, return PTR_ERR(namecopy); name.data = namecopy; name.len = namelen; - get_user(princhashlen, &ci->cc_princhash.cp_len); + if (get_user(princhashlen, &ci->cc_princhash.cp_len)) + return -EFAULT; if (princhashlen > 0) { princhashcopy = memdup_user( &ci->cc_princhash.cp_data, From 8371cc5c0a2cc2a71b3dcfd47ff1f7fcfc526a5e Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 21 May 2026 07:50:21 -0400 Subject: [PATCH 0139/2758] nfsd: fix dead ACL conflict guard in nfsd4_create commit a60f25a800846ab8e5a13f8a9d05111f2aee55a7 upstream. nfsd4_create() steals create->cr_dpacl/cr_pacl into the local nfsd_attrs via the designated initializer, then immediately sets the source pointers to NULL. The subsequent conflict guard tests the already-nilled source fields, making it permanently dead code: if (create->cr_acl) { if (create->cr_dpacl || create->cr_pacl) /* always false */ When a client encodes both FATTR4_WORD0_ACL and FATTR4_WORD2_POSIX_{DEFAULT,ACCESS}_ACL in the same CREATE fattr bitmap, nfsd4_acl_to_attr() overwrites attrs.na_pacl/na_dpacl without releasing the originals, leaking two posix_acl slab objects per request. Repeated requests cause unbounded slab exhaustion. Fix by checking attrs.na_dpacl/na_pacl (the stolen values) instead of the nilled create->cr_dpacl/cr_pacl, matching the correct pattern already used in nfsd4_setattr(). Reported-by: Chris Mason Assisted-by: kres:claude-opus-4-6 Fixes: d2ca50606f5f ("NFSD: Add support for POSIX draft ACLs for file creation") Cc: stable@vger.kernel.org Signed-off-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4proc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 3428eaf971f52..8561540ab2db9 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -840,7 +840,7 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out_aftermask; if (create->cr_acl) { - if (create->cr_dpacl || create->cr_pacl) { + if (attrs.na_dpacl || attrs.na_pacl) { status = nfserr_inval; goto out_aftermask; } From a10bf67fe06469a71a401f72f328237345d553c0 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 22 May 2026 10:36:14 -0400 Subject: [PATCH 0140/2758] nfsd: avoid leaking pre-allocated openowner on unconfirmed retry race commit 57aee7a35bb12753057c5b65d72d1f46c0e95b07 upstream. When find_or_alloc_open_stateowner() encounters an unconfirmed owner, it calls release_openowner() and sets oo = NULL. Control then falls through past the `if (oo)` guard -- which would have freed any pre-allocated `new` -- and unconditionally executes `new = alloc_stateowner(...)`. If `new` was already allocated on a prior iteration, the pointer is silently overwritten and the previous allocation (slab object + owner name buffer) is leaked. This requires a race: two NFSv4.0 OPEN threads with the same owner string, where a concurrent thread inserts a new unconfirmed owner into the hash between retry iterations. The window is narrow but repeatable under adversarial conditions. Fix by adding `goto retry` after `oo = NULL` so the already-allocated `new` is reused on the next iteration rather than overwritten. Reported-by: Chris Mason Fixes: 23df17788c62 ("nfsd: perform all find_openstateowner_str calls in the one place.") Cc: stable@vger.kernel.org Assisted-by: kres:claude-opus-4-6 Signed-off-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4state.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 6837b63d98645..d875e98d4dcbd 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -5169,6 +5169,7 @@ find_or_alloc_open_stateowner(unsigned int strhashval, struct nfsd4_open *open, /* Replace unconfirmed owners without checking for replay. */ release_openowner(oo); oo = NULL; + goto retry; } if (oo) { if (new) From b027cca33c97354149fcc0ddeede4525c41093cd Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 22 May 2026 12:44:19 -0400 Subject: [PATCH 0141/2758] nfsd: reset write verifier on deferred writeback errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 2090b05803faab8a9fa62fbff871007862cac1b7 upstream. nfsd_vfs_write() and nfsd_commit() both call filemap_check_wb_err() to detect deferred writeback errors, but neither rotates the server's write verifier (nn->writeverf) when this check fails. Every other durable-storage-failure path in these functions calls commit_reset_write_verifier() before returning an error. The missing rotation means clients holding UNSTABLE write data under the current verifier will COMMIT, receive the unchanged verifier back, and conclude their data is durable — silently dropping data that failed writeback. This violates the UNSTABLE+COMMIT durability contract (RFC 1813 §3.3.7, RFC 8881 §18.32). Add commit_reset_write_verifier() calls at both filemap_check_wb_err() error sites, matching the pattern used by adjacent error paths in the same functions. The helper already filters -EAGAIN and -ESTALE internally, so the calls are unconditionally safe. Reported-by: Chris Mason Fixes: 555dbf1a9aac ("nfsd: Replace use of rwsem with errseq_t") Cc: stable@vger.kernel.org Assisted-by: kres:claude-opus-4-6 Signed-off-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/vfs.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index eafdf7b7890fd..2c557a9989616 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1508,8 +1508,10 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, nfsd_stats_io_write_add(nn, exp, *cnt); fsnotify_modify(file); host_err = filemap_check_wb_err(file->f_mapping, since); - if (host_err < 0) + if (host_err < 0) { + commit_reset_write_verifier(nn, rqstp, host_err); goto out_nfserr; + } if (stable && fhp->fh_use_wgather) { host_err = wait_for_concurrent_writes(file); @@ -1689,6 +1691,8 @@ nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfsd_file *nf, nfsd_copy_write_verifier(verf, nn); err2 = filemap_check_wb_err(nf->nf_file->f_mapping, since); + if (err2 < 0) + commit_reset_write_verifier(nn, rqstp, err2); err = nfserrno(err2); break; case -EINVAL: From 2131ed64b767ffa8bcdb3677d90f3964e39aabc8 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 13 May 2026 12:26:56 -0400 Subject: [PATCH 0142/2758] NFSv4/flexfiles: reject zero filehandle version count commit 2c6bb3c40bc24f6aa8dfbe6fe98c3ad6389203f2 upstream. ff_layout_alloc_lseg() decodes the filehandle-version array count from the flexfiles layout body. The value is used as the count for kzalloc_objs(), and the current code only rejects NULL. A zero count yields ZERO_SIZE_PTR, which can be stored in dss_info->fh_versions even though later flexfiles paths assume that at least one filehandle version exists. Reject fh_count == 0 before the allocation, matching the existing zero version_count validation in the flexfiles GETDEVICEINFO parser. A QEMU/KASAN run with a malformed flexfiles layout hit: KASAN: null-ptr-deref in range [0x0000000000000010-0x0000000000000017] RIP: 0010:ff_layout_encode_ff_layoutupdate.isra.0+0x15f/0x750 ff_layout_encode_layoutreturn+0x683/0x970 nfs4_xdr_enc_layoutreturn+0x278/0x3a0 Kernel panic - not syncing: Fatal exception The patched kernel rejects the malformed layout without KASAN/oops/panic, and a valid fh_count=1 regression still opens, reads, and unmounts cleanly. Cc: stable@vger.kernel.org Fixes: d67ae825a59d ("pnfs/flexfiles: Add the FlexFile Layout Driver") Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Signed-off-by: Anna Schumaker Signed-off-by: Greg Kroah-Hartman --- fs/nfs/flexfilelayout/flexfilelayout.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c index 8b1559171fe39..e22a8e0daf2c5 100644 --- a/fs/nfs/flexfilelayout/flexfilelayout.c +++ b/fs/nfs/flexfilelayout/flexfilelayout.c @@ -551,6 +551,10 @@ ff_layout_alloc_lseg(struct pnfs_layout_hdr *lh, if (!p) goto out_err_free; fh_count = be32_to_cpup(p); + if (fh_count == 0) { + rc = -EINVAL; + goto out_err_free; + } dss_info->fh_versions = kzalloc_objs(struct nfs_fh, fh_count, gfp_flags); From 30aae62e50b4e074a90a9a5e15246548fbdc1182 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 27 May 2026 12:30:35 -0400 Subject: [PATCH 0143/2758] NFSv4/pNFS: reject zero-length r_addr in nfs4_decode_mp_ds_addr commit 41fe0f7b84f0cb822ae10ab08592996a592b2a25 upstream. nfs4_decode_mp_ds_addr() decodes the r_netid and r_addr opaques of a netaddr4 from a GETDEVICEINFO multipath-DS body, then immediately calls strrchr(buf, '.') to locate the port separator. Both decodes use xdr_stream_decode_string_dup(), and the current code checks only "nlen < 0" / "rlen < 0" before dereferencing the returned string. When the on-wire opaque has length zero, xdr_stream_decode_opaque_inline() returns 0 and xdr_stream_decode_string_dup() falls through to its "*str = NULL; return ret" tail, leaving buf NULL with a return value of 0. The "< 0" check does not catch this, and the next line is strrchr(NULL, '.'), a kernel NULL pointer dereference reachable from any pNFS-flexfile client mounted against a malicious or compromised metadata server. Reject the zero-length cases explicitly so the decoder fails with -EBADMSG (treated as a malformed GETDEVICEINFO body) instead of panicking the client. Cc: stable@vger.kernel.org Fixes: 6b7f3cf96364 ("nfs41: pull decode_ds_addr from file layout to generic pnfs") Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Signed-off-by: Anna Schumaker Signed-off-by: Greg Kroah-Hartman --- fs/nfs/pnfs_nfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/nfs/pnfs_nfs.c b/fs/nfs/pnfs_nfs.c index 12632a706da88..0ff43dbcb7cd7 100644 --- a/fs/nfs/pnfs_nfs.c +++ b/fs/nfs/pnfs_nfs.c @@ -1075,14 +1075,14 @@ nfs4_decode_mp_ds_addr(struct net *net, struct xdr_stream *xdr, gfp_t gfp_flags) /* r_netid */ nlen = xdr_stream_decode_string_dup(xdr, &netid, XDR_MAX_NETOBJ, gfp_flags); - if (unlikely(nlen < 0)) + if (unlikely(nlen <= 0)) goto out_err; /* r_addr: ip/ip6addr with port in dec octets - see RFC 5665 */ /* port is ".ABC.DEF", 8 chars max */ rlen = xdr_stream_decode_string_dup(xdr, &buf, INET6_ADDRSTRLEN + IPV6_SCOPE_ID_LEN + 8, gfp_flags); - if (unlikely(rlen < 0)) + if (unlikely(rlen <= 0)) goto out_free_netid; /* replace port '.' with '-' */ From a2c8befd06a4d42fe4eaab15e6cf0b86af9b1d1f Mon Sep 17 00:00:00 2001 From: Igor Raits Date: Wed, 29 Apr 2026 12:49:38 +0200 Subject: [PATCH 0144/2758] NFSv4: clear exception state on successful mkdir retry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 238e9b51aa29f48b6243212a3b75c8e48d6b96fd upstream. After a server returns NFS4ERR_DELAY for an NFSv4 CREATE issued by mkdir(2), the client correctly waits and retries. When the retry succeeds, however, mkdir(2) can still surface -EEXIST to userspace even though the directory was just created on the server. Reproducer (random 16-hex names so collisions are not the cause) against an in-kernel Linux nfsd; reproduces under both NFSv4.0 and NFSv4.2: N=2000000; base=/var/gdc/export for ((i=1; i<=N; i++)); do d=$base/$(openssl rand -hex 8) mkdir "$d" 2>/dev/null || echo "$(date +%T) failed loop=$i $d" rmdir "$d" 2>/dev/null done Failures cluster at the cadence at which the server-side auth/export cache refresh path causes nfsd to return NFS4ERR_DELAY for CREATE. A wire trace of one failure (the three CREATE RPCs all come from a single mkdir(2), generated by the do-while in nfs4_proc_mkdir()): client -> server CREATE name=... -> NFS4ERR_DELAY ~100 ms later client -> server CREATE name=... -> NFS4_OK (dir created) ~80 us later client -> server CREATE name=... -> NFS4ERR_EXIST (correct) Since commit dd862da61e91 ("nfs: fix incorrect handling of large-number NFS errors in nfs4_do_mkdir()"), nfs4_handle_exception() is called only when _nfs4_proc_mkdir() returned an error. That gate breaks retry-state hygiene: nfs4_do_handle_exception() resets exception.{delay,recovering, retry} to 0 on entry, so calling it on success is what previously cleared the retry flag set by the preceding NFS4ERR_DELAY iteration. With the gate in place, exception.retry stays at 1 after the successful retry, the loop runs once more, and the resulting CREATE for an already-created name yields NFS4ERR_EXIST -> -EEXIST to userspace. Drop the conditional and call nfs4_handle_exception() unconditionally, matching every other do-while in fs/nfs/nfs4proc.c (nfs4_proc_symlink(), nfs4_proc_link(), etc.). The dentry/status separation introduced by that commit is preserved. Fixes: dd862da61e91 ("nfs: fix incorrect handling of large-number NFS errors in nfs4_do_mkdir()") Reported-and-tested-by: Jan Čípa Closes: https://lore.kernel.org/linux-nfs/CA+9S74hSp_tJu2Ffe2BPNC2T25gfkhgjjDkdgSsF5c2rnJq_wA@mail.gmail.com/ Reviewed-by: NeilBrown Cc: stable@vger.kernel.org Signed-off-by: Igor Raits Signed-off-by: Anna Schumaker Signed-off-by: Greg Kroah-Hartman --- fs/nfs/nfs4proc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index a9b8d482d2894..c354b7b902939 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -5302,10 +5302,9 @@ static struct dentry *nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry, do { alias = _nfs4_proc_mkdir(dir, dentry, sattr, label, &err); trace_nfs4_mkdir(dir, &dentry->d_name, err); + err = nfs4_handle_exception(NFS_SERVER(dir), err, &exception); if (err) - alias = ERR_PTR(nfs4_handle_exception(NFS_SERVER(dir), - err, - &exception)); + alias = ERR_PTR(err); } while (exception.retry); nfs4_label_release_security(label); From 65b1bb5d24e58e5eede54107d5c991a15d4e2955 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sun, 14 Jun 2026 09:56:35 +0200 Subject: [PATCH 0145/2758] NFS: Prevent resource leak in nfs_alloc_server() commit d189f224308c8ac3feeea8e442c99922bd18f1b2 upstream. It was overlooked to call ida_free() after a failed nfs_alloc_iostats() call. Thus add the missed function call in an if branch. Fixes: 1c7251187dc067a6d460cf33ca67da9c1dd87807 ("NFS: add superblock sysfs entries") Cc: stable@vger.kernel.org Reported-by: Christophe Jaillet Closes: https://lore.kernel.org/linux-nfs/1c8e10c9-def7-4f0d-8aa1-23c8035a38c8@wanadoo.fr/ Signed-off-by: Markus Elfring Signed-off-by: Anna Schumaker Signed-off-by: Greg Kroah-Hartman --- fs/nfs/client.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/nfs/client.c b/fs/nfs/client.c index be02bb227741d..0781b15e7e05c 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -1074,6 +1074,7 @@ struct nfs_server *nfs_alloc_server(void) server->io_stats = nfs_alloc_iostats(); if (!server->io_stats) { + ida_free(&s_sysfs_ids, server->s_sysfs_id); kfree(server); return NULL; } From e36e35660adb9b8ef1435ac359151dda5f094c55 Mon Sep 17 00:00:00 2001 From: Hem Parekh Date: Tue, 2 Jun 2026 16:56:46 -0700 Subject: [PATCH 0146/2758] ksmbd: fix out-of-bounds read in smb_check_perm_dacl() commit 1ef06004ed4bd6d3ed8c840d9d1a376b66d4935b upstream. The permission-check ACE walk in smb_check_perm_dacl() validates the ACE header size and caps sid.num_subauth at SID_MAX_SUB_AUTHORITIES, but it never checks that ace->size is actually large enough to contain num_subauth sub-authorities before compare_sids() dereferences them. CIFS_SID_BASE_SIZE covers the SID header up to but excluding the sub_auth[] array, and offsetof(struct smb_ace, sid) is the ACE header, so the existing guards only guarantee the 8-byte SID base, i.e. zero sub-authorities. compare_sids() then reads ace->sid.sub_auth[i] for i < min(local_sid->num_subauth, ace->sid.num_subauth). The local comparison SIDs (sid_everyone, sid_unix_NFS_mode, and the id_to_sid() result) always have at least one sub-authority, and an attacker controls the ACE revision and authority bytes (which lie within the in-bounds SID base), so they can match one of those SIDs and force the sub_auth read. A crafted ACE with size == 16 and num_subauth >= 1 placed at the tail of the security descriptor therefore causes a heap out-of-bounds read of up to SID_MAX_SUB_AUTHORITIES * sizeof(__le32) bytes past the pntsd allocation. The security descriptor is loaded by ksmbd_vfs_get_sd_xattr() into a buffer sized exactly to the on-disk data (kzalloc(sd_size) in ndr_decode_v4_ntacl()), so the read lands past the allocation. The malformed descriptor can be stored verbatim via SMB2_SET_INFO (the DACL is not normalised before being written to the security.NTACL xattr) and the read fires on a subsequent SMB2_CREATE access check, making this reachable by an authenticated client on a share that uses ACL xattrs. Add the missing num_subauth-versus-ace_size check, mirroring the identical guards already present in the sibling parsers parse_dacl() and smb_inherit_dacl(). Fixes: d07b26f39246 ("ksmbd: require minimum ACE size in smb_check_perm_dacl()") Cc: stable@vger.kernel.org Signed-off-by: Hem Parekh Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smbacl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c index 664b1b4a3233d..340ea98fa494c 100644 --- a/fs/smb/server/smbacl.c +++ b/fs/smb/server/smbacl.c @@ -1477,7 +1477,9 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, break; aces_size -= ace_size; - if (ace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES) + if (ace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES || + ace_size < offsetof(struct smb_ace, sid) + CIFS_SID_BASE_SIZE + + sizeof(__le32) * ace->sid.num_subauth) break; if (!compare_sids(&sid, &ace->sid) || From 7ddc29a094d96e9b3aa280433c6dc443df9eabf2 Mon Sep 17 00:00:00 2001 From: HanQuan Date: Tue, 23 Jun 2026 01:52:08 +0000 Subject: [PATCH 0147/2758] net/tcp-ao: fix use-after-free of key in del_async path commit 5ba9950bc9078e19b69cca1e56d1553b125c6857 upstream. In tcp_ao_delete_key(), the del_async path skips the current_key and rnext_key validity checks present in the synchronous path, assuming these pointers are always NULL on LISTEN sockets. However, if a key was added with set_current=1/set_rnext=1 while the socket was in CLOSE state, current_key and rnext_key will be non-NULL after listen() transitions the socket to LISTEN. When such a key is deleted with del_async=1, hlist_del_rcu() and call_rcu() free the key without clearing the dangling pointers. After the RCU grace period, getsockopt(TCP_AO_INFO) dereferences current_key->sndid and rnext_key->rcvid from freed slab memory. Clear current_key and rnext_key in the del_async path when they reference the key being deleted. Fixes: d6732b95b6fb ("net/tcp: Allow asynchronous delete for TCP-AO keys (MKTs)") Signed-off-by: HanQuan Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260623015208.1191687-1-eilaimemedsnaimel@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv4/tcp_ao.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index 0a4b38b315fed..36984e9050700 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -1776,6 +1776,10 @@ static int tcp_ao_delete_key(struct sock *sk, struct tcp_ao_info *ao_info, * them and we can just free all resources in RCU fashion. */ if (del_async) { + if (ao_info->current_key == key) + WRITE_ONCE(ao_info->current_key, NULL); + if (ao_info->rnext_key == key) + WRITE_ONCE(ao_info->rnext_key, NULL); atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); call_rcu(&key->rcu, tcp_ao_key_free_rcu); return 0; From 5b872b77bd356ae23fe5b57523d7df9ee224253f Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 22 Jun 2026 16:34:13 -0700 Subject: [PATCH 0148/2758] apparmor: advertise the tcp fast open fix is applied commit 2f6701a5ce6257ae7a64ddc6d89d0a08d2a034f8 upstream. The fix for tcp-fast-open ensures that the connect permission is being mediated correctly but it didn't add an artifact to the feature set to advertise the fix is available. Add an artifact so that the test suite can identify if the fix has not been properly applied or a new unexpected regression has occurred. Fixes: 4d587cd8a7215 ("apparmor: mediate the implicit connect of TCP fast open sendmsg") Signed-off-by: John Johansen Signed-off-by: Greg Kroah-Hartman --- security/apparmor/net.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/security/apparmor/net.c b/security/apparmor/net.c index 44c04102062f3..1fc6145ccbb8d 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -22,12 +22,14 @@ struct aa_sfs_entry aa_sfs_entry_network[] = { AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK), + AA_SFS_FILE_BOOLEAN("tcp-fast-open", 1), { } }; struct aa_sfs_entry aa_sfs_entry_networkv9[] = { AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK), AA_SFS_FILE_BOOLEAN("af_unix", 1), + AA_SFS_FILE_BOOLEAN("tcp-fast-open", 1), { } }; From 199c9959d3a9b53f346c221757fc7ac507fbac50 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 4 Jul 2026 13:45:09 +0200 Subject: [PATCH 0149/2758] Linux 7.1.3 Link: https://lore.kernel.org/r/20260702155112.964534952@linuxfoundation.org Tested-by: Ronald Warsow Tested-by: Brett A C Sheffield Tested-by: Salvatore Bonaccorso Tested-by: Peter Schneider Tested-by: Miguel Ojeda Link: https://lore.kernel.org/r/20260703072822.817328079@linuxfoundation.org Tested-by: Pavel Machek (CIP) Tested-by: Brett A C Sheffield Tested-by: Ronald Warsow Tested-by: Dileep Malepu Tested-by: Mark Brown Tested-by: Takeshi Ogasawara Tested-by: Peter Schneider Tested-by: Ron Economos Tested-by: Miguel Ojeda Tested-by: Barry K. Nathan Signed-off-by: Greg Kroah-Hartman --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 68d8b4fc2c675..9e405218162b6 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 7 PATCHLEVEL = 1 -SUBLEVEL = 2 +SUBLEVEL = 3 EXTRAVERSION = NAME = Baby Opossum Posse From 77ddefb1aedaf99480cfe5e1a034f7130f7957c0 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Tue, 9 Jun 2026 12:41:51 +0200 Subject: [PATCH 0150/2758] rust: str: use the "kernel vertical" imports style [ Upstream commit 724a93a9f6033800b02a3530dbcb464638448e7f ] Convert the imports to use the "kernel vertical" imports style [1]. No functional changes intended. Link: https://docs.kernel.org/rust/coding-guidelines.html#imports [1] Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260609104152.261145-1-ojeda@kernel.org Signed-off-by: Miguel Ojeda Stable-dep-of: 3fff4271809b ("rust: str: clean unused import for Rust >= 1.98") Signed-off-by: Sasha Levin --- rust/kernel/str.rs | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs index 8311d91549e15..a7fccd4c4f3b1 100644 --- a/rust/kernel/str.rs +++ b/rust/kernel/str.rs @@ -3,14 +3,28 @@ //! String representations. use crate::{ - alloc::{flags::*, AllocError, KVec}, - error::{to_result, Result}, - fmt::{self, Write}, - prelude::*, + alloc::{ + flags::*, + AllocError, + KVec, // + }, + error::{ + to_result, + Result, // + }, + fmt::{ + self, + Write, // + }, + prelude::*, // }; use core::{ marker::PhantomData, - ops::{Deref, DerefMut, Index}, + ops::{ + Deref, + DerefMut, + Index, // + }, // }; pub use crate::prelude::CStr; From 6537884e9cf2f415bfba38a93d7bdef291dd3867 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Tue, 9 Jun 2026 12:41:52 +0200 Subject: [PATCH 0151/2758] rust: str: clean unused import for Rust >= 1.98 [ Upstream commit 3fff4271809b57182c4011811e96556bdd4cb2f9 ] Starting with Rust 1.98.0 (expected 2026-08-20), the compiler has changed how the resolution algorithm works [1] in upstream commit c4d84db5f184 ("Resolver: Batched import resolution."), and it now spots: error: unused import: `flags::*` --> rust/kernel/str.rs:7:9 | 7 | flags::*, | ^^^^^^^^ | = note: `-D unused-imports` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(unused_imports)]` It happens to not be needed because the `prelude::*` already provides the flags. Thus clean it up. Cc: stable@vger.kernel.org # Needed in 6.18.y and later (prelude added to `str`). Link: https://github.com/rust-lang/rust/pull/145108 [1] Reviewed-by: Gary Guo Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20260609104152.261145-2-ojeda@kernel.org Signed-off-by: Miguel Ojeda Signed-off-by: Sasha Levin --- rust/kernel/str.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs index a7fccd4c4f3b1..4517c1bc547af 100644 --- a/rust/kernel/str.rs +++ b/rust/kernel/str.rs @@ -4,7 +4,6 @@ use crate::{ alloc::{ - flags::*, AllocError, KVec, // }, From 710183888174639a15fcec16cd1af766b8480bb7 Mon Sep 17 00:00:00 2001 From: Kiryl Shutsemau Date: Thu, 2 Jul 2026 16:49:08 +0100 Subject: [PATCH 0152/2758] userfaultfd: gate must_wait writability check on pte_present() [ Upstream commit 8e80af52db652fbc41320eee45a4f73bc029faf2 ] userfaultfd_must_wait() and userfaultfd_huge_must_wait() read the PTE without taking the page table lock and then apply pte_write() / huge_pte_write() to it. Those accessors decode bits from the present encoding only; on a swap or migration entry they read the offset bits that happen to share the same position and return an undefined result. The intent of the check is "is this fault still WP-blocked?". A non-marker swap entry means the page is in transit -- the userfault context the original fault delivered against is no longer the same, and the swap-in or migration completion path will re-deliver a fresh fault if userspace still needs to handle it. Worst case under the current code the garbage write bit says "wait", and the thread stays asleep until a UFFDIO_WAKE that may never arrive. Gate the writability check on pte_present() so the lockless re-check only inspects present-PTE bits when the entry is actually present. The non-present, non-marker case returns "don't wait" and lets the fault path retry. Link: https://lore.kernel.org/20260529172331.356655-6-kas@kernel.org Fixes: 369cd2121be4 ("userfaultfd: hugetlbfs: userfaultfd_huge_must_wait for hugepmd ranges") Fixes: 63b2d4174c4a ("userfaultfd: wp: add the writeprotect API to userfaultfd ioctl") Signed-off-by: Kiryl Shutsemau Reported-by: Sashiko AI review Reviewed-by: Lorenzo Stoakes Cc: David Hildenbrand Cc: Michal Hocko Cc: Mike Rapoport Cc: Peter Xu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Balbir Singh Cc: Signed-off-by: Andrew Morton [ kas: apply to fs/userfaultfd.c; these checks moved to mm/userfaultfd.c only after 7.1, the change is otherwise identical to upstream ] Signed-off-by: Kiryl Shutsemau Signed-off-by: Sasha Levin --- fs/userfaultfd.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c index 390e4b7d9cb9f..dba1172436b74 100644 --- a/fs/userfaultfd.c +++ b/fs/userfaultfd.c @@ -253,6 +253,15 @@ static inline bool userfaultfd_huge_must_wait(struct userfaultfd_ctx *ctx, /* UFFD PTE markers require userspace to resolve the fault. */ if (pte_is_uffd_marker(pte)) return true; + /* + * Concurrent migration may have replaced the present PTE with a + * non-marker swap entry between fault delivery and this lockless + * re-check. huge_pte_write() on a swap entry decodes random offset + * bits, so gate it on pte_present(). The migration completion path + * will re-deliver the fault if it still needs userspace. + */ + if (!pte_present(pte)) + return false; /* * If VMA has UFFD WP faults enabled and WP fault, wait for userspace to * resolve the fault. @@ -339,6 +348,17 @@ static inline bool userfaultfd_must_wait(struct userfaultfd_ctx *ctx, /* UFFD PTE markers require userspace to resolve the fault. */ if (pte_is_uffd_marker(ptent)) goto out; + /* + * Concurrent swap-out / migration may have replaced the present PTE + * with a non-marker swap entry between fault delivery and this + * lockless re-check. pte_write() on a swap entry decodes random + * offset bits, so gate it on pte_present(). The page-in path will + * re-deliver the fault if it still needs userspace. + */ + if (!pte_present(ptent)) { + ret = false; + goto out; + } /* * If VMA has UFFD WP faults enabled and WP fault, wait for userspace to * resolve the fault. From 806586e33891066487db1f002be3d455cda6b516 Mon Sep 17 00:00:00 2001 From: Xingquan Liu Date: Fri, 19 Jun 2026 11:13:47 -0400 Subject: [PATCH 0153/2758] net/sched: dualpi2: fix GSO backlog accounting commit 05ed733b65ab977dd931e7f7ac0f62fdb81205c2 upstream. When DualPI2 splits a GSO skb into N segments, it propagates N additional packets to its parent before returning NET_XMIT_SUCCESS. The parent then accounts for the original skb once more, leaving its qlen one larger than the number of packets actually queued. With QFQ as the parent, after all real packets are dequeued, QFQ still has a non-zero qlen while its in-service aggregate has no active classes. qfq_choose_next_agg() returns NULL and qfq_dequeue() passes the result to qfq_peek_skb(), causing a NULL pointer dereference. Follow the same pattern used by tbf_segment() and taprio: count only successfully queued segments, propagate the difference between the original skb and those segments, and return NET_XMIT_SUCCESS whenever at least one segment was queued. Fixes: 8f9516daedd6 ("sched: Add enqueue/dequeue of dualpi2 qdisc") Cc: stable@vger.kernel.org Signed-off-by: Xingquan Liu Acked-by: Jamal Hadi Salim Reviewed-by: Victor Nogueira Link: https://patch.msgid.link/20260619151447.223640-1-b1n@b1n.io Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/sched/sch_dualpi2.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/net/sched/sch_dualpi2.c b/net/sched/sch_dualpi2.c index a22489c14458e..1ca4d2c03e61d 100644 --- a/net/sched/sch_dualpi2.c +++ b/net/sched/sch_dualpi2.c @@ -461,7 +461,7 @@ static int dualpi2_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch, if (IS_ERR_OR_NULL(nskb)) return qdisc_drop(skb, sch, to_free); - cnt = 1; + cnt = 0; byte_len = 0; orig_len = qdisc_pkt_len(skb); skb_list_walk_safe(nskb, nskb, next) { @@ -488,16 +488,15 @@ static int dualpi2_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch, byte_len += nskb->len; } } - if (cnt > 1) { + if (cnt > 0) { /* The caller will add the original skb stats to its * backlog, compensate this if any nskb is enqueued. */ - --cnt; - byte_len -= orig_len; + qdisc_tree_reduce_backlog(sch, 1 - cnt, + orig_len - byte_len); } - qdisc_tree_reduce_backlog(sch, -cnt, -byte_len); consume_skb(skb); - return err; + return cnt > 0 ? NET_XMIT_SUCCESS : err; } return dualpi2_enqueue_skb(skb, sch, to_free); } From 2dfe9f5c91d0963058f8a5e46e1c2a908382cc46 Mon Sep 17 00:00:00 2001 From: Pedro Falcato Date: Wed, 8 Jul 2026 16:13:57 +0100 Subject: [PATCH 0154/2758] mm/khugepaged: write all dirty file folios when collapsing [There is no upstream commit, as this code was removed by upstream commit 044925f9b565 ("mm: fs: remove filemap_nr_thps*() functions and their users")] As-is, khugepaged and writable-file opening exclude each other. A file cannot be open writeable and have THPs (because the filesystem is not aware of them). khugepaged will never collapse file pages for files that are opened writeable. On an open(O_RDWR/O_WRONLY), the page cache for that particular file is dropped. This is fine because nothing could've been dirtied. However, there is an edge-case: collapse_file() might not be able to coexist with concurrent writers, but it can coexist with dirty folios (from previous writers). Therefore, the following can happen: open(file, O_RDWR) write(file) close(file) madvise(file_mapping, MADV_COLLAPSE, some non-dirty range) open(file, O_RDWR) nr_thps > 0 truncate_inode_pages() /* THPs are cleared out, but so are the dirty folios */ When this edge-case happens, there is data loss, as the dirty folios are fully discarded. Fix it by fully writing back the page cache (and waiting) when collapsing file THPs. Doing so provides the guarantee that no dirty folio will be observed while there are active THPs. To fully ensure this is safe, the invalidate_lock needs to be held while doing the writeout, so that do_dentry_open()'s page cache truncation excludes this write-and-wait. As a side effect, move the nr_thps counter bumping outside the i_pages lock. This is correct since the counter itself is an atomic_t and the producer <-> consumer correctness is provided by a full memory barrier: smp_mb() in collapse_file()/memory barrier implied by full ordering in get_write_access() -> atomic_inc_unless_negative(). Cc: stable@vger.kernel.org Cc: Alexander Viro Cc: Christian Brauner Cc: Jan Kara Cc: Matthew Wilcox Cc: Song Liu Cc: Eric Hagberg Cc: Zi Yan Fixes: 99cb0dbd47a1 ("mm,thp: add read-only THP support for (non-shmem) FS") Reported-by: Gregg Leventhal Closes: https://lore.kernel.org/linux-mm/CAFN_u7H_0ECF3jixP=T=U7AH5=Q3wQNvJMo8an3VqUDMerQfUw@mail.gmail.com/ Tested-by: Zi Yan Tested-by: Lance Yang Signed-off-by: Pedro Falcato Acked-by: David Hildenbrand (Arm) Reviewed-by: Matthew Wilcox (Oracle) Signed-off-by: Sasha Levin --- mm/khugepaged.c | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/mm/khugepaged.c b/mm/khugepaged.c index b8452dbdb043f..d6e04041f5dc1 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -2094,32 +2094,43 @@ static enum scan_result collapse_file(struct mm_struct *mm, unsigned long addr, goto xa_unlocked; } - if (!is_shmem) { +xa_locked: + xas_unlock_irq(&xas); +xa_unlocked: + + /* + * If collapse is successful, flush must be done now before copying. + * If collapse is unsuccessful, does flush actually need to be done? + * Do it anyway, to clear the state. + */ + try_to_unmap_flush(); + + if (result == SCAN_SUCCEED && !is_shmem && !mapping_large_folio_support(mapping)) { + /* + * invalidate_lock as shared excludes against concurrent opens + * in do_dentry_open() truncating the page cache. This is + * particularly important if there are dirty folios in transit. + */ + filemap_invalidate_lock_shared(mapping); filemap_nr_thps_inc(mapping); /* * Paired with the fence in do_dentry_open() -> get_write_access() * to ensure i_writecount is up to date and the update to nr_thps * is visible. Ensures the page cache will be truncated if the - * file is opened writable. + * file is opened writable. If collapse looks to be successful, + * flush any dirty pages out the page cache. With the nr_thps + * incremented, there won't be any new writers (nor new dirties). */ smp_mb(); - if (inode_is_open_for_write(mapping->host)) { + if (inode_is_open_for_write(mapping->host) || filemap_write_and_wait(mapping)) { result = SCAN_FAIL; filemap_nr_thps_dec(mapping); + filemap_invalidate_unlock_shared(mapping); + goto rollback; } + filemap_invalidate_unlock_shared(mapping); } -xa_locked: - xas_unlock_irq(&xas); -xa_unlocked: - - /* - * If collapse is successful, flush must be done now before copying. - * If collapse is unsuccessful, does flush actually need to be done? - * Do it anyway, to clear the state. - */ - try_to_unmap_flush(); - if (result == SCAN_SUCCEED && nr_none && !shmem_charge(mapping->host, nr_none)) result = SCAN_FAIL; From 7d45ca69164e190bec4bf3a30eb5349f9eea0bb8 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 17 Jun 2026 09:31:25 -0700 Subject: [PATCH 0155/2758] slab: recognize @GFP parameter as optional in kernel-doc commit 7b5f5865fb11e60edd03c5e063e2d228b7062317 upstream. Since the @GFP parameter in kmalloc_obj() etc. is now optional, change the kernel-doc to indicate that it is optional. This avoids kernel-doc warnings: WARNING: include/linux/slab.h:1101 Excess function parameter 'GFP' description in 'kmalloc_obj' WARNING: include/linux/slab.h:1113 Excess function parameter 'GFP' description in 'kmalloc_objs' WARNING: include/linux/slab.h:1128 Excess function parameter 'GFP' description in 'kmalloc_flex' Fixes: e19e1b480ac7 ("add default_gfp() helper macro and use it in the new *alloc_obj() helpers") Signed-off-by: Randy Dunlap Acked-by: Harry Yoo (Oracle) Link: https://patch.msgid.link/20260617163125.2716279-1-rdunlap@infradead.org Signed-off-by: Vlastimil Babka (SUSE) Signed-off-by: Eric Biggers Signed-off-by: Greg Kroah-Hartman --- include/linux/slab.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/linux/slab.h b/include/linux/slab.h index 2b5ab488e96b0..1a69255bb87f2 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -1000,7 +1000,7 @@ void *kmalloc_nolock_noprof(size_t size, gfp_t gfp_flags, int node); /** * kmalloc_obj - Allocate a single instance of the given type * @VAR_OR_TYPE: Variable or type to allocate. - * @GFP: GFP flags for the allocation. + * @...: optional GFP flags for the allocation (GFP_KERNEL when not specified). * * Returns: newly allocated pointer to a @VAR_OR_TYPE on success, or NULL * on failure. @@ -1012,7 +1012,7 @@ void *kmalloc_nolock_noprof(size_t size, gfp_t gfp_flags, int node); * kmalloc_objs - Allocate an array of the given type * @VAR_OR_TYPE: Variable or type to allocate an array of. * @COUNT: How many elements in the array. - * @GFP: GFP flags for the allocation. + * @...: optional GFP flags for the allocation (GFP_KERNEL when not specified). * * Returns: newly allocated pointer to array of @VAR_OR_TYPE on success, * or NULL on failure. @@ -1025,7 +1025,7 @@ void *kmalloc_nolock_noprof(size_t size, gfp_t gfp_flags, int node); * @VAR_OR_TYPE: Variable or type to allocate (with its flex array). * @FAM: The name of the flexible array member of the structure. * @COUNT: How many flexible array member elements are desired. - * @GFP: GFP flags for the allocation. + * @...: optional GFP flags for the allocation (GFP_KERNEL when not specified). * * Returns: newly allocated pointer to @VAR_OR_TYPE on success, NULL on * failure. If @FAM has been annotated with __counted_by(), the allocation From 3b3ca5d3a28eb0277ce0512a8c902cc43326d0bd Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Wed, 13 May 2026 12:23:46 -0700 Subject: [PATCH 0156/2758] perf trace beauty fcntl: Fix build with older kernel headers commit 7ee7f48413c42b90230de4a8e40898b757bc8e82 upstream. Toolchains with older kernel headers that do not include upstream commit c75b1d9421f80f41 ("fs: add fcntl() interface for setting/getting write life time hints") will now fail to build perf due to missing definitions for F_GET_RW_HINT/F_SET_RW_HINT/F_GET_FILE_RW_HINT/F_SET_FILE_RW_HINT. Provide a fallback definition for these when they are not already defined. Fixes: 9c47f66748381ecb ("perf trace beauty fcntl: Basic 'arg' beautifier") Reviewed-by: Ian Rogers Signed-off-by: Florian Fainelli Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Markus Mayer Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Greg Kroah-Hartman --- tools/perf/trace/beauty/fcntl.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tools/perf/trace/beauty/fcntl.c b/tools/perf/trace/beauty/fcntl.c index d075904dccced..e1b99b8f55eb0 100644 --- a/tools/perf/trace/beauty/fcntl.c +++ b/tools/perf/trace/beauty/fcntl.c @@ -9,6 +9,22 @@ #include #include +#ifndef F_GET_RW_HINT +#define F_GET_RW_HINT (F_LINUX_SPECIFIC_BASE + 11) +#endif + +#ifndef F_SET_RW_HINT +#define F_SET_RW_HINT (F_LINUX_SPECIFIC_BASE + 12) +#endif + +#ifndef F_GET_FILE_RW_HINT +#define F_GET_FILE_RW_HINT (F_LINUX_SPECIFIC_BASE + 13) +#endif + +#ifndef F_SET_FILE_RW_HINT +#define F_SET_FILE_RW_HINT (F_LINUX_SPECIFIC_BASE + 14) +#endif + static size_t fcntl__scnprintf_getfd(unsigned long val, char *bf, size_t size, bool show_prefix) { return val ? scnprintf(bf, size, "%s", "0") : From 3db1ef139956d033031070e60060d0d4dbd7a30f Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 9 Jul 2026 15:21:07 +0200 Subject: [PATCH 0157/2758] KVM: x86: Move update_cr8_intercept() to lapic.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream Commit c7722e5e1daeeabbd9f969554d52bb7158120b27 ] Move update_cr8_intercept() to lapic.c so that it's globally visible in anticipation of extracting most of the register-specific code out of x86.c and into a new compilation unit. Opportunistically prefix the helper kvm_lapic_ to make its role/scope more obvious. No functional change intended. Reviewed-by: Kai Huang Reviewed-by: Yosry Ahmed Link: https://patch.msgid.link/20260529222223.870923-14-seanjc@google.com Signed-off-by: Sean Christopherson Signed-off-by: Carlos López Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/lapic.c | 26 ++++++++++++++++++++++++++ arch/x86/kvm/lapic.h | 1 + arch/x86/kvm/x86.c | 34 +++------------------------------- 3 files changed, 30 insertions(+), 31 deletions(-) diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 4078e624ca667..f4574b5a16d8e 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -2744,6 +2744,32 @@ u64 kvm_lapic_get_cr8(struct kvm_vcpu *vcpu) return (tpr & 0xf0) >> 4; } +void kvm_lapic_update_cr8_intercept(struct kvm_vcpu *vcpu) +{ + int max_irr, tpr; + + if (!kvm_x86_ops.update_cr8_intercept) + return; + + if (!lapic_in_kernel(vcpu)) + return; + + if (vcpu->arch.apic->apicv_active) + return; + + if (!vcpu->arch.apic->vapic_addr) + max_irr = kvm_lapic_find_highest_irr(vcpu); + else + max_irr = -1; + + if (max_irr != -1) + max_irr >>= 4; + + tpr = kvm_lapic_get_cr8(vcpu); + + kvm_x86_call(update_cr8_intercept)(vcpu, tpr, max_irr); +} + static void __kvm_apic_set_base(struct kvm_vcpu *vcpu, u64 value) { u64 old_value = vcpu->arch.apic_base; diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h index 274885af4ebc4..533581d061517 100644 --- a/arch/x86/kvm/lapic.h +++ b/arch/x86/kvm/lapic.h @@ -100,6 +100,7 @@ int kvm_apic_accept_events(struct kvm_vcpu *vcpu); void kvm_lapic_reset(struct kvm_vcpu *vcpu, bool init_event); u64 kvm_lapic_get_cr8(struct kvm_vcpu *vcpu); void kvm_lapic_set_tpr(struct kvm_vcpu *vcpu, unsigned long cr8); +void kvm_lapic_update_cr8_intercept(struct kvm_vcpu *vcpu); void kvm_lapic_set_eoi(struct kvm_vcpu *vcpu); void kvm_apic_set_version(struct kvm_vcpu *vcpu); void kvm_apic_after_set_mcg_cap(struct kvm_vcpu *vcpu); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 0550359ed798f..2c5bc03292e3b 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -128,7 +128,6 @@ static u64 __read_mostly efer_reserved_bits = ~((u64)EFER_SCE); KVM_X2APIC_ENABLE_SUPPRESS_EOI_BROADCAST | \ KVM_X2APIC_DISABLE_SUPPRESS_EOI_BROADCAST) -static void update_cr8_intercept(struct kvm_vcpu *vcpu); static void process_nmi(struct kvm_vcpu *vcpu); static void __kvm_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags); static void store_regs(struct kvm_vcpu *vcpu); @@ -5340,7 +5339,7 @@ static int kvm_vcpu_ioctl_set_lapic(struct kvm_vcpu *vcpu, r = kvm_apic_set_state(vcpu, s); if (r) return r; - update_cr8_intercept(vcpu); + kvm_lapic_update_cr8_intercept(vcpu); return 0; } @@ -10595,33 +10594,6 @@ static void post_kvm_run_save(struct kvm_vcpu *vcpu) kvm_run->flags |= KVM_RUN_X86_GUEST_MODE; } -static void update_cr8_intercept(struct kvm_vcpu *vcpu) -{ - int max_irr, tpr; - - if (!kvm_x86_ops.update_cr8_intercept) - return; - - if (!lapic_in_kernel(vcpu)) - return; - - if (vcpu->arch.apic->apicv_active) - return; - - if (!vcpu->arch.apic->vapic_addr) - max_irr = kvm_lapic_find_highest_irr(vcpu); - else - max_irr = -1; - - if (max_irr != -1) - max_irr >>= 4; - - tpr = kvm_lapic_get_cr8(vcpu); - - kvm_x86_call(update_cr8_intercept)(vcpu, tpr, max_irr); -} - - int kvm_check_nested_events(struct kvm_vcpu *vcpu) { if (kvm_test_request(KVM_REQ_TRIPLE_FAULT, vcpu)) { @@ -11362,7 +11334,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) kvm_x86_call(enable_irq_window)(vcpu); if (kvm_lapic_enabled(vcpu)) { - update_cr8_intercept(vcpu); + kvm_lapic_update_cr8_intercept(vcpu); kvm_lapic_sync_to_vapic(vcpu); } } @@ -12511,7 +12483,7 @@ static int __set_sregs_common(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs, kvm_set_segment(vcpu, &sregs->tr, VCPU_SREG_TR); kvm_set_segment(vcpu, &sregs->ldt, VCPU_SREG_LDTR); - update_cr8_intercept(vcpu); + kvm_lapic_update_cr8_intercept(vcpu); /* Older userspace won't unhalt the vcpu on reset. */ if (kvm_vcpu_is_bsp(vcpu) && kvm_rip_read(vcpu) == 0xfff0 && From db8407b9fd06d857a4a5e8bcff1d086d13007711 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 9 Jul 2026 15:21:08 +0200 Subject: [PATCH 0158/2758] KVM: VMX: Grab vmcs12 on CR8 interception update iff vCPU is in guest mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 7ef78d71ca713d8c00f7c34ddcf276c808143f77 ] When updating CR8 intercepts, get vmcs12 if and only if the vCPU is in guest mode so that a future change can have update CR8 intercepts during vCPU creation, without running afoul of get_vmcs12()'s lockdep assertion. ------------[ cut here ]------------ debug_locks && !(lock_is_held(&(&vcpu->mutex)->dep_map) || !refcount_read(&vcpu->kvm->users_count)) WARNING: arch/x86/kvm/vmx/nested.h:61 at get_vmcs12 arch/x86/kvm/vmx/nested.h:60 [inline], CPU#0: syz.2.19/5879 WARNING: arch/x86/kvm/vmx/nested.h:61 at vmx_update_cr8_intercept+0x3de/0x4e0 arch/x86/kvm/vmx/vmx.c:6879, CPU#0: syz.2.19/5879 Modules linked in: CPU: 0 UID: 0 PID: 5879 Comm: syz.2.19 Not tainted syzkaller #0 PREEMPT(full) Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014 RIP: 0010:get_vmcs12 arch/x86/kvm/vmx/nested.h:60 [inline] RIP: 0010:vmx_update_cr8_intercept+0x3de/0x4e0 arch/x86/kvm/vmx/vmx.c:6879 Call Trace: apic_update_ppr arch/x86/kvm/lapic.c:984 [inline] kvm_lapic_reset+0x1c24/0x2980 arch/x86/kvm/lapic.c:3023 kvm_vcpu_reset+0x44c/0x1bf0 arch/x86/kvm/x86.c:12986 kvm_arch_vcpu_create+0x746/0x8b0 arch/x86/kvm/x86.c:12847 kvm_vm_ioctl_create_vcpu+0x428/0x930 virt/kvm/kvm_main.c:4201 kvm_vm_ioctl+0x893/0xd50 virt/kvm/kvm_main.c:5159 vfs_ioctl fs/ioctl.c:51 [inline] __do_sys_ioctl fs/ioctl.c:597 [inline] __se_sys_ioctl+0xfc/0x170 fs/ioctl.c:583 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f No functional change intended. Reported-by: syzbot ci Closes: https://lore.kernel.org/all/6a2adf3b.3b0a2d4e.8c8d1.0012.GAE@google.com Cc: stable@vger.kernel.org Signed-off-by: Sean Christopherson Message-ID: <20260618174347.1981064-2-seanjc@google.com> Signed-off-by: Paolo Bonzini Signed-off-by: Carlos López Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/vmx/vmx.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index b9103de01428e..719dfa6b780fe 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -6855,11 +6855,10 @@ int vmx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath) void vmx_update_cr8_intercept(struct kvm_vcpu *vcpu, int tpr, int irr) { - struct vmcs12 *vmcs12 = get_vmcs12(vcpu); int tpr_threshold; if (is_guest_mode(vcpu) && - nested_cpu_has(vmcs12, CPU_BASED_TPR_SHADOW)) + nested_cpu_has(get_vmcs12(vcpu), CPU_BASED_TPR_SHADOW)) return; guard(vmx_vmcs01)(vcpu); From 8c8e8ac22ee17d52f9eb2bc814bca7fab90fb8df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Thu, 9 Jul 2026 15:21:09 +0200 Subject: [PATCH 0159/2758] KVM: x86: Unconditionally recompute CR8 intercept on PPR update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit bb365a506b1e6fb050c0fceaad354fe395385ef0 ] The TPR_THRESHOLD field in the VMCS is used by VMX to induce VM exits when the guest's virtual TPR falls under the specified threshold, allowing KVM to inject previously masked interrupts. KVM handles these VM exits in handle_tpr_below_threshold(). Commit eb90f3417a0c ("KVM: vmx: speed up TPR below threshold vmexits") optimized this function by calling apic_update_ppr() instead of raising KVM_REQ_EVENT. apic_update_ppr() then raises KVM_REQ_EVENT if there is a pending, deliverable interrupt. However, if there are no new interrupts pending, apic_update_ppr() does not issue the request. Thus, kvm_lapic_update_cr8_intercept() and vmx_update_cr8_intercept() are not called before VM entry, which results in a high, stale TPR_THRESHOLD. This is problematic due to the following sentence in 28.2.1.1 "VM-Execution Control Fields" in the SDM: The following check is performed if the “use TPR shadow” VM-execution control is 1 and the “virtualize APIC accesses” and “virtual-interrupt delivery” VM-execution controls are both 0: the value of bits 3:0 of the TPR threshold VM-execution control field should not be greater than the value of bits 7:4 of VTPR. This error condition is typically not observed when KVM runs on a bare metal system because modern processors support APICv, which enables virtual-interrupt delivery, and which KVM uses when possible. This causes the processor to no longer generate TPR-below-threshold exits and to no longer check TPR_THRESHOLD on entry. However, when running on older platforms, or under nested virtualization on a hypervisor that does not support virtual-interrupt delivery and enforces this check (like Hyper-V) this can cause a VM entry failure with hardware error 0x7, as seen in [1]. Call kvm_lapic_update_cr8_intercept() if apic_update_ppr() does not find a deliverable interrupt (and thus does not raise KVM_REQ_EVENT). Remove calls to kvm_lapic_update_cr8_intercept() on paths that end up in apic_update_ppr(), as they now become redundant. This ensures that any path that updates the guest's PPR also figures out if KVM needs to wait for a TPR change (using TPR_THRESHOLD on VMX or CR8 intercepts on SVM). Link: https://github.com/coconut-svsm/svsm/issues/1081 [1] Tested-by: Stefano Garzarella Cc: stable@vger.kernel.org Fixes: eb90f3417a0c ("KVM: vmx: speed up TPR below threshold vmexits") Signed-off-by: Carlos López Signed-off-by: Sean Christopherson Message-ID: <20260618174347.1981064-3-seanjc@google.com> Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/lapic.c | 2 ++ arch/x86/kvm/x86.c | 5 +---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index f4574b5a16d8e..63e068ae29822 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -980,6 +980,8 @@ static void apic_update_ppr(struct kvm_lapic *apic) if (__apic_update_ppr(apic, &ppr) && apic_has_interrupt_for_ppr(apic, ppr) != -1) kvm_make_request(KVM_REQ_EVENT, apic->vcpu); + else + kvm_lapic_update_cr8_intercept(apic->vcpu); } void kvm_apic_update_ppr(struct kvm_vcpu *vcpu) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 2c5bc03292e3b..485df779ae67e 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -5339,7 +5339,6 @@ static int kvm_vcpu_ioctl_set_lapic(struct kvm_vcpu *vcpu, r = kvm_apic_set_state(vcpu, s); if (r) return r; - kvm_lapic_update_cr8_intercept(vcpu); return 0; } @@ -12453,8 +12452,6 @@ static int __set_sregs_common(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs, kvm_register_mark_dirty(vcpu, VCPU_EXREG_CR3); kvm_x86_call(post_set_cr3)(vcpu, sregs->cr3); - kvm_set_cr8(vcpu, sregs->cr8); - *mmu_reset_needed |= vcpu->arch.efer != sregs->efer; kvm_x86_call(set_efer)(vcpu, sregs->efer); @@ -12483,7 +12480,7 @@ static int __set_sregs_common(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs, kvm_set_segment(vcpu, &sregs->tr, VCPU_SREG_TR); kvm_set_segment(vcpu, &sregs->ldt, VCPU_SREG_LDTR); - kvm_lapic_update_cr8_intercept(vcpu); + kvm_set_cr8(vcpu, sregs->cr8); /* Older userspace won't unhalt the vcpu on reset. */ if (kvm_vcpu_is_bsp(vcpu) && kvm_rip_read(vcpu) == 0xfff0 && From dc066bd13c860bb27d6ace511210e18b8064c1d9 Mon Sep 17 00:00:00 2001 From: Jeremy Linton Date: Mon, 1 Jun 2026 18:58:08 -0500 Subject: [PATCH 0160/2758] ACPI: CPPC: Suppress UBSAN warning caused by field misuse commit 1b1acf2dada0cc3931bb2cb9ff8832edfbee46a1 upstream. The definition of reg->access_width changes depending on the reg->space_id type. Type ACPI_ADR_SPACE_PLATFORM_COMM uses access_width to indicate the PCC region, which can result in a UBSAN if the value is greater than 4. For example: UBSAN: shift-out-of-bounds in drivers/acpi/cppc_acpi.c:1090:9 shift exponent 32 is too large for 32-bit type 'int' CPU: 61 UID: 0 PID: 1220 Comm: (udev-worker) Not tainted 7.0.10-201.fc44.aarch64 #1 PREEMPT(lazy) Hardware name: To be filled by O.E.M. Call trace: ...(trimming) ubsan_epilogue+0x10/0x48 __ubsan_handle_shift_out_of_bounds+0xdc/0x1e0 cpc_write+0x4d0/0x670 cppc_set_perf+0x18c/0x490 cppc_cpufreq_cpu_init+0x1c8/0x380 [cppc_cpufreq] ... (trimming) Lets fix this by validating the region type, as well as whether access_width has a value. Then since we are returning bit_width directly for ACPI_ADR_SPACE_PLATFORM_COMM, drop the code correcting the size. Fixes: 2f4a4d63a193 ("ACPI: CPPC: Use access_width over bit_width for system memory accesses") Signed-off-by: Jeremy Linton Tested-by: Jarred White Reviewed-by: Jarred White Reviewed-by: Easwar Hariharan Cc: All applicable Link: https://patch.msgid.link/20260601235808.1113137-1-jeremy.linton@arm.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/cppc_acpi.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index f370be8715ae2..34edec0f2bde2 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -185,8 +185,13 @@ show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, nominal_freq); show_cppc_data(cppc_get_perf_ctrs, cppc_perf_fb_ctrs, wraparound_time); -/* Check for valid access_width, otherwise, fallback to using bit_width */ -#define GET_BIT_WIDTH(reg) ((reg)->access_width ? (8 << ((reg)->access_width - 1)) : (reg)->bit_width) +/* + * PCC reuses the access_width field as the subspace id, so only decode access + * size for non-PCC registers. Otherwise, use the bit_width. + */ +#define GET_BIT_WIDTH(reg) (((reg)->access_width && \ + (reg)->space_id != ACPI_ADR_SPACE_PLATFORM_COMM) ? \ + (8 << ((reg)->access_width - 1)) : (reg)->bit_width) /* Shift and apply the mask for CPC reads/writes */ #define MASK_VAL_READ(reg, val) (((val) >> (reg)->bit_offset) & \ @@ -1045,7 +1050,6 @@ static int cpc_read(int cpu, struct cpc_register_resource *reg_res, u64 *val) * by the bit width field; the access size is used to indicate * the PCC subspace id. */ - size = reg->bit_width; vaddr = GET_PCC_VADDR(reg->address, pcc_ss_id); } else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) @@ -1118,7 +1122,6 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val) * by the bit width field; the access size is used to indicate * the PCC subspace id. */ - size = reg->bit_width; vaddr = GET_PCC_VADDR(reg->address, pcc_ss_id); } else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) From 873576e585da5d0fc5debbab74eed565c0acea99 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 3 Jun 2026 19:56:21 +0200 Subject: [PATCH 0161/2758] ACPI: NFIT: core: Fix possible NULL pointer dereference commit 027e128abb82788189d6d45b68e3e8e7329b67be upstream. After commit 9b311b7313d6 ("ACPI: NFIT: Install Notify() handler before getting NFIT table"), acpi_nfit_probe() installs an ACPI notify handler for the NFIT device before checking the presence of the NFIT table. If that table is not there, 0 is returned without allocating the acpi_desc object and setting the driver data pointer of the NFIT device. If the platform firmware triggers an NFIT_NOTIFY_UC_MEMORY_ERROR notification on the NFIT device at that point, acpi_nfit_uc_error_notify() will dereference a NULL pointer. Prevent that from occurring by adding an acpi_desc check against NULL to acpi_nfit_uc_error_notify(). Fixes: 9b311b7313d6 ("ACPI: NFIT: Install Notify() handler before getting NFIT table") Signed-off-by: Rafael J. Wysocki Cc: All applicable Reviewed-by: Dave Jiang Link: https://patch.msgid.link/2418508.ElGaqSPkdT@rafael.j.wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/nfit/core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 9304ac996d41a..84c8c2a60ec91 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -3460,6 +3460,9 @@ static void acpi_nfit_uc_error_notify(struct device *dev, acpi_handle handle) { struct acpi_nfit_desc *acpi_desc = dev_get_drvdata(dev); + if (!acpi_desc) + return; + if (acpi_desc->scrub_mode == HW_ERROR_SCRUB_ON) acpi_nfit_ars_rescan(acpi_desc, ARS_REQ_LONG); else From 7d69235bdc581a4346e9bcd6a8bea37d3e1abd25 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 3 Jun 2026 19:57:02 +0200 Subject: [PATCH 0162/2758] ACPI: NFIT: core: Fix acpi_nfit_init() error cleanup commit 38bf27511ef41bffebd157ec3eba41fc89ba59cd upstream. If acpi_nfit_init() fails after adding the acpi_desc object to the acpi_descs list, that object is never removed from that list because the acpi_nfit_shutdown() devm action is not added for the NFIT device in that case. Next, the acpi_nfit_init() failure causes acpi_nfit_probe() to fail, the acpi_desc object is freed, and a dangling pointer is left behind in the acpi_descs. Any subsequent ACPI Machine Check Exception will trigger nfit_handle_mce() which iterates over acpi_descs and so a use-after-free will occur. Moreover, if acpi_nfit_probe() returns 0 after installing a notify handler for the NFIT device and without allocating the acpi_desc object and setting the NFIT device's driver data pointer, the acpi_desc object will be allocated by acpi_nfit_update_notify() and acpi_nfit_init() will be called to initialize it. Regardless of whether or not acpi_nfit_init() fails in that case, the acpi_nfit_shutdown() devm action is not added for the NFIT device and acpi_desc is never removed from the acpi_descs list. If the acpi_desc object is freed subsequently on driver removal, any subsequent ACPI MCE will lead to a use-after-free like in the previous case. To address the first issue mentioned above, make acpi_nfit_probe() call acpi_nfit_shutdown() directly on acpi_nfit_init() failures and to address the other one, add a remove callback to the driver and make it call acpi_nfit_shutdown(). Also, since it is now possible to pass NULL to acpi_nfit_shutdown() or the acpi_desc object passed to it may not have been initialized, add checks against NULL for acpi_desc and its nvdimm_bus field to that function and make acpi_nfit_unregister() clear the latter after unregistering the NVDIMM bus. Fixes: a61fe6f7902e ("nfit, tools/testing/nvdimm: unify common init for acpi_nfit_desc") Fixes: fbabd829fe76 ("acpi, nfit: fix module unload vs workqueue shutdown race") Signed-off-by: Rafael J. Wysocki Cc: All applicable Reviewed-by: Dave Jiang Link: https://patch.msgid.link/1963615.tdWV9SEqCh@rafael.j.wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/nfit/core.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 84c8c2a60ec91..7f554ce6345f7 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -3069,6 +3069,8 @@ static void acpi_nfit_unregister(void *data) struct acpi_nfit_desc *acpi_desc = data; nvdimm_bus_unregister(acpi_desc->nvdimm_bus); + /* The nvdimm_bus object may have been freed, so clear the pointer. */ + acpi_desc->nvdimm_bus = NULL; } int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, void *data, acpi_size sz) @@ -3309,7 +3311,10 @@ static void acpi_nfit_remove_notify_handler(void *data) void acpi_nfit_shutdown(void *data) { struct acpi_nfit_desc *acpi_desc = data; - struct device *bus_dev = to_nvdimm_bus_dev(acpi_desc->nvdimm_bus); + struct device *bus_dev; + + if (!acpi_desc || !acpi_desc->nvdimm_bus) + return; /* * Destruct under acpi_desc_lock so that nfit_handle_mce does not @@ -3324,6 +3329,7 @@ void acpi_nfit_shutdown(void *data) mutex_unlock(&acpi_desc->init_mutex); cancel_delayed_work_sync(&acpi_desc->dwork); + bus_dev = to_nvdimm_bus_dev(acpi_desc->nvdimm_bus); /* * Bounce the nvdimm bus lock to make sure any in-flight * acpi_nfit_ars_rescan() submissions have had a chance to @@ -3406,9 +3412,14 @@ static int acpi_nfit_probe(struct platform_device *pdev) sz - sizeof(struct acpi_table_nfit)); if (rc) - return rc; + acpi_nfit_shutdown(acpi_desc); - return devm_add_action_or_reset(dev, acpi_nfit_shutdown, acpi_desc); + return rc; +} + +static void acpi_nfit_remove(struct platform_device *pdev) +{ + acpi_nfit_shutdown(platform_get_drvdata(pdev)); } static void acpi_nfit_update_notify(struct device *dev, acpi_handle handle) @@ -3492,6 +3503,7 @@ MODULE_DEVICE_TABLE(acpi, acpi_nfit_ids); static struct platform_driver acpi_nfit_driver = { .probe = acpi_nfit_probe, + .remove = acpi_nfit_remove, .driver = { .name = "acpi-nfit", .acpi_match_table = acpi_nfit_ids, From eace3b3e729d5ba11794d69acfafb58a7950217c Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Sat, 6 Jun 2026 02:49:05 +0900 Subject: [PATCH 0163/2758] platform/x86: intel-hid: Protect ACPI notify handler against recursion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit c085d82613d5618814b84406c8b2d64f1bc305e7 upstream. Since commit e2ffcda16290 ("ACPI: OSL: Allow Notify () handlers to run on all CPUs") ACPI notify handlers like the intel-hid notify_handler() may run on multiple CPU cores racing with themselves. On convertibles and detachables (matched by DMI chassis-type 31 and 32 in dmi_auto_add_switch[]) the SW_TABLET_MODE input device is registered lazily from notify_handler() on the first tablet-mode event, via intel_hid_switches_setup(). When two such events race on different CPUs both can pass the !priv->switches check and register the priv->switches input device twice, resulting in a duplicate sysfs entry and a subsequent NULL pointer dereference. This is the same class of bug fixed by commit e075c3b13a0a ("platform/x86: intel-vbtn: Protect ACPI notify handler against recursion") for the sibling intel-vbtn driver. Protect intel-hid notify_handler() from racing with itself with a mutex to fix this. Fixes: e2ffcda16290 ("ACPI: OSL: Allow Notify () handlers to run on all CPUs") Cc: stable@vger.kernel.org Signed-off-by: HyeongJun An Link: https://patch.msgid.link/20260605174905.131095-1-sammiee5311@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/intel/hid.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/platform/x86/intel/hid.c b/drivers/platform/x86/intel/hid.c index 085093506dda9..6b4bac5f7051f 100644 --- a/drivers/platform/x86/intel/hid.c +++ b/drivers/platform/x86/intel/hid.c @@ -7,11 +7,13 @@ */ #include +#include #include #include #include #include #include +#include #include #include #include @@ -230,6 +232,7 @@ static const struct dmi_system_id dmi_auto_add_switch[] = { }; struct intel_hid_priv { + struct mutex mutex; /* Avoid notify_handler() racing with itself */ struct input_dev *input_dev; struct input_dev *array; struct input_dev *switches; @@ -565,6 +568,8 @@ static void notify_handler(acpi_handle handle, u32 event, void *context) struct key_entry *ke; int err; + guard(mutex)(&priv->mutex); + /* * Some convertible have unreliable VGBS return which could cause incorrect * SW_TABLET_MODE report, in these cases we enable support when receiving @@ -720,6 +725,10 @@ static int intel_hid_probe(struct platform_device *device) return -ENOMEM; dev_set_drvdata(&device->dev, priv); + err = devm_mutex_init(&device->dev, &priv->mutex); + if (err) + return err; + /* See dual_accel_detect.h for more info on the dual_accel check. */ if (enable_sw_tablet_mode == TABLET_SW_AUTO) { if (dmi_check_system(dmi_vgbs_allow_list)) From fbe9f0ff0b5b1a1329964c3beb0c082d4fad7f9c Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 25 Jun 2026 13:03:47 +0800 Subject: [PATCH 0164/2758] LoongArch: Add PIO for early access before ACPI PCI root register commit 6061e65f95713b01f4313cda6637dfe3aa5412b4 upstream. For ACPI system we suppose the ISA/LPC PIO range is registered together with PCI root bridge. But the fact is there may be some early access to the ISA/LPC PIO range before ACPI PCI root register (most of them are due to abnormal BIOS). Unconditionally register the ISA/LPC PIO range usually causes ACPI PCI root register fail because of the address range confliction. So we add a pair of helpers: acpi_add_early_pio() to add PIO for early access, and acpi_remove_early_pio() to remove PIO before PCI root register. Since acpi_remove_early_pio() may be called multiple times, we add an acpi_pio flag to ensure PIO be removed only once. Cc: Tested-by: Yuanzhen Gan Signed-off-by: Huacai Chen Signed-off-by: Greg Kroah-Hartman --- arch/loongarch/include/asm/acpi.h | 2 ++ arch/loongarch/kernel/acpi.c | 28 ++++++++++++++++++++++++++++ arch/loongarch/kernel/setup.c | 2 ++ arch/loongarch/pci/acpi.c | 2 ++ 4 files changed, 34 insertions(+) diff --git a/arch/loongarch/include/asm/acpi.h b/arch/loongarch/include/asm/acpi.h index eda9d4d0a493c..c05168aedcaa2 100644 --- a/arch/loongarch/include/asm/acpi.h +++ b/arch/loongarch/include/asm/acpi.h @@ -38,6 +38,8 @@ static inline bool acpi_has_cpu_in_madt(void) extern struct list_head acpi_wakeup_device_list; extern struct acpi_madt_core_pic acpi_core_pic[MAX_CORE_PIC]; +extern void acpi_add_early_pio(void); +extern void acpi_remove_early_pio(void); extern int __init parse_acpi_topology(void); #endif /* !CONFIG_ACPI */ diff --git a/arch/loongarch/kernel/acpi.c b/arch/loongarch/kernel/acpi.c index 058f0dbe8e8f9..8f650c9ffecde 100644 --- a/arch/loongarch/kernel/acpi.c +++ b/arch/loongarch/kernel/acpi.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -59,6 +60,33 @@ void __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size) return ioremap_cache(phys, size); } +#define PIO_BASE (unsigned long)PCI_IOBASE +#define PIO_SIZE ALIGN(ISA_IOSIZE, PAGE_SIZE) + +static bool acpi_pio; + +/* Add PIO for early access */ +void acpi_add_early_pio(void) +{ + if (!acpi_disabled) { + acpi_pio = true; + vmap_page_range(PIO_BASE, PIO_BASE + PIO_SIZE, + LOONGSON_LIO_BASE, pgprot_device(PAGE_KERNEL)); + } +} + +/* Remove PIO for PCI register */ +void acpi_remove_early_pio(void) +{ + if (!acpi_pio) + return; + + if (!acpi_disabled) { + acpi_pio = false; + vunmap_range(PIO_BASE, PIO_BASE + PIO_SIZE); + } +} + #ifdef CONFIG_SMP static int set_processor_mask(u32 id, u32 pass) { diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c index 839b23edee876..bfc6f274e6e28 100644 --- a/arch/loongarch/kernel/setup.c +++ b/arch/loongarch/kernel/setup.c @@ -502,6 +502,8 @@ static __init int arch_reserve_pio_range(void) { struct device_node *np; + acpi_add_early_pio(); + for_each_node_by_name(np, "isa") { struct of_range range; struct of_range_parser parser; diff --git a/arch/loongarch/pci/acpi.c b/arch/loongarch/pci/acpi.c index b02698a338eef..ccbcea61fcd9c 100644 --- a/arch/loongarch/pci/acpi.c +++ b/arch/loongarch/pci/acpi.c @@ -65,6 +65,8 @@ static int acpi_prepare_root_resources(struct acpi_pci_root_info *ci) struct resource_entry *entry, *tmp; struct acpi_device *device = ci->bridge; + acpi_remove_early_pio(); + status = acpi_pci_probe_root_resources(ci); if (status > 0) { acpi_evaluate_integer(device->handle, "PCIH", NULL, &pci_h); From 3f096fb8647bb95aa42733303780d62e68db485e Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Sat, 30 May 2026 11:58:09 +0200 Subject: [PATCH 0165/2758] rust: cpufreq: clean new `clippy::map_or_identity` lint for Rust 1.98.0 commit 3473e0a219fdb2cb013da0a5d917e66fef052325 upstream. Starting with Rust 1.98.0 (expected 2026-08-20), Clippy is likely introducing a new lint `clippy::map_or_identity` [1][2], which currently triggers in a single case: warning: expression can be simplified using `Result::unwrap_or()` --> rust/kernel/cpufreq.rs:1326:60 | 1326 | PolicyCpu::from_cpu(cpu_id).map_or(0, |mut policy| T::get(&mut policy).map_or(0, |f| f)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#map_or_identity = note: `-W clippy::map-or-identity` implied by `-W clippy::all` = help: to override `-W clippy::all` add `#[allow(clippy::map_or_identity)]` help: consider using `unwrap_or` | 1326 - PolicyCpu::from_cpu(cpu_id).map_or(0, |mut policy| T::get(&mut policy).map_or(0, |f| f)) 1326 + PolicyCpu::from_cpu(cpu_id).map_or(0, |mut policy| T::get(&mut policy).unwrap_or(0)) | The suggestion is valid, thus clean it up. Cc: stable@vger.kernel.org # Needed in 6.18.y and later. Link: https://github.com/rust-lang/rust-clippy/issues/15801 [1] Link: https://github.com/rust-lang/rust-clippy/pull/16052 [2] Reviewed-by: Zhongqiu Han Reviewed-by: Gary Guo Reviewed-by: Alexandre Courbot Acked-by: Viresh Kumar Link: https://patch.msgid.link/20260530095809.213611-1-ojeda@kernel.org Signed-off-by: Miguel Ojeda Signed-off-by: Greg Kroah-Hartman --- rust/kernel/cpufreq.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs index d8d26870bea2e..a20bd5006f384 100644 --- a/rust/kernel/cpufreq.rs +++ b/rust/kernel/cpufreq.rs @@ -1323,7 +1323,7 @@ impl Registration { // SAFETY: The C API guarantees that `cpu` refers to a valid CPU number. let cpu_id = unsafe { CpuId::from_u32_unchecked(cpu) }; - PolicyCpu::from_cpu(cpu_id).map_or(0, |mut policy| T::get(&mut policy).map_or(0, |f| f)) + PolicyCpu::from_cpu(cpu_id).map_or(0, |mut policy| T::get(&mut policy).unwrap_or(0)) } /// Driver's `update_limit` callback. From 1b1cac9887ec7db15d2cd0c2d9210e4f4c75f70b Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Wed, 8 Apr 2026 08:32:16 +0000 Subject: [PATCH 0166/2758] rust: kasan: KASAN+RUST requires clang commit 5b271543d0f08e9733d4732721e960e285f6448f upstream. Kernel KASAN involves passing various llvm/gcc specific arguments to the C and Rust compiler. Since these arguments differ between llvm and gcc, it's not safe to mix an llvm-based rustc with a gcc build when kasan is enabled. Signed-off-by: Alice Ryhl Reviewed-by: Gary Guo Cc: stable@vger.kernel.org Fixes: e3117404b411 ("kbuild: rust: Enable KASAN support") Link: https://patch.msgid.link/20260408-kasan-rust-sw-tags-v3-1-e07964d14363@google.com Signed-off-by: Miguel Ojeda Signed-off-by: Greg Kroah-Hartman --- init/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/init/Kconfig b/init/Kconfig index 2937c4d308aec..826a7d768ca36 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -2198,6 +2198,7 @@ config RUST depends on !DEBUG_INFO_BTF || (PAHOLE_HAS_LANG_EXCLUDE && !LTO) depends on !CFI || HAVE_CFI_ICALL_NORMALIZE_INTEGERS_RUSTC select CFI_ICALL_NORMALIZE_INTEGERS if CFI + depends on !KASAN || CC_IS_CLANG depends on !KASAN_SW_TAGS help Enables Rust support in the kernel. From afa40a46407208b08b56e149ea6a1f1d304efe94 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 25 May 2026 22:20:48 +0200 Subject: [PATCH 0167/2758] rust: pci: use 'static lifetime for PCI BAR resource names commit e566a9e17f3774c962b6d2522750f227f027edc6 upstream. pci_request_region() stores the name pointer directly in struct resource; use &'static CStr to ensure the pointer remains valid even if the Bar is leaked. Cc: stable@vger.kernel.org Reported-by: Sashiko Closes: https://lore.kernel.org/all/20260522004943.CDA7C1F000E9@smtp.kernel.org/ Fixes: 3c2e31d717ac ("rust: pci: move I/O infrastructure to separate file") Reviewed-by: Alexandre Courbot Reviewed-by: Eliot Courtney Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260525202921.124698-2-dakr@kernel.org Signed-off-by: Danilo Krummrich Signed-off-by: Greg Kroah-Hartman --- rust/kernel/pci/io.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs index ae78676c927ff..3ce21482b0797 100644 --- a/rust/kernel/pci/io.rs +++ b/rust/kernel/pci/io.rs @@ -153,7 +153,7 @@ pub struct Bar { } impl Bar { - pub(super) fn new(pdev: &Device, num: u32, name: &CStr) -> Result { + pub(super) fn new(pdev: &Device, num: u32, name: &'static CStr) -> Result { let len = pdev.resource_len(num)?; if len == 0 { return Err(ENOMEM); @@ -252,7 +252,7 @@ impl Device { pub fn iomap_region_sized<'a, const SIZE: usize>( &'a self, bar: u32, - name: &'a CStr, + name: &'static CStr, ) -> impl PinInit>, Error> + 'a { Devres::new(self.as_ref(), Bar::::new(self, bar, name)) } @@ -261,7 +261,7 @@ impl Device { pub fn iomap_region<'a>( &'a self, bar: u32, - name: &'a CStr, + name: &'static CStr, ) -> impl PinInit, Error> + 'a { self.iomap_region_sized::<0>(bar, name) } From 6822a2685b4da9a87efd1fce4b042678a31ff734 Mon Sep 17 00:00:00 2001 From: Haoze Xie Date: Sat, 30 May 2026 14:11:54 +0800 Subject: [PATCH 0168/2758] rust: block: fix GenDisk cleanup paths commit 2957771379fa335103a4b539db57bb2271e12142 upstream. GenDiskBuilder::build() still has fallible work after __blk_mq_alloc_disk(), but its error path only recovers the foreign queue data. That leaks the temporary gendisk and request_queue until later teardown. If the caller moved the last Arc> into build(), the leaked queue can retain blk-mq state after the tag set is dropped. Fix the pre-registration failure path by dropping the temporary gendisk reference with put_disk() before recovering queue_data, so disk_release() can tear down the owned queue. Also pair GenDisk::drop() with put_disk() after del_gendisk(). Once a Rust GenDisk has been added with device_add_disk(), del_gendisk() only unregisters it; the final gendisk reference still has to be dropped to complete the release path. Fixes: 3253aba3408a ("rust: block: introduce `kernel::block::mq` module") Cc: stable@kernel.org Reported-by: Yuan Tan Reported-by: Xin Liu Reviewed-by: Andreas Hindborg Signed-off-by: Haoze Xie Signed-off-by: Ren Wei Link: https://patch.msgid.link/b70aff9a920cc42110fe5cf454c3099561863519.1780063368.git.royenheart@gmail.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- rust/kernel/block/mq/gen_disk.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/rust/kernel/block/mq/gen_disk.rs b/rust/kernel/block/mq/gen_disk.rs index 912cb805caf51..fc97dd8739746 100644 --- a/rust/kernel/block/mq/gen_disk.rs +++ b/rust/kernel/block/mq/gen_disk.rs @@ -150,6 +150,19 @@ impl GenDiskBuilder { // SAFETY: `gendisk` is a valid pointer as we initialized it above unsafe { (*gendisk).fops = &TABLE }; + let cleanup_failure = ScopeGuard::new_with_data((gendisk, data), |(gendisk, data)| { + // SAFETY: `gendisk` came from `__blk_mq_alloc_disk()` above and + // has not been added to the VFS on this cleanup path. + unsafe { bindings::put_disk(gendisk) }; + // SAFETY: `data` came from `into_foreign()` above and has not been + // converted back on this cleanup path. + drop(unsafe { T::QueueData::from_foreign(data) }); + }); + + // The failure guard now owns both pieces of cleanup; the early guard + // must not run on this path anymore. + recover_data.dismiss(); + let mut writer = NullTerminatedFormatter::new( // SAFETY: `gendisk` points to a valid and initialized instance. We // have exclusive access, since the disk is not added to the VFS @@ -172,7 +185,7 @@ impl GenDiskBuilder { }, )?; - recover_data.dismiss(); + cleanup_failure.dismiss(); // INVARIANT: `gendisk` was initialized above. // INVARIANT: `gendisk` was added to the VFS via `device_add_disk` above. @@ -215,6 +228,11 @@ impl Drop for GenDisk { // to the VFS. unsafe { bindings::del_gendisk(self.gendisk) }; + // SAFETY: By type invariant, `self.gendisk` was added to the VFS, so + // `put_disk()` must follow `del_gendisk()` to drop the final gendisk + // reference and trigger the remaining release path. + unsafe { bindings::put_disk(self.gendisk) }; + // SAFETY: `queue.queuedata` was created by `GenDiskBuilder::build` with // a call to `ForeignOwnable::into_foreign` to create `queuedata`. // `ForeignOwnable::from_foreign` is only called here. From c781009975c5c5e55da8037821325b96202b0e90 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 16 Jun 2026 14:25:56 +0100 Subject: [PATCH 0169/2758] rust: doctest: fix incorrect pattern in replacement commit ac4d1caa82d487e7ed46d0597da1adc9c1a51c70 upstream. The `-> Result<(), impl core::fmt::Debug>` string is generated by rustdoc and by adding "::" into the string it no longer finds anything, making the line useless. Remove the "::" in the pattern. Omit it in the replacement too, for consistency with upstream rustdoc. Fixes: de7cd3e4d638 ("rust: use absolute paths in macros referencing core and kernel") Signed-off-by: Gary Guo Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260616132559.2245814-1-gary@kernel.org [ Added link in code comment to `rustdoc`'s 1.87 PR that fully qualified it for context. Improved comments for consistency. Reworded to drop changelog and to fix typo. - Miguel ] Signed-off-by: Miguel Ojeda Signed-off-by: Greg Kroah-Hartman --- scripts/rustdoc_test_builder.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/scripts/rustdoc_test_builder.rs b/scripts/rustdoc_test_builder.rs index f7540bcf595ac..df864437cef70 100644 --- a/scripts/rustdoc_test_builder.rs +++ b/scripts/rustdoc_test_builder.rs @@ -28,7 +28,7 @@ fn main() { // // ``` // fn main() { #[allow(non_snake_case)] fn _doctest_main_rust_kernel_file_rs_28_0() { - // fn main() { #[allow(non_snake_case)] fn _doctest_main_rust_kernel_file_rs_37_0() -> Result<(), impl ::core::fmt::Debug> { + // fn main() { #[allow(non_snake_case)] fn _doctest_main_rust_kernel_file_rs_37_0() -> Result<(), impl core::fmt::Debug> { // ``` // // It should be unlikely that doctest code matches such lines (when code is formatted properly). @@ -47,12 +47,16 @@ fn main() { }) .expect("No test function found in `rustdoc`'s output."); - // Qualify `Result` to avoid the collision with our own `Result` coming from the prelude. + // Replicate `rustdoc` 1.87+ behaviour [1] by fully qualifying `Result` to avoid the collision + // with our own `Result` coming from the prelude. + // + // [1]: https://github.com/rust-lang/rust/pull/137807 + // + // TODO: Remove this when MSRV is bumped above 1.87. let body = body.replace( - &format!("{rustdoc_function_name}() -> Result<(), impl ::core::fmt::Debug> {{"), - &format!( - "{rustdoc_function_name}() -> ::core::result::Result<(), impl ::core::fmt::Debug> {{" - ), + &format!("{rustdoc_function_name}() -> Result<(), impl core::fmt::Debug> {{"), + // This intentionally does not use absolute paths to match `rustdoc` 1.87 behaviour. + &format!("{rustdoc_function_name}() -> core::result::Result<(), impl core::fmt::Debug> {{"), ); // For tests that get generated with `Result`, like above, `rustdoc` generates an `unwrap()` on From 89592176b7187ce79fce5e60943508f51b5b379e Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 16 Jun 2026 12:30:38 +0000 Subject: [PATCH 0170/2758] rust: Kbuild: set frame-pointer llvm module flag for CONFIG_FRAME_POINTER commit 191f49f1e38b1c10eb44b0f967c6175c884ef7db upstream. Due to a rustc bug, the -Cforce-frame-pointers=y flag only emits the frame-pointer annotation for functions, but not for the module. This means that functions generated by the LLVM backend such as 'asan.module_ctor' do not receive the frame-pointer annotation. This is likely to lead to broken backtraces and may also cause issues with ftrace if these features are used with functions generated by the LLVM backend. Thus, use -Zllvm_module_flag to work around this rustc bug if using a rustc without the fix. [ The fix [1] has landed for Rust 1.98.0 (expected release on 2026-08-20). - Miguel ] Cc: stable@vger.kernel.org # 6.12.y and later (flag not available in pinned Rust in older LTSs). Fixes: 2f7ab1267dc9 ("Kbuild: add Rust support") Link: https://github.com/rust-lang/rust/pull/156980 [1] Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260616-frame-ptr-fix-v1-1-dc6b29a631d9@google.com [ - Adjusted Cc: stable@ as discussed. - Added comment with link to the PR, similar to what we did in commit ac35b5580ace ("rust: arm64: set uwtable llvm module flag for CONFIG_UNWIND_TABLES"). - Miguel ] Signed-off-by: Miguel Ojeda Signed-off-by: Greg Kroah-Hartman --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 9e405218162b6..11b44c8a72235 100644 --- a/Makefile +++ b/Makefile @@ -959,6 +959,9 @@ KBUILD_CFLAGS += $(stackp-flags-y) ifdef CONFIG_FRAME_POINTER KBUILD_CFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls KBUILD_RUSTFLAGS += -Cforce-frame-pointers=y +# Work around rustc bug on compilers without +# https://github.com/rust-lang/rust/pull/156980. +KBUILD_RUSTFLAGS += $(if $(call rustc-min-version,109800),,-Zllvm_module_flag=frame-pointer:u32:2:max) else # Some targets (ARM with Thumb2, for example), can't be built with frame # pointers. For those, we don't have FUNCTION_TRACER automatically From 007f071b2c3904933b2124793b466b3bb5f514be Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 1 Jul 2026 15:11:50 +0200 Subject: [PATCH 0171/2758] futex/requeue: Revert "Prevent NULL pointer dereference in remove_waiter() on self-deadlock"" commit 39def6d250d370298f86c116f4ac60093cefadaa upstream. The commit cited below should not have been merged. It attemted to fix an existing problem ansd thereby introduced new problems by keeping the pi_state in state Q_REQUEUE_PI_IN_PROGRESS and leaking it. Based on the commit description the intention was to handle the case when task_blocks_on_rt_mutex() returns -EDEADLK and the following remove_waiter() dereferences the NULL pointer in waiter->task. That is already handled by Davidlohr in commit 40a25d59e85b3 ("locking/rtmutex: Skip remove_waiter() when waiter is not enqueued") and requires no further acting. Revert the commit breaking the "waiter == owner" case again. Fixes: 74e144274af39 ("futex/requeue: Prevent NULL pointer dereference in remove_waiter() on self-deadlock") Reported-by: Michael Bommarito Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260701131150.0Ijhq4Dw@linutronix.de Closes: https://lore.kernel.org/all/20260629020049.2082397-1-michael.bommarito@gmail.com Signed-off-by: Greg Kroah-Hartman --- kernel/futex/requeue.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/kernel/futex/requeue.c b/kernel/futex/requeue.c index 1d99a84dc9adb..b597cb3d17fc1 100644 --- a/kernel/futex/requeue.c +++ b/kernel/futex/requeue.c @@ -643,12 +643,6 @@ int futex_requeue(u32 __user *uaddr1, unsigned int flags1, continue; } - /* Self-deadlock: non-top waiter already owns the PI futex. */ - if (rt_mutex_owner(&pi_state->pi_mutex) == this->task) { - ret = -EDEADLK; - break; - } - ret = rt_mutex_start_proxy_lock(&pi_state->pi_mutex, this->rt_waiter, this->task); From 06ccef0434e98058ddae7bcebc901f93d22b7653 Mon Sep 17 00:00:00 2001 From: Taeyang Lee <0wn@theori.io> Date: Sun, 14 Jun 2026 23:22:18 +0900 Subject: [PATCH 0172/2758] perf/core: Detach event groups during remove_on_exec [ Upstream commit 037a3c43edfb597665dd34457cd22b14692f2ba3 ] perf_event_remove_on_exec() removes events by calling perf_event_exit_event(). For top-level events, this removes the event from the context with DETACH_EXIT only. This can leave inconsistent group state when a removed event is a group leader and the group contains siblings without remove_on_exec. If the group was active, the surviving siblings can remain active and attached to the removed leader's sibling list, but are no longer represented by a valid group leader on the PMU context active lists. A later close of the removed leader uses DETACH_GROUP and can promote the still-active siblings from this stale group state. The next schedule-in can then add an already-linked active_list entry again, corrupting the PMU context active list. With DEBUG_LIST enabled, this is caught as a list_add double-add in merge_sched_in(). Fix this by detaching group relationships when remove_on_exec removes an event. This preserves the existing task-exit and revoke behavior, while ensuring surviving siblings are ungrouped before the removed event leaves the context. Fixes: 2e498d0a74e5 ("perf: Add support for event removal on exec") Signed-off-by: Taeyang Lee <0wn@theori.io> Signed-off-by: Peter Zijlstra (Intel) Link: https://patch.msgid.link/ai65GgZcC0LAlWLG@Taeyangs-MacBook-Pro.local Signed-off-by: Sasha Levin --- kernel/events/core.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index 7935d5663944e..bab0f3bd4fa8d 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -4728,7 +4728,7 @@ static void perf_remove_from_owner(struct perf_event *event); static void perf_event_exit_event(struct perf_event *event, struct perf_event_context *ctx, struct task_struct *task, - bool revoke); + unsigned long detach_flags); /* * Removes all events from the current task that have been marked @@ -4755,7 +4755,7 @@ static void perf_event_remove_on_exec(struct perf_event_context *ctx) modified = true; - perf_event_exit_event(event, ctx, ctx->task, false); + perf_event_exit_event(event, ctx, ctx->task, DETACH_GROUP); } raw_spin_lock_irqsave(&ctx->lock, flags); @@ -12900,7 +12900,7 @@ static void __pmu_detach_event(struct pmu *pmu, struct perf_event *event, /* * De-schedule the event and mark it REVOKED. */ - perf_event_exit_event(event, ctx, ctx->task, true); + perf_event_exit_event(event, ctx, ctx->task, DETACH_REVOKE); /* * All _free_event() bits that rely on event->pmu: @@ -14488,12 +14488,13 @@ static void perf_event_exit_event(struct perf_event *event, struct perf_event_context *ctx, struct task_struct *task, - bool revoke) + unsigned long detach_flags) { struct perf_event *parent_event = event->parent; - unsigned long detach_flags = DETACH_EXIT; unsigned int attach_state; + detach_flags |= DETACH_EXIT; + if (parent_event) { /* * Do not destroy the 'original' grouping; because of the @@ -14516,8 +14517,8 @@ perf_event_exit_event(struct perf_event *event, sync_child_event(event, task); } - if (revoke) - detach_flags |= DETACH_GROUP | DETACH_REVOKE; + if (detach_flags & DETACH_REVOKE) + detach_flags |= DETACH_GROUP; perf_remove_from_context(event, detach_flags); /* @@ -14605,7 +14606,7 @@ static void perf_event_exit_task_context(struct task_struct *task, bool exit) perf_event_task(task, ctx, 0); list_for_each_entry_safe(child_event, next, &ctx->event_list, event_entry) - perf_event_exit_event(child_event, ctx, exit ? task : NULL, false); + perf_event_exit_event(child_event, ctx, exit ? task : NULL, 0); mutex_unlock(&ctx->mutex); From 7a6c171c6a1ac6d1509752dac131d941a3de0b37 Mon Sep 17 00:00:00 2001 From: Pawan Gupta Date: Thu, 9 Jul 2026 15:23:10 -0700 Subject: [PATCH 0173/2758] bpf: Support for hardening against JIT spraying commit 96cce16e26dd02a8678f1e87f88a4b5cdb63b995 upstream. The BPF JIT allocator packs many small programs into larger executable allocations and reuses space within those allocations as programs are loaded and freed. When fresh code is written into space that a previous program occupied, an indirect jump into the new program can reuse a branch prediction left behind by the old one. Flush the indirect branch predictors before reusing JIT memory so that indirect jumps into a newly written program don't reuse predictions from an old program that occupied the same space. Introduce bpf_arch_pred_flush_enabled static key and bpf_arch_pred_flush static call for flushing the branch predictors on JIT memory reuse. Architectures that need a flush, can update it to a predictor flush function. By default, its a NOP and does not emit any CALL. Allocations larger than a pack are not covered by this flush. That is safe because cBPF programs (the unprivileged attack surface) are bounded well below a pack size. Issue a warning if this assumption is ever violated while the flush is active. Signed-off-by: Pawan Gupta Acked-by: Daniel Borkmann Signed-off-by: Daniel Borkmann Signed-off-by: Sasha Levin --- include/linux/filter.h | 10 ++++++++++ kernel/bpf/core.c | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/include/linux/filter.h b/include/linux/filter.h index 88a241aac36a2..2a7e6cbbbe1d3 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -1291,6 +1292,15 @@ extern long bpf_jit_limit_max; typedef void (*bpf_jit_fill_hole_t)(void *area, unsigned int size); +/* + * Flush the indirect branch predictors before reusing JIT memory, so that + * indirect jumps into a newly written program don't reuse predictions left + * behind by an old program that occupied the same space. + */ +void bpf_arch_pred_flush(void); +DECLARE_STATIC_CALL(bpf_arch_pred_flush, bpf_arch_pred_flush); +DECLARE_STATIC_KEY_FALSE(bpf_pred_flush_enabled); + void bpf_jit_fill_hole_with_zero(void *area, unsigned int size); struct bpf_binary_header * diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 6aa2a8b240306..f49b9b23f95e6 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -883,6 +884,15 @@ void bpf_jit_fill_hole_with_zero(void *area, unsigned int size) memset(area, 0, size); } +DEFINE_STATIC_CALL_NULL(bpf_arch_pred_flush, bpf_arch_pred_flush); + +/* + * Enabled once bpf_arch_pred_flush points at a real flush routine. Lets the + * pack allocator test "is a predictor flush wired up at all" with a cheap + * static branch instead of repeatedly querying the static call target. + */ +DEFINE_STATIC_KEY_FALSE(bpf_pred_flush_enabled); + #define BPF_PROG_SIZE_TO_NBITS(size) (round_up(size, BPF_PROG_CHUNK_SIZE) / BPF_PROG_CHUNK_SIZE) static DEFINE_MUTEX(pack_mutex); @@ -941,6 +951,14 @@ void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns) mutex_lock(&pack_mutex); if (size > BPF_PROG_PACK_SIZE) { + /* + * Allocations larger than a pack get their own pages, and + * predictors are not flushed for such allocation. This is only + * safe because cBPF programs (the unprivileged attack surface) + * are bounded well below a pack size. + */ + if (static_branch_unlikely(&bpf_pred_flush_enabled)) + pr_warn_once("BPF: Predictors not flushed for allocations greater than BPF_PROG_PACK_SIZE\n"); size = round_up(size, PAGE_SIZE); ptr = bpf_jit_alloc_exec(size); if (ptr) { @@ -971,6 +989,7 @@ void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns) pos = 0; found_free_area: + static_call_cond(bpf_arch_pred_flush)(); bitmap_set(pack->bitmap, pos, nbits); ptr = (void *)(pack->ptr) + (pos << BPF_PROG_CHUNK_SHIFT); From 52440e15d9628f8f239373c0f2e5e8f92feea2df Mon Sep 17 00:00:00 2001 From: Pawan Gupta Date: Thu, 9 Jul 2026 15:23:25 -0700 Subject: [PATCH 0174/2758] x86/bugs: Enable IBPB flush on BPF JIT allocation commit a3af84b0fa00ead01fcd0e28b5d773ff25990a0d upstream. Enable hardening against JIT spraying when Spectre-v2 mitigations are in use. Specifically, issue an IBPB flush on BPF JIT memory reuse. Skip enabling the IBPB flush if the BPF dispatcher is already using a retpoline sequence. This hardening applies only when BPF-JIT is in use. Guard the enabling under CONFIG_BPF_JIT so that bugs.c still builds with CONFIG_BPF_JIT=n. Signed-off-by: Pawan Gupta Acked-by: Daniel Borkmann Acked-by: Dave Hansen Signed-off-by: Daniel Borkmann Signed-off-by: Sasha Levin --- arch/x86/include/asm/nospec-branch.h | 4 +++ arch/x86/kernel/cpu/bugs.c | 50 +++++++++++++++++++++++++--- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/arch/x86/include/asm/nospec-branch.h b/arch/x86/include/asm/nospec-branch.h index 4f4b5e8a15743..b68892e6d58c4 100644 --- a/arch/x86/include/asm/nospec-branch.h +++ b/arch/x86/include/asm/nospec-branch.h @@ -388,6 +388,10 @@ extern void srso_alias_return_thunk(void); extern void entry_untrain_ret(void); extern void write_ibpb(void); +#ifdef CONFIG_BPF_JIT +extern void bpf_arch_ibpb(void); +#endif + #ifdef CONFIG_X86_64 extern void clear_bhb_loop(void); #endif diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c index 83f51cab0b1e3..d9af230c05125 100644 --- a/arch/x86/kernel/cpu/bugs.c +++ b/arch/x86/kernel/cpu/bugs.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -1651,8 +1652,21 @@ static inline const char *spectre_v2_module_string(void) { return spectre_v2_bad_module ? " - vulnerable module loaded" : ""; } + +/* + * The "retpoline sequence" is the "call;mov;ret" sequence that + * replaces normal indirect branch instructions. Differentiate + * *the* retpoline sequence from the LFENCE-prefixed indirect + * branches that simply use the retpoline infrastructure. + */ +static inline bool retpoline_seq_enabled(void) +{ + return boot_cpu_has(X86_FEATURE_RETPOLINE) && !boot_cpu_has(X86_FEATURE_RETPOLINE_LFENCE); +} + #else static inline const char *spectre_v2_module_string(void) { return ""; } +static inline bool retpoline_seq_enabled(void) { return false; } #endif #define SPECTRE_V2_LFENCE_MSG "WARNING: LFENCE mitigation is not recommended for this CPU, data leaks possible!\n" @@ -2095,8 +2109,7 @@ static void __init bhi_apply_mitigation(void) return; /* Retpoline mitigates against BHI unless the CPU has RRSBA behavior */ - if (boot_cpu_has(X86_FEATURE_RETPOLINE) && - !boot_cpu_has(X86_FEATURE_RETPOLINE_LFENCE)) { + if (retpoline_seq_enabled()) { spec_ctrl_disable_kernel_rrsba(); if (rrsba_disabled) return; @@ -2238,6 +2251,27 @@ static void __init spectre_v2_update_mitigation(void) pr_info("%s\n", spectre_v2_strings[spectre_v2_enabled]); } +#ifdef CONFIG_BPF_JIT +static void __bpf_arch_ibpb(void *unused) +{ + write_ibpb(); +} + +void bpf_arch_ibpb(void) +{ + on_each_cpu(__bpf_arch_ibpb, NULL, 1); +} + +static bool __init cpu_wants_ibpb_bpf(void) +{ + /* A genuine retpoline already neutralizes ring0 indirect predictions */ + if (retpoline_seq_enabled()) + return false; + + return boot_cpu_has(X86_FEATURE_IBPB); +} +#endif + static void __init spectre_v2_apply_mitigation(void) { if (spectre_v2_enabled == SPECTRE_V2_EIBRS && unprivileged_ebpf_enabled()) @@ -2314,6 +2348,14 @@ static void __init spectre_v2_apply_mitigation(void) setup_force_cpu_cap(X86_FEATURE_USE_IBRS_FW); pr_info("Enabling Restricted Speculation for firmware calls\n"); } + +#ifdef CONFIG_BPF_JIT + if (cpu_wants_ibpb_bpf()) { + static_call_update(bpf_arch_pred_flush, bpf_arch_ibpb); + static_branch_enable(&bpf_pred_flush_enabled); + pr_info("Enabling IBPB for BPF\n"); + } +#endif } static void update_stibp_msr(void * __unused) @@ -3490,9 +3532,7 @@ static const char *spectre_bhi_state(void) return "; BHI: BHI_DIS_S"; else if (boot_cpu_has(X86_FEATURE_CLEAR_BHB_LOOP)) return "; BHI: SW loop, KVM: SW loop"; - else if (boot_cpu_has(X86_FEATURE_RETPOLINE) && - !boot_cpu_has(X86_FEATURE_RETPOLINE_LFENCE) && - rrsba_disabled) + else if (retpoline_seq_enabled() && rrsba_disabled) return "; BHI: Retpoline"; else if (boot_cpu_has(X86_FEATURE_CLEAR_BHB_VMEXIT)) return "; BHI: Vulnerable, KVM: SW loop"; From 7ff3b159b8b71714f747fee50cbd93f0b5d6a1dc Mon Sep 17 00:00:00 2001 From: Pawan Gupta Date: Thu, 9 Jul 2026 15:23:41 -0700 Subject: [PATCH 0175/2758] bpf: Restrict JIT predictor flush to cBPF commit 0bb99f2cfaae6822d734d69722de30af823efdf3 upstream. Currently predictor flush on memory reuse is done for all BPF JIT allocations, but only cBPF programs can be loaded by an unprivileged user. eBPF is privileged by default, and flushing predictors for all CPUs on every eBPF reuse penalizes the common case for no security benefit. eBPF allocations can be frequent on busy systems, only flush predictors for cBPF programs. Trampoline and dispatcher allocations also skip the flush as they are eBPF-only. Signed-off-by: Pawan Gupta Acked-by: Daniel Borkmann Signed-off-by: Daniel Borkmann Signed-off-by: Sasha Levin --- arch/arm64/net/bpf_jit_comp.c | 4 ++-- arch/loongarch/net/bpf_jit.c | 5 +++-- arch/powerpc/net/bpf_jit_comp.c | 4 ++-- arch/riscv/net/bpf_jit_comp64.c | 2 +- arch/riscv/net/bpf_jit_core.c | 3 ++- arch/x86/net/bpf_jit_comp.c | 5 +++-- include/linux/filter.h | 5 +++-- kernel/bpf/core.c | 13 ++++++++----- kernel/bpf/dispatcher.c | 2 +- 9 files changed, 25 insertions(+), 18 deletions(-) diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c index 0816c40fc7af9..9491c987e50c0 100644 --- a/arch/arm64/net/bpf_jit_comp.c +++ b/arch/arm64/net/bpf_jit_comp.c @@ -2094,7 +2094,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr image_size = extable_offset + extable_size; ro_header = bpf_jit_binary_pack_alloc(image_size, &ro_image_ptr, sizeof(u64), &header, &image_ptr, - jit_fill_hole); + jit_fill_hole, was_classic); if (!ro_header) goto out_off; @@ -2782,7 +2782,7 @@ int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags, void *arch_alloc_bpf_trampoline(unsigned int size) { - return bpf_prog_pack_alloc(size, jit_fill_hole); + return bpf_prog_pack_alloc(size, jit_fill_hole, false); } void arch_free_bpf_trampoline(void *image, unsigned int size) diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c index 24913dc7f4e83..e14a8aa47fc8e 100644 --- a/arch/loongarch/net/bpf_jit.c +++ b/arch/loongarch/net/bpf_jit.c @@ -1762,7 +1762,7 @@ static int invoke_bpf(struct jit_ctx *ctx, struct bpf_tramp_links *tl, void *arch_alloc_bpf_trampoline(unsigned int size) { - return bpf_prog_pack_alloc(size, jit_fill_hole); + return bpf_prog_pack_alloc(size, jit_fill_hole, false); } void arch_free_bpf_trampoline(void *image, unsigned int size) @@ -2228,7 +2228,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr image_size = prog_size + extable_size; /* Now we know the size of the structure to make */ ro_header = bpf_jit_binary_pack_alloc(image_size, &ro_image_ptr, sizeof(u32), - &header, &image_ptr, jit_fill_hole); + &header, &image_ptr, jit_fill_hole, + bpf_prog_was_classic(prog)); if (!ro_header) goto out_offset; diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c index 53ab97ad60745..73ff8a64bb7fa 100644 --- a/arch/powerpc/net/bpf_jit_comp.c +++ b/arch/powerpc/net/bpf_jit_comp.c @@ -295,7 +295,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr alloclen = proglen + FUNCTION_DESCR_SIZE + fixup_len + extable_len; fhdr = bpf_jit_binary_pack_alloc(alloclen, &fimage, 4, &hdr, &image, - bpf_jit_fill_ill_insns); + bpf_jit_fill_ill_insns, bpf_prog_was_classic(fp)); if (!fhdr) goto out_err; @@ -583,7 +583,7 @@ bool bpf_jit_inlines_helper_call(s32 imm) void *arch_alloc_bpf_trampoline(unsigned int size) { - return bpf_prog_pack_alloc(size, bpf_jit_fill_ill_insns); + return bpf_prog_pack_alloc(size, bpf_jit_fill_ill_insns, false); } void arch_free_bpf_trampoline(void *image, unsigned int size) diff --git a/arch/riscv/net/bpf_jit_comp64.c b/arch/riscv/net/bpf_jit_comp64.c index 2f1109dbf105b..404a98a0fc90e 100644 --- a/arch/riscv/net/bpf_jit_comp64.c +++ b/arch/riscv/net/bpf_jit_comp64.c @@ -1321,7 +1321,7 @@ int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags, void *arch_alloc_bpf_trampoline(unsigned int size) { - return bpf_prog_pack_alloc(size, bpf_fill_ill_insns); + return bpf_prog_pack_alloc(size, bpf_fill_ill_insns, false); } void arch_free_bpf_trampoline(void *image, unsigned int size) diff --git a/arch/riscv/net/bpf_jit_core.c b/arch/riscv/net/bpf_jit_core.c index 4365d07aaf547..ce3bd3762e08c 100644 --- a/arch/riscv/net/bpf_jit_core.c +++ b/arch/riscv/net/bpf_jit_core.c @@ -109,7 +109,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr bpf_jit_binary_pack_alloc(prog_size + extable_size, &jit_data->ro_image, sizeof(u32), &jit_data->header, &jit_data->image, - bpf_fill_ill_insns); + bpf_fill_ill_insns, + bpf_prog_was_classic(prog)); if (!jit_data->ro_header) goto out_offset; diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index ea9e707e8abff..ef8b112fd7b69 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -3520,7 +3520,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im void *arch_alloc_bpf_trampoline(unsigned int size) { - return bpf_prog_pack_alloc(size, jit_fill_hole); + return bpf_prog_pack_alloc(size, jit_fill_hole, false); } void arch_free_bpf_trampoline(void *image, unsigned int size) @@ -3831,7 +3831,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr /* allocate module memory for x86 insns and extable */ header = bpf_jit_binary_pack_alloc(roundup(proglen, align) + extable_size, &image, align, &rw_header, &rw_image, - jit_fill_hole); + jit_fill_hole, + bpf_prog_was_classic(prog)); if (!header) goto out_addrs; prog->aux->extable = (void *) image + roundup(proglen, align); diff --git a/include/linux/filter.h b/include/linux/filter.h index 2a7e6cbbbe1d3..ea654453e43a3 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -1315,7 +1315,7 @@ void bpf_jit_free(struct bpf_prog *fp); struct bpf_binary_header * bpf_jit_binary_pack_hdr(const struct bpf_prog *fp); -void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns); +void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns, bool was_classic); void bpf_prog_pack_free(void *ptr, u32 size); static inline bool bpf_prog_kallsyms_verify_off(const struct bpf_prog *fp) @@ -1329,7 +1329,8 @@ bpf_jit_binary_pack_alloc(unsigned int proglen, u8 **ro_image, unsigned int alignment, struct bpf_binary_header **rw_hdr, u8 **rw_image, - bpf_jit_fill_hole_t bpf_fill_ill_insns); + bpf_jit_fill_hole_t bpf_fill_ill_insns, + bool was_classic); int bpf_jit_binary_pack_finalize(struct bpf_binary_header *ro_header, struct bpf_binary_header *rw_header); void bpf_jit_binary_pack_free(struct bpf_binary_header *ro_header, diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index f49b9b23f95e6..1b7e74e63bd4b 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -942,7 +942,7 @@ static struct bpf_prog_pack *alloc_new_pack(bpf_jit_fill_hole_t bpf_fill_ill_ins return NULL; } -void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns) +void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns, bool was_classic) { unsigned int nbits = BPF_PROG_SIZE_TO_NBITS(size); struct bpf_prog_pack *pack; @@ -957,7 +957,7 @@ void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns) * safe because cBPF programs (the unprivileged attack surface) * are bounded well below a pack size. */ - if (static_branch_unlikely(&bpf_pred_flush_enabled)) + if (was_classic && static_branch_unlikely(&bpf_pred_flush_enabled)) pr_warn_once("BPF: Predictors not flushed for allocations greater than BPF_PROG_PACK_SIZE\n"); size = round_up(size, PAGE_SIZE); ptr = bpf_jit_alloc_exec(size); @@ -989,7 +989,9 @@ void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns) pos = 0; found_free_area: - static_call_cond(bpf_arch_pred_flush)(); + /* Flush only for cBPF as it may contain a crafted gadget */ + if (static_branch_unlikely(&bpf_pred_flush_enabled) && was_classic) + static_call_cond(bpf_arch_pred_flush)(); bitmap_set(pack->bitmap, pos, nbits); ptr = (void *)(pack->ptr) + (pos << BPF_PROG_CHUNK_SHIFT); @@ -1149,7 +1151,8 @@ bpf_jit_binary_pack_alloc(unsigned int proglen, u8 **image_ptr, unsigned int alignment, struct bpf_binary_header **rw_header, u8 **rw_image, - bpf_jit_fill_hole_t bpf_fill_ill_insns) + bpf_jit_fill_hole_t bpf_fill_ill_insns, + bool was_classic) { struct bpf_binary_header *ro_header; u32 size, hole, start; @@ -1162,7 +1165,7 @@ bpf_jit_binary_pack_alloc(unsigned int proglen, u8 **image_ptr, if (bpf_jit_charge_modmem(size)) return NULL; - ro_header = bpf_prog_pack_alloc(size, bpf_fill_ill_insns); + ro_header = bpf_prog_pack_alloc(size, bpf_fill_ill_insns, was_classic); if (!ro_header) { bpf_jit_uncharge_modmem(size); return NULL; diff --git a/kernel/bpf/dispatcher.c b/kernel/bpf/dispatcher.c index b77db7413f8c7..ea2d60dc1feeb 100644 --- a/kernel/bpf/dispatcher.c +++ b/kernel/bpf/dispatcher.c @@ -145,7 +145,7 @@ void bpf_dispatcher_change_prog(struct bpf_dispatcher *d, struct bpf_prog *from, mutex_lock(&d->mutex); if (!d->image) { - d->image = bpf_prog_pack_alloc(PAGE_SIZE, bpf_jit_fill_hole_with_zero); + d->image = bpf_prog_pack_alloc(PAGE_SIZE, bpf_jit_fill_hole_with_zero, false); if (!d->image) goto out; d->rw_image = bpf_jit_alloc_exec(PAGE_SIZE); From 80a96785fe42062cadac701dfc08049b13ac741a Mon Sep 17 00:00:00 2001 From: Pawan Gupta Date: Thu, 9 Jul 2026 15:23:56 -0700 Subject: [PATCH 0176/2758] bpf: Skip redundant IBPB in pack allocator commit a23c1c5396a91680703360d1ee28a44657c503c4 upstream. bpf_prog_pack_alloc() issues IBPB on all CPUs on every cBPF allocation, even when reusing chunks from an existing pack where no new memory was touched since the last IBPB. Since IBPB on all CPUs is heavy, Dave Hansen suggested to track allocation since last IBPB, and only issue IBPB at reuse for the chunks that have not seen an IBPB since they were last freed. Track per-pack whether an IBPB is needed via arch_flush_needed. Set it when allocating a chunk, reset on IBPB flush. On reuse, conditionally issue the flush. Since IBPB invalidates all BTB entries, clear the flag on all packs after flushing. Signed-off-by: Pawan Gupta Acked-by: Daniel Borkmann Signed-off-by: Daniel Borkmann Signed-off-by: Sasha Levin --- kernel/bpf/core.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 1b7e74e63bd4b..1bf4b8dffe915 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -876,6 +876,7 @@ int bpf_jit_add_poke_descriptor(struct bpf_prog *prog, struct bpf_prog_pack { struct list_head list; void *ptr; + bool arch_flush_needed; unsigned long bitmap[]; }; @@ -928,6 +929,8 @@ static struct bpf_prog_pack *alloc_new_pack(bpf_jit_fill_hole_t bpf_fill_ill_ins bpf_fill_ill_insns(pack->ptr, BPF_PROG_PACK_SIZE); bitmap_zero(pack->bitmap, BPF_PROG_PACK_SIZE / BPF_PROG_CHUNK_SIZE); + if (static_branch_unlikely(&bpf_pred_flush_enabled)) + pack->arch_flush_needed = true; set_vm_flush_reset_perms(pack->ptr); err = set_memory_rox((unsigned long)pack->ptr, BPF_PROG_PACK_SIZE / PAGE_SIZE); @@ -990,8 +993,15 @@ void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns, bool found_free_area: /* Flush only for cBPF as it may contain a crafted gadget */ - if (static_branch_unlikely(&bpf_pred_flush_enabled) && was_classic) + if (static_branch_unlikely(&bpf_pred_flush_enabled) && + pack->arch_flush_needed && + was_classic) { + struct bpf_prog_pack *p; + static_call_cond(bpf_arch_pred_flush)(); + list_for_each_entry(p, &pack_list, list) + p->arch_flush_needed = false; + } bitmap_set(pack->bitmap, pos, nbits); ptr = (void *)(pack->ptr) + (pos << BPF_PROG_CHUNK_SHIFT); @@ -1029,6 +1039,9 @@ void bpf_prog_pack_free(void *ptr, u32 size) "bpf_prog_pack bug: missing bpf_arch_text_invalidate?\n"); bitmap_clear(pack->bitmap, pos, nbits); + + if (static_branch_unlikely(&bpf_pred_flush_enabled)) + pack->arch_flush_needed = true; if (bitmap_find_next_zero_area(pack->bitmap, BPF_PROG_CHUNK_COUNT, 0, BPF_PROG_CHUNK_COUNT, 0) == 0) { list_del(&pack->list); From 3448efcb18aecb5c635e60958d1a29b1f53f0ac6 Mon Sep 17 00:00:00 2001 From: Pawan Gupta Date: Thu, 9 Jul 2026 15:24:31 -0700 Subject: [PATCH 0177/2758] bpf: Prefer packs that won't trigger an IBPB flush on allocation commit a9b1f19a6a673ba06820898d0f1ad02883ea1639 upstream. Currently BPF pack allocator picks the chunks from the first available pack. While this is okay, it naturally leads to more frequent flushes when there are multiple packs in the system that weren't used since the last flush. As an optimization prefer allocating the new programs from packs that are unused since last flush. When all packs are dirty, allocation forces a flush and marks all packs clean. Below are some future optimizations ideas: 1. Currently, the "dirty" tracking is only done at the pack-level. Flush frequency can further be reduced with chunk-level tracking. This requires a new bitmap per-pack to track the dirty state. 2. IBPB flush is done on all CPUs, even if only a single CPU ran the BPF program. On a system with hundreds of CPUs this could be a major bottleneck forcing hundreds of IPIs to deliver the flush. The solution is to track the CPUs where a BPF program ran, and issue IBPB only on those CPUs. 3. Avoid IBPB when flush is already done at other sources (e.g. context switch). Signed-off-by: Pawan Gupta Acked-by: Daniel Borkmann Signed-off-by: Daniel Borkmann Signed-off-by: Sasha Levin --- kernel/bpf/core.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 1bf4b8dffe915..bba4acd61d412 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -948,8 +948,8 @@ static struct bpf_prog_pack *alloc_new_pack(bpf_jit_fill_hole_t bpf_fill_ill_ins void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns, bool was_classic) { unsigned int nbits = BPF_PROG_SIZE_TO_NBITS(size); - struct bpf_prog_pack *pack; - unsigned long pos; + struct bpf_prog_pack *pack, *fallback_pack = NULL; + unsigned long pos, fallback_pos = 0; void *ptr = NULL; mutex_lock(&pack_mutex); @@ -981,8 +981,29 @@ void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns, bool list_for_each_entry(pack, &pack_list, list) { pos = bitmap_find_next_zero_area(pack->bitmap, BPF_PROG_CHUNK_COUNT, 0, nbits, 0); - if (pos < BPF_PROG_CHUNK_COUNT) + if (pos >= BPF_PROG_CHUNK_COUNT) + continue; + /* Flush not enabled, use any pack */ + if (!static_branch_unlikely(&bpf_pred_flush_enabled)) goto found_free_area; + /* + * cBPF reuse of a dirty pack triggers a flush, so prefer a + * clean pack for cBPF. eBPF never flushes, so pick the first + * free pack, dirty or clean. + */ + if (!was_classic || !pack->arch_flush_needed) + goto found_free_area; + if (!fallback_pack) { + fallback_pack = pack; + fallback_pos = pos; + } + } + + /* No preferred pack found */ + if (fallback_pack) { + pack = fallback_pack; + pos = fallback_pos; + goto found_free_area; } pack = alloc_new_pack(bpf_fill_ill_insns); From 29d3f527bc1af1fef124c0e55ba4ed3f746fe720 Mon Sep 17 00:00:00 2001 From: Pawan Gupta Date: Thu, 9 Jul 2026 15:24:47 -0700 Subject: [PATCH 0178/2758] bpf: Prefer dirty packs for eBPF allocations commit b72e29e0f7ee329d89f86db8700c8ea99b4a370a upstream. The pack allocator only flushes predictors when reusing a dirty pack for cBPF, eBPF allocations never trigger a flush. Currently, eBPF picks the first free pack, which could be a clean pack. As an optimization, leaving a clean pack for cBPF can avoid flushes. Prefer dirty packs for eBPF and keep clean packs free for cBPF. This mirrors the existing cBPF preference for clean packs: each program kind prefers the pack that avoids an extra flush, and falls back to the other kind only when no preferred pack has room. eBPF reuse of a dirty pack is harmless since eBPF being privileged does not flush. Signed-off-by: Pawan Gupta Acked-by: Daniel Borkmann Signed-off-by: Daniel Borkmann Signed-off-by: Sasha Levin --- kernel/bpf/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index bba4acd61d412..de61e1894452e 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -988,10 +988,10 @@ void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns, bool goto found_free_area; /* * cBPF reuse of a dirty pack triggers a flush, so prefer a - * clean pack for cBPF. eBPF never flushes, so pick the first - * free pack, dirty or clean. + * clean pack for cBPF. eBPF never flushes, so steer it to a + * dirty pack and keep clean packs free for cBPF. */ - if (!was_classic || !pack->arch_flush_needed) + if (was_classic ^ pack->arch_flush_needed) goto found_free_area; if (!fallback_pack) { fallback_pack = pack; From 994994cfadaf1fd362dea9b8d9d633f85dc1b3c3 Mon Sep 17 00:00:00 2001 From: Po-Hao Huang Date: Fri, 15 May 2026 09:44:29 +0800 Subject: [PATCH 0179/2758] wifi: rtw89: correct drop logic for malformed AMPDU frames [ Upstream commit 63ccdfac8677387dfdbd9d4336089e9823280704 ] The previous commit aims to fix issue caused by malformed AMPDU frames. But the drop logic fails to deal with the first AMPDU packet paired with certain range of sequence number, and leads to unexpected packet drop. It is more likely to encounter this failure when there are busy traffic during rekey process and could lead to disconnection from the AP. Fix this by adding a initial state judgement and only reset status during pairwise rekey. Fixes: bda294ed0ed0 ("wifi: rtw89: Drop malformed AMPDU frames with abnormal PN") Signed-off-by: Po-Hao Huang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260515014433.16168-10-pkshih@realtek.com Signed-off-by: Sasha Levin --- drivers/net/wireless/realtek/rtw89/core.c | 3 ++- drivers/net/wireless/realtek/rtw89/mac80211.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 70feab97dccb5..486c1774fb975 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -3363,7 +3363,8 @@ static bool rtw89_core_skb_pn_valid(struct rtw89_dev *rtwdev, last_pn = tid_stats->last_pn; if (pn > last_pn) { - if (ieee80211_sn_less(mpdu_sn, tid_stats->last_sn)) { + if (last_pn != -1LL && + ieee80211_sn_less(mpdu_sn, tid_stats->last_sn)) { dev_kfree_skb_any(skb); return false; diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index 501c3af1da01a..0672d13d13a64 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -964,7 +964,8 @@ static int rtw89_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, rtw89_err(rtwdev, "failed to add key to sec cam\n"); return ret; } - rtw89_core_tid_rx_stats_reset(rtwdev); + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) + rtw89_core_tid_rx_stats_reset(rtwdev); break; case DISABLE_KEY: flush_work(&rtwdev->txq_work); From b09716040f3fa4a252eeda3ceb5295ea0e39c1fb Mon Sep 17 00:00:00 2001 From: Griffin Kroah-Hartman Date: Wed, 8 Jul 2026 13:08:58 +0200 Subject: [PATCH 0180/2758] usb: gadget: function: rndis: add length check to response query commit 95f90eea070837f7c72207d5520f805bdefc3bc5 upstream. Add variable representations for BufLength and BufOffset in rndis_query_response(), and perform a length check on them. This is identical to how rndis_set_response() handles these parameters. Assisted-by: gkh_clanker_2000 Cc: stable Signed-off-by: Griffin Kroah-Hartman Link: https://patch.msgid.link/20260708-usb-gadget-rndis-v1-1-e77e026dcc6a@kroah.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/rndis.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c index 3da54a7d7aba8..fe2018ff071a6 100644 --- a/drivers/usb/gadget/function/rndis.c +++ b/drivers/usb/gadget/function/rndis.c @@ -591,6 +591,7 @@ static int rndis_init_response(struct rndis_params *params, static int rndis_query_response(struct rndis_params *params, rndis_query_msg_type *buf) { + u32 BufLength, BufOffset; rndis_query_cmplt_type *resp; rndis_resp_t *r; @@ -598,6 +599,13 @@ static int rndis_query_response(struct rndis_params *params, if (!params->dev) return -ENOTSUPP; + BufLength = le32_to_cpu(buf->InformationBufferLength); + BufOffset = le32_to_cpu(buf->InformationBufferOffset); + if ((BufLength > RNDIS_MAX_TOTAL_SIZE) || + (BufOffset > RNDIS_MAX_TOTAL_SIZE) || + (BufOffset + 8 >= RNDIS_MAX_TOTAL_SIZE)) + return -EINVAL; + /* * we need more memory: * gen_ndis_query_resp expects enough space for @@ -614,10 +622,8 @@ static int rndis_query_response(struct rndis_params *params, resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ if (gen_ndis_query_resp(params, le32_to_cpu(buf->OID), - le32_to_cpu(buf->InformationBufferOffset) - + 8 + (u8 *)buf, - le32_to_cpu(buf->InformationBufferLength), - r)) { + BufOffset + 8 + (u8 *)buf, + BufLength, r)) { /* OID not supported */ resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED); resp->MessageLength = cpu_to_le32(sizeof *resp); From 9facd79028a7807879eb441d12f0e00720980aa3 Mon Sep 17 00:00:00 2001 From: Griffin Kroah-Hartman Date: Wed, 8 Jul 2026 13:08:59 +0200 Subject: [PATCH 0181/2758] usb: gadget: function: rndis: add length check for header commit 21b5bf155435008e0fb0736795289788e63d426f upstream. Add a length check for the rndis header in rndis_rm_hdr, to ensure that MessageType, MessageLength, DataOffset, and DataLength fields are present before they are accessed. Assisted-by: gkh_clanker_2000 Cc: stable Signed-off-by: Griffin Kroah-Hartman Link: https://patch.msgid.link/20260708-usb-gadget-rndis-v1-2-e77e026dcc6a@kroah.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/rndis.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c index fe2018ff071a6..a2fd239b7ad33 100644 --- a/drivers/usb/gadget/function/rndis.c +++ b/drivers/usb/gadget/function/rndis.c @@ -1080,6 +1080,12 @@ int rndis_rm_hdr(struct gether *port, /* tmp points to a struct rndis_packet_msg_type */ __le32 *tmp = (void *)skb->data; + /* Need at least MessageType, MessageLength, DataOffset, DataLength */ + if (skb->len < 16) { + dev_kfree_skb_any(skb); + return -EINVAL; + } + /* MessageType, MessageLength */ if (cpu_to_le32(RNDIS_MSG_PACKET) != get_unaligned(tmp++)) { From 35a3cd8fd65e15029eb90f1e510045b1bb071175 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sat, 13 Jun 2026 02:18:39 -0500 Subject: [PATCH 0182/2758] iio: accel: bmc150: clamp the device-reported FIFO frame count commit ce0e1cae26096fe959a0da5563a6d6d5a801d5fb upstream. __bmc150_accel_fifo_flush() copies the number of samples the device reports in its hardware FIFO into an on-stack buffer u16 buffer[BMC150_ACCEL_FIFO_LENGTH * 3]; which is sized for at most BMC150_ACCEL_FIFO_LENGTH (32) samples. The frame count is read from the FIFO_STATUS register and only masked to its 7 valid bits: count = val & 0x7F; so it can be 0..127. The only other limit applied to it is the optional caller-supplied sample budget: if (samples && count > samples) count = samples; which does not constrain count on the flush-all path (samples == 0), and leaves it well above 32 whenever samples is larger. count samples are then transferred into buffer[]: bmc150_accel_fifo_transfer(data, (u8 *)buffer, count); bmc150_accel_fifo_transfer() reads count * 6 bytes through regmap, so a malfunctioning, malicious or counterfeit accelerometer (or an attacker tampering with the I2C/SPI bus) that reports up to 127 frames writes up to 762 bytes into the 192-byte buffer: a stack out-of-bounds write of up to 570 bytes that clobbers the stack canary, saved registers and the return address. Clamp count to BMC150_ACCEL_FIFO_LENGTH, the number of samples buffer[] is sized for, before the transfer, mirroring the watermark clamp already done in bmc150_accel_set_watermark(). A well-formed flush reports at most BMC150_ACCEL_FIFO_LENGTH frames, so legitimate devices are unaffected. Fixes: 3bbec9773389 ("iio: bmc150_accel: add support for hardware fifo") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/accel/bmc150-accel-core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index 2398eb7e12cdd..dc8a6285cf3df 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -991,6 +991,8 @@ static int __bmc150_accel_fifo_flush(struct iio_dev *indio_dev, if (samples && count > samples) count = samples; + count = min_t(u8, count, BMC150_ACCEL_FIFO_LENGTH); + ret = bmc150_accel_fifo_transfer(data, (u8 *)buffer, count); if (ret) return ret; From 13a91e8631cfeb68e5b7fd6687f194f5a86e83fe Mon Sep 17 00:00:00 2001 From: Biren Pandya Date: Sun, 14 Jun 2026 12:45:46 +0530 Subject: [PATCH 0183/2758] iio: accel: kxsd9: fix runtime PM imbalance on write_raw() error commit 44a5fd874bb6873bdaec59f722c1d57832fbc9df upstream. kxsd9_write_raw() takes a runtime PM reference with pm_runtime_get_sync() but returns -EINVAL directly when a scale with a non-zero integer part is requested, skipping the matching pm_runtime_put_autosuspend(). This leaks a runtime PM usage-counter reference on every such write, after which the device can no longer autosuspend. Set the error code and fall through to the existing put instead of returning early. Fixes: 9a9a369d6178 ("iio: accel: kxsd9: Deploy system and runtime PM") Signed-off-by: Biren Pandya Assisted-by: Claude:claude-opus-4-8 coccinelle Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/accel/kxsd9.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/iio/accel/kxsd9.c b/drivers/iio/accel/kxsd9.c index 4717d80fc24af..7ac885d94d7f4 100644 --- a/drivers/iio/accel/kxsd9.c +++ b/drivers/iio/accel/kxsd9.c @@ -147,8 +147,9 @@ static int kxsd9_write_raw(struct iio_dev *indio_dev, if (mask == IIO_CHAN_INFO_SCALE) { /* Check no integer component */ if (val) - return -EINVAL; - ret = kxsd9_write_scale(indio_dev, val2); + ret = -EINVAL; + else + ret = kxsd9_write_scale(indio_dev, val2); } pm_runtime_put_autosuspend(st->dev); From 5d32dd6338c872923f01a0c97a88c77aacdaffb5 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 15 May 2026 11:07:53 +0200 Subject: [PATCH 0184/2758] iio: adc: ad4062: add GPIOLIB dependency commit a5b7991d5a737df71a5e4230554481255af64ed4 upstream. The ad4062 driver gained support for the gpiochip and now fails to build when GPIOLIB is disabled: 390-linux-ld: drivers/iio/adc/ad4062.o: in function `ad4062_gpio_get': drivers/iio/adc/ad4062.c:1383:(.text+0x3dc): undefined reference to `gpiochip_get_data` Add a Kconfig dependency for this. Fixes: da1d3596b1e4 ("iio: adc: ad4062: Add GPIO Controller support") Signed-off-by: Arnd Bergmann Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index a9dedbb8eb461..2dcb9980d7c8d 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -78,6 +78,7 @@ config AD4030 config AD4062 tristate "Analog Devices AD4062 Driver" depends on I3C + depends on GPIOLIB select REGMAP_I3C select IIO_BUFFER select IIO_TRIGGERED_BUFFER From e3f3fcf011e7e4f2b34bfe91e468d15b13eba946 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Tue, 2 Jun 2026 16:45:39 +0000 Subject: [PATCH 0185/2758] iio: adc: ad7380: select REGMAP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 6697091b386a4e2830bdd38512c87a4befff2b32 upstream. The AD7380 driver uses generic regmap types and APIs. However, its Kconfig entry does not select REGMAP. As a result, AD7380 can be enabled from an allnoconfig-derived config with SPI_MASTER=y while REGMAP remains unset, causing ad7380.o to fail to build. Fixes: b095217c104b ("iio: adc: ad7380: new driver for AD7380 ADCs") Signed-off-by: Samuel Moelius Reviewed-by: Andy Shevchenko Reviewed-by: Nuno Sá Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 2dcb9980d7c8d..b1e8455d6b87f 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -312,6 +312,7 @@ config AD7298 config AD7380 tristate "Analog Devices AD7380 ADC driver" depends on SPI_MASTER + select REGMAP select SPI_OFFLOAD select IIO_BUFFER select IIO_BUFFER_DMAENGINE From f75a12808cd30dfc199230ad2c0e1dac1d8fb906 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 11 Jan 2026 16:55:28 +0000 Subject: [PATCH 0186/2758] iio: adc: ad7768-1: Select GPIOLIB commit 5a9c90350be4f6f175bdd193e8bd60a7aedfb4d2 upstream. This driver provides a gpio chip a some related functions are not stubbed if GPIOLIB is not built. ad7768-1.c:420: undefined reference to `gpiochip_get_data' ad7768-1.c:498: undefined reference to `devm_gpiochip_add_data_with_key' Dual sign off reflects change of affiliation whilst this was under review. Fixes: d569ae0f052e ("iio: adc: ad7768-1: Add GPIO controller support") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202512271235.WwAmAbOa-lkp@intel.com/ Signed-off-by: Jonathan Cameron Cc: Stable@vger.kernel.org Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index b1e8455d6b87f..f949dca0de589 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -416,6 +416,7 @@ config AD7766 config AD7768_1 tristate "Analog Devices AD7768-1 ADC driver" depends on SPI + select GPIOLIB select REGULATOR select REGMAP_SPI select RATIONAL From 73a92d5e3d78eabb21afc605b30e8a27c7529dbd Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Wed, 1 Jul 2026 21:21:47 +0200 Subject: [PATCH 0187/2758] iio: adc: ad7779: add missing 'select IIO_TRIGGERED_BUFFER' to Kconfig commit fd354554af1d2b33232ca6c8a3d79ed82413d715 upstream. The Kconfig entry for the AD7779 is missing a 'select IIO_TRIGGERED_BUFFER' parameter, causing build failures. Fixes: c9a3f8c7bfcb ("drivers: iio: adc: add support for ad777x family") Cc: stable@vger.kernel.org Signed-off-by: Joshua Crofts Tested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index f949dca0de589..3a3dcba3db3b0 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -437,6 +437,7 @@ config AD7779 depends on SPI select CRC8 select IIO_BUFFER + select IIO_TRIGGERED_BUFFER select IIO_BACKEND help Say yes here to build support for Analog Devices AD777X family From 3bceb26dfaf7ba805b459e41c1d0ba916862dade Mon Sep 17 00:00:00 2001 From: Radu Sabau Date: Wed, 27 May 2026 12:38:39 +0300 Subject: [PATCH 0188/2758] iio: adc: ad_sigma_delta: fix clear_pending_event for registerless devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 91bc6767a4f55dc470d8a56b55b9f2ea09094efe upstream. ad_sigma_delta_clear_pending_event() falls through to the status register read path for devices with has_registers = false and no rdy_gpiod. For such devices, ad_sd_read_reg() skips the address byte entirely and clocks raw MISO bytes with no address phase — making it byte-for-byte identical to reading conversion data. If a pending conversion result is present, this partially consumes it and corrupts the data stream for the subsequent ad_sd_read_reg() call in ad_sigma_delta_single_conversion(). Furthermore, with num_resetclks = 0 on these devices, data_read_len evaluates to 0. If the clocked byte has bit 7 clear, pending_event is set and the code attempts memset(data + 2, 0xff, 0 - 1), overflowing to SIZE_MAX and corrupting the heap. Fix by returning 0 immediately when neither rdy_gpiod nor has_registers is set. This is safe for all current registerless devices: ad7191 and ad7780 (with powerdown GPIO) are reset between conversions by CS deassertion, so there is no stale result to drain; ad7780 (without powerdown GPIO) and max11205 are continuously-converting and cycle ~DRDY at the output data rate regardless of whether the previous result was read, so the next falling edge fires naturally. A future registerless device that holds ~DRDY asserted until data is read would be broken by this early return and would require either num_resetclks set or a rdy-gpio. The same heap corruption is reachable on any device with rdy_gpiod set but num_resetclks = 0: if the GPIO indicates a pending event, the drain path executes memset(data + 2, 0xff, 0 - 1) regardless of has_registers. Add an explicit data_read_len == 0 guard after the pending event check; the stale result is then consumed by the first ad_sd_read_reg() call in ad_sigma_delta_single_conversion(). Fixes: 132d44dc6966 ("iio: adc: ad_sigma_delta: Check for previous ready signals") Signed-off-by: Radu Sabau Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/ad_sigma_delta.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index a955556f9ec88..eabf89e140285 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -262,11 +262,25 @@ static int ad_sigma_delta_clear_pending_event(struct ad_sigma_delta *sigma_delta /* * Read R̅D̅Y̅ pin (if possible) or status register to check if there is an - * old event. + * old event. For devices with neither an RDY GPIO nor registers, + * ad_sd_read_reg() transmits no address byte and clocks raw MISO bytes, + * which is indistinguishable from reading conversion data and would + * partially consume a pending result. Skip the check for such devices. + * + * This is safe for all current registerless devices: ad7191 and ad7780 + * (with powerdown GPIO) are reset between conversions by CS deassertion, + * so there is no stale result to drain; ad7780 (without powerdown GPIO) + * and max11205 are continuously-converting and cycle ~DRDY at the output + * data rate regardless of whether the previous result was read, so the + * next falling edge fires naturally. + * + * A future registerless device that holds ~DRDY asserted until data is + * read would be broken by this early return and would need either + * num_resetclks set or a rdy-gpio. */ if (sigma_delta->rdy_gpiod) { pending_event = gpiod_get_value(sigma_delta->rdy_gpiod); - } else { + } else if (sigma_delta->info->has_registers) { unsigned int status_reg; ret = ad_sd_read_reg(sigma_delta, AD_SD_REG_STATUS, 1, &status_reg); @@ -274,11 +288,24 @@ static int ad_sigma_delta_clear_pending_event(struct ad_sigma_delta *sigma_delta return ret; pending_event = !(status_reg & AD_SD_REG_STATUS_RDY); + } else { + return 0; } if (!pending_event) return 0; + /* + * With num_resetclks = 0, data_read_len is 0 and the drain sequence + * below would compute memset(data + 2, 0xff, 0 - 1), underflowing to + * SIZE_MAX and corrupting the heap. There is no safe way to drain the + * stale result without knowing the data register size; it will be + * consumed by the first ad_sd_read_reg() call in + * ad_sigma_delta_single_conversion(). + */ + if (!data_read_len) + return 0; + /* * In general the size of the data register is unknown. It varies from * device to device, might be one byte longer if CONTROL.DATA_STATUS is From f1de829ee87a1198d3465493ce430d36c5fa029c Mon Sep 17 00:00:00 2001 From: Radu Sabau Date: Wed, 27 May 2026 12:38:38 +0300 Subject: [PATCH 0189/2758] iio: adc: ad_sigma_delta: fix CS held asserted and state leaks commit c72da0688575e5ef39c36bb44fed53aa18f8ae65 upstream. In ad_sigma_delta_single_conversion(), set_mode(AD_SD_MODE_IDLE) and disable_one() were called from the out: block while keep_cs_asserted was still true. This caused any SPI transfer issued by those callbacks to carry cs_change=1, leaving CS permanently asserted after the conversion. Fix by moving both calls into the out_unlock: block, after keep_cs_asserted is cleared, matching the pattern already used in ad_sd_calibrate(). In the error path of ad_sd_buffer_postenable(), if an operation fails after set_mode(AD_SD_MODE_CONTINUOUS) has already succeeded (e.g. spi_offload_trigger_enable()), the device is left in continuous conversion mode with CS physically asserted. Additionally, bus_locked remaining true after spi_bus_unlock() causes subsequent SPI operations to call spi_sync_locked() without the bus lock actually held, allowing concurrent SPI access. Fix the error path by clearing keep_cs_asserted first, then calling set_mode(AD_SD_MODE_IDLE) to revert the device mode and deassert CS, then clearing bus_locked before releasing the bus. For devices that implement neither set_mode nor disable_one (such as MAX11205, which has no physical CS pin), no SPI transfer is issued during cleanup and the cs_change flag has no effect on any physical line. Fixes: 132d44dc6966 ("iio: adc: ad_sigma_delta: Check for previous ready signals") Signed-off-by: Radu Sabau Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/ad_sigma_delta.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index eabf89e140285..1b410291da537 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -468,11 +468,10 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, out: ad_sd_disable_irq(sigma_delta); - ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); - ad_sigma_delta_disable_one(sigma_delta, chan->address); - out_unlock: sigma_delta->keep_cs_asserted = false; + ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); + ad_sigma_delta_disable_one(sigma_delta, chan->address); sigma_delta->bus_locked = false; spi_bus_unlock(sigma_delta->spi->controller); out_release: @@ -605,6 +604,9 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) return 0; err_unlock: + sigma_delta->keep_cs_asserted = false; + ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); + sigma_delta->bus_locked = false; spi_bus_unlock(sigma_delta->spi->controller); spi_unoptimize_message(&sigma_delta->sample_msg); From 2f18c5551aa97ca7f39dbb151c67c9053ccadc17 Mon Sep 17 00:00:00 2001 From: Maxwell Doose Date: Fri, 12 Jun 2026 19:58:10 -0500 Subject: [PATCH 0190/2758] iio: adc: lpc32xx: Initialize completion before requesting IRQ commit e561b35633f450ee607e87a6401d97f156a0cd54 upstream. In the report from Jaeyoung Chung: "lpc32xx_adc_probe() in drivers/iio/adc/lpc32xx_adc.c registers its interrupt handler with devm_request_irq() before it initializes st->completion with init_completion(). If an interrupt arrives after devm_request_irq() and before init_completion(), the handler calls complete() on an uninitialized completion, causing a kernel panic. The probe path, in lpc32xx_adc_probe(): iodev = devm_iio_device_alloc(&pdev->dev, sizeof(*st)); /* st kzalloc-zeroed */ ... retval = devm_request_irq(&pdev->dev, irq, lpc32xx_adc_isr, 0, LPC32XXAD_NAME, st); /* register handler */ ... init_completion(&st->completion); /* initialize completion */ lpc32xx_adc_isr() calls complete(): complete(&st->completion); If the device raises an interrupt before init_completion() runs, complete() acquires the uninitialized wait.lock and walks the zeroed task_list in swake_up_locked(). The zeroed task_list makes list_empty() return false, so swake_up_locked() dereferences a NULL list entry, triggering a KASAN wild-memory-access." Fix the chance of a spurious IRQ causing an uninitialized pointer dereference by moving init_completion() above devm_request_irq(). Fixes: 7901b2a1453e ("staging:iio:adc:lpc32xx rename local state structure to _state") Reported-by: Sangyun Kim Reported-by: Kyungwook Boo Reported-by: Jaeyoung Chung Closes: https://lore.kernel.org/linux-iio/20260610115700.774689-1-jjy600901@snu.ac.kr/ Signed-off-by: Maxwell Doose Reviewed-by: Vladimir Zapolskiy Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/lpc32xx_adc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/lpc32xx_adc.c b/drivers/iio/adc/lpc32xx_adc.c index 43a7bc8158b57..db3a602327ffe 100644 --- a/drivers/iio/adc/lpc32xx_adc.c +++ b/drivers/iio/adc/lpc32xx_adc.c @@ -179,6 +179,8 @@ static int lpc32xx_adc_probe(struct platform_device *pdev) if (irq < 0) return irq; + init_completion(&st->completion); + retval = devm_request_irq(&pdev->dev, irq, lpc32xx_adc_isr, 0, LPC32XXAD_NAME, st); if (retval < 0) { @@ -197,8 +199,6 @@ static int lpc32xx_adc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, iodev); - init_completion(&st->completion); - iodev->name = LPC32XXAD_NAME; iodev->info = &lpc32xx_adc_iio_info; iodev->modes = INDIO_DIRECT_MODE; From af885d419b4d7491a7f674c976832493fd624aa6 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 19 May 2026 23:56:06 +0200 Subject: [PATCH 0191/2758] iio: adc: nxp-sar-adc: Fix the delay calculation in nxp_sar_adc_wait_for() commit a9f41809bf1bd8e5c1bc4b6a1052adac58eb7ab6 upstream. The original code was using ndelay() twice. In one case the delay is calculated as 1/3 of ADC clock and in the other as 80 ADC clocks. But according to the comments in all cases it should be a multiplier of the ADC clock, and not a fraction of it. Inadvertently nxp_sar_adc_wait_for() takes the wrong case and spread it over the code make it wrong in all places. Fix this by modifying a helper to correctly use the multiplier. Fixes: 7e5c0f97c66a ("iio: adc: nxp-sar-adc: Avoid division by zero") Fixes: 4434072a893e ("iio: adc: Add the NXP SAR ADC support for the s32g2/3 platforms") Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260416090122.758990-1-andriy.shevchenko%40linux.intel.com Signed-off-by: Andy Shevchenko Reviewed-by: Stepan Ionichev Acked-by: Daniel Lezcano Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/nxp-sar-adc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/nxp-sar-adc.c b/drivers/iio/adc/nxp-sar-adc.c index 8f4ed3db94f07..753f6a0a6e7bb 100644 --- a/drivers/iio/adc/nxp-sar-adc.c +++ b/drivers/iio/adc/nxp-sar-adc.c @@ -198,13 +198,13 @@ static void nxp_sar_adc_irq_cfg(struct nxp_sar_adc *info, bool enable) writel(0, NXP_SAR_ADC_IMR(info->regs)); } -static void nxp_sar_adc_wait_for(struct nxp_sar_adc *info, unsigned int cycles) +static void nxp_sar_adc_wait_for(struct nxp_sar_adc *info, u64 cycles) { u64 rate; rate = clk_get_rate(info->clk); if (rate) - ndelay(div64_u64(NSEC_PER_SEC, rate * cycles)); + ndelay(div64_u64(NSEC_PER_SEC * cycles, rate)); } static bool nxp_sar_adc_set_enabled(struct nxp_sar_adc *info, bool enable) From eb5b07c9d0ec1a9d4b6871b14793c19967d79dc4 Mon Sep 17 00:00:00 2001 From: Maxwell Doose Date: Fri, 12 Jun 2026 19:58:11 -0500 Subject: [PATCH 0192/2758] iio: adc: spear: Initialize completion before requesting IRQ commit 3ee2128b6f0eb0be7b6cb8f6e0f1f113a65201a0 upstream. In the report from Jaeyoung Chung: "spear_adc_probe() in drivers/iio/adc/spear_adc.c registers its interrupt handler with devm_request_irq() before it initializes st->completion with init_completion(). If an interrupt arrives after devm_request_irq() and before init_completion(), the handler calls complete() on an uninitialized completion, causing a kernel panic. The probe path, in spear_adc_probe(): iodev = devm_iio_device_alloc(&pdev->dev, sizeof(*st)); /* st kzalloc-zeroed */ ... retval = devm_request_irq(&pdev->dev, irq, spear_adc_isr, 0, LPC32XXAD_NAME, st); /* register handler */ ... init_completion(&st->completion); /* initialize completion */ spear_adc_isr() calls complete(): complete(&st->completion); If the device raises an interrupt before init_completion() runs, complete() acquires the uninitialized wait.lock and walks the zeroed task_list in swake_up_locked(). The zeroed task_list makes list_empty() return false, so swake_up_locked() dereferences a NULL list entry, triggering a KASAN wild-memory-access." Fix the chance of a spurious IRQ causing an uninitialized pointer dereference by moving init_completion() above devm_request_irq(). Fixes: b586e5d9eee0 ("staging:iio:adc:spear rename device specific state structure to _state") Reported-by: Sangyun Kim Reported-by: Kyungwook Boo Reported-by: Jaeyoung Chung Closes: https://lore.kernel.org/linux-iio/20260610115700.774689-1-jjy600901@snu.ac.kr/ Signed-off-by: Maxwell Doose Reviewed-by: Vladimir Zapolskiy Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/spear_adc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iio/adc/spear_adc.c b/drivers/iio/adc/spear_adc.c index 91995489bb1c6..d47278f1c4fc3 100644 --- a/drivers/iio/adc/spear_adc.c +++ b/drivers/iio/adc/spear_adc.c @@ -280,6 +280,7 @@ static int spear_adc_probe(struct platform_device *pdev) st = iio_priv(indio_dev); st->dev = dev; + init_completion(&st->completion); mutex_init(&st->lock); /* @@ -326,8 +327,6 @@ static int spear_adc_probe(struct platform_device *pdev) spear_adc_configure(st); - init_completion(&st->completion); - indio_dev->name = SPEAR_ADC_MOD_NAME; indio_dev->info = &spear_adc_info; indio_dev->modes = INDIO_DIRECT_MODE; From 6537f08100189d12bec4975000244e6ac4873c28 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 3 Jun 2026 20:16:40 +0800 Subject: [PATCH 0193/2758] iio: adc: ti-ads1119: fix PM reference leak in buffer preenable commit adf4bc07f814da8329278d32600147f5a150938c upstream. ads1119_triggered_buffer_preenable() resumes the device with pm_runtime_resume_and_get() before starting a conversion. If i2c_smbus_write_byte() fails, the function returns the error directly and leaves the runtime PM usage counter elevated. The matching postdisable callback is not called when preenable fails, so the reference is leaked and the device may remain runtime-active indefinitely. Store the I2C transfer result in ret and drop the runtime PM reference on failure before returning the error. Fixes: a9306887eba41 ("iio: adc: ti-ads1119: Add driver") Signed-off-by: Guangshuo Li Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/ti-ads1119.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/ti-ads1119.c b/drivers/iio/adc/ti-ads1119.c index 2320be0efb508..82040f9395b07 100644 --- a/drivers/iio/adc/ti-ads1119.c +++ b/drivers/iio/adc/ti-ads1119.c @@ -459,7 +459,11 @@ static int ads1119_triggered_buffer_preenable(struct iio_dev *indio_dev) if (ret) return ret; - return i2c_smbus_write_byte(st->client, ADS1119_CMD_START_SYNC); + ret = i2c_smbus_write_byte(st->client, ADS1119_CMD_START_SYNC); + if (ret) + pm_runtime_put_autosuspend(dev); + + return ret; } static int ads1119_triggered_buffer_postdisable(struct iio_dev *indio_dev) From 84552fdcef8f83850306c8ed63c2bc19ffb105ec Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 25 Jun 2026 13:44:07 +0800 Subject: [PATCH 0194/2758] iio: adc: ti-ads124s08: Return reset GPIO lookup errors commit 7dc4de2aa6316f1d044cde21f5acfec5f3ec6b47 upstream. devm_gpiod_get_optional() returns NULL when the optional GPIO is absent, but returns an ERR_PTR when the GPIO provider lookup fails, including probe deferral. Probe currently logs the ERR_PTR case as if the reset GPIO were simply absent and keeps the error pointer in reset_gpio. Later ads124s_reset() treats any non-NULL reset_gpio as a valid descriptor and passes it to gpiod_set_value_cansleep(). Return the lookup error instead of retaining the ERR_PTR. Fixes: e717f8c6dfec ("iio: adc: Add the TI ads124s08 ADC code") Cc: stable@vger.kernel.org Reviewed-by: Joshua Crofts Signed-off-by: Pengpeng Hou Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/ti-ads124s08.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/ti-ads124s08.c b/drivers/iio/adc/ti-ads124s08.c index 8ea1269f74db0..57eed8554bd92 100644 --- a/drivers/iio/adc/ti-ads124s08.c +++ b/drivers/iio/adc/ti-ads124s08.c @@ -321,7 +321,8 @@ static int ads124s_probe(struct spi_device *spi) ads124s_priv->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ads124s_priv->reset_gpio)) - dev_info(&spi->dev, "Reset GPIO not defined\n"); + return dev_err_probe(&spi->dev, PTR_ERR(ads124s_priv->reset_gpio), + "Failed to get reset GPIO\n"); ads124s_priv->chip_info = &ads124s_chip_info_tbl[spi_id->driver_data]; From 33b29764f6c4e1dbe720978b7299b95dd12560d0 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 25 May 2026 10:16:11 +0300 Subject: [PATCH 0195/2758] iio: backend: fix uninitialized data in debugfs commit a6e8b14a4897d0b6df9744f33d0a30e6b92368eb upstream. If the *ppos value is non-zero then simple_write_to_buffer() will not initialize the start of the buf[] buffer. Non-zero ppos values aren't going to work at all. Check for that at the start of the function and return -ENOSPC. Fixes: cdf01e0809a4 ("iio: backend: add debugFs interface") Signed-off-by: Dan Carpenter Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/industrialio-backend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c index 10e689f494419..cd697fbeae47b 100644 --- a/drivers/iio/industrialio-backend.c +++ b/drivers/iio/industrialio-backend.c @@ -156,7 +156,7 @@ static ssize_t iio_backend_debugfs_write_reg(struct file *file, ssize_t rc; int ret; - if (count >= sizeof(buf)) + if (*ppos != 0 || count >= sizeof(buf)) return -ENOSPC; rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, userbuf, count); From fb8e18f8ca724bd4de4643cad5b7c7230b9a5a71 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Mon, 27 Apr 2026 19:11:39 +0800 Subject: [PATCH 0196/2758] iio: buffer: hw-consumer: free scan_mask on buffer release MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 6325d6e2204327965b849c0a16efb6ac9202e5a8 upstream. The scan_mask lifetime changed in commit 9a2e1233d38c ("iio: buffer: hw-consumer: remove redundant scan_mask flexible array"). Before that change, the scan mask storage was embedded in struct hw_consumer_buffer, so iio_hw_buf_release() could free the whole allocation with a single kfree(hw_buf). That commit moved the scan mask to a separate bitmap_zalloc() allocation stored in buffer.scan_mask, but left iio_hw_buf_release() unchanged. Free the scan mask in iio_hw_buf_release() before freeing the buffer wrapper. Fixes: 9a2e1233d38c ("iio: buffer: hw-consumer: remove redundant scan_mask flexible array") Signed-off-by: Felix Gu Reviewed-by: Nuno Sá Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/buffer/industrialio-hw-consumer.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iio/buffer/industrialio-hw-consumer.c b/drivers/iio/buffer/industrialio-hw-consumer.c index 700528c9a0a46..10e912bbf0c5f 100644 --- a/drivers/iio/buffer/industrialio-hw-consumer.c +++ b/drivers/iio/buffer/industrialio-hw-consumer.c @@ -40,6 +40,8 @@ static void iio_hw_buf_release(struct iio_buffer *buffer) { struct hw_consumer_buffer *hw_buf = iio_buffer_to_hw_consumer_buffer(buffer); + + bitmap_free(buffer->scan_mask); kfree(hw_buf); } From d49ff54b2784aa56a7c97982de713604de89d23a Mon Sep 17 00:00:00 2001 From: Maxwell Doose Date: Tue, 26 May 2026 17:55:24 -0500 Subject: [PATCH 0197/2758] iio: chemical: scd30: Cleanup initializations and fix sign-extension bug commit 60d877910a43c305b5165131b258a17b1d772d57 upstream. Include linux/bitfield.h for FIELD_GET(). Create new macros for bit manipulation in combination with manual bit manipulation being replaced with FIELD_GET(). The current variable declaration and initializations are barely readable and use comma separations across multiple lines. Refactor the initializations so that mantissa and exp have separate declarations and sign gets initialized later. In addition (and due to the nature of the cleanup), fix a sign-extension bug where, float32 would get bitwise anded with ~BIT(31) (which is 0xFFFFFFFF7FFFFFFF) which corrupted the exponent. Fixes: 64b3d8b1b0f5c ("iio: chemical: scd30: add core driver") Reported-by: sashiko Closes: https://sashiko.dev/#/patchset/20260524020309.18618-1-m32285159%40gmail.com Signed-off-by: Maxwell Doose Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/chemical/scd30_core.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c index 11d6bc1b63e67..d02f2de42327d 100644 --- a/drivers/iio/chemical/scd30_core.c +++ b/drivers/iio/chemical/scd30_core.c @@ -4,6 +4,8 @@ * * Copyright (c) 2020 Tomasz Duszynski */ + +#include #include #include #include @@ -43,6 +45,11 @@ #define SCD30_TEMP_OFFSET_MAX 655360 #define SCD30_EXTRA_TIMEOUT_PER_S 250 +/* Floating point arithmetic macros */ +#define SCD30_FLOAT_MANTISSA_MSK GENMASK(22, 0) +#define SCD30_FLOAT_EXP_MSK GENMASK(30, 23) +#define SCD30_FLOAT_SIGN_MSK BIT(31) + enum { SCD30_CONC, SCD30_TEMP, @@ -89,10 +96,14 @@ static int scd30_reset(struct scd30_state *state) /* simplified float to fixed point conversion with a scaling factor of 0.01 */ static int scd30_float_to_fp(int float32) { - int fraction, shift, - mantissa = float32 & GENMASK(22, 0), - sign = (float32 & BIT(31)) ? -1 : 1, - exp = (float32 & ~BIT(31)) >> 23; + int fraction, shift, sign; + int mantissa = FIELD_GET(SCD30_FLOAT_MANTISSA_MSK, float32); + int exp = FIELD_GET(SCD30_FLOAT_EXP_MSK, float32); + + if (float32 & SCD30_FLOAT_SIGN_MSK) + sign = -1; + else + sign = 1; /* special case 0 */ if (!exp && !mantissa) From 20a5fee40c3d5808b1dc200cbb9c7d8a2eb9c95a Mon Sep 17 00:00:00 2001 From: Herman van Hazendonk Date: Tue, 16 Jun 2026 15:02:04 +0200 Subject: [PATCH 0198/2758] iio: common: st_sensors: honour channel endianness in read_axis_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 55052184ac9011db2ea983e54d6c21f0b1079a12 upstream. st_sensors_read_axis_data() unconditionally decoded multi-byte results with get_unaligned_le16() / get_unaligned_le24() regardless of the channel's declared scan_type.endianness. For every ST sensor that has used this helper since it was introduced this happened to be fine because the ST IMU/accel/gyro/pressure families publish their data registers as little-endian and the channel specs in those drivers declare IIO_LE accordingly. The LSM303DLH magnetometer however publishes its X/Y/Z output as a pair of big-endian bytes (the H register sits at the lower address, 0x03/0x05/0x07, and the L register immediately after), and its channel specs in st_magn_core.c correctly declare IIO_BE -- but read_axis_data() ignored that and decoded as little-endian, swapping the high and low bytes of every magnetometer sample. The LSM303DLHC and LSM303DLM share the same st_magn_16bit_channels (IIO_BE) and were therefore byte-swapped by the same bug; users of those parts will see different in_magn_*_raw values after this fix lands. The bug is most visible on a stationary chip: in earth's field the true X reading is small and the high byte sits at 0x00, so swapping the bytes pins sysfs X at exactly the low byte's pattern (e.g. 0x00F0 = 240). Y and Z still appear "to vary" because their magnitudes are larger and the noise in the low byte produces big swings in the swapped high byte: before (LSM303DLH flat, sysfs in_magn_*_raw): X=240 (stuck), Y= 12032..23296, Z=-16128..-9728 after (direct i2c-dev big-endian decode, same chip same orientation): X≈-4096, Y≈210, Z≈80 (sensible values reflecting earth's ambient field at low gauss range) Fix read_axis_data() to dispatch on ch->scan_type.endianness and call get_unaligned_be16() / get_unaligned_be24() when the channel declares IIO_BE. Existing IIO_LE consumers (st_accel, st_gyro, st_pressure, st_lsm6dsx and others) are unaffected because their channel specs already declare IIO_LE and the LE path is unchanged. While restructuring the branches, replace the previously implicit silent-success-with-uninitialised-*data fall-through for byte_for_channel outside 1..3 with an explicit return -EINVAL. No in-tree ST sensor publishes such a channel, but the new behaviour is strictly safer than handing userspace garbage. Fixes: 23491b513bcd ("iio:common: Add STMicroelectronics common library") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 sparse smatch clang-analyzer coccinelle checkpatch Assisted-by: Sashiko:claude-opus-4-7 Signed-off-by: Herman van Hazendonk Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- .../iio/common/st_sensors/st_sensors_core.c | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index dbc5e16fbde43..76f91696f66a3 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -498,6 +498,7 @@ static int st_sensors_read_axis_data(struct iio_dev *indio_dev, u8 *outdata; struct st_sensor_data *sdata = iio_priv(indio_dev); unsigned int byte_for_channel; + u32 tmp; byte_for_channel = DIV_ROUND_UP(ch->scan_type.realbits + ch->scan_type.shift, 8); @@ -508,12 +509,22 @@ static int st_sensors_read_axis_data(struct iio_dev *indio_dev, if (err < 0) return err; - if (byte_for_channel == 1) - *data = (s8)*outdata; - else if (byte_for_channel == 2) - *data = (s16)get_unaligned_le16(outdata); - else if (byte_for_channel == 3) - *data = (s32)sign_extend32(get_unaligned_le24(outdata), 23); + if (byte_for_channel == 1) { + tmp = *outdata; + } else if (byte_for_channel == 2) { + if (ch->scan_type.endianness == IIO_BE) + tmp = get_unaligned_be16(outdata); + else + tmp = get_unaligned_le16(outdata); + } else if (byte_for_channel == 3) { + if (ch->scan_type.endianness == IIO_BE) + tmp = get_unaligned_be24(outdata); + else + tmp = get_unaligned_le24(outdata); + } else { + return -EINVAL; + } + *data = sign_extend32(tmp, BYTES_TO_BITS(byte_for_channel) - 1); return 0; } From 89fbd3e32dffb6227f936a9578e6eb4632aa4580 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 25 May 2026 10:16:27 +0300 Subject: [PATCH 0199/2758] iio: core: fix uninitialized data in debugfs commit ab92ed206d41fd171ebd37bc46360d9f2140d043 upstream. If *ppos is non-zero then simple_write_to_buffer() will not initialize the start of buf[]. Non zero values for *ppos aren't going to work anyway. Test for them at the start of the function and return -EINVAL. Fixes: 6d5dd486c715 ("iio: core: make use of simple_write_to_buffer()") Signed-off-by: Dan Carpenter Reviewed-by: Maxwell Doose Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/industrialio-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 22eefd048ba92..15b56f8972fc9 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -418,7 +418,7 @@ static ssize_t iio_debugfs_write_reg(struct file *file, char buf[80]; int ret; - if (count >= sizeof(buf)) + if (*ppos != 0 || count >= sizeof(buf)) return -EINVAL; ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, userbuf, From b03fb2f8c6fca40caed23b9d3211e134c21d56f2 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 25 May 2026 10:15:46 +0300 Subject: [PATCH 0200/2758] iio: dac: ad3552r-hs: fix uninitialized data ni ad3552r_hs_write_data_source() commit eaaa7eef181892ef7ba56c6295b81f0ae4492c13 upstream. If the *ppos value is non-zero then the simple_write_to_buffer() function won't initialized the start of the buf[] buffer. Non-zero values for *ppos won't work here generally, so just test for them and return -ENOSPC at the start of the function. Fixes: b1c5d68ea66e ("iio: dac: ad3552r-hs: add support for internal ramp") Signed-off-by: Dan Carpenter Reviewed-by: Angelo Dureghello Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/dac/ad3552r-hs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/dac/ad3552r-hs.c b/drivers/iio/dac/ad3552r-hs.c index a9578afa7015d..6bc64f53bce91 100644 --- a/drivers/iio/dac/ad3552r-hs.c +++ b/drivers/iio/dac/ad3552r-hs.c @@ -549,7 +549,7 @@ static ssize_t ad3552r_hs_write_data_source(struct file *f, guard(mutex)(&st->lock); - if (count >= sizeof(buf)) + if (*ppos != 0 || count >= sizeof(buf)) return -ENOSPC; ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, userbuf, From f187dc5a4c4846ffa07d9bda6e760837ed005574 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 6 Jul 2026 21:48:26 -0700 Subject: [PATCH 0201/2758] iio: event: Fix event FIFO reset race MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit af791d295737ea6b6ff2c8d8488462a49c14af01 upstream. `iio_event_getfd()` creates the event file descriptor with `anon_inode_getfd()`, which allocates a new fd, creates the anonymous file and installs it in the process fd table before returning to the caller. The IIO code resets the event FIFO after `anon_inode_getfd()` has returned, but before `IIO_GET_EVENT_FD_IOCTL` has copied the fd number to userspace. But since fd tables are shared between threads, another thread can guess the newly allocated fd number and issue a `read()` on it as soon as the fd has been installed. This means the `kfifo_to_user()` in `iio_event_chrdev_read()` can run in parallel with the `kfifo_reset_out()` in `iio_event_getfd()`. The kfifo documentation says that `kfifo_reset_out()` is only safe when it is called from the reader thread and there is only one concurrent reader. Otherwise it is dangerous and must be handled in the same way as `kfifo_reset()`. If that happens, `kfifo_to_user()` can advance the FIFO `out` index based on state from before the reset, after the reset has already moved the `out` index to the current `in` index. That can leave the FIFO with an `out` index past the `in` index. A later `read()` can then see an underflowed FIFO length and copy more data than the event FIFO buffer contains. This can result in an out-of-bounds read and leak adjacent kernel memory to userspace. Move the FIFO reset before `anon_inode_getfd()`. At that point the event fd is marked busy, but the new fd has not been installed yet, so userspace cannot access it while the FIFO is reset. Fixes: b91accafbb10 ("iio:event: Fix and cleanup locking") Reported-by: Codex:gpt-5.5 Signed-off-by: Lars-Peter Clausen Reviewed-by: Nuno Sá Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/industrialio-event.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index a0d6fcf2a9c9b..e6730f52262a4 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -207,6 +207,8 @@ static int iio_event_getfd(struct iio_dev *indio_dev) goto unlock; } + kfifo_reset_out(&ev_int->det_events); + iio_device_get(indio_dev); fd = anon_inode_getfd("iio:event", &iio_event_chrdev_fileops, @@ -214,10 +216,7 @@ static int iio_event_getfd(struct iio_dev *indio_dev) if (fd < 0) { clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags); iio_device_put(indio_dev); - } else { - kfifo_reset_out(&ev_int->det_events); } - unlock: mutex_unlock(&iio_dev_opaque->mlock); return fd; From 6c8675468862161d1c59130266852b66867d3861 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Sun, 10 May 2026 07:35:00 +0500 Subject: [PATCH 0202/2758] iio: gyro: bmg160: bail out when bandwidth/filter is not in table commit 8320c77e67382d5d55d77043a5f60a867d408a2b upstream. bmg160_get_filter() walks bmg160_samp_freq_table[] looking for the entry matching the bw_bits value read from the chip: for (i = 0; i < ARRAY_SIZE(bmg160_samp_freq_table); ++i) { if (bmg160_samp_freq_table[i].bw_bits == bw_bits) break; } *val = bmg160_samp_freq_table[i].filter; If no entry matches, i ends up equal to the array size and the next line reads one slot past the end. bmg160_set_filter() has the same shape, driven by 'val' instead of bw_bits. smatch flags both: drivers/iio/gyro/bmg160_core.c:204 bmg160_get_filter() error: buffer overflow 'bmg160_samp_freq_table' 7 <= 7 drivers/iio/gyro/bmg160_core.c:222 bmg160_set_filter() error: buffer overflow 'bmg160_samp_freq_table' 7 <= 7 Return -EINVAL when no entry matches. The set_filter() path is reachable from userspace via the sysfs in_anglvel_filter_low_pass_3db_frequency interface, so userspace can trivially trigger the out-of-bounds read with a value that is not in bmg160_samp_freq_table[].filter. Fixes: 22b46c45fb9b ("iio:gyro:bmg160 Gyro Sensor driver") Signed-off-by: Stepan Ionichev Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/gyro/bmg160_core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/iio/gyro/bmg160_core.c b/drivers/iio/gyro/bmg160_core.c index 38394b5f32755..58963f3ea186f 100644 --- a/drivers/iio/gyro/bmg160_core.c +++ b/drivers/iio/gyro/bmg160_core.c @@ -201,6 +201,9 @@ static int bmg160_get_filter(struct bmg160_data *data, int *val) break; } + if (i == ARRAY_SIZE(bmg160_samp_freq_table)) + return -EINVAL; + *val = bmg160_samp_freq_table[i].filter; return ret ? ret : IIO_VAL_INT; @@ -218,6 +221,9 @@ static int bmg160_set_filter(struct bmg160_data *data, int val) break; } + if (i == ARRAY_SIZE(bmg160_samp_freq_table)) + return -EINVAL; + ret = regmap_write(data->regmap, BMG160_REG_PMU_BW, bmg160_samp_freq_table[i].bw_bits); if (ret < 0) { From a11bc637375e3bfd9dc8f2ba3e22c02dcf8993b4 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Mon, 11 May 2026 11:40:20 +0500 Subject: [PATCH 0203/2758] iio: gyro: bmg160: wait full startup time after mode change at probe commit 088fcb9b567f8723074ad9eb1bf5cb46f8a0096b upstream. bmg160_chip_init() calls bmg160_set_mode(BMG160_MODE_NORMAL) and then waits only 500-1000 us. Per the BMG160 datasheet (BST-BMG160-DS000-07 Rev. 1.0, May 2013), the start-up and wake-up times (tsu, twusm) are 30 ms. The same file already waits BMG160_MAX_STARTUP_TIME_MS (80 ms) in bmg160_runtime_resume() after the same set_mode(NORMAL) operation. The 500 us value at probe was likely a unit mix-up; the old comment said "500 ms" while the code used microseconds. Reuse the same constant via msleep() and add a code comment explaining the datasheet basis for the wait. Without this, register writes that follow the mode change can hit the chip before it is ready. Fixes: 22b46c45fb9b ("iio:gyro:bmg160 Gyro Sensor driver") Signed-off-by: Stepan Ionichev Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/gyro/bmg160_core.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/iio/gyro/bmg160_core.c b/drivers/iio/gyro/bmg160_core.c index 58963f3ea186f..d611341a0e2ad 100644 --- a/drivers/iio/gyro/bmg160_core.c +++ b/drivers/iio/gyro/bmg160_core.c @@ -264,8 +264,14 @@ static int bmg160_chip_init(struct bmg160_data *data) if (ret < 0) return ret; - /* Wait upto 500 ms to be ready after changing mode */ - usleep_range(500, 1000); + /* + * Wait for the chip to be ready after switching to normal mode. + * The BMG160 datasheet (BST-BMG160-DS000-07 Rev. 1.0, May 2013) + * specifies a start-up / wake-up time (tsu, twusm) of 30 ms; use + * BMG160_MAX_STARTUP_TIME_MS (80 ms) as a safety margin, matching + * what bmg160_runtime_resume() already does. + */ + msleep(BMG160_MAX_STARTUP_TIME_MS); /* Set Bandwidth */ ret = bmg160_set_bw(data, BMG160_DEF_BW); From 9f5690f2dc549cfa96bcde4d30ca194a1122671b Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Thu, 4 Jun 2026 09:42:46 +0800 Subject: [PATCH 0204/2758] iio: imu: adis: add IRQF_NO_THREAD to non-FIFO trigger IRQ commit 6e1b9bff1202da55c464e36bd34a2b6863d7fe30 upstream. devm_adis_probe_trigger() registers iio_trigger_generic_data_rdy_poll() through devm_request_irq() on the non-FIFO path, but it does not add IRQF_NO_THREAD to the IRQ flags. When the kernel is booted with forced IRQ threading, the parent IRQ can otherwise be threaded by the IRQ core and the subsequent IIO trigger child IRQ is then dispatched from irq/... thread context instead of hardirq context. Because iio_trigger_generic_data_rdy_poll() immediately drives iio_trigger_poll(), this violates the hardirq-only IIO trigger helper contract and can push downstream trigger consumers through the wrong execution context. Add IRQF_NO_THREAD on top of the existing adis->irq_flag value for the non-FIFO request_irq() path, while preserving the current trigger polarity and IRQF_NO_AUTOEN behavior. Fixes: fec86c6b8369 ("iio: imu: adis: Add Managed device functions") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/imu/adis_trigger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/imu/adis_trigger.c b/drivers/iio/imu/adis_trigger.c index d76e13cbac680..3e6a7af6ab017 100644 --- a/drivers/iio/imu/adis_trigger.c +++ b/drivers/iio/imu/adis_trigger.c @@ -94,7 +94,7 @@ int devm_adis_probe_trigger(struct adis *adis, struct iio_dev *indio_dev) else ret = devm_request_irq(&adis->spi->dev, adis->spi->irq, &iio_trigger_generic_data_rdy_poll, - adis->irq_flag, + adis->irq_flag | IRQF_NO_THREAD, indio_dev->name, adis->trig); if (ret) From 65f1f81e7521ddaadc00ddeef4c053f861b882d5 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Thu, 4 Jun 2026 09:42:47 +0800 Subject: [PATCH 0205/2758] iio: imu: bmi160: add IRQF_NO_THREAD to data-ready trigger IRQ commit cd5a6a5096b246e10600da3ac47a1274ce9573c8 upstream. bmi160_probe_trigger() registers iio_trigger_generic_data_rdy_poll() through devm_request_irq(), but it passes only irq_type and does not add IRQF_NO_THREAD. When the kernel is booted with forced IRQ threading, the parent IRQ can otherwise be threaded by the IRQ core and the subsequent IIO trigger child IRQ is dispatched from irq/... thread context instead of hardirq context. Because the handler immediately pushes the event into iio_trigger_poll(), this violates the hardirq-only IIO trigger helper contract and can drive downstream trigger consumers through the wrong execution context. Add IRQF_NO_THREAD on top of irq_type when registering the BMI160 data- ready trigger handler. Fixes: 895bf81e6bbf ("iio:bmi160: add drdy interrupt support") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/imu/bmi160/bmi160_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c index 4abb83b75e2e8..86f6ecfd64aa2 100644 --- a/drivers/iio/imu/bmi160/bmi160_core.c +++ b/drivers/iio/imu/bmi160/bmi160_core.c @@ -788,7 +788,8 @@ int bmi160_probe_trigger(struct iio_dev *indio_dev, int irq, u32 irq_type) ret = devm_request_irq(&indio_dev->dev, irq, &iio_trigger_generic_data_rdy_poll, - irq_type, "bmi160", data->trig); + irq_type | IRQF_NO_THREAD, + "bmi160", data->trig); if (ret) return ret; From 9fdc477b652ae03e2b787345cbd4afc401e59cfa Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Tue, 23 Jun 2026 16:22:15 +0200 Subject: [PATCH 0206/2758] iio: imu: inv_icm42600: fix timestamp clock period by using lower value commit 8b0b864c11a2e2ada470f9d5010e1c2bf1eceef2 upstream. Clock period value is used for computing periods of sampling. There is no need for it to be higher than the maximum odr, otherwise we are losing precision in the computation for nothing. Switch clock period value to maximum odr period (8kHz). Fixes: 0ecc363ccea7 ("iio: make invensense timestamp module generic") Cc: stable@vger.kernel.org Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c | 4 ++-- drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c index 532d5fdffaf8d..7df920ef3cf08 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c @@ -1170,10 +1170,10 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st) accel_st->filter = INV_ICM42600_FILTER_AVG_16X; /* - * clock period is 32kHz (31250ns) + * clock period is 8kHz (125000ns) * jitter is +/- 2% (20 per mille) */ - ts_chip.clock_period = 31250; + ts_chip.clock_period = 125000; ts_chip.jitter = 20; ts_chip.init_period = inv_icm42600_odr_to_period(st->conf.accel.odr); inv_sensors_timestamp_init(&accel_st->ts, &ts_chip); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c index 11339ddf1da36..a18dcac939298 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c @@ -755,10 +755,10 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st) } /* - * clock period is 32kHz (31250ns) + * clock period is 8kHz (125000ns) * jitter is +/- 2% (20 per mille) */ - ts_chip.clock_period = 31250; + ts_chip.clock_period = 125000; ts_chip.jitter = 20; ts_chip.init_period = inv_icm42600_odr_to_period(st->conf.accel.odr); inv_sensors_timestamp_init(&gyro_st->ts, &ts_chip); From 2e2595765dcb4bbdf852435f2597c6bdb67c75dc Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Mon, 29 Jun 2026 21:51:55 +0200 Subject: [PATCH 0207/2758] iio: imu: inv_icm42600: fix timestamping by limiting FIFO reading commit affe3f077d7a4eeb25937f5323ff059a54b4712c upstream. Timestamps are made by measuring the chip clock using the watermark interrupts. If we read more than watermark samples as done today, we are reducing the period between interrupts and distort the time measurement. Fix that by reading only watermark samples in the interrupt case. Fixes: 7f85e42a6c54 ("iio: imu: inv_icm42600: add buffer support in iio devices") Cc: stable@vger.kernel.org Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c | 9 +++++---- drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c index 68a3957580318..5c3840acf0857 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c @@ -248,6 +248,7 @@ int inv_icm42600_buffer_update_watermark(struct inv_icm42600_state *st) /* compute watermark value in bytes */ wm_size = watermark * packet_size; + st->fifo.watermark.value = watermark; /* changing FIFO watermark requires to turn off watermark interrupt */ ret = regmap_update_bits_check(st->map, INV_ICM42600_REG_INT_SOURCE0, @@ -454,11 +455,10 @@ int inv_icm42600_buffer_fifo_read(struct inv_icm42600_state *st, st->fifo.nb.accel = 0; st->fifo.nb.total = 0; - /* compute maximum FIFO read size */ + /* compute maximum FIFO read size (watermark for max = 0 interrupt case) */ if (max == 0) - max_count = sizeof(st->fifo.data); - else - max_count = max * inv_icm42600_get_packet_size(st->fifo.en); + max = st->fifo.watermark.value; + max_count = max * inv_icm42600_get_packet_size(st->fifo.en); /* read FIFO count value */ raw_fifo_count = (__be16 *)st->buffer; @@ -574,6 +574,7 @@ int inv_icm42600_buffer_init(struct inv_icm42600_state *st) st->fifo.watermark.eff_gyro = 1; st->fifo.watermark.eff_accel = 1; + st->fifo.watermark.value = 1; /* * Default FIFO configuration (bits 7 to 5) diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h index ffca4da1e2493..88b8b9f780af3 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h @@ -34,6 +34,7 @@ struct inv_icm42600_fifo { unsigned int accel; unsigned int eff_gyro; unsigned int eff_accel; + unsigned int value; } watermark; size_t count; struct { From 4f49fef6179d021115e7cfd7f288b3ee6682081c Mon Sep 17 00:00:00 2001 From: Andreas Kempe Date: Thu, 2 Jul 2026 10:41:23 +0000 Subject: [PATCH 0208/2758] iio: imu: st_lsm6dsx: deselect shub page before reading whoami commit aede83625ff5d9539508582036df30c809d51058 upstream. As part of driver initialization, e.g. st_lsm6dsx_init_shub() selects the shub register page using st_lsm6dsx_set_page(). Selecting the shub register page shadows the regular register space so whoami, among other registers, is no longer accessible. In applications where the IMU is permanently powered separately from the processor, there is a window where a reset of the CPU leaves the IMU in the shub register page. Once this occurs, any subsequent probe attempt fails because of the register shadowing. Using the ism330dlc, the error typically looks like st_lsm6dsx_i2c 3-006a: unsupported whoami [10] with the unknown whoami read from a reserved register in the shub page. The reset register is also shadowed by the page select, preventing a reset from recovering the chip. Unconditionally clear the shub page before the whoami readout to ensure normal register access and allow the initialization to proceed. Place the fix in st_lsm6dsx_check_whoami() before the whoami check because hw->settings, which st_lsm6dsx_set_page() relies on, is first assigned in that function. Placing the fix in a more logical place than the whoami check would require a bigger restructuring of the code. Fixes: c91c1c844ebd ("iio: imu: st_lsm6dsx: add i2c embedded controller support") Signed-off-by: Andreas Kempe Acked-by: Lorenzo Bianconi Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 21 +++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index 450cb5b47346e..8261d9cf0d940 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -1660,6 +1660,26 @@ static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id, return -ENODEV; } + hw->settings = &st_lsm6dsx_sensor_settings[i]; + + if (hw->settings->shub_settings.page_mux.addr) { + /* + * If the IMU has the shub page selected on init, for example + * after a CPU watchdog reset while the page is selected, the + * regular register space is shadowed. While the regular + * register space is shadowed, the registers needed for + * initializing the IMU are not available. + * + * Unconditionally clear the shub page selection to ensure + * normal register access. + */ + err = st_lsm6dsx_set_page(hw, false); + if (err < 0) { + dev_err(hw->dev, "failed to clear shub page\n"); + return err; + } + } + err = regmap_read(hw->regmap, ST_LSM6DSX_REG_WHOAMI_ADDR, &data); if (err < 0) { dev_err(hw->dev, "failed to read whoami register\n"); @@ -1672,7 +1692,6 @@ static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id, } *name = st_lsm6dsx_sensor_settings[i].id[j].name; - hw->settings = &st_lsm6dsx_sensor_settings[i]; return 0; } From c2d8c2696b8c65619f6a60a2fc24225a169261c2 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Thu, 25 Jun 2026 21:38:08 +0200 Subject: [PATCH 0209/2758] iio: light: al3000a: add missing REGMAP_I2C to Kconfig commit a2d30022b7c316ad845d1b696e724058b88e5a4e upstream. The KConfig entry for the al3000a is missing a `select REGMAP_I2C`, causing build failures. Fixes: d531b9f78949 ("iio: light: Add support for AL3000a illuminance sensor") Signed-off-by: Joshua Crofts Reviewed-by: Andy Shevchenko Reviewed-by: David Heidelberg Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/light/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index eff33e456c70d..b941249a92da0 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -45,6 +45,7 @@ config ADUX1020 config AL3000A tristate "AL3000a ambient light sensor" + select REGMAP_I2C depends on I2C help Say Y here if you want to build a driver for the Dyna Image AL3000a From acd4946b583a79cd6304a77e4ea7588e4a50e2c2 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Thu, 25 Jun 2026 21:38:09 +0200 Subject: [PATCH 0210/2758] iio: light: al3010: add missing REGMAP_I2C to Kconfig commit 84486e3bbda18a2df1ed74ca78e1e14bde9a941b upstream. The KConfig entry for the AL3010 is missing a `select REGMAP_I2C`, causing build failures. Fixes: 0e5e21e23dd6 ("iio: light: al3010: Implement regmap support") Signed-off-by: Joshua Crofts Reviewed-by: Andy Shevchenko Reviewed-by: David Heidelberg Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/light/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index b941249a92da0..25a31a0cd12c7 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -56,6 +56,7 @@ config AL3000A config AL3010 tristate "AL3010 ambient light sensor" + select REGMAP_I2C depends on I2C help Say Y here if you want to build a driver for the Dyna Image AL3010 From 78451f43e3f4d1a6cb21777a48bcff7cf20bb4d8 Mon Sep 17 00:00:00 2001 From: Vidhu Sarwal Date: Sat, 4 Jul 2026 17:22:45 +0530 Subject: [PATCH 0211/2758] iio: light: al3010: fix incorrect scale for the highest gain range commit aa411adc6ce40ad1a55ebc965f255a4cfc0005f8 upstream. al3010_scales[] encodes the highest gain range as {0, 1187200}. For IIO_VAL_INT_PLUS_MICRO, the fractional part must be less than 1000000, so the scale 1.1872 should instead be represented as { 1, 187200 }. Since write_raw() compares the value from userspace against this table, writing the advertised 1.1872 scale never matches the malformed entry and returns -EINVAL. As a result, the highest gain range cannot be selected. Reading the scale in that state also reports the malformed value. Fixes: c36b5195ab70 ("iio: light: add Dyna-Image AL3010 driver") Signed-off-by: Vidhu Sarwal Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/light/al3010.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/light/al3010.c b/drivers/iio/light/al3010.c index 0932fa2b49fa7..11eb68e889cb7 100644 --- a/drivers/iio/light/al3010.c +++ b/drivers/iio/light/al3010.c @@ -42,7 +42,7 @@ enum al3xxxx_range { }; static const int al3010_scales[][2] = { - {0, 1187200}, {0, 296800}, {0, 74200}, {0, 18600} + { 1, 187200 }, { 0, 296800 }, { 0, 74200 }, { 0, 18600 }, }; static const struct regmap_config al3010_regmap_config = { From e297afa1845f25bc348e7c7e97304aef76d72f4d Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Sat, 23 May 2026 13:44:57 +0200 Subject: [PATCH 0212/2758] iio: light: al3010: read both ALS ADC registers again commit ee78fae068f52a5582aaf448d9414f826855c106 upstream. al3010_read_raw() used to read two adjacent registers until the driver was modernized using the regmap framework. That cleanup accidentally replaced the 16-bit word read with a single byte read. I'm reverting latter. Fixes: 0e5e21e23dd6 ("iio: light: al3010: Implement regmap support") Signed-off-by: Alexander A. Klimov Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/light/al3010.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/iio/light/al3010.c b/drivers/iio/light/al3010.c index 11eb68e889cb7..0e72906c637f0 100644 --- a/drivers/iio/light/al3010.c +++ b/drivers/iio/light/al3010.c @@ -111,7 +111,8 @@ static int al3010_read_raw(struct iio_dev *indio_dev, int *val2, long mask) { struct al3010_data *data = iio_priv(indio_dev); - int ret, gain, raw; + int ret, gain; + __le16 raw; switch (mask) { case IIO_CHAN_INFO_RAW: @@ -120,11 +121,12 @@ static int al3010_read_raw(struct iio_dev *indio_dev, * - low byte of output is stored at AL3010_REG_DATA_LOW * - high byte of output is stored at AL3010_REG_DATA_LOW + 1 */ - ret = regmap_read(data->regmap, AL3010_REG_DATA_LOW, &raw); + ret = regmap_bulk_read(data->regmap, AL3010_REG_DATA_LOW, + &raw, sizeof(raw)); if (ret) return ret; - *val = raw; + *val = le16_to_cpu(raw); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: From 2b42c313b9411f2a660c944c6da1030b6aaeedba Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Thu, 25 Jun 2026 21:38:10 +0200 Subject: [PATCH 0213/2758] iio: light: al3320a: add missing REGMAP_I2C to Kconfig commit 9efcc9ba9b2e940cc01e63d132ae741e4c5d09c7 upstream. The Kconfig entry for the al3320a is missing a `select REGMAP_I2C`, causing build failures. Fixes: 1850e6ae7f91 ("iio: light: al3320a: Implement regmap support") Signed-off-by: Joshua Crofts Reviewed-by: Andy Shevchenko Reviewed-by: David Heidelberg Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/light/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 25a31a0cd12c7..a28f308bc14ec 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -67,6 +67,7 @@ config AL3010 config AL3320A tristate "AL3320A ambient light sensor" + select REGMAP_I2C depends on I2C help Say Y here if you want to build a driver for the Dyna Image AL3320A From a82b89a356923e5f0133a3401fd29adc6fc2685e Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Sat, 23 May 2026 13:44:58 +0200 Subject: [PATCH 0214/2758] iio: light: al3320a: read both ALS ADC registers again commit 744bccc2647c6b2206290e8e40890a23812116b3 upstream. al3320a_read_raw() used to read two adjacent registers until the driver was modernized using the regmap framework. That cleanup accidentally replaced the 16-bit word read with a single byte read. I'm reverting latter. Fixes: 1850e6ae7f91 ("iio: light: al3320a: Implement regmap support") Signed-off-by: Alexander A. Klimov Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/light/al3320a.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/iio/light/al3320a.c b/drivers/iio/light/al3320a.c index 63f5a85912fc9..b9076f4344179 100644 --- a/drivers/iio/light/al3320a.c +++ b/drivers/iio/light/al3320a.c @@ -135,7 +135,8 @@ static int al3320a_read_raw(struct iio_dev *indio_dev, int *val2, long mask) { struct al3320a_data *data = iio_priv(indio_dev); - int ret, gain, raw; + int ret, gain; + __le16 raw; switch (mask) { case IIO_CHAN_INFO_RAW: @@ -144,11 +145,12 @@ static int al3320a_read_raw(struct iio_dev *indio_dev, * - low byte of output is stored at AL3320A_REG_DATA_LOW * - high byte of output is stored at AL3320A_REG_DATA_LOW + 1 */ - ret = regmap_read(data->regmap, AL3320A_REG_DATA_LOW, &raw); + ret = regmap_bulk_read(data->regmap, AL3320A_REG_DATA_LOW, + &raw, sizeof(raw)); if (ret) return ret; - *val = raw; + *val = le16_to_cpu(raw); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: From 2ebaea7f3089decb01a8294d89d7e0cf288146c4 Mon Sep 17 00:00:00 2001 From: Biren Pandya Date: Sun, 14 Jun 2026 12:45:49 +0530 Subject: [PATCH 0215/2758] iio: light: gp2ap002: fix runtime PM leak on read error commit 38b72267b7e22768a1f26d9935de4e1752a1dc85 upstream. gp2ap002_read_raw() calls pm_runtime_get_sync() before reading the lux value, but if gp2ap002_get_lux() fails, it returns directly. This skips the pm_runtime_put_autosuspend() call at the "out" label, permanently leaking a runtime PM reference and preventing the device from autosuspending. Replace the direct return with a "goto out" to ensure the reference is properly dropped on the error path. Fixes: f6dbf83c17cb ("iio: light: gp2ap002: Take runtime PM reference on light read") Signed-off-by: Biren Pandya Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/light/gp2ap002.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/light/gp2ap002.c b/drivers/iio/light/gp2ap002.c index a0d8a58f27042..886accf5e8591 100644 --- a/drivers/iio/light/gp2ap002.c +++ b/drivers/iio/light/gp2ap002.c @@ -258,7 +258,7 @@ static int gp2ap002_read_raw(struct iio_dev *indio_dev, case IIO_LIGHT: ret = gp2ap002_get_lux(gp2ap002); if (ret < 0) - return ret; + goto out; *val = ret; ret = IIO_VAL_INT; goto out; From 56447eeab51ee980dbf636b3460b43321dfa1c25 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Tue, 26 May 2026 13:15:29 +0200 Subject: [PATCH 0216/2758] iio: light: opt3001: fix missing state reset on timeout commit c123ca6ee26ad98f70a866ff428b08145c5a24fe upstream. Currently in the function opt3001_get_processed(), there is a check that directly returns -ETIMEDOUT if the conversion IRQ times out, completely bypassing the err label, leaving ok_to_ignore_lock permanently true, potentially breaking the device's falling threshold interrupt detection. Assign -ETIMEDOUT to the return variable and jump to the error label to ensure ok_to_ignore_lock is properly reset. Fixes: 26d90b559057 ("iio: light: opt3001: Fixed timeout error when 0 lux") Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260525-opt3001-cleanup-v4-0-65b36a174f78%40gmail.com?part=1 Signed-off-by: Joshua Crofts Cc: stable@vger.kernel.org Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/light/opt3001.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index 53bc455b7bad1..0d35d23da091e 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -366,8 +366,10 @@ static int opt3001_get_processed(struct opt3001 *opt, int *val, int *val2) ret = wait_event_timeout(opt->result_ready_queue, opt->result_ready, msecs_to_jiffies(OPT3001_RESULT_READY_LONG)); - if (ret == 0) - return -ETIMEDOUT; + if (ret == 0) { + ret = -ETIMEDOUT; + goto err; + } } else { /* Sleep for result ready time */ timeout = (opt->int_time == OPT3001_INT_TIME_SHORT) ? From 9f45d437ce2488b7fd264f256290c5b7223154e8 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Mon, 18 May 2026 14:43:11 +0500 Subject: [PATCH 0217/2758] iio: light: tsl2591: return actual error from probe IRQ failure commit a00ffd15674bfaf8b906503c1600e3d8709af56c upstream. When devm_request_threaded_irq() fails, probe logs the error and then returns -EINVAL, dropping the real error code and breaking the deferred-probe flow for -EPROBE_DEFER. Return ret directly; the IRQ subsystem already prints on failure. Fixes: 2335f0d7c790 ("iio: light: Added AMS tsl2591 driver implementation") Cc: stable@vger.kernel.org Signed-off-by: Stepan Ionichev Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/light/tsl2591.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/light/tsl2591.c b/drivers/iio/light/tsl2591.c index c5557867ea43d..c5ccd833dde6f 100644 --- a/drivers/iio/light/tsl2591.c +++ b/drivers/iio/light/tsl2591.c @@ -1137,10 +1137,8 @@ static int tsl2591_probe(struct i2c_client *client) NULL, tsl2591_event_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "tsl2591_irq", indio_dev); - if (ret) { - dev_err_probe(&client->dev, ret, "IRQ request error\n"); - return -EINVAL; - } + if (ret) + return ret; indio_dev->info = &tsl2591_info; } else { indio_dev->info = &tsl2591_info_no_irq; From 8a383705c455133d77f9b1e5968c915d9de25663 Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Thu, 14 May 2026 14:01:11 +1300 Subject: [PATCH 0218/2758] iio: light: veml6030: fix channel type when pushing events commit c52bb33b641ebaae3e209f97714cb1758206f7d9 upstream. The events are registered for IIO_LIGHT and not for IIO_INTENSITY. Use the correct channel type. When at it, fix minor checkpatch code style warning (alignment). Cc: stable@vger.kernel.org Fixes: 7b779f573c48 ("iio: light: add driver for veml6030 ambient light sensor") Signed-off-by: Javier Carrasco Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/light/veml6030.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/iio/light/veml6030.c b/drivers/iio/light/veml6030.c index 6bcacae3863c0..da8c32cabfd68 100644 --- a/drivers/iio/light/veml6030.c +++ b/drivers/iio/light/veml6030.c @@ -875,9 +875,11 @@ static irqreturn_t veml6030_event_handler(int irq, void *private) else evtdir = IIO_EV_DIR_FALLING; - iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, - 0, IIO_EV_TYPE_THRESH, evtdir), - iio_get_time_ns(indio_dev)); + iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_LIGHT, + 0, + IIO_EV_TYPE_THRESH, + evtdir), + iio_get_time_ns(indio_dev)); return IRQ_HANDLED; } From 0adca7d78b7b0df2b4488f92b50acb4018a88d1d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 6 May 2026 10:27:55 +0200 Subject: [PATCH 0219/2758] iio: magnetometer: ak8975: Add missed pm_runtime_put_autosuspend() call commit e94944d7364d3ddb273539492f9bd9c9a622549a upstream. On the failure in the ak8975_read_axis() the PM runtime gets unbalanced. Balance it by calling pm_runtime_put_autosuspend() on error path as well. Fixes: cde4cb5dd422 ("iio: magn: ak8975: deploy runtime and system PM") Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260505-magnetometer-fixes-v5-0-831b9b5550fc%40gmail.com Signed-off-by: Andy Shevchenko Reviewed-by: Joshua Crofts Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/magnetometer/ak8975.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index b648b0afa5733..b3468756bdf1e 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -784,6 +784,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val) exit: mutex_unlock(&data->lock); + pm_runtime_put_autosuspend(&data->client->dev); dev_err(&client->dev, "Error in reading axis\n"); return ret; } From 9990e06016af6ee153ed52d9b92fe9450efd130a Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 5 May 2026 18:34:55 +0100 Subject: [PATCH 0220/2758] iio: pressure: bmp280: zero-init bmp580 trigger handler buffer commit 1172160f2a2de7bade3bec64b8c5ecf945cde5ed upstream. bmp580_trigger_handler() builds an on-stack scan buffer containing two __le32 fields and an aligned_s64 timestamp, and pushes it to userspace via iio_push_to_buffers_with_ts(). However, only the low 3 bytes of each __le32 field are populated by the device data: memcpy(&buffer.comp_press, &data->buf[3], 3); memcpy(&buffer.comp_temp, &data->buf[0], 3); The high byte of each field is left uninitialised on the stack. The bmp580 channels declare storagebits = 32, so the IIO core transports all four bytes per sample to userspace as part of the scan element, leaking two bytes of kernel stack per scan. Zero-initialise the buffer before populating it, mirroring the fix applied to bme280_trigger_handler() in commit 018f50909e66 ("iio: bmp280: zero-init buffer"). Fixes: 872c8014e05e ("iio: pressure: bmp280: drop sensor_data array") Cc: stable@vger.kernel.org Signed-off-by: David Carlier Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/pressure/bmp280-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index 9b489766e457a..0ebe0b682caae 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -1910,7 +1910,7 @@ static irqreturn_t bmp380_trigger_handler(int irq, void *p) u32 comp_press; s32 comp_temp; aligned_s64 timestamp; - } buffer; + } buffer = { }; int ret; guard(mutex)(&data->lock); From 46e69d3dd429b33e50e2731913239f6af4ea2705 Mon Sep 17 00:00:00 2001 From: Biren Pandya Date: Sun, 14 Jun 2026 12:45:48 +0530 Subject: [PATCH 0221/2758] iio: pressure: mpl115: fix runtime PM leak on read error commit fbe67ff37a6fd855a6c097f84f3738bd13d0a898 upstream. mpl115_read_raw() takes a runtime PM reference with pm_runtime_get_sync() before reading the processed pressure or raw temperature, but on the read error path it returns without calling pm_runtime_put_autosuspend(). Each failed read therefore leaks a runtime PM reference and prevents the device from autosuspending. Drop the reference before checking the return value so both the success and error paths are balanced. Fixes: 0c3a333524a3 ("iio: pressure: mpl115: Implementing low power mode by shutdown gpio") Signed-off-by: Biren Pandya Assisted-by: Claude:claude-opus-4-8 coccinelle Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/pressure/mpl115.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/pressure/mpl115.c b/drivers/iio/pressure/mpl115.c index 830a5065c008f..16e112b796ba9 100644 --- a/drivers/iio/pressure/mpl115.c +++ b/drivers/iio/pressure/mpl115.c @@ -106,18 +106,18 @@ static int mpl115_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_PROCESSED: pm_runtime_get_sync(data->dev); ret = mpl115_comp_pressure(data, val, val2); + pm_runtime_put_autosuspend(data->dev); if (ret < 0) return ret; - pm_runtime_put_autosuspend(data->dev); return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_RAW: pm_runtime_get_sync(data->dev); /* temperature -5.35 C / LSB, 472 LSB is 25 C */ ret = mpl115_read_temp(data); + pm_runtime_put_autosuspend(data->dev); if (ret < 0) return ret; - pm_runtime_put_autosuspend(data->dev); *val = ret >> 6; return IIO_VAL_INT; From 769e819e6925bc826a9158ce0f52222c25f633ce Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Thu, 14 May 2026 19:37:10 +0500 Subject: [PATCH 0222/2758] iio: proximity: vl53l0x: notify trigger and clear IRQ on error paths commit be843b0579f872ec7590d825e2c9a656d4790c4b upstream. vl53l0x_trigger_handler() returns directly on the I2C read failure paths without calling iio_trigger_notify_done() or vl53l0x_clear_irq(). A single transient i2c_smbus_read_i2c_block_data() failure (negative errno or a short read) therefore leaves two pieces of state behind: - iio_trigger_notify_done() never decrements the trigger's use_count, so iio_trigger_poll_nested() silently drops further dispatches (see industrialio-trigger.c, the !atomic_read(&trig->use_count) guard); - vl53l0x_clear_irq() never writes SYSTEM_INTERRUPT_CLEAR, so the chip keeps the DRDY interrupt asserted. The sensor's buffer mode stays wedged from then on, recoverable only by re-binding the driver. The sibling driver vl53l1x-i2c.c handles exactly the same case correctly by jumping to a "notify_and_clear_irq" label that always calls both helpers; mirror that here. The bogus negative-int return value cast to irqreturn_t also goes away as a side effect. Fixes: 762186c6e7b1 ("iio: proximity: vl53l0x-i2c: Added continuous mode support") Signed-off-by: Stepan Ionichev Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/proximity/vl53l0x-i2c.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/iio/proximity/vl53l0x-i2c.c b/drivers/iio/proximity/vl53l0x-i2c.c index ad3e46d47fa8d..6c6e6dab045f7 100644 --- a/drivers/iio/proximity/vl53l0x-i2c.c +++ b/drivers/iio/proximity/vl53l0x-i2c.c @@ -87,15 +87,14 @@ static irqreturn_t vl53l0x_trigger_handler(int irq, void *priv) ret = i2c_smbus_read_i2c_block_data(data->client, VL_REG_RESULT_RANGE_STATUS, sizeof(buffer), buffer); - if (ret < 0) - return ret; - else if (ret != 12) - return -EREMOTEIO; + if (ret != 12) + goto done; scan.chan = get_unaligned_be16(&buffer[10]); iio_push_to_buffers_with_ts(indio_dev, &scan, sizeof(scan), iio_get_time_ns(indio_dev)); +done: iio_trigger_notify_done(indio_dev->trig); vl53l0x_clear_irq(data); From 1c8150ee8f2f52af92c4b1ae8b109f2ec9531933 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Fri, 15 May 2026 18:31:38 +0500 Subject: [PATCH 0223/2758] iio: resolver: ad2s1210: notify trigger and clear state on fault read error commit 70247658d0e783c93b48fcbc3b81d99e992ff478 upstream. ad2s1210_trigger_handler() walks several scan-mask branches and uses "goto error_ret" to land on the iio_trigger_notify_done() teardown at the bottom of the function for every I/O error -- except the MOD_CONFIG fault-register read, which uses a bare "return ret": if (st->fixed_mode == MOD_CONFIG) { unsigned int reg_val; ret = regmap_read(st->regmap, AD2S1210_REG_FAULT, ®_val); if (ret < 0) return ret; ... } Two problems on that path: - the handler returns a negative errno where the prototype expects an irqreturn_t (IRQ_HANDLED / IRQ_NONE), so the caller in the IIO core sees a value outside the enum; - iio_trigger_notify_done() is skipped, leaving the trigger busy-flag set. A single transient SPI/regmap error on the fault read then wedges the trigger so subsequent samples are dropped until the consumer is detached. Convert the error path to "goto error_ret" so the failure path goes through the same notify_done() teardown as every other error in the handler. Fixes: f9b9ff95be8c ("iio: resolver: ad2s1210: add support for adi,fixed-mode") Signed-off-by: Stepan Ionichev Reviewed-by: David Lechner Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/resolver/ad2s1210.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/resolver/ad2s1210.c b/drivers/iio/resolver/ad2s1210.c index 774728c804c01..1be19fe8aa3f6 100644 --- a/drivers/iio/resolver/ad2s1210.c +++ b/drivers/iio/resolver/ad2s1210.c @@ -1334,7 +1334,7 @@ static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) ret = regmap_read(st->regmap, AD2S1210_REG_FAULT, ®_val); if (ret < 0) - return ret; + goto error_ret; st->sample.fault = reg_val; } From f87b86a7fd9e5a0316110fabe372939138b24f7d Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 25 Jun 2026 13:42:59 +0800 Subject: [PATCH 0224/2758] iio: temperature: Build mlx90635 with CONFIG_MLX90635 commit 63a76e3a587c4143e8e24e8a6b0c232fa0676034 upstream. drivers/iio/temperature/Kconfig has a dedicated MLX90635 option, but the Makefile currently builds mlx90635.o under CONFIG_MLX90632. This means enabling CONFIG_MLX90635 alone does not carry its provider object into the build, while enabling CONFIG_MLX90632 unexpectedly also builds mlx90635.o. Gate mlx90635.o on the matching generated Kconfig symbol. Fixes: a1d1ba5e1c28 ("iio: temperature: mlx90635 MLX90635 IR Temperature sensor") Cc: stable@vger.kernel.org Signed-off-by: Pengpeng Hou Reviewed-by: Andy Shevchenko Acked-by: Crt Mori Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/temperature/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/temperature/Makefile b/drivers/iio/temperature/Makefile index 07d6e65709f7f..0850bf6918204 100644 --- a/drivers/iio/temperature/Makefile +++ b/drivers/iio/temperature/Makefile @@ -13,7 +13,7 @@ obj-$(CONFIG_MAX31865) += max31865.o obj-$(CONFIG_MCP9600) += mcp9600.o obj-$(CONFIG_MLX90614) += mlx90614.o obj-$(CONFIG_MLX90632) += mlx90632.o -obj-$(CONFIG_MLX90632) += mlx90635.o +obj-$(CONFIG_MLX90635) += mlx90635.o obj-$(CONFIG_TMP006) += tmp006.o obj-$(CONFIG_TMP007) += tmp007.o obj-$(CONFIG_TMP117) += tmp117.o From e52f7939a41c75aef2439a2976efca84bc7ca5ec Mon Sep 17 00:00:00 2001 From: Liviu Stan Date: Mon, 25 May 2026 19:39:28 +0300 Subject: [PATCH 0225/2758] iio: temperature: ltc2983: Fix n_wires default bypassing rotation check commit 434c150752675f44dc52c384a7fa22e5176bc35a upstream. When adi,number-of-wires is absent, n_wires is left at 0. The binding documents a default of 2 wires, matching the hardware default. However the current-rotate validation checks n_wires == 2 || n_wires == 3, so with n_wires = 0 the guard is bypassed and adi,current-rotate is accepted for a 2-wire RTD. Initialize n_wires = 2 to match the binding default and ensure the rotation check fires correctly when the property is absent. Fixes: f110f3188e56 ("iio: temperature: Add support for LTC2983") Signed-off-by: Liviu Stan Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/temperature/ltc2983.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c index 38e6f8dfd3b80..1f835e326b932 100644 --- a/drivers/iio/temperature/ltc2983.c +++ b/drivers/iio/temperature/ltc2983.c @@ -741,7 +741,7 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st, struct ltc2983_rtd *rtd; int ret = 0; struct device *dev = &st->spi->dev; - u32 excitation_current = 0, n_wires = 0; + u32 excitation_current = 0, n_wires = 2; rtd = devm_kzalloc(dev, sizeof(*rtd), GFP_KERNEL); if (!rtd) From d8274d1a79af03f9d3366139df1beda90c5697c7 Mon Sep 17 00:00:00 2001 From: Liviu Stan Date: Mon, 25 May 2026 19:39:29 +0300 Subject: [PATCH 0226/2758] iio: temperature: ltc2983: Fix reinit_completion() called after conversion start commit 5cb9fdb446bfc3ae0524496f53fb68e67051701b upstream. reinit_completion() was called after regmap_write() initiated the hardware conversion, creating a race window where the interrupt could fire and call complete() before reinit_completion() reset the completion. Move reinit_completion() before the regmap_write() to close the race. ltc2983_eeprom_cmd() already does it in the correct order. Fixes: f110f3188e56 ("iio: temperature: Add support for LTC2983") Signed-off-by: Liviu Stan Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/temperature/ltc2983.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c index 1f835e326b932..2bc5cd46a72f3 100644 --- a/drivers/iio/temperature/ltc2983.c +++ b/drivers/iio/temperature/ltc2983.c @@ -1177,12 +1177,11 @@ static int ltc2983_chan_read(struct ltc2983_data *st, start_conversion |= LTC2983_STATUS_CHAN_SEL(sensor->chan); dev_dbg(&st->spi->dev, "Start conversion on chan:%d, status:%02X\n", sensor->chan, start_conversion); + reinit_completion(&st->completion); /* start conversion */ ret = regmap_write(st->regmap, LTC2983_STATUS_REG, start_conversion); if (ret) return ret; - - reinit_completion(&st->completion); /* * wait for conversion to complete. * 300 ms should be more than enough to complete the conversion. From d90f868f56a16e10eedc6552f48d99dff4d275b7 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Sun, 17 May 2026 23:26:13 +0500 Subject: [PATCH 0227/2758] iio: temperature: tmp006: use devm_iio_trigger_register commit 3c5eed894efd93d68d7f6a359a81ddef0e928774 upstream. tmp006_probe() allocates the DRDY trigger with devm_iio_trigger_alloc() but registers it with plain iio_trigger_register(). The driver has no .remove() callback, so on module unload the trigger stays in the global trigger list while its memory is freed by devm, leaving a dangling entry. Switch to devm_iio_trigger_register() so the registration is undone in the same devm scope as the allocation. Fixes: 91f75ccf9f03 ("iio: temperature: tmp006: add triggered buffer support") Cc: stable@vger.kernel.org Signed-off-by: Stepan Ionichev Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/temperature/tmp006.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/temperature/tmp006.c b/drivers/iio/temperature/tmp006.c index d8d8c8936d177..bf62143fa7191 100644 --- a/drivers/iio/temperature/tmp006.c +++ b/drivers/iio/temperature/tmp006.c @@ -350,7 +350,7 @@ static int tmp006_probe(struct i2c_client *client) data->drdy_trig->ops = &tmp006_trigger_ops; iio_trigger_set_drvdata(data->drdy_trig, indio_dev); - ret = iio_trigger_register(data->drdy_trig); + ret = devm_iio_trigger_register(&client->dev, data->drdy_trig); if (ret) return ret; From 27161c68d5e78807c9d897db222a775b298d05fd Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Wed, 1 Jul 2026 18:52:31 +0900 Subject: [PATCH 0228/2758] ALSA: usx2y: us144mkii: fix work UAF on disconnect commit 147996e7e7c9e8339c0e04f6fa7ccb3e4d448ff7 upstream. tascam_disconnect() cancels capture_work and midi_in_work before usb_kill_anchored_urbs() kills the capture/MIDI-in URBs. Those URBs self-resubmit, and their completion handlers reschedule the work. A URB that completes in the small window between cancel_work_sync() and usb_kill_anchored_urbs() therefore re-arms the work after its only cancel. Nothing cancels it again before snd_card_free() frees the card-private tascam structure, so the work handler then runs on freed memory. Kill the anchored URBs before cancelling the work; once the work is cancelled no remaining URB can complete to re-arm it. Fixes: c1bb0c13e430 ("ALSA: usb-audio: us144mkii: Implement audio capture and decoding") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: HyeongJun An Link: https://patch.msgid.link/20260701095231.1020811-1-sammiee5311@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/usx2y/us144mkii.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/sound/usb/usx2y/us144mkii.c b/sound/usb/usx2y/us144mkii.c index 94553b61013c5..58ef23146f209 100644 --- a/sound/usb/usx2y/us144mkii.c +++ b/sound/usb/usx2y/us144mkii.c @@ -585,19 +585,24 @@ static void tascam_disconnect(struct usb_interface *intf) return; if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { - /* Ensure all deferred work is complete before freeing resources */ snd_card_disconnect(tascam->card); - cancel_work_sync(&tascam->stop_work); - cancel_work_sync(&tascam->capture_work); - cancel_work_sync(&tascam->midi_in_work); - cancel_work_sync(&tascam->midi_out_work); - cancel_work_sync(&tascam->stop_pcm_work); + /* + * Kill the URBs before cancelling the work, so a late URB + * completion cannot re-arm a work that then runs after + * snd_card_free(). + */ usb_kill_anchored_urbs(&tascam->playback_anchor); usb_kill_anchored_urbs(&tascam->capture_anchor); usb_kill_anchored_urbs(&tascam->feedback_anchor); usb_kill_anchored_urbs(&tascam->midi_in_anchor); usb_kill_anchored_urbs(&tascam->midi_out_anchor); + + cancel_work_sync(&tascam->stop_work); + cancel_work_sync(&tascam->capture_work); + cancel_work_sync(&tascam->midi_in_work); + cancel_work_sync(&tascam->midi_out_work); + cancel_work_sync(&tascam->stop_pcm_work); timer_delete_sync(&tascam->error_timer); tascam_free_urbs(tascam); snd_card_free(tascam->card); From 6f3c7e552fd863b9df1cf164f29586ea9a5135bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Fri, 15 May 2026 10:32:25 -0300 Subject: [PATCH 0229/2758] ALSA: virtio: Add missing 384 kHz PCM rate mapping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 83fbbcb7935ec6d2c8ba3bc133e8a0ead2ab0b2d upstream. The VirtIO sound UAPI defines VIRTIO_SND_PCM_RATE_384000, and ALSA has SNDRV_PCM_RATE_384000. However, virtio-snd's rate conversion tables stop at 192 kHz. A device advertising only 384 kHz is rejected as having no supported PCM frame rates. A device advertising 384 kHz together with lower rates does not expose 384 kHz through the ALSA hardware constraints. The selected ALSA rate also needs a reverse mapping for SET_PARAMS. Add the missing 384 kHz entries to both conversion tables. Fixes: 29b96bf50ba9 ("ALSA: virtio: build PCM devices and substream hardware descriptors") Fixes: da76e9f3e43a ("ALSA: virtio: PCM substream operators") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260515-alsa-virtio-384k-rate-v1-1-35ecb5df835c@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/virtio/virtio_pcm.c | 3 ++- sound/virtio/virtio_pcm_ops.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/virtio/virtio_pcm.c b/sound/virtio/virtio_pcm.c index eb9cc8131905d..be3893de40a55 100644 --- a/sound/virtio/virtio_pcm.c +++ b/sound/virtio/virtio_pcm.c @@ -77,7 +77,8 @@ static const struct virtsnd_v2a_rate g_v2a_rate_map[] = { [VIRTIO_SND_PCM_RATE_88200] = { SNDRV_PCM_RATE_88200, 88200 }, [VIRTIO_SND_PCM_RATE_96000] = { SNDRV_PCM_RATE_96000, 96000 }, [VIRTIO_SND_PCM_RATE_176400] = { SNDRV_PCM_RATE_176400, 176400 }, - [VIRTIO_SND_PCM_RATE_192000] = { SNDRV_PCM_RATE_192000, 192000 } + [VIRTIO_SND_PCM_RATE_192000] = { SNDRV_PCM_RATE_192000, 192000 }, + [VIRTIO_SND_PCM_RATE_384000] = { SNDRV_PCM_RATE_384000, 384000 } }; /** diff --git a/sound/virtio/virtio_pcm_ops.c b/sound/virtio/virtio_pcm_ops.c index 6297a9c61e70b..1105e7ff3523a 100644 --- a/sound/virtio/virtio_pcm_ops.c +++ b/sound/virtio/virtio_pcm_ops.c @@ -90,7 +90,8 @@ static const struct virtsnd_a2v_rate g_a2v_rate_map[] = { { 88200, VIRTIO_SND_PCM_RATE_88200 }, { 96000, VIRTIO_SND_PCM_RATE_96000 }, { 176400, VIRTIO_SND_PCM_RATE_176400 }, - { 192000, VIRTIO_SND_PCM_RATE_192000 } + { 192000, VIRTIO_SND_PCM_RATE_192000 }, + { 384000, VIRTIO_SND_PCM_RATE_384000 } }; static int virtsnd_pcm_sync_stop(struct snd_pcm_substream *substream); From 21584672fd699abe1768241d6c501b2de6139b6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Thu, 7 May 2026 11:28:30 -0300 Subject: [PATCH 0230/2758] ALSA: virtio: Validate control metadata from the device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit c77a6cbb36ff8cbc1f084d94f8dcda5250935271 upstream. virtio-snd control handling trusts the device-provided control type and value count returned by the device. That metadata is then used directly to index g_v2a_type_map[] in virtsnd_kctl_info(), and to size loops and memcpy() operations in virtsnd_kctl_get() and virtsnd_kctl_put() against fixed-size virtio_snd_ctl_value and snd_ctl_elem_value arrays. A buggy or malicious device can therefore trigger out-of-bounds access by advertising an invalid control type or an oversized value count. Validate control type and count once in virtsnd_kctl_parse_cfg(), before querying enumerated items or exposing the control to ALSA. Fixes: d6568e3de42d ("ALSA: virtio: add support for audio controls") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260507-alsa-virtio-validate-kctl-info-v1-1-7404fb12ec37@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/virtio/virtio_kctl.c | 50 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/sound/virtio/virtio_kctl.c b/sound/virtio/virtio_kctl.c index ffb903d56297a..45f7b6a5b3080 100644 --- a/sound/virtio/virtio_kctl.c +++ b/sound/virtio/virtio_kctl.c @@ -18,6 +18,21 @@ static const snd_ctl_elem_type_t g_v2a_type_map[] = { [VIRTIO_SND_CTL_TYPE_IEC958] = SNDRV_CTL_ELEM_TYPE_IEC958 }; +/* Map for converting VirtIO types to maximum value counts. */ +static const unsigned int g_v2a_count_map[] = { + [VIRTIO_SND_CTL_TYPE_BOOLEAN] = + ARRAY_SIZE(((struct virtio_snd_ctl_value *)0)->value.integer), + [VIRTIO_SND_CTL_TYPE_INTEGER] = + ARRAY_SIZE(((struct virtio_snd_ctl_value *)0)->value.integer), + [VIRTIO_SND_CTL_TYPE_INTEGER64] = + ARRAY_SIZE(((struct virtio_snd_ctl_value *)0)->value.integer64), + [VIRTIO_SND_CTL_TYPE_ENUMERATED] = + ARRAY_SIZE(((struct virtio_snd_ctl_value *)0)->value.enumerated), + [VIRTIO_SND_CTL_TYPE_BYTES] = + ARRAY_SIZE(((struct virtio_snd_ctl_value *)0)->value.bytes), + [VIRTIO_SND_CTL_TYPE_IEC958] = 1 +}; + /* Map for converting VirtIO access rights to ALSA access rights. */ static const unsigned int g_v2a_access_map[] = { [VIRTIO_SND_CTL_ACCESS_READ] = SNDRV_CTL_ELEM_ACCESS_READ, @@ -36,6 +51,37 @@ static const unsigned int g_v2a_mask_map[] = { [VIRTIO_SND_CTL_EVT_MASK_TLV] = SNDRV_CTL_EVENT_MASK_TLV }; +static int virtsnd_kctl_validate_info(struct virtio_snd *snd, u32 cid, + struct virtio_snd_ctl_info *kinfo) +{ + struct virtio_device *vdev = snd->vdev; + unsigned int type = le32_to_cpu(kinfo->type); + unsigned int count = le32_to_cpu(kinfo->count); + + if (type >= ARRAY_SIZE(g_v2a_type_map)) { + dev_err(&vdev->dev, "control #%u: unknown type %u\n", + cid, type); + return -EINVAL; + } + + if (count > g_v2a_count_map[type] || + (type == VIRTIO_SND_CTL_TYPE_IEC958 && count != 1)) { + dev_err(&vdev->dev, "control #%u: invalid count %u for type %u\n", + cid, count, type); + return -EINVAL; + } + + if (type == VIRTIO_SND_CTL_TYPE_ENUMERATED && + !le32_to_cpu(kinfo->value.enumerated.items)) { + dev_err(&vdev->dev, + "control #%u: no items for enumerated control\n", + cid); + return -EINVAL; + } + + return 0; +} + /** * virtsnd_kctl_info() - Returns information about the control. * @kcontrol: ALSA control element. @@ -385,6 +431,10 @@ int virtsnd_kctl_parse_cfg(struct virtio_snd *snd) struct virtio_snd_ctl_info *kinfo = &snd->kctl_infos[i]; unsigned int type = le32_to_cpu(kinfo->type); + rc = virtsnd_kctl_validate_info(snd, i, kinfo); + if (rc) + return rc; + if (type == VIRTIO_SND_CTL_TYPE_ENUMERATED) { rc = virtsnd_kctl_get_enum_items(snd, i); if (rc) From 18ec7d7785be7a4ee8ea11e355122282caad4267 Mon Sep 17 00:00:00 2001 From: Zhao Dongdong Date: Wed, 27 May 2026 20:09:12 +0800 Subject: [PATCH 0231/2758] ALSA: ymfpci: check snd_ctl_new1() return value commit e64d170346d00b580c0043de3e5ccb3e331c47d4 upstream. snd_ctl_new1() can return NULL when memory allocation fails. snd_ymfpci_create_spdif_controls() does not check the return value before dereferencing kctl->id.device, which can lead to a NULL pointer dereference. Add NULL checks after snd_ctl_new1() calls and return -ENOMEM if any fails. Assisted-by: Opencode:DeepSeek-V4-Flash Cc: stable@vger.kernel.org Fixes: c9b83ae4a160 ("ALSA: ymfpci: Fix kctl->id initialization") Signed-off-by: Zhao Dongdong Link: https://patch.msgid.link/tencent_4745C5DC2333325C0EDAB1EFC88A136E6809@qq.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/ymfpci/ymfpci_main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index b9a09568afc9e..2ccb976e68e0b 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -1781,16 +1781,22 @@ int snd_ymfpci_mixer(struct snd_ymfpci *chip, int rear_switch) if (snd_BUG_ON(!chip->pcm_spdif)) return -ENXIO; kctl = snd_ctl_new1(&snd_ymfpci_spdif_default, chip); + if (!kctl) + return -ENOMEM; kctl->id.device = chip->pcm_spdif->device; err = snd_ctl_add(chip->card, kctl); if (err < 0) return err; kctl = snd_ctl_new1(&snd_ymfpci_spdif_mask, chip); + if (!kctl) + return -ENOMEM; kctl->id.device = chip->pcm_spdif->device; err = snd_ctl_add(chip->card, kctl); if (err < 0) return err; kctl = snd_ctl_new1(&snd_ymfpci_spdif_stream, chip); + if (!kctl) + return -ENOMEM; kctl->id.device = chip->pcm_spdif->device; err = snd_ctl_add(chip->card, kctl); if (err < 0) From e47f2a341adbac001b6f5d0211b0cd1c1668637b Mon Sep 17 00:00:00 2001 From: Zhao Dongdong Date: Wed, 27 May 2026 20:09:14 +0800 Subject: [PATCH 0232/2758] ALSA: aoa: check snd_ctl_new1() return value commit 8df560fefe6fed6a20b7e06720eeaeccec349ac0 upstream. snd_ctl_new1() can return NULL when memory allocation fails. In layout.c, the function does not check the return value before dereferencing ctl->id.name or passing to aoa_snd_ctl_add(), which can lead to a NULL pointer dereference. Add NULL checks after snd_ctl_new1() calls and return early if any fails. Assisted-by: Opencode:DeepSeek-V4-Flash Cc: stable@vger.kernel.org Fixes: f3d9478b2ce4 ("[ALSA] snd-aoa: add snd-aoa") Signed-off-by: Zhao Dongdong Link: https://patch.msgid.link/tencent_35F3A25FEEBF190A2E15ED787754C57E3708@qq.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/aoa/fabrics/layout.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/aoa/fabrics/layout.c b/sound/aoa/fabrics/layout.c index c3ebb6de47891..7bb541577a263 100644 --- a/sound/aoa/fabrics/layout.c +++ b/sound/aoa/fabrics/layout.c @@ -948,6 +948,8 @@ static void layout_attached_codec(struct aoa_codec *codec) if (lineout == 1) ldev->gpio.methods->set_lineout(codec->gpio, 1); ctl = snd_ctl_new1(&lineout_ctl, codec->gpio); + if (!ctl) + return; if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE) strscpy(ctl->id.name, "Headphone Switch"); ldev->lineout_ctrl = ctl; @@ -961,12 +963,16 @@ static void layout_attached_codec(struct aoa_codec *codec) if (ldev->have_lineout_detect) { ctl = snd_ctl_new1(&lineout_detect_choice, ldev); + if (!ctl) + return; if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE) strscpy(ctl->id.name, "Headphone Detect Autoswitch"); aoa_snd_ctl_add(ctl); ctl = snd_ctl_new1(&lineout_detected, ldev); + if (!ctl) + return; if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE) strscpy(ctl->id.name, "Headphone Detected"); From 0680413f2f10aab43878dd3db711a6a9e45bab7c Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Thu, 18 Jun 2026 14:03:15 +0800 Subject: [PATCH 0233/2758] ALSA: caiaq: fix out-of-bounds read in the Traktor Kontrol S4 input parser commit f7f3f9fd81e7adbaa12c2e62ee07f0e094a543fd upstream. snd_usb_caiaq_tks4_dispatch() decodes the Traktor Kontrol S4 input stream in fixed 16-byte (TKS4_MSGBLOCK_SIZE) message blocks. On every iteration it advances buf and subtracts the block size while looping on "while (len)". len is urb->actual_length. That value is supplied by the device and is not guaranteed to be a multiple of 16. When a final short block leaves len between 1 and 15, the loop runs once more, reads up to buf[15], and then does "len -= TKS4_MSGBLOCK_SIZE". As len is unsigned this underflows to a huge value. The loop then keeps iterating and walking buf far past the end of the 512-byte ep4_in_buf, reading out of bounds until a bogus block id happens to be hit. Iterate only while a full message block is available. This stops the unsigned underflow and silently drops any trailing partial block, which carries no complete control value anyway. The sibling endpoint-4 parsers are not affected. The Traktor Kontrol X1 and Maschine arms in snd_usb_caiaq_ep4_reply_dispatch() floor urb->actual_length before dispatching. Fixes: 15c5ab607045 ("ALSA: snd-usb-caiaq: Add support for Traktor Kontrol S4") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Link: https://patch.msgid.link/178176259547.3343534.2724779296835237429@maoyixie.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/caiaq/input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/usb/caiaq/input.c b/sound/usb/caiaq/input.c index 5c70fdf61cc13..2db4d1332df1c 100644 --- a/sound/usb/caiaq/input.c +++ b/sound/usb/caiaq/input.c @@ -330,7 +330,7 @@ static void snd_usb_caiaq_tks4_dispatch(struct snd_usb_caiaqdev *cdev, { struct device *dev = caiaqdev_to_dev(cdev); - while (len) { + while (len >= TKS4_MSGBLOCK_SIZE) { unsigned int i, block_id = (buf[0] << 8) | buf[1]; switch (block_id) { From 67e9ea92cd598cba1783ff701553c776a6cedee9 Mon Sep 17 00:00:00 2001 From: Zhao Dongdong Date: Wed, 27 May 2026 20:09:13 +0800 Subject: [PATCH 0234/2758] ALSA: cmipci: check snd_ctl_new1() return value commit c205bd1b28fb7e5f1061a4e78813fad7d315cb3e upstream. snd_ctl_new1() can return NULL when memory allocation fails. snd_cmipci_spdif_controls() does not check the return value before dereferencing kctl->id.device, which can lead to a NULL pointer dereference. Add NULL checks after snd_ctl_new1() calls and return -ENOMEM if any fails. Assisted-by: Opencode:DeepSeek-V4-Flash Cc: stable@vger.kernel.org Fixes: f2f312ad88c6 ("ALSA: cmipci: Fix kctl->id initialization") Signed-off-by: Zhao Dongdong Link: https://patch.msgid.link/tencent_964433DCD132125D5EDA79EE068A2D6EFA09@qq.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/cmipci.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index cd73b68336394..ff4bfbf94b81b 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -2637,16 +2637,22 @@ static int snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_device) } if (cm->can_ac3_hw) { kctl = snd_ctl_new1(&snd_cmipci_spdif_default, cm); + if (!kctl) + return -ENOMEM; kctl->id.device = pcm_spdif_device; err = snd_ctl_add(card, kctl); if (err < 0) return err; kctl = snd_ctl_new1(&snd_cmipci_spdif_mask, cm); + if (!kctl) + return -ENOMEM; kctl->id.device = pcm_spdif_device; err = snd_ctl_add(card, kctl); if (err < 0) return err; kctl = snd_ctl_new1(&snd_cmipci_spdif_stream, cm); + if (!kctl) + return -ENOMEM; kctl->id.device = pcm_spdif_device; err = snd_ctl_add(card, kctl); if (err < 0) From 426a9947a38d272d0e19c031658da68e31128667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Mon, 15 Jun 2026 10:37:26 -0300 Subject: [PATCH 0235/2758] ALSA: compress: Fix task creation error unwind MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 4a60127debb9e370d6c0e22a307326b624a141f3 upstream. snd_compr_task_new() allocates the driver task before validating the returned DMA buffers and reserving file descriptors. When either of those later steps fails, the core frees its task wrapper and DMA-buffer references without calling the driver's task_free() callback. Any driver resources allocated by task_create() are therefore leaked. The dual-fd allocation path also jumps to cleanup without storing the negative get_unused_fd_flags() result in retval. Since retval still contains the successful task_create() return value, TASK_CREATE can incorrectly report success although the task was discarded. Preserve the fd allocation errors and call task_free() when failure occurs after a successful task_create() callback. Fixes: 04177158cf98 ("ALSA: compress_offload: introduce accel operation mode") Fixes: 3d3f43fab4cf ("ALSA: compress_offload: improve file descriptors installation for dma-buf") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260615-alsa-compress-task-unwind-v1-1-39e8ad3ddb27@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/compress_offload.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index fd63d219bf866..ea699491f0c36 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -1083,15 +1083,18 @@ static int snd_compr_task_new(struct snd_compr_stream *stream, struct snd_compr_ file descriptors are allocated before fd_install() */ if (!task->input || !task->input->file || !task->output || !task->output->file) { retval = -EINVAL; - goto cleanup; + goto free_driver_task; } fd_i = get_unused_fd_flags(O_WRONLY|O_CLOEXEC); - if (fd_i < 0) - goto cleanup; + if (fd_i < 0) { + retval = fd_i; + goto free_driver_task; + } fd_o = get_unused_fd_flags(O_RDONLY|O_CLOEXEC); if (fd_o < 0) { + retval = fd_o; put_unused_fd(fd_i); - goto cleanup; + goto free_driver_task; } /* keep dmabuf reference until freed with task free ioctl */ get_dma_buf(task->input); @@ -1103,6 +1106,8 @@ static int snd_compr_task_new(struct snd_compr_stream *stream, struct snd_compr_ list_add_tail(&task->list, &stream->runtime->tasks); stream->runtime->total_tasks++; return 0; +free_driver_task: + stream->ops->task_free(stream, task); cleanup: snd_compr_task_free(task); return retval; From 1949163dee39e0e4a1468f37dd7302962f6af45a Mon Sep 17 00:00:00 2001 From: Zhao Dongdong Date: Wed, 27 May 2026 20:09:09 +0800 Subject: [PATCH 0236/2758] ALSA: es1938: check snd_ctl_new1() return value commit 1edd1f02dddd20aeb6066ded41017615766ea42f upstream. snd_ctl_new1() can return NULL when memory allocation fails. snd_es1938_mixer() does not check the return value before dereferencing the pointer, which can lead to a NULL pointer dereference. Add a NULL check after snd_ctl_new1() and return -ENOMEM if it fails. Assisted-by: Opencode:DeepSeek-V4-Flash Cc: stable@vger.kernel.org Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Zhao Dongdong Link: https://patch.msgid.link/tencent_E0DC65165FDF2C8982BAFB6794B854B53B0A@qq.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/es1938.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index 280125eff3624..b6dcb721fffce 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -1655,6 +1655,8 @@ static int snd_es1938_mixer(struct es1938 *chip) for (idx = 0; idx < ARRAY_SIZE(snd_es1938_controls); idx++) { struct snd_kcontrol *kctl; kctl = snd_ctl_new1(&snd_es1938_controls[idx], chip); + if (!kctl) + return -ENOMEM; switch (idx) { case 0: chip->master_volume = kctl; From aeeeae9c1a51c730288e199fb249ac7e00025d69 Mon Sep 17 00:00:00 2001 From: "Geoffrey D. Bennett" Date: Tue, 23 Jun 2026 02:59:04 +0930 Subject: [PATCH 0237/2758] ALSA: FCP: Add Focusrite ISA C8X support commit e0ecb324246be9cf3a0689346a658e48a38546b2 upstream. Add USB PID 0x821e to the list of devices handled by the Focusrite Control Protocol (FCP) driver. Cc: stable@vger.kernel.org Signed-off-by: Geoffrey D. Bennett Link: https://patch.msgid.link/ajlw4HK+2RSW3nUl@m.b4.vu Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/mixer_quirks.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 1bdaa46d4fe18..21ed3acba1b8e 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -4451,6 +4451,7 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) case USB_ID(0x1235, 0x821b): /* Focusrite Scarlett 16i16 4th Gen */ case USB_ID(0x1235, 0x821c): /* Focusrite Scarlett 18i16 4th Gen */ case USB_ID(0x1235, 0x821d): /* Focusrite Scarlett 18i20 4th Gen */ + case USB_ID(0x1235, 0x821e): /* Focusrite ISA C8X */ err = snd_fcp_init(mixer); break; From 31a01b70bb90e3ef3147f308e2ea899e1d2485ca Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Sun, 21 Jun 2026 23:09:07 +0800 Subject: [PATCH 0238/2758] ALSA: firewire: isight: bound the sample count to the packet payload commit 29b9667982e4df2ed7744f86b1144f8bb58eb698 upstream. isight_packet() takes the frame count from the device iso packet and checks it only against the device claimed iso length. count = be32_to_cpu(payload->sample_count); if (likely(count <= (length - 16) / 4)) isight_samples(isight, payload->samples, count); length is the iso header data_length. It can be up to 0xffff. So the gate allows a count up to about 16379. isight_samples() then copies count frames out of payload->samples into the PCM DMA buffer. payload->samples holds only 2 * MAX_FRAMES_PER_PACKET values. The device multiplexes two samples per frame. A count past MAX_FRAMES_PER_PACKET reads past the payload. A count past the buffer size writes past runtime->dma_area. The smallest PCM buffer is larger than MAX_FRAMES_PER_PACKET. Bounding the count to MAX_FRAMES_PER_PACKET keeps both the read and the write in range. A malicious or faulty Apple iSight on the FireWire bus reaches this during a normal capture. Add the MAX_FRAMES_PER_PACKET bound to the gate. Fixes: 3a691b28a0ca ("ALSA: add Apple iSight microphone driver") Suggested-by: Takashi Sakamoto Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Link: https://patch.msgid.link/178205454729.1900991.7807310178296762772@maoyixie.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/firewire/isight.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c index 2b7f071d593b9..33c9dd48b3b0b 100644 --- a/sound/firewire/isight.c +++ b/sound/firewire/isight.c @@ -179,7 +179,8 @@ static void isight_packet(struct fw_iso_context *context, u32 cycle, if (likely(length >= 16 && payload->signature == cpu_to_be32(0x73676874/*"sght"*/))) { count = be32_to_cpu(payload->sample_count); - if (likely(count <= (length - 16) / 4)) { + if (likely(count <= (length - 16) / 4 && + count <= MAX_FRAMES_PER_PACKET)) { total = be32_to_cpu(payload->sample_total); if (unlikely(total != isight->total_samples)) { if (!isight->first_packet) From 465075c6835103821d725c13f8c545898e5f2636 Mon Sep 17 00:00:00 2001 From: Zhao Dongdong Date: Wed, 27 May 2026 20:09:10 +0800 Subject: [PATCH 0239/2758] ALSA: gus: check snd_ctl_new1() return value commit c7fa99d30c7a166a5e5db5a585ce7501ff68326b upstream. snd_ctl_new1() can return NULL when memory allocation fails. snd_gf1_pcm_volume_control() does not check the return value before dereferencing kctl->id.index, which can lead to a NULL pointer dereference. Add a NULL check after snd_ctl_new1() and return -ENOMEM if it fails. Assisted-by: Opencode:DeepSeek-V4-Flash Cc: stable@vger.kernel.org Fixes: c5ae57b1bb99 ("ALSA: gus: Fix kctl->id initialization") Signed-off-by: Zhao Dongdong Link: https://patch.msgid.link/tencent_F644A3DCAD32945D62DB2FEEBE8A996F6809@qq.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/isa/gus/gus_pcm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c index a0757e1ede465..08ccb4d80adeb 100644 --- a/sound/isa/gus/gus_pcm.c +++ b/sound/isa/gus/gus_pcm.c @@ -851,6 +851,8 @@ int snd_gf1_pcm_new(struct snd_gus_card *gus, int pcm_dev, int control_index) kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control1, gus); else kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control, gus); + if (!kctl) + return -ENOMEM; kctl->id.index = control_index; err = snd_ctl_add(card, kctl); if (err < 0) From d6a40a4d083ef74d00c8f9516cb5ff07ac70720b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Mon, 11 May 2026 01:29:34 -0300 Subject: [PATCH 0240/2758] ALSA: hda/cs35l41: Fix firmware load work teardown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit b65020d5398f499c09498c9786dba6d67ae57664 upstream. cs35l41_hda creates ALSA controls whose private data points at the cs35l41_hda object. The firmware load control can also queue fw_load_work. Those controls are not removed on component unbind, and device remove only cancels fw_load_work through cs35l41_remove_dsp(). That helper is skipped when halo_initialized is false. With firmware_autostart disabled, a firmware load can be requested before the DSP has been initialized. If the component or device is removed before the queued work runs, the worker can run after teardown and dereference driver state that is no longer valid. Track the created controls and remove them on unbind so no new control callback can reach the driver data or queue more work. Then cancel fw_load_work to drain any request that was already queued. Also cancel the work unconditionally during device remove before runtime PM teardown. Fixes: 47ceabd99a28 ("ALSA: hda: cs35l41: Support Firmware switching and reloading") Fixes: 4c870513fbb0 ("ALSA: hda: cs35l41: Add read-only ALSA control for forced mute") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Reviewed-by: Stefan Binding Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260511-alsa-hda-cs35l41-fw-work-teardown-v1-1-1184e9bc4f25@gmail.com Signed-off-by: Greg Kroah-Hartman --- sound/hda/codecs/side-codecs/cs35l41_hda.c | 77 +++++++++++++++------- sound/hda/codecs/side-codecs/cs35l41_hda.h | 5 ++ 2 files changed, 60 insertions(+), 22 deletions(-) diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda.c b/sound/hda/codecs/side-codecs/cs35l41_hda.c index acfccc848f82d..64a5bd895fd1e 100644 --- a/sound/hda/codecs/side-codecs/cs35l41_hda.c +++ b/sound/hda/codecs/side-codecs/cs35l41_hda.c @@ -1325,6 +1325,43 @@ static int cs35l41_fw_type_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ct return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(cs35l41_hda_fw_ids), cs35l41_hda_fw_ids); } +static void cs35l41_remove_controls(struct cs35l41_hda *cs35l41) +{ + if (!cs35l41->codec) + return; + + snd_ctl_remove(cs35l41->codec->card, cs35l41->mute_override_ctl); + cs35l41->mute_override_ctl = NULL; + + snd_ctl_remove(cs35l41->codec->card, cs35l41->fw_load_ctl); + cs35l41->fw_load_ctl = NULL; + + snd_ctl_remove(cs35l41->codec->card, cs35l41->fw_type_ctl); + cs35l41->fw_type_ctl = NULL; +} + +static int cs35l41_add_control(struct cs35l41_hda *cs35l41, + struct snd_kcontrol_new *ctl, + struct snd_kcontrol **kctl) +{ + int ret; + + *kctl = snd_ctl_new1(ctl, cs35l41); + if (!*kctl) + return -ENOMEM; + + ret = snd_ctl_add(cs35l41->codec->card, *kctl); + if (ret) { + dev_err(cs35l41->dev, "Failed to add KControl %s = %d\n", ctl->name, ret); + *kctl = NULL; + return ret; + } + + dev_dbg(cs35l41->dev, "Added Control %s\n", ctl->name); + + return 0; +} + static int cs35l41_create_controls(struct cs35l41_hda *cs35l41) { char fw_type_ctl_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; @@ -1360,32 +1397,23 @@ static int cs35l41_create_controls(struct cs35l41_hda *cs35l41) scnprintf(mute_override_ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s Forced Mute Status", cs35l41->amp_name); - ret = snd_ctl_add(cs35l41->codec->card, snd_ctl_new1(&fw_type_ctl, cs35l41)); - if (ret) { - dev_err(cs35l41->dev, "Failed to add KControl %s = %d\n", fw_type_ctl.name, ret); - return ret; - } - - dev_dbg(cs35l41->dev, "Added Control %s\n", fw_type_ctl.name); - - ret = snd_ctl_add(cs35l41->codec->card, snd_ctl_new1(&fw_load_ctl, cs35l41)); - if (ret) { - dev_err(cs35l41->dev, "Failed to add KControl %s = %d\n", fw_load_ctl.name, ret); - return ret; - } - - dev_dbg(cs35l41->dev, "Added Control %s\n", fw_load_ctl.name); + ret = cs35l41_add_control(cs35l41, &fw_type_ctl, &cs35l41->fw_type_ctl); + if (ret) + goto err; - ret = snd_ctl_add(cs35l41->codec->card, snd_ctl_new1(&mute_override_ctl, cs35l41)); - if (ret) { - dev_err(cs35l41->dev, "Failed to add KControl %s = %d\n", mute_override_ctl.name, - ret); - return ret; - } + ret = cs35l41_add_control(cs35l41, &fw_load_ctl, &cs35l41->fw_load_ctl); + if (ret) + goto err; - dev_dbg(cs35l41->dev, "Added Control %s\n", mute_override_ctl.name); + ret = cs35l41_add_control(cs35l41, &mute_override_ctl, &cs35l41->mute_override_ctl); + if (ret) + goto err; return 0; + +err: + cs35l41_remove_controls(cs35l41); + return ret; } static bool cs35l41_dsm_supported(acpi_handle handle, unsigned int commands) @@ -1522,6 +1550,10 @@ static void cs35l41_hda_unbind(struct device *dev, struct device *master, void * device_link_remove(&cs35l41->codec->core.dev, cs35l41->dev); unlock_system_sleep(sleep_flags); memset(comp, 0, sizeof(*comp)); + + cs35l41_remove_controls(cs35l41); + cancel_work_sync(&cs35l41->fw_load_work); + cs35l41->codec = NULL; } } @@ -2060,6 +2092,7 @@ void cs35l41_hda_remove(struct device *dev) struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); component_del(cs35l41->dev, &cs35l41_hda_comp_ops); + cancel_work_sync(&cs35l41->fw_load_work); pm_runtime_get_sync(cs35l41->dev); pm_runtime_dont_use_autosuspend(cs35l41->dev); diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda.h b/sound/hda/codecs/side-codecs/cs35l41_hda.h index 7d003c598e93e..56ec07c0bb745 100644 --- a/sound/hda/codecs/side-codecs/cs35l41_hda.h +++ b/sound/hda/codecs/side-codecs/cs35l41_hda.h @@ -57,6 +57,8 @@ enum control_bus { SPI }; +struct snd_kcontrol; + struct cs35l41_hda { struct device *dev; struct regmap *regmap; @@ -75,6 +77,9 @@ struct cs35l41_hda { int speaker_id; struct mutex fw_mutex; struct work_struct fw_load_work; + struct snd_kcontrol *fw_type_ctl; + struct snd_kcontrol *fw_load_ctl; + struct snd_kcontrol *mute_override_ctl; struct regmap_irq_chip_data *irq_data; bool firmware_running; From 0056958bf3081cc9353e3cc8e0b3e9d2cb28d4c3 Mon Sep 17 00:00:00 2001 From: Cameron Graham Date: Fri, 12 Jun 2026 21:46:01 +1200 Subject: [PATCH 0241/2758] ALSA: hda/hdmi: Add force-connect quirk for HP EliteDesk 800 G5 Mini commit 0ec17ee704615125c0b6e100c38129393a346bcc upstream. The HP EliteDesk 800 G5 Mini (PCI subsystem 103c:8595) uses Cannon Lake PCH cAVS HDA with DisplayPort audio pins 0x05 and 0x06 set to AC_JACK_PORT_NONE (N/A) in BIOS defaults, causing hdmi_add_pin() to skip them and the DP audio device to not appear in ALSA. Add the board to the existing force_connect_list alongside the similar HP EliteDesk 800 G4 entries. Signed-off-by: Cameron Graham Cc: Link: https://patch.msgid.link/20260612094601.1209845-1-cam.graham@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/hda/codecs/hdmi/hdmi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/hdmi/hdmi.c b/sound/hda/codecs/hdmi/hdmi.c index f20d1715da62c..31c9544c79963 100644 --- a/sound/hda/codecs/hdmi/hdmi.c +++ b/sound/hda/codecs/hdmi/hdmi.c @@ -1549,6 +1549,7 @@ static const struct snd_pci_quirk force_connect_list[] = { SND_PCI_QUIRK(0x103c, 0x83e2, "HP EliteDesk 800 G4", 1), SND_PCI_QUIRK(0x103c, 0x83ef, "HP MP9 G4 Retail System AMS", 1), SND_PCI_QUIRK(0x103c, 0x845a, "HP EliteDesk 800 G4 DM 65W", 1), + SND_PCI_QUIRK(0x103c, 0x8595, "HP EliteDesk 800 G5 Mini", 1), SND_PCI_QUIRK(0x103c, 0x83f3, "HP ProDesk 400", 1), SND_PCI_QUIRK(0x103c, 0x870f, "HP", 1), SND_PCI_QUIRK(0x103c, 0x871a, "HP", 1), From 8bbba4ab5e6de4879bf5ecd7d8118b478e425892 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Wed, 27 May 2026 22:08:41 +0800 Subject: [PATCH 0242/2758] ALSA: hda/hdmi: Use 'AC_PINSENSE_ELDV' to detect pinsense for Loongson commit 958e4450e961d75bd9d8f5bfe245fb15bc78e02a upstream. Due to a hardware defect, for Loongson PCI HDMI devices with a reversion ID of 2, the pin sense status must be determined via the ELD. Add a codec flag, eld_jack_detect, to indicate this case, and do special handlings in read_pin_sense(). Cc: stable@vger.kernel.org Signed-off-by: Baoqi Zhang Signed-off-by: Haowei Zheng Signed-off-by: Huacai Chen Link: https://patch.msgid.link/20260527140841.3407183-1-chenhuacai@loongson.cn Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- include/sound/hda_codec.h | 1 + sound/hda/codecs/hdmi/hdmi.c | 8 +++++++- sound/hda/common/jack.c | 6 ++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index 24581080e26a0..1a1fe7a904c36 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -259,6 +259,7 @@ struct hda_codec { unsigned int forced_resume:1; /* forced resume for jack */ unsigned int no_stream_clean_at_suspend:1; /* do not clean streams at suspend */ unsigned int ctl_dev_id:1; /* old control element id build behaviour */ + unsigned int eld_jack_detect:1; /* Machine jack-detection by ELD */ unsigned long power_on_acct; unsigned long power_off_acct; diff --git a/sound/hda/codecs/hdmi/hdmi.c b/sound/hda/codecs/hdmi/hdmi.c index 31c9544c79963..1f4d646724edc 100644 --- a/sound/hda/codecs/hdmi/hdmi.c +++ b/sound/hda/codecs/hdmi/hdmi.c @@ -2286,6 +2286,7 @@ EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_acomp_init, "SND_HDA_CODEC_HDMI"); enum { MODEL_GENERIC, MODEL_GF, + MODEL_LOONGSON, }; static int generichdmi_probe(struct hda_codec *codec, @@ -2303,6 +2304,11 @@ static int generichdmi_probe(struct hda_codec *codec, if (id->driver_data == MODEL_GF) codec->no_sticky_stream = 1; + if (id->driver_data == MODEL_LOONGSON) { + if (codec->bus && codec->bus->pci->revision == 0x2) + codec->eld_jack_detect = 1; /* Jack-detection by ELD */ + } + return 0; } @@ -2320,7 +2326,7 @@ static const struct hda_codec_ops generichdmi_codec_ops = { /* */ static const struct hda_device_id snd_hda_id_generichdmi[] = { - HDA_CODEC_ID_MODEL(0x00147a47, "Loongson HDMI", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x00147a47, "Loongson HDMI", MODEL_LOONGSON), HDA_CODEC_ID_MODEL(0x10951390, "SiI1390 HDMI", MODEL_GENERIC), HDA_CODEC_ID_MODEL(0x10951392, "SiI1392 HDMI", MODEL_GENERIC), HDA_CODEC_ID_MODEL(0x11069f84, "VX11 HDMI/DP", MODEL_GENERIC), diff --git a/sound/hda/common/jack.c b/sound/hda/common/jack.c index 98ba1c4d5ba4f..e0a5cc38540ba 100644 --- a/sound/hda/common/jack.c +++ b/sound/hda/common/jack.c @@ -58,6 +58,12 @@ static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid, int dev_id) AC_VERB_GET_PIN_SENSE, dev_id); if (codec->inv_jack_detect) val ^= AC_PINSENSE_PRESENCE; + if (codec->eld_jack_detect) { + if (val & AC_PINSENSE_ELDV) + val |= AC_PINSENSE_PRESENCE; + else + val &= ~AC_PINSENSE_PRESENCE; + } return val; } From 17f31b904e8c32bfb90c541ec7129b4ddb0b796f Mon Sep 17 00:00:00 2001 From: Aaron Erhardt Date: Tue, 19 May 2026 17:49:48 +0200 Subject: [PATCH 0243/2758] ALSA: hda/realtek: Fix noisy mic for Clevo V6xxAW commit 493765b8e922a506e09e22e80b6cc9ff05e8295b upstream. Add a PCI quirk to reduce the volume of the internal microphone to prevent extremely noisy signal. Signed-off-by: Aaron Erhardt Signed-off-by: Werner Sembach Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260519155047.106096-1-wse@tuxedocomputers.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index dcbc669842e05..1f6fdbb45fc75 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7567,6 +7567,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1558, 0x51b3, "Clevo NS70AU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x5630, "Clevo NP50RNJS", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x5700, "Clevo X560WN[RST]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x6480, "Clevo V6xxAW", ALC245_FIXUP_CLEVO_NOISY_MIC), SND_PCI_QUIRK(0x1558, 0x70a1, "Clevo NB70T[HJK]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x70b3, "Clevo NK70SB", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x70f2, "Clevo NH79EPY", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), From 38a7cc46370a57122fb29c4bfe48a851c0c64459 Mon Sep 17 00:00:00 2001 From: Zhao Dongdong Date: Wed, 27 May 2026 20:09:11 +0800 Subject: [PATCH 0244/2758] ALSA: ice1712: check snd_ctl_new1() return value commit 2b929b91b0f3bc6de8a844370049cd99ee8e31ff upstream. snd_ctl_new1() can return NULL when memory allocation fails. The ice1712 driver calls snd_ctl_new1() without checking the return value before dereferencing the pointer in multiple places (ice1712.c, ice1724.c, aureon.c), which can lead to NULL pointer dereferences. Add NULL checks after snd_ctl_new1() calls and return -ENOMEM if any fails. Assisted-by: Opencode:DeepSeek-V4-Flash Cc: stable@vger.kernel.org Fixes: b9a4efd61b6b ("ALSA: ice1712,ice1724: fix the kcontrol->id initialization") Signed-off-by: Zhao Dongdong Link: https://patch.msgid.link/tencent_42E5E2AB1B6A5101F7EE8C2117F1F687BB07@qq.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/ice1712/aureon.c | 2 ++ sound/pci/ice1712/ice1712.c | 8 ++++++++ sound/pci/ice1712/ice1724.c | 6 ++++++ 3 files changed, 16 insertions(+) diff --git a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c index 1191a2686dfd2..d6abff2978f3d 100644 --- a/sound/pci/ice1712/aureon.c +++ b/sound/pci/ice1712/aureon.c @@ -1891,6 +1891,8 @@ static int aureon_add_controls(struct snd_ice1712 *ice) for (i = 0; i < ARRAY_SIZE(cs8415_controls); i++) { struct snd_kcontrol *kctl; kctl = snd_ctl_new1(&cs8415_controls[i], ice); + if (!kctl) + return -ENOMEM; if (i > 1) kctl->id.device = ice->pcm->device; err = snd_ctl_add(ice->card, kctl); diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index 1e39b985bef26..4cec56769c0f6 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -2346,21 +2346,29 @@ int snd_ice1712_spdif_build_controls(struct snd_ice1712 *ice) if (snd_BUG_ON(!ice->pcm_pro)) return -EIO; kctl = snd_ctl_new1(&snd_ice1712_spdif_default, ice); + if (!kctl) + return -ENOMEM; kctl->id.device = ice->pcm_pro->device; err = snd_ctl_add(ice->card, kctl); if (err < 0) return err; kctl = snd_ctl_new1(&snd_ice1712_spdif_maskc, ice); + if (!kctl) + return -ENOMEM; kctl->id.device = ice->pcm_pro->device; err = snd_ctl_add(ice->card, kctl); if (err < 0) return err; kctl = snd_ctl_new1(&snd_ice1712_spdif_maskp, ice); + if (!kctl) + return -ENOMEM; kctl->id.device = ice->pcm_pro->device; err = snd_ctl_add(ice->card, kctl); if (err < 0) return err; kctl = snd_ctl_new1(&snd_ice1712_spdif_stream, ice); + if (!kctl) + return -ENOMEM; kctl->id.device = ice->pcm_pro->device; err = snd_ctl_add(ice->card, kctl); if (err < 0) diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index 65bf48647d089..b16c84983b81a 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -2379,16 +2379,22 @@ static int snd_vt1724_spdif_build_controls(struct snd_ice1712 *ice) return err; kctl = snd_ctl_new1(&snd_vt1724_spdif_default, ice); + if (!kctl) + return -ENOMEM; kctl->id.device = ice->pcm->device; err = snd_ctl_add(ice->card, kctl); if (err < 0) return err; kctl = snd_ctl_new1(&snd_vt1724_spdif_maskc, ice); + if (!kctl) + return -ENOMEM; kctl->id.device = ice->pcm->device; err = snd_ctl_add(ice->card, kctl); if (err < 0) return err; kctl = snd_ctl_new1(&snd_vt1724_spdif_maskp, ice); + if (!kctl) + return -ENOMEM; kctl->id.device = ice->pcm->device; err = snd_ctl_add(ice->card, kctl); if (err < 0) From 6ded42615fa1f4949925afd0a8a9e1ab3bf96202 Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Wed, 24 Jun 2026 08:38:40 +0900 Subject: [PATCH 0245/2758] ALSA: seq: Fix uninitialised heap leak in snd_seq_event_dup() commit 435990e25bf1f4af3e6df12a6fbfd1f7ba4a97d4 upstream. snd_seq_event_dup() copies an incoming event into a pool cell and, in the UMP-enabled build, clears the trailing cell->ump.raw.extra word that the memcpy() did not cover. The guard deciding whether to clear it compares the copied size against sizeof(cell->event): memcpy(&cell->ump, event, size); if (size < sizeof(cell->event)) cell->ump.raw.extra = 0; For a legacy (non-UMP) event, size == sizeof(struct snd_seq_event) == sizeof(cell->event), so the condition is false and the extra word keeps stale data. The cell pool is allocated with kvmalloc() (not zeroed) and cells are reused via a free list, so that word holds uninitialised heap or leftover event data. When such a cell is delivered to a UMP client (client->midi_version > 0) that set SNDRV_SEQ_FILTER_NO_CONVERT -- so the legacy event reaches it unconverted -- snd_seq_read() reads it out as the larger struct snd_seq_ump_event and copies the stale word to user space, a 4-byte kernel heap infoleak to an unprivileged /dev/snd/seq client. Compare against sizeof(cell->ump) instead, so the trailing word is zeroed for every event shorter than the UMP cell. Fixes: 46397622a3fa ("ALSA: seq: Add UMP support") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: HyeongJun An Link: https://patch.msgid.link/20260623233841.853326-1-sammiee5311@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/seq/seq_memory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c index aaf808316c30d..670f6599e002d 100644 --- a/sound/core/seq/seq_memory.c +++ b/sound/core/seq/seq_memory.c @@ -364,7 +364,7 @@ int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event, size = snd_seq_event_packet_size(event); memcpy(&cell->ump, event, size); #if IS_ENABLED(CONFIG_SND_SEQ_UMP) - if (size < sizeof(cell->event)) + if (size < sizeof(cell->ump)) cell->ump.raw.extra = 0; #endif From ab1db64912428cdf06a4f9542e16e0575e9ad59f Mon Sep 17 00:00:00 2001 From: WenTao Liang Date: Sat, 27 Jun 2026 12:29:49 +0800 Subject: [PATCH 0246/2758] ALSA: us144mkii: capture_urb_complete: redundant usb_anchor_urb corrupts anchor list on each resubmission commit 5cff1529a2f9b3461a7f5a6e36a86682fc290534 upstream. In capture_urb_complete(), usb_anchor_urb() is called on every completion callback, but the URB is already anchored from the initial submission in tascam_trigger_start(). Each redundant call corrupts the anchor's doubly-linked list and inflates the URB refcount. When usb_kill_anchored_urbs() traverses the list during stream stop / suspend / disconnect, the corrupted list leads to use-after-free. Remove the redundant usb_anchor_urb() from the resubmit path. Cc: stable@vger.kernel.org Fixes: c1bb0c13e430 ("ALSA: usb-audio: us144mkii: Implement audio capture and decoding") Signed-off-by: WenTao Liang Link: https://patch.msgid.link/20260627042949.61767-1-vulab@iscas.ac.cn Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/usx2y/us144mkii_capture.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/usb/usx2y/us144mkii_capture.c b/sound/usb/usx2y/us144mkii_capture.c index af120bf621733..fa01da98151aa 100644 --- a/sound/usb/usx2y/us144mkii_capture.c +++ b/sound/usb/usx2y/us144mkii_capture.c @@ -302,7 +302,6 @@ void capture_urb_complete(struct urb *urb) } usb_get_urb(urb); - usb_anchor_urb(urb, &tascam->capture_anchor); ret = usb_submit_urb(urb, GFP_ATOMIC); if (ret < 0) { dev_err_ratelimited(tascam->card->dev, @@ -312,6 +311,7 @@ void capture_urb_complete(struct urb *urb) usb_put_urb(urb); atomic_dec( &tascam->active_urbs); /* Decrement on failed resubmission */ + return; } out: usb_put_urb(urb); From 344b64d4d411f5605867d988a076757bdabd7b46 Mon Sep 17 00:00:00 2001 From: Gordon Chen Date: Tue, 26 May 2026 15:29:06 +0800 Subject: [PATCH 0247/2758] ALSA: usb-audio: add IFB_SILENCE_ON_EMPTY quirk for Behringer Flow 8 commit a23812004228d4b041a858b927db787a7ff80f50 upstream. The Behringer Flow 8 (1397:050c) is an 8-channel USB mixer that declares OUT EP 0x01 with implicit feedback from capture EP 0x81 via its UAC2 endpoint companion descriptor. After 5-35 minutes of continuous playback, the device occasionally returns a capture URB in which every iso_frame_desc has a non-zero status (-EXDEV bursts, visible as rate-limited "frame N active: -18" lines in dmesg from pcm.c). In that case snd_usb_handle_sync_urb() at endpoint.c counts bytes==0 and falls into the early "skip empty packets" return originally added for M-Audio Fast Track Ultra. As a result the playback EP loses its sole IFB-driven feeder and the OUT ring starves permanently: hw_ptr stops advancing while substream state remains RUNNING. Only USB re-enumeration recovers. Three independent ftrace captures (taken at the moment of stall via a userspace watchdog) consistently show: - 60-70 capture URB completions in the 70ms window before the marker - 0 retire_playback_urb / queue_pending_output_urbs / snd_usb_endpoint_implicit_feedback_sink calls - every usb_submit_urb in the window comes from snd_complete_urb+0x64e (capture self-resubmit), none from the queue_pending_output_urbs path Add a new opt-in quirk QUIRK_FLAG_IFB_SILENCE_ON_EMPTY: when set, the early return is skipped and we fall through to enqueue a packet_info whose packet_size[i] are all 0 (the existing loop already maps status!=0 packets to size 0). prepare_outbound_urb then emits a silence packet, the OUT ring keeps moving, and the device rides through the glitch. The default behaviour (early return) is preserved for all existing devices including M-Audio Fast Track Ultra. Only Flow 8 opts in here. Cc: stable@vger.kernel.org Signed-off-by: Gordon Chen Link: https://patch.msgid.link/20260526072906.90106-1-chengordon326@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/endpoint.c | 10 +++++++++- sound/usb/quirks.c | 3 +++ sound/usb/usbaudio.h | 8 ++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 6fbcb117555c6..24cd7692bd01c 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -1780,8 +1780,16 @@ static void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, /* * skip empty packets. At least M-Audio's Fast Track Ultra stops * streaming once it received a 0-byte OUT URB + * + * However, on devices where bytes==0 means every sync-source + * packet errored (e.g. Behringer Flow 8 returning -EXDEV bursts + * for entire capture URBs), an unconditional return starves the + * IFB-fed OUT ring permanently. Such devices set + * QUIRK_FLAG_IFB_SILENCE_ON_EMPTY to fall through and enqueue a + * packet_info with size 0 packets, so playback emits silence + * and the OUT ring keeps moving. */ - if (bytes == 0) + if (bytes == 0 && !(ep->chip->quirk_flags & QUIRK_FLAG_IFB_SILENCE_ON_EMPTY)) return; spin_lock_irqsave(&ep->lock, flags); diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index f4e885f96e838..ed66e2af95143 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2365,6 +2365,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_PLAYBACK_FIRST | QUIRK_FLAG_GENERIC_IMPLICIT_FB), DEVICE_FLG(0x1397, 0x0509, /* Behringer UMC404HD */ QUIRK_FLAG_PLAYBACK_FIRST | QUIRK_FLAG_GENERIC_IMPLICIT_FB), + DEVICE_FLG(0x1397, 0x050c, /* Behringer Flow 8 */ + QUIRK_FLAG_IFB_SILENCE_ON_EMPTY), DEVICE_FLG(0x13e5, 0x0001, /* Serato Phono */ QUIRK_FLAG_IGNORE_CTL_ERROR), DEVICE_FLG(0x152a, 0x880a, /* NeuralDSP Quad Cortex */ @@ -2606,6 +2608,7 @@ static const char *const snd_usb_audio_quirk_flag_names[] = { QUIRK_STRING_ENTRY(SKIP_IFACE_SETUP), QUIRK_STRING_ENTRY(MIXER_PLAYBACK_LINEAR_VOL), QUIRK_STRING_ENTRY(MIXER_CAPTURE_LINEAR_VOL), + QUIRK_STRING_ENTRY(IFB_SILENCE_ON_EMPTY), NULL }; diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 58fd07f8c3c97..9afcad8f143a0 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -236,6 +236,12 @@ extern bool snd_usb_skip_validation; * QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL * Similar to QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL, but for capture streams. * Overrides QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE + * QUIRK_FLAG_IFB_SILENCE_ON_EMPTY + * In implicit feedback mode, when an entire capture URB returns with + * all iso_frame_desc[i].status != 0 (bytes==0), do not silently return + * from snd_usb_handle_sync_urb. Instead fall through and enqueue a + * packet_info containing only size-0 packets, so the OUT ring keeps + * moving (emits silence). Needed by Behringer Flow 8 (1397:050c). */ enum { @@ -268,6 +274,7 @@ enum { QUIRK_TYPE_SKIP_IFACE_SETUP = 26, QUIRK_TYPE_MIXER_PLAYBACK_LINEAR_VOL = 27, QUIRK_TYPE_MIXER_CAPTURE_LINEAR_VOL = 28, + QUIRK_TYPE_IFB_SILENCE_ON_EMPTY = 29, /* Please also edit snd_usb_audio_quirk_flag_names */ }; @@ -302,5 +309,6 @@ enum { #define QUIRK_FLAG_SKIP_IFACE_SETUP QUIRK_FLAG(SKIP_IFACE_SETUP) #define QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL QUIRK_FLAG(MIXER_PLAYBACK_LINEAR_VOL) #define QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL QUIRK_FLAG(MIXER_CAPTURE_LINEAR_VOL) +#define QUIRK_FLAG_IFB_SILENCE_ON_EMPTY QUIRK_FLAG(IFB_SILENCE_ON_EMPTY) #endif /* __USBAUDIO_H */ From 4246dd043b7a4f8e3bc1d2896e81d11220610eda Mon Sep 17 00:00:00 2001 From: Darvell Long Date: Wed, 24 Jun 2026 07:37:23 -0700 Subject: [PATCH 0248/2758] ALSA: usb-audio: avoid kobject path lookup in DualSense match commit 7693c0cc415f3a16a7a3355f245474a5e661be4e upstream. The DualSense jack-detection input handler verifies that a matching input device belongs to the same physical controller by building kobject path strings for both the input device and the USB audio device, then comparing the path prefix. This was observed when a weak physical connection caused the controller to rapidly disconnect and reconnect. During that repeated hotplug, snd_dualsense_ih_match() can run while the controller's USB device is being disconnected. kobject_get_path() walks ancestor kobjects and dereferences their names; if the USB device kobject name is no longer valid, this can fault in strlen(): RIP: 0010:strlen+0x10/0x30 Call Trace: kobject_get_path+0x34/0x150 snd_dualsense_ih_match+0x49/0xd0 [snd_usb_audio] input_register_device+0x566/0x6a0 ps_probe+0xb89/0x1590 [hid_playstation] The same ownership check can be done without building kobject path strings. The input device is parented below the HID device, USB interface and USB device, so walking the input device parent chain and comparing against the mixer USB device preserves the check without dereferencing kobject names during disconnect. Fixes: 79d561c4ec04 ("ALSA: usb-audio: Add mixer quirk for Sony DualSense PS5") Cc: Assisted-by: Cute:gpt-5.5 Signed-off-by: Darvell Long Link: https://patch.msgid.link/20260624143723.2986353-1-contact@darvell.me Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/mixer_quirks.c | 40 ++++++++++++---------------------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 21ed3acba1b8e..704ff393223a7 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -567,46 +567,30 @@ static bool snd_dualsense_ih_match(struct input_handler *handler, { struct dualsense_mixer_elem_info *mei; struct usb_device *snd_dev; - char *input_dev_path, *usb_dev_path; - size_t usb_dev_path_len; - bool match = false; + struct device *parent; mei = container_of(handler, struct dualsense_mixer_elem_info, ih); snd_dev = mei->info.head.mixer->chip->dev; - input_dev_path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); - if (!input_dev_path) { - dev_warn(&snd_dev->dev, "Failed to get input dev path\n"); - return false; - } - - usb_dev_path = kobject_get_path(&snd_dev->dev.kobj, GFP_KERNEL); - if (!usb_dev_path) { - dev_warn(&snd_dev->dev, "Failed to get USB dev path\n"); - goto free_paths; - } - /* * Ensure the VID:PID matched input device supposedly owned by the * hid-playstation driver belongs to the actual hardware handled by - * the current USB audio device, which implies input_dev_path being - * a subpath of usb_dev_path. + * the current USB audio device. * * This verification is necessary when there is more than one identical * controller attached to the host system. + * + * The input device is registered below the HID device, USB interface and + * USB device, so compare the parent chain directly instead of building + * kobject path strings. This avoids dereferencing kobject names while the + * USB device hierarchy is being torn down during disconnect. */ - usb_dev_path_len = strlen(usb_dev_path); - if (usb_dev_path_len >= strlen(input_dev_path)) - goto free_paths; - - usb_dev_path[usb_dev_path_len] = '/'; - match = !memcmp(input_dev_path, usb_dev_path, usb_dev_path_len + 1); - -free_paths: - kfree(input_dev_path); - kfree(usb_dev_path); + for (parent = dev->dev.parent; parent; parent = parent->parent) { + if (parent == &snd_dev->dev) + return true; + } - return match; + return false; } static int snd_dualsense_ih_connect(struct input_handler *handler, From edf3ce5a72ca2eec7847479030cc83a4909f112a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Sun, 19 Apr 2026 17:30:30 -0300 Subject: [PATCH 0249/2758] ALSA: usb-audio: Propagate errors in scarlett_ctl_enum_put() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 0f25cf1f02e3dba626791d949c759a48c0a44996 upstream. scarlett_ctl_enum_put() ignores the return value from snd_usb_set_cur_mix_value() and reports success whenever the requested enum value differs from the current one. If the SET_CUR request fails, the callback still returns success even though neither the hardware state nor the cached mixer value changed. Fixes: 76b188c4b370 ("ALSA: usb-audio: Scarlett mixer interface for 6i6, 18i6, 18i8 and 18i20") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260419-usb-write-error-propagation-v1-2-5a3bd4a673ae@gmail.com Signed-off-by: Greg Kroah-Hartman --- sound/usb/mixer_scarlett.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/usb/mixer_scarlett.c b/sound/usb/mixer_scarlett.c index 1bb01e8276549..673eb8d8724db 100644 --- a/sound/usb/mixer_scarlett.c +++ b/sound/usb/mixer_scarlett.c @@ -680,7 +680,9 @@ static int scarlett_ctl_enum_put(struct snd_kcontrol *kctl, val = ucontrol->value.integer.value[0]; val = val + opt->start; if (val != oval) { - snd_usb_set_cur_mix_value(elem, 0, 0, val); + err = snd_usb_set_cur_mix_value(elem, 0, 0, val); + if (err < 0) + return err; return 1; } return 0; From 14dfb2abae0181db9db21ea190d0bfbe2fb0e812 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Sun, 19 Apr 2026 17:30:31 -0300 Subject: [PATCH 0250/2758] ALSA: usb-audio: Propagate US-16x08 write errors in route/mix EQ-switch put callbacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 3c06aec8abda6ba068b58a8b7119cdb2a48456b1 upstream. Several US-16x08 mixer put callbacks log failed control URBs but still return success to userspace. That hides device write failures even though the requested value was not applied. Return the negative write error instead in the route, master, bus, channel, and EQ switch put callbacks. Fixes: d2bb390a2081 ("ALSA: usb-audio: Tascam US-16x08 DSP mixer quirk") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260419-usb-write-error-propagation-v1-3-5a3bd4a673ae@gmail.com Signed-off-by: Greg Kroah-Hartman --- sound/usb/mixer_us16x08.c | 49 +++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/sound/usb/mixer_us16x08.c b/sound/usb/mixer_us16x08.c index 8a02964e5d7b6..fcf7dfa4aa845 100644 --- a/sound/usb/mixer_us16x08.c +++ b/sound/usb/mixer_us16x08.c @@ -224,14 +224,14 @@ static int snd_us16x08_route_put(struct snd_kcontrol *kcontrol, err = snd_us16x08_send_urb(chip, buf, sizeof(route_msg)); - if (err > 0) { - elem->cached |= 1 << index; - elem->cache_val[index] = val; - } else { + if (err < 0) { usb_audio_dbg(chip, "Failed to set routing, err:%d\n", err); + return err; } - return err > 0 ? 1 : 0; + elem->cached |= 1 << index; + elem->cache_val[index] = val; + return 1; } static int snd_us16x08_master_info(struct snd_kcontrol *kcontrol, @@ -283,14 +283,14 @@ static int snd_us16x08_master_put(struct snd_kcontrol *kcontrol, buf[5] = index + 1; err = snd_us16x08_send_urb(chip, buf, sizeof(mix_msg_out)); - if (err > 0) { - elem->cached |= 1 << index; - elem->cache_val[index] = val; - } else { + if (err < 0) { usb_audio_dbg(chip, "Failed to set master, err:%d\n", err); + return err; } - return err > 0 ? 1 : 0; + elem->cached |= 1 << index; + elem->cache_val[index] = val; + return 1; } static int snd_us16x08_bus_put(struct snd_kcontrol *kcontrol, @@ -324,14 +324,14 @@ static int snd_us16x08_bus_put(struct snd_kcontrol *kcontrol, break; } - if (err > 0) { - elem->cached |= 1; - elem->cache_val[0] = val; - } else { + if (err < 0) { usb_audio_dbg(chip, "Failed to set bus parameter, err:%d\n", err); + return err; } - return err > 0 ? 1 : 0; + elem->cached |= 1; + elem->cache_val[0] = val; + return 1; } static int snd_us16x08_bus_get(struct snd_kcontrol *kcontrol, @@ -392,14 +392,14 @@ static int snd_us16x08_channel_put(struct snd_kcontrol *kcontrol, err = snd_us16x08_send_urb(chip, buf, sizeof(mix_msg_in)); - if (err > 0) { - elem->cached |= 1 << index; - elem->cache_val[index] = val; - } else { + if (err < 0) { usb_audio_dbg(chip, "Failed to set channel, err:%d\n", err); + return err; } - return err > 0 ? 1 : 0; + elem->cached |= 1 << index; + elem->cache_val[index] = val; + return 1; } static int snd_us16x08_mix_info(struct snd_kcontrol *kcontrol, @@ -529,13 +529,13 @@ static int snd_us16x08_eqswitch_put(struct snd_kcontrol *kcontrol, msleep(15); } - if (err > 0) { - elem->cached |= 1 << index; - elem->cache_val[index] = val; - } else { + if (err < 0) { usb_audio_dbg(chip, "Failed to set eq switch, err:%d\n", err); + return err; } + elem->cached |= 1 << index; + elem->cache_val[index] = val; return 1; } @@ -1418,4 +1418,3 @@ int snd_us16x08_controls_create(struct usb_mixer_interface *mixer) return 0; } - From 4e01d542e9103f79fe2a9f49d8f5e120163aff0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Wed, 29 Apr 2026 10:20:01 -0300 Subject: [PATCH 0251/2758] ALSA: usb-audio: Roll back quirk control caches on write errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 6380957fa24251856a532e48a46a4dc3d1ae26b6 upstream. Several mixer quirk callbacks cache the requested control value in kcontrol->private_value before issuing a single vendor or class write. Their paired get and resume paths consume that cache directly, so a failed write currently leaves software state changed even though the update did not succeed. That can make later reads report a value the device never accepted and can replay the stale cache on resume. Restore the previous cached value on failure in the Audigy2NX LED, Emu0204 channel switch, Xonar U1 output switch, Native Instruments controls, FTU effect program switch, and Sound Blaster E1 input source switch. Fixes: 9cf3689bfe07 ("ALSA: usb-audio: Add audigy2nx resume support") Fixes: 5f503ee9e270 ("ALSA: usb-audio: Add Emu0204 channel switch resume support") Fixes: 2bfb14c3b8fb ("ALSA: usb-audio: Add Xonar U1 resume support") Fixes: da6d276957ea ("ALSA: usb-audio: Add resume support for Native Instruments controls") Fixes: 0b4e9cfcef05 ("ALSA: usb-audio: Add resume support for FTU controls") Fixes: 388fdb8f882a ("ALSA: usb-audio: Support changing input on Sound Blaster E1") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260429-alsa-usb-quirks-cache-rollback-v1-1-01b35c688b80@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/mixer_quirks.c | 45 +++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 704ff393223a7..eb63baac64ed9 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -333,6 +333,7 @@ static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, int index = kcontrol->private_value & 0xff; unsigned int value = ucontrol->value.integer.value[0]; int old_value = kcontrol->private_value >> 8; + unsigned long old_pval = kcontrol->private_value; int err; if (value > 1) @@ -341,7 +342,11 @@ static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, return 0; kcontrol->private_value = (value << 8) | index; err = snd_audigy2nx_led_update(mixer, value, index); - return err < 0 ? err : 1; + if (err < 0) { + kcontrol->private_value = old_pval; + return err; + } + return 1; } static int snd_audigy2nx_led_resume(struct usb_mixer_elem_list *list) @@ -487,6 +492,7 @@ static int snd_emu0204_ch_switch_put(struct snd_kcontrol *kcontrol, struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); struct usb_mixer_interface *mixer = list->mixer; unsigned int value = ucontrol->value.enumerated.item[0]; + unsigned long old_pval = kcontrol->private_value; int err; if (value > 1) @@ -497,7 +503,11 @@ static int snd_emu0204_ch_switch_put(struct snd_kcontrol *kcontrol, kcontrol->private_value = value; err = snd_emu0204_ch_switch_update(mixer, value); - return err < 0 ? err : 1; + if (err < 0) { + kcontrol->private_value = old_pval; + return err; + } + return 1; } static int snd_emu0204_ch_switch_resume(struct usb_mixer_elem_list *list) @@ -805,7 +815,11 @@ static int snd_xonar_u1_switch_put(struct snd_kcontrol *kcontrol, kcontrol->private_value = new_status; err = snd_xonar_u1_switch_update(list->mixer, new_status); - return err < 0 ? err : 1; + if (err < 0) { + kcontrol->private_value = old_status; + return err; + } + return 1; } static int snd_xonar_u1_switch_resume(struct usb_mixer_elem_list *list) @@ -1143,7 +1157,8 @@ static int snd_nativeinstruments_control_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); - u8 oldval = (kcontrol->private_value >> 24) & 0xff; + unsigned long old_pval = kcontrol->private_value; + u8 oldval = (old_pval >> 24) & 0xff; u8 newval = ucontrol->value.integer.value[0]; int err; @@ -1153,7 +1168,11 @@ static int snd_nativeinstruments_control_put(struct snd_kcontrol *kcontrol, kcontrol->private_value &= ~(0xff << 24); kcontrol->private_value |= (unsigned int)newval << 24; err = snd_ni_update_cur_val(list); - return err < 0 ? err : 1; + if (err < 0) { + kcontrol->private_value = old_pval; + return err; + } + return 1; } static const struct snd_kcontrol_new snd_nativeinstruments_ta6_mixers[] = { @@ -1308,7 +1327,8 @@ static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl); - unsigned int pval = list->kctl->private_value; + unsigned long old_pval = list->kctl->private_value; + unsigned int pval = old_pval; int cur_val, err, new_val; cur_val = pval >> 24; @@ -1319,7 +1339,11 @@ static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl, kctl->private_value &= ~(0xff << 24); kctl->private_value |= new_val << 24; err = snd_ftu_eff_switch_update(list); - return err < 0 ? err : 1; + if (err < 0) { + kctl->private_value = old_pval; + return err; + } + return 1; } static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer, @@ -2098,13 +2122,18 @@ static int snd_soundblaster_e1_switch_put(struct snd_kcontrol *kcontrol, { struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); unsigned char value = !!ucontrol->value.integer.value[0]; + unsigned long old_pval = kcontrol->private_value; int err; if (kcontrol->private_value == value) return 0; kcontrol->private_value = value; err = snd_soundblaster_e1_switch_update(list->mixer, value); - return err < 0 ? err : 1; + if (err < 0) { + kcontrol->private_value = old_pval; + return err; + } + return 1; } static int snd_soundblaster_e1_switch_resume(struct usb_mixer_elem_list *list) From 3314c5af8a13f81ca61bb0bccec3ff7d19154282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Wed, 29 Apr 2026 10:20:02 -0300 Subject: [PATCH 0252/2758] ALSA: usb-audio: Update Babyface Pro control caches only after successful writes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit d8f802ccf1fdbeb89d62748d6a0d0fbd442c8127 upstream. snd_bbfpro_ctl_put() and snd_bbfpro_vol_put() cache the requested packed control state in kcontrol->private_value before issuing the USB write. Their get and resume paths use that cached value directly, so a failed write can leave the driver reporting and later replaying a setting the hardware never accepted. Update the cached state only after a successful USB write. Fixes: 3e8f3bd04716 ("ALSA: usb-audio: RME Babyface Pro mixer patch") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260429-alsa-usb-quirks-cache-rollback-v1-2-01b35c688b80@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/mixer_quirks.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index eb63baac64ed9..06d111e687ebb 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -3011,12 +3011,14 @@ static int snd_bbfpro_ctl_put(struct snd_kcontrol *kcontrol, if (val == old_value) return 0; + err = snd_bbfpro_ctl_update(mixer, reg, idx, val); + if (err < 0) + return err; + kcontrol->private_value = reg | ((idx & SND_BBFPRO_CTL_IDX_MASK) << SND_BBFPRO_CTL_IDX_SHIFT) | ((val & SND_BBFPRO_CTL_VAL_MASK) << SND_BBFPRO_CTL_VAL_SHIFT); - - err = snd_bbfpro_ctl_update(mixer, reg, idx, val); - return err < 0 ? err : 1; + return 1; } static int snd_bbfpro_ctl_resume(struct usb_mixer_elem_list *list) @@ -3201,11 +3203,13 @@ static int snd_bbfpro_vol_put(struct snd_kcontrol *kcontrol, new_val = uvalue & SND_BBFPRO_MIXER_VAL_MASK; + err = snd_bbfpro_vol_update(mixer, idx, new_val); + if (err < 0) + return err; + kcontrol->private_value = idx | (new_val << SND_BBFPRO_MIXER_VAL_SHIFT); - - err = snd_bbfpro_vol_update(mixer, idx, new_val); - return err < 0 ? err : 1; + return 1; } static int snd_bbfpro_vol_resume(struct usb_mixer_elem_list *list) From c4fe3d9551eaf92ce0950039fa7f6de347794253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Sun, 19 Apr 2026 17:30:32 -0300 Subject: [PATCH 0253/2758] ALSA: usb-audio: Update US-16x08 EQ/comp shadow state after successful writes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit a440c17869ecd71da0f295b62868fc742d09a8ba upstream. snd_us16x08_comp_put() and snd_us16x08_eq_put() update their software stores before sending the USB write. If the transfer fails, later get callbacks report a value the hardware never accepted. Build the outgoing message from the current store plus the pending value, then commit the store only after a successful write. Fixes: d2bb390a2081 ("ALSA: usb-audio: Tascam US-16x08 DSP mixer quirk") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260419-usb-write-error-propagation-v1-4-5a3bd4a673ae@gmail.com Signed-off-by: Greg Kroah-Hartman --- sound/usb/mixer_us16x08.c | 78 ++++++++++++++++++++++++++------------- 1 file changed, 52 insertions(+), 26 deletions(-) diff --git a/sound/usb/mixer_us16x08.c b/sound/usb/mixer_us16x08.c index fcf7dfa4aa845..ebff185cbd2c0 100644 --- a/sound/usb/mixer_us16x08.c +++ b/sound/usb/mixer_us16x08.c @@ -435,6 +435,7 @@ static int snd_us16x08_comp_put(struct snd_kcontrol *kcontrol, int index = ucontrol->id.index; char buf[sizeof(comp_msg)]; int val_idx, val; + int threshold, ratio, attack, release, gain, switch_on; int err; val = ucontrol->value.integer.value[0]; @@ -447,36 +448,61 @@ static int snd_us16x08_comp_put(struct snd_kcontrol *kcontrol, /* new control value incl. bias*/ val_idx = elem->head.id - SND_US16X08_ID_COMP_BASE; - store->val[val_idx][index] = ucontrol->value.integer.value[0]; + threshold = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_THRESHOLD)] + [index]; + ratio = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_RATIO)][index]; + attack = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_ATTACK)][index]; + release = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_RELEASE)] + [index]; + gain = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_GAIN)][index]; + switch_on = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_SWITCH)] + [index]; + + switch (val_idx) { + case COMP_STORE_IDX(SND_US16X08_ID_COMP_THRESHOLD): + threshold = val; + break; + case COMP_STORE_IDX(SND_US16X08_ID_COMP_RATIO): + ratio = val; + break; + case COMP_STORE_IDX(SND_US16X08_ID_COMP_ATTACK): + attack = val; + break; + case COMP_STORE_IDX(SND_US16X08_ID_COMP_RELEASE): + release = val; + break; + case COMP_STORE_IDX(SND_US16X08_ID_COMP_GAIN): + gain = val; + break; + case COMP_STORE_IDX(SND_US16X08_ID_COMP_SWITCH): + switch_on = val; + break; + } /* prepare compressor URB message from template */ memcpy(buf, comp_msg, sizeof(comp_msg)); /* place comp values in message buffer watch bias! */ - buf[8] = store->val[ - COMP_STORE_IDX(SND_US16X08_ID_COMP_THRESHOLD)][index] - - SND_US16X08_COMP_THRESHOLD_BIAS; - buf[11] = ratio_map[store->val[ - COMP_STORE_IDX(SND_US16X08_ID_COMP_RATIO)][index]]; - buf[14] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_ATTACK)][index] - + SND_US16X08_COMP_ATTACK_BIAS; - buf[17] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_RELEASE)][index] - + SND_US16X08_COMP_RELEASE_BIAS; - buf[20] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_GAIN)][index]; - buf[26] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_SWITCH)][index]; + buf[8] = threshold - SND_US16X08_COMP_THRESHOLD_BIAS; + buf[11] = ratio_map[ratio]; + buf[14] = attack + SND_US16X08_COMP_ATTACK_BIAS; + buf[17] = release + SND_US16X08_COMP_RELEASE_BIAS; + buf[20] = gain; + buf[26] = switch_on; /* place channel selector in message buffer */ buf[5] = index + 1; err = snd_us16x08_send_urb(chip, buf, sizeof(comp_msg)); - if (err > 0) { - elem->cached |= 1 << index; - elem->cache_val[index] = val; - } else { + if (err < 0) { usb_audio_dbg(chip, "Failed to set compressor, err:%d\n", err); + return err; } + store->val[val_idx][index] = val; + elem->cached |= 1 << index; + elem->cache_val[index] = val; return 1; } @@ -578,11 +604,10 @@ static int snd_us16x08_eq_put(struct snd_kcontrol *kcontrol, /* copy URB buffer from EQ template */ memcpy(buf, eqs_msq, sizeof(eqs_msq)); - store->val[b_idx][p_idx][index] = val; - buf[20] = store->val[b_idx][3][index]; - buf[17] = store->val[b_idx][2][index]; - buf[14] = store->val[b_idx][1][index]; - buf[11] = store->val[b_idx][0][index]; + buf[20] = p_idx == 3 ? val : store->val[b_idx][3][index]; + buf[17] = p_idx == 2 ? val : store->val[b_idx][2][index]; + buf[14] = p_idx == 1 ? val : store->val[b_idx][1][index]; + buf[11] = p_idx == 0 ? val : store->val[b_idx][0][index]; /* place channel index in URB buffer */ buf[5] = index + 1; @@ -592,14 +617,15 @@ static int snd_us16x08_eq_put(struct snd_kcontrol *kcontrol, err = snd_us16x08_send_urb(chip, buf, sizeof(eqs_msq)); - if (err > 0) { - /* store new value in EQ band cache */ - elem->cached |= 1 << index; - elem->cache_val[index] = val; - } else { + if (err < 0) { usb_audio_dbg(chip, "Failed to set eq param, err:%d\n", err); + return err; } + store->val[b_idx][p_idx][index] = val; + /* store new value in EQ band cache */ + elem->cached |= 1 << index; + elem->cache_val[index] = val; return 1; } From 58c5ec23b1a238eb75cb0aba6f69d8f9e68ef0b2 Mon Sep 17 00:00:00 2001 From: Reinette Chatre Date: Tue, 9 Jun 2026 14:02:27 -0700 Subject: [PATCH 0254/2758] x86,fs/resctrl: Prevent out-of-bounds access while offlining CPU when SNC enabled commit fc16126cc11d9f507130bf84ab137ee0938c900e upstream. The architecture updates the cpu_mask in a domain's header to track which online CPUs are associated with the domain. When this mask becomes empty the architecture initiates offline of the domain that includes calling on resctrl fs to offline the domain. If it is a monitoring domain in which LLC occupancy is tracked resctrl fs forces the limbo handler to clear all busy RMID state associated with the domain. The limbo handler always reads the current event value associated with a busy RMID irrespective of it being checked as part of regular "is it still busy" check or whether it will be forced released anyway. When reading an RMID on a system with SNC enabled the "logical RMID" is converted to the "physical RMID" and this conversion requires the NUMA node ID of the resctrl monitoring domain that is in turn determined by querying the NUMA node ID of any CPU belonging to the monitoring domain. When the monitoring domain is going offline its cpu_mask is empty causing the NUMA node ID query via cpu_to_node() to be done with "nr_cpu_ids" as argument resulting in an out-of-bounds access. Refactor the limbo handler to skip reading the RMID when the RMID will just be forced to no longer be dirty in the domain anyway. Add a safety check to the architecture's RMID reader to protect against this scenario. Fixes: e13db55b5a0d ("x86/resctrl: Introduce snc_nodes_per_l3_cache") Closes: https://sashiko.dev/#/patchset/cover.1780456704.git.reinette.chatre%40intel.com?part=9 Reported-by: Sashiko Signed-off-by: Reinette Chatre Signed-off-by: Borislav Petkov (AMD) Cc: Link: https://patch.msgid.link/16137433df42f85013b2f7a53626795cbd6637b9.1781029125.git.reinette.chatre@intel.com Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/cpu/resctrl/monitor.c | 5 ++++ fs/resctrl/monitor.c | 39 +++++++++++++++------------ 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/arch/x86/kernel/cpu/resctrl/monitor.c b/arch/x86/kernel/cpu/resctrl/monitor.c index 59215fef3924c..50ee1f640c68d 100644 --- a/arch/x86/kernel/cpu/resctrl/monitor.c +++ b/arch/x86/kernel/cpu/resctrl/monitor.c @@ -258,6 +258,11 @@ int resctrl_arch_rmid_read(struct rdt_resource *r, struct rdt_domain_hdr *hdr, if (!domain_header_is_valid(hdr, RESCTRL_MON_DOMAIN, RDT_RESOURCE_L3)) return -EINVAL; + if (cpumask_empty(&hdr->cpu_mask)) { + pr_warn_once("Domain %d has no CPUs\n", hdr->id); + return -EINVAL; + } + d = container_of(hdr, struct rdt_l3_mon_domain, hdr); hw_dom = resctrl_to_arch_mon_dom(d); cpu = cpumask_any(&hdr->cpu_mask); diff --git a/fs/resctrl/monitor.c b/fs/resctrl/monitor.c index 9fd901c78dc66..3d869fc35e837 100644 --- a/fs/resctrl/monitor.c +++ b/fs/resctrl/monitor.c @@ -135,10 +135,10 @@ void __check_limbo(struct rdt_l3_mon_domain *d, bool force_free) struct rdt_resource *r = resctrl_arch_get_resource(RDT_RESOURCE_L3); u32 idx_limit = resctrl_arch_system_num_rmid_idx(); struct rmid_entry *entry; + bool rmid_dirty = true; u32 idx, cur_idx = 1; void *arch_mon_ctx; void *arch_priv; - bool rmid_dirty; u64 val = 0; arch_priv = mon_event_all[QOS_L3_OCCUP_EVENT_ID].arch_priv; @@ -161,22 +161,27 @@ void __check_limbo(struct rdt_l3_mon_domain *d, bool force_free) break; entry = __rmid_entry(idx); - if (resctrl_arch_rmid_read(r, &d->hdr, entry->closid, entry->rmid, - QOS_L3_OCCUP_EVENT_ID, arch_priv, &val, - arch_mon_ctx)) { - rmid_dirty = true; - } else { - rmid_dirty = (val >= resctrl_rmid_realloc_threshold); - - /* - * x86's CLOSID and RMID are independent numbers, so the entry's - * CLOSID is an empty CLOSID (X86_RESCTRL_EMPTY_CLOSID). On Arm the - * RMID (PMG) extends the CLOSID (PARTID) space with bits that aren't - * used to select the configuration. It is thus necessary to track both - * CLOSID and RMID because there may be dependencies between them - * on some architectures. - */ - trace_mon_llc_occupancy_limbo(entry->closid, entry->rmid, d->hdr.id, val); + if (!force_free) { + if (resctrl_arch_rmid_read(r, &d->hdr, entry->closid, + entry->rmid, QOS_L3_OCCUP_EVENT_ID, + arch_priv, &val, arch_mon_ctx)) { + rmid_dirty = true; + } else { + rmid_dirty = (val >= resctrl_rmid_realloc_threshold); + + /* + * x86's CLOSID and RMID are independent numbers, + * so the entry's CLOSID is an empty CLOSID + * (X86_RESCTRL_EMPTY_CLOSID). On Arm the RMID + * (PMG) extends the CLOSID (PARTID) space with + * bits that aren't used to select the configuration. + * It is thus necessary to track both CLOSID and + * RMID because there may be dependencies between + * them on some architectures. + */ + trace_mon_llc_occupancy_limbo(entry->closid, entry->rmid, + d->hdr.id, val); + } } if (force_free || !rmid_dirty) { From 2bdb4c96287dee3a41a93de3cda401109eb5a08a Mon Sep 17 00:00:00 2001 From: Raghavendra Rao Ananta Date: Thu, 14 May 2026 17:34:49 +0000 Subject: [PATCH 0255/2758] vfio/pci: Use a private flag to prevent power state change with VFs commit 40ef3edf151e184d021917a5c4c771cc0870844a upstream. The current implementation uses pci_num_vf() while holding the memory_lock to prevent changing the power state of a PF when VFs are enabled. This creates a lockdep circular dependency warning because memory_lock is held during device probing. [ 286.997167] ====================================================== [ 287.003363] WARNING: possible circular locking dependency detected [ 287.009562] 7.0.0-dbg-DEV #3 Tainted: G S [ 287.015074] ------------------------------------------------------ [ 287.021270] vfio_pci_sriov_/18636 is trying to acquire lock: [ 287.026942] ff45bea2294d4968 (&vdev->memory_lock){+.+.}-{4:4}, at: vfio_pci_core_runtime_resume+0x1f/0xa0 [ 287.036530] [ 287.036530] but task is already holding lock: [ 287.042383] ff45bea3a96b8230 (&new_dev_set->lock){+.+.}-{4:4}, at: vfio_group_fops_unl_ioctl+0x44d/0x7b0 [ 287.051879] [ 287.051879] which lock already depends on the new lock. [ 287.051879] [ 287.060070] [ 287.060070] the existing dependency chain (in reverse order) is: [ 287.067568] [ 287.067568] -> #2 (&new_dev_set->lock){+.+.}-{4:4}: [ 287.073941] __mutex_lock+0x92/0xb80 [ 287.078058] vfio_assign_device_set+0x66/0x1b0 [ 287.083042] vfio_pci_core_register_device+0xd1/0x2a0 [ 287.088638] vfio_pci_probe+0xd2/0x100 [ 287.092933] local_pci_probe_callback+0x4d/0xa0 [ 287.098001] process_scheduled_works+0x2ca/0x680 [ 287.103158] worker_thread+0x1e8/0x2f0 [ 287.107452] kthread+0x10c/0x140 [ 287.111230] ret_from_fork+0x18e/0x360 [ 287.115519] ret_from_fork_asm+0x1a/0x30 [ 287.119983] [ 287.119983] -> #1 ((work_completion)(&arg.work)){+.+.}-{0:0}: [ 287.127219] __flush_work+0x345/0x490 [ 287.131429] pci_device_probe+0x2e3/0x490 [ 287.135979] really_probe+0x1f9/0x4e0 [ 287.140180] __driver_probe_device+0x77/0x100 [ 287.145079] driver_probe_device+0x1e/0x110 [ 287.149803] __device_attach_driver+0xe3/0x170 [ 287.154789] bus_for_each_drv+0x125/0x150 [ 287.159346] __device_attach+0xca/0x1a0 [ 287.163720] device_initial_probe+0x34/0x50 [ 287.168445] pci_bus_add_device+0x6e/0x90 [ 287.172995] pci_iov_add_virtfn+0x3c9/0x3e0 [ 287.177719] sriov_add_vfs+0x2c/0x60 [ 287.181838] sriov_enable+0x306/0x4a0 [ 287.186038] vfio_pci_core_sriov_configure+0x184/0x220 [ 287.191715] sriov_numvfs_store+0xd9/0x1c0 [ 287.196351] kernfs_fop_write_iter+0x13f/0x1d0 [ 287.201338] vfs_write+0x2be/0x3b0 [ 287.205286] ksys_write+0x73/0x100 [ 287.209233] do_syscall_64+0x14d/0x750 [ 287.213529] entry_SYSCALL_64_after_hwframe+0x77/0x7f [ 287.219120] [ 287.219120] -> #0 (&vdev->memory_lock){+.+.}-{4:4}: [ 287.225491] __lock_acquire+0x14c6/0x2800 [ 287.230048] lock_acquire+0xd3/0x2f0 [ 287.234168] down_write+0x3a/0xc0 [ 287.238019] vfio_pci_core_runtime_resume+0x1f/0xa0 [ 287.243436] __rpm_callback+0x8c/0x310 [ 287.247730] rpm_resume+0x529/0x6f0 [ 287.251765] __pm_runtime_resume+0x68/0x90 [ 287.256402] vfio_pci_core_enable+0x44/0x310 [ 287.261216] vfio_pci_open_device+0x1c/0x80 [ 287.265947] vfio_df_open+0x10f/0x150 [ 287.270148] vfio_group_fops_unl_ioctl+0x4a4/0x7b0 [ 287.275476] __se_sys_ioctl+0x71/0xc0 [ 287.279679] do_syscall_64+0x14d/0x750 [ 287.283975] entry_SYSCALL_64_after_hwframe+0x77/0x7f [ 287.289559] [ 287.289559] other info that might help us debug this: [ 287.289559] [ 287.297582] Chain exists of: [ 287.297582] &vdev->memory_lock --> (work_completion)(&arg.work) --> &new_dev_set->lock [ 287.297582] [ 287.310023] Possible unsafe locking scenario: [ 287.310023] [ 287.315961] CPU0 CPU1 [ 287.320510] ---- ---- [ 287.325059] lock(&new_dev_set->lock); [ 287.328917] lock((work_completion)(&arg.work)); [ 287.336153] lock(&new_dev_set->lock); [ 287.342523] lock(&vdev->memory_lock); [ 287.346382] [ 287.346382] *** DEADLOCK *** [ 287.346382] [ 287.352315] 2 locks held by vfio_pci_sriov_/18636: [ 287.357125] #0: ff45bea208ed3e18 (&group->group_lock){+.+.}-{4:4}, at: vfio_group_fops_unl_ioctl+0x3e3/0x7b0 [ 287.367048] #1: ff45bea3a96b8230 (&new_dev_set->lock){+.+.}-{4:4}, at: vfio_group_fops_unl_ioctl+0x44d/0x7b0 [ 287.376976] [ 287.376976] stack backtrace: [ 287.381353] CPU: 191 UID: 0 PID: 18636 Comm: vfio_pci_sriov_ Tainted: G S 7.0.0-dbg-DEV #3 PREEMPTLAZY [ 287.381355] Tainted: [S]=CPU_OUT_OF_SPEC [ 287.381356] Call Trace: [ 287.381357] [ 287.381358] dump_stack_lvl+0x54/0x70 [ 287.381361] print_circular_bug+0x2e1/0x300 [ 287.381363] check_noncircular+0xf9/0x120 [ 287.381364] ? __lock_acquire+0x5b4/0x2800 [ 287.381366] __lock_acquire+0x14c6/0x2800 [ 287.381368] ? pci_mmcfg_read+0x4f/0x220 [ 287.381370] ? pci_mmcfg_write+0x57/0x220 [ 287.381371] ? lock_acquire+0xd3/0x2f0 [ 287.381373] ? pci_mmcfg_write+0x57/0x220 [ 287.381374] ? lock_release+0xef/0x360 [ 287.381376] ? vfio_pci_core_runtime_resume+0x1f/0xa0 [ 287.381377] lock_acquire+0xd3/0x2f0 [ 287.381378] ? vfio_pci_core_runtime_resume+0x1f/0xa0 [ 287.381379] ? lock_is_held_type+0x76/0x100 [ 287.381382] down_write+0x3a/0xc0 [ 287.381382] ? vfio_pci_core_runtime_resume+0x1f/0xa0 [ 287.381383] vfio_pci_core_runtime_resume+0x1f/0xa0 [ 287.381384] ? __pfx_pci_pm_runtime_resume+0x10/0x10 [ 287.381385] __rpm_callback+0x8c/0x310 [ 287.381386] ? ktime_get_mono_fast_ns+0x3d/0xb0 [ 287.381389] ? __pfx_pci_pm_runtime_resume+0x10/0x10 [ 287.381390] rpm_resume+0x529/0x6f0 [ 287.381392] ? lock_is_held_type+0x76/0x100 [ 287.381394] __pm_runtime_resume+0x68/0x90 [ 287.381396] vfio_pci_core_enable+0x44/0x310 [ 287.381398] vfio_pci_open_device+0x1c/0x80 [ 287.381399] vfio_df_open+0x10f/0x150 [ 287.381401] vfio_group_fops_unl_ioctl+0x4a4/0x7b0 [ 287.381402] __se_sys_ioctl+0x71/0xc0 [ 287.381404] do_syscall_64+0x14d/0x750 [ 287.381405] ? entry_SYSCALL_64_after_hwframe+0x77/0x7f [ 287.381406] ? trace_irq_disable+0x25/0xd0 [ 287.381409] entry_SYSCALL_64_after_hwframe+0x77/0x7f Introduce a private flag 'sriov_active' in the vfio_pci_core_device struct. This allows the driver to track the SR-IOV power state requirement without relying on pci_num_vf() while holding the memory_lock. The lock is now only held to set the flag and ensure the device is in D0, after which pci_enable_sriov() can be called without the lock. Fixes: f4162eb1e2fc ("vfio/pci: Change the PF power state to D0 before enabling VFs") Cc: stable@vger.kernel.org Suggested-by: Jason Gunthorpe Suggested-by: Alex Williamson Signed-off-by: Raghavendra Rao Ananta Link: https://lore.kernel.org/r/20260514173449.3282188-1-rananta@google.com [promote bitfield to plain bool to avoid storage-unit races] Signed-off-by: Alex Williamson Signed-off-by: Greg Kroah-Hartman --- drivers/vfio/pci/vfio_pci_core.c | 17 ++++++++++++++--- include/linux/vfio_pci_core.h | 1 + 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c index 050e7542952ed..aefb9b085694c 100644 --- a/drivers/vfio/pci/vfio_pci_core.c +++ b/drivers/vfio/pci/vfio_pci_core.c @@ -271,8 +271,11 @@ int vfio_pci_set_power_state(struct vfio_pci_core_device *vdev, pci_power_t stat int ret; /* Prevent changing power state for PFs with VFs enabled */ - if (pci_num_vf(pdev) && state > PCI_D0) - return -EBUSY; + if (state > PCI_D0) { + lockdep_assert_held_write(&vdev->memory_lock); + if (vdev->sriov_active) + return -EBUSY; + } if (vdev->needs_pm_restore) { if (pdev->current_state < PCI_D3hot && state >= PCI_D3hot) { @@ -2327,8 +2330,9 @@ int vfio_pci_core_sriov_configure(struct vfio_pci_core_device *vdev, down_write(&vdev->memory_lock); vfio_pci_set_power_state(vdev, PCI_D0); - ret = pci_enable_sriov(pdev, nr_virtfn); + vdev->sriov_active = true; up_write(&vdev->memory_lock); + ret = pci_enable_sriov(pdev, nr_virtfn); if (ret) { pm_runtime_put(&pdev->dev); goto out_del; @@ -2342,6 +2346,13 @@ int vfio_pci_core_sriov_configure(struct vfio_pci_core_device *vdev, } out_del: + /* + * Avoid taking the memory_lock intentionally. A race with a power + * state transition would at most result in an -EBUSY, leaving the + * device in PCI_D0. + */ + vdev->sriov_active = false; + mutex_lock(&vfio_pci_sriov_pfs_mutex); list_del_init(&vdev->sriov_pfs_item); out_unlock: diff --git a/include/linux/vfio_pci_core.h b/include/linux/vfio_pci_core.h index 89165b769e5c1..41f2622106bcf 100644 --- a/include/linux/vfio_pci_core.h +++ b/include/linux/vfio_pci_core.h @@ -127,6 +127,7 @@ struct vfio_pci_core_device { bool needs_pm_restore:1; bool pm_intx_masked:1; bool pm_runtime_engaged:1; + bool sriov_active; struct pci_saved_state *pci_saved_state; struct pci_saved_state *pm_save; int ioeventfds_nr; From 062b820290bcb9778e43a73597df76e9bb08acfb Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 15 Jun 2026 13:12:29 -0600 Subject: [PATCH 0256/2758] vfio/pci: Latch disable_idle_d3 per device commit 4575e9aac5336d1365138c0284773bf8da4b1fa3 upstream. When disable_idle_d3 was introduced in vfio-pci, it directly manipulated the device power state with pci_set_power_state(). There were no refcounts to maintain or balanced operations, we could unconditionally bring the device to D0 and conditionally move it to D3hot. Therefore the module parameter was made writable. Later, in commit c61302aa48f7 ("vfio/pci: Move module parameters to vfio_pci.c"), as part of the vfio-pci-core split, the writable aspect of the module parameter was nullified. The parameter value could still be changed through sysfs, but the vfio-pci driver latched the values into vfio-pci-core globals at module init. Loading the vfio-pci module, or unloading and reloading, with non-default or different values could change the globals relative to existing devices bound to vfio-pci variant drivers. Runtime PM was introduced in commit 7ab5e10eda02 ("vfio/pci: Move the unused device into low power state with runtime PM"), which marks the point where power states became refcounted. PM get and put operations need to be balanced, but the same module operations noted above can change the global variables relative to those devices already bound to vfio-pci variant drivers. This introduces a window where PM operations can now become unbalanced. To resolve this with a narrow footprint for stable backports, the disable_idle_d3 flag is latched into the vfio_pci_core_device at the time of initialization, such that the device always operates with a consistent value. NB. vfio_pci_dev_set_try_reset() now unconditionally raises the runtime PM usage count around bus reset to account for disable_idle_d3 becoming a per-device rather than global flag. When this flag is set, the additional get/put pair is harmless and allows continued use of the shared vfio_pci_dev_set_pm_runtime_get() helper. Fixes: 7ab5e10eda02 ("vfio/pci: Move the unused device into low power state with runtime PM") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Alex Williamson Reviewed-by: Kevin Tian Link: https://lore.kernel.org/r/20260615191241.688297-2-alex.williamson@nvidia.com Signed-off-by: Alex Williamson Signed-off-by: Greg Kroah-Hartman --- drivers/vfio/pci/vfio_pci_core.c | 19 ++++++++++--------- include/linux/vfio_pci_core.h | 1 + 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c index aefb9b085694c..f0f10588c2308 100644 --- a/drivers/vfio/pci/vfio_pci_core.c +++ b/drivers/vfio/pci/vfio_pci_core.c @@ -538,7 +538,7 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev) u16 cmd; u8 msix_pos; - if (!disable_idle_d3) { + if (!vdev->disable_idle_d3) { ret = pm_runtime_resume_and_get(&pdev->dev); if (ret < 0) return ret; @@ -617,7 +617,7 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev) out_disable_device: pci_disable_device(pdev); out_power: - if (!disable_idle_d3) + if (!vdev->disable_idle_d3) pm_runtime_put(&pdev->dev); return ret; } @@ -753,7 +753,7 @@ void vfio_pci_core_disable(struct vfio_pci_core_device *vdev) vfio_pci_dev_set_try_reset(vdev->vdev.dev_set); /* Put the pm-runtime usage counter acquired during enable */ - if (!disable_idle_d3) + if (!vdev->disable_idle_d3) pm_runtime_put(&pdev->dev); } EXPORT_SYMBOL_GPL(vfio_pci_core_disable); @@ -2145,6 +2145,8 @@ int vfio_pci_core_init_dev(struct vfio_device *core_vdev) init_rwsem(&vdev->memory_lock); xa_init(&vdev->ctx); + vdev->disable_idle_d3 = disable_idle_d3; + return 0; } EXPORT_SYMBOL_GPL(vfio_pci_core_init_dev); @@ -2240,7 +2242,7 @@ int vfio_pci_core_register_device(struct vfio_pci_core_device *vdev) dev->driver->pm = &vfio_pci_core_pm_ops; pm_runtime_allow(dev); - if (!disable_idle_d3) + if (!vdev->disable_idle_d3) pm_runtime_put(dev); ret = vfio_register_group_dev(&vdev->vdev); @@ -2249,7 +2251,7 @@ int vfio_pci_core_register_device(struct vfio_pci_core_device *vdev) return 0; out_power: - if (!disable_idle_d3) + if (!vdev->disable_idle_d3) pm_runtime_get_noresume(dev); pm_runtime_forbid(dev); @@ -2268,7 +2270,7 @@ void vfio_pci_core_unregister_device(struct vfio_pci_core_device *vdev) vfio_pci_vf_uninit(vdev); vfio_pci_vga_uninit(vdev); - if (!disable_idle_d3) + if (!vdev->disable_idle_d3) pm_runtime_get_noresume(&vdev->pdev->dev); pm_runtime_forbid(&vdev->pdev->dev); @@ -2600,7 +2602,7 @@ static void vfio_pci_dev_set_try_reset(struct vfio_device_set *dev_set) * state. Increment the usage count for all the devices in the dev_set * before reset and decrement the same after reset. */ - if (!disable_idle_d3 && vfio_pci_dev_set_pm_runtime_get(dev_set)) + if (vfio_pci_dev_set_pm_runtime_get(dev_set)) return; if (!pci_reset_bus(pdev)) @@ -2610,8 +2612,7 @@ static void vfio_pci_dev_set_try_reset(struct vfio_device_set *dev_set) if (reset_done) cur->needs_reset = false; - if (!disable_idle_d3) - pm_runtime_put(&cur->pdev->dev); + pm_runtime_put(&cur->pdev->dev); } } diff --git a/include/linux/vfio_pci_core.h b/include/linux/vfio_pci_core.h index 41f2622106bcf..1a7882dc44b76 100644 --- a/include/linux/vfio_pci_core.h +++ b/include/linux/vfio_pci_core.h @@ -127,6 +127,7 @@ struct vfio_pci_core_device { bool needs_pm_restore:1; bool pm_intx_masked:1; bool pm_runtime_engaged:1; + bool disable_idle_d3:1; bool sriov_active; struct pci_saved_state *pci_saved_state; struct pci_saved_state *pm_save; From 278a5659c391fe5afe5f9ce1bad1fd24e90144f1 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 15 Jun 2026 13:12:30 -0600 Subject: [PATCH 0257/2758] vfio/pci: Release the VGA arbiter client on register_device() failure commit daedde7f024ecf88bc8e832ed40cf2c795f0796a upstream. The re-order in the Fixes commit below displaced vfio_pci_vga_init() as the last failure point of what is now vfio_pci_core_register_device() without introducing an unwind for the VGA arbiter registration. In current kernels this is mostly benign because vfio_pci_set_decode() only uses pci_dev state, but the original failure path could leave a callback with a freed vdev cookie. The stale registration also becomes unsafe again once the callback follows drvdata to the vfio device. Add the required VGA unwind callout. Fixes: 4aeec3984ddc ("vfio/pci: Re-order vfio_pci_probe()") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Alex Williamson Reviewed-by: Kevin Tian Link: https://lore.kernel.org/r/20260615191241.688297-3-alex.williamson@nvidia.com Signed-off-by: Alex Williamson Signed-off-by: Greg Kroah-Hartman --- drivers/vfio/pci/vfio_pci_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c index f0f10588c2308..beb1674c69450 100644 --- a/drivers/vfio/pci/vfio_pci_core.c +++ b/drivers/vfio/pci/vfio_pci_core.c @@ -2255,6 +2255,7 @@ int vfio_pci_core_register_device(struct vfio_pci_core_device *vdev) pm_runtime_get_noresume(dev); pm_runtime_forbid(dev); + vfio_pci_vga_uninit(vdev); out_vf: vfio_pci_vf_uninit(vdev); return ret; From ad0f12d2dfc23954fd06a386844c0611a728e096 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 15 Jun 2026 13:12:31 -0600 Subject: [PATCH 0258/2758] vfio/pci: Fix racy bitfields and tighten struct layout commit e73638e55f861758d49f14d7bb5dba3035981cd7 upstream. Bitfield operations are not atomic, they use a read-modify-write pattern, therefore we should be careful not to pack bitfields that can be concurrently updated into the same storage unit. This split takes a binary approach: flags that are only modified pre/post open/close remain bitfields, flags modified from user action, including actions that reach across to another device (ex. reset) use dedicated storage units. Note that the virq_disabled and bardirty flags are relocated to fill an existing hole in the structure. Bitfield justifications: has_dyn_msix: written only in vfio_pci_core_enable() pci_2_3: written only in vfio_pci_core_enable() reset_works: written only in vfio_pci_core_enable() extended_caps: written only in vfio_cap_len() under vfio_config_init() has_vga: written only in vfio_pci_core_enable() nointx: written only in vfio_pci_core_enable() needs_pm_restore: written only in vfio_pci_probe_power_state() disable_idle_d3: written only at .init in vfio_pci_core_init_dev() Dedicated storage units: virq_disabled: written by guest INTx command writes in vfio_basic_config_write() while the device is open bardirty: written by guest BAR writes in vfio_basic_config_write() while the device is open pm_intx_masked: written in the runtime-PM suspend path. pm_runtime_engaged: written by low-power feature entry/exit paths needs_reset: set in vfio_pci_core_disable() and cleared for devices in the set by vfio_pci_dev_set_try_reset() sriov_active: written by vfio_pci_core_sriov_configure() via sysfs sriov_numvfs while bound. Fixes: 9cd0f6d5cbb6 ("vfio/pci: Use bitfield for struct vfio_pci_core_device flags") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Alex Williamson Reviewed-by: Kevin Tian Link: https://lore.kernel.org/r/20260615191241.688297-4-alex.williamson@nvidia.com Signed-off-by: Alex Williamson Signed-off-by: Greg Kroah-Hartman --- include/linux/vfio_pci_core.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/include/linux/vfio_pci_core.h b/include/linux/vfio_pci_core.h index 1a7882dc44b76..6e21302db2f0e 100644 --- a/include/linux/vfio_pci_core.h +++ b/include/linux/vfio_pci_core.h @@ -101,6 +101,9 @@ struct vfio_pci_core_device { const struct vfio_pci_device_ops *pci_ops; void __iomem *barmap[PCI_STD_NUM_BARS]; bool bar_mmap_supported[PCI_STD_NUM_BARS]; + /* Flags modified at runtime - dedicated storage unit */ + bool virq_disabled; + bool bardirty; u8 *pci_config_map; u8 *vconfig; struct perm_bits *msi_perm; @@ -115,19 +118,19 @@ struct vfio_pci_core_device { u16 msix_size; u32 msix_offset; u32 rbar[7]; + /* Flags only modified on setup/release - bitfield ok */ bool has_dyn_msix:1; bool pci_2_3:1; - bool virq_disabled:1; bool reset_works:1; bool extended_caps:1; - bool bardirty:1; bool has_vga:1; - bool needs_reset:1; bool nointx:1; bool needs_pm_restore:1; - bool pm_intx_masked:1; - bool pm_runtime_engaged:1; bool disable_idle_d3:1; + /* Flags modified at runtime - dedicated storage unit */ + bool needs_reset; + bool pm_intx_masked; + bool pm_runtime_engaged; bool sriov_active; struct pci_saved_state *pci_saved_state; struct pci_saved_state *pm_save; From a3a8afa2f6e7f0dc266d08f02be3f3054241ba47 Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Tue, 2 Jun 2026 16:58:48 +0800 Subject: [PATCH 0259/2758] vfio: prevent infinite loop in vfio_mig_get_next_state() on blocked arc commit a26b499b757cfc8bbff1088bb1b844639e250893 upstream. vfio_mig_get_next_state() walks vfio_from_fsm_table[] one step at a time, looping to skip optional states the device does not support until *next_fsm is supported. A blocked transition is encoded as VFIO_DEVICE_STATE_ERROR, which the trailing return reports as -EINVAL. The skip loop does not account for the ERROR sentinel. state_flags_table[ERROR] is ~0U and vfio_from_fsm_table[ERROR][*] is ERROR, so once *next_fsm becomes ERROR the loop condition stays true and *next_fsm never changes. The blocked arcs STOP_COPY -> PRE_COPY and STOP_COPY -> PRE_COPY_P2P map to ERROR yet pass the support check on a precopy-capable device, causing the loop to spin forever while holding the driver state mutex. This can result in a soft lockup, and a panic with softlockup_panic set. Terminate the skip loop on the ERROR sentinel so a blocked transition falls through to the existing return and reports -EINVAL. Fixes: 4db52602a607 ("vfio: Extend the device migration protocol with PRE_COPY") Reported-by: Yuhao Jiang Cc: stable@vger.kernel.org Signed-off-by: Junrui Luo Reviewed-by: Kevin Tian Reviewed-by: Jason Gunthorpe Link: https://lore.kernel.org/r/SYBPR01MB7881290BBDE79B61AE6A017FAF122@SYBPR01MB7881.ausprd01.prod.outlook.com Signed-off-by: Alex Williamson Signed-off-by: Greg Kroah-Hartman --- drivers/vfio/vfio_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/vfio/vfio_main.c b/drivers/vfio/vfio_main.c index 6222376ab6ab5..5e0422014523e 100644 --- a/drivers/vfio/vfio_main.c +++ b/drivers/vfio/vfio_main.c @@ -858,7 +858,8 @@ int vfio_mig_get_next_state(struct vfio_device *device, * logical state, as per the above comment. */ *next_fsm = vfio_from_fsm_table[cur_fsm][new_fsm]; - while ((state_flags_table[*next_fsm] & device->migration_flags) != + while (*next_fsm != VFIO_DEVICE_STATE_ERROR && + (state_flags_table[*next_fsm] & device->migration_flags) != state_flags_table[*next_fsm]) *next_fsm = vfio_from_fsm_table[*next_fsm][new_fsm]; From a5df401dc84f091e20b045560569f0736758fea7 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 15 Jun 2026 14:47:00 -0600 Subject: [PATCH 0260/2758] vfio: Remove device debugfs before releasing devres commit dc7fe87de492ea7f33a72b78d26650b75bf37f4f upstream. VFIO device debugfs files created with debugfs_create_devm_seqfile() store a devres allocated debugfs_devm_entry as inode private data. vfio_unregister_group_dev() currently calls vfio_device_del() before vfio_device_debugfs_exit(), but device_del() releases devres. This can leave debugfs entries visible with stale inode private data while unregister waits for userspace references to drain. Remove the per-device debugfs tree before vfio_device_del(). The debugfs view is diagnostic only, so losing it at the start of unregister is preferable to preserving entries whose backing storage may already have been released. Complete the teardown by clearing the per-device debugfs root after removal. This matches the global debugfs root cleanup and prevents future users from mistaking a removed dentry for a live debugfs tree during the remainder of unregister. Fixes: 2202844e4468 ("vfio/migration: Add debugfs to live migration driver") Reported-by: Sashiko AI Review Link: https://lore.kernel.org/r/20260615192725.6A2221F000E9@smtp.kernel.org Cc: stable@vger.kernel.org Cc: Longfang Liu Assisted-by: OpenAI Codex:gpt-5 Signed-off-by: Alex Williamson Reviewed-by: Kevin Tian Link: https://lore.kernel.org/r/20260615204717.735302-1-alex.williamson@nvidia.com Signed-off-by: Alex Williamson Signed-off-by: Greg Kroah-Hartman --- drivers/vfio/debugfs.c | 1 + drivers/vfio/vfio_main.c | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/vfio/debugfs.c b/drivers/vfio/debugfs.c index 8b0ca7a090640..8a2f1b0cce3f1 100644 --- a/drivers/vfio/debugfs.c +++ b/drivers/vfio/debugfs.c @@ -97,6 +97,7 @@ void vfio_device_debugfs_init(struct vfio_device *vdev) void vfio_device_debugfs_exit(struct vfio_device *vdev) { debugfs_remove_recursive(vdev->debug_root); + vdev->debug_root = NULL; } void vfio_debugfs_create_root(void) diff --git a/drivers/vfio/vfio_main.c b/drivers/vfio/vfio_main.c index 5e0422014523e..ed538aebb0b8e 100644 --- a/drivers/vfio/vfio_main.c +++ b/drivers/vfio/vfio_main.c @@ -406,6 +406,13 @@ void vfio_unregister_group_dev(struct vfio_device *device) */ vfio_device_group_unregister(device); + /* + * Remove debugfs before device_del(), which releases devres. Some + * debugfs entries are created with debugfs_create_devm_seqfile() and + * therefore rely on devres-managed inode private data. + */ + vfio_device_debugfs_exit(device); + /* * Balances vfio_device_add() in register path, also prevents * new device opened by userspace in the cdev path. @@ -435,7 +442,6 @@ void vfio_unregister_group_dev(struct vfio_device *device) } } - vfio_device_debugfs_exit(device); /* Balances vfio_device_set_group in register path */ vfio_device_remove_group(device); } From 39d163627b51886492bf31f66cb02c94613d2287 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 15 Jun 2026 13:12:32 -0600 Subject: [PATCH 0261/2758] vfio/mlx5: Fix racy bitfields and tighten struct layout commit f2365a63b02ddea32e7db78b742c2503ec7b81f1 upstream. Bitfield operations are not atomic, they use a read-modify-write pattern, therefore we should be careful not to pack bitfields that can be concurrently updated into the same storage unit. This split takes a binary approach: flags that are only modified pre/post open/close remain bitfields, flags modified from user action, including actions that reach across to another device (ex. reset) use dedicated storage units. Note mlx5_vhca_page_tracker.status is relocated to fill the alignment hole this split exposes. Bitfield justifications: migrate_cap: written only in mlx5vf_cmd_set_migratable() at probe chunk_mode: written only in mlx5vf_cmd_set_migratable() at probe mig_state_cap: written only in mlx5vf_cmd_set_migratable() at probe Dedicated storage units: mdev_detach: written in the VF attach/detach event notifier mlx5fv_vf_event() at runtime log_active: written in mlx5vf_start_page_tracker()/ mlx5vf_stop_page_tracker() during runtime dirty tracking deferred_reset: written in mlx5vf_state_mutex_unlock()/ mlx5vf_pci_aer_reset_done() during runtime reset handling is_err: set by tracker error handling and dirty-log polling at runtime object_changed: set by tracker event handling and cleared by dirty-log polling at runtime Fixes: 61a2f1460fd0 ("vfio/mlx5: Manage the VF attach/detach callback from the PF") Fixes: 79c3cf279926 ("vfio/mlx5: Init QP based resources for dirty tracking") Fixes: f886473071d6 ("vfio/mlx5: Add support for tracker object change event") Cc: Yishai Hadas Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Alex Williamson Reviewed-by: Kevin Tian Link: https://lore.kernel.org/r/20260615191241.688297-5-alex.williamson@nvidia.com Signed-off-by: Alex Williamson Signed-off-by: Greg Kroah-Hartman --- drivers/vfio/pci/mlx5/cmd.h | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/vfio/pci/mlx5/cmd.h b/drivers/vfio/pci/mlx5/cmd.h index deed0f132f39f..c86d8b243a524 100644 --- a/drivers/vfio/pci/mlx5/cmd.h +++ b/drivers/vfio/pci/mlx5/cmd.h @@ -158,26 +158,29 @@ struct mlx5_vhca_qp { struct mlx5_vhca_page_tracker { u32 id; u32 pdn; - u8 is_err:1; - u8 object_changed:1; + /* Flags modified at runtime - dedicated storage unit */ + u8 is_err; + u8 object_changed; + int status; struct mlx5_uars_page *uar; struct mlx5_vhca_cq cq; struct mlx5_vhca_qp *host_qp; struct mlx5_vhca_qp *fw_qp; struct mlx5_nb nb; - int status; }; struct mlx5vf_pci_core_device { struct vfio_pci_core_device core_device; int vf_id; u16 vhca_id; + /* Flags only modified on setup/release - bitfield ok */ u8 migrate_cap:1; - u8 deferred_reset:1; - u8 mdev_detach:1; - u8 log_active:1; u8 chunk_mode:1; u8 mig_state_cap:1; + /* Flags modified at runtime - dedicated storage unit */ + u8 mdev_detach; + u8 log_active; + u8 deferred_reset; struct completion tracker_comp; /* protect migration state */ struct mutex state_mutex; From c028dfa0a3c79ed6db8498683b4ddab7759e111d Mon Sep 17 00:00:00 2001 From: Zenm Chen Date: Tue, 26 May 2026 00:19:42 +0800 Subject: [PATCH 0262/2758] Bluetooth: btusb: Add USB ID 2c4e:0128 for Mercusys MA60XNB commit 88b4d528eda4ac71c2952b3458f2abbc80a91cd2 upstream. Add USB ID 2c4e:0128 for Mercusys MA60XNB, an RTL8851BU-based Wi-Fi + Bluetooth adapter. The information in /sys/kernel/debug/usb/devices about the Bluetooth device is listed as the below: T: Bus=03 Lev=01 Prnt=01 Port=04 Cnt=01 Dev#= 3 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=2c4e ProdID=0128 Rev= 0.00 S: Manufacturer=Realtek S: Product=802.11ax WLAN Adapter S: SerialNumber=00e04c000001 C:* #Ifs= 3 Cfg#= 1 Atr=e0 MxPwr=500mA A: FirstIf#= 0 IfCount= 2 Cls=e0(wlcon) Sub=01 Prot=01 I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=1ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms I: If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms I: If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms I: If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms I: If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms I: If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms I: If#= 1 Alt= 6 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 63 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 63 Ivl=1ms I:* If#= 2 Alt= 0 #EPs= 8 Cls=ff(vend.) Sub=ff Prot=ff Driver=rtw89_8851bu E: Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=06(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=07(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=09(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=0a(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=0b(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=0c(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms Cc: stable@vger.kernel.org # 6.6.x Signed-off-by: Zenm Chen Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/btusb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 830fefb342c6b..17f39e2ff09f7 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -531,6 +531,8 @@ static const struct usb_device_id quirks_table[] = { BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x7392, 0xe611), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x2c4e, 0x0128), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, /* Realtek 8852AE Bluetooth devices */ { USB_DEVICE(0x0bda, 0x2852), .driver_info = BTUSB_REALTEK | From da7d7758fe884b256ddc9fef562e5ddef7952383 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 4 Jun 2026 08:37:36 +0200 Subject: [PATCH 0263/2758] Bluetooth: btusb: fix use-after-free on registration failure commit eedc6867ebad73edbfaf9a0a65fbef7115cc4753 upstream. Make sure to release the sibling interfaces in case controller registration fails to avoid use-after-free and double-free when they are eventually disconnected. This issue was reported by Sashiko while reviewing a fix for a wakeup source leak in the btusb probe errors paths. Link: https://sashiko.dev/#/patchset/20260402092704.2346710-1-johan%40kernel.org Fixes: 9bfa35fe422c ("[Bluetooth] Add SCO support to btusb driver") Fixes: 9d08f50401ac ("Bluetooth: btusb: Add support for Broadcom LM_DIAG interface") Cc: stable@vger.kernel.org # 2.6.27 Reviewed-by: Paul Menzel Signed-off-by: Johan Hovold Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/btusb.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 17f39e2ff09f7..a1250e4a126e7 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -4409,7 +4409,7 @@ static int btusb_probe(struct usb_interface *intf, err = hci_register_dev(hdev); if (err < 0) - goto out_free_dev; + goto err_release_siblings; usb_set_intfdata(intf, data); @@ -4418,6 +4418,15 @@ static int btusb_probe(struct usb_interface *intf, return 0; +err_release_siblings: + if (data->diag) { + usb_set_intfdata(data->diag, NULL); + usb_driver_release_interface(&btusb_driver, data->diag); + } + if (data->isoc) { + usb_set_intfdata(data->isoc, NULL); + usb_driver_release_interface(&btusb_driver, data->isoc); + } out_free_dev: if (data->reset_gpio) gpiod_put(data->reset_gpio); From 838c917a2f16eefe68def800ebf48a2af591149a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 4 Jun 2026 08:37:37 +0200 Subject: [PATCH 0264/2758] Bluetooth: btusb: fix use-after-free on marvell probe failure commit c5b600a3c05b1a7a110d558df935a8fc8a471c79 upstream. Make sure to stop any TX URBs submitted during Marvell OOB wakeup configuration on later probe failures to avoid use-after-free in the completion callback. This issue was reported by Sashiko while reviewing a fix for a wakeup source leak in the btusb probe errors paths. Link: https://sashiko.dev/#/patchset/20260402092704.2346710-1-johan%40kernel.org Fixes: a4ccc9e33d2f ("Bluetooth: btusb: Configure Marvell to use one of the pins for oob wakeup") Cc: stable@vger.kernel.org # 4.11 Cc: Rajat Jain Signed-off-by: Johan Hovold Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/btusb.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index a1250e4a126e7..8724701ac250d 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -4220,7 +4220,7 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_INTEL_COMBINED) { err = btintel_configure_setup(hdev, btusb_driver.name); if (err) - goto out_free_dev; + goto err_kill_tx_urbs; /* Transport specific configuration */ hdev->send = btusb_send_frame_intel; @@ -4383,7 +4383,7 @@ static int btusb_probe(struct usb_interface *intf, err = usb_set_interface(data->udev, 0, 0); if (err < 0) { BT_ERR("failed to set interface 0, alt 0 %d", err); - goto out_free_dev; + goto err_kill_tx_urbs; } } @@ -4391,7 +4391,7 @@ static int btusb_probe(struct usb_interface *intf, err = usb_driver_claim_interface(&btusb_driver, data->isoc, data); if (err < 0) - goto out_free_dev; + goto err_kill_tx_urbs; } if (IS_ENABLED(CONFIG_BT_HCIBTUSB_BCM) && data->diag) { @@ -4427,6 +4427,8 @@ static int btusb_probe(struct usb_interface *intf, usb_set_intfdata(data->isoc, NULL); usb_driver_release_interface(&btusb_driver, data->isoc); } +err_kill_tx_urbs: + usb_kill_anchored_urbs(&data->tx_anchor); out_free_dev: if (data->reset_gpio) gpiod_put(data->reset_gpio); From 087a305e025c2e11653142f64697801cf39a8d8a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 4 Jun 2026 08:37:38 +0200 Subject: [PATCH 0265/2758] Bluetooth: btusb: fix wakeup source leak on probe failure commit 3d93e1bb0fb881fe3ef961d1120556658e9cac4d upstream. Make sure to disable wakeup on probe failure to avoid leaking the wakeup source. Fixes: fd913ef7ce61 ("Bluetooth: btusb: Add out-of-band wakeup support") Cc: stable@vger.kernel.org # 4.11 Cc: Rajat Jain Signed-off-by: Johan Hovold Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/btusb.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 8724701ac250d..cc6392f8d98cd 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -2996,6 +2996,11 @@ static int marvell_config_oob_wake(struct hci_dev *hdev) return 0; } +#else +static inline int marvell_config_oob_wake(struct hci_dev *hdev) +{ + return 0; +} #endif static int btusb_set_bdaddr_marvell(struct hci_dev *hdev, @@ -3838,6 +3843,11 @@ static int btusb_config_oob_wake(struct hci_dev *hdev) bt_dev_info(hdev, "OOB Wake-on-BT configured at IRQ %u", irq); return 0; } +#else +static inline int btusb_config_oob_wake(struct hci_dev *hdev) +{ + return 0; +} #endif static void btusb_check_needs_reset_resume(struct usb_interface *intf) @@ -4174,7 +4184,6 @@ static int btusb_probe(struct usb_interface *intf, hdev->wakeup = btusb_wakeup; hdev->hci_drv = &btusb_hci_drv; -#ifdef CONFIG_PM err = btusb_config_oob_wake(hdev); if (err) goto out_free_dev; @@ -4183,9 +4192,9 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_MARVELL && data->oob_wake_irq) { err = marvell_config_oob_wake(hdev); if (err) - goto out_free_dev; + goto err_disable_wakeup; } -#endif + if (id->driver_info & BTUSB_CW6622) hci_set_quirk(hdev, HCI_QUIRK_BROKEN_STORED_LINK_KEY); @@ -4429,6 +4438,9 @@ static int btusb_probe(struct usb_interface *intf, } err_kill_tx_urbs: usb_kill_anchored_urbs(&data->tx_anchor); +err_disable_wakeup: + if (data->oob_wake_irq) + device_init_wakeup(&data->udev->dev, false); out_free_dev: if (data->reset_gpio) gpiod_put(data->reset_gpio); From ef5439ba5b9ac93349f5df12ef88b42a0ce26340 Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Fri, 19 Jun 2026 18:52:30 +0000 Subject: [PATCH 0266/2758] binder: fix UAF in binder_thread_release() commit 114a116aaa5f0295376cdf12da743c5bce3b20ce upstream. When a thread exits, binder_thread_release() walks its transaction stack to clear the t->from and t->to_proc that correspond with the exiting thread. However, a process dying in parallel might attempt to kfree some of these transactions. And if one of them has no associated t->to_proc, the t->to_proc->inner_lock will not be acquired. This means that transaction accesses in binder_thread_release() after t->to_proc has been cleared might race with binder_free_transaction() and cause a use-after-free error as reported by KASAN: ================================================================== BUG: KASAN: slab-use-after-free in binder_thread_release+0x5d0/0x798 Write of size 8 at addr ffff000016627500 by task X/715 CPU: 17 UID: 0 PID: 715 Comm: X Not tainted 7.1.0-rc5-00149-g8fde5d1d47f6 #30 PREEMPT Hardware name: linux,dummy-virt (DT) Call trace: binder_thread_release+0x5d0/0x798 binder_ioctl+0x12c0/0x299c [...] Allocated by task 717 on cpu 18 at 67.267803s: __kasan_kmalloc+0xa0/0xbc __kmalloc_cache_noprof+0x174/0x444 binder_transaction+0x554/0x8150 binder_thread_write+0xa30/0x4354 binder_ioctl+0x20f0/0x299c [...] Freed by task 202 on cpu 18 at 90.416221s: __kasan_slab_free+0x58/0x80 kfree+0x1a0/0x4a4 binder_free_transaction+0x150/0x294 binder_send_failed_reply+0x398/0x6d8 binder_release_work+0x3e4/0x4ec binder_deferred_func+0xbd8/0x104c [...] ================================================================== In order to avoid this, make sure that binder_free_transaction() reads the t->to_proc under the transaction lock. This will serialize the transaction release with the accesses in binder_thread_release(). Plus, it matches the documented locking rules for @to_proc. Cc: stable Fixes: 7a4408c6bd3e ("binder: make sure accesses to proc/thread are safe") Reviewed-by: Alice Ryhl Signed-off-by: Carlos Llamas Link: https://patch.msgid.link/20260619185233.2194678-1-cmllamas@google.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 9e61942245936..09bc052186cfa 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -1658,7 +1658,11 @@ static void binder_txn_latency_free(struct binder_transaction *t) static void binder_free_transaction(struct binder_transaction *t) { - struct binder_proc *target_proc = t->to_proc; + struct binder_proc *target_proc; + + spin_lock(&t->lock); + target_proc = t->to_proc; + spin_unlock(&t->lock); if (target_proc) { binder_inner_proc_lock(target_proc); From 0f15f0f6ca5df566275ce517f257af2559528b41 Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Fri, 19 Jun 2026 18:52:31 +0000 Subject: [PATCH 0267/2758] binder: fix UAF in binder_free_transaction() commit f223d27a546c1e1f48d38fd67760e78f068fe8c4 upstream. In binder_free_transaction(), the t->to_proc is read under the t->lock. However, once the t->lock is dropped, the to_proc can die in parallel. This leads to a use-after-free error when we attempt to acquire its inner lock right afterwards: ================================================================== BUG: KASAN: slab-use-after-free in _raw_spin_lock+0xe4/0x1a0 Write of size 4 at addr ffff00001125da70 by task B/672 CPU: 20 UID: 0 PID: 672 Comm: B Not tainted 7.1.0-rc6-00284-g8e65320d91cd #4 PREEMPT Hardware name: linux,dummy-virt (DT) Call trace: _raw_spin_lock+0xe4/0x1a0 binder_free_transaction+0x8c/0x320 binder_send_failed_reply+0x21c/0x2f8 binder_thread_release+0x488/0x7e0 binder_ioctl+0x12c0/0x29a0 [...] Allocated by task 675: __kmalloc_cache_noprof+0x174/0x444 binder_open+0x118/0xb70 do_dentry_open+0x374/0x1040 vfs_open+0x58/0x3bc [...] Freed by task 212: __kasan_slab_free+0x58/0x80 kfree+0x1a0/0x4a4 binder_proc_dec_tmpref+0x32c/0x5e0 binder_deferred_func+0xc48/0x104c process_one_work+0x53c/0xbc0 [...] ================================================================== To prevent this, pin the target thread (t->to_thread) to guarantee the target process remains alive. Undelivered transactions without a target thread are already safe, as the target process can only be the current context in those paths. Cc: stable Reported-by: Alice Ryhl Closes: https://lore.kernel.org/all/aikJKVuny_eOivwN@google.com/ Fixes: a370003cc301 ("binder: fix possible UAF when freeing buffer") Signed-off-by: Carlos Llamas Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20260619185233.2194678-2-cmllamas@google.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 09bc052186cfa..b85920c39694c 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -1658,10 +1658,19 @@ static void binder_txn_latency_free(struct binder_transaction *t) static void binder_free_transaction(struct binder_transaction *t) { + struct binder_thread *target_thread; struct binder_proc *target_proc; spin_lock(&t->lock); target_proc = t->to_proc; + target_thread = t->to_thread; + /* + * Pin target_thread to keep target_proc alive. Undelivered + * transactions with !target_thread are safe, as target_proc + * can only be the current context there. + */ + if (target_thread) + atomic_inc(&target_thread->tmp_ref); spin_unlock(&t->lock); if (target_proc) { @@ -1676,6 +1685,10 @@ static void binder_free_transaction(struct binder_transaction *t) t->buffer->transaction = NULL; binder_inner_proc_unlock(target_proc); } + + if (target_thread) + binder_thread_dec_tmpref(target_thread); + if (trace_binder_txn_latency_free_enabled()) binder_txn_latency_free(t); /* From 74920b1b4e474ba7a4de4323c0458deec49d210b Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Sun, 31 May 2026 22:29:24 +0900 Subject: [PATCH 0268/2758] rust_binder: use a u64 stride when cleaning up the offsets array commit 803c8a9502e9b97cd6ae937618ef4a8fd6274343 upstream. Allocation's Drop walks the offsets array (binder_size_t = u64 entries), cleaning up the objects, but it used usize instead of u64 for both the stride and the per-entry read. On 64-bit kernels (usize == u64) this is harmless, but on 32-bit kernels it walks the 8-byte entries in 4-byte steps, iterating an N-entry array 2N times, and reads the always-zero high word as offset 0, cleaning up the object at offset 0 N extra times. As a result the referenced node or handle ends up with a lower reference count than it actually has (a refcount over-decrement), and binder's reference accounting is corrupted; for example, the owner can be notified of a strong reference release (BR_RELEASE) even though references still remain. Change the stride to u64, and read each entry as a u64, narrowing it to usize with try_into(). On 32-bit ARM, when this over-decrement would drive a count below zero, the driver's existing refcount guard refuses it and fires: rust_binder: Failure: refcount underflow! Cc: stable Fixes: eafedbc7c050 ("rust_binder: add Rust Binder driver") Signed-off-by: Hyunwoo Kim Acked-by: Carlos Llamas Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/ahw3tFhLz9bMMJAO@v4bel Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/allocation.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/android/binder/allocation.rs b/drivers/android/binder/allocation.rs index b7b05e72970a2..ea5846e4da16a 100644 --- a/drivers/android/binder/allocation.rs +++ b/drivers/android/binder/allocation.rs @@ -259,7 +259,7 @@ impl Drop for Allocation { if let Some(offsets) = info.offsets.clone() { let view = AllocationView::new(self, offsets.start); - for i in offsets.step_by(size_of::()) { + for i in offsets.step_by(size_of::()) { if view.cleanup_object(i).is_err() { pr_warn!("Error cleaning up object at offset {}\n", i) } @@ -420,7 +420,8 @@ impl<'a> AllocationView<'a> { } fn cleanup_object(&self, index_offset: usize) -> Result { - let offset = self.alloc.read(index_offset)?; + let offset = self.alloc.read::(index_offset)?; + let offset: usize = offset.try_into().map_err(|_| EINVAL)?; let header = self.read::(offset)?; match header.type_ { BINDER_TYPE_WEAK_BINDER | BINDER_TYPE_BINDER => { From 3ffc336432da302b85ca35edcff56fa31b71bb40 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Fri, 5 Jun 2026 11:13:50 +0000 Subject: [PATCH 0269/2758] rust_binder: fix BINDER_GET_EXTENDED_ERROR commit 77bfebf110773f5a0d6b5ff8110896adb2c9c335 upstream. This code currently copies the ExtendedError struct to the stack, modifies the copy, and then doesn't modify the original. Thus, fix it. Furthermore, errors when replying must be delivered directly to the remote thread, so update deliver_reply() to take an extended error argument. Cc: stable Fixes: eafedbc7c050 ("rust_binder: add Rust Binder driver") Signed-off-by: Alice Ryhl Acked-by: Carlos Llamas Link: https://patch.msgid.link/20260605-set-extended-error-v3-1-d60b69a75f97@google.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/error.rs | 13 +++--- drivers/android/binder/thread.rs | 65 +++++++++++++++++++-------- drivers/android/binder/transaction.rs | 15 +++---- 3 files changed, 58 insertions(+), 35 deletions(-) diff --git a/drivers/android/binder/error.rs b/drivers/android/binder/error.rs index 45d85d4c28159..1296072c35d96 100644 --- a/drivers/android/binder/error.rs +++ b/drivers/android/binder/error.rs @@ -73,20 +73,17 @@ impl fmt::Debug for BinderError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.reply { BR_FAILED_REPLY => match self.source.as_ref() { - Some(source) => f - .debug_struct("BR_FAILED_REPLY") - .field("source", source) - .finish(), + Some(source) => source.fmt(f), None => f.pad("BR_FAILED_REPLY"), }, BR_DEAD_REPLY => f.pad("BR_DEAD_REPLY"), BR_FROZEN_REPLY => f.pad("BR_FROZEN_REPLY"), BR_TRANSACTION_PENDING_FROZEN => f.pad("BR_TRANSACTION_PENDING_FROZEN"), BR_TRANSACTION_COMPLETE => f.pad("BR_TRANSACTION_COMPLETE"), - _ => f - .debug_struct("BinderError") - .field("reply", &self.reply) - .finish(), + _ => match self.source.as_ref() { + Some(source) => source.fmt(f), + None => self.reply.fmt(f), + }, } } } diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index 97d5f31e8fe3c..3b85208139410 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -495,9 +495,16 @@ impl Thread { Ok(()) } + pub(crate) fn clear_extended_error(&self, debug_id: usize) { + self.inner.lock().extended_error = ExtendedError::new(debug_id as u32, BR_OK, 0); + } + pub(crate) fn get_extended_error(&self, data: UserSlice) -> Result { let mut writer = data.writer(); - let ee = self.inner.lock().extended_error; + let mut inner = self.inner.lock(); + let ee = inner.extended_error; + inner.extended_error = ExtendedError::new(0, BR_OK, 0); + drop(inner); writer.write(&ee)?; Ok(()) } @@ -1109,7 +1116,10 @@ impl Thread { inner.pop_transaction_to_reply(thread.as_ref()) } { let reply = Err(BR_DEAD_REPLY); - if !transaction.from.deliver_single_reply(reply, &transaction) { + if !transaction + .from + .deliver_single_reply(reply, &transaction, None) + { break; } @@ -1121,8 +1131,9 @@ impl Thread { &self, reply: Result, u32>, transaction: &DArc, + extended_error: Option, ) { - if self.deliver_single_reply(reply, transaction) { + if self.deliver_single_reply(reply, transaction, extended_error) { transaction.from.unwind_transaction_stack(); } } @@ -1136,6 +1147,7 @@ impl Thread { &self, reply: Result, u32>, transaction: &DArc, + extended_error: Option, ) -> bool { if let Ok(transaction) = &reply { crate::trace::trace_transaction(true, transaction, Some(&self.task)); @@ -1152,6 +1164,12 @@ impl Thread { return true; } + if let Some(ee) = extended_error { + if inner.extended_error.command == BR_OK { + inner.extended_error = ee; + } + } + match reply { Ok(work) => { inner.push_work(work); @@ -1222,6 +1240,9 @@ impl Thread { info.buffers_size = td.buffers_size as usize; // SAFETY: Above `read` call initializes all bytes, so this union read is ok. info.target_handle = unsafe { td.transaction_data.target.handle }; + + info.debug_id = super::next_debug_id(); + Ok(()) } @@ -1230,6 +1251,8 @@ impl Thread { let mut info = TransactionInfo::zeroed(); self.read_transaction_info(cmd, reader, &mut info)?; + self.clear_extended_error(info.debug_id); + let ret = if info.is_reply { self.reply_inner(&mut info) } else if info.is_oneway() { @@ -1239,23 +1262,21 @@ impl Thread { }; if let Err(err) = ret { - if err.reply != BR_TRANSACTION_COMPLETE { - info.reply = err.reply; - } - self.push_return_work(err.reply); - if let Some(source) = &err.source { - info.errno = source.to_errno(); + if err.reply != BR_TRANSACTION_COMPLETE { info.reply = err.reply; + if let Some(source) = &err.source { + info.errno = source.to_errno(); - { - let mut ee = self.inner.lock().extended_error; - ee.command = err.reply; - ee.param = source.to_errno(); + { + let mut inner = self.inner.lock(); + inner.extended_error = + ExtendedError::new(info.debug_id as u32, err.reply, source.to_errno()); + } } pr_warn!( - "{}:{} transaction to {} failed: {source:?}", + "{}:{} transaction to {} failed: {err:?}", info.from_pid, info.from_tid, info.to_pid @@ -1320,18 +1341,24 @@ impl Thread { let allow_fds = orig.flags & TF_ACCEPT_FDS != 0; let reply = Transaction::new_reply(self, process, info, allow_fds)?; self.inner.lock().push_work(completion); - orig.from.deliver_reply(Ok(reply), &orig); + orig.from.deliver_reply(Ok(reply), &orig, None); Ok(()) })() .map_err(|mut err| { // At this point we only return `BR_TRANSACTION_COMPLETE` to the caller, and we must let // the sender know that the transaction has completed (with an error in this case). + pr_warn!( - "Failure {:?} during reply - delivering BR_FAILED_REPLY to sender.", - err + "{}:{} reply to {} failed: {err:?}", + info.from_pid, + info.from_tid, + info.to_pid ); - let reply = Err(BR_FAILED_REPLY); - orig.from.deliver_reply(reply, &orig); + + let param = err.source.as_ref().map_or(0, |e| e.to_errno()); + let ee = ExtendedError::new(info.debug_id as u32, err.reply, param); + orig.from + .deliver_reply(Err(BR_FAILED_REPLY), &orig, Some(ee)); err.reply = BR_TRANSACTION_COMPLETE; err }); diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs index 1d9b66920a216..0e5d07b7e6f0a 100644 --- a/drivers/android/binder/transaction.rs +++ b/drivers/android/binder/transaction.rs @@ -42,6 +42,7 @@ pub(crate) struct TransactionInfo { pub(crate) reply: u32, pub(crate) oneway_spam_suspect: bool, pub(crate) is_reply: bool, + pub(crate) debug_id: usize, } impl TransactionInfo { @@ -93,7 +94,6 @@ impl Transaction { from: &Arc, info: &mut TransactionInfo, ) -> BinderResult> { - let debug_id = super::next_debug_id(); let allow_fds = node_ref.node.flags & FLAT_BINDER_FLAG_ACCEPTS_FDS != 0; let txn_security_ctx = node_ref.node.flags & FLAT_BINDER_FLAG_TXN_SECURITY_CTX != 0; let mut txn_security_ctx_off = if txn_security_ctx { Some(0) } else { None }; @@ -101,7 +101,7 @@ impl Transaction { let mut alloc = match from.copy_transaction_data( to.clone(), info, - debug_id, + info.debug_id, allow_fds, txn_security_ctx_off.as_mut(), ) { @@ -128,7 +128,7 @@ impl Transaction { let data_address = alloc.ptr; Ok(DTRWrap::arc_pin_init(pin_init!(Transaction { - debug_id, + debug_id: info.debug_id, target_node: Some(target_node), from_parent, sender_euid: Kuid::current_euid(), @@ -152,9 +152,8 @@ impl Transaction { info: &mut TransactionInfo, allow_fds: bool, ) -> BinderResult> { - let debug_id = super::next_debug_id(); let mut alloc = - match from.copy_transaction_data(to.clone(), info, debug_id, allow_fds, None) { + match from.copy_transaction_data(to.clone(), info, info.debug_id, allow_fds, None) { Ok(alloc) => alloc, Err(err) => { pr_warn!("Failure in copy_transaction_data: {:?}", err); @@ -165,7 +164,7 @@ impl Transaction { alloc.set_info_clear_on_drop(); } Ok(DTRWrap::arc_pin_init(pin_init!(Transaction { - debug_id, + debug_id: info.debug_id, target_node: None, from_parent: None, sender_euid: Kuid::current_euid(), @@ -394,7 +393,7 @@ impl DeliverToRead for Transaction { let send_failed_reply = ScopeGuard::new(|| { if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 { let reply = Err(BR_FAILED_REPLY); - self.from.deliver_reply(reply, &self); + self.from.deliver_reply(reply, &self, None); } self.drop_outstanding_txn(); }); @@ -478,7 +477,7 @@ impl DeliverToRead for Transaction { // If this is not a reply or oneway transaction, then send a dead reply. if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 { let reply = Err(BR_DEAD_REPLY); - self.from.deliver_reply(reply, &self); + self.from.deliver_reply(reply, &self, None); } self.drop_outstanding_txn(); From ad6af5c32dacaeb65a0b4731ce04cac1bb1ab075 Mon Sep 17 00:00:00 2001 From: Keshav Verma Date: Thu, 25 Jun 2026 16:09:57 +0530 Subject: [PATCH 0270/2758] rust_binder: reject context manager self-transaction commit 6849cabfd30fb5727cfd31e8241e15801e17ebf9 upstream. Rust binder resolved handle 0 to the context manager node, but it does not reject the case where the caller owns the same node. The C binder driver rejects transactions from the context-manager process to handle 0 after resolving the target node. Match that behavior in Rust Binder by rejecting handle 0 transactions when the resolved context-manager node is owned by the calling process. This applies to both synchronous and oneway transactions because both paths resolve the target through Process::get_transaction_node(). Cc: stable Fixes: eafedbc7c050 ("rust_binder: add Rust Binder driver") Signed-off-by: Keshav Verma Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20260625103957.730-1-iganschel@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/process.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index 96b8440ceac62..ca664fda8e810 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -900,7 +900,11 @@ impl Process { pub(crate) fn get_transaction_node(&self, handle: u32) -> BinderResult { // When handle is zero, try to get the context manager. if handle == 0 { - Ok(self.ctx.get_manager_node(true)?) + let node_ref = self.ctx.get_manager_node(true)?; + if core::ptr::eq(self, &*node_ref.node.owner) { + return Err(EINVAL.into()); + } + Ok(node_ref) } else { Ok(self.get_node_from_handle(handle, true)?) } From 59fbe6b204569d982146fcff4466c219b5cda577 Mon Sep 17 00:00:00 2001 From: Keshav Verma Date: Tue, 16 Jun 2026 02:47:43 +0530 Subject: [PATCH 0271/2758] rust_binder: synchronize Rust Binder stats with freeze commands commit eb1645bf10190e71f6f0316e37ff70755d719b53 upstream. Rust Binder stats use BC_COUNT and BR_COUNT to size the command and return counters, and use event string tables when printing debug statistics. The Binder protocol includes freeze-related commands and return codes, but the Rust Binder statistics code was not updated to cover them. As a result, those commands and return codes are not accounted for or printed by the stats debug output. Update the counts and event string tables so these commands and return codes are included in the debug statistics output. Fixes: eafedbc7c050 ("rust_binder: add Rust Binder driver") Cc: stable Acked-by: Carlos Llamas Reviewed-by: Alice Ryhl Signed-off-by: Keshav Verma Link: https://patch.msgid.link/20260615211743.734-1-iganschel@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/rust_binder_events.c | 7 ++++++- drivers/android/binder/stats.rs | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/android/binder/rust_binder_events.c b/drivers/android/binder/rust_binder_events.c index 488b1470060cc..5792aa59cc825 100644 --- a/drivers/android/binder/rust_binder_events.c +++ b/drivers/android/binder/rust_binder_events.c @@ -28,6 +28,9 @@ const char * const binder_command_strings[] = { "BC_DEAD_BINDER_DONE", "BC_TRANSACTION_SG", "BC_REPLY_SG", + "BC_REQUEST_FREEZE_NOTIFICATION", + "BC_CLEAR_FREEZE_NOTIFICATION", + "BC_FREEZE_NOTIFICATION_DONE", }; const char * const binder_return_strings[] = { @@ -51,7 +54,9 @@ const char * const binder_return_strings[] = { "BR_FAILED_REPLY", "BR_FROZEN_REPLY", "BR_ONEWAY_SPAM_SUSPECT", - "BR_TRANSACTION_PENDING_FROZEN" + "BR_TRANSACTION_PENDING_FROZEN", + "BR_FROZEN_BINDER", + "BR_CLEAR_FREEZE_NOTIFICATION_DONE", }; #define CREATE_TRACE_POINTS diff --git a/drivers/android/binder/stats.rs b/drivers/android/binder/stats.rs index ab75e9561cbf4..ec81dc7747db8 100644 --- a/drivers/android/binder/stats.rs +++ b/drivers/android/binder/stats.rs @@ -8,8 +8,8 @@ use crate::defs::*; use kernel::sync::atomic::{ordering::Relaxed, Atomic}; use kernel::{ioctl::_IOC_NR, seq_file::SeqFile, seq_print}; -const BC_COUNT: usize = _IOC_NR(BC_REPLY_SG) as usize + 1; -const BR_COUNT: usize = _IOC_NR(BR_TRANSACTION_PENDING_FROZEN) as usize + 1; +const BC_COUNT: usize = _IOC_NR(BC_FREEZE_NOTIFICATION_DONE) as usize + 1; +const BR_COUNT: usize = _IOC_NR(BR_CLEAR_FREEZE_NOTIFICATION_DONE) as usize + 1; pub(crate) static GLOBAL_STATS: BinderStats = BinderStats::new(); From 0644da3621ddd8e146280675a2a31d1e06634a1d Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Fri, 3 Jul 2026 11:25:12 +0000 Subject: [PATCH 0272/2758] rust_binder: clear freeze listener on node removal commit bc4a9828897871ff3e5a1f8a1d346decbf4ee95e upstream. Generally userspace is supposed to explicitly clear freeze listeners before they drop the refcount on the node ref to zero, but there's nothing forcing that. Currently, in this scenario the freeze listener remains in the freeze_listeners rbtree and in the remote node's freeze listener list, even though the ref for which the listener is registered is gone. This could potentially lead to a memory leak due to a refcount cycle. Thus, remove the freeze listener in this scenario. Cc: stable Fixes: eafedbc7c050 ("rust_binder: add Rust Binder driver") Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260703-remove-freeze-on-remove-node-v3-1-6e0c4547af46@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/freeze.rs | 11 +++++++++-- drivers/android/binder/node.rs | 10 ++++++---- drivers/android/binder/process.rs | 12 +++++++++++- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/drivers/android/binder/freeze.rs b/drivers/android/binder/freeze.rs index 53b60035639aa..f4df14568b252 100644 --- a/drivers/android/binder/freeze.rs +++ b/drivers/android/binder/freeze.rs @@ -154,10 +154,17 @@ impl DeliverToRead for FreezeMessage { } impl FreezeListener { - pub(crate) fn on_process_exit(&self, proc: &Arc) { + /// Called when this freeze listener is cleared abnormally. + /// + /// This occurs either because the process exited or because the process dropped its last + /// refcount on the node ref without explicitly removing the freeze listener first. + /// + /// The returned `KVVec` is just a value that should be dropped outside of the lock. + pub(crate) fn on_process_cleanup(&self, proc: &Process) -> KVVec> { if !self.is_clearing { - self.node.remove_freeze_listener(proc); + return self.node.remove_freeze_listener(proc); } + KVVec::new() } } diff --git a/drivers/android/binder/node.rs b/drivers/android/binder/node.rs index 69f757ff7461e..c10148e9069f3 100644 --- a/drivers/android/binder/node.rs +++ b/drivers/android/binder/node.rs @@ -682,12 +682,13 @@ impl Node { } } - pub(crate) fn remove_freeze_listener(&self, p: &Arc) { - let _unused_capacity; + pub(crate) fn remove_freeze_listener(&self, p: &Process) -> KVVec> { let mut guard = self.owner.inner.lock(); let inner = self.inner.access_mut(&mut guard); let len = inner.freeze_list.len(); - inner.freeze_list.retain(|proc| !Arc::ptr_eq(proc, p)); + inner + .freeze_list + .retain(|proc| !core::ptr::eq::(&**proc, p)); if len == inner.freeze_list.len() { pr_warn!( "Could not remove freeze listener for {}\n", @@ -695,8 +696,9 @@ impl Node { ); } if inner.freeze_list.is_empty() { - _unused_capacity = mem::take(&mut inner.freeze_list); + return mem::take(&mut inner.freeze_list); } + KVVec::new() } pub(crate) fn freeze_list<'a>(&'a self, guard: &'a ProcessInner) -> &'a [Arc] { diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index ca664fda8e810..cdd1a90797266 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -946,6 +946,8 @@ impl Process { // To preserve original binder behaviour, we only fail requests where the manager tries to // increment references on itself. + let _to_free_freeze_listener; + let _to_free_freeze_listener_cleanup; let mut refs = self.node_refs.lock(); if let Some(info) = refs.by_handle.get_mut(&handle) { if info.node_ref().update(inc, strong) { @@ -961,6 +963,14 @@ impl Process { unsafe { info.node_ref2().node.remove_node_info(info) }; let id = info.node_ref().node.global_id(); + + if let Some(freeze) = *info.freeze() { + if let Some(fl) = refs.freeze_listeners.remove(&freeze) { + _to_free_freeze_listener_cleanup = fl.on_process_cleanup(&self); + _to_free_freeze_listener = fl; + } + } + refs.by_handle.remove(&handle); refs.by_node.remove(&id); refs.handle_is_present.release_id(handle as usize); @@ -1384,7 +1394,7 @@ impl Process { // Clean up freeze listeners. let freeze_listeners = take(&mut self.node_refs.lock().freeze_listeners); for listener in freeze_listeners.values() { - listener.on_process_exit(&self); + listener.on_process_cleanup(&self); } drop(freeze_listeners); From 93cd037da94fcb93183bfb2457e3a56d3eb4c8f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E8=BF=9E=E5=8B=A4?= Date: Fri, 3 Jul 2026 17:40:33 +0300 Subject: [PATCH 0273/2758] usb: xhci: Fix sleep in atomic context in xhci_free_streams() commit 42c37c4b75d38b51d84f31a8e29427f5e06a7c2a upstream. When a USB device with active stream endpoints is disconnected, xhci_free_streams() is called from the hub_event workqueue to free the stream resources. It calls xhci_free_stream_info() while holding xhci->lock with irqs disabled. xhci_free_stream_info() invokes xhci_free_stream_ctx(), which calls dma_free_coherent() for large stream context arrays. dma_free_coherent() can sleep (e.g. via vunmap), triggering a BUG when called from atomic context. Call trace: dma_free_attrs+0x174/0x220 xhci_free_stream_info+0xd0/0x11c xhci_free_streams+0x278/0x37c usb_free_streams+0x98/0xc0 usb_unbind_interface+0x1b8/0x2f8 device_release_driver_internal+0x1d4/0x2cc device_release_driver+0x18/0x28 bus_remove_device+0x160/0x1a4 device_del+0x1ec/0x350 usb_disable_device+0x98/0x214 usb_disconnect+0xf0/0x35c hub_event+0xab4/0x19ec process_one_work+0x278/0x63c Fix this by saving the stream_info pointers and clearing the ep references under the lock, then calling xhci_free_stream_info() outside the lock where sleeping is allowed. Fixes: 8df75f42f8e6 ("USB: xhci: Add memory allocation for USB3 bulk streams.") Cc: stable Signed-off-by: Lianqin Hu Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20260703144033.483286-3-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index a54f5b57f2055..15346fdd66bc4 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3788,6 +3788,7 @@ static int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev, struct xhci_virt_device *vdev; struct xhci_command *command; struct xhci_input_control_ctx *ctrl_ctx; + struct xhci_stream_info *stream_info[EP_CTX_PER_DEV]; unsigned int ep_index; unsigned long flags; u32 changed_ep_bitmask; @@ -3848,10 +3849,15 @@ static int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev, if (ret < 0) return ret; + /* + * dma_free_coherent() called by xhci_free_stream_info() may sleep, + * so save stream_info pointers and clear references under lock, + * then free the memory outside lock. + */ spin_lock_irqsave(&xhci->lock, flags); for (i = 0; i < num_eps; i++) { ep_index = xhci_get_endpoint_index(&eps[i]->desc); - xhci_free_stream_info(xhci, vdev->eps[ep_index].stream_info); + stream_info[i] = vdev->eps[ep_index].stream_info; vdev->eps[ep_index].stream_info = NULL; /* FIXME Unset maxPstreams in endpoint context and * update deq ptr to point to normal string ring. @@ -3861,6 +3867,9 @@ static int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev, } spin_unlock_irqrestore(&xhci->lock, flags); + for (i = 0; i < num_eps; i++) + xhci_free_stream_info(xhci, stream_info[i]); + return 0; } From a3eaf82ff842d6ca95937ea584417e04828235a6 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Fri, 3 Jul 2026 17:40:32 +0300 Subject: [PATCH 0274/2758] xhci: sideband: fix ring sg table pages leak commit 49f6e3c3ef19f04f6657ed8dce550e36c763abb8 upstream. xhci_ring_to_sgtable() allocates a temporary pages array and uses it to build the returned sg_table with sg_alloc_table_from_pages(). The error paths free the pages array, but the success path returns the sg_table without freeing it. This leaks the temporary array every time a sideband client gets an endpoint or event ring buffer. Free the pages array after sg_alloc_table_from_pages() succeeds. The returned sg_table has its own scatterlist entries and does not depend on the temporary array after construction. Fixes: de66754e9f80 ("xhci: sideband: add initial api to register a secondary interrupter entity") Cc: stable Signed-off-by: Xu Rao Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20260703144033.483286-2-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-sideband.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/host/xhci-sideband.c b/drivers/usb/host/xhci-sideband.c index 23153e136d4b8..a5deeee4d5dce 100644 --- a/drivers/usb/host/xhci-sideband.c +++ b/drivers/usb/host/xhci-sideband.c @@ -58,6 +58,8 @@ xhci_ring_to_sgtable(struct xhci_sideband *sb, struct xhci_ring *ring) if (sg_alloc_table_from_pages(sgt, pages, n_pages, 0, sz, GFP_KERNEL)) goto err; + kvfree(pages); + /* * Save first segment dma address to sg dma_address field for the sideband * client to have access to the IOVA of the ring. From 569f18a83eed0b0be4615f0c7bed40fb5c50e2e6 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Mon, 6 Jul 2026 23:53:12 +0900 Subject: [PATCH 0275/2758] usb: typec: tcpci_rt1711h: unregister TCPCI port with devres commit e8da46d99d3710106e7c44db14566bf9b57386b5 upstream. rt1711h_probe() registers the TCPCI port before requesting the interrupt and enabling alert interrupts. If either of those later steps fails, the probe function returns without unregistering the TCPCI port. The explicit unregister currently only happens from the remove callback. Register a devres action immediately after tcpci_register_port() succeeds, so tcpci_unregister_port() runs on later probe failures and on driver detach. Drop the remove callback to avoid unregistering the same port twice. This issue was identified during our ongoing static-analysis research while reviewing kernel code. Fixes: 302c570bf36e ("usb: typec: tcpci_rt1711h: avoid screaming irq causing boot hangs") Cc: stable Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Link: https://patch.msgid.link/20260706145312.37260-1-mhun512@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpci_rt1711h.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpci_rt1711h.c b/drivers/usb/typec/tcpm/tcpci_rt1711h.c index 4b3e4e22a82e8..0af95c2d33fc4 100644 --- a/drivers/usb/typec/tcpm/tcpci_rt1711h.c +++ b/drivers/usb/typec/tcpm/tcpci_rt1711h.c @@ -295,6 +295,8 @@ static int rt1711h_sw_reset(struct rt1711h_chip *chip) return 0; } +static void rt1711h_unregister_tcpci_port(void *tcpci); + static int rt1711h_probe(struct i2c_client *client) { int ret; @@ -340,6 +342,10 @@ static int rt1711h_probe(struct i2c_client *client) if (IS_ERR_OR_NULL(chip->tcpci)) return PTR_ERR(chip->tcpci); + ret = devm_add_action_or_reset(chip->dev, rt1711h_unregister_tcpci_port, chip->tcpci); + if (ret) + return ret; + ret = devm_request_threaded_irq(chip->dev, client->irq, NULL, rt1711h_irq, IRQF_ONESHOT | IRQF_TRIGGER_LOW, @@ -357,11 +363,9 @@ static int rt1711h_probe(struct i2c_client *client) return 0; } -static void rt1711h_remove(struct i2c_client *client) +static void rt1711h_unregister_tcpci_port(void *tcpci) { - struct rt1711h_chip *chip = i2c_get_clientdata(client); - - tcpci_unregister_port(chip->tcpci); + tcpci_unregister_port(tcpci); } static const struct rt1711h_chip_info rt1711h = { @@ -394,7 +398,6 @@ static struct i2c_driver rt1711h_i2c_driver = { .of_match_table = rt1711h_of_match, }, .probe = rt1711h_probe, - .remove = rt1711h_remove, .id_table = rt1711h_id, }; module_i2c_driver(rt1711h_i2c_driver); From 1d3f464bb158ca040f0e7f34f3f6d0f21751eeba Mon Sep 17 00:00:00 2001 From: Han Gao Date: Wed, 1 Apr 2026 01:12:48 +0800 Subject: [PATCH 0276/2758] riscv: dts: sophgo: Add dma-coherent to SG2042 PCIe controllers commit 2647eabde8de748ee2c9a2816615d4af3bd4bf9d upstream. SG2042's PCIe root complexes are cache-coherent with the CPU. Mark all four PCIe controller nodes (pcie_rc0 through pcie_rc3) as dma-coherent so the kernel uses coherent DMA mappings instead of non-coherent bounce buffering. Cc: stable@vger.kernel.org Signed-off-by: Han Gao Link: https://patch.msgid.link/20260331171248.973014-3-gaohan@iscas.ac.cn Signed-off-by: Inochi Amaoto Signed-off-by: Chen Wang Signed-off-by: Greg Kroah-Hartman --- arch/riscv/boot/dts/sophgo/sg2042.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/riscv/boot/dts/sophgo/sg2042.dtsi b/arch/riscv/boot/dts/sophgo/sg2042.dtsi index 9fddf3f0b3b99..3af7705497426 100644 --- a/arch/riscv/boot/dts/sophgo/sg2042.dtsi +++ b/arch/riscv/boot/dts/sophgo/sg2042.dtsi @@ -417,6 +417,7 @@ vendor-id = <0x1f1c>; device-id = <0x2042>; cdns,no-bar-match-nbits = <48>; + dma-coherent; msi-parent = <&msi>; status = "disabled"; }; @@ -439,6 +440,7 @@ vendor-id = <0x1f1c>; device-id = <0x2042>; cdns,no-bar-match-nbits = <48>; + dma-coherent; msi-parent = <&msi>; status = "disabled"; }; @@ -461,6 +463,7 @@ vendor-id = <0x1f1c>; device-id = <0x2042>; cdns,no-bar-match-nbits = <48>; + dma-coherent; msi-parent = <&msi>; status = "disabled"; }; @@ -483,6 +486,7 @@ vendor-id = <0x1f1c>; device-id = <0x2042>; cdns,no-bar-match-nbits = <48>; + dma-coherent; msi-parent = <&msi>; status = "disabled"; }; From bc29e49364ea7fb6c3a5c9692d953c61ae91329b Mon Sep 17 00:00:00 2001 From: Ziyao Li Date: Sun, 12 Apr 2026 18:17:31 +0800 Subject: [PATCH 0277/2758] PCI: loongson: Override PCIe bridge supported speeds for Loongson-3C6000 series commit e373c789bac0ad73b472d8b44714df3bd18a4edf upstream. Older steppings of the Loongson-3C6000 series incorrectly report the supported link speeds on their PCIe bridges (device IDs 0x3c19, 0x3c29) as only 2.5 GT/s, despite the upstream bus supporting speeds from 2.5 GT/s up to 16 GT/s. As a result, since commit 774c71c52aa4 ("PCI/bwctrl: Enable only if more than one speed is supported"), bwctrl will be disabled if there's only one 2.5 GT/s value in vector 'supported_speeds'. Manually override the 'supported_speeds' field for affected PCIe bridges with those found on the upstream bus to correctly reflect the supported link speeds. Updating the speeds to reflect what the hardware actually supports avoids quirks in drivers consuming the speed information. This commit was originally found from AOSC OS[1]. Fixes: cd89edda4002 ("PCI: loongson: Add ACPI init support") Signed-off-by: Ayden Meng Signed-off-by: Mingcong Bai [Ziyao Li: move from drivers/pci/quirks.c to drivers/pci/controller/pci-loongson.c] Signed-off-by: Ziyao Li [Xi Ruoyao: Fixed falling through logic, added debug log, Fixes tag and rebased to 7.0-rc7] Signed-off-by: Xi Ruoyao Signed-off-by: Manivannan Sadhasivam [bhelgaas: commit log, https://lore.kernel.org/all/9d815df3b33a63223112b97440c01247935363c1.camel@xry111.site] Signed-off-by: Bjorn Helgaas Tested-by: Lain Fearyncess Yang Tested-by: Ayden Meng Tested-by: Mingcong Bai Reviewed-by: Huacai Chen Cc: stable@vger.kernel.org Link: https://github.com/AOSC-Tracking/linux/commit/4392f441363abdf6fa0a0433d73175a17f493454 Link: https://github.com/AOSC-Tracking/linux/pull/2 #1 Link: https://patch.msgid.link/20260412101731.107059-1-xry111@xry111.site Signed-off-by: Greg Kroah-Hartman --- drivers/pci/controller/pci-loongson.c | 36 +++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/drivers/pci/controller/pci-loongson.c b/drivers/pci/controller/pci-loongson.c index bc630ab8a2831..9609e6f50b981 100644 --- a/drivers/pci/controller/pci-loongson.c +++ b/drivers/pci/controller/pci-loongson.c @@ -176,6 +176,42 @@ static void loongson_pci_msi_quirk(struct pci_dev *dev) } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_PCIE_PORT5, loongson_pci_msi_quirk); +/* + * Older steppings of the Loongson-3C6000 series incorrectly report the + * supported link speeds on their PCIe bridges (device IDs 0x3c19, + * 0x3c29) as only 2.5 GT/s, despite the upstream bus supporting speeds + * from 2.5 GT/s up to 16 GT/s. + */ +static void loongson_pci_bridge_speed_quirk(struct pci_dev *pdev) +{ + u8 old_supported_speeds = pdev->supported_speeds; + + switch (pdev->bus->max_bus_speed) { + case PCIE_SPEED_16_0GT: + pdev->supported_speeds |= PCI_EXP_LNKCAP2_SLS_16_0GB; + fallthrough; + case PCIE_SPEED_8_0GT: + pdev->supported_speeds |= PCI_EXP_LNKCAP2_SLS_8_0GB; + fallthrough; + case PCIE_SPEED_5_0GT: + pdev->supported_speeds |= PCI_EXP_LNKCAP2_SLS_5_0GB; + fallthrough; + case PCIE_SPEED_2_5GT: + pdev->supported_speeds |= PCI_EXP_LNKCAP2_SLS_2_5GB; + break; + default: + pci_warn(pdev, "unexpected max bus speed"); + + return; + } + + if (pdev->supported_speeds != old_supported_speeds) + pci_info(pdev, "fixed up supported link speeds: 0x%x => 0x%x", + old_supported_speeds, pdev->supported_speeds); +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LOONGSON, 0x3c19, loongson_pci_bridge_speed_quirk); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LOONGSON, 0x3c29, loongson_pci_bridge_speed_quirk); + static struct loongson_pci *pci_bus_to_loongson_pci(struct pci_bus *bus) { struct pci_config_window *cfg; From ff396bab155f6d8f2b3646fe926a20336b68a15b Mon Sep 17 00:00:00 2001 From: Mahesh Vaidya Date: Thu, 30 Apr 2026 13:43:29 -0700 Subject: [PATCH 0278/2758] PCI: altera: Do not dispose parent IRQ mapping commit 5ef4bac02189bee0b7c170e352d7a38e13fe9678 upstream. altera_pcie_irq_teardown() calls irq_dispose_mapping() on pcie->irq. However, pcie->irq is the parent IRQ returned by platform_get_irq(), not the mapping created by Altera INTx irq_domain. The Altera driver only sets the chained handler on the parent IRQ. It should detach that handler during teardown, but it should not dispose the parent IRQ mapping, which belongs to the parent interrupt controller's irq_domain. Drop irq_dispose_mapping(pcie->irq) from the teardown path. Note that during irqchip remove(), the child IRQs should've disposed. But since the chained handler itself is removed, there is no way the stale child IRQs (if exists) could fire. So it is safe here. Fixes: ec15c4d0d5d2 ("PCI: altera: Allow building as module") Signed-off-by: Mahesh Vaidya [mani: added a note about IRQ disposal] Signed-off-by: Manivannan Sadhasivam Reviewed-by: Subhransu S. Prusty Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260430204330.3121003-2-mahesh.vaidya@altera.com Signed-off-by: Greg Kroah-Hartman --- drivers/pci/controller/pcie-altera.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pci/controller/pcie-altera.c b/drivers/pci/controller/pcie-altera.c index 3dbb7adc421c4..3d3519b8d88f0 100644 --- a/drivers/pci/controller/pcie-altera.c +++ b/drivers/pci/controller/pcie-altera.c @@ -868,7 +868,6 @@ static void altera_pcie_irq_teardown(struct altera_pcie *pcie) { irq_set_chained_handler_and_data(pcie->irq, NULL, NULL); irq_domain_remove(pcie->irq_domain); - irq_dispose_mapping(pcie->irq); } static int altera_pcie_parse_dt(struct altera_pcie *pcie) From 6864c789b570e57f932847fa83f6b56917182d73 Mon Sep 17 00:00:00 2001 From: Mahesh Vaidya Date: Thu, 30 Apr 2026 13:43:30 -0700 Subject: [PATCH 0279/2758] PCI: altera: Fix resource leaks on probe failure commit 7a94138caeb27f3c49c1dbd93bf422098925bb28 upstream. The chained IRQ handler is set during probe, but is only removed during the driver remove(). If pci_host_probe() fails, the handler and INTx IRQ domain remain set even though the devm-managed host bridge storage containing struct altera_pcie will be released, leaving the handler with a stale data pointer. Interrupts are also enabled before pci_host_probe() is called. If probe fails after that point, the controller interrupt source should be disabled before the chained handler and INTx domain are removed. So set the chained handler only after the INTx domain has been created. Disable controller interrupts during IRQ teardown, and tear the IRQ setup down if pci_host_probe() fails. Fixes: c63aed7334c2 ("PCI: altera: Use pci_host_probe() to register host") Signed-off-by: Mahesh Vaidya [mani: commit log] Signed-off-by: Manivannan Sadhasivam Reviewed-by: Subhransu S. Prusty Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260430204330.3121003-3-mahesh.vaidya@altera.com Signed-off-by: Greg Kroah-Hartman --- drivers/pci/controller/pcie-altera.c | 35 ++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/drivers/pci/controller/pcie-altera.c b/drivers/pci/controller/pcie-altera.c index 3d3519b8d88f0..76f3823d96139 100644 --- a/drivers/pci/controller/pcie-altera.c +++ b/drivers/pci/controller/pcie-altera.c @@ -864,8 +864,23 @@ static int altera_pcie_init_irq_domain(struct altera_pcie *pcie) return 0; } +static void altera_pcie_disable_irq(struct altera_pcie *pcie) +{ + if (pcie->pcie_data->version == ALTERA_PCIE_V1 || + pcie->pcie_data->version == ALTERA_PCIE_V2) { + /* Disable all P2A interrupts */ + cra_writel(pcie, 0, P2A_INT_ENABLE); + } else if (pcie->pcie_data->version == ALTERA_PCIE_V3) { + /* Disable port-level interrupts (CFG_AER, etc.) */ + writel(0, pcie->hip_base + + pcie->pcie_data->port_conf_offset + + pcie->pcie_data->port_irq_enable_offset); + } +} + static void altera_pcie_irq_teardown(struct altera_pcie *pcie) { + altera_pcie_disable_irq(pcie); irq_set_chained_handler_and_data(pcie->irq, NULL, NULL); irq_domain_remove(pcie->irq_domain); } @@ -890,7 +905,6 @@ static int altera_pcie_parse_dt(struct altera_pcie *pcie) if (pcie->irq < 0) return pcie->irq; - irq_set_chained_handler_and_data(pcie->irq, pcie->pcie_data->ops->rp_isr, pcie); return 0; } @@ -1019,6 +1033,14 @@ static int altera_pcie_probe(struct platform_device *pdev) return ret; } + /* + * The chained handler uses pcie->irq_domain, so set it only after the + * INTx domain has been created. + */ + irq_set_chained_handler_and_data(pcie->irq, + pcie->pcie_data->ops->rp_isr, + pcie); + if (pcie->pcie_data->version == ALTERA_PCIE_V1 || pcie->pcie_data->version == ALTERA_PCIE_V2) { /* clear all interrupts */ @@ -1036,7 +1058,16 @@ static int altera_pcie_probe(struct platform_device *pdev) bridge->busnr = pcie->root_bus_nr; bridge->ops = &altera_pcie_ops; - return pci_host_probe(bridge); + ret = pci_host_probe(bridge); + if (ret) + goto err_teardown_irq; + + return 0; + +err_teardown_irq: + altera_pcie_irq_teardown(pcie); + + return ret; } static void altera_pcie_remove(struct platform_device *pdev) From 669c4f38760080919db88343228a07e8ed7267c3 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Mon, 8 Dec 2025 19:24:29 +0000 Subject: [PATCH 0280/2758] PCI: Always lift 2.5GT/s restriction in PCIe failed link retraining commit 72780f7964684939d7d2f69c348876213b184484 upstream. Discard Vendor:Device ID matching in the PCIe failed link retraining quirk and ignore the link status for the removal of the 2.5GT/s speed clamp, whether applied by the quirk itself or the firmware earlier on. Revert to the original target link speed if this final link retraining has failed. This is so that link training noise in hot-plug scenarios does not make a link remain clamped to the 2.5GT/s speed where an event race has led the quirk to apply the speed clamp for one device, only to leave it in place for a subsequent device to be plugged in. Refer to the Link Capabilities register directly for the maximum link speed determination so as to streamline backporting. Fixes: a89c82249c37 ("PCI: Work around PCIe link training failures") Signed-off-by: Maciej W. Rozycki Signed-off-by: Bjorn Helgaas Tested-by: Alok Tiwari Cc: stable@vger.kernel.org # v6.5+ Link: https://patch.msgid.link/alpine.DEB.2.21.2512080331530.49654@angie.orcam.me.uk Signed-off-by: Greg Kroah-Hartman --- drivers/pci/quirks.c | 51 ++++++++++++++++---------------------------- 1 file changed, 18 insertions(+), 33 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index caaed1a01dc02..dd0025d3914e6 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -80,11 +80,10 @@ static bool pcie_lbms_seen(struct pci_dev *dev, u16 lnksta) * Restrict the speed to 2.5GT/s then with the Target Link Speed field, * request a retrain and check the result. * - * If this turns out successful and we know by the Vendor:Device ID it is - * safe to do so, then lift the restriction, letting the devices negotiate - * a higher speed. Also check for a similar 2.5GT/s speed restriction the - * firmware may have already arranged and lift it with ports that already - * report their data link being up. + * If this turns out successful, or where a 2.5GT/s speed restriction has + * been previously arranged by the firmware and the port reports its link + * already being up, lift the restriction, in a hope it is safe to do so, + * letting the devices negotiate a higher speed. * * Otherwise revert the speed to the original setting and request a retrain * again to remove any residual state, ignoring the result as it's supposed @@ -95,51 +94,37 @@ static bool pcie_lbms_seen(struct pci_dev *dev, u16 lnksta) */ int pcie_failed_link_retrain(struct pci_dev *dev) { - static const struct pci_device_id ids[] = { - { PCI_VDEVICE(ASMEDIA, 0x2824) }, /* ASMedia ASM2824 */ - {} - }; - u16 lnksta, lnkctl2; + u16 lnksta, lnkctl2, oldlnkctl2; int ret = -ENOTTY; + u32 lnkcap; if (!pci_is_pcie(dev) || !pcie_downstream_port(dev) || !pcie_cap_has_lnkctl2(dev) || !dev->link_active_reporting) return ret; pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta); + pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &oldlnkctl2); if (!(lnksta & PCI_EXP_LNKSTA_DLLLA) && pcie_lbms_seen(dev, lnksta)) { - u16 oldlnkctl2; - pci_info(dev, "broken device, retraining non-functional downstream link at 2.5GT/s\n"); - - pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &oldlnkctl2); ret = pcie_set_target_speed(dev, PCIE_SPEED_2_5GT, false); - if (ret) { - pci_info(dev, "retraining failed\n"); - pcie_set_target_speed(dev, PCIE_LNKCTL2_TLS2SPEED(oldlnkctl2), - true); - return ret; - } - - pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta); + if (ret) + goto err; } pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &lnkctl2); - - if ((lnksta & PCI_EXP_LNKSTA_DLLLA) && - (lnkctl2 & PCI_EXP_LNKCTL2_TLS) == PCI_EXP_LNKCTL2_TLS_2_5GT && - pci_match_id(ids, dev)) { - u32 lnkcap; - + pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap); + if ((lnkctl2 & PCI_EXP_LNKCTL2_TLS) == PCI_EXP_LNKCTL2_TLS_2_5GT && + (lnkcap & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) { pci_info(dev, "removing 2.5GT/s downstream link speed restriction\n"); - pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap); ret = pcie_set_target_speed(dev, PCIE_LNKCAP_SLS2SPEED(lnkcap), false); - if (ret) { - pci_info(dev, "retraining failed\n"); - return ret; - } + if (ret) + goto err; } + return ret; +err: + pci_info(dev, "retraining failed\n"); + pcie_set_target_speed(dev, PCIE_LNKCTL2_TLS2SPEED(oldlnkctl2), true); return ret; } From 7707ac040967e4da2fc65ae578f1d3e8c8307621 Mon Sep 17 00:00:00 2001 From: Ratheesh Kannoth Date: Tue, 14 Apr 2026 13:47:30 +0530 Subject: [PATCH 0281/2758] PCI: host-common: Request bus reassignment when not probe-only commit fda8749ba73638f5bbca3ffb39bc6861eb3b23fa upstream. pci_host_common_init() is used by several generic ECAM host drivers. After PCI core changes around pci_flags and preserve_config, these hosts no longer opted into full bus number reassignment the way they did before, which broke enumeration of devices on a Marvell CN106XX board. When PCI_PROBE_ONLY is not set, add PCI_REASSIGN_ALL_BUS so pci_scan_bridge_extend() takes the reassignment path: bus numbers can be assigned from firmware EA data (e.g. pci_ea_fixed_busnrs()). Skip the flag in probe-only mode so existing assignments are not overridden. Fixes: 7246a4520b4b ("PCI: Use preserve_config in place of pci_flags") Closes: https://lore.kernel.org/all/abkqm_LCd9zAM8cW@rkannoth-OptiPlex-7090/ Signed-off-by: Ratheesh Kannoth [mani: added stable tag] Signed-off-by: Manivannan Sadhasivam [bhelgaas: add problem report link] Signed-off-by: Bjorn Helgaas Cc: stable@vger.kernel.org Cc: Vidya Sagar Link: https://patch.msgid.link/20260414081730.3864372-1-rkannoth@marvell.com Signed-off-by: Greg Kroah-Hartman --- drivers/pci/controller/pci-host-common.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c index d6258c1cffe5e..99952fb7189b1 100644 --- a/drivers/pci/controller/pci-host-common.c +++ b/drivers/pci/controller/pci-host-common.c @@ -68,6 +68,10 @@ int pci_host_common_init(struct platform_device *pdev, if (IS_ERR(cfg)) return PTR_ERR(cfg); + /* Do not reassign bus numbers if probe only */ + if (!pci_has_flag(PCI_PROBE_ONLY)) + pci_add_flags(PCI_REASSIGN_ALL_BUS); + bridge->sysdata = cfg; bridge->ops = (struct pci_ops *)&ops->pci_ops; bridge->enable_device = ops->enable_device; From 76143cbb18dcb085d1cfcb4a71b6a03b050713ed Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Mon, 18 May 2026 15:27:14 +0800 Subject: [PATCH 0282/2758] PCI: imx6: Configure REF_USE_PAD before PHY reset for i.MX95 commit 0c26b1c34d12d4debfb5363cc0be6cdf68e87ba2 upstream. According to the i.MX95 PCIe PHY Databook, the ref_use_pad signal in the Common Block Signals section selects the reference clock source connected to the PHY pads. Per the specification, any change to this input must be followed by a PHY reset assertion to take effect. Move the REF_USE_PAD configuration before the PHY reset toggle to comply with the required initialization sequence. Fixes: 47f54a902dcd ("PCI: imx6: Toggle the core reset for i.MX95 PCIe") Signed-off-by: Richard Zhu [mani: renamed the callback and helper to match the usecase] Signed-off-by: Manivannan Sadhasivam Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260518072715.3166514-2-hongxing.zhu@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/pci/controller/dwc/pci-imx6.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index e35044cc52185..fa7c92e428c4c 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -137,6 +137,7 @@ struct imx_pcie_drvdata { const u32 mode_off[IMX_PCIE_MAX_INSTANCES]; const u32 mode_mask[IMX_PCIE_MAX_INSTANCES]; const struct pci_epc_features *epc_features; + int (*select_ref_clk_src)(struct imx_pcie *pcie); int (*init_phy)(struct imx_pcie *pcie); int (*enable_ref_clk)(struct imx_pcie *pcie, bool enable); int (*core_reset)(struct imx_pcie *pcie, bool assert); @@ -247,6 +248,24 @@ static unsigned int imx_pcie_grp_offset(const struct imx_pcie *imx_pcie) return imx_pcie->controller_id == 1 ? IOMUXC_GPR16 : IOMUXC_GPR14; } +static int imx95_pcie_select_ref_clk_src(struct imx_pcie *imx_pcie) +{ + bool ext = imx_pcie->enable_ext_refclk; + + /* + * Regarding the Signal Descriptions of i.MX95 PCIe PHY, ref_use_pad is + * used to select reference clock connected to a pair of pads. + * + * Any change in this input must be followed by phy_reset assertion. + */ + + regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_GEN_CTRL, + IMX95_PCIE_REF_USE_PAD, + ext ? IMX95_PCIE_REF_USE_PAD : 0); + + return 0; +} + static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie) { bool ext = imx_pcie->enable_ext_refclk; @@ -269,9 +288,6 @@ static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie) IMX95_PCIE_PHY_CR_PARA_SEL, IMX95_PCIE_PHY_CR_PARA_SEL); - regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_GEN_CTRL, - IMX95_PCIE_REF_USE_PAD, - ext ? IMX95_PCIE_REF_USE_PAD : 0); regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0, IMX95_PCIE_REF_CLKEN, ext ? 0 : IMX95_PCIE_REF_CLKEN); @@ -1256,6 +1272,9 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp) pp->bridge->disable_device = imx_pcie_disable_device; } + if (imx_pcie->drvdata->select_ref_clk_src) + imx_pcie->drvdata->select_ref_clk_src(imx_pcie); + imx_pcie_assert_core_reset(imx_pcie); imx_pcie_assert_perst(imx_pcie, true); @@ -1967,6 +1986,7 @@ static const struct imx_pcie_drvdata drvdata[] = { .mode_mask[0] = IMX95_PCIE_DEVICE_TYPE, .core_reset = imx95_pcie_core_reset, .init_phy = imx95_pcie_init_phy, + .select_ref_clk_src = imx95_pcie_select_ref_clk_src, .wait_pll_lock = imx95_pcie_wait_for_phy_pll_lock, .enable_ref_clk = imx95_pcie_enable_ref_clk, .clr_clkreq_override = imx95_pcie_clr_clkreq_override, @@ -2022,6 +2042,7 @@ static const struct imx_pcie_drvdata drvdata[] = { .ltssm_mask = IMX95_PCIE_LTSSM_EN, .mode_off[0] = IMX95_PE0_GEN_CTRL_1, .mode_mask[0] = IMX95_PCIE_DEVICE_TYPE, + .select_ref_clk_src = imx95_pcie_select_ref_clk_src, .init_phy = imx95_pcie_init_phy, .core_reset = imx95_pcie_core_reset, .wait_pll_lock = imx95_pcie_wait_for_phy_pll_lock, From edefa5f4b70159f90162b2b9982c12c98fbd0479 Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Thu, 19 Mar 2026 17:08:44 +0800 Subject: [PATCH 0283/2758] PCI: imx6: Fix IMX6SX_GPR12_PCIE_TEST_POWERDOWN handling commit aad953fb4eed0df5486cd54ccad80ac197678e01 upstream. The IMX6SX_GPR12_PCIE_TEST_POWERDOWN bit does not control the PCIe reference clock on i.MX6SX. Instead, it is part of i.MX6SX PCIe core reset sequence. Move the IMX6SX_GPR12_PCIE_TEST_POWERDOWN assertion/deassertion into the core reset functions to properly reflect its purpose. Remove the .enable_ref_clk() callback for i.MX6SX since it was incorrectly manipulating this bit. Fixes: e3c06cd063d6 ("PCI: imx6: Add initial imx6sx support") Signed-off-by: Richard Zhu Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Frank Li Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260319090844.444987-1-hongxing.zhu@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/pci/controller/dwc/pci-imx6.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index fa7c92e428c4c..4dabc8cc05e4b 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -681,14 +681,6 @@ static int imx_pcie_attach_pd(struct device *dev) return 0; } -static int imx6sx_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) -{ - regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12, - IMX6SX_GPR12_PCIE_TEST_POWERDOWN, - enable ? 0 : IMX6SX_GPR12_PCIE_TEST_POWERDOWN); - return 0; -} - static int imx6q_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) { if (enable) { @@ -802,6 +794,9 @@ static int imx6sx_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert) if (assert) regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX6SX_GPR12_PCIE_TEST_POWERDOWN); + else + regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12, + IMX6SX_GPR12_PCIE_TEST_POWERDOWN); /* Force PCIe PHY reset */ regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR5, IMX6SX_GPR5_PCIE_BTNRST_RESET, @@ -1896,7 +1891,6 @@ static const struct imx_pcie_drvdata drvdata[] = { .mode_off[0] = IOMUXC_GPR12, .mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE, .init_phy = imx6sx_pcie_init_phy, - .enable_ref_clk = imx6sx_pcie_enable_ref_clk, .core_reset = imx6sx_pcie_core_reset, .ops = &imx_pcie_host_ops, }, From 6a2363bf9eae1fedbd35a28c66f97c85899b8a3e Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Mon, 18 May 2026 15:27:15 +0800 Subject: [PATCH 0284/2758] PCI: imx6: Assert ref_clk_en after reference clock stabilizes on i.MX95 commit 9dda3f83ba677b9cc2613cecd9120123000ae50f upstream. According to the PHY Databook Common Block Signals section, the ref_clk_en signal must remain de-asserted until the reference clock is running at the appropriate frequency. Once the clock is stable, ref_clk_en can be asserted. For lower power states where the reference clock to the PHY is disabled, ref_clk_en should also be de-asserted. Move the ref_clk_en bit manipulation into imx95_pcie_enable_ref_clk() to ensure the reference clock stabilizes before ref_clk_en is asserted and before the PHY reset is de-asserted. This aligns with the timing requirements specified in the PHY documentation. Fixes: d8574ce57d76 ("PCI: imx6: Add external reference clock input mode support") Signed-off-by: Richard Zhu Signed-off-by: Manivannan Sadhasivam Reviewed-by: Frank Li Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260518072715.3166514-3-hongxing.zhu@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/pci/controller/dwc/pci-imx6.c | 28 +++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 4dabc8cc05e4b..38b2d8262e516 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -268,8 +268,6 @@ static int imx95_pcie_select_ref_clk_src(struct imx_pcie *imx_pcie) static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie) { - bool ext = imx_pcie->enable_ext_refclk; - /* * ERR051624: The Controller Without Vaux Cannot Exit L23 Ready * Through Beacon or PERST# De-assertion @@ -288,10 +286,6 @@ static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie) IMX95_PCIE_PHY_CR_PARA_SEL, IMX95_PCIE_PHY_CR_PARA_SEL); - regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0, - IMX95_PCIE_REF_CLKEN, - ext ? 0 : IMX95_PCIE_REF_CLKEN); - return 0; } @@ -740,7 +734,29 @@ static void imx95_pcie_clkreq_override(struct imx_pcie *imx_pcie, bool enable) static int imx95_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) { + bool ext = imx_pcie->enable_ext_refclk; + imx95_pcie_clkreq_override(imx_pcie, enable); + /* + * The ref_clk_en signal must remain de-asserted until the + * reference clock is running at appropriate frequency, at which + * point this bit can be asserted. For lower power states where + * the reference clock to the PHY is disabled, it may also be + * de-asserted. + * +------------------- -+--------+----------------+ + * | External clock mode | Enable | PCIE_REF_CLKEN | + * +---------------------+--------+----------------+ + * | TRUE | X | 1b'0 | + * +---------------------+--------+----------------+ + * | FALSE | TRUE | 1b'1 | + * +---------------------+--------+----------------+ + * | FALSE | FALSE | 1b'0 | + * +---------------------+--------+----------------+ + */ + regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0, + IMX95_PCIE_REF_CLKEN, + ext || !enable ? 0 : IMX95_PCIE_REF_CLKEN); + return 0; } From df77314b3bedbd9ad5d6f0682f98b99e3c5f7e2e Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 21 May 2026 23:16:17 +0530 Subject: [PATCH 0285/2758] PCI: mediatek: Fix IRQ domain leak when port fails to enable commit f865a57896bd92d7662eb2818d8f48872e2cbbc7 upstream. When mtk_pcie_enable_port() fails, mtk_pcie_port_free() removes the port from pcie->ports and frees the port structure. However, the IRQ domains set up earlier by mtk_pcie_init_irq_domain() are never freed. Fix this by refactoring mtk_pcie_irq_teardown() into a per-port helper, mtk_pcie_irq_teardown_port(), and calling it from mtk_pcie_setup() when mtk_pcie_enable_port() fails. Since the IRQ teardown must only happen in the probe error path (during resume, child devices may have active MSI mappings and the NOIRQ context prohibits sleeping locks), mtk_pcie_enable_port() is changed to return an error code so callers can distinguish the two paths and act accordingly. This issue was reported by Sashiko while reviewing the EcoNet EN7528 SoC support series. Fixes: b099631df160 ("PCI: mediatek: Add controller support for MT2712 and MT7622") Signed-off-by: Manivannan Sadhasivam Signed-off-by: Manivannan Sadhasivam Cc: stable@vger.kernel.org # 5.10 Cc: Caleb James DeLisle Link: https://patch.msgid.link/20260521174617.17692-1-mani@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/pci/controller/pcie-mediatek.c | 63 ++++++++++++++++---------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index 75722524fe744..907ae4285ecb1 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -529,23 +529,27 @@ static void mtk_pcie_enable_msi(struct mtk_pcie_port *port) writel(val, port->base + PCIE_INT_MASK); } -static void mtk_pcie_irq_teardown(struct mtk_pcie *pcie) +static void mtk_pcie_irq_teardown_port(struct mtk_pcie_port *port) { - struct mtk_pcie_port *port, *tmp; + irq_set_chained_handler_and_data(port->irq, NULL, NULL); - list_for_each_entry_safe(port, tmp, &pcie->ports, list) { - irq_set_chained_handler_and_data(port->irq, NULL, NULL); + if (port->irq_domain) + irq_domain_remove(port->irq_domain); - if (port->irq_domain) - irq_domain_remove(port->irq_domain); + if (IS_ENABLED(CONFIG_PCI_MSI)) { + if (port->inner_domain) + irq_domain_remove(port->inner_domain); + } - if (IS_ENABLED(CONFIG_PCI_MSI)) { - if (port->inner_domain) - irq_domain_remove(port->inner_domain); - } + irq_dispose_mapping(port->irq); +} - irq_dispose_mapping(port->irq); - } +static void mtk_pcie_irq_teardown(struct mtk_pcie *pcie) +{ + struct mtk_pcie_port *port, *tmp; + + list_for_each_entry_safe(port, tmp, &pcie->ports, list) + mtk_pcie_irq_teardown_port(port); } static int mtk_pcie_intx_map(struct irq_domain *domain, unsigned int irq, @@ -865,7 +869,7 @@ static int mtk_pcie_startup_port_an7583(struct mtk_pcie_port *port) return mtk_pcie_startup_port_v2(port); } -static void mtk_pcie_enable_port(struct mtk_pcie_port *port) +static int mtk_pcie_enable_port(struct mtk_pcie_port *port) { struct mtk_pcie *pcie = port->pcie; struct device *dev = pcie->dev; @@ -874,7 +878,7 @@ static void mtk_pcie_enable_port(struct mtk_pcie_port *port) err = clk_prepare_enable(port->sys_ck); if (err) { dev_err(dev, "failed to enable sys_ck%d clock\n", port->slot); - goto err_sys_clk; + return err; } err = clk_prepare_enable(port->ahb_ck); @@ -922,11 +926,15 @@ static void mtk_pcie_enable_port(struct mtk_pcie_port *port) goto err_phy_on; } - if (!pcie->soc->startup(port)) - return; + err = pcie->soc->startup(port); + if (err) { + dev_info(dev, "Port%d link down\n", port->slot); + goto err_soc_startup; + } - dev_info(dev, "Port%d link down\n", port->slot); + return 0; +err_soc_startup: phy_power_off(port->phy); err_phy_on: phy_exit(port->phy); @@ -942,8 +950,8 @@ static void mtk_pcie_enable_port(struct mtk_pcie_port *port) clk_disable_unprepare(port->ahb_ck); err_ahb_clk: clk_disable_unprepare(port->sys_ck); -err_sys_clk: - mtk_pcie_port_free(port); + + return err; } static int mtk_pcie_parse_port(struct mtk_pcie *pcie, @@ -1109,8 +1117,13 @@ static int mtk_pcie_setup(struct mtk_pcie *pcie) return err; /* enable each port, and then check link status */ - list_for_each_entry_safe(port, tmp, &pcie->ports, list) - mtk_pcie_enable_port(port); + list_for_each_entry_safe(port, tmp, &pcie->ports, list) { + err = mtk_pcie_enable_port(port); + if (err) { + mtk_pcie_irq_teardown_port(port); + mtk_pcie_port_free(port); + } + } /* power down PCIe subsys if slots are all empty (link down) */ if (list_empty(&pcie->ports)) @@ -1209,14 +1222,18 @@ static int mtk_pcie_resume_noirq(struct device *dev) { struct mtk_pcie *pcie = dev_get_drvdata(dev); struct mtk_pcie_port *port, *tmp; + int err; if (list_empty(&pcie->ports)) return 0; clk_prepare_enable(pcie->free_ck); - list_for_each_entry_safe(port, tmp, &pcie->ports, list) - mtk_pcie_enable_port(port); + list_for_each_entry_safe(port, tmp, &pcie->ports, list) { + err = mtk_pcie_enable_port(port); + if (err) + mtk_pcie_port_free(port); + } /* In case of EP was removed while system suspend. */ if (list_empty(&pcie->ports)) From 67a8b1d876d5a7990e0d23a3510df013a50f5376 Mon Sep 17 00:00:00 2001 From: Yadu M G Date: Thu, 4 Jun 2026 17:54:18 +0530 Subject: [PATCH 0286/2758] PCI: qcom: Initialize DWC MSI lock for firmware-managed ECAM hosts commit e0779713a1e2f891aeec53e629dbbd33f423c629 upstream. A lockdep warning is observed during boot on a Qcom firmware-managed platform: INFO: trying to register non-static key. The code is fine but needs lockdep annotation, or maybe you didn't initialize this object before use? turning off the locking correctness validator. ... Call trace: register_lock_class+0x128/0x4d8 __lock_acquire+0x110/0x1db0 lock_acquire+0x278/0x3d8 _raw_spin_lock_irq+0x6c/0xc0 dw_pcie_irq_domain_alloc+0x48/0x190 irq_domain_alloc_irqs_parent+0x2c/0x48 msi_domain_alloc+0x90/0x160 ... dw_pcie_irq_domain_alloc() takes pp->lock while allocating MSI interrupts. pp->lock is normally initialized by dw_pcie_host_init(), but Qcom firmware-managed hosts use the ECAM init path instead: pci_host_common_ecam_create() pci_ecam_create() qcom_pcie_ecam_host_init() dw_pcie_msi_host_init() dw_pcie_allocate_domains() That path constructs a fresh struct dw_pcie_rp and calls dw_pcie_msi_host_init() directly, without going through dw_pcie_host_init(). As a result, pp->lock was not initialized, which triggers the warning. Initialize pp->lock in qcom_pcie_ecam_host_init() before registering the MSI domains so the firmware-managed ECAM path matches the normal DWC host initialization sequence. Fixes: 7d944c0f1469 ("PCI: qcom: Add support for Qualcomm SA8255p based PCIe Root Complex") Signed-off-by: Yadu M G [mani: added fixes tag and CCed stable] Signed-off-by: Manivannan Sadhasivam Cc: stable@kernel.org Link: https://patch.msgid.link/20260604122418.727274-1-yadu.mg@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/pci/controller/dwc/pcie-qcom.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index af6bf5cce65be..7d8eaef436efe 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -1674,6 +1674,12 @@ static int qcom_pcie_ecam_host_init(struct pci_config_window *cfg) pci->dbi_base = cfg->win; pp->num_vectors = MSI_DEF_NUM_VECTORS; + /* + * dw_pcie_msi_host_init() is called directly here, bypassing + * dw_pcie_host_init() where pp->lock is normally initialized. + */ + raw_spin_lock_init(&pp->lock); + ret = dw_pcie_msi_host_init(pp); if (ret) return ret; From 7908ddb6f8b3e77df191d2fda3969ecfce4538e9 Mon Sep 17 00:00:00 2001 From: Marco Nenciarini Date: Fri, 17 Apr 2026 15:24:36 +0200 Subject: [PATCH 0287/2758] PCI: Skip Resizable BAR restore on read error commit ee7471fe968d210939be9046089a924cd23c8c3b upstream. pci_restore_rebar_state() uses the Resizable BAR Control register to decide how many BARs to restore (nbars) and which BAR each iteration addresses (bar_idx). When a device does not respond, config reads typically return PCI_ERROR_RESPONSE (~0). Both fields are 3 bits wide, so nbars and bar_idx both evaluate to 7, past the spec's valid ranges for both fields. pci_resource_n() then returns an unrelated resource slot, whose size is used to derive a nonsensical value written back to the Resizable BAR Control register. Bail out if any Resizable BAR Control read returns PCI_ERROR_RESPONSE. No further BARs are touched, which is safe because a config read that returns PCI_ERROR_RESPONSE indicates the device is unreachable and restoration is pointless. Fixes: d3252ace0bc6 ("PCI: Restore resized BAR state on resume") Signed-off-by: Marco Nenciarini Signed-off-by: Bjorn Helgaas Cc: stable@vger.kernel.org Link: https://patch.msgid.link/666cac19b5daa0ab0e0ab64454e76b4d24465dbd.1776429882.git.mnencia@kcore.it Signed-off-by: Greg Kroah-Hartman --- drivers/pci/rebar.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/pci/rebar.c b/drivers/pci/rebar.c index 39f8cf3b70d57..11965947c4cb5 100644 --- a/drivers/pci/rebar.c +++ b/drivers/pci/rebar.c @@ -231,6 +231,9 @@ void pci_restore_rebar_state(struct pci_dev *pdev) return; pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); + if (PCI_POSSIBLE_ERROR(ctrl)) + return; + nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl); for (i = 0; i < nbars; i++, pos += 8) { @@ -238,6 +241,9 @@ void pci_restore_rebar_state(struct pci_dev *pdev) int bar_idx, size; pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); + if (PCI_POSSIBLE_ERROR(ctrl)) + return; + bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX; res = pci_resource_n(pdev, bar_idx); size = pci_rebar_bytes_to_size(resource_size(res)); From 55fd485e66d0ad5c762c23dba1461fe9c741cd96 Mon Sep 17 00:00:00 2001 From: Marco Nenciarini Date: Fri, 17 Apr 2026 15:24:37 +0200 Subject: [PATCH 0288/2758] PCI/IOV: Skip VF Resizable BAR restore on read error commit f34f1712229d71ce4286440fef12526fd4590b37 upstream. sriov_restore_vf_rebar_state() uses the VF Resizable BAR Control register to decide how many VF BARs to restore (nbars) and which VF BAR each iteration addresses (bar_idx). bar_idx indexes into dev->sriov->barsz[], which has only PCI_SRIOV_NUM_BARS (6) entries. When a device does not respond, config reads typically return PCI_ERROR_RESPONSE (~0). Both fields are 3 bits wide, so nbars and bar_idx both evaluate to 7. The barsz[] access then goes out of bounds. UBSAN reports this as: UBSAN: array-index-out-of-bounds in drivers/pci/iov.c:948:51 index 7 is out of range for type 'resource_size_t [6]' Observed on an NVIDIA RTX PRO 1000 GPU (GB207GLM) that stopped responding during a failed GC6 power state exit. The subsequent pci_restore_state() invoked sriov_restore_vf_rebar_state() while config reads returned 0xffffffff, triggering the splat. Bail out if any VF Resizable BAR Control read returns PCI_ERROR_RESPONSE. No further VF BARs are touched, which is safe because a config read that returns PCI_ERROR_RESPONSE indicates the device is unreachable and restoration is pointless. This mirrors the guard in pci_restore_rebar_state(). Fixes: 5a8f77e24a30 ("PCI/IOV: Restore VF resizable BAR state after reset") Signed-off-by: Marco Nenciarini Signed-off-by: Bjorn Helgaas Cc: stable@vger.kernel.org Link: https://patch.msgid.link/44a4ae53ec2825816b816c85cd378430d9a95cc6.1776429882.git.mnencia@kcore.it Signed-off-by: Greg Kroah-Hartman --- drivers/pci/iov.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 91ac4e37ecb9c..08df9bace13d1 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -938,12 +938,18 @@ static void sriov_restore_vf_rebar_state(struct pci_dev *dev) return; pci_read_config_dword(dev, pos + PCI_VF_REBAR_CTRL, &ctrl); + if (PCI_POSSIBLE_ERROR(ctrl)) + return; + nbars = FIELD_GET(PCI_VF_REBAR_CTRL_NBAR_MASK, ctrl); for (i = 0; i < nbars; i++, pos += 8) { int bar_idx, size; pci_read_config_dword(dev, pos + PCI_VF_REBAR_CTRL, &ctrl); + if (PCI_POSSIBLE_ERROR(ctrl)) + return; + bar_idx = FIELD_GET(PCI_VF_REBAR_CTRL_BAR_IDX, ctrl); size = pci_rebar_bytes_to_size(dev->sriov->barsz[bar_idx]); ctrl &= ~PCI_VF_REBAR_CTRL_BAR_SIZE; From 4caf12c778fed3dc3824cf36263be5e2c491fbd0 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Thu, 25 Jun 2026 19:21:39 +0100 Subject: [PATCH 0289/2758] tcp: restore RCU grace period in tcp_ao_destroy_sock commit 8bc4d43bccbd60efe85d0a44d5bf41762f2f0c30 upstream. Commit 51e547e8c89c ("tcp: Free TCP-AO/TCP-MD5 info/keys without RCU") removed the call_rcu() callback from tcp_ao_destroy_sock(), arguing that "the destruction of info/keys is delayed until the socket destructor" and therefore "no one can discover it anymore". That argument does not hold for the call site in tcp_connect() (net/ipv4/tcp_output.c:4327-4332). At that point the socket is in TCP_SYN_SENT, has already been inserted into the inet ehash by inet_hash_connect() in tcp_v4_connect(), and is therefore very much discoverable: any softirq running tcp_v4_rcv() on another CPU can take the socket out of the ehash, walk into tcp_inbound_hash(), and load tp->ao_info via implicit RCU before bh_lock_sock_nested() is taken on the destroying CPU. The reader path then enters __tcp_ao_do_lookup() (net/ipv4/tcp_ao.c:208) which re-loads tp->ao_info via rcu_dereference_check(); the re-load can still observe the (about-to-be-freed) pointer because there is no synchronize_rcu() between rcu_assign_pointer(tp->ao_info, NULL) and tcp_ao_info_free() in tcp_ao_destroy_sock(). The captured pointer is then walked at line 223: hlist_for_each_entry_rcu(key, &ao->head, node, ...) The writer's synchronous kfree() is free to complete between the line 218 re-fetch and the line 223 hlist iteration. The slab is reused (or simply LIST_POISON1-stamped if not yet reused) and the iteration walks attacker-controlled or poison memory in softirq context. Reproducer (no debug shim, stock x86_64 v7.1-rc2 SMP+KASAN, QEMU+KVM): an unprivileged uid=1000 process inside CLONE_NEWUSER|CLONE_NEWNET installs TCP_MD5SIG + TCP_AO_ADD_KEY on a TCP socket, sprays forged TCP-AO segments toward its eventual 4-tuple via raw sockets, then calls connect(). The md5-wins reconciliation in tcp_connect() fires tcp_ao_destroy_sock(); the softirq backlog reader on the loopback NAPI path crashes on the freed ao->head.first walk: Oops: general protection fault, probably for non-canonical address 0xfbd59c000000002f KASAN: maybe wild-memory-access in range [0xdead000000000178-0xdead00000000017f] CPU: 0 UID: 1000 PID: 100 Comm: repro_userns RIP: 0010:__tcp_ao_do_lookup+0x107/0x1c0 Call Trace: __tcp_ao_do_lookup+0x107/0x1c0 tcp_ao_inbound_lookup.constprop.0+0x12a/0x200 tcp_inbound_ao_hash+0x5ea/0x1520 tcp_inbound_hash+0x7ce/0x1240 tcp_v4_rcv+0x1e7a/0x3e10 ... Restore the RCU grace period: re-add struct rcu_head to tcp_ao_info and replace the synchronous tcp_ao_info_free() with a call_rcu() callback. Readers that captured tp->ao_info before rcu_assign_pointer NULLed it now see the object remain valid until rcu_read_unlock(). With the patch applied the reproducer runs cleanly for 2000 iterations on the same kernel build. Fixes: 51e547e8c89c ("tcp: Free TCP-AO/TCP-MD5 info/keys without RCU") Cc: stable@vger.kernel.org # v6.18+ Reviewed-by: Dmitry Safonov Signed-off-by: Michael Bommarito Reviewed-by: Eric Dumazet Signed-off-by: Dmitry Safonov <0x7f454c46@gmail.com> Link: https://patch.msgid.link/20260625-tcp-md5-connect-v3-1-1fd313d6c1e0@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- include/net/tcp_ao.h | 1 + net/ipv4/tcp_ao.c | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index 1e9e27d6e06ba..df655ce6987d3 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -130,6 +130,7 @@ struct tcp_ao_info { u32 snd_sne; u32 rcv_sne; refcount_t refcnt; /* Protects twsk destruction */ + struct rcu_head rcu; }; #ifdef CONFIG_TCP_MD5SIG diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index 36984e9050700..69c11bfd8f2ba 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -270,8 +270,9 @@ static void tcp_ao_key_free_rcu(struct rcu_head *head) kfree_sensitive(key); } -static void tcp_ao_info_free(struct tcp_ao_info *ao) +static void tcp_ao_info_free_rcu(struct rcu_head *head) { + struct tcp_ao_info *ao = container_of(head, struct tcp_ao_info, rcu); struct tcp_ao_key *key; struct hlist_node *n; @@ -311,7 +312,7 @@ void tcp_ao_destroy_sock(struct sock *sk, bool twsk) if (!twsk) tcp_ao_sk_omem_free(sk, ao); - tcp_ao_info_free(ao); + call_rcu(&ao->rcu, tcp_ao_info_free_rcu); } void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp) From 76e415ea88d20f022ed5cfcf78c50e156a267e91 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Tue, 23 Jun 2026 06:58:31 -0700 Subject: [PATCH 0290/2758] mm/damon/ops-common: handle extreme intervals in damon_hot_score() commit 35d4a3cf70a855b50e53189ac2f8463e20a02046 upstream. Fix three issues in damon_hot_score() that comes from wrong handling of extreme (zero or too high) monitoring intervals user setup. When the user sets sampling interval zero, damon_max_nr_accesses(), which is called from damon_hot_score(), causes a divide-by-zero. Needless to say, it is a problem. When the user sets the aggregation interval zero, the function returns zero. It is wrong, since the real maximum nr_acceses in the setup should be one. Worse yet, it can cause another divide-by-zero from its caller, damon_hot_score(), since it uses damon_max_nr_accesses() return value as a denominator. When the user sets the aggregation interval very high, damon_hot_score() could return a value out of [0, DAMOS_MAX_SCORE] range. Since the return value is used as an index to the regions_score_histogram array, which is DAMOS_MAX_SCORE+1 size, it causes out of bounds array access. The issues can be relatively easily reproduced like below. The sysfs write permission is required, though. # ./damo start --damos_action lru_prio --damos_quota_space 100M \ --damos_quota_interval 1s # cd /sys/kernel/mm/damon/admin/kdamonds/0 # echo 0 > contexts/0/monitoring_attrs/intervals/sample_us # echo 0 > contexts/0/monitoring_attrs/intervals/aggr_us # echo commit > state # dmesg [...] [ 131.329762] Oops: divide error: 0000 [#1] SMP NOPTI [...] [ 131.336089] RIP: 0010:damon_hot_score+0x27/0xd0 [...] Fix the divide-by-zero intervals problems by explicitly handling the zero intervals in damon_max_nr_accesses(). Fix the out-of-bound array access by applying [0, DAMOS_MAX_SCORE] bounds before returning from damon_hot_score(). The issue was discovered [1] by Sashiko. Link: https://lore.kernel.org/20260623135834.67189-1-sj@kernel.org Link: https://lore.kernel.org/20260619202459.145010-1-sj@kernel.org [1] Fixes: 198f0f4c58b9 ("mm/damon/vaddr,paddr: support pageout prioritization") Signed-off-by: SeongJae Park Cc: # 5.16.x Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- include/linux/damon.h | 8 ++++++-- mm/damon/ops-common.c | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/include/linux/damon.h b/include/linux/damon.h index f2cdb7c3f5e6c..18c19d41c58fa 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -979,9 +979,13 @@ static inline bool damon_target_has_pid(const struct damon_ctx *ctx) static inline unsigned int damon_max_nr_accesses(const struct damon_attrs *attrs) { - /* {aggr,sample}_interval are unsigned long, hence could overflow */ - return min(attrs->aggr_interval / attrs->sample_interval, + unsigned long sample_interval; + unsigned long max_nr_accesses; + + sample_interval = attrs->sample_interval ? : 1; + max_nr_accesses = min(attrs->aggr_interval / sample_interval, (unsigned long)UINT_MAX); + return max_nr_accesses ? : 1; } diff --git a/mm/damon/ops-common.c b/mm/damon/ops-common.c index c3e4c871b0bb2..4752d6118eb93 100644 --- a/mm/damon/ops-common.c +++ b/mm/damon/ops-common.c @@ -140,6 +140,7 @@ int damon_hot_score(struct damon_ctx *c, struct damon_region *r, * Transform it to fit in [0, DAMOS_MAX_SCORE] */ hotness = hotness * DAMOS_MAX_SCORE / DAMON_MAX_SUBSCORE; + hotness = max(min(hotness, DAMOS_MAX_SCORE), 0); return hotness; } From e8ee198bbc04a32d336e79160fde980e0235b39f Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Wed, 24 Jun 2026 18:00:06 -0700 Subject: [PATCH 0291/2758] netfilter: ipset: fix race between dump and ip_set_list resize commit 7cd9103283b26b917360ec99d7d2f2d761bcf1ab upstream. The release path of ip_set_dump_do() and ip_set_dump_done() read inst->ip_set_list via ip_set_ref_netlink(), a plain rcu_dereference_raw() of the array pointer. These run from netlink_recvmsg() without the nfnl mutex and without an RCU read-side critical section. A concurrent ip_set_create() can grow the array: it publishes the new array, calls synchronize_net() and then kvfree()s the old one. Since the dump paths read the array outside any RCU reader, synchronize_net() does not wait for them and the old array can be freed while they still index into it, causing a use-after-free. The dumped set itself stays pinned via set->ref_netlink, so only the array load needs protecting. Take rcu_read_lock() around it, matching ip_set_get_byname() and __ip_set_put_byindex(). BUG: KASAN: slab-use-after-free in ip_set_dump_do (net/netfilter/ipset/ip_set_core.c:1697) Read of size 8 at addr ffff88800b5c4018 by task exploit/150 Call Trace: ... kasan_report (mm/kasan/report.c:595) ip_set_dump_do (net/netfilter/ipset/ip_set_core.c:1697) netlink_dump (net/netlink/af_netlink.c:2325) netlink_recvmsg (net/netlink/af_netlink.c:1976) sock_recvmsg (net/socket.c:1159) __sys_recvfrom (net/socket.c:2315) ... Oops: general protection fault, probably for non-canonical address ... KASAN NOPTI KASAN: maybe wild-memory-access in range [0x02d6...d0-0x02d6...d7] RIP: 0010:ip_set_dump_do (net/netfilter/ipset/ip_set_core.c:1698) Kernel panic - not syncing: Fatal exception Fixes: 8a02bdd50b2e ("netfilter: ipset: Fix calling ip_set() macro at dumping") Cc: stable@vger.kernel.org Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Acked-by: Jozsef Kadlecsik Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/ipset/ip_set_core.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 3706b4a85a0f1..955556513eeb6 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -1480,7 +1480,11 @@ ip_set_dump_done(struct netlink_callback *cb) struct ip_set_net *inst = (struct ip_set_net *)cb->args[IPSET_CB_NET]; ip_set_id_t index = (ip_set_id_t)cb->args[IPSET_CB_INDEX]; - struct ip_set *set = ip_set_ref_netlink(inst, index); + struct ip_set *set; + + rcu_read_lock(); + set = ip_set_ref_netlink(inst, index); + rcu_read_unlock(); if (set->variant->uref) set->variant->uref(set, cb, false); @@ -1686,7 +1690,9 @@ ip_set_dump_do(struct sk_buff *skb, struct netlink_callback *cb) release_refcount: /* If there was an error or set is done, release set */ if (ret || !cb->args[IPSET_CB_ARG0]) { + rcu_read_lock(); set = ip_set_ref_netlink(inst, index); + rcu_read_unlock(); if (set->variant->uref) set->variant->uref(set, cb, false); pr_debug("release set %s\n", set->name); From 64a4c0befa77bcc01076aec9f93863ffd4ed06b7 Mon Sep 17 00:00:00 2001 From: Ammar Faizi Date: Sun, 15 Mar 2026 21:18:08 +0700 Subject: [PATCH 0292/2758] virtio_pci: fix vq info pointer lookup via wrong index commit f7d380fb525c13bdd114369a1979c80c346e6abc upstream. Unbinding a virtio balloon device: echo virtio0 > /sys/bus/virtio/drivers/virtio_balloon/unbind triggers a NULL pointer dereference. The dmesg says: BUG: kernel NULL pointer dereference, address: 0000000000000008 [...] RIP: 0010:__list_del_entry_valid_or_report+0x5/0xf0 Call Trace: vp_del_vqs+0x121/0x230 remove_common+0x135/0x150 virtballoon_remove+0xee/0x100 virtio_dev_remove+0x3b/0x80 device_release_driver_internal+0x187/0x2c0 unbind_store+0xb9/0xe0 kernfs_fop_write_iter.llvm.11660790530567441834+0xf6/0x180 vfs_write+0x2a9/0x3b0 ksys_write+0x5c/0xd0 do_syscall_64+0x54/0x230 entry_SYSCALL_64_after_hwframe+0x29/0x31 [...] The virtio_balloon device registers 5 queues (inflate, deflate, stats, free_page, reporting) but only the first two are unconditional. The stats, free_page and reporting queues are each conditional on their respective feature bits. When any of these features are absent, the corresponding vqs_info entry has name == NULL, creating holes in the array. The root cause is an indexing mismatch introduced when vq info storage was changed to be passed as an argument. vp_find_vqs_msix() and vp_find_vqs_intx() store the info pointer at vp_dev->vqs[i], where 'i' is the caller's sparse array index. However, the virtqueue itself gets vq->index assigned from queue_idx, a dense index that skips NULL entries. When holes exist, 'i' and queue_idx diverge. Later, vp_del_vqs() looks up info via vp_dev->vqs[vq->index] using the dense index into the sparsely-populated array, and hits NULL. Fix this by storing info at vp_dev->vqs[queue_idx] instead of vp_dev->vqs[i], so the store index matches the lookup index (vq->index). Apply the fix to both the MSIX and INTX paths. Cc: Yichun Zhang Cc: Jiri Pirko Cc: stable@vger.kernel.org # v6.11+ Tested-by: Yuka Fixes: 89a1c435aec2 ("virtio_pci: pass vq info as an argument to vp_setup_vq()") Signed-off-by: Ammar Faizi Message-Id: <20260315141808.547081-1-ammarfaizi2@openresty.com> Signed-off-by: Michael S. Tsirkin Signed-off-by: Greg Kroah-Hartman --- drivers/virtio/virtio_pci_common.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c index da97b6a988deb..164f480b18a6f 100644 --- a/drivers/virtio/virtio_pci_common.c +++ b/drivers/virtio/virtio_pci_common.c @@ -423,10 +423,11 @@ static int vp_find_vqs_msix(struct virtio_device *vdev, unsigned int nvqs, vqs[i] = NULL; continue; } - vqs[i] = vp_find_one_vq_msix(vdev, queue_idx++, vqi->callback, + vqs[i] = vp_find_one_vq_msix(vdev, queue_idx, vqi->callback, vqi->name, vqi->ctx, false, &allocated_vectors, vector_policy, - &vp_dev->vqs[i]); + &vp_dev->vqs[queue_idx]); + queue_idx++; if (IS_ERR(vqs[i])) { err = PTR_ERR(vqs[i]); goto error_find; @@ -485,9 +486,10 @@ static int vp_find_vqs_intx(struct virtio_device *vdev, unsigned int nvqs, vqs[i] = NULL; continue; } - vqs[i] = vp_setup_vq(vdev, queue_idx++, vqi->callback, + vqs[i] = vp_setup_vq(vdev, queue_idx, vqi->callback, vqi->name, vqi->ctx, - VIRTIO_MSI_NO_VECTOR, &vp_dev->vqs[i]); + VIRTIO_MSI_NO_VECTOR, &vp_dev->vqs[queue_idx]); + queue_idx++; if (IS_ERR(vqs[i])) { err = PTR_ERR(vqs[i]); goto out_del_vqs; From 378493da2980dcc66bb9d028b047644b1cbc8924 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 27 Apr 2026 16:37:10 +0200 Subject: [PATCH 0293/2758] virtio-mmio: fix device release warning on module unload commit c687bc35694698ec4c7f92bf929c3d659f0cecb8 upstream. Driver core expects devices to be allocated dynamically and complains loudly when a device that lacks a release function is freed. Use __root_device_register() to allocate and register the root device instead of open coding using a static device. Note that root_device_register(), which also creates a link to the module, cannot be used as the device is registered when parsing the module parameters which happens before the module kobject has been set up. Fixes: 81a054ce0b46 ("virtio-mmio: Devices parameter parsing") Cc: stable@vger.kernel.org # 3.5 Cc: Pawel Moll Signed-off-by: Johan Hovold Signed-off-by: Michael S. Tsirkin Message-ID: <20260427143710.14702-1-johan@kernel.org> Signed-off-by: Greg Kroah-Hartman --- drivers/virtio/virtio_mmio.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c index 595c2274fbb53..510b7c4efdff8 100644 --- a/drivers/virtio/virtio_mmio.c +++ b/drivers/virtio/virtio_mmio.c @@ -662,9 +662,7 @@ static void virtio_mmio_remove(struct platform_device *pdev) #if defined(CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES) -static struct device vm_cmdline_parent = { - .init_name = "virtio-mmio-cmdline", -}; +static struct device *vm_cmdline_parent; static int vm_cmdline_parent_registered; static int vm_cmdline_id; @@ -672,7 +670,6 @@ static int vm_cmdline_id; static int vm_cmdline_set(const char *device, const struct kernel_param *kp) { - int err; struct resource resources[2] = {}; char *str; long long base, size; @@ -704,11 +701,10 @@ static int vm_cmdline_set(const char *device, resources[1].start = resources[1].end = irq; if (!vm_cmdline_parent_registered) { - err = device_register(&vm_cmdline_parent); - if (err) { - put_device(&vm_cmdline_parent); + vm_cmdline_parent = __root_device_register("virtio-mmio-cmdline", NULL); + if (IS_ERR(vm_cmdline_parent)) { pr_err("Failed to register parent device!\n"); - return err; + return PTR_ERR(vm_cmdline_parent); } vm_cmdline_parent_registered = 1; } @@ -719,7 +715,7 @@ static int vm_cmdline_set(const char *device, (unsigned long long)resources[0].end, (int)resources[1].start); - pdev = platform_device_register_resndata(&vm_cmdline_parent, + pdev = platform_device_register_resndata(vm_cmdline_parent, "virtio-mmio", vm_cmdline_id++, resources, ARRAY_SIZE(resources), NULL, 0); @@ -743,8 +739,12 @@ static int vm_cmdline_get_device(struct device *dev, void *data) static int vm_cmdline_get(char *buffer, const struct kernel_param *kp) { buffer[0] = '\0'; - device_for_each_child(&vm_cmdline_parent, buffer, - vm_cmdline_get_device); + + if (vm_cmdline_parent_registered) { + device_for_each_child(vm_cmdline_parent, buffer, + vm_cmdline_get_device); + } + return strlen(buffer) + 1; } @@ -766,9 +766,9 @@ static int vm_unregister_cmdline_device(struct device *dev, static void vm_unregister_cmdline_devices(void) { if (vm_cmdline_parent_registered) { - device_for_each_child(&vm_cmdline_parent, NULL, + device_for_each_child(vm_cmdline_parent, NULL, vm_unregister_cmdline_device); - device_unregister(&vm_cmdline_parent); + root_device_unregister(vm_cmdline_parent); vm_cmdline_parent_registered = 0; } } From 92d5736a62040ec1cfff23ea57e6599301690ad5 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 31 May 2026 10:22:51 -0400 Subject: [PATCH 0294/2758] hwrng: virtio: clamp device-reported used.len at copy_data() commit e3046eeada299f917a8ad883af4434bfb86556b1 upstream. random_recv_done() stores the device-reported used.len directly into vi->data_avail. copy_data() then indexes vi->data[] using vi->data_idx (advanced by previous copy_data() calls) and issues a memcpy() without re-validating either value against the posted buffer size sizeof(vi->data) (SMP_CACHE_BYTES bytes, typically 32 or 64). A malicious or buggy virtio-rng backend can set used.len beyond sizeof(vi->data), steering the memcpy() past the end of the inline array into adjacent kmalloc-1k slab bytes. hwrng_fillfn() mixes those bytes into the guest RNG, and guest root can also observe them directly via /dev/hwrng. Concrete impact is inside the guest: - Memory-safety / hardening: any virtio-rng backend that over-reports used.len causes the driver to read past vi->data into unrelated slab contents. hwrng_fillfn() is a kernel thread that runs as soon as the device is probed; no guest userspace interaction is required to first-trigger the OOB. - Cross-boundary leak (confidential-compute threat model): a malicious hypervisor cooperating with a malicious or compromised guest root userspace can use /dev/hwrng as a leak channel for guest-kernel heap data. The host sets a large used.len, guest root reads /dev/hwrng, and the returned bytes contain guest kernel slab contents that were adjacent to vi->data. In practice, confidential-compute guests (SEV-SNP, TDX) usually disable virtio-rng entirely, so this path is narrow, but the fix is still worth carrying because the underlying memory-safety bug contaminates the guest RNG on any host. KASAN confirms the OOB on a 7.1-rc4 guest whose virtio-rng backend has been patched to report used.len = 0x10000: BUG: KASAN: slab-out-of-bounds in virtio_read+0x394/0x5d0 Read of size 64 at addr ffff88800ae0ba20 by task hwrng/52 Call Trace: __asan_memcpy+0x23/0x60 virtio_read+0x394/0x5d0 hwrng_fillfn+0xb2/0x470 kthread+0x2cc/0x3a0 Allocated by task 1: probe_common+0xa5/0x660 virtio_dev_probe+0x549/0xbc0 The buggy address belongs to the object at ffff88800ae0b800 which belongs to the cache kmalloc-1k of size 1024 The buggy address is located 0 bytes to the right of allocated 544-byte region [ffff88800ae0b800, ffff88800ae0ba20) Same class of bug as commit c04db81cd028 ("net/9p: Fix buffer overflow in USB transport layer"), which hardened usb9pfs_rx_complete() against unchecked device-reported length in the USB 9p transport. With the clamp at point of use and array_index_nospec() in place, the same harness boots cleanly: copy_data() returns zero for the bogus report, the device-supplied bytes after data_idx are discarded, and the driver issues a fresh request. Fixes: f7f510ec1957 ("virtio: An entropy device, as suggested by hpa.") Cc: stable@vger.kernel.org Suggested-by: Michael S. Tsirkin Signed-off-by: Michael Bommarito Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael S. Tsirkin Message-ID: <20260531142251.2792061-1-michael.bommarito@gmail.com> Signed-off-by: Greg Kroah-Hartman --- drivers/char/hw_random/virtio-rng.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index 0ce02d7e5048e..5e83ffa105e41 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -69,8 +70,26 @@ static void request_entropy(struct virtrng_info *vi) static unsigned int copy_data(struct virtrng_info *vi, void *buf, unsigned int size) { - size = min_t(unsigned int, size, vi->data_avail); - memcpy(buf, vi->data + vi->data_idx, size); + unsigned int idx, avail; + + /* + * vi->data_avail was set from the device-reported used.len and + * vi->data_idx was advanced by previous copy_data() calls. A + * malicious or buggy virtio-rng backend can drive either past + * sizeof(vi->data). Clamp at point of use and harden the index + * with array_index_nospec() so the memcpy() below cannot be + * steered into adjacent slab memory, including under + * speculation. + */ + avail = min_t(unsigned int, vi->data_avail, sizeof(vi->data)); + if (vi->data_idx >= avail) { + vi->data_avail = 0; + request_entropy(vi); + return 0; + } + size = min_t(unsigned int, size, avail - vi->data_idx); + idx = array_index_nospec(vi->data_idx, sizeof(vi->data)); + memcpy(buf, vi->data + idx, size); vi->data_idx += size; vi->data_avail -= size; if (vi->data_avail == 0) From 8f50613bff228272577893aa10a346a2f3063e49 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 9 Jun 2026 13:37:36 -0400 Subject: [PATCH 0295/2758] USB: chaoskey: Fix slab-use-after-free in chaoskey_release() commit abf76d3239dee97b66e7241ad04811f1ce562e28 upstream. The chaoskey driver has a use-after-free bug in its release routine. If the user closes the device file after the USB device has been unplugged, a debugging log statement will try to access the usb_interface structure after it has been deallocated: BUG: KASAN: slab-use-after-free in dev_driver_string (drivers/base/core.c:2406) Read of size 8 at addr ffff888168e8a0b8 by task chaoskey_raw_re/10106 Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 Call Trace: dump_stack_lvl (lib/dump_stack.c:94 lib/dump_stack.c:120) print_report (mm/kasan/report.c:378 mm/kasan/report.c:482) kasan_report (mm/kasan/report.c:595) dev_driver_string (drivers/base/core.c:2406) __dynamic_dev_dbg (lib/dynamic_debug.c:906) chaoskey_release (drivers/usb/misc/chaoskey.c:323) __fput (fs/file_table.c:510) fput_close_sync (fs/file_table.c:615) __x64_sys_close (fs/open.c:1507 fs/open.c:1492 fs/open.c:1492) do_syscall_64 (arch/x86/entry/syscall_64.c:63 arch/x86/entry/syscall_64.c:94) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) The driver's last reference to the interface structure is dropped in the chaoskey_free() routine, so the code must not use the interface -- even in a debugging statement -- after that routine returns. (Exception: If we know that another reference is held by someone else, such as the device core while the disconnect routine runs, there's no problem. Thanks to Johan Hovold for pointing this out.) Since the bad access is part of an unimportant debugging statement, we can fix the problem simply by removing the whole statement. Reported-by: Shuangpeng Bai Closes: https://lore.kernel.org/linux-usb/20EC9664-054E-438B-B411-2145D347F97B@gmail.com/ Tested-by: Shuangpeng Bai Signed-off-by: Alan Stern Fixes: 66e3e591891d ("usb: Add driver for Altus Metrum ChaosKey device (v2)") Cc: stable Reviewed-by: Johan Hovold Link: https://patch.msgid.link/bb5b1dc6-eb59-43e1-8d26-51e658e88bbe@rowland.harvard.edu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/chaoskey.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/misc/chaoskey.c b/drivers/usb/misc/chaoskey.c index d8016540953fc..9c06f7775301f 100644 --- a/drivers/usb/misc/chaoskey.c +++ b/drivers/usb/misc/chaoskey.c @@ -320,7 +320,6 @@ static int chaoskey_release(struct inode *inode, struct file *file) mutex_unlock(&dev->lock); destruction: mutex_unlock(&chaoskey_list_lock); - usb_dbg(interface, "release success"); return rv; } From 642e04f5c292d04070ae6e4374fbf14cc40a2465 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Fri, 12 Jun 2026 13:20:05 +0800 Subject: [PATCH 0296/2758] usb: dwc3: run gadget disconnect from sleepable suspend context commit 010382937fb69892b3469ac4d30af072262f59e8 upstream. dwc3_gadget_suspend() takes dwc->lock with IRQs disabled and then calls dwc3_disconnect_gadget(). For async callbacks that helper only uses plain spin_unlock()/spin_lock(), so the gadget ->disconnect() callback still runs with IRQs disabled and any sleepable callback trips Lockdep. This issue was found by our static analysis tool and then manually reviewed against the current tree. The grounded PoC kept the dwc3_gadget_suspend() -> dwc3_disconnect_gadget() -> gadget_driver->disconnect() chain, and Lockdep reported: BUG: sleeping function called from invalid context gadget_disconnect+0x21/0x39 [vuln_msv] dwc3_gadget_suspend.constprop.0+0x2b/0x42 [vuln_msv] Keep the disconnect callback selection in one common helper, but add a sleepable suspend-side wrapper which snapshots the callback under dwc->lock and then runs it after spin_unlock_irqrestore(). The regular event path still uses the existing spin_unlock()/spin_lock() window. Fixes: c8540870af4c ("usb: dwc3: gadget: Improve dwc3_gadget_suspend() and dwc3_gadget_resume()") Cc: stable Signed-off-by: Runyu Xiao Acked-by: Thinh Nguyen Link: https://patch.msgid.link/20260612052005.3849659-1-runyu.xiao@seu.edu.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/gadget.c | 43 ++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 3d4ca68e584c7..1082e9c9afaa8 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -3934,15 +3934,48 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, } } +static bool dwc3_prepare_disconnect_gadget(struct dwc3 *dwc, + struct usb_gadget_driver **driver, + struct usb_gadget **gadget) +{ + if (!dwc->async_callbacks || !dwc->gadget_driver || + !dwc->gadget_driver->disconnect) + return false; + + *driver = dwc->gadget_driver; + *gadget = dwc->gadget; + + return true; +} + static void dwc3_disconnect_gadget(struct dwc3 *dwc) { - if (dwc->async_callbacks && dwc->gadget_driver->disconnect) { + struct usb_gadget_driver *driver; + struct usb_gadget *gadget; + + if (dwc3_prepare_disconnect_gadget(dwc, &driver, &gadget)) { spin_unlock(&dwc->lock); - dwc->gadget_driver->disconnect(dwc->gadget); + driver->disconnect(gadget); spin_lock(&dwc->lock); } } +static void dwc3_disconnect_gadget_sleepable(struct dwc3 *dwc) +{ + struct usb_gadget_driver *driver; + struct usb_gadget *gadget; + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + if (!dwc3_prepare_disconnect_gadget(dwc, &driver, &gadget)) { + spin_unlock_irqrestore(&dwc->lock, flags); + return; + } + + spin_unlock_irqrestore(&dwc->lock, flags); + driver->disconnect(gadget); +} + static void dwc3_suspend_gadget(struct dwc3 *dwc) { if (dwc->async_callbacks && dwc->gadget_driver->suspend) { @@ -4838,7 +4871,6 @@ EXPORT_SYMBOL_GPL(dwc3_gadget_exit); int dwc3_gadget_suspend(struct dwc3 *dwc) { - unsigned long flags; int ret; ret = dwc3_gadget_soft_disconnect(dwc); @@ -4852,10 +4884,7 @@ int dwc3_gadget_suspend(struct dwc3 *dwc) return -EAGAIN; } - spin_lock_irqsave(&dwc->lock, flags); - if (dwc->gadget_driver) - dwc3_disconnect_gadget(dwc); - spin_unlock_irqrestore(&dwc->lock, flags); + dwc3_disconnect_gadget_sleepable(dwc); return 0; } From 1947b6411460d68b54b13c536961933166937d05 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Thu, 18 Jun 2026 20:40:29 +0800 Subject: [PATCH 0297/2758] usb: misc: usbio: fix disconnect UAF in client teardown commit 0bfeec21984fedd32987f4e4c0cde34b445af404 upstream. usbio_disconnect() walks usbio->cli_list in reverse and uninitializes each auxiliary device. auxiliary_device_uninit() drops the device reference, and for an unbound child that can run usbio_auxdev_release() and free the containing struct usbio_client. list_for_each_entry_reverse() advances after the loop body by reading client->link.prev. If the current client is freed by auxiliary_device_uninit(), the iterator dereferences freed memory. Use list_for_each_entry_safe_reverse() so the previous client is cached before the body can drop the final reference. This preserves reverse teardown order while keeping the next iterator cursor independent of the current client's lifetime. Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in usbio_disconnect+0x12e/0x150 Call Trace: dump_stack_lvl+0x66/0xa0 print_report+0xce/0x630 ? usbio_disconnect+0x12e/0x150 ? srso_alias_return_thunk+0x5/0xfbef5 ? __virt_addr_valid+0x188/0x320 ? usbio_disconnect+0x12e/0x150 kasan_report+0xe0/0x110 ? usbio_disconnect+0x12e/0x150 usbio_disconnect+0x12e/0x150 usb_unbind_interface+0xf3/0x400 really_probe+0x316/0x660 __driver_probe_device+0x106/0x240 driver_probe_device+0x4a/0x110 __device_attach_driver+0xf1/0x1a0 ? __pfx___device_attach_driver+0x10/0x10 bus_for_each_drv+0xf9/0x160 ? __pfx_bus_for_each_drv+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? trace_hardirqs_on+0x18/0x130 ? srso_alias_return_thunk+0x5/0xfbef5 ? _raw_spin_unlock_irqrestore+0x44/0x60 __device_attach+0x133/0x2a0 ? __pfx___device_attach+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? do_raw_spin_unlock+0x9a/0x100 ? srso_alias_return_thunk+0x5/0xfbef5 device_initial_probe+0x55/0x70 bus_probe_device+0x4a/0xd0 device_add+0x9b9/0xc10 ? __pfx_device_add+0x10/0x10 ? _raw_spin_unlock_irqrestore+0x44/0x60 ? srso_alias_return_thunk+0x5/0xfbef5 ? lockdep_hardirqs_on_prepare+0xea/0x1a0 ? srso_alias_return_thunk+0x5/0xfbef5 ? usb_enable_lpm+0x3c/0x260 usb_set_configuration+0xb64/0xf20 usb_generic_driver_probe+0x5f/0x90 usb_probe_device+0x71/0x1b0 really_probe+0x46b/0x660 __driver_probe_device+0x106/0x240 driver_probe_device+0x4a/0x110 __device_attach_driver+0xf1/0x1a0 ? __pfx___device_attach_driver+0x10/0x10 bus_for_each_drv+0xf9/0x160 ? __pfx_bus_for_each_drv+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? trace_hardirqs_on+0x18/0x130 ? srso_alias_return_thunk+0x5/0xfbef5 ? _raw_spin_unlock_irqrestore+0x44/0x60 __device_attach+0x133/0x2a0 ? __pfx___device_attach+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? do_raw_spin_unlock+0x9a/0x100 ? srso_alias_return_thunk+0x5/0xfbef5 device_initial_probe+0x55/0x70 bus_probe_device+0x4a/0xd0 device_add+0x9b9/0xc10 ? __pfx_device_add+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? add_device_randomness+0xb7/0xf0 usb_new_device+0x492/0x870 hub_event+0x1b10/0x29c0 ? __pfx_hub_event+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_acquire+0x187/0x300 ? process_one_work+0x475/0xb90 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_release+0xc8/0x290 ? srso_alias_return_thunk+0x5/0xfbef5 process_one_work+0x4d7/0xb90 ? __pfx_process_one_work+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? srso_alias_return_thunk+0x5/0xfbef5 ? __list_add_valid_or_report+0x37/0xf0 ? __pfx_hub_event+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 worker_thread+0x2d8/0x570 ? __pfx_worker_thread+0x10/0x10 kthread+0x1ad/0x1f0 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x3c9/0x540 ? __pfx_ret_from_fork+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __switch_to+0x2e9/0x730 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1a/0x30 Fixes: 121a0f839dbb ("usb: misc: Add Intel USBIO bridge driver") Cc: stable Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Reviewed-by: Hans de Goede Acked-by: Sakari Ailus Link: https://patch.msgid.link/20260618124029.3704089-1-zzzccc427@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usbio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/misc/usbio.c b/drivers/usb/misc/usbio.c index 02d1e0760f0c4..0e5ccd2f04d81 100644 --- a/drivers/usb/misc/usbio.c +++ b/drivers/usb/misc/usbio.c @@ -518,7 +518,7 @@ static int usbio_resume(struct usb_interface *intf) static void usbio_disconnect(struct usb_interface *intf) { struct usbio_device *usbio = usb_get_intfdata(intf); - struct usbio_client *client; + struct usbio_client *client, *next; /* Wakeup any clients waiting for a reply */ usbio->rxdat_len = 0; @@ -535,7 +535,7 @@ static void usbio_disconnect(struct usb_interface *intf) usb_kill_urb(usbio->urb); usb_free_urb(usbio->urb); - list_for_each_entry_reverse(client, &usbio->cli_list, link) { + list_for_each_entry_safe_reverse(client, next, &usbio->cli_list, link) { auxiliary_device_delete(&client->auxdev); auxiliary_device_uninit(&client->auxdev); } From b713aa0cc344f10f7a9928a230b5f5e780d04078 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Tue, 9 Jun 2026 16:00:52 +0800 Subject: [PATCH 0298/2758] 6lowpan: fix NHC entry use-after-free on error path commit 1720db928e5a58ca7d75ac1d514c3b73fd7061a7 upstream. lowpan_nhc_do_uncompression() looks up an NHC descriptor while holding lowpan_nhc_lock. If the descriptor has no uncompress callback, the error path drops the lock before printing nhc->name. lowpan_nhc_del() removes descriptors under the same lock and then relies on synchronize_net() before the owning module can be unloaded. That only waits for net RX RCU readers. lowpan_header_decompress() is also exported and can be reached from callers that are not necessarily covered by the net core RX critical section, for example the Bluetooth 6LoWPAN L2CAP receive path. This leaves a race where one task drops lowpan_nhc_lock in the error path, another task unregisters and frees the matching descriptor after synchronize_net() returns, and the first task then dereferences nhc->name for the warning. With the post-unlock window widened, KASAN reports: BUG: KASAN: slab-use-after-free in lowpan_nhc_do_uncompression+0x1f4/0x220 Read of size 8 lowpan_nhc_do_uncompression lowpan_header_decompress Fix this by printing the warning before dropping lowpan_nhc_lock, so the descriptor name is read while unregister is still excluded. The malformed packet is still rejected with -ENOTSUPP. Fixes: 92aa7c65d295 ("6lowpan: add generic nhc layer interface") Cc: stable@vger.kernel.org Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Signed-off-by: Yizhou Zhao Acked-by: Alexander Aring Link: https://patch.msgid.link/20260609080054.4541-1-zhaoyz24@mails.tsinghua.edu.cn Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/6lowpan/nhc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/6lowpan/nhc.c b/net/6lowpan/nhc.c index 7b374595328de..a4dde85664f2f 100644 --- a/net/6lowpan/nhc.c +++ b/net/6lowpan/nhc.c @@ -117,9 +117,9 @@ int lowpan_nhc_do_uncompression(struct sk_buff *skb, return ret; } } else { - spin_unlock_bh(&lowpan_nhc_lock); netdev_warn(dev, "received nhc id for %s which is not implemented.\n", nhc->name); + spin_unlock_bh(&lowpan_nhc_lock); return -ENOTSUPP; } } else { From 69f17ac132a38974cf1defb480cef6b79d1ab768 Mon Sep 17 00:00:00 2001 From: Yuanhe Shu Date: Wed, 24 Jun 2026 14:17:15 +0800 Subject: [PATCH 0299/2758] tracing: Fix NULL pointer dereference in func_set_flag() commit c3e94604675e3db186111b8942650d86577df9b0 upstream. func_set_flag() dereferences tr->current_trace_flags before verifying that the current tracer is actually the function tracer. When the active tracer has been switched away from "function" (e.g., to "wakeup_rt"), tr->current_trace_flags can be NULL, leading to a NULL pointer dereference and kernel crash. The call chain that triggers this is: trace_options_write() -> __set_tracer_option() -> trace->set_flag() /* func_set_flag */ In func_set_flag(), the first operation is: if (!!set == !!(tr->current_trace_flags->val & bit)) This dereferences tr->current_trace_flags unconditionally. The safety check that guards against a non-function tracer: if (tr->current_trace != &function_trace) return 0; is placed *after* the dereference, which is too late. This was observed with the following crash dump: BUG: unable to handle page fault at 0000000000000000 RIP: func_set_flag+0xd Call Trace: __set_tracer_option+0x27 trace_options_write+0x75 vfs_write+0x12a ksys_write+0x66 do_syscall_64+0x5b RIP: ffffffff914c973d RSP: ff67ec88b01dfdf0 RFLAGS: 00010202 RAX: 0000000000000000 RBX: ff3a826e80354580 RCX: 0000000000000001 RDX: 0000000000000001 RSI: 0000000000000000 RDI: ffffffff93918080 The disassembly confirms the fault: func_set_flag+0: mov 0x1f08(%rdi), %rax ; RAX = tr->current_trace_flags = NULL func_set_flag+13: mov (%rax), %eax ; page fault: dereference NULL At the time of the crash: tr->current_trace_flags = 0x0 (NULL) tr->current_trace = wakeup_rt_tracer (not function_trace) The scenario is that a process opens a function tracer option file (such as "func_stack_trace"), then the current tracer is switched to another tracer (e.g., "wakeup_rt"), which sets current_trace_flags to NULL. When the process subsequently writes to the option file, func_set_flag() is invoked and crashes on the NULL dereference. Fix this by moving the current_trace check before the current_trace_flags dereference, so that func_set_flag() returns early when the function tracer is not active. Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260624061715.1445655-1-xiangzao@linux.alibaba.com Fixes: 76680d0d2825 ("tracing: Have function tracer define options per instance") Signed-off-by: Yuanhe Shu Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace_functions.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c index f283391a4dc80..cd37f20137581 100644 --- a/kernel/trace/trace_functions.c +++ b/kernel/trace/trace_functions.c @@ -458,12 +458,12 @@ func_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set) ftrace_func_t func; u32 new_flags; - /* Do nothing if already set. */ - if (!!set == !!(tr->current_trace_flags->val & bit)) + /* We can change this flag only when current tracer is function. */ + if (tr->current_trace != &function_trace) return 0; - /* We can change this flag only when not running. */ - if (tr->current_trace != &function_trace) + /* Do nothing if already set. */ + if (!!set == !!(tr->current_trace_flags->val & bit)) return 0; new_flags = (tr->current_trace_flags->val & ~bit) | (set ? bit : 0); From f333b6851bdf326fd2134133272dbbed0c94d921 Mon Sep 17 00:00:00 2001 From: Samuel Page Date: Thu, 25 Jun 2026 15:38:15 +0100 Subject: [PATCH 0300/2758] tipc: fix out-of-bounds read in broadcast Gap ACK blocks commit 2b66974a1b6134a4bbc3bfed181f7418f688eb54 upstream. A broadcast PROTOCOL/STATE_MSG can carry a Gap ACK blocks record in its data area. tipc_get_gap_ack_blks() only verifies that the record's len field is self-consistent with its ugack_cnt/bgack_cnt counts (sz == struct_size(p, gacks, ugack_cnt + bgack_cnt)); it does not check that the record actually fits in the message data area, msg_data_sz(). The unicast caller tipc_link_proto_rcv() bounds it ("if (glen > dlen) break;"), but the broadcast caller tipc_bcast_sync_rcv() discards the returned size, so tipc_link_advance_transmq() copies the record off the receive skb with an attacker-controlled count: this_ga = kmemdup(ga, struct_size(ga, gacks, ga->bgack_cnt), GFP_ATOMIC); A TIPC neighbour that negotiated TIPC_GAP_ACK_BLOCK triggers it with one ordinary broadcast STATE_MSG (msg_bc_ack_invalid() clear), sized so its data area is short, carrying a Gap ACK record with len = 0x400, bgack_cnt = 0xff and ugack_cnt = 0. len then equals struct_size(p, gacks, 255), so the consistency check passes and ga is non-NULL; kmemdup() reads struct_size(ga, gacks, 255) = 1024 bytes out of the much smaller skb: BUG: KASAN: slab-out-of-bounds in kmemdup_noprof+0x48/0x60 Read of size 1024 at addr ffff0000c7030d38 by task poc864/69 Call trace: kmemdup_noprof+0x48/0x60 tipc_link_advance_transmq+0x86c/0xb80 tipc_link_bc_ack_rcv+0x19c/0x1e0 tipc_bcast_sync_rcv+0x1c4/0x2c4 tipc_rcv+0x85c/0x1340 tipc_l2_rcv_msg+0xac/0x104 The buggy address belongs to the object at ffff0000c7030d00 which belongs to the cache skbuff_small_head of size 704 The buggy address is located 56 bytes inside of allocated 704-byte region [ffff0000c7030d00, ffff0000c7030fc0) The copied-out bytes are subsequently consumed as gap/ack values, but the read is already out of bounds at the kmemdup() regardless of how they are used. The unicast STATE path drops such a message: "if (glen > dlen) break;" skips the rest of STATE_MSG handling and the skb is freed. Make the broadcast path drop it too. tipc_bcast_sync_rcv() now bounds the record against msg_data_sz() and, when it does not fit, reports it back through tipc_node_bc_sync_rcv() to tipc_rcv() so the skb is discarded rather than processed. ga is not cleared on this path: ga == NULL already means "legacy peer without Selective ACK", a distinct legitimate state. Fixes: d7626b5acff9 ("tipc: introduce Gap ACK blocks for broadcast link") Cc: stable@vger.kernel.org Signed-off-by: Samuel Page Reviewed-by: Tung Nguyen Link: https://patch.msgid.link/20260625143815.1525412-1-sam@bynar.io Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/tipc/bcast.c | 22 ++++++++++++++-------- net/tipc/bcast.h | 2 +- net/tipc/node.c | 15 ++++++++++++--- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c index 76a1585d3f6b6..10d1ec5930846 100644 --- a/net/tipc/bcast.c +++ b/net/tipc/bcast.c @@ -497,12 +497,13 @@ void tipc_bcast_ack_rcv(struct net *net, struct tipc_link *l, */ int tipc_bcast_sync_rcv(struct net *net, struct tipc_link *l, struct tipc_msg *hdr, - struct sk_buff_head *retrq) + struct sk_buff_head *retrq, bool *valid) { struct sk_buff_head *inputq = &tipc_bc_base(net)->inputq; struct tipc_gap_ack_blks *ga; struct sk_buff_head xmitq; int rc = 0; + u16 glen; __skb_queue_head_init(&xmitq); @@ -510,13 +511,18 @@ int tipc_bcast_sync_rcv(struct net *net, struct tipc_link *l, if (msg_type(hdr) != STATE_MSG) { tipc_link_bc_init_rcv(l, hdr); } else if (!msg_bc_ack_invalid(hdr)) { - tipc_get_gap_ack_blks(&ga, l, hdr, false); - if (!sysctl_tipc_bc_retruni) - retrq = &xmitq; - rc = tipc_link_bc_ack_rcv(l, msg_bcast_ack(hdr), - msg_bc_gap(hdr), ga, &xmitq, - retrq); - rc |= tipc_link_bc_sync_rcv(l, hdr, &xmitq); + glen = tipc_get_gap_ack_blks(&ga, l, hdr, false); + if (glen > msg_data_sz(hdr)) { + /* Malformed Gap ACK blocks; caller drops the msg */ + *valid = false; + } else { + if (!sysctl_tipc_bc_retruni) + retrq = &xmitq; + rc = tipc_link_bc_ack_rcv(l, msg_bcast_ack(hdr), + msg_bc_gap(hdr), ga, &xmitq, + retrq); + rc |= tipc_link_bc_sync_rcv(l, hdr, &xmitq); + } } tipc_bcast_unlock(net); diff --git a/net/tipc/bcast.h b/net/tipc/bcast.h index 2d9352dc7b0e6..55d17b5413e16 100644 --- a/net/tipc/bcast.h +++ b/net/tipc/bcast.h @@ -97,7 +97,7 @@ void tipc_bcast_ack_rcv(struct net *net, struct tipc_link *l, struct tipc_msg *hdr); int tipc_bcast_sync_rcv(struct net *net, struct tipc_link *l, struct tipc_msg *hdr, - struct sk_buff_head *retrq); + struct sk_buff_head *retrq, bool *valid); int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg, struct tipc_link *bcl); int tipc_nl_bc_link_set(struct net *net, struct nlattr *attrs[]); diff --git a/net/tipc/node.c b/net/tipc/node.c index 97aa970a0d83c..8e4ef2630ae4f 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -1831,12 +1831,15 @@ static void tipc_node_mcast_rcv(struct tipc_node *n) } static void tipc_node_bc_sync_rcv(struct tipc_node *n, struct tipc_msg *hdr, - int bearer_id, struct sk_buff_head *xmitq) + int bearer_id, struct sk_buff_head *xmitq, + bool *valid) { struct tipc_link *ucl; int rc; - rc = tipc_bcast_sync_rcv(n->net, n->bc_entry.link, hdr, xmitq); + rc = tipc_bcast_sync_rcv(n->net, n->bc_entry.link, hdr, xmitq, valid); + if (!*valid) + return; if (rc & TIPC_LINK_DOWN_EVT) { tipc_node_reset_links(n); @@ -2140,12 +2143,18 @@ void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b) /* Ensure broadcast reception is in synch with peer's send state */ if (unlikely(usr == LINK_PROTOCOL)) { + bool valid = true; + if (unlikely(skb_linearize(skb))) { tipc_node_put(n); goto discard; } hdr = buf_msg(skb); - tipc_node_bc_sync_rcv(n, hdr, bearer_id, &xmitq); + tipc_node_bc_sync_rcv(n, hdr, bearer_id, &xmitq, &valid); + if (!valid) { + tipc_node_put(n); + goto discard; + } } else if (unlikely(tipc_link_acked(n->bc_entry.link) != bc_ack)) { tipc_bcast_ack_rcv(net, n->bc_entry.link, hdr); } From 1b495fa0d4927c88d88bf346bf311f2e26e860ed Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Thu, 18 Jun 2026 13:47:09 +0200 Subject: [PATCH 0301/2758] staging: vme_user: bound slave read/write to the kern_buf size commit 9f32f38265014fac7f5dc9490fb01a638ce6e121 upstream. The SLAVE-path helpers buffer_to_user() and buffer_from_user() copy 'count' bytes into/out of the fixed-size kern_buf (size_buf == PCI_BUF_SIZE == 0x20000, 128 KiB) using *ppos as the offset, without bounding *ppos + count against size_buf. vme_user_write()/vme_user_read() only clamp count to the VME window size (image_size = vme_get_size(resource)), which VME_SET_SLAVE sets from the user-supplied slave.size -- validated against the VME address space (up to VME_A32_MAX = 4 GiB), not against PCI_BUF_SIZE. When the window exceeds 128 KiB, a write()/read() copies past the kern_buf allocation. Clamp count against size_buf in both helpers, with an early return when *ppos is already at/after the buffer end. *ppos is >= 0 here (the caller rejects negative offsets), so size_buf - *ppos cannot wrap. This mirrors the existing clamp in the MASTER-path helpers resource_to_user() / resource_from_user(), and matches the read()/write() convention of a short transfer at end-of-buffer. Found by static analysis (CodeQL taint tracking + CBMC bounded model checking) and confirmed dynamically under KASAN with the vme_fake bridge: BUG: KASAN: slab-out-of-bounds in _copy_from_user+0x2d/0x80 Write of size 262144 at addr ffff888004100000 by task trigger/68 _copy_from_user+0x2d/0x80 vme_user_write+0x13e/0x240 [vme_user] vfs_write+0x1b8/0x7a0 ksys_write+0xb8/0x150 Fixes: f00a86d98a1e ("Staging: vme: add VME userspace driver") Cc: stable Signed-off-by: Michael Tautschnig Link: https://patch.msgid.link/20260618114709.72499-1-tautschn@amazon.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/staging/vme_user/vme_user.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/staging/vme_user/vme_user.c b/drivers/staging/vme_user/vme_user.c index 11e25c2f6b0aa..a472a38ef613a 100644 --- a/drivers/staging/vme_user/vme_user.c +++ b/drivers/staging/vme_user/vme_user.c @@ -156,6 +156,17 @@ static ssize_t buffer_to_user(unsigned int minor, char __user *buf, { void *image_ptr; + /* + * The slave window (image_size) can exceed the fixed kern_buf + * (size_buf == PCI_BUF_SIZE), so bound the copy to kern_buf. + * *ppos is >= 0 here (checked by the caller), so the + * subtraction below cannot wrap. + */ + if (*ppos >= image[minor].size_buf) + return 0; + if (count > image[minor].size_buf - *ppos) + count = image[minor].size_buf - *ppos; + image_ptr = image[minor].kern_buf + *ppos; if (copy_to_user(buf, image_ptr, (unsigned long)count)) return -EFAULT; @@ -168,6 +179,17 @@ static ssize_t buffer_from_user(unsigned int minor, const char __user *buf, { void *image_ptr; + /* + * The slave window (image_size) can exceed the fixed kern_buf + * (size_buf == PCI_BUF_SIZE), so bound the copy to kern_buf. + * *ppos is >= 0 here (checked by the caller), so the + * subtraction below cannot wrap. + */ + if (*ppos >= image[minor].size_buf) + return 0; + if (count > image[minor].size_buf - *ppos) + count = image[minor].size_buf - *ppos; + image_ptr = image[minor].kern_buf + *ppos; if (copy_from_user(image_ptr, buf, (unsigned long)count)) return -EFAULT; From 6e9d10f62773b99bd927940fd9cbdfe7207e23ff Mon Sep 17 00:00:00 2001 From: Shoichiro Miyamoto Date: Tue, 7 Jul 2026 20:23:58 +0900 Subject: [PATCH 0302/2758] smb: client: restrict implied bcc[0] exemption to responses without data area commit 53b7c271f06be4dd5cfc8c6ef552a8355c891a7f upstream. smb2_check_message() has a long-standing quirk that accepts a response whose calculated length is one byte larger than the bytes actually received ("server can return one byte more due to implied bcc[0]"). This was introduced to accommodate servers that omit the trailing bcc[0] overlap byte when no data area is present. However, the exemption is applied unconditionally, regardless of whether the command actually carries a data area (has_smb2_data_area[]). When a response with a data area is subject to the +1 exemption, the reported data can extend one byte beyond the bytes actually received, yet smb2_check_message() still accepts it. The subsequent decoder then reads past the end of the receive buffer. This is reachable during NEGOTIATE and SESSION_SETUP, before the session is established. The resulting out-of-bounds reads are visible under KASAN when mounting against a non-conforming server; both the SPNEGO/negTokenInit and the NTLMSSP challenge decoders are affected: BUG: KASAN: slab-out-of-bounds in asn1_ber_decoder+0x16a7/0x1b00 Read of size 1 at addr ffff8880084d67c0 by task mount.cifs/81 CPU: 1 UID: 0 PID: 81 Comm: mount.cifs Not tainted 7.1.0-rc6 #1 Call Trace: dump_stack_lvl+0x4e/0x70 print_report+0x157/0x4c9 kasan_report+0xce/0x100 asn1_ber_decoder+0x16a7/0x1b00 decode_negTokenInit+0x19/0x30 SMB2_negotiate+0x31d9/0x4c90 cifs_negotiate_protocol+0x1f2/0x3f0 cifs_get_smb_ses+0x93f/0x17e0 cifs_mount_get_session+0x7f/0x3a0 cifs_mount+0xb4/0xcf0 cifs_smb3_do_mount+0x23a/0x1500 smb3_get_tree+0x3b0/0x630 vfs_get_tree+0x82/0x2d0 fc_mount+0x10/0x1b0 path_mount+0x50d/0x1de0 __x64_sys_mount+0x20b/0x270 do_syscall_64+0xee/0x590 entry_SYSCALL_64_after_hwframe+0x77/0x7f Allocated by task 85: kmem_cache_alloc_noprof+0x106/0x380 mempool_alloc_noprof+0x116/0x1e0 cifs_small_buf_get+0x31/0x80 allocate_buffers+0x10d/0x2b0 cifs_demultiplex_thread+0x1d5/0x1d50 kthread+0x2c6/0x390 ret_from_fork+0x36e/0x5a0 ret_from_fork_asm+0x1a/0x30 The buggy address is located 0 bytes to the right of allocated 448-byte region [ffff8880084d6600, ffff8880084d67c0) which belongs to the cache cifs_small_rq of size 448 BUG: KASAN: slab-out-of-bounds in kmemdup_noprof+0x36/0x50 Read of size 329 at addr ffff88800726c678 by task mount.cifs/89 CPU: 0 UID: 0 PID: 89 Comm: mount.cifs Tainted: G B 7.1.0-rc6 #1 Call Trace: dump_stack_lvl+0x4e/0x70 print_report+0x157/0x4c9 kasan_report+0xce/0x100 kasan_check_range+0x10f/0x1e0 __asan_memcpy+0x23/0x60 kmemdup_noprof+0x36/0x50 decode_ntlmssp_challenge+0x457/0x680 SMB2_sess_auth_rawntlmssp_negotiate+0x6f0/0xcb0 SMB2_sess_setup+0x219/0x4f0 cifs_setup_session+0x248/0xaf0 cifs_get_smb_ses+0xf79/0x17e0 cifs_mount_get_session+0x7f/0x3a0 cifs_mount+0xb4/0xcf0 cifs_smb3_do_mount+0x23a/0x1500 smb3_get_tree+0x3b0/0x630 vfs_get_tree+0x82/0x2d0 fc_mount+0x10/0x1b0 path_mount+0x50d/0x1de0 __x64_sys_mount+0x20b/0x270 do_syscall_64+0xee/0x590 entry_SYSCALL_64_after_hwframe+0x77/0x7f Allocated by task 93: kmem_cache_alloc_noprof+0x106/0x380 mempool_alloc_noprof+0x116/0x1e0 cifs_small_buf_get+0x31/0x80 allocate_buffers+0x10d/0x2b0 cifs_demultiplex_thread+0x1d5/0x1d50 kthread+0x2c6/0x390 ret_from_fork+0x36e/0x5a0 ret_from_fork_asm+0x1a/0x30 The buggy address is located 120 bytes inside of allocated 448-byte region [ffff88800726c600, ffff88800726c7c0) which belongs to the cache cifs_small_rq of size 448 Restrict the +1 exemption to responses that have no data area, so that it still covers the bcc[0] omission it was meant for. When a data area is present, the +1 discrepancy instead means the reported data length overruns the received buffer, so the response must be rejected. Fixes: 093b2bdad322 ("CIFS: Make demultiplex_thread work with SMB2 code") Cc: stable@vger.kernel.org Signed-off-by: Shoichiro Miyamoto Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2misc.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/fs/smb/client/smb2misc.c b/fs/smb/client/smb2misc.c index 2a7355ce1a078..6270b33147d25 100644 --- a/fs/smb/client/smb2misc.c +++ b/fs/smb/client/smb2misc.c @@ -19,6 +19,8 @@ #include "nterr.h" #include "cached_dir.h" +static unsigned int __smb2_calc_size(void *buf, bool *have_data); + static int check_smb2_hdr(struct smb2_hdr *shdr, __u64 mid) { @@ -145,6 +147,7 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len, int command; __u32 calc_len; /* calculated length */ __u64 mid; + bool have_data; /* If server is a channel, select the primary channel */ pserver = SERVER_IS_CHAN(server) ? server->primary_server : server; @@ -228,7 +231,8 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len, } } - calc_len = smb2_calc_size(buf); + have_data = false; + calc_len = __smb2_calc_size(buf, &have_data); /* For SMB2_IOCTL, OutputOffset and OutputLength are optional, so might * be 0, and not a real miscalculation */ @@ -247,8 +251,13 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len, /* Windows 7 server returns 24 bytes more */ if (calc_len + 24 == len && command == SMB2_OPLOCK_BREAK_HE) return 0; - /* server can return one byte more due to implied bcc[0] */ - if (calc_len == len + 1) + /* + * Server can return one byte more due to implied bcc[0]. + * Allow it only when there is no data area; if data_length > 0 + * the +1 gap indicates an overreported data length rather than + * the bcc[0] omission. + */ + if (calc_len == len + 1 && !have_data) return 0; /* @@ -409,14 +418,17 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *shdr) /* * Calculate the size of the SMB message based on the fixed header * portion, the number of word parameters and the data portion of the message. + * If have_data is non-NULL, it is set to true when a non-empty data area was + * found (data_length > 0), allowing callers to distinguish the implied bcc[0] + * case (no data area) from an overreported data length. */ -unsigned int -smb2_calc_size(void *buf) +static unsigned int +__smb2_calc_size(void *buf, bool *have_data) { struct smb2_pdu *pdu = buf; struct smb2_hdr *shdr = &pdu->hdr; int offset; /* the offset from the beginning of SMB to data area */ - int data_length; /* the length of the variable length data area */ + int data_length = 0; /* the length of the variable length data area */ /* Structure Size has already been checked to make sure it is 64 */ int len = le16_to_cpu(shdr->StructureSize); @@ -449,9 +461,17 @@ smb2_calc_size(void *buf) } calc_size_exit: cifs_dbg(FYI, "SMB2 len %d\n", len); + if (have_data) + *have_data = (data_length > 0); return len; } +unsigned int +smb2_calc_size(void *buf) +{ + return __smb2_calc_size(buf, NULL); +} + /* Note: caller must free return buffer */ __le16 * cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb) From 157bcfc7955cb23cdfc833faf842c669a1ef9364 Mon Sep 17 00:00:00 2001 From: Hao-Qun Huang Date: Sat, 4 Jul 2026 14:58:15 +0800 Subject: [PATCH 0303/2758] staging: vme_user: fix location monitor leak in fake bridge commit e8422d89e8af41d87f0e9db564be8e2634f4c602 upstream. fake_init() allocates a location monitor resource and links it into fake_bridge->lm_resources. The init error path frees this list, but fake_exit() only frees the slave and master resource lists. Loading and unloading the module therefore triggers a kmemleak warning: unreferenced object 0xffff8b8b82aebe40 (size 64): comm "init", pid 1, jiffies 4294894572 backtrace (crc c1e013ef): kmemleak_alloc+0x4e/0x90 __kmalloc_cache_noprof+0x338/0x430 0xffffffffc0602246 do_one_initcall+0x4f/0x320 do_init_module+0x68/0x270 load_module+0x2a3b/0x2d90 Free the lm_resources list in fake_exit() as well, before fake_bridge is freed. Fixes: 658bcdae9c67 ("vme: Adding Fake VME driver") Cc: stable Cc: Martyn Welch Assisted-by: Claude:claude-fable-5 Signed-off-by: Hao-Qun Huang Link: https://patch.msgid.link/20260704065817.403111-1-alvinhuang0603@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/vme_user/vme_fake.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/staging/vme_user/vme_fake.c b/drivers/staging/vme_user/vme_fake.c index 8abaa3165fbb7..434cf760ade6c 100644 --- a/drivers/staging/vme_user/vme_fake.c +++ b/drivers/staging/vme_user/vme_fake.c @@ -1239,6 +1239,7 @@ static void __exit fake_exit(void) { struct list_head *pos = NULL; struct list_head *tmplist; + struct vme_lm_resource *lm; struct vme_master_resource *master_image; struct vme_slave_resource *slave_image; int i; @@ -1268,6 +1269,13 @@ static void __exit fake_exit(void) vme_unregister_bridge(fake_bridge); fake_crcsr_exit(fake_bridge); + /* resources are stored in link list */ + list_for_each_safe(pos, tmplist, &fake_bridge->lm_resources) { + lm = list_entry(pos, struct vme_lm_resource, list); + list_del(pos); + kfree(lm); + } + /* resources are stored in link list */ list_for_each_safe(pos, tmplist, &fake_bridge->slave_resources) { slave_image = list_entry(pos, struct vme_slave_resource, list); From c6cda17e98545980e42291fc4282daa8a8ebe384 Mon Sep 17 00:00:00 2001 From: Hao-Qun Huang Date: Sat, 4 Jul 2026 14:58:16 +0800 Subject: [PATCH 0304/2758] staging: vme_user: fix location monitor leak in tsi148 bridge commit 151edde741f8bc7f2931c5f44ab376d32b0c8beb upstream. tsi148_probe() allocates a location monitor resource and links it into tsi148_bridge->lm_resources. The probe error path frees this list, but tsi148_remove() only frees the dma, slave and master resource lists, so the location monitor resource is leaked on device unbind or module unload. Free the lm_resources list in tsi148_remove() as well, before tsi148_bridge is freed. Fixes: d22b8ed9a3b0 ("Staging: vme: add Tundra TSI148 VME-PCI Bridge driver") Cc: stable Cc: Martyn Welch Assisted-by: Claude:claude-fable-5 Signed-off-by: Hao-Qun Huang Link: https://patch.msgid.link/20260704065817.403111-2-alvinhuang0603@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/vme_user/vme_tsi148.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/staging/vme_user/vme_tsi148.c b/drivers/staging/vme_user/vme_tsi148.c index 4cf3486646cee..c695ad9b4ca26 100644 --- a/drivers/staging/vme_user/vme_tsi148.c +++ b/drivers/staging/vme_user/vme_tsi148.c @@ -2534,6 +2534,7 @@ static void tsi148_remove(struct pci_dev *pdev) { struct list_head *pos = NULL; struct list_head *tmplist; + struct vme_lm_resource *lm; struct vme_master_resource *master_image; struct vme_slave_resource *slave_image; struct vme_dma_resource *dma_ctrlr; @@ -2590,6 +2591,13 @@ static void tsi148_remove(struct pci_dev *pdev) tsi148_crcsr_exit(tsi148_bridge, pdev); + /* resources are stored in link list */ + list_for_each_safe(pos, tmplist, &tsi148_bridge->lm_resources) { + lm = list_entry(pos, struct vme_lm_resource, list); + list_del(pos); + kfree(lm); + } + /* resources are stored in link list */ list_for_each_safe(pos, tmplist, &tsi148_bridge->dma_resources) { dma_ctrlr = list_entry(pos, struct vme_dma_resource, list); From 753e684fa55f18ad97b982fbda631d679949234c Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Thu, 7 May 2026 20:58:10 +0000 Subject: [PATCH 0305/2758] media: staging: ipu3-imgu: Add range check for imgu_css_cfg_acc_stripe commit c32fe4c4918c9aa49f61359e3b42619c4d8686de upstream. If the driver's stripe information is invalid it can result in an integer underflow. Add a range check to avoid this kind of error. This patch fixes the following smatch error: drivers/staging/media/ipu3/ipu3-css-params.c:1792 imgu_css_cfg_acc_stripe() warn: 'acc->stripe.bds_out_stripes[0]->width - 2 * f' 4294967168 can't fit into 65535 'acc->stripe.bds_out_stripes[1]->offset' Cc: stable@vger.kernel.org Fixes: e11110a5b744 ("media: staging/intel-ipu3: css: Compute and program ccs") Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/staging/media/ipu3/ipu3-css-params.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/ipu3/ipu3-css-params.c b/drivers/staging/media/ipu3/ipu3-css-params.c index 2c48d57a3180f..92cce31e35c5d 100644 --- a/drivers/staging/media/ipu3/ipu3-css-params.c +++ b/drivers/staging/media/ipu3/ipu3-css-params.c @@ -1770,6 +1770,8 @@ static int imgu_css_cfg_acc_stripe(struct imgu_css *css, unsigned int pipe, acc->stripe.bds_out_stripes[0].width = ALIGN(css_pipe->rect[IPU3_CSS_RECT_BDS].width, f); } else { + u32 offset; + /* Image processing is divided into two stripes */ acc->stripe.bds_out_stripes[0].width = acc->stripe.bds_out_stripes[1].width = @@ -1788,8 +1790,10 @@ static int imgu_css_cfg_acc_stripe(struct imgu_css *css, unsigned int pipe, acc->stripe.bds_out_stripes[1].width += f; } /* Overlap between stripes is IPU3_UAPI_ISP_VEC_ELEMS * 4 */ - acc->stripe.bds_out_stripes[1].offset = - acc->stripe.bds_out_stripes[0].width - 2 * f; + offset = acc->stripe.bds_out_stripes[0].width - 2 * f; + if (offset > 65535) + return -EINVAL; + acc->stripe.bds_out_stripes[1].offset = offset; } acc->stripe.effective_stripes[0].height = From 7c973c5113e37e98dd2c1c04f3bc8826b679e6e9 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 25 Mar 2026 13:59:43 +0100 Subject: [PATCH 0306/2758] staging: media: atomisp: reduce load_primary_binaries() stack usage commit f4d51e55dd47ef467fbe37d8575e20eee41b092d upstream. The load_primary_binaries() function is overly complex and has som large variables on the stack, which can cause warnings depending on CONFIG_FRAME_WARN setting: drivers/staging/media/atomisp/pci/sh_css.c: In function 'load_primary_binaries': drivers/staging/media/atomisp/pci/sh_css.c:5260:1: error: the frame size of 1560 bytes is larger than 1536 bytes [-Werror=frame-larger-than=] Half of the stack usage is for the prim_descr[] array, but only one member of the array is used at any given time. Reduce the stack usage by turning the array into a single structure. Fixes: a49d25364dfb ("staging/atomisp: Add support for the Intel IPU v2") Cc: stable@vger.kernel.org Signed-off-by: Arnd Bergmann Reviewed-by: Andy Shevchenko Signed-off-by: Sakari Ailus Signed-off-by: Greg Kroah-Hartman --- drivers/staging/media/atomisp/pci/sh_css.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c index 6cda5925fa45e..584751516b696 100644 --- a/drivers/staging/media/atomisp/pci/sh_css.c +++ b/drivers/staging/media/atomisp/pci/sh_css.c @@ -5020,7 +5020,6 @@ static int load_primary_binaries( struct ia_css_capture_settings *mycs; unsigned int i; bool need_extra_yuv_scaler = false; - struct ia_css_binary_descr prim_descr[MAX_NUM_PRIMARY_STAGES]; IA_CSS_ENTER_PRIVATE(""); assert(pipe); @@ -5189,15 +5188,16 @@ static int load_primary_binaries( /* Primary */ for (i = 0; i < mycs->num_primary_stage; i++) { + struct ia_css_binary_descr prim_descr; struct ia_css_frame_info *local_vf_info = NULL; if (pipe->enable_viewfinder[IA_CSS_PIPE_OUTPUT_STAGE_0] && (i == mycs->num_primary_stage - 1)) local_vf_info = &vf_info; - ia_css_pipe_get_primary_binarydesc(pipe, &prim_descr[i], + ia_css_pipe_get_primary_binarydesc(pipe, &prim_descr, &prim_in_info, &prim_out_info, local_vf_info, i); - err = ia_css_binary_find(&prim_descr[i], &mycs->primary_binary[i]); + err = ia_css_binary_find(&prim_descr, &mycs->primary_binary[i]); if (err) { IA_CSS_LEAVE_ERR_PRIVATE(err); return err; From 837c1f9655421055f751ed34745e820a54a27642 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Mon, 13 Apr 2026 17:12:44 +0200 Subject: [PATCH 0307/2758] staging: media: ipu7: fix double-free and use-after-free in error paths commit d3a9a8cf2d7fd61a2f63df61f6cbc0a9bb007cc0 upstream. In both ipu7_isys_init() and ipu7_psys_init(), pdata is allocated and then passed to ipu7_bus_initialize_device(), which stores it in adev->pdata. The ipu7_bus_release() function frees adev->pdata when the device's reference count drops to zero. Two error paths incorrectly call kfree(pdata) after the device teardown has already freed it: 1. When ipu7_mmu_init() fails: put_device() is called, which drops the reference count to zero and triggers ipu7_bus_release() -> kfree(pdata). The subsequent kfree(pdata) is a double-free. 2. When ipu7_bus_add_device() fails: it calls auxiliary_device_uninit() internally, which calls put_device() -> ipu7_bus_release() -> kfree(pdata). The subsequent kfree(pdata) is again a double-free. Note that the kfree(pdata) when ipu7_bus_initialize_device() itself fails is correct, because in that case auxiliary_device_init() failed and the release function was never set up, so pdata must be freed manually. Additionally, the error code was not saved before calling put_device(), causing ERR_CAST() to dereference the already-freed adev pointer when constructing the return value. Fix this by saving the error from dev_err_probe() before put_device() and returning ERR_PTR() instead. Remove the redundant kfree(pdata) calls and fix the use-after-free in the return values of the two affected error paths. Fixes: b7fe4c0019b1 ("media: staging/ipu7: add Intel IPU7 PCI device driver") Cc: stable@vger.kernel.org Reviewed-by: Dan Carpenter Signed-off-by: Alexandru Hossu Signed-off-by: Sakari Ailus Signed-off-by: Greg Kroah-Hartman --- drivers/staging/media/ipu7/ipu7.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c index c771e763f8c50..310e3f24e571f 100644 --- a/drivers/staging/media/ipu7/ipu7.c +++ b/drivers/staging/media/ipu7/ipu7.c @@ -2169,21 +2169,18 @@ ipu7_isys_init(struct pci_dev *pdev, struct device *parent, isys_adev->mmu = ipu7_mmu_init(dev, base, ISYS_MMID, &ipdata->hw_variant); if (IS_ERR(isys_adev->mmu)) { - dev_err_probe(dev, PTR_ERR(isys_adev->mmu), - "ipu7_mmu_init(isys_adev->mmu) failed\n"); + ret = dev_err_probe(dev, PTR_ERR(isys_adev->mmu), + "ipu7_mmu_init(isys_adev->mmu) failed\n"); put_device(&isys_adev->auxdev.dev); - kfree(pdata); - return ERR_CAST(isys_adev->mmu); + return ERR_PTR(ret); } isys_adev->mmu->dev = &isys_adev->auxdev.dev; isys_adev->subsys = IPU_IS; ret = ipu7_bus_add_device(isys_adev); - if (ret) { - kfree(pdata); + if (ret) return ERR_PTR(ret); - } return isys_adev; } @@ -2216,21 +2213,18 @@ ipu7_psys_init(struct pci_dev *pdev, struct device *parent, psys_adev->mmu = ipu7_mmu_init(&pdev->dev, base, PSYS_MMID, &ipdata->hw_variant); if (IS_ERR(psys_adev->mmu)) { - dev_err_probe(&pdev->dev, PTR_ERR(psys_adev->mmu), - "ipu7_mmu_init(psys_adev->mmu) failed\n"); + ret = dev_err_probe(&pdev->dev, PTR_ERR(psys_adev->mmu), + "ipu7_mmu_init(psys_adev->mmu) failed\n"); put_device(&psys_adev->auxdev.dev); - kfree(pdata); - return ERR_CAST(psys_adev->mmu); + return ERR_PTR(ret); } psys_adev->mmu->dev = &psys_adev->auxdev.dev; psys_adev->subsys = IPU_PS; ret = ipu7_bus_add_device(psys_adev); - if (ret) { - kfree(pdata); + if (ret) return ERR_PTR(ret); - } return psys_adev; } From 35f4dbec73806b8f013903f57f117fcf05f26f8d Mon Sep 17 00:00:00 2001 From: Christopher Mackle Date: Sat, 20 Jun 2026 01:39:16 +0000 Subject: [PATCH 0308/2758] staging: rtl8723bs: don't drop short TX frames in _rtw_pktfile_read() commit 252f8c681adc8614b70f844ba3de3a138c33a783 upstream. Commit bc4df274dca6 ("staging: rtl8723bs: update _rtw_pktfile_read() to return error codes") changed _rtw_pktfile_read() to fail when the caller asks for more bytes than remain in the packet: if (rtw_remainder_len(pfile) < rlen) return -EINVAL; That breaks the assumption made by the data TX path. In rtw_xmitframe_coalesce() (core/rtw_xmit.c) the per-fragment copy is issued with the full fragment length, mpdu_len, which is derived from pxmitpriv->frag_len (~2300 bytes), and the code relies on the historical behaviour of copying only what is left and returning the number of bytes actually copied: mem_sz = _rtw_pktfile_read(&pktfile, pframe, mpdu_len); if (mem_sz < 0) return mem_sz; So for every outbound packet smaller than the fragmentation threshold - i.e. essentially all normal traffic, including the EAPOL frames of the WPA 4-way handshake and DHCP - rlen is larger than the bytes remaining, _rtw_pktfile_read() returns -EINVAL, rtw_xmitframe_coalesce() aborts, and the frame is dropped before it is queued to the hardware. The driver floods the log with: rtl8723bs ...: xmit_xmitframes: coalesce failed with error -22 Management frames (authentication/association) use a different path and still go out, so the interface scans and associates, but no data frame is ever transmitted. The 4-way handshake therefore never completes and wpa_supplicant misreports it as: WPA: 4-Way Handshake failed - pre-shared key may be incorrect AP mode is unaffected. The net effect is that the chip is unusable in station mode on any kernel carrying the offending commit. This was confirmed with a wpa_supplicant -dd trace on an RTL8723BS SDIO adapter (Bay Trail): message 1/4 is received and the PTK is derived, but each "Sending EAPOL-Key 2/4" coincides 1:1 with a "coalesce failed with error -22", so message 2/4 never reaches the AP, which keeps retrying message 1/4 until the handshake times out. Restore the original semantics: clamp the requested length to the bytes remaining in the packet and return that length. The skb_copy_bits() error path is kept, so genuine copy failures are still propagated. Fixes: bc4df274dca6 ("staging: rtl8723bs: update _rtw_pktfile_read() to return error codes") Cc: stable Tested-by: Christopher Mackle Signed-off-by: Christopher Mackle Link: https://patch.msgid.link/20260620013916.7148-1-christophermackle01@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/os_dep/xmit_linux.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/staging/rtl8723bs/os_dep/xmit_linux.c b/drivers/staging/rtl8723bs/os_dep/xmit_linux.c index dc0b77f38b1ae..9bdb67a8a408b 100644 --- a/drivers/staging/rtl8723bs/os_dep/xmit_linux.c +++ b/drivers/staging/rtl8723bs/os_dep/xmit_linux.c @@ -24,9 +24,11 @@ void _rtw_open_pktfile(struct sk_buff *pktptr, struct pkt_file *pfile) int _rtw_pktfile_read(struct pkt_file *pfile, u8 *rmem, unsigned int rlen) { int ret; + unsigned int remain = rtw_remainder_len(pfile); - if (rtw_remainder_len(pfile) < rlen) - return -EINVAL; + /* clamp to bytes remaining; the coalesce loop relies on short reads */ + if (rlen > remain) + rlen = remain; if (rmem) { ret = skb_copy_bits(pfile->pkt, pfile->buf_len - pfile->pkt_len, rmem, rlen); From 138cd190efd56ab36c9fdd8fef8749d06937f24b Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:45:27 +0200 Subject: [PATCH 0309/2758] staging: rtl8723bs: fix heap buffer overflow in rtw_cfg80211_set_wpa_ie() commit 5a752a616e756844388a1a45404db9fc29fec655 upstream. supplicant_ie is a 256-byte array in struct security_priv. The WPA and WPA2 IE copy paths use: memcpy(padapter->securitypriv.supplicant_ie, &pwpa[0], wpa_ielen + 2); where wpa_ielen is the raw IE length field (u8, 0-255). When a local user supplies a connect request via nl80211 with a crafted WPA IE of length 255, wpa_ielen + 2 equals 257, overflowing the 256-byte buffer by one byte into the adjacent last_mic_err_time field. rtw_parse_wpa_ie() does not prevent this: its length consistency check compares *(wpa_ie+1) against (u8)(wpa_ie_len-2), which is (u8)(255) == 255 when wpa_ie_len = 257, so the check passes silently. Add explicit bounds checks for both the WPA and WPA2 paths before the memcpy, rejecting any IE whose total size (wpa_ielen + 2) exceeds the supplicant_ie buffer. Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable Reviewed-by: Luka Gejak Signed-off-by: Alexandru Hossu Link: https://patch.msgid.link/20260522004531.1038924-4-hossu.alexandru@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c index fd3bae31b0edb..e7ba5ccfa03cf 100644 --- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c @@ -1445,6 +1445,10 @@ static int rtw_cfg80211_set_wpa_ie(struct adapter *padapter, u8 *pie, size_t iel pwpa = rtw_get_wpa_ie(buf, &wpa_ielen, ielen); if (pwpa && wpa_ielen > 0) { + if (wpa_ielen + 2 > sizeof(padapter->securitypriv.supplicant_ie)) { + ret = -EINVAL; + goto exit; + } if (rtw_parse_wpa_ie(pwpa, wpa_ielen + 2, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) { padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X; padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPAPSK; @@ -1454,6 +1458,10 @@ static int rtw_cfg80211_set_wpa_ie(struct adapter *padapter, u8 *pie, size_t iel pwpa2 = rtw_get_wpa2_ie(buf, &wpa2_ielen, ielen); if (pwpa2 && wpa2_ielen > 0) { + if (wpa2_ielen + 2 > sizeof(padapter->securitypriv.supplicant_ie)) { + ret = -EINVAL; + goto exit; + } if (rtw_parse_wpa2_ie(pwpa2, wpa2_ielen + 2, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) { padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X; padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPA2PSK; From d90b9f39f375c9826ef145605dfe97765d0ecb91 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:46:05 +0200 Subject: [PATCH 0310/2758] staging: rtl8723bs: fix WEP length underflow and OOB read in OnAuth() commit a1fc19d61f661d47204f095b593de507884849f7 upstream. OnAuth() has two bugs in the shared-key authentication path. When the Privacy bit is set, rtw_wep_decrypt() is called without verifying that the frame is long enough to contain a valid WEP IV and ICV. Inside rtw_wep_decrypt(), length is computed as: length = len - WLAN_HDR_A3_LEN - iv_len and then passed as (length - 4) to crc32_le(). If len is less than WLAN_HDR_A3_LEN + iv_len + icv_len (32 bytes), length - 4 is negative and, after the implicit cast to size_t, causes crc32_le() to read far beyond the frame buffer. Add a minimum length check before accessing the IV field and calling the decryption path. When processing a seq=3 response, rtw_get_ie() stores the Challenge Text IE length in ie_len, but the subsequent memcmp() always reads 128 bytes regardless of ie_len. IEEE 802.11 mandates a challenge text of exactly 128 bytes; reject any IE whose length field differs, matching the check already applied to OnAuthClient(). Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable Signed-off-by: Alexandru Hossu Link: https://patch.msgid.link/20260522004605.1039209-1-hossu.alexandru@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c index 5f00fe282d1be..a537b2b9794a9 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c @@ -687,6 +687,9 @@ unsigned int OnAuth(struct adapter *padapter, union recv_frame *precv_frame) if ((pmlmeinfo->state&0x03) != WIFI_FW_AP_STATE) return _FAIL; + if (len < WLAN_HDR_A3_LEN) + return _FAIL; + sa = GetAddr2Ptr(pframe); auth_mode = psecuritypriv->dot11AuthAlgrthm; @@ -698,6 +701,9 @@ unsigned int OnAuth(struct adapter *padapter, union recv_frame *precv_frame) prxattrib->hdrlen = WLAN_HDR_A3_LEN; prxattrib->encrypt = _WEP40_; + if (len < WLAN_HDR_A3_LEN + 8) + return _FAIL; + iv = pframe+prxattrib->hdrlen; prxattrib->key_index = ((iv[3]>>6)&0x3); @@ -802,7 +808,7 @@ unsigned int OnAuth(struct adapter *padapter, union recv_frame *precv_frame) p = rtw_get_ie(pframe + WLAN_HDR_A3_LEN + 4 + _AUTH_IE_OFFSET_, WLAN_EID_CHALLENGE, (int *)&ie_len, len - WLAN_HDR_A3_LEN - _AUTH_IE_OFFSET_ - 4); - if (!p || ie_len <= 0) { + if (!p || ie_len != 128) { status = WLAN_STATUS_CHALLENGE_FAIL; goto auth_fail; } From 7e7741c8315e4160aead00a60cdd6f81ab880717 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:45:29 +0200 Subject: [PATCH 0311/2758] staging: rtl8723bs: fix OOB read in OnAssocRsp() IE loop commit f9654207e92283e0acac5d64fe5f8835383b5a23 upstream. The IE parsing loop in OnAssocRsp() advances by (pIE->length + 2) each iteration but only guards on i < pkt_len. When a malicious AP sends an AssocResponse whose last IE has only one byte remaining in the frame (the element_id byte lands at pkt_len-1), the loop reads pIE->length from pframe[pkt_len], which is one byte past the allocated receive buffer. Additionally, even when the header bytes are in bounds, pIE->length itself can extend the data window beyond pkt_len, silently passing a truncated IE to the handler functions. Add two guards at the top of the loop body: 1. Break if fewer than sizeof(*pIE) bytes remain (can't read header). 2. Break if the IE's declared data extends past pkt_len. Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable Signed-off-by: Alexandru Hossu Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260522004531.1038924-6-hossu.alexandru@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c index a537b2b9794a9..28d232d46bbde 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c @@ -1406,7 +1406,11 @@ unsigned int OnAssocRsp(struct adapter *padapter, union recv_frame *precv_frame) /* to handle HT, WMM, rate adaptive, update MAC reg */ /* for not to handle the synchronous IO in the tasklet */ for (i = (6 + WLAN_HDR_A3_LEN); i < pkt_len;) { + if (i + sizeof(*pIE) > pkt_len) + break; pIE = (struct ndis_80211_var_ie *)(pframe + i); + if (i + sizeof(*pIE) + pIE->length > pkt_len) + break; switch (pIE->element_id) { case WLAN_EID_VENDOR_SPECIFIC: From b5cc2f999927f69723ca53f1f2a3aa37dbeda907 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:45:25 +0200 Subject: [PATCH 0312/2758] staging: rtl8723bs: fix OOB read in update_beacon_info() IE loop commit ed51de4a86e173c3b0ef78e039c2e49e08b11f16 upstream. The IE parsing loop in update_beacon_info() advances by (pIE->length + 2) each iteration but only guards on i < len. When a malicious AP sends a Beacon whose last IE has only one byte remaining in the frame (the element_id byte lands at len-1), the loop reads pIE->length from one byte past the allocated receive buffer. Additionally, even when the header bytes are in bounds, pIE->length itself can extend the data window beyond len, passing a truncated IE to the handler functions. Add two guards at the top of the loop body: 1. Break if fewer than sizeof(*pIE) bytes remain (can't read header). 2. Break if the IE's declared data extends past len. Also replace i += (pIE->length + 2) with i += sizeof(*pIE) + pIE->length for consistency with the sizeof(*pIE) guards added above. Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable Reviewed-by: Luka Gejak Signed-off-by: Alexandru Hossu Link: https://patch.msgid.link/20260522004531.1038924-2-hossu.alexandru@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_wlan_util.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c index 6a7c09db4cd93..e0d73c267786c 100644 --- a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c +++ b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c @@ -1289,7 +1289,11 @@ void update_beacon_info(struct adapter *padapter, u8 *pframe, uint pkt_len, stru len = pkt_len - (_BEACON_IE_OFFSET_ + WLAN_HDR_A3_LEN); for (i = 0; i < len;) { + if (i + sizeof(*pIE) > len) + break; pIE = (struct ndis_80211_var_ie *)(pframe + (_BEACON_IE_OFFSET_ + WLAN_HDR_A3_LEN) + i); + if (i + sizeof(*pIE) + pIE->length > len) + break; switch (pIE->element_id) { case WLAN_EID_VENDOR_SPECIFIC: @@ -1314,7 +1318,7 @@ void update_beacon_info(struct adapter *padapter, u8 *pframe, uint pkt_len, stru break; } - i += (pIE->length + 2); + i += sizeof(*pIE) + pIE->length; } } From 402f13ec95945f34a210b28df1f8740d3d4a58c5 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:45:26 +0200 Subject: [PATCH 0313/2758] staging: rtl8723bs: fix OOB reads in IE loops in issue_assocreq() and join_cmd_hdl() commit ef61d628dfad38fead1fd2e08979ae9126d011d5 upstream. Two IE parsing loops are missing the header bounds checks before they dereference pIE->length: - issue_assocreq() walks pmlmeinfo->network.ies to build the association request. If the stored IE data ends with only an element_id byte and no length byte, pIE->length is read one byte past the end of the buffer. - join_cmd_hdl() walks pnetwork->ies during station join and has the same problem under the same conditions. Both buffers are filled from AP beacon and probe-response frames, so a malicious AP that sends a truncated final IE can trigger the issue. Apply the two-guard pattern established in update_beacon_info(): 1. Break if fewer than sizeof(*pIE) bytes remain. 2. Break if the IE's declared data extends past the buffer end. Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable Reviewed-by: Luka Gejak Signed-off-by: Alexandru Hossu Link: https://patch.msgid.link/20260522004531.1038924-3-hossu.alexandru@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c index 28d232d46bbde..e78450402d193 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c @@ -2935,7 +2935,11 @@ void issue_assocreq(struct adapter *padapter) /* vendor specific IE, such as WPA, WMM, WPS */ for (i = sizeof(struct ndis_802_11_fix_ie); i < pmlmeinfo->network.ie_length;) { + if (i + sizeof(*pIE) > pmlmeinfo->network.ie_length) + break; pIE = (struct ndis_80211_var_ie *)(pmlmeinfo->network.ies + i); + if (i + sizeof(*pIE) + pIE->length > pmlmeinfo->network.ie_length) + break; switch (pIE->element_id) { case WLAN_EID_VENDOR_SPECIFIC: @@ -5328,7 +5332,11 @@ u8 join_cmd_hdl(struct adapter *padapter, u8 *pbuf) /* sizeof(struct ndis_802_11_fix_ie) */ for (i = _FIXED_IE_LENGTH_; i < pnetwork->ie_length;) { + if (i + sizeof(*pIE) > pnetwork->ie_length) + break; pIE = (struct ndis_80211_var_ie *)(pnetwork->ies + i); + if (i + sizeof(*pIE) + pIE->length > pnetwork->ie_length) + break; switch (pIE->element_id) { case WLAN_EID_VENDOR_SPECIFIC:/* Get WMM IE. */ From 4380b3860d887a13555ff024a58dfc05b490dfd6 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:45:30 +0200 Subject: [PATCH 0314/2758] staging: rtl8723bs: fix OOB reads in is_ap_in_tkip() IE loop commit 3bf39f711ff27c64be8680a8938bcc5001982e81 upstream. The loop in is_ap_in_tkip() iterates over IEs without verifying that enough bytes remain before dereferencing the IE header or its payload: - pIE->element_id and pIE->length are read without checking that i + sizeof(*pIE) <= ie_length, so a truncated IE at the end of the buffer causes an OOB read. - For WLAN_EID_VENDOR_SPECIFIC the code compares pIE->data + 12, which requires pIE->length >= 16. For WLAN_EID_RSN it compares pIE->data + 8, requiring pIE->length >= 12. Neither requirement is checked. Add the missing IE header and payload bounds checks and guard each data access with an explicit pIE->length minimum, matching the pattern established in update_beacon_info(). Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable Signed-off-by: Alexandru Hossu Link: https://patch.msgid.link/20260522004531.1038924-7-hossu.alexandru@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_wlan_util.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c index e0d73c267786c..8be55e1b520a9 100644 --- a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c +++ b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c @@ -1334,15 +1334,23 @@ unsigned int is_ap_in_tkip(struct adapter *padapter) for (i = sizeof(struct ndis_802_11_fix_ie); i < pmlmeinfo->network.ie_length;) { pIE = (struct ndis_80211_var_ie *)(pmlmeinfo->network.ies + i); + if (i + sizeof(*pIE) > pmlmeinfo->network.ie_length) + break; + if (i + sizeof(*pIE) + pIE->length > pmlmeinfo->network.ie_length) + break; + switch (pIE->element_id) { case WLAN_EID_VENDOR_SPECIFIC: - if ((!memcmp(pIE->data, RTW_WPA_OUI, 4)) && (!memcmp((pIE->data + 12), WPA_TKIP_CIPHER, 4))) + if (pIE->length >= 16 && + !memcmp(pIE->data, RTW_WPA_OUI, 4) && + !memcmp((pIE->data + 12), WPA_TKIP_CIPHER, 4)) return true; break; case WLAN_EID_RSN: - if (!memcmp((pIE->data + 8), RSN_TKIP_CIPHER, 4)) + if (pIE->length >= 12 && + !memcmp((pIE->data + 8), RSN_TKIP_CIPHER, 4)) return true; break; @@ -1350,7 +1358,7 @@ unsigned int is_ap_in_tkip(struct adapter *padapter) break; } - i += (pIE->length + 2); + i += sizeof(*pIE) + pIE->length; } return false; From 729c4e72563bda0f1725db1db9ea08df06f41d9b Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:45:31 +0200 Subject: [PATCH 0315/2758] staging: rtl8723bs: fix OOB reads in rtw_get_sec_ie(), rtw_get_wapi_ie(), and rtw_get_wps_attr() commit 1463ca3ec6601cbb097d8d87dbf5dcf1cb86a344 upstream. Three IE/attribute parsing functions have missing bounds checks. rtw_get_sec_ie() and rtw_get_wapi_ie() iterate over a raw IE buffer without verifying that the header bytes (tag + length) are within the remaining buffer before reading them. Additionally, rtw_get_sec_ie() compares the 4-byte WPA OUI at cnt+2 without checking that at least 6 bytes remain, and rtw_get_wapi_ie() compares a 4-byte WAPI OUI at cnt+6 without checking that at least 10 bytes remain. rtw_get_wps_attr() reads wps_ie[0] and wps_ie+2 unconditionally at entry, before verifying that wps_ielen is large enough to contain the 6-byte WPS IE header (element_id + length + 4-byte OUI). Inside the attribute loop, get_unaligned_be16() is called on attr_ptr and attr_ptr+2 without checking that 4 bytes remain in the buffer. Add a cnt+2 bounds check before each loop body in rtw_get_sec_ie() and rtw_get_wapi_ie(), guard each multi-byte comparison with a minimum IE length requirement, add a wps_ielen < 6 early return in rtw_get_wps_attr(), and add a 4-byte bounds check in its inner loop. Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable Signed-off-by: Alexandru Hossu Link: https://patch.msgid.link/20260522004531.1038924-8-hossu.alexandru@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_ieee80211.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c index 72b7f731dd471..3c1f0068cd92d 100644 --- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c +++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c @@ -583,9 +583,14 @@ int rtw_get_wapi_ie(u8 *in_ie, uint in_len, u8 *wapi_ie, u16 *wapi_len) cnt = (_TIMESTAMP_ + _BEACON_ITERVAL_ + _CAPABILITY_); while (cnt < in_len) { + if (cnt + 2 > in_len) + break; + if (cnt + 2 + in_ie[cnt + 1] > in_len) + break; authmode = in_ie[cnt]; if (authmode == WLAN_EID_BSS_AC_ACCESS_DELAY && + in_ie[cnt + 1] >= 8 && (!memcmp(&in_ie[cnt + 6], wapi_oui1, 4) || !memcmp(&in_ie[cnt + 6], wapi_oui2, 4))) { if (wapi_ie) @@ -616,9 +621,14 @@ void rtw_get_sec_ie(u8 *in_ie, uint in_len, u8 *rsn_ie, u16 *rsn_len, u8 *wpa_ie cnt = (_TIMESTAMP_ + _BEACON_ITERVAL_ + _CAPABILITY_); while (cnt < in_len) { + if (cnt + 2 > in_len) + break; + if (cnt + 2 + in_ie[cnt + 1] > in_len) + break; authmode = in_ie[cnt]; if ((authmode == WLAN_EID_VENDOR_SPECIFIC) && + in_ie[cnt + 1] >= 4 && (!memcmp(&in_ie[cnt + 2], &wpa_oui[0], 4))) { if (wpa_ie) memcpy(wpa_ie, &in_ie[cnt], in_ie[cnt + 1] + 2); @@ -699,6 +709,9 @@ u8 *rtw_get_wps_attr(u8 *wps_ie, uint wps_ielen, u16 target_attr_id, u8 *buf_att if (len_attr) *len_attr = 0; + if (wps_ielen < 6) + return attr_ptr; + if ((wps_ie[0] != WLAN_EID_VENDOR_SPECIFIC) || (memcmp(wps_ie + 2, wps_oui, 4))) { return attr_ptr; @@ -709,6 +722,8 @@ u8 *rtw_get_wps_attr(u8 *wps_ie, uint wps_ielen, u16 target_attr_id, u8 *buf_att while (attr_ptr - wps_ie < wps_ielen) { /* 4 = 2(Attribute ID) + 2(Length) */ + if (attr_ptr + 4 > wps_ie + wps_ielen) + break; u16 attr_id = get_unaligned_be16(attr_ptr); u16 attr_data_len = get_unaligned_be16(attr_ptr + 2); u16 attr_len = attr_data_len + 4; From 225b6d3fc7e99ac3d20b6c861d1e47d24e7ea31d Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:45:28 +0200 Subject: [PATCH 0316/2758] staging: rtl8723bs: fix OOB write in HT_caps_handler() commit f8001e1a516ba3b495728c65b61f799cbfad6bd0 upstream. HT_caps_handler() iterates pIE->length bytes and writes into HT_caps.u.HT_cap[], which is a fixed 26-byte array (sizeof struct HT_caps_element). Because pIE->length is a raw u8 from an over-the-air 802.11 AssocResponse frame and is never validated, a malicious AP can set it up to 255, causing up to 229 bytes of out-of-bounds writes into adjacent fields of struct mlme_ext_info. Truncate the iteration count to the size of HT_caps.u.HT_cap using umin() so that data from a longer-than-expected IE is silently ignored rather than written out of bounds, preserving interoperability with APs that pad the element. An early return on oversized IEs was considered but rejected: it would bypass the pmlmeinfo->HT_caps_enable = 1 assignment that precedes the loop, silently disabling HT mode for APs that append extra bytes to the HT Capabilities IE. Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable Signed-off-by: Alexandru Hossu Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260522004531.1038924-5-hossu.alexandru@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_wlan_util.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c index 8be55e1b520a9..94bbe7ac13ac7 100644 --- a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c +++ b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c @@ -936,7 +936,8 @@ void HT_caps_handler(struct adapter *padapter, struct ndis_80211_var_ie *pIE) pmlmeinfo->HT_caps_enable = 1; - for (i = 0; i < (pIE->length); i++) { + for (i = 0; i < umin(pIE->length, + sizeof(pmlmeinfo->HT_caps.u.HT_cap)); i++) { if (i != 2) { /* Commented by Albert 2010/07/12 */ /* Got the endian issue here. */ From 6dda8406d8a3da2519c8b388d443d7357839cb63 Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Fri, 8 May 2026 12:24:16 +0800 Subject: [PATCH 0317/2758] crypto: amlogic - avoid double cleanup in meson_crypto_probe() commit 6d827ade51a24e18d81afb9f32756d339520a14c upstream. When meson_allocate_chanlist() fails after a partial allocation, it already unwinds the allocated chanlist state through its local error path. meson_crypto_probe() then jump to error_flow and calls meson_free_chanlist() again, causing the same per-flow resources to be torn down twice. In the reproduced failure path, the second teardown re-entered crypto_engine_exit() on an already destroyed worker and KASAN reported a slab-use-after-free in kthread_destroy_worker(). Prevent double-free by handling partial allocation failures locally within meson_allocate_chanlist() and skipping the outer cleanup path. The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. The bug was reproduced in a QEMU x86_64 guest booted with KASAN on v7.1, using the reproducer under tools/testing/meson_crypto_probe. The reproducer forces the second dma_alloc_attrs() call in the gxl-crypto probe path to return NULL, making meson_allocate_chanlist() fail after partial initialization. On the unpatched kernel this reliably triggered a slab-use-after-free. With this fix applied, the same reproducer no longer emits any KASAN report and the probe fails cleanly with -ENOMEM. ================================================================== BUG: KASAN: slab-use-after-free in kthread_destroy_worker+0xb2/0xd0 Read of size 8 at addr ff1100010c057a68 by task insmod/265 CPU: 1 UID: 0 PID: 265 Comm: insmod Tainted: G O 7.1.0-rc2-00376-g810af9adc907-dirty #10 PREEMPT(lazy) Tainted: [O]=OOT_MODULE Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.15.0-1 04/01/2014 Call Trace: dump_stack_lvl+0x68/0xa0 print_report+0xcb/0x5e0 ? __virt_addr_valid+0x21d/0x3f0 ? kthread_destroy_worker+0xb2/0xd0 ? kthread_destroy_worker+0xb2/0xd0 kasan_report+0xca/0x100 ? kthread_destroy_worker+0xb2/0xd0 kthread_destroy_worker+0xb2/0xd0 meson_crypto_probe+0x4d0/0xc10 [amlogic_gxl_crypto] platform_probe+0x99/0x140 really_probe+0x1c6/0x6a0 ? __pfx___device_attach_driver+0x10/0x10 __driver_probe_device+0x248/0x310 ? acpi_driver_match_device+0xb0/0x100 driver_probe_device+0x48/0x210 ? __pfx___device_attach_driver+0x10/0x10 __device_attach_driver+0x160/0x320 bus_for_each_drv+0x104/0x190 ? __pfx_bus_for_each_drv+0x10/0x10 ? _raw_spin_unlock_irqrestore+0x2c/0x50 __device_attach+0x19d/0x3b0 ? __pfx___device_attach+0x10/0x10 ? do_raw_spin_unlock+0x53/0x220 device_initial_probe+0x78/0xa0 bus_probe_device+0x5b/0x130 device_add+0xcfd/0x1430 ? __pfx_device_add+0x10/0x10 ? insert_resource+0x34/0x50 ? lock_release+0xc9/0x290 platform_device_add+0x24e/0x590 ? __pfx_meson_crypto_probe_repro_init+0x10/0x10 [meson_crypto_probe_repro] meson_crypto_probe_repro_init+0x330/0xff0 [meson_crypto_probe_repro] do_one_initcall+0xc0/0x450 ? __pfx_do_one_initcall+0x10/0x10 ? _raw_spin_unlock_irqrestore+0x2c/0x50 ? __create_object+0x59/0x80 ? kasan_unpoison+0x27/0x60 do_init_module+0x27b/0x7d0 ? __pfx_do_init_module+0x10/0x10 ? kasan_quarantine_put+0x84/0x1d0 ? kfree+0x32c/0x510 ? load_module+0x561e/0x5ff0 load_module+0x54fe/0x5ff0 ? __pfx_load_module+0x10/0x10 ? security_file_permission+0x20/0x40 ? kernel_read_file+0x23d/0x6e0 ? mmap_region+0x235/0x4a0 ? __pfx_kernel_read_file+0x10/0x10 ? __file_has_perm+0x2c0/0x3e0 init_module_from_file+0x158/0x180 ? __pfx_init_module_from_file+0x10/0x10 ? __lock_acquire+0x45a/0x1ba0 ? idempotent_init_module+0x315/0x610 ? lock_release+0xc9/0x290 ? lockdep_init_map_type+0x4b/0x220 ? do_raw_spin_unlock+0x53/0x220 idempotent_init_module+0x330/0x610 ? __pfx_idempotent_init_module+0x10/0x10 ? __pfx_cred_has_capability.isra.0+0x10/0x10 ? ksys_mmap_pgoff+0x385/0x520 __x64_sys_finit_module+0xbe/0x120 do_syscall_64+0x115/0x690 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7f7d6d31690d Code: 5b 41 5c c3 66 0f 1f 84 00 00 00 00 00 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 8b 0d f3 b4 0f 00 f7 d8 > RSP: 002b:00007fffc027ac68 EFLAGS: 00000246 ORIG_RAX: 0000000000000139 RAX: ffffffffffffffda RBX: 000055f7b81967c0 RCX: 00007f7d6d31690d RDX: 0000000000000000 RSI: 000055f79a0d6cd2 RDI: 0000000000000003 RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000003 R11: 0000000000000246 R12: 000055f79a0d6cd2 R13: 000055f7b8196790 R14: 000055f79a0d5888 R15: 000055f7b81968e0 Fixes: 48fe583fe541 ("crypto: amlogic - Add crypto accelerator for amlogic GXL") Cc: stable@vger.kernel.org Signed-off-by: Zilin Guan Signed-off-by: Dawei Feng Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/amlogic/amlogic-gxl-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/crypto/amlogic/amlogic-gxl-core.c b/drivers/crypto/amlogic/amlogic-gxl-core.c index 1c18a5b8470e0..6cb33949915fe 100644 --- a/drivers/crypto/amlogic/amlogic-gxl-core.c +++ b/drivers/crypto/amlogic/amlogic-gxl-core.c @@ -291,8 +291,8 @@ static int meson_crypto_probe(struct platform_device *pdev) return 0; error_alg: meson_unregister_algs(mc); -error_flow: meson_free_chanlist(mc, MAXFLOW - 1); +error_flow: clk_disable_unprepare(mc->busclk); return err; } From 2b7bd6dccff14b8b632c5244f1fd506918077221 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 10 May 2026 19:24:55 -0400 Subject: [PATCH 0318/2758] crypto: krb5 - filter out async aead implementations at alloc commit 6c9dddeb582fde005360f4fe02c760d45ca05fb5 upstream. krb5_aead_encrypt(), krb5_aead_decrypt() in rfc3961_simplified.c and rfc8009_encrypt(), rfc8009_decrypt() in rfc8009_aes2.c set a NULL completion callback and treat any negative return from crypto_aead_{encrypt,decrypt}() as terminal, falling through to kfree_sensitive(buffer). When the encrypt_name resolves to an async AEAD instance the request returns -EINPROGRESS, the buffer is freed while the backend's worker still holds a pointer, and the worker dereferences the freed slab on completion. KASAN report under UML+SLUB with a synthetic async aead backend bound to krb5->encrypt_name: BUG: KASAN: slab-use-after-free in t5_stub_complete+0x7d/0xc7 The helpers were written synchronously, so filter the async instances out at allocation time instead of plumbing crypto_wait_req() through every call site. Reachable via net/rxrpc/rxgk.c, fs/afs/cm_security.c and net/ceph/crypto.c on systems with an async AEAD provider bound to the krb5 enctype name. Fixes: 00244da40f78 ("crypto/krb5: Implement the Kerberos5 rfc3961 encrypt and decrypt functions") Fixes: 6c3c0e86c2ac ("crypto/krb5: Implement the AES enctypes from rfc8009") Cc: stable@vger.kernel.org Suggested-by: Herbert Xu Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- crypto/krb5/krb5_api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/krb5/krb5_api.c b/crypto/krb5/krb5_api.c index c7ea40f900a77..03395b89cc61e 100644 --- a/crypto/krb5/krb5_api.c +++ b/crypto/krb5/krb5_api.c @@ -207,7 +207,7 @@ struct crypto_aead *krb5_prepare_encryption(const struct krb5_enctype *krb5, struct crypto_aead *ci = NULL; int ret = -ENOMEM; - ci = crypto_alloc_aead(krb5->encrypt_name, 0, 0); + ci = crypto_alloc_aead(krb5->encrypt_name, 0, CRYPTO_ALG_ASYNC); if (IS_ERR(ci)) { ret = PTR_ERR(ci); if (ret == -ENOENT) From 6e92b28cd74fa433658efeadf21b9d4b01023d7d Mon Sep 17 00:00:00 2001 From: Giovanni Cabiddu Date: Wed, 13 May 2026 15:47:32 +0100 Subject: [PATCH 0319/2758] crypto: qat - fix VF2PF work teardown race in adf_disable_sriov() commit 277281c10c63791067d24d421f7c43a15faa9096 upstream. The VF2PF interrupt handler queues PF-side response work that stores a raw pointer to per-VF state (struct adf_accel_vf_info). Currently, adf_disable_sriov() destroys per-VF mutexes and frees vf_info without stopping new VF2PF work or waiting for in-flight workers to complete. A concurrently scheduled or already queued worker can then dereference freed memory. This manifests as a use-after-free when KASAN is enabled: BUG: KASAN: null-ptr-deref in mutex_lock+0x76/0xe0 Write of size 8 at addr 0000000000000260 by task kworker/24:2/... Workqueue: qat_pf2vf_resp_wq adf_iov_send_resp [intel_qat] Call Trace: kasan_report+0x119/0x140 mutex_lock+0x76/0xe0 adf_gen4_pfvf_send+0xd4/0x1f0 [intel_qat] adf_recv_and_handle_vf2pf_msg+0x290/0x360 [intel_qat] adf_iov_send_resp+0x8c/0xe0 [intel_qat] process_one_work+0x6ac/0xfd0 worker_thread+0x4dd/0xd30 kthread+0x326/0x410 ret_from_fork+0x33b/0x670 Add a PF-local flag, vf2pf_disabled, that gates work queueing, worker processing, and interrupt re-enabling during teardown. Set this flag atomically with the hardware interrupt mask inside adf_disable_all_vf2pf_interrupts(). After masking, synchronize the AE cluster MSI-X interrupt and flush the PF response workqueue before tearing down per-VF locks and state so all in-flight work completes before vf_info is destroyed. Introduce adf_enable_all_vf2pf_interrupts() to clear the flag and unmask all VF2PF interrupts under the same lock when SR-IOV is re-enabled. This ensures the software flag and hardware state transition atomically on both the enable and disable paths. Cc: stable@vger.kernel.org Fixes: ed8ccaef52fa ("crypto: qat - Add support for SRIOV") Signed-off-by: Giovanni Cabiddu Reviewed-by: Ahsan Atta Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- .../intel/qat/qat_common/adf_accel_devices.h | 2 + .../intel/qat/qat_common/adf_common_drv.h | 2 + drivers/crypto/intel/qat/qat_common/adf_isr.c | 39 +++++++++++++++++++ .../crypto/intel/qat/qat_common/adf_sriov.c | 20 +++++++++- 4 files changed, 61 insertions(+), 2 deletions(-) diff --git a/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h b/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h index cbd1d1eda5a34..58cf98fadb6ca 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h +++ b/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h @@ -475,6 +475,8 @@ struct adf_accel_dev { struct { /* protects VF2PF interrupts access */ spinlock_t vf2pf_ints_lock; + /* prevents VF2PF handling from racing with VF state teardown */ + bool vf2pf_disabled; /* vf_info is non-zero when SR-IOV is init'ed */ struct adf_accel_vf_info *vf_info; } pf; diff --git a/drivers/crypto/intel/qat/qat_common/adf_common_drv.h b/drivers/crypto/intel/qat/qat_common/adf_common_drv.h index 8ff35dd840f3f..da0ac836c2b41 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_common_drv.h +++ b/drivers/crypto/intel/qat/qat_common/adf_common_drv.h @@ -118,6 +118,7 @@ void qat_comp_alg_callback(void *resp); int adf_isr_resource_alloc(struct adf_accel_dev *accel_dev); void adf_isr_resource_free(struct adf_accel_dev *accel_dev); +void adf_isr_sync_ae_cluster(struct adf_accel_dev *accel_dev); int adf_vf_isr_resource_alloc(struct adf_accel_dev *accel_dev); void adf_vf_isr_resource_free(struct adf_accel_dev *accel_dev); @@ -191,6 +192,7 @@ int adf_sriov_configure(struct pci_dev *pdev, int numvfs); void adf_disable_sriov(struct adf_accel_dev *accel_dev); void adf_reenable_sriov(struct adf_accel_dev *accel_dev); void adf_enable_vf2pf_interrupts(struct adf_accel_dev *accel_dev, u32 vf_mask); +void adf_enable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev, u32 num_vfs); void adf_disable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev); bool adf_recv_and_handle_pf2vf_msg(struct adf_accel_dev *accel_dev); bool adf_recv_and_handle_vf2pf_msg(struct adf_accel_dev *accel_dev, u32 vf_nr); diff --git a/drivers/crypto/intel/qat/qat_common/adf_isr.c b/drivers/crypto/intel/qat/qat_common/adf_isr.c index 4639d7fd93e6e..159e91a50106b 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_isr.c +++ b/drivers/crypto/intel/qat/qat_common/adf_isr.c @@ -62,6 +62,23 @@ void adf_enable_vf2pf_interrupts(struct adf_accel_dev *accel_dev, u32 vf_mask) unsigned long flags; spin_lock_irqsave(&accel_dev->pf.vf2pf_ints_lock, flags); + if (!READ_ONCE(accel_dev->pf.vf2pf_disabled)) + GET_PFVF_OPS(accel_dev)->enable_vf2pf_interrupts(pmisc_addr, vf_mask); + spin_unlock_irqrestore(&accel_dev->pf.vf2pf_ints_lock, flags); +} + +void adf_enable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev, u32 num_vfs) +{ + void __iomem *pmisc_addr = adf_get_pmisc_base(accel_dev); + unsigned long flags; + u32 vf_mask; + + vf_mask = BIT_ULL(num_vfs) - 1; + if (!vf_mask) + return; + + spin_lock_irqsave(&accel_dev->pf.vf2pf_ints_lock, flags); + WRITE_ONCE(accel_dev->pf.vf2pf_disabled, false); GET_PFVF_OPS(accel_dev)->enable_vf2pf_interrupts(pmisc_addr, vf_mask); spin_unlock_irqrestore(&accel_dev->pf.vf2pf_ints_lock, flags); } @@ -72,6 +89,7 @@ void adf_disable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev) unsigned long flags; spin_lock_irqsave(&accel_dev->pf.vf2pf_ints_lock, flags); + WRITE_ONCE(accel_dev->pf.vf2pf_disabled, true); GET_PFVF_OPS(accel_dev)->disable_all_vf2pf_interrupts(pmisc_addr); spin_unlock_irqrestore(&accel_dev->pf.vf2pf_ints_lock, flags); } @@ -174,6 +192,27 @@ static irqreturn_t adf_msix_isr_ae(int irq, void *dev_ptr) return IRQ_NONE; } +void adf_isr_sync_ae_cluster(struct adf_accel_dev *accel_dev) +{ + struct adf_accel_pci *pci_dev_info = &accel_dev->accel_pci_dev; + struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev); + u32 num_entries = pci_dev_info->msix_entries.num_entries; + struct adf_irq *irqs = pci_dev_info->msix_entries.irqs; + u32 irq_idx; + int irq; + + if (!test_bit(ADF_STATUS_IRQ_ALLOCATED, &accel_dev->status) || !irqs) + return; + + irq_idx = num_entries > 1 ? hw_data->num_banks : 0; + if (irq_idx >= num_entries || !irqs[irq_idx].enabled) + return; + + irq = pci_irq_vector(pci_dev_info->pci_dev, hw_data->num_banks); + if (irq > 0) + synchronize_irq(irq); +} + static void adf_free_irqs(struct adf_accel_dev *accel_dev) { struct adf_accel_pci *pci_dev_info = &accel_dev->accel_pci_dev; diff --git a/drivers/crypto/intel/qat/qat_common/adf_sriov.c b/drivers/crypto/intel/qat/qat_common/adf_sriov.c index 8bf0fe1fcb4db..96939572109eb 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_sriov.c +++ b/drivers/crypto/intel/qat/qat_common/adf_sriov.c @@ -26,6 +26,9 @@ static void adf_iov_send_resp(struct work_struct *work) u32 vf_nr = vf_info->vf_nr; bool ret; + if (READ_ONCE(accel_dev->pf.vf2pf_disabled)) + goto out; + mutex_lock(&vf_info->pfvf_mig_lock); ret = adf_recv_and_handle_vf2pf_msg(accel_dev, vf_nr); if (ret) @@ -33,13 +36,18 @@ static void adf_iov_send_resp(struct work_struct *work) adf_enable_vf2pf_interrupts(accel_dev, 1 << vf_nr); mutex_unlock(&vf_info->pfvf_mig_lock); +out: kfree(pf2vf_resp); } void adf_schedule_vf2pf_handler(struct adf_accel_vf_info *vf_info) { + struct adf_accel_dev *accel_dev = vf_info->accel_dev; struct adf_pf2vf_resp *pf2vf_resp; + if (READ_ONCE(accel_dev->pf.vf2pf_disabled)) + return; + pf2vf_resp = kzalloc_obj(*pf2vf_resp, GFP_ATOMIC); if (!pf2vf_resp) return; @@ -49,6 +57,12 @@ void adf_schedule_vf2pf_handler(struct adf_accel_vf_info *vf_info) queue_work(pf2vf_resp_wq, &pf2vf_resp->pf2vf_resp_work); } +static void adf_flush_pf2vf_resp_wq(void) +{ + if (pf2vf_resp_wq) + flush_workqueue(pf2vf_resp_wq); +} + static int adf_enable_sriov(struct adf_accel_dev *accel_dev) { struct pci_dev *pdev = accel_to_pci_dev(accel_dev); @@ -75,7 +89,7 @@ static int adf_enable_sriov(struct adf_accel_dev *accel_dev) hw_data->configure_iov_threads(accel_dev, true); /* Enable VF to PF interrupts for all VFs */ - adf_enable_vf2pf_interrupts(accel_dev, BIT_ULL(totalvfs) - 1); + adf_enable_all_vf2pf_interrupts(accel_dev, totalvfs); /* * Due to the hardware design, when SR-IOV and the ring arbiter @@ -248,8 +262,10 @@ void adf_disable_sriov(struct adf_accel_dev *accel_dev) adf_pf2vf_wait_for_restarting_complete(accel_dev); pci_disable_sriov(accel_to_pci_dev(accel_dev)); - /* Disable VF to PF interrupts */ + /* Block VF2PF work and disable VF to PF interrupts */ adf_disable_all_vf2pf_interrupts(accel_dev); + adf_isr_sync_ae_cluster(accel_dev); + adf_flush_pf2vf_resp_wq(); /* Clear Valid bits in AE Thread to PCIe Function Mapping */ if (hw_data->configure_iov_threads) From 12c36c99655f325befe50c26842f7deca414c381 Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Wed, 10 Jun 2026 19:53:14 +0900 Subject: [PATCH 0320/2758] ksmbd: fix use-after-free of a deferred file_lock on SMB2_CLOSE then SMB2_CANCEL commit 10f293a07f9e10e988b0ae44e2e99c631f5a68e0 upstream. Commit f580d27e8928 ("ksmbd: fix use-after-free of a deferred file_lock on double SMB2_CANCEL") made smb2_cancel() skip a work whose state is KSMBD_WORK_CANCELLED, so its cancel_fn cannot be fired a second time. But KSMBD_WORK has three states (ACTIVE, CANCELLED, CLOSED), and the same freeing producer path is reached for CLOSED too: SMB2_CLOSE on the locking handle -> set_close_state_blocked_works() sets the deferred work's state to KSMBD_WORK_CLOSED and wakes the smb2_lock() worker. The worker takes the non-ACTIVE early-exit, locks_free_lock()s the file_lock and, because the state is not KSMBD_WORK_CANCELLED, takes the STATUS_RANGE_NOT_LOCKED branch with "goto out2" -- which, like the cancelled branch, skips release_async_work(). The work stays on conn->async_requests with a live cancel_fn = smb2_remove_blocked_lock pointing at the freed file_lock. A subsequent SMB2_CANCEL for the same AsyncId then passes the KSMBD_WORK_CANCELLED-only guard (its state is KSMBD_WORK_CLOSED), so smb2_cancel() fires cancel_fn again over the freed file_lock -- the same use-after-free fixed, via SMB2_CLOSE instead of a first SMB2_CANCEL: BUG: KASAN: slab-use-after-free in __locks_delete_block __locks_delete_block locks_delete_block ksmbd_vfs_posix_lock_unblock smb2_remove_blocked_lock smb2_cancel <- 2nd SMB2_CANCEL fires cancel_fn handle_ksmbd_work Allocated by ...: locks_alloc_lock <- smb2_lock Freed by ...: locks_free_lock <- smb2_lock (non-ACTIVE early-exit) ... cache file_lock_cache of size 192 Reproduced on mainline 7.1-rc7 (which already contains f580d27e8928) with KASAN by an authenticated SMB client; the double-SMB2_CANCEL control is silent on that kernel, so the splat is attributable to the CLOSE trigger. Only an ACTIVE deferred work may have its cancel_fn fired: both terminal states (CANCELLED and CLOSED) reach the smb2_lock() early-exit that frees the file_lock and skips release_async_work(). Guard on KSMBD_WORK_ACTIVE so any non-active work is skipped. Fixes: f580d27e8928 ("ksmbd: fix use-after-free of a deferred file_lock on double SMB2_CANCEL") Cc: stable@vger.kernel.org Signed-off-by: Gil Portnoy Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smb2pdu.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 95d0ea045cfb0..53c0883d1bb9a 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -7328,14 +7328,14 @@ int smb2_cancel(struct ksmbd_work *work) continue; /* - * A cancelled deferred byte-range lock frees its - * file_lock and takes the smb2_lock() early-exit that - * skips release_async_work(), so the work stays on - * conn->async_requests with a live cancel_fn pointing - * at the freed file_lock. Re-firing it on a second - * SMB2_CANCEL is a use-after-free. + * Only an ACTIVE deferred work may have its cancel_fn + * fired. A CANCELLED or CLOSED work already took the + * smb2_lock() non-ACTIVE early-exit that frees the + * file_lock and skips release_async_work(), so it is + * still on conn->async_requests with a live cancel_fn + * pointing at the freed file_lock. */ - if (iter->state == KSMBD_WORK_CANCELLED) + if (iter->state != KSMBD_WORK_ACTIVE) break; ksmbd_debug(SMB, From cea34abc94b0a81e3a8b5cfb41cf45af37c2c67e Mon Sep 17 00:00:00 2001 From: Zijing Yin Date: Mon, 8 Jun 2026 07:44:41 -0700 Subject: [PATCH 0321/2758] net: af_key: initialize alg_key_len for IPComp states commit d129c3177d7b1138fd5066fcc63a698b3ba415b0 upstream. pfkey_msg2xfrm_state() handles the IPComp (SADB_X_SATYPE_IPCOMP) case by allocating x->calg and copying only the algorithm name: x->calg = kmalloc_obj(*x->calg); if (!x->calg) { err = -ENOMEM; goto out; } strcpy(x->calg->alg_name, a->name); x->props.calgo = sa->sadb_sa_encrypt; Unlike the authentication (x->aalg) and encryption (x->ealg) branches of the same function, the compression branch never initializes calg->alg_key_len. IPComp carries no key and the allocation only reserves sizeof(struct xfrm_algo) (i.e. no room for a key), so the field is left containing uninitialized slab data. calg->alg_key_len is later used as a length by xfrm_algo_clone() when an IPComp state is cloned during XFRM_MSG_MIGRATE: xfrm_state_migrate() xfrm_state_clone_and_setup() x->calg = xfrm_algo_clone(orig->calg); kmemdup(orig, xfrm_alg_len(orig)); where xfrm_alg_len() returns sizeof(*alg) + (alg_key_len + 7) / 8. With a non-zero garbage alg_key_len, kmemdup() reads past the end of the 68-byte calg object. Adding an IPComp SA via PF_KEY and then migrating it triggers (net-next, KASAN, init_on_alloc=0): BUG: KASAN: slab-out-of-bounds in kmemdup_noprof+0x44/0x60 Read of size 4164 at addr ff11000025a74980 by task diag2/9287 CPU: 3 UID: 0 PID: 9287 Comm: diag2 7.1.0-rc6-g903db046d557 #1 Call Trace: dump_stack_lvl+0x10e/0x1f0 print_report+0xf7/0x600 kasan_report+0xe4/0x120 kasan_check_range+0x105/0x1b0 __asan_memcpy+0x23/0x60 kmemdup_noprof+0x44/0x60 xfrm_state_migrate+0x70a/0x1da0 xfrm_migrate+0x753/0x18a0 xfrm_do_migrate+0xb47/0xf10 xfrm_user_rcv_msg+0x411/0xb50 netlink_rcv_skb+0x158/0x420 xfrm_netlink_rcv+0x71/0x90 netlink_unicast+0x584/0x850 netlink_sendmsg+0x8b0/0xdc0 ____sys_sendmsg+0x9f7/0xb90 ___sys_sendmsg+0x134/0x1d0 __sys_sendmsg+0x16d/0x220 do_syscall_64+0x116/0x7d0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Allocated by task 9287: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0xaa/0xb0 pfkey_add+0x2652/0x2ea0 pfkey_process+0x6d0/0x830 pfkey_sendmsg+0x42c/0x850 __sys_sendto+0x461/0x4b0 __x64_sys_sendto+0xe0/0x1c0 do_syscall_64+0x116/0x7d0 entry_SYSCALL_64_after_hwframe+0x77/0x7f The buggy address belongs to the object at ff11000025a74980 which belongs to the cache kmalloc-96 of size 96 The buggy address is located 0 bytes inside of allocated 68-byte region [ff11000025a74980, ff11000025a749c4) Depending on the uninitialized value the same field can instead request an oversized kmemdup() allocation and make the migration clone fail. The XFRM netlink path is not affected: verify_one_alg() rejects an XFRMA_ALG_COMP attribute shorter than xfrm_alg_len(), so a calg added via XFRM_MSG_NEWSA is always self-consistent. Initialize calg->alg_key_len to 0, matching the aalg/ealg branches. Fixes: 80c9abaabf42 ("[XFRM]: Extension for dynamic update of endpoint address(es)") Cc: stable@vger.kernel.org Signed-off-by: Zijing Yin Reviewed-by: Sabrina Dubroca Signed-off-by: Steffen Klassert Signed-off-by: Greg Kroah-Hartman --- net/key/af_key.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/key/af_key.c b/net/key/af_key.c index 9cffeef18cd91..3216f897a305e 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -1218,6 +1218,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net, goto out; } strcpy(x->calg->alg_name, a->name); + x->calg->alg_key_len = 0; x->props.calgo = sa->sadb_sa_encrypt; } else { int keysize = 0; From c5186201fa7030289cc4fe23fae87a3fcb566856 Mon Sep 17 00:00:00 2001 From: Chi Wang Date: Fri, 19 Jun 2026 15:42:44 +0800 Subject: [PATCH 0322/2758] audit: Fix data races of skb_queue_len() readers on audit_queue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit c9a71daaecb2fb1d8c704545cc0b1c920b9bf5d7 upstream. Multiple readers access audit_queue.qlen via skb_queue_len() without holding the queue lock or using READ_ONCE(), while kauditd writes to this field via the skb_dequeue() → __skb_unlink() path with WRITE_ONCE() protected by a spinlock. This constitutes data races. All affected skb_queue_len(&audit_queue) call sites: - kauditd_thread() wait_event_freezable() condition - audit_receive_msg() AUDIT_GET handler (s.backlog assignment) - audit_receive() backlog check - audit_log_start() backlog check and pr_warn() KCSAN reports the following conflicting access pattern (one example): ================================================================== BUG: KCSAN: data-race in audit_log_start / skb_dequeue write (marked) to 0xffffffff8512ee20 of 4 bytes by task 661 on cpu 57: skb_dequeue+0x70/0xf0 kauditd_send_queue+0x71/0x220 kauditd_thread+0x1cb/0x430 kthread+0x1c2/0x210 ret_from_fork+0x162/0x1a0 ret_from_fork_asm+0x1a/0x30 read to 0xffffffff8512ee20 of 4 bytes by task 36586 on cpu 1: audit_log_start+0x2a0/0x6b0 audit_core_dumps+0x64/0xa0 do_coredump+0x14b/0x1260 get_signal+0xeb2/0xf70 arch_do_signal_or_restart+0x41/0x170 exit_to_user_mode_loop+0xa2/0x1c0 do_syscall_64+0x1a3/0x1c0 entry_SYSCALL_64_after_hwframe+0x76/0xe0 value changed: 0x00000001 -> 0x00000000 ================================================================== Resolve the race by switching to lockless helper skb_queue_len_lockless(), which internally uses READ_ONCE() and properly pairs with the WRITE_ONCE() write accesses already present on the writer side. Cc: stable@vger.kernel.org Fixes: 3197542482df ("audit: rework audit_log_start()") Signed-off-by: Chi Wang Reviewed-by: Ricardo Robaina [PM: line length tweak] Signed-off-by: Paul Moore Signed-off-by: Greg Kroah-Hartman --- kernel/audit.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/kernel/audit.c b/kernel/audit.c index 34dc7cb246ff2..18ed0beba12f7 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -950,7 +950,7 @@ static int kauditd_thread(void *dummy) * do the multicast send and rotate records from the * main queue to the retry/hold queues */ wait_event_freezable(kauditd_wait, - (skb_queue_len(&audit_queue) ? 1 : 0)); + (skb_queue_len_lockless(&audit_queue) ? 1 : 0)); } return 0; @@ -1283,7 +1283,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh, s.rate_limit = audit_rate_limit; s.backlog_limit = audit_backlog_limit; s.lost = atomic_read(&audit_lost); - s.backlog = skb_queue_len(&audit_queue); + s.backlog = skb_queue_len_lockless(&audit_queue); s.feature_bitmap = AUDIT_FEATURE_BITMAP_ALL; s.backlog_wait_time = audit_backlog_wait_time; s.backlog_wait_time_actual = atomic_read(&audit_backlog_wait_time_actual); @@ -1627,7 +1627,7 @@ static void audit_receive(struct sk_buff *skb) /* can't block with the ctrl lock, so penalize the sender now */ if (audit_backlog_limit && - (skb_queue_len(&audit_queue) > audit_backlog_limit)) { + (skb_queue_len_lockless(&audit_queue) > audit_backlog_limit)) { DECLARE_WAITQUEUE(wait, current); /* wake kauditd to try and flush the queue */ @@ -1933,7 +1933,7 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, long stime = audit_backlog_wait_time; while (audit_backlog_limit && - (skb_queue_len(&audit_queue) > audit_backlog_limit)) { + (skb_queue_len_lockless(&audit_queue) > audit_backlog_limit)) { /* wake kauditd to try and flush the queue */ wake_up_interruptible(&kauditd_wait); @@ -1953,7 +1953,7 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, } else { if (audit_rate_check() && printk_ratelimit()) pr_warn("audit_backlog=%d > audit_backlog_limit=%d\n", - skb_queue_len(&audit_queue), + skb_queue_len_lockless(&audit_queue), audit_backlog_limit); audit_log_lost("backlog limit exceeded"); return NULL; From 50c38d9f42a529691e4e67ea9cedf4f0bfc8d277 Mon Sep 17 00:00:00 2001 From: Marco Elver Date: Fri, 5 Jun 2026 16:23:35 +0200 Subject: [PATCH 0323/2758] Bluetooth: L2CAP: Fix UAF in channel timeout by holding conn ref commit b66774b48dd98f07254951f74ea6f513efe7ff8b upstream. l2cap_chan_timeout() runs asynchronously and accesses chan->conn. If the connection is torn down while the timer is running or pending, chan->conn can be freed, leading to a use-after-free when the timer worker attempts to lock conn->lock: | BUG: KASAN: slab-use-after-free in instrument_atomic_read_write include/linux/instrumented.h:112 [inline] | BUG: KASAN: slab-use-after-free in atomic_long_try_cmpxchg_acquire include/linux/atomic/atomic-instrumented.h:4456 [inline] | BUG: KASAN: slab-use-after-free in __mutex_trylock_fast kernel/locking/mutex.c:161 [inline] | BUG: KASAN: slab-use-after-free in mutex_lock+0x4f/0xa0 kernel/locking/mutex.c:318 | Write of size 8 at addr ffff8881298d9550 by task kworker/2:1/83 | | CPU: 2 UID: 0 PID: 83 Comm: kworker/2:1 Not tainted 7.1.0-rc6-next-20260601-dirty #6 PREEMPT(full) | Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-debian-1.17.0-1 04/01/2014 | Workqueue: events l2cap_chan_timeout | Call Trace: | | instrument_atomic_read_write include/linux/instrumented.h:112 [inline] | atomic_long_try_cmpxchg_acquire include/linux/atomic/atomic-instrumented.h:4456 [inline] | __mutex_trylock_fast kernel/locking/mutex.c:161 [inline] | mutex_lock+0x4f/0xa0 kernel/locking/mutex.c:318 | l2cap_chan_timeout+0x5d/0x1b0 net/bluetooth/l2cap_core.c:422 | process_one_work kernel/workqueue.c:3326 [inline] | process_scheduled_works+0x7c8/0xfb0 kernel/workqueue.c:3409 | worker_thread+0x8a9/0xcf0 kernel/workqueue.c:3490 | kthread+0x346/0x430 kernel/kthread.c:436 | ret_from_fork+0x1a3/0x470 arch/x86/kernel/process.c:158 | ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 | | | Allocated by task 320: | l2cap_conn_add+0xa7/0x820 net/bluetooth/l2cap_core.c:7075 | l2cap_connect_cfm+0xdb/0xd70 net/bluetooth/l2cap_core.c:7452 | hci_connect_cfm include/net/bluetooth/hci_core.h:2139 [inline] | hci_remote_features_evt+0x52f/0x9f0 net/bluetooth/hci_event.c:3760 | hci_event_func net/bluetooth/hci_event.c:7796 [inline] | hci_event_packet+0x561/0xa70 net/bluetooth/hci_event.c:7847 | hci_rx_work+0x370/0x890 net/bluetooth/hci_core.c:4040 | process_one_work kernel/workqueue.c:3326 [inline] | process_scheduled_works+0x7c8/0xfb0 kernel/workqueue.c:3409 | worker_thread+0x8a9/0xcf0 kernel/workqueue.c:3490 | kthread+0x346/0x430 kernel/kthread.c:436 | ret_from_fork+0x1a3/0x470 arch/x86/kernel/process.c:158 | ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 | | Freed by task 322: | hci_disconn_cfm include/net/bluetooth/hci_core.h:2154 [inline] | hci_conn_hash_flush+0x101/0x1f0 net/bluetooth/hci_conn.c:2736 | hci_dev_close_sync+0x889/0xde0 net/bluetooth/hci_sync.c:5405 | hci_dev_do_close net/bluetooth/hci_core.c:502 [inline] | hci_unregister_dev+0x1f7/0x370 net/bluetooth/hci_core.c:2679 | vhci_release+0x12a/0x180 drivers/bluetooth/hci_vhci.c:690 | __fput+0x369/0x890 fs/file_table.c:510 | task_work_run+0x160/0x1d0 kernel/task_work.c:233 | get_signal+0xf5b/0x1120 kernel/signal.c:2810 | arch_do_signal_or_restart+0x4d/0x600 arch/x86/kernel/signal.c:337 | __exit_to_user_mode_loop kernel/entry/common.c:64 [inline] | exit_to_user_mode_loop+0x85/0x510 kernel/entry/common.c:98 | do_syscall_64+0x263/0x3d0 arch/x86/entry/syscall_64.c:100 | entry_SYSCALL_64_after_hwframe+0x77/0x7f | | The buggy address belongs to the object at ffff8881298d9400 | which belongs to the cache kmalloc-512 of size 512 | The buggy address is located 336 bytes inside of | freed 512-byte region [ffff8881298d9400, ffff8881298d9600) Fix it by having chan->conn hold a reference to l2cap_conn (via l2cap_conn_get) when the channel is added to the connection, and releasing it in the channel destructor. This ensures the l2cap_conn remains alive as long as the channel exists. A new FLAG_DEL channel flag is introduced to indicate that the channel has been deleted from its connection. l2cap_chan_del() atomically sets this flag using test_and_set_bit() instead of setting chan->conn to NULL. All asynchronous workers (l2cap_chan_timeout, l2cap_ack_timeout, l2cap_monitor_timeout, l2cap_retrans_timeout) and l2cap_chan_send() check FLAG_DEL to determine whether the channel has been torn down, rather than testing chan->conn for NULL. Fixes: 8c8e620467a7 ("Bluetooth: L2CAP: use chan timer to close channels in cleanup_listen()") Cc: Cc: Siwei Zhang Cc: Luiz Augusto von Dentz Assisted-by: Gemini:gemini-3.1-pro-preview Reported-by: https://sashiko.dev/#/patchset/20260521021249.3258069-1-oss%40fourdim.xyz Signed-off-by: Marco Elver Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/l2cap_core.c | 34 ++++++++++++++++++++-------------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index e0a1f2293679a..6578da0048fb4 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -748,6 +748,7 @@ enum { FLAG_ECRED_CONN_REQ_SENT, FLAG_PENDING_SECURITY, FLAG_HOLD_HCI_CONN, + FLAG_DEL, }; /* Lock nesting levels for L2CAP channels. We need these because lockdep diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index c4ccfbda9d789..1fbd52165fb20 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -411,7 +411,7 @@ static void l2cap_chan_timeout(struct work_struct *work) BT_DBG("chan %p state %s", chan, state_to_string(chan->state)); - if (!conn) { + if (test_bit(FLAG_DEL, &chan->flags)) { l2cap_chan_put(chan); return; } @@ -422,6 +422,9 @@ static void l2cap_chan_timeout(struct work_struct *work) */ l2cap_chan_lock(chan); + if (test_bit(FLAG_DEL, &chan->flags)) + goto unlock; + if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG) reason = ECONNREFUSED; else if (chan->state == BT_CONNECT && @@ -434,10 +437,10 @@ static void l2cap_chan_timeout(struct work_struct *work) chan->ops->close(chan); +unlock: l2cap_chan_unlock(chan); - l2cap_chan_put(chan); - mutex_unlock(&conn->lock); + l2cap_chan_put(chan); } struct l2cap_chan *l2cap_chan_create(void) @@ -490,6 +493,9 @@ static void l2cap_chan_destroy(struct kref *kref) list_del(&chan->global_l); write_unlock(&chan_list_lock); + if (chan->conn) + l2cap_conn_put(chan->conn); + kfree(chan); } @@ -593,7 +599,7 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM; - chan->conn = conn; + chan->conn = l2cap_conn_get(conn); switch (chan->chan_type) { case L2CAP_CHAN_CONN_ORIENTED: @@ -648,30 +654,26 @@ void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) void l2cap_chan_del(struct l2cap_chan *chan, int err) { - struct l2cap_conn *conn = chan->conn; - __clear_chan_timer(chan); - BT_DBG("chan %p, conn %p, err %d, state %s", chan, conn, err, + BT_DBG("chan %p, err %d, state %s", chan, err, state_to_string(chan->state)); chan->ops->teardown(chan, err); - if (conn) { + if (!test_and_set_bit(FLAG_DEL, &chan->flags)) { /* Delete from channel list */ list_del(&chan->list); l2cap_chan_put(chan); - chan->conn = NULL; - /* Reference was only held for non-fixed channels or * fixed channels that explicitly requested it using the * FLAG_HOLD_HCI_CONN flag. */ if (chan->chan_type != L2CAP_CHAN_FIXED || test_bit(FLAG_HOLD_HCI_CONN, &chan->flags)) - hci_conn_drop(conn->hcon); + hci_conn_drop(chan->conn->hcon); } if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state)) @@ -1903,7 +1905,7 @@ static void l2cap_monitor_timeout(struct work_struct *work) l2cap_chan_lock(chan); - if (!chan->conn) { + if (test_bit(FLAG_DEL, &chan->flags)) { l2cap_chan_unlock(chan); l2cap_chan_put(chan); return; @@ -1924,7 +1926,7 @@ static void l2cap_retrans_timeout(struct work_struct *work) l2cap_chan_lock(chan); - if (!chan->conn) { + if (test_bit(FLAG_DEL, &chan->flags)) { l2cap_chan_unlock(chan); l2cap_chan_put(chan); return; @@ -2565,7 +2567,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, int err; struct sk_buff_head seg_queue; - if (!chan->conn) + if (test_bit(FLAG_DEL, &chan->flags)) return -ENOTCONN; /* Connectionless channel */ @@ -3160,12 +3162,16 @@ static void l2cap_ack_timeout(struct work_struct *work) l2cap_chan_lock(chan); + if (test_bit(FLAG_DEL, &chan->flags)) + goto unlock; + frames_to_ack = __seq_offset(chan, chan->buffer_seq, chan->last_acked_seq); if (frames_to_ack) l2cap_send_rr_or_rnr(chan, 0); +unlock: l2cap_chan_unlock(chan); l2cap_chan_put(chan); } From 9531014c60c804e16099885d4a98aedcf31bce8d Mon Sep 17 00:00:00 2001 From: Samuel Page Date: Mon, 15 Jun 2026 16:09:22 +0100 Subject: [PATCH 0324/2758] Bluetooth: MGMT: Fix UAF of hci_conn_params in add_device_complete commit fa85d985f614bc3feb343000f14a1072e99b0df1 upstream. add_device_complete() runs from the hci_cmd_sync_work kworker, which holds only hci_req_sync_lock and *not* hci_dev_lock. It calls hci_conn_params_lookup() and then dereferences the returned object (params->flags) without taking hci_dev_lock: params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr, le_addr_type(cp->addr.type)); ... device_flags_changed(NULL, hdev, &cp->addr.bdaddr, cp->addr.type, hdev->conn_flags, params ? params->flags : 0); hci_conn_params_lookup() walks hdev->le_conn_params and is documented to require hdev->lock. A concurrent MGMT_OP_REMOVE_DEVICE (remove_device()), which does run under hci_dev_lock, can call hci_conn_params_free() to list_del() and kfree() the very object the lookup returned, so the subsequent params->flags read touches freed memory [0]. Hold hci_dev_lock() across the hci_conn_params_lookup() and the read of params->flags (and the matching event emission) so the lookup result cannot be freed by a concurrent remove_device() before it is used, honouring the locking contract of hci_conn_params_lookup(). [0]: (trailing page/memory-state dump trimmed) BUG: KASAN: slab-use-after-free in add_device_complete+0x358/0x3d8 net/bluetooth/mgmt.c:7671 Read of size 1 at addr ffff000017ab26c1 by task kworker/u9:8/388 CPU: 1 UID: 0 PID: 388 Comm: kworker/u9:8 Not tainted 7.0.11 #20 PREEMPT Hardware name: linux,dummy-virt (DT) Workqueue: hci0 hci_cmd_sync_work Call trace: show_stack+0x2c/0x3c arch/arm64/kernel/stacktrace.c:499 (C) __dump_stack lib/dump_stack.c:94 [inline] dump_stack_lvl+0xb4/0xd4 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:378 [inline] print_report+0x118/0x5d8 mm/kasan/report.c:482 kasan_report+0xb0/0xf4 mm/kasan/report.c:595 __asan_report_load1_noabort+0x20/0x2c mm/kasan/report_generic.c:378 add_device_complete+0x358/0x3d8 net/bluetooth/mgmt.c:7671 hci_cmd_sync_work+0x14c/0x240 net/bluetooth/hci_sync.c:334 process_one_work+0x628/0xd38 kernel/workqueue.c:3289 process_scheduled_works kernel/workqueue.c:3372 [inline] worker_thread+0x7a8/0xac0 kernel/workqueue.c:3453 kthread+0x39c/0x444 kernel/kthread.c:436 ret_from_fork+0x10/0x20 arch/arm64/kernel/entry.S:860 Allocated by task 3401: kasan_save_stack+0x3c/0x64 mm/kasan/common.c:57 kasan_save_track+0x20/0x3c mm/kasan/common.c:78 kasan_save_alloc_info+0x40/0x54 mm/kasan/generic.c:570 poison_kmalloc_redzone mm/kasan/common.c:398 [inline] __kasan_kmalloc+0xd4/0xd8 mm/kasan/common.c:415 kasan_kmalloc include/linux/kasan.h:263 [inline] __kmalloc_cache_noprof+0x1b0/0x458 mm/slub.c:5385 kmalloc_noprof include/linux/slab.h:950 [inline] kzalloc_noprof include/linux/slab.h:1188 [inline] hci_conn_params_add+0x10c/0x4b0 net/bluetooth/hci_core.c:2279 hci_conn_params_set net/bluetooth/mgmt.c:5162 [inline] add_device+0x5b4/0xa54 net/bluetooth/mgmt.c:7755 hci_mgmt_cmd net/bluetooth/hci_sock.c:1721 [inline] hci_sock_sendmsg+0x10b4/0x1dd0 net/bluetooth/hci_sock.c:1841 sock_sendmsg_nosec net/socket.c:727 [inline] __sock_sendmsg+0xe0/0x128 net/socket.c:742 sock_write_iter+0x250/0x390 net/socket.c:1195 new_sync_write fs/read_write.c:595 [inline] vfs_write+0x66c/0xab0 fs/read_write.c:688 ksys_write+0x1fc/0x24c fs/read_write.c:740 __do_sys_write fs/read_write.c:751 [inline] __se_sys_write fs/read_write.c:748 [inline] __arm64_sys_write+0x70/0xa4 fs/read_write.c:748 __invoke_syscall arch/arm64/kernel/syscall.c:35 [inline] invoke_syscall+0x84/0x2a8 arch/arm64/kernel/syscall.c:49 el0_svc_common.constprop.0+0xe4/0x294 arch/arm64/kernel/syscall.c:132 do_el0_svc+0x44/0x5c arch/arm64/kernel/syscall.c:151 el0_svc+0x38/0xac arch/arm64/kernel/entry-common.c:724 el0t_64_sync_handler+0xa0/0xe4 arch/arm64/kernel/entry-common.c:743 el0t_64_sync+0x198/0x19c arch/arm64/kernel/entry.S:596 Freed by task 3740: kasan_save_stack+0x3c/0x64 mm/kasan/common.c:57 kasan_save_track+0x20/0x3c mm/kasan/common.c:78 kasan_save_free_info+0x4c/0x74 mm/kasan/generic.c:584 poison_slab_object mm/kasan/common.c:253 [inline] __kasan_slab_free+0x88/0xb8 mm/kasan/common.c:285 kasan_slab_free include/linux/kasan.h:235 [inline] slab_free_hook mm/slub.c:2685 [inline] slab_free mm/slub.c:6170 [inline] kfree+0x14c/0x458 mm/slub.c:6488 hci_conn_params_free+0x288/0x484 net/bluetooth/hci_core.c:2312 remove_device+0x4b0/0x968 net/bluetooth/mgmt.c:7919 hci_mgmt_cmd net/bluetooth/hci_sock.c:1721 [inline] hci_sock_sendmsg+0x10b4/0x1dd0 net/bluetooth/hci_sock.c:1841 sock_sendmsg_nosec net/socket.c:727 [inline] __sock_sendmsg+0xe0/0x128 net/socket.c:742 sock_write_iter+0x250/0x390 net/socket.c:1195 new_sync_write fs/read_write.c:595 [inline] vfs_write+0x66c/0xab0 fs/read_write.c:688 ksys_write+0x1fc/0x24c fs/read_write.c:740 __do_sys_write fs/read_write.c:751 [inline] __se_sys_write fs/read_write.c:748 [inline] __arm64_sys_write+0x70/0xa4 fs/read_write.c:748 __invoke_syscall arch/arm64/kernel/syscall.c:35 [inline] invoke_syscall+0x84/0x2a8 arch/arm64/kernel/syscall.c:49 el0_svc_common.constprop.0+0xe4/0x294 arch/arm64/kernel/syscall.c:132 do_el0_svc+0x44/0x5c arch/arm64/kernel/syscall.c:151 el0_svc+0x38/0xac arch/arm64/kernel/entry-common.c:724 el0t_64_sync_handler+0xa0/0xe4 arch/arm64/kernel/entry-common.c:743 el0t_64_sync+0x198/0x19c arch/arm64/kernel/entry.S:596 Fixes: 1e2e3044c1bc ("Bluetooth: MGMT: Fix MGMT_OP_ADD_DEVICE invalid device flags") Cc: stable@vger.kernel.org Assisted-by: Bynario AI Signed-off-by: Samuel Page Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/mgmt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index f4aa814a03975..7c164a17ef59e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -7661,6 +7661,8 @@ static void add_device_complete(struct hci_dev *hdev, void *data, int err) if (!err) { struct hci_conn_params *params; + hci_dev_lock(hdev); + params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr, le_addr_type(cp->addr.type)); @@ -7669,6 +7671,7 @@ static void add_device_complete(struct hci_dev *hdev, void *data, int err) device_flags_changed(NULL, hdev, &cp->addr.bdaddr, cp->addr.type, hdev->conn_flags, params ? params->flags : 0); + hci_dev_unlock(hdev); } mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_ADD_DEVICE, From 2edd162cbd4a00f5467a51a4c37deb5f7956f692 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Fri, 29 May 2026 00:52:01 +0800 Subject: [PATCH 0325/2758] coresight: etb10: restore atomic_t for shared reading state commit fa09f08ede3db3050ae16ae1ed92c902d0cada23 upstream. The etb10 miscdevice uses drvdata->reading as a shared exclusivity gate for userspace buffer access. etb_open() claims that gate with local_cmpxchg(), and etb_release() clears it with local_set(). That gate is shared per-device state rather than CPU-local state. A running system can reach it whenever /dev/ is opened, closed, and reopened by different tasks while the device remains registered, so the same drvdata->reading variable may be claimed on one CPU and later cleared on another. This code used to use atomic_t for the same gate, but commit 27b10da8fff2 ("coresight: etb10: moving to local atomic operations") changed it to local_t even though the access pattern remained cross-task and cross-CPU. Restore atomic_t together with atomic_cmpxchg() and atomic_set() so the exclusivity gate again uses a primitive intended for shared state. The issue was found on Linux v6.18.21 by our static analysis tool while scanning surviving local_t-on-shared-state sites, and then manually reviewed against the live etb10 file-op path. It was runtime-validated with a reproducible QEMU no-device KCSAN PoC that kept the same report-local contract: 1. use one shared struct etb_drvdata carrier and its drvdata->reading gate; 2. call etb_open() and etb_release() sequentially on that gate to confirm the original claim/clear path; 3. bind the open side to CPU0 and the release side to CPU1 for the same gate to show cross-CPU ownership; 4. run bound workers that repeatedly race etb_open() and etb_release() on the same gate until KCSAN reports a target hit. The harness recorded: L1 passed open=1 release=1 reading_after_open=1 reading_after_release=0 L2 passed open_cpu=0 release_cpu=1 cross_cpu_release=1 reading_after=0 open_ret=0 Representative KCSAN excerpt from the no-device validation run: BUG: KCSAN: data-race in etb_open.constprop.0.isra.0 [vuln_msv] write to 0xffffffffc0003810 of 4 bytes by task 216 on cpu 1: etb_open.constprop.0.isra.0+0x38/0x80 [vuln_msv] l3_worker_thread_fn+0x4f/0xf0 [vuln_msv] kthread+0x17e/0x1c0 ret_from_fork+0x22/0x30 read to 0xffffffffc0003810 of 4 bytes by task 215 on cpu 0: etb_open.constprop.0.isra.0+0x18/0x80 [vuln_msv] l3_worker_thread_fn+0x4f/0xf0 [vuln_msv] kthread+0x17e/0x1c0 ret_from_fork+0x22/0x30 value changed: 0x00000000 -> 0x00000001 Reported by Kernel Concurrency Sanitizer on: CPU: 0 PID: 215 Comm: etb10_l3_a Tainted: G O 6.1.66 #2 This no-device harness is not a real ETB10 hardware end-to-end run, but it preserves the same shared drvdata->reading gate and the same etb_open()/etb_release() claim/clear contract. No real ETB10 hardware was available for runtime testing. Build-tested with: make olddefconfig make -j"$(nproc)" drivers/hwtracing/coresight/coresight-etb10.o Fixes: 27b10da8fff2 ("coresight: etb10: moving to local atomic operations") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Reviewed-by: James Clark Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260528165201.319452-1-runyu.xiao@seu.edu.cn Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etb10.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index b952a1d47f12f..a827f76b8144a 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -83,7 +83,7 @@ struct etb_drvdata { struct coresight_device *csdev; struct miscdevice miscdev; raw_spinlock_t spinlock; - local_t reading; + atomic_t reading; pid_t pid; u8 *buf; u32 buffer_depth; @@ -601,7 +601,7 @@ static int etb_open(struct inode *inode, struct file *file) struct etb_drvdata *drvdata = container_of(file->private_data, struct etb_drvdata, miscdev); - if (local_cmpxchg(&drvdata->reading, 0, 1)) + if (atomic_cmpxchg(&drvdata->reading, 0, 1)) return -EBUSY; dev_dbg(&drvdata->csdev->dev, "%s: successfully opened\n", __func__); @@ -639,7 +639,7 @@ static int etb_release(struct inode *inode, struct file *file) { struct etb_drvdata *drvdata = container_of(file->private_data, struct etb_drvdata, miscdev); - local_set(&drvdata->reading, 0); + atomic_set(&drvdata->reading, 0); dev_dbg(&drvdata->csdev->dev, "%s: released\n", __func__); return 0; From 1f4f02b336c3be125c8fcf87df73db2e0e028b8b Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 21 Jun 2026 16:47:44 +0200 Subject: [PATCH 0326/2758] debugobjects: Plug race against a concurrent OOM disable commit b81dde13cc163450dcb402dcc915ef13ba241e01 upstream. syzbot reported a puzzling splat: WARNING: kernel/time/hrtimer.c:443 at stub_timer+0xa/0x20 stub_timer() is installed as timer callback function in hrtimer_fixup_assert_init(), which is invoked when debug_object_assert_init() can't find a shadow object. In that case debug objects emits a warning about it before invoking the fixup. Though the provided console log lacks this warning and instead has the following a few seconds before the splat: ODEBUG: Out of memory. ODEBUG disabled So the object was looked up in debug_object_assert_init() and the lookup failed due a concurrent out of memory situation which disabled debug objects and freed the shadow objects: debug_object_assert_init() if (!debug_objects_enabled) return; obj = alloc(); if (!obj) { // Out of memory debug_objects_enabled = false; free_objects(); obj = lookup_or_alloc(); // The lookup failed because the other side // removed the objects, so this returns // an error code as the object in question // is not statically initialized if (!IS_ERR_OR_NULL(obj)) return; if (!obj) { debug_oom(); return; } print(...) if (!debug_objects_enabled) return; fixup(...) The debug object splat is skipped because debug_objects_enabled is false, but the fixup callback is invoked unconditionally, which makes the timer disfunctional. This is only a problem in debug_object_assert_init() and debug_object_activate() as both have to handle statically initialized objects and therefore must handle the error pointer return case gracefully. All other places only handle the found/not found case and the NULL pointer return is a signal for OOM. Otherwise they get a valid shadow object. Plug the hole by checking whether debug objects are still enabled before invoking the print and fixup function in those two places. Fixes: b84d435cc228 ("debugobjects: Extend to assert that an object is initialized") Reported-by: syzbot+5e8dda76ca21dae314b6@syzkaller.appspotmail.com Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Link: https://patch.msgid.link/874iiwlzlb.ffs@fw13 Signed-off-by: Greg Kroah-Hartman --- lib/debugobjects.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lib/debugobjects.c b/lib/debugobjects.c index 6fb00e08a4e29..877f7675ba26b 100644 --- a/lib/debugobjects.c +++ b/lib/debugobjects.c @@ -894,6 +894,14 @@ int debug_object_activate(void *addr, const struct debug_obj_descr *descr) } raw_spin_unlock_irqrestore(&db->lock, flags); + + /* + * lookup_object_or_alloc() might have raced with a concurrent + * allocation failure which disabled debug objects. + */ + if (!debug_objects_enabled) + return 0; + debug_print_object(&o, "activate"); switch (o.state) { @@ -1071,6 +1079,15 @@ void debug_object_assert_init(void *addr, const struct debug_obj_descr *descr) return; } + /* + * lookup_object_or_alloc() might have raced with a concurrent + * allocation failure which disabled debug objects. Don't run the fixup + * as it might turn a valid object useless. See for example + * hrtimer_fixup_assert_init(). + */ + if (!debug_objects_enabled) + return; + /* Object is neither tracked nor static. It's not initialized. */ debug_print_object(&o, "assert_init"); debug_object_fixup(descr->fixup_assert_init, addr, ODEBUG_STATE_NOTAVAILABLE); From f433acc85b86f327d03ba8b03a33c105c51053de Mon Sep 17 00:00:00 2001 From: Yunpeng Tian Date: Mon, 4 May 2026 07:19:43 -0700 Subject: [PATCH 0327/2758] fs/ntfs3: validate Dirty Page Table capacity in log_replay copy_lcns commit 57382ec6ac63b63dce2789e835fded28b698ae79 upstream. In the analysis pass of $LogFile journal replay, log_replay() copies LCNs from each action log record into an existing Dirty Page Table (DPT) entry without bounding the destination index. A crafted NTFS image with DPT entry lcns_follow=1 and an action log record with lcns_follow=2 produces a kernel slab out-of-bounds write at mount time: BUG: KASAN: slab-out-of-bounds in log_replay+0x654c/0xdb60 Write of size 8 at addr ffff8880095e1040 by task mount Two attacker-controlled fields can drive j+i past the allocated page_lcns[] array: 1. dp->lcns_follow (capacity) can be smaller than lrh->lcns_follow. 2. lrh->target_vcn may be smaller than dp->vcn, making the u64 subtraction wrap to a huge size_t. Validate target VCN delta and per-record LCN count against the DPT entry capacity, bail via the existing out: cleanup label with -EINVAL. This mirrors the bounds-check pattern added in commit b2bc7c44ed17 ("fs/ntfs3: Fix slab-out-of-bounds read in DeleteIndexEntryRoot") and commit 0ca0485e4b2e ("fs/ntfs3: validate rec->used in journal-replay file record check"). Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Reported-by: Yunpeng Tian Reported-by: Mingda Zhang Reported-by: Gongming Wang Reported-by: Peiyuan Xu Reported-by: Qinrun Dai Cc: stable@vger.kernel.org Signed-off-by: Yunpeng Tian Signed-off-by: Konstantin Komarov Signed-off-by: Greg Kroah-Hartman --- fs/ntfs3/fslog.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index acfa18b84401e..ca78cfe2b37f8 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -4547,11 +4547,21 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) * whole routine a loop, case Lcns do not fit below. */ t16 = le16_to_cpu(lrh->lcns_follow); - for (i = 0; i < t16; i++) { - size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) - - le64_to_cpu(dp->vcn)); - dp->page_lcns[j + i] = lrh->page_lcns[i]; - } + t32 = le32_to_cpu(dp->lcns_follow); + if (le64_to_cpu(lrh->target_vcn) < le64_to_cpu(dp->vcn)) { + err = -EINVAL; + goto out; + } + + for (i = 0; i < t16; i++) { + size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) - + le64_to_cpu(dp->vcn)); + if (j >= t32 || i >= t32 - j) { + err = -EINVAL; + goto out; + } + dp->page_lcns[j + i] = lrh->page_lcns[i]; + } goto next_log_record_analyze; From e2018628301a6d9f54e34b0cb417f1688c66df1d Mon Sep 17 00:00:00 2001 From: Valeriy Yashnikov Date: Sat, 4 Jul 2026 19:38:57 +1000 Subject: [PATCH 0328/2758] ntfs: avoid calling post_write_mst_fixup() for invalid index_block commit 5b6eedd7cc2936f9238e852b553a1b326105bde8 upstream. ntfs_icx_ib_sync_write() calls post_write_mst_fixup() when ntfs_ib_write() returns an error, intending to restore the buffer after a failed write. However, ntfs_ib_write() returns an error immediately if pre_write_mst_fixup() validation fails. The caller, ntfs_icx_ib_sync_write(), interprets any error as a write failure requiring rollback. It does not differentiate between I/O errors and validation failures, and calls post_write_mst_fixup() anyway. Since post_write_mst_fixup() assumes that the index_block contents is correct, it doesn't perform the boundary checks, which results in out-of-bounds memory access. An attacker can craft a malicious NTFS image with: - large index_block.usa_ofs offset, pointing outside the ntfs_record - index_block.usa_count = 0, causing integer underflow - or index_block.usa_count larger than actual number of sectors in the ntfs_record, causing out-of-bounds access KASAN reports describing the memory corruption: ================================================================== BUG: KASAN: slab-out-of-bounds in post_write_mst_fixup+0x19c/0x1d0 Read of size 2 at addr ffff8881586c9018 by task p/9428 Call Trace: dump_stack_lvl+0x100/0x190 print_report+0x139/0x4ad ? post_write_mst_fixup+0x19c/0x1d0 ? __virt_addr_valid+0x262/0x500 ? post_write_mst_fixup+0x19c/0x1d0 kasan_report+0xe4/0x1d0 ? post_write_mst_fixup+0x19c/0x1d0 post_write_mst_fixup+0x19c/0x1d0 ntfs_icx_ib_sync_write+0x179/0x220 ntfs_inode_sync_filename+0x83d/0x1080 __ntfs_write_inode+0x1049/0x1480 ntfs_file_fsync+0x131/0x9b0 ================================================================== BUG: KASAN: slab-out-of-bounds in post_write_mst_fixup+0x1aa/0x1d0 Write of size 2 at addr ffff8881586c91fe by task p/9428 Call Trace: dump_stack_lvl+0x100/0x190 print_report+0x139/0x4ad ? post_write_mst_fixup+0x1aa/0x1d0 ? __virt_addr_valid+0x262/0x500 ? post_write_mst_fixup+0x1aa/0x1d0 kasan_report+0xe4/0x1d0 ? post_write_mst_fixup+0x1aa/0x1d0 post_write_mst_fixup+0x1aa/0x1d0 ntfs_icx_ib_sync_write+0x179/0x220 ntfs_inode_sync_filename+0x83d/0x1080 __ntfs_write_inode+0x1049/0x1480 ntfs_file_fsync+0x131/0x9b0 ================================================================== Let's move the post_write_mst_fixup() call to ntfs_ib_write(). The ntfs_ib_write() function calls pre_write_mst_fixup() at the beginning. If the index_block contents is invalid, pre_write_mst_fixup() fails and ntfs_ib_write() returns early without calling post_write_mst_fixup() on bad index_block. Fixes: 0a8ac0c1fa0b ("ntfs: update directory operations") Cc: stable@vger.kernel.org Signed-off-by: Valeriy Yashnikov Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/index.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/ntfs/index.c b/fs/ntfs/index.c index 146e011c1a418..4200a8138efad 100644 --- a/fs/ntfs/index.c +++ b/fs/ntfs/index.c @@ -141,6 +141,10 @@ static int ntfs_ib_write(struct ntfs_index_context *icx, struct index_block *ib) ret = ntfs_inode_attr_pwrite(VFS_I(icx->ia_ni), ntfs_ib_vcn_to_pos(icx, vcn), icx->block_size, (u8 *)ib, icx->sync_write); + + /* Perform data restoration before returning */ + post_write_mst_fixup((struct ntfs_record *)ib); + if (ret != icx->block_size) { ntfs_debug("Failed to write index block %lld, inode %llu", vcn, (unsigned long long)icx->idx_ni->mft_no); @@ -178,7 +182,6 @@ int ntfs_icx_ib_sync_write(struct ntfs_index_context *icx) icx->ib = NULL; icx->ib_dirty = false; } else { - post_write_mst_fixup((struct ntfs_record *)icx->ib); icx->sync_write = false; } From 6350df503897d57c5634f71b0767d48c3b837583 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 4 Mar 2026 17:30:28 +0900 Subject: [PATCH 0329/2758] NTB: epf: Avoid calling pci_irq_vector() from hardirq context commit 4dcddc1c794d1c65eda68f1f8dd04a0fecc0870f upstream. ntb_epf_vec_isr() calls pci_irq_vector() in hardirq context to derive the vector number. pci_irq_vector() calls msi_get_virq() that takes a mutex and can therefore trigger "scheduling while atomic" splats: BUG: scheduling while atomic: kworker/u33:0/55/0x00010001 ... Call trace: ... schedule+0x38/0x110 schedule_preempt_disabled+0x28/0x50 __mutex_lock.constprop.0+0x848/0x908 __mutex_lock_slowpath+0x18/0x30 mutex_lock+0x4c/0x60 msi_domain_get_virq+0xe8/0x138 pci_irq_vector+0x2c/0x60 ntb_epf_vec_isr+0x28/0x120 [ntb_hw_epf] __handle_irq_event_percpu+0x70/0x3a8 handle_irq_event+0x48/0x100 handle_edge_irq+0x100/0x1c8 ... Cache the Linux IRQ number for vector 0 when vectors are allocated and use it as a base in the ISR. Running the ISR in a threaded IRQ handler would also avoid the problem, but that would be unnecessary here. Fixes: 812ce2f8d14e ("NTB: Add support for EPF PCI Non-Transparent Bridge") Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Dave Jiang Cc: stable@vger.kernel.org # v5.12+ Link: https://patch.msgid.link/20260304083028.1391068-3-den@valinux.co.jp Signed-off-by: Greg Kroah-Hartman --- drivers/ntb/hw/epf/ntb_hw_epf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c index 9935da48a52ea..8aaf8f9bc69c9 100644 --- a/drivers/ntb/hw/epf/ntb_hw_epf.c +++ b/drivers/ntb/hw/epf/ntb_hw_epf.c @@ -92,6 +92,7 @@ struct ntb_epf_dev { int db_val; u64 db_valid_mask; + int irq_base; }; #define ntb_ndev(__ntb) container_of(__ntb, struct ntb_epf_dev, ntb) @@ -318,7 +319,7 @@ static irqreturn_t ntb_epf_vec_isr(int irq, void *dev) struct ntb_epf_dev *ndev = dev; int irq_no; - irq_no = irq - pci_irq_vector(ndev->ntb.pdev, 0); + irq_no = irq - ndev->irq_base; ndev->db_val = irq_no + 1; if (irq_no == 0) @@ -350,6 +351,7 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max) argument &= ~MSIX_ENABLE; } + ndev->irq_base = pci_irq_vector(pdev, 0); for (i = 0; i < irq; i++) { ret = request_irq(pci_irq_vector(pdev, i), ntb_epf_vec_isr, 0, "ntb_epf", ndev); From 5c3c9ec1172a4c3384b8b800b3a8896cc2c1b20e Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Wed, 17 Jun 2026 23:40:35 +0800 Subject: [PATCH 0330/2758] gpio: eic-sprd: use raw_spinlock_t in the irq startup path commit 90f0109019e6817eb40a486671b7722d1544ae29 upstream. sprd_eic_irq_unmask() enables the GPIO IRQ and then updates controller state through sprd_eic_update(), which takes sprd_eic->lock with spin_lock_irqsave(). The callback can be reached from irq_startup() while setting up a requested IRQ. That path is not sleepable, but on PREEMPT_RT a regular spinlock_t becomes a sleeping lock. This issue was found by our static analysis tool and then manually reviewed against the current tree. The grounded PoC kept the request_threaded_irq() -> __setup_irq() -> irq_startup() -> sprd_eic_irq_unmask() -> sprd_eic_update() carrier and used the original spin_lock_irqsave(&sprd_eic->lock) edge. Lockdep reported: BUG: sleeping function called from invalid context hardirqs last disabled at ... __setup_irq.constprop.0 ... [vuln_msv] sprd_rt_spin_lock_irqsave+0x1c/0x30 [vuln_msv] sprd_eic_update.constprop.0+0x48/0x90 [vuln_msv] sprd_eic_irq_unmask.constprop.0+0x35/0x50 [vuln_msv] __setup_irq.constprop.0+0xd/0x30 [vuln_msv] Convert the Spreadtrum EIC controller lock to raw_spinlock_t. The locked section only serializes MMIO register updates and does not contain sleepable operations, so keeping it non-sleeping is appropriate for the irqchip callbacks. Fixes: 25518e024e3a ("gpio: Add Spreadtrum EIC driver support") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Reviewed-by: Sebastian Andrzej Siewior Link: https://patch.msgid.link/20260617154035.1199948-3-runyu.xiao@seu.edu.cn Signed-off-by: Bartosz Golaszewski Signed-off-by: Greg Kroah-Hartman --- drivers/gpio/gpio-eic-sprd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-eic-sprd.c b/drivers/gpio/gpio-eic-sprd.c index 50fafeda8d7e2..3b7ebcf12fe7f 100644 --- a/drivers/gpio/gpio-eic-sprd.c +++ b/drivers/gpio/gpio-eic-sprd.c @@ -95,7 +95,7 @@ struct sprd_eic { struct notifier_block irq_nb; void __iomem *base[SPRD_EIC_MAX_BANK]; enum sprd_eic_type type; - spinlock_t lock; + raw_spinlock_t lock; int irq; }; @@ -149,7 +149,7 @@ static void sprd_eic_update(struct gpio_chip *chip, unsigned int offset, unsigned long flags; u32 tmp; - spin_lock_irqsave(&sprd_eic->lock, flags); + raw_spin_lock_irqsave(&sprd_eic->lock, flags); tmp = readl_relaxed(base + reg); if (val) @@ -158,7 +158,7 @@ static void sprd_eic_update(struct gpio_chip *chip, unsigned int offset, tmp &= ~BIT(SPRD_EIC_BIT(offset)); writel_relaxed(tmp, base + reg); - spin_unlock_irqrestore(&sprd_eic->lock, flags); + raw_spin_unlock_irqrestore(&sprd_eic->lock, flags); } static int sprd_eic_read(struct gpio_chip *chip, unsigned int offset, u16 reg) @@ -628,7 +628,7 @@ static int sprd_eic_probe(struct platform_device *pdev) if (!sprd_eic) return -ENOMEM; - spin_lock_init(&sprd_eic->lock); + raw_spin_lock_init(&sprd_eic->lock); sprd_eic->type = pdata->type; sprd_eic->irq = platform_get_irq(pdev, 0); From 41cad91a09d69e8fff4e936db29b1054b4e9f9f7 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Wed, 17 Jun 2026 23:40:34 +0800 Subject: [PATCH 0331/2758] gpio: sch: use raw_spinlock_t in the irq startup path commit 286533cb14a3c8a8bd39ff64ea2fc8e1aa0f638b upstream. sch_irq_unmask() enables the GPIO IRQ and then updates the controller state through sch_irq_mask_unmask(), which takes sch->lock with spin_lock_irqsave(). The callback can be reached from irq_startup() while setting up a requested IRQ. That path is not sleepable, but on PREEMPT_RT a regular spinlock_t becomes a sleeping lock. This issue was found by our static analysis tool and then manually reviewed against the current tree. The grounded PoC kept the request_threaded_irq() -> __setup_irq() -> irq_startup() -> sch_irq_unmask() -> sch_irq_mask_unmask() carrier and used the original spin_lock_irqsave(&sch->lock) edge. Lockdep reported: BUG: sleeping function called from invalid context hardirqs last disabled at ... __setup_irq.constprop.0 ... [vuln_msv] sch_rt_spin_lock_irqsave+0x1c/0x30 [vuln_msv] sch_irq_mask_unmask.constprop.0+0x31/0x70 [vuln_msv] __setup_irq.constprop.0+0xd/0x30 [vuln_msv] Convert the SCH controller lock to raw_spinlock_t. The same lock is also used by the GPIO direction and value callbacks, but those critical sections only update MMIO-backed GPIO registers and do not contain sleepable operations. Keeping this register lock non-sleeping is therefore appropriate for the irqchip callbacks and does not change the GPIO-side locking contract. Fixes: 7a81638485c1 ("gpio: sch: Add edge event support") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Reviewed-by: Sebastian Andrzej Siewior Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260617154035.1199948-2-runyu.xiao@seu.edu.cn Signed-off-by: Bartosz Golaszewski Signed-off-by: Greg Kroah-Hartman --- drivers/gpio/gpio-sch.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/gpio/gpio-sch.c b/drivers/gpio/gpio-sch.c index 966d16a6d5157..5e361742a11ad 100644 --- a/drivers/gpio/gpio-sch.c +++ b/drivers/gpio/gpio-sch.c @@ -39,7 +39,7 @@ struct sch_gpio { struct gpio_chip chip; void __iomem *regs; - spinlock_t lock; + raw_spinlock_t lock; unsigned short resume_base; /* GPE handling */ @@ -104,9 +104,9 @@ static int sch_gpio_direction_in(struct gpio_chip *gc, unsigned int gpio_num) struct sch_gpio *sch = gpiochip_get_data(gc); unsigned long flags; - spin_lock_irqsave(&sch->lock, flags); + raw_spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GIO, 1); - spin_unlock_irqrestore(&sch->lock, flags); + raw_spin_unlock_irqrestore(&sch->lock, flags); return 0; } @@ -122,9 +122,9 @@ static int sch_gpio_set(struct gpio_chip *gc, unsigned int gpio_num, int val) struct sch_gpio *sch = gpiochip_get_data(gc); unsigned long flags; - spin_lock_irqsave(&sch->lock, flags); + raw_spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GLV, val); - spin_unlock_irqrestore(&sch->lock, flags); + raw_spin_unlock_irqrestore(&sch->lock, flags); return 0; } @@ -135,9 +135,9 @@ static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned int gpio_num, struct sch_gpio *sch = gpiochip_get_data(gc); unsigned long flags; - spin_lock_irqsave(&sch->lock, flags); + raw_spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GIO, 0); - spin_unlock_irqrestore(&sch->lock, flags); + raw_spin_unlock_irqrestore(&sch->lock, flags); /* * according to the datasheet, writing to the level register has no @@ -196,14 +196,14 @@ static int sch_irq_type(struct irq_data *d, unsigned int type) return -EINVAL; } - spin_lock_irqsave(&sch->lock, flags); + raw_spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GTPE, rising); sch_gpio_reg_set(sch, gpio_num, GTNE, falling); irq_set_handler_locked(d, handle_edge_irq); - spin_unlock_irqrestore(&sch->lock, flags); + raw_spin_unlock_irqrestore(&sch->lock, flags); return 0; } @@ -215,9 +215,9 @@ static void sch_irq_ack(struct irq_data *d) irq_hw_number_t gpio_num = irqd_to_hwirq(d); unsigned long flags; - spin_lock_irqsave(&sch->lock, flags); + raw_spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GTS, 1); - spin_unlock_irqrestore(&sch->lock, flags); + raw_spin_unlock_irqrestore(&sch->lock, flags); } static void sch_irq_mask_unmask(struct gpio_chip *gc, irq_hw_number_t gpio_num, int val) @@ -225,9 +225,9 @@ static void sch_irq_mask_unmask(struct gpio_chip *gc, irq_hw_number_t gpio_num, struct sch_gpio *sch = gpiochip_get_data(gc); unsigned long flags; - spin_lock_irqsave(&sch->lock, flags); + raw_spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GGPE, val); - spin_unlock_irqrestore(&sch->lock, flags); + raw_spin_unlock_irqrestore(&sch->lock, flags); } static void sch_irq_mask(struct irq_data *d) @@ -268,12 +268,12 @@ static u32 sch_gpio_gpe_handler(acpi_handle gpe_device, u32 gpe, void *context) int offset; u32 ret; - spin_lock_irqsave(&sch->lock, flags); + raw_spin_lock_irqsave(&sch->lock, flags); core_status = ioread32(sch->regs + CORE_BANK_OFFSET + GTS); resume_status = ioread32(sch->regs + RESUME_BANK_OFFSET + GTS); - spin_unlock_irqrestore(&sch->lock, flags); + raw_spin_unlock_irqrestore(&sch->lock, flags); pending = (resume_status << sch->resume_base) | core_status; for_each_set_bit(offset, &pending, sch->chip.ngpio) @@ -343,7 +343,7 @@ static int sch_gpio_probe(struct platform_device *pdev) sch->regs = regs; - spin_lock_init(&sch->lock); + raw_spin_lock_init(&sch->lock); sch->chip = sch_gpio_chip; sch->chip.label = dev_name(dev); sch->chip.parent = dev; From 7a89ad762fad53d56b7002d7ffc923a4b7f4006f Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Thu, 18 Jun 2026 15:37:37 +0900 Subject: [PATCH 0332/2758] HID: logitech-dj: Fix maxfield check in DJ short report validation commit 590cc4d782487632a52f37c2171bee1eeea29627 upstream. Commit b6a57912854e ("HID: logitech-dj: Prevent REPORT_ID_DJ_SHORT related user initiated OOB write") added validation for the DJ short output report, but the error path dereferences rep->field[0] even when rep->maxfield is zero. Commit 8b9a097eb2fc ("HID: logitech-dj: fix wrong detection of bad DJ_SHORT output report") made the check conditional on rep being present, but a crafted descriptor can still create report ID 0x20 with only padding output items. hid-core registers the report, ignores the padding field, and leaves rep->maxfield as zero. In that case the validation enters the rep->maxfield < 1 branch and then dereferences rep->field[0]->report_count while printing the error message, causing a NULL pointer dereference during probe. This is reproducible with uhid by emulating a Logitech receiver with a padding-only DJ short output report: BUG: KASAN: null-ptr-deref in logi_dj_probe+0xb1/0x754 [hid_logitech_dj] Read of size 4 at addr 0000000000000028 by task kworker/4:1/129 ... Call Trace: logi_dj_probe+0xb1/0x754 [hid_logitech_dj] hid_device_probe+0x329/0x3f0 [hid] really_probe+0x162/0x570 __device_attach+0x137/0x2c0 bus_probe_device+0x38/0xc0 device_add+0xa56/0xce0 hid_add_device+0x19c/0x280 [hid] uhid_device_add_worker+0x2c/0xb0 [uhid] Reject the zero-field report before printing the field report_count. Fixes: b6a57912854e ("HID: logitech-dj: Prevent REPORT_ID_DJ_SHORT related user initiated OOB write") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: HyeongJun An Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-logitech-dj.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 381e4dc5aba7a..9c574ab8b60b3 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -1907,8 +1907,13 @@ static int logi_dj_probe(struct hid_device *hdev, output_report_enum = &hdev->report_enum[HID_OUTPUT_REPORT]; rep = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT]; - if (rep && (rep->maxfield < 1 || - rep->field[0]->report_count != DJREPORT_SHORT_LENGTH - 1)) { + if (rep && rep->maxfield < 1) { + hid_err(hdev, "Expected size of DJ short report is %d, but got 0", + DJREPORT_SHORT_LENGTH - 1); + return -EINVAL; + } + + if (rep && rep->field[0]->report_count != DJREPORT_SHORT_LENGTH - 1) { hid_err(hdev, "Expected size of DJ short report is %d, but got %d", DJREPORT_SHORT_LENGTH - 1, rep->field[0]->report_count); return -EINVAL; From 7267717f35787167fcce4bc14f6ef3fa06682dcf Mon Sep 17 00:00:00 2001 From: Vasileios Almpanis Date: Mon, 15 Jun 2026 16:45:57 +0200 Subject: [PATCH 0333/2758] io_uring/nop: fix file reference leak with IOSQE_FIXED_FILE commit 2564ca2e31bd8ee8348362941af2ee4671e487ca upstream. NOP file-acquisition support choses between a fixed (registered) file and a normal fget()'d file based on its own IORING_NOP_FIXED_FILE flag in sqe->nop_flags. However, a request's REQ_F_FIXED_FILE is set independently from the generic IOSQE_FIXED_FILE sqe flag during request init, before the issue handler runs. If a NOP is submitted with IOSQE_FIXED_FILE set (so REQ_F_FIXED_FILE is set) but without IORING_NOP_FIXED_FILE, io_nop() takes the normal path and grabs a real reference via io_file_get_normal(). On completion, io_put_file() only drops the reference when REQ_F_FIXED_FILE is clear, so the fget()'d file is never released and leaks: BUG: memory leak unreferenced object 0xffff88800f42c240 (size 176): kmem_cache_alloc_noprof+0x358/0x440 alloc_empty_file+0x57/0x180 path_openat+0x44/0x1e50 do_file_open+0x121/0x200 do_sys_openat2+0xa7/0x150 __x64_sys_openat+0x82/0xf0 Decide between fixed and normal file acquisition from REQ_F_FIXED_FILE, the same way io_assign_file() does for every other opcode, and fold IORING_NOP_FIXED_FILE into REQ_F_FIXED_FILE at prep time. Cc: stable@vger.kernel.org Fixes: a85f31052bce ("io_uring/nop: add support for testing registered files and buffers") Reported-by: syzbot+2cd473471e77bda12b0e@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?id=879092631b98f73a28ea405adacfa5bb34a14a25 Signed-off-by: Vasileios Almpanis Link: https://patch.msgid.link/20260615144619.482749-1-vasilisalmpanis@gmail.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- io_uring/nop.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/io_uring/nop.c b/io_uring/nop.c index f5c9969e7f64a..2f9aa48832619 100644 --- a/io_uring/nop.c +++ b/io_uring/nop.c @@ -41,6 +41,8 @@ int io_nop_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) nop->fd = READ_ONCE(sqe->fd); else nop->fd = -1; + if (nop->flags & IORING_NOP_FIXED_FILE) + req->flags |= REQ_F_FIXED_FILE; if (nop->flags & IORING_NOP_FIXED_BUFFER) req->buf_index = READ_ONCE(sqe->buf_index); if (nop->flags & IORING_NOP_CQE32) { @@ -60,12 +62,10 @@ int io_nop(struct io_kiocb *req, unsigned int issue_flags) int ret = nop->result; if (nop->flags & IORING_NOP_FILE) { - if (nop->flags & IORING_NOP_FIXED_FILE) { + if (req->flags & REQ_F_FIXED_FILE) req->file = io_file_get_fixed(req, nop->fd, issue_flags); - req->flags |= REQ_F_FIXED_FILE; - } else { + else req->file = io_file_get_normal(req, nop->fd); - } if (!req->file) { ret = -EBADF; goto done; From ab85765cbe3258b43dc6729af0e6ce3a87a133d8 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Thu, 28 May 2026 01:22:03 +0800 Subject: [PATCH 0334/2758] io_uring/io-wq: re-check IO_WQ_BIT_EXIT for each linked work item commit 29bef9934b2521f787bb15dd1985d4c0d12ae02a upstream. commit 10dc95939817 ("io_uring/io-wq: check IO_WQ_BIT_EXIT inside work run loop") fixed the obvious case where io_worker_handle_work() took one exit-bit snapshot before draining pending work, but the fix stops one level too early. io_worker_handle_work() now re-checks IO_WQ_BIT_EXIT in its outer work run loop, yet it still snapshots that bit once before processing a whole dependent linked-work chain. If io_wq_exit_start() sets IO_WQ_BIT_EXIT after the first linked item has started, the remaining linked items can still reuse stale do_kill = false, skip IO_WQ_WORK_CANCEL, and continue running after exit has begun. Move the check further inside, so it covers linked items too. Note: this is a syzbot special as it loves setting up tons of slow linked work on weird devices like msr that take forever to read, and immediately close the ring. Exit then takes a long time. Fixes: 10dc95939817 ("io_uring/io-wq: check IO_WQ_BIT_EXIT inside work run loop") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Link: https://patch.msgid.link/20260527172203.2043962-1-runyu.xiao@seu.edu.cn Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- io_uring/io-wq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c index 8cc7b47d30894..2e14880eef92a 100644 --- a/io_uring/io-wq.c +++ b/io_uring/io-wq.c @@ -602,7 +602,6 @@ static void io_worker_handle_work(struct io_wq_acct *acct, struct io_wq *wq = worker->wq; do { - bool do_kill = test_bit(IO_WQ_BIT_EXIT, &wq->state); struct io_wq_work *work; /* @@ -638,6 +637,7 @@ static void io_worker_handle_work(struct io_wq_acct *acct, /* handle a whole dependent link */ do { + bool do_kill = test_bit(IO_WQ_BIT_EXIT, &wq->state); struct io_wq_work *next_hashed, *linked; unsigned int work_flags = atomic_read(&work->flags); unsigned int hash = __io_wq_is_hashed(work_flags) From f254713ac5391e58e930741a969aa0cbbe432e8b Mon Sep 17 00:00:00 2001 From: Michael Wigham Date: Sat, 13 Jun 2026 23:52:16 +0100 Subject: [PATCH 0335/2758] io_uring/rw: preserve partial result for iopoll commit c554246ff4c68abf71b61a89c6e39d3cf94f523e upstream. A partial read will store the completed byte count in io->bytes_done. The regular completion path applies io_fixup_rw_res() so that, when the following operation reaches EOF, the number of bytes already read is returned. The iopoll completion path does not apply this fixup to the return value and can return zero instead. Use the fixup result when updating the CQE, and the raw result for the reissue check. Cc: stable@vger.kernel.org Fixes: 4d9cb92ca41d ("io_uring/rw: fix short rw error handling") Signed-off-by: Michael Wigham Link: https://patch.msgid.link/20260613225240.34032-1-michael@wigham.net Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- io_uring/rw.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/io_uring/rw.c b/io_uring/rw.c index 0c48346452797..63b6519e498cd 100644 --- a/io_uring/rw.c +++ b/io_uring/rw.c @@ -601,15 +601,15 @@ static void io_complete_rw_iopoll(struct kiocb *kiocb, long res) { struct io_rw *rw = container_of(kiocb, struct io_rw, kiocb); struct io_kiocb *req = cmd_to_io_kiocb(rw); + int final_res = io_fixup_rw_res(req, res); if (kiocb->ki_flags & IOCB_WRITE) io_req_end_write(req); - if (unlikely(res != req->cqe.res)) { - if (res == -EAGAIN && io_rw_should_reissue(req)) - req->flags |= REQ_F_REISSUE | REQ_F_BL_NO_RECYCLE; - else - req->cqe.res = res; - } + + if (res == -EAGAIN && io_rw_should_reissue(req)) + req->flags |= REQ_F_REISSUE | REQ_F_BL_NO_RECYCLE; + else if (unlikely(final_res != req->cqe.res)) + req->cqe.res = final_res; /* order with io_iopoll_complete() checking ->iopoll_completed */ smp_store_release(&req->iopoll_completed, 1); From 5ed09a108d93a3b002cc79823d9455b50c4a8be7 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Thu, 25 Jun 2026 05:03:18 -0700 Subject: [PATCH 0336/2758] netpoll: fix a use-after-free on shutdown path commit 45f1458a85017a023f138b22ac5c76abd477db42 upstream. There is a use-after-free error on netpoll, which is clearly detected by KASAN. BUG: KASAN: slab-use-after-free in _raw_spin_lock_irqsave+0x3b/0x80 Read of size 1 at addr ... by task kworker/9:1 Workqueue: events queue_process Call Trace: skb_dequeue+0x1e/0xb0 queue_process+0x2c/0x600 process_scheduled_works+0x4b6/0x850 worker_thread+0x414/0x5a0 Allocated by task 242: __netpoll_setup+0x201/0x4a0 netpoll_setup+0x249/0x550 enabled_store+0x32f/0x380 Freed by task 0: kfree+0x1b7/0x540 rcu_core+0x3f8/0x7a0 The problem happens when there is a pending TX worker running in parallel with the cleanup path. This is what happens on netpoll shutdown path: 1) __netpoll_cleanup() is called 2) set dev->npinfo to NULL 3) call_rcu() with rcu_cleanup_netpoll_info() 3.1) rcu_cleanup_netpoll_info() tries to cancel all workers with cancel_delayed_work(), but doesn't wait for the worker to finish 4) and kfree(npinfo); Because 3.1) doesn't really cancel the work, as the comment says "we can't call cancel_delayed_work_sync here, as we are in softirq", the TX worker can run after 4). Tl;DR: queue_process() is not an RCU reader, it reaches npinfo through the work item via container_of(). Use disable_delayed_work_sync() to ensure the worker is completely stopped and prevent any future re-arming attempts. Once npinfo is set to NULL, senders will bail out and not queue new work. The disable flag ensures any in-flight re-arming attempts also fail silently. In the future, we can do the cleanup inline here without needing the npinfo->rcu rcu_head, but that is net-next material. Cc: stable@vger.kernel.org Fixes: 38e6bc185d95 ("netpoll: make __netpoll_cleanup non-block") Reviewed-by: Pavan Chebbi Signed-off-by: Breno Leitao Link: https://patch.msgid.link/20260625-netpoll_rcu_fix-v2-1-0748ffac1e98@debian.org Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/core/netpoll.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 3f4a17fa5713a..9004e3145c11b 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -814,14 +814,6 @@ static void rcu_cleanup_netpoll_info(struct rcu_head *rcu_head) container_of(rcu_head, struct netpoll_info, rcu); skb_queue_purge(&npinfo->txq); - - /* we can't call cancel_delayed_work_sync here, as we are in softirq */ - cancel_delayed_work(&npinfo->tx_work); - - /* clean after last, unfinished work */ - __skb_queue_purge(&npinfo->txq); - /* now cancel it again */ - cancel_delayed_work(&npinfo->tx_work); kfree(npinfo); } @@ -845,6 +837,7 @@ static void __netpoll_cleanup(struct netpoll *np) ops->ndo_netpoll_cleanup(np->dev); RCU_INIT_POINTER(np->dev->npinfo, NULL); + disable_delayed_work_sync(&npinfo->tx_work); call_rcu(&npinfo->rcu, rcu_cleanup_netpoll_info); } From f91883031e5a62877a29ce139442973cbea769f1 Mon Sep 17 00:00:00 2001 From: Yuyang Huang Date: Thu, 2 Jul 2026 08:50:14 +0900 Subject: [PATCH 0337/2758] ipv4: igmp: remove multicast group from hash table on device destruction commit 7993211bde166471dffac074dc965489f86531f8 upstream. When a device is destroyed under RTNL, ip_mc_destroy_dev() iterates through the multicast list and calls ip_ma_put() on each membership, scheduling them for RCU reclamation. However, they are not unlinked from the device's multicast hash table (mc_hash). Since the device remains published in dev->ip_ptr until after ip_mc_destroy_dev() completes, concurrent RCU readers traversing mc_hash can still locate and access the multicast group after its refcount is decremented. If the RCU callback runs and frees the group while a reader is accessing it, a use-after-free occurs. Fix this by unlinking the multicast group from mc_hash using ip_mc_hash_remove() before scheduling it for reclamation. BUG: KASAN: slab-use-after-free in ip_check_mc_rcu+0x149/0x3f0 Read of size 4 at addr ffff888009bf1408 by task mausezahn/2276 Call Trace: dump_stack_lvl+0x67/0x90 print_report+0x175/0x7c0 kasan_report+0x147/0x180 ip_check_mc_rcu+0x149/0x3f0 udp_v4_early_demux+0x36d/0x12d0 ip_rcv_finish_core+0xb8b/0x1390 ip_rcv_finish+0x54/0x120 NF_HOOK+0x213/0x2b0 __netif_receive_skb+0x126/0x340 process_backlog+0x4f2/0xf00 __napi_poll+0x92/0x2c0 net_rx_action+0x583/0xc60 handle_softirqs+0x236/0x7f0 do_softirq+0x57/0x80 Allocated by task 2239: kasan_save_track+0x3e/0x80 __kasan_kmalloc+0x72/0x90 ____ip_mc_inc_group+0x31a/0xa40 __ip_mc_join_group+0x334/0x3f0 do_ip_setsockopt+0x16fa/0x2010 ip_setsockopt+0x3f/0x90 do_sock_setsockopt+0x1ad/0x300 Freed by task 0: kasan_save_track+0x3e/0x80 kasan_save_free_info+0x40/0x50 __kasan_slab_free+0x3a/0x60 __rcu_free_sheaf_prepare+0xd4/0x220 rcu_free_sheaf+0x36/0x190 rcu_core+0x8d9/0x12f0 handle_softirqs+0x236/0x7f0 Fixes: e9897071350b ("igmp: hash a hash table to speedup ip_check_mc_rcu()") Cc: stable@vger.kernel.org Signed-off-by: Yuyang Huang Reviewed-by: Kuniyuki Iwashima Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260701235014.73505-1-yuyanghuang@google.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- net/ipv4/igmp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index a9ad39064f3bb..523aecb3c9be7 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -1918,6 +1918,7 @@ void ip_mc_destroy_dev(struct in_device *in_dev) #endif while ((i = rtnl_dereference(in_dev->mc_list)) != NULL) { + ip_mc_hash_remove(in_dev, i); in_dev->mc_list = i->next_rcu; in_dev->mc_count--; ip_mc_clear_src(i); From a094ac95d3b69adfa1676eb9c8eae6835d4f1671 Mon Sep 17 00:00:00 2001 From: Wyatt Feng Date: Mon, 15 Jun 2026 18:31:18 +0800 Subject: [PATCH 0338/2758] net: ipv4: bound TCP reordering sysctl writes and MTU probe sizes commit efb8763d7bbb40cff4cc55a6b62c3095a038149c upstream. Reject invalid `net.ipv4.tcp_reordering` values before they reach TCP socket state. The sysctl is stored as an `int` but copied into the `u32` `tp->reordering` field for new sockets, so negative writes wrap to large values. With `tcp_mtu_probing=2`, the wrapped value can overflow the `tcp_mtu_probe()` size calculation and drive the MTU probing path into an out-of-bounds read. Route `tcp_reordering` writes through `proc_dointvec_minmax()` and require it to be at least 1. Also require `tcp_max_reordering` to be at least 1 so the configured maximum cannot become negative either. When registering the table for a non-init network namespace, relocate `extra2` pointers that refer into `init_net.ipv4` so the `tcp_reordering` upper bound follows that namespace's `tcp_max_reordering`. Harden `tcp_mtu_probe()` itself by computing `size_needed` as `u64`. This keeps the send queue and window checks from being bypassed through signed integer overflow. Fixes: 91cc17c0e5e5 ("[TCP]: MTUprobe: receiver window & data available checks fixed") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Zhengchuan Liang Reported-by: Xin Liu Suggested-by: Eric Dumazet Signed-off-by: Wyatt Feng Signed-off-by: Ren Wei Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/1a5b7e1ef4d70fbad8c8ee0b82d8405f3c964a3d.1781395200.git.bronzed_45_vested@icloud.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv4/sysctl_net_ipv4.c | 10 ++++++++-- net/ipv4/tcp_output.c | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index c0e85cc171aec..ca1180dba1dea 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -1058,7 +1058,9 @@ static struct ctl_table ipv4_net_table[] = { .data = &init_net.ipv4.sysctl_tcp_reordering, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec + .proc_handler = proc_dointvec_minmax, + .extra1 = SYSCTL_ONE, + .extra2 = &init_net.ipv4.sysctl_tcp_max_reordering, }, { .procname = "tcp_retries1", @@ -1293,7 +1295,8 @@ static struct ctl_table ipv4_net_table[] = { .data = &init_net.ipv4.sysctl_tcp_max_reordering, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec + .proc_handler = proc_dointvec_minmax, + .extra1 = SYSCTL_ONE, }, { .procname = "tcp_dsack", @@ -1676,6 +1679,9 @@ static __net_init int ipv4_sysctl_init_net(struct net *net) */ table[i].mode &= ~0222; } + if (table[i].extra2 >= (void *)&init_net.ipv4 && + table[i].extra2 < (void *)(&init_net.ipv4 + 1)) + table[i].extra2 += (void *)net - (void *)&init_net; } } diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 6e4bb411dc041..193637a58dcc1 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2687,7 +2687,7 @@ static int tcp_mtu_probe(struct sock *sk) struct sk_buff *skb, *nskb, *next; struct net *net = sock_net(sk); int probe_size; - int size_needed; + u64 size_needed; int copy, len; int mss_now; int interval; @@ -2711,7 +2711,7 @@ static int tcp_mtu_probe(struct sock *sk) mss_now = tcp_current_mss(sk); probe_size = tcp_mtu_to_mss(sk, (icsk->icsk_mtup.search_high + icsk->icsk_mtup.search_low) >> 1); - size_needed = probe_size + (tp->reordering + 1) * tp->mss_cache; + size_needed = probe_size + (tp->reordering + 1) * (u64)tp->mss_cache; interval = icsk->icsk_mtup.search_high - icsk->icsk_mtup.search_low; /* When misfortune happens, we are reprobing actively, * and then reprobe timer has expired. We stick with current From c12a5b2261351cd3b03921ce4720332ff5184b50 Mon Sep 17 00:00:00 2001 From: Xiaolei Wang Date: Thu, 7 May 2026 12:13:15 +0800 Subject: [PATCH 0339/2758] media: nxp: imx8-isi: Fix use-after-free on remove commit b670bf89824ede5d07d20bb9bfbafb754846081d upstream. KASAN reports a slab-use-after-free in __media_entity_remove_link() during rmmod of imx8_isi: BUG: KASAN: slab-use-after-free in __media_entity_remove_link+0x608/0x650 Read of size 2 at addr ffff0000d47cb02a by task rmmod/724 Call trace: __media_entity_remove_link+0x608/0x650 __media_entity_remove_links+0x78/0x144 __media_device_unregister_entity+0x150/0x280 media_device_unregister_entity+0x48/0x68 v4l2_device_unregister_subdev+0x158/0x300 v4l2_async_unbind_subdev_one+0x22c/0x358 v4l2_async_nf_unbind_all_subdevs+0xfc/0x1c0 v4l2_async_nf_unregister+0x5c/0x14c mxc_isi_remove+0x124/0x2a0 [imx8_isi] Allocated by task 249: __kmalloc_noprof+0x27c/0x690 mxc_isi_crossbar_init+0x22c/0x560 [imx8_isi] Freed by task 724: kfree+0x1e4/0x5b0 mxc_isi_crossbar_cleanup+0x34/0x80 [imx8_isi] mxc_isi_remove+0x11c/0x2a0 [imx8_isi] The problem is that mxc_isi_remove() calls mxc_isi_crossbar_cleanup() before mxc_isi_v4l2_cleanup(). The crossbar cleanup frees the media entity pads, but the subsequent v4l2 cleanup still tries to remove media links that reference those pads. Fix this by calling mxc_isi_v4l2_cleanup() before mxc_isi_crossbar_cleanup() to ensure all media entities are properly unregistered while the pads are still valid. Fixes: cf21f328fcaf ("media: nxp: Add i.MX8 ISI driver") Cc: stable@vger.kernel.org Signed-off-by: Xiaolei Wang Reviewed-by: Frank Li Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260507041318.491594-2-xiaolei.wang@windriver.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c index 4bf8570e1b9e9..2d639b7899105 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c @@ -556,8 +556,8 @@ static void mxc_isi_remove(struct platform_device *pdev) mxc_isi_pipe_cleanup(pipe); } - mxc_isi_crossbar_cleanup(&isi->crossbar); mxc_isi_v4l2_cleanup(isi); + mxc_isi_crossbar_cleanup(&isi->crossbar); } static const struct of_device_id mxc_isi_of_match[] = { From fc030c5b116f668d4ca86dca63742ddbc98d1665 Mon Sep 17 00:00:00 2001 From: Andrei Kuchynski Date: Mon, 27 Apr 2026 13:17:21 +0000 Subject: [PATCH 0340/2758] mfd: cros_ec: Delay dev_set_drvdata() until probe success commit 8b2c1d41bc36c100b38ce5ee6def246c527eaf8a upstream. If ec_device_probe() fails, cros_ec_class_release releases memory for the cros_ec_dev structure. However, because the drvdata was already set, sub-drivers like cros_ec_typec can still retrieve the stale pointer via the platform device. This leads to a use-after-free when cros_ec_typec attempts to access &typec->ec->ec->dev on a device that has already been released. Move dev_set_drvdata() to ensure that the pointer is only made available once all initialization steps have succeeded. sysfs: cannot create duplicate filename '/class/chromeos/cros_ec' Call trace: sysfs_do_create_link_sd+0x94/0xdc sysfs_create_link+0x30/0x44 device_add_class_symlinks+0x90/0x13c device_add+0xf0/0x50c ec_device_probe+0x150/0x4f0 platform_probe+0xa0/0xe0 ... BUG: KASAN: invalid-access in __memcpy+0x44/0x230 Write at addr f5ffff809e2d33ac by task kworker/u32:5/125 Pointer tag: [f5], memory tag: [fe] Tainted : [W]=WARN, [O]=OOT_MODULE Hardware name: Google Navi unprovisioned 0x7FFFFFFF/sku0 board/sku3 Workqueue: events_unbound deferred_probe_work_func Call trace: __memcpy+0x44/0x230 cros_ec_check_features+0x60/0xcc [cros_ec_proto] cros_typec_probe+0xe8/0x6e0 [cros_ec_typec] platform_probe+0xa0/0xe0 Cc: stable@vger.kernel.org Fixes: 1c1d152cc5ac ("platform/chrome: cros_ec_dev - utilize new cdev_device_add helper function") Co-developed-by: Sergey Senozhatsky Signed-off-by: Sergey Senozhatsky Signed-off-by: Andrei Kuchynski Reviewed-by: Benson Leung Link: https://patch.msgid.link/20260427131721.1165078-1-akuchynski@chromium.org Signed-off-by: Lee Jones Signed-off-by: Greg Kroah-Hartman --- drivers/mfd/cros_ec_dev.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c index 39430dd44e30c..56fb7cceafc6c 100644 --- a/drivers/mfd/cros_ec_dev.c +++ b/drivers/mfd/cros_ec_dev.c @@ -195,7 +195,6 @@ static int ec_device_probe(struct platform_device *pdev) if (!ec) return retval; - dev_set_drvdata(dev, ec); ec->ec_dev = dev_get_drvdata(dev->parent); ec->dev = dev; ec->cmd_offset = ec_platform->cmd_offset; @@ -237,6 +236,8 @@ static int ec_device_probe(struct platform_device *pdev) if (retval) goto failed; + dev_set_drvdata(dev, ec); + /* check whether this EC is a sensor hub. */ if (cros_ec_get_sensor_count(ec) > 0) { retval = mfd_add_hotplug_devices(ec->dev, From 86237e56091e70f09c0fbf217f9d9c0e08f556c4 Mon Sep 17 00:00:00 2001 From: Shakeel Butt Date: Wed, 10 Jun 2026 16:20:48 -0700 Subject: [PATCH 0341/2758] mm/shrinker: do not hold RCU lock in shrinker_debugfs_count_show() commit b902890c62d200b3509cb5e09cf1e0a66553c128 upstream. Reading the debugfs "count" file of a memcg-aware shrinker can sleep inside an RCU read-side critical section: BUG: sleeping function called from invalid context at kernel/cgroup/rstat.c:421 RCU nest depth: 1, expected: 0 css_rstat_flush mem_cgroup_flush_stats zswap_shrinker_count shrinker_debugfs_count_show shrinker_debugfs_count_show() invokes the ->count_objects() callback under rcu_read_lock(). The zswap callback flushes memcg stats via css_rstat_flush(), which may sleep, so it must not run under RCU. The RCU lock is not needed here. mem_cgroup_iter() takes RCU internally and returns a memcg holding a css reference (dropped on the next iteration or by mem_cgroup_iter_break()), so the memcg stays alive without it. The shrinker is kept alive by the open debugfs file: shrinker_free() removes the debugfs entries via debugfs_remove_recursive(), which waits for in-flight readers to drain, before call_rcu(..., shrinker_free_rcu_cb). The sibling "scan" handler already invokes the sleeping ->scan_objects() callback with no RCU section. Drop the rcu_read_lock()/rcu_read_unlock(). Link: https://lore.kernel.org/20260610232048.62930-1-shakeel.butt@linux.dev Fixes: 5035ebc644ae ("mm: shrinkers: introduce debugfs interface for memory shrinkers") Signed-off-by: Shakeel Butt Reported-by: Zenghui Yu Closes: https://lore.kernel.org/all/c052a064-cddb-494f-a0d8-f8a10b4b1c4d@linux.dev/ Suggested-by: Nhat Pham Reviewed-by: SeongJae Park Reviewed-by: Qi Zheng Tested-by: Zenghui Yu (Huawei) Reviewed-by: Nhat Pham Acked-by: Muchun Song Reviewed-by: Roman Gushchin Cc: Dave Chinner Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/shrinker_debug.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/mm/shrinker_debug.c b/mm/shrinker_debug.c index affa644373021..cda4e86428c8f 100644 --- a/mm/shrinker_debug.c +++ b/mm/shrinker_debug.c @@ -57,8 +57,6 @@ static int shrinker_debugfs_count_show(struct seq_file *m, void *v) if (!count_per_node) return -ENOMEM; - rcu_read_lock(); - memcg_aware = shrinker->flags & SHRINKER_MEMCG_AWARE; memcg = mem_cgroup_iter(NULL, NULL, NULL); @@ -88,8 +86,6 @@ static int shrinker_debugfs_count_show(struct seq_file *m, void *v) } } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL)) != NULL); - rcu_read_unlock(); - kfree(count_per_node); return ret; } From 284c267f013e45d8c89d9fb9373105dc8e6c0947 Mon Sep 17 00:00:00 2001 From: Qi Zheng Date: Wed, 17 Jun 2026 16:56:58 +0800 Subject: [PATCH 0342/2758] mm: shrinker: fix shrinker_info teardown race with expansion commit 65476d31d8056e859c48580f82295ce159196ffe upstream. expand_shrinker_info() iterates all visible memcgs under shrinker_mutex, including memcgs that have not finished ->css_online() yet. Once pn->shrinker_info has been published, teardown must stay serialized with expand_shrinker_info() until that memcg is either fully online or no longer visible to iteration. Today alloc_shrinker_info() breaks that rule by dropping shrinker_mutex before freeing a partially initialized shrinker_info array, which may cause the following race: CPU0 CPU1 ==== ==== css_create --> list_add_tail_rcu(&css->sibling, &parent_css->children); online_css --> mem_cgroup_css_online --> alloc_shrinker_info --> alloc node0 info rcu_assign_pointer(C->node0->shrinker_info, old0) alloc node1 info -> FAIL -> goto err mutex_unlock(shrinker_mutex) shrinker_alloc() --> shrinker_memcg_alloc --> mutex_lock(shrinker_mutex) expand_shrinker_info --> mem_cgroup_iter see the memcg expand_one_shrinker_info --> old0 = C->node0->shrinker_info memcpy(new->unit, old0->unit, ...); free_shrinker_info --> kvfree(old0); /* double free !! */ kvfree_rcu(old0, rcu); The same problem exists later in mem_cgroup_css_online(). If alloc_shrinker_info() succeeds but a subsequent objcg allocation fails, the free_objcg -> free_shrinker_info() unwind path tears down the already published pn->shrinker_info arrays without shrinker_mutex. The expand_one_shrinker_info() can race with that teardown in the same way, leading to use-after-free or double-free of the old shrinker_info. Fix this by serializing shrinker_info teardown with shrinker_mutex, and by keeping alloc_shrinker_info() error cleanup inside the locked section. Link: https://lore.kernel.org/20260617085658.27096-1-qi.zheng@linux.dev Fixes: 307bececcd12 ("mm: shrinker: add a secondary array for shrinker_info::{map, nr_deferred}") Signed-off-by: Qi Zheng Acked-by: Muchun Song Cc: Dave Chinner Cc: Qi Zheng Cc: Roman Gushchin Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/shrinker.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/mm/shrinker.c b/mm/shrinker.c index 76b3f750cf655..d6c1a9b1e34a4 100644 --- a/mm/shrinker.c +++ b/mm/shrinker.c @@ -59,12 +59,14 @@ static inline int shrinker_unit_alloc(struct shrinker_info *new, return 0; } -void free_shrinker_info(struct mem_cgroup *memcg) +static void __free_shrinker_info(struct mem_cgroup *memcg) { struct mem_cgroup_per_node *pn; struct shrinker_info *info; int nid; + lockdep_assert_held(&shrinker_mutex); + for_each_node(nid) { pn = memcg->nodeinfo[nid]; info = rcu_dereference_protected(pn->shrinker_info, true); @@ -74,6 +76,13 @@ void free_shrinker_info(struct mem_cgroup *memcg) } } +void free_shrinker_info(struct mem_cgroup *memcg) +{ + mutex_lock(&shrinker_mutex); + __free_shrinker_info(memcg); + mutex_unlock(&shrinker_mutex); +} + int alloc_shrinker_info(struct mem_cgroup *memcg) { int nid, ret = 0; @@ -98,8 +107,8 @@ int alloc_shrinker_info(struct mem_cgroup *memcg) return ret; err: + __free_shrinker_info(memcg); mutex_unlock(&shrinker_mutex); - free_shrinker_info(memcg); return -ENOMEM; } From b9beed2322f3538b0d2d53307062da4102b8d8d8 Mon Sep 17 00:00:00 2001 From: Qi Zheng Date: Wed, 17 Jun 2026 17:00:52 +0800 Subject: [PATCH 0343/2758] mm: shrinker: fix NULL pointer dereference in debugfs commit e30453c61e185e914fde83c650e268067b140218 upstream. shrinker_debugfs_add() creates both "count" and "scan" debugfs files unconditionally. That assumes every shrinker implements both count_objects() and scan_objects(), which is not guaranteed. For example, the xen-backend shrinker sets count_objects() but leaves scan_objects() NULL, so writing to its scan file calls through a NULL function pointer and panics the kernel: BUG: kernel NULL pointer dereference, address: 0000000000000000 RIP: 0010:0x0 Code: Unable to access opcode bytes at 0xffffffffffffffd6. Call Trace: shrinker_debugfs_scan_write+0x12e/0x270 full_proxy_write+0x5f/0x90 vfs_write+0xde/0x420 ? filp_flush+0x75/0x90 ? filp_close+0x1d/0x30 ? do_dup2+0xb8/0x120 ksys_write+0x68/0xf0 ? filp_flush+0x75/0x90 do_syscall_64+0xb3/0x5b0 entry_SYSCALL_64_after_hwframe+0x76/0x7e The count path has the same issue in principle if a shrinker omits count_objects(). To fix it, only create "count" and "scan" debugfs files when the corresponding callbacks are present. Link: https://lore.kernel.org/20260617090052.27325-1-qi.zheng@linux.dev Fixes: bbf535fd6f06 ("mm: shrinkers: add scan interface for shrinker debugfs") Signed-off-by: Qi Zheng Reviewed-by: Muchun Song Cc: Dave Chinner Cc: Qi Zheng Cc: Roman Gushchin Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/shrinker_debug.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/mm/shrinker_debug.c b/mm/shrinker_debug.c index cda4e86428c8f..cafb566301326 100644 --- a/mm/shrinker_debug.c +++ b/mm/shrinker_debug.c @@ -183,10 +183,12 @@ int shrinker_debugfs_add(struct shrinker *shrinker) } shrinker->debugfs_entry = entry; - debugfs_create_file("count", 0440, entry, shrinker, - &shrinker_debugfs_count_fops); - debugfs_create_file("scan", 0220, entry, shrinker, - &shrinker_debugfs_scan_fops); + if (shrinker->count_objects) + debugfs_create_file("count", 0440, entry, shrinker, + &shrinker_debugfs_count_fops); + if (shrinker->scan_objects) + debugfs_create_file("scan", 0220, entry, shrinker, + &shrinker_debugfs_scan_fops); return 0; } From 6a4196d19f477524d2f92adca90fc1fbe9a0420a Mon Sep 17 00:00:00 2001 From: "Jose Fernandez (Anthropic)" Date: Mon, 4 May 2026 12:55:17 +0000 Subject: [PATCH 0344/2758] mm: swap_cgroup: fix NULL deref in lookup_swap_cgroup_id on swapless host commit 63b02a9409cb5180398491b093e48bcb5315f5fb upstream. lookup_swap_cgroup_id() passes swap_cgroup_ctrl[type].map to __swap_cgroup_id_lookup() without checking that the type was ever registered via swap_cgroup_swapon(). On a swapless host every ctrl->map is NULL, so __swap_cgroup_id_lookup() dereferences NULL + a scaled swp_offset(). Since commit bea67dcc5eea ("mm: attempt to batch free swap entries for zap_pte_range()"), zap_pte_range() -> swap_pte_batch() calls lookup_swap_cgroup_id() on any non-present, non-none PTE that decodes as a real swap entry, without first validating it against swap_info[]. A single PTE corrupted into a type-0 swap entry takes the host down at process exit. We hit this in production on a swapless 6.12.58 host: ~1s of "get_swap_device: Bad swap file entry 3f800204222bb" (do_swap_page() being correctly defensive about the same entry) followed by BUG: unable to handle page fault for address: 000003f800204220 RIP: 0010:lookup_swap_cgroup_id+0x2b/0x60 Call Trace: swap_pte_batch+0xbf/0x230 zap_pte_range+0x4c8/0x780 unmap_page_range+0x190/0x3e0 exit_mmap+0xd9/0x3c0 do_exit+0x20c/0x4b0 syzbot has reported the identical stack. The source of the PTE corruption is a separate bug; this change makes the teardown path as robust as the fault path already is. Every other caller of lookup_swap_cgroup_id() is downstream of a get_swap_device() that has already validated the entry, so the new branch is cold. Link: https://lore.kernel.org/20260504-swap-cgroup-fix-7-0-v1-1-f53ff41ee553@linux.dev Fixes: bea67dcc5eea ("mm: attempt to batch free swap entries for zap_pte_range()") Signed-off-by: Jose Fernandez (Anthropic) Reported-by: syzbot+e12bd9ca48157add237a@syzkaller.appspotmail.com Link: https://lore.kernel.org/r/69859728.050a0220.3b3015.0033.GAE@google.com Assisted-by: Claude:unspecified Cc: Barry Song Cc: David Hildenbrand Cc: Hugh Dickins Cc: Johannes Weiner Cc: Kairui Song Cc: Michal Hocko Cc: Muchun Song Cc: Roman Gushchin Cc: Shakeel Butt Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/swap_cgroup.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mm/swap_cgroup.c b/mm/swap_cgroup.c index de779fed8c210..95c38e54dd587 100644 --- a/mm/swap_cgroup.c +++ b/mm/swap_cgroup.c @@ -124,6 +124,8 @@ unsigned short lookup_swap_cgroup_id(swp_entry_t ent) return 0; ctrl = &swap_cgroup_ctrl[swp_type(ent)]; + if (unlikely(!ctrl->map)) + return 0; return __swap_cgroup_id_lookup(ctrl->map, swp_offset(ent)); } From 2a55fdf9f746a1a6ced7fd62ea1080b8a917e0b0 Mon Sep 17 00:00:00 2001 From: Zijiang Huang Date: Wed, 6 May 2026 21:09:19 +0800 Subject: [PATCH 0345/2758] mm/swap: add cond_resched() in swap_reclaim_full_clusters to prevent softlockup commit 66366d291f666ddeda5f8c84f253e308de3e6b55 upstream. We hit a real softlockup in an internal stress test environment. The workload was LTP memory/swap stress on a large arm64 machine, with 320 CPUs, about 1TB memory and an 8.6GB swap device. The system was under heavy load and the swap device had a large number of full clusters. The softlockup was triggered during a stress test after about 3 days. So, add periodic cond_resched() calls during large full_clusters reclaim operations to prevent softlockup issues. Detailed call trace as follow: PID: 3817773 TASK: ffff0883bb28b780 CPU: 48 COMMAND: "kworker/48:7" #0 [ffff800080183d10] __crash_kexec at ffffa4c1361e5de4 #1 [ffff800080183d90] panic at ffffa4c1360d5e9c #2 [ffff800080183e20] watchdog_timer_fn at ffffa4c136231fa8 ... #16 [ffff8000c4ad3cb0] swap_cache_del_folio at ffffa4c1363e1614 #17 [ffff8000c4ad3ce0] __try_to_reclaim_swap at ffffa4c1363e4bfc #18 [ffff8000c4ad3d40] swap_reclaim_full_clusters at ffffa4c1363e5474 #19 [ffff8000c4ad3da0] swap_reclaim_work at ffffa4c1363e550c #20 [ffff8000c4ad3dc0] process_one_work at ffffa4c136102edc #21 [ffff8000c4ad3e10] worker_thread at ffffa4c136103398 #22 [ffff8000c4ad3e70] kthread at ffffa4c13610d95c Link: https://lore.kernel.org/20260506130919.2298807-1-kerayhuang@tencent.com Fixes: 5168a68eb78f ("mm, swap: avoid over reclaim of full clusters") Signed-off-by: Zijiang Huang Reviewed-by: Kairui Song Reviewed-by: Hao Peng Reviewed-by: albinwyang Reviewed-by: Baoquan He Acked-by: Chris Li Cc: Barry Song Cc: Kairui Song Cc: Kemeng Shi Cc: Nhat Pham Cc: Youngjun Park Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/swapfile.c | 1 + 1 file changed, 1 insertion(+) diff --git a/mm/swapfile.c b/mm/swapfile.c index 9174f1eeffb09..74a1e324449dc 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -1054,6 +1054,7 @@ static void swap_reclaim_full_clusters(struct swap_info_struct *si, bool force) swap_cluster_unlock(ci); if (to_scan <= 0) break; + cond_resched(); } } From 02f8a0a1da2ec02ead3715f9c247ca4da95de663 Mon Sep 17 00:00:00 2001 From: Pratham Gupta Date: Mon, 4 May 2026 22:11:57 -0700 Subject: [PATCH 0346/2758] netfilter: ctnetlink: use nf_ct_exp_net() in expectation dump commit a7f57320bbbc67e347bf5fff4b4a9bab980d5956 upstream. Commit 02a3231b6d82 ("netfilter: nf_conntrack_expect: store netns and zone in expectation") introduced exp->net so RCU-only expectation paths no longer need to dereference exp->master for netns lookups. Commit 3db5647984de ("netfilter: nf_conntrack_expect: skip expectations in other netns via proc") updated the proc path accordingly, but ctnetlink_exp_dump_table() still compares against nf_ct_net(exp->master). Use nf_ct_exp_net(exp) here as well so the netlink dump path matches the rest of the March 2026 expectation netns/RCU cleanup. Fixes: 02a3231b6d82 ("netfilter: nf_conntrack_expect: store netns and zone in expectation") Cc: stable@vger.kernel.org Signed-off-by: Pratham Gupta Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_conntrack_netlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index befa7e83ee49f..d429f9c9546cd 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -3173,7 +3173,7 @@ ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb) if (l3proto && exp->tuple.src.l3num != l3proto) continue; - if (!net_eq(nf_ct_net(exp->master), net)) + if (!net_eq(nf_ct_exp_net(exp), net)) continue; if (cb->args[1]) { From 57056be3ec12e7d9ecd20a60d4060f510e4f284c Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sun, 5 Jul 2026 15:29:13 +0200 Subject: [PATCH 0347/2758] netfilter: handle unreadable frags commit da5b58478a9c1b85608c9e40a3b8432d071b409e upstream. sashiko reports: When an skb with unreadable fragments (such as from devmem TCP, where skb_frags_readable(skb) returns false) is processed by the u32 module, skb_copy_bits() will safely return a negative error code [..] xt_u32: bail out with hotdrop in this case. gather_frags: return -1, just as if we had no fragment header. nfnetlink_queue: restrict to the linear part. nfnetlink_log: restrict to the linear part. v2: - skb_zerocopy helpers don't copy readable flag, i.e. nfnetlink_queue is broken too xt_u32 shouldn't return true if hotdrop was set. Fixes: 65249feb6b3d ("net: add support for skbs with unreadable frags") Cc: stable@vger.kernel.org Acked-by: Mina Almasry Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/ipv6/netfilter/nf_conntrack_reasm.c | 2 +- net/netfilter/nfnetlink_log.c | 26 ++++++++++++++++--------- net/netfilter/nfnetlink_queue.c | 16 +++++++++++---- net/netfilter/xt_u32.c | 16 ++++++++++----- 4 files changed, 41 insertions(+), 19 deletions(-) diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 64ab23ff559bb..7edca05dbfc93 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -418,7 +418,7 @@ find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff) return -1; } if (skb_copy_bits(skb, start, &hdr, sizeof(hdr))) - BUG(); + return -1; if (nexthdr == NEXTHDR_AUTH) hdrlen = ipv6_authlen(&hdr); else diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index fa36575998616..5fee61b3813cb 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -676,7 +676,7 @@ __build_packet_message(struct nfnl_log_net *log, goto nla_put_failure; if (skb_copy_bits(skb, 0, nla_data(nla), data_len)) - BUG(); + goto nla_put_failure; } nlh->nlmsg_len = inst->skb->tail - old_tail; @@ -698,6 +698,21 @@ static const struct nf_loginfo default_loginfo = { }, }; +static unsigned int nfulnl_get_copy_len(const struct nf_loginfo *li, + const struct sk_buff *skb, + unsigned int copy_len) +{ + unsigned int len = skb->len; + + if ((li->u.ulog.flags & NF_LOG_F_COPY_LEN) && + li->u.ulog.copy_len < copy_len) + copy_len = li->u.ulog.copy_len; + if (!skb_frags_readable(skb)) + len = skb_headlen(skb); + + return min(len, copy_len); +} + /* log handler for internal netfilter logging api */ static void nfulnl_log_packet(struct net *net, @@ -790,14 +805,7 @@ nfulnl_log_packet(struct net *net, break; case NFULNL_COPY_PACKET: - data_len = inst->copy_range; - if ((li->u.ulog.flags & NF_LOG_F_COPY_LEN) && - (li->u.ulog.copy_len < data_len)) - data_len = li->u.ulog.copy_len; - - if (data_len > skb->len) - data_len = skb->len; - + data_len = nfulnl_get_copy_len(li, skb, inst->copy_range); size += nla_total_size(data_len); break; diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index c5e29fec419bb..e0cf9977887f6 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -690,6 +690,17 @@ static int nfqnl_put_master_ifindex(struct sk_buff *nlskb, int attr, } #endif +static unsigned int nfqnl_get_data_len(const struct sk_buff *entskb, + unsigned int copy_range) +{ + unsigned int data_len = entskb->len; + + if (!skb_frags_readable(entskb)) + data_len = skb_headlen(entskb); + + return min(data_len, copy_range); +} + static struct sk_buff * nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, struct nf_queue_entry *entry, @@ -755,10 +766,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, nf_queue_checksum_help(entskb)) return NULL; - data_len = READ_ONCE(queue->copy_range); - if (data_len > entskb->len) - data_len = entskb->len; - + data_len = nfqnl_get_data_len(entskb, READ_ONCE(queue->copy_range)); hlen = skb_zerocopy_headlen(entskb); hlen = min_t(unsigned int, hlen, data_len); size += sizeof(struct nlattr) + hlen; diff --git a/net/netfilter/xt_u32.c b/net/netfilter/xt_u32.c index 117d4615d6684..1ae7f926b0df7 100644 --- a/net/netfilter/xt_u32.c +++ b/net/netfilter/xt_u32.c @@ -14,8 +14,8 @@ #include #include -static bool u32_match_it(const struct xt_u32 *data, - const struct sk_buff *skb) +static int u32_match_it(const struct xt_u32 *data, + const struct sk_buff *skb) { const struct xt_u32_test *ct; unsigned int testind; @@ -40,7 +40,8 @@ static bool u32_match_it(const struct xt_u32 *data, return false; if (skb_copy_bits(skb, pos, &n, sizeof(n)) < 0) - BUG(); + return -1; + val = ntohl(n); nnums = ct->nnums; @@ -68,7 +69,7 @@ static bool u32_match_it(const struct xt_u32 *data, if (skb_copy_bits(skb, at + pos, &n, sizeof(n)) < 0) - BUG(); + return -1; val = ntohl(n); break; } @@ -90,9 +91,14 @@ static bool u32_match_it(const struct xt_u32 *data, static bool u32_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_u32 *data = par->matchinfo; - bool ret; + int ret; ret = u32_match_it(data, skb); + if (ret < 0) { + par->hotdrop = true; + return false; + } + return ret ^ data->invert; } From 5ee856e4208acafaaaf7b84824d39b78c21345d6 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sat, 4 Jul 2026 10:23:31 +0200 Subject: [PATCH 0348/2758] netfilter: ebtables: zero chainstack array commit cbfe53599eebffd188938ab6774cc41794f6f9d5 upstream. sashiko reports: looking at ebtables table translation, could a sparse cpu_possible_mask lead to an uninitialized pointer free? If cpu_possible_mask is sparse (for example, CPU 0 and CPU 2 are possible, but CPU 1 is not), the allocation loop skips CPU 1. If vmalloc_node() fails at CPU 2, the cleanup loop will blindly decrement and call vfree() on newinfo->chainstack[1]. Not a real-world bug, such allocation isn't expected to fail in the first place. Cc: stable@vger.kernel.org Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/bridge/netfilter/ebtables.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 8a6a069329d21..475b92beab2b0 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -921,8 +921,7 @@ static int translate_table(struct net *net, const char *name, * if an error occurs */ newinfo->chainstack = - vmalloc_array(nr_cpu_ids, - sizeof(*(newinfo->chainstack))); + vcalloc(nr_cpu_ids, sizeof(*(newinfo->chainstack))); if (!newinfo->chainstack) return -ENOMEM; for_each_possible_cpu(i) { From 7b217960e88b5d2d1e8cdcbcaf3bdf6fe199a0c8 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sat, 4 Jul 2026 12:05:15 +0200 Subject: [PATCH 0349/2758] netfilter: ebtables: module names must be null-terminated commit 084d23f818321390509e9738a0b08bbf46df6425 upstream. We need to explicitly check the length, else we may pass non-null terminated string to request_module(). Cc: stable@vger.kernel.org Fixes: bcf493428840 ("netfilter: ebtables: Fix extension lookup with identical name") Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/bridge/netfilter/ebtables.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 475b92beab2b0..f3bbd526f5dd9 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -403,6 +403,9 @@ ebt_check_match(struct ebt_entry_match *m, struct xt_mtchk_param *par, left - sizeof(struct ebt_entry_match) < m->match_size) return -EINVAL; + if (strnlen(m->u.name, XT_EXTENSION_MAXNAMELEN) == XT_EXTENSION_MAXNAMELEN) + return -EINVAL; + match = xt_find_match(NFPROTO_BRIDGE, m->u.name, m->u.revision); if (IS_ERR(match) || match->family != NFPROTO_BRIDGE) { if (!IS_ERR(match)) From b6183b1b88a722b6d8ea0cecc99eba168a15e0be Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sun, 5 Jul 2026 14:58:00 -0700 Subject: [PATCH 0350/2758] netfilter: ebtables: terminate table name before find_table_lock() commit a622d2e9608c9dff47fc2e5759ac7aa3a836b45d upstream. update_counters() and compat_update_counters() forward a user-supplied 32-byte table name to find_table_lock() without NUL-terminating it. On a lookup miss, find_inlist_lock() calls try_then_request_module(..., "%s%s", "ebtable_", name), and vsnprintf() reads past the name field and the stack object until it hits a zero byte. BUG: KASAN: stack-out-of-bounds in string (lib/vsprintf.c:648 lib/vsprintf.c:730) Read of size 1 at addr ffff8880119dfb20 by task exploit/147 Call Trace: ... string (lib/vsprintf.c:648 lib/vsprintf.c:730) vsnprintf (lib/vsprintf.c:2945) __request_module (kernel/module/kmod.c:150) do_update_counters.isra.0 (net/bridge/netfilter/ebtables.c:371 net/bridge/netfilter/ebtables.c:380) update_counters (net/bridge/netfilter/ebtables.c:1440) do_ebt_set_ctl (net/bridge/netfilter/ebtables.c:2573) nf_setsockopt (net/netfilter/nf_sockopt.c:101) ip_setsockopt (net/ipv4/ip_sockglue.c:1424) raw_setsockopt (net/ipv4/raw.c:847) __sys_setsockopt (net/socket.c:2393) ... compat_do_replace() shares the same unterminated name via compat_copy_ebt_replace_from_user(); terminate it there too so all find_table_lock() callers behave alike. The other callers already terminate the name after the copy. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Fixes: 81e675c227ec ("netfilter: ebtables: add CONFIG_COMPAT support") Cc: stable@vger.kernel.org Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/bridge/netfilter/ebtables.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index f3bbd526f5dd9..1147ae4fee971 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -1436,6 +1436,8 @@ static int update_counters(struct net *net, sockptr_t arg, unsigned int len) if (copy_from_sockptr(&hlp, arg, sizeof(hlp))) return -EFAULT; + hlp.name[sizeof(hlp.name) - 1] = '\0'; + if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter)) return -EINVAL; @@ -2275,6 +2277,8 @@ static int compat_copy_ebt_replace_from_user(struct ebt_replace *repl, memcpy(repl, &tmp, offsetof(struct ebt_replace, hook_entry)); + repl->name[sizeof(repl->name) - 1] = '\0'; + /* starting with hook_entry, 32 vs. 64 bit structures are different */ for (i = 0; i < NF_BR_NUMHOOKS; i++) repl->hook_entry[i] = compat_ptr(tmp.hook_entry[i]); @@ -2397,6 +2401,8 @@ static int compat_update_counters(struct net *net, sockptr_t arg, if (copy_from_sockptr(&hlp, arg, sizeof(hlp))) return -EFAULT; + hlp.name[sizeof(hlp.name) - 1] = '\0'; + /* try real handler in case userland supplied needed padding */ if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter)) return update_counters(net, arg, len); From 419835f1bd5fa33c45c6153f0968771c3ada0f44 Mon Sep 17 00:00:00 2001 From: Adrian Bente Date: Thu, 28 May 2026 10:08:51 +0300 Subject: [PATCH 0351/2758] netfilter: flowtable: fix offloaded ct timeout never being extended commit 53b3e60edb674b442b2b3bbdba484667b0f47a5d upstream. OpenWrt has recently migrated many platforms to kernel 6.18. On the MediaTek platform, which supports hardware network offloading, WiFi connections accelerated via the WED path were observed to drop after roughly 300 seconds. After several debugging sessions, assisted by the Claude LLM, the problem was narrowed down as follows: nf_flow_table_extend_ct_timeout() extends ct->timeout for offloaded flows using: cmpxchg(&ct->timeout, expires, new_timeout); 'expires' comes from nf_ct_expires(ct) and is a relative value, while ct->timeout holds an absolute timestamp. The two are never equal, so the cmpxchg always fails and the timeout is never extended. This goes unnoticed for most flows, but a long-lived hardware (WED) offloaded flow on MediaTek MT7986 eventually has ct->timeout decay to zero, the conntrack entry is reaped and the connection breaks. Open-code the relative value from a single READ_ONCE(ct->timeout) snapshot and compare against that same absolute snapshot in the cmpxchg, so the timeout extension actually takes effect while the datapath remains authoritative if it updates ct->timeout concurrently. Fixes: 03428ca5cee9 ("netfilter: conntrack: rework offload nf_conn timeout extension logic") Cc: stable@vger.kernel.org Suggested-by: Florian Westphal Signed-off-by: Adrian Bente Signed-off-by: Pablo Neira Ayuso Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_flow_table_core.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c index 785d8c244a771..99c5b9d671a0c 100644 --- a/net/netfilter/nf_flow_table_core.c +++ b/net/netfilter/nf_flow_table_core.c @@ -505,8 +505,13 @@ static u32 nf_flow_table_tcp_timeout(const struct nf_conn *ct) */ static void nf_flow_table_extend_ct_timeout(struct nf_conn *ct) { - static const u32 min_timeout = 5 * 60 * HZ; - u32 expires = nf_ct_expires(ct); + static const s32 min_timeout = 5 * 60 * HZ; + u32 ct_timeout = READ_ONCE(ct->timeout); + s32 expires; + + expires = ct_timeout - nfct_time_stamp; + if (expires <= 0) /* already expired */ + return; /* normal case: large enough timeout, nothing to do. */ if (likely(expires >= min_timeout)) @@ -524,7 +529,7 @@ static void nf_flow_table_extend_ct_timeout(struct nf_conn *ct) if (nf_ct_is_confirmed(ct) && test_bit(IPS_OFFLOAD_BIT, &ct->status)) { u8 l4proto = nf_ct_protonum(ct); - u32 new_timeout = true; + u32 new_timeout = 1; switch (l4proto) { case IPPROTO_UDP: @@ -549,7 +554,7 @@ static void nf_flow_table_extend_ct_timeout(struct nf_conn *ct) */ if (new_timeout) { new_timeout += nfct_time_stamp; - cmpxchg(&ct->timeout, expires, new_timeout); + cmpxchg(&ct->timeout, ct_timeout, new_timeout); } } From 9efe838c13133acb70c78d04c49e8362fe533566 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 30 Jun 2026 11:40:55 +0200 Subject: [PATCH 0352/2758] netfilter: flowtable: IPIP tunnel hardware offload is not yet support commit 6c5dcab95f4cd42a1648739ec9300fbb4b1a021f upstream. No driver supports for IPIP tunnels yet, give up early on setting up the hardware offload for this scenario. This patch adds a stub that can be enhanced to add more configuration that are currently not supported. As of now, the offload work is enqueued to the worker, then ignored if the hardware offload configuration is not supported. Check the NF_FLOW_HW flag to know if this entry was already tried once to be offloaded so this is not retried on refresh when unsupported. Move NF_FLOW_HW flag check to nf_flow_offload_add(). If this NF_FLOW_HW flag is unset the _del and _stats variants are never called. This can be updated later on to skip hardware offload work to be queued in case hardware offload does not support it. Fixes: d98103575dcd ("netfilter: flowtable: Add IP6IP6 rx sw acceleration") Fixes: ab427db17885 ("netfilter: flowtable: Add IPIP rx sw acceleration") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Xin Liu Reported-by: Zhengyang Chen Signed-off-by: Pablo Neira Ayuso Acked-by: Lorenzo Bianconi Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- include/net/netfilter/nf_flow_table.h | 2 ++ net/netfilter/nf_flow_table_core.c | 7 +++---- net/netfilter/nf_flow_table_offload.c | 22 ++++++++++++++++++++-- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h index 7b23b245a5a86..dc5c9b48e65a9 100644 --- a/include/net/netfilter/nf_flow_table.h +++ b/include/net/netfilter/nf_flow_table.h @@ -357,6 +357,8 @@ static inline int nf_flow_register_bpf(void) void nf_flow_offload_add(struct nf_flowtable *flowtable, struct flow_offload *flow); +void nf_flow_offload_refresh(struct nf_flowtable *flowtable, + struct flow_offload *flow); void nf_flow_offload_del(struct nf_flowtable *flowtable, struct flow_offload *flow); void nf_flow_offload_stats(struct nf_flowtable *flowtable, diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c index 99c5b9d671a0c..d06ce0848b68e 100644 --- a/net/netfilter/nf_flow_table_core.c +++ b/net/netfilter/nf_flow_table_core.c @@ -345,10 +345,8 @@ int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow) nf_ct_refresh(flow->ct, NF_CT_DAY); - if (nf_flowtable_hw_offload(flow_table)) { - __set_bit(NF_FLOW_HW, &flow->flags); + if (nf_flowtable_hw_offload(flow_table)) nf_flow_offload_add(flow_table, flow); - } return 0; } @@ -369,7 +367,8 @@ void flow_offload_refresh(struct nf_flowtable *flow_table, test_bit(NF_FLOW_CLOSING, &flow->flags)) return; - nf_flow_offload_add(flow_table, flow); + if (test_bit(NF_FLOW_HW, &flow->flags)) + nf_flow_offload_refresh(flow_table, flow); } EXPORT_SYMBOL_GPL(flow_offload_refresh); diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c index 002ec15d988bd..801a3dd9ceea3 100644 --- a/net/netfilter/nf_flow_table_offload.c +++ b/net/netfilter/nf_flow_table_offload.c @@ -1101,9 +1101,17 @@ nf_flow_offload_work_alloc(struct nf_flowtable *flowtable, return offload; } +static bool nf_flow_offload_unsupported(struct flow_offload *flow) +{ + if (flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.tun_num || + flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.tun_num) + return true; -void nf_flow_offload_add(struct nf_flowtable *flowtable, - struct flow_offload *flow) + return false; +} + +void nf_flow_offload_refresh(struct nf_flowtable *flowtable, + struct flow_offload *flow) { struct flow_offload_work *offload; @@ -1114,6 +1122,16 @@ void nf_flow_offload_add(struct nf_flowtable *flowtable, flow_offload_queue_work(offload); } +void nf_flow_offload_add(struct nf_flowtable *flowtable, + struct flow_offload *flow) +{ + if (nf_flow_offload_unsupported(flow)) + return; + + set_bit(NF_FLOW_HW, &flow->flags); + nf_flow_offload_refresh(flowtable, flow); +} + void nf_flow_offload_del(struct nf_flowtable *flowtable, struct flow_offload *flow) { From 0f0a83e26a9c7fd4b243c315ce07161d2496d83d Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Tue, 9 Jun 2026 21:10:06 +0900 Subject: [PATCH 0353/2758] Bluetooth: btmtksdio: fix infinite loop in btmtksdio_txrx_work() commit a257407e2bbbb099ed427719a50563f67fa366d8 upstream. Every once in a while we see a hung btmtksdio_flush() task: INFO: task kworker/u17:0:189 blocked for more than 122 seconds. __cancel_work_timer+0x3f4/0x460 cancel_work_sync+0x1c/0x2c btmtksdio_flush+0x2c/0x40 hci_dev_open_sync+0x10c4/0x2190 [..] It all boils down to incorrect time_is_before_jiffies() usage in btmtksdio_txrx_work(). The btmtksdio_txrx_work() loop is expected to be terminated if running for longer than 5*HZ. However the timeout check is twisted: time_is_before_jiffies(old_jiffies + 5*HZ) evaluates to true when old_jiffies + 5*HZ is in the past i.e. when a timeout has occurred. Using OR with time_is_before_jiffies(txrx_timeout) means that: - before the 5-second timeout: the condition is `int_status || false`, so it loops as long as there are pending interrupts. - after the 5-second timeout: the condition becomes `int_status || true`, which is always true. When the loop becomes infinite btmtksdio_txrx_work() loop never terminates and never releases the SDIO host. Fix loop termination condition to actually enforce a 5*HZ timeout. Fixes: 26270bc189ea4 ("Bluetooth: btmtksdio: move interrupt service to work") Cc: stable@vger.kernel.org Signed-off-by: Sergey Senozhatsky Reviewed-by: Sean Wang Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/btmtksdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c index 5b0fab7b89b5a..c6f80c419e901 100644 --- a/drivers/bluetooth/btmtksdio.c +++ b/drivers/bluetooth/btmtksdio.c @@ -620,7 +620,7 @@ static void btmtksdio_txrx_work(struct work_struct *work) if (btmtksdio_rx_packet(bdev, rx_size) < 0) bdev->hdev->stat.err_rx++; } - } while (int_status || time_is_before_jiffies(txrx_timeout)); + } while (int_status && time_is_after_jiffies(txrx_timeout)); /* Enable interrupt */ if (bdev->func->irq_handler) From 563a8573047182f550b1e1e030615755cd8c41da Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Sun, 28 Jun 2026 02:50:58 +0200 Subject: [PATCH 0354/2758] Bluetooth: bnep: pin L2CAP connection during netdev registration commit bb067a99a0356196c0b89a95721985485ebce5a5 upstream. bnep_add_connection() reads the L2CAP connection without holding the channel lock, then passes its HCI device to register_netdev(). Controller teardown can clear and release that connection concurrently, leaving the network device registration path to dereference a freed parent device. Take a reference to the L2CAP connection while holding the channel lock. Retain it until register_netdev() has taken the parent device reference. Fixes: 65f53e9802db ("Bluetooth: Access BNEP session addresses through L2CAP channel") Reported-by: syzbot+fed5dce4553262f3b35c@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=fed5dce4553262f3b35c Cc: stable@vger.kernel.org Signed-off-by: Yousef Alhouseen Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/bnep/core.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index 5c5f53ff30e8e..ed3ca38d8a6e1 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -562,14 +562,18 @@ static int bnep_session(void *arg) return 0; } -static struct device *bnep_get_device(struct bnep_session *session) +static struct l2cap_conn *bnep_get_conn(struct bnep_session *session) { - struct l2cap_conn *conn = l2cap_pi(session->sock->sk)->chan->conn; + struct l2cap_chan *chan = l2cap_pi(session->sock->sk)->chan; + struct l2cap_conn *conn; - if (!conn || !conn->hcon) - return NULL; + l2cap_chan_lock(chan); + conn = chan->conn; + if (conn) + l2cap_conn_get(conn); + l2cap_chan_unlock(chan); - return &conn->hcon->dev; + return conn; } static const struct device_type bnep_type = { @@ -581,6 +585,7 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock) u32 valid_flags = BIT(BNEP_SETUP_RESPONSE); struct net_device *dev; struct bnep_session *s, *ss; + struct l2cap_conn *conn = NULL; u8 dst[ETH_ALEN], src[ETH_ALEN]; int err; @@ -640,10 +645,18 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock) bnep_set_default_proto_filter(s); #endif - SET_NETDEV_DEV(dev, bnep_get_device(s)); + conn = bnep_get_conn(s); + if (!conn) { + err = -ENOTCONN; + goto failed; + } + + SET_NETDEV_DEV(dev, &conn->hcon->dev); SET_NETDEV_DEVTYPE(dev, &bnep_type); err = register_netdev(dev); + l2cap_conn_put(conn); + conn = NULL; if (err) goto failed; @@ -665,6 +678,8 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock) return 0; failed: + if (conn) + l2cap_conn_put(conn); up_write(&bnep_session_sem); free_netdev(dev); return err; From 49bcb39e3a041ce26021f77971eaccb49a275118 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Wed, 17 Jun 2026 16:36:52 +0800 Subject: [PATCH 0355/2758] Bluetooth: btnxpuart: Fix out-of-bounds firmware read in nxp_recv_fw_req_v3() commit badff6c3bed8923a1257a853f137d447976eec30 upstream. During the v3 firmware download the controller sends a v3_data_req with a 32 bit offset and a 16 bit len. nxp_recv_fw_req_v3() checks only the lower bound of the offset and then sends firmware from that offset. nxpdev->fw_dnld_v3_offset = offset - nxpdev->fw_v3_offset_correction; serdev_device_write_buf(nxpdev->serdev, nxpdev->fw->data + nxpdev->fw_dnld_v3_offset, len); Nothing checks that fw_dnld_v3_offset + len stays within nxpdev->fw->size, so a controller that asks for an offset or length past the firmware image makes the driver read past the end of nxpdev->fw->data and send that memory back over UART. nxp_recv_fw_req_v1() already bounds the same write. Add the equivalent check to the v3 path, reject the request when it falls outside the firmware image, and zero len on the error path so the fw_v3_prev_sent bookkeeping at free_skb stays consistent. Fixes: 689ca16e5232 ("Bluetooth: NXP: Add protocol support for NXP Bluetooth chipsets") Suggested-by: Neeraj Sanjay Kale Reviewed-by: Neeraj Sanjay Kale Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/btnxpuart.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index e7036a48ce486..6a1cffe08d5f7 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -1267,6 +1267,12 @@ static int nxp_recv_fw_req_v3(struct hci_dev *hdev, struct sk_buff *skb) } nxpdev->fw_dnld_v3_offset = offset - nxpdev->fw_v3_offset_correction; + if (nxpdev->fw_dnld_v3_offset >= nxpdev->fw->size || + len > nxpdev->fw->size - nxpdev->fw_dnld_v3_offset) { + bt_dev_err(hdev, "FW download out of bounds, ignoring request"); + len = 0; + goto free_skb; + } serdev_device_write_buf(nxpdev->serdev, nxpdev->fw->data + nxpdev->fw_dnld_v3_offset, len); From 50c662bdcd51b03033a0abed6716bfd377ba1049 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Sun, 28 Jun 2026 02:23:05 +0200 Subject: [PATCH 0356/2758] Bluetooth: fix UAF in bt_accept_dequeue() commit 4bd0b274054f2679f28b70222b607bb0afc3ab9a upstream. bt_accept_get() takes a temporary reference before dropping the accept queue lock. bt_accept_dequeue() currently drops that reference before bt_accept_unlink(), leaving only the queue reference. bt_accept_unlink() drops the queue reference. The subsequent sock_hold() therefore accesses freed memory if it was the final reference, as observed by KASAN during listening L2CAP socket cleanup. Retain the temporary queue-walk reference through unlink and hand it to the caller on success. Drop it explicitly on the closed and not-yet-connected paths. Fixes: ab1513597c6c ("Bluetooth: fix UAF in l2cap_sock_cleanup_listen() vs l2cap_conn_del()") Reported-by: syzbot+674ff7e4d7fdfd572afc@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=674ff7e4d7fdfd572afc Cc: stable@vger.kernel.org Signed-off-by: Yousef Alhouseen Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/af_bluetooth.c | 17 +++-------------- net/bluetooth/l2cap_sock.c | 4 ++-- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 1a6aa3f8d4d61..ee8d92ea1e5ac 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -308,7 +308,7 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) restart: for (sk = bt_accept_get(parent, NULL); sk; sk = next) { - /* Prevent early freeing of sk due to unlink and sock_kill */ + /* The reference from bt_accept_get() keeps sk alive. */ lock_sock(sk); /* Check sk has not already been unlinked via @@ -324,13 +324,11 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) next = bt_accept_get(parent, sk); - /* sk is safely in the parent list so reduce reference count */ - sock_put(sk); - /* FIXME: Is this check still needed */ if (sk->sk_state == BT_CLOSED) { bt_accept_unlink(sk); release_sock(sk); + sock_put(sk); continue; } @@ -340,16 +338,6 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) if (newsock) sock_graft(sk, newsock); - /* Hand the caller a reference taken while sk is - * still locked. bt_accept_unlink() just dropped - * the accept-queue reference; without this hold a - * concurrent teardown (e.g. l2cap_conn_del() -> - * l2cap_sock_kill()) could free sk between - * release_sock() and the caller using it. Every - * caller drops this with sock_put() when done. - */ - sock_hold(sk); - release_sock(sk); if (next) sock_put(next); @@ -357,6 +345,7 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) } release_sock(sk); + sock_put(sk); } return NULL; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index c138aa4ae2669..1e93de1559871 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1482,8 +1482,8 @@ static void l2cap_sock_cleanup_listen(struct sock *parent) /* Close not yet accepted channels. * - * bt_accept_dequeue() now returns sk with an extra reference held - * (taken while sk was still locked) so a concurrent l2cap_conn_del() + * bt_accept_dequeue() returns sk with its temporary queue-walk + * reference held, so a concurrent l2cap_conn_del() * -> l2cap_sock_kill() cannot free sk under us. * * cleanup_listen() runs under the parent sk lock, so unlike From b42cb640a0493d16b61ddd267420274be15efdc1 Mon Sep 17 00:00:00 2001 From: Siwei Zhang Date: Mon, 15 Jun 2026 11:33:05 -0400 Subject: [PATCH 0357/2758] Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn() commit 12917f591cea1af36087dba5b9ec888652f0b42a upstream. hci_abort_conn() read hci_skb_event(hdev->sent_cmd) when a connection was pending, but hdev->sent_cmd can be NULL while req_status is still HCI_REQ_PEND, leading to a NULL pointer dereference and a general protection fault from the hci_rx_work() receive path. Instead of inspecting hdev->sent_cmd, track the in-flight create connection command with a new per-connection HCI_CONN_CREATE flag and route all cancellation through hci_cancel_connect_sync(), which dispatches to a dedicated per-type cancel function. The create command is in exactly one of two states: still queued, or in flight. The cancel function holds cmd_sync_work_lock across the whole decision: the worker takes this lock to dequeue every entry, so while it is held a queued command cannot start running and an in-flight command cannot complete and let the next command become pending. This keeps the flag test and hci_cmd_sync_cancel() atomic with respect to the worker, so a queued command is simply dequeued, and an in-flight command owned by this connection is cancelled without the risk of cancelling an unrelated command that became pending in the meantime. CIS uses the same flag mechanism via HCI_CONN_CREATE_CIS but cannot be dequeued per-connection. hci_acl_create_conn_sync() and hci_le_create_conn_sync() clear HCI_CONN_CREATE after the create command completes, but the command status handler can free conn via hci_conn_del() (for example when the controller rejects the connection) while the worker is still blocked on the connection complete event. Hold a reference on conn across the create command so the flag can be cleared without a use-after-free. Fixes: a13f316e90fd ("Bluetooth: hci_conn: Consolidate code for aborting connections") Cc: stable@vger.kernel.org Suggested-by: XIAO WU Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Siwei Zhang Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_conn.c | 21 +---- net/bluetooth/hci_sync.c | 133 +++++++++++++++++++++++++++---- 3 files changed, 123 insertions(+), 32 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index aa600fbf9a535..aa554c34f9ec3 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -988,6 +988,7 @@ enum { HCI_CONN_AUTH_FAILURE, HCI_CONN_PER_ADV, HCI_CONN_BIG_CREATED, + HCI_CONN_CREATE, HCI_CONN_CREATE_CIS, HCI_CONN_CREATE_BIG_SYNC, HCI_CONN_BIG_SYNC, diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 54eabaa46960b..eba4a548bef52 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -3181,26 +3181,11 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason) conn->abort_reason = reason; - /* If the connection is pending check the command opcode since that - * might be blocking on hci_cmd_sync_work while waiting its respective - * event so we need to hci_cmd_sync_cancel to cancel it. - * - * hci_connect_le serializes the connection attempts so only one - * connection can be in BT_CONNECT at time. + /* Cancel the connect attempt. A return of 0 means the create command + * was still queued and got dequeued, so there is nothing to disconnect. */ - if (conn->state == BT_CONNECT && READ_ONCE(hdev->req_status) == HCI_REQ_PEND) { - switch (hci_skb_event(hdev->sent_cmd)) { - case HCI_EV_CONN_COMPLETE: - case HCI_EV_LE_CONN_COMPLETE: - case HCI_EV_LE_ENHANCED_CONN_COMPLETE: - case HCI_EVT_LE_CIS_ESTABLISHED: - hci_cmd_sync_cancel(hdev, ECANCELED); - break; - } - /* Cancel connect attempt if still queued/pending */ - } else if (!hci_cancel_connect_sync(hdev, conn)) { + if (!hci_cancel_connect_sync(hdev, conn)) return 0; - } /* Run immediately if on cmd_sync_work since this may be called * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index df23245d6ccda..a3df69bdec1e0 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -6611,6 +6611,11 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) bt_dev_dbg(hdev, "conn %p", conn); + /* Hold a reference so conn stays valid for the HCI_CONN_CREATE + * clear_bit() at done. + */ + hci_conn_get(conn); + clear_bit(HCI_CONN_SCANNING, &conn->flags); conn->state = BT_CONNECT; @@ -6623,6 +6628,7 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) hdev->le_scan_type == LE_SCAN_ACTIVE && !hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES)) { hci_conn_del(conn); + hci_conn_put(conn); return -EBUSY; } @@ -6668,6 +6674,12 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) &own_addr_type); if (err) goto done; + + /* Mark create connection in flight so hci_cancel_connect_sync() can + * cancel it while blocking on the connection complete event. + */ + set_bit(HCI_CONN_CREATE, &conn->flags); + /* Send command LE Extended Create Connection if supported */ if (use_ext_conn(hdev)) { err = hci_le_ext_create_conn_sync(hdev, conn, own_addr_type); @@ -6703,11 +6715,14 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) conn->conn_timeout, NULL); done: + clear_bit(HCI_CONN_CREATE, &conn->flags); + if (err == -ETIMEDOUT) hci_le_connect_cancel_sync(hdev, conn, 0x00); /* Re-enable advertising after the connection attempt is finished. */ hci_resume_advertising_sync(hdev); + hci_conn_put(conn); return err; } @@ -6982,10 +6997,25 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data) else cp.role_switch = 0x00; - return __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN, - sizeof(cp), &cp, - HCI_EV_CONN_COMPLETE, - conn->conn_timeout, NULL); + /* Hold a reference so conn stays valid for the HCI_CONN_CREATE + * clear_bit() below. + */ + hci_conn_get(conn); + + /* Mark create connection in flight so hci_cancel_connect_sync() can + * cancel it while blocking on the connection complete event. + */ + set_bit(HCI_CONN_CREATE, &conn->flags); + + err = __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN, + sizeof(cp), &cp, + HCI_EV_CONN_COMPLETE, + conn->conn_timeout, NULL); + + clear_bit(HCI_CONN_CREATE, &conn->flags); + hci_conn_put(conn); + + return err; } int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn) @@ -7037,22 +7067,97 @@ int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn) return (err == -EEXIST) ? 0 : err; } -int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn) +static int hci_acl_cancel_create_conn_sync(struct hci_dev *hdev, + struct hci_conn *conn) { - if (conn->state != BT_OPEN) - return -EINVAL; + struct hci_cmd_sync_work_entry *entry; + int err = -EBUSY; + + /* cmd_sync_work_lock makes the HCI_CONN_CREATE test and the cancel + * atomic against the worker, which takes this lock to dequeue every + * entry: while it is held no other command can become pending, so + * hci_cmd_sync_cancel() cannot cancel an unrelated command. + */ + mutex_lock(&hdev->cmd_sync_work_lock); + + /* In flight: this connection owns the pending request, cancel it. */ + if (test_bit(HCI_CONN_CREATE, &conn->flags)) { + hci_cmd_sync_cancel(hdev, ECANCELED); + goto unlock; + } + + /* Still queued: a successful dequeue means it never started, so there + * is nothing to disconnect. + */ + entry = _hci_cmd_sync_lookup_entry(hdev, hci_acl_create_conn_sync, conn, + NULL); + if (entry) { + _hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED); + err = 0; + } + +unlock: + mutex_unlock(&hdev->cmd_sync_work_lock); + return err; +} + +static int hci_le_cancel_create_conn_sync(struct hci_dev *hdev, + struct hci_conn *conn) +{ + struct hci_cmd_sync_work_entry *entry; + int err = -EBUSY; + + /* cmd_sync_work_lock keeps the HCI_CONN_CREATE test and the cancel + * atomic against the cmd_sync worker. + */ + mutex_lock(&hdev->cmd_sync_work_lock); + if (test_bit(HCI_CONN_CREATE, &conn->flags)) { + hci_cmd_sync_cancel(hdev, ECANCELED); + goto unlock; + } + + entry = _hci_cmd_sync_lookup_entry(hdev, hci_le_create_conn_sync, conn, + create_le_conn_complete); + if (entry) { + _hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED); + err = 0; + } + +unlock: + mutex_unlock(&hdev->cmd_sync_work_lock); + return err; +} + +static int hci_cis_cancel_create_conn_sync(struct hci_dev *hdev, + struct hci_conn *conn) +{ + /* LE Create CIS is shared by the whole CIG and cannot be dequeued + * per-connection, so only an in-flight command can be cancelled. + * cmd_sync_work_lock keeps the test and the cancel atomic against the + * cmd_sync worker. + */ + mutex_lock(&hdev->cmd_sync_work_lock); + + if (test_bit(HCI_CONN_CREATE_CIS, &conn->flags)) + hci_cmd_sync_cancel(hdev, ECANCELED); + + mutex_unlock(&hdev->cmd_sync_work_lock); + return -EBUSY; +} + +int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn) +{ switch (conn->type) { case ACL_LINK: - return !hci_cmd_sync_dequeue_once(hdev, - hci_acl_create_conn_sync, - conn, NULL); + return hci_acl_cancel_create_conn_sync(hdev, conn); case LE_LINK: - return !hci_cmd_sync_dequeue_once(hdev, hci_le_create_conn_sync, - conn, create_le_conn_complete); + return hci_le_cancel_create_conn_sync(hdev, conn); + case CIS_LINK: + return hci_cis_cancel_create_conn_sync(hdev, conn); + default: + return -ENOENT; } - - return -ENOENT; } int hci_le_conn_update_sync(struct hci_dev *hdev, struct hci_conn *conn, From 714d861d35d937f23375a4517569b13917bbbe51 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 13 Jun 2026 21:43:37 +0300 Subject: [PATCH 0358/2758] Bluetooth: hci_uart: clear HCI_UART_SENDING when write_work is canceled commit 1b0d946d6f08bd39211385bc703a440911b41e46 upstream. HCI_UART_SENDING bit in tx_state means write_work is pending and blocks queueing it again. Currently this bit is not cleared when canceling the work in hci_uart_close(), which blocks future writes when device is reopened later if write_work was pending. Fix by clearing HCI_UART_SENDING when canceling the work. Also make clearing of tx_skb safe by using disable_work_sync + enable_work instead of just cancel_work_sync. hci_uart_flush() purges the proto tx queue so we can cancel the pending write_work there, instead of doing it just in hci_uart_close(). Re-enable and possibly requeue the work after queue flush. Fixes: c1bb9336ae6b ("Bluetooth: hci_uart: fix UAFs and race conditions in close and init paths") Link: https://lore.kernel.org/linux-bluetooth/07e0a28650773abec711ee492fdb1bf5d21a6c98.camel@iki.fi/ Cc: stable@vger.kernel.org Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/hci_ldisc.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 47f4902b40b47..2ad42c3bbaac7 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -239,6 +239,8 @@ static int hci_uart_flush(struct hci_dev *hdev) BT_DBG("hdev %p tty %p", hdev, tty); + disable_work_sync(&hu->write_work); + if (hu->tx_skb) { kfree_skb(hu->tx_skb); hu->tx_skb = NULL; } @@ -254,6 +256,14 @@ static int hci_uart_flush(struct hci_dev *hdev) percpu_up_read(&hu->proto_lock); + /* Resume TX. Also reschedule in case work was queued concurrently; + * this may schedule write_work although there's nothing to do. + */ + enable_work(&hu->write_work); + clear_bit(HCI_UART_SENDING, &hu->tx_state); + if (test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state)) + hci_uart_tx_wakeup(hu); + return 0; } @@ -271,12 +281,8 @@ static int hci_uart_open(struct hci_dev *hdev) /* Close device */ static int hci_uart_close(struct hci_dev *hdev) { - struct hci_uart *hu = hci_get_drvdata(hdev); - BT_DBG("hdev %p", hdev); - cancel_work_sync(&hu->write_work); - hci_uart_flush(hdev); hdev->flush = NULL; return 0; From 01afd198c2c286cd3b81f44d4e33a2e638711550 Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Sun, 21 Jun 2026 21:23:05 +0500 Subject: [PATCH 0359/2758] Bluetooth: ISO: avoid NULL deref of conn in iso_conn_big_sync() commit d5541eb148da72d5e0a1bca8ecd171f9fc8b366f upstream. iso_conn_big_sync() drops the socket lock to call hci_get_route() and then re-acquires it, but dereferences iso_pi(sk)->conn->hcon afterwards without re-checking that conn is still valid. While the lock is dropped, the connection can be torn down under the same socket lock: iso_disconn_cfm() -> iso_conn_del() -> iso_chan_del() sets iso_pi(sk)->conn to NULL (and the broadcast teardown path can also clear conn->hcon on its own). When iso_conn_big_sync() re-acquires the lock and reads conn->hcon, conn may be NULL, causing a NULL pointer dereference (hcon is the first member of struct iso_conn). This path is reached from iso_sock_recvmsg() for a PA-sync broadcast sink socket (BT_SK_DEFER_SETUP | BT_SK_PA_SYNC), so the dropped-lock window can race with connection teardown driven by controller events. Re-validate iso_pi(sk)->conn and its hcon after re-acquiring the socket lock and bail out if the connection went away, as already done in the sibling iso_sock_rebind_bc(). Fixes: 7a17308c17880d ("Bluetooth: iso: Fix circular lock in iso_conn_big_sync") Cc: stable@vger.kernel.org Signed-off-by: Muhammad Bilal Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/iso.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 3abd8111dda83..7186e8d88c757 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -1589,6 +1589,7 @@ static void iso_conn_big_sync(struct sock *sk) { int err; struct hci_dev *hdev; + struct iso_conn *conn; bdaddr_t src, dst; u8 src_type; @@ -1611,8 +1612,17 @@ static void iso_conn_big_sync(struct sock *sk) hci_dev_lock(hdev); lock_sock(sk); + /* The socket lock was dropped for hci_get_route(), so the connection + * may have been torn down meanwhile: iso_chan_del() clears conn and + * the broadcast teardown path can clear conn->hcon on its own. Check + * both before dereferencing conn->hcon. + */ + conn = iso_pi(sk)->conn; + if (!conn || !conn->hcon) + goto unlock; + if (!test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags)) { - err = hci_conn_big_create_sync(hdev, iso_pi(sk)->conn->hcon, + err = hci_conn_big_create_sync(hdev, conn->hcon, &iso_pi(sk)->qos, iso_pi(sk)->sync_handle, iso_pi(sk)->bc_num_bis, @@ -1621,6 +1631,7 @@ static void iso_conn_big_sync(struct sock *sk) bt_dev_err(hdev, "hci_big_create_sync: %d", err); } +unlock: release_sock(sk); hci_dev_unlock(hdev); hci_dev_put(hdev); From e96fbac8d3a73b0bc165383c092a30628561d320 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Wed, 17 Jun 2026 23:36:13 +0800 Subject: [PATCH 0360/2758] Bluetooth: L2CAP: cancel pending_rx_work before taking conn->lock commit 2641a9e0a1dd4af2e21995470a21d55dd35e5203 upstream. l2cap_conn_del() takes conn->lock and then calls cancel_work_sync() for pending_rx_work. process_pending_rx() takes the same mutex, so teardown can deadlock against the worker it is flushing. This issue was found by our static analysis tool and then manually reviewed against the current tree. The grounded PoC kept the l2cap_conn_ready() -> queue_work(..., &conn->pending_rx_work) submit path, the l2cap_conn_del() -> cancel_work_sync(&conn->pending_rx_work) teardown path, and the process_pending_rx() -> mutex_lock(&conn->lock) worker edge. Lockdep reported: WARNING: possible circular locking dependency detected process_pending_rx+0x21/0x2a [vuln_msv] l2cap_conn_del.constprop.0+0x3f/0x4e [vuln_msv] *** DEADLOCK *** Cancel pending_rx_work before taking conn->lock, matching the existing lock-before-drain ordering used for the two delayed works in the same teardown path. The pending_rx queue is still purged after the work has been cancelled and conn->lock has been acquired. Fixes: 7ab56c3a6ecc ("Bluetooth: Fix deadlock in l2cap_conn_del()") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/l2cap_core.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 1fbd52165fb20..a0dd6bb47406f 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1778,19 +1778,13 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err) disable_delayed_work_sync(&conn->info_timer); disable_delayed_work_sync(&conn->id_addr_timer); + cancel_work_sync(&conn->pending_rx_work); + mutex_lock(&conn->lock); kfree_skb(conn->rx_skb); skb_queue_purge(&conn->pending_rx); - - /* We can not call flush_work(&conn->pending_rx_work) here since we - * might block if we are running on a worker from the same workqueue - * pending_rx_work is waiting on. - */ - if (work_pending(&conn->pending_rx_work)) - cancel_work_sync(&conn->pending_rx_work); - ida_destroy(&conn->tx_ida); l2cap_unregister_all_users(conn); From 6b47bdaacfd0045687880177e0987055d8f4765a Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Sun, 21 Jun 2026 00:56:35 +0500 Subject: [PATCH 0361/2758] Bluetooth: L2CAP: validate option length before reading conf opt value commit 687617555cedfb74c9e3cb85d759b908dcb17856 upstream. l2cap_get_conf_opt() derives the option length from the attacker-controlled opt->len field and immediately dereferences opt->val (as u8, get_unaligned_le16() or get_unaligned_le32(), or a raw pointer for the default case) before any caller has confirmed that opt->len bytes are present in the buffer. The callers (l2cap_parse_conf_req(), l2cap_parse_conf_rsp() and l2cap_conf_rfc_get()) only detect a malformed option afterwards, once the running length has gone negative, by which point the out-of-bounds read has already executed. An existing post-hoc length check keeps the garbage value from being consumed, so this is not a data leak in the current control flow. It is still a validate-after-use ordering bug: up to 4 bytes are read past the end of the buffer before it is known to contain them, and it is fragile to future changes in the callers. Fix it at the source. Pass the end of the buffer into l2cap_get_conf_opt() and refuse to touch opt->val unless the full option (header + value) fits. Each caller computes an end pointer once before the loop and checks the return value directly instead of inferring the error from a negative length. Fixes: 7c9cbd0b5e38 ("Bluetooth: Verify that l2cap_get_conf_opt provides large enough buffer") Cc: stable@vger.kernel.org Signed-off-by: Muhammad Bilal Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/l2cap_core.c | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index a0dd6bb47406f..32f0c8b73b17f 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3048,13 +3048,24 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, u8 code, return NULL; } -static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen, - unsigned long *val) +static inline int l2cap_get_conf_opt(void **ptr, void *end, int *type, + int *olen, unsigned long *val) { struct l2cap_conf_opt *opt = *ptr; int len; + /* opt->len is attacker-controlled. Validate that the full option + * (header + value) actually fits in the buffer before touching + * opt->val, otherwise the switch below reads past the end of the + * caller's buffer. + */ + if (end - *ptr < L2CAP_CONF_OPT_SIZE) + return -EINVAL; + len = L2CAP_CONF_OPT_SIZE + opt->len; + if (end - *ptr < len) + return -EINVAL; + *ptr += len; *type = opt->type; @@ -3426,6 +3437,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data void *ptr = rsp->data; void *endptr = data + data_size; void *req = chan->conf_req; + void *req_end = req + chan->conf_len; int len = chan->conf_len; int type, hint, olen; unsigned long val; @@ -3439,9 +3451,11 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data BT_DBG("chan %p", chan); while (len >= L2CAP_CONF_OPT_SIZE) { - len -= l2cap_get_conf_opt(&req, &type, &olen, &val); - if (len < 0) + int ret = l2cap_get_conf_opt(&req, req_end, &type, &olen, &val); + + if (ret < 0) break; + len -= ret; hint = type & L2CAP_CONF_HINT; type &= L2CAP_CONF_MASK; @@ -3669,6 +3683,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, struct l2cap_conf_req *req = data; void *ptr = req->data; void *endptr = data + size; + void *rsp_end = rsp + len; int type, olen; unsigned long val; struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC }; @@ -3677,9 +3692,11 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, BT_DBG("chan %p, rsp %p, len %d, req %p", chan, rsp, len, data); while (len >= L2CAP_CONF_OPT_SIZE) { - len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val); - if (len < 0) + int ret = l2cap_get_conf_opt(&rsp, rsp_end, &type, &olen, &val); + + if (ret < 0) break; + len -= ret; switch (type) { case L2CAP_CONF_MTU: @@ -3930,6 +3947,7 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len) { int type, olen; unsigned long val; + void *rsp_end = rsp + len; /* Use sane default values in case a misbehaving remote device * did not send an RFC or extended window size option. */ @@ -3948,9 +3966,11 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len) return; while (len >= L2CAP_CONF_OPT_SIZE) { - len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val); - if (len < 0) + int ret = l2cap_get_conf_opt(&rsp, rsp_end, &type, &olen, &val); + + if (ret < 0) break; + len -= ret; switch (type) { case L2CAP_CONF_RFC: From daf6246ab988fc8bdc82ad7c8d0b1c182d11b15f Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Thu, 4 Jun 2026 15:34:25 +0800 Subject: [PATCH 0362/2758] coresight: ultrasoc-smb: Fix OOB write in smb_sync_perf_buffer() commit 98495b5a4d77dd22e106f462b76e1093a55b29a7 upstream. When the SMB sink is used as a perf AUX sink, smb_update_buffer() calls smb_sync_perf_buffer() to copy hardware trace data into the perf AUX ring buffer pages. It derives pg_idx = head >> PAGE_SHIFT from @head, which is handle->head, and indexes dst_pages[pg_idx]. The pg_idx %= nr_pages normalization is only applied after the first loop iteration. This leaves the initial page index underived from the buffer size, which can result in an out-of-bounds write past dst_pages[] when head exceeds the AUX buffer size. Normalize head modulo the AUX buffer size before deriving the page index and offset, mirroring tmc_etr_sync_perf_buffer(). Fixes: 06f5c2926aaa ("drivers/coresight: Add UltraSoc System Memory Buffer driver") Reported-by: Yuhao Jiang Cc: stable@vger.kernel.org Signed-off-by: Junrui Luo Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/SYBPR01MB788156B3380A36835DB22290AF102@SYBPR01MB7881.ausprd01.prod.outlook.com Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/ultrasoc-smb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwtracing/coresight/ultrasoc-smb.c b/drivers/hwtracing/coresight/ultrasoc-smb.c index 5776f63468fa0..20a950b9dd4ff 100644 --- a/drivers/hwtracing/coresight/ultrasoc-smb.c +++ b/drivers/hwtracing/coresight/ultrasoc-smb.c @@ -337,6 +337,7 @@ static void smb_sync_perf_buffer(struct smb_drv_data *drvdata, unsigned long to_copy; long pg_idx, pg_offset; + head %= (unsigned long)buf->nr_pages << PAGE_SHIFT; pg_idx = head >> PAGE_SHIFT; pg_offset = head & (PAGE_SIZE - 1); From 0700f946659d0ab2352ec8a9b1c6fc74b13a27d7 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 17 May 2026 20:11:49 -0400 Subject: [PATCH 0363/2758] smb: client: resolve SWN tcon from live registrations commit ec457f9afe5ae9538bdcd58fd4cb442b9787e183 upstream. cifs_swn_notify() looks up a witness registration by id under cifs_swnreg_idr_mutex, drops the mutex, and then uses the registration's cached tcon pointer. That pointer is not a lifetime reference, and it is not a stable representative once cifs_get_swn_reg() lets multiple tcons for the same net/share name share one registration id. A same-share second mount can keep the cifs_swn_reg alive after the first tcon unregisters and is freed. The registration then still points at the freed first tcon, so taking tc_lock or incrementing tc_count through swnreg->tcon only moves the use-after-free earlier. Taking tc_lock while holding cifs_swnreg_idr_mutex also violates the documented CIFS lock order. Fix this by making the registration store only the stable witness identity: id, net name, share name, and notify flags. When a notify arrives, copy that identity under cifs_swnreg_idr_mutex, drop the mutex, then find and pin a live witness tcon that currently matches the net/share pair under the normal cifs_tcp_ses_lock -> tc_lock order. The notification path uses that pinned tcon directly and drops the reference when done. Registration and unregister messages now use the live tcon passed by the caller instead of a cached tcon in the registration. The final unregister send is folded into cifs_swn_unregister() while the registration is still protected by cifs_swnreg_idr_mutex. This removes the previous find/drop/reacquire raw-pointer window. The release path only removes the idr entry and frees the stable identity strings. This preserves the intended one-registration/many-tcon behavior: a registration id represents a net/share pair, and notify handling acts on a live representative selected at use time. It also preserves CLIENT_MOVE ordering for the representative tcon because the old-IP unregister is sent before cifs_swn_register() sends the new-IP register. Fixes: fed979a7e082 ("cifs: Set witness notification handler for messages from userspace daemon") Cc: stable@vger.kernel.org Signed-off-by: Michael Bommarito Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/cifs_swn.c | 314 ++++++++++++++++++++++++++++++++------- fs/smb/client/trace.h | 2 + 2 files changed, 262 insertions(+), 54 deletions(-) diff --git a/fs/smb/client/cifs_swn.c b/fs/smb/client/cifs_swn.c index 9753a432d0998..9951817d0d7ff 100644 --- a/fs/smb/client/cifs_swn.c +++ b/fs/smb/client/cifs_swn.c @@ -28,10 +28,54 @@ struct cifs_swn_reg { bool net_name_notify; bool share_name_notify; bool ip_notify; +}; - struct cifs_tcon *tcon; +struct cifs_swn_reg_info { + int id; + unsigned int ref_count; + const char *net_name; + const char *share_name; + bool net_name_notify; + bool share_name_notify; + bool ip_notify; }; +static void cifs_swn_snapshot_reg(struct cifs_swn_reg *swnreg, + struct cifs_swn_reg_info *info) +{ + info->id = swnreg->id; + info->ref_count = kref_read(&swnreg->ref_count); + info->net_name = swnreg->net_name; + info->share_name = swnreg->share_name; + info->net_name_notify = swnreg->net_name_notify; + info->share_name_notify = swnreg->share_name_notify; + info->ip_notify = swnreg->ip_notify; +} + +static int cifs_swn_dup_reg(struct cifs_swn_reg *swnreg, + struct cifs_swn_reg_info *info) +{ + cifs_swn_snapshot_reg(swnreg, info); + + info->net_name = kstrdup(swnreg->net_name, GFP_KERNEL); + if (!info->net_name) + return -ENOMEM; + + info->share_name = kstrdup(swnreg->share_name, GFP_KERNEL); + if (!info->share_name) { + kfree(info->net_name); + return -ENOMEM; + } + + return 0; +} + +static void cifs_swn_free_reg_info(struct cifs_swn_reg_info *info) +{ + kfree(info->net_name); + kfree(info->share_name); +} + static int cifs_swn_auth_info_krb(struct cifs_tcon *tcon, struct sk_buff *skb) { int ret; @@ -73,7 +117,8 @@ static int cifs_swn_auth_info_ntlm(struct cifs_tcon *tcon, struct sk_buff *skb) * The authentication information to connect to the witness service is bundled * into the message. */ -static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg) +static int cifs_swn_send_register_message(struct cifs_swn_reg_info *swnreg, + struct cifs_tcon *tcon) { struct sk_buff *skb; struct genlmsghdr *hdr; @@ -109,10 +154,10 @@ static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg) * told to switch to it (client move message). In these cases we unregister from the * server address and register to the new address when we receive the notification. */ - if (swnreg->tcon->ses->server->use_swn_dstaddr) - addr = &swnreg->tcon->ses->server->swn_dstaddr; + if (tcon->ses->server->use_swn_dstaddr) + addr = &tcon->ses->server->swn_dstaddr; else - addr = &swnreg->tcon->ses->server->dstaddr; + addr = &tcon->ses->server->dstaddr; ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage), addr); if (ret < 0) @@ -136,10 +181,10 @@ static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg) goto nlmsg_fail; } - authtype = cifs_select_sectype(swnreg->tcon->ses->server, swnreg->tcon->ses->sectype); + authtype = cifs_select_sectype(tcon->ses->server, tcon->ses->sectype); switch (authtype) { case Kerberos: - ret = cifs_swn_auth_info_krb(swnreg->tcon, skb); + ret = cifs_swn_auth_info_krb(tcon, skb); if (ret < 0) { cifs_dbg(VFS, "%s: Failed to get kerberos auth info: %d\n", __func__, ret); goto nlmsg_fail; @@ -147,7 +192,7 @@ static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg) break; case NTLMv2: case RawNTLMSSP: - ret = cifs_swn_auth_info_ntlm(swnreg->tcon, skb); + ret = cifs_swn_auth_info_ntlm(tcon, skb); if (ret < 0) { cifs_dbg(VFS, "%s: Failed to get NTLM auth info: %d\n", __func__, ret); goto nlmsg_fail; @@ -176,7 +221,8 @@ static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg) /* * Sends an uregister message to the userspace daemon based on the registration */ -static int cifs_swn_send_unregister_message(struct cifs_swn_reg *swnreg) +static int cifs_swn_send_unregister_message(struct cifs_swn_reg_info *swnreg, + struct cifs_tcon *tcon) { struct sk_buff *skb; struct genlmsghdr *hdr; @@ -205,7 +251,7 @@ static int cifs_swn_send_unregister_message(struct cifs_swn_reg *swnreg) goto nlmsg_fail; ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage), - &swnreg->tcon->ses->server->dstaddr); + &tcon->ses->server->dstaddr); if (ret < 0) goto nlmsg_fail; @@ -241,6 +287,88 @@ static int cifs_swn_send_unregister_message(struct cifs_swn_reg *swnreg) return ret; } +/* + * Allocation-free mirror of extract_hostname() + extract_sharename() from + * fs/smb/client/unc.c. Those helpers kmalloc(GFP_KERNEL); this runs under + * cifs_tcp_ses_lock and tcon->tc_lock, both spinlocks, so we mirror their + * parsing in place against the caller's stable net_name/share_name strings. + * Keep in sync with unc.c. + */ +static bool cifs_swn_tcon_matches(struct cifs_tcon *tcon, + const char *net_name, + const char *share_name) +{ + const char *unc = tcon->tree_name; + const char *host, *share, *delim; + size_t host_len, share_len; + + if (!tcon->use_witness) + return false; + + /* extract_hostname: require strlen(unc) >= 3 */ + if (strnlen(unc, 3) < 3) + return false; + /* extract_hostname: skip all leading '\' characters */ + for (host = unc; *host == '\\'; host++) + ; + if (!*host) + return false; + delim = strchr(host, '\\'); + if (!delim) + return false; + host_len = delim - host; + if (strlen(net_name) != host_len || + strncasecmp(host, net_name, host_len)) + return false; + + /* extract_sharename: start at unc + 2, then first '\' onward */ + share = unc + 2; + delim = strchr(share, '\\'); + if (!delim) + return false; + share = delim + 1; + share_len = strlen(share); + + return strlen(share_name) == share_len && + !strncasecmp(share, share_name, share_len); +} + +/* + * One SWN registration id represents one net/share name pair. Multiple + * mounted tcons can therefore share the id. Pick a live representative at + * use time instead of caching the first tcon pointer in the registration. + */ +static struct cifs_tcon *cifs_swn_get_tcon(struct cifs_swn_reg_info *swnreg) +{ + struct TCP_Server_Info *server; + struct cifs_ses *ses; + struct cifs_tcon *tcon; + + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { + list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + spin_lock(&tcon->tc_lock); + if (tcon->status == TID_EXITING || + !cifs_swn_tcon_matches(tcon, swnreg->net_name, + swnreg->share_name)) { + spin_unlock(&tcon->tc_lock); + continue; + } + ++tcon->tc_count; + trace_smb3_tcon_ref(tcon->debug_id, + tcon->tc_count, + netfs_trace_tcon_ref_get_swn_notify); + spin_unlock(&tcon->tc_lock); + spin_unlock(&cifs_tcp_ses_lock); + return tcon; + } + } + } + spin_unlock(&cifs_tcp_ses_lock); + return NULL; +} + /* * Try to find a matching registration for the tcon's server name and share name. * Calls to this function must be protected by cifs_swnreg_idr_mutex. @@ -347,8 +475,6 @@ static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon) reg->net_name_notify = true; reg->share_name_notify = true; reg->ip_notify = (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT); - - reg->tcon = tcon; unlock: mutex_unlock(&cifs_swnreg_idr_mutex); @@ -368,11 +494,6 @@ static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon) static void cifs_swn_reg_release(struct kref *ref) { struct cifs_swn_reg *swnreg = container_of(ref, struct cifs_swn_reg, ref_count); - int ret; - - ret = cifs_swn_send_unregister_message(swnreg); - if (ret < 0) - cifs_dbg(VFS, "%s: Failed to send unregister message: %d\n", __func__, ret); idr_remove(&cifs_swnreg_idr, swnreg->id); kfree(swnreg->net_name); @@ -380,23 +501,33 @@ static void cifs_swn_reg_release(struct kref *ref) kfree(swnreg); } -static void cifs_put_swn_reg(struct cifs_swn_reg *swnreg) +static void cifs_put_swn_reg_locked(struct cifs_swn_reg *swnreg, + struct cifs_tcon *tcon) { - mutex_lock(&cifs_swnreg_idr_mutex); + if (kref_read(&swnreg->ref_count) == 1) { + struct cifs_swn_reg_info swnreg_info; + int ret; + + cifs_swn_snapshot_reg(swnreg, &swnreg_info); + ret = cifs_swn_send_unregister_message(&swnreg_info, tcon); + if (ret < 0) + cifs_dbg(VFS, "%s: Failed to send unregister message: %d\n", + __func__, ret); + } + kref_put(&swnreg->ref_count, cifs_swn_reg_release); - mutex_unlock(&cifs_swnreg_idr_mutex); } -static int cifs_swn_resource_state_changed(struct cifs_swn_reg *swnreg, const char *name, int state) +static int cifs_swn_resource_state_changed(struct cifs_tcon *tcon, const char *name, int state) { switch (state) { case CIFS_SWN_RESOURCE_STATE_UNAVAILABLE: cifs_dbg(FYI, "%s: resource name '%s' become unavailable\n", __func__, name); - cifs_signal_cifsd_for_reconnect(swnreg->tcon->ses->server, true); + cifs_signal_cifsd_for_reconnect(tcon->ses->server, true); break; case CIFS_SWN_RESOURCE_STATE_AVAILABLE: cifs_dbg(FYI, "%s: resource name '%s' become available\n", __func__, name); - cifs_signal_cifsd_for_reconnect(swnreg->tcon->ses->server, true); + cifs_signal_cifsd_for_reconnect(tcon->ses->server, true); break; case CIFS_SWN_RESOURCE_STATE_UNKNOWN: cifs_dbg(FYI, "%s: resource name '%s' changed to unknown state\n", __func__, name); @@ -502,7 +633,7 @@ static int cifs_swn_reconnect(struct cifs_tcon *tcon, struct sockaddr_storage *a return ret; } -static int cifs_swn_client_move(struct cifs_swn_reg *swnreg, struct sockaddr_storage *addr) +static int cifs_swn_client_move(struct cifs_tcon *tcon, struct sockaddr_storage *addr) { struct sockaddr_in *ipv4 = (struct sockaddr_in *)addr; struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)addr; @@ -512,14 +643,17 @@ static int cifs_swn_client_move(struct cifs_swn_reg *swnreg, struct sockaddr_sto else if (addr->ss_family == AF_INET6) cifs_dbg(FYI, "%s: move to %pI6\n", __func__, &ipv6->sin6_addr); - return cifs_swn_reconnect(swnreg->tcon, addr); + return cifs_swn_reconnect(tcon, addr); } int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info) { struct cifs_swn_reg *swnreg; + struct cifs_swn_reg_info swnreg_info; + struct cifs_tcon *tcon; char name[256]; int type; + int ret = 0; if (info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]) { int swnreg_id; @@ -527,21 +661,34 @@ int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info) swnreg_id = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]); mutex_lock(&cifs_swnreg_idr_mutex); swnreg = idr_find(&cifs_swnreg_idr, swnreg_id); - mutex_unlock(&cifs_swnreg_idr_mutex); if (swnreg == NULL) { + mutex_unlock(&cifs_swnreg_idr_mutex); cifs_dbg(FYI, "%s: registration id %d not found\n", __func__, swnreg_id); return -EINVAL; } + ret = cifs_swn_dup_reg(swnreg, &swnreg_info); + mutex_unlock(&cifs_swnreg_idr_mutex); + if (ret) + return ret; } else { cifs_dbg(FYI, "%s: missing registration id attribute\n", __func__); return -EINVAL; } + tcon = cifs_swn_get_tcon(&swnreg_info); + if (!tcon) { + cifs_dbg(FYI, "%s: registration id %d has no live tcon\n", + __func__, swnreg_info.id); + ret = -ENODEV; + goto free_info; + } + if (info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]) { type = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]); } else { cifs_dbg(FYI, "%s: missing notification type attribute\n", __func__); - return -EINVAL; + ret = -EINVAL; + goto out; } switch (type) { @@ -553,15 +700,18 @@ int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info) sizeof(name)); } else { cifs_dbg(FYI, "%s: missing resource name attribute\n", __func__); - return -EINVAL; + ret = -EINVAL; + goto out; } if (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]) { state = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]); } else { cifs_dbg(FYI, "%s: missing resource state attribute\n", __func__); - return -EINVAL; + ret = -EINVAL; + goto out; } - return cifs_swn_resource_state_changed(swnreg, name, state); + ret = cifs_swn_resource_state_changed(tcon, name, state); + break; } case CIFS_SWN_NOTIFICATION_CLIENT_MOVE: { struct sockaddr_storage addr; @@ -570,28 +720,36 @@ int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info) nla_memcpy(&addr, info->attrs[CIFS_GENL_ATTR_SWN_IP], sizeof(addr)); } else { cifs_dbg(FYI, "%s: missing IP address attribute\n", __func__); - return -EINVAL; + ret = -EINVAL; + goto out; } - return cifs_swn_client_move(swnreg, &addr); + ret = cifs_swn_client_move(tcon, &addr); + break; } default: cifs_dbg(FYI, "%s: unknown notification type %d\n", __func__, type); break; } - return 0; +out: + cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_swn_notify); +free_info: + cifs_swn_free_reg_info(&swnreg_info); + return ret; } int cifs_swn_register(struct cifs_tcon *tcon) { struct cifs_swn_reg *swnreg; + struct cifs_swn_reg_info swnreg_info; int ret; swnreg = cifs_get_swn_reg(tcon); if (IS_ERR(swnreg)) return PTR_ERR(swnreg); - ret = cifs_swn_send_register_message(swnreg); + cifs_swn_snapshot_reg(swnreg, &swnreg_info); + ret = cifs_swn_send_register_message(&swnreg_info, tcon); if (ret < 0) { cifs_dbg(VFS, "%s: Failed to send swn register message: %d\n", __func__, ret); /* Do not put the swnreg or return error, the echo task will retry */ @@ -612,35 +770,68 @@ int cifs_swn_unregister(struct cifs_tcon *tcon) return PTR_ERR(swnreg); } + cifs_put_swn_reg_locked(swnreg, tcon); mutex_unlock(&cifs_swnreg_idr_mutex); - cifs_put_swn_reg(swnreg); - return 0; } -void cifs_swn_dump(struct seq_file *m) +/* + * Snapshot one registration under cifs_swnreg_idr_mutex and return. Callers + * intentionally do the per-registration network/genlmsg work without the + * mutex held, both to keep the critical section short and to avoid nesting + * cifs_swnreg_idr_mutex inside the higher tc_lock when a live tcon is then + * pinned for the send. + */ +static int cifs_swn_get_next_reg_info(int *id, struct cifs_swn_reg_info *info) { struct cifs_swn_reg *swnreg; + int ret = 0; + + mutex_lock(&cifs_swnreg_idr_mutex); + swnreg = idr_get_next(&cifs_swnreg_idr, id); + if (swnreg) { + ret = cifs_swn_dup_reg(swnreg, info); + if (!ret) { + *id = swnreg->id + 1; + ret = 1; + } + } + mutex_unlock(&cifs_swnreg_idr_mutex); + + return ret; +} + +void cifs_swn_dump(struct seq_file *m) +{ + struct cifs_swn_reg_info swnreg_info; + struct cifs_tcon *tcon; struct sockaddr_in *sa; struct sockaddr_in6 *sa6; - int id; + int id = 0; + int ret; seq_puts(m, "Witness registrations:"); - mutex_lock(&cifs_swnreg_idr_mutex); - idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) { + while ((ret = cifs_swn_get_next_reg_info(&id, &swnreg_info)) > 0) { seq_printf(m, "\nId: %u Refs: %u Network name: '%s'%s Share name: '%s'%s Ip address: ", - id, kref_read(&swnreg->ref_count), - swnreg->net_name, swnreg->net_name_notify ? "(y)" : "(n)", - swnreg->share_name, swnreg->share_name_notify ? "(y)" : "(n)"); - switch (swnreg->tcon->ses->server->dstaddr.ss_family) { + swnreg_info.id, swnreg_info.ref_count, + swnreg_info.net_name, swnreg_info.net_name_notify ? "(y)" : "(n)", + swnreg_info.share_name, swnreg_info.share_name_notify ? "(y)" : "(n)"); + + tcon = cifs_swn_get_tcon(&swnreg_info); + if (!tcon) { + seq_puts(m, "(no live tcon)"); + goto next; + } + + switch (tcon->ses->server->dstaddr.ss_family) { case AF_INET: - sa = (struct sockaddr_in *) &swnreg->tcon->ses->server->dstaddr; + sa = (struct sockaddr_in *)&tcon->ses->server->dstaddr; seq_printf(m, "%pI4", &sa->sin_addr.s_addr); break; case AF_INET6: - sa6 = (struct sockaddr_in6 *) &swnreg->tcon->ses->server->dstaddr; + sa6 = (struct sockaddr_in6 *)&tcon->ses->server->dstaddr; seq_printf(m, "%pI6", &sa6->sin6_addr.s6_addr); if (sa6->sin6_scope_id) seq_printf(m, "%%%u", sa6->sin6_scope_id); @@ -648,23 +839,38 @@ void cifs_swn_dump(struct seq_file *m) default: seq_puts(m, "(unknown)"); } - seq_printf(m, "%s", swnreg->ip_notify ? "(y)" : "(n)"); + cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_swn_notify); +next: + seq_printf(m, "%s", swnreg_info.ip_notify ? "(y)" : "(n)"); + cifs_swn_free_reg_info(&swnreg_info); } - mutex_unlock(&cifs_swnreg_idr_mutex); + if (ret < 0) + seq_printf(m, "\nFailed to snapshot witness registration: %d", ret); seq_puts(m, "\n"); } void cifs_swn_check(void) { - struct cifs_swn_reg *swnreg; - int id; + struct cifs_swn_reg_info swnreg_info; + struct cifs_tcon *tcon; + int id = 0; int ret; - mutex_lock(&cifs_swnreg_idr_mutex); - idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) { - ret = cifs_swn_send_register_message(swnreg); + while ((ret = cifs_swn_get_next_reg_info(&id, &swnreg_info)) > 0) { + tcon = cifs_swn_get_tcon(&swnreg_info); + if (!tcon) { + cifs_dbg(FYI, "%s: registration id %d has no live tcon\n", + __func__, swnreg_info.id); + goto free_info; + } + + ret = cifs_swn_send_register_message(&swnreg_info, tcon); if (ret < 0) cifs_dbg(FYI, "%s: Failed to send register message: %d\n", __func__, ret); + cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_swn_notify); +free_info: + cifs_swn_free_reg_info(&swnreg_info); } - mutex_unlock(&cifs_swnreg_idr_mutex); + if (ret < 0) + cifs_dbg(FYI, "%s: Failed to snapshot registration: %d\n", __func__, ret); } diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h index b99ec5a417fad..5b21ad3c15fb5 100644 --- a/fs/smb/client/trace.h +++ b/fs/smb/client/trace.h @@ -181,6 +181,7 @@ EM(netfs_trace_tcon_ref_get_find, "GET Find ") \ EM(netfs_trace_tcon_ref_get_find_sess_tcon, "GET FndSes") \ EM(netfs_trace_tcon_ref_get_reconnect_server, "GET Reconn") \ + EM(netfs_trace_tcon_ref_get_swn_notify, "GET SwnNot") \ EM(netfs_trace_tcon_ref_new, "NEW ") \ EM(netfs_trace_tcon_ref_new_ipc, "NEW Ipc ") \ EM(netfs_trace_tcon_ref_new_reconnect_server, "NEW Reconn") \ @@ -192,6 +193,7 @@ EM(netfs_trace_tcon_ref_put_mnt_ctx, "PUT MntCtx") \ EM(netfs_trace_tcon_ref_put_dfs_refer, "PUT DfsRfr") \ EM(netfs_trace_tcon_ref_put_reconnect_server, "PUT Reconn") \ + EM(netfs_trace_tcon_ref_put_swn_notify, "PUT SwnNot") \ EM(netfs_trace_tcon_ref_put_tlink, "PUT Tlink ") \ EM(netfs_trace_tcon_ref_see_cancelled_close, "SEE Cn-Cls") \ EM(netfs_trace_tcon_ref_see_fscache_collision, "SEE FV-CO!") \ From a1cc432cb0b0a1f74f98a0db3b94ca880c7947ac Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 11 Jun 2026 10:35:28 +0300 Subject: [PATCH 0364/2758] smb/client: Fix error code in smb2_aead_req_alloc() commit 61f28012e5650c619223decdb7970e0d3162e949 upstream. The "*num_sgs" variable is a u32 so "ERR_PTR(*num_sgs)" doesn't work. We would have to do something similar to the previous line where it's cast to int and then long. However, it's simpler to store the return in an int ret variable. This bug would eventually result in a crash when dereference the invalid error pointer. Fixes: d08089f649a0 ("cifs: Change the I/O paths to use an iterator rather than a page list") Cc: stable@kernel.org Signed-off-by: Dan Carpenter Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2ops.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index d4875f9532b4d..1987332192d63 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -4359,11 +4359,13 @@ static void *smb2_aead_req_alloc(struct crypto_aead *tfm, const struct smb_rqst unsigned int req_size = sizeof(**req) + crypto_aead_reqsize(tfm); unsigned int iv_size = crypto_aead_ivsize(tfm); unsigned int len; + int ret; u8 *p; - *num_sgs = cifs_get_num_sgs(rqst, num_rqst, sig); - if (IS_ERR_VALUE((long)(int)*num_sgs)) - return ERR_PTR(*num_sgs); + ret = cifs_get_num_sgs(rqst, num_rqst, sig); + if (ret < 0) + return ERR_PTR(ret); + *num_sgs = ret; len = iv_size; len += crypto_aead_alignmask(tfm) & ~(crypto_tfm_ctx_alignment() - 1); From 8c9a4f1327eb71efbf14842e7b8a6d965077eb67 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 7 Jun 2026 20:15:51 +0900 Subject: [PATCH 0365/2758] ksmbd: prevent path traversal bypass by restricting caseless retry commit 54bab9ba5a9f156ffa9324fcbe5a356fd0242f95 upstream. ksmbd_vfs_path_lookup() enforces LOOKUP_BENEATH to restrict path resolution within the share root. When a crafted path attempts to escape the share boundary using parent-directory components ('..'), vfs_path_parent_lookup() detects this and immediately fails, returning -EXDEV. However, a bug exists in __ksmbd_vfs_kern_path() under caseless mode. The function fails to intercept the -EXDEV error and erroneously falls through to the caseless retry logic, which is intended only for genuinely missing files. During this retry process, the path is reconstructed, leading to an unintended LOOKUP_BENEATH bypass that allows write-capable users to create zero-length files or directories outside the exported share. Fix this by ensuring that the execution only proceeds to the caseless lookup retry when the error is specifically -ENOENT. Any other errors, such as -EXDEV from a path traversal attempt, must be returned immediately. Cc: stable@vger.kernel.org Reported-by: Y s65 Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/vfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index d08973b288e58..e020e59d1d5d2 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -1149,7 +1149,7 @@ int __ksmbd_vfs_kern_path(struct ksmbd_work *work, char *filepath, retry: err = ksmbd_vfs_path_lookup(share_conf, filepath, flags, path, for_remove); - if (!err || !caseless) + if (!err || err != -ENOENT || !caseless) return err; path_len = strlen(filepath); From c917e4522d251071dde9871b9142d8ea1186ebfe Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Fri, 12 Jun 2026 07:15:38 +0900 Subject: [PATCH 0366/2758] ksmbd: add permission checks for FSCTL_DUPLICATE_EXTENTS_TO_FILE commit 388e4139db27a9e3612c9d356b826f5b1ff6a9e3 upstream. The FSCTL_DUPLICATE_EXTENTS_TO_FILE arm of smb2_ioctl() overwrites the destination file's data via vfs_clone_file_range() with neither the share-level KSMBD_TREE_CONN_FLAG_WRITABLE check nor a per-handle fp->daccess check that the other write-bearing arms carry. A client can overwrite destination data on a read-only share, or from a handle opened with only FILE_WRITE_ATTRIBUTES (which still yields an FMODE_WRITE filp). FILE_WRITE_ATTRIBUTES-only destination handle overwrote the file's data via the clone. Add both checks, matching the FSCTL_SET_SPARSE permission fix; require FILE_WRITE_DATA since this writes data. Cc: stable@vger.kernel.org Signed-off-by: Gil Portnoy Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smb2pdu.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 53c0883d1bb9a..438e45b4e22de 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -8562,6 +8562,17 @@ int smb2_ioctl(struct ksmbd_work *work) goto dup_ext_out; } + if (!test_tree_conn_flag(work->tcon, + KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ret = -EACCES; + goto dup_ext_out; + } + + if (!(fp_out->daccess & FILE_WRITE_DATA_LE)) { + ret = -EACCES; + goto dup_ext_out; + } + src_off = le64_to_cpu(dup_ext->SourceFileOffset); dst_off = le64_to_cpu(dup_ext->TargetFileOffset); length = le64_to_cpu(dup_ext->ByteCount); From deffa929086d7902e30918adf3dd27ccfe9c08b1 Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Wed, 10 Jun 2026 20:07:04 +0900 Subject: [PATCH 0367/2758] ksmbd: add a permission check for FSCTL_SET_ZERO_DATA commit 3320ba068198adc144c89d6661b805acce01735b upstream. FSCTL_SET_ZERO_DATA in smb2_ioctl() destroys file data via ksmbd_vfs_zero_data() -> vfs_fallocate(PUNCH_HOLE/ZERO_RANGE) after checking only the share-level KSMBD_TREE_CONN_FLAG_WRITABLE, with no per-handle access check. A handle opened with only FILE_WRITE_ATTRIBUTES still yields an FMODE_WRITE filp (FILE_WRITE_ATTRIBUTES is part of FILE_WRITE_DESIRE_ACCESS_LE, so smb2_create_open_flags() opens it O_WRONLY), so the vfs_fallocate FMODE_WRITE check does not stop it; only the missing fp->daccess gate would. Reproduced on mainline 7.1-rc7 with KASAN by an authenticated SMB client: a FILE_WRITE_ATTRIBUTES-only handle zeroed 4096 bytes of file data it had no FILE_WRITE_DATA right to (6/6; a FILE_READ_DATA-only handle was correctly denied). This is the unfixed sibling of commit cc57232cae23 ("ksmbd: fix FSCTL permission bypass by adding a permission check for FSCTL_SET_SPARSE"). Because SET_ZERO_DATA writes data (not an attribute), require FILE_WRITE_DATA. Cc: stable@vger.kernel.org Signed-off-by: Gil Portnoy Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smb2pdu.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 438e45b4e22de..b1dc0bb080f2a 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -8487,6 +8487,12 @@ int smb2_ioctl(struct ksmbd_work *work) goto out; } + if (!(fp->daccess & FILE_WRITE_DATA_LE)) { + ksmbd_fd_put(work, fp); + ret = -EACCES; + goto out; + } + ret = ksmbd_vfs_zero_data(work, fp, off, len); ksmbd_fd_put(work, fp); if (ret < 0) From fd22b039a5a05bc1d6818e9dcd1001fb432a829d Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 12 Jun 2026 08:00:00 +0900 Subject: [PATCH 0368/2758] ksmbd: serialize QUERY_DIRECTORY requests per file commit be6d26bf27499977c746abc163659915082348d8 upstream. smb2_query_dir() stores a pointer to its stack-allocated private data in the ksmbd_file readdir_data. Concurrent QUERY_DIRECTORY requests using the same file handle can overwrite this pointer while an iterate_dir() callback is still using it, resulting in a stack use-after-free. Add a per-file mutex and hold it while accessing the shared directory enumeration state. The lock covers scan restart, dot entry state, readdir_data setup and iteration, and response construction. This prevents another request from replacing readdir_data.private before the current request has finished using it and also serializes the shared file position. Cc: stable@vger.kernel.org Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-30527 Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smb2pdu.c | 4 ++++ fs/smb/server/vfs_cache.c | 1 + fs/smb/server/vfs_cache.h | 2 ++ 3 files changed, 7 insertions(+) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index b1dc0bb080f2a..46594ae47220f 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -4470,6 +4470,8 @@ int smb2_query_dir(struct ksmbd_work *work) ksmbd_debug(SMB, "Search pattern is %s\n", srch_ptr); } + mutex_lock(&dir_fp->readdir_lock); + if (srch_flag & SMB2_REOPEN || srch_flag & SMB2_RESTART_SCANS) { ksmbd_debug(SMB, "Restart directory scan\n"); generic_file_llseek(dir_fp->filp, 0, SEEK_SET); @@ -4574,6 +4576,7 @@ int smb2_query_dir(struct ksmbd_work *work) goto err_out; } + mutex_unlock(&dir_fp->readdir_lock); kfree(srch_ptr); ksmbd_fd_put(work, dir_fp); ksmbd_revert_fsids(work); @@ -4581,6 +4584,7 @@ int smb2_query_dir(struct ksmbd_work *work) err_out: pr_err("error while processing smb2 query dir rc = %d\n", rc); + mutex_unlock(&dir_fp->readdir_lock); kfree(srch_ptr); err_out2: diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index ba3355a6057a2..4daccc77f9eca 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -790,6 +790,7 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) INIT_LIST_HEAD(&fp->node); INIT_LIST_HEAD(&fp->lock_list); spin_lock_init(&fp->f_lock); + mutex_init(&fp->readdir_lock); atomic_set(&fp->refcount, 1); fp->filp = filp; diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h index e6871266a94ba..7d547e1a74f7f 100644 --- a/fs/smb/server/vfs_cache.h +++ b/fs/smb/server/vfs_cache.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -113,6 +114,7 @@ struct ksmbd_file { /* if ls is happening on directory, below is valid*/ struct ksmbd_readdir_data readdir_data; + struct mutex readdir_lock; int dot_dotdot[2]; unsigned int f_state; bool reserve_lease_break; From 5c75275c0fc9a2deb0d8f5604edcb16f288171c8 Mon Sep 17 00:00:00 2001 From: Davide Ornaghi Date: Sat, 6 Jun 2026 16:11:04 +0900 Subject: [PATCH 0369/2758] ksmbd: fix UAF of struct file_lock in SMB2_LOCK deferred-lock cancellation commit d20d1c8ba5765d1d12eefc0aee6385ab3f240e1e upstream. When a blocking byte-range lock request is deferred in the FILE_LOCK_DEFERRED path, ksmbd registers the asynchronous work into the connection's async_requests list via setup_async_work(). The cancel callback smb2_remove_blocked_lock() holds a reference to the flock. If the lock waiter is subsequently woken up but the work state is no longer KSMBD_WORK_ACTIVE (e.g., due to a concurrent cancellation), the cleanup path calls locks_free_lock(flock) without dequeuing the work from the async_requests list. Concurrently, smb2_cancel() walks the list under conn->request_lock and invokes the cancel callback, which then dereferences the already freed 'flock'. This leads to a slab-use-after-free inside __wake_up_common. Fix this by restructuring the cleanup logic after the worker returns from ksmbd_vfs_posix_lock_wait(). Move list_del(&smb_lock->llist) and release_async_work(work) to the top of the cleanup block. This guarantees that the async work is completely dequeued and serialized under conn->request_lock before locks_free_lock(flock) is called, rendering the flock unreachable for any concurrent smb2_cancel(). Cc: stable@vger.kernel.org Signed-off-by: Davide Ornaghi Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smb2pdu.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 46594ae47220f..b628af26dc8b5 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -7746,29 +7746,27 @@ int smb2_lock(struct ksmbd_work *work) list_del(&work->fp_entry); spin_unlock(&fp->f_lock); - if (work->state != KSMBD_WORK_ACTIVE) { - list_del(&smb_lock->llist); - locks_free_lock(flock); + list_del(&smb_lock->llist); + release_async_work(work); - if (work->state == KSMBD_WORK_CANCELLED) { - rsp->hdr.Status = - STATUS_CANCELLED; - kfree(smb_lock); - smb2_send_interim_resp(work, - STATUS_CANCELLED); - work->send_no_response = 1; - goto out; - } + if (work->state == KSMBD_WORK_ACTIVE) + goto retry; + + locks_free_lock(flock); - rsp->hdr.Status = - STATUS_RANGE_NOT_LOCKED; + if (work->state == KSMBD_WORK_CANCELLED) { + rsp->hdr.Status = STATUS_CANCELLED; kfree(smb_lock); - goto out2; + smb2_send_interim_resp(work, + STATUS_CANCELLED); + work->send_no_response = 1; + goto out; } - list_del(&smb_lock->llist); - release_async_work(work); - goto retry; + rsp->hdr.Status = + STATUS_RANGE_NOT_LOCKED; + kfree(smb_lock); + goto out2; } else if (!rc) { list_add(&smb_lock->llist, &rollback_list); spin_lock(&work->conn->llist_lock); From a10942af27832c2761d020863a46e79bebe0567d Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 13 Jun 2026 22:00:02 +0900 Subject: [PATCH 0370/2758] ksmbd: require source read access for duplicate extents commit cedff600f1642aa982178503552f0d007bc829c8 upstream. FSCTL_DUPLICATE_EXTENTS_TO_FILE passes the source file directly to vfs_clone_file_range() or vfs_copy_file_range() without checking the SMB access mask granted to the source handle. A handle opened with attribute access can consequently be used to copy file contents into an attacker-readable destination. Require FILE_READ_DATA on the source handle before either VFS operation, matching other ksmbd data-copy paths. Cc: stable@vger.kernel.org Reported-by: Musaab Khan Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smb2pdu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index b628af26dc8b5..043b69e2eece5 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -8580,6 +8580,10 @@ int smb2_ioctl(struct ksmbd_work *work) ret = -EACCES; goto dup_ext_out; } + if (!(fp_in->daccess & FILE_READ_DATA_LE)) { + ret = -EACCES; + goto dup_ext_out; + } src_off = le64_to_cpu(dup_ext->SourceFileOffset); dst_off = le64_to_cpu(dup_ext->TargetFileOffset); From aae600cdaffc6d9ce97645f129799a103a97d06d Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Tue, 9 Jun 2026 00:00:00 +0000 Subject: [PATCH 0371/2758] ksmbd: add a WRITE_DAC/WRITE_OWNER check to SMB2 SET_INFO SECURITY MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 44df157a1183a7f746caa970c169255da5ac61f8 upstream. commit cc57232cae23 ("ksmbd: fix FSCTL permission bypass by adding a permission check for FSCTL_SET_SPARSE") added a fp->daccess gate to fsctl_set_sparse and noted that "similar handle-level checks exist in other functions but are missing here." The SMB2 SET_INFO SECURITY arm is one of the missing ones, and the most security-relevant: smb2_set_info_sec() calls set_info_sec() with no per-handle access check. set_info_sec() (fs/smb/server/smbacl.c) re-permissions the file: it rewrites owner/group/mode via notify_change(), rewrites the POSIX ACL via set_posix_acl(), and on KSMBD_SHARE_FLAG_ACL_XATTR shares removes and rewrites the Windows security descriptor via ksmbd_vfs_set_sd_xattr(). Every other persistent-mutation arm of the sibling handler smb2_set_info_file() checks fp->daccess first (FILE_WRITE_DATA / FILE_DELETE / FILE_WRITE_EA / FILE_WRITE_ATTRIBUTES); the SECURITY arm — which mutates the access control itself — is the only one with no gate. A client can therefore open a handle with FILE_WRITE_ATTRIBUTES only (no FILE_WRITE_DAC / FILE_WRITE_OWNER) and use SMB2_SET_INFO with InfoType SMB2_O_INFO_SECURITY to rewrite the file's DACL and owner, granting itself access the handle's daccess never carried. Unlike the FSCTL data arms this is a metadata/xattr operation, so there is no FMODE_WRITE VFS backstop — the missing fp->daccess check is the entire gate. Setting a security descriptor is the WRITE_DAC / WRITE_OWNER operation, so require at least one of those on the handle before re-permissioning the file. -EACCES is mapped to STATUS_ACCESS_DENIED by smb2_set_info(). Cc: stable@vger.kernel.org Signed-off-by: Gil Portnoy Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smb2pdu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 043b69e2eece5..77b6e25978dcb 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -6615,6 +6615,9 @@ static int smb2_set_info_sec(struct ksmbd_file *fp, int addition_info, fp->saccess |= FILE_SHARE_DELETE_LE; + if (!(fp->daccess & (FILE_WRITE_DAC_LE | FILE_WRITE_OWNER_LE))) + return -EACCES; + return set_info_sec(fp->conn, fp->tcon, &fp->filp->f_path, pntsd, buf_len, false, true); } From 8cc9ec711f5255167247a9ab6a7179b787426ed7 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 13 Jun 2026 22:00:01 +0900 Subject: [PATCH 0372/2758] ksmbd: run set info with opener credentials commit b383bcad3d2fe634b26efbce53e22bbb5753a520 upstream. SMB2 SET_INFO handlers call path-based VFS helpers after checking the access mask granted to the SMB handle. Those helpers perform their owner, inode permission and LSM checks using the current ksmbd worker credentials. Run the complete SET_INFO dispatch with the credentials captured when the handle was opened. This also removes the separate security information credential setup and keeps all SET_INFO classes under one credential scope. Direct override_creds() is used because it can nest with the request credential overrides already used by rename and link helpers. Cc: stable@vger.kernel.org Reported-by: Musaab Khan Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smb2pdu.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 77b6e25978dcb..f130ddb2e67be 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -6630,6 +6630,7 @@ static int smb2_set_info_sec(struct ksmbd_file *fp, int addition_info, */ int smb2_set_info(struct ksmbd_work *work) { + const struct cred *saved_cred; struct smb2_set_info_req *req; struct smb2_set_info_rsp *rsp; struct ksmbd_file *fp = NULL; @@ -6671,6 +6672,7 @@ int smb2_set_info(struct ksmbd_work *work) goto err_out; } + saved_cred = override_creds(fp->filp->f_cred); switch (req->InfoType) { case SMB2_O_INFO_FILE: ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n"); @@ -6678,19 +6680,15 @@ int smb2_set_info(struct ksmbd_work *work) break; case SMB2_O_INFO_SECURITY: ksmbd_debug(SMB, "GOT SMB2_O_INFO_SECURITY\n"); - if (ksmbd_override_fsids(work)) { - rc = -ENOMEM; - goto err_out; - } rc = smb2_set_info_sec(fp, le32_to_cpu(req->AdditionalInformation), (char *)req + le16_to_cpu(req->BufferOffset), le32_to_cpu(req->BufferLength)); - ksmbd_revert_fsids(work); break; default: rc = -EOPNOTSUPP; } + revert_creds(saved_cred); if (rc < 0) goto err_out; From 5bc2aa358b5722ef71377e0175df8faba78fcf85 Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Thu, 11 Jun 2026 22:59:51 +0900 Subject: [PATCH 0373/2758] ksmbd: enforce FILE_READ_ATTRIBUTES on SMB_FIND_FILE_POSIX_INFORMATION commit 20c8442dc1003f9f7bb522d3dcd81d09ea59a79e upstream. find_file_posix_info() in smb2_query_info() returns file metadata (owner uid, group gid, mode, inode, size, allocation size, hard-link count and all four timestamps) but performs no per-handle access check. Every sibling query handler gates on the handle's granted access first -- get_file_basic_info(), get_file_all_info(), get_file_network_open_info() and get_file_attribute_tag_info() all reject a handle lacking FILE_READ_ATTRIBUTES_LE with -EACCES. The POSIX handler is gated only by the connection-scoped tcon->posix_extensions flag, which is not a per-handle authorization, so a handle opened with only FILE_WRITE_DATA is correctly denied FileBasicInformation yet is allowed the strict-superset POSIX info. Mirror the FILE_READ_ATTRIBUTES_LE gate the sibling info handlers already use. Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") Cc: stable@vger.kernel.org Signed-off-by: Gil Portnoy Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smb2pdu.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index f130ddb2e67be..1a0b379bc7415 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -5337,6 +5337,12 @@ static int find_file_posix_info(struct smb2_query_info_rsp *rsp, int out_buf_len = sizeof(struct smb311_posix_qinfo) + 32; int ret; + if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { + pr_err("no right to read the attributes : 0x%x\n", + fp->daccess); + return -EACCES; + } + ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS, AT_STATX_SYNC_AS_STAT); if (ret) From 3bed9974fdf897eea46121306ac416b1ae3f0652 Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Wed, 10 Jun 2026 20:13:51 +0900 Subject: [PATCH 0374/2758] ksmbd: add per-handle permission check to FILE_LINK_INFORMATION commit 13f3942f2bf45856bb751faed2f0c4618f41ca20 upstream. The FILE_LINK_INFORMATION arm of smb2_set_info_file() calls smb2_create_link() with no per-handle fp->daccess check. On the ReplaceIfExists path smb2_create_link() unlinks an existing file at the target name (ksmbd_vfs_remove_file) and creates a hardlink (ksmbd_vfs_link); neither helper checks daccess. A handle opened with FILE_READ_DATA only (no FILE_DELETE, no FILE_WRITE_DATA) can therefore delete an arbitrary file in the share and plant a hardlink over its name. The sibling delete/move arms in the same switch already gate: FILE_RENAME_INFORMATION and FILE_DISPOSITION_INFORMATION both require FILE_DELETE_LE; FILE_FULL_EA_INFORMATION requires FILE_WRITE_EA_LE. Gate the link arm the same way as its closest analogue (rename), since it mutates the namespace and, on replace, deletes an existing entry. This is a sibling of commit cc57232cae23 ("ksmbd: fix FSCTL permission bypass by adding a permission check for FSCTL_SET_SPARSE"). Cc: stable@vger.kernel.org Signed-off-by: Gil Portnoy Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smb2pdu.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 1a0b379bc7415..16f972ab3b51a 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -6564,6 +6564,11 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, } case FILE_LINK_INFORMATION: { + if (!(fp->daccess & FILE_DELETE_LE)) { + pr_err("no right to delete : 0x%x\n", fp->daccess); + return -EACCES; + } + if (buf_len < sizeof(struct smb2_file_link_info)) return -EMSGSIZE; From 4b7059974549d278e30fe70e2a4e421f9839817d Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 13 Jun 2026 22:00:00 +0900 Subject: [PATCH 0375/2758] ksmbd: use opener credentials for delete-on-close commit 52e2f21911158ec961cd5aae19c56460db382af0 upstream. Delete-on-close can be completed by deferred or durable handle teardown, where no request work is available. Both the base-file unlink and the ADS xattr removal consequently run with the ksmbd worker credentials and can bypass filesystem permission checks. Run both operations with the credentials captured in struct file when the handle was opened. This preserves the authenticated user's fsuid, fsgid, supplementary groups and capability restrictions at final close. Cc: stable@vger.kernel.org Reported-by: Musaab Khan Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/vfs.c | 7 +++++-- fs/smb/server/vfs_cache.c | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index e020e59d1d5d2..b20af16debbc8 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -1009,13 +1009,15 @@ int ksmbd_vfs_remove_xattr(struct mnt_idmap *idmap, int ksmbd_vfs_unlink(struct file *filp) { + const struct cred *saved_cred; int err = 0; struct dentry *dir, *dentry = filp->f_path.dentry; struct mnt_idmap *idmap = file_mnt_idmap(filp); + saved_cred = override_creds(filp->f_cred); err = mnt_want_write(filp->f_path.mnt); if (err) - return err; + goto out_revert; dir = dget_parent(dentry); dentry = start_removing_dentry(dir, dentry); @@ -1034,7 +1036,8 @@ int ksmbd_vfs_unlink(struct file *filp) out: dput(dir); mnt_drop_write(filp->f_path.mnt); - +out_revert: + revert_creds(saved_cred); return err; } diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 4daccc77f9eca..8c556e46cc102 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -385,10 +385,14 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp) up_write(&ci->m_lock); if (remove_stream_xattr) { + const struct cred *saved_cred; + + saved_cred = override_creds(filp->f_cred); err = ksmbd_vfs_remove_xattr(file_mnt_idmap(filp), &filp->f_path, fp->stream.name, true); + revert_creds(saved_cred); if (err) pr_err("remove xattr failed : %s\n", fp->stream.name); From 52a56cf53ec834c44ac1b4d16d585f26613ee5ce Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 13 Jun 2026 22:00:03 +0900 Subject: [PATCH 0376/2758] ksmbd: use opener credentials for ADS I/O commit baa5e094886fffa7e6272edcb5e08be5ce28262c upstream. Alternate data streams are stored as xattrs. Unlike regular file I/O, their read and write paths therefore call VFS xattr helpers which recheck inode permissions and LSM policy using the current task credentials. Run ADS I/O with the credentials captured when the SMB handle was opened. Cc: stable@vger.kernel.org Reported-by: Musaab Khan Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/vfs.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index b20af16debbc8..d17b4f0ee30ab 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -254,17 +254,20 @@ static ssize_t ksmbd_vfs_getcasexattr(struct mnt_idmap *idmap, static int ksmbd_vfs_stream_read(struct ksmbd_file *fp, char *buf, loff_t *pos, size_t count) { + const struct cred *saved_cred; ssize_t v_len; char *stream_buf = NULL; ksmbd_debug(VFS, "read stream data pos : %llu, count : %zd\n", *pos, count); + saved_cred = override_creds(fp->filp->f_cred); v_len = ksmbd_vfs_getcasexattr(file_mnt_idmap(fp->filp), fp->filp->f_path.dentry, fp->stream.name, fp->stream.size, &stream_buf); + revert_creds(saved_cred); if ((int)v_len <= 0) return (int)v_len; @@ -388,6 +391,7 @@ int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count, static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, size_t count) { + const struct cred *saved_cred; char *stream_buf = NULL, *wbuf; struct mnt_idmap *idmap = file_mnt_idmap(fp->filp); size_t size; @@ -408,6 +412,7 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, count = XATTR_SIZE_MAX - *pos; } + saved_cred = override_creds(fp->filp->f_cred); v_len = ksmbd_vfs_getcasexattr(idmap, fp->filp->f_path.dentry, fp->stream.name, @@ -416,14 +421,14 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, if (v_len < 0) { pr_err("not found stream in xattr : %zd\n", v_len); err = v_len; - goto out; + goto out_revert; } if (v_len < size) { wbuf = kvzalloc(size, KSMBD_DEFAULT_GFP); if (!wbuf) { err = -ENOMEM; - goto out; + goto out_revert; } if (v_len > 0) @@ -441,6 +446,8 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, size, 0, true); +out_revert: + revert_creds(saved_cred); if (err < 0) goto out; else From 5fecc15a30cb9ebd310f7b52c1ab607edcea78f6 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Mon, 15 Jun 2026 10:00:00 +0900 Subject: [PATCH 0377/2758] ksmbd: track the connection owning a byte-range lock commit c1016dd1d8b2bcd1158bbaabe94a31bb7e7431fb upstream. SMB2_LOCK adds each granted byte-range lock to both the file lock list and the lock list of the connection which handled the request. The final close and durable handle paths, however, remove the connection list entry while holding fp->conn->llist_lock. With SMB3 multichannel, the connection handling the LOCK request can be different from the connection which opened the file. The entry can therefore be removed under a different spinlock from the one protecting the list it belongs to. A concurrent traversal can then access freed struct ksmbd_lock and struct file_lock objects. Record the connection owning each lock's clist entry and hold a reference to it while the entry is linked. Use that connection and its llist_lock for unlock, rollback, close, and durable preserve. Durable reconnect assigns the new connection as the owner when publishing the locks again. Fixes: f5a544e3bab7 ("ksmbd: add support for SMB3 multichannel") Cc: stable@vger.kernel.org Reported-by: Musaab Khan Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smb2pdu.c | 10 ++++++++-- fs/smb/server/vfs_cache.c | 23 +++++++++++++++++------ fs/smb/server/vfs_cache.h | 1 + 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 16f972ab3b51a..f31efb3c095af 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -7644,9 +7644,11 @@ int smb2_lock(struct ksmbd_work *work) nolock = 0; list_del(&cmp_lock->flist); list_del(&cmp_lock->clist); + cmp_lock->conn = NULL; spin_unlock(&conn->llist_lock); up_read(&conn_list_lock); + ksmbd_conn_put(conn); locks_free_lock(cmp_lock->fl); kfree(cmp_lock); goto out_check_cl; @@ -7781,6 +7783,7 @@ int smb2_lock(struct ksmbd_work *work) goto out2; } else if (!rc) { list_add(&smb_lock->llist, &rollback_list); + smb_lock->conn = ksmbd_conn_get(work->conn); spin_lock(&work->conn->llist_lock); list_add_tail(&smb_lock->clist, &work->conn->lock_list); @@ -7835,11 +7838,14 @@ int smb2_lock(struct ksmbd_work *work) } list_del(&smb_lock->llist); - spin_lock(&work->conn->llist_lock); + conn = smb_lock->conn; + spin_lock(&conn->llist_lock); if (!list_empty(&smb_lock->flist)) list_del(&smb_lock->flist); list_del(&smb_lock->clist); - spin_unlock(&work->conn->llist_lock); + smb_lock->conn = NULL; + spin_unlock(&conn->llist_lock); + ksmbd_conn_put(conn); locks_free_lock(smb_lock->fl); if (rlock) diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 8c556e46cc102..39c56942ae440 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -484,10 +484,14 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) * there are not accesses to fp->lock_list. */ list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) { - if (!list_empty(&smb_lock->clist) && fp->conn) { - spin_lock(&fp->conn->llist_lock); - list_del(&smb_lock->clist); - spin_unlock(&fp->conn->llist_lock); + struct ksmbd_conn *conn = smb_lock->conn; + + if (conn) { + spin_lock(&conn->llist_lock); + list_del_init(&smb_lock->clist); + smb_lock->conn = NULL; + spin_unlock(&conn->llist_lock); + ksmbd_conn_put(conn); } list_del(&smb_lock->flist); @@ -1303,9 +1307,15 @@ static bool session_fd_check(struct ksmbd_tree_connect *tcon, up_write(&ci->m_lock); list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) { - spin_lock(&conn->llist_lock); + struct ksmbd_conn *lock_conn = smb_lock->conn; + + if (!lock_conn) + continue; + spin_lock(&lock_conn->llist_lock); list_del_init(&smb_lock->clist); - spin_unlock(&conn->llist_lock); + smb_lock->conn = NULL; + spin_unlock(&lock_conn->llist_lock); + ksmbd_conn_put(lock_conn); } fp->conn = NULL; @@ -1435,6 +1445,7 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) } list_for_each_entry(smb_lock, &fp->lock_list, flist) { + smb_lock->conn = ksmbd_conn_get(conn); spin_lock(&conn->llist_lock); list_add_tail(&smb_lock->clist, &conn->lock_list); spin_unlock(&conn->llist_lock); diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h index 7d547e1a74f7f..a3a9fda6de917 100644 --- a/fs/smb/server/vfs_cache.h +++ b/fs/smb/server/vfs_cache.h @@ -32,6 +32,7 @@ struct ksmbd_session; struct ksmbd_lock { struct file_lock *fl; + struct ksmbd_conn *conn; struct list_head clist; struct list_head flist; struct list_head llist; From 89ca7756d5566ba636bb9092cdbe57dab095e136 Mon Sep 17 00:00:00 2001 From: Haofeng Li Date: Tue, 23 Jun 2026 09:30:26 +0800 Subject: [PATCH 0378/2758] ksmbd: validate NTLMv2 response before updating session key commit 954d196bebb2b50151cb96454c72dc113b2af1ac upstream. ksmbd_auth_ntlmv2() derives the NTLMv2 session key into sess->sess_key before it verifies the NTLMv2 response. ksmbd_decode_ntlmssp_auth_blob() then continues into KEY_XCH even when ksmbd_auth_ntlmv2() failed. With SMB3 multichannel binding, the failed authentication operates on an existing session and the session setup error path does not expire binding sessions. A client can send a binding session setup with a bad NT proof and KEY_XCH and still modify sess->sess_key before STATUS_LOGON_FAILURE is returned. Relevant path: smb2_sess_setup() -> conn->binding = true -> ntlm_authenticate() -> session_user() -> ksmbd_decode_ntlmssp_auth_blob() -> ksmbd_auth_ntlmv2() -> calc_ntlmv2_hash() -> hmac_md5_usingrawkey(..., sess->sess_key) -> crypto_memneq() returns mismatch -> KEY_XCH arc4_crypt(..., sess->sess_key, ...) -> out_err without expiring the binding session Derive the base session key into a local buffer and copy it to sess->sess_key only after the proof matches. Return immediately on authentication failure so KEY_XCH is only processed after successful authentication. Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") Fixes: f9929ef6a2a5 ("ksmbd: add support for key exchange") Cc: stable@vger.kernel.org Signed-off-by: Haofeng Li Reviewed-by: ChenXiaoSong Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/auth.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/fs/smb/server/auth.c b/fs/smb/server/auth.c index e99409fa721cd..86f521e849d5e 100644 --- a/fs/smb/server/auth.c +++ b/fs/smb/server/auth.c @@ -142,6 +142,7 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, { char ntlmv2_hash[CIFS_ENCPWD_SIZE]; char ntlmv2_rsp[CIFS_HMAC_MD5_HASH_SIZE]; + char sess_key[SMB2_NTLMV2_SESSKEY_SIZE]; struct hmac_md5_ctx ctx; int rc; @@ -164,12 +165,21 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, /* Generate the session key */ hmac_md5_usingrawkey(ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE, ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE, - sess->sess_key); + sess_key); if (crypto_memneq(ntlmv2->ntlmv2_hash, ntlmv2_rsp, - CIFS_HMAC_MD5_HASH_SIZE)) - return -EINVAL; - return 0; + CIFS_HMAC_MD5_HASH_SIZE)) { + rc = -EINVAL; + goto out; + } + + memcpy(sess->sess_key, sess_key, sizeof(sess_key)); + rc = 0; +out: + memzero_explicit(ntlmv2_hash, sizeof(ntlmv2_hash)); + memzero_explicit(ntlmv2_rsp, sizeof(ntlmv2_rsp)); + memzero_explicit(sess_key, sizeof(sess_key)); + return rc; } /** @@ -226,6 +236,8 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, nt_len - CIFS_ENCPWD_SIZE, domain_name, conn->ntlmssp.cryptkey); kfree(domain_name); + if (ret) + return ret; /* The recovered secondary session key */ if (conn->ntlmssp.client_flags & NTLMSSP_NEGOTIATE_KEY_XCH) { From 550cfb8a81181331d4d0f76ab75ee58a0bf41e3e Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Sun, 7 Jun 2026 15:23:18 +0200 Subject: [PATCH 0379/2758] smb/client: fix chown/chgrp with SMB3 POSIX Extensions commit 760ef2c579c2609cf17fb1cd5392f64d42d43d33 upstream. Ownership (chown) and group (chgrp) modifications were being ignored when mounting with SMB3 POSIX Extensions unless CIFS_MOUNT_CIFS_ACL or CIFS_MOUNT_MODE_FROM_SID were also explicitly set. Fix this by checking for posix_extensions in cifs_setattr_nounix() when updating UID and GID, ensuring that id_mode_to_cifs_acl() is called to map and set the ownership/group information on the server. Cc: stable@vger.kernel.org Signed-off-by: Ralph Boehme Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/inode.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index 9472c0a6c187c..3b5bac93812d6 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -3348,7 +3348,8 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) if (attrs->ia_valid & ATTR_GID) gid = attrs->ia_gid; - if (sbflags & (CIFS_MOUNT_CIFS_ACL | CIFS_MOUNT_MODE_FROM_SID)) { + if ((sbflags & (CIFS_MOUNT_CIFS_ACL | CIFS_MOUNT_MODE_FROM_SID)) || + cifs_sb_master_tcon(cifs_sb)->posix_extensions) { if (uid_valid(uid) || gid_valid(gid)) { mode = NO_CHANGE_64; rc = id_mode_to_cifs_acl(inode, full_path, &mode, From 3317a5d015fca976475aa71df224056777316fde Mon Sep 17 00:00:00 2001 From: Henrique Carvalho Date: Thu, 18 Jun 2026 17:34:38 -0300 Subject: [PATCH 0380/2758] smb: client: fix query directory replay double-free commit 9647492b5e41954be59d5157eddbcd4cdc1656f7 upstream. A response-bearing attempt can return a replayable error and free its response buffer. If SMB2_query_directory_init() fails before the next send, cleanup retains the previous buffer type and frees that response again. Reset response bookkeeping before each attempt to prevent the stale free. Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set") Cc: stable@vger.kernel.org Signed-off-by: Henrique Carvalho Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index fbeb2156ddb6b..8ed9356cbcc8b 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -5683,6 +5683,8 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, replay_again: /* reinitialize for possible replay */ + resp_buftype = CIFS_NO_BUFFER; + memset(&rsp_iov, 0, sizeof(rsp_iov)); flags = 0; server = cifs_pick_channel(ses); From 89234773e8348918111aa15f6922b58cf3843364 Mon Sep 17 00:00:00 2001 From: Henrique Carvalho Date: Thu, 18 Jun 2026 17:34:36 -0300 Subject: [PATCH 0381/2758] smb: client: fix query_info() replay double-free commit 2a88561d66eb855813cf004a0abe648bbb17de5e upstream. A response-bearing attempt can return a replayable error and free its response buffer. If SMB2_query_info_init() fails before the next send, cleanup retains the previous buffer type and frees that response again. Reset response bookkeeping before each attempt to prevent the stale free. Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set") Cc: stable@vger.kernel.org Signed-off-by: Henrique Carvalho Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 8ed9356cbcc8b..66ef9cd1e0d25 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -3911,6 +3911,8 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon, replay_again: /* reinitialize for possible replay */ + resp_buftype = CIFS_NO_BUFFER; + memset(&rsp_iov, 0, sizeof(rsp_iov)); flags = 0; allocated = false; server = cifs_pick_channel(ses); From fc65ffb4ef1bf540da16b17c225ae51091e07d72 Mon Sep 17 00:00:00 2001 From: Henrique Carvalho Date: Thu, 18 Jun 2026 17:34:34 -0300 Subject: [PATCH 0382/2758] smb: client: fix double-free in SMB2_ioctl() replay commit f9bbadb6c94583e3b4af1afc449bfceb1d1ddec9 upstream. A response-bearing attempt can return a replayable error and free its response buffer. If SMB2_ioctl_init() fails before the next send, cleanup retains the previous buffer type and frees that response again. Reset response bookkeeping before each attempt to prevent the stale free. Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set") Cc: stable@vger.kernel.org Signed-off-by: Henrique Carvalho Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 66ef9cd1e0d25..358d0fb1188f9 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -3505,6 +3505,8 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, replay_again: /* reinitialize for possible replay */ + resp_buftype = CIFS_NO_BUFFER; + memset(&rsp_iov, 0, sizeof(rsp_iov)); flags = 0; server = cifs_pick_channel(ses); From 901891513951bc8322ece754863909ea45af95c6 Mon Sep 17 00:00:00 2001 From: Henrique Carvalho Date: Thu, 18 Jun 2026 17:34:37 -0300 Subject: [PATCH 0383/2758] smb: client: fix change notify replay double-free commit 145f820dcbb2cced374f2532f8a61a44dce4a615 upstream. A response-bearing attempt can return a replayable error and free its response buffer. If SMB2_notify_init() fails before the next send, cleanup retains the previous buffer type and frees that response again. Reset response bookkeeping before each attempt to prevent the stale free. Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set") Cc: stable@vger.kernel.org Signed-off-by: Henrique Carvalho Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 358d0fb1188f9..d4d20aced10f2 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -4087,6 +4087,8 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, replay_again: /* reinitialize for possible replay */ + resp_buftype = CIFS_NO_BUFFER; + memset(&rsp_iov, 0, sizeof(rsp_iov)); flags = 0; server = cifs_pick_channel(ses); From 013a9a3da46c5dabcf18f65ea6a47874ba12a15d Mon Sep 17 00:00:00 2001 From: Zhao Zhang Date: Thu, 18 Jun 2026 23:28:05 +0800 Subject: [PATCH 0384/2758] smb: client: fix double-free in SMB2_flush() replay commit 4be31c943a3a27a5a0251dbb8f5cb89059ec3d5a upstream. SMB2_flush() keeps its response buffer bookkeeping across replay attempts. If a replayable flush response is received and the retry then fails before cifs_send_recv() stores a replacement response, flush_exit will free the stale response pointer a second time. Reinitialize resp_buftype and rsp_iov at the top of the replay loop so cleanup only acts on response state produced by the current attempt. This fixes a double-free without changing replay handling for successful requests. Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Acked-by: Henrique Carvalho Signed-off-by: Zhao Zhang Signed-off-by: Ren Wei Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index d4d20aced10f2..273fe8ec3d040 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -4431,6 +4431,8 @@ SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, replay_again: /* reinitialize for possible replay */ + resp_buftype = CIFS_NO_BUFFER; + memset(&rsp_iov, 0, sizeof(rsp_iov)); flags = 0; server = cifs_pick_channel(ses); From ff2d30927bc3bf3c629f0768d2068096e64ef5ce Mon Sep 17 00:00:00 2001 From: Henrique Carvalho Date: Thu, 18 Jun 2026 17:34:33 -0300 Subject: [PATCH 0385/2758] smb: client: fix double-free in SMB2_open() replay commit b55e182f2324bc6a604c21a47aa6c448f719a532 upstream. A response-bearing attempt can return a replayable error and free its response buffer. If SMB2_open_init() fails before the next send, cleanup retains the previous buffer type and frees that response again. Reset response bookkeeping before each attempt to prevent the stale free. Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set") Cc: stable@vger.kernel.org Signed-off-by: Henrique Carvalho Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 273fe8ec3d040..07757daccd067 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -3280,6 +3280,8 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, replay_again: /* reinitialize for possible replay */ + resp_buftype = CIFS_NO_BUFFER; + memset(&rsp_iov, 0, sizeof(rsp_iov)); flags = 0; server = cifs_pick_channel(ses); oparms->replay = !!(retries); From b18ed621dbfceecea5539848cddcb9272c9a61e1 Mon Sep 17 00:00:00 2001 From: Henrique Carvalho Date: Thu, 18 Jun 2026 17:34:35 -0300 Subject: [PATCH 0386/2758] smb: client: fix double-free in SMB2_close() replay commit f96e1cdcb63ed3321142ff2fcdf784e32cda8fee upstream. A response-bearing attempt can return a replayable error and free its response buffer. If SMB2_close_init() fails before the next send, cleanup retains the previous buffer type and frees that response again. Reset response bookkeeping before each attempt to prevent the stale free. Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set") Cc: stable@vger.kernel.org Signed-off-by: Henrique Carvalho Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 07757daccd067..491f7ff19e4a1 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -3703,6 +3703,8 @@ __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, replay_again: /* reinitialize for possible replay */ + resp_buftype = CIFS_NO_BUFFER; + memset(&rsp_iov, 0, sizeof(rsp_iov)); flags = 0; query_attrs = false; server = cifs_pick_channel(ses); From 927d4805aea0a287d36dd4f826ee24d69a2afee3 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Tue, 23 Jun 2026 09:45:38 +0800 Subject: [PATCH 0387/2758] smb: client: Fix next buffer leak in receive_encrypted_standard() commit 1c6267a1d5cf4c73b656f8181b310cbbb3e4767b upstream. receive_encrypted_standard() allocates next_buffer before checking whether the number of compound PDUs already reached MAX_COMPOUND. If the limit check fails, the function returns immediately and the newly allocated next_buffer is not assigned to server->smallbuf/server->bigbuf, making it leaked. Move the MAX_COMPOUND check before allocating next_buffer. Fixes: b24df3e30cbf ("cifs: update receive_encrypted_standard to handle compounded responses") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2ops.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 1987332192d63..ce34b3fbfca3d 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -5111,6 +5111,12 @@ receive_encrypted_standard(struct TCP_Server_Info *server, one_more: shdr = (struct smb2_hdr *)buf; next_cmd = le32_to_cpu(shdr->NextCommand); + + if (*num_mids >= MAX_COMPOUND) { + cifs_server_dbg(VFS, "too many PDUs in compound\n"); + return -1; + } + if (next_cmd) { if (WARN_ON_ONCE(next_cmd > pdu_length)) return -1; @@ -5134,10 +5140,6 @@ receive_encrypted_standard(struct TCP_Server_Info *server, mid_entry->resp_buf_size = server->pdu_size; } - if (*num_mids >= MAX_COMPOUND) { - cifs_server_dbg(VFS, "too many PDUs in compound\n"); - return -1; - } bufs[*num_mids] = buf; mids[(*num_mids)++] = mid_entry; From 52f9c9dafefc92a942fe5c9c110b117fbb76baa5 Mon Sep 17 00:00:00 2001 From: Zihan Xi Date: Wed, 1 Jul 2026 18:23:21 +0800 Subject: [PATCH 0388/2758] smb: client: use unaligned reads in parse_posix_ctxt() commit b86467cd2691192ad4809a5a6e922fc24b8e9839 upstream. The server controls create-context DataOffset, so the POSIX context data pointer may be misaligned on strict-alignment architectures. Use get_unaligned_le32() when reading nlink, reparse_tag, and mode. Fixes: 69dda3059e7a ("cifs: add SMB2_open() arg to return POSIX data") Cc: stable@vger.kernel.org Signed-off-by: Zihan Xi Signed-off-by: Ren Wei Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2pdu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 491f7ff19e4a1..50144e004cfea 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -2371,9 +2371,9 @@ parse_posix_ctxt(struct create_context *cc, struct smb2_file_all_info *info, memset(posix, 0, sizeof(*posix)); - posix->nlink = le32_to_cpu(*(__le32 *)(beg + 0)); - posix->reparse_tag = le32_to_cpu(*(__le32 *)(beg + 4)); - posix->mode = le32_to_cpu(*(__le32 *)(beg + 8)); + posix->nlink = get_unaligned_le32(beg); + posix->reparse_tag = get_unaligned_le32(beg + 4); + posix->mode = get_unaligned_le32(beg + 8); sid = beg + 12; sid_len = posix_info_sid_size(sid, end); From 46a84715a015cb48e1b9c219dc88c03d8a541ea4 Mon Sep 17 00:00:00 2001 From: Zihan Xi Date: Sun, 28 Jun 2026 17:19:24 +0800 Subject: [PATCH 0389/2758] smb: client: harden POSIX SID length parsing commit 7ad2bcf2441430bb2e918fb3ef9a90d775a6e422 upstream. posix_info_sid_size() reads sid[1] to obtain the subauthority count, but its existing boundary check still accepts buffers with only one remaining byte. Require two bytes before reading sid[1] so all client paths that reuse the helper reject truncated POSIX SIDs safely. Fixes: 349e13ad30b4 ("cifs: add smb2 POSIX info level") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Assisted-by: Codex:gpt-5.4 Signed-off-by: Zihan Xi Signed-off-by: Ren Wei Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2pdu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 50144e004cfea..69b618e2f67a9 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -5380,7 +5380,7 @@ int posix_info_sid_size(const void *beg, const void *end) size_t subauth; int total; - if (beg + 1 > end) + if (beg + 2 > end) return -1; subauth = *(u8 *)(beg+1); From fc25bbc893f6a8be68ffd9ae205aa6672723b3b5 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Tue, 7 Jul 2026 21:30:17 +0800 Subject: [PATCH 0390/2758] smb: client: fix atime clamp check in read completion commit 0b043279e73880bee21d3b1f221bafda5af1b27e upstream. cifs_rreq_done() updates the inode atime to current_time(inode) after a netfs read. It then preserves the CIFS rule that atime should not be older than mtime, because some applications break if atime is less than mtime. That rule only requires clamping when atime < mtime. The current check uses the raw non-zero result of timespec64_compare(). It therefore takes the clamp path for both atime < mtime and atime > mtime. The latter is the normal case when reading an older file: the newly recorded atime is newer than the file mtime. The completion handler then immediately moves atime back to mtime, losing the access time that was just recorded. Userspace tools that rely on atime, such as stat, find -atime, backup tools or cold-data classifiers, can therefore see a recently read CIFS file as not recently accessed. This is easy to miss because the bug is silent: read I/O still succeeds, no error is reported, and many systems either do not check atime after reads or mount with policies such as relatime/noatime. It becomes visible when a CIFS file has an mtime older than the current time, the file is read, and the local inode atime is inspected before a later revalidation replaces the cached timestamps. Clamp only when atime is actually older than mtime. This matches the same atime/mtime rule used when applying CIFS inode attributes. Fixes: 69c3c023af25 ("cifs: Implement netfslib hooks") Cc: stable@vger.kernel.org Signed-off-by: Xu Rao Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c index b60344125f271..e536e424b9b75 100644 --- a/fs/smb/client/file.c +++ b/fs/smb/client/file.c @@ -301,7 +301,7 @@ static void cifs_rreq_done(struct netfs_io_request *rreq) /* we do not want atime to be less than mtime, it broke some apps */ atime = inode_set_atime_to_ts(inode, current_time(inode)); mtime = inode_get_mtime(inode); - if (timespec64_compare(&atime, &mtime)) + if (timespec64_compare(&atime, &mtime) < 0) inode_set_atime_to_ts(inode, inode_get_mtime(inode)); } From f80add1bfb3425100a325b14f19648e75669a954 Mon Sep 17 00:00:00 2001 From: Norbert Manthey Date: Thu, 9 Jul 2026 15:54:39 +0000 Subject: [PATCH 0391/2758] smb: client: mask server-provided mode to 07777 in modefromsid commit e3d9c7160d483fc8f9e225aafad8ecbbc43f3151 upstream. When modefromsid is active, parse_dacl() applies the server-provided sub_auth[2] value from the NFS mode SID to cf_mode without masking to 07777. Apply the correct masking, same as in the read path. Fixes: e2f8fbfb8d09c ("cifs: get mode bits from special sid on stat") Signed-off-by: Norbert Manthey Assisted-by: Kiro:claude-opus-4.6 Cc: stable@vger.kernel.org Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/cifsacl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c index 786dbbc43c5b9..59a2d702fce15 100644 --- a/fs/smb/client/cifsacl.c +++ b/fs/smb/client/cifsacl.c @@ -889,7 +889,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, */ fattr->cf_mode &= ~07777; fattr->cf_mode |= - le32_to_cpu(ppace[i]->sid.sub_auth[2]); + le32_to_cpu(ppace[i]->sid.sub_auth[2]) & 07777; break; } else { if (compare_sids(&(ppace[i]->sid), pownersid) == 0) { From eedf142d994e39ca81f9b5deabfddfb1abcb96b4 Mon Sep 17 00:00:00 2001 From: ChenXiaoSong Date: Sun, 28 Jun 2026 07:42:43 +0000 Subject: [PATCH 0392/2758] smb/server: do not require delete access for non-replacing links commit 851ed9e09639e0daf79a506ce26097b296ed5518 upstream. Reproducer: 1. server: systemctl start ksmbd 2. client: mount -t cifs //${server_ip}/export /mnt 3. client: touch /mnt/file; ln /mnt/file /mnt/hardlink 4. client err log: ln: failed to create hard link 'hardlink' => 'file': Permission denied 5. server err log: ksmbd: no right to delete : 0x80 Fixes: 13f3942f2bf4 ("ksmbd: add per-handle permission check to FILE_LINK_INFORMATION") Cc: stable@vger.kernel.org Reported-by: Steve French Signed-off-by: ChenXiaoSong Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smb2pdu.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index f31efb3c095af..a1b163763dade 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -6564,16 +6564,18 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, } case FILE_LINK_INFORMATION: { - if (!(fp->daccess & FILE_DELETE_LE)) { - pr_err("no right to delete : 0x%x\n", fp->daccess); - return -EACCES; - } + struct smb2_file_link_info *file_info; if (buf_len < sizeof(struct smb2_file_link_info)) return -EMSGSIZE; - return smb2_create_link(work, work->tcon->share_conf, - (struct smb2_file_link_info *)buffer, + file_info = (struct smb2_file_link_info *)buffer; + if (file_info->ReplaceIfExists && !(fp->daccess & FILE_DELETE_LE)) { + pr_err("no right to delete : 0x%x\n", fp->daccess); + return -EACCES; + } + + return smb2_create_link(work, work->tcon->share_conf, file_info, buf_len, fp->filp, work->conn->local_nls); } From 53eeaf4d63068dbc7708b0c7adb20151c812feca Mon Sep 17 00:00:00 2001 From: Baokun Li Date: Thu, 21 May 2026 17:50:14 +0800 Subject: [PATCH 0393/2758] writeback: fix race between cgroup_writeback_umount() and inode_switch_wbs() commit cba38ec4cbd3a7b8b942a8d52531a05be8a9ff0d upstream. When a container exits, the following BUG_ON() is occasionally triggered: ================================================================== VFS: Busy inodes after unmount of sdb (ext4) ------------[ cut here ]------------ kernel BUG at fs/super.c:695! CPU: 3 PID: 6 Comm: containerd-shim Tainted: G OE K 6.6 #1 pstate: 63400009 (nZCv daif +PAN -UAO +TCO +DIT -SSBS BTYPE=--) pc : generic_shutdown_super+0xf0/0x100 lr : generic_shutdown_super+0xf0/0x100 Call trace: generic_shutdown_super+0xf0/0x100 kill_block_super+0x20/0x48 ext4_kill_sb+0x28/0x60 deactivate_locked_super+0x54/0x130 deactivate_super+0x84/0xa0 cleanup_mnt+0xa4/0x140 __cleanup_mnt+0x18/0x28 task_work_run+0x78/0xe0 do_notify_resume+0x204/0x240 ================================================================== The root cause is a race between cgroup_writeback_umount() and inode_switch_wbs()/cleanup_offline_cgwb(). There is a window between inode_prepare_wbs_switch() returning true and the subsequent wb_queue_isw() call. Following is the process that triggers the issue: CPU A (umount) | CPU B (writeback) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ inode_switch_wbs/cleanup_offline_cgwb atomic_inc(&isw_nr_in_flight) inode_prepare_wbs_switch -> passes SB_ACTIVE check __iget(inode) generic_shutdown_super sb->s_flags &= ~SB_ACTIVE cgroup_writeback_umount(sb) smp_mb() atomic_read(&isw_nr_in_flight) rcu_barrier() -> no pending RCU callbacks flush_workqueue(isw_wq) -> nothing queued, returns evict_inodes(sb) -> Inode skipped as isw still holds a ref. sop->put_super(sb) /* destroys percpu counters */ -> VFS: Busy inodes after unmount! wb_queue_isw() queue_work(isw_wq, ...) /* later in work function */ inode_switch_wbs_work_fn process_inode_switch_wbs iput() -> evict percpu_counter_dec() // UAF! Fix this by extending the RCU read-side critical section in inode_switch_wbs() and cleanup_offline_cgwb() to cover from inode_prepare_wbs_switch() through wb_queue_isw(). Since there is no sleep in this window, rcu_read_lock() can be used. Then add a synchronize_rcu() in cgroup_writeback_umount() before the existing rcu_barrier(), so that all in-flight switchers that have passed the SB_ACTIVE check have completed queue_work() before flush_workqueue() is called. The existing rcu_barrier() is intentionally retained so this fix can be backported unchanged to stable kernels (5.10.y, 6.6.y, ...) that still queue switches via queue_rcu_work(). It is a no-op on current mainline (since commit e1b849cfa6b6 ("writeback: Avoid contention on wb->list_lock when switching inodes")) and is removed in a follow-up patch. Fixes: a1a0e23e4903 ("writeback: flush inode cgroup wb switches instead of pinning super_block") Cc: stable@vger.kernel.org Suggested-by: Jan Kara Link: https://lore.kernel.org/all/mxnjq2l6guusfchvauxr3v7c4bwjasybxlleqbbh4efloeqspz@iqylk76ohufz Reviewed-by: Jan Kara Signed-off-by: Baokun Li Link: https://patch.msgid.link/20260521095016.2791354-2-libaokun@linux.alibaba.com Acked-by: Tejun Heo Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/fs-writeback.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index a65694cbfe689..6766de9f9d75b 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -660,12 +660,19 @@ static void inode_switch_wbs(struct inode *inode, int new_wb_id) atomic_inc(&isw_nr_in_flight); - /* find and pin the new wb */ + /* + * Paired with synchronize_rcu() in cgroup_writeback_umount(): + * holding rcu_read_lock across inode_prepare_wbs_switch() + * (covering the SB_ACTIVE check and the inode grab) and + * wb_queue_isw() ensures synchronize_rcu() cannot return until + * the work is queued, so the subsequent flush_workqueue() will + * wait for the switch. + */ rcu_read_lock(); + /* find and pin the new wb */ memcg_css = css_from_id(new_wb_id, &memory_cgrp_subsys); if (memcg_css && !css_tryget(memcg_css)) memcg_css = NULL; - rcu_read_unlock(); if (!memcg_css) goto out_free; @@ -681,9 +688,11 @@ static void inode_switch_wbs(struct inode *inode, int new_wb_id) trace_inode_switch_wbs_queue(inode->i_wb, new_wb, 1); wb_queue_isw(new_wb, isw); + rcu_read_unlock(); return; out_free: + rcu_read_unlock(); atomic_dec(&isw_nr_in_flight); if (new_wb) wb_put(new_wb); @@ -741,6 +750,14 @@ bool cleanup_offline_cgwb(struct bdi_writeback *wb) new_wb = &wb->bdi->wb; /* wb_get() is noop for bdi's wb */ nr = 0; + /* + * Paired with synchronize_rcu() in cgroup_writeback_umount(). + * Holding rcu_read_lock across the SB_ACTIVE check, the inode grab + * and wb_queue_isw() ensures synchronize_rcu() cannot return until + * the work is queued, so the subsequent flush_workqueue() will wait + * for the switch. + */ + rcu_read_lock(); spin_lock(&wb->list_lock); /* * In addition to the inodes that have completed writeback, also switch @@ -758,6 +775,7 @@ bool cleanup_offline_cgwb(struct bdi_writeback *wb) /* no attached inodes? bail out */ if (nr == 0) { + rcu_read_unlock(); atomic_dec(&isw_nr_in_flight); wb_put(new_wb); kfree(isw); @@ -766,6 +784,7 @@ bool cleanup_offline_cgwb(struct bdi_writeback *wb) trace_inode_switch_wbs_queue(wb, new_wb, nr); wb_queue_isw(new_wb, isw); + rcu_read_unlock(); return restart; } @@ -1221,6 +1240,14 @@ void cgroup_writeback_umount(struct super_block *sb) smp_mb(); if (atomic_read(&isw_nr_in_flight)) { + /* + * Paired with rcu_read_lock() in inode_switch_wbs() and + * cleanup_offline_cgwb(). synchronize_rcu() waits for any + * in-flight switcher that already passed the SB_ACTIVE check + * to finish queueing its work, so flush_workqueue() below + * will then drain it. + */ + synchronize_rcu(); /* * Use rcu_barrier() to wait for all pending callbacks to * ensure that all in-flight wb switches are in the workqueue. From 625b014f922c992c804f88687ff0f86a539772ce Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Mon, 11 May 2026 12:12:11 +0530 Subject: [PATCH 0394/2758] OPP: of: Fix potential memory leak in opp_parse_supplies() commit 69f888381d2ecbe18ed9f112c096f8fd3623db98 upstream. The memory allocated for microvolt, microamp and microwatt is not freed in one of the paths in opp_parse_supplies() which returns directly. Fix that by adding a goto to the error unwind ladder. Fixes: 2eedf62e66c2 ("OPP: decouple dt properties in opp_parse_supplies()") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/opp/of.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/opp/of.c b/drivers/opp/of.c index f96adfd5b2197..c02e20632fa64 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c @@ -673,7 +673,7 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, */ if (unlikely(opp_table->regulator_count == -1)) { opp_table->regulator_count = 0; - return 0; + goto free_microwatt; } for (i = 0, j = 0; i < opp_table->regulator_count; i++) { @@ -696,6 +696,7 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, opp->supplies[i].u_watt = microwatt[i]; } +free_microwatt: kfree(microwatt); free_microamp: kfree(microamp); From 9de568ef6cdfc7912d5ea8db02843c0e4ef0c75d Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Sat, 2 May 2026 03:00:05 +0800 Subject: [PATCH 0395/2758] cpufreq: qcom-cpufreq-hw: Fix possible double free commit bcb8889c4981fdde42d4fd2c29a77d510fe21da2 upstream. qcom_cpufreq.data is allocated with devm_kzalloc() in probe() as an array of per-domain data. qcom_cpufreq_hw_cpu_init() stores a pointer to one element of this array in policy->driver_data. qcom_cpufreq_hw_cpu_exit() currently calls kfree() on policy->driver_data. This is not valid because the memory is devm-managed. For the first domain, this can free the devm-managed allocation while the devres entry is still active, leading to a possible double free when the platform device is later detached. For other domains, the pointer may refer to an element inside the array rather than the allocation base. Remove the kfree(data) call and let devres release qcom_cpufreq.data. This issue was found by a static analysis tool I am developing. Fixes: 054a3ef683a1 ("cpufreq: qcom-hw: Allocate qcom_cpufreq_data during probe") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Reviewed-by: Zhongqiu Han Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/cpufreq/qcom-cpufreq-hw.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index ea9a20d27b8fd..ef19faedbfec3 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -578,7 +578,6 @@ static void qcom_cpufreq_hw_cpu_exit(struct cpufreq_policy *policy) dev_pm_opp_of_cpumask_remove_table(policy->related_cpus); qcom_cpufreq_hw_lmh_exit(data); kfree(policy->freq_table); - kfree(data); } static void qcom_cpufreq_ready(struct cpufreq_policy *policy) From 15432f19562fdb9199cce6d9fc24db12c71ed574 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Tue, 5 May 2026 17:12:31 +0800 Subject: [PATCH 0396/2758] firmware_loader: fix device reference leak in firmware_upload_register() commit 896df22ee57648b0c505bd76ddbc6b2341834696 upstream. firmware_upload_register() -> fw_create_instance() -> device_initialize() After fw_create_instance() succeeds, the lifetime of the embedded struct device is expected to be managed through the device core reference counting, since fw_create_instance() has already called device_initialize(). In firmware_upload_register(), if alloc_lookup_fw_priv() fails after fw_create_instance() succeeds, the code reaches free_fw_sysfs and frees fw_sysfs directly instead of releasing the device reference with put_device(). This may leave the reference count of the embedded struct device unbalanced, resulting in a refcount leak. The issue was identified by a static analysis tool I developed and confirmed by manual review. Fix this by using put_device(fw_dev) in the failure path and letting fw_dev_release() handle the final cleanup, instead of freeing the instance directly from the error path. Fixes: 97730bbb242c ("firmware_loader: Add firmware-upload support") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Link: https://patch.msgid.link/20260505091231.607089-1-lgs201920130244@gmail.com Signed-off-by: Danilo Krummrich Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_loader/sysfs_upload.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/base/firmware_loader/sysfs_upload.c b/drivers/base/firmware_loader/sysfs_upload.c index f59a7856934ce..efc33294212fd 100644 --- a/drivers/base/firmware_loader/sysfs_upload.c +++ b/drivers/base/firmware_loader/sysfs_upload.c @@ -343,7 +343,6 @@ firmware_upload_register(struct module *module, struct device *parent, goto free_fw_upload_priv; } fw_upload->priv = fw_sysfs; - fw_sysfs->fw_upload_priv = fw_upload_priv; fw_dev = &fw_sysfs->dev; ret = alloc_lookup_fw_priv(name, &fw_cache, &fw_priv, NULL, 0, 0, @@ -351,10 +350,12 @@ firmware_upload_register(struct module *module, struct device *parent, if (ret != 0) { if (ret > 0) ret = -EINVAL; - goto free_fw_sysfs; + put_device(fw_dev); + goto free_fw_upload_priv; } fw_priv->is_paged_buf = true; fw_sysfs->fw_priv = fw_priv; + fw_sysfs->fw_upload_priv = fw_upload_priv; ret = device_add(fw_dev); if (ret) { @@ -365,9 +366,6 @@ firmware_upload_register(struct module *module, struct device *parent, return fw_upload; -free_fw_sysfs: - kfree(fw_sysfs); - free_fw_upload_priv: kfree(fw_upload_priv); From 8e931557b317f0fb414839fa51fae1d5feb0ad97 Mon Sep 17 00:00:00 2001 From: John Hubbard Date: Wed, 3 Jun 2026 19:53:14 -0700 Subject: [PATCH 0397/2758] libfs: set SB_I_NOEXEC and SB_I_NODEV by default in init_pseudo() commit 6de2aeffabaafaeda819e60ec8d04f199711e11a upstream. Since commit 1e7ab6f67824 ("anon_inode: rework assertions"), path_noexec() warns when an anonymous-inode file is mmap'd from a superblock that has not set SB_I_NOEXEC. dma-buf backs its files this way and never set the flag, so mmap of any exported buffer trips the warning on a CONFIG_DEBUG_VFS=y kernel: WARNING: CPU: 11 PID: 121813 at fs/exec.c:118 path_noexec+0x47/0x50 do_mmap+0x2b5/0x680 vm_mmap_pgoff+0x129/0x210 ksys_mmap_pgoff+0x177/0x240 __x64_sys_mmap+0x33/0x70 init_pseudo() sets up internal SB_NOUSER mounts that are never path-reachable. Set both flags here so every pseudo filesystem gets them by default instead of each caller setting them. SB_I_NODEV is inert for unreachable mounts. SB_I_NOEXEC has one visible effect: an executable mapping of a pseudo-fs fd, such as a dma-buf, now fails with -EPERM, which is the invariant the assertion enforces. No in-tree caller maps these executable. Reproduce on CONFIG_DEBUG_VFS=y: make -C tools/testing/selftests/dmabuf-heaps sudo ./tools/testing/selftests/dmabuf-heaps/dmabuf-heap -t system Fixes: 1e7ab6f67824 ("anon_inode: rework assertions") Suggested-by: Christoph Hellwig Cc: stable@vger.kernel.org Signed-off-by: John Hubbard Link: https://patch.msgid.link/20260604025315.245910-2-jhubbard@nvidia.com Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/libfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/libfs.c b/fs/libfs.c index 1bbea5e7bae37..e8226b9e1bc8c 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -736,6 +736,7 @@ struct pseudo_fs_context *init_pseudo(struct fs_context *fc, fc->fs_private = ctx; fc->ops = &pseudo_fs_context_ops; fc->sb_flags |= SB_NOUSER; + fc->s_iflags |= SB_I_NOEXEC | SB_I_NODEV; fc->global = true; } return ctx; From dfd1894cb64cbd8758b461ed713800fe73db4f82 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Mon, 18 May 2026 18:35:16 +0200 Subject: [PATCH 0398/2758] proc: protect ptrace_may_access() with exec_update_lock (FD links) commit 6255da28d4bb5349fe18e84cb043ccd394eba75d upstream. proc_pid_get_link() and proc_pid_readlink() currently look up the task from the pid once, then do the ptrace access check on that task, then look up the task from the pid a second time to do the actual access. That's racy in several ways. To fix it, pass the task to the ->proc_get_link() handler, and instead of proc_fd_access_allowed(), introduce a new helper call_proc_get_link() that looks up and locks the task, does the access check, and calls ->proc_get_link(). Fixes: 778c1144771f ("[PATCH] proc: Use sane permission checks on the /proc//fd/ symlinks") Cc: stable@vger.kernel.org Signed-off-by: Jann Horn Link: https://patch.msgid.link/20260518-procfs-lockfix-part1-v1-2-5c3d20e0ac33@google.com Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/proc/base.c | 119 ++++++++++++++++++--------------------------- fs/proc/fd.c | 27 +++++----- fs/proc/internal.h | 2 +- 3 files changed, 59 insertions(+), 89 deletions(-) diff --git a/fs/proc/base.c b/fs/proc/base.c index d9acfa89c894b..2b7348dca8ba0 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -218,33 +218,24 @@ static int get_task_root(struct task_struct *task, struct path *root) return result; } -static int proc_cwd_link(struct dentry *dentry, struct path *path) +static int proc_cwd_link(struct dentry *dentry, struct path *path, + struct task_struct *task) { - struct task_struct *task = get_proc_task(d_inode(dentry)); int result = -ENOENT; - if (task) { - task_lock(task); - if (task->fs) { - get_fs_pwd(task->fs, path); - result = 0; - } - task_unlock(task); - put_task_struct(task); + task_lock(task); + if (task->fs) { + get_fs_pwd(task->fs, path); + result = 0; } + task_unlock(task); return result; } -static int proc_root_link(struct dentry *dentry, struct path *path) +static int proc_root_link(struct dentry *dentry, struct path *path, + struct task_struct *task) { - struct task_struct *task = get_proc_task(d_inode(dentry)); - int result = -ENOENT; - - if (task) { - result = get_task_root(task, path); - put_task_struct(task); - } - return result; + return get_task_root(task, path); } /* @@ -704,23 +695,6 @@ static int proc_pid_syscall(struct seq_file *m, struct pid_namespace *ns, /* Here the fs part begins */ /************************************************************************/ -/* permission checks */ -static bool proc_fd_access_allowed(struct inode *inode) -{ - struct task_struct *task; - bool allowed = false; - /* Allow access to a task's file descriptors if it is us or we - * may use ptrace attach to the process and find out that - * information. - */ - task = get_proc_task(inode); - if (task) { - allowed = ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS); - put_task_struct(task); - } - return allowed; -} - int proc_nochmod_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *attr) { @@ -1777,16 +1751,12 @@ static const struct file_operations proc_pid_set_comm_operations = { .release = single_release, }; -static int proc_exe_link(struct dentry *dentry, struct path *exe_path) +static int proc_exe_link(struct dentry *dentry, struct path *exe_path, + struct task_struct *task) { - struct task_struct *task; struct file *exe_file; - task = get_proc_task(d_inode(dentry)); - if (!task) - return -ENOENT; exe_file = get_task_exe_file(task); - put_task_struct(task); if (exe_file) { *exe_path = exe_file->f_path; path_get(&exe_file->f_path); @@ -1796,26 +1766,42 @@ static int proc_exe_link(struct dentry *dentry, struct path *exe_path) return -ENOENT; } +static int call_proc_get_link(struct dentry *dentry, struct inode *inode, struct path *path_out) +{ + struct task_struct *task; + int ret; + + task = get_proc_task(inode); + if (!task) + return -ENOENT; + ret = down_read_killable(&task->signal->exec_update_lock); + if (ret) + goto out_put_task; + if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) { + ret = -EACCES; + goto out; + } + ret = PROC_I(inode)->op.proc_get_link(dentry, path_out, task); + +out: + up_read(&task->signal->exec_update_lock); +out_put_task: + put_task_struct(task); + return ret; +} + static const char *proc_pid_get_link(struct dentry *dentry, struct inode *inode, struct delayed_call *done) { struct path path; - int error = -EACCES; + int error; if (!dentry) return ERR_PTR(-ECHILD); - - /* Are we allowed to snoop on the tasks file descriptors? */ - if (!proc_fd_access_allowed(inode)) - goto out; - - error = PROC_I(inode)->op.proc_get_link(dentry, &path); - if (error) - goto out; - - error = nd_jump_link(&path); -out: + error = call_proc_get_link(dentry, inode, &path); + if (!error) + error = nd_jump_link(&path); return ERR_PTR(error); } @@ -1849,17 +1835,11 @@ static int proc_pid_readlink(struct dentry * dentry, char __user * buffer, int b struct inode *inode = d_inode(dentry); struct path path; - /* Are we allowed to snoop on the tasks file descriptors? */ - if (!proc_fd_access_allowed(inode)) - goto out; - - error = PROC_I(inode)->op.proc_get_link(dentry, &path); - if (error) - goto out; - - error = do_proc_readlink(&path, buffer, buflen); - path_put(&path); -out: + error = call_proc_get_link(dentry, inode, &path); + if (!error) { + error = do_proc_readlink(&path, buffer, buflen); + path_put(&path); + } return error; } @@ -2250,21 +2230,16 @@ static const struct dentry_operations tid_map_files_dentry_operations = { .d_delete = pid_delete_dentry, }; -static int map_files_get_link(struct dentry *dentry, struct path *path) +static int map_files_get_link(struct dentry *dentry, struct path *path, + struct task_struct *task) { unsigned long vm_start, vm_end; struct vm_area_struct *vma; - struct task_struct *task; struct mm_struct *mm; int rc; rc = -ENOENT; - task = get_proc_task(d_inode(dentry)); - if (!task) - goto out; - mm = get_task_mm(task); - put_task_struct(task); if (!mm) goto out; diff --git a/fs/proc/fd.c b/fs/proc/fd.c index 05c7513e77c7e..0f9a1556f2a34 100644 --- a/fs/proc/fd.c +++ b/fs/proc/fd.c @@ -171,24 +171,19 @@ static const struct dentry_operations tid_fd_dentry_operations = { .d_delete = pid_delete_dentry, }; -static int proc_fd_link(struct dentry *dentry, struct path *path) +static int proc_fd_link(struct dentry *dentry, struct path *path, + struct task_struct *task) { - struct task_struct *task; int ret = -ENOENT; - - task = get_proc_task(d_inode(dentry)); - if (task) { - unsigned int fd = proc_fd(d_inode(dentry)); - struct file *fd_file; - - fd_file = fget_task(task, fd); - if (fd_file) { - *path = fd_file->f_path; - path_get(&fd_file->f_path); - ret = 0; - fput(fd_file); - } - put_task_struct(task); + unsigned int fd = proc_fd(d_inode(dentry)); + struct file *fd_file; + + fd_file = fget_task(task, fd); + if (fd_file) { + *path = fd_file->f_path; + path_get(&fd_file->f_path); + ret = 0; + fput(fd_file); } return ret; diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 64dc44832808a..d31984c3c7979 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -107,7 +107,7 @@ extern struct kmem_cache *proc_dir_entry_cache; void pde_free(struct proc_dir_entry *pde); union proc_op { - int (*proc_get_link)(struct dentry *, struct path *); + int (*proc_get_link)(struct dentry *, struct path *, struct task_struct *); int (*proc_show)(struct seq_file *m, struct pid_namespace *ns, struct pid *pid, struct task_struct *task); From a5f5f5053f9883fe2a839eedd2419b5f9b88a09a Mon Sep 17 00:00:00 2001 From: Zide Chen Date: Tue, 2 Jun 2026 07:49:05 -0700 Subject: [PATCH 0399/2758] perf/x86/intel/uncore: Defer ADL global PMON enable to enable_box() commit 9a0bb848a37150aeccc10088e141339917d995dc upstream. On some Raptor Cove CPUs, enabling uncore PMON globally at driver init may increase power consumption even when no perf events are in use. Drop adl_uncore_msr_init_box() and defer programming the global control register to enable_box(), so it is only set when a box is actually used. IMC and IMC freerunning counters use a separate control path and are unaffected. Fixes: 772ed05f3c5c ("perf/x86/intel/uncore: Add Alder Lake support") Signed-off-by: Zide Chen Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Dapeng Mi Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260602144908.263680-5-zide.chen@intel.com Signed-off-by: Greg Kroah-Hartman --- arch/x86/events/intel/uncore_snb.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/arch/x86/events/intel/uncore_snb.c b/arch/x86/events/intel/uncore_snb.c index 3dbc6bacbd9de..edddd4f9ab5fc 100644 --- a/arch/x86/events/intel/uncore_snb.c +++ b/arch/x86/events/intel/uncore_snb.c @@ -563,12 +563,6 @@ void tgl_uncore_cpu_init(void) skl_uncore_msr_ops.init_box = rkl_uncore_msr_init_box; } -static void adl_uncore_msr_init_box(struct intel_uncore_box *box) -{ - if (box->pmu->pmu_idx == 0) - wrmsrq(ADL_UNC_PERF_GLOBAL_CTL, SNB_UNC_GLOBAL_CTL_EN); -} - static void adl_uncore_msr_enable_box(struct intel_uncore_box *box) { wrmsrq(ADL_UNC_PERF_GLOBAL_CTL, SNB_UNC_GLOBAL_CTL_EN); @@ -587,7 +581,6 @@ static void adl_uncore_msr_exit_box(struct intel_uncore_box *box) } static struct intel_uncore_ops adl_uncore_msr_ops = { - .init_box = adl_uncore_msr_init_box, .enable_box = adl_uncore_msr_enable_box, .disable_box = adl_uncore_msr_disable_box, .exit_box = adl_uncore_msr_exit_box, From e0d0adc3d2040dacd7563391ec78d4ac64431ded Mon Sep 17 00:00:00 2001 From: Fushuai Wang Date: Wed, 20 May 2026 11:21:19 +0800 Subject: [PATCH 0400/2758] cpufreq: intel_pstate: Sync policy->cur during CPU offline commit bcbdaa1086c25a8a5d48e04e1b82fdfb0682b681 upstream. When a CPU goes offline with HWP disabled, intel_pstate_set_min_pstate() sets the MSR_IA32_PERF_CTL to minimum frequency to prevent SMT siblings from being restricted. However, the policy->cur value was not updated, leaving it at the previous value. When the CPU comes back online, governor->limits() checks if target_freq equals policy->cur and skips the frequency adjustment if they match. Since policy->cur still holds the previous value, the governor does not call cpufreq_driver->target to update MSR_IA32_PERF_CTL. Fix this by synchronizing policy->cur with the hardware state when setting minimum pstate during CPU offline. Fixes: bb18008f8086 ("intel_pstate: Set core to min P state during core offline") Cc: stable@vger.kernel.org # 3.15+ Suggested-by: Srinivas Pandruvada Signed-off-by: Fushuai Wang [ rjw: Subject refinement ] Link: https://patch.msgid.link/20260520032119.30615-1-fushuai.wang@linux.dev Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/cpufreq/intel_pstate.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 1f093e3464301..0fbbdbd5765cf 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -2984,10 +2984,12 @@ static int intel_cpufreq_cpu_offline(struct cpufreq_policy *policy) * from getting to lower performance levels, so force the minimum * performance on CPU offline to prevent that from happening. */ - if (hwp_active) + if (hwp_active) { intel_pstate_hwp_offline(cpu); - else + } else { intel_pstate_set_min_pstate(cpu); + policy->cur = cpu->pstate.min_freq; + } intel_pstate_exit_perf_limits(policy); From a18f80bf5359238c4f067d691b96af00286fdd89 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Fri, 15 May 2026 10:37:40 -0400 Subject: [PATCH 0401/2758] sched/rt: Have RT_PUSH_IPI be default off for non PREEMPT_RT commit dd29c017aed628076e915fe4cdfb5392fd4c5cab upstream. RT migration is done aggressively. When a CPU schedules out a high priority RT task for a lower priority task, it will look to see if there's any RT tasks that are waiting to run on another CPU that is of higher priority than the task this CPU is about to run. If it finds one, it will pull that task over to the CPU and allow it to run there instead. Normally, this pulling is done by looking at the RT overloaded mask (rto) which contains all the CPUs in the scheduler domain with RT tasks that are waiting to run due to a higher priority RT task currently running on their CPU. The CPU that is about to schedule a lower priority task will grab the rq lock of the overloaded CPU and move the RT task from that CPU's runqueue to the local one and schedule the higher priority RT task. This caused issues when a lot of CPUs would schedule a lower priority task at the same time. They would all try to grab the same runqueue lock of the CPU with the overloaded RT tasks. Only the first CPU that got in will get that task. All the others would wait until they got the runqueue lock and see there's nothing to pull and do nothing. On systems with lots of CPUs, this caused a large latency (up to 500us) which is beyond what PREEMPT_RT is to allow. The solution to that was to create an RT_PUSH_IPI logic. When any CPU wanted to pull a task, instead of grabbing the runqueue lock of the overloaded CPU, it would start by sending an IPI to the overloaded CPU, and that IPI handler would have the CPU with the waiting RT task do a push instead. Then that handler would send an IPI to the next CPU with overloaded RT tasks, and so on. Note, after the first CPU starts this process, if another CPU wanted to do a pull, it would see that the process has already begun and would only increment a counter to have the IPIs continue again. The RT_PUSH_IPI solved the latency problem with PREEMPT_RT but could cause a new issue with non PREEMPT_RT. Namely, softirqs run in a threaded context on PREEMPT_RT but they can run in an interrupt context in non-RT. If an IPI lands on a CPU that has just woken up multiple RT tasks and the current CPU is running a non RT or a low priority RT task, instead of doing a push, it would simply do a schedule on that CPU. But if a softirq was also executing on this CPU, the schedule would need to wait until the softirq finished. Until then, the CPU would still be considered overloaded as there are RT tasks still waiting to run on it. A live lock occurred on a workload that was doing heavy networking traffic on a large machine where the softirqs would run 500us out of 750us. And it would also be waking up RT tasks, causing the RT pull logic to be constantly executed. When a softirq triggered on a CPU with RT tasks queued but not running yet, and the other CPUs would see this CPU as being overloaded, they would send an IPI over to it. The CPU would notice that the waiting RT tasks are of higher priority than the currently running task and simply schedule that CPU instead. But because the softirq was executing, before it could schedule, it would receive another IPI to do the same. The amount of IPIs would slow down the currently running softirq so much that before it could return back to task context, it would execute another softirq never allowing the CPU to schedule. This live locked that CPU. As RT_PUSH_IPI was created to help PREEMPT_RT, make it default off if PREEMPT_RT is not enabled. Fixes: b6366f048e0c ("sched/rt: Use IPI to trigger RT task push migration instead of pulling") Closes: https://lore.kernel.org/all/20260506235716.2530720-1-tj@kernel.org/ Reported-by: Tejun Heo Signed-off-by: Steven Rostedt Signed-off-by: Peter Zijlstra (Intel) Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260515103740.25ccbed8@gandalf.local.home Signed-off-by: Greg Kroah-Hartman --- kernel/sched/features.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/kernel/sched/features.h b/kernel/sched/features.h index 84c4fe3abd743..8f0dee8fc4755 100644 --- a/kernel/sched/features.h +++ b/kernel/sched/features.h @@ -110,8 +110,16 @@ SCHED_FEAT(WARN_DOUBLE_CLOCK, false) * rq lock and possibly create a large contention, sending an * IPI to that CPU and let that CPU push the RT task to where * it should go may be a better scenario. + * + * This is best for PREEMPT_RT, but for non-RT it can cause issues + * when preemption is disabled for long periods of time. Have + * it only default enabled for PREEMPT_RT. */ +# ifdef CONFIG_PREEMPT_RT SCHED_FEAT(RT_PUSH_IPI, true) +# else +SCHED_FEAT(RT_PUSH_IPI, false) +# endif #endif SCHED_FEAT(RT_RUNTIME_SHARE, false) From a0106b41f9a724868d390b8b3b4ea5ca0e04ea53 Mon Sep 17 00:00:00 2001 From: Tianxiang Chen Date: Wed, 8 Apr 2026 22:19:14 +0800 Subject: [PATCH 0402/2758] cpufreq: Fix hotplug-suspend race during reboot commit a9029dd55696c651ee46912afa2a166fa456bb3e upstream. During system reboot, cpufreq_suspend() is called via the kernel_restart() -> device_shutdown() path. Unlike the normal system suspend path, the reboot path does not call freeze_processes(), so userspace processes and kernel threads remain active. This allows CPU hotplug operations to run concurrently with cpufreq_suspend(). The original code has no synchronization with CPU hotplug, leading to a race condition where governor_data can be freed by the hotplug path while cpufreq_suspend() is still accessing it, resulting in a null pointer dereference: Unable to handle kernel NULL pointer dereference Call Trace: do_kernel_fault+0x28/0x3c cpufreq_suspend+0xdc/0x160 device_shutdown+0x18/0x200 kernel_restart+0x40/0x80 arm64_sys_reboot+0x1b0/0x200 Fix this by adding cpus_read_lock()/cpus_read_unlock() to cpufreq_suspend() to block CPU hotplug operations while suspend is in progress. Fixes: 65650b35133f ("cpufreq: Avoid cpufreq_suspend() deadlock on system shutdown") Signed-off-by: Tianxiang Chen Reviewed-by: Zhongqiu Han Cc: All applicable [ rjw: Changelog edits ] Link: https://patch.msgid.link/20260408141914.35281-1-nanmu@xiaomi.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/cpufreq/cpufreq.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 44eb1b7e7fc1b..d41067cd1f1b2 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1972,6 +1972,7 @@ void cpufreq_suspend(void) if (!cpufreq_driver) return; + cpus_read_lock(); if (!has_target() && !cpufreq_driver->suspend) goto suspend; @@ -1991,6 +1992,7 @@ void cpufreq_suspend(void) suspend: cpufreq_suspended = true; + cpus_read_unlock(); } /** From 0e3c739a2f6fc1de5b19a8839ab80696b9cb2a29 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Thu, 16 Apr 2026 10:46:21 -0400 Subject: [PATCH 0403/2758] cpufreq: pcc: fix use-after-free and double free in _OSC evaluation commit 266d3dd8b757b48a576e90f018b51f7b7563cc32 upstream. pcc_cpufreq_do_osc() calls acpi_evaluate_object() twice for the two-phase _OSC negotiation. Between the two calls it freed output.pointer but left output.length unchanged. Since acpi_evaluate_object() treats a non-zero length with a non-NULL pointer as an existing buffer to write into, the second call wrote into freed memory (use-after-free). The subsequent kfree(output.pointer) at out_free then freed the same pointer a second time (double free). Reset output.pointer to NULL and output.length to ACPI_ALLOCATE_BUFFER after freeing the first result, so ACPICA allocates a fresh buffer for each phase independently. Fixes: 0f1d683fb35d ("[CPUFREQ] Processor Clocking Control interface driver") Signed-off-by: Yuho Choi Reviewed-by: Zhongqiu Han Cc: All applicable Link: https://patch.msgid.link/20260416144621.93964-1-dbgh9129@gmail.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/cpufreq/pcc-cpufreq.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/cpufreq/pcc-cpufreq.c b/drivers/cpufreq/pcc-cpufreq.c index ac2e90a65f0c4..a355ec4f3dd4c 100644 --- a/drivers/cpufreq/pcc-cpufreq.c +++ b/drivers/cpufreq/pcc-cpufreq.c @@ -352,6 +352,8 @@ static int __init pcc_cpufreq_do_osc(acpi_handle *handle) } kfree(output.pointer); + output.pointer = NULL; + output.length = ACPI_ALLOCATE_BUFFER; capabilities[0] = 0x0; capabilities[1] = 0x1; From c1cfd63326f5d09999134e9052c353faf738286e Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Mon, 18 May 2026 18:35:15 +0200 Subject: [PATCH 0404/2758] proc: protect ptrace_may_access() with exec_update_lock (part 1) commit 6650527444dadc63d84aa939d14ecba4fadb2f69 upstream. Fix the easy cases where procfs currently calls ptrace_may_access() without exec_update_lock protection, where the fix is to simply add the extra lock or use mm_access(): - do_task_stat(): grab exec_update_lock - proc_pid_wchan(): grab exec_update_lock - proc_map_files_lookup(): use mm_access() instead of get_task_mm() - proc_map_files_readdir(): use mm_access() instead of get_task_mm() - proc_ns_get_link(): grab exec_update_lock - proc_ns_readlink(): grab exec_update_lock Fixes: f83ce3e6b02d ("proc: avoid information leaks to non-privileged processes") Cc: stable@vger.kernel.org Signed-off-by: Jann Horn Link: https://patch.msgid.link/20260518-procfs-lockfix-part1-v1-1-5c3d20e0ac33@google.com Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/proc/array.c | 6 ++++++ fs/proc/base.c | 41 ++++++++++++++++++++++------------------- fs/proc/namespaces.c | 12 ++++++++++++ 3 files changed, 40 insertions(+), 19 deletions(-) diff --git a/fs/proc/array.c b/fs/proc/array.c index 90fb0c6b5f99f..479ea8cb4ef43 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -482,6 +482,11 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, unsigned long flags; int exit_code = task->exit_code; struct signal_struct *sig = task->signal; + int ret; + + ret = down_read_killable(&task->signal->exec_update_lock); + if (ret) + return ret; state = *get_task_state(task); vsize = eip = esp = 0; @@ -657,6 +662,7 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, seq_puts(m, " 0"); seq_putc(m, '\n'); + up_read(&task->signal->exec_update_lock); if (mm) mmput(mm); return 0; diff --git a/fs/proc/base.c b/fs/proc/base.c index 2b7348dca8ba0..5042f14d24f36 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -414,18 +414,24 @@ static int proc_pid_wchan(struct seq_file *m, struct pid_namespace *ns, { unsigned long wchan; char symname[KSYM_NAME_LEN]; + int err; + err = down_read_killable(&task->signal->exec_update_lock); + if (err) + return err; if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) goto print0; wchan = get_wchan(task); if (wchan && !lookup_symbol_name(wchan, symname)) { seq_puts(m, symname); + up_read(&task->signal->exec_update_lock); return 0; } print0: seq_putc(m, '0'); + up_read(&task->signal->exec_update_lock); return 0; } #endif /* CONFIG_KALLSYMS */ @@ -2335,17 +2341,15 @@ static struct dentry *proc_map_files_lookup(struct inode *dir, if (!task) goto out; - result = ERR_PTR(-EACCES); - if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) - goto out_put_task; - result = ERR_PTR(-ENOENT); if (dname_to_vma_addr(dentry, &vm_start, &vm_end)) goto out_put_task; - mm = get_task_mm(task); - if (!mm) + mm = mm_access(task, PTRACE_MODE_READ_FSCREDS); + if (IS_ERR(mm)) { + result = ERR_CAST(mm); goto out_put_task; + } result = ERR_PTR(-EINTR); if (mmap_read_lock_killable(mm)) @@ -2395,23 +2399,22 @@ proc_map_files_readdir(struct file *file, struct dir_context *ctx) if (!task) goto out; - ret = -EACCES; - if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) - goto out_put_task; - ret = 0; if (!dir_emit_dots(file, ctx)) goto out_put_task; - mm = get_task_mm(task); - if (!mm) + mm = mm_access(task, PTRACE_MODE_READ_FSCREDS); + if (IS_ERR(mm)) { + ret = PTR_ERR(mm); + /* if the task has no mm, the directory should just be empty */ + if (ret == -ESRCH) + ret = 0; goto out_put_task; + } ret = mmap_read_lock_killable(mm); - if (ret) { - mmput(mm); - goto out_put_task; - } + if (ret) + goto out_put_mm; nr_files = 0; @@ -2437,8 +2440,7 @@ proc_map_files_readdir(struct file *file, struct dir_context *ctx) if (!p) { ret = -ENOMEM; mmap_read_unlock(mm); - mmput(mm); - goto out_put_task; + goto out_put_mm; } p->start = vma->vm_start; @@ -2446,7 +2448,6 @@ proc_map_files_readdir(struct file *file, struct dir_context *ctx) p->mode = vma->vm_file->f_mode; } mmap_read_unlock(mm); - mmput(mm); for (i = 0; i < nr_files; i++) { char buf[4 * sizeof(long) + 2]; /* max: %lx-%lx\0 */ @@ -2463,6 +2464,8 @@ proc_map_files_readdir(struct file *file, struct dir_context *ctx) ctx->pos++; } +out_put_mm: + mmput(mm); out_put_task: put_task_struct(task); out: diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c index 39f4169f669f6..2f46f13967445 100644 --- a/fs/proc/namespaces.c +++ b/fs/proc/namespaces.c @@ -55,6 +55,10 @@ static const char *proc_ns_get_link(struct dentry *dentry, if (!task) return ERR_PTR(-EACCES); + error = down_read_killable(&task->signal->exec_update_lock); + if (error) + goto out_put_task; + if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) goto out; @@ -64,6 +68,8 @@ static const char *proc_ns_get_link(struct dentry *dentry, error = nd_jump_link(&ns_path); out: + up_read(&task->signal->exec_update_lock); +out_put_task: put_task_struct(task); return ERR_PTR(error); } @@ -80,11 +86,17 @@ static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int bufl if (!task) return res; + res = down_read_killable(&task->signal->exec_update_lock); + if (res) + goto out_put_task; + if (ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) { res = ns_get_name(name, sizeof(name), task, ns_ops); if (res >= 0) res = readlink_copy(buffer, buflen, name, strlen(name)); } + up_read(&task->signal->exec_update_lock); +out_put_task: put_task_struct(task); return res; } From 8f06363446c5d043c9a7c008b250040e9de98cf9 Mon Sep 17 00:00:00 2001 From: WenTao Liang Date: Fri, 12 Jun 2026 00:17:38 +0800 Subject: [PATCH 0405/2758] posix-cpu-timers: Fix pid refcount leak in do_cpu_nanosleep() error path commit 87bd2ad568e15b90d5f7d4bcd70342d05dad649c upstream. In do_cpu_nanosleep(), posix_cpu_timer_create() takes a pid reference via get_pid() and stores it in timer.it.cpu.pid. If the subsequent posix_cpu_timer_set() call fails, the function returns immediately without calling posix_cpu_timer_del() to release the pid reference, causing a leak. Fix it by calling posix_cpu_timer_del() before the unlock-and-return on the error path, consistent with the other exit paths in the same function. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: WenTao Liang Signed-off-by: Thomas Gleixner Reviewed-by: Frederic Weisbecker Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260611161738.97043-1-vulab@iscas.ac.cn Signed-off-by: Greg Kroah-Hartman --- kernel/time/posix-cpu-timers.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c index 0de2bb7cbec01..6f3ddb2b1f469 100644 --- a/kernel/time/posix-cpu-timers.c +++ b/kernel/time/posix-cpu-timers.c @@ -1504,6 +1504,7 @@ static int do_cpu_nanosleep(const clockid_t which_clock, int flags, spin_lock_irq(&timer.it_lock); error = posix_cpu_timer_set(&timer, flags, &it, NULL); if (error) { + posix_cpu_timer_del(&timer); spin_unlock_irq(&timer.it_lock); return error; } From 75b478096c6bbf57fe366f7f0a8cd5365043ffaa Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 9 Jun 2026 17:14:45 +0200 Subject: [PATCH 0406/2758] time/jiffies: Register jiffies clocksource before usage commit f24df84cbe05e4471c04ac4b921fc0340bbc7752 upstream. Teddy reported that a XEN HVM has a long boot delay, which was bisected to the recent enhancements to the negative motion detection. It turned out that the jiffies clocksource is used in early boot before it is registered, which leaves the max_delta_raw field at zero. That causes the read out to be clamped to the max delta of 0, which means time is not making progress. Cure it by ensuring that it is initialized before its first usage in timekeeping_init(). Fixes: 76031d9536a0 ("clocksource: Make negative motion detection more robust") Reported-by: Teddy Astie Signed-off-by: Thomas Gleixner Tested-by: Teddy Astie Cc: stable@vger.kernel.org Link: https://patch.msgid.link/87y0gn3fve.ffs@fw13 Closes: https://lore.kernel.org/all/1780914594.8631fc262581453bbf619ec5b2062170.19ea6c8227b000701b@vates.tech Signed-off-by: Greg Kroah-Hartman --- kernel/time/jiffies.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/kernel/time/jiffies.c b/kernel/time/jiffies.c index 1c954f330dfe7..d51428867a339 100644 --- a/kernel/time/jiffies.c +++ b/kernel/time/jiffies.c @@ -60,15 +60,14 @@ EXPORT_SYMBOL(get_jiffies_64); EXPORT_SYMBOL(jiffies); -static int __init init_jiffies_clocksource(void) -{ - return __clocksource_register(&clocksource_jiffies); -} - -core_initcall(init_jiffies_clocksource); +static bool cs_jiffies_registered __initdata; struct clocksource * __init __weak clocksource_default_clock(void) { + if (!cs_jiffies_registered) { + __clocksource_register(&clocksource_jiffies); + cs_jiffies_registered = true; + } return &clocksource_jiffies; } From c67b06370ade3a60f4981f8abcaf652334cff1ba Mon Sep 17 00:00:00 2001 From: Kartik Rajput Date: Thu, 7 May 2026 21:15:54 +0530 Subject: [PATCH 0407/2758] clocksource/drivers/timer-tegra186: Fix support for multiple watchdog instances commit ca57bf46e7a94f8c53d05c376df9fcfdcb482100 upstream. Tegra SoCs support multiple watchdogs; currently only one (WDT0) is used. When multiple watchdogs are registered, tegra186_wdt_enable() overwrites the TKEIE(x) register, discarding any existing watchdog interrupt enable bits. As a result, enabling one watchdog inadvertently disables interrupts for the others. Fix this by preserving the existing TKEIE(x) value and updating it using a read-modify-write sequence. Fixes: 42cee19a9f83 ("clocksource: Add Tegra186 timers support") Cc: stable@vger.kernel.org Signed-off-by: Kartik Rajput Signed-off-by: Daniel Lezcano Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20260507154557.2082697-2-kkartik@nvidia.com Signed-off-by: Greg Kroah-Hartman --- drivers/clocksource/timer-tegra186.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/clocksource/timer-tegra186.c b/drivers/clocksource/timer-tegra186.c index 355558893e5f3..bfe16d2d5104e 100644 --- a/drivers/clocksource/timer-tegra186.c +++ b/drivers/clocksource/timer-tegra186.c @@ -149,7 +149,8 @@ static void tegra186_wdt_enable(struct tegra186_wdt *wdt) u32 value; /* unmask hardware IRQ, this may have been lost across powergate */ - value = TKEIE_WDT_MASK(wdt->index, 1); + value = readl(tegra->regs + TKEIE(wdt->tmr->hwirq)); + value |= TKEIE_WDT_MASK(wdt->index, 1); writel(value, tegra->regs + TKEIE(wdt->tmr->hwirq)); /* clear interrupt */ From c94806905e02cc8e17a69c822d93c41743b7ffc5 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Thu, 11 Jun 2026 17:37:46 +0200 Subject: [PATCH 0408/2758] s390: Revert support for DCACHE_WORD_ACCESS commit 37540b8c287fc817bdbd0c62bb75ad6eab0e5d03 upstream. load_unaligned_zeropad() reads eight bytes from unaligned addresses and may cross page boundaries. It handles exceptions which may happen if reading from the second page results in an exception. For pages which are donated to the Ultravisor for secure execution purposes the do_secure_storage_access() exception handler however does not handle such exceptions correctly. Such an exception may result in an endless exception loop which will never be resolved. An attempt to fix this [1] turned out to be not sufficient. For now revert load_unaligned_zeropad() until this problem has been resolved in a proper way. Note that the implementation of load_unaligned_zeropad() itself is correct. The revert is just a temporary workaround until there is complete fix for secure storage access exceptions. [1] commit b00be77302d7 ("s390/mm: Add missing secure storage access fixups for donated memory") Fixes: 802ba53eefc5 ("s390: add support for DCACHE_WORD_ACCESS") Cc: stable@vger.kernel.org Acked-by: Christian Borntraeger Signed-off-by: Heiko Carstens Signed-off-by: Alexander Gordeev Signed-off-by: Greg Kroah-Hartman --- arch/s390/Kconfig | 1 - arch/s390/include/asm/asm-extable.h | 4 ---- arch/s390/include/asm/word-at-a-time.h | 22 ---------------------- arch/s390/mm/extable.c | 18 ------------------ 4 files changed, 45 deletions(-) diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 9921a3772bf73..c7a65e129dbc1 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -163,7 +163,6 @@ config S390 select ARCH_WANTS_THP_SWAP select BUILDTIME_TABLE_SORT select CLONE_BACKWARDS2 - select DCACHE_WORD_ACCESS if !KMSAN select DYNAMIC_FTRACE if FUNCTION_TRACER select FUNCTION_ALIGNMENT_8B if CC_IS_GCC select FUNCTION_ALIGNMENT_16B if !CC_IS_GCC diff --git a/arch/s390/include/asm/asm-extable.h b/arch/s390/include/asm/asm-extable.h index d23ea0c94e4ea..99748c20e7671 100644 --- a/arch/s390/include/asm/asm-extable.h +++ b/arch/s390/include/asm/asm-extable.h @@ -12,7 +12,6 @@ #define EX_TYPE_UA_FAULT 3 #define EX_TYPE_UA_LOAD_REG 5 #define EX_TYPE_UA_LOAD_REGPAIR 6 -#define EX_TYPE_ZEROPAD 7 #define EX_TYPE_FPC 8 #define EX_TYPE_UA_MVCOS_TO 9 #define EX_TYPE_UA_MVCOS_FROM 10 @@ -80,9 +79,6 @@ #define EX_TABLE_UA_LOAD_REGPAIR(_fault, _target, _regerr, _regzero) \ __EX_TABLE(__ex_table, _fault, _target, EX_TYPE_UA_LOAD_REGPAIR, _regerr, _regzero, 0) -#define EX_TABLE_ZEROPAD(_fault, _target, _regdata, _regaddr) \ - __EX_TABLE(__ex_table, _fault, _target, EX_TYPE_ZEROPAD, _regdata, _regaddr, 0) - #define EX_TABLE_FPC(_fault, _target) \ __EX_TABLE(__ex_table, _fault, _target, EX_TYPE_FPC, __stringify(%%r0), __stringify(%%r0), 0) diff --git a/arch/s390/include/asm/word-at-a-time.h b/arch/s390/include/asm/word-at-a-time.h index eaa19dee76994..e9287036392d0 100644 --- a/arch/s390/include/asm/word-at-a-time.h +++ b/arch/s390/include/asm/word-at-a-time.h @@ -4,7 +4,6 @@ #include #include -#include #include struct word_at_a_time { @@ -41,25 +40,4 @@ static inline unsigned long zero_bytemask(unsigned long data) return ~1UL << data; } -/* - * Load an unaligned word from kernel space. - * - * In the (very unlikely) case of the word being a page-crosser - * and the next page not being mapped, take the exception and - * return zeroes in the non-existing part. - */ -static inline unsigned long load_unaligned_zeropad(const void *addr) -{ - unsigned long data; - - asm_inline volatile( - "0: lg %[data],0(%[addr])\n" - "1: nopr %%r7\n" - EX_TABLE_ZEROPAD(0b, 1b, %[data], %[addr]) - EX_TABLE_ZEROPAD(1b, 1b, %[data], %[addr]) - : [data] "=d" (data) - : [addr] "a" (addr), "m" (*(unsigned long *)addr)); - return data; -} - #endif /* _ASM_WORD_AT_A_TIME_H */ diff --git a/arch/s390/mm/extable.c b/arch/s390/mm/extable.c index 7498e858c4019..063b4346742d9 100644 --- a/arch/s390/mm/extable.c +++ b/arch/s390/mm/extable.c @@ -50,22 +50,6 @@ static bool ex_handler_ua_load_reg(const struct exception_table_entry *ex, return true; } -static bool ex_handler_zeropad(const struct exception_table_entry *ex, struct pt_regs *regs) -{ - unsigned int reg_addr = FIELD_GET(EX_DATA_REG_ADDR, ex->data); - unsigned int reg_data = FIELD_GET(EX_DATA_REG_ERR, ex->data); - unsigned long data, addr, offset; - - addr = regs->gprs[reg_addr]; - offset = addr & (sizeof(unsigned long) - 1); - addr &= ~(sizeof(unsigned long) - 1); - data = *(unsigned long *)addr; - data <<= BITS_PER_BYTE * offset; - regs->gprs[reg_data] = data; - regs->psw.addr = extable_fixup(ex); - return true; -} - static bool ex_handler_fpc(const struct exception_table_entry *ex, struct pt_regs *regs) { fpu_sfpc(0); @@ -134,8 +118,6 @@ bool fixup_exception(struct pt_regs *regs) return ex_handler_ua_load_reg(ex, false, regs); case EX_TYPE_UA_LOAD_REGPAIR: return ex_handler_ua_load_reg(ex, true, regs); - case EX_TYPE_ZEROPAD: - return ex_handler_zeropad(ex, regs); case EX_TYPE_FPC: return ex_handler_fpc(ex, regs); case EX_TYPE_UA_MVCOS_TO: From 942dfe844229a8b8b1f32dadfa2388fae0295b20 Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Fri, 29 May 2026 15:33:45 +0100 Subject: [PATCH 0409/2758] perf/arm-cmn: Fix DVM node events commit 5936245125f78d896fdb1bbc2ae79213e28a6579 upstream. The new DVM node events added in CMN-700 also apply to CMN S3; fix the model encoding so that we can expose the aliases and handle occupancy filtering on newer CMNs too. Cc: stable@vger.kernel.org Fixes: 0dc2f4963f7e ("perf/arm-cmn: Support CMN S3") Signed-off-by: Robin Murphy Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman --- drivers/perf/arm-cmn.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/perf/arm-cmn.c b/drivers/perf/arm-cmn.c index f5305c8fdca43..6e5cc4086a9e2 100644 --- a/drivers/perf/arm-cmn.c +++ b/drivers/perf/arm-cmn.c @@ -197,13 +197,14 @@ enum cmn_model { CMN600 = 1, CMN650 = 2, - CMN700 = 4, - CI700 = 8, + CI700 = 4, + CMN700 = 8, CMNS3 = 16, /* ...and then we can use bitmap tricks for commonality */ CMN_ANY = -1, NOT_CMN600 = -2, - CMN_650ON = CMN650 | CMN700 | CMNS3, + CMN_700ON = ~(CMN700 - 1), + CMN_650ON = CMN_700ON | CMN650, }; /* Actual part numbers and revision IDs defined by the hardware */ @@ -919,14 +920,14 @@ static struct attribute *arm_cmn_event_attrs[] = { CMN_EVENT_DVM(NOT_CMN600, txsnp_stall, 0x0a), CMN_EVENT_DVM(NOT_CMN600, trkfull, 0x0b), CMN_EVENT_DVM_OCC(NOT_CMN600, trk_occupancy, 0x0c), - CMN_EVENT_DVM_OCC(CMN700, trk_occupancy_cxha, 0x0d), - CMN_EVENT_DVM_OCC(CMN700, trk_occupancy_pdn, 0x0e), - CMN_EVENT_DVM(CMN700, trk_alloc, 0x0f), - CMN_EVENT_DVM(CMN700, trk_cxha_alloc, 0x10), - CMN_EVENT_DVM(CMN700, trk_pdn_alloc, 0x11), - CMN_EVENT_DVM(CMN700, txsnp_stall_limit, 0x12), - CMN_EVENT_DVM(CMN700, rxsnp_stall_starv, 0x13), - CMN_EVENT_DVM(CMN700, txsnp_sync_stall_op, 0x14), + CMN_EVENT_DVM_OCC(CMN_700ON, trk_occupancy_cxha, 0x0d), + CMN_EVENT_DVM_OCC(CMN_700ON, trk_occupancy_pdn, 0x0e), + CMN_EVENT_DVM(CMN_700ON, trk_alloc, 0x0f), + CMN_EVENT_DVM(CMN_700ON, trk_cxha_alloc, 0x10), + CMN_EVENT_DVM(CMN_700ON, trk_pdn_alloc, 0x11), + CMN_EVENT_DVM(CMN_700ON, txsnp_stall_limit, 0x12), + CMN_EVENT_DVM(CMN_700ON, rxsnp_stall_starv, 0x13), + CMN_EVENT_DVM(CMN_700ON, txsnp_sync_stall_op, 0x14), CMN_EVENT_HNF(CMN_ANY, cache_miss, 0x01), CMN_EVENT_HNF(CMN_ANY, slc_sf_cache_access, 0x02), From d33dc0d5824cddc84561a899169e3e4a2c19a0b6 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Thu, 14 May 2026 08:55:58 +0200 Subject: [PATCH 0410/2758] X.509: Fix validation of ASN.1 certificate header commit 3b626ba431c4501512ad07549310685e07fe4706 upstream. x509_load_certificate_list() seeks to enforce that a certificate starts with 0x30 0x82 (ASN.1 SEQUENCE tag followed by a length of more than 256 and less than 65535 bytes). But it only enforces that *either* of those two byte values are present, instead of checking for the *conjunction* of the two values. Fix it. Fixes: 631cc66eb9ea ("MODSIGN: Provide module signing public keys to the kernel") Reported-by: Sashiko Closes: https://lore.kernel.org/r/20260508033917.B5873C2BCB0@smtp.kernel.org/ Signed-off-by: Lukas Wunner Cc: stable@vger.kernel.org # v3.7+ Reviewed-by: Ignat Korchagin Reviewed-by: Alistair Francis Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- crypto/asymmetric_keys/x509_loader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/asymmetric_keys/x509_loader.c b/crypto/asymmetric_keys/x509_loader.c index a417413269989..0d516c77cc26a 100644 --- a/crypto/asymmetric_keys/x509_loader.c +++ b/crypto/asymmetric_keys/x509_loader.c @@ -20,7 +20,7 @@ int x509_load_certificate_list(const u8 cert_list[], */ if (end - p < 4) goto dodgy_cert; - if (p[0] != 0x30 && + if (p[0] != 0x30 || p[1] != 0x82) goto dodgy_cert; plen = (p[2] << 8) | p[3]; From 0d18ccef142f04433dfb2a0c120cf223d2b8a42c Mon Sep 17 00:00:00 2001 From: "Vlastimil Babka (SUSE)" Date: Wed, 10 Jun 2026 17:40:03 +0200 Subject: [PATCH 0411/2758] mm/slab: do not limit zeroing to orig_size when only red zoning is enabled commit 648927ceb84021a25a0fbd5673740956f318d534 upstream. When init (zeroing) on allocation is requested, for kmalloc() we generally have to zero the full object size even if a smaller size is requested, in order to provide krealloc()'s __GFP_ZERO guarantees. But if we track the requested size, krealloc() uses that information to do the right thing, so we can zero only the requested size. With red zoning also enabled, any extra size became part of the red zone, so it must not be zeroed and thus we must zero only the requested size. However the current check is imprecise, and will trigger also when only SLAB_RED_ZONE is enabled without SLAB_STORE_USER (which enables tracking the requested size). This means enabling red zoning alone can compromise krealloc()'s __GFP_ZERO contract. Fix this by using slub_debug_orig_size() instead, which is the exact check for whether the requested size is tracked. We don't need to care if red zoning is also enabled or not. Also update and expand the comment accordingly. Fixes: 9ce67395f5a0 ("mm/slub: only zero requested size of buffer for kzalloc when debug enabled") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260610-slab_alloc_flags-v2-1-7190909db118@kernel.org Reviewed-by: Harry Yoo (Oracle) Reviewed-by: Hao Li Signed-off-by: Vlastimil Babka (SUSE) Signed-off-by: Greg Kroah-Hartman --- mm/slub.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index a2bf3756ca7d0..9365501d0df14 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -4537,15 +4537,17 @@ bool slab_post_alloc_hook(struct kmem_cache *s, struct list_lru *lru, gfp_t init_flags = flags & gfp_allowed_mask; /* - * For kmalloc object, the allocated memory size(object_size) is likely - * larger than the requested size(orig_size). If redzone check is - * enabled for the extra space, don't zero it, as it will be redzoned - * soon. The redzone operation for this extra space could be seen as a - * replacement of current poisoning under certain debug option, and - * won't break other sanity checks. + * For kmalloc object, the allocated size (object_size) can be larger + * than the requested size (orig_size). We however need to zero the + * whole object_size to handle possible later krealloc() with + *__GFP_ZERO properly. + * + * But if we keep track of the requested size, krealloc() uses that + * information. Additionally if red zoning is enabled, the extra space + * is also red zone, so we should not overwrite it. So limit zeroing to + * orig_size if we track it. */ - if (kmem_cache_debug_flags(s, SLAB_STORE_USER | SLAB_RED_ZONE) && - (s->flags & SLAB_KMALLOC)) + if (slub_debug_orig_size(s)) zero_size = orig_size; /* From 27c83f66757504cf7442411709aa2e490cd1b813 Mon Sep 17 00:00:00 2001 From: Xuewen Wang Date: Mon, 18 May 2026 14:21:57 +0800 Subject: [PATCH 0412/2758] tools/mm/slabinfo: Fix trace disable logic inversion commit 235ab68d67eadbef1fdbfb771f21f5bacc77a2ae upstream. The disable trace path in slab_debug() had a logic error where it would set trace=1 instead of trace=0. This made trace functionality permanently enabled once turned on for any slab cache. Fixes: a87615b8f9e2 ("SLUB: slabinfo upgrade") Cc: stable@vger.kernel.org Reviewed-by: SeongJae Park Signed-off-by: Xuewen Wang WARNING: From:/Signed-off-by: email address mismatch: 'From: wangxuewen <18810879172@163.com>' != 'Signed-off-by: wangxuewen ' Link: https://patch.msgid.link/20260518062159.80664-2-wangxuewen@kylinos.cn Signed-off-by: Vlastimil Babka (SUSE) Signed-off-by: Greg Kroah-Hartman --- tools/mm/slabinfo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/mm/slabinfo.c b/tools/mm/slabinfo.c index 54c7265ab52d9..39f7eae7eecda 100644 --- a/tools/mm/slabinfo.c +++ b/tools/mm/slabinfo.c @@ -798,7 +798,7 @@ static void slab_debug(struct slabinfo *s) fprintf(stderr, "%s can only enable trace for one slab at a time\n", s->name); } if (!tracing && s->trace) - set_obj(s, "trace", 1); + set_obj(s, "trace", 0); } static void totals(void) From 493f261c0772c50b790f7d1f7c63a331f68a7769 Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Fri, 12 Jun 2026 15:13:59 +0800 Subject: [PATCH 0413/2758] tools/mm/slabinfo: fix total_objects attribute name commit 892a7864730775c3dbee2a39e9ead4fa8d4256e7 upstream. SLUB exports the total_objects sysfs attribute, but slabinfo tries to read objects_total. As a result, the lookup fails and the field remains zero. Use the correct attribute name and rename the corresponding structure member to match. Fixes: 205ab99dd103 ("slub: Update statistics handling for variable order slabs") Signed-off-by: Yichong Chen Cc: Reviewed-by: SeongJae Park Link: https://patch.msgid.link/96556748872BB47E+20260612071359.649946-1-chenyichong@uniontech.com Signed-off-by: Vlastimil Babka (SUSE) Signed-off-by: Greg Kroah-Hartman --- tools/mm/slabinfo.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/mm/slabinfo.c b/tools/mm/slabinfo.c index 39f7eae7eecda..583bb699404ed 100644 --- a/tools/mm/slabinfo.c +++ b/tools/mm/slabinfo.c @@ -33,7 +33,7 @@ struct slabinfo { unsigned int hwcache_align, object_size, objs_per_slab; unsigned int sanity_checks, slab_size, store_user, trace; int order, poison, reclaim_account, red_zone; - unsigned long partial, objects, slabs, objects_partial, objects_total; + unsigned long partial, objects, slabs, objects_partial, total_objects; unsigned long alloc_fastpath, alloc_slowpath; unsigned long free_fastpath, free_slowpath; unsigned long free_frozen, free_add_partial, free_remove_partial; @@ -1263,7 +1263,7 @@ static void read_slab_dir(void) slab->object_size = get_obj("object_size"); slab->objects = get_obj("objects"); slab->objects_partial = get_obj("objects_partial"); - slab->objects_total = get_obj("objects_total"); + slab->total_objects = get_obj("total_objects"); slab->objs_per_slab = get_obj("objs_per_slab"); slab->order = get_obj("order"); slab->partial = get_obj("partial"); From 835fcc8655569737e3f057d42875a96259db74c2 Mon Sep 17 00:00:00 2001 From: Tianchu Chen Date: Fri, 29 May 2026 13:42:47 +0000 Subject: [PATCH 0414/2758] HID: hid-goodix-spi: validate report size to prevent stack buffer overflow commit db0a0768d09273aadadeb76730cd658d720333a4 upstream. goodix_hid_set_raw_report() builds a protocol frame in a 128-byte stack buffer (tmp_buf), writing an 11-12 byte header followed by the caller-supplied report data. The HID core caps report size at HID_MAX_BUFFER_SIZE (16384) by default, while the driver does not set hid_ll_driver.max_buffer_size and performs no bounds checking before copying the payload: memcpy(tmp_buf + tx_len, buf, len); A hidraw SET_REPORT ioctl with a report larger than ~116 bytes overflows the stack buffer. Add a size check after constructing the header, rejecting reports that would exceed the buffer capacity. Discovered by Atuin - Automated Vulnerability Discovery Engine. Fixes: 75e16c8ce283 ("HID: hid-goodix: Add Goodix HID-over-SPI driver") Cc: stable@vger.kernel.org Signed-off-by: Tianchu Chen Reviewed-by: Dmitry Torokhov Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-goodix-spi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/hid/hid-goodix-spi.c b/drivers/hid/hid-goodix-spi.c index 80c0288a3a38b..288cb827e9d6d 100644 --- a/drivers/hid/hid-goodix-spi.c +++ b/drivers/hid/hid-goodix-spi.c @@ -520,6 +520,9 @@ static int goodix_hid_set_raw_report(struct hid_device *hid, memcpy(tmp_buf + tx_len, args, args_len); tx_len += args_len; + if (tx_len + len > sizeof(tmp_buf)) + return -EINVAL; + memcpy(tmp_buf + tx_len, buf, len); tx_len += len; From cfc0d283d931d6c8c2081f29e8a50ae8ec2ac069 Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Sat, 6 Jun 2026 18:15:52 +0000 Subject: [PATCH 0415/2758] HID: uhid: convert to hid_safe_input_report() commit 63a694c51bf120a37550890b8e7736b4888985e9 upstream. Commit 0a3fe972a7cb ("HID: core: Mitigate potential OOB by removing bogus memset()"), added a check in hid_report_raw_event() to reject reports if the received data size is smaller than expected. This was intended to prevent OOB errors by no longer allowing zeroing-out of shorter reports due to the lack of buffer size information. However, this leads to regressions in hid_report_raw_event(), where shorter than expected reports are rejected, even though their buffers are sufficiently large to be zero-padded. To solve this issue, Benjamin introduced a safer alternative in commit 206342541fc8 ("HID: core: introduce hid_safe_input_report()"), which forwards the buffer size and allows hid_report_raw_event() to safely zero-pad the data. Convert uhid to use hid_safe_input_report() and pass UHID_DATA_MAX as the buffer size. This prevents the reported regressions [1], allowing hid core to zero-pad the shorter reports safely as expected. Cc: stable@vger.kernel.org Fixes: 0a3fe972a7cb ("HID: core: Mitigate potential OOB by removing bogus memset()") Closes: https://lore.kernel.org/all/ahsh0UtTX6e0ZeHa@google.com/ [1] Signed-off-by: Carlos Llamas Reviewed-by: Lee Jones Closes: https://lore.kernel.org/all/ahsh0UtTX6e0ZeHa@google.com/ Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/uhid.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 524b53a3c87b3..37b60c3aaf66a 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -595,8 +595,8 @@ static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev) if (!READ_ONCE(uhid->running)) return -EINVAL; - hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input.data, - min_t(size_t, ev->u.input.size, UHID_DATA_MAX), 0); + hid_safe_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input.data, UHID_DATA_MAX, + min_t(size_t, ev->u.input.size, UHID_DATA_MAX), 0); return 0; } @@ -606,8 +606,8 @@ static int uhid_dev_input2(struct uhid_device *uhid, struct uhid_event *ev) if (!READ_ONCE(uhid->running)) return -EINVAL; - hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input2.data, - min_t(size_t, ev->u.input2.size, UHID_DATA_MAX), 0); + hid_safe_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input2.data, UHID_DATA_MAX, + min_t(size_t, ev->u.input2.size, UHID_DATA_MAX), 0); return 0; } From e2cc711a9df37f359159b21db56cea9c21f58a9c Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Thu, 4 Jun 2026 13:56:58 +0900 Subject: [PATCH 0416/2758] HID: wacom: stop hardware after post-start probe failures commit ec2612b8ad9e642596db011dd8b6568ef1edeaa1 upstream. wacom_parse_and_register() starts HID hardware before registering inputs and initializing pad LEDs/remotes. Those later steps can fail, but their error paths currently release Wacom resources without stopping the HID hardware. Route post-hid_hw_start() failures through hid_hw_stop() before releasing driver resources. This issue was identified during our ongoing static-analysis research while reviewing kernel code. Fixes: c1d6708bf0d3 ("HID: wacom: Do not register input devices until after hid_hw_start") Cc: stable@vger.kernel.org Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Reviewed-by: Dmitry Torokhov Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/wacom_sys.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 2220168bf1164..252771dcfd8f8 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -2462,16 +2462,16 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless) error = wacom_register_inputs(wacom); if (error) - goto fail; + goto fail_hw_stop; if (wacom->wacom_wac.features.device_type & WACOM_DEVICETYPE_PAD) { error = wacom_initialize_leds(wacom); if (error) - goto fail; + goto fail_hw_stop; error = wacom_initialize_remotes(wacom); if (error) - goto fail; + goto fail_hw_stop; } if (!wireless) { @@ -2485,14 +2485,14 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless) cancel_delayed_work_sync(&wacom->init_work); _wacom_query_tablet_data(wacom); error = -ENODEV; - goto fail_quirks; + goto fail_hw_stop; } if (features->device_type & WACOM_DEVICETYPE_WL_MONITOR) { error = hid_hw_open(hdev); if (error) { hid_err(hdev, "hw open failed\n"); - goto fail_quirks; + goto fail_hw_stop; } } @@ -2501,7 +2501,7 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless) return 0; -fail_quirks: +fail_hw_stop: hid_hw_stop(hdev); fail: wacom_release_resources(wacom); From 48218df0422009301723badd5ec99b5be3a41e2f Mon Sep 17 00:00:00 2001 From: Oleg Makarenko Date: Tue, 9 Jun 2026 19:00:27 +0300 Subject: [PATCH 0417/2758] HID: pidff: Use correct effect type in effect update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit b251598b8bf37300510868f739a79e07800d41ce upstream. When updating an existing effect, the effect type from the last created effect was sent to the device instead of the updated one. This caused incorrect reports when a game creates multiple different effects and updates only one that is not the last created. Fixes FFB in multiple games that create multiple simultaneous effects (Forza Horizon 5/6). Fixes: 224ee88fe395 ("Input: add force feedback driver for PID devices") Cc: stable@vger.kernel.org Tested-by: Oliver Roundtree Co-developed-by: Ryno Kotzé Signed-off-by: Ryno Kotzé Signed-off-by: Oleg Makarenko Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/usbhid/hid-pidff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c index c45f182d04480..5f4395f7c6458 100644 --- a/drivers/hid/usbhid/hid-pidff.c +++ b/drivers/hid/usbhid/hid-pidff.c @@ -522,7 +522,7 @@ static void pidff_set_effect_report(struct pidff_device *pidff, pidff->set_effect[PID_EFFECT_BLOCK_INDEX].value[0] = pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; pidff->set_effect_type->value[0] = - pidff->create_new_effect_type->value[0]; + pidff_get_effect_type_id(pidff, effect); pidff_set_duration(&pidff->set_effect[PID_DURATION], effect->replay.length); From 3e7761f7bf9f0187bb18cf52b5119bdf4940e686 Mon Sep 17 00:00:00 2001 From: Manish Khadka Date: Fri, 15 May 2026 23:30:11 +0545 Subject: [PATCH 0418/2758] HID: hid-lenovo-go: cancel cfg_setup work in hid_go_cfg_remove() commit 73fde0cbff7d9d618591774a12c23434232752c1 upstream. hid_go_cfg_probe() initialises drvdata.go_cfg_setup and schedules it to run 2 ms later: INIT_DELAYED_WORK(&drvdata.go_cfg_setup, &cfg_setup); schedule_delayed_work(&drvdata.go_cfg_setup, msecs_to_jiffies(2)); cfg_setup() dereferences drvdata.hdev to issue MCU command requests. hid_go_cfg_remove() tears down sysfs and stops the HID device, but never drains the delayed work. If the device is unbound within the 2 ms scheduling delay (a probe failure rolling back via remove, or a fast rmmod after probe), the work fires after hid_destroy_device() has dropped its reference and released the underlying hdev struct, leaving cfg_setup() with a stale drvdata.hdev pointer. Mirror the sibling driver hid-lenovo-go-s.c, whose hid_gos_cfg_remove() already calls cancel_delayed_work_sync() on its analogous work, and drain go_cfg_setup at the top of hid_go_cfg_remove(). The cancel must come before guard(mutex)(&drvdata.cfg_mutex) because cfg_setup() acquires that mutex; reversing the order would deadlock. Fixes: d69ccfcbc955 ("HID: hid-lenovo-go: Add Lenovo Legion Go Series HID Driver") Cc: stable@vger.kernel.org Signed-off-by: Manish Khadka Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-lenovo-go.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/hid/hid-lenovo-go.c b/drivers/hid/hid-lenovo-go.c index e0c9d5ec9451b..318b1152ff8ba 100644 --- a/drivers/hid/hid-lenovo-go.c +++ b/drivers/hid/hid-lenovo-go.c @@ -2405,6 +2405,15 @@ static int hid_go_cfg_probe(struct hid_device *hdev, static void hid_go_cfg_remove(struct hid_device *hdev) { + /* + * cfg_setup is scheduled from hid_go_cfg_probe() with a 2 ms delay + * and dereferences drvdata.hdev. Drain it here before tearing + * down so the workqueue cannot run after hid_destroy_device()'s + * put_device() has released the underlying hdev and dereference + * a stale drvdata.hdev pointer. + */ + cancel_delayed_work_sync(&drvdata.go_cfg_setup); + guard(mutex)(&drvdata.cfg_mutex); sysfs_remove_groups(&hdev->dev.kobj, top_level_attr_groups); hid_hw_close(hdev); From 57bdd10ad50d68341f500a7b330f0d8949e510ec Mon Sep 17 00:00:00 2001 From: Jinmo Yang Date: Fri, 29 May 2026 02:59:45 +0900 Subject: [PATCH 0419/2758] HID: wacom: fix slab-out-of-bounds write in wacom_wac_queue_insert commit 6b3014ec0e9a390ca563030b2d7689921f0daef5 upstream. wacom_wac_queue_insert() calls kfifo_skip() in a loop when the kfifo doesn't have enough space for the incoming report. If the kfifo is empty, kfifo_skip() reads stale data left in the kmalloc'd buffer via __kfifo_peek_n() and interprets it as a record length, advancing fifo->out by that garbage value. This corrupts the internal kfifo state, causing kfifo_unused() to return a value much larger than the actual buffer size, which bypasses __kfifo_in_r()'s guard: if (len + recsize > kfifo_unused(fifo)) return 0; kfifo_copy_in() then performs an out-of-bounds memcpy, writing up to 3842 bytes past the 256-byte buffer. Add a !kfifo_is_empty() condition to the while loop so kfifo_skip() is never called on an empty fifo, and check the return value of kfifo_in() to reject reports that are too large for the fifo. Suggested-by: Dmitry Torokhov Fixes: 5e013ad20689 ("HID: wacom: Remove static WACOM_PKGLEN_MAX limit") Cc: stable@vger.kernel.org Signed-off-by: Jinmo Yang Reviewed-by: Dmitry Torokhov Signed-off-by: Benjamin Tissoires Signed-off-by: Greg Kroah-Hartman --- drivers/hid/wacom_sys.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 252771dcfd8f8..3d40e4bef6e37 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -54,7 +54,7 @@ static void wacom_wac_queue_insert(struct hid_device *hdev, { bool warned = false; - while (kfifo_avail(fifo) < size) { + while (kfifo_avail(fifo) < size && !kfifo_is_empty(fifo)) { if (!warned) hid_warn(hdev, "%s: kfifo has filled, starting to drop events\n", __func__); warned = true; @@ -62,7 +62,9 @@ static void wacom_wac_queue_insert(struct hid_device *hdev, kfifo_skip(fifo); } - kfifo_in(fifo, raw_data, size); + if (!kfifo_in(fifo, raw_data, size)) + hid_warn_ratelimited(hdev, "%s: report is too large (%d)\n", + __func__, size); } static void wacom_wac_queue_flush(struct hid_device *hdev, From 27c4dad1b7917b747bf080792a527997e3147c69 Mon Sep 17 00:00:00 2001 From: Jinmo Yang Date: Mon, 1 Jun 2026 22:41:23 +0900 Subject: [PATCH 0420/2758] HID: wacom: use GFP_ATOMIC in wacom_wac_queue_flush() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 55f1ad573e34abf9a0443c34bc5a63d74edba7d7 upstream. wacom_wac_queue_flush() is called via the .raw_event callback (wacom_raw_event → wacom_wac_pen_serial_enforce → wacom_wac_queue_flush). For USB HID devices, this callback is invoked from hid_irq_in(), which is a URB completion handler running in atomic context. Using GFP_KERNEL in this path can sleep, leading to a "scheduling while atomic" bug. Use GFP_ATOMIC instead. The existing code already handles allocation failure by skipping the fifo entry and continuing. Reported-by: Sashiko-bot Fixes: 5e013ad20689 ("HID: wacom: Remove static WACOM_PKGLEN_MAX limit") Cc: stable@vger.kernel.org Reviewed-by: Dmitry Torokhov Signed-off-by: Jinmo Yang Signed-off-by: Benjamin Tissoires Signed-off-by: Greg Kroah-Hartman --- drivers/hid/wacom_sys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 3d40e4bef6e37..24b8b33a07f60 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -76,7 +76,7 @@ static void wacom_wac_queue_flush(struct hid_device *hdev, unsigned int count; int err; - buf = kzalloc(size, GFP_KERNEL); + buf = kzalloc(size, GFP_ATOMIC); if (!buf) { kfifo_skip(fifo); continue; From df3d8aa1a9392da3de66398e7a03422463806b21 Mon Sep 17 00:00:00 2001 From: Manish Khadka Date: Fri, 15 May 2026 22:27:00 +0545 Subject: [PATCH 0421/2758] HID: letsketch: fix UAF on inrange_timer at driver unbind commit 46c8beeccd8ab2c863827254a85ea877654a3534 upstream. letsketch_driver does not provide a .remove callback, but letsketch_probe() arms a per-device timer: timer_setup(&data->inrange_timer, letsketch_inrange_timeout, 0); The timer is re-armed from letsketch_raw_event() with a 100 ms timeout on every pen-in-range report, and its callback dereferences data->input_tablet to deliver a synthetic BTN_TOOL_PEN release. letsketch_data is allocated with devm_kzalloc(), and its input_dev fields are devm-allocated via letsketch_setup_input_tablet(). On device unbind (USB unplug or rmmod), the HID core runs its default teardown and devm cleanup frees both letsketch_data and the input devices. Because no .remove callback exists, nothing drains the timer first: if raw_event armed it within ~100 ms of the unbind, the pending timer fires on freed memory. This is a UAF read of data and of data->input_tablet, followed by input_report_key() / input_sync() into the freed input_dev. The same problem can occur on the probe error path: if hid_hw_start() enabled I/O on an always-poll-quirk device and then failed, raw_event may have armed the timer before devm releases data. Fix by adding a .remove callback that calls hid_hw_stop() first. hid_hw_stop() synchronously kills the URBs that deliver raw_event(), so once it returns no path can re-arm the timer. timer_shutdown_sync() then drains any in-flight callback and permanently disables further mod_timer() calls. Apply the same timer_shutdown_sync() in the probe error path so the timer is guaranteed not to outlive data. Fixes: 33a5c2793451 ("HID: Add new Letsketch tablet driver") Cc: stable@vger.kernel.org Signed-off-by: Manish Khadka Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-letsketch.c | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/drivers/hid/hid-letsketch.c b/drivers/hid/hid-letsketch.c index 11e21f9887232..b52e93a91ae54 100644 --- a/drivers/hid/hid-letsketch.c +++ b/drivers/hid/hid-letsketch.c @@ -296,13 +296,42 @@ static int letsketch_probe(struct hid_device *hdev, const struct hid_device_id * ret = letsketch_setup_input_tablet(data); if (ret) - return ret; + goto err_shutdown_timer; ret = letsketch_setup_input_tablet_pad(data); if (ret) - return ret; + goto err_shutdown_timer; + + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); + if (ret) + goto err_shutdown_timer; - return hid_hw_start(hdev, HID_CONNECT_HIDRAW); + return 0; + +err_shutdown_timer: + /* + * Drain any pending callback and permanently disable the timer + * before devm releases data: if hid_hw_start() enabled I/O on an + * always-poll-quirk device and then failed, raw_event may have + * armed the timer already. + */ + timer_shutdown_sync(&data->inrange_timer); + return ret; +} + +static void letsketch_remove(struct hid_device *hdev) +{ + struct letsketch_data *data = hid_get_drvdata(hdev); + + /* + * hid_hw_stop() synchronously kills the URBs that deliver + * raw_event(), so once it returns no path can re-arm + * inrange_timer. timer_shutdown_sync() then drains any + * in-flight callback and permanently disables further + * mod_timer() calls before devm releases data. + */ + hid_hw_stop(hdev); + timer_shutdown_sync(&data->inrange_timer); } static const struct hid_device_id letsketch_devices[] = { @@ -315,6 +344,7 @@ static struct hid_driver letsketch_driver = { .name = "letsketch", .id_table = letsketch_devices, .probe = letsketch_probe, + .remove = letsketch_remove, .raw_event = letsketch_raw_event, }; module_hid_driver(letsketch_driver); From 6493ebf9489efef0105078377b973ab33d51af22 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Thu, 2 Jul 2026 00:13:19 +0700 Subject: [PATCH 0422/2758] HID: multitouch: fix out-of-bounds bit access on mt_io_flags commit 8813b0612275cc61fe9e6603d0ee019247ade6be upstream. mt_io_flags is a single unsigned long, but mt_process_slot(), mt_release_pending_palms() and mt_release_contacts() use it as a per-slot bitmap indexed by the slot number. That slot number is only bounded by td->maxcontacts, which is taken from the device's ContactCountMaximum feature report and can be up to 255, not by BITS_PER_LONG. As a result, a multitouch device that advertises a large contact count makes set_bit()/clear_bit() operate past the mt_io_flags word and corrupt the adjacent members of struct mt_device. The sticky-fingers release timer is the easiest way to reach this. mt_release_contacts() runs for (i = 0; i < mt->num_slots; i++) clear_bit(i, &td->mt_io_flags); with num_slots == maxcontacts. For maxcontacts around 250 the loop clears the bits that overlap td->applications.next, zeroing that list head, and the list_for_each_entry() that immediately follows then dereferences NULL. The kernel panics from timer (softirq) context. On a KASAN build this shows up as a general protection fault in mt_release_contacts() with a null-ptr-deref at offset 0x58, which is offsetof(struct mt_application, num_received). The state is reachable from an untrusted USB or Bluetooth HID multitouch device; no local privileges are required. Store the per-slot active state in a separately allocated bitmap sized for maxcontacts, the same pattern already used for pending_palm_slots, and keep only MT_IO_FLAGS_RUNNING in mt_io_flags. The two "mt_io_flags & MT_IO_SLOTS_MASK" arming checks become bitmap_empty(td->active_slots, td->maxcontacts). Move MT_IO_FLAGS_RUNNING back to bit 0. It was bumped to bit 32 by the same commit to leave the low byte for the slot bits; with the slot bits gone it fits in bit 0 again, which also keeps it within the unsigned long on 32-bit. Fixes: 46f781e0d151 ("HID: multitouch: fix sticky fingers") Cc: stable@vger.kernel.org Signed-off-by: Trung Nguyen Signed-off-by: Benjamin Tissoires Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-multitouch.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index eeab0b6e32ccc..b085bed874bf9 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -31,6 +31,7 @@ * [1] https://gitlab.freedesktop.org/libevdev/hid-tools */ +#include #include #include #include @@ -97,8 +98,7 @@ enum report_mode { TOUCHPAD_REPORT_ALL = TOUCHPAD_REPORT_BUTTONS | TOUCHPAD_REPORT_CONTACTS, }; -#define MT_IO_SLOTS_MASK GENMASK(7, 0) /* reserve first 8 bits for slot tracking */ -#define MT_IO_FLAGS_RUNNING 32 +#define MT_IO_FLAGS_RUNNING 0 static const bool mtrue = true; /* default for true */ static const bool mfalse; /* default for false */ @@ -174,10 +174,9 @@ struct mt_device { struct timer_list release_timer; /* to release sticky fingers */ struct hid_haptic_device *haptic; /* haptic related configuration */ struct hid_device *hdev; /* hid_device we're attached to */ - unsigned long mt_io_flags; /* mt flags (MT_IO_FLAGS_RUNNING) - * first 8 bits are reserved for keeping the slot - * states, this is fine because we only support up - * to 250 slots (MT_MAX_MAXCONTACT) + unsigned long mt_io_flags; /* mt flags (MT_IO_FLAGS_RUNNING) */ + unsigned long *active_slots; /* bitmap of slots with an active + * contact, sized for maxcontacts */ __u8 inputmode_value; /* InputMode HID feature value */ __u8 maxcontacts; @@ -1033,7 +1032,7 @@ static void mt_release_pending_palms(struct mt_device *td, for_each_set_bit(slotnum, app->pending_palm_slots, td->maxcontacts) { clear_bit(slotnum, app->pending_palm_slots); - clear_bit(slotnum, &td->mt_io_flags); + clear_bit(slotnum, td->active_slots); input_mt_slot(input, slotnum); input_mt_report_slot_inactive(input); @@ -1244,9 +1243,9 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input, input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); - set_bit(slotnum, &td->mt_io_flags); + set_bit(slotnum, td->active_slots); } else { - clear_bit(slotnum, &td->mt_io_flags); + clear_bit(slotnum, td->active_slots); } return 0; @@ -1381,7 +1380,7 @@ static void mt_touch_report(struct hid_device *hid, * defect. */ if (app->quirks & MT_QUIRK_STICKY_FINGERS) { - if (td->mt_io_flags & MT_IO_SLOTS_MASK) + if (!bitmap_empty(td->active_slots, td->maxcontacts)) mod_timer(&td->release_timer, jiffies + msecs_to_jiffies(100)); else @@ -1440,6 +1439,15 @@ static int mt_touch_input_configured(struct hid_device *hdev, if (td->is_pressurepad) __set_bit(INPUT_PROP_PRESSUREPAD, input->propbit); + if (!td->active_slots) { + td->active_slots = devm_kcalloc(&td->hdev->dev, + BITS_TO_LONGS(td->maxcontacts), + sizeof(long), + GFP_KERNEL); + if (!td->active_slots) + return -ENOMEM; + } + app->pending_palm_slots = devm_kcalloc(&hi->input->dev, BITS_TO_LONGS(td->maxcontacts), sizeof(long), @@ -1917,7 +1925,7 @@ static void mt_release_contacts(struct hid_device *hid) for (i = 0; i < mt->num_slots; i++) { input_mt_slot(input_dev, i); input_mt_report_slot_inactive(input_dev); - clear_bit(i, &td->mt_io_flags); + clear_bit(i, td->active_slots); } input_mt_sync_frame(input_dev); input_sync(input_dev); @@ -1940,7 +1948,7 @@ static void mt_expired_timeout(struct timer_list *t) */ if (test_and_set_bit_lock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags)) return; - if (td->mt_io_flags & MT_IO_SLOTS_MASK) + if (!bitmap_empty(td->active_slots, td->maxcontacts)) mt_release_contacts(hdev); clear_bit_unlock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags); } From 6b0838e86da88b1d3bff86f19761ff25af73eaca Mon Sep 17 00:00:00 2001 From: Manish Khadka Date: Fri, 15 May 2026 23:17:52 +0545 Subject: [PATCH 0423/2758] HID: appleir: fix UAF on pending key_up_timer in remove() commit 75fe87e19d8aff81eb2c64d15d244ab8da4de945 upstream. appleir_remove() runs hid_hw_stop() before timer_delete_sync(). hid_hw_stop() synchronously unregisters the HID input device via hid_disconnect() -> hidinput_disconnect() -> input_unregister_device(), which drops the last reference and frees the underlying input_dev when no userspace handle holds it open. key_up_tick() reads appleir->input_dev and calls input_report_key() / input_sync() on it. The timer is armed from appleir_raw_event() with a HZ/8 (~125 ms) timeout on every keydown and key-repeat report. If a key was pressed shortly before the device is disconnected, the timer can fire after hid_hw_stop() has freed input_dev but before the teardown drains it. A simple reorder is not sufficient. Putting the timer drain first still leaves a window where a USB URB completion (raw_event) running during hid_hw_stop() can call mod_timer() and re-arm the timer, which then fires after hidinput_disconnect() has freed input_dev. The same URB-completion window also lets raw_event() reach key_up(), key_down() and battery_flat() directly, all of which dereference appleir->input_dev. Introduce a 'removing' flag on struct appleir, gated by the existing spinlock. appleir_remove() sets the flag under the lock and then shuts down the timer with timer_shutdown_sync(), which both drains any in-flight callback and permanently disables further mod_timer() calls. appleir_raw_event() and key_up_tick() bail out early if the flag is set, so no path can arm or run the timer, or dereference appleir->input_dev, after remove() has started tearing down. The keyrepeat and flatbattery branches of appleir_raw_event() previously called into the input layer without holding the spinlock; take it now so the flag check is well-defined. This incidentally closes a pre-existing read-side race on appleir->current_key in the keyrepeat branch. This bug is structurally a sibling of commit 4db2af929279 ("HID: appletb-kbd: fix UAF in inactivity-timer cleanup path") and has been present since the driver was introduced. Fixes: 9a4a5574ce42 ("HID: appleir: add support for Apple ir devices") Cc: stable@vger.kernel.org Signed-off-by: Manish Khadka Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-appleir.c | 45 ++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/drivers/hid/hid-appleir.c b/drivers/hid/hid-appleir.c index 5e8ced7bc05a3..adaa44a858edd 100644 --- a/drivers/hid/hid-appleir.c +++ b/drivers/hid/hid-appleir.c @@ -109,9 +109,10 @@ struct appleir { struct hid_device *hid; unsigned short keymap[ARRAY_SIZE(appleir_key_table)]; struct timer_list key_up_timer; /* timer for key up */ - spinlock_t lock; /* protects .current_key */ + spinlock_t lock; /* protects .current_key, .removing */ int current_key; /* the currently pressed key */ int prev_key_idx; /* key index in a 2 packets message */ + bool removing; /* set during teardown; gates input_dev access */ }; static int get_key(int data) @@ -172,7 +173,7 @@ static void key_up_tick(struct timer_list *t) unsigned long flags; spin_lock_irqsave(&appleir->lock, flags); - if (appleir->current_key) { + if (!appleir->removing && appleir->current_key) { key_up(hid, appleir, appleir->current_key); appleir->current_key = 0; } @@ -195,6 +196,10 @@ static int appleir_raw_event(struct hid_device *hid, struct hid_report *report, int index; spin_lock_irqsave(&appleir->lock, flags); + if (appleir->removing) { + spin_unlock_irqrestore(&appleir->lock, flags); + goto out; + } /* * If we already have a key down, take it up before marking * this one down @@ -229,17 +234,25 @@ static int appleir_raw_event(struct hid_device *hid, struct hid_report *report, appleir->prev_key_idx = 0; if (!memcmp(data, keyrepeat, sizeof(keyrepeat))) { - key_down(hid, appleir, appleir->current_key); - /* - * Remote doesn't do key up, either pull them up, in the test - * above, or here set a timer which pulls them up after 1/8 s - */ - mod_timer(&appleir->key_up_timer, jiffies + HZ / 8); + spin_lock_irqsave(&appleir->lock, flags); + if (!appleir->removing) { + key_down(hid, appleir, appleir->current_key); + /* + * Remote doesn't do key up, either pull them up, in + * the test above, or here set a timer which pulls them + * up after 1/8 s + */ + mod_timer(&appleir->key_up_timer, jiffies + HZ / 8); + } + spin_unlock_irqrestore(&appleir->lock, flags); goto out; } if (!memcmp(data, flatbattery, sizeof(flatbattery))) { - battery_flat(appleir); + spin_lock_irqsave(&appleir->lock, flags); + if (!appleir->removing) + battery_flat(appleir); + spin_unlock_irqrestore(&appleir->lock, flags); /* Fall through */ } @@ -318,8 +331,20 @@ static int appleir_probe(struct hid_device *hid, const struct hid_device_id *id) static void appleir_remove(struct hid_device *hid) { struct appleir *appleir = hid_get_drvdata(hid); + unsigned long flags; + + /* + * Mark the driver as tearing down so that any concurrent raw_event + * (e.g. from a USB URB completion that hid_hw_stop() has not yet + * killed) and the key_up_timer softirq stop touching input_dev + * before hid_hw_stop() frees it via hidinput_disconnect(). + */ + spin_lock_irqsave(&appleir->lock, flags); + appleir->removing = true; + spin_unlock_irqrestore(&appleir->lock, flags); + + timer_shutdown_sync(&appleir->key_up_timer); hid_hw_stop(hid); - timer_delete_sync(&appleir->key_up_timer); } static const struct hid_device_id appleir_devices[] = { From 8131f4226688c4be5f30874d167e44dab838eb09 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Thu, 18 Jun 2026 15:06:35 +0800 Subject: [PATCH 0424/2758] HID: lg-g15: cancel pending work on remove to fix a use-after-free commit 7705b4140d188ce22656f6e541ae7ef834c7e11a upstream. lg_g15_data is allocated with devm and holds a work item. The report handlers schedule that work straight from device input. lg_g15_event() and lg_g15_v2_event() do it on the backlight cycle key, and lg_g510_leds_event() does it too. The worker dereferences the lg_g15_data back through container_of. The driver had no remove callback and never cancelled the work. So if a report scheduled the work and the keyboard was then unplugged, devres freed lg_g15_data while the work was still pending or running, and the worker touched freed memory. This is a use-after-free. It is reachable as a race on device unplug. Add a remove callback that cancels the work before devres frees the state. g15->work is only initialized for the models that schedule it (G15, G15 v2, G510). The G13 and Z-10 leave it zeroed, so guard the cancel on g15->work.func to avoid cancelling a work that was never set up. The g15 NULL test mirrors the one already in lg_g15_raw_event(). Fixes: 97b741aba918 ("HID: lg-g15: Add keyboard and LCD backlight control") Cc: stable@vger.kernel.org Suggested-by: Hans de Goede Signed-off-by: Maoyi Xie Reviewed-by: Hans de Goede Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-lg-g15.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/hid/hid-lg-g15.c b/drivers/hid/hid-lg-g15.c index 1a88bc44ada44..02ef3e2094b4c 100644 --- a/drivers/hid/hid-lg-g15.c +++ b/drivers/hid/hid-lg-g15.c @@ -1374,11 +1374,27 @@ static const struct hid_device_id lg_g15_devices[] = { }; MODULE_DEVICE_TABLE(hid, lg_g15_devices); +static void lg_g15_remove(struct hid_device *hdev) +{ + struct lg_g15_data *g15 = hid_get_drvdata(hdev); + + /* + * g15->work is only initialized for the models that schedule it + * (G15, G15 v2, G510). The G13 and Z-10 leave it zeroed, so only + * cancel it when it was set up. + */ + if (g15 && g15->work.func) + cancel_work_sync(&g15->work); + + hid_hw_stop(hdev); +} + static struct hid_driver lg_g15_driver = { .name = "lg-g15", .id_table = lg_g15_devices, .raw_event = lg_g15_raw_event, .probe = lg_g15_probe, + .remove = lg_g15_remove, }; module_hid_driver(lg_g15_driver); From d36e69c8c0c81474b20ddd894e1325e5cae59c26 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Wed, 10 Jun 2026 16:29:09 +0800 Subject: [PATCH 0425/2758] HID: sensor-hub: Add sensor_hub_input_attr_read_values() for multi-byte reads commit f784fcea450617055d2d12eec5b2f6e0e38bf878 upstream. sensor_hub_input_attr_get_raw_value() is limited to returning a single 32-bit value, which is insufficient for sensors that report data larger than 32 bits, such as a quaternion with four s16 elements. Add sensor_hub_input_attr_read_values() that accepts a caller-provided buffer and accumulates incoming data until the buffer is full. The two paths are distinguished in sensor_hub_raw_event() by pending.max_raw_size being non-zero, preserving backward compatibility. Signed-off-by: Srinivas Pandruvada Co-developed-by: Zhang Lixu Signed-off-by: Zhang Lixu Acked-by: Jiri Kosina Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-sensor-hub.c | 77 +++++++++++++++++++++++++++++++--- include/linux/hid-sensor-hub.h | 25 +++++++++++ 2 files changed, 96 insertions(+), 6 deletions(-) diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index 90666ff629def..34f710c465b80 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -286,6 +286,54 @@ int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, } EXPORT_SYMBOL_GPL(sensor_hub_get_feature); +int sensor_hub_input_attr_read_values(struct hid_sensor_hub_device *hsdev, + u32 usage_id, u32 attr_usage_id, + u32 report_id, + enum sensor_hub_read_flags flag, + u32 buffer_size, u8 *buffer) +{ + struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); + struct hid_report *report; + unsigned long flags; + long cycles; + int ret; + + report = sensor_hub_report(report_id, hsdev->hdev, HID_INPUT_REPORT); + if (!report) + return -EINVAL; + + mutex_lock(hsdev->mutex_ptr); + if (flag == SENSOR_HUB_SYNC) { + memset(&hsdev->pending, 0, sizeof(hsdev->pending)); + init_completion(&hsdev->pending.ready); + hsdev->pending.usage_id = usage_id; + hsdev->pending.attr_usage_id = attr_usage_id; + hsdev->pending.max_raw_size = buffer_size; + hsdev->pending.raw_data = buffer; + + spin_lock_irqsave(&data->lock, flags); + hsdev->pending.status = true; + spin_unlock_irqrestore(&data->lock, flags); + } + mutex_lock(&data->mutex); + hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT); + mutex_unlock(&data->mutex); + ret = 0; + if (flag == SENSOR_HUB_SYNC) { + cycles = wait_for_completion_interruptible_timeout(&hsdev->pending.ready, + HZ * 5); + if (cycles == 0) + ret = -ETIMEDOUT; + else if (cycles < 0) + ret = cycles; + + hsdev->pending.status = false; + } + mutex_unlock(hsdev->mutex_ptr); + + return ret; +} +EXPORT_SYMBOL_GPL(sensor_hub_input_attr_read_values); int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev, u32 usage_id, @@ -478,6 +526,8 @@ static int sensor_hub_raw_event(struct hid_device *hdev, struct hid_collection *collection = NULL; void *priv = NULL; struct hid_sensor_hub_device *hsdev = NULL; + u32 copy_size; + u32 avail; hid_dbg(hdev, "sensor_hub_raw_event report id:0x%x size:%d type:%d\n", report->id, size, report->type); @@ -518,12 +568,27 @@ static int sensor_hub_raw_event(struct hid_device *hdev, hsdev->pending.attr_usage_id == report->field[i]->logical)) { hid_dbg(hdev, "data was pending ...\n"); - hsdev->pending.raw_data = kmemdup(ptr, sz, GFP_ATOMIC); - if (hsdev->pending.raw_data) - hsdev->pending.raw_size = sz; - else - hsdev->pending.raw_size = 0; - complete(&hsdev->pending.ready); + if (hsdev->pending.max_raw_size) { + if (hsdev->pending.index < hsdev->pending.max_raw_size) { + avail = hsdev->pending.max_raw_size - hsdev->pending.index; + copy_size = clamp(sz, 0U, avail); + + memcpy(hsdev->pending.raw_data + hsdev->pending.index, + ptr, copy_size); + hsdev->pending.index += copy_size; + if (hsdev->pending.index >= hsdev->pending.max_raw_size) { + hsdev->pending.raw_size = hsdev->pending.index; + complete(&hsdev->pending.ready); + } + } + } else { + hsdev->pending.raw_data = kmemdup(ptr, sz, GFP_ATOMIC); + if (hsdev->pending.raw_data) + hsdev->pending.raw_size = sz; + else + hsdev->pending.raw_size = 0; + complete(&hsdev->pending.ready); + } } if (callback->capture_sample) { if (report->field[i]->logical) diff --git a/include/linux/hid-sensor-hub.h b/include/linux/hid-sensor-hub.h index e710565531085..ab5cc8db3fbb3 100644 --- a/include/linux/hid-sensor-hub.h +++ b/include/linux/hid-sensor-hub.h @@ -43,6 +43,8 @@ struct hid_sensor_hub_attribute_info { * @attr_usage_id: Usage Id of a field, e.g. X-axis for a gyro. * @raw_size: Response size for a read request. * @raw_data: Place holder for received response. + * @index: Current write index into raw_data for multi-byte reads. + * @max_raw_size: Total buffer size for multi-byte reads; 0 for single-value reads. */ struct sensor_hub_pending { bool status; @@ -51,6 +53,8 @@ struct sensor_hub_pending { u32 attr_usage_id; int raw_size; u8 *raw_data; + u32 index; + u32 max_raw_size; }; /** @@ -183,6 +187,27 @@ int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev, bool is_signed ); +/** + * sensor_hub_input_attr_read_values() - Synchronous multi-byte read request + * @hsdev: Hub device instance. + * @usage_id: Attribute usage id of parent physical device as per spec + * @attr_usage_id: Attribute usage id as per spec + * @report_id: Report id to look for + * @flag: Synchronous or asynchronous read + * @buffer_size: Size of the buffer in bytes + * @buffer: Buffer to store the read data + * + * Issues a synchronous or asynchronous read request for an input attribute, + * accumulating data into the provided buffer until it is full. + * Return: 0 on success, -ETIMEDOUT if the device did not respond, or a + * negative error code. + */ +int sensor_hub_input_attr_read_values(struct hid_sensor_hub_device *hsdev, + u32 usage_id, u32 attr_usage_id, + u32 report_id, + enum sensor_hub_read_flags flag, + u32 buffer_size, u8 *buffer); + /** * sensor_hub_set_feature() - Feature set request * @hsdev: Hub device instance. From c25d3c931a63e762fcaa9cb125b901c53b62403f Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Tue, 5 May 2026 11:12:58 +0000 Subject: [PATCH 0426/2758] hfs/hfsplus: fix u32 overflow in check_and_correct_requested_length commit 966cb76fb2857a4242cab6ea2ea17acf818a3da7 upstream. check_and_correct_requested_length() compares (off + len) against node_size using u32 arithmetic. When the caller passes a large len value (e.g. from an underflowed subtraction in hfs_brec_remove()), off + len can wrap past 2^32 and produce a small result, causing the bounds check to pass when it should fail. For example, with off=14 and len=0xFFFFFFF2 (underflowed from data_off - keyoffset - size in hfs_brec_remove), off + len wraps to 6, which is less than a typical node_size of 512, so the check passes and the subsequent memmove reads ~4GB past the node buffer. Fix this by widening the addition to u64 before comparing against node_size. This prevents the u32 wrap while keeping the logic straightforward. Reported-by: syzbot+6df204b70bf3261691c5@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=6df204b70bf3261691c5 Tested-by: syzbot+6df204b70bf3261691c5@syzkaller.appspotmail.com Reported-by: syzbot+e76bf3d19b85350571ac@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=e76bf3d19b85350571ac Tested-by: syzbot+e76bf3d19b85350571ac@syzkaller.appspotmail.com Fixes: a431930c9bac ("hfs: fix slab-out-of-bounds in hfs_bnode_read()") Cc: stable@vger.kernel.org Signed-off-by: Tristan Madani Reviewed-by: Viacheslav Dubeyko Signed-off-by: Viacheslav Dubeyko Link: https://lore.kernel.org/r/20260505111300.3592757-2-tristmd@gmail.com Signed-off-by: Viacheslav Dubeyko Signed-off-by: Greg Kroah-Hartman --- fs/hfs/bnode.c | 2 +- fs/hfsplus/hfsplus_fs.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/hfs/bnode.c b/fs/hfs/bnode.c index 13d58c51fc46b..9571f33b91085 100644 --- a/fs/hfs/bnode.c +++ b/fs/hfs/bnode.c @@ -41,7 +41,7 @@ u32 check_and_correct_requested_length(struct hfs_bnode *node, u32 off, u32 len) node_size = node->tree->node_size; - if ((off + len) > node_size) { + if ((u64)off + len > node_size) { u32 new_len = node_size - off; pr_err("requested length has been corrected: " diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index 3545b8dbf11c5..0e4268de9e60e 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -600,7 +600,7 @@ u32 check_and_correct_requested_length(struct hfs_bnode *node, u32 off, u32 len) node_size = node->tree->node_size; - if ((off + len) > node_size) { + if ((u64)off + len > node_size) { u32 new_len = node_size - off; pr_err("requested length has been corrected: " From d5b45bad75cd2730b8452aed4d3b20a2b2a12576 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Tue, 5 May 2026 11:12:59 +0000 Subject: [PATCH 0427/2758] hfs/hfsplus: zero-initialize buffer in hfs_bnode_read commit d67aadee19ffdf3cc8520c5a4f4d5b2916d30baf upstream. hfs_bnode_read() can return early without writing to the output buffer when is_bnode_offset_valid() fails or when check_and_correct_requested_ length() corrects the length to zero. Callers such as hfs_bnode_read_ u16() and hfs_bnode_read_u8() pass stack-allocated buffers and use the result unconditionally, leading to KMSAN uninit-value reports. Rather than initializing at each individual call site, zero the buffer at the start of hfs_bnode_read() before any validation checks. This ensures all callers in both hfs and hfsplus get a deterministic zero value regardless of which early-return path is taken. Reported-by: syzbot+217eb327242d08197efb@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=217eb327242d08197efb Tested-by: syzbot+217eb327242d08197efb@syzkaller.appspotmail.com Fixes: a431930c9bac ("hfs: fix slab-out-of-bounds in hfs_bnode_read()") Cc: stable@vger.kernel.org Signed-off-by: Tristan Madani Reviewed-by: Viacheslav Dubeyko Signed-off-by: Viacheslav Dubeyko Link: https://lore.kernel.org/r/20260505111300.3592757-3-tristmd@gmail.com Signed-off-by: Viacheslav Dubeyko Signed-off-by: Greg Kroah-Hartman --- fs/hfs/bnode.c | 2 ++ fs/hfsplus/bnode.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/fs/hfs/bnode.c b/fs/hfs/bnode.c index 9571f33b91085..25cef62fbba6d 100644 --- a/fs/hfs/bnode.c +++ b/fs/hfs/bnode.c @@ -64,6 +64,8 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len) u32 bytes_read; u32 bytes_to_read; + memset(buf, 0, len); + if (!is_bnode_offset_valid(node, off)) return; diff --git a/fs/hfsplus/bnode.c b/fs/hfsplus/bnode.c index f8b5a8ae58ff5..14d1af2c7ba93 100644 --- a/fs/hfsplus/bnode.c +++ b/fs/hfsplus/bnode.c @@ -25,6 +25,8 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len) struct page **pagep; u32 l; + memset(buf, 0, len); + if (!is_bnode_offset_valid(node, off)) return; From d26aef771b4f6923da9f89d6d5b70d8def5853de Mon Sep 17 00:00:00 2001 From: Deepanshu Kartikey Date: Sun, 3 May 2026 13:33:29 +0900 Subject: [PATCH 0428/2758] nilfs2: reject CLEAN_SEGMENTS ioctl with out-of-range segment numbers commit 0e7a690fe435f8d5ea3feb7c1d8d73ba7e8b8aa9 upstream. Syzbot reported a hung task in nilfs_transaction_begin() where multiple tasks performing chmod() on a nilfs2 mount blocked for over 143 seconds waiting to acquire ns_segctor_sem for read: INFO: task syz.0.17:5918 blocked for more than 143 seconds. Call Trace: schedule+0x164/0x360 rwsem_down_read_slowpath+0x6d9/0x940 down_read+0x99/0x2e0 nilfs_transaction_begin+0x364/0x710 fs/nilfs2/segment.c:221 nilfs_setattr+0x124/0x2c0 fs/nilfs2/inode.c:921 notify_change+0xc1a/0xf40 chmod_common+0x273/0x4a0 do_fchmodat+0x12d/0x230 The writer holding ns_segctor_sem was a concurrent NILFS_IOCTL_CLEAN_SEGMENTS caller, stuck inside printk while emitting per-element warnings from nilfs_sufile_updatev(): __nilfs_msg+0x373/0x450 fs/nilfs2/super.c:78 nilfs_sufile_updatev+0x21c/0x6d0 fs/nilfs2/sufile.c:186 nilfs_sufile_freev fs/nilfs2/sufile.h:93 [inline] nilfs_free_segments fs/nilfs2/segment.c:1140 [inline] nilfs_segctor_collect_blocks fs/nilfs2/segment.c:1261 [inline] nilfs_segctor_do_construct+0x1f55/0x76c0 nilfs_clean_segments+0x3bd/0xa50 nilfs_ioctl_clean_segments fs/nilfs2/ioctl.c:922 [inline] nilfs_ioctl+0x261f/0x2780 The root cause is that user-supplied segment numbers are not validated before nilfs_clean_segments() begins doing work; the range check on each segnum is performed deep inside the call chain by nilfs_sufile_updatev(), which emits a nilfs_warn() per invalid entry while still holding the segctor lock and the sufile mi_sem. Under load (repeated invocations across multiple mounts saturating the global printk path), the cumulative printk latency keeps ns_segctor_sem held long enough to trip the hung_task watchdog, blocking concurrent operations such as chmod() that need ns_segctor_sem for read. Fix by validating the contents of kbufs[4] in nilfs_clean_segments() immediately after acquiring ns_segctor_sem via nilfs_transaction_lock(). Holding ns_segctor_sem serializes the check against nilfs_ioctl_resize(), which can modify ns_nsegments, so the validation uses a consistent value. Out-of-range segment numbers are rejected with -EINVAL before any segment-cleaning work begins, so the bad entries never reach the per-element diagnostic path inside nilfs_sufile_updatev(). Reported-by: syzbot+62f0f99d2f2bb8e3bbd7@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=62f0f99d2f2bb8e3bbd7 Tested-by: syzbot+62f0f99d2f2bb8e3bbd7@syzkaller.appspotmail.com Cc: stable@vger.kernel.org Signed-off-by: Deepanshu Kartikey Fixes: 071cb4b81987 ("nilfs2: eliminate removal list of segments") Signed-off-by: Ryusuke Konishi Signed-off-by: Viacheslav Dubeyko Signed-off-by: Greg Kroah-Hartman --- fs/nilfs2/segment.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c index 1491a4d4b1e19..9332f5ac60833 100644 --- a/fs/nilfs2/segment.c +++ b/fs/nilfs2/segment.c @@ -2512,12 +2512,33 @@ int nilfs_clean_segments(struct super_block *sb, struct nilfs_argv *argv, struct nilfs_sc_info *sci = nilfs->ns_writer; struct nilfs_transaction_info ti; int err; + size_t i, nfreesegs = argv[4].v_nmembs; + __u64 *segnumv = kbufs[4]; if (unlikely(!sci)) return -EROFS; nilfs_transaction_lock(sb, &ti, 1); + /* + * Validate segment numbers under ns_segctor_sem (held for write + * by nilfs_transaction_lock above) so the check is serialized + * against nilfs_ioctl_resize(), which can modify ns_nsegments. + * Rejecting bad input here, before any segment-cleaning work + * begins, avoids the per-element diagnostic path inside + * nilfs_sufile_updatev() that would otherwise run under this + * same lock and stall concurrent readers. + */ + for (i = 0; i < nfreesegs; i++) { + if (segnumv[i] >= nilfs->ns_nsegments) { + nilfs_err(sb, + "Segment number %llu to be freed is out of range", + (unsigned long long)segnumv[i]); + err = -EINVAL; + goto bail_unlock; + } + } + err = nilfs_mdt_save_to_shadow_map(nilfs->ns_dat); if (unlikely(err)) goto out_unlock; @@ -2558,6 +2579,7 @@ int nilfs_clean_segments(struct super_block *sb, struct nilfs_argv *argv, sci->sc_freesegs = NULL; sci->sc_nfreesegs = 0; nilfs_mdt_clear_shadow_map(nilfs->ns_dat); + bail_unlock: nilfs_transaction_unlock(sb); return err; } From 4c4b4af4a9f278da096f0dbdb6b59594701d29bf Mon Sep 17 00:00:00 2001 From: Louis-Alexis Eyraud Date: Wed, 1 Apr 2026 11:44:15 +0200 Subject: [PATCH 0429/2758] media: mtk-jpeg: cancel workqueue on release for supported platforms only commit b1845a227fda37b2fe5327df3ca0015d7e290235 upstream. Since a recent fix the mtk_jpeg_release function cancels any pending or running work present in the driver workqueue using cancel_work_sync function. Currently, only the multicore based variants use this workqueue and they have the jpeg_worker platform data field initialized with a workqueue callback function. For the others, this field value remain NULL by default. The cancel_work_sync function is unconditionally called in mtk_jpeg_release function, even for the variants that do not use the workqueue. This call generates a WARN_ON print in __flush_work because the workqueue callback function presence check fails in __flush_work function (used by cancel_work_sync). So, to avoid these warnings, call cancel_work_sync only if a workqueue callback is defined in platform data. Fixes: 34c519feef3e ("media: mtk-jpeg: fix use-after-free in release path due to uncancelled work") Cc: stable@vger.kernel.org Signed-off-by: Louis-Alexis Eyraud Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c index 8c684756d5fc2..d147ec4830811 100644 --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c @@ -1202,7 +1202,8 @@ static int mtk_jpeg_release(struct file *file) struct mtk_jpeg_dev *jpeg = video_drvdata(file); struct mtk_jpeg_ctx *ctx = mtk_jpeg_file_to_ctx(file); - cancel_work_sync(&ctx->jpeg_work); + if (jpeg->variant->jpeg_worker) + cancel_work_sync(&ctx->jpeg_work); mutex_lock(&jpeg->lock); v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); v4l2_ctrl_handler_free(&ctx->ctrl_hdl); From 9690e8a342632344984af72bc56b7a1fba61e6cb Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 26 Jun 2026 11:49:37 +0200 Subject: [PATCH 0430/2758] serial: 8250_mid: Disable DMA for selected platforms commit b1b4efea05a56c0995e4702a86d6624b4fdff32f upstream. In accordance with Errata (specification updates) HSUART May Stop Functioning when DMA is Active. - Denverton document #572409, rev 3.4, DNV60 - Ice Lake Xeon D document #714070, ICXD65 - Snowridge document #731931, SNR44 For a quick fix just disable the respective callbacks during the device probe. Depending on the future development we might remove them completely. Reported-by: micas-opensource Closes: https://lore.kernel.org/linux-serial/20250625031409.2404219-1-opensource@ruijie.com.cn/ Fixes: 6ede6dcd87aa ("serial: 8250_mid: add support for DMA engine handling from UART MMIO") Cc: stable Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20260626094937.561776-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_mid.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_mid.c b/drivers/tty/serial/8250/8250_mid.c index 8ec03863606e4..f88809ff370b7 100644 --- a/drivers/tty/serial/8250/8250_mid.c +++ b/drivers/tty/serial/8250/8250_mid.c @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -368,8 +369,16 @@ static const struct mid8250_board dnv_board = { .freq = 133333333, .base_baud = 115200, .bar = 1, - .setup = dnv_setup, - .exit = dnv_exit, + /* + * Errata: + * HSUART May Stop Functioning when DMA is Active. + * + * - Denverton document #572409, rev 3.4, DNV60 + * - Ice Lake Xeon D document #714070, ICXD65 + * - Snowridge document #731931, SNR44 + */ + .setup = PTR_IF(false, dnv_setup), + .exit = PTR_IF(false, dnv_exit), }; static const struct pci_device_id pci_ids[] = { From 33c2c9d717f67f9d6d6aca157c0f09287aba792c Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Tue, 30 Jun 2026 12:06:07 +0200 Subject: [PATCH 0431/2758] xfs: use null daddr for unset first bad log block commit cc9af5e461ea5f6e37738f3f1e41c45a9b7f45d6 upstream. xlog_do_recovery_pass() may return before setting first_bad. The caller must distinguish that case from an error at a valid log block, including block zero after the log wraps. Initialize first_bad to XFS_BUF_DADDR_NULL and test it explicitly before treating the error as a torn write. Fixes: 7088c4136fa1 ("xfs: detect and trim torn writes during log recovery") Suggested-by: Darrick J. Wong Reported-by: syzbot+b7dfbed0c6c2b5e9fd34@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=b7dfbed0c6c2b5e9fd34 Cc: stable@vger.kernel.org # v4.5 Signed-off-by: Yousef Alhouseen Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/xfs_log_recover.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 09e6678ca4878..5f984bf5698a8 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -1028,7 +1028,7 @@ xlog_verify_head( { struct xlog_rec_header *tmp_rhead; char *tmp_buffer; - xfs_daddr_t first_bad; + xfs_daddr_t first_bad = XFS_BUF_DADDR_NULL; xfs_daddr_t tmp_rhead_blk; int found; int error; @@ -1057,7 +1057,8 @@ xlog_verify_head( */ error = xlog_do_recovery_pass(log, *head_blk, tmp_rhead_blk, XLOG_RECOVER_CRCPASS, &first_bad); - if ((error == -EFSBADCRC || error == -EFSCORRUPTED) && first_bad) { + if ((error == -EFSBADCRC || error == -EFSCORRUPTED) && + first_bad != XFS_BUF_DADDR_NULL) { /* * We've hit a potential torn write. Reset the error and warn * about it. @@ -3575,4 +3576,3 @@ xlog_recover_cancel( if (xlog_recovery_needed(log)) xlog_recover_cancel_intents(log); } - From d108043cc04e7811f995c15138ae26e01cff8f11 Mon Sep 17 00:00:00 2001 From: Yingjie Gao Date: Thu, 25 Jun 2026 21:16:23 +0800 Subject: [PATCH 0432/2758] xfs: release dquot buffer after dqflush failure commit 0c1b3a823a22af623d55f225fe2ac7e8b9052821 upstream. xfs_qm_dqpurge() gets a locked buffer from xfs_dquot_use_attached_buf(). If xfs_qm_dqflush() fails, the error path skips xfs_buf_relse() and then calls xfs_dquot_detach_buf(), which tries to lock the same buffer again. Release the buffer after xfs_qm_dqflush() returns so the error path drops the caller hold and unlocks the buffer before the dquot is detached, matching the other dqflush callers. Fixes: a40fe30868ba ("xfs: separate dquot buffer reads from xfs_dqflush") Cc: stable@vger.kernel.org # v6.13+ Signed-off-by: Yingjie Gao Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/xfs_qm.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index 7bd15d9e7fba0..02d23226611ee 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -166,10 +166,9 @@ xfs_qm_dqpurge( * does it on success. */ error = xfs_qm_dqflush(dqp, bp); - if (!error) { + if (!error) error = xfs_bwrite(bp); - xfs_buf_relse(bp); - } + xfs_buf_relse(bp); xfs_dqflock(dqp); } xfs_dquot_detach_buf(dqp); From 4693131bee055fa21b11ba3474ae54856ea9c595 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 9 Jun 2026 09:52:44 +0200 Subject: [PATCH 0433/2758] xfs: pass back updated nb from xfs_growfs_compute_deltas commit 4cb6e89a3d901d4da515977e55f9a9a779238660 upstream. xfs_growfs_compute_deltas can update nb for corner cases like a number of blocks that would create a less the minimal sized AG, or running past the max AG limit. Pass back the calculated value to the caller, as it relies on to calculate the new number of perag structures. Note that the grown file system size is not affected by this miscalculation as it uses the passed back delta value. Fixes: a49b7ff63f98 ("xfs: Refactoring the nagcount and delta calculation") Cc: stable@vger.kernel.org # v7.0 Signed-off-by: Christoph Hellwig Reviewed-by: "Darrick J. Wong" Reviewed-by: Carlos Maiolino Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/libxfs/xfs_ag.c | 10 +++++----- fs/xfs/libxfs/xfs_ag.h | 2 +- fs/xfs/xfs_fsops.c | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/fs/xfs/libxfs/xfs_ag.c b/fs/xfs/libxfs/xfs_ag.c index dcd2f93b6a6c7..0c5f0548021f4 100644 --- a/fs/xfs/libxfs/xfs_ag.c +++ b/fs/xfs/libxfs/xfs_ag.c @@ -866,7 +866,7 @@ xfs_ag_shrink_space( void xfs_growfs_compute_deltas( struct xfs_mount *mp, - xfs_rfsblock_t nb, + xfs_rfsblock_t *nb, int64_t *deltap, xfs_agnumber_t *nagcountp) { @@ -874,19 +874,19 @@ xfs_growfs_compute_deltas( int64_t delta; xfs_agnumber_t nagcount; - nb_div = nb; + nb_div = *nb; nb_mod = do_div(nb_div, mp->m_sb.sb_agblocks); if (nb_mod && nb_mod >= XFS_MIN_AG_BLOCKS) nb_div++; else if (nb_mod) - nb = nb_div * mp->m_sb.sb_agblocks; + *nb = nb_div * mp->m_sb.sb_agblocks; if (nb_div > XFS_MAX_AGNUMBER + 1) { nb_div = XFS_MAX_AGNUMBER + 1; - nb = nb_div * mp->m_sb.sb_agblocks; + *nb = nb_div * mp->m_sb.sb_agblocks; } nagcount = nb_div; - delta = nb - mp->m_sb.sb_dblocks; + delta = *nb - mp->m_sb.sb_dblocks; *deltap = delta; *nagcountp = nagcount; } diff --git a/fs/xfs/libxfs/xfs_ag.h b/fs/xfs/libxfs/xfs_ag.h index 16a9b43a3c270..8aa4266c55714 100644 --- a/fs/xfs/libxfs/xfs_ag.h +++ b/fs/xfs/libxfs/xfs_ag.h @@ -330,7 +330,7 @@ int xfs_ag_init_headers(struct xfs_mount *mp, struct aghdr_init_data *id); int xfs_ag_shrink_space(struct xfs_perag *pag, struct xfs_trans **tpp, xfs_extlen_t delta); void -xfs_growfs_compute_deltas(struct xfs_mount *mp, xfs_rfsblock_t nb, +xfs_growfs_compute_deltas(struct xfs_mount *mp, xfs_rfsblock_t *nb, int64_t *deltap, xfs_agnumber_t *nagcountp); int xfs_ag_extend_space(struct xfs_perag *pag, struct xfs_trans *tp, xfs_extlen_t len); diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c index 8d64d904d73c1..436857356a0a1 100644 --- a/fs/xfs/xfs_fsops.c +++ b/fs/xfs/xfs_fsops.c @@ -124,7 +124,7 @@ xfs_growfs_data_private( mp->m_sb.sb_rextsize); if (error) return error; - xfs_growfs_compute_deltas(mp, nb, &delta, &nagcount); + xfs_growfs_compute_deltas(mp, &nb, &delta, &nagcount); /* * Reject filesystems with a single AG because they are not From ce03e51a24c100e06da0e9b304d4abab52028c4d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 10 Jun 2026 07:07:21 +0200 Subject: [PATCH 0434/2758] xfs: only log freed extents for the current RTG in zoned growfs commit 44cccefe65749821d9a13523c8b763bf1262ef73 upstream. Otherwise a power fail or crash during growfs could lead to an elevated sb_rblocks counter. Note that the step function is much simpler compared to the classic RT allocator as zoned RT sections must be aligned to real time group boundaries. Fixes: 01b71e64bb87 ("xfs: support growfs on zoned file systems") Cc: # v6.15 Signed-off-by: Christoph Hellwig Reviewed-by: "Darrick J. Wong" Reviewed-by: Damien Le Moal Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/xfs_rtalloc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/xfs/xfs_rtalloc.c b/fs/xfs/xfs_rtalloc.c index 153f3c378f9fb..419140dd1d519 100644 --- a/fs/xfs/xfs_rtalloc.c +++ b/fs/xfs/xfs_rtalloc.c @@ -890,8 +890,7 @@ xfs_growfs_rt_sb_fields( static int xfs_growfs_rt_zoned( - struct xfs_rtgroup *rtg, - xfs_rfsblock_t nrblocks) + struct xfs_rtgroup *rtg) { struct xfs_mount *mp = rtg_mount(rtg); struct xfs_mount *nmp; @@ -903,7 +902,8 @@ xfs_growfs_rt_zoned( * Calculate new sb and mount fields for this round. Also ensure the * rtg_extents value is uptodate as the rtbitmap code relies on it. */ - nmp = xfs_growfs_rt_alloc_fake_mount(mp, nrblocks, + nmp = xfs_growfs_rt_alloc_fake_mount(mp, + xfs_rtgs_to_rfsbs(mp, rtg_rgno(rtg) + 1), mp->m_sb.sb_rextsize); if (!nmp) return -ENOMEM; @@ -1218,7 +1218,7 @@ xfs_growfs_rtg( } if (xfs_has_zoned(mp)) { - error = xfs_growfs_rt_zoned(rtg, nrblocks); + error = xfs_growfs_rt_zoned(rtg); goto out_rele; } From ed16544d0d8b61332c60b8193a70f3e41898c566 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 9 Jun 2026 09:53:44 +0200 Subject: [PATCH 0435/2758] xfs: initialize iomap->flags earlier in xfs_bmbt_to_iomap commit 327e58826eb72f8bae9419cf1a4e722b57c85694 upstream. Otherwise we lose the IOMAP_IOEND_BOUNDARY assingment for writes to the first block in a realtime group, and could cause incorrect merges for such writes. Fixes: b91afef72471 ("xfs: don't merge ioends across RTGs") Cc: # v6.13 Signed-off-by: Christoph Hellwig Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/xfs_iomap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c index f20a02f49ed96..1403d568a4425 100644 --- a/fs/xfs/xfs_iomap.c +++ b/fs/xfs/xfs_iomap.c @@ -113,6 +113,7 @@ xfs_bmbt_to_iomap( return xfs_alert_fsblock_zero(ip, imap); } + iomap->flags = iomap_flags; if (imap->br_startblock == HOLESTARTBLOCK) { iomap->addr = IOMAP_NULL_ADDR; iomap->type = IOMAP_HOLE; @@ -143,7 +144,6 @@ xfs_bmbt_to_iomap( } iomap->offset = XFS_FSB_TO_B(mp, imap->br_startoff); iomap->length = XFS_FSB_TO_B(mp, imap->br_blockcount); - iomap->flags = iomap_flags; if (mapping_flags & IOMAP_DAX) { iomap->dax_dev = target->bt_daxdev; } else { From cbcb09dacb71ebce524d37e71d297edbd3d9815a Mon Sep 17 00:00:00 2001 From: Alexey Nepomnyashih Date: Wed, 3 Jun 2026 20:41:47 +0000 Subject: [PATCH 0436/2758] xfs: fix unreachable BIGTIME check in dquot flush validation commit 03866d130ed33ab68cc7faaf4bf2c4abef96d42e upstream. The dqp->q_id == 0 check inside the XFS_DQTYPE_BIGTIME block is unreachable because root dquots return successfully earlier. Reject root dquots with XFS_DQTYPE_BIGTIME before that early return, preserving the intended validation and removing the unreachable condition. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 4ea1ff3b4968 ("xfs: widen ondisk quota expiration timestamps to handle y2038+") Cc: stable@vger.kernel.org # v5.10+ Signed-off-by: Alexey Nepomnyashih Reviewed-by: "Darrick J. Wong" Reviewed-by: Allison Henderson Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/xfs_dquot.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index 69e9bc588c8b6..c311f61d95541 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -1216,6 +1216,14 @@ xfs_qm_dqflush_check( type != XFS_DQTYPE_PROJ) return __this_address; + /* bigtime flag should never be set on root dquots */ + if (dqp->q_type & XFS_DQTYPE_BIGTIME) { + if (!xfs_has_bigtime(dqp->q_mount)) + return __this_address; + if (dqp->q_id == 0) + return __this_address; + } + if (dqp->q_id == 0) return NULL; @@ -1231,14 +1239,6 @@ xfs_qm_dqflush_check( !dqp->q_rtb.timer) return __this_address; - /* bigtime flag should never be set on root dquots */ - if (dqp->q_type & XFS_DQTYPE_BIGTIME) { - if (!xfs_has_bigtime(dqp->q_mount)) - return __this_address; - if (dqp->q_id == 0) - return __this_address; - } - return NULL; } From 60db12509ec06d0805d92507bd20fdf24867ea29 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 9 Jun 2026 21:57:24 -0700 Subject: [PATCH 0437/2758] xfs: fix pointer arithmetic error on 32-bit systems commit 84eec3f7fc73144d1a230c9e8ad92721e37dcaab upstream. The translation of the old XFS_BMBT_KEY_ADDR macro into a static function is not correct on 32-bit systems because the sizeof() argument went from being a xfs_bmbt_key_t (i.e. a struct) to a (struct xfs_bmbt_key *) (i.e. a pointer to the same struct). On 64-bit systems this turns out ok because they are the same size, but on 32-bit systems this is catastrophic because they are not the same size. So far there have been no complaints, most likely because the xfs developers urge against running it on 32-bit systems. But this needs fixing asap. Cc: stable@vger.kernel.org # v6.12 Fixes: 79124b37400635 ("xfs: replace shouty XFS_BM{BT,DR} macros") Signed-off-by: "Darrick J. Wong" Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/libxfs/xfs_bmap_btree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/libxfs/xfs_bmap_btree.h b/fs/xfs/libxfs/xfs_bmap_btree.h index b238d559ab036..e0c870beaf670 100644 --- a/fs/xfs/libxfs/xfs_bmap_btree.h +++ b/fs/xfs/libxfs/xfs_bmap_btree.h @@ -89,7 +89,7 @@ xfs_bmbt_key_addr( { return (struct xfs_bmbt_key *) ((char *)block + xfs_bmbt_block_len(mp) + - (index - 1) * sizeof(struct xfs_bmbt_key *)); + (index - 1) * sizeof(struct xfs_bmbt_key)); } static inline xfs_bmbt_ptr_t * From 4707344b0d36d1012c8a1716e20167cd3afdd5f1 Mon Sep 17 00:00:00 2001 From: Yingjie Gao Date: Thu, 4 Jun 2026 20:03:17 +0800 Subject: [PATCH 0438/2758] xfs: fix exchmaps reservation limit check commit 0a5213bbff62b51c7d4999ac8c7e11ea57d00d45 upstream. xfs_exchmaps_estimate_overhead() adds the bmbt and rmapbt overhead to a local resblks variable, but the final UINT_MAX check still tests req->resblks. That is the reservation value from before the overhead was added. The computed value is stored back in req->resblks and later passed to xfs_trans_alloc(), whose block reservation argument is unsigned int. Check the computed reservation so the existing limit applies to the value that will be used. Fixes: 966ceafc7a43 ("xfs: create deferred log items for file mapping exchanges") Cc: stable@vger.kernel.org # v6.10 Signed-off-by: Yingjie Gao Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/libxfs/xfs_exchmaps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/libxfs/xfs_exchmaps.c b/fs/xfs/libxfs/xfs_exchmaps.c index 5d28f4eac527b..541e33f331672 100644 --- a/fs/xfs/libxfs/xfs_exchmaps.c +++ b/fs/xfs/libxfs/xfs_exchmaps.c @@ -711,7 +711,7 @@ xfs_exchmaps_estimate_overhead( return -ENOSPC; /* Can't actually reserve more than UINT_MAX blocks. */ - if (req->resblks > UINT_MAX) + if (resblks > UINT_MAX) return -ENOSPC; req->resblks = resblks; From 06a2e6dbaa26c0740ac76dfa66b0aedc78d05820 Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Sat, 27 Jun 2026 14:04:02 +0800 Subject: [PATCH 0439/2758] xfs: fix memory leak in xfs_dqinode_metadir_create() commit 45de375b25060edf46e20abb36521ba530336ceb upstream. If xfs_metadir_create() fails in xfs_dqinode_metadir_create(), the current code returns directly, leaking the allocated update and transaction state. If the subsequent commit fails, the caller-owned inode reference is left behind. Fix this memory leak by routing the create failure path through xfs_metadir_cancel(). For both create and commit failures, finish and release any inode returned to the caller, mirroring the unwind pattern in xfs_metadir_mkdir(). The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1.1. An x86_64 allyesconfig build showed no new warnings. Runtime validation used kprobe fault injection during `mount -o uquota` on a metadir XFS image. Injecting xfs_metadir_create() reproduced the old active-update path that left mount stuck later in mount setup; after this change, the same injection reported cancel_hits=1 and irele_hits=1. Injecting xfs_metadir_commit() exercised the old inode-reference leak path; after this change, it reported irele_hits=1. Fixes: e80fbe1ad8ef ("xfs: use metadir for quota inodes") Cc: stable@vger.kernel.org # v6.13 Signed-off-by: Dawei Feng Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/libxfs/xfs_dquot_buf.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/fs/xfs/libxfs/xfs_dquot_buf.c b/fs/xfs/libxfs/xfs_dquot_buf.c index ce767b40482f8..bbada0d3cc081 100644 --- a/fs/xfs/libxfs/xfs_dquot_buf.c +++ b/fs/xfs/libxfs/xfs_dquot_buf.c @@ -436,17 +436,27 @@ xfs_dqinode_metadir_create( error = xfs_metadir_create(&upd, S_IFREG); if (error) - return error; + goto out_cancel; xfs_trans_log_inode(upd.tp, upd.ip, XFS_ILOG_CORE); error = xfs_metadir_commit(&upd); if (error) - return error; + goto out_irele; xfs_finish_inode_setup(upd.ip); *ipp = upd.ip; return 0; + +out_cancel: + xfs_metadir_cancel(&upd, error); +out_irele: + /* Have to finish setting up the inode to ensure it's deleted. */ + if (upd.ip) { + xfs_finish_inode_setup(upd.ip); + xfs_irele(upd.ip); + } + return error; } #ifndef __KERNEL__ From 51d07c12ca411e692c424ecdabf077f1e61a61be Mon Sep 17 00:00:00 2001 From: Zhao Zhang Date: Tue, 2 Jun 2026 16:43:33 +0800 Subject: [PATCH 0440/2758] bpf: Reject fragmented frames in devmap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit aa496720618f1a6054f1c870bf10b4f6c99bf656 upstream. Devmap broadcast redirects clone the packet for all but the last destination. For native XDP, that clone path copies only the linear xdp_frame data, while fragmented frames keep skb_shared_info in tailroom outside the linear area. Cloning such a frame leaves XDP_FLAGS_HAS_FRAGS set but without valid frag metadata, and the later free path can interpret uninitialized tail data as skb_shared_info, leading to an out-of-bounds access during frame return. Reject fragmented native XDP frames in dev_map_enqueue_clone(). Add the same restriction to the generic XDP clone path in dev_map_redirect_clone(). Generic XDP represents fragmented packets as nonlinear skbs, and rejecting them here keeps clone-based broadcast support aligned between native and generic XDP. Fixes: e624d4ed4aa8 ("xdp: Extend xdp_redirect_map with broadcast support") Cc: stable@kernel.org Reported-by: Yuan Tan Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Signed-off-by: Zhao Zhang Signed-off-by: Ren Wei Reviewed-by: Emil Tsalapatis Reviewed-by: Toke Høiland-Jørgensen Link: https://lore.kernel.org/r/21c2d153dd25603d359069a02bf06779b51f6423.1780385378.git.zzhan461@ucr.edu Signed-off-by: Alexei Starovoitov Signed-off-by: Greg Kroah-Hartman --- kernel/bpf/devmap.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/kernel/bpf/devmap.c b/kernel/bpf/devmap.c index cc0a43ebab6b9..5b9eac5342a90 100644 --- a/kernel/bpf/devmap.c +++ b/kernel/bpf/devmap.c @@ -581,6 +581,10 @@ static int dev_map_enqueue_clone(struct bpf_dtab_netdev *obj, { struct xdp_frame *nxdpf; + /* Frags live outside the linear frame and cannot be cloned safely. */ + if (unlikely(xdp_frame_has_frags(xdpf))) + return -EOPNOTSUPP; + nxdpf = xdpf_clone(xdpf); if (!nxdpf) return -ENOMEM; @@ -726,6 +730,9 @@ static int dev_map_redirect_clone(struct bpf_dtab_netdev *dst, struct sk_buff *nskb; int err; + if (unlikely(skb_is_nonlinear(skb))) + return -EOPNOTSUPP; + nskb = skb_clone(skb, GFP_ATOMIC); if (!nskb) return -ENOMEM; From ee7099359f965d649a52150b76ae450c68ed6b56 Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Wed, 3 Jun 2026 18:53:17 +0800 Subject: [PATCH 0441/2758] bpf: Restore sysctl new-value from 1 to 0 commit 2566c3b24219c5b30e35205cba029ff34ff7c78b upstream. Commit 4e63acdff864 ("bpf: Introduce bpf_sysctl_{get,set}_new_value helpers") changed the success return value to 0, but failed to update the corresponding check in __cgroup_bpf_run_filter_sysctl(). Since bpf_prog_run_array_cg() now returns 0 on success, the legacy ret == 1 condition is never satisfied. As a result, the modified value is ignored, and bpf_sysctl_set_new_value() fails to replace the write buffer. Fix this by checking for a return value of 0 instead, so cgroup/sysctl programs can correctly replace the pending sysctl buffer. This bug was discovered during a manual code review. Tested via a cgroup/sysctl BPF reproducer overriding writes to a target sysctl. Pre-fix, bpf_sysctl_set_new_value("foo") was silently ignored: the write returned 8192 and the value remained "600". Post-fix, the BPF replacement buffer properly propagates: the write returns 3 and the value updates to "foo". Fixes: f10d05966196 ("bpf: Make BPF_PROG_RUN_ARRAY return -err instead of allow boolean") Cc: stable@vger.kernel.org Acked-by: Yonghong Song Signed-off-by: Zilin Guan Signed-off-by: Dawei Feng Reviewed-by: Jiayuan Chen Acked-by: Xu Kuohai Link: https://lore.kernel.org/r/20260603105317.944304-4-dawei.feng@seu.edu.cn Signed-off-by: Alexei Starovoitov Signed-off-by: Greg Kroah-Hartman --- kernel/bpf/cgroup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index faadcfb9b5e5b..f4eefdacd4533 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -1935,7 +1935,7 @@ int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head, kfree(ctx.cur_val); - if (ret == 1 && ctx.new_updated) { + if (!ret && ctx.new_updated) { kvfree(*buf); *buf = ctx.new_val; *pcount = ctx.new_len; From ff77d013b737c0f77d925e2f2c59f0cf3d76bd35 Mon Sep 17 00:00:00 2001 From: Paul Moses Date: Fri, 5 Jun 2026 23:43:09 +0000 Subject: [PATCH 0442/2758] bpf: Validate BTF repeated field counts before expansion commit b9452b594fd3aecbfd4aa0a6a1f741330a37dab7 upstream. btf_parse_struct_metas() walks user-supplied BTF during BPF_BTF_LOAD, and btf_repeat_fields() expands repeatable fields from array elements into the fixed BTF_FIELDS_MAX scratch array used by btf_parse_fields(). The remaining-capacity check performs the expanded field count calculation in u32. A malformed BTF can wrap that calculation, causing the check to pass even when the expanded field count exceeds the scratch array capacity. The following memcpy() can then write past the end of the array. Use checked addition and multiplication before copying repeated fields and reject impossible counts. Fixes: 797d73ee232d ("bpf: Check the remaining info_cnt before repeating btf fields") Cc: stable@vger.kernel.org Signed-off-by: Paul Moses Acked-by: Eduard Zingerman Link: https://lore.kernel.org/bpf/20260605234301.1109063-1-p@1g4.org Signed-off-by: Kumar Kartikeya Dwivedi Signed-off-by: Greg Kroah-Hartman --- kernel/bpf/btf.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index a62d785812076..510aa32847da9 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -3668,7 +3668,7 @@ static int btf_get_field_type(const struct btf *btf, const struct btf_type *var_ static int btf_repeat_fields(struct btf_field_info *info, int info_cnt, u32 field_cnt, u32 repeat_cnt, u32 elem_size) { - u32 i, j; + u32 i, j, total_cnt, total_repeats; u32 cur; /* Ensure not repeating fields that should not be repeated. */ @@ -3686,10 +3686,9 @@ static int btf_repeat_fields(struct btf_field_info *info, int info_cnt, } } - /* The type of struct size or variable size is u32, - * so the multiplication will not overflow. - */ - if (field_cnt * (repeat_cnt + 1) > info_cnt) + if (check_add_overflow(repeat_cnt, 1, &total_repeats) || + check_mul_overflow(field_cnt, total_repeats, &total_cnt) || + total_cnt > (u32)info_cnt) return -E2BIG; cur = field_cnt; From d57db0d975053e01410c54e708a85b6d32ef2ebd Mon Sep 17 00:00:00 2001 From: Nuoqi Gui Date: Sun, 7 Jun 2026 21:24:13 +0800 Subject: [PATCH 0443/2758] bpf: Keep dynamic inner array lookups nullable commit 53040a81ae57cdca8af8ac36fe4e661730cf7c6b upstream. An ARRAY_OF_MAPS can use an array created with BPF_F_INNER_MAP as its inner map template. A concrete inner array with a different max_entries value can then replace the template. After a successful outer map lookup, the verifier represents the resulting map pointer using the inner map template. Const-key lookup nullness elision consequently uses the template max_entries even though the runtime helper uses the concrete inner map max_entries. Do not elide lookup result nullness for maps marked with BPF_F_INNER_MAP, because the template max_entries does not prove that the key is in bounds for the concrete runtime map. Fixes: d2102f2f5d75 ("bpf: verifier: Support eliding map lookup nullness") Signed-off-by: Nuoqi Gui Acked-by: Eduard Zingerman Acked-by: Jiri Olsa Cc: stable@vger.kernel.org Link: https://lore.kernel.org/bpf/20260607-f01-v2-v2-1-da48453146e8@mails.tsinghua.edu.cn Signed-off-by: Kumar Kartikeya Dwivedi Signed-off-by: Greg Kroah-Hartman --- kernel/bpf/verifier.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 7fb88e1cd7c4d..ff9b1f68ceca4 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -8471,7 +8471,7 @@ static int get_constant_map_key(struct bpf_verifier_env *env, return 0; } -static bool can_elide_value_nullness(enum bpf_map_type type); +static bool can_elide_value_nullness(const struct bpf_map *map); static int check_func_arg(struct bpf_verifier_env *env, u32 arg, struct bpf_call_arg_meta *meta, @@ -8621,7 +8621,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg, err = check_helper_mem_access(env, regno, key_size, BPF_READ, false, NULL); if (err) return err; - if (can_elide_value_nullness(meta->map.ptr->map_type)) { + if (can_elide_value_nullness(meta->map.ptr)) { err = get_constant_map_key(env, reg, key_size, &meta->const_map_key); if (err < 0) { meta->const_map_key = -1; @@ -10221,13 +10221,16 @@ static void update_loop_inline_state(struct bpf_verifier_env *env, u32 subprogno state->callback_subprogno == subprogno); } -/* Returns whether or not the given map type can potentially elide +/* Returns whether or not the given map can potentially elide * lookup return value nullness check. This is possible if the key * is statically known. */ -static bool can_elide_value_nullness(enum bpf_map_type type) +static bool can_elide_value_nullness(const struct bpf_map *map) { - switch (type) { + if (map->map_flags & BPF_F_INNER_MAP) + return false; + + switch (map->map_type) { case BPF_MAP_TYPE_ARRAY: case BPF_MAP_TYPE_PERCPU_ARRAY: return true; @@ -10589,7 +10592,7 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn } if (func_id == BPF_FUNC_map_lookup_elem && - can_elide_value_nullness(meta.map.ptr->map_type) && + can_elide_value_nullness(meta.map.ptr) && meta.const_map_key >= 0 && meta.const_map_key < meta.map.ptr->max_entries) ret_flag &= ~PTR_MAYBE_NULL; From bd6ad9a6b30498d845413e863fb95c6fab3babe3 Mon Sep 17 00:00:00 2001 From: Vlad Poenaru Date: Tue, 9 Jun 2026 06:55:57 -0700 Subject: [PATCH 0444/2758] bpf: Allow LPM map access from sleepable BPF programs commit 2f884d371fafea137afea504d49ee4a7c8d7985b upstream. trie_lookup_elem() annotates its rcu_dereference_check() walks with only rcu_read_lock_bh_held(). Because rcu_dereference_check(p, c) resolves to "c || rcu_read_lock_held()", this passes for XDP/NAPI and classic RCU readers but fails for sleepable BPF programs, which enter via __bpf_prog_enter_sleepable() and hold only rcu_read_lock_trace(). trie_update_elem() and trie_delete_elem() have the same problem in a different form: they walk the trie with plain rcu_dereference(), which asserts rcu_read_lock_held() unconditionally. Both are reachable from sleepable BPF programs via the bpf_map_update_elem / bpf_map_delete_elem helpers, and from the syscall path under classic rcu_read_lock(). In the writer paths the trie is actually protected by trie->lock (an rqspinlock taken across the walk); we never relied on the RCU read-side lock to keep nodes alive there. A sleepable LSM hook that ends up touching an LPM trie therefore triggers lockdep on debug kernels: ============================= WARNING: suspicious RCU usage 7.1.0-... Tainted: G E ----------------------------- kernel/bpf/lpm_trie.c:249 suspicious rcu_dereference_check() usage! 1 lock held by net_tests/540: #0: (rcu_tasks_trace_srcu_struct){....}-{0:0}, at: __bpf_prog_enter_sleepable+0x26/0x280 Call Trace: dump_stack_lvl lockdep_rcu_suspicious trie_lookup_elem bpf_prog_..._enforce_security_socket_connect bpf_trampoline_... security_socket_connect __sys_connect do_syscall_64 This is lockdep-only -- no UAF, since Tasks Trace RCU does serialize against the trie's reclaim path -- but it spams the console once per distinct callsite on every debug kernel running a sleepable BPF LSM that touches an LPM trie, which is increasingly common. For the lookup path, switch the rcu_dereference_check() annotation from rcu_read_lock_bh_held() to bpf_rcu_lock_held(), which accepts all three contexts (classic, BH, Tasks Trace). Other map types already follow this convention. For trie_update_elem() and trie_delete_elem(), annotate the walks as rcu_dereference_protected(*p, 1) -- matching trie_free() in the same file -- since trie->lock is held across the walk. rqspinlock has no lockdep_map, so the predicate degenerates to '1' rather than lockdep_is_held(&trie->lock); the protection is real but not machine-verifiable. trie_get_next_key() also uses bare rcu_dereference() but is reachable only from the BPF syscall, which holds classic rcu_read_lock() before dispatching, so it is left untouched. Fixes: 694cea395fde ("bpf: Allow RCU-protected lookups to happen from bh context") Cc: stable@vger.kernel.org Signed-off-by: Vlad Poenaru Reviewed-by: Emil Tsalapatis Link: https://lore.kernel.org/r/20260609135558.193287-2-vlad.wing@gmail.com Signed-off-by: Alexei Starovoitov Signed-off-by: Greg Kroah-Hartman --- kernel/bpf/lpm_trie.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/bpf/lpm_trie.c b/kernel/bpf/lpm_trie.c index 0f57608b385d4..4d6f25db9ba12 100644 --- a/kernel/bpf/lpm_trie.c +++ b/kernel/bpf/lpm_trie.c @@ -246,7 +246,7 @@ static void *trie_lookup_elem(struct bpf_map *map, void *_key) /* Start walking the trie from the root node ... */ - for (node = rcu_dereference_check(trie->root, rcu_read_lock_bh_held()); + for (node = rcu_dereference_check(trie->root, bpf_rcu_lock_held()); node;) { unsigned int next_bit; size_t matchlen; @@ -280,7 +280,7 @@ static void *trie_lookup_elem(struct bpf_map *map, void *_key) */ next_bit = extract_bit(key->data, node->prefixlen); node = rcu_dereference_check(node->child[next_bit], - rcu_read_lock_bh_held()); + bpf_rcu_lock_held()); } if (!found) @@ -359,7 +359,7 @@ static long trie_update_elem(struct bpf_map *map, */ slot = &trie->root; - while ((node = rcu_dereference(*slot))) { + while ((node = rcu_dereference_protected(*slot, 1))) { matchlen = longest_prefix_match(trie, node, key); if (node->prefixlen != matchlen || @@ -482,7 +482,7 @@ static long trie_delete_elem(struct bpf_map *map, void *_key) trim = &trie->root; trim2 = trim; parent = NULL; - while ((node = rcu_dereference(*trim))) { + while ((node = rcu_dereference_protected(*trim, 1))) { matchlen = longest_prefix_match(trie, node, key); if (node->prefixlen != matchlen || From 51e65f1d78457ea4f9513d90ab22c9dccbb35110 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Mon, 22 Jun 2026 16:01:57 +0800 Subject: [PATCH 0445/2758] net: usb: kalmia: bound RX frame length in kalmia_rx_fixup() commit 47b6bcef6e679593d2e86e04ee72c46a4e2f7139 upstream. kalmia_rx_fixup() computes usb_packet_length = skb->len - (2 * KALMIA_HEADER_LENGTH) as a u16, guarded only by a pre-loop check that skb->len is at least KALMIA_HEADER_LENGTH, which is 6. A device can deliver a short bulk-IN frame with skb->len in the 6 to 11 range, or leave a short trailing remainder on a later loop iteration. Either case underflows usb_packet_length to about 65530. That bypasses the usb_packet_length < ether_packet_length truncation path. The device-supplied ether_packet_length, a le16 up to 65535 read from header_start[2], then drives a memcmp() and the following skb_trim() and skb_pull() past the end of the rx buffer. The rx buffer is hard_mtu * 10, which is 14000 bytes. That is an out of bounds read. Require both the start and end framing headers to be present before subtracting them, on every loop iteration. Fixes: d40261236e8e ("net/usb: Add Samsung Kalmia driver for Samsung GT-B3730") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/178211531778.2216480.12637613349790980750@maoyixie.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/usb/kalmia.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/usb/kalmia.c b/drivers/net/usb/kalmia.c index ee9c48f7f68f9..0dd0a30c3db41 100644 --- a/drivers/net/usb/kalmia.c +++ b/drivers/net/usb/kalmia.c @@ -276,6 +276,14 @@ kalmia_rx_fixup(struct usbnet *dev, struct sk_buff *skb) "Received header: %6phC. Package length: %i\n", header_start, skb->len - KALMIA_HEADER_LENGTH); + /* both framing headers must be present before we subtract + * them, otherwise usb_packet_length underflows and the + * device-supplied ether_packet_length drives an out of bounds + * access below + */ + if (skb->len < 2 * KALMIA_HEADER_LENGTH) + return 0; + /* subtract start header and end header */ usb_packet_length = skb->len - (2 * KALMIA_HEADER_LENGTH); ether_packet_length = get_unaligned_le16(&header_start[2]); From 0aa71f2588103e83ffe953f3d7b38ee15f209582 Mon Sep 17 00:00:00 2001 From: Jens Glathe Date: Sat, 30 May 2026 10:20:22 +0200 Subject: [PATCH 0446/2758] Revert "usb: typec: mux: avoid duplicated mux switches" commit f576c75f95a52c71b30167d7efb6d47148f9c279 upstream. This reverts commit b145c3f29d62f71cc9d2d714e2d4ae4c8d3f863d. The deduplication logic appears to cause issues with separate SBU muxes. The mode-switch call on these (like gpio-sbu-mux) never appeared, so no successful mode-switch happened. The more high-end Parade PS883X redrivers are not affected due to being retimer-switch. The revert fixes dp altmode mode-switch for both. Tested on: Lenovo Thinkbook 16 G7 QOY Lenovo Ideapad 5 2in1 14Q8X9 Microsoft Windows Dev Kit 2023 (Blackrock) Lenovo Thinkpad T14s G6 Fixes: b145c3f29d62 ("usb: typec: mux: avoid duplicated mux switches") Cc: stable Signed-off-by: Jens Glathe Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260530-typc-mux-modeset-v1-1-64b0281e2cd6@oldschoolsolutions.biz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c index db5e4a4c0a996..9b908c46bd7df 100644 --- a/drivers/usb/typec/mux.c +++ b/drivers/usb/typec/mux.c @@ -275,9 +275,7 @@ static int mux_fwnode_match(struct device *dev, const void *fwnode) static void *typec_mux_match(const struct fwnode_handle *fwnode, const char *id, void *data) { - struct typec_mux_dev **mux_devs = data; struct device *dev; - int i; /* * Device graph (OF graph) does not give any means to identify the @@ -293,14 +291,6 @@ static void *typec_mux_match(const struct fwnode_handle *fwnode, dev = class_find_device(&typec_mux_class, NULL, fwnode, mux_fwnode_match); - /* Skip duplicates */ - for (i = 0; i < TYPEC_MUX_MAX_DEVS; i++) - if (to_typec_mux_dev(dev) == mux_devs[i]) { - put_device(dev); - return NULL; - } - - return dev ? to_typec_mux_dev(dev) : ERR_PTR(-EPROBE_DEFER); } @@ -326,8 +316,7 @@ struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode) return ERR_PTR(-ENOMEM); count = fwnode_connection_find_matches(fwnode, "mode-switch", - (void **)mux_devs, - typec_mux_match, + NULL, typec_mux_match, (void **)mux_devs, ARRAY_SIZE(mux_devs)); if (count <= 0) { From 35179684907c60946cbfc7fb9a355a44d8c891ba Mon Sep 17 00:00:00 2001 From: Jared Baldridge Date: Sat, 30 May 2026 18:19:48 -0400 Subject: [PATCH 0447/2758] usb: cdc_acm: Add quirk for Uniden BC125AT scanner commit 6eba58568f6cc3ff8515a00b05e258d8cfb72b72 upstream. Uniden BC125AT radio scanner has a USB interface which fails to work with the cdc_acm driver: usb 1-1: new full-speed USB device number 2 using uhci_hcd cdc_acm 1-1:1.0: Zero length descriptor references cdc_acm 1-1:1.0: probe with driver cdc_acm failed with error -22 usbcore: registered new interface driver cdc_acm Adding the NO_UNION_NORMAL quirk for the device fixes the issue: usb 1-1: new full-speed USB device number 2 using uhci_hcd cdc_acm 1-1:1.0: ttyACM0: USB ACM device usbcore: registered new interface driver cdc_acm `lsusb -v` of the device: Bus 001 Device 002: ID 1965:0017 Uniden Corporation BC125AT Negotiated speed: Full Speed (12Mbps) Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 2 Communications bDeviceSubClass 0 [unknown] bDeviceProtocol 0 bMaxPacketSize0 64 idVendor 0x1965 Uniden Corporation idProduct 0x0017 BC125AT bcdDevice 0.01 iManufacturer 1 Uniden America Corp. iProduct 2 BC125AT iSerial 3 0001 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 0x0030 bNumInterfaces 2 bConfigurationValue 1 iConfiguration 0 bmAttributes 0x80 (Bus Powered) MaxPower 500mA Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 1 bInterfaceClass 2 Communications bInterfaceSubClass 2 Abstract (modem) bInterfaceProtocol 0 iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x87 EP 7 IN bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x0008 1x 8 bytes bInterval 10 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 1 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 10 CDC Data bInterfaceSubClass 0 [unknown] bInterfaceProtocol 0 iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x02 EP 2 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 0 Device Status: 0x0000 (Bus Powered) Signed-off-by: Jared Baldridge Cc: stable Link: https://patch.msgid.link/20260530221959.612526-1-jrb@expunge.us Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index ddf0b59638595..3cb372721282a 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1810,6 +1810,9 @@ static const struct usb_device_id acm_ids[] = { { USB_DEVICE(0x1901, 0x0006), /* GE Healthcare Patient Monitor UI Controller */ .driver_info = DISABLE_ECHO, /* DISABLE ECHO in termios flag */ }, + { USB_DEVICE(0x1965, 0x0017), /* Uniden BC125AT */ + .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ + }, { USB_DEVICE(0x1965, 0x0018), /* Uniden UBC125XLT */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, From 963075c4da0cd43b3d17b107c355e1eb0ee64a58 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Mon, 22 Jun 2026 13:26:27 +0800 Subject: [PATCH 0448/2758] usb: cdnsp: fix stream context array leak in cdnsp_alloc_stream_info() commit 3348f444a4ce43dd5c2d1aa41634cb6eff33aa64 upstream. cdnsp_alloc_stream_info() allocates stream_info->stream_ctx_array with cdnsp_alloc_stream_ctx(). If a later stream ring allocation or stream mapping update fails, the error path frees the allocated stream rings and stream_rings array, but leaves stream_ctx_array allocated. Free the stream context array before falling through to the stream_rings cleanup path. Fixes: 3d82904559f4 ("usb: cdnsp: cdns3 Add main part of Cadence USBSSP DRD Driver") Cc: stable Signed-off-by: Haoxiang Li Acked-by: Peter Chen Link: https://patch.msgid.link/20260622052627.696373-1-haoxiang_li2024@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdnsp-mem.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/cdns3/cdnsp-mem.c b/drivers/usb/cdns3/cdnsp-mem.c index a2a1b21f2ef87..880097f1007d7 100644 --- a/drivers/usb/cdns3/cdnsp-mem.c +++ b/drivers/usb/cdns3/cdnsp-mem.c @@ -631,6 +631,8 @@ int cdnsp_alloc_stream_info(struct cdnsp_device *pdev, } } + cdnsp_free_stream_ctx(pdev, pep); + cleanup_stream_rings: kfree(pep->stream_info.stream_rings); From effc5f7942b43cbe54d85f694fdfa8247b9e7877 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Lugathe=20da=20Concei=C3=A7=C3=A3o=20Alves?= Date: Wed, 3 Jun 2026 08:36:26 -0300 Subject: [PATCH 0449/2758] USB: core: add USB_QUIRK_NO_LPM for VIA Labs USB 2.0 hub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit bd728c3d9b1cc0bb0fda6a7055c5c8b55d7477b2 upstream. The VIA Labs, Inc. USB 2.0 hub controller (2109:2817), found in a KVM switch, fails to enumerate high-power devices during cold boot and system restart. Applying the kernel parameter usbcore.quirks=2109:2817:k resolves the issue. Enumeration failure log: usb 1-1.2.3: device descriptor read/64, error -32 usb 1-1.2.3: Device not responding to setup address. usb 1-1.2.3: device not accepting address 11, error -71 usb 1-1.2-port3: unable to enumerate USB device Add USB_QUIRK_NO_LPM for this device. Signed-off-by: Rodrigo Lugathe da Conceição Alves Cc: stable Link: https://patch.msgid.link/20260603113626.395612-1-lugathe2@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/quirks.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 87810eff974ef..a72c8cdf97771 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -576,6 +576,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* VLI disk */ { USB_DEVICE(0x2109, 0x0711), .driver_info = USB_QUIRK_NO_LPM }, + /* VIA Labs, Inc. USB2.0 Hub */ + { USB_DEVICE(0x2109, 0x2817), .driver_info = USB_QUIRK_NO_LPM }, + /* Raydium Touchscreen */ { USB_DEVICE(0x2386, 0x3114), .driver_info = USB_QUIRK_NO_LPM }, From 4349e487a1149ff33b65d53427b8aca57f2e4578 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 3 Jul 2026 17:20:33 +0100 Subject: [PATCH 0450/2758] usb: dwc3: fix dwc3_readl() and dwc3_writel() calls in dwc3_ulpi_setup() commit e0f844d9d74200d311c6438a0f04270834ba5365 upstream. The dwc3_ulpi_setup() calls the register read and write calls with dwc3->regs when both these calls take the dwc3 structure directly. Chnage these two calls to fix the following sparse warning, and possibly a nasty bug in the dwc3_ulpi_setup() code: drivers/usb/dwc3/core.c:796:45: warning: incorrect type in argument 1 (different address spaces) drivers/usb/dwc3/core.c:796:45: expected struct dwc3 *dwc drivers/usb/dwc3/core.c:796:45: got void [noderef] __iomem *regs drivers/usb/dwc3/core.c:798:40: warning: incorrect type in argument 1 (different address spaces) drivers/usb/dwc3/core.c:798:40: expected struct dwc3 *dwc drivers/usb/dwc3/core.c:798:40: got void [noderef] __iomem *regs Cc: stable Fixes: 9accc68b1cf0 ("usb: dwc3: Add dwc pointer to dwc3_readl/writel") Acked-by: Thinh Nguyen Signed-off-by: Ben Dooks Link: https://patch.msgid.link/20260703162033.2847599-1-ben.dooks@codethink.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 65213896de998..6f3c00b089371 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -793,9 +793,9 @@ static void dwc3_ulpi_setup(struct dwc3 *dwc) if (dwc->enable_usb2_transceiver_delay) { for (index = 0; index < dwc->num_usb2_ports; index++) { - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(index)); + reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(index)); reg |= DWC3_GUSB2PHYCFG_XCVRDLY; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg); + dwc3_writel(dwc, DWC3_GUSB2PHYCFG(index), reg); } } } From f4f5219c06d4944a573ef4cf0f089c4ddd9919cf Mon Sep 17 00:00:00 2001 From: WenTao Liang Date: Thu, 11 Jun 2026 21:11:21 +0800 Subject: [PATCH 0451/2758] usb: dwc3: meson-g12a: fix refcount leak in dwc3_meson_g12a_resume() commit 692c354bef03b77b30e57e61934da502c8a12d45 upstream. If dwc3_meson_g12a_resume() succeeds in calling reset_control_reset(), an internal triggered_count reference is acquired. If any later step fails (usb_init, phy_init, phy_power_on, regulator_enable, or usb_post_init), the function returns the error without rearming the reset control. This leaks the reference and leaves the reset control in a triggered state, causing future reset_control_reset() calls to incorrectly return early as if already reset. Add an error path that calls reset_control_rearm() to balance the reference before returning the error. Cc: stable Fixes: 5b0ba0caaf3a ("usb: dwc3: meson-g12a: refactor usb init") Signed-off-by: WenTao Liang Link: https://patch.msgid.link/20260611131121.81784-1-vulab@iscas.ac.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-meson-g12a.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c index 55e144ba8cfc6..4d611c08e8a4c 100644 --- a/drivers/usb/dwc3/dwc3-meson-g12a.c +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -907,35 +907,39 @@ static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev) ret = priv->drvdata->usb_init(priv); if (ret) - return ret; + goto err_rearm; /* Init PHYs */ for (i = 0 ; i < PHY_COUNT ; ++i) { ret = phy_init(priv->phys[i]); if (ret) - return ret; + goto err_rearm; } /* Set PHY Power */ for (i = 0 ; i < PHY_COUNT ; ++i) { ret = phy_power_on(priv->phys[i]); if (ret) - return ret; + goto err_rearm; } if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) { ret = regulator_enable(priv->vbus); if (ret) - return ret; + goto err_rearm; } if (priv->drvdata->usb_post_init) { ret = priv->drvdata->usb_post_init(priv); if (ret) - return ret; + goto err_rearm; } return 0; + +err_rearm: + reset_control_rearm(priv->reset); + return ret; } static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = { From 4bb88aee6b868cbf73bf453f62497802f5fe4769 Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Tue, 30 Jun 2026 15:14:19 +0800 Subject: [PATCH 0452/2758] usb: free iso schedules on failed submit commit b9399d25fbb34a05bbe76eeedd730f62ff2670e9 upstream. EHCI and FOTG210 isochronous submits build an ehci_iso_sched before linking the URB to the endpoint queue, and keep the staged schedule in urb->hcpriv until iso_stream_schedule() and the link helpers consume it. If the controller is no longer accessible, or usb_hcd_link_urb_to_ep() fails, submit jumps to done_not_linked before that handoff happens and leaks the staged schedule still attached to urb->hcpriv. Free the staged schedule from done_not_linked when submit fails before the URB is linked and clear urb->hcpriv after the free. The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1.1. An x86_64 allyesconfig build showed no new warnings. As we do not have an EHCI host controller with a USB isochronous device to test with, no runtime testing was able to be performed. Fixes: 8de98402652c ("[PATCH] USB: Fix USB suspend/resume crasher (#2)") Fixes: e9df41c5c589 ("USB: make HCDs responsible for managing endpoint queues") Fixes: 7d50195f6c50 ("usb: host: Faraday fotg210-hcd driver") Cc: stable Signed-off-by: Dawei Feng Reviewed-by: Alan Stern Link: https://patch.msgid.link/20260630071419.349161-1-dawei.feng@seu.edu.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/fotg210/fotg210-hcd.c | 6 ++++-- drivers/usb/host/ehci-sched.c | 11 +++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/usb/fotg210/fotg210-hcd.c b/drivers/usb/fotg210/fotg210-hcd.c index 1a48329a4e089..956be5b565102 100644 --- a/drivers/usb/fotg210/fotg210-hcd.c +++ b/drivers/usb/fotg210/fotg210-hcd.c @@ -4267,8 +4267,6 @@ static int iso_stream_schedule(struct fotg210_hcd *fotg210, struct urb *urb, return 0; fail: - iso_sched_free(stream, sched); - urb->hcpriv = NULL; return status; } @@ -4562,6 +4560,10 @@ static int itd_submit(struct fotg210_hcd *fotg210, struct urb *urb, else usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); done_not_linked: + if (status < 0) { + iso_sched_free(stream, urb->hcpriv); + urb->hcpriv = NULL; + } spin_unlock_irqrestore(&fotg210->lock, flags); done: return status; diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index a241337c9af86..57d07d1c2dfa6 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1623,6 +1623,7 @@ iso_stream_schedule( status = 1; /* and give it back immediately */ iso_sched_free(stream, sched); sched = NULL; + urb->hcpriv = NULL; } } urb->error_count = skip / period; @@ -1653,8 +1654,6 @@ iso_stream_schedule( return status; fail: - iso_sched_free(stream, sched); - urb->hcpriv = NULL; return status; } @@ -1966,6 +1965,10 @@ static int itd_submit(struct ehci_hcd *ehci, struct urb *urb, usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); } done_not_linked: + if (status < 0) { + iso_sched_free(stream, urb->hcpriv); + urb->hcpriv = NULL; + } spin_unlock_irqrestore(&ehci->lock, flags); done: return status; @@ -2343,6 +2346,10 @@ static int sitd_submit(struct ehci_hcd *ehci, struct urb *urb, usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); } done_not_linked: + if (status < 0) { + iso_sched_free(stream, urb->hcpriv); + urb->hcpriv = NULL; + } spin_unlock_irqrestore(&ehci->lock, flags); done: return status; From fcb21bf747640c9d6bd1eda9da85420f076d59c1 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Wed, 27 May 2026 23:08:32 +0800 Subject: [PATCH 0453/2758] usb: gadget: composite: fix dead empty check in the USB_DT_OTG handler commit f8f680609c2b3ab795ffcd6f21585b6dfc46d395 upstream. The OTG branch of composite_setup() falls back to the first configuration when none is selected: if (cdev->config) config = cdev->config; else config = list_first_entry(&cdev->configs, struct usb_configuration, list); if (!config) goto done; ... memcpy(req->buf, config->descriptors[0], value); list_first_entry() never returns NULL. On an empty list it returns container_of() of the list head. So the "if (!config)" check is dead. When cdev->configs is empty, config points at the head inside struct usb_composite_dev. config->descriptors[0] reads whatever sits at that offset. The memcpy copies up to w_length bytes of it into the response buffer. cdev->configs can be empty in two cases. One is a teardown race on gadget unbind with a control transfer in flight. The other is a driver that sets is_otg before it adds a config. A reproducer that holds cdev->configs empty triggers a KASAN fault in this branch. Use list_first_entry_or_null() so the existing check does its job. Fixes: 53e6242db8d6 ("usb: gadget: composite: add USB_DT_OTG request handling") Cc: stable Signed-off-by: Maoyi Xie Link: https://patch.msgid.link/20260527150832.2943293-1-maoyixie.tju@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/composite.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index dc36643745963..df39e3487c1f3 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1863,9 +1863,10 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) if (cdev->config) config = cdev->config; else - config = list_first_entry( + config = list_first_entry_or_null( &cdev->configs, - struct usb_configuration, list); + struct usb_configuration, + list); if (!config) goto done; From 54fa390aae393eb130f307a85562e3001cc39a52 Mon Sep 17 00:00:00 2001 From: Jimmy Hu Date: Thu, 25 Jun 2026 15:37:04 +0800 Subject: [PATCH 0454/2758] usb: gadget: udc: Fix use-after-free in gadget_match_driver commit 67e511d2989eb1c8c588b599ce2fcc6bb8e6f7ea upstream. The udc structure acts as the management structure for the gadget, but their lifecycles are decoupled. A race condition exists where usb_del_gadget() frees the udc memory (e.g., via mode-switch work) while gadget_match_driver() concurrently accesses the freed udc memory (e.g., via configfs), causing a Use-After-Free (UAF) that triggers a NULL pointer dereference when the freed memory is zeroed: [39430.908615][ T1171] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000 [39430.911397][ T1171] pc : __pi_strcmp+0x20/0x140 [39430.911441][ T1171] lr : gadget_match_driver+0x34/0x60 ... [39430.911890][ T1171] usb_gadget_register_driver_owner+0x50/0xf8 [39430.911910][ T1171] gadget_dev_desc_UDC_store+0xf4/0x140 [39430.931308][ T1171] configfs_write_iter+0xec/0x134 [39430.957058][ T1171] Workqueue: events_freezable __dwc3_set_mode [39430.957287][ T1171] dwc3_gadget_exit+0x34/0x8c [39430.957304][ T1171] __dwc3_set_mode+0xc0/0x664 Fix this by ensuring the udc structure remains allocated until the gadget is released. To achieve this, introduce a new usb_gadget_release() routine to the core. When the gadget is added, usb_add_gadget() stores the gadget's release routine in the udc structure and takes a reference to the udc. When the gadget is released, usb_gadget_release() drops the reference to the udc and then calls the gadget's release routine. Suggested-by: Alan Stern Cc: stable Signed-off-by: Jimmy Hu Reviewed-by: Alan Stern Link: https://patch.msgid.link/20260625073705.803880-1-hhhuuu@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/core.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index e8861eaad9077..068423fc581ae 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -31,8 +31,9 @@ static const struct bus_type gadget_bus_type; /** * struct usb_udc - describes one usb device controller * @driver: the gadget driver pointer. For use by the class code - * @dev: the child device to the actual controller * @gadget: the gadget. For use by the class code + * @gadget_release: the gadget's release routine + * @dev: the child device to the actual controller * @list: for use by the udc class driver * @vbus: for udcs who care about vbus status, this value is real vbus status; * for udcs who do not care about vbus status, this value is always true @@ -53,6 +54,7 @@ static const struct bus_type gadget_bus_type; struct usb_udc { struct usb_gadget_driver *driver; struct usb_gadget *gadget; + void (*gadget_release)(struct device *dev); struct device dev; struct list_head list; bool vbus; @@ -1357,6 +1359,17 @@ static void usb_udc_nop_release(struct device *dev) dev_vdbg(dev, "%s\n", __func__); } +static void usb_gadget_release(struct device *dev) +{ + struct usb_gadget *gadget = dev_to_usb_gadget(dev); + struct usb_udc *udc = gadget->udc; + /* Cache the gadget's release routine to prevent UAF */ + void (*release)(struct device *dev) = udc->gadget_release; + + put_device(&udc->dev); + release(dev); +} + /** * usb_initialize_gadget - initialize a gadget and its embedded struct device * @parent: the parent device to this udc. Usually the controller driver's @@ -1413,6 +1426,14 @@ int usb_add_gadget(struct usb_gadget *gadget) mutex_init(&udc->connect_lock); udc->started = false; + /* + * Align decoupled lifecycles: take a UDC reference to ensure it + * remains allocated until the gadget is released, requiring an + * override of the gadget's release routine to drop it. + */ + udc->gadget_release = gadget->dev.release; + gadget->dev.release = usb_gadget_release; + get_device(&udc->dev); mutex_lock(&udc_lock); list_add_tail(&udc->list, &udc_list); @@ -1457,6 +1478,12 @@ int usb_add_gadget(struct usb_gadget *gadget) mutex_lock(&udc_lock); list_del(&udc->list); mutex_unlock(&udc_lock); + /* + * Revert the override and drop the UDC reference to prevent + * leaking the UDC if the gadget was statically allocated. + */ + gadget->dev.release = udc->gadget_release; + put_device(&udc->dev); err_put_udc: put_device(&udc->dev); From 7f1f24c367938c5537e2308bf9a965f051d14774 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Fri, 26 Jun 2026 14:46:17 +0800 Subject: [PATCH 0455/2758] usb: gadget: f_printer: take kref only for successful open commit 30adce93d5c4a5a1ec29d9249e3fdfcc391d406b upstream. printer_open() returns -EBUSY when the character device is already open, but it increments dev->kref regardless of the return value. VFS does not call ->release() for a failed open, so every rejected second open permanently leaks one reference. Move kref_get() into the successful-open branch. Fixes: e8d5f92b8d30 ("usb: gadget: function: printer: fix use-after-free in __lock_acquire") Cc: stable Signed-off-by: Xu Rao Link: https://patch.msgid.link/80295742B820DA9B+20260626064617.4090626-1-raoxu@uniontech.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_printer.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c index e4f7828ae75df..837f753d0cae5 100644 --- a/drivers/usb/gadget/function/f_printer.c +++ b/drivers/usb/gadget/function/f_printer.c @@ -363,12 +363,11 @@ printer_open(struct inode *inode, struct file *fd) ret = 0; /* Change the printer status to show that it's on-line. */ dev->printer_status |= PRINTER_SELECTED; + kref_get(&dev->kref); } spin_unlock_irqrestore(&dev->lock, flags); - kref_get(&dev->kref); - return ret; } From e88cff5fbaa629f3cab45c8b46f395d62c2eb515 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 22 Jun 2026 17:26:10 +0200 Subject: [PATCH 0456/2758] USB: idmouse: fix use-after-free on disconnect race commit ff002c153f9722caece3983cc23dc4d9d4652cb4 upstream. mutex_unlock() may access the mutex structure after releasing the lock and therefore cannot be used to manage lifetime of objects directly (unlike spinlocks and refcounts). [1][2] Use a kref to release the driver data to avoid use-after-free in mutex_unlock() when release() races with disconnect(). [1] a51749ab34d9 ("locking/mutex: Document that mutex_unlock() is non-atomic") [2] 2b9d9e0a9ba0 ("locking/mutex: Clarify that mutex_unlock(), and most other sleeping locks, can still use the lock object after it's unlocked") Fixes: 54d2bc068fd2 ("USB: fix locking in idmouse") Cc: stable@vger.kernel.org # 2.6.24 Cc: Oliver Neukum Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260622152612.116422-3-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/idmouse.c | 45 +++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c index 0f6b3464c2d6a..3e37adf2bb573 100644 --- a/drivers/usb/misc/idmouse.c +++ b/drivers/usb/misc/idmouse.c @@ -63,6 +63,7 @@ MODULE_DEVICE_TABLE(usb, idmouse_table); /* structure to hold all of our device specific stuff */ struct usb_idmouse { + struct kref kref; struct usb_device *udev; /* save off the usb device pointer */ struct usb_interface *interface; /* the interface for this device */ @@ -209,8 +210,10 @@ static int idmouse_resume(struct usb_interface *intf) return 0; } -static inline void idmouse_delete(struct usb_idmouse *dev) +static inline void idmouse_delete(struct kref *kref) { + struct usb_idmouse *dev = container_of(kref, struct usb_idmouse, kref); + kfree(dev->bulk_in_buffer); kfree(dev); } @@ -254,6 +257,8 @@ static int idmouse_open(struct inode *inode, struct file *file) /* increment our usage count for the driver */ ++dev->open; + kref_get(&dev->kref); + /* save our object in the file's private structure */ file->private_data = dev; @@ -277,16 +282,11 @@ static int idmouse_release(struct inode *inode, struct file *file) /* lock our device */ mutex_lock(&dev->lock); - --dev->open; + mutex_unlock(&dev->lock); + + kref_put(&dev->kref, idmouse_delete); - if (!dev->present) { - /* the device was unplugged before the file was released */ - mutex_unlock(&dev->lock); - idmouse_delete(dev); - } else { - mutex_unlock(&dev->lock); - } return 0; } @@ -334,6 +334,7 @@ static int idmouse_probe(struct usb_interface *interface, if (dev == NULL) return -ENOMEM; + kref_init(&dev->kref); mutex_init(&dev->lock); dev->udev = udev; dev->interface = interface; @@ -342,8 +343,7 @@ static int idmouse_probe(struct usb_interface *interface, result = usb_find_bulk_in_endpoint(iface_desc, &endpoint); if (result) { dev_err(&interface->dev, "Unable to find bulk-in endpoint.\n"); - idmouse_delete(dev); - return result; + goto err_put_kref; } dev->orig_bi_size = usb_endpoint_maxp(endpoint); @@ -351,8 +351,8 @@ static int idmouse_probe(struct usb_interface *interface, dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; dev->bulk_in_buffer = kmalloc(IMGSIZE + dev->bulk_in_size, GFP_KERNEL); if (!dev->bulk_in_buffer) { - idmouse_delete(dev); - return -ENOMEM; + result = -ENOMEM; + goto err_put_kref; } /* allow device read, write and ioctl */ @@ -364,14 +364,18 @@ static int idmouse_probe(struct usb_interface *interface, if (result) { /* something prevented us from registering this device */ dev_err(&interface->dev, "Unable to allocate minor number.\n"); - idmouse_delete(dev); - return result; + goto err_put_kref; } /* be noisy */ dev_info(&interface->dev,"%s now attached\n",DRIVER_DESC); return 0; + +err_put_kref: + kref_put(&dev->kref, idmouse_delete); + + return result; } static void idmouse_disconnect(struct usb_interface *interface) @@ -387,14 +391,9 @@ static void idmouse_disconnect(struct usb_interface *interface) /* prevent device read, write and ioctl */ dev->present = 0; - /* if the device is opened, idmouse_release will clean this up */ - if (!dev->open) { - mutex_unlock(&dev->lock); - idmouse_delete(dev); - } else { - /* unlock */ - mutex_unlock(&dev->lock); - } + mutex_unlock(&dev->lock); + + kref_put(&dev->kref, idmouse_delete); dev_info(&interface->dev, "disconnected\n"); } From a3e794136ab5e3ad1e7019175a4b837aec86db4b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 22 Jun 2026 17:26:11 +0200 Subject: [PATCH 0457/2758] USB: ldusb: fix use-after-free on disconnect race commit 19bdfc7b3c179331eafa423d87e1336f43bbfeb8 upstream. mutex_unlock() may access the mutex structure after releasing the lock and therefore cannot be used to manage lifetime of objects directly (unlike spinlocks and refcounts). [1][2] Use a kref to release the driver data to avoid use-after-free in mutex_unlock() when release() races with disconnect(). [1] a51749ab34d9 ("locking/mutex: Document that mutex_unlock() is non-atomic") [2] 2b9d9e0a9ba0 ("locking/mutex: Clarify that mutex_unlock(), and most other sleeping locks, can still use the lock object after it's unlocked") Fixes: ce0d7d3f575f ("usb: ldusb: ld_usb semaphore to mutex") Cc: stable Cc: Daniel Walker Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260622152612.116422-4-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/ldusb.c | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c index c74f142f6637a..71132a15e7711 100644 --- a/drivers/usb/misc/ldusb.c +++ b/drivers/usb/misc/ldusb.c @@ -150,6 +150,7 @@ MODULE_PARM_DESC(min_interrupt_out_interval, "Minimum interrupt out interval in /* Structure to hold all of our device specific stuff */ struct ld_usb { + struct kref kref; struct mutex mutex; /* locks this structure */ struct usb_interface *intf; /* save off the usb interface pointer */ unsigned long disconnected:1; @@ -201,8 +202,10 @@ static void ld_usb_abort_transfers(struct ld_usb *dev) /* * ld_usb_delete */ -static void ld_usb_delete(struct ld_usb *dev) +static void ld_usb_delete(struct kref *kref) { + struct ld_usb *dev = container_of(kref, struct ld_usb, kref); + /* free data structures */ usb_free_urb(dev->interrupt_in_urb); usb_free_urb(dev->interrupt_out_urb); @@ -355,6 +358,8 @@ static int ld_usb_open(struct inode *inode, struct file *file) goto unlock_exit; } + kref_get(&dev->kref); + /* save device in the file's private structure */ file->private_data = dev; @@ -381,17 +386,8 @@ static int ld_usb_release(struct inode *inode, struct file *file) mutex_lock(&dev->mutex); - if (dev->open_count != 1) { - retval = -ENODEV; + if (dev->disconnected) goto unlock_exit; - } - if (dev->disconnected) { - /* the device was unplugged before the file was released */ - mutex_unlock(&dev->mutex); - /* unlock here as ld_usb_delete frees dev */ - ld_usb_delete(dev); - goto exit; - } /* wait until write transfer is finished */ if (dev->interrupt_out_busy) @@ -401,7 +397,7 @@ static int ld_usb_release(struct inode *inode, struct file *file) unlock_exit: mutex_unlock(&dev->mutex); - + kref_put(&dev->kref, ld_usb_delete); exit: return retval; } @@ -659,6 +655,8 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id * dev = kzalloc_obj(*dev); if (!dev) goto exit; + + kref_init(&dev->kref); mutex_init(&dev->mutex); spin_lock_init(&dev->rbsl); dev->intf = intf; @@ -740,7 +738,7 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id * return retval; error: - ld_usb_delete(dev); + kref_put(&dev->kref, ld_usb_delete); return retval; } @@ -768,18 +766,18 @@ static void ld_usb_disconnect(struct usb_interface *intf) mutex_lock(&dev->mutex); - /* if the device is not opened, then we clean up right now */ - if (!dev->open_count) { - mutex_unlock(&dev->mutex); - ld_usb_delete(dev); - } else { - dev->disconnected = 1; + dev->disconnected = 1; + + if (dev->open_count) { /* wake up pollers */ wake_up_interruptible_all(&dev->read_wait); wake_up_interruptible_all(&dev->write_wait); - mutex_unlock(&dev->mutex); } + mutex_unlock(&dev->mutex); + + kref_put(&dev->kref, ld_usb_delete); + dev_info(&intf->dev, "LD USB Device #%d now disconnected\n", (minor - USB_LD_MINOR_BASE)); } From e4596816984efc537e7c04c1af0c639394f967f7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sat, 23 May 2026 19:05:23 +0200 Subject: [PATCH 0458/2758] USB: iowarrior: fix use-after-free on disconnect commit bc0e4f16c44e50daa0b1ea729934baa3b4815dee upstream. Submitted write URBs are not stopped on close() and therefore need to be stopped unconditionally on disconnect() to avoid use-after-free in the completion handler. Fixes: b5f8d46867ca ("USB: iowarrior: fix use-after-free after driver unbind") Fixes: 946b960d13c1 ("USB: add driver for iowarrior devices.") Reported-by: syzbot+ad2aac2febc3bedf0962@syzkaller.appspotmail.com Link: https://lore.kernel.org/all/6a0ce39b.170a0220.39a13.0007.GAE@google.com/ Cc: stable Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260523170523.1074563-1-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/iowarrior.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index 22504c0a28416..88c6d1d1da116 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -905,13 +905,15 @@ static void iowarrior_disconnect(struct usb_interface *interface) /* prevent device read, write and ioctl */ dev->present = 0; + /* write urbs are not stopped on close() so kill unconditionally */ + usb_kill_anchored_urbs(&dev->submitted); + if (dev->opened) { /* There is a process that holds a filedescriptor to the device , so we only shutdown read-/write-ops going on. Deleting the device is postponed until close() was called. */ usb_kill_urb(dev->int_in_urb); - usb_kill_anchored_urbs(&dev->submitted); wake_up_interruptible(&dev->read_wait); wake_up_interruptible(&dev->write_wait); mutex_unlock(&dev->mutex); From 71590982700fdeb39a37a500c877228b0140978e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 22 Jun 2026 17:26:09 +0200 Subject: [PATCH 0459/2758] USB: iowarrior: fix use-after-free on disconnect race commit c602254ba4c10f60a73cd99d147874f86a3f485c upstream. mutex_unlock() may access the mutex structure after releasing the lock and therefore cannot be used to manage lifetime of objects directly (unlike spinlocks and refcounts). [1][2] Use a kref to release the driver data to avoid use-after-free in mutex_unlock() when release() races with disconnect(). [1] a51749ab34d9 ("locking/mutex: Document that mutex_unlock() is non-atomic") [2] 2b9d9e0a9ba0 ("locking/mutex: Clarify that mutex_unlock(), and most other sleeping locks, can still use the lock object after it's unlocked") Fixes: 946b960d13c1 ("USB: add driver for iowarrior devices.") Cc: stable Reported-by: Yue Sun Link: https://lore.kernel.org/r/20260618080204.38322-1-samsun1006219@gmail.com Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260622152612.116422-2-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/iowarrior.c | 57 +++++++++++++++--------------------- 1 file changed, 24 insertions(+), 33 deletions(-) diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index 88c6d1d1da116..de2b236ef9030 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -72,6 +72,7 @@ static struct usb_driver iowarrior_driver; /* Structure to hold all of our device specific stuff */ struct iowarrior { + struct kref kref; struct mutex mutex; /* locks this structure */ struct usb_device *udev; /* save off the usb device pointer */ struct usb_interface *interface; /* the interface for this device */ @@ -240,8 +241,10 @@ static void iowarrior_write_callback(struct urb *urb) /* * iowarrior_delete */ -static inline void iowarrior_delete(struct iowarrior *dev) +static inline void iowarrior_delete(struct kref *kref) { + struct iowarrior *dev = container_of(kref, struct iowarrior, kref); + kfree(dev->int_in_buffer); usb_free_urb(dev->int_in_urb); kfree(dev->read_queue); @@ -637,6 +640,9 @@ static int iowarrior_open(struct inode *inode, struct file *file) } /* increment our usage count for the driver */ ++dev->opened; + + kref_get(&dev->kref); + /* save our object in the file's private structure */ file->private_data = dev; retval = 0; @@ -652,7 +658,6 @@ static int iowarrior_open(struct inode *inode, struct file *file) static int iowarrior_release(struct inode *inode, struct file *file) { struct iowarrior *dev; - int retval = 0; dev = file->private_data; if (!dev) @@ -660,29 +665,18 @@ static int iowarrior_release(struct inode *inode, struct file *file) /* lock our device */ mutex_lock(&dev->mutex); + dev->opened = 0; /* we're closing now */ - if (dev->opened <= 0) { - retval = -ENODEV; /* close called more than once */ - mutex_unlock(&dev->mutex); - } else { - dev->opened = 0; /* we're closing now */ - retval = 0; - if (dev->present) { - /* - The device is still connected so we only shutdown - pending read-/write-ops. - */ - usb_kill_urb(dev->int_in_urb); - wake_up_interruptible(&dev->read_wait); - wake_up_interruptible(&dev->write_wait); - mutex_unlock(&dev->mutex); - } else { - /* The device was unplugged, cleanup resources */ - mutex_unlock(&dev->mutex); - iowarrior_delete(dev); - } + if (dev->present) { + usb_kill_urb(dev->int_in_urb); + wake_up_interruptible(&dev->read_wait); + wake_up_interruptible(&dev->write_wait); } - return retval; + mutex_unlock(&dev->mutex); + + kref_put(&dev->kref, iowarrior_delete); + + return 0; } static __poll_t iowarrior_poll(struct file *file, poll_table * wait) @@ -767,6 +761,7 @@ static int iowarrior_probe(struct usb_interface *interface, if (!dev) return retval; + kref_init(&dev->kref); mutex_init(&dev->mutex); atomic_set(&dev->intr_idx, 0); @@ -885,7 +880,8 @@ static int iowarrior_probe(struct usb_interface *interface, return retval; error: - iowarrior_delete(dev); + kref_put(&dev->kref, iowarrior_delete); + return retval; } @@ -909,19 +905,14 @@ static void iowarrior_disconnect(struct usb_interface *interface) usb_kill_anchored_urbs(&dev->submitted); if (dev->opened) { - /* There is a process that holds a filedescriptor to the device , - so we only shutdown read-/write-ops going on. - Deleting the device is postponed until close() was called. - */ usb_kill_urb(dev->int_in_urb); wake_up_interruptible(&dev->read_wait); wake_up_interruptible(&dev->write_wait); - mutex_unlock(&dev->mutex); - } else { - /* no process is using the device, cleanup now */ - mutex_unlock(&dev->mutex); - iowarrior_delete(dev); } + + mutex_unlock(&dev->mutex); + + kref_put(&dev->kref, iowarrior_delete); } /* usb specific object needed to register this driver with the usb subsystem */ From 452c5d97ba38f46fcfffa0c4234fba159eff6b52 Mon Sep 17 00:00:00 2001 From: "Erich E. Hoover" Date: Tue, 2 Jun 2026 14:45:08 -0600 Subject: [PATCH 0460/2758] USB: quirks: add NO_LPM for the Samsung T5 EVO Portable SSD commit fc591787785b9709a0bb65a7df3ba2537d611c47 upstream. The Samsung T5 EVO Portable SSD (04e8:6200) exhibit two forms of link instability when USB Link Power Management is enabled: 1. The units fail to initialize properly on first detection, resulting in a lockup in the drive where it must be power cycled or the kernel will not recognize the presence of the device. 2. If used for sustained operations (small amounts of continuous data are transferred to the unit) then the unit will "hiccup" after roughly 8 hours of use and will disconnect and reconnect. This has a certain probability of triggering the first issue, but also causes mount points to become invalid since the device gets issued a new letter. Signed-off-by: Erich E. Hoover Cc: stable Link: https://patch.msgid.link/20260602204508.48856-1-erich.e.hoover@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/quirks.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index a72c8cdf97771..87ee2d938bc03 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -296,6 +296,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* CarrolTouch 4500U */ { USB_DEVICE(0x04e7, 0x0030), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Samsung T5 EVO Portable SSD */ + { USB_DEVICE(0x04e8, 0x6200), .driver_info = USB_QUIRK_NO_LPM }, + /* Samsung Android phone modem - ID conflict with SPH-I500 */ { USB_DEVICE(0x04e8, 0x6601), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS }, From 9ba62966461a8e3cc593b62c56ec62eb2d80436d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 22 Jun 2026 17:26:12 +0200 Subject: [PATCH 0461/2758] USB: legousbtower: fix use-after-free on disconnect race commit 62fc8eb1b1481051f7bab4aa93d79809053dd09f upstream. mutex_unlock() may access the mutex structure after releasing the lock and therefore cannot be used to manage lifetime of objects directly (unlike spinlocks and refcounts). [1][2] Use a kref to release the driver data to avoid use-after-free in mutex_unlock() when release() races with disconnect(). [1] a51749ab34d9 ("locking/mutex: Document that mutex_unlock() is non-atomic") [2] 2b9d9e0a9ba0 ("locking/mutex: Clarify that mutex_unlock(), and most other sleeping locks, can still use the lock object after it's unlocked") Fixes: 18bcbcfe9ca2 ("USB: misc: legousbtower: semaphore to mutex") Cc: stable Cc: Daniel Walker Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260622152612.116422-5-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/legousbtower.c | 37 +++++++++++++++++---------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 052ffc2e71eea..18dd4115befb2 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -185,6 +185,7 @@ MODULE_DEVICE_TABLE(usb, tower_table); /* Structure to hold all of our device specific stuff */ struct lego_usb_tower { + struct kref kref; struct mutex lock; /* locks this structure */ struct usb_device *udev; /* save off the usb device pointer */ unsigned char minor; /* the starting minor number for this device */ @@ -220,7 +221,6 @@ struct lego_usb_tower { /* local function prototypes */ static ssize_t tower_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos); static ssize_t tower_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos); -static inline void tower_delete(struct lego_usb_tower *dev); static int tower_open(struct inode *inode, struct file *file); static int tower_release(struct inode *inode, struct file *file); static __poll_t tower_poll(struct file *file, poll_table *wait); @@ -286,8 +286,10 @@ static inline void lego_usb_tower_debug_data(struct device *dev, /* * tower_delete */ -static inline void tower_delete(struct lego_usb_tower *dev) +static inline void tower_delete(struct kref *kref) { + struct lego_usb_tower *dev = container_of(kref, struct lego_usb_tower, kref); + /* free data structures */ usb_free_urb(dev->interrupt_in_urb); usb_free_urb(dev->interrupt_out_urb); @@ -381,6 +383,8 @@ static int tower_open(struct inode *inode, struct file *file) dev->open_count = 1; + kref_get(&dev->kref); + unlock_exit: mutex_unlock(&dev->lock); @@ -404,14 +408,8 @@ static int tower_release(struct inode *inode, struct file *file) mutex_lock(&dev->lock); - if (dev->disconnected) { - /* the device was unplugged before the file was released */ - - /* unlock here as tower_delete frees dev */ - mutex_unlock(&dev->lock); - tower_delete(dev); - goto exit; - } + if (dev->disconnected) + goto out_unlock; /* wait until write transfer is finished */ if (dev->interrupt_out_busy) { @@ -425,7 +423,9 @@ static int tower_release(struct inode *inode, struct file *file) dev->open_count = 0; +out_unlock: mutex_unlock(&dev->lock); + kref_put(&dev->kref, tower_delete); exit: return retval; } @@ -752,6 +752,7 @@ static int tower_probe(struct usb_interface *interface, const struct usb_device_ if (!dev) goto exit; + kref_init(&dev->kref); mutex_init(&dev->lock); dev->udev = usb_get_dev(udev); spin_lock_init(&dev->read_buffer_lock); @@ -828,7 +829,7 @@ static int tower_probe(struct usb_interface *interface, const struct usb_device_ return retval; error: - tower_delete(dev); + kref_put(&dev->kref, tower_delete); return retval; } @@ -856,18 +857,18 @@ static void tower_disconnect(struct usb_interface *interface) mutex_lock(&dev->lock); - /* if the device is not opened, then we clean up right now */ - if (!dev->open_count) { - mutex_unlock(&dev->lock); - tower_delete(dev); - } else { - dev->disconnected = 1; + dev->disconnected = 1; + + if (dev->open_count) { /* wake up pollers */ wake_up_interruptible_all(&dev->read_wait); wake_up_interruptible_all(&dev->write_wait); - mutex_unlock(&dev->lock); } + mutex_unlock(&dev->lock); + + kref_put(&dev->kref, tower_delete); + dev_info(&interface->dev, "LEGO USB Tower #%d now disconnected\n", (minor - LEGO_USB_TOWER_MINOR_BASE)); } From e4271a74bf9909eaf7a814e810ad361d36866119 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Wed, 1 Jul 2026 21:16:25 +0900 Subject: [PATCH 0462/2758] usb: sl811-hcd: disable controller wakeup on remove commit 4e8ba83ac4d311992e6a4c21de5dd705010df06e upstream. sl811h_probe() enables the HCD controller device as a wakeup source after usb_add_hcd() succeeds, but sl811h_remove() removes the HCD and releases the driver resources without disabling that wakeup source. Disable controller wakeup after usb_remove_hcd() and before usb_put_hcd() so the wakeup source object is detached while the controller device pointer is still available. This issue was identified during our ongoing static-analysis research while reviewing kernel code. Fixes: 3c9740a117d4 ("usb: hcd: move controller wakeup setting initialization to individual driver") Cc: stable Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Link: https://patch.msgid.link/20260701121625.96815-1-mhun512@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/sl811-hcd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 4ae47edd4b8b4..b044977f6f567 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -1591,6 +1591,7 @@ sl811h_remove(struct platform_device *dev) remove_debug_file(sl811); usb_remove_hcd(hcd); + device_wakeup_disable(hcd->self.controller); /* some platforms may use IORESOURCE_IO */ res = platform_get_resource(dev, IORESOURCE_MEM, 1); From 88bf7b68ac909a59a4705123b879baed3a76765e Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Tue, 2 Jun 2026 13:38:42 +0800 Subject: [PATCH 0463/2758] USB: storage: include US_FL_NO_SAME in quirks mask commit 2c00e09e3f9f06f8434f5ea2ee6179ce46692ee6 upstream. usb_stor_adjust_quirks() parses the usb-storage.quirks module parameter into a new flag set and then applies it with the quirk mask to override built-in flags. The mask is meant to cover the flags that can be overridden by the module parameter. The 'k' quirk character sets US_FL_NO_SAME, but US_FL_NO_SAME is not included in the mask. As a result, the module parameter can set US_FL_NO_SAME, but it cannot clear a built-in US_FL_NO_SAME flag by providing an override entry that omits 'k'. Add US_FL_NO_SAME to the mask so that the module parameter can override it in the same way as the other supported flags. Fixes: 8010622c86ca ("USB: UAS: introduce a quirk to set no_write_same") Cc: stable Signed-off-by: Xu Rao Reviewed-by: Alan Stern Link: https://patch.msgid.link/3BCE5880F9A45C2E+20260602053842.2920137-1-raoxu@uniontech.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index fa83fe0defe2d..064c7fc8e368a 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -570,7 +570,7 @@ void usb_stor_adjust_quirks(struct usb_device *udev, u64 *fflags) US_FL_INITIAL_READ10 | US_FL_WRITE_CACHE | US_FL_NO_ATA_1X | US_FL_NO_REPORT_OPCODES | US_FL_MAX_SECTORS_240 | US_FL_NO_REPORT_LUNS | - US_FL_ALWAYS_SYNC); + US_FL_ALWAYS_SYNC | US_FL_NO_SAME); p = quirks; while (*p) { From fc1b546973c1442d5b947fcdd03581f20ecc5bd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?HE=20WEI=20=28=E3=82=AE=E3=82=AB=E3=82=AF=29?= Date: Wed, 24 Jun 2026 18:09:52 +0900 Subject: [PATCH 0464/2758] usb: misc: usbio: bound bulk IN response length to the received transfer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 8c6314489550fa81d41723a0ff33f655b5b6c7b6 upstream. usbio_bulk_msg() copies bpkt_len = le16_to_cpu(bpkt->len) bytes out of the bulk IN buffer (usbio->rxbuf, allocated with size usbio->rxbuf_len) into the caller's buffer. bpkt_len is fully controlled by the device and is only checked against ibuf_len; ibuf_len in turn is checked against usbio->txbuf_len, not against rxbuf_len: if ((obuf_len > (usbio->txbuf_len - sizeof(*bpkt))) || (ibuf_len > (usbio->txbuf_len - sizeof(*bpkt)))) return -EMSGSIZE; txbuf_len and rxbuf_len are taken independently from the bulk OUT and bulk IN endpoint wMaxPacketSize in usbio_probe(). A malicious or malfunctioning device that advertises a large bulk OUT endpoint and a small bulk IN endpoint (e.g. by claiming one of the quirk-free IDs such as the Lattice NX33U, 0x2ac1:0x20cb) therefore makes ibuf_len, and hence the device-supplied bpkt_len, exceed rxbuf_len. memcpy() then reads up to txbuf_len - rxbuf_len bytes past the end of the rxbuf slab object. The over-read bytes are handed back to the i2c layer and on to user space through i2c-dev, disclosing adjacent slab memory; with KASAN this is reported as a slab-out-of-bounds read. The number of bytes actually received is already known: act equals the URB actual_length and is bounded by rxbuf_len. Reject any response that claims more payload than was received, mirroring the existing "act < sizeof(*bpkt)" check just above. The control path (usbio_ctrl_msg()) is not affected: it uses a single buffer (ctrlbuf) for both directions, so its analogous copy can never leave the allocation. Found by code review. The out-of-bounds read was confirmed under AddressSanitizer with a faithful userspace model of usbio_bulk_msg()'s receive path (an rxbuf_len-sized buffer, the same act/ibuf_len/bpkt_len checks and the memcpy). A USB raw-gadget + dummy_hcd reproducer is also available. Fixes: 121a0f839dbb ("usb: misc: Add Intel USBIO bridge driver") Cc: stable Signed-off-by: HE WEI (ギカク) Link: https://patch.msgid.link/20260624090952.86439-1-skyexpoc@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usbio.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/misc/usbio.c b/drivers/usb/misc/usbio.c index 0e5ccd2f04d81..3c2474dca8101 100644 --- a/drivers/usb/misc/usbio.c +++ b/drivers/usb/misc/usbio.c @@ -344,6 +344,10 @@ int usbio_bulk_msg(struct auxiliary_device *adev, u8 type, u8 cmd, bool last, if (ibuf_len < bpkt_len) return -ENOSPC; + /* The device must not claim more payload than it actually sent. */ + if (bpkt_len > act - sizeof(*bpkt)) + return -EPROTO; + memcpy(ibuf, bpkt->data, bpkt_len); return bpkt_len; From 48dd0b2ec9f2e97c486eb68cd0a64b25c1c3df3e Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Tue, 7 Jul 2026 00:10:49 +0900 Subject: [PATCH 0465/2758] USB: misc: uss720: unregister parport on probe failure commit b4ecbdc4f8830f5586c4a5cfc384c00f20f8f8b3 upstream. uss720_probe() registers a parport before reading the 1284 register used to detect unsupported Belkin F5U002 adapters. If get_1284_register() fails, the error path drops the driver private data and the USB device reference, but leaves the parport device registered. Leaving the port registered is more than a private allocation leak: parport_register_port() has already reserved a parport number and registered the parport bus device, while pp->private_data still points at the private data that the common error path is about to release. Undo the pre-announce registration in the get_1284_register() failure branch before jumping to the common private-data cleanup path. Clear priv->pp first, matching the disconnect path and avoiding a stale pointer in the private data. This issue was identified during our ongoing static-analysis research while reviewing kernel code. Fixes: 3295f1b866bf ("usb: misc: uss720: check for incompatible versions of the Belkin F5U002") Cc: stable Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Reviewed-by: Alex Henrie Link: https://patch.msgid.link/20260706151049.63470-1-mhun512@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/uss720.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c index b7d3c44b970e9..1ce48f5832d71 100644 --- a/drivers/usb/misc/uss720.c +++ b/drivers/usb/misc/uss720.c @@ -732,8 +732,11 @@ static int uss720_probe(struct usb_interface *intf, * here. */ ret = get_1284_register(pp, 0, ®, GFP_KERNEL); dev_dbg(&intf->dev, "reg: %7ph\n", priv->reg); - if (ret < 0) + if (ret < 0) { + priv->pp = NULL; + parport_del_port(pp); goto probe_abort; + } ret = usb_find_last_int_in_endpoint(interface, &epd); if (!ret) { From 835b0596d4c9bdef93f842d8f826978fb4956b74 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Tue, 23 Jun 2026 17:33:25 +0800 Subject: [PATCH 0466/2758] usb: mtu3: unmap request DMA on queue failure commit 0bddda5a11665c210339de76d27ebbd1a2e0b43c upstream. mtu3_gadget_queue() maps the request before checking whether the QMU GPD ring can accept another transfer. the request is returned with -EAGAIN before it is linked on the endpoint request list if mtu3_prepare_transfer() fails. Normal completion and dequeue paths unmap requests from mtu3_req_complete(), but this error path never reaches that helper, so the DMA mapping is left active. Unmap the request before returning from the failed queue path. Fixes: df2069acb005 ("usb: Add MediaTek USB3 DRD driver") Cc: stable Signed-off-by: Haoxiang Li Link: https://patch.msgid.link/20260623093325.2105323-1-haoxiang_li2024@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/mtu3/mtu3_gadget.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/mtu3/mtu3_gadget.c b/drivers/usb/mtu3/mtu3_gadget.c index da29f467943f0..f224f2ee379ab 100644 --- a/drivers/usb/mtu3/mtu3_gadget.c +++ b/drivers/usb/mtu3/mtu3_gadget.c @@ -305,6 +305,7 @@ static int mtu3_gadget_queue(struct usb_ep *ep, if (mtu3_prepare_transfer(mep)) { ret = -EAGAIN; + usb_gadget_unmap_request(&mtu->g, req, mep->is_in); goto error; } From d4b12b6b395e43a2b1d80be3745631fcaa9c047b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 29 Jun 2026 14:45:26 +0200 Subject: [PATCH 0467/2758] USB: serial: keyspan_pda: fix information leak commit 6bfc8d01ac4068eced509f8fc74d0cd205e4dcec upstream. The write() callback is supposed to return the number of characters accepted or a negative errno. Since the addition of write fifo support the keyspan_pda implementation will however return the number characters submitted to the device if the write urb is not already in use. If this number is larger than the number of characters passed to write(), the line discipline continues writing data from beyond the tty write buffer. Fix the information leak by making sure that keyspan_pda_write_start() returns zero on success as intended. Fixes: 034e38e8f687 ("USB: serial: keyspan_pda: add write-fifo support") Cc: stable@vger.kernel.org # 5.11 Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/keyspan_pda.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 5036600dd334c..377f5695cfc6a 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -518,7 +518,7 @@ static int keyspan_pda_write_start(struct usb_serial_port *port) if (count == room) schedule_work(&priv->unthrottle_work); - return count; + return 0; } static void keyspan_pda_write_bulk_callback(struct urb *urb) From 6c8ccd8db36e0de9a72e8d88a8ae43d498325cff Mon Sep 17 00:00:00 2001 From: Fabio Porcedda Date: Fri, 12 Jun 2026 13:39:16 +0200 Subject: [PATCH 0468/2758] USB: serial: option: add Telit Cinterion FE990D50 compositions commit b33ab1dd80f5c1742f49eb6ec7b337c5ffcf3d32 upstream. Add support for Telit Cinterion FE990D50 compositions: 0x990: RNDIS + tty (AT/NMEA) + tty (AT) + tty (AT) + tty (AT) + tty (diag) + ADPL + adb T: Bus=01 Lev=01 Prnt=01 Port=06 Cnt=03 Dev#= 3 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=1bc7 ProdID=0990 Rev=06.06 S: Manufacturer=Telit Cinterion S: Product=FE990 S: SerialNumber=90b6a3ed C: #Ifs=10 Cfg#= 1 Atr=e0 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 1 Cls=ef(misc ) Sub=04 Prot=01 Driver=rndis_host E: Ad=82(I) Atr=03(Int.) MxPS= 8 Ivl=32ms I: If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=rndis_host E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=60 Driver=option E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=84(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=86(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=88(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 5 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=89(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8a(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 6 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=06(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8b(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 7 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=80 Driver=(none) E: Ad=8c(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 8 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=70 Driver=(none) E: Ad=8d(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 9 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=(none) E: Ad=07(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8e(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms 0x991: rmnet + tty (AT/NMEA) + tty (AT) + tty (AT) + tty (AT) + tty (diag) + ADPL + adb T: Bus=01 Lev=01 Prnt=01 Port=06 Cnt=03 Dev#= 9 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=1bc7 ProdID=0991 Rev=06.06 S: Manufacturer=Telit Cinterion S: Product=FE990 S: SerialNumber=90b6a3ed C: #Ifs= 9 Cfg#= 1 Atr=e0 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=50 Driver=(none) E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=82(I) Atr=03(Int.) MxPS= 8 Ivl=32ms I: If#= 1 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=60 Driver=option E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=84(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=86(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=88(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=89(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8a(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=06(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8b(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 6 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=80 Driver=(none) E: Ad=8c(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 7 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=70 Driver=(none) E: Ad=8d(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 8 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=(none) E: Ad=07(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8e(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms 0x992: MBIM + tty (AT/NMEA) + tty (AT) + tty (AT) + tty (AT) + tty (diag) + ADPL + adb T: Bus=01 Lev=01 Prnt=01 Port=06 Cnt=03 Dev#= 12 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=1bc7 ProdID=0992 Rev=06.06 S: Manufacturer=Telit Cinterion S: Product=FE990 S: SerialNumber=90b6a3ed C: #Ifs=10 Cfg#= 1 Atr=e0 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 1 Cls=02(commc) Sub=0e Prot=00 Driver=cdc_mbim E: Ad=82(I) Atr=03(Int.) MxPS= 64 Ivl=32ms I: If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=60 Driver=option E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=84(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=86(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=88(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 5 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=89(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8a(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 6 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=06(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8b(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 7 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=80 Driver=(none) E: Ad=8c(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 8 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=70 Driver=(none) E: Ad=8d(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 9 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=(none) E: Ad=07(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8e(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms 0x993: ECM + tty (AT/NMEA) + tty (AT) + tty (AT) + tty (AT) + tty (diag) + ADPL + adb T: Bus=01 Lev=01 Prnt=01 Port=06 Cnt=03 Dev#= 15 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=1bc7 ProdID=0993 Rev=06.06 S: Manufacturer=Telit Cinterion S: Product=FE990 S: SerialNumber=90b6a3ed C: #Ifs=10 Cfg#= 1 Atr=e0 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 1 Cls=02(commc) Sub=06 Prot=00 Driver=cdc_ether E: Ad=82(I) Atr=03(Int.) MxPS= 16 Ivl=32ms I: If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=cdc_ether E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=60 Driver=option E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=84(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=86(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=88(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 5 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=89(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8a(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 6 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=06(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8b(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 7 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=80 Driver=(none) E: Ad=8c(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 8 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=70 Driver=(none) E: Ad=8d(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 9 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=(none) E: Ad=07(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8e(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms Cc: stable@vger.kernel.org Signed-off-by: Fabio Porcedda Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index a34e79cfd5b60..7a0f37feb9f22 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1326,6 +1326,22 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_CC864_SINGLE) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_DE910_DUAL) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UE910_V2) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0990, 0xff, 0xff, 0x30), /* Telit FE990D50 (RNDIS) */ + .driver_info = NCTRL(6) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0990, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0990, 0xff, 0xff, 0x60) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0991, 0xff, 0xff, 0x30), /* Telit FE990D50 (rmnet) */ + .driver_info = NCTRL(5) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0991, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0991, 0xff, 0xff, 0x60) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0992, 0xff, 0xff, 0x30), /* Telit FE990D50 (MBIM) */ + .driver_info = NCTRL(6) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0992, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0992, 0xff, 0xff, 0x60) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0993, 0xff, 0xff, 0x30), /* Telit FE990D50 (ECM) */ + .driver_info = NCTRL(6) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0993, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0993, 0xff, 0xff, 0x60) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1031, 0xff), /* Telit LE910C1-EUX */ .driver_info = NCTRL(0) | RSVD(3) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1033, 0xff), /* Telit LE910C1-EUX (ECM) */ From 92fa3e1a49848509ea3f7995751963fc65095998 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 23 Jun 2026 17:08:15 +0200 Subject: [PATCH 0469/2758] USB: serial: digi_acceleport: fix broken rx after throttle commit 83a3dfc018943b05b6daf3a6f891833e1aabfa1f upstream. If the port is closed while throttled, the read urb is never resubmitted and the port will not receive any further data until the device is reconnected (or the driver is rebound). Clear the throttle flags and submit the urb if needed when opening the port. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/digi_acceleport.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index c481208255ebe..b26a6ca7b706b 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -1071,6 +1071,7 @@ static int digi_open(struct tty_struct *tty, struct usb_serial_port *port) unsigned char buf[32]; struct digi_port *priv = usb_get_serial_port_data(port); struct ktermios not_termios; + int throttled; /* be sure the device is started up */ if (digi_startup_device(port->serial) != 0) @@ -1098,6 +1099,21 @@ static int digi_open(struct tty_struct *tty, struct usb_serial_port *port) not_termios.c_iflag = ~tty->termios.c_iflag; digi_set_termios(tty, port, ¬_termios); } + + spin_lock_irq(&priv->dp_port_lock); + throttled = priv->dp_throttle_restart; + priv->dp_throttled = 0; + priv->dp_throttle_restart = 0; + spin_unlock_irq(&priv->dp_port_lock); + + if (throttled) { + ret = usb_submit_urb(port->read_urb, GFP_KERNEL); + if (ret) { + dev_err(&port->dev, "failed to submit read urb: %d\n", ret); + return ret; + } + } + return 0; } From 79bc131df0e50f8f663c1fdbbe952aaf193a8d39 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 23 Jun 2026 17:11:10 +0200 Subject: [PATCH 0470/2758] USB: serial: digi_acceleport: fix hard lockup on disconnect commit 5c1ea24b53bf3bfb859f0a05573997487975da23 upstream. If submitting the OOB write urb fails persistently (e.g if the device is being disconnected) the driver would loop indefinitely with interrupts disabled. Check for urb submission errors when sending OOB commands to avoid hanging if, for example, open(), set_termios() or close() races with a physical disconnect. This is issue was flagged by Sashiko when reviewing an unrelated change to the driver. Link: https://sashiko.dev/#/patchset/20260610132232.356139-1-johan%40kernel.org?part=1 Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/digi_acceleport.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index b26a6ca7b706b..71ef5649499f4 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -394,12 +394,14 @@ static int digi_write_oob_command(struct usb_serial_port *port, len &= ~3; memcpy(oob_port->write_urb->transfer_buffer, buf, len); oob_port->write_urb->transfer_buffer_length = len; + ret = usb_submit_urb(oob_port->write_urb, GFP_ATOMIC); - if (ret == 0) { - oob_priv->dp_write_urb_in_use = 1; - count -= len; - buf += len; - } + if (ret) + break; + + oob_priv->dp_write_urb_in_use = 1; + count -= len; + buf += len; } spin_unlock_irqrestore(&oob_priv->dp_port_lock, flags); if (ret) From a3a13fdc53103b07335918e2cdeb465038a71725 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 23 Jun 2026 17:12:29 +0200 Subject: [PATCH 0471/2758] USB: serial: digi_acceleport: fix write buffer corruption commit 24ca1fea8f2753bf33e1d458ec1ae5d9b7796a65 upstream. The digi_write_inb_command() is supposed to wait for the write urb to become available or return an error, but instead it updates the transfer buffer and tries to resubmit the urb on timeout. To make things worse, for commands like break control where no timeout is used, the driver would corrupt the urb immediately due to a broken jiffies comparison (on 32-bit machines this takes five minutes of uptime to trigger due to INITIAL_JIFFIES). Fix this by adding the missing return on timeout and waiting indefinitely when no timeout has been specified as intended. This issue was (sort of) flagged by Sashiko when reviewing an unrelated change to the driver. Link: https://sashiko.dev/#/patchset/20260610132232.356139-1-johan%40kernel.org?part=11 Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/digi_acceleport.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 71ef5649499f4..c96e5b76f3137 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -431,20 +431,22 @@ static int digi_write_inb_command(struct usb_serial_port *port, int len; struct digi_port *priv = usb_get_serial_port_data(port); unsigned char *data = port->write_urb->transfer_buffer; + unsigned long expire; unsigned long flags; dev_dbg(&port->dev, "digi_write_inb_command: TOP: port=%d, count=%d\n", priv->dp_port_num, count); if (timeout) - timeout += jiffies; - else - timeout = ULONG_MAX; + expire = jiffies + timeout; spin_lock_irqsave(&priv->dp_port_lock, flags); while (count > 0 && ret == 0) { - while (priv->dp_write_urb_in_use && - time_before(jiffies, timeout)) { + while (priv->dp_write_urb_in_use) { + if (timeout && time_after(jiffies, expire)) { + ret = -ETIMEDOUT; + break; + } cond_wait_interruptible_timeout_irqrestore( &priv->write_wait, DIGI_RETRY_TIMEOUT, &priv->dp_port_lock, flags); @@ -453,6 +455,9 @@ static int digi_write_inb_command(struct usb_serial_port *port, spin_lock_irqsave(&priv->dp_port_lock, flags); } + if (ret) + break; + /* len must be a multiple of 4 and small enough to */ /* guarantee the write will send buffered data first, */ /* so commands are in order with data and not split */ From e5493c9a98ffe083acf13ac064828ae598ba3c16 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 8 Jun 2026 16:58:03 +0200 Subject: [PATCH 0472/2758] USB: ulpi: fix memory leak on registration failure commit 8af6812795869a66e9b26044f455b13deecdb69c upstream. The allocated device name is never freed on early ULPI device registration failures. Fix this by initialising the device structure earlier and releasing the initial reference whenever registration fails. Fixes: 289fcff4bcdb ("usb: add bus type for USB ULPI") Cc: stable Cc: Heikki Krogerus Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260608145803.69360-1-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/common/ulpi.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c index 9b69148128e5b..7e43429e996e7 100644 --- a/drivers/usb/common/ulpi.c +++ b/drivers/usb/common/ulpi.c @@ -281,28 +281,24 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi) ulpi->dev.parent = dev; /* needed early for ops */ ulpi->dev.bus = &ulpi_bus; ulpi->dev.type = &ulpi_dev_type; + + device_initialize(&ulpi->dev); + dev_set_name(&ulpi->dev, "%s.ulpi", dev_name(dev)); ACPI_COMPANION_SET(&ulpi->dev, ACPI_COMPANION(dev)); ret = ulpi_of_register(ulpi); - if (ret) { - kfree(ulpi); + if (ret) return ret; - } ret = ulpi_read_id(ulpi); - if (ret) { - of_node_put(ulpi->dev.of_node); - kfree(ulpi); + if (ret) return ret; - } - ret = device_register(&ulpi->dev); - if (ret) { - put_device(&ulpi->dev); + ret = device_add(&ulpi->dev); + if (ret) return ret; - } root = debugfs_create_dir(dev_name(&ulpi->dev), ulpi_root); debugfs_create_file("regs", 0444, root, ulpi, &ulpi_regs_fops); @@ -334,9 +330,10 @@ struct ulpi *ulpi_register_interface(struct device *dev, ulpi->ops = ops; ret = ulpi_register(dev, ulpi); - if (ret) + if (ret) { + put_device(&ulpi->dev); return ERR_PTR(ret); - + } return ulpi; } From e9b316d99a5c7f6e7500964b4e26fdd72b8dbd94 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Fri, 26 Jun 2026 15:06:07 +0800 Subject: [PATCH 0473/2758] USB: usb-storage: ene_ub6250: restore media-ready check commit 5fc3f333c001f1e308bbcdeecdec0d054d24338b upstream. Commit 1892bf90677a ("USB: usb-storage: Fix use of bitfields for hardware data in ene_ub6250.c") converted the media status fields from bitfields to bit masks. The original ene_transport() test called ene_init() only when neither media type was ready: !(sd_ready || ms_ready) The converted test became: !sd_ready || ms_ready This is not equivalent. Restore the original semantics by testing that both ready bits are clear before calling ene_init(). Fixes: 1892bf90677a ("USB: usb-storage: Fix use of bitfields for hardware data in ene_ub6250.c") Cc: stable Signed-off-by: Xu Rao Reviewed-by: Alan Stern Link: https://patch.msgid.link/F42641386E32404F+20260626070607.4119527-1-raoxu@uniontech.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/ene_ub6250.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c index 8770de01a384d..ed49a3bc859ce 100644 --- a/drivers/usb/storage/ene_ub6250.c +++ b/drivers/usb/storage/ene_ub6250.c @@ -2305,7 +2305,8 @@ static int ene_transport(struct scsi_cmnd *srb, struct us_data *us) /*US_DEBUG(usb_stor_show_command(us, srb)); */ scsi_set_resid(srb, 0); - if (unlikely(!(info->SD_Status & SD_Ready) || (info->MS_Status & MS_Ready))) + if (unlikely(!(info->SD_Status & SD_Ready) && + !(info->MS_Status & MS_Ready))) result = ene_init(us); if (result == USB_STOR_XFER_GOOD) { result = USB_STOR_TRANSPORT_ERROR; From ddc4619707af619dc5f4879977de558cf467a020 Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Wed, 17 Jun 2026 10:06:13 +0800 Subject: [PATCH 0474/2758] usbip: tools: support SuperSpeedPlus devices commit 195e667c8719480c320cd48ac0fbf1cb81d6ffe0 upstream. USB devices running at SuperSpeedPlus report "10000" or "20000" in their sysfs speed attribute. usbip currently maps only "5000" to USB_SPEED_SUPER, so a SuperSpeedPlus device is imported as USB_SPEED_UNKNOWN. The attach request is then rejected by vhci_hcd: vhci_hcd: Failed attach request for unsupported USB speed: UNKNOWN Map the SuperSpeedPlus sysfs speed values to USB_SPEED_SUPER_PLUS, use the SuperSpeed VHCI hub for SuperSpeedPlus devices, and recognize the gadget current_speed string used by the kernel. Fixes: b2316645ca5e ("usb: show speed "10000" in sysfs for USB 3.1 SuperSpeedPlus devices") Cc: stable Signed-off-by: Yichong Chen Reviewed-by: Shuah Khan Link: https://patch.msgid.link/00C828F338E43447+20260617020613.199086-1-chenyichong@uniontech.com Signed-off-by: Greg Kroah-Hartman --- tools/usb/usbip/libsrc/usbip_common.c | 2 ++ tools/usb/usbip/libsrc/usbip_device_driver.c | 4 ++++ tools/usb/usbip/libsrc/vhci_driver.c | 1 + 3 files changed, 7 insertions(+) diff --git a/tools/usb/usbip/libsrc/usbip_common.c b/tools/usb/usbip/libsrc/usbip_common.c index b8d7d480595a8..f4734f552d314 100644 --- a/tools/usb/usbip/libsrc/usbip_common.c +++ b/tools/usb/usbip/libsrc/usbip_common.c @@ -29,6 +29,8 @@ static const struct speed_string speed_strings[] = { { USB_SPEED_HIGH, "480", "High Speed(480Mbps)" }, { USB_SPEED_WIRELESS, "53.3-480", "Wireless"}, { USB_SPEED_SUPER, "5000", "Super Speed(5000Mbps)" }, + { USB_SPEED_SUPER_PLUS, "10000", "Super Speed Plus(10000Mbps)" }, + { USB_SPEED_SUPER_PLUS, "20000", "Super Speed Plus(20000Mbps)" }, { 0, NULL, NULL } }; diff --git a/tools/usb/usbip/libsrc/usbip_device_driver.c b/tools/usb/usbip/libsrc/usbip_device_driver.c index 1dfbb76ab26c3..c9b3619d86f31 100644 --- a/tools/usb/usbip/libsrc/usbip_device_driver.c +++ b/tools/usb/usbip/libsrc/usbip_device_driver.c @@ -57,6 +57,10 @@ static struct { .speed = USB_SPEED_SUPER, .name = "super-speed", }, + { + .speed = USB_SPEED_SUPER_PLUS, + .name = "super-speed-plus", + }, }; static diff --git a/tools/usb/usbip/libsrc/vhci_driver.c b/tools/usb/usbip/libsrc/vhci_driver.c index 8159fd98680b4..4ca3783ee5b7c 100644 --- a/tools/usb/usbip/libsrc/vhci_driver.c +++ b/tools/usb/usbip/libsrc/vhci_driver.c @@ -338,6 +338,7 @@ int usbip_vhci_get_free_port(uint32_t speed) switch (speed) { case USB_SPEED_SUPER: + case USB_SPEED_SUPER_PLUS: if (vhci_driver->idev[i].hub != HUB_SPEED_SUPER) continue; break; From 0443e4416aa1ee97748d1ed904eaf3352c60045e Mon Sep 17 00:00:00 2001 From: Sam Day Date: Fri, 26 Jun 2026 14:29:10 +1000 Subject: [PATCH 0475/2758] usbip: vudc: fix NULL deref in vep_dequeue() commit c5371e0b91b24159a3ebaa61e70b0980bcf03c0a upstream. vep_alloc_request() wasn't initializing vrequest->udc, so cancellations on the FunctionFS AIO path were arriving in vep_dequeue without a valid UDC reference. Since vrequest->udc is never actually properly used anywhere, we opt to remove it, and update vep_dequeue to obtain a reference to the udc with ep_to_vudc(), consistent with the other vep_ ops. AFAICT this bug has existed for ~10 years. Seems that nobody has really stressed the FunctionFS AIO path on usbip's vudc. I tested this fix in a QEMU aarch64 guest driving FunctionFS endpoints via AIO. Before the fix, running `usbip attach` from the host would cause the guest to oops with the following backtrace: Call trace: vep_dequeue+0x1c/0xe4 (P) usb_ep_dequeue+0x14/0x20 ffs_aio_cancel+0x24/0x34 __arm64_sys_io_cancel+0xb0/0x124 do_el0_svc+0x68/0x100 el0_svc+0x18/0x5c el0t_64_sync_handler+0x98/0xdc el0t_64_sync+0x154/0x158 Assisted-by: opencode:openai/gpt-5.5 Cc: stable Fixes: b6a0ca111867 ("usbip: vudc: Add UDC specific ops") Reviewed-by: Igor Kotrasinski Signed-off-by: Sam Day Link: https://patch.msgid.link/20260626-usbip-vudc-deque-fix-v3-1-98c2dc4d6a48@samcday.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/vudc.h | 1 - drivers/usb/usbip/vudc_dev.c | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/usb/usbip/vudc.h b/drivers/usb/usbip/vudc.h index faf61c9c6a989..5ef0e7d9b23ad 100644 --- a/drivers/usb/usbip/vudc.h +++ b/drivers/usb/usbip/vudc.h @@ -38,7 +38,6 @@ struct vep { struct vrequest { struct usb_request req; - struct vudc *udc; struct list_head req_entry; /* Request queue */ }; diff --git a/drivers/usb/usbip/vudc_dev.c b/drivers/usb/usbip/vudc_dev.c index c5f079c5a1ea5..5ef88117965d1 100644 --- a/drivers/usb/usbip/vudc_dev.c +++ b/drivers/usb/usbip/vudc_dev.c @@ -333,7 +333,6 @@ static int vep_queue(struct usb_ep *_ep, struct usb_request *_req, static int vep_dequeue(struct usb_ep *_ep, struct usb_request *_req) { struct vep *ep; - struct vrequest *req; struct vudc *udc; struct vrequest *lst; unsigned long flags; @@ -343,8 +342,7 @@ static int vep_dequeue(struct usb_ep *_ep, struct usb_request *_req) return ret; ep = to_vep(_ep); - req = to_vrequest(_req); - udc = req->udc; + udc = ep_to_vudc(ep); if (!udc->driver) return -ESHUTDOWN; From 575cb72b5ed04118cb9219b376b52c797af1b142 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Wed, 1 Jul 2026 20:40:06 +0900 Subject: [PATCH 0476/2758] usb: typec: anx7411: use devm_pm_runtime_enable() commit 0ef7cc27da8b9e315a4a5a665c68c44206f5e559 upstream. anx7411_i2c_probe() enables runtime PM before returning successfully, but anx7411_i2c_remove() tears down the Type-C partner state, workqueue, dummy I2C device, mux, switch and port without disabling runtime PM. Use devm_pm_runtime_enable() so runtime PM is disabled automatically on driver detach. Since devres action registration can fail, route that failure through the existing probe unwind path. This issue was identified during our ongoing static-analysis research while reviewing kernel code. Fixes: fe6d8a9c8e64 ("usb: typec: anx7411: Add Analogix PD ANX7411 support") Cc: stable Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260701114006.75738-1-mhun512@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/anx7411.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/typec/anx7411.c b/drivers/usb/typec/anx7411.c index 2e8ae1d2faf98..db917b05dcdeb 100644 --- a/drivers/usb/typec/anx7411.c +++ b/drivers/usb/typec/anx7411.c @@ -1537,7 +1537,9 @@ static int anx7411_i2c_probe(struct i2c_client *client) if (anx7411_typec_check_connection(plat)) dev_err(dev, "check status\n"); - pm_runtime_enable(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + goto free_wq; return 0; From 14457cb92258f657a4a8d1b14794a1d36097d6d7 Mon Sep 17 00:00:00 2001 From: Shuangpeng Bai Date: Thu, 2 Jul 2026 15:13:29 -0400 Subject: [PATCH 0477/2758] usb: typec: class: drop PD lookup reference commit 43ae2f90b70cda374c487c1639a01d0f14e5d583 upstream. usb_power_delivery_find() wraps class_find_device_by_name(). That helper returns a device reference that must be released by the caller. select_usb_power_delivery_store() only needs this reference while calling the pd_set callback. Drop it once the callback returns. Otherwise the sysfs write can pin the selected USB Power Delivery object and prevent it from being released on unregister. Fixes: a7cff92f0635 ("usb: typec: USB Power Delivery helpers for ports and partners") Cc: stable Signed-off-by: Shuangpeng Bai Link: https://patch.msgid.link/20260702191329.2648043-1-shuangpeng.kernel@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/class.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 0977581ad1b6e..0595e8cb83aa6 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -1619,6 +1619,7 @@ static ssize_t select_usb_power_delivery_store(struct device *dev, return -EINVAL; ret = port->ops->pd_set(port, pd); + put_device(&pd->dev); if (ret) return ret; From 9a95bf88c6e49dbd21c1782757eed051c933a409 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Mon, 1 Jun 2026 15:55:02 +0200 Subject: [PATCH 0478/2758] usb: typec: ps883x: Fix DP+USB3 configuration commit b229b22b0a945d52dee887c856991ad08744d08e upstream. Commit 6bebd9b77726 ("usb: typec: ps883x: Rework ps883x_set()") introduced two regressions: 1. The CONN_STATUS_0_USB_3_1_CONNECTED bit is mistakenly written to the wrong configuration register (cfg1 instead of cfg0). This breaks USB3 when using USB3+DP adapters. 2. The switch-case fallthrough block is inverted: Currently, TYPEC_DP_STATE_C (DP-only) inherits the USB3 configuration, while TYPEC_DP_STATE_D (DP+USB3) is missing the necessary DP sink flags. Fix these by writing the USB3 bit to the correct register and swapping the case statement order so both states get their correct bits assigned. Cc: stable Fixes: 6bebd9b77726 ("usb: typec: ps883x: Rework ps883x_set()") Signed-off-by: Stephan Gerhold Reviewed-by: Heikki Krogerus Tested-by: Jens Glathe Reviewed-by: Konrad Dybcio Link: https://patch.msgid.link/20260601-ps883x-usb3dp-fixes-v1-1-d19bec3a6d26@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux/ps883x.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/typec/mux/ps883x.c b/drivers/usb/typec/mux/ps883x.c index 1256252eceedc..c9bf4b4c2ba1e 100644 --- a/drivers/usb/typec/mux/ps883x.c +++ b/drivers/usb/typec/mux/ps883x.c @@ -118,12 +118,12 @@ static int ps883x_set(struct ps883x_retimer *retimer, struct typec_retimer_state CONN_STATUS_1_DP_HPD_LEVEL; switch (state->mode) { + case TYPEC_DP_STATE_D: + cfg0 |= CONN_STATUS_0_USB_3_1_CONNECTED; + fallthrough; case TYPEC_DP_STATE_C: cfg1 |= CONN_STATUS_1_DP_SINK_REQUESTED | CONN_STATUS_1_DP_PIN_ASSIGNMENT_C_D; - fallthrough; - case TYPEC_DP_STATE_D: - cfg1 |= CONN_STATUS_0_USB_3_1_CONNECTED; break; default: /* MODE_E */ break; From 20f38be1d2620f4638ebf5df65c3d373ceefd6d8 Mon Sep 17 00:00:00 2001 From: Andy Yan Date: Thu, 4 Jun 2026 18:50:24 +0800 Subject: [PATCH 0479/2758] usb: typec: tcpm: Fix VDM type for Enter Mode commands commit 9cff680e47632b7723cb19f9c5e63669063c3417 upstream. VDO() second parameter is VDM type (bit 15): 1 for SVDM, 0 for UVDM. Using 'vdo ? 2 : 1' corrupts SVID low bit when vdo is non-NULL (2 << 15 = BIT(16)). Enter Mode is always SVDM, hardcode to 1. Fixes: 8face9aa57c8 ("usb: typec: Add parameter for the VDO to typec_altmode_enter()") Cc: stable Signed-off-by: Andy Yan Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260604105059.18750-1-andyshrk@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 7ef746a90a177..66e8bf45e998d 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -3088,7 +3088,7 @@ static int tcpm_altmode_enter(struct typec_altmode *altmode, u32 *vdo) if (svdm_version < 0) return svdm_version; - header = VDO(altmode->svid, vdo ? 2 : 1, svdm_version, CMD_ENTER_MODE); + header = VDO(altmode->svid, 1, svdm_version, CMD_ENTER_MODE); header |= VDO_OPOS(altmode->mode); return tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0, TCPC_TX_SOP); @@ -3136,7 +3136,7 @@ static int tcpm_cable_altmode_enter(struct typec_altmode *altmode, enum typec_pl if (svdm_version < 0) return svdm_version; - header = VDO(altmode->svid, vdo ? 2 : 1, svdm_version, CMD_ENTER_MODE); + header = VDO(altmode->svid, 1, svdm_version, CMD_ENTER_MODE); header |= VDO_OPOS(altmode->mode); return tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0, TCPC_TX_SOP_PRIME); From 313ca06e7e224ca1dfadd5722fe71fb8bc276b8b Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 22 Jun 2026 22:08:03 +0000 Subject: [PATCH 0480/2758] usb: typec: tcpm: Validate SVID index in svdm_consume_modes() commit 7b681dd5fbf60b24a13c14661e5b7735759fb491 upstream. In svdm_consume_modes(), the SVID value is read from pmdata->svids using pmdata->svid_index as an array index without bounds validation: paltmode->svid = pmdata->svids[pmdata->svid_index]; If pmdata->svid_index is driven beyond SVID_DISCOVERY_MAX (16), it results in an out-of-bounds read of the pmdata->svids array. Because pd_mode_data is embedded inside struct tcpm_port, indexing past svids reads into adjacent fields. In particular: - At index 16, it reads the altmodes count. - At index 18 and beyond, it reads into altmode_desc[], which contains partner-supplied SVDM Discovery Modes VDOs. By injecting a chosen SVID into altmode_desc[0].vdo and driving svid_index to 20, the partner can force paltmode->svid to be loaded with an arbitrary, partner- chosen SVID, which is then registered via typec_partner_register_altmode(). Fix this by validating that pmdata->svid_index is non-negative and strictly less than pmdata->nsvids before accessing the pmdata->svids array inside svdm_consume_modes(). Assisted-by: Antigravity:gemini-3.5-flash Fixes: 4ab8c18d4d67 ("usb: typec: Register a device for every mode") Cc: stable Signed-off-by: Badhri Jagan Sridharan Reviewed-by: RD Babiera Acked-by: Heikki Krogerus Link: https://patch.msgid.link/20260622220803.305750-1-badhri@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 66e8bf45e998d..89eec20a2064c 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -2000,6 +2000,11 @@ static void svdm_consume_modes(struct tcpm_port *port, const u32 *p, int cnt, return; } + if (pmdata->svid_index < 0 || pmdata->svid_index >= pmdata->nsvids) { + tcpm_log(port, "Invalid SVID index %d", pmdata->svid_index); + return; + } + for (i = 1; i < cnt; i++) { if (pmdata->altmodes >= ALTMODE_DISCOVERY_MAX) { /* Already logged in svdm_consume_svids() */ From 42ac1cc7de06d127101f7989e23bb92a03aff732 Mon Sep 17 00:00:00 2001 From: Andrei Kuchynski Date: Mon, 1 Jun 2026 14:28:37 +0000 Subject: [PATCH 0481/2758] usb: typec: ucsi: Invert DisplayPort role assignment commit d092d7edf8faefa3e27b9fc7f0e7904b06c833a2 upstream. The existing implementation assigned these flags backwards, configuring the partner's DisplayPort role to match the port's role instead of complementing it. This prevents proper configuration during DP altmode activation, often causing `pin_assignment` to remain 0 in `dp_altmode_configure()` and resulting in VDM negotiation failures: [ 583.328246] typec port1.1: VDM 0xff01a150 failed Additionally, the fix ensures that the `pin_assignment` sysfs attribute displays the correct values. Cc: stable Fixes: af8622f6a585 ("usb: typec: ucsi: Support for DisplayPort alt mode") Signed-off-by: Andrei Kuchynski Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260601142837.3240207-1-akuchynski@chromium.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/displayport.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c index 67a0991a7b769..c44da2fae81fb 100644 --- a/drivers/usb/typec/ucsi/displayport.c +++ b/drivers/usb/typec/ucsi/displayport.c @@ -166,12 +166,12 @@ static int ucsi_displayport_status_update(struct ucsi_dp *dp) * that Multi-function is preferred. */ if (DP_CAP_CAPABILITY(cap) & DP_CAP_UFP_D) { - dp->data.status |= DP_STATUS_CON_UFP_D; + dp->data.status |= DP_STATUS_CON_DFP_D; if (DP_CAP_UFP_D_PIN_ASSIGN(cap) & BIT(DP_PIN_ASSIGN_D)) dp->data.status |= DP_STATUS_PREFER_MULTI_FUNC; } else { - dp->data.status |= DP_STATUS_CON_DFP_D; + dp->data.status |= DP_STATUS_CON_UFP_D; if (DP_CAP_DFP_D_PIN_ASSIGN(cap) & BIT(DP_PIN_ASSIGN_D)) dp->data.status |= DP_STATUS_PREFER_MULTI_FUNC; From f1736bb63f7492ef924ce0e2c2512f01f6300516 Mon Sep 17 00:00:00 2001 From: Madhu M Date: Fri, 19 Jun 2026 21:03:11 +0530 Subject: [PATCH 0482/2758] usb: typec: ucsi: Pass full DP config payload in SET_NEW_CAM for DP alt mode commit 735a461d060d4eeb2f9732aba1295bc32a3982e2 upstream. In the UCSI Specification Revision 3.1 RC1, bits 32-63 of the SET_NEW_CAM command hold the 32-bit Alternate Mode Specific (AMSpecific) field. For DisplayPort Alternate Mode, this field must contain the full 32-bit DisplayPort configuration VDO payload that the OPM wants the connector to operate in, rather than just the pin assignment value. This AMSpecific value follows the DisplayPort Configurations defined in the DisplayPort Alt Mode on USB Type-C Specification v2.1a, Table 5-13: SOP DisplayPort Configurations. Fixes: af8622f6a585 ("usb: typec: ucsi: Support for DisplayPort alt mode") Cc: stable Signed-off-by: Madhu M Reviewed-by: Jameson Thies Reviewed-by: Andrei Kuchynski Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260619153311.3526083-1-madhu.m@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/displayport.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c index c44da2fae81fb..7067f2561b846 100644 --- a/drivers/usb/typec/ucsi/displayport.c +++ b/drivers/usb/typec/ucsi/displayport.c @@ -185,13 +185,12 @@ static int ucsi_displayport_status_update(struct ucsi_dp *dp) static int ucsi_displayport_configure(struct ucsi_dp *dp) { - u32 pins = DP_CONF_GET_PIN_ASSIGN(dp->data.conf); u64 command; if (!dp->override) return 0; - command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 1, dp->offset, pins); + command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 1, dp->offset, dp->data.conf); return ucsi_send_command(dp->con->ucsi, command, NULL, 0); } From dbb500bad02146b388041877574829016591ddc8 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Tue, 16 Jun 2026 13:20:11 +0000 Subject: [PATCH 0483/2758] usb: typec: ucsi: ccg: Fix use-after-free of ucsi on remove commit 1f0bdc2884b67de337215079bba166df0cdf4ac5 upstream. The threaded IRQ handler ccg_irq_handler() calls ucsi_notify_common(), which on a connector-change event calls ucsi_connector_change() and schedules connector work. In ucsi_ccg_remove(), ucsi_destroy() frees uc->ucsi (kfree) before free_irq() is called, so a handler invocation already in flight may access the freed object after ucsi_destroy(). CPU 0 (remove) | CPU 1 (threaded IRQ) ucsi_destroy(uc->ucsi) | ccg_irq_handler() kfree(ucsi) // FREE | ucsi_notify_common(uc->ucsi) // USE Move free_irq() before ucsi_destroy() in the remove path. It is kept after ucsi_unregister(): ucsi_unregister() cancels connector work whose handler issues GET_CONNECTOR_STATUS through ucsi_send_command_common(), which waits for a completion that is signalled from the IRQ handler, so the IRQ must stay active until that work has been cancelled. The probe error path already orders free_irq() before ucsi_destroy(). This bug was found by static analysis. Fixes: e32fd989ac1c ("usb: typec: ucsi: ccg: Move to the new API") Cc: stable Signed-off-by: Fan Wu Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260616132011.103279-1-fanwu01@zju.edu.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi_ccg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index 4463c1ae96bd4..51498329a4d50 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -1519,8 +1519,8 @@ static void ucsi_ccg_remove(struct i2c_client *client) cancel_work_sync(&uc->work); pm_runtime_disable(uc->dev); ucsi_unregister(uc->ucsi); - ucsi_destroy(uc->ucsi); free_irq(uc->irq, uc); + ucsi_destroy(uc->ucsi); } static const struct of_device_id ucsi_ccg_of_match_table[] = { From 3aeed2451603cb119f2a4dc016b671ff7851469f Mon Sep 17 00:00:00 2001 From: Paul Menzel Date: Fri, 3 Jul 2026 13:07:37 +0200 Subject: [PATCH 0484/2758] usb: typec: ucsi: cancel pending work on system suspend commit 7c4a234bd31a64a8dbd0140dc812da592c5e0787 upstream. On a Dell XPS 13 9360 (BIOS 2.21.0), entering system suspend (deep/S3) races a pending UCSI connector-change worker against the ACPI EC teardown. The worker evaluates the UCSI _DSM (GET_CONNECTOR_STATUS), whose AML accesses the Embedded Controller. By that point the ACPI EC has already been stopped for suspend, so the EC address space handler rejects the access with AE_BAD_PARAMETER, aborting the AML and failing the connector query: [22314.689495] ACPI: EC: interrupt blocked [22314.711981] ACPI: PM: Preparing to enter system sleep state S3 [22314.743260] ACPI: EC: event blocked [22314.743265] ACPI: EC: EC stopped [22314.743267] ACPI: PM: Saving platform NVS memory [22314.744241] ACPI Error: AE_BAD_PARAMETER, Returned by Handler for [EmbeddedControl] (20260408/evregion-303) [22314.744432] ACPI Error: Aborting method \_SB.PCI0.LPCB.ECDV.ECW1 due to previous error (AE_BAD_PARAMETER) (20260408/psparse-543) [22314.744673] ACPI Error: Aborting method \ECWB due to previous error (AE_BAD_PARAMETER) (20260408/psparse-543) [22314.745201] ACPI Error: Aborting method \_SB.UBTC._DSM due to previous error (AE_BAD_PARAMETER) (20260408/psparse-543) [22314.745394] ACPI: \_SB_.UBTC: failed to evaluate _DSM c298836f-a47c-e411-ad36-631042b5008f rev:1 func:1 (0x1001) [22314.745414] ucsi_acpi USBC000:00: ucsi_acpi_dsm: failed to evaluate _DSM 1 [22314.745424] ucsi_acpi USBC000:00: ucsi_handle_connector_change: GET_CONNECTOR_STATUS failed (-5) ucsi_acpi implements a resume callback but no suspend callback, so nothing cancels the connector-change work before the firmware/EC is torn down. Add a `ucsi_suspend()` core helper that cancels the pending init and connector-change work, and wire it into ucsi_acpi's PM ops. The connector state is re-read on resume by `ucsi_resume()`, so cancelling the work loses nothing. Fixes: 4e3a50293c2b ("usb: typec: ucsi: acpi: Implement resume callback") Cc: stable Signed-off-by: Paul Menzel Assisted-by: Claude Opus 4.8 Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260703110738.8457-2-pmenzel@molgen.mpg.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 20 ++++++++++++++++++++ drivers/usb/typec/ucsi/ucsi.h | 1 + drivers/usb/typec/ucsi/ucsi_acpi.c | 10 +++++++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 61cb24ed820f8..66b8799571659 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1978,6 +1978,26 @@ static void ucsi_resume_work(struct work_struct *work) } } +int ucsi_suspend(struct ucsi *ucsi) +{ + int i; + + /* + * Cancel pending work so it cannot access the firmware after the ACPI + * EC is stopped for suspend; state is re-read on resume. + */ + cancel_delayed_work_sync(&ucsi->work); + + if (!ucsi->connector) + return 0; + + for (i = 0; i < ucsi->cap.num_connectors; i++) + cancel_work_sync(&ucsi->connector[i].work); + + return 0; +} +EXPORT_SYMBOL_GPL(ucsi_suspend); + int ucsi_resume(struct ucsi *ucsi) { if (ucsi->connector) diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index cff9ddc2ae215..dd64ddc42118b 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -566,6 +566,7 @@ int ucsi_send_command(struct ucsi *ucsi, u64 command, void *retval, size_t size); void ucsi_altmode_update_active(struct ucsi_connector *con); +int ucsi_suspend(struct ucsi *ucsi); int ucsi_resume(struct ucsi *ucsi); void ucsi_notify_common(struct ucsi *ucsi, u32 cci); diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c index 6b92f296e9850..94b52ccf6f426 100644 --- a/drivers/usb/typec/ucsi/ucsi_acpi.c +++ b/drivers/usb/typec/ucsi/ucsi_acpi.c @@ -245,6 +245,13 @@ static void ucsi_acpi_remove(struct platform_device *pdev) ucsi_acpi_notify); } +static int ucsi_acpi_suspend(struct device *dev) +{ + struct ucsi_acpi *ua = dev_get_drvdata(dev); + + return ucsi_suspend(ua->ucsi); +} + static int ucsi_acpi_resume(struct device *dev) { struct ucsi_acpi *ua = dev_get_drvdata(dev); @@ -252,7 +259,8 @@ static int ucsi_acpi_resume(struct device *dev) return ucsi_resume(ua->ucsi); } -static DEFINE_SIMPLE_DEV_PM_OPS(ucsi_acpi_pm_ops, NULL, ucsi_acpi_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(ucsi_acpi_pm_ops, ucsi_acpi_suspend, + ucsi_acpi_resume); static const struct acpi_device_id ucsi_acpi_match[] = { { "PNP0CA0", 0 }, From ba1867999dbc4085e6d8c52ac5266005b8b2bf07 Mon Sep 17 00:00:00 2001 From: Tyler Baker Date: Tue, 9 Jun 2026 15:36:34 -0400 Subject: [PATCH 0485/2758] usb: gadget: f_fs: initialize reset_work at allocation time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 3137b243c93982fe3460335e12f9247739766e10 upstream. ffs_fs_kill_sb() unconditionally calls cancel_work_sync() on ffs->reset_work when a functionfs instance is unmounted: ffs_data_reset(ffs); cancel_work_sync(&ffs->reset_work); However ffs->reset_work is only ever initialized via INIT_WORK() in ffs_func_set_alt() and ffs_func_disable(), and only on the FFS_DEACTIVATED path. That state is reached solely by ffs_data_closed() when the instance is mounted with the "no_disconnect" option, so for the common case (no "no_disconnect", or mounted and unmounted without ever being deactivated) reset_work is never initialized. ffs_data_new() allocates the ffs_data with kzalloc_obj() and does not initialize reset_work, and ffs_data_reset()/ffs_data_clear() do not touch it either, so reset_work.func is left NULL. cancel_work_sync() on such a work then trips the WARN_ON(!work->func) guard in __flush_work(): WARNING: kernel/workqueue.c:4301 at __flush_work+0x330/0x360, CPU#3: umount Call trace: __flush_work cancel_work_sync ffs_fs_kill_sb [usb_f_fs] deactivate_locked_super deactivate_super cleanup_mnt __cleanup_mnt task_work_run exit_to_user_mode_loop el0_svc On older kernels cancel_work_sync() on a zero-initialized work struct was a silent no-op, which hid the missing initialization. Initialize reset_work once in ffs_data_new() so it is always valid for the lifetime of the ffs_data, and drop the now-redundant INIT_WORK() calls from the two deactivation paths. Fixes: 18d6b32fca38 ("usb: gadget: f_fs: add "no_disconnect" mode") Cc: stable Signed-off-by: Tyler Baker Cc: Loic Poulain Cc: Dmitry Baryshkov Cc: Srinivas Kandagatla Tested-by: Loic Poulain Reviewed-by: Peter Chen Acked-by: Michał Nazarewicz Link: https://patch.msgid.link/20260609193635.2284430-1-tyler.baker@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_fs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 75912ce6ab556..1ee21e29ef73c 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -288,6 +288,7 @@ static int ffs_acquire_dev(const char *dev_name, struct ffs_data *ffs_data); static void ffs_release_dev(struct ffs_dev *ffs_dev); static int ffs_ready(struct ffs_data *ffs); static void ffs_closed(struct ffs_data *ffs); +static void ffs_reset_work(struct work_struct *work); /* Misc helper functions ****************************************************/ @@ -2221,6 +2222,7 @@ static struct ffs_data *ffs_data_new(const char *dev_name) init_waitqueue_head(&ffs->ev.waitq); init_waitqueue_head(&ffs->wait); init_completion(&ffs->ep0req_completion); + INIT_WORK(&ffs->reset_work, ffs_reset_work); /* XXX REVISIT need to update it in some places, or do we? */ ffs->ev.can_stall = 1; @@ -3775,7 +3777,6 @@ static int ffs_func_set_alt(struct usb_function *f, if (ffs->state == FFS_DEACTIVATED) { ffs->state = FFS_CLOSING; spin_unlock_irqrestore(&ffs->eps_lock, flags); - INIT_WORK(&ffs->reset_work, ffs_reset_work); schedule_work(&ffs->reset_work); return -ENODEV; } @@ -3806,7 +3807,6 @@ static void ffs_func_disable(struct usb_function *f) if (ffs->state == FFS_DEACTIVATED) { ffs->state = FFS_CLOSING; spin_unlock_irqrestore(&ffs->eps_lock, flags); - INIT_WORK(&ffs->reset_work, ffs_reset_work); schedule_work(&ffs->reset_work); return; } From 0cae3d6109427c455bad0a18dfb3e2a91657e38a Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Tue, 9 Jun 2026 17:29:05 +0200 Subject: [PATCH 0486/2758] usb: gadget: f_fs: Fix DMA fence leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit baa6b6068a3f2bf2ed525a1cb37975905dadc658 upstream. In ffs_dmabuf_transfer(), a ffs_dma_fence object is kmalloc'd, with the underlying dma_fence later initialized by dma_fence_init(), which sets its kref counter to 1. Then, dma_resv_add_fence() gets a second reference, and a pointer to the ffs_dma_fence is passed as the usb_request's "context" field. The dma-resv mechanism will manage the second reference, but the first reference is never properly released; the ffs_dmabuf_cleanup() function decreases the reference count, but only to balance with the reference grab in ffs_dmabuf_signal_done(). The code will then slowly leak memory as more ffs_dma_fence objects are created without being ever freed. Address this issue by transferring ownership of the fence to the DMA reservation object, by calling dma_fence_put() right after dma_resv_add_fence(). The ffs_dma_fence then gets properly discarded after being signalled. Fixes: 7b07a2a7ca02 ("usb: gadget: functionfs: Add DMABUF import interface") Cc: stable Signed-off-by: Paul Cercueil Tested-by: Nuno Sá Reviewed-by: Nuno Sá Link: https://patch.msgid.link/20260609152905.729328-1-paul@crapouillou.net Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_fs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 1ee21e29ef73c..745c44d251f7f 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -1705,6 +1705,7 @@ static int ffs_dmabuf_transfer(struct file *file, resv_dir = epfile->in ? DMA_RESV_USAGE_READ : DMA_RESV_USAGE_WRITE; dma_resv_add_fence(dmabuf->resv, &fence->base, resv_dir); + dma_fence_put(&fence->base); dma_resv_unlock(dmabuf->resv); /* Now that the dma_fence is in place, queue the transfer. */ From f99f32ea9aa976afcbec20647ed33b50a52002c1 Mon Sep 17 00:00:00 2001 From: Neill Kapron Date: Fri, 19 Jun 2026 04:06:03 +0000 Subject: [PATCH 0487/2758] usb: gadget: f_fs: Initialize epfile->in early to fix endpoint direction checks commit 82cfd4739011bdc7e87b5d585703427e89ddfaa5 upstream. When parsing endpoint descriptors, ffs_data_got_descs() generates the eps_addrmap which contains the endpoint direction. However, epfile->in was previously only populated in ffs_func_eps_enable() which executes upon USB host connection. As a result, early userspace ioctls like FUNCTIONFS_DMABUF_ATTACH that run before the host connects would see epfile->in as 0, leading to incorrect DMA directions. By moving the initialization to ffs_epfiles_create(), epfile->in is accurate before userspace opens the endpoint files. Fixes: 7b07a2a7ca02 ("usb: gadget: functionfs: Add DMABUF import interface") Cc: stable Assisted-by: Antigravity:gemini-3.1-pro Signed-off-by: Neill Kapron Link: https://patch.msgid.link/20260619040609.4010746-2-nkapron@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 745c44d251f7f..d9b95fb830dfe 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -2367,6 +2367,7 @@ static int ffs_epfiles_create(struct ffs_data *ffs) sprintf(epfile->name, "ep%02x", ffs->eps_addrmap[i]); else sprintf(epfile->name, "ep%u", i); + epfile->in = (ffs->eps_addrmap[i] & USB_ENDPOINT_DIR_MASK) ? 1 : 0; err = ffs_sb_create_file(ffs->sb, epfile->name, epfile, &ffs_epfile_operations); if (err) { @@ -2456,7 +2457,6 @@ static int ffs_func_eps_enable(struct ffs_function *func) ret = usb_ep_enable(ep->ep); if (!ret) { epfile->ep = ep; - epfile->in = usb_endpoint_dir_in(ep->ep->desc); epfile->isoc = usb_endpoint_xfer_isoc(ep->ep->desc); } else { break; From dfecbb9ee08d5f3a4d706d4bc3759e086015b3d9 Mon Sep 17 00:00:00 2001 From: Neill Kapron Date: Fri, 19 Jun 2026 04:06:04 +0000 Subject: [PATCH 0488/2758] usb: gadget: f_fs: Tie read_buffer lifetime to ffs_epfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 8bdcf96eb135aebacac319667f87db034fb38406 upstream. Currently, ffs_epfile_release unconditionally frees the endpoint's read_buffer when a file descriptor is closed. If userspace explicitly opens the endpoint multiple times and closes one, the read_buffer is destroyed. This can lead to silent data loss if other file descriptors are still actively reading from the endpoint. By tying the lifetime of the read_buffer to the ffs_epfile structure itself (which is destroyed when the functionfs instance is torn down in ffs_epfiles_destroy), we eliminate the brittle dependency on open/release calls while correctly matching the conceptual lifetime of unread data on the hardware endpoint. Fixes: 9353afbbfa7b ("usb: gadget: f_fs: buffer data from ‘oversized’ OUT requests") Cc: stable Assisted-by: Antigravity:gemini-3.1-pro Signed-off-by: Neill Kapron Link: https://patch.msgid.link/20260619040609.4010746-3-nkapron@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index d9b95fb830dfe..44218be1e6768 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -1375,7 +1375,6 @@ ffs_epfile_release(struct inode *inode, struct file *file) mutex_unlock(&epfile->dmabufs_mutex); - __ffs_epfile_read_buffer_free(epfile); ffs_data_closed(epfile->ffs); return 0; @@ -2393,6 +2392,7 @@ static void ffs_epfiles_destroy(struct super_block *sb, for (; count; --count, ++epfile) { BUG_ON(mutex_is_locked(&epfile->mutex)); + __ffs_epfile_read_buffer_free(epfile); simple_remove_by_name(root, epfile->name, clear_one); } From aa4c4a9315764b2b7a7182e72cc5ea87520436b4 Mon Sep 17 00:00:00 2001 From: Chao Shi Date: Fri, 22 May 2026 18:00:25 -0400 Subject: [PATCH 0489/2758] block: skip sync_blockdev() on surprise removal in bdev_mark_dead() commit 49f06cff50a4ccf3b7a1a662ceb892b3b21a527a upstream. bdev_mark_dead()'s @surprise == true means the device is already gone. The filesystem callback fs_bdev_mark_dead() honours this and skips sync_filesystem(), but the bare block device path (no ->mark_dead op) lost its !surprise guard when the holder ->mark_dead callback was wired up (see Fixes), and now calls sync_blockdev() unconditionally, which can hang forever waiting on writeback that can no longer complete. syzkaller hit this via nvme_reset_work()'s "I/O queues lost" path: nvme_mark_namespaces_dead() -> blk_mark_disk_dead() -> bdev_mark_dead(bdev, true) -> sync_blockdev() blocks in folio_wait_writeback(), wedging the reset worker and every task waiting on it. Skip the sync on surprise removal, matching fs_bdev_mark_dead(); invalidate_bdev() still runs. Orderly removal (surprise == false) is unchanged. Found by FuzzNvme(Syzkaller with FEMU fuzzing framework). Fixes: d8530de5a6e8 ("block: call into the file system for bdev_mark_dead") Acked-by: Sungwoo Kim Acked-by: Dave Tian Acked-by: Weidong Zhu Signed-off-by: Chao Shi Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260522220025.1770388-1-coshi036@gmail.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/bdev.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/block/bdev.c b/block/bdev.c index 107ac9eaac7f1..85ce57bd2ae4f 100644 --- a/block/bdev.c +++ b/block/bdev.c @@ -1245,7 +1245,13 @@ void bdev_mark_dead(struct block_device *bdev, bool surprise) bdev->bd_holder_ops->mark_dead(bdev, surprise); else { mutex_unlock(&bdev->bd_holder_lock); - sync_blockdev(bdev); + /* + * On surprise removal the device is already gone; syncing is + * futile and can hang forever waiting on I/O that will never + * complete. Match fs_bdev_mark_dead(), which also skips it. + */ + if (!surprise) + sync_blockdev(bdev); } invalidate_bdev(bdev); From 77e7b127472a191e086e1e0b1b051703f33b1801 Mon Sep 17 00:00:00 2001 From: Arjan van de Ven Date: Mon, 4 May 2026 07:51:06 -0700 Subject: [PATCH 0490/2758] wifi: mt76: mt7921/mt7925: fix NULL dereference in CSA beacon [ Upstream commit 351dd7d2c80d23e56dcce6faa4e62bea5b0877c7 ] This patch is based on a BUG as reported by Bongani Hlope at https://lore.kernel.org/all/20260502125824.425d7159@bongani-mini.home.org.za/ When a channel-switch announcement (CSA) beacon is received, cfg80211 queues a wiphy work item that eventually calls mt7921_channel_switch_rx_beacon(). If the station disconnects (or the channel context is otherwise torn down) between the time the work is queued and the time it runs, the driver's dev->new_ctx pointer can already have been cleared to NULL. mt7921_channel_switch_rx_beacon() then dereferences new_ctx unconditionally, triggering a NULL pointer dereference at address 0x0: BUG: kernel NULL pointer dereference, address: 0000000000000000 RIP: 0010:mt7921_channel_switch_rx_beacon+0x1f/0x100 [mt7921_common] The same missing guard exists in mt7925_channel_switch_rx_beacon(), which shares the same code pattern introduced by the same commit. Add an early-return NULL check for dev->new_ctx in both mt7921_channel_switch_rx_beacon() and mt7925_channel_switch_rx_beacon(). When new_ctx is NULL there is no pending channel switch to process, so returning immediately is the correct and safe action. Fixes: 8aa2f59260eb ("wifi: mt76: mt7921: introduce CSA support") Reported-by: Bongani Hlope Oops-Analysis: http://oops.fenrus.org/reports/lkml/20260502125824.425d7159@bongani-mini.home.org.za/report.html Link: https://lore.kernel.org/all/20260502125824.425d7159@bongani-mini.home.org.za/ Signed-off-by: Arjan van de Ven Cc: linux-wireless@vger.kernel.org Cc: linux-mediatek@lists.infradead.org Cc: Felix Fietkau Cc: Lorenzo Bianconi Cc: Ryder Lee Link: https://patch.msgid.link/20260504145107.1329197-1-arjan@linux.intel.com Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7921/main.c | 3 +++ drivers/net/wireless/mediatek/mt76/mt7925/main.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c index 3d74fabe74085..a326f4c95c7c8 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c @@ -1508,6 +1508,9 @@ static void mt7921_channel_switch_rx_beacon(struct ieee80211_hw *hw, struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; u16 beacon_interval = vif->bss_conf.beacon_int; + if (!dev->new_ctx) + return; + if (cfg80211_chandef_identical(&chsw->chandef, &dev->new_ctx->def) && chsw->count) { diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 79cc30a3b29ef..9dc5ee51eb9f9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -2403,6 +2403,9 @@ static void mt7925_channel_switch_rx_beacon(struct ieee80211_hw *hw, if (ieee80211_vif_is_mld(vif)) return; + if (!dev->new_ctx) + return; + beacon_interval = vif->bss_conf.beacon_int; if (cfg80211_chandef_identical(&chsw->chandef, From be87de7789a82a030a4896bc7683415ec9fa6f2b Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Fri, 15 May 2026 10:23:27 -0400 Subject: [PATCH 0491/2758] udf: validate free block extents against the partition length commit 5f0419457f89dce1a3f1c8e62a3adf2f39ab8168 upstream. udf_free_blocks() checks the logical block number and count against the partition length, but drops the extent offset from that final bound. A crafted extent can pass the guard while logicalBlockNum + offset + count points past the partition, which later indexes past the space bitmap array. A single ftruncate(2) on a file backed by such an extent reliably panics the kernel. This is a local availability issue. On desktop systems where UDisks/polkit allows the active user to mount removable UDF media without CAP_SYS_ADMIN, an unprivileged local user can supply the crafted filesystem and trigger the panic by truncating a writable file on it. Systems that require root or CAP_SYS_ADMIN to mount the image have a higher prerequisite. No confidentiality or integrity impact is claimed: the reproduced primitive is an out-of-bounds read of a bitmap pointer slot followed by a kernel panic. Use the already computed logicalBlockNum + offset + count value for the partition length check. Also make load_block_bitmap() reject an out-of-range block group before indexing s_block_bitmap[], so corrupted callers cannot walk past the flexible array. Fixes: 56e69e59751d ("udf: prevent integer overflow in udf_bitmap_free_blocks()") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Link: https://patch.msgid.link/20260515142327.1120767-1-michael.bommarito@gmail.com Signed-off-by: Jan Kara Signed-off-by: Greg Kroah-Hartman --- fs/udf/balloc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/udf/balloc.c b/fs/udf/balloc.c index 807c493ed0cd5..cc6dc6e1d84de 100644 --- a/fs/udf/balloc.c +++ b/fs/udf/balloc.c @@ -82,8 +82,9 @@ static int load_block_bitmap(struct super_block *sb, int nr_groups = bitmap->s_nr_groups; if (block_group >= nr_groups) { - udf_debug("block_group (%u) > nr_groups (%d)\n", + udf_debug("block_group (%u) >= nr_groups (%d)\n", block_group, nr_groups); + return -EFSCORRUPTED; } if (bitmap->s_block_bitmap[block_group]) { @@ -662,7 +663,7 @@ void udf_free_blocks(struct super_block *sb, struct inode *inode, if (check_add_overflow(bloc->logicalBlockNum, offset, &blk) || check_add_overflow(blk, count, &blk) || - bloc->logicalBlockNum + count > map->s_partition_len) { + blk > map->s_partition_len) { udf_debug("Invalid request to free blocks: (%d, %u), off %u, " "len %u, partition len %u\n", partition, bloc->logicalBlockNum, offset, count, From 74580fdf022909e184223cacc364feb826982d96 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Fri, 12 Jun 2026 02:53:31 -0500 Subject: [PATCH 0492/2758] udf: validate VAT header length against the VAT inode size commit d8202786b3d75125c84ebc4de6d946f92fde0ee8 upstream. udf_load_vat() takes the virtual partition's start offset straight from the on-disk VAT 2.0 header without checking it against the VAT inode size: map->s_type_specific.s_virtual.s_start_offset = le16_to_cpu(vat20->lengthHeader); map->s_type_specific.s_virtual.s_num_entries = (sbi->s_vat_inode->i_size - map->s_type_specific.s_virtual.s_start_offset) >> 2; lengthHeader is a fully attacker-controlled 16-bit value. If it exceeds the VAT inode size, the s_num_entries subtraction underflows to a huge count, which defeats the "block > s_num_entries" bound in udf_get_pblock_virt15(); and on the ICB-inline path that function reads ((__le32 *)(iinfo->i_data + s_start_offset))[block] so a large s_start_offset indexes past the inode's in-ICB data. Mounting a crafted UDF image with a virtual (VAT) partition then triggers an out-of-bounds read. Reject a VAT whose header length does not leave room for at least one entry within the VAT inode. Fixes: fa5e08156335 ("udf: Handle VAT packed inside inode properly") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Link: https://patch.msgid.link/20260612-b4-disp-9a2317ee-v1-1-fefef5736154@proton.me Signed-off-by: Jan Kara Signed-off-by: Greg Kroah-Hartman --- fs/udf/super.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/udf/super.c b/fs/udf/super.c index cad2e15d633b6..f012edeb877f8 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -1263,6 +1263,14 @@ static int udf_load_vat(struct super_block *sb, int p_index, int type1_index) map->s_type_specific.s_virtual.s_start_offset = le16_to_cpu(vat20->lengthHeader); + if (map->s_type_specific.s_virtual.s_start_offset + > sbi->s_vat_inode->i_size) { + udf_err(sb, "Corrupted VAT header length %u (VAT inode size %lld)\n", + map->s_type_specific.s_virtual.s_start_offset, + sbi->s_vat_inode->i_size); + brelse(bh); + return -EFSCORRUPTED; + } map->s_type_specific.s_virtual.s_num_entries = (sbi->s_vat_inode->i_size - map->s_type_specific.s_virtual. From 7f7774b9da0ef17b87bfa238cf966ad0b3376150 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Fri, 12 Jun 2026 01:40:01 -0500 Subject: [PATCH 0493/2758] udf: validate sparing table length as an entry count, not a byte count commit 3ec997bd5508e9b25210b5bbec89031629cdb093 upstream. udf_load_sparable_map() accepts a sparing table when sizeof(*st) + le16_to_cpu(st->reallocationTableLen) > sb->s_blocksize is false, i.e. it treats reallocationTableLen as a number of BYTES that must fit in the block. But the table is walked as an array of 8-byte sparingEntry elements: for (i = 0; i < le16_to_cpu(st->reallocationTableLen); i++) { struct sparingEntry *entry = &st->mapEntry[i]; ... entry->origLocation ... } in udf_get_pblock_spar15() and udf_relocate_blocks(). A reallocationTableLen of N therefore passes the check whenever sizeof(*st) + N <= blocksize, yet the consumers index sizeof(*st) + N * sizeof(struct sparingEntry) bytes -- up to ~8x the block. On a crafted UDF image this is an out-of-bounds read in udf_get_pblock_spar15(); udf_relocate_blocks() additionally feeds the same length to udf_update_tag(), whose crc_itu_t() reads far past the block, and its memmove() through st->mapEntry[] is an out-of-bounds write. Validate reallocationTableLen as the entry count it is, with struct_size(). Fixes: 1df2ae31c724 ("udf: Fortify loading of sparing table") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Link: https://patch.msgid.link/20260612-b4-disp-91780c4e-v1-1-f15112ff6882@proton.me Signed-off-by: Jan Kara Signed-off-by: Greg Kroah-Hartman --- fs/udf/super.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/udf/super.c b/fs/udf/super.c index f012edeb877f8..1b5282790de6f 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -1426,7 +1426,8 @@ static int udf_load_sparable_map(struct super_block *sb, if (ident != 0 || strncmp(st->sparingIdent.ident, UDF_ID_SPARING, strlen(UDF_ID_SPARING)) || - sizeof(*st) + le16_to_cpu(st->reallocationTableLen) > + struct_size(st, mapEntry, + le16_to_cpu(st->reallocationTableLen)) > sb->s_blocksize) { brelse(bh); continue; From 68896ba8ccb80655f810486657164db5bfc7dd0e Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Wed, 3 Jun 2026 11:03:27 +0000 Subject: [PATCH 0494/2758] hwrng: jh7110 - fix refcount leak in starfive_trng_read() commit 8d13f7a8450206e3f820cdb26e33e91d181071b4 upstream. The starfive_trng_read() function acquires a runtime PM reference via pm_runtime_get_sync() but fails to release it on two error paths. If starfive_trng_wait_idle() or starfive_trng_cmd() returns an error, the function exits without calling pm_runtime_put_sync_autosuspend(), leaving the runtime PM usage counter permanently elevated and preventing the device from entering runtime suspend. Refactor the function to use a unified error path that calls pm_runtime_put_sync_autosuspend() before returning. Cc: stable@vger.kernel.org Fixes: c388f458bc34 ("hwrng: starfive - Add TRNG driver for StarFive SoC") Signed-off-by: Wentao Liang Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/char/hw_random/jh7110-trng.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/char/hw_random/jh7110-trng.c b/drivers/char/hw_random/jh7110-trng.c index 9776f4daa0445..4712c3c530e42 100644 --- a/drivers/char/hw_random/jh7110-trng.c +++ b/drivers/char/hw_random/jh7110-trng.c @@ -256,19 +256,22 @@ static int starfive_trng_read(struct hwrng *rng, void *buf, size_t max, bool wai if (wait) { ret = starfive_trng_wait_idle(trng); - if (ret) - return -ETIMEDOUT; + if (ret) { + ret = -ETIMEDOUT; + goto out_put; + } } ret = starfive_trng_cmd(trng, STARFIVE_CTRL_GENE_RANDNUM, wait); if (ret) - return ret; + goto out_put; memcpy_fromio(buf, trng->base + STARFIVE_RAND0, max); + ret = max; +out_put: pm_runtime_put_sync_autosuspend(trng->dev); - - return max; + return ret; } static int starfive_trng_probe(struct platform_device *pdev) From 5c925be839d8f8e456305dfdbe3603d5f137df3a Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Tue, 28 Apr 2026 12:14:32 +0200 Subject: [PATCH 0495/2758] crypto: atmel-sha204a - drop hwrng quality reduction for ATSHA204A commit ea5e57cc97185329dcc5ebdcaae7e1500bf0ad0b upstream. Commit 8006aff15516 ("crypto: atmel-sha204a - Set hwrng quality to lowest possible") reduced the hwrng quality to 1 based on a review by Bill Cox [1]. However, despite its title, the review only tested the ATSHA204, not the ATSHA204A. In the same thread, Atmel engineer Landon Cox wrote "this behavior has been eliminated entirely"[2] in the ATSHA204A and "this problem does not affect the ATECC108 or the ATECC108A (or the ATSHA204A)"[3]. According to the official ATSHA204A datasheet [4], the device contains a high-quality hardware RNG that combines its output with an internal seed value stored in EEPROM or SRAM to generate random numbers. The device also implements all security functions using SHA-256, and the driver uses the chip's Random command in seed-update mode. Keep 'quality = 1' for ATSHA204, but drop the explicit hwrng quality reduction for ATSHA204A and fall back to the hwrng core default. [1] https://www.metzdowd.com/pipermail/cryptography/2014-December/023858.html [2] https://www.metzdowd.com/pipermail/cryptography/2014-December/023852.html [3] https://www.metzdowd.com/pipermail/cryptography/2014-December/023886.html [4] https://ww1.microchip.com/downloads/en/DeviceDoc/ATSHA204A-Data-Sheet-40002025A.pdf Fixes: 8006aff15516 ("crypto: atmel-sha204a - Set hwrng quality to lowest possible") Cc: stable@vger.kernel.org Signed-off-by: Thorsten Blum Reviewed-by: Ard Biesheuvel Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/atmel-sha204a.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/crypto/atmel-sha204a.c b/drivers/crypto/atmel-sha204a.c index dbb39ed0cea11..a8c1b00b12f50 100644 --- a/drivers/crypto/atmel-sha204a.c +++ b/drivers/crypto/atmel-sha204a.c @@ -19,6 +19,12 @@ #include #include "atmel-i2c.h" +/* + * According to review by Bill Cox [1], the ATSHA204 has very low entropy. + * [1] https://www.metzdowd.com/pipermail/cryptography/2014-December/023858.html + */ +static const unsigned short atsha204_quality = 1; + static void atmel_sha204a_rng_done(struct atmel_i2c_work_data *work_data, void *areq, int status) { @@ -158,6 +164,7 @@ static const struct attribute_group atmel_sha204a_groups = { static int atmel_sha204a_probe(struct i2c_client *client) { struct atmel_i2c_client_priv *i2c_priv; + const unsigned short *quality; int ret; ret = atmel_i2c_probe(client); @@ -171,11 +178,9 @@ static int atmel_sha204a_probe(struct i2c_client *client) i2c_priv->hwrng.name = dev_name(&client->dev); i2c_priv->hwrng.read = atmel_sha204a_rng_read; - /* - * According to review by Bill Cox [1], this HWRNG has very low entropy. - * [1] https://www.metzdowd.com/pipermail/cryptography/2014-December/023858.html - */ - i2c_priv->hwrng.quality = 1; + quality = i2c_get_match_data(client); + if (quality) + i2c_priv->hwrng.quality = *quality; ret = devm_hwrng_register(&client->dev, &i2c_priv->hwrng); if (ret) @@ -203,14 +208,14 @@ static void atmel_sha204a_remove(struct i2c_client *client) } static const struct of_device_id atmel_sha204a_dt_ids[] __maybe_unused = { - { .compatible = "atmel,atsha204", }, + { .compatible = "atmel,atsha204", .data = &atsha204_quality }, { .compatible = "atmel,atsha204a", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, atmel_sha204a_dt_ids); static const struct i2c_device_id atmel_sha204a_id[] = { - { "atsha204" }, + { "atsha204", (kernel_ulong_t)&atsha204_quality }, { "atsha204a" }, { /* sentinel */ } }; From 1c046253291691617f8af55d44c095028459ab42 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sun, 17 May 2026 18:27:40 +0200 Subject: [PATCH 0496/2758] crypto: atmel-sha204a - fail on hwrng registration error in probe path commit 49e05bb00f2e8168695f7af4d694c39e1423e8a2 upstream. Commit 13909a0c8897 ("crypto: atmel-sha204a - provide the otp content") overwrote the hwrng registration return value when creating the sysfs group, which allowed atmel_sha204a_probe() to succeed even if devm_hwrng_register() failed. Return immediately when devm_hwrng_register() fails, and report both hwrng and sysfs registration errors with dev_err(). Adjust the sysfs error log message for consistency. Fixes: 13909a0c8897 ("crypto: atmel-sha204a - provide the otp content") Cc: stable@vger.kernel.org Signed-off-by: Thorsten Blum Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/atmel-sha204a.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/crypto/atmel-sha204a.c b/drivers/crypto/atmel-sha204a.c index a8c1b00b12f50..987eadae2007a 100644 --- a/drivers/crypto/atmel-sha204a.c +++ b/drivers/crypto/atmel-sha204a.c @@ -183,12 +183,14 @@ static int atmel_sha204a_probe(struct i2c_client *client) i2c_priv->hwrng.quality = *quality; ret = devm_hwrng_register(&client->dev, &i2c_priv->hwrng); - if (ret) - dev_warn(&client->dev, "failed to register RNG (%d)\n", ret); + if (ret) { + dev_err(&client->dev, "failed to register RNG (%d)\n", ret); + return ret; + } ret = sysfs_create_group(&client->dev.kobj, &atmel_sha204a_groups); if (ret) { - dev_err(&client->dev, "failed to register sysfs entry\n"); + dev_err(&client->dev, "failed to create sysfs group (%d)\n", ret); return ret; } From 5828517d17eda27f21d29ea14800c9e0a57bad11 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Wed, 27 May 2026 08:45:44 +0000 Subject: [PATCH 0497/2758] nvme: target: rdma: fix ndev refcount leak on queue connect commit badc53620fe813b3a9f727ef9526f98567c2c898 upstream. nvmet_rdma_queue_connect() calls nvmet_rdma_find_get_device() which acquires a reference on the returned ndev via kref_get(). On the path where the host queue backlog is exceeded and the function returns NVME_SC_CONNECT_CTRL_BUSY, reference of ndev is not released, leaking the kref. Fix this by adding a goto to the existing put_device label before the early return. Fixes: 31deaeb11ba7 ("nvmet-rdma: avoid circular locking dependency on install_queue()") Cc: stable@vger.kernel.org Reviewed-by: Christoph Hellwig Signed-off-by: Wentao Liang Signed-off-by: Keith Busch Signed-off-by: Greg Kroah-Hartman --- drivers/nvme/target/rdma.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/nvme/target/rdma.c b/drivers/nvme/target/rdma.c index e6e2c3f9afdf5..ac26f4f774c4d 100644 --- a/drivers/nvme/target/rdma.c +++ b/drivers/nvme/target/rdma.c @@ -1598,8 +1598,10 @@ static int nvmet_rdma_queue_connect(struct rdma_cm_id *cm_id, pending++; } mutex_unlock(&nvmet_rdma_queue_mutex); - if (pending > NVMET_RDMA_BACKLOG) - return NVME_SC_CONNECT_CTRL_BUSY; + if (pending > NVMET_RDMA_BACKLOG) { + ret = NVME_SC_CONNECT_CTRL_BUSY; + goto put_device; + } } ret = nvmet_rdma_cm_accept(cm_id, queue, &event->param.conn); From 0d3d19f785959558faa4ee6b56250d05e66b8c48 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Tue, 26 May 2026 10:21:24 +0000 Subject: [PATCH 0498/2758] block: partitions: fix of_node refcount leak in of_partition() commit 148cd4873115feb266c002d4d4618ea7f14342d9 upstream. of_partition() calls of_node_get() on the parent device node at the beginning of the function, storing the reference in 'partitions_np'. This reference is leaked in two paths: 1. The compatibility check at the top of the function returns 0 without releasing partitions_np when the node exists but is not "fixed-partitions" compatible. 2. The function returns 1 at the end after successfully processing all partitions without releasing partitions_np. Fix both leaks by adding of_node_put(partitions_np) on each path. Fixes: 2e3a191e89f9 ("block: add support for partition table defined in OF") Cc: stable@vger.kernel.org Signed-off-by: Wentao Liang Reviewed-by: Md Haris Iqbal Link: https://patch.msgid.link/20260526102124.2283846-1-vulab@iscas.ac.cn Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/partitions/of.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/block/partitions/of.c b/block/partitions/of.c index c22b606610981..53664ea06b654 100644 --- a/block/partitions/of.c +++ b/block/partitions/of.c @@ -74,8 +74,10 @@ int of_partition(struct parsed_partitions *state) struct device_node *partitions_np = of_node_get(ddev->of_node); if (!partitions_np || - !of_device_is_compatible(partitions_np, "fixed-partitions")) + !of_device_is_compatible(partitions_np, "fixed-partitions")) { + of_node_put(partitions_np); return 0; + } slot = 1; /* Validate parition offset and size */ @@ -104,5 +106,6 @@ int of_partition(struct parsed_partitions *state) seq_buf_puts(&state->pp_buf, "\n"); + of_node_put(partitions_np); return 1; } From f815869f926cb15f7cac79d47dbf9bcb97cc907e Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Mon, 11 May 2026 13:04:16 +0200 Subject: [PATCH 0499/2758] dm-ioctl: report an error if a device has no table commit 457e32348d606a77f9b20e25e989734189834c07 upstream. When we send a message to a device that has no table, the return code was not set. The code would return "2", which is not considered a valid return value. Signed-off-by: Mikulas Patocka Cc: stable@vger.kernel.org Reviewed-by: Benjamin Marzinski Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-ioctl.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index a529174c94cf3..c23c03f0cb6e4 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -1816,8 +1816,11 @@ static int target_message(struct file *filp, struct dm_ioctl *param, size_t para goto out_argv; table = dm_get_live_table(md, &srcu_idx); - if (!table) + if (!table) { + DMERR("The device has no table."); + r = -EINVAL; goto out_table; + } if (dm_deleting_md(md)) { r = -ENXIO; From 16898de2ca35c1fe44d6cf9e4d508804088a6b72 Mon Sep 17 00:00:00 2001 From: "Achkinazi, Igor" Date: Thu, 28 May 2026 15:24:27 +0000 Subject: [PATCH 0500/2758] nvme-multipath: set BIO_REMAPPED on bios remapped to per-path namespace disks commit 88bac2c1a72b8f4f71e9845699aa872df04e5850 upstream. When nvme_ns_head_submit_bio() remaps a bio from the multipath head to a per-path namespace, bio_set_dev() clears BIO_REMAPPED. The remapped bio is then resubmitted through submit_bio_noacct() which calls bio_check_eod() because BIO_REMAPPED is not set. This races with nvme_ns_remove() which zeroes the per-path capacity before synchronize_srcu(): CPU 0 (IO submission) --------------------- srcu_read_lock() nvme_find_path() -> ns [NVME_NS_READY is set] CPU 1 (namespace removal) ------------------------- clear_bit(NVME_NS_READY) set_capacity(ns->disk, 0) synchronize_srcu() <- blocks CPU 0 (IO submission) --------------------- bio_set_dev(bio, ns->disk->part0) [clears BIO_REMAPPED] submit_bio_noacct(bio) -> bio_check_eod() sees capacity=0 -> bio fails with IO error The SRCU read lock prevents synchronize_srcu() from completing, but does not prevent set_capacity(0) from executing. The bio fails the EOD check before it reaches the NVMe driver, so nvme_failover_req() never gets a chance to redirect it to another path of multipath. IO errors are reported to the application despite another path being available. On older kernels (before commit 0b64682e78f7 "block: skip unnecessary checks for split bio"), the same race was also reachable through split remainders resubmitted via submit_bio_noacct(). Fix this by setting BIO_REMAPPED after bio_set_dev() in nvme_ns_head_submit_bio(). This skips bio_check_eod() on the per-path device; the EOD check already passed on the multipath head. NVMe per-path namespace devices are always whole disks (bd_partno=0), so the blk_partition_remap() skip also gated by BIO_REMAPPED is a no-op. The flag does not persist across failover and cannot go stale if the namespace geometry changes between attempts: nvme_failover_req() calls bio_set_dev() to redirect the bio back to the multipath head, which clears BIO_REMAPPED. When nvme_requeue_work() resubmits through submit_bio_noacct(), bio_check_eod() runs normally against the current capacity. Same approach as commit 3a905c37c351 ("block: skip bio_check_eod for partition-remapped bios"). Fixes: a7c7f7b2b641 ("nvme: use bio_set_dev to assign ->bi_bdev") Cc: stable@vger.kernel.org Reviewed-by: Christoph Hellwig Signed-off-by: Igor Achkinazi Signed-off-by: Keith Busch Signed-off-by: Greg Kroah-Hartman --- drivers/nvme/host/multipath.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c index 263161cb8ac06..9d3ac937f5949 100644 --- a/drivers/nvme/host/multipath.c +++ b/drivers/nvme/host/multipath.c @@ -511,6 +511,12 @@ static void nvme_ns_head_submit_bio(struct bio *bio) ns = nvme_find_path(head); if (likely(ns)) { bio_set_dev(bio, ns->disk->part0); + /* + * Use BIO_REMAPPED to skip bio_check_eod() when this bio + * enters submit_bio_noacct() for the per-path device. The EOD + * check already passed on the multipath head. + */ + bio_set_flag(bio, BIO_REMAPPED); bio->bi_opf |= REQ_NVME_MPATH; trace_block_bio_remap(bio, disk_devt(ns->head->disk), bio->bi_iter.bi_sector); From a29b316b9bbfd269f323ab4ba9906a894025680f Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Wed, 27 May 2026 15:00:00 -0500 Subject: [PATCH 0501/2758] nvmet: fix pre-auth out-of-bounds heap read in Discovery Get Log Page commit 53cd102a7a56079b11b897835bd9b94c14e6322c upstream. nvmet_execute_disc_get_log_page() validates only the dword alignment of the host-supplied Log Page Offset (lpo). The 64-bit offset is then added to a small kzalloc'd buffer that holds the discovery log page and the result is passed straight to nvmet_copy_to_sgl(), which memcpy()s data_len bytes out to the host with no source-side bound check: u64 offset = nvmet_get_log_page_offset(req->cmd); /* 64-bit host */ size_t data_len = nvmet_get_log_page_len(req->cmd); /* 32-bit host */ ... if (offset & 0x3) { ... } /* only check */ ... alloc_len = sizeof(*hdr) + entry_size * discovery_log_entries(req); buffer = kzalloc(alloc_len, GFP_KERNEL); ... status = nvmet_copy_to_sgl(req, 0, buffer + offset, data_len); The Discovery controller is unauthenticated -- nvmet_host_allowed() returns true unconditionally for the discovery subsystem -- so the call is reachable pre-authentication by any TCP/RDMA/FC peer that can reach the nvmet target. With a discovery log page of ~1 KiB, an attacker requesting up to 4 KiB starting at offset == alloc_len reads the next slab page out and gets its content returned over the fabric (an empirical run on a default nvmet-tcp loopback target leaked 81 canonical kernel pointers in one Get Log Page response). Pointing the offset at unmapped kernel memory faults the in-kernel memcpy and crashes (or panics, on panic_on_oops=1) the target host instead. The attacker-controlled source-side offset pattern "nvmet_copy_to_sgl(req, 0, buffer + ATTACKER_OFFSET, ...)" is unique to nvmet_execute_disc_get_log_page in the entire nvmet codebase: every other Get Log Page handler in admin-cmd.c either ignores lpo (and silently starts every response at offset 0) or tracks a local destination offset with a fixed source pointer. Validate the host-supplied offset against the log page size, cap the copy length to what is actually available, and zero-fill any remainder of the host transfer buffer. The zero-fill matches the existing short-response pattern in nvmet_execute_get_log_changed_ns() (admin-cmd.c) and prevents leaking transport SGL contents when the host asks for more bytes than the log page contains. Fixes: a07b4970f464 ("nvmet: add a generic NVMe target") Cc: stable@vger.kernel.org Reviewed-by: Chaitanya Kulkarni Reviewed-by: Christoph Hellwig Signed-off-by: Bryam Vargas Signed-off-by: Keith Busch Signed-off-by: Greg Kroah-Hartman --- drivers/nvme/target/discovery.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/nvme/target/discovery.c b/drivers/nvme/target/discovery.c index e9b35549e254f..114869d16a1f3 100644 --- a/drivers/nvme/target/discovery.c +++ b/drivers/nvme/target/discovery.c @@ -166,6 +166,7 @@ static void nvmet_execute_disc_get_log_page(struct nvmet_req *req) u64 offset = nvmet_get_log_page_offset(req->cmd); size_t data_len = nvmet_get_log_page_len(req->cmd); size_t alloc_len; + size_t copy_len; struct nvmet_subsys_link *p; struct nvmet_port *r; u32 numrec = 0; @@ -242,7 +243,27 @@ static void nvmet_execute_disc_get_log_page(struct nvmet_req *req) up_read(&nvmet_config_sem); - status = nvmet_copy_to_sgl(req, 0, buffer + offset, data_len); + /* + * Validate the host-supplied log page offset before copying out. + * Without this check, the host controls a 64-bit byte offset into + * a small kzalloc'd buffer: a value past the log page lets the + * subsequent memcpy read adjacent kernel heap, and a value aimed + * at unmapped kernel memory faults the in-kernel copy and crashes + * the target host. The Discovery controller is unauthenticated, + * so the bug is reachable from any reachable fabric peer. + */ + if (offset > alloc_len) { + req->error_loc = + offsetof(struct nvme_get_log_page_command, lpo); + status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR; + goto out_free_buffer; + } + + copy_len = min_t(size_t, data_len, alloc_len - offset); + status = nvmet_copy_to_sgl(req, 0, buffer + offset, copy_len); + if (!status && copy_len < data_len) + status = nvmet_zero_sgl(req, copy_len, data_len - copy_len); +out_free_buffer: kfree(buffer); out: nvmet_req_complete(req, status); From 05645271751e128d551b4366d451de82c9a37d7c Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Thu, 4 Jun 2026 09:59:46 +0930 Subject: [PATCH 0502/2758] btrfs: fix false IO failure after falling back to buffered write commit 66ff4d366e7eb4d31813d2acabf3af512ce03aa5 upstream. [BUG] The test case generic/362 will fail with "nodatasum" mount option (*): MOUNT_OPTIONS -- -o nodatasum /dev/mapper/test-scratch1 /mnt/scratch # generic/362 0s ... - output mismatch (see /home/adam/xfstests/results//generic/362.out.bad) # --- tests/generic/362.out 2024-08-24 15:31:37.200000000 +0930 # +++ /home/adam/xfstests/results//generic/362.out.bad 2026-05-27 10:21:17.574771567 +0930 # @@ -1,2 +1,3 @@ # QA output created by 362 # +First write failed: Input/output error # Silence is golden # ... *: If the test case has been executed before with default data checksum, the failure will not reproduce. Need the following fix to make it reliably reproducible: https://lore.kernel.org/linux-btrfs/20260528111659.87113-1-wqu@suse.com/ [CAUSE] Inside __iomap_dio_rw(), the -EFAULT/-ENOTBLK error is not directly returned. Thus we never got an error pointer from __iomap_dio_rw(). The call chain looks like this: btrfs_direct_write() |- btrfs_dio_write() |- __iomap_dio_rw() | |- iomap_iter() | | |- btrfs_dio_iomap_begin() | | Now an ordered extent is allocated for the 4K write. | | | |- iomi.status = iomap_dio_iter() | | Where iomap_dio_iter() returned -EFAULT. | | | |- ret = iomap_iter() | | |- btrfs_dio_iomap_end() | | | |- btrfs_finish_ordered_extent(uptodate = false) | | | | |- can_finish_ordered_extent() | | | | |- btrfs_mark_ordered_extent_error() | | | | |- mapping_set_error() | | | | Now the address space is marked error. | | | | return -ENOTBLK | | |- return -ENOTBLK | |- if (ret == -ENOTBLK) { ret = 0; } | Now the return value is reset to 0. | Thus no error pointer will be returned. | |- ret = iomap_dio_complete() | Since no byte is submitted, @ret is 0. | |- Fallback to buffered IO | And the buffered write finished without error | |- filemap_fdatawait_range() |- filemap_check_errors() The previous error is recorded, thus an error is returned However the buffered write is properly submitted and finished, the error is from the btrfs_finish_ordered_extent() call with @uptodate = false. [FIX] When a short dio write happened, any range that is submitted will have btrfs_extract_ordered_extent() to be called, thus the submitted range will always have an OE just covering the submitted range. The remaining OE range is never submitted, thus they should be treated as truncated, not an error. So that we can properly reclaim and not insert an unnecessary file extent item, without marking the mapping as error. Extract a helper, btrfs_mark_ordered_extent_truncated(), and utilize that helper to mark the direct IO ordered extent as truncated, so it won't cause failure for the later buffered fallback. [REASON FOR NO FIXES TAG] The bug itself is pretty old, at commit f85781fb505e ("btrfs: switch to iomap for direct IO") we're already passing @uptodate=false finishing the OE. But at that time OE with IOERR won't call mapping_set_error(), so it's not exposed. Later commit d61bec08b904 ("btrfs: mark ordered extent and inode with error if we fail to finish") finally exposed the bug, but that commit is doing a correct job, not the root cause. Anyway the bug is very old, dating back to 5.1x days, thus only CC to stable. CC: stable@vger.kernel.org # 5.15+ Reviewed-by: Boris Burkov Signed-off-by: Qu Wenruo Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/direct-io.c | 17 ++++++++++++++--- fs/btrfs/inode.c | 6 +----- fs/btrfs/ordered-data.c | 12 ++++++++++++ fs/btrfs/ordered-data.h | 2 ++ 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/fs/btrfs/direct-io.c b/fs/btrfs/direct-io.c index 57167d56dc727..88cb2e82a5076 100644 --- a/fs/btrfs/direct-io.c +++ b/fs/btrfs/direct-io.c @@ -624,12 +624,23 @@ static int btrfs_dio_iomap_end(struct inode *inode, loff_t pos, loff_t length, if (submitted < length) { pos += submitted; length -= submitted; - if (write) + if (write) { + /* + * We have a short write, if there is any range + * that is submitted properly, that part will have + * its own OE split from the original one. + * + * So for the OE at dio_data->ordered, it's the part + * that is not submitted, and should be marked + * as fully truncated. + */ + btrfs_mark_ordered_extent_truncated(dio_data->ordered, 0); btrfs_finish_ordered_extent(dio_data->ordered, - pos, length, false); - else + pos, length, true); + } else { btrfs_unlock_dio_extent(&BTRFS_I(inode)->io_tree, pos, pos + length - 1, NULL); + } ret = -ENOTBLK; } if (write) { diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 1ca1cbdf25bcd..6257c4ba0be73 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -7776,11 +7776,7 @@ static void btrfs_invalidate_folio(struct folio *folio, size_t offset, EXTENT_LOCKED | EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG, &cached_state); - spin_lock(&inode->ordered_tree_lock); - set_bit(BTRFS_ORDERED_TRUNCATED, &ordered->flags); - ordered->truncated_len = min(ordered->truncated_len, - cur - ordered->file_offset); - spin_unlock(&inode->ordered_tree_lock); + btrfs_mark_ordered_extent_truncated(ordered, cur - ordered->file_offset); /* * If the ordered extent has finished, we're safe to delete all diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index e5a24b3ff95e4..896de1c3c1474 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -357,6 +357,18 @@ void btrfs_mark_ordered_extent_error(struct btrfs_ordered_extent *ordered) mapping_set_error(ordered->inode->vfs_inode.i_mapping, -EIO); } +void btrfs_mark_ordered_extent_truncated(struct btrfs_ordered_extent *ordered, + u64 truncate_len) +{ + struct btrfs_inode *inode = ordered->inode; + + ASSERT(truncate_len <= ordered->num_bytes); + spin_lock(&inode->ordered_tree_lock); + set_bit(BTRFS_ORDERED_TRUNCATED, &ordered->flags); + ordered->truncated_len = min(ordered->truncated_len, truncate_len); + spin_unlock(&inode->ordered_tree_lock); +} + static void finish_ordered_fn(struct btrfs_work *work) { struct btrfs_ordered_extent *ordered_extent; diff --git a/fs/btrfs/ordered-data.h b/fs/btrfs/ordered-data.h index 03e12380a2fda..8d5d5ba1e02f0 100644 --- a/fs/btrfs/ordered-data.h +++ b/fs/btrfs/ordered-data.h @@ -226,6 +226,8 @@ bool btrfs_try_lock_ordered_range(struct btrfs_inode *inode, u64 start, u64 end, struct btrfs_ordered_extent *btrfs_split_ordered_extent( struct btrfs_ordered_extent *ordered, u64 len); void btrfs_mark_ordered_extent_error(struct btrfs_ordered_extent *ordered); +void btrfs_mark_ordered_extent_truncated(struct btrfs_ordered_extent *ordered, + u64 truncate_len); int __init ordered_data_init(void); void __cold ordered_data_exit(void); From caa71b3a43ea5c13fe7141cb019ebcb03b8ac857 Mon Sep 17 00:00:00 2001 From: Tianchu Chen Date: Fri, 29 May 2026 14:18:39 +0000 Subject: [PATCH 0503/2758] nvmet-auth: validate reply message payload bounds against transfer length commit 3a413ece2504c70aa34a20be4dafec04e8c741f9 upstream. nvmet_auth_reply() accesses the variable-length rval[] array using attacker-controlled hl (hash length) and dhvlen (DH value length) fields without verifying they fit within the allocated buffer of tl bytes. A malicious NVMe-oF initiator can craft a DHCHAP_REPLY message with a small transfer length but large hl/dhvlen values, causing out-of-bounds heap reads when the target processes the DH public key (rval + 2*hl) or performs the host response memcmp. With DH authentication configured, the OOB pointer is passed directly to sg_init_one() and read by crypto_kpp_compute_shared_secret(), reaching up to 526 bytes past the buffer. This is exploitable pre-authentication. Add bounds validation ensuring sizeof(*data) + 2*hl + dhvlen <= tl before any access to the variable-length fields. Discovered by Atuin - Automated Vulnerability Discovery Engine. Fixes: db1312dd9548 ("nvmet: implement basic In-Band Authentication") Cc: stable@vger.kernel.org Reviewed-by: Hannes Reinecke Signed-off-by: Tianchu Chen Signed-off-by: Keith Busch Signed-off-by: Greg Kroah-Hartman --- drivers/nvme/target/fabrics-cmd-auth.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/nvme/target/fabrics-cmd-auth.c b/drivers/nvme/target/fabrics-cmd-auth.c index f1e613e7c63e5..0a85acf1e5c71 100644 --- a/drivers/nvme/target/fabrics-cmd-auth.c +++ b/drivers/nvme/target/fabrics-cmd-auth.c @@ -132,13 +132,22 @@ static u8 nvmet_auth_negotiate(struct nvmet_req *req, void *d) return 0; } -static u8 nvmet_auth_reply(struct nvmet_req *req, void *d) +static u8 nvmet_auth_reply(struct nvmet_req *req, void *d, u32 tl) { struct nvmet_ctrl *ctrl = req->sq->ctrl; struct nvmf_auth_dhchap_reply_data *data = d; - u16 dhvlen = le16_to_cpu(data->dhvlen); + u16 dhvlen; u8 *response; + if (tl < sizeof(*data)) + return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD; + + dhvlen = le16_to_cpu(data->dhvlen); + + /* Validate that hl and dhvlen fit within the transfer length */ + if (sizeof(*data) + 2 * (size_t)data->hl + dhvlen > tl) + return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD; + pr_debug("%s: ctrl %d qid %d: data hl %d cvalid %d dhvlen %u\n", __func__, ctrl->cntlid, req->sq->qid, data->hl, data->cvalid, dhvlen); @@ -338,7 +347,7 @@ void nvmet_execute_auth_send(struct nvmet_req *req) switch (data->auth_id) { case NVME_AUTH_DHCHAP_MESSAGE_REPLY: - dhchap_status = nvmet_auth_reply(req, d); + dhchap_status = nvmet_auth_reply(req, d, tl); if (dhchap_status == 0) req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1; From 5d444a2a06d03c44576831ffdd3605deb4037984 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 20 Apr 2026 18:32:49 +0930 Subject: [PATCH 0504/2758] btrfs: check and set EXTENT_DELALLOC_NEW before clearing EXTENT_DELALLOC commit 95ee2231896d5f2a31760411429075a99d6045a7 upstream. [WARNING] When running test cases with injected errors or shutdown, e.g. generic/388 or generic/475, there is a chance that the following kernel warning is triggered: BTRFS info (device dm-2): first mount of filesystem d8a19a28-3232-4809-b0df-38df83e71bff BTRFS info (device dm-2): using crc32c checksum algorithm BTRFS info (device dm-2): checking UUID tree BTRFS info (device dm-2): turning on async discard BTRFS info (device dm-2): enabling free space tree BTRFS critical (device dm-2 state E): emergency shutdown ------------[ cut here ]------------ WARNING: extent_io.c:1742 at extent_writepage_io+0x437/0x520 [btrfs], CPU#2: kworker/u43:2/651591 CPU: 2 UID: 0 PID: 651591 Comm: kworker/u43:2 Tainted: G W OE 7.0.0-rc6-custom+ #365 PREEMPT(full) 5804053f02137e627472d94b5128cc9fcb110e88 RIP: 0010:extent_writepage_io+0x437/0x520 [btrfs] Call Trace: extent_write_cache_pages+0x2a5/0x820 [btrfs 70299925d0856939e93b17d480651713b3cbba58] btrfs_writepages+0x74/0x130 [btrfs 70299925d0856939e93b17d480651713b3cbba58] do_writepages+0xd0/0x160 __writeback_single_inode+0x42/0x340 writeback_sb_inodes+0x22d/0x580 wb_writeback+0xc6/0x360 wb_workfn+0xbd/0x470 process_one_work+0x198/0x3b0 worker_thread+0x1c8/0x330 kthread+0xee/0x120 ret_from_fork+0x2a6/0x330 ret_from_fork_asm+0x11/0x20 ---[ end trace 0000000000000000 ]--- BTRFS error (device dm-2 state E): root 5 ino 259 folio 1323008 is marked dirty without notifying the fs BTRFS error (device dm-2 state E): failed to submit blocks, root=5 inode=259 folio=1323008 submit_bitmap=0: -117 BTRFS info (device dm-2 state E): last unmount of filesystem d8a19a28-3232-4809-b0df-38df83e71bff [CAUSE] Inside btrfs we have the following pattern in several locations, for example inside btrfs_dirty_folio(): btrfs_clear_extent_bit(&inode->io_tree, start_pos, end_of_last_block, EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG, cached); ret = btrfs_set_extent_delalloc(inode, start_pos, end_of_last_block, extra_bits, cached); if (ret) return ret; However btrfs_set_extent_delalloc() can return IO errors other than -ENOMEM through the following callchain: btrfs_set_extent_delalloc() \- btrfs_find_new_delalloc_bytes() \- btrfs_get_extent() \- btrfs_lookup_file_extent() \- btrfs_search_slot() When such IO error happened, the previous btrfs_clear_extent_bit() has cleared the EXTENT_DELALLOC for the range, and we're expecting btrfs_set_extent_delalloc() to re-set EXTENT_DELALLOC. But since btrfs_set_extent_delalloc() failed before btrfs_set_extent_bit(), EXTENT_DELALLOC flag is no longer present. And if the folio range is dirty before entering btrfs_set_extent_delalloc(), we got a dirty folio but no EXTENT_DELALLOC flag now. Then we hit the folio writeback: extent_writepage() |- writepage_delalloc() | No ordered extent is created, as there is no EXTENT_DELALLOC set | for the folio range. | This also means the folio has no ordered flag set. | |- extent_writepage_io() \- if (unlikely(!folio_test_ordered(folio)) Now we hit the warning. [FIX] Introduce a new helper, btrfs_reset_extent_delalloc() to replace the currently open-coded btrfs_clear_extent_bit() + btrfs_set_extent_delalloc() combination. Instead of calling btrfs_clear_extent_bit() first, update EXTENT_DELALLOC_NEW first, as that part can fail due to metadata IO, meanwhile btrfs_clear_extent_bit() and btrfs_set_extent_bit() won't return any error but retry memory allocation until succeeded. This allows us to fail early without clearing EXTENT_DELALLOC bit, so even if that new btrfs_reset_extent_delalloc() failed before touching EXTENT_DELALLOC, the existing dirty range will still have their old EXTENT_DELALLOC flag present, thus avoid the warning. CC: stable@vger.kernel.org # 6.1+ Reviewed-by: Filipe Manana Signed-off-by: Qu Wenruo Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/btrfs_inode.h | 2 ++ fs/btrfs/file.c | 25 +++-------------- fs/btrfs/inode.c | 61 +++++++++++++++++++++++++++++++++++++----- fs/btrfs/reflink.c | 4 +-- 4 files changed, 60 insertions(+), 32 deletions(-) diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index 55c272fe5d92a..5d8fda94fe221 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -569,6 +569,8 @@ int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, long nr, int btrfs_set_extent_delalloc(struct btrfs_inode *inode, u64 start, u64 end, unsigned int extra_bits, struct extent_state **cached_state); +int btrfs_reset_extent_delalloc(struct btrfs_inode *inode, u64 start, u64 end, + unsigned int extra_bits, struct extent_state **cached_state); struct btrfs_new_inode_args { /* Input */ diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 8c171ed07008b..8636ebe1fcabd 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -93,16 +93,8 @@ int btrfs_dirty_folio(struct btrfs_inode *inode, struct folio *folio, loff_t pos end_of_last_block = start_pos + num_bytes - 1; - /* - * The pages may have already been dirty, clear out old accounting so - * we can set things up properly - */ - btrfs_clear_extent_bit(&inode->io_tree, start_pos, end_of_last_block, - EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG, - cached); - - ret = btrfs_set_extent_delalloc(inode, start_pos, end_of_last_block, - extra_bits, cached); + ret = btrfs_reset_extent_delalloc(inode, start_pos, end_of_last_block, + extra_bits, cached); if (ret) return ret; @@ -1966,18 +1958,7 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf) } } - /* - * page_mkwrite gets called when the page is firstly dirtied after it's - * faulted in, but write(2) could also dirty a page and set delalloc - * bits, thus in this case for space account reason, we still need to - * clear any delalloc bits within this page range since we have to - * reserve data&meta space before lock_page() (see above comments). - */ - btrfs_clear_extent_bit(io_tree, page_start, end, - EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING | - EXTENT_DEFRAG, &cached_state); - - ret = btrfs_set_extent_delalloc(inode, page_start, end, 0, &cached_state); + ret = btrfs_reset_extent_delalloc(inode, page_start, end, 0, &cached_state); if (ret < 0) { btrfs_unlock_extent(io_tree, page_start, page_end, &cached_state); goto out_unlock; diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 6257c4ba0be73..f1f7ac8684735 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -2810,7 +2810,13 @@ int btrfs_set_extent_delalloc(struct btrfs_inode *inode, u64 start, u64 end, unsigned int extra_bits, struct extent_state **cached_state) { - WARN_ON(PAGE_ALIGNED(end)); + const u32 blocksize = inode->root->fs_info->sectorsize; + + /* Basic alignment check. */ + ASSERT(IS_ALIGNED(start, blocksize), "start=%llu blocksize=%u", + start, blocksize); + ASSERT(IS_ALIGNED(end + 1, blocksize), "inclusive end=%llu blocksize=%u", + end, blocksize); if (start >= i_size_read(&inode->vfs_inode) && !(inode->flags & BTRFS_INODE_PREALLOC)) { @@ -3035,6 +3041,52 @@ int btrfs_writepage_cow_fixup(struct folio *folio) return -EAGAIN; } +/* + * Clear the old accounting flags and set EXTENT_DELALLOC for the range. + * + * Return <0 for error, in that case no range has EXTENT_DELALLOC bit cleared or set. + */ +int btrfs_reset_extent_delalloc(struct btrfs_inode *inode, u64 start, u64 end, + unsigned int extra_bits, struct extent_state **cached_state) +{ + const u32 blocksize = inode->root->fs_info->sectorsize; + + /* The @extra_bits can only be EXTENT_NORESERVE for now. */ + ASSERT(!(extra_bits & ~EXTENT_NORESERVE), "extra_bits=0x%x", extra_bits); + + /* Basic alignment check. */ + ASSERT(IS_ALIGNED(start, blocksize), "start=%llu blocksize=%u", + start, blocksize); + ASSERT(IS_ALIGNED(end + 1, blocksize), "inclusive end=%llu blocksize=%u", + end, blocksize); + + /* + * Check and set DELALLOC_NEW flag, this needs to search tree thus can + * fail early. Thus we want to do this before clearing EXTENT_DELALLOC. + */ + if (start >= i_size_read(&inode->vfs_inode) && + !(inode->flags & BTRFS_INODE_PREALLOC)) { + /* + * There can't be any extents following EOF in this case so just + * set the delalloc new bit for the range directly. + */ + extra_bits |= EXTENT_DELALLOC_NEW; + } else { + int ret; + + ret = btrfs_find_new_delalloc_bytes(inode, start, end + 1 - start, + NULL); + if (unlikely(ret)) + return ret; + } + /* Clear the old accounting as the range may already be dirty. */ + btrfs_clear_extent_bit(&inode->io_tree, start, end, + EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING | + EXTENT_DEFRAG, cached_state); + return btrfs_set_extent_bit(&inode->io_tree, start, end, + EXTENT_DELALLOC | extra_bits, cached_state); +} + static int insert_reserved_file_extent(struct btrfs_trans_handle *trans, struct btrfs_inode *inode, u64 file_pos, struct btrfs_file_extent_item *stack_fi, @@ -5181,12 +5233,7 @@ int btrfs_truncate_block(struct btrfs_inode *inode, u64 offset, u64 start, u64 e goto again; } - btrfs_clear_extent_bit(&inode->io_tree, block_start, block_end, - EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG, - &cached_state); - - ret = btrfs_set_extent_delalloc(inode, block_start, block_end, 0, - &cached_state); + ret = btrfs_reset_extent_delalloc(inode, block_start, block_end, 0, &cached_state); if (ret) { btrfs_unlock_extent(io_tree, block_start, block_end, &cached_state); goto out_unlock; diff --git a/fs/btrfs/reflink.c b/fs/btrfs/reflink.c index 49865a4637809..76a7b56f6cdee 100644 --- a/fs/btrfs/reflink.c +++ b/fs/btrfs/reflink.c @@ -94,9 +94,7 @@ static int copy_inline_to_page(struct btrfs_inode *inode, if (ret < 0) goto out_unlock; - btrfs_clear_extent_bit(&inode->io_tree, file_offset, range_end, - EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG, NULL); - ret = btrfs_set_extent_delalloc(inode, file_offset, range_end, 0, NULL); + ret = btrfs_reset_extent_delalloc(inode, file_offset, range_end, 0, NULL); if (ret) goto out_unlock; From b4af31b898a948e29861cb0bae734058f9a49d9b Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Tue, 2 Jun 2026 13:34:46 +0930 Subject: [PATCH 0505/2758] btrfs: do not trim a device which is not writeable commit 1b1937eb08f51319bf71575484cde2b8c517aedc upstream. [BUG] There is a bug report that btrfs/242 can randomly fail with the following NULL pointer dereference: run fstests btrfs/242 at 2026-06-01 10:25:08 BTRFS: device fsid d4d7f234-487c-4787-88e4-47a8b68c9874 devid 1 transid 9 /dev/sdc (8:32) scanned by mount (122609) BTRFS info (device sdc): first mount of filesystem d4d7f234-487c-4787-88e4-47a8b68c9874 BTRFS info (device sdc): using crc32c checksum algorithm BTRFS warning (device sdc): devid 2 uuid fbe72d72-3272-482d-80fb-ab88ed398192 is missing BTRFS warning (device sdc): devid 2 uuid fbe72d72-3272-482d-80fb-ab88ed398192 is missing BTRFS info (device sdc): allowing degraded mounts BTRFS info (device sdc): turning on async discard BTRFS info (device sdc): enabling free space tree Unable to handle kernel NULL pointer dereference at virtual address 0000000000000018 user pgtable: 4k pages, 48-bit VAs, pgdp=000000013fd6b000 CPU: 4 UID: 0 PID: 122625 Comm: fstrim Not tainted 7.0.10-2-default #1 PREEMPT(full) openSUSE Tumbleweed e9a5f6b24978fba3bf015a992f865837fdfff3dd Hardware name: QEMU KVM Virtual Machine, BIOS edk2-20250812-19.fc42 08/12/2025 pstate: 01400005 (nzcv daif +PAN -UAO -TCO +DIT -SSBS BTYPE=--) pc : btrfs_trim_fs+0x34c/0xa00 [btrfs] lr : btrfs_trim_fs+0x1f0/0xa00 [btrfs] Call trace: btrfs_trim_fs+0x34c/0xa00 [btrfs f02c1d570ceea621c69d302ba75dd61868083840] (P) btrfs_ioctl_fitrim+0xe8/0x178 [btrfs f02c1d570ceea621c69d302ba75dd61868083840] btrfs_ioctl+0xdd4/0x2bd8 [btrfs f02c1d570ceea621c69d302ba75dd61868083840] __arm64_sys_ioctl+0xac/0x108 invoke_syscall.constprop.0+0x5c/0xd0 el0_svc_common.constprop.0+0x40/0xf0 do_el0_svc+0x24/0x40 el0_svc+0x40/0x1d0 el0t_64_sync_handler+0xa0/0xe8 el0t_64_sync+0x1b0/0x1b8 Code: 17ffff83 f94017e0 f9002be0 f9402ea0 (f9400c00) ---[ end trace 0000000000000000 ]--- Also the reporter is very kind to test the following ASSERT() added to btrfs_trim_free_extents_throttle(): ASSERT(device->bdev, "devid=%llu path=%s dev_state=0x%lx\n", device->devid, btrfs_dev_name(device), device->dev_state); And it shows the following output: assertion failed: device->bdev, in extent-tree.c:6630 (devid=2 path=/dev/sdd dev_state=0x82) Which means the device->bdev is NULL, and the dev_state is BTRFS_DEV_STATE_IN_FS_METADATA | BTRFS_DEV_STATE_ITEM_FOUND, without BTRFS_DEV_STATE_WRITEABLE flag set. [CAUSE] The pc points to the following call chain: btrfs_trim_fs() |- btrfs_trim_free_extents() |- btrfs_trim_free_extents_throttle() |- bdev_max_discard_sectors(device->bdev) So the NULL pointer dereference is caused by device->bdev being NULL. This looks impossible by a quick glance, as just before calling btrfs_trim_free_extents_throttle(), we have skipped any device that has BTRFS_DEV_STATE_MISSING flag set. However in this particular case, there is a window where the missing device is later re-scanned, causing btrfs to remove the BTRFS_DEV_STATE_MISSING flag: btrfs_control_ioctl() |- btrfs_scan_one_device() |- device_list_add() |- rcu_assign_pointer(device->name, name); | This updates the missing device's path to the new good path. | |- clear_bit(BTRFS_DEV_STATE_MISSING, &device->dev_state) This removes the BTRFS_DEV_STATE_MISSING flag. This allows the missing device to re-appear and clear the BTRFS_DEV_STATE_MISSING flag. However the device still does not have the BTRFS_DEV_STATE_WRITEABLE flag set, nor is its bdev pointer updated. The bdev pointer remains NULL, triggering the crash later. [FIX] This is a big de-synchronization between BTRFS_DEV_STATE_MISSING and device->bdev pointer, and shows a gap in btrfs's re-appearing-device handling. The proper handling of re-appearing device will need quite some extra work, which is out of the context of this small fix. Thankfully the regular bbio submission path has already handled it well by checking if the device->bdev is NULL before submitting. So here we just fix the crash by checking if the device is writeable and has a bdev pointer before calling bdev_max_discard_sectors(). Reported-by: Su Yue Link: https://lore.kernel.org/linux-btrfs/wlwir19t.fsf@damenly.org/ Fixes: 499f377f49f0 ("btrfs: iterate over unused chunk space in FITRIM") CC: stable@vger.kernel.org # 5.10+ Reviewed-by: Filipe Manana Signed-off-by: Qu Wenruo Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/extent-tree.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 391fad41c3b6d..a8c77f31ff784 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -6613,12 +6613,16 @@ static int btrfs_trim_free_extents_throttle(struct btrfs_device *device, *trimmed = 0; - /* Discard not supported = nothing to do. */ - if (!bdev_max_discard_sectors(device->bdev)) + /* + * The caller only filters out MISSING devices, but a device that was + * missing at mount and later rescanned has MISSING cleared while bdev + * is still NULL and WRITEABLE is still unset. Skip those here. + */ + if (!test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state) || !device->bdev) return 0; - /* Not writable = nothing to do. */ - if (!test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state)) + /* Discard not supported = nothing to do. */ + if (!bdev_max_discard_sectors(device->bdev)) return 0; /* No free space = nothing to do. */ From 44f37ee92fdcd377c41bdf6a31cdd8cc7d4c410e Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sun, 7 Jun 2026 06:41:43 +0000 Subject: [PATCH 0506/2758] partitions: aix: bound the pp_count scan to the ppe array commit 2dc0bfd2fe355fb930de63c2f2eb8ced8570c579 upstream. aix_partition() reads the physical volume descriptor into a fixed-size struct pvd and then scans its physical-partition-extent array: int numpps = be16_to_cpu(pvd->pp_count); ... for (i = 0; i < numpps; i += 1) { struct ppe *p = pvd->ppe + i; ... lp_ix = be16_to_cpu(p->lp_ix); pvd points at a single kmalloc()'d struct pvd whose ppe[] member holds a fixed ARRAY_SIZE(pvd->ppe) (1016) entries, but the loop runs up to the on-disk pp_count. pp_count is an unvalidated __be16 read straight from the descriptor, so a crafted AIX image with pp_count larger than 1016 drives the loop to read pvd->ppe[i] past the end of the allocation (up to 65535 entries, ~2 MB out of bounds). The partition scan runs without mounting anything, when a block device with a crafted AIX/IBM partition table appears (an attacker-supplied image attached with losetup -P, or a device auto-scanned by udev), via msdos_partition() -> aix_partition(). Clamp the scan to the number of entries the ppe[] array can hold. Fixes: 6ceea22bbbc8 ("partitions: add aix lvm partition support files") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Acked-by: Philippe De Muyter Link: https://patch.msgid.link/20260607064137.302574-1-hexlabsecurity@proton.me Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/partitions/aix.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/block/partitions/aix.c b/block/partitions/aix.c index 29b8f4cebb63d..f3c4174e003e9 100644 --- a/block/partitions/aix.c +++ b/block/partitions/aix.c @@ -226,6 +226,15 @@ int aix_partition(struct parsed_partitions *state) int next_lp_ix = 1; int lp_ix; + /* + * pvd was read into a fixed-size struct pvd whose ppe[] array + * holds ARRAY_SIZE(pvd->ppe) entries. pp_count is an + * unvalidated on-disk __be16, so clamp the scan to the array + * size to avoid walking past the allocation. + */ + if (numpps > ARRAY_SIZE(pvd->ppe)) + numpps = ARRAY_SIZE(pvd->ppe); + for (i = 0; i < numpps; i += 1) { struct ppe *p = pvd->ppe + i; unsigned int lv_ix; From b8df7f486a4617d3096b8fa27148cfa54b1d6a8d Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Thu, 4 Jun 2026 09:59:47 +0930 Subject: [PATCH 0507/2758] btrfs: fix incorrect buffered IO fallback for append direct writes commit ff66fe6662330226b3f486014c375538d91c44aa upstream. [BUG] With the previous bug of short direct writes fixed, test case generic/362 (*) still fails with the following error with nodatasum mount option: # generic/362 0s ... - output mismatch (see /home/adam/xfstests/results//generic/362.out.bad) # - output mismatch (see /home/adam/xfstests/results//generic/362.out.bad) # --- tests/generic/362.out 2024-08-24 15:31:37.200000000 +0930 # +++ /home/adam/xfstests/results//generic/362.out.bad 2026-05-27 10:13:09.072485767 +0930 # @@ -1,2 +1,3 @@ # QA output created by 362 # +Wrong file size after first write, got 8192 expected 4096 # Silence is golden # ... *: If the test case has been executed before with default data checksum, the failure will not reproduce. Need the following fix to make it reliably reproducible: https://lore.kernel.org/linux-btrfs/20260528111659.87113-1-wqu@suse.com/ [CAUSE] Inside btrfs_dio_iomap_begin() for a direct write, we increase the isize if it's beyond the current isize. But if the direct io finished short, we do not revert the isize to the previous value nor to the short write end. Then if we need to fall back to buffered writes, and the write has IOCB_APPEND flag, then the buffered write will be positioned at the incorrect isize. The call chain looks like this: btrfs_direct_write(pos=0, length=4K) |- __iomap_dio_rw() | |- iomap_iter() | | |- btrfs_dio_iomap_begin() | | |- btrfs_get_blocks_direct_write() | | |- i_size_write() | | Which updates the isize to the write end (4K). | | | |- iomap_dio_iter() | | Failed with -EFAULT on the first page. | | | |- iomap_iter() | | |- btrfs_dio_iomap_end() | | Detects a short write, return -ENOTBLK | |- if (ret == -ENOTBLK) { ret = 0;} | Which resets the return value. | |- ret = iomap_dio_complet() | Which returns 0. | |- btrfs_buffered_write(iocb, from); |- generic_write_checks() |- iocb->ki_pos = i_size_read() Which is still the new size (4K), other than the original isize 0. [FIX] Introduce the following btrfs_dio_data members: - old_isize - updated_isize If the direct write has enlarged the isize. Then if we got a short write, and btrfs_dio_data::updated_isize is set, revert to the correct isize based on old_isize and current file position. And here we call i_size_write() without holding an extent lock, which is a very special case that we're safe to do: - Only a single writer can be enlarging isize Enlarging isize will take the exclusive inode lock. - Buffered readers need to wait for the OE we're holding Buffered readers will lock extent and wait for OE of the folio range. Sometimes we can skip the OE wait, but since all page cache is invalidated, the OE wait can not be skipped. But I do not think this is the most elegant solution, nor covers all cases. E.g. if the bio is submitted but IO failed, we are unable to do the revert. I believe the more elegant one would be extend the EXTENT_DIO_LOCKED lifespan for direct writes, so that we can update the isize when a write beyond EOF finished successfully. However that change is too huge for a small bug fix. So only implement the minimal partial fix for now. [REASON FOR NO FIXES TAG] The bug is again very old, before commit f85781fb505e ("btrfs: switch to iomap for direct IO") we are already increasing isize without a proper rollback for short writes. Thus only a CC to stable. CC: stable@vger.kernel.org # 5.15+ Reviewed-by: Boris Burkov Signed-off-by: Qu Wenruo Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/direct-io.c | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/direct-io.c b/fs/btrfs/direct-io.c index 88cb2e82a5076..412309825d6fa 100644 --- a/fs/btrfs/direct-io.c +++ b/fs/btrfs/direct-io.c @@ -15,10 +15,12 @@ struct btrfs_dio_data { ssize_t submitted; + loff_t old_isize; struct extent_changeset *data_reserved; struct btrfs_ordered_extent *ordered; bool data_space_reserved; bool nocow_done; + bool updated_isize; }; struct btrfs_dio_private { @@ -228,6 +230,7 @@ static int btrfs_get_blocks_direct_write(struct extent_map **map, bool space_reserved = false; u64 len = *lenp; u64 prev_len; + loff_t old_isize; int ret = 0; /* @@ -341,8 +344,14 @@ static int btrfs_get_blocks_direct_write(struct extent_map **map, * Need to update the i_size under the extent lock so buffered * readers will get the updated i_size when we unlock. */ - if (start + len > i_size_read(inode)) + old_isize = i_size_read(inode); + if (start + len > old_isize) { + if (!dio_data->updated_isize) { + dio_data->old_isize = old_isize; + dio_data->updated_isize = true; + } i_size_write(inode, start + len); + } out: if (ret && space_reserved) { btrfs_delalloc_release_extents(BTRFS_I(inode), len); @@ -625,6 +634,38 @@ static int btrfs_dio_iomap_end(struct inode *inode, loff_t pos, loff_t length, pos += submitted; length -= submitted; if (write) { + /* + * Got a short write and have updated the isize, need to + * revert the isize change. + * + * Normally we need to update isize with extent lock hold, + * but we're safe due to the following factors: + * + * - Only a single writer can be enlarging isize + * Enlarging isize will take the exclusive inode lock. + * + * - Buffered readers need to wait for the OE we're holding + * Buffered readers will lock extent and wait for OE + * of the folio range, and since page cache is invalidated + * the OE wait can not be skipped. + * + * So here we are safe to revert the isize before + * finishing the OE, and no reader of the remaining range + * can see the enlarged size. + * + * TODO: Extend the DIO_LOCKED lifespan for direct writes, + * and only enlarge isize after a successful write. + */ + if (dio_data->updated_isize) { + u64 new_isize; + + if (submitted == 0) + new_isize = dio_data->old_isize; + else + new_isize = max(dio_data->old_isize, pos); + i_size_write(inode, new_isize); + dio_data->updated_isize = false; + } /* * We have a short write, if there is any range * that is submitted properly, that part will have From 9830725078c8483c6831ec10222ae724806ea36b Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sun, 7 Jun 2026 01:18:27 +0000 Subject: [PATCH 0508/2758] isofs: bound Rock Ridge symlink components to the SL record commit 5fa1d6a5ec2356d2107dead614437c66fa7138b1 upstream. get_symlink_chunk() and the SL handling in parse_rock_ridge_inode_internal() walk the variable-length components of a Rock Ridge "SL" (symbolic link) record. Each component is a two-byte header (flags, len) followed by len bytes of text, so it occupies slp->len + 2 bytes. Both loops read slp->len and advance to the next component, and get_symlink_chunk() additionally does memcpy(rpnt, slp->text, slp->len), but neither checks that the component lies within the SL record before dereferencing it. A crafted SL record whose component declares a len that runs past the record (rr->len) therefore triggers an out-of-bounds read of up to 255 bytes. When the record sits at the tail of its backing buffer - for example a small kmalloc()ed continuation block reached through a CE record - the read crosses the allocation; get_symlink_chunk() then copies the out-of-bounds bytes into the symlink body returned to user space by readlink(), disclosing adjacent kernel memory. ISO 9660 images are routinely mounted from untrusted removable media - desktop environments auto-mount them (e.g. via udisks2) without CAP_SYS_ADMIN - so the record contents are attacker-controlled. Reject any component that does not fit in the remaining record bytes before using it. In get_symlink_chunk() return NULL, like the existing output-buffer (plimit) checks, so a malformed record makes readlink() fail with -EIO rather than silently returning a truncated target; in parse_rock_ridge_inode_internal() stop the inode-size walk. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Suggested-by: Michael Bommarito Signed-off-by: Bryam Vargas Link: https://patch.msgid.link/20260607011823.217748-1-hexlabsecurity@proton.me Signed-off-by: Jan Kara Signed-off-by: Greg Kroah-Hartman --- fs/isofs/rock.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c index 1232fab59a4e6..2628f31bd3a5d 100644 --- a/fs/isofs/rock.c +++ b/fs/isofs/rock.c @@ -466,6 +466,9 @@ parse_rock_ridge_inode_internal(struct iso_directory_record *de, inode->i_size = symlink_len; while (slen > 1) { rootflag = 0; + /* keep the component within the SL record */ + if (slp->len + 2 > slen) + goto eio; switch (slp->flags & ~1) { case 0: inode->i_size += @@ -621,6 +624,14 @@ static char *get_symlink_chunk(char *rpnt, struct rock_ridge *rr, char *plimit) slp = &rr->u.SL.link; while (slen > 1) { rootflag = 0; + /* + * A component is slp->len + 2 bytes (a two-byte header plus + * len bytes of text). If it does not fit in the bytes left in + * the SL record the record is malformed: fail like the plimit + * checks below so readlink() returns -EIO, not a truncated path. + */ + if (slp->len + 2 > slen) + return NULL; switch (slp->flags & ~1) { case 0: if (slp->len > plimit - rpnt) From d9dbf9a484cb28093953cdf48e68f94684c0ea2d Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 4 May 2026 15:53:28 -0700 Subject: [PATCH 0509/2758] crypto: af_alg - Remove zero-copy support from skcipher and aead commit ffdd2bc378953b525aca61902534e753f1f8e734 upstream. The zero-copy support is one of the riskiest aspects of AF_ALG. It allows userspace to request cryptographic operations directly on pagecache pages of files like the 'su' binary. It also allows userspace to concurrently modify the memory which is being operated on, a recipe for TOCTOU vulnerabilities. While zero-copy support is more valuable in other areas of the kernel like the frequently used networking and file I/O code, it has far less value in AF_ALG, which is a niche UAPI. AF_ALG primarily just exists for backwards compatibility with a small set of userspace programs such as 'iwd' that haven't yet been fixed to use userspace crypto code. Originally AF_ALG was intended to be used to access hardware crypto accelerators. However, it isn't an efficient interface for that anyway, and it turned out to be rarely used in this way in practice. Thus, the risks of the zero-copy support in AF_ALG vastly outweigh its benefits. Let's just remove it. This commit removes it from the "skcipher" and "aead" algorithm types. "hash" will be handled separately. This is a soft break, not a hard break. Even after this commit, it still works to use splice() or sendfile() to transfer data to an AF_ALG request socket from a pipe or any file, respectively. What changes is just that the kernel now makes an internal, stable copy of the data before doing the crypto operation. So performance is slightly reduced, but the UAPI isn't broken. And, very importantly, it's much safer. Tested with libkcapi/test.sh. All its test cases still pass. I also verified that this would have prevented the copy.fail exploit as well. I also used a custom test program to verify that sendfile() still works. Fixes: 8ff590903d5f ("crypto: algif_skcipher - User-space interface for skcipher operations") Fixes: 400c40cf78da ("crypto: algif - add AEAD support") Reported-by: Taeyang Lee <0wn@theori.io> Link: https://copy.fail/ Reported-by: Feng Ning Closes: https://lore.kernel.org/r/afYcc-tZFwvZZo76@ans-MacBook-Pro.local Reviewed-by: Demi Marie Obenour Cc: stable@vger.kernel.org Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- Documentation/crypto/userspace-if.rst | 31 ++---------- crypto/af_alg.c | 73 +++++++++------------------ crypto/algif_aead.c | 8 +-- 3 files changed, 33 insertions(+), 79 deletions(-) diff --git a/Documentation/crypto/userspace-if.rst b/Documentation/crypto/userspace-if.rst index 021759198fe77..d220e266b8deb 100644 --- a/Documentation/crypto/userspace-if.rst +++ b/Documentation/crypto/userspace-if.rst @@ -327,33 +327,10 @@ CRYPTO_USER_API_RNG_CAVP option: Zero-Copy Interface ------------------- -In addition to the send/write/read/recv system call family, the AF_ALG -interface can be accessed with the zero-copy interface of -splice/vmsplice. As the name indicates, the kernel tries to avoid a copy -operation into kernel space. - -The zero-copy operation requires data to be aligned at the page -boundary. Non-aligned data can be used as well, but may require more -operations of the kernel which would defeat the speed gains obtained -from the zero-copy interface. - -The system-inherent limit for the size of one zero-copy operation is 16 -pages. If more data is to be sent to AF_ALG, user space must slice the -input into segments with a maximum size of 16 pages. - -Zero-copy can be used with the following code example (a complete -working example is provided with libkcapi): - -:: - - int pipes[2]; - - pipe(pipes); - /* input data in iov */ - vmsplice(pipes[1], iov, iovlen, SPLICE_F_GIFT); - /* opfd is the file descriptor returned from accept() system call */ - splice(pipes[0], NULL, opfd, NULL, ret, 0); - read(opfd, out, outlen); +AF_ALG used to have zero-copy support, but it was removed due to it being a +frequent source of vulnerabilities. For backwards compatibility the splice() +and sendfile() system calls are still supported, but the kernel will make an +internal copy of the data before passing it to the crypto code. Setsockopt Interface diff --git a/crypto/af_alg.c b/crypto/af_alg.c index 5a00c18eb145c..fce0b87c2b652 100644 --- a/crypto/af_alg.c +++ b/crypto/af_alg.c @@ -973,7 +973,7 @@ int af_alg_sendmsg(struct socket *sock, struct msghdr *msg, size_t size, ssize_t plen; /* use the existing memory in an allocated page */ - if (ctx->merge && !(msg->msg_flags & MSG_SPLICE_PAGES)) { + if (ctx->merge) { sgl = list_entry(ctx->tsgl_list.prev, struct af_alg_tsgl, list); sg = sgl->sg + sgl->cur - 1; @@ -1017,60 +1017,37 @@ int af_alg_sendmsg(struct socket *sock, struct msghdr *msg, size_t size, if (sgl->cur) sg_unmark_end(sg + sgl->cur - 1); - if (msg->msg_flags & MSG_SPLICE_PAGES) { - struct sg_table sgtable = { - .sgl = sg, - .nents = sgl->cur, - .orig_nents = sgl->cur, - }; - - plen = extract_iter_to_sg(&msg->msg_iter, len, &sgtable, - MAX_SGL_ENTS - sgl->cur, 0); - if (plen < 0) { - err = plen; + do { + struct page *pg; + unsigned int i = sgl->cur; + + plen = min_t(size_t, len, PAGE_SIZE); + + pg = alloc_page(GFP_KERNEL); + if (!pg) { + err = -ENOMEM; goto unlock; } - for (; sgl->cur < sgtable.nents; sgl->cur++) - get_page(sg_page(&sg[sgl->cur])); + sg_assign_page(sg + i, pg); + + err = memcpy_from_msg(page_address(sg_page(sg + i)), + msg, plen); + if (err) { + __free_page(sg_page(sg + i)); + sg_assign_page(sg + i, NULL); + goto unlock; + } + + sg[i].length = plen; len -= plen; ctx->used += plen; copied += plen; size -= plen; - } else { - do { - struct page *pg; - unsigned int i = sgl->cur; - - plen = min_t(size_t, len, PAGE_SIZE); - - pg = alloc_page(GFP_KERNEL); - if (!pg) { - err = -ENOMEM; - goto unlock; - } - - sg_assign_page(sg + i, pg); - - err = memcpy_from_msg( - page_address(sg_page(sg + i)), - msg, plen); - if (err) { - __free_page(sg_page(sg + i)); - sg_assign_page(sg + i, NULL); - goto unlock; - } - - sg[i].length = plen; - len -= plen; - ctx->used += plen; - copied += plen; - size -= plen; - sgl->cur++; - } while (len && sgl->cur < MAX_SGL_ENTS); - - ctx->merge = plen & (PAGE_SIZE - 1); - } + sgl->cur++; + } while (len && sgl->cur < MAX_SGL_ENTS); + + ctx->merge = plen & (PAGE_SIZE - 1); if (!size) sg_mark_end(sg + sgl->cur - 1); diff --git a/crypto/algif_aead.c b/crypto/algif_aead.c index cb651ab58d629..c6c2ce21895dd 100644 --- a/crypto/algif_aead.c +++ b/crypto/algif_aead.c @@ -9,10 +9,10 @@ * The following concept of the memory management is used: * * The kernel maintains two SGLs, the TX SGL and the RX SGL. The TX SGL is - * filled by user space with the data submitted via sendmsg (maybe with - * MSG_SPLICE_PAGES). Filling up the TX SGL does not cause a crypto operation - * -- the data will only be tracked by the kernel. Upon receipt of one recvmsg - * call, the caller must provide a buffer which is tracked with the RX SGL. + * filled by user space with the data submitted via sendmsg. Filling up the TX + * SGL does not cause a crypto operation -- the data will only be tracked by the + * kernel. Upon receipt of one recvmsg call, the caller must provide a buffer + * which is tracked with the RX SGL. * * During the processing of the recvmsg operation, the cipher request is * allocated and prepared. As part of the recvmsg operation, the processed From 8cf5fb0503129e53052fe29302379cf83891d0fb Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 27 Apr 2026 18:39:37 +0200 Subject: [PATCH 0510/2758] crypto: caam - use print_hex_dump_devel to guard key hex dumps commit 3f57657b6ea23f933371f2c2846322f441773cee upstream. Use print_hex_dump_devel() for dumping sensitive key material in *_setkey() and gen_split_key() to avoid leaking secrets at runtime when CONFIG_DYNAMIC_DEBUG is enabled. Fixes: 6e005503199b ("crypto: caam - print debug messages at debug level") Cc: stable@vger.kernel.org Signed-off-by: Thorsten Blum Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/caam/caamalg.c | 12 ++++++------ drivers/crypto/caam/caamalg_qi.c | 12 ++++++------ drivers/crypto/caam/caamhash.c | 4 ++-- drivers/crypto/caam/key_gen.c | 4 ++-- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c index 32a6e6e15ee25..ddbd60cf3b3c1 100644 --- a/drivers/crypto/caam/caamalg.c +++ b/drivers/crypto/caam/caamalg.c @@ -603,7 +603,7 @@ static int aead_setkey(struct crypto_aead *aead, dev_dbg(jrdev, "keylen %d enckeylen %d authkeylen %d\n", keys.authkeylen + keys.enckeylen, keys.enckeylen, keys.authkeylen); - print_hex_dump_debug("key in @"__stringify(__LINE__)": ", + print_hex_dump_devel("key in @"__stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); /* @@ -639,7 +639,7 @@ static int aead_setkey(struct crypto_aead *aead, dma_sync_single_for_device(jrdev, ctx->key_dma, ctx->adata.keylen_pad + keys.enckeylen, ctx->dir); - print_hex_dump_debug("ctx.key@"__stringify(__LINE__)": ", + print_hex_dump_devel("ctx.key@"__stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, ctx->key, ctx->adata.keylen_pad + keys.enckeylen, 1); @@ -680,7 +680,7 @@ static int gcm_setkey(struct crypto_aead *aead, if (err) return err; - print_hex_dump_debug("key in @"__stringify(__LINE__)": ", + print_hex_dump_devel("key in @"__stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); memcpy(ctx->key, key, keylen); @@ -701,7 +701,7 @@ static int rfc4106_setkey(struct crypto_aead *aead, if (err) return err; - print_hex_dump_debug("key in @"__stringify(__LINE__)": ", + print_hex_dump_devel("key in @"__stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); memcpy(ctx->key, key, keylen); @@ -727,7 +727,7 @@ static int rfc4543_setkey(struct crypto_aead *aead, if (err) return err; - print_hex_dump_debug("key in @"__stringify(__LINE__)": ", + print_hex_dump_devel("key in @"__stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); memcpy(ctx->key, key, keylen); @@ -754,7 +754,7 @@ static int skcipher_setkey(struct crypto_skcipher *skcipher, const u8 *key, u32 *desc; const bool is_rfc3686 = alg->caam.rfc3686; - print_hex_dump_debug("key in @"__stringify(__LINE__)": ", + print_hex_dump_devel("key in @"__stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); /* Here keylen is actual key length */ diff --git a/drivers/crypto/caam/caamalg_qi.c b/drivers/crypto/caam/caamalg_qi.c index 65f6adb6c673f..aa779caacfe59 100644 --- a/drivers/crypto/caam/caamalg_qi.c +++ b/drivers/crypto/caam/caamalg_qi.c @@ -212,7 +212,7 @@ static int aead_setkey(struct crypto_aead *aead, const u8 *key, dev_dbg(jrdev, "keylen %d enckeylen %d authkeylen %d\n", keys.authkeylen + keys.enckeylen, keys.enckeylen, keys.authkeylen); - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); /* @@ -248,7 +248,7 @@ static int aead_setkey(struct crypto_aead *aead, const u8 *key, ctx->adata.keylen_pad + keys.enckeylen, ctx->dir); - print_hex_dump_debug("ctx.key@" __stringify(__LINE__)": ", + print_hex_dump_devel("ctx.key@" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, ctx->key, ctx->adata.keylen_pad + keys.enckeylen, 1); @@ -371,7 +371,7 @@ static int gcm_setkey(struct crypto_aead *aead, if (ret) return ret; - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); memcpy(ctx->key, key, keylen); @@ -475,7 +475,7 @@ static int rfc4106_setkey(struct crypto_aead *aead, if (ret) return ret; - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); memcpy(ctx->key, key, keylen); @@ -581,7 +581,7 @@ static int rfc4543_setkey(struct crypto_aead *aead, if (ret) return ret; - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); memcpy(ctx->key, key, keylen); @@ -631,7 +631,7 @@ static int skcipher_setkey(struct crypto_skcipher *skcipher, const u8 *key, const bool is_rfc3686 = alg->caam.rfc3686; int ret = 0; - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); ctx->cdata.keylen = keylen; diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c index ddb2a35aec2d5..3cd71810380f6 100644 --- a/drivers/crypto/caam/caamhash.c +++ b/drivers/crypto/caam/caamhash.c @@ -505,7 +505,7 @@ static int axcbc_setkey(struct crypto_ahash *ahash, const u8 *key, DMA_TO_DEVICE); ctx->adata.keylen = keylen; - print_hex_dump_debug("axcbc ctx.key@" __stringify(__LINE__)" : ", + print_hex_dump_devel("axcbc ctx.key@" __stringify(__LINE__)" : ", DUMP_PREFIX_ADDRESS, 16, 4, ctx->key, keylen, 1); return axcbc_set_sh_desc(ahash); @@ -525,7 +525,7 @@ static int acmac_setkey(struct crypto_ahash *ahash, const u8 *key, ctx->adata.key_virt = key; ctx->adata.keylen = keylen; - print_hex_dump_debug("acmac ctx.key@" __stringify(__LINE__)" : ", + print_hex_dump_devel("acmac ctx.key@" __stringify(__LINE__)" : ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); return acmac_set_sh_desc(ahash); diff --git a/drivers/crypto/caam/key_gen.c b/drivers/crypto/caam/key_gen.c index 88cc4fe2a585d..de2fcc387477d 100644 --- a/drivers/crypto/caam/key_gen.c +++ b/drivers/crypto/caam/key_gen.c @@ -58,7 +58,7 @@ int gen_split_key(struct device *jrdev, u8 *key_out, dev_dbg(jrdev, "split keylen %d split keylen padded %d\n", adata->keylen, adata->keylen_pad); - print_hex_dump_debug("ctx.key@" __stringify(__LINE__)": ", + print_hex_dump_devel("ctx.key@" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key_in, keylen, 1); if (local_max > max_keylen) @@ -113,7 +113,7 @@ int gen_split_key(struct device *jrdev, u8 *key_out, wait_for_completion(&result.completion); ret = result.err; - print_hex_dump_debug("ctx.key@"__stringify(__LINE__)": ", + print_hex_dump_devel("ctx.key@"__stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key_out, adata->keylen_pad, 1); } From 59057f5d4e9a195c6dd61695ad3bc4481ddf4f14 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 27 Apr 2026 18:39:39 +0200 Subject: [PATCH 0511/2758] crypto: caam - use print_hex_dump_devel to guard key hex dumps again commit 8005dc808bcce7d6cc2ae015a3cde1683bee602d upstream. Use print_hex_dump_devel() for dumping sensitive key material in *_setkey() to avoid leaking secrets at runtime when CONFIG_DYNAMIC_DEBUG is enabled. Fixes: 8d818c105501 ("crypto: caam/qi2 - add DPAA2-CAAM driver") Fixes: 226853ac3ebe ("crypto: caam/qi2 - add skcipher algorithms") Cc: stable@vger.kernel.org Signed-off-by: Thorsten Blum Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/caam/caamalg_qi2.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/crypto/caam/caamalg_qi2.c b/drivers/crypto/caam/caamalg_qi2.c index bf10c3dda745a..6b47bcc16a506 100644 --- a/drivers/crypto/caam/caamalg_qi2.c +++ b/drivers/crypto/caam/caamalg_qi2.c @@ -301,7 +301,7 @@ static int aead_setkey(struct crypto_aead *aead, const u8 *key, dev_dbg(dev, "keylen %d enckeylen %d authkeylen %d\n", keys.authkeylen + keys.enckeylen, keys.enckeylen, keys.authkeylen); - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); ctx->adata.keylen = keys.authkeylen; @@ -315,7 +315,7 @@ static int aead_setkey(struct crypto_aead *aead, const u8 *key, memcpy(ctx->key + ctx->adata.keylen_pad, keys.enckey, keys.enckeylen); dma_sync_single_for_device(dev, ctx->key_dma, ctx->adata.keylen_pad + keys.enckeylen, ctx->dir); - print_hex_dump_debug("ctx.key@" __stringify(__LINE__)": ", + print_hex_dump_devel("ctx.key@" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, ctx->key, ctx->adata.keylen_pad + keys.enckeylen, 1); @@ -732,7 +732,7 @@ static int gcm_setkey(struct crypto_aead *aead, ret = aes_check_keylen(keylen); if (ret) return ret; - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); memcpy(ctx->key, key, keylen); @@ -828,7 +828,7 @@ static int rfc4106_setkey(struct crypto_aead *aead, if (ret) return ret; - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); memcpy(ctx->key, key, keylen); @@ -927,7 +927,7 @@ static int rfc4543_setkey(struct crypto_aead *aead, if (ret) return ret; - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); memcpy(ctx->key, key, keylen); @@ -955,7 +955,7 @@ static int skcipher_setkey(struct crypto_skcipher *skcipher, const u8 *key, u32 *desc; const bool is_rfc3686 = alg->caam.rfc3686; - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); ctx->cdata.keylen = keylen; From e74df53b36cdc6b6b9e5488ec883d1d55624737f Mon Sep 17 00:00:00 2001 From: Xiaonan Zhao Date: Tue, 26 May 2026 18:11:43 +0800 Subject: [PATCH 0512/2758] crypto: chacha20poly1305 - validate poly1305 template argument commit 265b861bece38318b8e0fc8fac0643d4ef906d31 upstream. chachapoly_create() still accepts the compatibility poly1305 parameter in the template name, but it assumes the second template argument is always present and immediately passes it to strcmp(). When the argument is missing, crypto_attr_alg_name() returns an error pointer. Check for that before comparing the name so malformed template instantiations fail with an error instead of dereferencing the error pointer in strcmp(). This matches the surrounding Crypto API template pattern where crypto_attr_alg_name() results are validated before string-specific use. Fixes: a298765e28ad ("crypto: chacha20poly1305 - Use lib/crypto poly1305") Cc: stable@kernel.org Reported-by: Yuan Tan Reported-by: Zhengchuan Liang Reported-by: Xin Liu Co-developed-by: Luxing Yin Signed-off-by: Luxing Yin Signed-off-by: Xiaonan Zhao Signed-off-by: Ren Wei Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- crypto/chacha20poly1305.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/crypto/chacha20poly1305.c b/crypto/chacha20poly1305.c index b4b5a7198d841..27df9e1eb0580 100644 --- a/crypto/chacha20poly1305.c +++ b/crypto/chacha20poly1305.c @@ -375,6 +375,7 @@ static int chachapoly_create(struct crypto_template *tmpl, struct rtattr **tb, struct aead_instance *inst; struct chachapoly_instance_ctx *ctx; struct skcipher_alg_common *chacha; + const char *poly_name; int err; if (ivsize > CHACHAPOLY_IV_SIZE) @@ -396,9 +397,15 @@ static int chachapoly_create(struct crypto_template *tmpl, struct rtattr **tb, goto err_free_inst; chacha = crypto_spawn_skcipher_alg_common(&ctx->chacha); + poly_name = crypto_attr_alg_name(tb[2]); + if (IS_ERR(poly_name)) { + err = PTR_ERR(poly_name); + goto err_free_inst; + } + err = -EINVAL; - if (strcmp(crypto_attr_alg_name(tb[2]), "poly1305") && - strcmp(crypto_attr_alg_name(tb[2]), "poly1305-generic")) + if (strcmp(poly_name, "poly1305") && + strcmp(poly_name, "poly1305-generic")) goto err_free_inst; /* Need 16-byte IV size, including Initial Block Counter value */ if (chacha->ivsize != CHACHA_IV_SIZE) From a856bc7d0fbbbdaa55de4674823e77a40da784cf Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 29 May 2026 15:04:30 -0700 Subject: [PATCH 0513/2758] crypto: crypto4xx - Remove insecure and unused rng_alg commit 7811ec9e973d2c9e465083699f0c8240b98cb8c4 upstream. Remove crypto4xx_rng, as it is insecure and unused: - It has only a 64-bit security strength, which is highly inadequate. This can be seen by the fact that crypto4xx_hw_init() seeds it with only 64 bits of entropy, and the fact that the original commit mentions that it implements ANSI X9.17 Annex C. Another issue was that this driver didn't implement the crypto_rng API correctly, as crypto4xx_prng_generate() didn't return 0 on success. - No user of this code is known. It's usable only theoretically via the "rng" algorithm type of AF_ALG. But userspace actually just uses the actual Linux RNG (/dev/random etc) instead. And rng_algs don't contribute entropy to the actual Linux RNG either. (This may have been confused with hwrng, which does contribute entropy.) Fixes: d072bfa48853 ("crypto: crypto4xx - add prng crypto support") Cc: stable@vger.kernel.org Signed-off-by: Eric Biggers Acked-by: Christian Lamparter Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/Kconfig | 1 - drivers/crypto/amcc/crypto4xx_core.c | 88 ------------------------- drivers/crypto/amcc/crypto4xx_core.h | 4 -- drivers/crypto/amcc/crypto4xx_reg_def.h | 11 ---- 4 files changed, 104 deletions(-) diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index d23b58b81ca34..ce1c0bc3efaaa 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -301,7 +301,6 @@ config CRYPTO_DEV_PPC4XX select CRYPTO_CCM select CRYPTO_CTR select CRYPTO_GCM - select CRYPTO_RNG select CRYPTO_SKCIPHER help This option allows you to have support for AMCC crypto acceleration. diff --git a/drivers/crypto/amcc/crypto4xx_core.c b/drivers/crypto/amcc/crypto4xx_core.c index b7b6c97d2147b..68c5ff7a85b4f 100644 --- a/drivers/crypto/amcc/crypto4xx_core.c +++ b/drivers/crypto/amcc/crypto4xx_core.c @@ -31,11 +31,9 @@ #include #include #include -#include #include #include #include -#include #include #include "crypto4xx_reg_def.h" #include "crypto4xx_core.h" @@ -985,10 +983,6 @@ static int crypto4xx_register_alg(struct crypto4xx_device *sec_dev, rc = crypto_register_aead(&alg->alg.u.aead); break; - case CRYPTO_ALG_TYPE_RNG: - rc = crypto_register_rng(&alg->alg.u.rng); - break; - default: rc = crypto_register_skcipher(&alg->alg.u.cipher); break; @@ -1014,10 +1008,6 @@ static void crypto4xx_unregister_alg(struct crypto4xx_device *sec_dev) crypto_unregister_aead(&alg->alg.u.aead); break; - case CRYPTO_ALG_TYPE_RNG: - crypto_unregister_rng(&alg->alg.u.rng); - break; - default: crypto_unregister_skcipher(&alg->alg.u.cipher); } @@ -1076,69 +1066,6 @@ static irqreturn_t crypto4xx_ce_interrupt_handler_revb(int irq, void *data) PPC4XX_TMO_ERR_INT); } -static int ppc4xx_prng_data_read(struct crypto4xx_device *dev, - u8 *data, unsigned int max) -{ - unsigned int i, curr = 0; - u32 val[2]; - - do { - /* trigger PRN generation */ - writel(PPC4XX_PRNG_CTRL_AUTO_EN, - dev->ce_base + CRYPTO4XX_PRNG_CTRL); - - for (i = 0; i < 1024; i++) { - /* usually 19 iterations are enough */ - if ((readl(dev->ce_base + CRYPTO4XX_PRNG_STAT) & - CRYPTO4XX_PRNG_STAT_BUSY)) - continue; - - val[0] = readl_be(dev->ce_base + CRYPTO4XX_PRNG_RES_0); - val[1] = readl_be(dev->ce_base + CRYPTO4XX_PRNG_RES_1); - break; - } - if (i == 1024) - return -ETIMEDOUT; - - if ((max - curr) >= 8) { - memcpy(data, &val, 8); - data += 8; - curr += 8; - } else { - /* copy only remaining bytes */ - memcpy(data, &val, max - curr); - break; - } - } while (curr < max); - - return curr; -} - -static int crypto4xx_prng_generate(struct crypto_rng *tfm, - const u8 *src, unsigned int slen, - u8 *dstn, unsigned int dlen) -{ - struct rng_alg *alg = crypto_rng_alg(tfm); - struct crypto4xx_alg *amcc_alg; - struct crypto4xx_device *dev; - int ret; - - amcc_alg = container_of(alg, struct crypto4xx_alg, alg.u.rng); - dev = amcc_alg->dev; - - mutex_lock(&dev->core_dev->rng_lock); - ret = ppc4xx_prng_data_read(dev, dstn, dlen); - mutex_unlock(&dev->core_dev->rng_lock); - return ret; -} - - -static int crypto4xx_prng_seed(struct crypto_rng *tfm, const u8 *seed, - unsigned int slen) -{ - return 0; -} - /* * Supported Crypto Algorithms */ @@ -1268,18 +1195,6 @@ static struct crypto4xx_alg_common crypto4xx_alg[] = { .cra_module = THIS_MODULE, }, } }, - { .type = CRYPTO_ALG_TYPE_RNG, .u.rng = { - .base = { - .cra_name = "stdrng", - .cra_driver_name = "crypto4xx_rng", - .cra_priority = 300, - .cra_ctxsize = 0, - .cra_module = THIS_MODULE, - }, - .generate = crypto4xx_prng_generate, - .seed = crypto4xx_prng_seed, - .seedsize = 0, - } }, }; /* @@ -1353,9 +1268,6 @@ static int crypto4xx_probe(struct platform_device *ofdev) core_dev->dev->core_dev = core_dev; core_dev->dev->is_revb = is_revb; core_dev->device = dev; - rc = devm_mutex_init(&ofdev->dev, &core_dev->rng_lock); - if (rc) - return rc; spin_lock_init(&core_dev->lock); INIT_LIST_HEAD(&core_dev->dev->alg_list); ratelimit_default_init(&core_dev->dev->aead_ratelimit); diff --git a/drivers/crypto/amcc/crypto4xx_core.h b/drivers/crypto/amcc/crypto4xx_core.h index ee36630c670f2..3a028aec3f0c1 100644 --- a/drivers/crypto/amcc/crypto4xx_core.h +++ b/drivers/crypto/amcc/crypto4xx_core.h @@ -14,10 +14,8 @@ #define __CRYPTO4XX_CORE_H__ #include -#include #include #include -#include #include #include "crypto4xx_reg_def.h" #include "crypto4xx_sa.h" @@ -111,7 +109,6 @@ struct crypto4xx_core_device { u32 irq; struct tasklet_struct tasklet; spinlock_t lock; - struct mutex rng_lock; }; struct crypto4xx_ctx { @@ -135,7 +132,6 @@ struct crypto4xx_alg_common { union { struct skcipher_alg cipher; struct aead_alg aead; - struct rng_alg rng; } u; }; diff --git a/drivers/crypto/amcc/crypto4xx_reg_def.h b/drivers/crypto/amcc/crypto4xx_reg_def.h index 1038061224da6..73d626308a848 100644 --- a/drivers/crypto/amcc/crypto4xx_reg_def.h +++ b/drivers/crypto/amcc/crypto4xx_reg_def.h @@ -90,20 +90,9 @@ #define CRYPTO4XX_BYTE_ORDER_CFG 0x000600d8 #define CRYPTO4XX_ENDIAN_CFG 0x000600d8 -#define CRYPTO4XX_PRNG_STAT 0x00070000 -#define CRYPTO4XX_PRNG_STAT_BUSY 0x1 #define CRYPTO4XX_PRNG_CTRL 0x00070004 #define CRYPTO4XX_PRNG_SEED_L 0x00070008 #define CRYPTO4XX_PRNG_SEED_H 0x0007000c - -#define CRYPTO4XX_PRNG_RES_0 0x00070020 -#define CRYPTO4XX_PRNG_RES_1 0x00070024 -#define CRYPTO4XX_PRNG_RES_2 0x00070028 -#define CRYPTO4XX_PRNG_RES_3 0x0007002C - -#define CRYPTO4XX_PRNG_LFSR_L 0x00070030 -#define CRYPTO4XX_PRNG_LFSR_H 0x00070034 - /* * Initialize CRYPTO ENGINE registers, and memory bases. */ From ebaae7c4251cc0cdb2602f334d4f08a3e82d271e Mon Sep 17 00:00:00 2001 From: Anastasia Tishchenko Date: Wed, 13 May 2026 13:57:40 +0300 Subject: [PATCH 0514/2758] crypto: ecc - Fix carry overflow in vli multiplication commit 27b536a2ec8e2f85a0380c2d13c9ecbc7aaab406 upstream. The carry flag calculation fails when r01.m_high is saturated (0xFFFFFFFFFFFFFFFF) and addition of lower bits overflows. The condition (r01.m_high < product.m_high) doesn't handle the case where r01.m_high == product.m_high and an additional carry exists from lower-bit overflow. When commit 3c4b23901a0c ("crypto: ecdh - Add ECDH software support") introduced crypto/ecc.c, it split the muladd() function in the micro-ecc library into separate mul_64_64() and add_128_128() helpers. It seems the check got lost in translation. Add proper handling for this boundary by accounting for the carry from the lower addition. Fixes: 3c4b23901a0c ("crypto: ecdh - Add ECDH software support") Signed-off-by: Anastasia Tishchenko Cc: stable@vger.kernel.org # v4.8+ Reviewed-by: Lukas Wunner Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- crypto/ecc.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/crypto/ecc.c b/crypto/ecc.c index 43b0def3a225c..6eb4d97a5f0d3 100644 --- a/crypto/ecc.c +++ b/crypto/ecc.c @@ -393,14 +393,26 @@ static uint128_t mul_64_64(u64 left, u64 right) return result; } -static uint128_t add_128_128(uint128_t a, uint128_t b) +/* Calculate addition with overflow checking. Returns true on wrap-around, + * false otherwise. + */ +static bool check_add_128_128_overflow(uint128_t *result, uint128_t a, + uint128_t b) { - uint128_t result; + bool carry; - result.m_low = a.m_low + b.m_low; - result.m_high = a.m_high + b.m_high + (result.m_low < a.m_low); + result->m_low = a.m_low + b.m_low; + carry = (result->m_low < a.m_low); - return result; + result->m_high = a.m_high + b.m_high + carry; + + /* Using constant-time bitwise arithmetic to prevent timing + * side-channels. + */ + carry = (result->m_high < a.m_high) | + ((result->m_high == a.m_high) & carry); + + return carry; } static void vli_mult(u64 *result, const u64 *left, const u64 *right, @@ -425,9 +437,7 @@ static void vli_mult(u64 *result, const u64 *left, const u64 *right, uint128_t product; product = mul_64_64(left[i], right[k - i]); - - r01 = add_128_128(r01, product); - r2 += (r01.m_high < product.m_high); + r2 += check_add_128_128_overflow(&r01, r01, product); } result[k] = r01.m_low; @@ -450,7 +460,7 @@ static void vli_umult(u64 *result, const u64 *left, u32 right, uint128_t product; product = mul_64_64(left[k], right); - r01 = add_128_128(r01, product); + check_add_128_128_overflow(&r01, r01, product); /* no carry */ result[k] = r01.m_low; r01.m_low = r01.m_high; @@ -487,8 +497,7 @@ static void vli_square(u64 *result, const u64 *left, unsigned int ndigits) product.m_low <<= 1; } - r01 = add_128_128(r01, product); - r2 += (r01.m_high < product.m_high); + r2 += check_add_128_128_overflow(&r01, r01, product); } result[k] = r01.m_low; From 98a771d340bd9c35cf11c8d917b3ee59fac1abc9 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 30 May 2026 13:26:23 -0700 Subject: [PATCH 0515/2758] crypto: hisi-trng - Remove crypto_rng interface commit 216a7795ec210bdabd5dad42323eee70bbfc8d90 upstream. drivers/crypto/hisilicon/trng/trng.c exposes the same hardware through two completely separate interfaces, crypto_rng and hwrng. However, the implementation of this is buggy because it permits generation operations from these interfaces to run concurrently with each other, accessing the same registers. That is, hisi_trng_generate() synchronizes with itself but not with hisi_trng_read(). This results in potential repetition of output from the RNG, output of non-random values, etc. Fortunately, there's actually no point in hardware RNG drivers implementing the crypto_rng interface. It's not actually used by anything besides the "rng" algorithm type of AF_ALG, which in turn is not actually used in practice. Other crypto_rng hardware drivers are likewise being phased out, leaving just the hwrng support. Thus, remove it to simplify the code and avoid conflict (and confusion) with the hwrng interface which is the one that actually matters. Fixes: e4d9d10ef4be ("crypto: hisilicon/trng - add support for PRNG") Cc: stable@vger.kernel.org Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/hisilicon/Kconfig | 1 - drivers/crypto/hisilicon/trng/trng.c | 296 +-------------------------- 2 files changed, 2 insertions(+), 295 deletions(-) diff --git a/drivers/crypto/hisilicon/Kconfig b/drivers/crypto/hisilicon/Kconfig index 1e6d772f4bb65..8aa23c9397759 100644 --- a/drivers/crypto/hisilicon/Kconfig +++ b/drivers/crypto/hisilicon/Kconfig @@ -80,6 +80,5 @@ config CRYPTO_DEV_HISI_TRNG tristate "Support for HISI TRNG Driver" depends on ARM64 && ACPI select HW_RANDOM - select CRYPTO_RNG help Support for HiSilicon TRNG Driver. diff --git a/drivers/crypto/hisilicon/trng/trng.c b/drivers/crypto/hisilicon/trng/trng.c index 5ca0b90859a81..6584ed051e099 100644 --- a/drivers/crypto/hisilicon/trng/trng.c +++ b/drivers/crypto/hisilicon/trng/trng.c @@ -1,234 +1,27 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2019 HiSilicon Limited. */ -#include #include -#include #include #include #include #include #include -#include #include -#include #include #include #define HISI_TRNG_REG 0x00F0 #define HISI_TRNG_BYTES 4 #define HISI_TRNG_QUALITY 512 -#define HISI_TRNG_VERSION 0x01B8 -#define HISI_TRNG_VER_V1 GENMASK(31, 0) #define SLEEP_US 10 #define TIMEOUT_US 10000 -#define SW_DRBG_NUM_SHIFT 2 -#define SW_DRBG_KEY_BASE 0x082C -#define SW_DRBG_SEED(n) (SW_DRBG_KEY_BASE - ((n) << SW_DRBG_NUM_SHIFT)) -#define SW_DRBG_SEED_REGS_NUM 12 -#define SW_DRBG_SEED_SIZE 48 -#define SW_DRBG_BLOCKS 0x0830 -#define SW_DRBG_INIT 0x0834 -#define SW_DRBG_GEN 0x083c -#define SW_DRBG_STATUS 0x0840 -#define SW_DRBG_BLOCKS_NUM 4095 -#define SW_DRBG_DATA_BASE 0x0850 -#define SW_DRBG_DATA_NUM 4 -#define SW_DRBG_DATA(n) (SW_DRBG_DATA_BASE - ((n) << SW_DRBG_NUM_SHIFT)) -#define SW_DRBG_BYTES 16 -#define SW_DRBG_ENABLE_SHIFT 12 -#define SEED_SHIFT_24 24 -#define SEED_SHIFT_16 16 -#define SEED_SHIFT_8 8 -#define SW_MAX_RANDOM_BYTES 65520 - -struct hisi_trng_list { - struct mutex lock; - struct list_head list; - bool is_init; -}; struct hisi_trng { void __iomem *base; - struct hisi_trng_list *trng_list; - struct list_head list; struct hwrng rng; - u32 ver; - u32 ctx_num; - /* The bytes of the random number generated since the last seeding. */ - u32 random_bytes; - struct mutex lock; -}; - -struct hisi_trng_ctx { - struct hisi_trng *trng; }; -static atomic_t trng_active_devs; -static struct hisi_trng_list trng_devices; -static int hisi_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait); - -static int hisi_trng_set_seed(struct hisi_trng *trng, const u8 *seed) -{ - u32 val, seed_reg, i; - int ret; - - writel(0x0, trng->base + SW_DRBG_BLOCKS); - - for (i = 0; i < SW_DRBG_SEED_SIZE; - i += SW_DRBG_SEED_SIZE / SW_DRBG_SEED_REGS_NUM) { - val = seed[i] << SEED_SHIFT_24; - val |= seed[i + 1UL] << SEED_SHIFT_16; - val |= seed[i + 2UL] << SEED_SHIFT_8; - val |= seed[i + 3UL]; - - seed_reg = (i >> SW_DRBG_NUM_SHIFT) % SW_DRBG_SEED_REGS_NUM; - writel(val, trng->base + SW_DRBG_SEED(seed_reg)); - } - - writel(SW_DRBG_BLOCKS_NUM | (0x1 << SW_DRBG_ENABLE_SHIFT), - trng->base + SW_DRBG_BLOCKS); - writel(0x1, trng->base + SW_DRBG_INIT); - ret = readl_relaxed_poll_timeout(trng->base + SW_DRBG_STATUS, - val, val & BIT(0), SLEEP_US, TIMEOUT_US); - if (ret) { - pr_err("failed to init trng(%d)\n", ret); - return -EIO; - } - - trng->random_bytes = 0; - - return 0; -} - -static int hisi_trng_seed(struct crypto_rng *tfm, const u8 *seed, - unsigned int slen) -{ - struct hisi_trng_ctx *ctx = crypto_rng_ctx(tfm); - struct hisi_trng *trng = ctx->trng; - int ret; - - if (slen < SW_DRBG_SEED_SIZE) { - pr_err("slen(%u) is not matched with trng(%d)\n", slen, - SW_DRBG_SEED_SIZE); - return -EINVAL; - } - - mutex_lock(&trng->lock); - ret = hisi_trng_set_seed(trng, seed); - mutex_unlock(&trng->lock); - - return ret; -} - -static int hisi_trng_reseed(struct hisi_trng *trng) -{ - u8 seed[SW_DRBG_SEED_SIZE]; - int size; - - if (!trng->random_bytes) - return 0; - - size = hisi_trng_read(&trng->rng, seed, SW_DRBG_SEED_SIZE, false); - if (size != SW_DRBG_SEED_SIZE) - return -EIO; - - return hisi_trng_set_seed(trng, seed); -} - -static int hisi_trng_get_bytes(struct hisi_trng *trng, u8 *dstn, unsigned int dlen) -{ - u32 data[SW_DRBG_DATA_NUM]; - u32 currsize = 0; - u32 val = 0; - int ret; - u32 i; - - ret = hisi_trng_reseed(trng); - if (ret) - return ret; - - do { - ret = readl_relaxed_poll_timeout(trng->base + SW_DRBG_STATUS, - val, val & BIT(1), SLEEP_US, TIMEOUT_US); - if (ret) { - pr_err("failed to generate random number(%d)!\n", ret); - break; - } - - for (i = 0; i < SW_DRBG_DATA_NUM; i++) - data[i] = readl(trng->base + SW_DRBG_DATA(i)); - - if (dlen - currsize >= SW_DRBG_BYTES) { - memcpy(dstn + currsize, data, SW_DRBG_BYTES); - currsize += SW_DRBG_BYTES; - } else { - memcpy(dstn + currsize, data, dlen - currsize); - currsize = dlen; - } - - trng->random_bytes += SW_DRBG_BYTES; - writel(0x1, trng->base + SW_DRBG_GEN); - } while (currsize < dlen); - - return ret; -} - -static int hisi_trng_generate(struct crypto_rng *tfm, const u8 *src, - unsigned int slen, u8 *dstn, unsigned int dlen) -{ - struct hisi_trng_ctx *ctx = crypto_rng_ctx(tfm); - struct hisi_trng *trng = ctx->trng; - unsigned int currsize = 0; - unsigned int block_size; - int ret; - - if (!dstn || !dlen) { - pr_err("output is error, dlen %u!\n", dlen); - return -EINVAL; - } - - do { - block_size = min_t(unsigned int, dlen - currsize, SW_MAX_RANDOM_BYTES); - mutex_lock(&trng->lock); - ret = hisi_trng_get_bytes(trng, dstn + currsize, block_size); - mutex_unlock(&trng->lock); - if (ret) - return ret; - currsize += block_size; - } while (currsize < dlen); - - return 0; -} - -static int hisi_trng_init(struct crypto_tfm *tfm) -{ - struct hisi_trng_ctx *ctx = crypto_tfm_ctx(tfm); - struct hisi_trng *trng; - u32 ctx_num = ~0; - - mutex_lock(&trng_devices.lock); - list_for_each_entry(trng, &trng_devices.list, list) { - if (trng->ctx_num < ctx_num) { - ctx_num = trng->ctx_num; - ctx->trng = trng; - } - } - ctx->trng->ctx_num++; - mutex_unlock(&trng_devices.lock); - - return 0; -} - -static void hisi_trng_exit(struct crypto_tfm *tfm) -{ - struct hisi_trng_ctx *ctx = crypto_tfm_ctx(tfm); - - mutex_lock(&trng_devices.lock); - ctx->trng->ctx_num--; - mutex_unlock(&trng_devices.lock); -} - static int hisi_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait) { struct hisi_trng *trng; @@ -260,42 +53,6 @@ static int hisi_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait) return currsize; } -static struct rng_alg hisi_trng_alg = { - .generate = hisi_trng_generate, - .seed = hisi_trng_seed, - .seedsize = SW_DRBG_SEED_SIZE, - .base = { - .cra_name = "stdrng", - .cra_driver_name = "hisi_stdrng", - .cra_priority = 300, - .cra_ctxsize = sizeof(struct hisi_trng_ctx), - .cra_module = THIS_MODULE, - .cra_init = hisi_trng_init, - .cra_exit = hisi_trng_exit, - }, -}; - -static void hisi_trng_add_to_list(struct hisi_trng *trng) -{ - mutex_lock(&trng_devices.lock); - list_add_tail(&trng->list, &trng_devices.list); - mutex_unlock(&trng_devices.lock); -} - -static int hisi_trng_del_from_list(struct hisi_trng *trng) -{ - int ret = -EBUSY; - - mutex_lock(&trng_devices.lock); - if (!trng->ctx_num) { - list_del(&trng->list); - ret = 0; - } - mutex_unlock(&trng_devices.lock); - - return ret; -} - static int hisi_trng_probe(struct platform_device *pdev) { struct hisi_trng *trng; @@ -305,68 +62,20 @@ static int hisi_trng_probe(struct platform_device *pdev) if (!trng) return -ENOMEM; - platform_set_drvdata(pdev, trng); - trng->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(trng->base)) return PTR_ERR(trng->base); - trng->ctx_num = 0; - trng->random_bytes = SW_MAX_RANDOM_BYTES; - mutex_init(&trng->lock); - trng->ver = readl(trng->base + HISI_TRNG_VERSION); - if (!trng_devices.is_init) { - INIT_LIST_HEAD(&trng_devices.list); - mutex_init(&trng_devices.lock); - trng_devices.is_init = true; - } - - hisi_trng_add_to_list(trng); - if (trng->ver != HISI_TRNG_VER_V1 && - atomic_inc_return(&trng_active_devs) == 1) { - ret = crypto_register_rng(&hisi_trng_alg); - if (ret) { - dev_err(&pdev->dev, - "failed to register crypto(%d)\n", ret); - atomic_dec_return(&trng_active_devs); - goto err_remove_from_list; - } - } - trng->rng.name = pdev->name; trng->rng.read = hisi_trng_read; trng->rng.quality = HISI_TRNG_QUALITY; + ret = devm_hwrng_register(&pdev->dev, &trng->rng); - if (ret) { + if (ret) dev_err(&pdev->dev, "failed to register hwrng: %d!\n", ret); - goto err_crypto_unregister; - } - - return ret; - -err_crypto_unregister: - if (trng->ver != HISI_TRNG_VER_V1 && - atomic_dec_return(&trng_active_devs) == 0) - crypto_unregister_rng(&hisi_trng_alg); - -err_remove_from_list: - hisi_trng_del_from_list(trng); return ret; } -static void hisi_trng_remove(struct platform_device *pdev) -{ - struct hisi_trng *trng = platform_get_drvdata(pdev); - - /* Wait until the task is finished */ - while (hisi_trng_del_from_list(trng)) - ; - - if (trng->ver != HISI_TRNG_VER_V1 && - atomic_dec_return(&trng_active_devs) == 0) - crypto_unregister_rng(&hisi_trng_alg); -} - static const struct acpi_device_id hisi_trng_acpi_match[] = { { "HISI02B3", 0 }, { } @@ -375,7 +84,6 @@ MODULE_DEVICE_TABLE(acpi, hisi_trng_acpi_match); static struct platform_driver hisi_trng_driver = { .probe = hisi_trng_probe, - .remove = hisi_trng_remove, .driver = { .name = "hisi-trng-v2", .acpi_match_table = ACPI_PTR(hisi_trng_acpi_match), From 83fa1397d5853de1e27dd52ec44b068ff358ca18 Mon Sep 17 00:00:00 2001 From: Ruijie Li Date: Mon, 25 May 2026 19:45:21 +0800 Subject: [PATCH 0516/2758] crypto: pcrypt - restore callback for non-parallel fallback commit ed459fe319376e876de433d12b6c6772e612ca36 upstream. pcrypt installs pcrypt_aead_done() on the child AEAD request before trying to submit it through padata. If padata_do_parallel() returns -EBUSY, pcrypt falls back to calling the child AEAD directly. That fallback must not keep the padata completion callback. Otherwise an asynchronous completion runs pcrypt_aead_done() even though the request was never enrolled in padata. Restore the original request callback and callback data before calling the child AEAD directly. This keeps the fallback path aligned with a direct AEAD request while leaving the parallel path unchanged. Fixes: 662f2f13e66d ("crypto: pcrypt - Call crypto layer directly when padata_do_parallel() return -EBUSY") Cc: stable@kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:gpt-5.4 Signed-off-by: Ruijie Li Signed-off-by: Ren Wei Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- crypto/pcrypt.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crypto/pcrypt.c b/crypto/pcrypt.c index ed0feaba23832..9f372442981e6 100644 --- a/crypto/pcrypt.c +++ b/crypto/pcrypt.c @@ -122,6 +122,8 @@ static int pcrypt_aead_encrypt(struct aead_request *req) return -EINPROGRESS; if (err == -EBUSY) { /* try non-parallel mode */ + aead_request_set_callback(creq, flags, req->base.complete, + req->base.data); return crypto_aead_encrypt(creq); } @@ -173,6 +175,8 @@ static int pcrypt_aead_decrypt(struct aead_request *req) return -EINPROGRESS; if (err == -EBUSY) { /* try non-parallel mode */ + aead_request_set_callback(creq, flags, req->base.complete, + req->base.data); return crypto_aead_decrypt(creq); } From 855240d4d24387fa21d3dd6f4c8b36e74d983add Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Thu, 4 Jun 2026 10:27:06 +0000 Subject: [PATCH 0517/2758] crypto: tegra - fix refcount leak in tegra_se_host1x_submit() commit 6ea0ce3a19f9c37a014099e2b0a46b27fa164564 upstream. The timeout error path in tegra_se_host1x_submit() returns without calling host1x_job_put(), while all other paths (success, submit error, pin error) properly release the job reference through the job_put label. Since host1x_job_alloc() initializes the reference count and host1x_job_put() is required to drop it, omitting it on timeout causes a permanent refcount leak. Fix this by redirecting the timeout return to the existing job_put label, ensuring the job reference and any associated syncpt references are consistently released. Cc: stable@vger.kernel.org Fixes: 0880bb3b00c8 ("crypto: tegra - Add Tegra Security Engine driver") Signed-off-by: Wentao Liang Reviewed-by: Akhil R Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/tegra/tegra-se-main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/crypto/tegra/tegra-se-main.c b/drivers/crypto/tegra/tegra-se-main.c index eb71113ed1469..e8d8c3a23d7ad 100644 --- a/drivers/crypto/tegra/tegra-se-main.c +++ b/drivers/crypto/tegra/tegra-se-main.c @@ -180,7 +180,7 @@ int tegra_se_host1x_submit(struct tegra_se *se, struct tegra_se_cmdbuf *cmdbuf, MAX_SCHEDULE_TIMEOUT, NULL); if (ret) { dev_err(se->dev, "host1x job timed out\n"); - return ret; + goto job_put; } host1x_job_put(job); From 0927083d5e3e4566a6dee889ec2ae8f6cadd0918 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 21 May 2026 21:25:25 -0500 Subject: [PATCH 0518/2758] crypto: loongson - Select CRYPTO_RNG commit 4c600ab0d8cfc9d75b92f3426dbcb2ad85eac91d upstream. This driver registers a rng_alg, so it requires CRYPTO_RNG. Fixes: 766b2d724c8d ("crypto: loongson - add Loongson RNG driver support") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202605201622.qWOiiZTV-lkp@intel.com/ Cc: stable@vger.kernel.org Signed-off-by: Eric Biggers Reviewed-by: Huacai Chen Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/loongson/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/crypto/loongson/Kconfig b/drivers/crypto/loongson/Kconfig index 15475da8fc11d..f4e1544ffbb49 100644 --- a/drivers/crypto/loongson/Kconfig +++ b/drivers/crypto/loongson/Kconfig @@ -1,5 +1,6 @@ config CRYPTO_DEV_LOONGSON_RNG tristate "Support for Loongson RNG Driver" depends on MFD_LOONGSON_SE + select CRYPTO_RNG help Support for Loongson RNG Driver. From 43de8b9f01b7dd2f6ca5360c6bf2f203c02288dc Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 29 May 2026 16:32:08 -0700 Subject: [PATCH 0519/2758] crypto: loongson - Remove broken and unused loongson-rng commit af3d1bb9a09daf928fc3f173689fb7904d6a6d4f upstream. The loongson-rng rng_alg has several vulnerabilities, including not providing forward security, and a use-after-free bug due to the use of wait_for_completion_interruptible(). Meanwhile, the rng_alg framework doesn't really have any purpose in the first place other than to access the software algorithms crypto/drbg.c and crypto/jitterentropy.c. Hardware-specific rng_algs have no in-kernel user, and unlike hwrng there's no feed into the actual Linux RNG. As such, there's really no point to this code. There are of course other rng_alg drivers that are similarly unused, but they're similarly in the process of being phased out, e.g. https://lore.kernel.org/r/20260529193648.18172-1-ebiggers@kernel.org and https://lore.kernel.org/r/20260529220430.34135-1-ebiggers@kernel.org Given that, there's no point in fixing forward these vulnerabilities, and it makes much more sense to simply roll back the addition of this driver. If this platform provides TRNG (not PRNG) functionality, it could make sense to add a hwrng driver, but it would be quite different. Link: https://lore.kernel.org/linux-crypto/20260525145939.GC2018@quark/ Fixes: 766b2d724c8d ("crypto: loongson - add Loongson RNG driver support") Cc: stable@vger.kernel.org Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 - arch/loongarch/configs/loongson32_defconfig | 1 - arch/loongarch/configs/loongson64_defconfig | 1 - drivers/crypto/Kconfig | 1 - drivers/crypto/Makefile | 1 - drivers/crypto/loongson/Kconfig | 6 - drivers/crypto/loongson/Makefile | 1 - drivers/crypto/loongson/loongson-rng.c | 209 -------------------- 8 files changed, 221 deletions(-) delete mode 100644 drivers/crypto/loongson/Kconfig delete mode 100644 drivers/crypto/loongson/Makefile delete mode 100644 drivers/crypto/loongson/loongson-rng.c diff --git a/MAINTAINERS b/MAINTAINERS index c8d4b913f26c1..f74da642290aa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15087,7 +15087,6 @@ M: Qunqin Zhao L: linux-crypto@vger.kernel.org S: Maintained F: drivers/char/tpm/tpm_loongson.c -F: drivers/crypto/loongson/ F: drivers/mfd/loongson-se.c F: include/linux/mfd/loongson-se.h diff --git a/arch/loongarch/configs/loongson32_defconfig b/arch/loongarch/configs/loongson32_defconfig index d5ef396dffe30..82897236863fb 100644 --- a/arch/loongarch/configs/loongson32_defconfig +++ b/arch/loongarch/configs/loongson32_defconfig @@ -1091,7 +1091,6 @@ CONFIG_CRYPTO_USER_API_SKCIPHER=m CONFIG_CRYPTO_USER_API_RNG=m CONFIG_CRYPTO_USER_API_AEAD=m CONFIG_CRYPTO_DEV_VIRTIO=m -CONFIG_CRYPTO_DEV_LOONGSON_RNG=m CONFIG_DMA_CMA=y CONFIG_CMA_SIZE_MBYTES=0 CONFIG_PRINTK_TIME=y diff --git a/arch/loongarch/configs/loongson64_defconfig b/arch/loongarch/configs/loongson64_defconfig index cba4cdff5acd8..a94e88bd7ec5c 100644 --- a/arch/loongarch/configs/loongson64_defconfig +++ b/arch/loongarch/configs/loongson64_defconfig @@ -1124,7 +1124,6 @@ CONFIG_CRYPTO_USER_API_SKCIPHER=m CONFIG_CRYPTO_USER_API_RNG=m CONFIG_CRYPTO_USER_API_AEAD=m CONFIG_CRYPTO_DEV_VIRTIO=m -CONFIG_CRYPTO_DEV_LOONGSON_RNG=m CONFIG_DMA_CMA=y CONFIG_DMA_NUMA_CMA=y CONFIG_CMA_SIZE_MBYTES=0 diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index ce1c0bc3efaaa..0712e9bf6e54b 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -845,7 +845,6 @@ config CRYPTO_DEV_CCREE If unsure say Y. source "drivers/crypto/hisilicon/Kconfig" -source "drivers/crypto/loongson/Kconfig" source "drivers/crypto/amlogic/Kconfig" diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile index 283bbc650b5b2..ad773158ae560 100644 --- a/drivers/crypto/Makefile +++ b/drivers/crypto/Makefile @@ -43,7 +43,6 @@ obj-y += inside-secure/ obj-$(CONFIG_CRYPTO_DEV_ARTPEC6) += axis/ obj-y += xilinx/ obj-y += hisilicon/ -obj-y += loongson/ obj-$(CONFIG_CRYPTO_DEV_AMLOGIC_GXL) += amlogic/ obj-y += intel/ obj-y += starfive/ diff --git a/drivers/crypto/loongson/Kconfig b/drivers/crypto/loongson/Kconfig deleted file mode 100644 index f4e1544ffbb49..0000000000000 --- a/drivers/crypto/loongson/Kconfig +++ /dev/null @@ -1,6 +0,0 @@ -config CRYPTO_DEV_LOONGSON_RNG - tristate "Support for Loongson RNG Driver" - depends on MFD_LOONGSON_SE - select CRYPTO_RNG - help - Support for Loongson RNG Driver. diff --git a/drivers/crypto/loongson/Makefile b/drivers/crypto/loongson/Makefile deleted file mode 100644 index 1ce5ec32b5534..0000000000000 --- a/drivers/crypto/loongson/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_CRYPTO_DEV_LOONGSON_RNG) += loongson-rng.o diff --git a/drivers/crypto/loongson/loongson-rng.c b/drivers/crypto/loongson/loongson-rng.c deleted file mode 100644 index 3a4940260f9e5..0000000000000 --- a/drivers/crypto/loongson/loongson-rng.c +++ /dev/null @@ -1,209 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Copyright (c) 2019 HiSilicon Limited. */ -/* Copyright (c) 2025 Loongson Technology Corporation Limited. */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define SE_SEED_SIZE 32 - -struct loongson_rng_list { - struct mutex lock; - struct list_head list; - int registered; -}; - -struct loongson_rng { - u32 used; - struct loongson_se_engine *engine; - struct list_head list; - struct mutex lock; -}; - -struct loongson_rng_ctx { - struct loongson_rng *rng; -}; - -struct loongson_rng_cmd { - u32 cmd_id; - union { - u32 len; - u32 ret; - } u; - u32 seed_off; - u32 out_off; - u32 pad[4]; -}; - -static struct loongson_rng_list rng_devices = { - .lock = __MUTEX_INITIALIZER(rng_devices.lock), - .list = LIST_HEAD_INIT(rng_devices.list), -}; - -static int loongson_rng_generate(struct crypto_rng *tfm, const u8 *src, - unsigned int slen, u8 *dstn, unsigned int dlen) -{ - struct loongson_rng_ctx *ctx = crypto_rng_ctx(tfm); - struct loongson_rng *rng = ctx->rng; - struct loongson_rng_cmd *cmd = rng->engine->command; - int err, len; - - mutex_lock(&rng->lock); - cmd->seed_off = 0; - do { - len = min(dlen, rng->engine->buffer_size); - cmd = rng->engine->command; - cmd->u.len = len; - err = loongson_se_send_engine_cmd(rng->engine); - if (err) - break; - - cmd = rng->engine->command_ret; - if (cmd->u.ret) { - err = -EIO; - break; - } - - memcpy(dstn, rng->engine->data_buffer, len); - dlen -= len; - dstn += len; - } while (dlen > 0); - mutex_unlock(&rng->lock); - - return err; -} - -static int loongson_rng_init(struct crypto_tfm *tfm) -{ - struct loongson_rng_ctx *ctx = crypto_tfm_ctx(tfm); - struct loongson_rng *rng; - u32 min_used = U32_MAX; - - mutex_lock(&rng_devices.lock); - list_for_each_entry(rng, &rng_devices.list, list) { - if (rng->used < min_used) { - ctx->rng = rng; - min_used = rng->used; - } - } - ctx->rng->used++; - mutex_unlock(&rng_devices.lock); - - return 0; -} - -static void loongson_rng_exit(struct crypto_tfm *tfm) -{ - struct loongson_rng_ctx *ctx = crypto_tfm_ctx(tfm); - - mutex_lock(&rng_devices.lock); - ctx->rng->used--; - mutex_unlock(&rng_devices.lock); -} - -static int loongson_rng_seed(struct crypto_rng *tfm, const u8 *seed, - unsigned int slen) -{ - struct loongson_rng_ctx *ctx = crypto_rng_ctx(tfm); - struct loongson_rng *rng = ctx->rng; - struct loongson_rng_cmd *cmd; - int err; - - if (slen < SE_SEED_SIZE) - return -EINVAL; - - slen = min(slen, rng->engine->buffer_size); - - mutex_lock(&rng->lock); - cmd = rng->engine->command; - cmd->u.len = slen; - cmd->seed_off = rng->engine->buffer_off; - memcpy(rng->engine->data_buffer, seed, slen); - err = loongson_se_send_engine_cmd(rng->engine); - if (err) - goto out; - - cmd = rng->engine->command_ret; - if (cmd->u.ret) - err = -EIO; -out: - mutex_unlock(&rng->lock); - - return err; -} - -static struct rng_alg loongson_rng_alg = { - .generate = loongson_rng_generate, - .seed = loongson_rng_seed, - .seedsize = SE_SEED_SIZE, - .base = { - .cra_name = "stdrng", - .cra_driver_name = "loongson_stdrng", - .cra_priority = 300, - .cra_ctxsize = sizeof(struct loongson_rng_ctx), - .cra_module = THIS_MODULE, - .cra_init = loongson_rng_init, - .cra_exit = loongson_rng_exit, - }, -}; - -static int loongson_rng_probe(struct platform_device *pdev) -{ - struct loongson_rng_cmd *cmd; - struct loongson_rng *rng; - int ret = 0; - - rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL); - if (!rng) - return -ENOMEM; - - rng->engine = loongson_se_init_engine(pdev->dev.parent, SE_ENGINE_RNG); - if (!rng->engine) - return -ENODEV; - cmd = rng->engine->command; - cmd->cmd_id = SE_CMD_RNG; - cmd->out_off = rng->engine->buffer_off; - mutex_init(&rng->lock); - - mutex_lock(&rng_devices.lock); - - if (!rng_devices.registered) { - ret = crypto_register_rng(&loongson_rng_alg); - if (ret) { - dev_err(&pdev->dev, "failed to register crypto(%d)\n", ret); - goto out; - } - rng_devices.registered = 1; - } - - list_add_tail(&rng->list, &rng_devices.list); -out: - mutex_unlock(&rng_devices.lock); - - return ret; -} - -static struct platform_driver loongson_rng_driver = { - .probe = loongson_rng_probe, - .driver = { - .name = "loongson-rng", - }, -}; -module_platform_driver(loongson_rng_driver); - -MODULE_ALIAS("platform:loongson-rng"); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Yinggang Gu "); -MODULE_AUTHOR("Qunqin Zhao "); -MODULE_DESCRIPTION("Loongson Random Number Generator driver"); From d51207735e7c224cf591fa260c557a451a69a5cf Mon Sep 17 00:00:00 2001 From: "Tycho Andersen (AMD)" Date: Mon, 4 May 2026 10:51:44 -0600 Subject: [PATCH 0520/2758] crypto: ccp - Do not initialize SNP for SEV ioctls commit fb1758e74b8061aacfbce7bbb7a7cc650537e167 upstream. Sashiko notes: > if SEV initialization fails and KVM is actively running normal VMs, could a > userspace process trigger this code path via /dev/sev ioctls (e.g., > SEV_PDH_GEN) and zero out MSR_VM_HSAVE_PA globally? Would the next VMRUN > execution for an active VM trigger a general protection fault and crash the > host? sev_move_to_init_state() is called for ioctls requiring only SEV firmware: SEV_PEK_GEN, SEV_PDH_GEN, SEV_PEK_CSR, SEV_PEK_CERT_IMPORT, and SEV_PDH_CERT_EXPORT. After the firmware command, it does SEV_SHUTDOWN on the SEV firmware. Since these commands do not require SNP to be initialized, skip it by calling __sev_platform_init_locked() which only initializes the SEV firmware. This way SNP is not Initialized at all, and HSAVE_PA is not cleared. The previous code saved any SEV initialization firmware error to init_args.error and then threw it away and hardcoded the return value of INVALID_PLATFORM_STATE regardless of the real firmware error. This patch changes it to surface the underlying error, which is hopefully both more useful and doesn't cause any problems. Note that it is still safe to call __sev_firmware_shutdown() directly: it calls __sev_snp_shutdown_locked(), which skips SNP shutdown if SNP was not initialized. Fixes: ceac7fb89e8d ("crypto: ccp - Ensure implicit SEV/SNP init and shutdown in ioctls") Reported-by: Sashiko Assisted-by: Gemini:gemini-3.1-pro-preview Link: https://sashiko.dev/#/patchset/20260324161301.1353976-1-tycho%40kernel.org CC: Signed-off-by: Tycho Andersen (AMD) Reviewed-by: Tom Lendacky Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/ccp/sev-dev.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index d1e9e0ac63b60..6891b90bbb88f 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -1716,14 +1716,11 @@ static int sev_get_platform_state(int *state, int *error) static int sev_move_to_init_state(struct sev_issue_cmd *argp, bool *shutdown_required) { - struct sev_platform_init_args init_args = {0}; int rc; - rc = _sev_platform_init_locked(&init_args); - if (rc) { - argp->error = SEV_RET_INVALID_PLATFORM_STATE; + rc = __sev_platform_init_locked(&argp->error); + if (rc) return rc; - } *shutdown_required = true; From 67ed191b4c8bdf432a3f32d1eb302880b4795cd1 Mon Sep 17 00:00:00 2001 From: "Tycho Andersen (AMD)" Date: Mon, 4 May 2026 10:51:45 -0600 Subject: [PATCH 0521/2758] crypto: ccp - Do not initialize SNP for ioctl(SNP_COMMIT) commit 5a1364da2f04217a36e2fdfa2db4ee025b383a20 upstream. Sashiko notes: > if SEV initialization fails and KVM is actively running normal VMs, could a > userspace process trigger this code path via /dev/sev ioctls (e.g., > SEV_PDH_GEN) and zero out MSR_VM_HSAVE_PA globally? Would the next VMRUN > execution for an active VM trigger a general protection fault and crash the > host? The SNP_COMMIT command does not require the firmware to be in any particular state. Skip initializing it if it was previously uninitialized. The SEV-SNP firmware specification doc 56860 does not mention SNP_COMMIT in Table 5 as a command that is allowed in the UNINIT state, but it is in fact allowed and a future documentation update will reflect that. Fixes: ceac7fb89e8d ("crypto: ccp - Ensure implicit SEV/SNP init and shutdown in ioctls") Reported-by: Sashiko Assisted-by: Gemini:gemini-3.1-pro-preview Link: https://sashiko.dev/#/patchset/20260324161301.1353976-1-tycho%40kernel.org CC: Signed-off-by: Tycho Andersen (AMD) Reviewed-by: Tom Lendacky Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/ccp/sev-dev.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index 6891b90bbb88f..572f06368d4b6 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -2437,24 +2437,13 @@ static int sev_ioctl_do_snp_platform_status(struct sev_issue_cmd *argp) static int sev_ioctl_do_snp_commit(struct sev_issue_cmd *argp) { - struct sev_device *sev = psp_master->sev_data; struct sev_data_snp_commit buf; - bool shutdown_required = false; - int ret, error; - - if (!sev->snp_initialized) { - ret = snp_move_to_init_state(argp, &shutdown_required); - if (ret) - return ret; - } + int ret; buf.len = sizeof(buf); ret = __sev_do_cmd_locked(SEV_CMD_SNP_COMMIT, &buf, &argp->error); - if (shutdown_required) - __sev_snp_shutdown_locked(&error, false); - return ret; } From 8836801847b9479ac046cb18a24981e1b0b05e9d Mon Sep 17 00:00:00 2001 From: "Tycho Andersen (AMD)" Date: Mon, 4 May 2026 10:51:46 -0600 Subject: [PATCH 0522/2758] crypto: ccp - Do not initialize SNP for ioctl(SNP_VLEK_LOAD) commit f91e9dbb5845d1e5abf1028e6df57dcf61583e1b upstream. Sashiko notes: > if SEV initialization fails and KVM is actively running normal VMs, could a > userspace process trigger this code path via /dev/sev ioctls (e.g., > SEV_PDH_GEN) and zero out MSR_VM_HSAVE_PA globally? Would the next VMRUN > execution for an active VM trigger a general protection fault and crash the > host? The SEV firmware docs for SNP_VLEK_LOAD note: > On SNP_SHUTDOWN, the VLEK is deleted. That is, the initialization/shutdown wrapper here is pointless, because the firmware immediately throws away the key anyway. Instead, refuse to do anything if SNP has not been previously initialized. This is an ABI break: before, this was a no-op and almost certainly a mistake by userspace, and now it returns -ENODEV. ABI compatibility could be maintained here by simply returning 0 in the check instead. Fixes: ceac7fb89e8d ("crypto: ccp - Ensure implicit SEV/SNP init and shutdown in ioctls") Reported-by: Sashiko Assisted-by: Gemini:gemini-3.1-pro-preview Link: https://sashiko.dev/#/patchset/20260324161301.1353976-1-tycho%40kernel.org CC: Signed-off-by: Tycho Andersen (AMD) Reviewed-by: Tom Lendacky Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/ccp/sev-dev.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index 572f06368d4b6..ad6c2525a3057 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -2481,9 +2481,8 @@ static int sev_ioctl_do_snp_vlek_load(struct sev_issue_cmd *argp, bool writable) { struct sev_device *sev = psp_master->sev_data; struct sev_user_data_snp_vlek_load input; - bool shutdown_required = false; - int ret, error; void *blob; + int ret; if (!argp->data) return -EINVAL; @@ -2491,6 +2490,9 @@ static int sev_ioctl_do_snp_vlek_load(struct sev_issue_cmd *argp, bool writable) if (!writable) return -EPERM; + if (!sev->snp_initialized) + return -ENODEV; + if (copy_from_user(&input, u64_to_user_ptr(argp->data), sizeof(input))) return -EFAULT; @@ -2504,18 +2506,7 @@ static int sev_ioctl_do_snp_vlek_load(struct sev_issue_cmd *argp, bool writable) input.vlek_wrapped_address = __psp_pa(blob); - if (!sev->snp_initialized) { - ret = snp_move_to_init_state(argp, &shutdown_required); - if (ret) - goto cleanup; - } - ret = __sev_do_cmd_locked(SEV_CMD_SNP_VLEK_LOAD, &input, &argp->error); - - if (shutdown_required) - __sev_snp_shutdown_locked(&error, false); - -cleanup: kfree(blob); return ret; From 20f548cdac94860a164e5ebba4f7e4a01051cb06 Mon Sep 17 00:00:00 2001 From: "Tycho Andersen (AMD)" Date: Mon, 4 May 2026 10:51:47 -0600 Subject: [PATCH 0523/2758] crypto: ccp - Do not initialize SNP for ioctl(SNP_CONFIG) commit 08f0e65e784c4b20e6e620dd4f68d8636073a3d2 upstream. Sashiko notes: > if SEV initialization fails and KVM is actively running normal VMs, could a > userspace process trigger this code path via /dev/sev ioctls (e.g., > SEV_PDH_GEN) and zero out MSR_VM_HSAVE_PA globally? Would the next VMRUN > execution for an active VM trigger a general protection fault and crash the > host? Refuse to re-try initialization if SNP is not already initialized for SNP_CONFIG. This is technically an ABI break: before if SNP initialization failed it could be transparently retriggered by this ioctl, and if no VMs were running, everything worked fine. Hopefully this is enough of a corner case that nobody will notice, but someone does, there are a few options: * do something like symbol_get() for kvm and refuse to initialize if KVM is loaded * check each cpu's HSAVE_PA for non-zero data before re-initializing * once initialization has failed, continue to refuse to initialize until the ccp module is unloaded Fixes: ceac7fb89e8d ("crypto: ccp - Ensure implicit SEV/SNP init and shutdown in ioctls") Reported-by: Sashiko Assisted-by: Gemini:gemini-3.1-pro-preview Link: https://sashiko.dev/#/patchset/20260324161301.1353976-1-tycho%40kernel.org CC: Signed-off-by: Tycho Andersen (AMD) Reviewed-by: Tom Lendacky Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/ccp/sev-dev.c | 33 ++++----------------------------- 1 file changed, 4 insertions(+), 29 deletions(-) diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index ad6c2525a3057..7c4dd57fabb91 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -1727,21 +1727,6 @@ static int sev_move_to_init_state(struct sev_issue_cmd *argp, bool *shutdown_req return 0; } -static int snp_move_to_init_state(struct sev_issue_cmd *argp, bool *shutdown_required) -{ - int error, rc; - - rc = __sev_snp_init_locked(&error, 0); - if (rc) { - argp->error = SEV_RET_INVALID_PLATFORM_STATE; - return rc; - } - - *shutdown_required = true; - - return 0; -} - static int sev_ioctl_do_reset(struct sev_issue_cmd *argp, bool writable) { int state, rc; @@ -2451,8 +2436,6 @@ static int sev_ioctl_do_snp_set_config(struct sev_issue_cmd *argp, bool writable { struct sev_device *sev = psp_master->sev_data; struct sev_user_data_snp_config config; - bool shutdown_required = false; - int ret, error; if (!argp->data) return -EINVAL; @@ -2460,21 +2443,13 @@ static int sev_ioctl_do_snp_set_config(struct sev_issue_cmd *argp, bool writable if (!writable) return -EPERM; + if (!sev->snp_initialized) + return -ENODEV; + if (copy_from_user(&config, (void __user *)argp->data, sizeof(config))) return -EFAULT; - if (!sev->snp_initialized) { - ret = snp_move_to_init_state(argp, &shutdown_required); - if (ret) - return ret; - } - - ret = __sev_do_cmd_locked(SEV_CMD_SNP_CONFIG, &config, &argp->error); - - if (shutdown_required) - __sev_snp_shutdown_locked(&error, false); - - return ret; + return __sev_do_cmd_locked(SEV_CMD_SNP_CONFIG, &config, &argp->error); } static int sev_ioctl_do_snp_vlek_load(struct sev_issue_cmd *argp, bool writable) From a9e886f73dd717027028bb7e3bbca93601ecdfc7 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 19 Apr 2026 23:33:45 -0700 Subject: [PATCH 0524/2758] crypto: drbg - Fix returning success on failure in CTR_DRBG commit 39a31ad9e2a5ed7e9c9c6f711dca96c8c8f5f26b upstream. drbg_ctr_generate() sometimes returns success when it fails, leaving the output buffer uninitialized. Fix it. Fixes: cde001e4c3c3 ("crypto: rng - RNGs must return 0 in success case") Cc: stable@vger.kernel.org Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- crypto/drbg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/drbg.c b/crypto/drbg.c index 9204e6edb4268..e4eb78ed222b9 100644 --- a/crypto/drbg.c +++ b/crypto/drbg.c @@ -377,7 +377,7 @@ static int drbg_ctr_generate(struct drbg_state *drbg, if (addtl && !list_empty(addtl)) { ret = drbg_ctr_update(drbg, addtl, 2); if (ret) - return 0; + return ret; } /* 10.2.1.5.2 step 4.1 */ From d9f4acde5ae96ce806067778e63c94e5848be40a Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 19 Apr 2026 23:33:46 -0700 Subject: [PATCH 0525/2758] crypto: drbg - Fix misaligned writes in CTR_DRBG and HASH_DRBG commit ddc4dedb9ba3c8eecbc8c050fffd46d1b7e75c21 upstream. drbg_cpu_to_be32() is being used to do a plain write to a byte array, which doesn't have any alignment guarantee. This can cause a misaligned write. Replace it with the correct function, put_unaligned_be32(). Fixes: 72f3e00dd67e ("crypto: drbg - replace int2byte with cpu_to_be") Cc: stable@vger.kernel.org Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- crypto/df_sp80090a.c | 7 ++++--- crypto/drbg.c | 3 ++- include/crypto/internal/drbg.h | 18 ------------------ 3 files changed, 6 insertions(+), 22 deletions(-) diff --git a/crypto/df_sp80090a.c b/crypto/df_sp80090a.c index b8134be6f7ad9..f4bb7be016e85 100644 --- a/crypto/df_sp80090a.c +++ b/crypto/df_sp80090a.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -141,10 +142,10 @@ int crypto_drbg_ctr_df(struct aes_enckey *aeskey, /* 10.4.2 step 2 -- calculate the entire length of all input data */ list_for_each_entry(seed, seedlist, list) inputlen += seed->len; - drbg_cpu_to_be32(inputlen, &L_N[0]); + put_unaligned_be32(inputlen, &L_N[0]); /* 10.4.2 step 3 */ - drbg_cpu_to_be32(bytes_to_return, &L_N[4]); + put_unaligned_be32(bytes_to_return, &L_N[4]); /* 10.4.2 step 5: length is L_N, input_string, one byte, padding */ padlen = (inputlen + sizeof(L_N) + 1) % (blocklen_bytes); @@ -175,7 +176,7 @@ int crypto_drbg_ctr_df(struct aes_enckey *aeskey, * holds zeros after allocation -- even the increment of i * is irrelevant as the increment remains within length of i */ - drbg_cpu_to_be32(i, iv); + put_unaligned_be32(i, iv); /* 10.4.2 step 9.2 -- BCC and concatenation with temp */ drbg_ctr_bcc(aeskey, temp + templen, K, &bcc_list, blocklen_bytes, keylen); diff --git a/crypto/drbg.c b/crypto/drbg.c index e4eb78ed222b9..de4c69032155e 100644 --- a/crypto/drbg.c +++ b/crypto/drbg.c @@ -103,6 +103,7 @@ #include #include #include +#include /*************************************************************** * Backend cipher definitions available to DRBG @@ -601,7 +602,7 @@ static int drbg_hash_df(struct drbg_state *drbg, /* 10.4.1 step 3 */ input[0] = 1; - drbg_cpu_to_be32((outlen * 8), &input[1]); + put_unaligned_be32(outlen * 8, &input[1]); /* 10.4.1 step 4.1 -- concatenation of data for input into hash */ drbg_string_fill(&data, input, 5); diff --git a/include/crypto/internal/drbg.h b/include/crypto/internal/drbg.h index 371e52dcee6c5..b4e5ef0be602f 100644 --- a/include/crypto/internal/drbg.h +++ b/include/crypto/internal/drbg.h @@ -9,24 +9,6 @@ #ifndef _INTERNAL_DRBG_H #define _INTERNAL_DRBG_H -/* - * Convert an integer into a byte representation of this integer. - * The byte representation is big-endian - * - * @val value to be converted - * @buf buffer holding the converted integer -- caller must ensure that - * buffer size is at least 32 bit - */ -static inline void drbg_cpu_to_be32(__u32 val, unsigned char *buf) -{ - struct s { - __be32 conv; - }; - struct s *conversion = (struct s *)buf; - - conversion->conv = cpu_to_be32(val); -} - /* * Concatenation Helper and string operation helper * From 59a27cf2d01a9b3da2e6ab0bef5a95ae007e3a5f Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 19 Apr 2026 23:33:47 -0700 Subject: [PATCH 0526/2758] crypto: drbg - Fix ineffective sanity check commit 040ad83b0e8aa065fd2fc641cacba8491a8b186d upstream. Fix drbg_healthcheck_sanity() to correctly check the return value of drbg_generate(). drbg_generate() returns 0 on success, or a negative errno value on failure. drbg_healthcheck_sanity() incorrectly assumed that it returned a positive value on success. This didn't make the sanity check fail, but it made it ineffective. Fixes: cde001e4c3c3 ("crypto: rng - RNGs must return 0 in success case") Cc: stable@vger.kernel.org Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- crypto/drbg.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/crypto/drbg.c b/crypto/drbg.c index de4c69032155e..f23b431bd4902 100644 --- a/crypto/drbg.c +++ b/crypto/drbg.c @@ -1737,7 +1737,6 @@ static int drbg_kcapi_seed(struct crypto_rng *tfm, */ static inline int __init drbg_healthcheck_sanity(void) { - int len = 0; #define OUTBUFLEN 16 unsigned char buf[OUTBUFLEN]; struct drbg_state *drbg = NULL; @@ -1782,11 +1781,11 @@ static inline int __init drbg_healthcheck_sanity(void) max_request_bytes = drbg_max_request_bytes(drbg); drbg_string_fill(&addtl, buf, max_addtllen + 1); /* overflow addtllen with additional info string */ - len = drbg_generate(drbg, buf, OUTBUFLEN, &addtl); - BUG_ON(0 < len); + ret = drbg_generate(drbg, buf, OUTBUFLEN, &addtl); + BUG_ON(ret == 0); /* overflow max_bits */ - len = drbg_generate(drbg, buf, (max_request_bytes + 1), NULL); - BUG_ON(0 < len); + ret = drbg_generate(drbg, buf, max_request_bytes + 1, NULL); + BUG_ON(ret == 0); /* overflow max addtllen with personalization string */ ret = drbg_seed(drbg, &addtl, false); From 044aaaba99e10a915645cb75e82506630d90fcfc Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 19 Apr 2026 23:33:48 -0700 Subject: [PATCH 0527/2758] crypto: drbg - Fix drbg_max_addtl() on 64-bit kernels commit 6f49f00c981bbb9ef602966f19bfdbef46b681d2 upstream. On 64-bit kernels, drbg_max_addtl() returns 2**35 bytes. That's too large, for two reasons: 1. SP800-90A says the maximum limit is 2**35 *bits*, not 2**35 bytes. So the implemented limit has confused bits and bytes. 2. When drbg_kcapi_hash() calls crypto_shash_update() on the additional information string, the length is implicitly cast to 'unsigned int'. That truncates the additional information string to U32_MAX bytes. Fix the maximum additional information string length to always be U32_MAX - 1, causing an error to be returned for any longer lengths. Fixes: 541af946fe13 ("crypto: drbg - SP800-90A Deterministic Random Bit Generator") Cc: stable@vger.kernel.org Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- include/crypto/drbg.h | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/include/crypto/drbg.h b/include/crypto/drbg.h index 2d42518cbdce8..c11eaf757ed02 100644 --- a/include/crypto/drbg.h +++ b/include/crypto/drbg.h @@ -148,19 +148,15 @@ static inline size_t drbg_max_request_bytes(struct drbg_state *drbg) return (1 << 16); } +/* + * SP800-90A allows implementations to support additional info / personalization + * strings of up to 2**35 bits. Implementations can have a smaller maximum. We + * use 2**35 - 16 bits == U32_MAX - 1 bytes so that the max + 1 always fits in a + * size_t, allowing drbg_healthcheck_sanity() to verify its enforcement. + */ static inline size_t drbg_max_addtl(struct drbg_state *drbg) { - /* SP800-90A requires 2**35 bytes additional info str / pers str */ -#if (__BITS_PER_LONG == 32) - /* - * SP800-90A allows smaller maximum numbers to be returned -- we - * return SIZE_MAX - 1 to allow the verification of the enforcement - * of this value in drbg_healthcheck_sanity. - */ - return (SIZE_MAX - 1); -#else - return (1UL<<35); -#endif + return U32_MAX - 1; } static inline size_t drbg_max_requests(struct drbg_state *drbg) From d5c0a6f8dc7e4c421aa952305550fadc5499824e Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 19 Apr 2026 23:33:49 -0700 Subject: [PATCH 0528/2758] crypto: drbg - Fix the fips_enabled priority boost commit a8a1f93080efc83a9ff8452954429ae379e9e614 upstream. When fips_enabled=1, it seems to have been intended for one of the algorithms defined in crypto/drbg.c to be the highest priority "stdrng" algorithm, so that it is what is used by "stdrng" users. However, the code only boosts the priority to 400, which is less than the priority 500 used in drivers/crypto/caam/caamprng.c. Thus, the CAAM RNG could be used instead. Fix this by boosting the priority by 2000 instead of 200. Fixes: 541af946fe13 ("crypto: drbg - SP800-90A Deterministic Random Bit Generator") Cc: stable@vger.kernel.org Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- crypto/drbg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/drbg.c b/crypto/drbg.c index f23b431bd4902..e3065fb9541b2 100644 --- a/crypto/drbg.c +++ b/crypto/drbg.c @@ -1832,7 +1832,7 @@ static inline void __init drbg_fill_array(struct rng_alg *alg, * it is selected. */ if (fips_enabled) - alg->base.cra_priority += 200; + alg->base.cra_priority += 2000; alg->base.cra_ctxsize = sizeof(struct drbg_state); alg->base.cra_module = THIS_MODULE; From c98aed00e65ade43337a22ca816c3d87af482c36 Mon Sep 17 00:00:00 2001 From: Ahsan Atta Date: Wed, 13 May 2026 17:16:56 +0200 Subject: [PATCH 0529/2758] crypto: qat - centralize bus master enable commit e5712ff82947dd02d118cee119ed36cec671d814 upstream. QAT driver currently toggles PCI bus mastering in multiple places (probe paths, and reset callbacks). This makes BME state depend on call ordering and on what PCI command bits were captured in saved PCI config state. Make BME control explicit and deterministic: - remove pci_set_master() from device-specific probe paths - add adf_set_bme() and call it from adf_dev_init() so BME is enabled at one point before device bring-up - drop redundant pci_set_master() and pci_clear_master from adf_aer.c and rely on the unified init path for BME enablement This is in preparation for adding reset_prepare() and reset_done() hooks. In the PCI reset callback flow, the PCI core saves and restores device configuration state around reset_prepare() and reset_done(). This change is needed to ensure that we are able to properly shutdown or reinitialize the device post sysfs triggered resets. Cc: stable@vger.kernel.org Signed-off-by: Ahsan Atta Reviewed-by: Giovanni Cabiddu Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/intel/qat/qat_420xx/adf_drv.c | 2 -- drivers/crypto/intel/qat/qat_4xxx/adf_drv.c | 2 -- drivers/crypto/intel/qat/qat_6xxx/adf_drv.c | 2 -- drivers/crypto/intel/qat/qat_c3xxx/adf_drv.c | 1 - drivers/crypto/intel/qat/qat_c3xxxvf/adf_drv.c | 1 - drivers/crypto/intel/qat/qat_c62x/adf_drv.c | 1 - drivers/crypto/intel/qat/qat_c62xvf/adf_drv.c | 1 - drivers/crypto/intel/qat/qat_common/adf_aer.c | 10 +++++++--- drivers/crypto/intel/qat/qat_common/adf_common_drv.h | 1 + drivers/crypto/intel/qat/qat_common/adf_init.c | 2 ++ drivers/crypto/intel/qat/qat_dh895xcc/adf_drv.c | 1 - drivers/crypto/intel/qat/qat_dh895xccvf/adf_drv.c | 1 - 12 files changed, 10 insertions(+), 15 deletions(-) diff --git a/drivers/crypto/intel/qat/qat_420xx/adf_drv.c b/drivers/crypto/intel/qat/qat_420xx/adf_drv.c index cfa00daeb4fba..3acbbb586dc0c 100644 --- a/drivers/crypto/intel/qat/qat_420xx/adf_drv.c +++ b/drivers/crypto/intel/qat/qat_420xx/adf_drv.c @@ -146,8 +146,6 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } } - pci_set_master(pdev); - if (pci_save_state(pdev)) { dev_err(&pdev->dev, "Failed to save pci state.\n"); ret = -ENOMEM; diff --git a/drivers/crypto/intel/qat/qat_4xxx/adf_drv.c b/drivers/crypto/intel/qat/qat_4xxx/adf_drv.c index c9be5dcddb277..f33182c101e2e 100644 --- a/drivers/crypto/intel/qat/qat_4xxx/adf_drv.c +++ b/drivers/crypto/intel/qat/qat_4xxx/adf_drv.c @@ -148,8 +148,6 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } } - pci_set_master(pdev); - if (pci_save_state(pdev)) { dev_err(&pdev->dev, "Failed to save pci state.\n"); ret = -ENOMEM; diff --git a/drivers/crypto/intel/qat/qat_6xxx/adf_drv.c b/drivers/crypto/intel/qat/qat_6xxx/adf_drv.c index c52462a48c340..ab62b91ecb510 100644 --- a/drivers/crypto/intel/qat/qat_6xxx/adf_drv.c +++ b/drivers/crypto/intel/qat/qat_6xxx/adf_drv.c @@ -189,8 +189,6 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } } - pci_set_master(pdev); - /* * The PCI config space is saved at this point and will be restored * after a Function Level Reset (FLR) as the FLR does not completely diff --git a/drivers/crypto/intel/qat/qat_c3xxx/adf_drv.c b/drivers/crypto/intel/qat/qat_c3xxx/adf_drv.c index bceb5dd8b148c..c745397e63a52 100644 --- a/drivers/crypto/intel/qat/qat_c3xxx/adf_drv.c +++ b/drivers/crypto/intel/qat/qat_c3xxx/adf_drv.c @@ -167,7 +167,6 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_err_free_reg; } } - pci_set_master(pdev); if (pci_save_state(pdev)) { dev_err(&pdev->dev, "Failed to save pci state\n"); diff --git a/drivers/crypto/intel/qat/qat_c3xxxvf/adf_drv.c b/drivers/crypto/intel/qat/qat_c3xxxvf/adf_drv.c index c622793e94a87..b31c58fdfb58e 100644 --- a/drivers/crypto/intel/qat/qat_c3xxxvf/adf_drv.c +++ b/drivers/crypto/intel/qat/qat_c3xxxvf/adf_drv.c @@ -163,7 +163,6 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_err_free_reg; } } - pci_set_master(pdev); /* Completion for VF2PF request/response message exchange */ init_completion(&accel_dev->vf.msg_received); diff --git a/drivers/crypto/intel/qat/qat_c62x/adf_drv.c b/drivers/crypto/intel/qat/qat_c62x/adf_drv.c index 23ccb72b6ea22..941400c64d4be 100644 --- a/drivers/crypto/intel/qat/qat_c62x/adf_drv.c +++ b/drivers/crypto/intel/qat/qat_c62x/adf_drv.c @@ -167,7 +167,6 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_err_free_reg; } } - pci_set_master(pdev); if (pci_save_state(pdev)) { dev_err(&pdev->dev, "Failed to save pci state\n"); diff --git a/drivers/crypto/intel/qat/qat_c62xvf/adf_drv.c b/drivers/crypto/intel/qat/qat_c62xvf/adf_drv.c index 4840d44bbd5b2..631eb2b411506 100644 --- a/drivers/crypto/intel/qat/qat_c62xvf/adf_drv.c +++ b/drivers/crypto/intel/qat/qat_c62xvf/adf_drv.c @@ -163,7 +163,6 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_err_free_reg; } } - pci_set_master(pdev); /* Completion for VF2PF request/response message exchange */ init_completion(&accel_dev->vf.msg_received); diff --git a/drivers/crypto/intel/qat/qat_common/adf_aer.c b/drivers/crypto/intel/qat/qat_common/adf_aer.c index ed01fb9ad74ef..5d20960016965 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_aer.c +++ b/drivers/crypto/intel/qat/qat_common/adf_aer.c @@ -41,7 +41,6 @@ static pci_ers_result_t adf_error_detected(struct pci_dev *pdev, adf_error_notifier(accel_dev); adf_pf2vf_notify_fatal_error(accel_dev); adf_dev_restarting_notify(accel_dev); - pci_clear_master(pdev); adf_dev_down(accel_dev); return PCI_ERS_RESULT_NEED_RESET; @@ -106,6 +105,13 @@ void adf_dev_restore(struct adf_accel_dev *accel_dev) } } +void adf_set_bme(struct adf_accel_dev *accel_dev) +{ + struct pci_dev *pdev = accel_to_pci_dev(accel_dev); + + pci_set_master(pdev); +} + static void adf_device_sriov_worker(struct work_struct *work) { struct adf_sriov_dev_data *sriov_data = @@ -198,8 +204,6 @@ static pci_ers_result_t adf_slot_reset(struct pci_dev *pdev) return PCI_ERS_RESULT_DISCONNECT; } - if (!pdev->is_busmaster) - pci_set_master(pdev); pci_restore_state(pdev); res = adf_dev_up(accel_dev, false); if (res && res != -EALREADY) diff --git a/drivers/crypto/intel/qat/qat_common/adf_common_drv.h b/drivers/crypto/intel/qat/qat_common/adf_common_drv.h index da0ac836c2b41..db26e0b31170e 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_common_drv.h +++ b/drivers/crypto/intel/qat/qat_common/adf_common_drv.h @@ -84,6 +84,7 @@ extern const struct pci_error_handlers adf_err_handler; void adf_reset_sbr(struct adf_accel_dev *accel_dev); void adf_reset_flr(struct adf_accel_dev *accel_dev); void adf_dev_restore(struct adf_accel_dev *accel_dev); +void adf_set_bme(struct adf_accel_dev *accel_dev); int adf_init_aer(void); void adf_exit_aer(void); int adf_init_arb(struct adf_accel_dev *accel_dev); diff --git a/drivers/crypto/intel/qat/qat_common/adf_init.c b/drivers/crypto/intel/qat/qat_common/adf_init.c index f8088388cf12d..f9f5696ed476b 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_init.c +++ b/drivers/crypto/intel/qat/qat_common/adf_init.c @@ -74,6 +74,8 @@ static int adf_dev_init(struct adf_accel_dev *accel_dev) return -EFAULT; } + adf_set_bme(accel_dev); + if (!test_bit(ADF_STATUS_CONFIGURED, &accel_dev->status) && !accel_dev->is_vf) { dev_err(&GET_DEV(accel_dev), "Device not configured\n"); diff --git a/drivers/crypto/intel/qat/qat_dh895xcc/adf_drv.c b/drivers/crypto/intel/qat/qat_dh895xcc/adf_drv.c index b59e0cc49e521..99a6bf9e2b005 100644 --- a/drivers/crypto/intel/qat/qat_dh895xcc/adf_drv.c +++ b/drivers/crypto/intel/qat/qat_dh895xcc/adf_drv.c @@ -167,7 +167,6 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_err_free_reg; } } - pci_set_master(pdev); if (pci_save_state(pdev)) { dev_err(&pdev->dev, "Failed to save pci state\n"); diff --git a/drivers/crypto/intel/qat/qat_dh895xccvf/adf_drv.c b/drivers/crypto/intel/qat/qat_dh895xccvf/adf_drv.c index 7cd528ee31e77..e3eab03ad65bd 100644 --- a/drivers/crypto/intel/qat/qat_dh895xccvf/adf_drv.c +++ b/drivers/crypto/intel/qat/qat_dh895xccvf/adf_drv.c @@ -163,7 +163,6 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_err_free_reg; } } - pci_set_master(pdev); /* Completion for VF2PF request/response message exchange */ init_completion(&accel_dev->vf.msg_received); From 59c0901db2b72998a789cda2ed443960a2031210 Mon Sep 17 00:00:00 2001 From: Ahsan Atta Date: Wed, 20 May 2026 13:33:00 +0100 Subject: [PATCH 0530/2758] crypto: qat - fix restarting state leak on allocation failure commit 7d3ed20f7e46b3e991936fedd7a28f3ff4aec8d2 upstream. In adf_dev_aer_schedule_reset(), ADF_STATUS_RESTARTING is set before allocating reset_data. If the allocation fails, the function returns -ENOMEM without queuing reset work, so nothing ever clears the bit. This leaves the device permanently stuck in the restarting state, causing all subsequent reset attempts to be silently skipped. Fix this by using test_and_set_bit() to atomically claim the RESTARTING state, preventing duplicate reset scheduling races under concurrent fatal error reporting. If the subsequent allocation fails, clear the bit to restore clean state so future reset attempts can proceed. Cc: stable@vger.kernel.org Fixes: d8cba25d2c68 ("crypto: qat - Intel(R) QAT driver framework") Signed-off-by: Ahsan Atta Co-developed-by: Maksim Lukoshkov Signed-off-by: Maksim Lukoshkov Reviewed-by: Giovanni Cabiddu Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/intel/qat/qat_common/adf_aer.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/crypto/intel/qat/qat_common/adf_aer.c b/drivers/crypto/intel/qat/qat_common/adf_aer.c index 5d20960016965..842b7120f786b 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_aer.c +++ b/drivers/crypto/intel/qat/qat_common/adf_aer.c @@ -162,13 +162,14 @@ static int adf_dev_aer_schedule_reset(struct adf_accel_dev *accel_dev, struct adf_reset_dev_data *reset_data; if (!adf_dev_started(accel_dev) || - test_bit(ADF_STATUS_RESTARTING, &accel_dev->status)) + test_and_set_bit(ADF_STATUS_RESTARTING, &accel_dev->status)) return 0; - set_bit(ADF_STATUS_RESTARTING, &accel_dev->status); reset_data = kzalloc_obj(*reset_data); - if (!reset_data) + if (!reset_data) { + clear_bit(ADF_STATUS_RESTARTING, &accel_dev->status); return -ENOMEM; + } reset_data->accel_dev = accel_dev; init_completion(&reset_data->compl); reset_data->mode = mode; From 4515bf525c9648bfc930fe7e0ba10168d6fb9cef Mon Sep 17 00:00:00 2001 From: Ahsan Atta Date: Wed, 13 May 2026 17:16:59 +0200 Subject: [PATCH 0531/2758] crypto: qat - handle sysfs-triggered reset callbacks commit 4627ef7019bc532f992c0723e881811ce12f0a02 upstream. A reset requested through /sys/bus/pci/devices/.../reset invokes the driver reset_prepare() and reset_done() callbacks. The QAT driver does not implement those callbacks today, so the reset proceeds without quiescing the device or bringing it back up afterward, which leaves the device unusable. Hook reset_prepare() and reset_done() into adf_err_handler so the common shutdown and recovery flow also runs for reset. Skip device quiesce if the device is already in a down state. Cc: stable@vger.kernel.org Signed-off-by: Ahsan Atta Reviewed-by: Giovanni Cabiddu Reviewed-by: Damian Muszynski Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/intel/qat/qat_common/adf_aer.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/crypto/intel/qat/qat_common/adf_aer.c b/drivers/crypto/intel/qat/qat_common/adf_aer.c index 842b7120f786b..9e9c98454ba98 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_aer.c +++ b/drivers/crypto/intel/qat/qat_common/adf_aer.c @@ -223,10 +223,22 @@ static void adf_resume(struct pci_dev *pdev) dev_info(&pdev->dev, "Device is up and running\n"); } +static void adf_reset_prepare(struct pci_dev *pdev) +{ + reset_prepare(pdev); +} + +static void adf_reset_done(struct pci_dev *pdev) +{ + reset_done(pdev); +} + const struct pci_error_handlers adf_err_handler = { .error_detected = adf_error_detected, .slot_reset = adf_slot_reset, .resume = adf_resume, + .reset_prepare = adf_reset_prepare, + .reset_done = adf_reset_done, }; EXPORT_SYMBOL_GPL(adf_err_handler); From 6f52fe576ac67b6d557225016b4a6393d1abc0ce Mon Sep 17 00:00:00 2001 From: Ahsan Atta Date: Wed, 13 May 2026 17:16:54 +0200 Subject: [PATCH 0532/2758] crypto: qat - keep VFs enabled during reset commit 57518500053987672050dc2f7bf8a774d5d52fd9 upstream. When a reset is triggered via sysfs, the PCI core invokes the reset_prepare() callback while holding pci_dev_lock(), which includes the PCI configuration space access semaphore. If reset_prepare() calls adf_dev_down(), the call chain adf_dev_stop() -> adf_disable_sriov() -> pci_disable_sriov() attempts to acquire the same semaphore, resulting in a deadlock. Avoid this by skipping pci_disable_sriov() when ADF_STATUS_RESTARTING is set. During reset the PCI topology is preserved, so VF devices remain valid and enumerated across the reset. VF notification and the quiesce handshake via adf_pf2vf_notify_restarting() are still performed unconditionally so that VFs stop submitting work before the PF shuts down. Correspondingly, skip pci_enable_sriov() in adf_enable_sriov() when VFs are already present, since their PCI devices were preserved from before the restart. This is in preparation for adding reset_prepare() and reset_done() callbacks in adf_aer.c. Cc: stable@vger.kernel.org Signed-off-by: Ahsan Atta Reviewed-by: Giovanni Cabiddu Reviewed-by: Damian Muszynski Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/intel/qat/qat_common/adf_sriov.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/crypto/intel/qat/qat_common/adf_sriov.c b/drivers/crypto/intel/qat/qat_common/adf_sriov.c index 96939572109eb..f2011300a9294 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_sriov.c +++ b/drivers/crypto/intel/qat/qat_common/adf_sriov.c @@ -91,6 +91,10 @@ static int adf_enable_sriov(struct adf_accel_dev *accel_dev) /* Enable VF to PF interrupts for all VFs */ adf_enable_all_vf2pf_interrupts(accel_dev, totalvfs); + /* Do not enable SR-IOV if already enabled */ + if (pci_num_vf(pdev)) + return 0; + /* * Due to the hardware design, when SR-IOV and the ring arbiter * are enabled all the VFs supported in hardware must be enabled in @@ -260,7 +264,13 @@ void adf_disable_sriov(struct adf_accel_dev *accel_dev) adf_pf2vf_notify_restarting(accel_dev); adf_pf2vf_wait_for_restarting_complete(accel_dev); - pci_disable_sriov(accel_to_pci_dev(accel_dev)); + /* + * When the device is restarting, preserve VF PCI devices across + * the reset by skipping pci_disable_sriov(). VFs are notified to + * quiesce regardless so the PF can safely shut down. + */ + if (!test_bit(ADF_STATUS_RESTARTING, &accel_dev->status)) + pci_disable_sriov(accel_to_pci_dev(accel_dev)); /* Block VF2PF work and disable VF to PF interrupts */ adf_disable_all_vf2pf_interrupts(accel_dev); From 425f1260ecb9fefca302ef95eb9764db5fe3b4fa Mon Sep 17 00:00:00 2001 From: Ahsan Atta Date: Wed, 13 May 2026 17:16:55 +0200 Subject: [PATCH 0533/2758] crypto: qat - notify fatal error before AER reset preparation commit 6931835f2fdd0cb9b1c7791748d7e67d0749056a upstream. Send fatal error notifications to subsystems and VFs as soon as AER error detection starts, before entering the reset preparation shutdown sequence. This reduces notification latency and ensures peers are informed immediately on fatal detection, rather than after restart-state setup and arbitration teardown. Cc: stable@vger.kernel.org Signed-off-by: Ahsan Atta Reviewed-by: Giovanni Cabiddu Reviewed-by: Damian Muszynski Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/intel/qat/qat_common/adf_aer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/crypto/intel/qat/qat_common/adf_aer.c b/drivers/crypto/intel/qat/qat_common/adf_aer.c index 9e9c98454ba98..d2b72276ac3f4 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_aer.c +++ b/drivers/crypto/intel/qat/qat_common/adf_aer.c @@ -33,13 +33,13 @@ static pci_ers_result_t adf_error_detected(struct pci_dev *pdev, return PCI_ERS_RESULT_DISCONNECT; } + adf_error_notifier(accel_dev); + adf_pf2vf_notify_fatal_error(accel_dev); set_bit(ADF_STATUS_RESTARTING, &accel_dev->status); if (accel_dev->hw_device->exit_arb) { dev_dbg(&pdev->dev, "Disabling arbitration\n"); accel_dev->hw_device->exit_arb(accel_dev); } - adf_error_notifier(accel_dev); - adf_pf2vf_notify_fatal_error(accel_dev); adf_dev_restarting_notify(accel_dev); adf_dev_down(accel_dev); From 0dbcecea740d943002c1cbdafa39bdfc108e32a5 Mon Sep 17 00:00:00 2001 From: Ahsan Atta Date: Wed, 20 May 2026 13:41:55 +0100 Subject: [PATCH 0534/2758] crypto: qat - protect service table iterations with service_lock commit 5c6f845e77ec35f9b7b047cc8f9789bf397cdd3e upstream. The service_table list is protected by service_lock when entries are added or removed (in adf_service_add() and adf_service_remove()), but several functions iterate over the list without holding this lock. A concurrent adf_service_register() or adf_service_unregister() call could modify the list during traversal, leading to list corruption or a use-after-free. Fix this by holding service_lock across all list_for_each_entry() iterations of service_table in adf_dev_init(), adf_dev_start(), adf_dev_stop(), adf_dev_shutdown(), adf_dev_restarting_notify(), adf_dev_restarted_notify(), and adf_error_notifier(). The lock ordering is safe: callers of the static helpers (adf_dev_up() and adf_dev_down()) acquire state_lock before service_lock, and no event_hld callback or service_lock holder ever acquires state_lock in the reverse order. Cc: stable@vger.kernel.org Fixes: d8cba25d2c68 ("crypto: qat - Intel(R) QAT driver framework") Signed-off-by: Ahsan Atta Co-developed-by: Maksim Lukoshkov Signed-off-by: Maksim Lukoshkov Reviewed-by: Giovanni Cabiddu Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/intel/qat/qat_common/adf_init.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/crypto/intel/qat/qat_common/adf_init.c b/drivers/crypto/intel/qat/qat_common/adf_init.c index f9f5696ed476b..1c7f9e49914d4 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_init.c +++ b/drivers/crypto/intel/qat/qat_common/adf_init.c @@ -155,15 +155,18 @@ static int adf_dev_init(struct adf_accel_dev *accel_dev) * This is to facilitate any ordering dependencies between services * prior to starting any of the accelerators. */ + mutex_lock(&service_lock); list_for_each_entry(service, &service_table, list) { if (service->event_hld(accel_dev, ADF_EVENT_INIT)) { dev_err(&GET_DEV(accel_dev), "Failed to initialise service %s\n", service->name); + mutex_unlock(&service_lock); return -EFAULT; } set_bit(accel_dev->accel_id, service->init_status); } + mutex_unlock(&service_lock); return 0; } @@ -233,15 +236,18 @@ static int adf_dev_start(struct adf_accel_dev *accel_dev) if (ret && ret != -EOPNOTSUPP) return ret; + mutex_lock(&service_lock); list_for_each_entry(service, &service_table, list) { if (service->event_hld(accel_dev, ADF_EVENT_START)) { dev_err(&GET_DEV(accel_dev), "Failed to start service %s\n", service->name); + mutex_unlock(&service_lock); return -EFAULT; } set_bit(accel_dev->accel_id, service->start_status); } + mutex_unlock(&service_lock); clear_bit(ADF_STATUS_STARTING, &accel_dev->status); set_bit(ADF_STATUS_STARTED, &accel_dev->status); @@ -315,6 +321,7 @@ static void adf_dev_stop(struct adf_accel_dev *accel_dev) qat_comp_algs_unregister(hw_data->accel_capabilities_ext_mask); clear_bit(ADF_STATUS_COMP_ALGS_REGISTERED, &accel_dev->status); + mutex_lock(&service_lock); list_for_each_entry(service, &service_table, list) { if (!test_bit(accel_dev->accel_id, service->start_status)) continue; @@ -326,6 +333,7 @@ static void adf_dev_stop(struct adf_accel_dev *accel_dev) clear_bit(accel_dev->accel_id, service->start_status); } } + mutex_unlock(&service_lock); if (hw_data->stop_timer) hw_data->stop_timer(accel_dev); @@ -375,6 +383,7 @@ static void adf_dev_shutdown(struct adf_accel_dev *accel_dev) &accel_dev->status); } + mutex_lock(&service_lock); list_for_each_entry(service, &service_table, list) { if (!test_bit(accel_dev->accel_id, service->init_status)) continue; @@ -385,6 +394,7 @@ static void adf_dev_shutdown(struct adf_accel_dev *accel_dev) else clear_bit(accel_dev->accel_id, service->init_status); } + mutex_unlock(&service_lock); adf_rl_exit(accel_dev); @@ -419,12 +429,14 @@ int adf_dev_restarting_notify(struct adf_accel_dev *accel_dev) { struct service_hndl *service; + mutex_lock(&service_lock); list_for_each_entry(service, &service_table, list) { if (service->event_hld(accel_dev, ADF_EVENT_RESTARTING)) dev_err(&GET_DEV(accel_dev), "Failed to restart service %s.\n", service->name); } + mutex_unlock(&service_lock); return 0; } @@ -432,12 +444,14 @@ int adf_dev_restarted_notify(struct adf_accel_dev *accel_dev) { struct service_hndl *service; + mutex_lock(&service_lock); list_for_each_entry(service, &service_table, list) { if (service->event_hld(accel_dev, ADF_EVENT_RESTARTED)) dev_err(&GET_DEV(accel_dev), "Failed to restart service %s.\n", service->name); } + mutex_unlock(&service_lock); return 0; } @@ -445,12 +459,14 @@ void adf_error_notifier(struct adf_accel_dev *accel_dev) { struct service_hndl *service; + mutex_lock(&service_lock); list_for_each_entry(service, &service_table, list) { if (service->event_hld(accel_dev, ADF_EVENT_FATAL_ERROR)) dev_err(&GET_DEV(accel_dev), "Failed to send error event to %s.\n", service->name); } + mutex_unlock(&service_lock); } int adf_dev_down(struct adf_accel_dev *accel_dev) From 8e8391469a92b19e3ad20a40d7da9f61202eb7a9 Mon Sep 17 00:00:00 2001 From: Ahsan Atta Date: Wed, 13 May 2026 17:16:57 +0200 Subject: [PATCH 0535/2758] crypto: qat - skip restart for down devices commit 9676657117b79f88c22875680c36d7da84b75eca upstream. Skip the shutdown and restart flow when adf_slot_reset() is entered for a device that is already down. In that case, leave ADF_STATUS_RESTARTING clear and let adf_slot_reset() restore PCI function state without calling adf_dev_up(), re-enabling SR-IOV, or sending restarted notifications. This is in preparation for adding reset_prepare() and reset_done() callbacks in adf_aer.c. Cc: stable@vger.kernel.org Signed-off-by: Ahsan Atta Reviewed-by: Giovanni Cabiddu Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/intel/qat/qat_common/adf_aer.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/crypto/intel/qat/qat_common/adf_aer.c b/drivers/crypto/intel/qat/qat_common/adf_aer.c index d2b72276ac3f4..fbb2db80043b8 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_aer.c +++ b/drivers/crypto/intel/qat/qat_common/adf_aer.c @@ -33,6 +33,9 @@ static pci_ers_result_t adf_error_detected(struct pci_dev *pdev, return PCI_ERS_RESULT_DISCONNECT; } + if (!adf_dev_started(accel_dev)) + return PCI_ERS_RESULT_CAN_RECOVER; + adf_error_notifier(accel_dev); adf_pf2vf_notify_fatal_error(accel_dev); set_bit(ADF_STATUS_RESTARTING, &accel_dev->status); @@ -205,6 +208,9 @@ static pci_ers_result_t adf_slot_reset(struct pci_dev *pdev) return PCI_ERS_RESULT_DISCONNECT; } + if (!adf_devmgr_in_reset(accel_dev)) + goto reset_complete; + pci_restore_state(pdev); res = adf_dev_up(accel_dev, false); if (res && res != -EALREADY) @@ -214,6 +220,8 @@ static pci_ers_result_t adf_slot_reset(struct pci_dev *pdev) adf_pf2vf_notify_restarted(accel_dev); adf_dev_restarted_notify(accel_dev); clear_bit(ADF_STATUS_RESTARTING, &accel_dev->status); + +reset_complete: return PCI_ERS_RESULT_RECOVERED; } From ce42224487c504aee4b7ff3a7342e7b4d7e28cc9 Mon Sep 17 00:00:00 2001 From: Giovanni Cabiddu Date: Thu, 28 May 2026 16:57:44 +0100 Subject: [PATCH 0536/2758] crypto: qat - validate RSA CRT component lengths commit b3ac78756588059729b9195fcc9f4b37d54057a5 upstream. The generic RSA key parser (rsa_helper.c) bounds each CRT component (p, q, dp, dq, qinv) by the modulus size n_sz, but qat_rsa_setkey_crt() allocates half-size DMA buffers (key_sz / 2) and right-aligns each component with: memcpy(dst + half_key_sz - len, src, len) When a CRT component is larger than half_key_sz the subtraction underflows and memcpy writes past the DMA buffer, causing memory corruption. Add a len > half_key_sz check next to the existing !len check for each of the five CRT components so the driver falls back to the non-CRT path instead of writing out of bounds. Fixes: 879f77e9071f ("crypto: qat - Add RSA CRT mode") Cc: stable@vger.kernel.org Signed-off-by: Giovanni Cabiddu Reviewed-by: Ahsan Atta Reviewed-by: Laurent M Coquerel Tested-by: Laurent M Coquerel Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/intel/qat/qat_common/qat_asym_algs.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/crypto/intel/qat/qat_common/qat_asym_algs.c b/drivers/crypto/intel/qat/qat_common/qat_asym_algs.c index e09b9edfce423..75c15c8e41dba 100644 --- a/drivers/crypto/intel/qat/qat_common/qat_asym_algs.c +++ b/drivers/crypto/intel/qat/qat_common/qat_asym_algs.c @@ -1085,7 +1085,7 @@ static void qat_rsa_setkey_crt(struct qat_rsa_ctx *ctx, struct rsa_key *rsa_key) ptr = rsa_key->p; len = rsa_key->p_sz; qat_rsa_drop_leading_zeros(&ptr, &len); - if (!len) + if (!len || len > half_key_sz) goto err; ctx->p = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_p, GFP_KERNEL); if (!ctx->p) @@ -1096,7 +1096,7 @@ static void qat_rsa_setkey_crt(struct qat_rsa_ctx *ctx, struct rsa_key *rsa_key) ptr = rsa_key->q; len = rsa_key->q_sz; qat_rsa_drop_leading_zeros(&ptr, &len); - if (!len) + if (!len || len > half_key_sz) goto free_p; ctx->q = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_q, GFP_KERNEL); if (!ctx->q) @@ -1107,7 +1107,7 @@ static void qat_rsa_setkey_crt(struct qat_rsa_ctx *ctx, struct rsa_key *rsa_key) ptr = rsa_key->dp; len = rsa_key->dp_sz; qat_rsa_drop_leading_zeros(&ptr, &len); - if (!len) + if (!len || len > half_key_sz) goto free_q; ctx->dp = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_dp, GFP_KERNEL); @@ -1119,7 +1119,7 @@ static void qat_rsa_setkey_crt(struct qat_rsa_ctx *ctx, struct rsa_key *rsa_key) ptr = rsa_key->dq; len = rsa_key->dq_sz; qat_rsa_drop_leading_zeros(&ptr, &len); - if (!len) + if (!len || len > half_key_sz) goto free_dp; ctx->dq = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_dq, GFP_KERNEL); @@ -1131,7 +1131,7 @@ static void qat_rsa_setkey_crt(struct qat_rsa_ctx *ctx, struct rsa_key *rsa_key) ptr = rsa_key->qinv; len = rsa_key->qinv_sz; qat_rsa_drop_leading_zeros(&ptr, &len); - if (!len) + if (!len || len > half_key_sz) goto free_dq; ctx->qinv = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_qinv, GFP_KERNEL); From abf9a568013c07ca18122b7529ef747f71a8a41f Mon Sep 17 00:00:00 2001 From: Ahsan Atta Date: Wed, 13 May 2026 17:16:58 +0200 Subject: [PATCH 0537/2758] crypto: qat - factor out AER reset helpers commit 56707afb92fee371c0f2e04332c9aa03cdb89793 upstream. Move the shutdown and recovery sequences out of adf_error_detected() and adf_slot_reset() into reset_prepare() and reset_done() helpers. This makes the AER recovery path easier to follow and prepares the common reset flow for reuse by additional PCI reset callbacks without duplicating the logic. No functional change intended. Cc: stable@vger.kernel.org Signed-off-by: Ahsan Atta Reviewed-by: Giovanni Cabiddu Reviewed-by: Damian Muszynski Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/intel/qat/qat_common/adf_aer.c | 86 ++++++++++++------- 1 file changed, 53 insertions(+), 33 deletions(-) diff --git a/drivers/crypto/intel/qat/qat_common/adf_aer.c b/drivers/crypto/intel/qat/qat_common/adf_aer.c index fbb2db80043b8..3fc7d13e882c8 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_aer.c +++ b/drivers/crypto/intel/qat/qat_common/adf_aer.c @@ -17,27 +17,18 @@ struct adf_fatal_error_data { static struct workqueue_struct *device_reset_wq; static struct workqueue_struct *device_sriov_wq; -static pci_ers_result_t adf_error_detected(struct pci_dev *pdev, - pci_channel_state_t state) +static pci_ers_result_t reset_prepare(struct pci_dev *pdev) { struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev); - dev_info(&pdev->dev, "Acceleration driver hardware error detected.\n"); if (!accel_dev) { dev_err(&pdev->dev, "Can't find acceleration device\n"); return PCI_ERS_RESULT_DISCONNECT; } - if (state == pci_channel_io_perm_failure) { - dev_err(&pdev->dev, "Can't recover from device error\n"); - return PCI_ERS_RESULT_DISCONNECT; - } - if (!adf_dev_started(accel_dev)) return PCI_ERS_RESULT_CAN_RECOVER; - adf_error_notifier(accel_dev); - adf_pf2vf_notify_fatal_error(accel_dev); set_bit(ADF_STATUS_RESTARTING, &accel_dev->status); if (accel_dev->hw_device->exit_arb) { dev_dbg(&pdev->dev, "Disabling arbitration\n"); @@ -49,6 +40,57 @@ static pci_ers_result_t adf_error_detected(struct pci_dev *pdev, return PCI_ERS_RESULT_NEED_RESET; } +static pci_ers_result_t reset_done(struct pci_dev *pdev) +{ + struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev); + int res; + + if (!accel_dev) { + dev_err(&pdev->dev, "Can't find acceleration device\n"); + return PCI_ERS_RESULT_DISCONNECT; + } + + if (!adf_devmgr_in_reset(accel_dev)) + goto reset_complete; + + pci_restore_state(pdev); + res = adf_dev_up(accel_dev, false); + if (res && res != -EALREADY) + return PCI_ERS_RESULT_DISCONNECT; + + adf_reenable_sriov(accel_dev); + adf_pf2vf_notify_restarted(accel_dev); + adf_dev_restarted_notify(accel_dev); + clear_bit(ADF_STATUS_RESTARTING, &accel_dev->status); + +reset_complete: + dev_info(&pdev->dev, "Device reset completed successfully\n"); + + return PCI_ERS_RESULT_RECOVERED; +} + +static pci_ers_result_t adf_error_detected(struct pci_dev *pdev, + pci_channel_state_t state) +{ + struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev); + + dev_info(&pdev->dev, "Acceleration driver hardware error detected.\n"); + if (!accel_dev) { + dev_err(&pdev->dev, "Can't find acceleration device\n"); + return PCI_ERS_RESULT_DISCONNECT; + } + + if (state == pci_channel_io_perm_failure) { + dev_err(&pdev->dev, "Can't recover from device error\n"); + return PCI_ERS_RESULT_DISCONNECT; + } + + adf_error_notifier(accel_dev); + adf_pf2vf_notify_fatal_error(accel_dev); + + return reset_prepare(pdev); +} + /* reset dev data */ struct adf_reset_dev_data { int mode; @@ -200,29 +242,7 @@ static int adf_dev_aer_schedule_reset(struct adf_accel_dev *accel_dev, static pci_ers_result_t adf_slot_reset(struct pci_dev *pdev) { - struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev); - int res = 0; - - if (!accel_dev) { - pr_err("QAT: Can't find acceleration device\n"); - return PCI_ERS_RESULT_DISCONNECT; - } - - if (!adf_devmgr_in_reset(accel_dev)) - goto reset_complete; - - pci_restore_state(pdev); - res = adf_dev_up(accel_dev, false); - if (res && res != -EALREADY) - return PCI_ERS_RESULT_DISCONNECT; - - adf_reenable_sriov(accel_dev); - adf_pf2vf_notify_restarted(accel_dev); - adf_dev_restarted_notify(accel_dev); - clear_bit(ADF_STATUS_RESTARTING, &accel_dev->status); - -reset_complete: - return PCI_ERS_RESULT_RECOVERED; + return reset_done(pdev); } static void adf_resume(struct pci_dev *pdev) From 9d4ea20a402daa0c6cca89d81ecbe0ebe80db5f4 Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Thu, 7 May 2026 16:41:47 +0200 Subject: [PATCH 0538/2758] crypto: talitos - use dma_sync_single_for_cpu() before reading descriptor header commit e17ff3d6ff907dc8406261e8fd3e1fc8a908f0f6 upstream. In order to know if a descriptor has been processed by the device, the driver polls the FIFO to see if DESC_HDR_DONE is set on a descriptor header to confirm completion. The current code does not make sure that the CPU gets up to date data before reading the descriptor. Fix this by calling dma_sync_single_for_cpu() before reading memory written by the device. Cc: stable@vger.kernel.org Fixes: 58cdbc6d2263 ("crypto: talitos - fix hash on SEC1.") Signed-off-by: Paul Louvel Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/talitos.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index bc61d0fe35140..440e19dc8de60 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -322,19 +322,31 @@ static int talitos_submit(struct device *dev, int ch, struct talitos_desc *desc, return -EINPROGRESS; } -static __be32 get_request_hdr(struct talitos_request *request, bool is_sec1) +static __be32 get_request_hdr(struct device *dev, + struct talitos_request *request, bool is_sec1) { struct talitos_edesc *edesc; - if (!is_sec1) + if (!is_sec1) { + dma_sync_single_for_cpu(dev, request->dma_desc, + TALITOS_DESC_SIZE, DMA_BIDIRECTIONAL); + return request->desc->hdr; + } - if (!request->desc->next_desc) + if (!request->desc->next_desc) { + dma_sync_single_for_cpu(dev, request->dma_desc, + TALITOS_DESC_SIZE, DMA_BIDIRECTIONAL); return request->desc->hdr1; + } else { + dma_sync_single_for_cpu(dev, + be32_to_cpu(request->desc->next_desc), + TALITOS_DESC_SIZE, DMA_BIDIRECTIONAL); + edesc = container_of(request->desc, struct talitos_edesc, desc); - edesc = container_of(request->desc, struct talitos_edesc, desc); - - return ((struct talitos_desc *)(edesc->buf + edesc->dma_len))->hdr1; + return ((struct talitos_desc *)(edesc->buf + edesc->dma_len)) + ->hdr1; + } } /* @@ -358,7 +370,7 @@ static void flush_channel(struct device *dev, int ch, int error, int reset_ch) /* descriptors with their done bits set don't get the error */ rmb(); - hdr = get_request_hdr(request, is_sec1); + hdr = get_request_hdr(dev, request, is_sec1); if ((hdr & DESC_HDR_DONE) == DESC_HDR_DONE) status = 0; From b624dcd2fda9259665dedcc600711cbfa4036b1b Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Thu, 7 May 2026 16:41:48 +0200 Subject: [PATCH 0539/2758] crypto: talitos - add chaining of arbitrary number of descriptor for the SEC1 commit f126384ed55279c3b676f89d5ab547b8de8df782 upstream. The SEC1 hardware can process a chain of descriptors without host intervention. Only the hash implementation currently use this feature, but with a chain of at most 2 descriptors added in commit 37b5e8897eb5 ("crypto: talitos - chain in buffered data for ahash on SEC1"). Add supports for chaining an arbitrary number of descriptors in a chain. Adapt the ahash implementation to make it compatible. Cc: stable@vger.kernel.org Signed-off-by: Paul Louvel Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/talitos.c | 180 ++++++++++++++++++++++++++------------- drivers/crypto/talitos.h | 2 + 2 files changed, 124 insertions(+), 58 deletions(-) diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index 440e19dc8de60..b0ebf99b94f5f 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -273,7 +273,10 @@ static int talitos_submit(struct device *dev, int ch, struct talitos_desc *desc, void *context, int error), void *context) { + struct talitos_edesc *edesc = container_of(desc, struct talitos_edesc, desc); struct talitos_private *priv = dev_get_drvdata(dev); + dma_addr_t dma_desc, prev_dma_desc; + struct talitos_edesc *prev_edesc = NULL; struct talitos_request *request; unsigned long flags; int head; @@ -292,10 +295,31 @@ static int talitos_submit(struct device *dev, int ch, struct talitos_desc *desc, /* map descriptor and save caller data */ if (is_sec1) { - desc->hdr1 = desc->hdr; - request->dma_desc = dma_map_single(dev, &desc->hdr1, + while (edesc) { + edesc->desc.hdr1 = edesc->desc.hdr; + + dma_desc = dma_map_single(dev, &edesc->desc.hdr1, + TALITOS_DESC_SIZE, + DMA_BIDIRECTIONAL); + + if (!prev_edesc) { + request->dma_desc = dma_desc; + goto next; + } + + /* Chain in any previous descriptors. */ + + prev_edesc->desc.next_desc = cpu_to_be32(dma_desc); + + dma_sync_single_for_device(dev, prev_dma_desc, TALITOS_DESC_SIZE, - DMA_BIDIRECTIONAL); + DMA_TO_DEVICE); + +next: + prev_edesc = edesc; + prev_dma_desc = dma_desc; + edesc = edesc->next_desc; + } } else { request->dma_desc = dma_map_single(dev, desc, TALITOS_DESC_SIZE, @@ -326,6 +350,7 @@ static __be32 get_request_hdr(struct device *dev, struct talitos_request *request, bool is_sec1) { struct talitos_edesc *edesc; + dma_addr_t dma_desc; if (!is_sec1) { dma_sync_single_for_cpu(dev, request->dma_desc, @@ -334,19 +359,17 @@ static __be32 get_request_hdr(struct device *dev, return request->desc->hdr; } - if (!request->desc->next_desc) { - dma_sync_single_for_cpu(dev, request->dma_desc, - TALITOS_DESC_SIZE, DMA_BIDIRECTIONAL); - return request->desc->hdr1; - } else { - dma_sync_single_for_cpu(dev, - be32_to_cpu(request->desc->next_desc), - TALITOS_DESC_SIZE, DMA_BIDIRECTIONAL); - edesc = container_of(request->desc, struct talitos_edesc, desc); - - return ((struct talitos_desc *)(edesc->buf + edesc->dma_len)) - ->hdr1; + edesc = container_of(request->desc, struct talitos_edesc, desc); + dma_desc = request->dma_desc; + while (edesc->next_desc) { + dma_desc = be32_to_cpu(edesc->desc.next_desc); + edesc = edesc->next_desc; } + + dma_sync_single_for_cpu(dev, dma_desc, TALITOS_DESC_SIZE, + DMA_BIDIRECTIONAL); + + return edesc->desc.hdr1; } /* @@ -356,6 +379,7 @@ static void flush_channel(struct device *dev, int ch, int error, int reset_ch) { struct talitos_private *priv = dev_get_drvdata(dev); struct talitos_request *request, saved_req; + struct talitos_edesc *edesc; unsigned long flags; int tail, status; bool is_sec1 = has_ftr_sec1(priv); @@ -380,9 +404,22 @@ static void flush_channel(struct device *dev, int ch, int error, int reset_ch) else status = error; - dma_unmap_single(dev, request->dma_desc, - TALITOS_DESC_SIZE, - DMA_BIDIRECTIONAL); + if (is_sec1) { + dma_unmap_single(dev, request->dma_desc, + TALITOS_DESC_SIZE, DMA_BIDIRECTIONAL); + edesc = container_of(request->desc, + struct talitos_edesc, desc); + while (edesc->next_desc) { + dma_unmap_single( + dev, be32_to_cpu(edesc->desc.next_desc), + TALITOS_DESC_SIZE, DMA_BIDIRECTIONAL); + edesc = edesc->next_desc; + } + } else { + dma_unmap_single(dev, request->dma_desc, + TALITOS_DESC_SIZE, + DMA_BIDIRECTIONAL); + } /* copy entries so we can call callback outside lock */ saved_req.desc = request->desc; @@ -477,8 +514,12 @@ DEF_TALITOS2_DONE(ch1_3, TALITOS2_ISR_CH_1_3_DONE) static __be32 current_desc_hdr(struct device *dev, int ch) { struct talitos_private *priv = dev_get_drvdata(dev); + bool is_sec1 = has_ftr_sec1(priv); + struct talitos_request *request; + struct talitos_edesc *edesc; int tail, iter; dma_addr_t cur_desc; + __be32 hdr = 0; cur_desc = ((u64)in_be32(priv->chan[ch].reg + TALITOS_CDPR)) << 32; cur_desc |= in_be32(priv->chan[ch].reg + TALITOS_CDPR_LO); @@ -489,27 +530,35 @@ static __be32 current_desc_hdr(struct device *dev, int ch) } tail = priv->chan[ch].tail; - iter = tail; - while (priv->chan[ch].fifo[iter].dma_desc != cur_desc && - priv->chan[ch].fifo[iter].desc->next_desc != cpu_to_be32(cur_desc)) { - iter = (iter + 1) & (priv->fifo_len - 1); - if (iter == tail) { - dev_err(dev, "couldn't locate current descriptor\n"); - return 0; + do { + request = &priv->chan[ch].fifo[iter]; + + if (request->dma_desc == cur_desc) { + hdr = request->desc->hdr; + } else if (is_sec1) { + edesc = container_of(request->desc, + struct talitos_edesc, desc); + while (edesc->next_desc) { + if (edesc->desc.next_desc == + cpu_to_be32(cur_desc)) { + hdr = edesc->next_desc->desc.hdr1; + break; + } + edesc = edesc->next_desc; + } } - } - if (priv->chan[ch].fifo[iter].desc->next_desc == cpu_to_be32(cur_desc)) { - struct talitos_edesc *edesc; + if (hdr) + break; - edesc = container_of(priv->chan[ch].fifo[iter].desc, - struct talitos_edesc, desc); - return ((struct talitos_desc *) - (edesc->buf + edesc->dma_len))->hdr; - } + iter = (iter + 1) & (priv->fifo_len - 1); + } while (iter != tail); + + if (!hdr) + dev_err(dev, "couldn't locate current descriptor\n"); - return priv->chan[ch].fifo[iter].desc->hdr; + return hdr; } /* @@ -1408,10 +1457,6 @@ static struct talitos_edesc *talitos_edesc_alloc(struct device *dev, dma_len = 0; } alloc_len += icv_stashing ? authsize : 0; - - /* if its a ahash, add space for a second desc next to the first one */ - if (is_sec1 && !dst) - alloc_len += sizeof(struct talitos_desc); alloc_len += ivsize; edesc = kmalloc(ALIGN(alloc_len, dma_get_cache_alignment()), flags); @@ -1427,6 +1472,7 @@ static struct talitos_edesc *talitos_edesc_alloc(struct device *dev, edesc->dst_nents = dst_nents; edesc->iv_dma = iv_dma; edesc->dma_len = dma_len; + edesc->next_desc = NULL; if (dma_len) edesc->dma_link_tbl = dma_map_single(dev, &edesc->link_tbl[0], edesc->dma_len, @@ -1727,8 +1773,10 @@ static void common_nonsnoop_hash_unmap(struct device *dev, struct talitos_private *priv = dev_get_drvdata(dev); bool is_sec1 = has_ftr_sec1(priv); struct talitos_desc *desc = &edesc->desc; - struct talitos_desc *desc2 = (struct talitos_desc *) - (edesc->buf + edesc->dma_len); + struct talitos_desc *desc2; + + if (desc->next_desc) + desc2 = &edesc->next_desc->desc; unmap_single_talitos_ptr(dev, &desc->ptr[5], DMA_FROM_DEVICE); if (desc->next_desc && @@ -1756,10 +1804,17 @@ static void common_nonsnoop_hash_unmap(struct device *dev, if (edesc->dma_len) dma_unmap_single(dev, edesc->dma_link_tbl, edesc->dma_len, DMA_BIDIRECTIONAL); +} - if (desc->next_desc) - dma_unmap_single(dev, be32_to_cpu(desc->next_desc), - TALITOS_DESC_SIZE, DMA_BIDIRECTIONAL); +static void free_edesc_list_from(struct talitos_edesc *edesc) +{ + struct talitos_edesc *next; + + while (edesc) { + next = edesc->next_desc; + kfree(edesc); + edesc = next; + } } static void ahash_done(struct device *dev, @@ -1778,7 +1833,7 @@ static void ahash_done(struct device *dev, } common_nonsnoop_hash_unmap(dev, edesc, areq); - kfree(edesc); + free_edesc_list_from(edesc); if (err) { ahash_request_complete(areq, err); @@ -1894,14 +1949,23 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc, talitos_handle_buggy_hash(ctx, edesc, &desc->ptr[3]); if (is_sec1 && req_ctx->nbuf && length) { - struct talitos_desc *desc2 = (struct talitos_desc *) - (edesc->buf + edesc->dma_len); - dma_addr_t next_desc; + struct talitos_edesc *edesc2; + struct talitos_desc *desc2; + + edesc2 = kzalloc(sizeof(*edesc2), + areq->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP ? + GFP_KERNEL : + GFP_ATOMIC); + if (!edesc2) { + ret = -ENOMEM; + goto err; + } + edesc->next_desc = edesc2; + + desc2 = &edesc2->desc; - memset(desc2, 0, sizeof(*desc2)); desc2->hdr = desc->hdr; desc2->hdr &= ~DESC_HDR_MODE0_MDEU_INIT; - desc2->hdr1 = desc2->hdr; desc->hdr &= ~DESC_HDR_MODE0_MDEU_PAD; desc->hdr |= DESC_HDR_MODE0_MDEU_CONT; desc->hdr &= ~DESC_HDR_DONE_NOTIFY; @@ -1925,21 +1989,21 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc, req_ctx->hw_context_size, req_ctx->hw_context, DMA_FROM_DEVICE); - - next_desc = dma_map_single(dev, &desc2->hdr1, TALITOS_DESC_SIZE, - DMA_BIDIRECTIONAL); - desc->next_desc = cpu_to_be32(next_desc); } if (sync_needed) dma_sync_single_for_device(dev, edesc->dma_link_tbl, edesc->dma_len, DMA_BIDIRECTIONAL); - ret = talitos_submit(dev, ctx->ch, desc, callback, areq); - if (ret != -EINPROGRESS) { - common_nonsnoop_hash_unmap(dev, edesc, areq); - kfree(edesc); - } + ret = talitos_submit(dev, ctx->ch, desc, callback, + areq); + if (ret != -EINPROGRESS) + goto err; + + return -EINPROGRESS; +err: + common_nonsnoop_hash_unmap(dev, edesc, areq); + kfree(edesc); return ret; } diff --git a/drivers/crypto/talitos.h b/drivers/crypto/talitos.h index 1a93ee355929d..596f96bba3efa 100644 --- a/drivers/crypto/talitos.h +++ b/drivers/crypto/talitos.h @@ -49,6 +49,7 @@ struct talitos_desc { * @iv_dma: dma address of iv for checking continuity and link table * @dma_len: length of dma mapped link_tbl space * @dma_link_tbl: bus physical address of link_tbl/buf + * @next_desc: next descriptor * @desc: h/w descriptor * @link_tbl: input and output h/w link tables (if {src,dst}_nents > 1) (SEC2) * @buf: input and output buffeur (if {src,dst}_nents > 1) (SEC1) @@ -63,6 +64,7 @@ struct talitos_edesc { dma_addr_t iv_dma; int dma_len; dma_addr_t dma_link_tbl; + struct talitos_edesc *next_desc; struct talitos_desc desc; union { DECLARE_FLEX_ARRAY(struct talitos_ptr, link_tbl); From ec6669f1c162f6c9969bd405261ba39d8f8bf0cb Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Thu, 7 May 2026 16:41:49 +0200 Subject: [PATCH 0540/2758] crypto: talitos - move dma unmapping code in flush_channel() into a standalone dma_unmap_request() function commit 4d9b0b7415b9e79a3d54d18b5ff230974ea78740 upstream. Previously added code to flush_channel() in order to unmap an entire descriptor. Move that code into a standalone function to improve readability. Cc: stable@vger.kernel.org Signed-off-by: Paul Louvel Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/talitos.c | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index b0ebf99b94f5f..a98b40f566dd8 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -372,6 +372,27 @@ static __be32 get_request_hdr(struct device *dev, return edesc->desc.hdr1; } +static void dma_unmap_request(struct device *dev, + struct talitos_request *request, bool is_sec1) +{ + struct talitos_edesc *edesc; + + if (is_sec1) { + dma_unmap_single(dev, request->dma_desc, TALITOS_DESC_SIZE, + DMA_BIDIRECTIONAL); + edesc = container_of(request->desc, struct talitos_edesc, desc); + while (edesc->next_desc) { + dma_unmap_single(dev, + be32_to_cpu(edesc->desc.next_desc), + TALITOS_DESC_SIZE, DMA_BIDIRECTIONAL); + edesc = edesc->next_desc; + } + } else { + dma_unmap_single(dev, request->dma_desc, TALITOS_DESC_SIZE, + DMA_BIDIRECTIONAL); + } +} + /* * process what was done, notify callback of error if not */ @@ -379,7 +400,6 @@ static void flush_channel(struct device *dev, int ch, int error, int reset_ch) { struct talitos_private *priv = dev_get_drvdata(dev); struct talitos_request *request, saved_req; - struct talitos_edesc *edesc; unsigned long flags; int tail, status; bool is_sec1 = has_ftr_sec1(priv); @@ -404,22 +424,7 @@ static void flush_channel(struct device *dev, int ch, int error, int reset_ch) else status = error; - if (is_sec1) { - dma_unmap_single(dev, request->dma_desc, - TALITOS_DESC_SIZE, DMA_BIDIRECTIONAL); - edesc = container_of(request->desc, - struct talitos_edesc, desc); - while (edesc->next_desc) { - dma_unmap_single( - dev, be32_to_cpu(edesc->desc.next_desc), - TALITOS_DESC_SIZE, DMA_BIDIRECTIONAL); - edesc = edesc->next_desc; - } - } else { - dma_unmap_single(dev, request->dma_desc, - TALITOS_DESC_SIZE, - DMA_BIDIRECTIONAL); - } + dma_unmap_request(dev, request, is_sec1); /* copy entries so we can call callback outside lock */ saved_req.desc = request->desc; From 873e34c46cb7aa3ed64af1ebb3391e3481fa924a Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Thu, 7 May 2026 16:41:50 +0200 Subject: [PATCH 0541/2758] crypto: talitos - move dma mapping code in talitos_submit() into a standalone dma_map_request() function commit 5c0aa8cad7745505297103f05dda3fa06e8ac670 upstream. Previously added code to talitos_submit() in order to map an entire descriptor chain. Move that code into a standalone function to improve readability. Cc: stable@vger.kernel.org Signed-off-by: Paul Louvel Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/talitos.c | 75 ++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index a98b40f566dd8..183d51fa9a4ac 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -255,6 +255,46 @@ static int init_device(struct device *dev) return 0; } +static void dma_map_request(struct device *dev, struct talitos_request *request, + struct talitos_desc *desc, bool is_sec1) +{ + struct talitos_edesc *edesc = + container_of(desc, struct talitos_edesc, desc); + dma_addr_t dma_desc, prev_dma_desc; + struct talitos_edesc *prev_edesc = NULL; + + if (is_sec1) { + while (edesc) { + edesc->desc.hdr1 = edesc->desc.hdr; + + dma_desc = dma_map_single(dev, &edesc->desc.hdr1, + TALITOS_DESC_SIZE, + DMA_BIDIRECTIONAL); + + if (!prev_edesc) { + request->dma_desc = dma_desc; + goto next; + } + + /* Chain in any previous descriptors. */ + + prev_edesc->desc.next_desc = cpu_to_be32(dma_desc); + + dma_sync_single_for_device(dev, prev_dma_desc, + TALITOS_DESC_SIZE, + DMA_TO_DEVICE); + +next: + prev_edesc = edesc; + prev_dma_desc = dma_desc; + edesc = edesc->next_desc; + } + } else { + request->dma_desc = dma_map_single(dev, desc, TALITOS_DESC_SIZE, + DMA_BIDIRECTIONAL); + } +} + /** * talitos_submit - submits a descriptor to the device for processing * @dev: the SEC device to be used @@ -273,10 +313,7 @@ static int talitos_submit(struct device *dev, int ch, struct talitos_desc *desc, void *context, int error), void *context) { - struct talitos_edesc *edesc = container_of(desc, struct talitos_edesc, desc); struct talitos_private *priv = dev_get_drvdata(dev); - dma_addr_t dma_desc, prev_dma_desc; - struct talitos_edesc *prev_edesc = NULL; struct talitos_request *request; unsigned long flags; int head; @@ -294,37 +331,7 @@ static int talitos_submit(struct device *dev, int ch, struct talitos_desc *desc, request = &priv->chan[ch].fifo[head]; /* map descriptor and save caller data */ - if (is_sec1) { - while (edesc) { - edesc->desc.hdr1 = edesc->desc.hdr; - - dma_desc = dma_map_single(dev, &edesc->desc.hdr1, - TALITOS_DESC_SIZE, - DMA_BIDIRECTIONAL); - - if (!prev_edesc) { - request->dma_desc = dma_desc; - goto next; - } - - /* Chain in any previous descriptors. */ - - prev_edesc->desc.next_desc = cpu_to_be32(dma_desc); - - dma_sync_single_for_device(dev, prev_dma_desc, - TALITOS_DESC_SIZE, - DMA_TO_DEVICE); - -next: - prev_edesc = edesc; - prev_dma_desc = dma_desc; - edesc = edesc->next_desc; - } - } else { - request->dma_desc = dma_map_single(dev, desc, - TALITOS_DESC_SIZE, - DMA_BIDIRECTIONAL); - } + dma_map_request(dev, request, desc, is_sec1); request->callback = callback; request->context = context; From 0f21d65f4381d482277266313ca9c23c1caa4493 Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Thu, 7 May 2026 16:41:51 +0200 Subject: [PATCH 0542/2758] crypto: talitos - move code in current_desc_hdr() into a standalone function commit f8713d9e6091755dd30f7f1cfa25f8440cddf81b upstream. Previously added code in current_desc_hdr() in order to add support for searching an offending descriptor inside a descriptor chain. Move that code into a standalone function to improve readability. Cc: stable@vger.kernel.org Signed-off-by: Paul Louvel Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/talitos.c | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index 183d51fa9a4ac..b808833d18ef1 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -520,6 +520,24 @@ DEF_TALITOS2_DONE(ch0, TALITOS2_ISR_CH_0_DONE) DEF_TALITOS2_DONE(ch0_2, TALITOS2_ISR_CH_0_2_DONE) DEF_TALITOS2_DONE(ch1_3, TALITOS2_ISR_CH_1_3_DONE) +static __be32 search_desc_hdr_in_request(struct talitos_request *request, + dma_addr_t cur_desc, bool is_sec1) +{ + struct talitos_edesc *edesc; + + if (request->dma_desc == cur_desc) { + return request->desc->hdr; + } else if (is_sec1) { + edesc = container_of(request->desc, struct talitos_edesc, desc); + while (edesc->next_desc) { + if (edesc->desc.next_desc == cpu_to_be32(cur_desc)) + return edesc->next_desc->desc.hdr1; + edesc = edesc->next_desc; + } + } + return 0; +} + /* * locate current (offending) descriptor */ @@ -528,7 +546,6 @@ static __be32 current_desc_hdr(struct device *dev, int ch) struct talitos_private *priv = dev_get_drvdata(dev); bool is_sec1 = has_ftr_sec1(priv); struct talitos_request *request; - struct talitos_edesc *edesc; int tail, iter; dma_addr_t cur_desc; __be32 hdr = 0; @@ -546,21 +563,7 @@ static __be32 current_desc_hdr(struct device *dev, int ch) do { request = &priv->chan[ch].fifo[iter]; - if (request->dma_desc == cur_desc) { - hdr = request->desc->hdr; - } else if (is_sec1) { - edesc = container_of(request->desc, - struct talitos_edesc, desc); - while (edesc->next_desc) { - if (edesc->desc.next_desc == - cpu_to_be32(cur_desc)) { - hdr = edesc->next_desc->desc.hdr1; - break; - } - edesc = edesc->next_desc; - } - } - + hdr = search_desc_hdr_in_request(request, cur_desc, is_sec1); if (hdr) break; From a0cf230cb4df24f88124fb6727de3da7ea11d56a Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Thu, 7 May 2026 16:41:52 +0200 Subject: [PATCH 0543/2758] crypto: talitos/hash - prepare SEC1 descriptor chaining, remove additional descriptor commit 59b5d899e33701665f18abe6bebf987427876e3e upstream. Currently, when SEC1 has buffered data (nbuf != 0), the ahash code creates an additional descriptor on the fly inside common_nonsnoop_hash() to handle the remainder of the data. This approach is incompatible with the arbitrary-length descriptor chaining that follows. Remove the "additional descriptor" logic from common_nonsnoop_hash() and common_nonsnoop_hash_unmap(). Also remove the nbytes adjustment for SEC1 in ahash_edesc_alloc() that subtracted nbuf. Cc: stable@vger.kernel.org Signed-off-by: Paul Louvel Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/talitos.c | 101 +++------------------------------------ 1 file changed, 6 insertions(+), 95 deletions(-) diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index b808833d18ef1..0184982cb39b8 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -1788,15 +1788,9 @@ static void common_nonsnoop_hash_unmap(struct device *dev, struct talitos_private *priv = dev_get_drvdata(dev); bool is_sec1 = has_ftr_sec1(priv); struct talitos_desc *desc = &edesc->desc; - struct talitos_desc *desc2; - - if (desc->next_desc) - desc2 = &edesc->next_desc->desc; unmap_single_talitos_ptr(dev, &desc->ptr[5], DMA_FROM_DEVICE); - if (desc->next_desc && - desc->ptr[5].ptr != desc2->ptr[5].ptr) - unmap_single_talitos_ptr(dev, &desc2->ptr[5], DMA_FROM_DEVICE); + if (req_ctx->last_desc) memcpy(areq->result, req_ctx->hw_context, crypto_ahash_digestsize(tfm)); @@ -1808,13 +1802,6 @@ static void common_nonsnoop_hash_unmap(struct device *dev, if (from_talitos_ptr_len(&desc->ptr[1], is_sec1)) unmap_single_talitos_ptr(dev, &desc->ptr[1], DMA_TO_DEVICE); - else if (desc->next_desc) - unmap_single_talitos_ptr(dev, &desc2->ptr[1], - DMA_TO_DEVICE); - - if (is_sec1 && req_ctx->nbuf) - unmap_single_talitos_ptr(dev, &desc->ptr[3], - DMA_TO_DEVICE); if (edesc->dma_len) dma_unmap_single(dev, edesc->dma_link_tbl, edesc->dma_len, @@ -1922,9 +1909,6 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc, to_talitos_ptr(&desc->ptr[2], ctx->dma_key, ctx->keylen, is_sec1); - if (is_sec1 && req_ctx->nbuf) - length -= req_ctx->nbuf; - sg_count = edesc->src_nents ?: 1; if (is_sec1 && sg_count > 1) sg_copy_to_buffer(req_ctx->psrc, sg_count, edesc->buf, length); @@ -1934,16 +1918,10 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc, /* * data in */ - if (is_sec1 && req_ctx->nbuf) { - map_single_talitos_ptr(dev, &desc->ptr[3], req_ctx->nbuf, - req_ctx->buf[req_ctx->buf_idx], - DMA_TO_DEVICE); - } else { - sg_count = talitos_sg_map(dev, req_ctx->psrc, length, edesc, - &desc->ptr[3], sg_count, 0, 0); - if (sg_count > 1) - sync_needed = true; - } + sg_count = talitos_sg_map(dev, req_ctx->psrc, length, edesc, + &desc->ptr[3], sg_count, 0, 0); + if (sg_count > 1) + sync_needed = true; /* fifth DWORD empty */ @@ -1963,49 +1941,6 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc, if (is_sec1 && from_talitos_ptr_len(&desc->ptr[3], true) == 0) talitos_handle_buggy_hash(ctx, edesc, &desc->ptr[3]); - if (is_sec1 && req_ctx->nbuf && length) { - struct talitos_edesc *edesc2; - struct talitos_desc *desc2; - - edesc2 = kzalloc(sizeof(*edesc2), - areq->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP ? - GFP_KERNEL : - GFP_ATOMIC); - if (!edesc2) { - ret = -ENOMEM; - goto err; - } - edesc->next_desc = edesc2; - - desc2 = &edesc2->desc; - - desc2->hdr = desc->hdr; - desc2->hdr &= ~DESC_HDR_MODE0_MDEU_INIT; - desc->hdr &= ~DESC_HDR_MODE0_MDEU_PAD; - desc->hdr |= DESC_HDR_MODE0_MDEU_CONT; - desc->hdr &= ~DESC_HDR_DONE_NOTIFY; - - if (desc->ptr[1].ptr) - copy_talitos_ptr(&desc2->ptr[1], &desc->ptr[1], - is_sec1); - else - map_single_talitos_ptr_nosync(dev, &desc2->ptr[1], - req_ctx->hw_context_size, - req_ctx->hw_context, - DMA_TO_DEVICE); - copy_talitos_ptr(&desc2->ptr[2], &desc->ptr[2], is_sec1); - sg_count = talitos_sg_map(dev, req_ctx->psrc, length, edesc, - &desc2->ptr[3], sg_count, 0, 0); - if (sg_count > 1) - sync_needed = true; - copy_talitos_ptr(&desc2->ptr[5], &desc->ptr[5], is_sec1); - if (req_ctx->last_desc) - map_single_talitos_ptr_nosync(dev, &desc->ptr[5], - req_ctx->hw_context_size, - req_ctx->hw_context, - DMA_FROM_DEVICE); - } - if (sync_needed) dma_sync_single_for_device(dev, edesc->dma_link_tbl, edesc->dma_len, DMA_BIDIRECTIONAL); @@ -2028,11 +1963,6 @@ static struct talitos_edesc *ahash_edesc_alloc(struct ahash_request *areq, struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); struct talitos_ctx *ctx = crypto_ahash_ctx(tfm); struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq); - struct talitos_private *priv = dev_get_drvdata(ctx->dev); - bool is_sec1 = has_ftr_sec1(priv); - - if (is_sec1) - nbytes -= req_ctx->nbuf; return talitos_edesc_alloc(ctx->dev, req_ctx->psrc, NULL, NULL, 0, nbytes, 0, 0, 0, areq->base.flags, false); @@ -2051,8 +1981,6 @@ static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes unsigned int nsg; int nents; struct device *dev = ctx->dev; - struct talitos_private *priv = dev_get_drvdata(dev); - bool is_sec1 = has_ftr_sec1(priv); u8 *ctx_buf = req_ctx->buf[req_ctx->buf_idx]; if (!req_ctx->last_desc && (nbytes + req_ctx->nbuf <= blocksize)) { @@ -2084,30 +2012,13 @@ static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes } /* Chain in any previously buffered data */ - if (!is_sec1 && req_ctx->nbuf) { + if (req_ctx->nbuf) { nsg = (req_ctx->nbuf < nbytes_to_hash) ? 2 : 1; sg_init_table(req_ctx->bufsl, nsg); sg_set_buf(req_ctx->bufsl, ctx_buf, req_ctx->nbuf); if (nsg > 1) sg_chain(req_ctx->bufsl, 2, req_ctx->request_sl); req_ctx->psrc = req_ctx->bufsl; - } else if (is_sec1 && req_ctx->nbuf && req_ctx->nbuf < blocksize) { - int offset; - - if (nbytes_to_hash > blocksize) - offset = blocksize - req_ctx->nbuf; - else - offset = nbytes_to_hash - req_ctx->nbuf; - nents = sg_nents_for_len(req_ctx->request_sl, offset); - if (nents < 0) { - dev_err(dev, "Invalid number of src SG.\n"); - return nents; - } - sg_copy_to_buffer(req_ctx->request_sl, nents, - ctx_buf + req_ctx->nbuf, offset); - req_ctx->nbuf += offset; - req_ctx->psrc = scatterwalk_ffwd(req_ctx->bufsl, req_ctx->request_sl, - offset); } else req_ctx->psrc = req_ctx->request_sl; From 96a8955d70201e69dffd4a0a5d653207426c1d43 Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Thu, 7 May 2026 16:41:53 +0200 Subject: [PATCH 0544/2758] crypto: talitos/hash - use descriptor chaining for SEC1 instead of workqueue commit f1ede6d95d8ad3b32c6a552d2baab805bd00fc38 upstream. Rework the SEC1 ahash implementation to build a chain of hardware descriptors, replacing the previous approach of submitting one descriptor at a time via a workqueue, introduced by commit 655ef638a2bc ("crypto: talitos - fix SEC1 32k ahash request limitation"). Introduce ahash_process_req_prepare() which iterates over the request data, allocating enough descriptors to cover the entire ahash request. The new fields (bufsl, src, first, last) are added to talitos_edesc for this purpose. common_nonsnoop_hash() no longer calls talitos_submit(); it only maps and sets up the descriptor. Submission is now done by the caller after the chain is built. ahash_free_desc_list_from() takes over calling common_nonsnoop_hash_unmap() for each descriptor during cleanup. Compared to the workqueue based solution, request are slightly faster since there is no more scheduling latency induced by the workqueue, and only one interrupt is generated by the device at the end of a chain. Commit 655ef638a2bc ("crypto: talitos - fix SEC1 32k ahash request limitation") : $ /usr/libexec/libkcapi/sha256sum ./test_5M.bin 013c5609d63c... ./test_5M.bin real 0m 0.41s user 0m 0.01s sys 0m 0.07s Now : $ /usr/libexec/libkcapi/sha256sum ./test_5M.bin 013c5609d63c... ./test_5M.bin real 0m 0.33s user 0m 0.01s sys 0m 0.20s Tested on a system with an MPC885 SoC featuring the SEC1 Lite. The increase in sys time is due to the fact that commit 37b5e8897eb5 ("crypto: talitos - chain in buffered data for ahash on SEC1") can no longer be applied. Cc: stable@vger.kernel.org Signed-off-by: Paul Louvel Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/talitos.c | 168 ++++++++++++++++++++++++--------------- drivers/crypto/talitos.h | 10 +++ 2 files changed, 115 insertions(+), 63 deletions(-) diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index 0184982cb39b8..883115d66fc44 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -1791,12 +1791,12 @@ static void common_nonsnoop_hash_unmap(struct device *dev, unmap_single_talitos_ptr(dev, &desc->ptr[5], DMA_FROM_DEVICE); - if (req_ctx->last_desc) + if (edesc->last && req_ctx->last_request) memcpy(areq->result, req_ctx->hw_context, crypto_ahash_digestsize(tfm)); - if (req_ctx->psrc) - talitos_sg_unmap(dev, edesc, req_ctx->psrc, NULL, 0, 0); + if (edesc->src) + talitos_sg_unmap(dev, edesc, edesc->src, NULL, 0, 0); /* When using hashctx-in, must unmap it. */ if (from_talitos_ptr_len(&desc->ptr[1], is_sec1)) @@ -1808,12 +1808,14 @@ static void common_nonsnoop_hash_unmap(struct device *dev, DMA_BIDIRECTIONAL); } -static void free_edesc_list_from(struct talitos_edesc *edesc) +static void free_edesc_list_from(struct ahash_request *areq, struct talitos_edesc *edesc) { + struct talitos_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(areq)); struct talitos_edesc *next; while (edesc) { next = edesc->next_desc; + common_nonsnoop_hash_unmap(ctx->dev, edesc, areq); kfree(edesc); edesc = next; } @@ -1828,19 +1830,18 @@ static void ahash_done(struct device *dev, container_of(desc, struct talitos_edesc, desc); struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq); + if (!req_ctx->last_desc && req_ctx->to_hash_later) { /* Position any partial block for next update/final/finup */ req_ctx->buf_idx = (req_ctx->buf_idx + 1) & 1; req_ctx->nbuf = req_ctx->to_hash_later; } - common_nonsnoop_hash_unmap(dev, edesc, areq); - free_edesc_list_from(edesc); + free_edesc_list_from(areq, edesc); - if (err) { - ahash_request_complete(areq, err); - return; - } + ahash_request_complete(areq, err); + + return; req_ctx->remaining_ahash_request_bytes -= req_ctx->current_ahash_request_bytes; @@ -1874,18 +1875,15 @@ static void talitos_handle_buggy_hash(struct talitos_ctx *ctx, (char *)padded_hash, DMA_TO_DEVICE); } -static int common_nonsnoop_hash(struct talitos_edesc *edesc, - struct ahash_request *areq, unsigned int length, - void (*callback) (struct device *dev, - struct talitos_desc *desc, - void *context, int error)) +static void common_nonsnoop_hash(struct talitos_edesc *edesc, + struct ahash_request *areq, + unsigned int length) { struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); struct talitos_ctx *ctx = crypto_ahash_ctx(tfm); struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq); struct device *dev = ctx->dev; struct talitos_desc *desc = &edesc->desc; - int ret; bool sync_needed = false; struct talitos_private *priv = dev_get_drvdata(dev); bool is_sec1 = has_ftr_sec1(priv); @@ -1894,7 +1892,7 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc, /* first DWORD empty */ /* hash context in */ - if (!req_ctx->first_desc || req_ctx->swinit) { + if (!edesc->first || !req_ctx->first_desc || req_ctx->swinit) { map_single_talitos_ptr_nosync(dev, &desc->ptr[1], req_ctx->hw_context_size, req_ctx->hw_context, @@ -1911,22 +1909,22 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc, sg_count = edesc->src_nents ?: 1; if (is_sec1 && sg_count > 1) - sg_copy_to_buffer(req_ctx->psrc, sg_count, edesc->buf, length); + sg_copy_to_buffer(edesc->src, sg_count, edesc->buf, length); else if (length) - sg_count = dma_map_sg(dev, req_ctx->psrc, sg_count, - DMA_TO_DEVICE); + sg_count = dma_map_sg(dev, edesc->src, sg_count, DMA_TO_DEVICE); + /* * data in */ - sg_count = talitos_sg_map(dev, req_ctx->psrc, length, edesc, - &desc->ptr[3], sg_count, 0, 0); + sg_count = talitos_sg_map(dev, edesc->src, length, edesc, &desc->ptr[3], + sg_count, 0, 0); if (sg_count > 1) sync_needed = true; /* fifth DWORD empty */ /* hash/HMAC out -or- hash context out */ - if (req_ctx->last_desc) + if (edesc->last && req_ctx->last_request) map_single_talitos_ptr(dev, &desc->ptr[5], crypto_ahash_digestsize(tfm), req_ctx->hw_context, DMA_FROM_DEVICE); @@ -1944,30 +1942,89 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc, if (sync_needed) dma_sync_single_for_device(dev, edesc->dma_link_tbl, edesc->dma_len, DMA_BIDIRECTIONAL); - - ret = talitos_submit(dev, ctx->ch, desc, callback, - areq); - if (ret != -EINPROGRESS) - goto err; - - return -EINPROGRESS; -err: - common_nonsnoop_hash_unmap(dev, edesc, areq); - kfree(edesc); - return ret; } static struct talitos_edesc *ahash_edesc_alloc(struct ahash_request *areq, + struct scatterlist *src, unsigned int nbytes) { struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); struct talitos_ctx *ctx = crypto_ahash_ctx(tfm); - struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq); - return talitos_edesc_alloc(ctx->dev, req_ctx->psrc, NULL, NULL, 0, + return talitos_edesc_alloc(ctx->dev, src, NULL, NULL, 0, nbytes, 0, 0, 0, areq->base.flags, false); } +static struct talitos_edesc * +ahash_process_req_prepare(struct ahash_request *areq, unsigned int nbytes, + unsigned int blocksize, bool is_sec1) +{ + struct talitos_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(areq)); + struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq); + struct talitos_edesc *first = NULL, *prev_edesc = NULL, *edesc; + size_t desc_max = is_sec1 ? TALITOS1_MAX_DATA_LEN : SIZE_MAX; + struct scatterlist tmp[2]; + size_t to_hash_this_desc; + struct scatterlist *src; + size_t offset = 0; + + do { + src = scatterwalk_ffwd(tmp, req_ctx->psrc, offset); + + to_hash_this_desc = + min(nbytes, ALIGN_DOWN(desc_max, blocksize)); + + /* Allocate extended descriptor */ + edesc = ahash_edesc_alloc(areq, src, to_hash_this_desc); + if (IS_ERR(edesc)) { + if (first) + free_edesc_list_from(areq, first); + return edesc; + } + + edesc->src = + scatterwalk_ffwd(edesc->bufsl, req_ctx->psrc, offset); + edesc->desc.hdr = ctx->desc_hdr_template; + edesc->first = offset == 0; + edesc->last = nbytes - to_hash_this_desc == 0; + + /* On last one, request SEC to pad; otherwise continue */ + if (req_ctx->last_request && edesc->last) + edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_PAD; + else + edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_CONT; + + /* request SEC to INIT hash. */ + if (req_ctx->first_desc && edesc->first && !req_ctx->swinit) + edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_INIT; + + /* + * When the tfm context has a keylen, it's an HMAC. + * A first or last (ie. not middle) descriptor must request HMAC. + */ + if (ctx->keylen && ((req_ctx->first_desc && edesc->first) || + (req_ctx->last_request && edesc->last))) + edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_HMAC; + + /* clear the DN bit */ + if (is_sec1 && !edesc->last) + edesc->desc.hdr &= ~DESC_HDR_DONE_NOTIFY; + + common_nonsnoop_hash(edesc, areq, to_hash_this_desc); + + offset += to_hash_this_desc; + nbytes -= to_hash_this_desc; + + if (!prev_edesc) + first = edesc; + else + prev_edesc->next_desc = edesc; + prev_edesc = edesc; + } while (nbytes); + + return first; +} + static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes) { struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); @@ -1976,14 +2033,16 @@ static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes struct talitos_edesc *edesc; unsigned int blocksize = crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm)); + bool is_sec1 = has_ftr_sec1(dev_get_drvdata(ctx->dev)); unsigned int nbytes_to_hash; unsigned int to_hash_later; unsigned int nsg; int nents; struct device *dev = ctx->dev; u8 *ctx_buf = req_ctx->buf[req_ctx->buf_idx]; + int ret; - if (!req_ctx->last_desc && (nbytes + req_ctx->nbuf <= blocksize)) { + if (!req_ctx->last_request && (nbytes + req_ctx->nbuf <= blocksize)) { /* Buffer up to one whole block */ nents = sg_nents_for_len(req_ctx->request_sl, nbytes); if (nents < 0) { @@ -2000,7 +2059,7 @@ static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes nbytes_to_hash = nbytes + req_ctx->nbuf; to_hash_later = nbytes_to_hash & (blocksize - 1); - if (req_ctx->last_desc) + if (req_ctx->last_request) to_hash_later = 0; else if (to_hash_later) /* There is a partial block. Hash the full block(s) now */ @@ -2035,30 +2094,16 @@ static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes } req_ctx->to_hash_later = to_hash_later; - /* Allocate extended descriptor */ - edesc = ahash_edesc_alloc(req_ctx->areq, nbytes_to_hash); + edesc = ahash_process_req_prepare(areq, nbytes_to_hash, blocksize, + is_sec1); if (IS_ERR(edesc)) return PTR_ERR(edesc); - edesc->desc.hdr = ctx->desc_hdr_template; - - /* On last one, request SEC to pad; otherwise continue */ - if (req_ctx->last_desc) - edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_PAD; - else - edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_CONT; - - /* request SEC to INIT hash. */ - if (req_ctx->first_desc && !req_ctx->swinit) - edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_INIT; - - /* When the tfm context has a keylen, it's an HMAC. - * A first or last (ie. not middle) descriptor must request HMAC. - */ - if (ctx->keylen && (req_ctx->first_desc || req_ctx->last_desc)) - edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_HMAC; + ret = talitos_submit(dev, ctx->ch, &edesc->desc, ahash_done, areq); + if (ret != -EINPROGRESS) + free_edesc_list_from(areq, edesc); - return common_nonsnoop_hash(edesc, req_ctx->areq, nbytes_to_hash, ahash_done); + return ret; } static void sec1_ahash_process_remaining(struct work_struct *work) @@ -2102,16 +2147,13 @@ static int ahash_process_req(struct ahash_request *areq, unsigned int nbytes) req_ctx->remaining_ahash_request_bytes = nbytes; if (is_sec1) { - if (nbytes > TALITOS1_MAX_DATA_LEN) - nbytes = TALITOS1_MAX_DATA_LEN; - else if (req_ctx->last_request) + if (req_ctx->last_request) req_ctx->last_desc = 1; } req_ctx->current_ahash_request_bytes = nbytes; - return ahash_process_req_one(req_ctx->areq, - req_ctx->current_ahash_request_bytes); + return ahash_process_req_one(req_ctx->areq, nbytes); } static int ahash_init(struct ahash_request *areq) diff --git a/drivers/crypto/talitos.h b/drivers/crypto/talitos.h index 596f96bba3efa..11f0eb6a41db6 100644 --- a/drivers/crypto/talitos.h +++ b/drivers/crypto/talitos.h @@ -44,6 +44,11 @@ struct talitos_desc { /* * talitos_edesc - s/w-extended descriptor + * @bufsl: scatterlist buffer + * @src: pointer to input scatterlist + * @first: first descriptor of a chain + * @last: last descriptor of a chain + * * @src_nents: number of segments in input scatterlist * @dst_nents: number of segments in output scatterlist * @iv_dma: dma address of iv for checking continuity and link table @@ -59,6 +64,11 @@ struct talitos_desc { * of link_tbl data */ struct talitos_edesc { + struct scatterlist bufsl[2]; + struct scatterlist *src; + int first; + int last; + int src_nents; int dst_nents; dma_addr_t iv_dma; From ab3b0f3e4e72e42f9c257b3e1aad09da70ab7e98 Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Thu, 7 May 2026 16:41:54 +0200 Subject: [PATCH 0545/2758] crypto: talitos/hash - drop workqueue mechanism for SEC1 commit be4802afb1700534e48cb776d0d1e772c27de130 upstream. Now that SEC1 hash uses hardware descriptor chaining instead of a workqueue to process requests exceeding TALITOS1_MAX_DATA_LEN, the workqueue code is no longer needed. Remove sec1_ahash_process_remaining(), the related fields from talitos_ahash_req_ctx (request_bufsl, areq, request_sl, remaining_ahash_request_bytes, current_ahash_request_bytes, sec1_ahash_process_remaining), the dead code in ahash_done(), and simplify ahash_process_req() to call ahash_process_req_one() directly with the original areq->src and areq->nbytes. Cc: stable@vger.kernel.org Signed-off-by: Paul Louvel Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/talitos.c | 80 ++++------------------------------------ 1 file changed, 7 insertions(+), 73 deletions(-) diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index 883115d66fc44..1f497930800b6 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -12,7 +12,6 @@ * All rights reserved. */ -#include #include #include #include @@ -952,13 +951,6 @@ struct talitos_ahash_req_ctx { unsigned int nbuf; struct scatterlist bufsl[2]; struct scatterlist *psrc; - - struct scatterlist request_bufsl[2]; - struct ahash_request *areq; - struct scatterlist *request_sl; - unsigned int remaining_ahash_request_bytes; - unsigned int current_ahash_request_bytes; - struct work_struct sec1_ahash_process_remaining; }; struct talitos_export_state { @@ -1840,18 +1832,6 @@ static void ahash_done(struct device *dev, free_edesc_list_from(areq, edesc); ahash_request_complete(areq, err); - - return; - - req_ctx->remaining_ahash_request_bytes -= - req_ctx->current_ahash_request_bytes; - - if (!req_ctx->remaining_ahash_request_bytes) { - ahash_request_complete(areq, 0); - return; - } - - schedule_work(&req_ctx->sec1_ahash_process_remaining); } /* @@ -2044,12 +2024,12 @@ static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes if (!req_ctx->last_request && (nbytes + req_ctx->nbuf <= blocksize)) { /* Buffer up to one whole block */ - nents = sg_nents_for_len(req_ctx->request_sl, nbytes); + nents = sg_nents_for_len(areq->src, nbytes); if (nents < 0) { dev_err(dev, "Invalid number of src SG.\n"); return nents; } - sg_copy_to_buffer(req_ctx->request_sl, nents, + sg_copy_to_buffer(areq->src, nents, ctx_buf + req_ctx->nbuf, nbytes); req_ctx->nbuf += nbytes; return 0; @@ -2076,18 +2056,18 @@ static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes sg_init_table(req_ctx->bufsl, nsg); sg_set_buf(req_ctx->bufsl, ctx_buf, req_ctx->nbuf); if (nsg > 1) - sg_chain(req_ctx->bufsl, 2, req_ctx->request_sl); + sg_chain(req_ctx->bufsl, 2, areq->src); req_ctx->psrc = req_ctx->bufsl; } else - req_ctx->psrc = req_ctx->request_sl; + req_ctx->psrc = areq->src; if (to_hash_later) { - nents = sg_nents_for_len(req_ctx->request_sl, nbytes); + nents = sg_nents_for_len(areq->src, nbytes); if (nents < 0) { dev_err(dev, "Invalid number of src SG.\n"); return nents; } - sg_pcopy_to_buffer(req_ctx->request_sl, nents, + sg_pcopy_to_buffer(areq->src, nents, req_ctx->buf[(req_ctx->buf_idx + 1) & 1], to_hash_later, nbytes - to_hash_later); @@ -2106,54 +2086,9 @@ static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes return ret; } -static void sec1_ahash_process_remaining(struct work_struct *work) -{ - struct talitos_ahash_req_ctx *req_ctx = - container_of(work, struct talitos_ahash_req_ctx, - sec1_ahash_process_remaining); - int err = 0; - - req_ctx->request_sl = scatterwalk_ffwd(req_ctx->request_bufsl, - req_ctx->request_sl, TALITOS1_MAX_DATA_LEN); - - if (req_ctx->remaining_ahash_request_bytes > TALITOS1_MAX_DATA_LEN) - req_ctx->current_ahash_request_bytes = TALITOS1_MAX_DATA_LEN; - else { - req_ctx->current_ahash_request_bytes = - req_ctx->remaining_ahash_request_bytes; - - if (req_ctx->last_request) - req_ctx->last_desc = 1; - } - - err = ahash_process_req_one(req_ctx->areq, - req_ctx->current_ahash_request_bytes); - - if (err != -EINPROGRESS) - ahash_request_complete(req_ctx->areq, err); -} - static int ahash_process_req(struct ahash_request *areq, unsigned int nbytes) { - struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); - struct talitos_ctx *ctx = crypto_ahash_ctx(tfm); - struct device *dev = ctx->dev; - struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq); - struct talitos_private *priv = dev_get_drvdata(dev); - bool is_sec1 = has_ftr_sec1(priv); - - req_ctx->areq = areq; - req_ctx->request_sl = areq->src; - req_ctx->remaining_ahash_request_bytes = nbytes; - - if (is_sec1) { - if (req_ctx->last_request) - req_ctx->last_desc = 1; - } - - req_ctx->current_ahash_request_bytes = nbytes; - - return ahash_process_req_one(req_ctx->areq, nbytes); + return ahash_process_req_one(areq, nbytes); } static int ahash_init(struct ahash_request *areq) @@ -2176,7 +2111,6 @@ static int ahash_init(struct ahash_request *areq) req_ctx->hw_context_size = size; req_ctx->last_request = 0; req_ctx->last_desc = 0; - INIT_WORK(&req_ctx->sec1_ahash_process_remaining, sec1_ahash_process_remaining); dma = dma_map_single(dev, req_ctx->hw_context, req_ctx->hw_context_size, DMA_TO_DEVICE); From a4ffe8e7bdfe5b9a128e0e34acb6c42cdb068df0 Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Thu, 7 May 2026 16:41:55 +0200 Subject: [PATCH 0546/2758] crypto: talitos/hash - rename first_desc/last_desc to first_request/last_request commit 8bcf00671400ac3b3a4cc3011e6c1496dbd880fd upstream. In talitos_ahash_req_ctx and talitos_export_state, the fields first_desc and last_desc describe request-level (not descriptor-level) state. Rename them to first_request and last_request for clarity. last_desc is also removed from talitos_ahash_req_ctx as it is no longer used. Cc: stable@vger.kernel.org Signed-off-by: Paul Louvel Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/talitos.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index 1f497930800b6..6be42935068a8 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -944,8 +944,7 @@ struct talitos_ahash_req_ctx { u8 buf[2][HASH_MAX_BLOCK_SIZE]; int buf_idx; unsigned int swinit; - unsigned int first_desc; - unsigned int last_desc; + unsigned int first_request; unsigned int last_request; unsigned int to_hash_later; unsigned int nbuf; @@ -957,8 +956,8 @@ struct talitos_export_state { u32 hw_context[TALITOS_MDEU_MAX_CONTEXT_SIZE / sizeof(u32)]; u8 buf[HASH_MAX_BLOCK_SIZE]; unsigned int swinit; - unsigned int first_desc; - unsigned int last_desc; + unsigned int first_request; + unsigned int last_request; unsigned int to_hash_later; unsigned int nbuf; }; @@ -1822,8 +1821,7 @@ static void ahash_done(struct device *dev, container_of(desc, struct talitos_edesc, desc); struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq); - - if (!req_ctx->last_desc && req_ctx->to_hash_later) { + if (!req_ctx->last_request && req_ctx->to_hash_later) { /* Position any partial block for next update/final/finup */ req_ctx->buf_idx = (req_ctx->buf_idx + 1) & 1; req_ctx->nbuf = req_ctx->to_hash_later; @@ -1872,7 +1870,7 @@ static void common_nonsnoop_hash(struct talitos_edesc *edesc, /* first DWORD empty */ /* hash context in */ - if (!edesc->first || !req_ctx->first_desc || req_ctx->swinit) { + if (!edesc->first || !req_ctx->first_request || req_ctx->swinit) { map_single_talitos_ptr_nosync(dev, &desc->ptr[1], req_ctx->hw_context_size, req_ctx->hw_context, @@ -1880,7 +1878,7 @@ static void common_nonsnoop_hash(struct talitos_edesc *edesc, req_ctx->swinit = 0; } /* Indicate next op is not the first. */ - req_ctx->first_desc = 0; + req_ctx->first_request = 0; /* HMAC key */ if (ctx->keylen) @@ -1975,14 +1973,14 @@ ahash_process_req_prepare(struct ahash_request *areq, unsigned int nbytes, edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_CONT; /* request SEC to INIT hash. */ - if (req_ctx->first_desc && edesc->first && !req_ctx->swinit) + if (req_ctx->first_request && edesc->first && !req_ctx->swinit) edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_INIT; /* * When the tfm context has a keylen, it's an HMAC. * A first or last (ie. not middle) descriptor must request HMAC. */ - if (ctx->keylen && ((req_ctx->first_desc && edesc->first) || + if (ctx->keylen && ((req_ctx->first_request && edesc->first) || (req_ctx->last_request && edesc->last))) edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_HMAC; @@ -2103,14 +2101,13 @@ static int ahash_init(struct ahash_request *areq) /* Initialize the context */ req_ctx->buf_idx = 0; req_ctx->nbuf = 0; - req_ctx->first_desc = 1; /* first_desc indicates h/w must init its context */ + req_ctx->first_request = 1; req_ctx->swinit = 0; /* assume h/w init of context */ size = (crypto_ahash_digestsize(tfm) <= SHA256_DIGEST_SIZE) ? TALITOS_MDEU_CONTEXT_SIZE_MD5_SHA1_SHA256 : TALITOS_MDEU_CONTEXT_SIZE_SHA384_SHA512; req_ctx->hw_context_size = size; req_ctx->last_request = 0; - req_ctx->last_desc = 0; dma = dma_map_single(dev, req_ctx->hw_context, req_ctx->hw_context_size, DMA_TO_DEVICE); @@ -2202,8 +2199,8 @@ static int ahash_export(struct ahash_request *areq, void *out) req_ctx->hw_context_size); memcpy(export->buf, req_ctx->buf[req_ctx->buf_idx], req_ctx->nbuf); export->swinit = req_ctx->swinit; - export->first_desc = req_ctx->first_desc; - export->last_desc = req_ctx->last_desc; + export->first_request = req_ctx->first_request; + export->last_request = req_ctx->last_request; export->to_hash_later = req_ctx->to_hash_later; export->nbuf = req_ctx->nbuf; @@ -2228,8 +2225,8 @@ static int ahash_import(struct ahash_request *areq, const void *in) memcpy(req_ctx->hw_context, export->hw_context, size); memcpy(req_ctx->buf[0], export->buf, export->nbuf); req_ctx->swinit = export->swinit; - req_ctx->first_desc = export->first_desc; - req_ctx->last_desc = export->last_desc; + req_ctx->first_request = export->first_request; + req_ctx->last_request = export->last_request; req_ctx->to_hash_later = export->to_hash_later; req_ctx->nbuf = export->nbuf; From 1691f2c4a4f27248d3f326be147fd64d78612e13 Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Thu, 7 May 2026 16:41:56 +0200 Subject: [PATCH 0547/2758] crypto: talitos/hash - remove useless wrapper commit 907ae6088c82c9abae2d26477fddd60df6ad003b upstream. ahash_process_req() was a wrapper used in commit 655ef638a2bc ("crypto: talitos - fix SEC1 32k ahash request limitation"). Rename ahash_process_req_one() to ahash_process_req() and remove the wrapper. Cc: stable@vger.kernel.org Signed-off-by: Paul Louvel Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/talitos.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index 6be42935068a8..b4283b6c18ef6 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -2003,7 +2003,7 @@ ahash_process_req_prepare(struct ahash_request *areq, unsigned int nbytes, return first; } -static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes) +static int ahash_process_req(struct ahash_request *areq, unsigned int nbytes) { struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); struct talitos_ctx *ctx = crypto_ahash_ctx(tfm); @@ -2084,11 +2084,6 @@ static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes return ret; } -static int ahash_process_req(struct ahash_request *areq, unsigned int nbytes) -{ - return ahash_process_req_one(areq, nbytes); -} - static int ahash_init(struct ahash_request *areq) { struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); From 4519290ed20dec10c56501ef880994088dfdcd31 Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Thu, 7 May 2026 16:41:57 +0200 Subject: [PATCH 0548/2758] crypto: talitos/hash - fix SEC2 64k - 1 ahash request limitation commit 6e12daff6ec125102a6fdcafc5aa7199f7ce8933 upstream. The problem described in commit 655ef638a2bc ("crypto: talitos - fix SEC1 32k ahash request limitation") also apply for the SEC2 hardware, but with a limitation of 64k - 1 bytes. Split ahash_done() into SEC1 and SEC2 paths: SEC1 continues to free the whole descriptor list at once, while SEC2 now iterates through descriptors one by one, submitting the next only after the previous completes, which is required since SEC2 cannot chain descriptors in hardware. Cc: stable@vger.kernel.org Fixes: c662b043cdca ("crypto: af_alg/hash: Support MSG_SPLICE_PAGES") Signed-off-by: Paul Louvel Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/talitos.c | 47 +++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index b4283b6c18ef6..4b53b13f96d9f 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -1820,16 +1820,46 @@ static void ahash_done(struct device *dev, struct talitos_edesc *edesc = container_of(desc, struct talitos_edesc, desc); struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); + bool is_sec1 = has_ftr_sec1(dev_get_drvdata(dev)); + struct talitos_ctx *ctx = crypto_ahash_ctx(tfm); + struct talitos_edesc *next; - if (!req_ctx->last_request && req_ctx->to_hash_later) { - /* Position any partial block for next update/final/finup */ - req_ctx->buf_idx = (req_ctx->buf_idx + 1) & 1; - req_ctx->nbuf = req_ctx->to_hash_later; - } + if (is_sec1) { + if (!req_ctx->last_request && req_ctx->to_hash_later) { + /* Position any partial block for next update/final/finup */ + req_ctx->buf_idx = (req_ctx->buf_idx + 1) & 1; + req_ctx->nbuf = req_ctx->to_hash_later; + } + + free_edesc_list_from(areq, edesc); + ahash_request_complete(areq, err); + } else { + next = edesc->next_desc; - free_edesc_list_from(areq, edesc); + common_nonsnoop_hash_unmap(dev, edesc, areq); + kfree(edesc); - ahash_request_complete(areq, err); + if (err) + goto out; + + if (next) { + err = talitos_submit(dev, ctx->ch, &next->desc, + ahash_done, areq); + if (err != -EINPROGRESS) + goto out; + return; + } +out: + if (!req_ctx->last_request && req_ctx->to_hash_later) { + /* Position any partial block for next update/final/finup */ + req_ctx->buf_idx = (req_ctx->buf_idx + 1) & 1; + req_ctx->nbuf = req_ctx->to_hash_later; + } + if (err && next) + free_edesc_list_from(areq, next); + ahash_request_complete(areq, err); + } } /* @@ -1940,7 +1970,8 @@ ahash_process_req_prepare(struct ahash_request *areq, unsigned int nbytes, struct talitos_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(areq)); struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq); struct talitos_edesc *first = NULL, *prev_edesc = NULL, *edesc; - size_t desc_max = is_sec1 ? TALITOS1_MAX_DATA_LEN : SIZE_MAX; + size_t desc_max = is_sec1 ? TALITOS1_MAX_DATA_LEN : + TALITOS2_MAX_DATA_LEN; struct scatterlist tmp[2]; size_t to_hash_this_desc; struct scatterlist *src; From 02d9dac2b84325a28a4ee8fb40d50df02cf30bd7 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Wed, 3 Jun 2026 12:06:12 +0100 Subject: [PATCH 0549/2758] arm64: fpsimd: Fix type mismatch in sme_{save,load}_state() commit 247bd153905085c18ff9006cca1ccb96dfd18e7f upstream. The sme_save_state() and sme_load_state() functions take a 32-bit int argument that describes whether to save/restore ZT0. Their assembly implementations consume the entire 64-bit register containing this 32-bit value, and will attempt to save/restore ZT0 if any bit of that 64-bit register is non-zero. Per the AAPCS64 parameter passing rules, the callee is responsible for any necessary widening, and the upper 32-bits are permitted to contain arbitrary values. If the upper 32 bits are non-zero, this could result in an unexpected attempt to save/restore ZT0, and consequently could lead to unexpected traps/undefs/faults. In practice compilers are very unlikely to generate code where the upper 32-bits would be non-zero, but they are permitted to do so. Fix this by only consuming the low 32 bits of the register, and update comments accordingly. Fixes: 95fcec713259 ("arm64/sme: Implement context switching for ZT0") Signed-off-by: Mark Rutland Cc: Catalin Marinas Cc: Fuad Tabba Cc: James Morse Cc: Marc Zyngier Cc: Mark Brown Cc: Oliver Upton Cc: Vladimir Murzin Cc: Will Deacon Cc: stable@vger.kernel.org Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kernel/entry-fpsimd.S | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm64/kernel/entry-fpsimd.S b/arch/arm64/kernel/entry-fpsimd.S index 6325db1a2179c..fb441702495fa 100644 --- a/arch/arm64/kernel/entry-fpsimd.S +++ b/arch/arm64/kernel/entry-fpsimd.S @@ -103,13 +103,13 @@ SYM_FUNC_END(sme_set_vq) * Save the ZA and ZT state * * x0 - pointer to buffer for state - * x1 - number of ZT registers to save + * w1 - number of ZT registers to save */ SYM_FUNC_START(sme_save_state) _sme_rdsvl 2, 1 // x2 = VL/8 sme_save_za 0, x2, 12 // Leaves x0 pointing to the end of ZA - cbz x1, 1f + cbz w1, 1f _str_zt 0 1: ret @@ -119,13 +119,13 @@ SYM_FUNC_END(sme_save_state) * Load the ZA and ZT state * * x0 - pointer to buffer for state - * x1 - number of ZT registers to save + * w1 - number of ZT registers to save */ SYM_FUNC_START(sme_load_state) _sme_rdsvl 2, 1 // x2 = VL/8 sme_load_za 0, x2, 12 // Leaves x0 pointing to the end of ZA - cbz x1, 1f + cbz w1, 1f _ldr_zt 0 1: ret From 507c1378110190eb59c353a398b203a3f0fd0af9 Mon Sep 17 00:00:00 2001 From: Carlos Song Date: Mon, 25 May 2026 14:23:56 +0800 Subject: [PATCH 0550/2758] spi: fsl-lpspi: replace dmaengine_terminate_all() with dmaengine_terminate_sync() commit e703ce47691b967fe9b4057fb1d062273211afa9 upstream. dmaengine_terminate_all() has been deprecated, so replace it with dmaengine_terminate_sync(). Fixes: 09c04466ce7e ("spi: lpspi: add dma mode support") Cc: stable@vger.kernel.org Signed-off-by: Carlos Song Link: https://patch.msgid.link/20260525062357.3191349-2-carlos.song@oss.nxp.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi-fsl-lpspi.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index e201309f8aae3..1a94a42fac319 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -647,7 +647,7 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller, tx->sgl, tx->nents, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc_tx) { - dmaengine_terminate_all(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_tx); return -EINVAL; } @@ -668,8 +668,8 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller, transfer_timeout); if (!time_left) { dev_err(fsl_lpspi->dev, "I/O Error in DMA TX\n"); - dmaengine_terminate_all(controller->dma_tx); - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_rx); fsl_lpspi_reset(fsl_lpspi); return -ETIMEDOUT; } @@ -678,8 +678,8 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller, transfer_timeout); if (!time_left) { dev_err(fsl_lpspi->dev, "I/O Error in DMA RX\n"); - dmaengine_terminate_all(controller->dma_tx); - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_rx); fsl_lpspi_reset(fsl_lpspi); return -ETIMEDOUT; } @@ -688,8 +688,8 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller, fsl_lpspi->target_aborted) { dev_dbg(fsl_lpspi->dev, "I/O Error in DMA TX interrupted\n"); - dmaengine_terminate_all(controller->dma_tx); - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_rx); fsl_lpspi_reset(fsl_lpspi); return -EINTR; } @@ -698,8 +698,8 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller, fsl_lpspi->target_aborted) { dev_dbg(fsl_lpspi->dev, "I/O Error in DMA RX interrupted\n"); - dmaengine_terminate_all(controller->dma_tx); - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_rx); fsl_lpspi_reset(fsl_lpspi); return -EINTR; } From 9d000bdd250d649a11cd7f733175686877344582 Mon Sep 17 00:00:00 2001 From: Carlos Song Date: Mon, 25 May 2026 14:23:57 +0800 Subject: [PATCH 0551/2758] spi: fsl-lpspi: terminate the RX channel on TX prepare failure path commit 01980b5da56e573d62798d0ff6c86bcaa2b22cbe upstream. When dmaengine_prep_slave_sg() fails for the TX channel, the error path terminates the TX DMA channel but leaves the RX channel running. Since the RX channel was already submitted and issued prior to preparing the TX descriptor, returning -EINVAL causes the SPI core to unmap the DMA buffers while the RX DMA engine continues writing to them, leading to potential memory corruption or use-after-free. Terminate the RX channel before returning on the TX prepare failure path. Fixes: 09c04466ce7e ("spi: lpspi: add dma mode support") Cc: stable@vger.kernel.org Signed-off-by: Carlos Song Link: https://patch.msgid.link/20260525062357.3191349-3-carlos.song@oss.nxp.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi-fsl-lpspi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index 1a94a42fac319..e14753144e19d 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -647,7 +647,7 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller, tx->sgl, tx->nents, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc_tx) { - dmaengine_terminate_sync(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_rx); return -EINVAL; } From 03f6ecbc446c33b38fd452cd3c494092a8116967 Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Wed, 29 Apr 2026 12:49:14 +0200 Subject: [PATCH 0552/2758] x86/mm: Fix freeing of PMD-sized vmemmap pages commit 39406c05f8f150f1685839acd38ffdd69ff92031 upstream. Commit bf9e4e30f353 ("x86/mm: use pagetable_free()"), switched from freeing non-boot page tables through __free_pages() to pagetable_free(). However, the function is also called to free vmemmap pages. Given that vmemmap pages are not page tables, already the page_ptdesc(page) is wrong. But worse, pagetable_free() calls: __free_pages(page, compound_order(page)); Since vmemmap pages are not compound pages (see vmemmap_alloc_block()) -- except for HVO, which doesn't apply here -- only first page of a PMD-sized vmemmap page is freed, leaking the other ones. Fix it by properly decoupling pagetable and vmemmap freeing. free_pagetable() no longer has to mess with SECTION_INFO, as only the vmemmap is marked like that in register_page_bootmem_memmap(). The indentation in remove_pmd_table() is messed up. Fix that while touching it. Bootmem info handling will soon be fixed up. For now, handle it similar to free_pagetable(), just avoiding the ifdef. [ dhansen: changelog munging. More imperative voice ] Fixes: bf9e4e30f353 ("x86/mm: use pagetable_free()") Signed-off-by: David Hildenbrand (Arm) Signed-off-by: Andrew Morton Signed-off-by: Dave Hansen Acked-by: Mike Rapoport (Microsoft) Tested-by: Lance Yang Link: https://lore.kernel.org/20260429-vmemmap-v2-1-8dfcacffd877@kernel.org Link: https://patch.msgid.link/20260429-vmemmap-v2-1-8dfcacffd877@kernel.org Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- arch/x86/mm/init_64.c | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index df2261fa4f985..7e20b22d658b9 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -1014,7 +1014,7 @@ static void __meminit free_pagetable(struct page *page, int order) #ifdef CONFIG_HAVE_BOOTMEM_INFO_NODE enum bootmem_type type = bootmem_type(page); - if (type == SECTION_INFO || type == MIX_SECTION_INFO) { + if (type == MIX_SECTION_INFO) { while (nr_pages--) put_page_bootmem(page++); } else { @@ -1028,13 +1028,24 @@ static void __meminit free_pagetable(struct page *page, int order) } } -static void __meminit free_hugepage_table(struct page *page, +static void __meminit free_vmemmap_pages(struct page *page, unsigned int order, struct vmem_altmap *altmap) { - if (altmap) - vmem_altmap_free(altmap, PMD_SIZE / PAGE_SIZE); - else - free_pagetable(page, get_order(PMD_SIZE)); + unsigned long nr_pages = 1u << order; + + if (altmap) { + vmem_altmap_free(altmap, nr_pages); + } else if (PageReserved(page)) { + if (IS_ENABLED(CONFIG_HAVE_BOOTMEM_INFO_NODE) && + bootmem_type(page) == SECTION_INFO) { + while (nr_pages--) + put_page_bootmem(page++); + } else { + free_reserved_pages(page, nr_pages); + } + } else { + __free_pages(page, order); + } } static void __meminit free_pte_table(pte_t *pte_start, pmd_t *pmd) @@ -1118,7 +1129,8 @@ remove_pte_table(pte_t *pte_start, unsigned long addr, unsigned long end, return; if (!direct) - free_pagetable(pte_page(*pte), 0); + /* We never populate base pages from the altmap. */ + free_vmemmap_pages(pte_page(*pte), 0, NULL); spin_lock(&init_mm.page_table_lock); pte_clear(&init_mm, addr, pte); @@ -1153,19 +1165,19 @@ remove_pmd_table(pmd_t *pmd_start, unsigned long addr, unsigned long end, if (IS_ALIGNED(addr, PMD_SIZE) && IS_ALIGNED(next, PMD_SIZE)) { if (!direct) - free_hugepage_table(pmd_page(*pmd), - altmap); + free_vmemmap_pages(pmd_page(*pmd), + PMD_ORDER, altmap); spin_lock(&init_mm.page_table_lock); pmd_clear(pmd); spin_unlock(&init_mm.page_table_lock); pages++; } else if (vmemmap_pmd_is_unused(addr, next)) { - free_hugepage_table(pmd_page(*pmd), - altmap); - spin_lock(&init_mm.page_table_lock); - pmd_clear(pmd); - spin_unlock(&init_mm.page_table_lock); + free_vmemmap_pages(pmd_page(*pmd), PMD_ORDER, + altmap); + spin_lock(&init_mm.page_table_lock); + pmd_clear(pmd); + spin_unlock(&init_mm.page_table_lock); } continue; } From 7057fcf3a6d3b65543d66d29506184d015a79b1d Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Tue, 14 Apr 2026 11:17:16 -0700 Subject: [PATCH 0553/2758] EDAC/i10nm: Don't fail probing if ADXL is missing commit e360a6d65bb46c527a5909430a31d640cdd5036e upstream. ADXL is not present in Coreboot- or Slimbootloader-based BIOSes and as result, the driver fails to probe there. Since commit 2738c69a8813 ("EDAC/i10nm: Add driver decoder for Ice Lake and Tremont CPUs"), i10nm_edac supports driver decoder. Switch to driver decoding when ADXL is not present. Signed-off-by: Vasily Khoruzhick Signed-off-by: Tony Luck Reviewed-by: Qiuxu Zhuo Cc: stable@vger.kernel.org # v6.1+ Link: https://patch.msgid.link/20260414181735.87023-1-anarsoul@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/edac/i10nm_base.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/edac/i10nm_base.c b/drivers/edac/i10nm_base.c index 63df35444214b..de6c52dbd9d25 100644 --- a/drivers/edac/i10nm_base.c +++ b/drivers/edac/i10nm_base.c @@ -79,6 +79,7 @@ static struct res_config *res_cfg; static int retry_rd_err_log; static int decoding_via_mca; static bool mem_cfg_2lm; +static bool no_adxl; static struct reg_rrl icx_reg_rrl_ddr = { .set_num = 2, @@ -1222,8 +1223,14 @@ static int __init i10nm_init(void) } rc = skx_adxl_get(); - if (rc) - goto fail; + if (rc) { + /* Decoding errors via MCA banks for 2LM isn't supported yet */ + if (rc != -ENODEV || mem_cfg_2lm) + goto fail; + i10nm_printk(KERN_INFO, "ADXL not found, falling back to MCA-based decoding.\n"); + no_adxl = true; + decoding_via_mca = true; + } opstate_init(); mce_register_decode_chain(&i10nm_mce_dec); @@ -1257,7 +1264,8 @@ static void __exit i10nm_exit(void) skx_teardown_debug(); mce_unregister_decode_chain(&i10nm_mce_dec); - skx_adxl_put(); + if (!no_adxl) + skx_adxl_put(); skx_remove(); } From 3e6e9f17f5833c66bf2ff9a20c1cafca6b98d581 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Wed, 31 Dec 2025 13:07:21 +0100 Subject: [PATCH 0554/2758] watchdog: apple: Add "apple,t8103-wdt" compatible commit 14ca4868886f2188401fe06cd7bf01a330b3fb99 upstream. After discussion with the devicetree maintainers we agreed to not extend lists with the generic compatible "apple,wdt" anymore [1]. Use "apple,t8103-wdt" as base compatible as it is the SoC the driver and bindings were written for. [1]: https://lore.kernel.org/asahi/12ab93b7-1fc2-4ce0-926e-c8141cfe81bf@kernel.org/ Fixes: 4ed224aeaf66 ("watchdog: Add Apple SoC watchdog driver") Cc: stable@vger.kernel.org Reviewed-by: Neal Gompa Signed-off-by: Janne Grunau Link: https://lore.kernel.org/r/20251231-watchdog-apple-t8103-base-compat-v1-1-1702a02e0c45@jannau.net Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/watchdog/apple_wdt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/watchdog/apple_wdt.c b/drivers/watchdog/apple_wdt.c index 66a158f67a712..6b9b0f9b05ced 100644 --- a/drivers/watchdog/apple_wdt.c +++ b/drivers/watchdog/apple_wdt.c @@ -218,6 +218,7 @@ static int apple_wdt_suspend(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(apple_wdt_pm_ops, apple_wdt_suspend, apple_wdt_resume); static const struct of_device_id apple_wdt_of_match[] = { + { .compatible = "apple,t8103-wdt" }, { .compatible = "apple,wdt" }, {}, }; From a935b64548fcfe1d5b4dbdd31dddfb0d7019367f Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Wed, 27 May 2026 10:48:50 +0000 Subject: [PATCH 0555/2758] regulator: scmi: fix of_node refcount leak in scmi_regulator_probe() commit fa11039d6cdff84584a3ef8cc1f5e1b56e045da2 upstream. scmi_regulator_probe() calls of_find_node_by_name() which takes a reference on the returned device node. On the error path where process_scmi_regulator_of_node() fails, the function returns without calling of_node_put() on the child node, leaking the reference. Add of_node_put(np) on the error path to properly release the reference. Cc: stable@vger.kernel.org Fixes: 0fbeae70ee7c ("regulator: add SCMI driver") Signed-off-by: Wentao Liang Link: https://patch.msgid.link/20260527104850.872415-1-vulab@iscas.ac.cn Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/regulator/scmi-regulator.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/scmi-regulator.c b/drivers/regulator/scmi-regulator.c index 6d609c42e4793..c005e65ba0ecb 100644 --- a/drivers/regulator/scmi-regulator.c +++ b/drivers/regulator/scmi-regulator.c @@ -345,8 +345,10 @@ static int scmi_regulator_probe(struct scmi_device *sdev) for_each_child_of_node_scoped(np, child) { ret = process_scmi_regulator_of_node(sdev, ph, child, rinfo); /* abort on any mem issue */ - if (ret == -ENOMEM) + if (ret == -ENOMEM) { + of_node_put(np); return ret; + } } of_node_put(np); /* From 5fd2dbeded544f5ba647cf0a1fa597266ee94035 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 16:37:07 +0200 Subject: [PATCH 0556/2758] i2c: core: fix hang on adapter registration failure commit 3c7e164344e5bcf6f274bbf59a3274f5caad9bc1 upstream. Clients may be registered from bus notifier callbacks when the adapter is registered. On a subsequent error during registration, the adapter references taken by such clients prevent the wait for the references to be released from ever completing. Fix this by refactoring client deregistration and deregistering also on late adapter registration failures. Fixes: f8756c67b3de ("i2c: core: call of_i2c_setup_smbus_alert in i2c_register_adapter") Cc: stable@vger.kernel.org # 4.15 Cc: Phil Reid Signed-off-by: Johan Hovold Signed-off-by: Wolfram Sang Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/i2c-core-base.c | 49 ++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 4039d1175c4c4..add940a7542ef 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -63,6 +63,7 @@ static DEFINE_MUTEX(core_lock); static DEFINE_IDR(i2c_adapter_idr); +static void i2c_deregister_clients(struct i2c_adapter *adap); static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver); static DEFINE_STATIC_KEY_FALSE(i2c_trace_msg_key); @@ -1608,6 +1609,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap) return 0; out_reg: + i2c_deregister_clients(adap); debugfs_remove_recursive(adap->debugfs); init_completion(&adap->dev_released); device_unregister(&adap->dev); @@ -1747,29 +1749,10 @@ static int __process_removed_adapter(struct device_driver *d, void *data) return 0; } -/** - * i2c_del_adapter - unregister I2C adapter - * @adap: the adapter being unregistered - * Context: can sleep - * - * This unregisters an I2C adapter which was previously registered - * by @i2c_add_adapter or @i2c_add_numbered_adapter. - */ -void i2c_del_adapter(struct i2c_adapter *adap) +static void i2c_deregister_clients(struct i2c_adapter *adap) { - struct i2c_adapter *found; struct i2c_client *client, *next; - /* First make sure that this adapter was ever added */ - mutex_lock(&core_lock); - found = idr_find(&i2c_adapter_idr, adap->nr); - mutex_unlock(&core_lock); - if (found != adap) { - pr_debug("attempting to delete unregistered adapter [%s]\n", adap->name); - return; - } - - i2c_acpi_remove_space_handler(adap); /* Tell drivers about this removal */ mutex_lock(&core_lock); bus_for_each_drv(&i2c_bus_type, NULL, adap, @@ -1795,6 +1778,32 @@ void i2c_del_adapter(struct i2c_adapter *adap) * them up properly, so we give them a chance to do that first. */ device_for_each_child(&adap->dev, NULL, __unregister_client); device_for_each_child(&adap->dev, NULL, __unregister_dummy); +} + +/** + * i2c_del_adapter - unregister I2C adapter + * @adap: the adapter being unregistered + * Context: can sleep + * + * This unregisters an I2C adapter which was previously registered + * by @i2c_add_adapter or @i2c_add_numbered_adapter. + */ +void i2c_del_adapter(struct i2c_adapter *adap) +{ + struct i2c_adapter *found; + + /* First make sure that this adapter was ever added */ + mutex_lock(&core_lock); + found = idr_find(&i2c_adapter_idr, adap->nr); + mutex_unlock(&core_lock); + if (found != adap) { + pr_debug("attempting to delete unregistered adapter [%s]\n", adap->name); + return; + } + + i2c_acpi_remove_space_handler(adap); + + i2c_deregister_clients(adap); /* device name is gone after device_unregister */ dev_dbg(&adap->dev, "adapter [%s] unregistered\n", adap->name); From 0cff05bd2186020f8706233e261016d149cc24db Mon Sep 17 00:00:00 2001 From: Lee Jia Jie Date: Thu, 9 Jul 2026 21:56:19 +0800 Subject: [PATCH 0557/2758] perf/aux: Fix page UAF in map_range() commit 5948aaf64f81f217a25dcc2bf6c0779bca19566c upstream. map_range() reads rb->aux_pages[], rb->aux_nr_pages and rb->aux_pgoff via perf_mmap_to_page() while holding only event->mmap_mutex. Those fields are serialized by rb->aux_mutex, and mmap_mutex is per event. Thus, two events sharing one rb via PERF_EVENT_IOC_SET_OUTPUT can race rb_alloc_aux() with map_range(), leading to a page-UAF scenario as follows: CPU 0 CPU 1 ===== ===== rb_alloc_aux() map_range() [1]: allocate rb->aux_pages[0] [2]: rb->aux_nr_pages++ [3]: perf_mmap_to_page() returns rb->aux_pages[0] [4]: map it as VM_PFNMAP [5]: rb->aux_pgoff = 1 munmap the page [6]: free rb->aux_pages[0] Pages mapped as VM_PFNMAP have no refcount protection, so CPU 1 holds a mapping to a freed physical frame. Fix this by taking rb->aux_mutex across the page walk in map_range(). Fixes: b709eb872e19 ("perf: map pages in advance") Signed-off-by: Lee Jia Jie Signed-off-by: Ingo Molnar Cc: stable@vger.kernel.org Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Namhyung Kim Signed-off-by: Greg Kroah-Hartman --- kernel/events/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/events/core.c b/kernel/events/core.c index bab0f3bd4fa8d..240684f4b18c1 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -7150,6 +7150,8 @@ static int map_range(struct perf_buffer *rb, struct vm_area_struct *vma) int err = 0; unsigned long pagenum; + guard(mutex)(&rb->aux_mutex); + /* * We map this as a VM_PFNMAP VMA. * From c04873ea85d954c29695151f0425824ffc1ab74b Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Wed, 29 Apr 2026 22:21:14 +0100 Subject: [PATCH 0558/2758] liveupdate: reject LIVEUPDATE_IOCTL_CREATE_SESSION with invalid name length commit e947433cd0d2b95a277757451b9b9c2714136dc2 upstream. A session name must not be an empty string, and must not exceed the maximum size define in the uapi header, including null termination. Fixes: 0153094d03df ("liveupdate: luo_session: add sessions support") Cc: stable@vger.kernel.org Signed-off-by: Luca Boccassi Reviewed-by: Pasha Tatashin Reviewed-by: Pratyush Yadav Acked-by: Mike Rapoport (Microsoft) Link: https://lore.kernel.org/r/20260429212221.814107-2-luca.boccassi@gmail.com Signed-off-by: Pasha Tatashin Signed-off-by: Mike Rapoport (Microsoft) Signed-off-by: Greg Kroah-Hartman --- kernel/liveupdate/luo_session.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c index 7a42385dabe27..ec7aebc15a801 100644 --- a/kernel/liveupdate/luo_session.c +++ b/kernel/liveupdate/luo_session.c @@ -382,9 +382,13 @@ static int luo_session_getfile(struct luo_session *session, struct file **filep) int luo_session_create(const char *name, struct file **filep) { + size_t len = strnlen(name, LIVEUPDATE_SESSION_NAME_LENGTH); struct luo_session *session; int err; + if (len == 0 || len > LIVEUPDATE_SESSION_NAME_LENGTH - 1) + return -EINVAL; + session = luo_session_alloc(name); if (IS_ERR(session)) return PTR_ERR(session); From 792118d05f01f26693f1327cda8f5e37e51bfa92 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Wed, 29 Apr 2026 22:21:15 +0100 Subject: [PATCH 0559/2758] selftests/liveupdate: add test cases for LIVEUPDATE_IOCTL_CREATE_SESSION calls with invalid length commit dab2b4c66aa0f44ccb6a0096906e5680c604fe39 upstream. Verify that LIVEUPDATE_IOCTL_CREATE_SESSION ioctl which provide a name that is an empty string or too long are not allowed. Cc: stable@vger.kernel.org Signed-off-by: Luca Boccassi Reviewed-by: Pasha Tatashin Reviewed-by: Pratyush Yadav Link: https://lore.kernel.org/r/20260429212221.814107-3-luca.boccassi@gmail.com Signed-off-by: Pasha Tatashin Signed-off-by: Mike Rapoport (Microsoft) Signed-off-by: Greg Kroah-Hartman --- .../testing/selftests/liveupdate/liveupdate.c | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tools/testing/selftests/liveupdate/liveupdate.c b/tools/testing/selftests/liveupdate/liveupdate.c index 37c808fbe1e92..90268d86684f5 100644 --- a/tools/testing/selftests/liveupdate/liveupdate.c +++ b/tools/testing/selftests/liveupdate/liveupdate.c @@ -386,4 +386,46 @@ TEST_F(liveupdate_device, prevent_double_preservation) ASSERT_EQ(close(session_fd2), 0); } +/* + * Test Case: Create Session with No Null Termination + * + * Verifies that filling the entire 64-byte name field with non-null characters + * (no '\0' terminator) is rejected by the kernel with EINVAL. + */ +TEST_F(liveupdate_device, create_session_no_null_termination) +{ + struct liveupdate_ioctl_create_session args = {}; + + self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); + if (self->fd1 < 0 && errno == ENOENT) + SKIP(return, "%s does not exist", LIVEUPDATE_DEV); + ASSERT_GE(self->fd1, 0); + + /* Fill entire name field with 'X', no null terminator */ + args.size = sizeof(args); + memset(args.name, 'X', sizeof(args.name)); + + EXPECT_LT(ioctl(self->fd1, LIVEUPDATE_IOCTL_CREATE_SESSION, &args), 0); + EXPECT_EQ(errno, EINVAL); +} + +/* + * Test Case: Create Session with Empty Name + * + * Verifies that creating a session with an empty string name fails + * with EINVAL. + */ +TEST_F(liveupdate_device, create_session_empty_name) +{ + int session_fd; + + self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); + if (self->fd1 < 0 && errno == ENOENT) + SKIP(return, "%s does not exist", LIVEUPDATE_DEV); + ASSERT_GE(self->fd1, 0); + + session_fd = create_session(self->fd1, ""); + EXPECT_EQ(session_fd, -EINVAL); +} + TEST_HARNESS_MAIN From e5d5f3bd053a5f14787526c9f0f55ef900d43ac6 Mon Sep 17 00:00:00 2001 From: Huihui Huang Date: Wed, 1 Jul 2026 18:28:46 +0800 Subject: [PATCH 0560/2758] tracing: Prevent out-of-bounds read in glob matching commit 0a6070839b1ef276d5b05bedfb787743e140fb17 upstream. String event fields are not necessarily NUL-terminated, so the filter predicate functions (filter_pred_string(), filter_pred_strloc() and filter_pred_strrelloc()) pass the field length to the regex match callbacks, and the length-aware matchers honour it. regex_match_glob() was the exception: it ignored the length and called glob_match(), which scans the string until it hits a NUL byte. Some string fields are not NUL-terminated. One example is the dynamic char array of the xfs_* namespace tracepoints, which is copied without a trailing NUL. For such a field, glob matching reads past the end of the event field, causing a KASAN slab-out-of-bounds read in glob_match(), reached via regex_match_glob() and filter_match_preds() from the xfs_lookup tracepoint. Add a length-bounded glob_match_len() and use it from regex_match_glob() so glob matching always stops at the field boundary. The matching loop is factored into a shared helper so glob_match() keeps its behaviour. Fixes: 60f1d5e3bac4 ("ftrace: Support full glob matching") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/da1aaf125fc3b63320b0c540fd6afa7c3d5b4f1a.1782836943.git.hhhuang@smu.edu.sg Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Signed-off-by: Huihui Huang Signed-off-by: Ren Wei Acked-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- include/linux/glob.h | 1 + kernel/trace/trace_events_filter.c | 6 ++---- lib/glob.c | 31 ++++++++++++++++++++++++++++-- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/include/linux/glob.h b/include/linux/glob.h index 861327b33e41d..91595e750936e 100644 --- a/include/linux/glob.h +++ b/include/linux/glob.h @@ -6,5 +6,6 @@ #include /* For __pure */ bool __pure glob_match(char const *pat, char const *str); +bool __pure glob_match_len(char const *pat, char const *str, size_t len); #endif /* _LINUX_GLOB_H */ diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index 609325f579427..6385cd662d8d1 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c @@ -1056,11 +1056,9 @@ static int regex_match_end(char *str, struct regex *r, int len) return 0; } -static int regex_match_glob(char *str, struct regex *r, int len __maybe_unused) +static int regex_match_glob(char *str, struct regex *r, int len) { - if (glob_match(r->pattern, str)) - return 1; - return 0; + return glob_match_len(r->pattern, str, len) ? 1 : 0; } /** diff --git a/lib/glob.c b/lib/glob.c index 7aca76c25bcb2..c80d9dd736b4f 100644 --- a/lib/glob.c +++ b/lib/glob.c @@ -11,6 +11,9 @@ MODULE_DESCRIPTION("glob(7) matching"); MODULE_LICENSE("Dual MIT/GPL"); +static bool __pure glob_match_str(char const *pat, char const *str, + char const *str_end); + /** * glob_match - Shell-style pattern matching, like !fnmatch(pat, str, 0) * @pat: Shell-style pattern to match, e.g. "*.[ch]". @@ -40,6 +43,29 @@ MODULE_LICENSE("Dual MIT/GPL"); * An opening bracket without a matching close is matched literally. */ bool __pure glob_match(char const *pat, char const *str) +{ + return glob_match_str(pat, str, NULL); +} +EXPORT_SYMBOL(glob_match); + +/** + * glob_match_len - glob match against a length-bounded string + * @pat: Shell-style pattern to match. + * @str: String to match. Need not be NUL-terminated. + * @len: Number of bytes of @str that may be read. + * + * Like glob_match(), but @str is only read up to @len bytes, so it can be + * used on buffers that are not NUL-terminated (e.g. trace event fields). + * A NUL byte within @len still terminates the string. + */ +bool __pure glob_match_len(char const *pat, char const *str, size_t len) +{ + return glob_match_str(pat, str, str + len); +} +EXPORT_SYMBOL(glob_match_len); + +static bool __pure glob_match_str(char const *pat, char const *str, + char const *str_end) { /* * Backtrack to previous * on mismatch and retry starting one @@ -55,9 +81,11 @@ bool __pure glob_match(char const *pat, char const *str) * on mismatch, or true after matching the trailing nul bytes. */ for (;;) { - unsigned char c = *str++; + unsigned char c = (str_end && str >= str_end) ? '\0' : *str; unsigned char d = *pat++; + str++; + switch (d) { case '?': /* Wildcard: anything but nul */ if (c == '\0') @@ -125,4 +153,3 @@ bool __pure glob_match(char const *pat, char const *str) } } } -EXPORT_SYMBOL(glob_match); From 80a7608376e595dd27e535cc4cf2525f4ef2540e Mon Sep 17 00:00:00 2001 From: Ricardo Robaina Date: Thu, 2 Jul 2026 11:04:11 -0300 Subject: [PATCH 0561/2758] audit: fix potential integer overflow in audit_log_n_hex() commit 65dfde57d1e29ce2b76fc23dd565eccd5c0bc0f0 upstream. The function calculates new_len as len << 1 for hex encoding. This has two overflow risks: the shift itself can overflow when len is large, and the result can be truncated when assigned to new_len (declared as int) from the size_t calculation. Fix by using check_shl_overflow() to catch shift overflow and changing new_len and loop counter i to size_t to prevent truncation. Cc: stable@vger.kernel.org Fixes: 168b7173959f ("AUDIT: Clean up logging of untrusted strings") Reviewed-by: Richard Guy Briggs Signed-off-by: Ricardo Robaina [PM: remove vertical whitspace noise] Signed-off-by: Paul Moore Signed-off-by: Greg Kroah-Hartman --- kernel/audit.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/kernel/audit.c b/kernel/audit.c index 18ed0beba12f7..41a2de70fa43f 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -62,6 +62,7 @@ #include #include #include +#include #include "audit.h" @@ -2080,7 +2081,8 @@ void audit_log_format(struct audit_buffer *ab, const char *fmt, ...) void audit_log_n_hex(struct audit_buffer *ab, const unsigned char *buf, size_t len) { - int i, avail, new_len; + int avail; + size_t i, new_len; unsigned char *ptr; struct sk_buff *skb; @@ -2090,7 +2092,12 @@ void audit_log_n_hex(struct audit_buffer *ab, const unsigned char *buf, BUG_ON(!ab->skb); skb = ab->skb; avail = skb_tailroom(skb); - new_len = len<<1; + + if (check_shl_overflow(len, 1, &new_len)) { + audit_log_format(ab, "?"); + return; + } + if (new_len >= avail) { /* Round the buffer request up to the next multiple */ new_len = AUDIT_BUFSIZ*(((new_len-avail)/AUDIT_BUFSIZ) + 1); From e36501b7d4abdcd6d69a7cb901b2f286b7a3d041 Mon Sep 17 00:00:00 2001 From: Benjamin Coddington Date: Thu, 11 Jun 2026 17:02:15 -0400 Subject: [PATCH 0562/2758] NFSv4: include MAY_WRITE in open permission mask for O_TRUNC commit 5140f099ecd8a2f2808b7f7b720ee1bad8468974 upstream. POSIX requires write permission to truncate a file, so an open() that specifies O_TRUNC must be authorized for write access regardless of the O_ACCMODE access mode. nfs_open_permission_mask() builds the access mask passed to nfs_may_open(), which is the local authorization gate for OPENs the client serves itself from a cached write delegation via the can_open_delegated() path in nfs4_try_open_cached(). The mask is derived from O_ACCMODE alone, so an open(O_RDONLY | O_TRUNC) against a file the caller cannot write requests only MAY_READ and passes the local check. The OPEN is then satisfied locally and the truncation is issued to the server as a SETATTR(size=0) over the delegation stateid, which the server accepts under standard write-delegation semantics. POSIX requires that this open fail with EACCES. Include MAY_WRITE in the mask whenever O_TRUNC is set so the local check matches the access the server would have enforced. Suggested-by: Trond Myklebust Fixes: af22f94ae02a ("NFSv4: Simplify _nfs4_do_access()") Cc: stable@vger.kernel.org Signed-off-by: Benjamin Coddington Signed-off-by: Anna Schumaker Signed-off-by: Greg Kroah-Hartman --- fs/nfs/dir.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index e9ce1883288c5..cb7ddaefdfb22 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -3344,6 +3344,8 @@ static int nfs_open_permission_mask(int openflags) mask |= MAY_READ; if ((openflags & O_ACCMODE) != O_RDONLY) mask |= MAY_WRITE; + if (openflags & O_TRUNC) + mask |= MAY_WRITE; } return mask; From 8e0a22bc728e173efafff23c4bb422e5bb8c6d30 Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Wed, 10 Jun 2026 11:04:29 +0200 Subject: [PATCH 0563/2758] rqspinlock: Fix order in raw_res_spin_(un)lock_irq to allow schedule commit b48bd16eb9fc57a463a337ca148516cdf3212d61 upstream. raw_res_spin_unlock_irqrestore() calls raw_res_spin_unlock() and then restores interrupts, this means preemption is enabled when interrupts are still disabled (as part of raw_res_spin_unlock()) so this cannot trigger an actual preemption. This is inconsistent with other spinlock implementations (raw_spin_unlock_irqrestore() and bpf_res_spin_unlock_irqrestore() itself). Adjust the macro to ensure interrupts are enabled before enabling preemption, allowing to schedule at that point. Make the same modification in the error path of raw_res_spin_lock_irqsave(). Fixes: 101acd2e78b1 ("rqspinlock: Add macros for rqspinlock usage") Cc: stable@vger.kernel.org Acked-by: Arnd Bergmann # asm-generic Acked-by: Waiman Long Acked-by: Kumar Kartikeya Dwivedi Signed-off-by: Gabriele Monaco Link: https://lore.kernel.org/r/20260610090431.32427-1-gmonaco@redhat.com Signed-off-by: Alexei Starovoitov Signed-off-by: Greg Kroah-Hartman --- include/asm-generic/rqspinlock.h | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/include/asm-generic/rqspinlock.h b/include/asm-generic/rqspinlock.h index 151d267a496b3..4d46643f46cbc 100644 --- a/include/asm-generic/rqspinlock.h +++ b/include/asm-generic/rqspinlock.h @@ -243,12 +243,20 @@ static __always_inline void res_spin_unlock(rqspinlock_t *lock) ({ \ int __ret; \ local_irq_save(flags); \ - __ret = raw_res_spin_lock(lock); \ - if (__ret) \ + preempt_disable(); \ + __ret = res_spin_lock(lock); \ + if (__ret) { \ local_irq_restore(flags); \ + preempt_enable(); \ + } \ __ret; \ }) -#define raw_res_spin_unlock_irqrestore(lock, flags) ({ raw_res_spin_unlock(lock); local_irq_restore(flags); }) +#define raw_res_spin_unlock_irqrestore(lock, flags) \ + ({ \ + res_spin_unlock(lock); \ + local_irq_restore(flags); \ + preempt_enable(); \ + }) #endif /* __ASM_GENERIC_RQSPINLOCK_H */ From e7da02659c229f73492fb1ed87ceda4090153aaa Mon Sep 17 00:00:00 2001 From: Andrii Kuchmenko Date: Mon, 18 May 2026 17:32:33 +0300 Subject: [PATCH 0564/2758] module: decompress: check return value of module_extend_max_pages() commit 786d2d84416a9a1c1a47b71a68d679d886284be2 upstream. module_extend_max_pages() calls kvrealloc() internally and returns -ENOMEM on allocation failure. The return value is never checked. If the initial allocation fails, info->pages remains NULL and info->max_pages remains 0. Subsequent calls to module_get_next_page() will attempt to dynamically grow the array by calling module_extend_max_pages(info, 0) since info->used_pages is 0. This results in kvrealloc(NULL, 0) returning ZERO_SIZE_PTR, which is treated as a success, leading to a dereference of ZERO_SIZE_PTR and a kernel oops. Fix: add the missing error check after module_extend_max_pages() and return immediately on failure. This matches the pattern used by every other kvrealloc() caller in the module loading path. Fixes: b1ae6dc41eaa ("module: add in-kernel support for decompressing") Cc: Dmitry Torokhov Cc: Luis Chamberlain Cc: stable@vger.kernel.org Signed-off-by: Andrii Kuchmenko Reviewed-by: Christophe Leroy (CS GROUP) [Sami: Corrected the analysis in the commit message.] Signed-off-by: Sami Tolvanen Signed-off-by: Greg Kroah-Hartman --- kernel/module/decompress.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/module/decompress.c b/kernel/module/decompress.c index 36f52a232a120..cce098671be97 100644 --- a/kernel/module/decompress.c +++ b/kernel/module/decompress.c @@ -307,6 +307,8 @@ int module_decompress(struct load_info *info, const void *buf, size_t size) */ n_pages = DIV_ROUND_UP(size, PAGE_SIZE) * 2; error = module_extend_max_pages(info, n_pages); + if (error) + return error; data_size = MODULE_DECOMPRESS_FN(info, buf, size); if (data_size < 0) { From c4c82cdfdf8ac042c6a885d915dd75fc91478b3e Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 25 Jun 2026 22:48:33 -0400 Subject: [PATCH 0565/2758] vt: fix spurious modifier in CSI/cursor key sequences commit e9ad4d5ca309cb517d3f7a85251c3c5328f40f1f upstream. csi_modifier_param() builds the xterm modifier parameter from shift_state, counting KG_SHIFTL/KG_SHIFTR as Shift, KG_ALTGR as Alt and KG_CTRLL/KG_CTRLR as Ctrl in addition to the canonical KG_SHIFT, KG_ALT and KG_CTRL. That is wrong when those weights are not plain modifiers. Keymaps derived from XKB layouts (by kbd's xkbsupport, and by the console-setup used in Debian, Ubuntu and others) encode the active layout group using KG_SHIFTL/KG_SHIFTR: group 1: - group 2: shiftl group 3: shiftr group 4: shiftl | shiftr So while a non-default layout group is selected, KG_SHIFTL and/or KG_SHIFTR are set in shift_state with no Shift key held. csi_modifier_param() then adds a spurious Shift to every cursor and CSI key: pressing Up while group 2 is active emits ESC[1;2A (Shift+Up) instead of ESC[A. KG_ALTGR has the same problem since it is the standard third-level selector. Normal keymaps bind the physical Shift/Ctrl/Alt keys to KG_SHIFT, KG_CTRL and KG_ALT, leaving the left/right and AltGr weights free for layout and level selection. Count only those canonical weights, so genuine modifiers are still encoded while layout/level selectors are not. Fixes: 4af70f151671 ("vt: add modifier support to cursor keys") Reported-by: Alexey Gladkov Closes: https://lore.kernel.org/kbd/aj2gR0Y7sM6i9s2G@example.org/ Cc: stable Signed-off-by: Nicolas Pitre Link: https://patch.msgid.link/20260626024833.3419086-1-nico@fluxnic.net Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/keyboard.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index dfdea08421495..763a3f1b7be08 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -765,16 +765,22 @@ static void k_fn(struct vc_data *vc, unsigned char value, char up_flag) /* * Compute xterm-style modifier parameter for CSI sequences. * Returns 1 + (shift ? 1 : 0) + (alt ? 2 : 0) + (ctrl ? 4 : 0) + * + * Only the canonical modifier weights are counted. The left/right variants + * (KG_SHIFTL, KG_SHIFTR, KG_CTRLL, KG_CTRLR) and KG_ALTGR are commonly + * repurposed as keymap layout-group or level selectors rather than as plain + * modifiers (for instance XKB-derived keymaps select the layout group with + * KG_SHIFTL/KG_SHIFTR), so counting them would encode a spurious modifier. */ static int csi_modifier_param(void) { int mod = 1; - if (shift_state & (BIT(KG_SHIFT) | BIT(KG_SHIFTL) | BIT(KG_SHIFTR))) + if (shift_state & BIT(KG_SHIFT)) mod += 1; - if (shift_state & (BIT(KG_ALT) | BIT(KG_ALTGR))) + if (shift_state & BIT(KG_ALT)) mod += 2; - if (shift_state & (BIT(KG_CTRL) | BIT(KG_CTRLL) | BIT(KG_CTRLR))) + if (shift_state & BIT(KG_CTRL)) mod += 4; return mod; } From 5bfa2814528d1df254a080b271ea31519ad4e919 Mon Sep 17 00:00:00 2001 From: Rochan Avlur Date: Thu, 28 May 2026 07:21:37 -0700 Subject: [PATCH 0566/2758] exfat: preserve benign secondary entries during rename and move commit 942296784b2a9439651750c42f540bf2579b330f upstream. Commit 8258ef28001a ("exfat: handle unreconized benign secondary entries") added cluster freeing for benign secondary entries inside exfat_remove_entries(). However, exfat_remove_entries() is also called from the rename and move paths (exfat_rename_file and exfat_move_file), where the old entry set is being relocated rather than deleted. This causes benign secondary entries such as vendor extension entries to be silently destroyed on rename or cross-directory move, violating the exFAT spec requirement (section 8.2) that implementations preserve unrecognized benign secondary entries. Fix this by adding a free_benign parameter to exfat_remove_entries() so callers can suppress cluster freeing during relocation, and extending exfat_init_ext_entry() to copy trailing benign secondary entries from the old entry set into the new one internally. Also clean up the error paths to delete newly allocated entries on failure. Fixes: 8258ef28001a ("exfat: handle unreconized benign secondary entries") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/linux-fsdevel/CAG7tbBV--waov7XVu2FHQEc6paR92dufS=em9DW5Kzsrpu3iQg@mail.gmail.com/ Signed-off-by: Rochan Avlur Reviewed-by: Yuezhang Mo Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/exfat/dir.c | 50 ++++++++++++++++++++++--- fs/exfat/exfat_fs.h | 5 ++- fs/exfat/namei.c | 89 +++++++++++++++++++++++++++++++++++---------- 3 files changed, 116 insertions(+), 28 deletions(-) diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c index 561fd23492183..6af3f6b932e65 100644 --- a/fs/exfat/dir.c +++ b/fs/exfat/dir.c @@ -470,32 +470,70 @@ static void exfat_free_benign_secondary_clusters(struct inode *inode, exfat_free_cluster(inode, &dir); } +/* + * exfat_init_ext_entry - initialize extension entries in a directory entry set + * @es: target entry set + * @num_entries: number of entries excluding benign secondary entries + * @p_uniname: filename to store + * @old_es: optional source entry set with benign secondary entries, or NULL + * @num_extra: number of benign secondary entries to copy from @old_es + * + * Set up the file, stream extension, and filename entries in @es, optionally + * preserving @num_extra benign secondary entries from @old_es. @es and @old_es + * may refer to the same entry set; excess entries are marked as deleted. + */ void exfat_init_ext_entry(struct exfat_entry_set_cache *es, int num_entries, - struct exfat_uni_name *p_uniname) + struct exfat_uni_name *p_uniname, + struct exfat_entry_set_cache *old_es, int num_extra) { - int i; + int i, src_start = 0, old_num; unsigned short *uniname = p_uniname->name; struct exfat_dentry *ep; - es->num_entries = num_entries; + if (WARN_ON(num_extra < 0 || (num_extra && (!old_es || + old_es->num_entries < ES_IDX_FIRST_FILENAME + num_extra)))) + num_extra = 0; + + /* + * Save old entry count and source position before modifying + * es->num_entries, since old_es and es may point to the same + * entry set. + */ + old_num = es->num_entries; + if (old_es && num_extra > 0) + src_start = old_es->num_entries - num_extra; + + es->num_entries = num_entries + num_extra; ep = exfat_get_dentry_cached(es, ES_IDX_FILE); - ep->dentry.file.num_ext = (unsigned char)(num_entries - 1); + ep->dentry.file.num_ext = (unsigned char)(num_entries - 1 + num_extra); ep = exfat_get_dentry_cached(es, ES_IDX_STREAM); ep->dentry.stream.name_len = p_uniname->name_len; ep->dentry.stream.name_hash = cpu_to_le16(p_uniname->name_hash); + if (old_es && num_extra > 0) { + for (i = 0; i < num_extra; i++) + *exfat_get_dentry_cached(es, num_entries + i) = + *exfat_get_dentry_cached(old_es, src_start + i); + } + for (i = ES_IDX_FIRST_FILENAME; i < num_entries; i++) { ep = exfat_get_dentry_cached(es, i); exfat_init_name_entry(ep, uniname); uniname += EXFAT_FILE_NAME_LEN; } + /* Mark excess old entries as deleted (in-place shrink) */ + for (i = num_entries + num_extra; i < old_num; i++) { + ep = exfat_get_dentry_cached(es, i); + exfat_set_entry_type(ep, TYPE_DELETED); + } + exfat_update_dir_chksum(es); } void exfat_remove_entries(struct inode *inode, struct exfat_entry_set_cache *es, - int order) + int order, bool free_benign) { int i; struct exfat_dentry *ep; @@ -503,7 +541,7 @@ void exfat_remove_entries(struct inode *inode, struct exfat_entry_set_cache *es, for (i = order; i < es->num_entries; i++) { ep = exfat_get_dentry_cached(es, i); - if (exfat_get_entry_type(ep) & TYPE_BENIGN_SEC) + if (free_benign && (exfat_get_entry_type(ep) & TYPE_BENIGN_SEC)) exfat_free_benign_secondary_clusters(inode, ep); exfat_set_entry_type(ep, TYPE_DELETED); diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h index 89ef5368277f8..e22b4ca3ec7fb 100644 --- a/fs/exfat/exfat_fs.h +++ b/fs/exfat/exfat_fs.h @@ -524,9 +524,10 @@ void exfat_init_dir_entry(struct exfat_entry_set_cache *es, unsigned int type, unsigned int start_clu, unsigned long long size, struct timespec64 *ts); void exfat_init_ext_entry(struct exfat_entry_set_cache *es, int num_entries, - struct exfat_uni_name *p_uniname); + struct exfat_uni_name *p_uniname, + struct exfat_entry_set_cache *old_es, int num_extra); void exfat_remove_entries(struct inode *inode, struct exfat_entry_set_cache *es, - int order); + int order, bool free_benign); void exfat_update_dir_chksum(struct exfat_entry_set_cache *es); int exfat_calc_num_entries(struct exfat_uni_name *p_uniname); int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c index 2c5636634b4a4..76b2e2db80fba 100644 --- a/fs/exfat/namei.c +++ b/fs/exfat/namei.c @@ -503,7 +503,7 @@ static int exfat_add_entry(struct inode *inode, const char *path, * the first cluster is not determined yet. (0) */ exfat_init_dir_entry(&es, type, start_clu, clu_size, &ts); - exfat_init_ext_entry(&es, num_entries, &uniname); + exfat_init_ext_entry(&es, num_entries, &uniname, NULL, 0); ret = exfat_put_dentry_set(&es, IS_DIRSYNC(inode)); if (ret) @@ -814,7 +814,7 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry) exfat_set_volume_dirty(sb); /* update the directory entry */ - exfat_remove_entries(inode, &es, ES_IDX_FILE); + exfat_remove_entries(inode, &es, ES_IDX_FILE, true); err = exfat_put_dentry_set(&es, IS_DIRSYNC(inode)); if (err) @@ -969,7 +969,7 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry) exfat_set_volume_dirty(sb); - exfat_remove_entries(inode, &es, ES_IDX_FILE); + exfat_remove_entries(inode, &es, ES_IDX_FILE, true); err = exfat_put_dentry_set(&es, IS_DIRSYNC(dir)); if (err) @@ -996,6 +996,23 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry) return err; } +/* + * Count benign secondary entries beyond the filename entries. + * Returns the count, or -EIO if the entry set is inconsistent. + */ +static int exfat_count_extra_entries(struct exfat_entry_set_cache *es) +{ + struct exfat_dentry *stream; + unsigned int name_entries; + int extra; + + stream = exfat_get_dentry_cached(es, ES_IDX_STREAM); + name_entries = EXFAT_FILENAME_ENTRY_NUM(stream->dentry.stream.name_len); + extra = es->num_entries - (ES_IDX_FIRST_FILENAME + name_entries); + + return extra >= 0 ? extra : -EIO; +} + static int exfat_rename_file(struct inode *parent_inode, struct exfat_uni_name *p_uniname, struct exfat_inode_info *ei) { @@ -1004,6 +1021,7 @@ static int exfat_rename_file(struct inode *parent_inode, struct super_block *sb = parent_inode->i_sb; struct exfat_entry_set_cache old_es, new_es; int sync = IS_DIRSYNC(parent_inode); + unsigned int num_extra_entries, num_total_entries; if (unlikely(exfat_forced_shutdown(sb))) return -EIO; @@ -1013,19 +1031,23 @@ static int exfat_rename_file(struct inode *parent_inode, return num_new_entries; ret = exfat_get_dentry_set_by_ei(&old_es, sb, ei); - if (ret) { - ret = -EIO; - return ret; - } + if (ret) + return -EIO; epold = exfat_get_dentry_cached(&old_es, ES_IDX_FILE); - if (old_es.num_entries < num_new_entries) { + ret = exfat_count_extra_entries(&old_es); + if (ret < 0) + goto put_old_es; + num_extra_entries = ret; + num_total_entries = num_new_entries + num_extra_entries; + + if (old_es.num_entries < num_total_entries) { int newentry; struct exfat_chain dir; newentry = exfat_find_empty_entry(parent_inode, &dir, - num_new_entries, &new_es); + num_total_entries, &new_es); if (newentry < 0) { ret = newentry; /* -EIO or -ENOSPC */ goto put_old_es; @@ -1042,13 +1064,23 @@ static int exfat_rename_file(struct inode *parent_inode, epnew = exfat_get_dentry_cached(&new_es, ES_IDX_STREAM); *epnew = *epold; - exfat_init_ext_entry(&new_es, num_new_entries, p_uniname); + exfat_init_ext_entry(&new_es, num_new_entries, p_uniname, + &old_es, num_extra_entries); ret = exfat_put_dentry_set(&new_es, sync); - if (ret) + if (ret) { + /* Best-effort delete to avoid duplicate entries */ + if (!exfat_get_dentry_set(&new_es, sb, + &dir, newentry, + ES_ALL_ENTRIES)) { + exfat_remove_entries(parent_inode, &new_es, + ES_IDX_FILE, false); + exfat_put_dentry_set(&new_es, false); + } goto put_old_es; + } - exfat_remove_entries(parent_inode, &old_es, ES_IDX_FILE); + exfat_remove_entries(parent_inode, &old_es, ES_IDX_FILE, false); ei->dir = dir; ei->entry = newentry; } else { @@ -1057,8 +1089,8 @@ static int exfat_rename_file(struct inode *parent_inode, ei->attr |= EXFAT_ATTR_ARCHIVE; } - exfat_remove_entries(parent_inode, &old_es, ES_IDX_FIRST_FILENAME + 1); - exfat_init_ext_entry(&old_es, num_new_entries, p_uniname); + exfat_init_ext_entry(&old_es, num_new_entries, p_uniname, + &old_es, num_extra_entries); } return exfat_put_dentry_set(&old_es, sync); @@ -1074,6 +1106,7 @@ static int exfat_move_file(struct inode *parent_inode, struct exfat_dentry *epmov, *epnew; struct exfat_entry_set_cache mov_es, new_es; struct exfat_chain newdir; + unsigned int num_extra_entries, num_total_entries; num_new_entries = exfat_calc_num_entries(p_uniname); if (num_new_entries < 0) @@ -1083,8 +1116,14 @@ static int exfat_move_file(struct inode *parent_inode, if (ret) return -EIO; + ret = exfat_count_extra_entries(&mov_es); + if (ret < 0) + goto put_mov_es; + num_extra_entries = ret; + num_total_entries = num_new_entries + num_extra_entries; + newentry = exfat_find_empty_entry(parent_inode, &newdir, - num_new_entries, &new_es); + num_total_entries, &new_es); if (newentry < 0) { ret = newentry; /* -EIO or -ENOSPC */ goto put_mov_es; @@ -1102,21 +1141,31 @@ static int exfat_move_file(struct inode *parent_inode, epnew = exfat_get_dentry_cached(&new_es, ES_IDX_STREAM); *epnew = *epmov; - exfat_init_ext_entry(&new_es, num_new_entries, p_uniname); - exfat_remove_entries(parent_inode, &mov_es, ES_IDX_FILE); + exfat_init_ext_entry(&new_es, num_new_entries, p_uniname, + &mov_es, num_extra_entries); + + exfat_remove_entries(parent_inode, &mov_es, ES_IDX_FILE, false); ei->dir = newdir; ei->entry = newentry; ret = exfat_put_dentry_set(&new_es, IS_DIRSYNC(parent_inode)); - if (ret) + if (ret) { + /* Best-effort delete to avoid duplicate entries */ + if (!exfat_get_dentry_set(&new_es, parent_inode->i_sb, + &newdir, newentry, + ES_ALL_ENTRIES)) { + exfat_remove_entries(parent_inode, &new_es, + ES_IDX_FILE, false); + exfat_put_dentry_set(&new_es, false); + } goto put_mov_es; + } return exfat_put_dentry_set(&mov_es, IS_DIRSYNC(parent_inode)); put_mov_es: exfat_put_dentry_set(&mov_es, false); - return ret; } @@ -1190,7 +1239,7 @@ static int __exfat_rename(struct inode *old_parent_inode, goto del_out; } - exfat_remove_entries(new_inode, &es, ES_IDX_FILE); + exfat_remove_entries(new_inode, &es, ES_IDX_FILE, true); ret = exfat_put_dentry_set(&es, IS_DIRSYNC(new_inode)); if (ret) From c8e041c68c0bbb73aa62371ee63947bb6949d8b2 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Fri, 12 Jun 2026 14:29:06 -0500 Subject: [PATCH 0567/2758] exfat: bound uniname advance in exfat_find_dir_entry() commit 3a1230e7b043c62737b05a3e9275ca83a43ad20a upstream. In exfat_find_dir_entry(), each TYPE_EXTEND (file name) entry advances the output pointer by a fixed amount while the loop guard only tracks the accumulated name length: if (++order == 2) uniname = p_uniname->name; else uniname += EXFAT_FILE_NAME_LEN; len = exfat_extract_uni_name(ep, entry_uniname); name_len += len; unichar = *(uniname+len); *(uniname+len) = 0x0; uniname grows by EXFAT_FILE_NAME_LEN (15) per name entry, but name_len grows only by the actual extracted length, which is shorter when a name fragment contains an early NUL. The only guard is `name_len >= MAX_NAME_LENGTH`, so a crafted directory with many short name fragments lets uniname run far past the p_uniname->name[MAX_NAME_LENGTH + 3] buffer while name_len stays small, causing an out-of-bounds read and write at *(uniname+len). The sibling extractor exfat_get_uniname_from_ext_entry() already stops on a short fragment (the lockstep `len != EXFAT_FILE_NAME_LEN` guard added in commit d42334578eba ("exfat: check if filename entries exceeds max filename length")); exfat_find_dir_entry() never got the equivalent. Track the per-entry write offset as a count and reject a fragment once the offset, or the offset plus the extracted length, would exceed MAX_NAME_LENGTH, before forming the output pointer. Fixes: ca06197382bd ("exfat: add directory operations") Cc: stable@vger.kernel.org Suggested-by: Namjae Jeon Signed-off-by: Bryam Vargas Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/exfat/dir.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c index 6af3f6b932e65..500094c60c138 100644 --- a/fs/exfat/dir.c +++ b/fs/exfat/dir.c @@ -1067,6 +1067,7 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, if (entry_type == TYPE_EXTEND) { unsigned short entry_uniname[16], unichar; + unsigned int offset; if (step != DIRENT_STEP_NAME || name_len >= MAX_NAME_LENGTH) { @@ -1075,13 +1076,15 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, continue; } - if (++order == 2) - uniname = p_uniname->name; - else - uniname += EXFAT_FILE_NAME_LEN; - + offset = (++order - 2) * EXFAT_FILE_NAME_LEN; len = exfat_extract_uni_name(ep, entry_uniname); brelse(bh); + if (offset > MAX_NAME_LENGTH || + len > MAX_NAME_LENGTH - offset) { + step = DIRENT_STEP_FILE; + continue; + } + uniname = p_uniname->name + offset; name_len += len; unichar = *(uniname+len); From 84ab222021c2e17ed68f48091dbecb6ab3565b70 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 4 Mar 2026 17:30:27 +0900 Subject: [PATCH 0568/2758] NTB: epf: Fix request_irq() unwind in ntb_epf_init_isr() commit fcba26efe5efc7441f5505f4ccc69791214b40be upstream. ntb_epf_init_isr() requests multiple MSI/MSI-X vectors in a loop. If request_irq() fails part-way through, it jumps straight to pci_free_irq_vectors() without freeing already requested IRQs. Fix the error path by freeing any successfully requested IRQs before releasing the vectors. Fixes: 812ce2f8d14e ("NTB: Add support for EPF PCI Non-Transparent Bridge") Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Dave Jiang Cc: stable@vger.kernel.org # v5.12+ Link: https://patch.msgid.link/20260304083028.1391068-2-den@valinux.co.jp Signed-off-by: Greg Kroah-Hartman --- drivers/ntb/hw/epf/ntb_hw_epf.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c index 8aaf8f9bc69c9..15d99d08f84f9 100644 --- a/drivers/ntb/hw/epf/ntb_hw_epf.c +++ b/drivers/ntb/hw/epf/ntb_hw_epf.c @@ -357,7 +357,7 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max) 0, "ntb_epf", ndev); if (ret) { dev_err(dev, "Failed to request irq\n"); - goto err_request_irq; + goto err_free_irq; } } @@ -367,16 +367,14 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max) argument | irq); if (ret) { dev_err(dev, "Failed to configure doorbell\n"); - goto err_configure_db; + goto err_free_irq; } return 0; -err_configure_db: - for (i = 0; i < ndev->db_count + 1; i++) +err_free_irq: + while (i--) free_irq(pci_irq_vector(pdev, i), ndev); - -err_request_irq: pci_free_irq_vectors(pdev); return ret; From bc773b8b4c819408eb37edea0dc2f0132ccbba89 Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Sat, 6 Jun 2026 20:17:54 -0600 Subject: [PATCH 0569/2758] riscv: mm: Define DIRECT_MAP_PHYSMEM_END commit f3336b48cf9d3f2d1fc78e3289c0ded2f00876ee upstream. On RISC-V, the actual mappable range of physical address space is dependent on the current MMU mode i.e. satp_mode (See Documentation/arch/riscv/vm-layout.rst). Define the DIRECT_MAP_PHYSMEM_END macro based on the existing virtual address space layout macros to expose this information to get_free_mem_region(). Otherwise, it returns a region that couldn't be mapped, which breaks ZONE_DEVICE. Cc: stable@vger.kernel.org # v6.13+ Tested-by: Han Gao # SG2044 Signed-off-by: Vivian Wang Link: https://patch.msgid.link/20260309-riscv-sparsemem-vmemmap-limits-v1-2-f40efe18e3cd@iscas.ac.cn Signed-off-by: Paul Walmsley Signed-off-by: Greg Kroah-Hartman --- arch/riscv/include/asm/pgtable.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h index a1a7c6520a095..5d5756bda82e3 100644 --- a/arch/riscv/include/asm/pgtable.h +++ b/arch/riscv/include/asm/pgtable.h @@ -93,6 +93,16 @@ */ #define vmemmap ((struct page *)VMEMMAP_START - vmemmap_start_pfn) +/* Needed to limit get_free_mem_region() */ +#if defined(CONFIG_FLATMEM) +#define DIRECT_MAP_PHYSMEM_END (phys_ram_base + KERN_VIRT_SIZE - 1) +#elif defined(CONFIG_SPARSEMEM_VMEMMAP) +#define DIRECT_MAP_PHYSMEM_END \ + ((vmemmap_start_pfn + VMEMMAP_SIZE / sizeof(struct page)) * PAGE_SIZE - 1) +#elif defined(CONFIG_SPARSEMEM) +/* DIRECT_MAP_PHYSMEM_END is not limited by VA space assignment in this case */ +#endif + #define PCI_IO_SIZE SZ_16M #define PCI_IO_END VMEMMAP_START #define PCI_IO_START (PCI_IO_END - PCI_IO_SIZE) From ede985ff4b569ed2454024f8bb107a6729fe08aa Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Tue, 3 Mar 2026 13:29:49 +0800 Subject: [PATCH 0570/2758] riscv: mm: Unconditionally sfence.vma for spurious fault commit 1b2c6b56a9fa0dcbef461039937de22b1cbecc7d upstream. Svvptc does not guarantee that it's safe to just return here. Since we have already cleared our bit, if, theoretically, the bounded timeframe for the accessed page to become valid still hasn't happened after sret, we could fault again and actually crash. Hopefully, these spurious faults should be rare enough that this is an acceptable slowdown. Cc: stable@vger.kernel.org Fixes: 503638e0babf ("riscv: Stop emitting preventive sfence.vma for new vmalloc mappings") Signed-off-by: Vivian Wang Link: https://patch.msgid.link/20260303-handle-kfence-protect-spurious-fault-v2-5-f80d8354d79d@iscas.ac.cn Signed-off-by: Paul Walmsley Signed-off-by: Greg Kroah-Hartman --- arch/riscv/kernel/entry.S | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index 6c14ed7a56135..e189c477c0e23 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -75,8 +75,11 @@ /* Atomically reset the current cpu bit in new_vmalloc */ amoxor.d a0, a1, (a0) - /* Only emit a sfence.vma if the uarch caches invalid entries */ - ALTERNATIVE("sfence.vma", "nop", 0, RISCV_ISA_EXT_SVVPTC, 1) + /* + * A sfence.vma is required here. Even if we had Svvptc, there's no + * guarantee that after returning we wouldn't just fault again. + */ + sfence.vma REG_L a0, TASK_TI_A0(tp) REG_L a1, TASK_TI_A1(tp) From 9adedf9c885c16207c0b3adf787281e939d15d27 Mon Sep 17 00:00:00 2001 From: Hao Ge Date: Wed, 13 May 2026 16:25:25 +0800 Subject: [PATCH 0571/2758] lib/test_hmm: use kvfree() to free kvcalloc() allocations commit 59f19bf6f119eecfa16355186b593abba8eb5198 upstream. Coccinelle scripts/coccinelle/api/kfree_mismatch.cocci reports the following warnings: lib/test_hmm.c:1256:15-16: WARNING kvmalloc is used to allocate this memory at line 1191 lib/test_hmm.c:1257:15-16: WARNING kvmalloc is used to allocate this memory at line 1196 Fix this by replacing kfree() with kvfree() to correctly handle the vmalloc() fallback path of kvcalloc(). Link: https://lore.kernel.org/20260513082525.154036-1-hao.ge@linux.dev Fixes: 775465fd26a3 ("lib/test_hmm: add zone device private THP test infrastructure") Signed-off-by: Hao Ge Acked-by: Balbir Singh Cc: Jason Gunthorpe Cc: Leon Romanovsky Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- lib/test_hmm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/test_hmm.c b/lib/test_hmm.c index 2135049157372..38996c4baa408 100644 --- a/lib/test_hmm.c +++ b/lib/test_hmm.c @@ -1253,8 +1253,8 @@ static int dmirror_migrate_to_device(struct dmirror *dmirror, mmap_read_unlock(mm); mmput(mm); free_mem: - kfree(src_pfns); - kfree(dst_pfns); + kvfree(src_pfns); + kvfree(dst_pfns); return ret; } From 246f0713360a10126fb455fe2baee51bdc35f7a4 Mon Sep 17 00:00:00 2001 From: Anthony Yznaga Date: Wed, 15 Apr 2026 20:39:37 -0700 Subject: [PATCH 0572/2758] mm: fix mmap errno value when MAP_DROPPABLE is not supported commit d86c9e971af2315119a78c564a802fafcebf1b6b upstream. Patch series "fix MAP_DROPPABLE not supported errno", v4. Mark Brown reported seeing a regression in -next on 32 bit arm with the mlock selftests. Before exiting and marking the tests failed, the following message was logged after an attempt to create a MAP_DROPPABLE mapping: Bail out! mmap error: Unknown error 524 It turns out error 524 is ENOTSUPP which is an error that userspace is not supposed to see, but it indicates in this instance that MAP_DROPPABLE is not supported. The first patch changes the errno returned to EOPNOTSUPP. The second patch is a second version of a prior patch to introduce selftests to verify locking behavior with droppable mappings with the additional change to skip the tests when MAP_DROPPABLE is not supported. The third patch fixes the MAP_DROPPABLE selftest so that it is run by the framework and skips if MAP_DROPPABLE is not supported. This patch (of 3): On configs where MAP_DROPPABLE is not supported (currently any 32-bit config except for PPC32), mmap fails with errno set to ENOTSUPP. However, ENOTSUPP is not a standard error value that userspace knows about. The acceptable userspace-visible errno to use is EOPNOTSUPP. checkpatch.pl has a warning to this effect. Link: https://lore.kernel.org/20260416033939.49981-1-anthony.yznaga@oracle.com Link: https://lore.kernel.org/20260416033939.49981-2-anthony.yznaga@oracle.com Fixes: 9651fcedf7b9 ("mm: add MAP_DROPPABLE for designating always lazily freeable mappings") Signed-off-by: Anthony Yznaga Acked-by: David Hildenbrand (Arm) Acked-by: Vlastimil Babka (SUSE) Reported-by: Mark Brown Reviewed-by: Pedro Falcato Reviewed-by: Lorenzo Stoakes (Oracle) Cc: Jann Horn Cc: Jason A. Donenfeld Cc: Liam Howlett Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/mmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/mmap.c b/mm/mmap.c index 5754d1c364624..2311ae7c2ff45 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -504,7 +504,7 @@ unsigned long do_mmap(struct file *file, unsigned long addr, break; case MAP_DROPPABLE: if (VM_DROPPABLE == VM_NONE) - return -ENOTSUPP; + return -EOPNOTSUPP; /* * A locked or stack area makes no sense to be droppable. * From fceca62a095eb211999d6db77e4526bc56914e61 Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Thu, 11 Jun 2026 12:01:55 +0200 Subject: [PATCH 0573/2758] selftests: mm: fix and speedup "droppable" test commit cc13a7a618fe8354f16d74c06aaf9565a68e9ebd upstream. The droppable test currently relies on creating memory pressure in a child process to trigger dropping the droppable pages. That not only takes a long time on some machines (allocating and filling all that memory), on large machines this will not work as we hardcode the area size to 134217728 bytes. ... further, we rely on timeouts to detect that memory was not dropped, which is really suboptimal. Instead, let's just use MADV_PAGEOUT on a 2 MiB region. MADV_PAGEOUT works with droppable memory even without swap. There is the low chance of MADV_PAGEOUT failing to drop a page because of speculative references. We'll wait 1s and retry 10 times to rule that unlikely case out as best as we can. On a machine without swap: $ ./droppable TAP version 13 1..1 ok 1 madvise(MADV_PAGEOUT) behavior # Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 Link: https://lore.kernel.org/20260611-droppable_test-v1-1-b6a73d99f658@kernel.org Fixes: 9651fcedf7b9 ("mm: add MAP_DROPPABLE for designating always lazily freeable mappings") Signed-off-by: David Hildenbrand (Arm) Reported-by: Aishwarya TCV Tested-by: Sarthak Sharma Tested-by: Lance Yang Reviewed-by: Dev Jain Reviewed-by: SeongJae Park Tested-by: Lorenzo Stoakes Reviewed-by: Lorenzo Stoakes Reviewed-by: Jason A. Donenfeld Cc: Anthony Yznaga Cc: Liam R. Howlett Cc: Mark Brown Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/mm/droppable.c | 46 +++++++++++++++----------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/tools/testing/selftests/mm/droppable.c b/tools/testing/selftests/mm/droppable.c index 44940f75c461d..5f74827a714db 100644 --- a/tools/testing/selftests/mm/droppable.c +++ b/tools/testing/selftests/mm/droppable.c @@ -17,10 +17,10 @@ int main(int argc, char *argv[]) { - size_t alloc_size = 134217728; - size_t page_size = getpagesize(); + const size_t alloc_size = 2 * 1024 * 1024; + int retry_count = 10; + bool dropped; void *alloc; - pid_t child; ksft_print_header(); ksft_set_plan(1); @@ -28,26 +28,32 @@ int main(int argc, char *argv[]) alloc = mmap(0, alloc_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_DROPPABLE, -1, 0); assert(alloc != MAP_FAILED); memset(alloc, 'A', alloc_size); - for (size_t i = 0; i < alloc_size; i += page_size) - assert(*(uint8_t *)(alloc + i)); - - child = fork(); - assert(child >= 0); - if (!child) { - for (;;) - *(char *)malloc(page_size) = 'B'; - } - for (bool done = false; !done;) { - for (size_t i = 0; i < alloc_size; i += page_size) { - if (!*(uint8_t *)(alloc + i)) { - done = true; - break; + while (retry_count--) { + if (madvise(alloc, alloc_size, MADV_PAGEOUT)) { + if (errno == EINVAL) { + ksft_test_result_skip("madvise(MADV_PAGEOUT) not supported\n"); + exit(KSFT_SKIP); } + ksft_test_result_fail("madvise(MADV_PAGEOUT) error: %s\n", strerror(errno)); + exit(KSFT_FAIL); } + + dropped = memchr(alloc, 'A', alloc_size) == NULL; + + /* + * Speculative reference can temporarily prevent some + * pages from getting dropped. So sleep and retry. + * + * If a page is not droppable for 10s, something + * is seriously messed up and we want to fail. + */ + if (dropped) + break; + sleep(1); } - kill(child, SIGTERM); - ksft_test_result_pass("MAP_DROPPABLE: PASS\n"); - exit(KSFT_PASS); + ksft_test_result(dropped, "madvise(MADV_PAGEOUT) behavior\n"); + + ksft_finished(); } From 377b1cd6bbcf327338cd951cc2fd74bc75540235 Mon Sep 17 00:00:00 2001 From: Ketan Date: Tue, 23 Jun 2026 02:48:04 +0530 Subject: [PATCH 0574/2758] mm: page_ext: add count limit to page_ext_iter_next to prevent invalid PFN access commit ffd017237cfe99e6e5602ab14179b0e6878a0840 upstream. The page_ext iteration API does not validate if the PFN still belongs to a valid section while advancing the iterator. When dynamically adding memory in the hotplug path, it can lead to a NULL pointer dereference during page_ext_lookup at the boundary of the last valid section when iterator count equals __pgcount. The for_each_page_ext() macro calls page_ext_iter_next() as its loop increment. for_each_page_ext() does a "__page_ext = page_ext_iter_next(&__iter)" at the end. This causes page_ext_iter_next() to increment iter->index past __pgcount and call page_ext_lookup(start_pfn + __pgcount). During memory hotplug (online), the PFN at start_pfn + __pgcount may belong to a section that has not yet been initialized, causing page_ext_lookup() to trigger a NULL pointer dereference. [ 14.555124][ T846] Call trace: [ 14.555125][ T846] lookup_page_ext+0x6c/0x108 (P) [ 14.555127][ T846] page_ext_lookup+0x30/0x3c [ 14.555129][ T846] __reset_page_owner+0x11c/0x260 [ 14.571201][ T846] __free_pages_ok+0x5e8/0x8e0 [ 14.571204][ T846] __free_pages_core+0x78/0xf0 [ 14.571206][ T846] generic_online_page+0x14/0x24 [ 14.597782][ T846] online_pages+0x178/0x30c [ 14.597784][ T846] memory_block_change_state+0x284/0x32c [ 14.597787][ T846] memory_subsys_online+0x4c/0x64 [ 14.597789][ T846] device_online+0x88/0xb0 [ 14.597791][ T846] online_memory_block+0x30/0x40 [ 14.597793][ T846] walk_memory_blocks+0xac/0xe8 [ 14.597794][ T846] add_memory_resource+0x280/0x298 [ 14.656161][ T846] add_memory+0x60/0x98 Move the iteration boundary enforcement inside the iterator functions, so callers cannot inadvertently access beyond the requested range. Link: https://lore.kernel.org/20260623-page_ext-v3-1-a89799a5367c@oss.qualcomm.com Fixes: 9039b9096ea2 ("mm: page_ext: add an iteration API for page extensions") Signed-off-by: Ketan Kishore Suggested-by: David Hildenbrand Suggested-by: Matthew Wilcox Acked-by: Zi Yan Acked-by: David Hildenbrand (Arm) Cc: Brendan Jackman Cc: Johannes Weiner Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Luiz Capitulino Cc: Michal Hocko Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- include/linux/page_ext.h | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/include/linux/page_ext.h b/include/linux/page_ext.h index 61e876e255e89..f23d4b218da07 100644 --- a/include/linux/page_ext.h +++ b/include/linux/page_ext.h @@ -120,14 +120,18 @@ struct page_ext_iter { * page_ext_iter_begin() - Prepare for iterating through page extensions. * @iter: page extension iterator. * @pfn: PFN of the page we're interested in. + * @count: maximum number of page extensions to return. * * Must be called with RCU read lock taken. * * Return: NULL if no page_ext exists for this page. */ static inline struct page_ext *page_ext_iter_begin(struct page_ext_iter *iter, - unsigned long pfn) + unsigned long pfn, unsigned long count) { + if (!count) + return NULL; + iter->index = 0; iter->start_pfn = pfn; iter->page_ext = page_ext_lookup(pfn); @@ -138,19 +142,22 @@ static inline struct page_ext *page_ext_iter_begin(struct page_ext_iter *iter, /** * page_ext_iter_next() - Get next page extension * @iter: page extension iterator. + * @count: maximum number of page extensions to return. * * Must be called with RCU read lock taken. * * Return: NULL if no next page_ext exists. */ -static inline struct page_ext *page_ext_iter_next(struct page_ext_iter *iter) +static inline struct page_ext *page_ext_iter_next(struct page_ext_iter *iter, + unsigned long count) { unsigned long pfn; if (WARN_ON_ONCE(!iter->page_ext)) return NULL; - iter->index++; + if (++iter->index >= count) + return NULL; pfn = iter->start_pfn + iter->index; if (page_ext_iter_next_fast_possible(pfn)) @@ -183,9 +190,9 @@ static inline struct page_ext *page_ext_iter_get(const struct page_ext_iter *ite * IMPORTANT: must be called with RCU read lock taken. */ #define for_each_page_ext(__page, __pgcount, __page_ext, __iter) \ - for (__page_ext = page_ext_iter_begin(&__iter, page_to_pfn(__page));\ - __page_ext && __iter.index < __pgcount; \ - __page_ext = page_ext_iter_next(&__iter)) + for (__page_ext = page_ext_iter_begin(&__iter, page_to_pfn(__page), __pgcount); \ + __page_ext; \ + __page_ext = page_ext_iter_next(&__iter, __pgcount)) #else /* !CONFIG_PAGE_EXTENSION */ struct page_ext; From 04ba248d02d9eaa3d9077b00a6134caa75fa3e90 Mon Sep 17 00:00:00 2001 From: Pedro Falcato Date: Thu, 25 Jun 2026 16:38:53 +0100 Subject: [PATCH 0575/2758] mm: do file ownership checks with the proper mount idmap commit e187bc02f8fa4226d62814592cf064ee4557c470 upstream. Ever since idmapped mounts were introduced, inode ownership checks (for side-channel protection) in mincore() and madvise(MADV_PAGEOUT) were done against the nop_mnt_idmap, which completely ignores the file's mount's idmap. This results in odd edgecases like: 1) mount/bind-mount with an idmap userA:userB:1 2) userB runs an owner_or_capable() check on file that is owned by userA on-disk/in-memory, but owned by userB after idmap translation 3) owner_or_capable() mysteriously fails as the correct idmap wasn't supplied In the case of mincore/madvise MADV_PAGEOUT, this is usually benign, because file_permission(file, MAY_WRITE) will probably succeed, as it uses the proper idmap internally, but it does not need to be the case on e.g a 0444 file where even the owner itself doesn't have permissions to write to it. Since this is clearly not trivial to get right, introduce a file_owner_or_capable() that can carry the correct semantics, and switch the various users in mm to it. The issue was found by manual code inspection & an off-list discussion with Jan Kara. Link: https://lore.kernel.org/20260625153853.913949-1-pfalcato@suse.de Fixes: 9caccd41541a ("fs: introduce MOUNT_ATTR_IDMAP") Signed-off-by: Pedro Falcato Reviewed-by: Jan Kara Reviewed-by: Christian Brauner (Amutable) Acked-by: David Hildenbrand (Arm) Cc: Al Viro Cc: Jann Horn Cc: Liam R. Howlett Cc: Matthew Wilcox (Oracle) Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- include/linux/fs.h | 5 +++++ mm/filemap.c | 2 +- mm/madvise.c | 3 +-- mm/mincore.c | 3 +-- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/include/linux/fs.h b/include/linux/fs.h index 11559c513dfbb..f93b216149cc9 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2438,6 +2438,11 @@ static inline struct mnt_idmap *file_mnt_idmap(const struct file *file) return mnt_idmap(file->f_path.mnt); } +static inline bool file_owner_or_capable(const struct file *file) +{ + return inode_owner_or_capable(file_mnt_idmap(file), file_inode(file)); +} + /** * is_idmapped_mnt - check whether a mount is mapped * @mnt: the mount to check diff --git a/mm/filemap.c b/mm/filemap.c index 4e636647100c1..b72f8c23c3226 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -4671,7 +4671,7 @@ static inline bool can_do_cachestat(struct file *f) { if (f->f_mode & FMODE_WRITE) return true; - if (inode_owner_or_capable(file_mnt_idmap(f), file_inode(f))) + if (file_owner_or_capable(f)) return true; return file_permission(f, MAY_WRITE) == 0; } diff --git a/mm/madvise.c b/mm/madvise.c index 69708e953cf56..5665785e5ac38 100644 --- a/mm/madvise.c +++ b/mm/madvise.c @@ -336,8 +336,7 @@ static inline bool can_do_file_pageout(struct vm_area_struct *vma) * otherwise we'd be including shared non-exclusive mappings, which * opens a side channel. */ - return inode_owner_or_capable(&nop_mnt_idmap, - file_inode(vma->vm_file)) || + return file_owner_or_capable(vma->vm_file) || file_permission(vma->vm_file, MAY_WRITE) == 0; } diff --git a/mm/mincore.c b/mm/mincore.c index 296f2e3922b5f..c8757c5085bf9 100644 --- a/mm/mincore.c +++ b/mm/mincore.c @@ -227,8 +227,7 @@ static inline bool can_do_mincore(struct vm_area_struct *vma) * for writing; otherwise we'd be including shared non-exclusive * mappings, which opens a side channel. */ - return inode_owner_or_capable(&nop_mnt_idmap, - file_inode(vma->vm_file)) || + return file_owner_or_capable(vma->vm_file) || file_permission(vma->vm_file, MAY_WRITE) == 0; } From 913324904ce97147dc35fadd71750255d928ee12 Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Sun, 28 Jun 2026 18:11:18 +0800 Subject: [PATCH 0576/2758] selftests/mm: pagemap_ioctl: use the correct page size for transact_test() commit dccf636bf1e68c3fda92f0c9e1018ab7e0ac8b2c upstream. There are several places in transact_test() where we use the hardcoded 0x1000 (4k) as page size, which is not always correct for architectures supporting multiple page sizes. Switch to use the correct page size. Otherwise ./ksft_pagemap.sh on a 16k-page-size arm64 box fails with $ ./ksft_pagemap.sh [...] # ok 96 mprotect_tests Both pages written after remap and mprotect # ok 97 mprotect_tests Clear and make the pages written # Bail out! ioctl failed # # Planned tests != run tests (117 != 97) # # Totals: pass:97 fail:0 xfail:0 xpass:0 skip:0 error:0 # [FAIL] not ok 1 pagemap_ioctl # exit=1 # SUMMARY: PASS=0 SKIP=0 FAIL=1 1..1 Link: https://lore.kernel.org/20260628101118.35861-1-zenghui.yu@linux.dev Fixes: 46fd75d4a3c9 ("selftests: mm: add pagemap ioctl tests") Signed-off-by: Zenghui Yu Cc: Muhammad Usama Anjum Cc: David Hildenbrand Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zenghui Yu Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/mm/pagemap_ioctl.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/mm/pagemap_ioctl.c b/tools/testing/selftests/mm/pagemap_ioctl.c index 7f9428d6062ca..0222a3dda0297 100644 --- a/tools/testing/selftests/mm/pagemap_ioctl.c +++ b/tools/testing/selftests/mm/pagemap_ioctl.c @@ -1366,7 +1366,7 @@ void *thread_proc(void *mem) ksft_exit_fail_msg("pthread_barrier_wait\n"); for (i = 0; i < access_per_thread; ++i) - __atomic_add_fetch(m + i * (0x1000 / sizeof(*m)), 1, __ATOMIC_SEQ_CST); + __atomic_add_fetch(m + i * (page_size / sizeof(*m)), 1, __ATOMIC_SEQ_CST); ret = pthread_barrier_wait(&end_barrier); if (ret && ret != PTHREAD_BARRIER_SERIAL_THREAD) @@ -1401,15 +1401,15 @@ static void transact_test(int page_size) if (pthread_barrier_init(&end_barrier, NULL, nthreads + 1)) ksft_exit_fail_msg("pthread_barrier_init\n"); - mem = mmap(NULL, 0x1000 * nthreads * pages_per_thread, PROT_READ | PROT_WRITE, + mem = mmap(NULL, page_size * nthreads * pages_per_thread, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); if (mem == MAP_FAILED) ksft_exit_fail_msg("Error mmap %s.\n", strerror(errno)); - wp_init(mem, 0x1000 * nthreads * pages_per_thread); - wp_addr_range(mem, 0x1000 * nthreads * pages_per_thread); + wp_init(mem, page_size * nthreads * pages_per_thread); + wp_addr_range(mem, page_size * nthreads * pages_per_thread); - memset(mem, 0, 0x1000 * nthreads * pages_per_thread); + memset(mem, 0, page_size * nthreads * pages_per_thread); count = get_dirty_pages_reset(mem, nthreads * pages_per_thread, 1, page_size); ksft_test_result(count > 0, "%s count %u\n", __func__, count); @@ -1418,7 +1418,7 @@ static void transact_test(int page_size) finish = 0; for (i = 0; i < nthreads; ++i) - pthread_create(&th, NULL, thread_proc, mem + 0x1000 * i * pages_per_thread); + pthread_create(&th, NULL, thread_proc, mem + page_size * i * pages_per_thread); extra_pages = 0; for (i = 0; i < iter_count; ++i) { From f250db8ea6e9be6ca91959edd0cae5402c8f6367 Mon Sep 17 00:00:00 2001 From: Sarthak Sharma Date: Mon, 8 Jun 2026 16:02:24 +0530 Subject: [PATCH 0577/2758] selftests/mm: fix ksft_process_madv.sh test category commit 4b0363cb1f3ec42b0b1346e5ab0b8a3dceeee9be upstream. ksft_process_madv.sh currently runs run_vmtests.sh with the mmap category. Update it to run the process_madv category, since ksft_mmap.sh already runs the mmap category tests. This avoids running mmap tests twice and ensures that process_madv tests are run through the kselftest harness. Link: https://lore.kernel.org/20260608103224.344101-1-sarthak.sharma@arm.com Fixes: 6ce964c02f1c ("selftests/mm: have the harness run each test category separately") Signed-off-by: Sarthak Sharma Reviewed-by: Mark Brown Reviewed-by: Dev Jain Acked-by: David Hildenbrand (Arm) Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/mm/ksft_process_madv.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/mm/ksft_process_madv.sh b/tools/testing/selftests/mm/ksft_process_madv.sh index 2c3137ae8bc85..edad2d2d888f2 100755 --- a/tools/testing/selftests/mm/ksft_process_madv.sh +++ b/tools/testing/selftests/mm/ksft_process_madv.sh @@ -1,4 +1,4 @@ #!/bin/sh -e # SPDX-License-Identifier: GPL-2.0 -./run_vmtests.sh -t mmap +./run_vmtests.sh -t process_madv From 49af4044ed983f65d9e2b72764e267d71bab7c27 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Mon, 15 Jun 2026 14:47:37 +1000 Subject: [PATCH 0578/2758] nouveau/vmm: fix another SPT/LPT race commit 6763a0aea6d658d69b9215ab9151d7bd4c1c314b upstream. We've had an unknown Turing issue for a while with page faults since large pages and compression. I've got a patch series that syncs all our L2 handling with ogkm and it made this fault happen more. After writing a bunch of debugging patches, I spotted an invalid LPT entry where there should have been a valid one. A 64K MAP succeeds on a range, but a subsequent SPT put drops SPT refs across multiple ranges, We shouldn't assume all ranges where SPTEs go away will have the same sparse/invalid/valid state, just iterate over each instead and do the right thing. Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie Fixes: d19512f5abb1 ("nouveau/vmm: start tracking if the LPT PTE is valid. (v6)") Link: https://patch.msgid.link/20260615044737.3419585-1-airlied@gmail.com [ Properly format commit message. - Danilo ] Signed-off-by: Danilo Krummrich (cherry picked from commit d008141ed4ce924167a03d46fbce9ad1fe4efa29) Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c index 958fd78080bdb..c2d854c7af99c 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c @@ -230,29 +230,26 @@ nvkm_vmm_unref_sptes(struct nvkm_vmm_iter *it, struct nvkm_vmm_pt *pgt, * covered by a number of LPTEs, the LPTEs once again take * control over their address range. * - * Determine how many LPTEs need to transition state. + * Transition each LPTE individually as each may have a + * different target state (sparse, invalid, or valid). */ - pgt->pte[ptei].s.spte_valid = false; - for (ptes = 1, ptei++; ptei < lpti; ptes++, ptei++) { + for (ptei++; ptei < lpti; ptei++) { if (pgt->pte[ptei].s.sptes) break; - pgt->pte[ptei].s.spte_valid = false; } - if (pgt->pte[pteb].s.sparse) { - TRA(it, "LPTE %05x: U -> S %d PTEs", pteb, ptes); - pair->func->sparse(vmm, pgt->pt[0], pteb, ptes); - } else if (!pgt->pte[pteb].s.lpte_valid) { - if (pair->func->invalid) { - /* If the MMU supports it, restore the LPTE to the - * INVALID state to tell the MMU there is no point - * trying to fetch the corresponding SPTEs. - */ - TRA(it, "LPTE %05x: U -> I %d PTEs", pteb, ptes); - pair->func->invalid(vmm, pgt->pt[0], pteb, ptes); + while (pteb < ptei) { + pgt->pte[pteb].s.spte_valid = false; + if (pgt->pte[pteb].s.sparse) { + TRA(it, "LPTE %05x: U -> S", pteb); + pair->func->sparse(vmm, pgt->pt[0], pteb, 1); + } else if (!pgt->pte[pteb].s.lpte_valid) { + if (pair->func->invalid) { + TRA(it, "LPTE %05x: U -> I", pteb); + pair->func->invalid(vmm, pgt->pt[0], pteb, 1); + } } - } else { - TRA(it, "LPTE %05x: V %d PTEs", pteb, ptes); + pteb++; } } } From c76b8abce575e0c6e4096957220b4515ed847d89 Mon Sep 17 00:00:00 2001 From: Matt Bobrowski Date: Sun, 28 Jun 2026 20:11:03 +0000 Subject: [PATCH 0579/2758] bpf: Reject BPF_MAP_TYPE_INODE_STORAGE creation if BPF LSM is uninitialized commit a6f0643e4f63cfaa0d5d4a69de4f132eac4b8fe4 upstream. When CONFIG_BPF_LSM=y is set, BPF inode storage maps (BPF_MAP_TYPE_INODE_STORAGE) are compiled into the kernel. However, if the BPF LSM is not explicitly enabled at boot time (e.g. omitted from the "lsm=" boot parameter), lsm_prepare() is never executed for the BPF LSM. Consequently, the BPF inode security blob offset (bpf_lsm_blob_sizes.lbs_inode) is never initialized and remains at its default compiled size of 8 bytes instead of being updated to a valid offset past the reserved struct rcu_head (typically 16 bytes or more). When a privileged user creates and updates a BPF_MAP_TYPE_INODE_STORAGE map, bpf_inode() evaluates inode->i_security + 8. This erroneously aliases the struct rcu_head.func callback pointer at the beginning of the inode->i_security blob. During subsequent map element cleanup or inode destruction, writing NULL to owner_storage clears the queued RCU callback pointer. When rcu_do_batch() later executes the queued callback, it attempts an instruction fetch at address 0x0, triggering an immediate kernel panic. Fix this by introducing a global bpf_lsm_initialized boolean flag marked with __ro_after_init. Set this flag to true inside bpf_lsm_init() when the LSM framework successfully registers the BPF LSM. Gate map allocation in inode_storage_map_alloc() on this flag, returning -EOPNOTSUPP if the BPF LSM is in turn uninitialized. This fail-fast approach prevents userspace from allocating inode storage maps when the supporting BPF LSM infrastructure is absent, avoiding zombie map states. Fixes: 8ea636848aca ("bpf: Implement bpf_local_storage for inodes") Reported-by: oxsignal Signed-off-by: Matt Bobrowski Signed-off-by: Daniel Borkmann Reviewed-by: Emil Tsalapatis Reviewed-by: Amery Hung Link: https://lore.kernel.org/bpf/20260628201103.3624525-1-mattbobrowski@google.com Signed-off-by: Greg Kroah-Hartman --- include/linux/bpf_lsm.h | 4 ++++ kernel/bpf/bpf_inode_storage.c | 9 +++++++++ security/bpf/hooks.c | 3 +++ 3 files changed, 16 insertions(+) diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h index 643809cc78c33..e4593b4a12382 100644 --- a/include/linux/bpf_lsm.h +++ b/include/linux/bpf_lsm.h @@ -14,6 +14,8 @@ #ifdef CONFIG_BPF_LSM +extern bool bpf_lsm_initialized __ro_after_init; + #define LSM_HOOK(RET, DEFAULT, NAME, ...) \ RET bpf_lsm_##NAME(__VA_ARGS__); #include @@ -55,6 +57,8 @@ bool bpf_lsm_has_d_inode_locked(const struct bpf_prog *prog); #else /* !CONFIG_BPF_LSM */ +#define bpf_lsm_initialized false + static inline bool bpf_lsm_is_sleepable_hook(u32 btf_id) { return false; diff --git a/kernel/bpf/bpf_inode_storage.c b/kernel/bpf/bpf_inode_storage.c index 0da8d923e39d4..f9e81060c1f45 100644 --- a/kernel/bpf/bpf_inode_storage.c +++ b/kernel/bpf/bpf_inode_storage.c @@ -178,6 +178,15 @@ static int notsupp_get_next_key(struct bpf_map *map, void *key, static struct bpf_map *inode_storage_map_alloc(union bpf_attr *attr) { + /* + * Do not allow allocation of BPF_MAP_TYPE_INODE_STORAGE if the BPF LSM + * was not initialized by the LSM framework at boot. Without proper + * initialization, the BPF inode security blob offset remains unprepared, + * causing bpf_inode() to calculate an invalid memory offset and corrupt + * inode->i_security. + */ + if (!bpf_lsm_initialized) + return ERR_PTR(-EOPNOTSUPP); return bpf_local_storage_map_alloc(attr, &inode_cache); } diff --git a/security/bpf/hooks.c b/security/bpf/hooks.c index 40efde233f3ab..7b98f5d1e2be8 100644 --- a/security/bpf/hooks.c +++ b/security/bpf/hooks.c @@ -7,6 +7,8 @@ #include #include +bool bpf_lsm_initialized __ro_after_init; + static struct security_hook_list bpf_lsm_hooks[] __ro_after_init = { #define LSM_HOOK(RET, DEFAULT, NAME, ...) \ LSM_HOOK_INIT(NAME, bpf_lsm_##NAME), @@ -24,6 +26,7 @@ static int __init bpf_lsm_init(void) { security_add_hooks(bpf_lsm_hooks, ARRAY_SIZE(bpf_lsm_hooks), &bpf_lsmid); + bpf_lsm_initialized = true; pr_info("LSM support for eBPF active\n"); return 0; } From 477f8dec3b5ae54e8ef3c33bdd2257e47896512e Mon Sep 17 00:00:00 2001 From: Lu Baolu Date: Thu, 4 Jun 2026 14:03:06 +0800 Subject: [PATCH 0580/2758] iommu/vt-d: Avoid WARNING in sva unbind path commit 534b5f98ab7319d8004bbc7dab6481462243e883 upstream. The Intel IOMMU driver allows SVA on devices even if they do not support PCI/PRI. Commit 39c20c4e83b9 ("iommu/vt-d: Only handle IOPF for SVA when PRI is supported") modified the SVA bind path to allow this configuration by skipping IOPF enablement when PRI is missing. However, it failed to update the unbind path. This creates an imbalance: the unbind path attempts to disable IOPF for a device that never had it enabled, triggering a WARNING in intel_iommu_disable_iopf(): WARNING: drivers/iommu/intel/iommu.c:3475 at intel_iommu_disable_iopf+0x4f/0x90d Call Trace: blocking_domain_set_dev_pasid+0x50/0x70 iommu_detach_device_pasid+0x89/0xc0 iommu_sva_unbind_device+0x73/0x150 xe_vm_close_and_put+0x4d2/0x1200 [xe] Fix this by bypassing IOPF operations for SVA domains on non-PRI hardware in both the bind and unbind paths. Fixes: 39c20c4e83b9 ("iommu/vt-d: Only handle IOPF for SVA when PRI is supported") Cc: stable@vger.kernel.org Reported-by: Nareshkumar Gollakoti Signed-off-by: Lu Baolu Reviewed-by: Kevin Tian Link: https://lore.kernel.org/r/20260519052917.3729796-1-baolu.lu@linux.intel.com Signed-off-by: Joerg Roedel Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/intel/iommu.h | 11 +++++++++++ drivers/iommu/intel/svm.c | 12 ++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/drivers/iommu/intel/iommu.h b/drivers/iommu/intel/iommu.h index ef145560aa987..775f1c4ae3463 100644 --- a/drivers/iommu/intel/iommu.h +++ b/drivers/iommu/intel/iommu.h @@ -1254,18 +1254,29 @@ void intel_iommu_disable_iopf(struct device *dev); static inline int iopf_for_domain_set(struct iommu_domain *domain, struct device *dev) { + struct device_domain_info *info = dev_iommu_priv_get(dev); + if (!domain || !domain->iopf_handler) return 0; + /* SVA with non-IOMMU/PRI IOPF handling is allowed. */ + if (domain->type == IOMMU_DOMAIN_SVA && !info->pri_supported) + return 0; + return intel_iommu_enable_iopf(dev); } static inline void iopf_for_domain_remove(struct iommu_domain *domain, struct device *dev) { + struct device_domain_info *info = dev_iommu_priv_get(dev); + if (!domain || !domain->iopf_handler) return; + if (domain->type == IOMMU_DOMAIN_SVA && !info->pri_supported) + return; + intel_iommu_disable_iopf(dev); } diff --git a/drivers/iommu/intel/svm.c b/drivers/iommu/intel/svm.c index 57cd1db7207ab..fea10acd4f021 100644 --- a/drivers/iommu/intel/svm.c +++ b/drivers/iommu/intel/svm.c @@ -164,12 +164,9 @@ static int intel_svm_set_dev_pasid(struct iommu_domain *domain, if (IS_ERR(dev_pasid)) return PTR_ERR(dev_pasid); - /* SVA with non-IOMMU/PRI IOPF handling is allowed. */ - if (info->pri_supported) { - ret = iopf_for_domain_replace(domain, old, dev); - if (ret) - goto out_remove_dev_pasid; - } + ret = iopf_for_domain_replace(domain, old, dev); + if (ret) + goto out_remove_dev_pasid; /* Setup the pasid table: */ sflags = cpu_feature_enabled(X86_FEATURE_LA57) ? PASID_FLAG_FL5LP : 0; @@ -184,8 +181,7 @@ static int intel_svm_set_dev_pasid(struct iommu_domain *domain, return 0; out_unwind_iopf: - if (info->pri_supported) - iopf_for_domain_replace(old, domain, dev); + iopf_for_domain_replace(old, domain, dev); out_remove_dev_pasid: domain_remove_dev_pasid(domain, dev, pasid); return ret; From ff189754fc34e04fc507ce5782b8129101a253a2 Mon Sep 17 00:00:00 2001 From: Weinan Liu Date: Thu, 28 May 2026 22:31:47 +0000 Subject: [PATCH 0581/2758] iommu/amd: Don't split flush for amd_iommu_domain_flush_all() commit 69fe699afe1afcb730164b86c228483c2da05f94 upstream. We have observed multiple full invalidations occurring during device detach when we are done using the vfio-device. blocked_domain_attach_device() -> detach_device() -> amd_iommu_domain_flush_all() -> amd_iommu_domain_flush_pages(..., CMD_INV_IOMMU_ALL_PAGES_ADDRESS) while (size != 0) { -> __domain_flush_pages( flush_size /* power of 2 flush_size */) -> domain_flush_pages_v1() -> build_inv_iommu_pages() -> build_inv_address() } build_inv_address() will trigger a full invalidation if the chunk size > (1 << 51). Consequently, the guest will issue multiple full invalidations for a single call to amd_iommu_domain_flush_all() Without this patch, we will see 10 time instead of 1 time full invalidations for every amd_iommu_domain_flush_all(). Cc: stable@vger.kernel.org Fixes: a270be1b3fdf ("iommu/amd: Use only natural aligned flushes in a VM") Suggested-by: Josef Bacik Suggested-by: Jason Gunthorpe Signed-off-by: Weinan Liu Reviewed-by: Wei Wang Reviewed-by: Jason Gunthorpe Reviewed-by: Samiullah Khawaja Reviewed-by: Suravee Suthikulpanit Reviewed-by: Vasant Hegde Signed-off-by: Joerg Roedel Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/amd/iommu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c index 57dc8fabc7d9b..15ffc4742183e 100644 --- a/drivers/iommu/amd/iommu.c +++ b/drivers/iommu/amd/iommu.c @@ -1774,7 +1774,8 @@ void amd_iommu_domain_flush_pages(struct protection_domain *domain, { lockdep_assert_held(&domain->lock); - if (likely(!amd_iommu_np_cache)) { + if (likely(!amd_iommu_np_cache) || + size >= (1ULL<<52)) { __domain_flush_pages(domain, address, size); /* Wait until IOMMU TLB and all device IOTLB flushes are complete */ From 0cdbb97a4dbd69abdd2ab998b4fbc7803d4b0b72 Mon Sep 17 00:00:00 2001 From: Kai Aizen Date: Thu, 30 Apr 2026 20:56:30 +0300 Subject: [PATCH 0582/2758] iommufd: Use sizeof(*hdr) instead of sizeof(hdr) in veventq read commit be93d186ae88a92e7aa77e122d4e661fa57b1e39 upstream. The bound-check in iommufd_veventq_fops_read() for the normal vEVENT path uses sizeof(hdr) where the surrounding code uses sizeof(*hdr): if (!vevent_for_lost_events_header(cur) && sizeof(hdr) + cur->data_len > count - done) { hdr is declared as struct iommufd_vevent_header *, so sizeof(hdr) evaluates to the size of the pointer. Surrounding code uses sizeof(*hdr) consistently: if (done >= count || sizeof(*hdr) > count - done) { ... if (copy_to_user(buf + done, hdr, sizeof(*hdr))) { ... done += sizeof(*hdr); struct iommufd_vevent_header is currently 8 bytes (two __u32 fields, flags and sequence), so on 64-bit (sizeof(void *) == 8) the two expressions happen to be equal and the check works as intended. On 32-bit (sizeof(void *) == 4) the check under-counts the header by 4 bytes: a vEVENT whose data_len causes 8 + cur->data_len to exceed count - done while 4 + cur->data_len does not will pass the check, then the loop will copy_to_user 8 bytes of header followed by data_len bytes of payload, writing past the user-supplied buffer. It is also a latent bug for any future expansion of struct iommufd_vevent_header beyond sizeof(void *) on 64-bit; the check should not depend on the type happening to match the host pointer width. Use sizeof(*hdr) to match the rest of the function and the actual amount that will be copied. Fixes: e36ba5ab808e ("iommufd: Add IOMMUFD_OBJ_VEVENTQ and IOMMUFD_CMD_VEVENTQ_ALLOC") Link: https://patch.msgid.link/r/20260430175630.67078-1-kai.aizen.dev@gmail.com Cc: stable@vger.kernel.org Reported-by: Kai Aizen Signed-off-by: Kai Aizen Reviewed-by: Kevin Tian Reviewed-by: Nicolin Chen Reviewed-by: Lu Baolu Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/iommufd/eventq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/iommufd/eventq.c b/drivers/iommu/iommufd/eventq.c index 710eef0b60045..78689fb52d24c 100644 --- a/drivers/iommu/iommufd/eventq.c +++ b/drivers/iommu/iommufd/eventq.c @@ -321,7 +321,7 @@ static ssize_t iommufd_veventq_fops_read(struct file *filep, char __user *buf, /* If being a normal vEVENT, validate against the full size */ if (!vevent_for_lost_events_header(cur) && - sizeof(hdr) + cur->data_len > count - done) { + sizeof(*hdr) + cur->data_len > count - done) { iommufd_veventq_deliver_restore(veventq, cur); break; } From f9f08e46b9e3144ed94d0d7b1ff399f5e5feb9a9 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Thu, 21 May 2026 17:36:32 -0700 Subject: [PATCH 0583/2758] iommufd: Fix data_len byte-count vs element-count mismatch commit 85345becfead3255a5f875d4b4d82ea01d926239 upstream. kzalloc_flex() computes the allocation size. With event_data typed as u64, data_len is interpreted as a u64 element count. Yet, every caller and the read path treat data_len as a byte count. The current code over-allocates by sizeof(u64) and the __counted_by() annotation overstates the length by the same factor. Re-type event_data as u8. No functional change in user-visible behavior. Fixes: e36ba5ab808e ("iommufd: Add IOMMUFD_OBJ_VEVENTQ and IOMMUFD_CMD_VEVENTQ_ALLOC") Link: https://patch.msgid.link/r/f7665f839b9dce917d6bd394375a1cf56568d86b.1779408671.git.nicolinc@nvidia.com Cc: stable@vger.kernel.org Reviewed-by: Jason Gunthorpe Signed-off-by: Nicolin Chen Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/iommufd/iommufd_private.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h index 6ac1965199e9a..43fbc5bed8de3 100644 --- a/drivers/iommu/iommufd/iommufd_private.h +++ b/drivers/iommu/iommufd/iommufd_private.h @@ -602,7 +602,7 @@ struct iommufd_vevent { struct iommufd_vevent_header header; struct list_head node; /* for iommufd_eventq::deliver */ ssize_t data_len; - u64 event_data[] __counted_by(data_len); + u8 event_data[] __counted_by(data_len); }; #define vevent_for_lost_events_header(vevent) \ From 6c5fc40200cd0a87d66a368eee00df4d1cca946e Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Thu, 21 May 2026 17:36:33 -0700 Subject: [PATCH 0584/2758] iommufd: Move vevent memory allocation outside spinlock commit 47443565d10c51366c9382dbc8597cd6c460b8a2 upstream. The veventq memory allocation happens inside the spinlock. Given its depth is decided by the user space, this leaves a vulnerability, where userspace can allocate large queues to exhaust atomic memory reserves. Move the allocation outside the spinlock and use GFP_NOWAIT, which can fail fast under memory pressure without dipping into the GFP_ATOMIC reserves or direct-reclaiming from the threaded IRQ handler. On allocation failure, queue the lost_events_header (so userspace learns of the drop) and return -ENOMEM so the caller learns of the kernel-side memory pressure. This is intentionally distinct from the queue-overflow path, which also queues the lost_events_header but returns 0: a full queue is an expected userspace-pacing condition rather than a kernel error. A subsequent change will cap the upper bound of the veventq_depth. Fixes: e36ba5ab808e ("iommufd: Add IOMMUFD_OBJ_VEVENTQ and IOMMUFD_CMD_VEVENTQ_ALLOC") Link: https://patch.msgid.link/r/5ff36b5d80f7f6299f851be532a5195c1d2f1dae.1779408671.git.nicolinc@nvidia.com Cc: stable@vger.kernel.org Reviewed-by: Jason Gunthorpe Signed-off-by: Nicolin Chen Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/iommufd/driver.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/iommu/iommufd/driver.c b/drivers/iommu/iommufd/driver.c index 61e6b02601d1a..3b8067976eac0 100644 --- a/drivers/iommu/iommufd/driver.c +++ b/drivers/iommu/iommufd/driver.c @@ -149,15 +149,18 @@ int iommufd_viommu_report_event(struct iommufd_viommu *viommu, goto out_unlock_veventqs; } - spin_lock(&veventq->common.lock); - if (veventq->num_events == veventq->depth) { + /* Pre-allocate to avoid GFP_ATOMIC; use GFP_NOWAIT to avoid sleeping */ + vevent = kzalloc_flex(*vevent, event_data, data_len, GFP_NOWAIT); + if (!vevent) { + spin_lock(&veventq->common.lock); vevent = &veventq->lost_events_header; + rc = -ENOMEM; goto out_set_header; } - vevent = kzalloc_flex(*vevent, event_data, data_len, GFP_ATOMIC); - if (!vevent) { - rc = -ENOMEM; + spin_lock(&veventq->common.lock); + if (veventq->num_events == veventq->depth) { + kfree(vevent); vevent = &veventq->lost_events_header; goto out_set_header; } From e7b5e55652746b1221b9c10ff80eae8a154101ba Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Thu, 21 May 2026 17:36:34 -0700 Subject: [PATCH 0585/2758] iommufd: Set veventq_depth upper bound commit 6ebf2eb46fbd5b40393ff8fbb847ba96925beaff upstream. iommufd_veventq_alloc() accepts any !0 veventq_depth from userspace, with an upper bound at U32_MAX. This leaves a vulnerability where userspace can allocate excessively large queues to exhaust kernel memory reserves. Cap the veventq_depth (maximum number of entries) to 1 << 19, matching the maximum number of entries in the SMMUv3 EVTQ (the largest use case today). Fixes: e36ba5ab808e ("iommufd: Add IOMMUFD_OBJ_VEVENTQ and IOMMUFD_CMD_VEVENTQ_ALLOC") Link: https://patch.msgid.link/r/8426cbaa5e8294472ec7f076ef427cc473be5985.1779408671.git.nicolinc@nvidia.com Cc: stable@vger.kernel.org Reviewed-by: Jason Gunthorpe Signed-off-by: Nicolin Chen Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/iommufd/eventq.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/iommufd/eventq.c b/drivers/iommu/iommufd/eventq.c index 78689fb52d24c..1f1e415285b1a 100644 --- a/drivers/iommu/iommufd/eventq.c +++ b/drivers/iommu/iommufd/eventq.c @@ -473,6 +473,9 @@ int iommufd_fault_iopf_handler(struct iopf_group *group) static const struct file_operations iommufd_veventq_fops = INIT_EVENTQ_FOPS(iommufd_veventq_fops_read, NULL); +/* An arbitrary upper bound for veventq_depth that fits all existing HWs */ +#define VEVENTQ_MAX_DEPTH (1U << 19) + int iommufd_veventq_alloc(struct iommufd_ucmd *ucmd) { struct iommu_veventq_alloc *cmd = ucmd->cmd; @@ -484,7 +487,7 @@ int iommufd_veventq_alloc(struct iommufd_ucmd *ucmd) if (cmd->flags || cmd->__reserved || cmd->type == IOMMU_VEVENTQ_TYPE_DEFAULT) return -EOPNOTSUPP; - if (!cmd->veventq_depth) + if (!cmd->veventq_depth || cmd->veventq_depth > VEVENTQ_MAX_DEPTH) return -EINVAL; viommu = iommufd_get_viommu(ucmd, cmd->viommu_id); From 7a860d6f260ed7a2e555865aa9a67761a7ad75ed Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Mon, 1 Jun 2026 13:42:32 -0700 Subject: [PATCH 0586/2758] iommufd: Rewind header length in done if iommufd_veventq_fops_read() fails commit 01e41ad76c12ae5c49ab4ef4fc7dd54e9b8784d6 upstream. When the first event copy fails, rc = -EFAULT will not be reported as done is set to the length of the copied header. Rewind it to report rc correctly. Fixes: e36ba5ab808e ("iommufd: Add IOMMUFD_OBJ_VEVENTQ and IOMMUFD_CMD_VEVENTQ_ALLOC") Link: https://patch.msgid.link/r/78f8caeb6a5d667a26b870e3068cec47dd4b5be1.1780343944.git.nicolinc@nvidia.com Cc: stable@vger.kernel.org Signed-off-by: Nicolin Chen Reviewed-by: Pranjal Shrivastava Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/iommufd/eventq.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iommu/iommufd/eventq.c b/drivers/iommu/iommufd/eventq.c index 1f1e415285b1a..896f45be0d2ee 100644 --- a/drivers/iommu/iommufd/eventq.c +++ b/drivers/iommu/iommufd/eventq.c @@ -336,6 +336,7 @@ static ssize_t iommufd_veventq_fops_read(struct file *filep, char __user *buf, if (cur->data_len && copy_to_user(buf + done, cur->event_data, cur->data_len)) { iommufd_veventq_deliver_restore(veventq, cur); + done -= sizeof(*hdr); rc = -EFAULT; break; } From f682c833f7d5ceb0396a9cfa3daa21c60b4ffeef Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Mon, 1 Jun 2026 13:42:33 -0700 Subject: [PATCH 0587/2758] iommufd: Reject invalid read count in iommufd_veventq_fops_read() commit 00203ca8323f9714630408c19a209b52397975e6 upstream. The read count must be large enough to hold a vEVENT header. For a normal vEVENT, it must also hold the trailing data following the header. iommufd_veventq_fops_read() does not validate the count, but returns 0 as if the read had succeeded while leaving the pending event in the queue. Return -EINVAL in both undersize cases. Fixes: e36ba5ab808e ("iommufd: Add IOMMUFD_OBJ_VEVENTQ and IOMMUFD_CMD_VEVENTQ_ALLOC") Link: https://patch.msgid.link/r/e1111adcc8a8882fbfd84accd6674dc846dc5689.1780343944.git.nicolinc@nvidia.com Cc: stable@vger.kernel.org Signed-off-by: Nicolin Chen Reviewed-by: Pranjal Shrivastava Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/iommufd/eventq.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/iommu/iommufd/eventq.c b/drivers/iommu/iommufd/eventq.c index 896f45be0d2ee..ac485d010a439 100644 --- a/drivers/iommu/iommufd/eventq.c +++ b/drivers/iommu/iommufd/eventq.c @@ -310,6 +310,9 @@ static ssize_t iommufd_veventq_fops_read(struct file *filep, char __user *buf, if (*ppos) return -ESPIPE; + /* Minimum read count is a vEVENT header */ + if (count < sizeof(*hdr)) + return -EINVAL; while ((cur = iommufd_veventq_deliver_fetch(veventq))) { /* Validate the remaining bytes against the header size */ @@ -323,6 +326,9 @@ static ssize_t iommufd_veventq_fops_read(struct file *filep, char __user *buf, if (!vevent_for_lost_events_header(cur) && sizeof(*hdr) + cur->data_len > count - done) { iommufd_veventq_deliver_restore(veventq, cur); + /* Read count doesn't fit a single normal vEVENT */ + if (done == 0) + rc = -EINVAL; break; } From db4e1a1e9f1364a5cb386d94f32e85fde6bfb631 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Mon, 1 Jun 2026 13:42:34 -0700 Subject: [PATCH 0588/2758] iommufd: Propagate allocation failure in iommufd_veventq_deliver_fetch() commit 489e63dd120bad52eba63f5506c214750cd5bc75 upstream. When the kzalloc_obj() fails in iommufd_veventq_deliver_fetch(), it returns NULL, falsely advertising to userspace that the queue is empty. Propagate the -ENOMEM properly to the caller. Fixes: e36ba5ab808e ("iommufd: Add IOMMUFD_OBJ_VEVENTQ and IOMMUFD_CMD_VEVENTQ_ALLOC") Link: https://patch.msgid.link/r/25d29feac909e36f78c145fa99ef2d4cb7a415da.1780343944.git.nicolinc@nvidia.com Cc: stable@vger.kernel.org Signed-off-by: Nicolin Chen Reviewed-by: Pranjal Shrivastava Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/iommufd/eventq.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/iommufd/eventq.c b/drivers/iommu/iommufd/eventq.c index ac485d010a439..f55d173c59f61 100644 --- a/drivers/iommu/iommufd/eventq.c +++ b/drivers/iommu/iommufd/eventq.c @@ -264,8 +264,10 @@ iommufd_veventq_deliver_fetch(struct iommufd_veventq *veventq) /* Make a copy of the lost_events_header for copy_to_user */ if (next == &veventq->lost_events_header) { vevent = kzalloc_obj(*vevent, GFP_ATOMIC); - if (!vevent) + if (!vevent) { + vevent = ERR_PTR(-ENOMEM); goto out_unlock; + } } list_del(&next->node); if (vevent) @@ -315,6 +317,12 @@ static ssize_t iommufd_veventq_fops_read(struct file *filep, char __user *buf, return -EINVAL; while ((cur = iommufd_veventq_deliver_fetch(veventq))) { + if (IS_ERR(cur)) { + if (done == 0) + rc = PTR_ERR(cur); + break; + } + /* Validate the remaining bytes against the header size */ if (done >= count || sizeof(*hdr) > count - done) { iommufd_veventq_deliver_restore(veventq, cur); From 76c05bd8f6341840f17db401e4edaf60ea6b43e0 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Mon, 1 Jun 2026 13:42:35 -0700 Subject: [PATCH 0589/2758] iommufd: Reject invalid read count in iommufd_fault_fops_read() commit 47916a54eeb2a9e654512ee609f71bd5b29db702 upstream. The read count must be large enough to hold one fault or a group's faults. iommufd_fault_fops_read() does not validate the count, but returns 0 as if the read had succeeded while leaving the pending fault in the queue. Return -EINVAL in the undersize cases. Fixes: 07838f7fd529 ("iommufd: Add iommufd fault object") Link: https://patch.msgid.link/r/85c118a606fbedc5c132a1f5ec223a5ba23b92d2.1780343944.git.nicolinc@nvidia.com Cc: stable@vger.kernel.org Signed-off-by: Nicolin Chen Reviewed-by: Pranjal Shrivastava Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/iommufd/eventq.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/iommu/iommufd/eventq.c b/drivers/iommu/iommufd/eventq.c index f55d173c59f61..613024ca8f1ff 100644 --- a/drivers/iommu/iommufd/eventq.c +++ b/drivers/iommu/iommufd/eventq.c @@ -142,6 +142,9 @@ static ssize_t iommufd_fault_fops_read(struct file *filep, char __user *buf, if (done >= count || group->fault_count * fault_size > count - done) { iommufd_fault_deliver_restore(fault, group); + /* Read count doesn't fit the first fault group */ + if (done == 0) + rc = -EINVAL; break; } From f66c16b175509642ee7082df57c9bf3deaebae1a Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Mon, 1 Jun 2026 13:42:36 -0700 Subject: [PATCH 0590/2758] iommufd: Break the loop on failure in iommufd_fault_fops_read() commit 172fc8b19825a0f5884c38f2289188284e2d45ee upstream. On a copy_to_user() failure inside the inner list_for_each_entry, only the inner loop breaks; the outer while re-fetches the just-restored fault group and retries the failing copy_to_user() forever, spinning the reader at 100% CPU with fault->mutex held. Check rc after the inner loop and break the outer while as well. Fixes: 07838f7fd529 ("iommufd: Add iommufd fault object") Link: https://patch.msgid.link/r/336a9b6e44fe66a24199d3be777c405c85c98622.1780343944.git.nicolinc@nvidia.com Cc: stable@vger.kernel.org Signed-off-by: Nicolin Chen Reviewed-by: Pranjal Shrivastava Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/iommufd/eventq.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iommu/iommufd/eventq.c b/drivers/iommu/iommufd/eventq.c index 613024ca8f1ff..1c010e691f972 100644 --- a/drivers/iommu/iommufd/eventq.c +++ b/drivers/iommu/iommufd/eventq.c @@ -168,6 +168,8 @@ static ssize_t iommufd_fault_fops_read(struct file *filep, char __user *buf, } done += fault_size; } + if (rc) + break; } mutex_unlock(&fault->mutex); From 0714e5a4c83e8338386fbbf5446d5d4649fbfd81 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Mon, 1 Jun 2026 13:42:37 -0700 Subject: [PATCH 0591/2758] iommufd: Avoid partial fault group delivery in iommufd_fault_fops_read() commit 091ab6d70dc444f56ed14faedbcacfc979f4c613 upstream. The cookie returned by xa_alloc() in iommufd_fault_fops_read() is per fault group, but the inner copy_to_user() runs per fault inside the group. If a copy fails mid-group, xa_erase clears the cookie and the group is restored to the deliver list, yet done is not rolled back. The function returns the partial byte count, with the successfully copied faults sitting at offsets below done carrying the now-erased cookie. The next read() then re-fetches the group, allocates a fresh cookie, and re-delivers every fault including the ones already copied; userspace sees duplicates carrying the new cookie, and a stale cookie that can never be responded to. Use a local group_done variable that tracks the per-group progress inside the inner loop, and only commit done = group_done after the inner loop has finished successfully. On a copy_to_user failure the outer break skips the commit, so done remains at its prior start-of-group baseline; the partial bytes already written past done are undefined to userspace per the read(2) contract, and the next read re-delivers the whole group atomically. Fixes: 07838f7fd529 ("iommufd: Add iommufd fault object") Link: https://patch.msgid.link/r/360cab4d4aeccb0bae275a970e2b3c340a71e0e0.1780343944.git.nicolinc@nvidia.com Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Nicolin Chen Reviewed-by: Pranjal Shrivastava Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/iommufd/eventq.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/iommu/iommufd/eventq.c b/drivers/iommu/iommufd/eventq.c index 1c010e691f972..5129e3bf5461c 100644 --- a/drivers/iommu/iommufd/eventq.c +++ b/drivers/iommu/iommufd/eventq.c @@ -139,6 +139,8 @@ static ssize_t iommufd_fault_fops_read(struct file *filep, char __user *buf, mutex_lock(&fault->mutex); while ((group = iommufd_fault_deliver_fetch(fault))) { + size_t group_done = done; + if (done >= count || group->fault_count * fault_size > count - done) { iommufd_fault_deliver_restore(fault, group); @@ -160,16 +162,17 @@ static ssize_t iommufd_fault_fops_read(struct file *filep, char __user *buf, iommufd_compose_fault_message(&iopf->fault, &data, idev, group->cookie); - if (copy_to_user(buf + done, &data, fault_size)) { + if (copy_to_user(buf + group_done, &data, fault_size)) { xa_erase(&fault->response, group->cookie); iommufd_fault_deliver_restore(fault, group); rc = -EFAULT; break; } - done += fault_size; + group_done += fault_size; } if (rc) break; + done = group_done; } mutex_unlock(&fault->mutex); From 2c6381d90898089287e0a358f06f89f6b4b389f2 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Wed, 3 Jun 2026 14:26:53 -0700 Subject: [PATCH 0592/2758] iommufd: Set upper bounds on cache invalidation entry_num and entry_len commit 4d70986002f2f3eaaed89124fb2522bded38b016 upstream. iommufd_hwpt_invalidate() takes a user-controlled entry_num and entry_len, each bounded only by U32_MAX. An entry_len beyond the kernel's struct size makes the copy helper verify the extra bytes are zero, scanning that excess in one uninterruptible pass; a multi-gigabyte value over zeroed user memory trips the soft-lockup watchdog. A large entry_num is the other half, driving the backend invalidation loop with no reschedule. The VT-d nested handler, for one, copies each entry and flushes caches per iteration, pinning the CPU on a non-preemptible kernel. Cap both in the ioctl. entry_len is held under PAGE_SIZE, above any request struct, and entry_num under 1 << 19, the order of a hardware invalidation queue and well beyond any real batch, bounding the per-call loop length. Fixes: 8c6eabae3807 ("iommufd: Add IOMMU_HWPT_INVALIDATE") Link: https://patch.msgid.link/r/447fa93663f7526eb361719e83fa8b649464483d.1780521606.git.nicolinc@nvidia.com Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Nicolin Chen Reviewed-by: Lu Baolu Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/iommufd/hw_pagetable.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/iommufd/hw_pagetable.c b/drivers/iommu/iommufd/hw_pagetable.c index fe789c2dc0c97..623cc608ca0cd 100644 --- a/drivers/iommu/iommufd/hw_pagetable.c +++ b/drivers/iommu/iommufd/hw_pagetable.c @@ -489,6 +489,9 @@ int iommufd_hwpt_get_dirty_bitmap(struct iommufd_ucmd *ucmd) return rc; } +/* An arbitrary entry_num cap, far above any realistic invalidation batch */ +#define IOMMU_HWPT_INVALIDATE_ENTRY_NUM_MAX (1U << 19) + int iommufd_hwpt_invalidate(struct iommufd_ucmd *ucmd) { struct iommu_hwpt_invalidate *cmd = ucmd->cmd; @@ -507,7 +510,13 @@ int iommufd_hwpt_invalidate(struct iommufd_ucmd *ucmd) goto out; } - if (cmd->entry_num && (!cmd->data_uptr || !cmd->entry_len)) { + /* + * Bound entry_num and entry_len so a single call cannot pin the CPU; + * entry_len also caps the copy_struct_from_user() trailing-zero scan. + */ + if (cmd->entry_num && + (!cmd->data_uptr || !cmd->entry_len || cmd->entry_len > PAGE_SIZE || + cmd->entry_num > IOMMU_HWPT_INVALIDATE_ENTRY_NUM_MAX)) { rc = -EINVAL; goto out; } From fe85607ceffc035cee287cc31600fb6a6e70db62 Mon Sep 17 00:00:00 2001 From: Ricardo Robaina Date: Wed, 13 May 2026 18:47:59 -0300 Subject: [PATCH 0593/2758] audit: fix removal of dangling executable rules commit 888a0396e154524f4027f27da84bdbec9eb68916 upstream. When an audited executable is deleted from the disk, its dentry becomes negative. Any later attempt to delete the associated audit rule will lead to audit_alloc_mark() encountering this negative dentry and immediately aborting, returning -ENOENT. This early abort prevents the subsystem from allocating the temporary fsnotify mark needed to construct the search key, meaning the kernel cannot find the existing rule in its own lists to delete it. This leaves a dangling rule in memory, resulting in the following error while attempting to delete the rule: # ./audit-dupe-exe-deadlock.sh No rules Error deleting rule (No such file or directory) There was an error while processing parameters # auditctl -l -a always,exit -S all -F exe=/tmp/file -F path=/tmp/file -F key=dr # auditctl -D Error deleting rule (No such file or directory) There was an error while processing parameters This patch fixes this issue by removing the d_really_is_negative() check. By doing so, a dummy mark can be successfully generated for the deleted path, which allows the audit subsystem to properly match and flush the dangling rule. Cc: stable@kernel.org Fixes: 76a53de6f7ff ("VFS/audit: introduce kern_path_parent() for audit") Acked-by: Waiman Long Acked-by: Richard Guy Briggs Signed-off-by: Ricardo Robaina Signed-off-by: Paul Moore Signed-off-by: Greg Kroah-Hartman --- kernel/audit_fsnotify.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/kernel/audit_fsnotify.c b/kernel/audit_fsnotify.c index 711454f9f7242..ae0e75403f768 100644 --- a/kernel/audit_fsnotify.c +++ b/kernel/audit_fsnotify.c @@ -84,10 +84,6 @@ struct audit_fsnotify_mark *audit_alloc_mark(struct audit_krule *krule, char *pa dentry = kern_path_parent(pathname, &path); if (IS_ERR(dentry)) return ERR_CAST(dentry); /* returning an error */ - if (d_really_is_negative(dentry)) { - audit_mark = ERR_PTR(-ENOENT); - goto out; - } audit_mark = kzalloc_obj(*audit_mark); if (unlikely(!audit_mark)) { From 0254cef9bf187aad53890d189890120ef65ff79f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= Date: Fri, 12 Jun 2026 19:27:55 +0200 Subject: [PATCH 0594/2758] landlock: Set audit_net.sk for socket access checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit d936e1a9170f9cadaa5f37586b1dfe6f20f98799 upstream. Set audit_net.sk in current_check_access_socket() to provide the socket object to audit_log_lsm_data(). This makes Landlock consistent with AppArmor, which always sets .sk for socket operations, and with SELinux's generic socket permission checks. The socket's local and foreign address information (laddr, lport, faddr, fport) is logged by the shared lsm_audit.c infrastructure when the socket has bound or connected state. Fields with zero values are suppressed by print_ipv4_addr()/print_ipv6_addr(), so the audit output is unchanged for the common case of bind denials on unbound sockets. For connect denials after a prior bind, the bound local address (laddr, lport) appears before the existing sockaddr fields (daddr, dest). No existing fields are removed or reordered, and the new field names (laddr, lport, faddr, fport) are standard audit fields already emitted by other LSMs through the same lsm_audit.c code path. Add a connect_tcp_bound audit test that binds to an allowed port and then connects to a denied one, verifying that the denial record reports laddr/lport from the bound socket in addition to the connect destination. Cc: Günther Noack Cc: Tingmao Wang Cc: stable@vger.kernel.org Fixes: 9f74411a40ce ("landlock: Log TCP bind and connect denials") Link: https://patch.msgid.link/20260612172757.1003481-1-mic@digikod.net Signed-off-by: Mickaël Salaün Signed-off-by: Greg Kroah-Hartman --- security/landlock/net.c | 1 + tools/testing/selftests/landlock/net_test.c | 62 +++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/security/landlock/net.c b/security/landlock/net.c index c368649985c53..a38bdfcffc22a 100644 --- a/security/landlock/net.c +++ b/security/landlock/net.c @@ -198,6 +198,7 @@ static int current_check_access_socket(struct socket *const sock, return 0; audit_net.family = address->sa_family; + audit_net.sk = sock->sk; landlock_log_denial(subject, &(struct landlock_request){ .type = LANDLOCK_REQUEST_NET_ACCESS, diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c index 4c528154ea92b..0c256e7c86752 100644 --- a/tools/testing/selftests/landlock/net_test.c +++ b/tools/testing/selftests/landlock/net_test.c @@ -2026,4 +2026,66 @@ TEST_F(audit, connect) EXPECT_EQ(0, close(sock_fd)); } +static int matches_log_tcp_bound(int audit_fd, const char *const addr, + __u16 lport, __u16 dport) +{ + static const char log_template[] = REGEX_LANDLOCK_PREFIX + " blockers=net\\.connect_tcp laddr=%s lport=%u daddr=%s dest=%u$"; + /* Slack for two addresses and two port numbers. */ + char log_match[sizeof(log_template) + 40]; + int log_match_len; + + log_match_len = snprintf(log_match, sizeof(log_match), log_template, + addr, lport, addr, dport); + if (log_match_len > sizeof(log_match)) + return -E2BIG; + + return audit_match_record(audit_fd, AUDIT_LANDLOCK_ACCESS, log_match, + NULL); +} + +/* + * After a bind() to an allowed port, a denied connect must report laddr/lport + * from the bound socket (made available through audit_net.sk) in addition to + * the connect sockaddr's daddr/dest. + */ +TEST_F(audit, connect_tcp_bound) +{ + const struct landlock_ruleset_attr ruleset_attr = { + .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | + LANDLOCK_ACCESS_NET_CONNECT_TCP, + }; + const struct landlock_net_port_attr rule_bind = { + .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP, + .port = self->srv0.port, + }; + struct service_fixture srv_remote; + struct audit_records records; + int ruleset_fd, sock_fd; + + /* Uses a second port as the denied connect target. */ + ASSERT_EQ(0, set_service(&srv_remote, variant->prot, 1)); + + ruleset_fd = + landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); + ASSERT_LE(0, ruleset_fd); + ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, + &rule_bind, 0)); + enforce_ruleset(_metadata, ruleset_fd); + EXPECT_EQ(0, close(ruleset_fd)); + + sock_fd = socket_variant(&self->srv0); + ASSERT_LE(0, sock_fd); + EXPECT_EQ(0, bind_variant(sock_fd, &self->srv0)); + EXPECT_EQ(-EACCES, connect_variant(sock_fd, &srv_remote)); + EXPECT_EQ(0, matches_log_tcp_bound(self->audit_fd, variant->addr, + self->srv0.port, srv_remote.port)); + + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + EXPECT_EQ(0, records.access); + EXPECT_EQ(1, records.domain); + + EXPECT_EQ(0, close(sock_fd)); +} + TEST_HARNESS_MAIN From c02f2a0ae1c12fb8e37e617d64951275b9204af1 Mon Sep 17 00:00:00 2001 From: Maximilian Heyne Date: Fri, 29 May 2026 20:03:41 +0000 Subject: [PATCH 0595/2758] selftests/landlock: Explicitly disable audit in teardowns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 0302cd72fe196aee933e3fb76f6d175d1ab0e843 upstream. I'm seeing sporadic selftest failures, such as # RUN scoped_audit.connect_to_child ... # scoped_abstract_unix_test.c:314:connect_to_child:Expected 0 (0) == records.access (8) # connect_to_child: Test failed # FAIL scoped_audit.connect_to_child not ok 19 scoped_audit.connect_to_child This seems similar to what commit 3647a4977fb73d ("selftests/landlock: Drain stale audit records on init") tried to fix. However, the added drain loop is not effective. When setting the AUDIT_STATUS_PID, the kauditd_thread is woken up starting to send messages from the hold queue to the netlink. Depending on scheduling of this kthread not all messages might be send via the netlink in the 1 us interval. Therefore, instead of trying to drain the queue, let's just disable audit when running non-audit tests or more precisely disable it after audit-tests. This way we won't generate any new audit message that could interfere with the other tests. The comment saying that on process exit audit will be disabled is wrong. The closed file descriptor just causes an auditd_reset(), not a disablement. So future messages will be queued in the hold queue. Cc: stable@vger.kernel.org Fixes: 6a500b22971c ("selftests/landlock: Add tests for audit flags and domain IDs") Signed-off-by: Maximilian Heyne Link: https://patch.msgid.link/20260529-welsh-nagoya-b4d9ca60@mheyne-amazon [mic: Fix FD leak, update subject, call audit_cleanup() in audit_exec teardown] Signed-off-by: Mickaël Salaün Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/landlock/audit.h | 21 +++++++------------ tools/testing/selftests/landlock/audit_test.c | 4 +--- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/tools/testing/selftests/landlock/audit.h b/tools/testing/selftests/landlock/audit.h index 834005b2b0f09..ed736fcb3c1fa 100644 --- a/tools/testing/selftests/landlock/audit.h +++ b/tools/testing/selftests/landlock/audit.h @@ -494,10 +494,9 @@ static int audit_init_filter_exe(struct audit_filter *filter, const char *path) static int audit_cleanup(int audit_fd, struct audit_filter *filter) { struct audit_filter new_filter; + int err = 0; if (audit_fd < 0 || !filter) { - int err; - /* * Simulates audit_init_with_exe_filter() when called from * FIXTURE_TEARDOWN_PARENT(). @@ -508,23 +507,19 @@ static int audit_cleanup(int audit_fd, struct audit_filter *filter) filter = &new_filter; err = audit_init_filter_exe(filter, NULL); - if (err) { - close(audit_fd); - return err; - } + if (err) + goto err_close; } /* Filters might not be in place. */ audit_filter_exe(audit_fd, filter, AUDIT_DEL_RULE); audit_filter_drop(audit_fd, AUDIT_DEL_RULE); - /* - * Because audit_cleanup() might not be called by the test auditd - * process, it might not be possible to explicitly set it. Anyway, - * AUDIT_STATUS_ENABLED will implicitly be set to 0 when the auditd - * process will exit. - */ - return close(audit_fd); + err = audit_set_status(audit_fd, AUDIT_STATUS_ENABLED, 0); + +err_close: + close(audit_fd); + return err; } static int audit_init_with_exe_filter(struct audit_filter *filter) diff --git a/tools/testing/selftests/landlock/audit_test.c b/tools/testing/selftests/landlock/audit_test.c index 93ae5bd0dcce0..d3d011649a762 100644 --- a/tools/testing/selftests/landlock/audit_test.c +++ b/tools/testing/selftests/landlock/audit_test.c @@ -849,10 +849,8 @@ FIXTURE_SETUP(audit_exec) FIXTURE_TEARDOWN(audit_exec) { set_cap(_metadata, CAP_AUDIT_CONTROL); - EXPECT_EQ(0, audit_filter_exe(self->audit_fd, &self->audit_filter, - AUDIT_DEL_RULE)); + EXPECT_EQ(0, audit_cleanup(self->audit_fd, &self->audit_filter)); clear_cap(_metadata, CAP_AUDIT_CONTROL); - EXPECT_EQ(0, close(self->audit_fd)); } TEST_F(audit_exec, signal_and_open) From 7621e00a4059a7401e5a3dfdd9de81f9696ea729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= Date: Wed, 13 May 2026 20:03:08 +0200 Subject: [PATCH 0596/2758] landlock: Account all audit data allocations to user space MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit b232bd12789fa57405b5092f28788be97aae9999 upstream. Mark the kzalloc_flex() of struct landlock_details with GFP_KERNEL_ACCOUNT so the allocation is charged to the calling task, like the other Landlock per-domain allocations which have used GFP_KERNEL_ACCOUNT forever. Every property of landlock_details is caller-attributable: allocated by landlock_restrict_self(2), owned by the caller's landlock_hierarchy, contents are the caller's pid, uid, comm, and exe_path, lifetime bounded by the caller's domain. While the caller may not know nor control the size of this allocation (i.e. exe_path), this data should still be accounted for it. The deciding factor is whether userspace can trigger the allocation, not whether the size of the data is known nor controlled by the caller. This aligns with the kmemcg accounting policy established by commit 5d097056c9a0 ("kmemcg: account certain kmem allocations to memcg"). No new failure modes: the hierarchy and ruleset are allocated before details and are already accounted, so landlock_restrict_self(2) already returns -ENOMEM under memcg pressure. This change widens that existing failure window slightly; it does not introduce a new error code. Cc: Günther Noack Cc: Paul Moore Cc: stable@vger.kernel.org Fixes: 1d636984e088 ("landlock: Add AUDIT_LANDLOCK_DOMAIN and log domain status") Link: https://patch.msgid.link/20260513180309.165840-1-mic@digikod.net Signed-off-by: Mickaël Salaün Signed-off-by: Greg Kroah-Hartman --- security/landlock/domain.c | 9 +++++---- security/landlock/domain.h | 5 +---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/security/landlock/domain.c b/security/landlock/domain.c index 06b6bd845060f..04b69b43fbf56 100644 --- a/security/landlock/domain.c +++ b/security/landlock/domain.c @@ -90,11 +90,12 @@ static struct landlock_details *get_current_details(void) return ERR_CAST(buffer); /* - * Create the new details according to the path's length. Do not - * allocate with GFP_KERNEL_ACCOUNT because it is independent from the - * caller. + * Create the new details according to the path's length. Account to + * the calling task's memcg, like the other Landlock per-domain + * allocations, even if it may not control the related size. */ - details = kzalloc_flex(*details, exe_path, path_size); + details = + kzalloc_flex(*details, exe_path, path_size, GFP_KERNEL_ACCOUNT); if (!details) return ERR_PTR(-ENOMEM); diff --git a/security/landlock/domain.h b/security/landlock/domain.h index a9d57db0120dc..35cac8f6daee7 100644 --- a/security/landlock/domain.h +++ b/security/landlock/domain.h @@ -33,10 +33,7 @@ enum landlock_log_status { * Rarely accessed, mainly when logging the first domain's denial. * * The contained pointers are initialized at the domain creation time and never - * changed again. Contrary to most other Landlock object types, this one is - * not allocated with GFP_KERNEL_ACCOUNT because its size may not be under the - * caller's control (e.g. unknown exe_path) and the data is not explicitly - * requested nor used by tasks. + * changed again. */ struct landlock_details { /** From 395135628ad54e05ec3fab69d4d392bca6e8e6e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= Date: Wed, 13 May 2026 12:51:08 +0200 Subject: [PATCH 0597/2758] selftests/landlock: Filter dealloc records in audit_count_records() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 26679fad81a471428707d2dd7b0418204c52b7e4 upstream. audit_count_records() counts both AUDIT_LANDLOCK_DOMAIN allocation and deallocation records in records.domain . Domain deallocation is tied to asynchronous credential freeing via kworker threads (landlock_put_ruleset_deferred), so the dealloc record can arrive after the drain in audit_init() and after the preceding audit_match_record() call. This causes flaky failures in tests that assert an exact records.domain count: a stale dealloc record from a previous test's domain inflates the count by one. Observed on x86_64 under build configurations that delay the kworker firing the dealloc callback (e.g. coverage instrumentation): the audit_layout1 tests in fs_test.c intermittently saw records.domain == 2 where 1 was expected. The fix is in the shared helper, so those existing checks become robust without needing a fs_test.c edit. Filter audit_count_records() with a regex to skip records containing deallocation status. The remaining domain records (allocation, emitted synchronously during landlock_log_denial()) are deterministic. Deallocation records are already tested explicitly via matches_log_domain_deallocated() in audit_test.c, which uses its own domain-ID-based filtering and longer timeout. With this filter in place, re-add the records.domain == 0 checks that were removed in commit 3647a4977fb7 ("selftests/landlock: Drain stale audit records on init") as a workaround for this race. Cc: Günther Noack Cc: stable@vger.kernel.org Depends-on: 07c2572a8757 ("selftests/landlock: Skip stale records in audit_match_record()") Fixes: 6a500b22971c ("selftests/landlock: Add tests for audit flags and domain IDs") Tested-by: Günther Noack Link: https://patch.msgid.link/20260513105112.140137-1-mic@digikod.net Signed-off-by: Mickaël Salaün Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/landlock/audit.h | 39 ++++++++++++------- tools/testing/selftests/landlock/audit_test.c | 2 + .../testing/selftests/landlock/ptrace_test.c | 1 + .../landlock/scoped_abstract_unix_test.c | 1 + 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/tools/testing/selftests/landlock/audit.h b/tools/testing/selftests/landlock/audit.h index ed736fcb3c1fa..17ede77c31b0e 100644 --- a/tools/testing/selftests/landlock/audit.h +++ b/tools/testing/selftests/landlock/audit.h @@ -381,18 +381,24 @@ struct audit_records { }; /* - * WARNING: Do not assert records.domain == 0 without a preceding - * audit_match_record() call. Domain deallocation records are emitted - * asynchronously from kworker threads and can arrive after the drain in - * audit_init(), corrupting the domain count. A preceding audit_match_record() - * call consumes stale records while scanning, making the assertion safe in - * practice because stale deallocation records arrive before the expected access - * records. + * Counts remaining audit records by type, skipping domain deallocation records. + * Deallocation records are emitted asynchronously from kworker threads after a + * previous test's child has exited, so they can arrive after the drain in + * audit_init() and after the preceding audit_match_record() call. Allocation + * records are emitted synchronously during landlock_log_denial() in the current + * test's syscall context, so only those are counted in records->domain. */ static int audit_count_records(int audit_fd, struct audit_records *records) { + static const char dealloc_pattern[] = REGEX_LANDLOCK_PREFIX + " status=deallocated "; struct audit_message msg; - int err; + regex_t dealloc_re; + int ret, err = 0; + + ret = regcomp(&dealloc_re, dealloc_pattern, 0); + if (ret) + return -ENOMEM; records->access = 0; records->domain = 0; @@ -402,9 +408,8 @@ static int audit_count_records(int audit_fd, struct audit_records *records) err = audit_recv(audit_fd, &msg); if (err) { if (err == -EAGAIN) - return 0; - else - return err; + err = 0; + break; } switch (msg.header.nlmsg_type) { @@ -412,12 +417,20 @@ static int audit_count_records(int audit_fd, struct audit_records *records) records->access++; break; case AUDIT_LANDLOCK_DOMAIN: - records->domain++; + ret = regexec(&dealloc_re, msg.data, 0, NULL, 0); + if (ret == REG_NOMATCH) { + records->domain++; + } else if (ret != 0) { + err = -EIO; + goto out; + } break; } } while (true); - return 0; +out: + regfree(&dealloc_re); + return err; } static int audit_init(void) diff --git a/tools/testing/selftests/landlock/audit_test.c b/tools/testing/selftests/landlock/audit_test.c index d3d011649a762..bd9f207b36e40 100644 --- a/tools/testing/selftests/landlock/audit_test.c +++ b/tools/testing/selftests/landlock/audit_test.c @@ -730,6 +730,7 @@ TEST_F(audit_flags, signal) } else { EXPECT_EQ(1, records.access); } + EXPECT_EQ(0, records.domain); /* Updates filter rules to match the drop record. */ set_cap(_metadata, CAP_AUDIT_CONTROL); @@ -915,6 +916,7 @@ TEST_F(audit_exec, signal_and_open) /* Tests that there was no denial until now. */ EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); EXPECT_EQ(0, records.access); + EXPECT_EQ(0, records.domain); /* * Wait for the child to do a first denied action by layer1 and diff --git a/tools/testing/selftests/landlock/ptrace_test.c b/tools/testing/selftests/landlock/ptrace_test.c index 1b6c8b53bf33a..4f64c90583cd6 100644 --- a/tools/testing/selftests/landlock/ptrace_test.c +++ b/tools/testing/selftests/landlock/ptrace_test.c @@ -342,6 +342,7 @@ TEST_F(audit, trace) /* Makes sure there is no superfluous logged records. */ EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); EXPECT_EQ(0, records.access); + EXPECT_EQ(0, records.domain); yama_ptrace_scope = get_yama_ptrace_scope(); ASSERT_LE(0, yama_ptrace_scope); diff --git a/tools/testing/selftests/landlock/scoped_abstract_unix_test.c b/tools/testing/selftests/landlock/scoped_abstract_unix_test.c index c47491d2d1c14..72f97648d4a7d 100644 --- a/tools/testing/selftests/landlock/scoped_abstract_unix_test.c +++ b/tools/testing/selftests/landlock/scoped_abstract_unix_test.c @@ -312,6 +312,7 @@ TEST_F(scoped_audit, connect_to_child) /* Makes sure there is no superfluous logged records. */ EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); EXPECT_EQ(0, records.access); + EXPECT_EQ(0, records.domain); ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC)); ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC)); From 5c22e38cfb739bb2456bd3f53383ab0abef60f9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= Date: Wed, 13 May 2026 12:51:09 +0200 Subject: [PATCH 0598/2758] selftests/landlock: Increase default audit socket timeout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit d8dfb4c7faa87c3e41a8678f38f136c2c7c036fa upstream. matches_log_fs() and other audit_match_record() callers intermittently return -EAGAIN under heavy debug configs (KASAN, lockdep). The audit record delivery pipeline is asynchronous: landlock_log_denial() queues the record to audit_queue, and kauditd_thread dequeues and delivers via netlink. Under debug configs, kauditd scheduling between audit_log_end() and netlink_unicast() can exceed a syscall round trip (more than 1 usec), which was the value of the socket timeout used for the recvfrom() calls. The observed failure [1] is an EAGAIN error code (-11) which means that the access record had not arrived within the 1 usec timeout of recvfrom(). The expected record does arrive, but only after matches_log_fs() has already returned. It is then consumed by a later audit_count_records() call, making records.access == 1 instead of 0. Switch the default socket timeout to the slow value (1 second) so all audit_match_record() callers wait long enough for kauditd delivery, and lower it to the fast value (1 usec) only on the two paths that expect no record: audit_count_records() and the expected_domain_id == 0 probe in matches_log_domain_deallocated(). audit_init() drains stale records with the fast timeout (terminating on -EAGAIN once the backlog is empty) and switches to the patient default before returning. 1 second gives ~10x margin over the observed maximum (~100 ms, while the happy path is ~23 us). Rename the timeval constants to reflect their new roles: - audit_tv_dom_drop (1 second) -> audit_tv_default: default socket timeout, patient enough for asynchronous kauditd delivery. - audit_tv_default (1 usec) -> audit_tv_fast: fast timeout for paths that expect no record (drain, audit_count_records(), probes). Invert the conditional in matches_log_domain_deallocated(). Check setsockopt returns on both the lower and restore paths; preserve the first error via !err when the restore fails after a prior error so the actionable return code is not masked by a bookkeeping failure. Cc: Günther Noack Cc: Thomas Weißschuh Cc: stable@vger.kernel.org Depends-on: 07c2572a8757 ("selftests/landlock: Skip stale records in audit_match_record()") Fixes: 6a500b22971c ("selftests/landlock: Add tests for audit flags and domain IDs") Reported-by: Günther Noack Closes: https://lore.kernel.org/r/20260402.eb5c4e85f472@gnoack.org [1] Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-lkp/202605111649.a8b30a62-lkp@intel.com Closes: https://lore.kernel.org/oe-lkp/202604300436.a07fae12-lkp@intel.com Tested-by: Günther Noack Link: https://patch.msgid.link/20260513105112.140137-2-mic@digikod.net Signed-off-by: Mickaël Salaün Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/landlock/audit.h | 80 +++++++++++++++++++----- 1 file changed, 63 insertions(+), 17 deletions(-) diff --git a/tools/testing/selftests/landlock/audit.h b/tools/testing/selftests/landlock/audit.h index 17ede77c31b0e..f45fdef35681a 100644 --- a/tools/testing/selftests/landlock/audit.h +++ b/tools/testing/selftests/landlock/audit.h @@ -45,17 +45,25 @@ struct audit_message { }; }; -static const struct timeval audit_tv_dom_drop = { +static const struct timeval audit_tv_default = { /* - * Because domain deallocation is tied to asynchronous credential - * freeing, receiving such event may take some time. In practice, - * on a small VM, it should not exceed 100k usec, but let's wait up - * to 1 second to be safe. + * Default socket timeout for audit_match_record() callers that expect a + * record to arrive. Asynchronous kauditd delivery can exceed 1 usec + * under heavy debug configs (KASAN, lockdep), where kauditd_thread + * scheduling between audit_log_end() and netlink_unicast() takes longer + * than the previous 1 usec timeout. 1 second is a generous ceiling: on + * the happy path, kauditd delivers within dozens of usec. */ .tv_sec = 1, }; -static const struct timeval audit_tv_default = { +static const struct timeval audit_tv_fast = { + /* + * Fast timeout for paths that expect no record (audit_init() drain, + * audit_count_records(), probes). Causes audit_recv() to return + * -EAGAIN once the socket buffer is empty, naturally terminating the + * read loop. + */ .tv_usec = 1, }; @@ -334,8 +342,13 @@ static int __maybe_unused matches_log_domain_allocated(int audit_fd, pid_t pid, * Matches a domain deallocation record. When expected_domain_id is non-zero, * the pattern includes the specific domain ID so that stale deallocation * records from a previous test (with a different domain ID) are skipped by - * audit_match_record(), and the socket timeout is temporarily increased to - * audit_tv_dom_drop to wait for the asynchronous kworker deallocation. + * audit_match_record(), waiting for the asynchronous kworker deallocation with + * the default patient timeout. + * + * When expected_domain_id is zero, the caller is probing for any dealloc record + * that may or may not arrive. Temporarily lowers the socket timeout to + * audit_tv_fast for this probe so it returns promptly when no record is + * pending; restores audit_tv_default after. */ static int __maybe_unused matches_log_domain_deallocated(int audit_fd, unsigned int num_denials, @@ -361,16 +374,21 @@ matches_log_domain_deallocated(int audit_fd, unsigned int num_denials, if (log_match_len >= sizeof(log_match)) return -E2BIG; - if (expected_domain_id) - setsockopt(audit_fd, SOL_SOCKET, SO_RCVTIMEO, - &audit_tv_dom_drop, sizeof(audit_tv_dom_drop)); + if (!expected_domain_id) { + if (setsockopt(audit_fd, SOL_SOCKET, SO_RCVTIMEO, + &audit_tv_fast, sizeof(audit_tv_fast))) + return -errno; + } err = audit_match_record(audit_fd, AUDIT_LANDLOCK_DOMAIN, log_match, domain_id); - if (expected_domain_id) - setsockopt(audit_fd, SOL_SOCKET, SO_RCVTIMEO, &audit_tv_default, - sizeof(audit_tv_default)); + if (!expected_domain_id) { + if (setsockopt(audit_fd, SOL_SOCKET, SO_RCVTIMEO, + &audit_tv_default, sizeof(audit_tv_default)) && + !err) + err = -errno; + } return err; } @@ -387,6 +405,11 @@ struct audit_records { * audit_init() and after the preceding audit_match_record() call. Allocation * records are emitted synchronously during landlock_log_denial() in the current * test's syscall context, so only those are counted in records->domain. + * + * Temporarily lowers SO_RCVTIMEO to audit_tv_fast for the read loop: this is a + * "no record expected" path that should terminate on the first -EAGAIN. The + * default patient timeout is restored on exit for subsequent + * audit_match_record() callers. */ static int audit_count_records(int audit_fd, struct audit_records *records) { @@ -403,6 +426,12 @@ static int audit_count_records(int audit_fd, struct audit_records *records) records->access = 0; records->domain = 0; + if (setsockopt(audit_fd, SOL_SOCKET, SO_RCVTIMEO, &audit_tv_fast, + sizeof(audit_tv_fast))) { + err = -errno; + goto out; + } + do { memset(&msg, 0, sizeof(msg)); err = audit_recv(audit_fd, &msg); @@ -429,6 +458,10 @@ static int audit_count_records(int audit_fd, struct audit_records *records) } while (true); out: + if (setsockopt(audit_fd, SOL_SOCKET, SO_RCVTIMEO, &audit_tv_default, + sizeof(audit_tv_default)) && + !err) + err = -errno; regfree(&dealloc_re); return err; } @@ -449,9 +482,9 @@ static int audit_init(void) if (err) goto err_close; - /* Sets a timeout for negative tests. */ - err = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &audit_tv_default, - sizeof(audit_tv_default)); + /* Uses the fast timeout to drain stale records below. */ + err = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &audit_tv_fast, + sizeof(audit_tv_fast)); if (err) { err = -errno; goto err_close; @@ -467,6 +500,19 @@ static int audit_init(void) while (audit_recv(fd, NULL) == 0) ; + /* + * Restores the default timeout for audit_match_record() callers that + * expect a record to arrive. Paths that expect no record restore the + * fast timeout locally (audit_count_records(), the expected_domain_id + * == 0 probe in matches_log_domain_deallocated()). + */ + err = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &audit_tv_default, + sizeof(audit_tv_default)); + if (err) { + err = -errno; + goto err_close; + } + return fd; err_close: From 5fd30133af864a1de0a0bd87d3fe3cf23205fbc7 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Sun, 7 Jun 2026 18:57:45 +0100 Subject: [PATCH 0599/2758] KVM: arm64: nv: Avoid dereferencing NULL VNCR pseudo-TLB commit 4be6cbeb93d26994bd1827ddbce391e3c4395c8f upstream. VNCR TLB invalidation occurs from MMU notifiers or TLBI instructions, and either can race against a vcpu not being onlined yet (no pseudo-TLB allocated). Similarly, the TLB might be invalid, and the invalidation should be skipped in this case. Both kvm_invalidate_vncr_ipa() and kvm_invalidate_vncr_va() are expected to perform the same checks, except that the latter doesn't check for the allocation and blindly dereferences the pointer. Solve this by introducing a new iterator built on top of the usual kvm_for_each_vcpu() that checks for both of the above conditions, and convert the two users to it. Reported-by: Hyunwoo Kim Link: https://lore.kernel.org/r/aiUvSbrWndQeUPc8@v4bel Fixes: 4ffa72ad8f37 ("KVM: arm64: nv: Add S1 TLB invalidation primitive for VNCR_EL2") Cc: stable@vger.kernel.org Reviewed-by: Oliver Upton Link: https://patch.msgid.link/20260607175745.297793-1-maz@kernel.org Signed-off-by: Marc Zyngier Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/nested.c | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 6f7bc9a9992e0..2fe6f8e617256 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -904,9 +904,21 @@ static void invalidate_vncr(struct vncr_tlb *vt) clear_fixmap(vncr_fixmap(vt->cpu)); } +/* + * VNCR TLB invalidation occurs from MMU notifiers or TLBI instructions, and + * either can race against a vcpu not being onlined yet (no pseudo-TLB + * allocated). Similarly, the TLB might be invalid. Skip those, as they + * obviously don't participate in the invalidation at this stage. + */ +#define kvm_for_each_vncr_tlb(idx, vcpup, tlbp, kvm) \ + kvm_for_each_vcpu(idx, vcpup, kvm) \ + if (((tlbp) = vcpup->arch.vncr_tlb) && \ + (tlbp)->valid) + static void kvm_invalidate_vncr_ipa(struct kvm *kvm, u64 start, u64 end) { struct kvm_vcpu *vcpu; + struct vncr_tlb *vt; unsigned long i; lockdep_assert_held_write(&kvm->mmu_lock); @@ -914,24 +926,9 @@ static void kvm_invalidate_vncr_ipa(struct kvm *kvm, u64 start, u64 end) if (!kvm_has_feat(kvm, ID_AA64MMFR4_EL1, NV_frac, NV2_ONLY)) return; - kvm_for_each_vcpu(i, vcpu, kvm) { - struct vncr_tlb *vt = vcpu->arch.vncr_tlb; + kvm_for_each_vncr_tlb(i, vcpu, vt, kvm) { u64 ipa_start, ipa_end, ipa_size; - /* - * Careful here: We end-up here from an MMU notifier, - * and this can race against a vcpu not being onlined - * yet, without the pseudo-TLB being allocated. - * - * Skip those, as they obviously don't participate in - * the invalidation at this stage. - */ - if (!vt) - continue; - - if (!vt->valid) - continue; - ipa_size = ttl_to_size(pgshift_level_to_ttl(vt->wi.pgshift, vt->wr.level)); ipa_start = vt->wr.pa & ~(ipa_size - 1); @@ -961,17 +958,14 @@ static void invalidate_vncr_va(struct kvm *kvm, struct s1e2_tlbi_scope *scope) { struct kvm_vcpu *vcpu; + struct vncr_tlb *vt; unsigned long i; lockdep_assert_held_write(&kvm->mmu_lock); - kvm_for_each_vcpu(i, vcpu, kvm) { - struct vncr_tlb *vt = vcpu->arch.vncr_tlb; + kvm_for_each_vncr_tlb(i, vcpu, vt, kvm) { u64 va_start, va_end, va_size; - if (!vt->valid) - continue; - va_size = ttl_to_size(pgshift_level_to_ttl(vt->wi.pgshift, vt->wr.level)); va_start = vt->gva & ~(va_size - 1); From 60e51a62170a88900f06468ec20c83341530d717 Mon Sep 17 00:00:00 2001 From: Zeng Chi Date: Thu, 11 Jun 2026 20:46:44 +0800 Subject: [PATCH 0600/2758] LoongArch: KVM: Add missing slots_lock for device register/unregister commit aeded601d6aceb57cdda4b2701d2ee00c43a8b69 upstream. kvm_io_bus_register_dev() and kvm_io_bus_unregister_dev() should be called under kvm->slots_lock. The unregister calls in ipi.c, eiointc.c and pch_pic.c were also missing this protection. Add it to match the register side. Cc: stable@vger.kernel.org Reviewed-by: Bibo Mao Signed-off-by: Zeng Chi Signed-off-by: Huacai Chen Signed-off-by: Greg Kroah-Hartman --- arch/loongarch/kvm/intc/eiointc.c | 6 ++++++ arch/loongarch/kvm/intc/ipi.c | 2 ++ arch/loongarch/kvm/intc/pch_pic.c | 2 ++ 3 files changed, 10 insertions(+) diff --git a/arch/loongarch/kvm/intc/eiointc.c b/arch/loongarch/kvm/intc/eiointc.c index 2ab7fafa86d59..2b14485d14a70 100644 --- a/arch/loongarch/kvm/intc/eiointc.c +++ b/arch/loongarch/kvm/intc/eiointc.c @@ -645,10 +645,14 @@ static int kvm_eiointc_create(struct kvm_device *dev, u32 type) device = &s->device_vext; kvm_iodevice_init(device, &kvm_eiointc_virt_ops); + mutex_lock(&kvm->slots_lock); ret = kvm_io_bus_register_dev(kvm, KVM_IOCSR_BUS, EIOINTC_VIRT_BASE, EIOINTC_VIRT_SIZE, device); + mutex_unlock(&kvm->slots_lock); if (ret < 0) { + mutex_lock(&kvm->slots_lock); kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &s->device); + mutex_unlock(&kvm->slots_lock); kfree(s); return ret; } @@ -667,8 +671,10 @@ static void kvm_eiointc_destroy(struct kvm_device *dev) kvm = dev->kvm; eiointc = kvm->arch.eiointc; + mutex_lock(&kvm->slots_lock); kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &eiointc->device); kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &eiointc->device_vext); + mutex_unlock(&kvm->slots_lock); kfree(eiointc); kfree(dev); } diff --git a/arch/loongarch/kvm/intc/ipi.c b/arch/loongarch/kvm/intc/ipi.c index 1f6ebbd0af5c8..4fa0897d7bdb0 100644 --- a/arch/loongarch/kvm/intc/ipi.c +++ b/arch/loongarch/kvm/intc/ipi.c @@ -447,7 +447,9 @@ static void kvm_ipi_destroy(struct kvm_device *dev) kvm = dev->kvm; ipi = kvm->arch.ipi; + mutex_lock(&kvm->slots_lock); kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &ipi->device); + mutex_unlock(&kvm->slots_lock); kfree(ipi); kfree(dev); } diff --git a/arch/loongarch/kvm/intc/pch_pic.c b/arch/loongarch/kvm/intc/pch_pic.c index aa0ed59ae8cf9..175a630aceb41 100644 --- a/arch/loongarch/kvm/intc/pch_pic.c +++ b/arch/loongarch/kvm/intc/pch_pic.c @@ -481,7 +481,9 @@ static void kvm_pch_pic_destroy(struct kvm_device *dev) kvm = dev->kvm; s = kvm->arch.pch_pic; /* unregister pch pic device and free it's memory */ + mutex_lock(&kvm->slots_lock); kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS, &s->device); + mutex_unlock(&kvm->slots_lock); kfree(s); kfree(dev); } From 7fca3fcef81c713bc82a37bf741e0f28e6d04a6f Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Sun, 7 Jun 2026 02:56:11 +0900 Subject: [PATCH 0601/2758] KVM: arm64: Bound used_lrs when flushing the pKVM hyp vCPU commit 8cc8bbbfab14c22c5551d0dd19b208a44b141c76 upstream. flush_hyp_vcpu() copies the host vGIC state into the hyp's private vCPU on every run. The vGIC list register save and restore use used_lrs as their loop bound and expect it to stay within the number of implemented list registers. While this is generally the case, flush_hyp_vcpu() copies vgic_v3 verbatim and does not enforce this, so a value provided by the host is used at EL2 to index vgic_lr[] and access ICH_LR_EL2 (host -> EL2). Fix by clamping used_lrs to the number of implemented list registers after the copy, as the trusted path already does in vgic_flush_lr_state(). The number of implemented list registers is constant after init, so it is replicated once from kvm_vgic_global_state.nr_lr into hyp_gicv3_nr_lr rather than read on every entry. Cc: stable@vger.kernel.org Fixes: be66e67f1750 ("KVM: arm64: Use the pKVM hyp vCPU structure in handle___kvm_vcpu_run()") Signed-off-by: Hyunwoo Kim Reviewed-by: Fuad Tabba Tested-by: Fuad Tabba Link: https://patch.msgid.link/20260606175614.83273-3-imv4bel@gmail.com Signed-off-by: Marc Zyngier Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/kvm_hyp.h | 1 + arch/arm64/kvm/arm.c | 2 ++ arch/arm64/kvm/hyp/nvhe/hyp-main.c | 9 +++++++++ 3 files changed, 12 insertions(+) diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h index 8d06b62e7188c..e9b2b0c40ec63 100644 --- a/arch/arm64/include/asm/kvm_hyp.h +++ b/arch/arm64/include/asm/kvm_hyp.h @@ -157,5 +157,6 @@ extern unsigned long kvm_nvhe_sym(__icache_flags); extern unsigned int kvm_nvhe_sym(kvm_arm_vmid_bits); extern unsigned int kvm_nvhe_sym(kvm_host_sve_max_vl); extern unsigned long kvm_nvhe_sym(hyp_nr_cpus); +extern unsigned int kvm_nvhe_sym(hyp_gicv3_nr_lr); #endif /* __ARM64_KVM_HYP_H__ */ diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 9453321ef8c67..9ffd5d4079e6c 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -2426,6 +2426,8 @@ static int __init init_subsystems(void) switch (err) { case 0: vgic_present = true; + if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif)) + kvm_nvhe_sym(hyp_gicv3_nr_lr) = kvm_vgic_global_state.nr_lr; break; case -ENODEV: case -ENXIO: diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c index 06db299c37a89..15fa12130df0a 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c @@ -24,6 +24,9 @@ DEFINE_PER_CPU(struct kvm_nvhe_init_params, kvm_init_params); +/* Number of implemented GICv3 LRs. Used by flush_hyp_vcpu(). */ +unsigned int hyp_gicv3_nr_lr; + void __kvm_hyp_host_forward_smc(struct kvm_cpu_context *host_ctxt); static void __hyp_sve_save_guest(struct kvm_vcpu *vcpu) @@ -139,6 +142,12 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu) hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3 = host_vcpu->arch.vgic_cpu.vgic_v3; + /* Bound used_lrs by the number of implemented list registers. */ + hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3.used_lrs = + min_t(unsigned int, + hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3.used_lrs, + hyp_gicv3_nr_lr); + hyp_vcpu->vcpu.arch.pid = host_vcpu->arch.pid; } From d4f4d61715d1061ba83b88196a3605662be30750 Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Sun, 7 Jun 2026 02:56:10 +0900 Subject: [PATCH 0602/2758] KVM: arm64: Clear __hyp_running_vcpu when flushing the pKVM hyp vCPU commit e8042f6e1d7befb2fb6b10a75918642bcd0acf9a upstream. flush_hyp_vcpu() copies the host vCPU context into the hyp's private vCPU on every run. ctxt_to_vcpu() expects a guest context to have a NULL __hyp_running_vcpu, which is only ever set on the host context, so that it resolves the vCPU via container_of(). While this is generally the case, flush_hyp_vcpu() copies the context verbatim and does not enforce this, so a value provided by the host is dereferenced at EL2 (host -> EL2). Fix by clearing __hyp_running_vcpu after the copy. Cc: stable@vger.kernel.org Fixes: be66e67f1750 ("KVM: arm64: Use the pKVM hyp vCPU structure in handle___kvm_vcpu_run()") Signed-off-by: Hyunwoo Kim Reviewed-by: Fuad Tabba Tested-by: Fuad Tabba Link: https://patch.msgid.link/20260606175614.83273-2-imv4bel@gmail.com Signed-off-by: Marc Zyngier Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/hyp/nvhe/hyp-main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c index 15fa12130df0a..a0da08caa6c27 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c @@ -131,6 +131,9 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu) hyp_vcpu->vcpu.arch.ctxt = host_vcpu->arch.ctxt; + /* __hyp_running_vcpu must be NULL in a guest context. */ + hyp_vcpu->vcpu.arch.ctxt.__hyp_running_vcpu = NULL; + hyp_vcpu->vcpu.arch.mdcr_el2 = host_vcpu->arch.mdcr_el2; hyp_vcpu->vcpu.arch.hcr_el2 &= ~(HCR_TWI | HCR_TWE); hyp_vcpu->vcpu.arch.hcr_el2 |= READ_ONCE(host_vcpu->arch.hcr_el2) & From dcdb476f5fc5701ec06c23efe3e3529f07ca391e Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 22 May 2026 15:46:06 -0700 Subject: [PATCH 0603/2758] KVM: SEV: Pin source page for write when adding CPUID data for SNP guest commit f13e900599089b10113ceb36013423f0837c6792 upstream. When populating a guest_memfd instance with the initial CPUID data for an SNP guest, acquire a writable pin on the source page as KVM will write back the "correct" CPUID information if the userspace provided data is rejected by trusted firmware. Because KVM writes to the source page using a kernel mapping, pinning for read could result in KVM clobbering read-only memory. Note, well-behaved VMMs are unlikely to be affected, as CPUID information is almost always dynamically generated by userspace, i.e. it's unlikely for the CPUID information to be backed by a read-only mapping. Fixes: 2a62345b30529 ("KVM: guest_memfd: GUP source pages prior to populating guest memory") Cc: stable@vger.kernel.org Signed-off-by: Ackerley Tng Link: https://patch.msgid.link/20260522-fix-sev-gmem-post-populate-v2-1-3f196bfad5a1@google.com [sean: rewrite shortlog and changelog, tag for stable@] Signed-off-by: Sean Christopherson Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/svm/sev.c | 1 + arch/x86/kvm/vmx/tdx.c | 2 +- include/linux/kvm_host.h | 3 ++- virt/kvm/guest_memfd.c | 6 ++++-- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 29c3d557b78ba..f7da48c13f77f 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -2470,6 +2470,7 @@ static int snp_launch_update(struct kvm *kvm, struct kvm_sev_cmd *argp) sev_populate_args.type = params.type; count = kvm_gmem_populate(kvm, params.gfn_start, src, npages, + params.type == KVM_SEV_SNP_PAGE_TYPE_CPUID, sev_gmem_post_populate, &sev_populate_args); if (count < 0) { argp->error = sev_populate_args.fw_error; diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index 04ce321ebdf39..326b74eb98b64 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -3185,7 +3185,7 @@ static int tdx_vcpu_init_mem_region(struct kvm_vcpu *vcpu, struct kvm_tdx_cmd *c }; gmem_ret = kvm_gmem_populate(kvm, gpa_to_gfn(region.gpa), u64_to_user_ptr(region.source_addr), - 1, tdx_gmem_post_populate, &arg); + 1, false, tdx_gmem_post_populate, &arg); if (gmem_ret < 0) { ret = gmem_ret; break; diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 0135c1c6ff060..5366f31e95b05 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -2601,7 +2601,8 @@ int kvm_arch_gmem_prepare(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn, int max_ord typedef int (*kvm_gmem_populate_cb)(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn, struct page *page, void *opaque); -long kvm_gmem_populate(struct kvm *kvm, gfn_t gfn, void __user *src, long npages, +long kvm_gmem_populate(struct kvm *kvm, gfn_t start_gfn, void __user *src, + long npages, bool may_writeback_src, kvm_gmem_populate_cb post_populate, void *opaque); #endif diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c index 69c9d6d546b28..07d8db344872b 100644 --- a/virt/kvm/guest_memfd.c +++ b/virt/kvm/guest_memfd.c @@ -858,7 +858,8 @@ static long __kvm_gmem_populate(struct kvm *kvm, struct kvm_memory_slot *slot, return ret; } -long kvm_gmem_populate(struct kvm *kvm, gfn_t start_gfn, void __user *src, long npages, +long kvm_gmem_populate(struct kvm *kvm, gfn_t start_gfn, void __user *src, + long npages, bool may_writeback_src, kvm_gmem_populate_cb post_populate, void *opaque) { struct kvm_memory_slot *slot; @@ -892,8 +893,9 @@ long kvm_gmem_populate(struct kvm *kvm, gfn_t start_gfn, void __user *src, long if (src) { unsigned long uaddr = (unsigned long)src + i * PAGE_SIZE; + unsigned int flags = may_writeback_src ? FOLL_WRITE : 0; - ret = get_user_pages_fast(uaddr, 1, 0, &src_page); + ret = get_user_pages_fast(uaddr, 1, flags, &src_page); if (ret < 0) break; if (ret != 1) { From 256034648b9e41e521b7ba10c1208452fb500830 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 14 May 2026 14:31:13 -0700 Subject: [PATCH 0604/2758] KVM: x86: Add dedicated API for getting mask of accelerated x2APIC MSRs commit 8ba621f335a519b47cb7d3e3f4f15b5101b3a56f upstream. Add a dedicated local APIC API, kvm_x2apic_disable_intercept_reg_mask(), to provide the mask of x2APIC registers whose MSRs can and should be passed through to the guest when x2APIC virtualization is enable, and use it in lieu of the open-coded equivalent VMX logic. Providing a common helper will allow sharing the logic with SVM (x2AVIC), and as a bonus eliminates the somewhat confusing code where KVM enables interception for MSR_TYPE_RW, even though only the READ case actually needs to be updated. No functional change intended. Cc: stable@vger.kernel.org Reviewed-by: Naveen N Rao (AMD) Link: https://patch.msgid.link/20260514213115.1637082-2-seanjc@google.com Signed-off-by: Sean Christopherson Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/lapic.c | 21 +++++++++++++++++++-- arch/x86/kvm/lapic.h | 2 +- arch/x86/kvm/vmx/vmx.c | 3 +-- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 63e068ae29822..92a1e69c59626 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -1732,7 +1732,7 @@ static inline struct kvm_lapic *to_lapic(struct kvm_io_device *dev) #define APIC_REGS_MASK(first, count) \ (APIC_REG_MASK(first) * ((1ull << (count)) - 1)) -u64 kvm_lapic_readable_reg_mask(struct kvm_lapic *apic) +static u64 kvm_lapic_readable_reg_mask(struct kvm_lapic *apic) { /* Leave bits '0' for reserved and write-only registers. */ u64 valid_reg_mask = @@ -1768,7 +1768,24 @@ u64 kvm_lapic_readable_reg_mask(struct kvm_lapic *apic) return valid_reg_mask; } -EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_lapic_readable_reg_mask); + +u64 kvm_x2apic_disable_read_intercept_reg_mask(struct kvm_vcpu *vcpu) +{ + if (WARN_ON_ONCE(!lapic_in_kernel(vcpu))) + return 0; + + /* + * TMMCT, a.k.a. the current APIC timer count, reads aren't accelerated + * by hardware (Intel or AMD) as the timer is emulated in software (by + * KVM), i.e. reads from the virtual APIC page would return garbage. + * Intercept RDMSR, as handling the fault-like APIC-access VM-Exit is + * more expensive than handling a RDMSR VM-Exit (the APIC-access exit + * requires slow emulation of the code stream). + */ + return kvm_lapic_readable_reg_mask(vcpu->arch.apic) & + ~APIC_REG_MASK(APIC_TMCCT); +} +EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_x2apic_disable_read_intercept_reg_mask); static int kvm_lapic_reg_read(struct kvm_lapic *apic, u32 offset, int len, void *data) diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h index 533581d061517..71970213dc1fc 100644 --- a/arch/x86/kvm/lapic.h +++ b/arch/x86/kvm/lapic.h @@ -157,7 +157,7 @@ int kvm_hv_vapic_msr_read(struct kvm_vcpu *vcpu, u32 msr, u64 *data); int kvm_lapic_set_pv_eoi(struct kvm_vcpu *vcpu, u64 data, unsigned long len); void kvm_lapic_exit(void); -u64 kvm_lapic_readable_reg_mask(struct kvm_lapic *apic); +u64 kvm_x2apic_disable_read_intercept_reg_mask(struct kvm_vcpu *vcpu); static inline void kvm_lapic_set_irr(int vec, struct kvm_lapic *apic) { diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 719dfa6b780fe..3b32bfa666f41 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -4141,7 +4141,7 @@ static void vmx_update_msr_bitmap_x2apic(struct kvm_vcpu *vcpu) * mode, only the current timer count needs on-demand emulation by KVM. */ if (mode & MSR_BITMAP_MODE_X2APIC_APICV) - msr_bitmap[read_idx] = ~kvm_lapic_readable_reg_mask(vcpu->arch.apic); + msr_bitmap[read_idx] = ~kvm_x2apic_disable_read_intercept_reg_mask(vcpu); else msr_bitmap[read_idx] = ~0ull; msr_bitmap[write_idx] = ~0ull; @@ -4154,7 +4154,6 @@ static void vmx_update_msr_bitmap_x2apic(struct kvm_vcpu *vcpu) !(mode & MSR_BITMAP_MODE_X2APIC)); if (mode & MSR_BITMAP_MODE_X2APIC_APICV) { - vmx_enable_intercept_for_msr(vcpu, X2APIC_MSR(APIC_TMCCT), MSR_TYPE_RW); vmx_disable_intercept_for_msr(vcpu, X2APIC_MSR(APIC_EOI), MSR_TYPE_W); vmx_disable_intercept_for_msr(vcpu, X2APIC_MSR(APIC_SELF_IPI), MSR_TYPE_W); if (enable_ipiv) From a3487d5926dd7476bf946f2ea7c4c44a1d84ee2f Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 14 May 2026 14:31:14 -0700 Subject: [PATCH 0605/2758] KVM: SVM: Disable x2AVIC RDMSR interception for MSRs KVM actually supports commit 7f4b7092d9a173a4271e28c0ed1fc235994e309b upstream. When toggling x2AVIC on/off, use KVM's curated mask of x2APIC MSRs that can/should be passed through to the guest (or not) when 2AVIC is enabled. Using the effective list provided by the local APIC emulation fixes multiple (classes of) bugs, as the existing hand-coded list of MSRs is wrong on multiple fronts: - ARBPRI isn't supported by KVM, isn't accelerated by AVIC (for read or write), and its #VMEXIT is fault-like, i.e. requires decoding the instruction. Disabling interception is nonsensical and suboptimal. - DFR and ICR2 aren't supported by x2APIC and so don't need their intercepts disabled for performance reasons. While the #GP due to x2APIC being abled has higher priority than the trap-like #VMEXIT, disabling interception of unsupported MSRs is confusing and unnecessary. - RRR is completely unsupported. - AVIC currently fails to pass through the "range of vectors" registers, IRR, ISR, and TMR, as e.g. X2APIC_MSR(APIC_IRR) only affects IRR0, and thus only disables intercept for vectors 31:0 (which are the *least* interesting registers). - TMCCT (the current APIC timer count) isn't accelerated by hardware, and generates a fault-like AVIC_UNACCELERATED_ACCESS #VMEXIT, i.e. requires KVM to decode the instruction to figure out what the guest was trying to access. Note, the only reason this isn't a fatal bug is that the AVIC architecture had the foresight to guard against buggy hypervisors. E.g. if hardware simply read from the virtual APIC page, the guest would get garbage (because the timer is emulated in software). Fixes: 4d1d7942e36a ("KVM: SVM: Introduce logic to (de)activate x2AVIC mode") Cc: stable@vger.kernel.org Reviewed-by: Naveen N Rao (AMD) Link: https://patch.msgid.link/20260514213115.1637082-3-seanjc@google.com Signed-off-by: Sean Christopherson Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/svm/avic.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c index cdd5a6dc646f1..9c286c4caedd3 100644 --- a/arch/x86/kvm/svm/avic.c +++ b/arch/x86/kvm/svm/avic.c @@ -122,6 +122,9 @@ static u32 x2avic_max_physical_id; static void avic_set_x2apic_msr_interception(struct vcpu_svm *svm, bool intercept) { + struct kvm_vcpu *vcpu = &svm->vcpu; + u64 rd_regs; + static const u32 x2avic_passthrough_msrs[] = { X2APIC_MSR(APIC_ID), X2APIC_MSR(APIC_LVR), @@ -162,9 +165,15 @@ static void avic_set_x2apic_msr_interception(struct vcpu_svm *svm, if (!x2avic_enabled) return; + rd_regs = kvm_x2apic_disable_read_intercept_reg_mask(vcpu); + + for_each_set_bit(i, (unsigned long *)&rd_regs, BITS_PER_TYPE(rd_regs)) + svm_set_intercept_for_msr(vcpu, APIC_BASE_MSR + i, + MSR_TYPE_R, intercept); + for (i = 0; i < ARRAY_SIZE(x2avic_passthrough_msrs); i++) - svm_set_intercept_for_msr(&svm->vcpu, x2avic_passthrough_msrs[i], - MSR_TYPE_RW, intercept); + svm_set_intercept_for_msr(vcpu, x2avic_passthrough_msrs[i], + MSR_TYPE_W, intercept); svm->x2avic_msrs_intercepted = intercept; } From dda5ce3fdf89daf0148f726c8cc6a55e6083c189 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 14 May 2026 14:31:15 -0700 Subject: [PATCH 0606/2758] KVM: SVM: Only disable x2AVIC WRMSR interception for MSRs that are accelerated commit 8c63179d975f2029c948ecce622f72af616dbff7 upstream. When x2AVIC is enabled, disable WRMSR interception only for MSRs that are actually accelerated by hardware. Disabling interception for MSRs that aren't accelerated is functionally "fine", and in some cases a weird "win" for performance, but only for cases that should never be triggered by a well-behaved VM (writes to read-only registers; the #GP will typically occur in the guest without taking a #VMEXIT, even for fault-like exits). But overall, disabling interception for MSRs that aren't accelerated is at best confusing and unintuitive, and at worst introduces avoidable risk, as the APM's documentation is imperfect and contradictory. The table in "15.29.3.1 Virtual APIC Register Accesses" of simply states that such writes generate exits, where as "Section 15.29.10 x2AVIC" says: x2APIC MSR intercept checks and access checks have higher priority than AVIC access permission checks. CPU behavior follows the latter (which makes perfect sense), but all in all there's simply no reason to disable interception just to make a #GP faster. Note, the set of MSRs that are passed through for write is identical to VMX's set when IPI virtualization is enabled. This is not a coincidence, and is another motiviating factor for cleaning up the intercepts, as x2AVIC is functionally equivalent to APICv+IPIv. Fixes: 4d1d7942e36a ("KVM: SVM: Introduce logic to (de)activate x2AVIC mode") Cc: stable@vger.kernel.org Reviewed-by: Naveen N Rao (AMD) Link: https://patch.msgid.link/20260514213115.1637082-4-seanjc@google.com Signed-off-by: Sean Christopherson Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/svm/avic.c | 40 ++++------------------------------------ 1 file changed, 4 insertions(+), 36 deletions(-) diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c index 9c286c4caedd3..0726f88e679aa 100644 --- a/arch/x86/kvm/svm/avic.c +++ b/arch/x86/kvm/svm/avic.c @@ -124,39 +124,6 @@ static void avic_set_x2apic_msr_interception(struct vcpu_svm *svm, { struct kvm_vcpu *vcpu = &svm->vcpu; u64 rd_regs; - - static const u32 x2avic_passthrough_msrs[] = { - X2APIC_MSR(APIC_ID), - X2APIC_MSR(APIC_LVR), - X2APIC_MSR(APIC_TASKPRI), - X2APIC_MSR(APIC_ARBPRI), - X2APIC_MSR(APIC_PROCPRI), - X2APIC_MSR(APIC_EOI), - X2APIC_MSR(APIC_RRR), - X2APIC_MSR(APIC_LDR), - X2APIC_MSR(APIC_DFR), - X2APIC_MSR(APIC_SPIV), - X2APIC_MSR(APIC_ISR), - X2APIC_MSR(APIC_TMR), - X2APIC_MSR(APIC_IRR), - X2APIC_MSR(APIC_ESR), - X2APIC_MSR(APIC_ICR), - X2APIC_MSR(APIC_ICR2), - - /* - * Note! Always intercept LVTT, as TSC-deadline timer mode - * isn't virtualized by hardware, and the CPU will generate a - * #GP instead of a #VMEXIT. - */ - X2APIC_MSR(APIC_LVTTHMR), - X2APIC_MSR(APIC_LVTPC), - X2APIC_MSR(APIC_LVT0), - X2APIC_MSR(APIC_LVT1), - X2APIC_MSR(APIC_LVTERR), - X2APIC_MSR(APIC_TMICT), - X2APIC_MSR(APIC_TMCCT), - X2APIC_MSR(APIC_TDCR), - }; int i; if (intercept == svm->x2avic_msrs_intercepted) @@ -171,9 +138,10 @@ static void avic_set_x2apic_msr_interception(struct vcpu_svm *svm, svm_set_intercept_for_msr(vcpu, APIC_BASE_MSR + i, MSR_TYPE_R, intercept); - for (i = 0; i < ARRAY_SIZE(x2avic_passthrough_msrs); i++) - svm_set_intercept_for_msr(vcpu, x2avic_passthrough_msrs[i], - MSR_TYPE_W, intercept); + svm_set_intercept_for_msr(vcpu, X2APIC_MSR(APIC_TASKPRI), MSR_TYPE_W, intercept); + svm_set_intercept_for_msr(vcpu, X2APIC_MSR(APIC_EOI), MSR_TYPE_W, intercept); + svm_set_intercept_for_msr(vcpu, X2APIC_MSR(APIC_SELF_IPI), MSR_TYPE_W, intercept); + svm_set_intercept_for_msr(vcpu, X2APIC_MSR(APIC_ICR), MSR_TYPE_W, intercept); svm->x2avic_msrs_intercepted = intercept; } From 77eab9571f6d672481503552b3eb5a60b0b66edb Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 15 May 2026 15:26:29 -0700 Subject: [PATCH 0607/2758] KVM: VMX: Refresh GUEST_PENDING_DBG_EXCEPTIONS.BS on all injected #DBs commit c5bad4fa2d5dfd8c25140051a9807eba387a19b8 upstream. Move KVM's stuffing of GUEST_PENDING_DBG_EXCEPTIONS.BS when RFLAGS.TF=1 and MOV/POP SS or STI blocking is active into the exception injection code so that KVM fixes up the VMCS for all injected #DBs, not only those that are reflected back into the guest after #DB interception. E.g. if KVM queues a #DB in the emulator, or more importantly if userspace does save/restore exactly on the #DB+shadow boundary, then KVM needs to massage the VMCS to avoid the VM-Entry consistency check. Opportunistically update the wording of the comment to describe the behavior as a workaround of flawed CPU behavior/architecture, to make it clear that the *only* thing KVM is doing is fudging around a consistency check. Per the SDM: There are no pending debug exceptions after VM entry if any of the following are true: * The VM entry is vectoring with one of the following interruption types: external interrupt, non-maskable interrupt (NMI), hardware exception, or privileged software exception. I.e. forcing GUEST_PENDING_DBG_EXCEPTIONS.BS does *not* impact guest- visible behavior. Fixes: b9bed78e2fa9 ("KVM: VMX: Set vmcs.PENDING_DBG.BS on #DB in STI/MOVSS blocking shadow") Cc: stable@vger.kernel.org Reported-by: Hou Wenlong Closes: https://lore.kernel.org/all/b1a294bc9ed4dae532474a5dc6c8cb6e5962de7c.1757416809.git.houwenlong.hwl@antgroup.com Reviewed-by: Hou Wenlong Link: https://patch.msgid.link/20260515222638.1949982-2-seanjc@google.com Signed-off-by: Sean Christopherson Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/vmx/vmx.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 3b32bfa666f41..3759dd336dfc9 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -1908,6 +1908,24 @@ void vmx_inject_exception(struct kvm_vcpu *vcpu) u32 intr_info = ex->vector | INTR_INFO_VALID_MASK; struct vcpu_vmx *vmx = to_vmx(vcpu); + /* + * When injecting a #DB, single-stepping is enabled in RFLAGS, and STI + * or MOV-SS blocking is active, set vmcs.PENDING_DBG_EXCEPTIONS.BS to + * prevent a false positive from VM-Entry consistency check. VM-Entry + * asserts that a single-step #DB _must_ be pending in this scenario, + * as the previous instruction cannot have toggled RFLAGS.TF 0=>1 + * (because STI and POP/MOV don't modify RFLAGS), therefore the one + * instruction delay when activating single-step breakpoints must have + * already expired. However, the CPU isn't smart enough to peek at + * vmcs.VM_ENTRY_INTR_INFO_FIELD and so doesn't realize that yes, there + * is indeed a #DB pending/imminent. + */ + if (ex->vector == DB_VECTOR && + (vmx_get_rflags(vcpu) & X86_EFLAGS_TF) && + vmx_get_interrupt_shadow(vcpu)) + vmcs_writel(GUEST_PENDING_DBG_EXCEPTIONS, + vmcs_readl(GUEST_PENDING_DBG_EXCEPTIONS) | DR6_BS); + kvm_deliver_exception_payload(vcpu, ex); if (ex->has_error_code) { @@ -5476,26 +5494,9 @@ static int handle_exception_nmi(struct kvm_vcpu *vcpu) * avoid single-step #DB and MTF updates, as ICEBP is * higher priority. Note, skipping ICEBP still clears * STI and MOVSS blocking. - * - * For all other #DBs, set vmcs.PENDING_DBG_EXCEPTIONS.BS - * if single-step is enabled in RFLAGS and STI or MOVSS - * blocking is active, as the CPU doesn't set the bit - * on VM-Exit due to #DB interception. VM-Entry has a - * consistency check that a single-step #DB is pending - * in this scenario as the previous instruction cannot - * have toggled RFLAGS.TF 0=>1 (because STI and POP/MOV - * don't modify RFLAGS), therefore the one instruction - * delay when activating single-step breakpoints must - * have already expired. Note, the CPU sets/clears BS - * as appropriate for all other VM-Exits types. */ if (is_icebp(intr_info)) WARN_ON(!skip_emulated_instruction(vcpu)); - else if ((vmx_get_rflags(vcpu) & X86_EFLAGS_TF) && - (vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & - (GUEST_INTR_STATE_STI | GUEST_INTR_STATE_MOV_SS))) - vmcs_writel(GUEST_PENDING_DBG_EXCEPTIONS, - vmcs_readl(GUEST_PENDING_DBG_EXCEPTIONS) | DR6_BS); kvm_queue_exception_p(vcpu, DB_VECTOR, dr6); return 1; From aa41338ee2db66e35407af92c3388aad4caaf5c5 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 20:35:43 +0200 Subject: [PATCH 0608/2758] KVM: SEV: Don't terminate SNP VMs on #VMGEXIT without a registered GHCB commit 1797e00bf802d64b859ea18505087daad92019c8 upstream. If the guest attempts a non-MSR #VMGEXIT without the registered GHCB, return a GHCB_HV_RESP_MALFORMED_INPUT+GHCB_ERR_NOT_REGISTERED error to the guest instead of exiting KVM_RUN with -EINVAL (and in likelihood killing the VM). KVM has already mapped the requested GHCB, i.e. can cleanly report an error, and so exiting with -EINVAL is completely unjustified. Fixes: 0c76b1d08280 ("KVM: SEV: Add support to handle GHCB GPA register VMGEXIT") Cc: stable@vger.kernel.org Reviewed-by: Tom Lendacky Reviewed-by: Michael Roth Signed-off-by: Sean Christopherson Message-ID: <20260501202250.2115252-19-seanjc@google.com> Signed-off-by: Paolo Bonzini Message-ID: <20260529183549.1104619-19-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/svm/sev.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index f7da48c13f77f..d90f5a41e3404 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -4522,9 +4522,12 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) sev_es_sync_from_ghcb(svm); /* SEV-SNP guest requires that the GHCB GPA must be registered */ - if (is_sev_snp_guest(vcpu) && !ghcb_gpa_is_registered(svm, ghcb_gpa)) { - vcpu_unimpl(&svm->vcpu, "vmgexit: GHCB GPA [%#llx] is not registered.\n", ghcb_gpa); - return -EINVAL; + if (is_sev_snp_guest(vcpu) && + !ghcb_gpa_is_registered(svm, control->ghcb_gpa)) { + vcpu_unimpl(vcpu, "vmgexit: GHCB GPA [%#llx] is not registered.\n", + control->ghcb_gpa); + svm_vmgexit_bad_input(svm, GHCB_ERR_NOT_REGISTERED); + return 1; } ret = sev_es_validate_vmgexit(svm); From f0a47e6cb0357e2e5ccb1762f709fd9b81970eed Mon Sep 17 00:00:00 2001 From: Xuanqing Shi <1356292400@qq.com> Date: Tue, 26 May 2026 19:26:17 -0700 Subject: [PATCH 0609/2758] KVM: VMX: Handle bad values on proxied writes to LBR MSRs commit ca674df13b195eb6d124ab059799d4e03fa40624 upstream. Use the "safe" WRMSR API when writing LBRs on behalf of the guest (or host userspace), and propagate any errors back to the instigator, as the value being written is untrusted. E.g. if the guest (or host userspace) attempts to set reserved bits in LBR_SELECT, then KVM needs to return an error, and not WARN on the bad value. Continue using the "unsafe" version of RDMSR, as it should be impossible to reach the helper with a completely bogus MSR, i.e. WARNing on RDMSR failure is very desirable, e.g. to make KVM bugs more visible. unchecked MSR access error: WRMSR to 0x1c8 (tried to write 0x0000000000004000) Call Trace: intel_pmu_set_msr+0x4e0/0x7f0 [kvm_intel] kvm_pmu_set_msr+0x17e/0x1c0 [kvm] kvm_set_msr_common+0xc76/0x1440 [kvm] vmx_set_msr+0x5e6/0x1570 [kvm_intel] kvm_emulate_wrmsr+0x54/0x1d0 [kvm] vmx_handle_exit+0x7fc/0x970 [kvm_intel] Fixes: 1b5ac3226a1a ("KVM: vmx/pmu: Pass-through LBR msrs when the guest LBR event is ACTIVE") Cc: stable@vger.kernel.org Signed-off-by: Xuanqing Shi <1356292400@qq.com> [sean: rework changelog, only modify WRMSR path, tag for stable@] Link: https://patch.msgid.link/20260527022617.3973884-1-seanjc@google.com Signed-off-by: Sean Christopherson Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/vmx/pmu_intel.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c index 27eb76e6b6a03..22138c487216e 100644 --- a/arch/x86/kvm/vmx/pmu_intel.c +++ b/arch/x86/kvm/vmx/pmu_intel.c @@ -308,13 +308,15 @@ static bool intel_pmu_handle_lbr_msrs_access(struct kvm_vcpu *vcpu, */ local_irq_disable(); if (lbr_desc->event->state == PERF_EVENT_STATE_ACTIVE) { + int err = 0; + if (read) rdmsrq(index, msr_info->data); else - wrmsrq(index, msr_info->data); + err = wrmsrq_safe(index, msr_info->data); __set_bit(INTEL_PMC_IDX_FIXED_VLBR, vcpu_to_pmu(vcpu)->pmc_in_use); local_irq_enable(); - return true; + return !err; } clear_bit(INTEL_PMC_IDX_FIXED_VLBR, vcpu_to_pmu(vcpu)->pmc_in_use); local_irq_enable(); From bf27cd2d58a4e8aab685c088552aefc23435f1f9 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 28 Jan 2026 17:14:35 -0800 Subject: [PATCH 0610/2758] KVM: TDX: Account all non-transient page allocations for per-TD structures commit a8b2924676ec42d76597f849baba31acc3ba1bfc upstream. Account all non-transient allocations associated with a single TD (or its vCPUs), as KVM's ABI is that allocations that are active for the lifetime of a VM are accounted. Leave temporary allocations, i.e. allocations that are freed within a single function/ioctl, unaccounted, to again align with KVM's existing behavior, e.g. see commit dd103407ca31 ("KVM: X86: Remove unnecessary GFP_KERNEL_ACCOUNT for temporary variables"). Fixes: 8d032b683c29 ("KVM: TDX: create/destroy VM structure") Fixes: a50f673f25e0 ("KVM: TDX: Do TDX specific vcpu initialization") Cc: stable@vger.kernel.org Reviewed-by: Kai Huang Reviewed-by: Rick Edgecombe Link: https://patch.msgid.link/20260129011517.3545883-4-seanjc@google.com Signed-off-by: Sean Christopherson Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/vmx/tdx.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index 326b74eb98b64..ec29a2db1adce 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -2387,20 +2387,20 @@ static int __tdx_td_init(struct kvm *kvm, struct td_params *td_params, ret = -ENOMEM; - tdr_page = alloc_page(GFP_KERNEL); + tdr_page = alloc_page(GFP_KERNEL_ACCOUNT); if (!tdr_page) goto free_hkid; kvm_tdx->td.tdcs_nr_pages = tdx_sysinfo->td_ctrl.tdcs_base_size / PAGE_SIZE; /* TDVPS = TDVPR(4K page) + TDCX(multiple 4K pages), -1 for TDVPR. */ kvm_tdx->td.tdcx_nr_pages = tdx_sysinfo->td_ctrl.tdvps_base_size / PAGE_SIZE - 1; - tdcs_pages = kzalloc_objs(*kvm_tdx->td.tdcs_pages, - kvm_tdx->td.tdcs_nr_pages); + tdcs_pages = kzalloc_objs(*kvm_tdx->td.tdcs_pages, kvm_tdx->td.tdcs_nr_pages, + GFP_KERNEL_ACCOUNT); if (!tdcs_pages) goto free_tdr; for (i = 0; i < kvm_tdx->td.tdcs_nr_pages; i++) { - tdcs_pages[i] = alloc_page(GFP_KERNEL); + tdcs_pages[i] = alloc_page(GFP_KERNEL_ACCOUNT); if (!tdcs_pages[i]) goto free_tdcs; } @@ -2875,7 +2875,7 @@ static int tdx_td_vcpu_init(struct kvm_vcpu *vcpu, u64 vcpu_rcx) int ret, i; u64 err; - page = alloc_page(GFP_KERNEL); + page = alloc_page(GFP_KERNEL_ACCOUNT); if (!page) return -ENOMEM; tdx->vp.tdvpr_page = page; @@ -2888,14 +2888,14 @@ static int tdx_td_vcpu_init(struct kvm_vcpu *vcpu, u64 vcpu_rcx) tdx->vp.tdvpr_pa = page_to_phys(tdx->vp.tdvpr_page); tdx->vp.tdcx_pages = kcalloc(kvm_tdx->td.tdcx_nr_pages, sizeof(*tdx->vp.tdcx_pages), - GFP_KERNEL); + GFP_KERNEL_ACCOUNT); if (!tdx->vp.tdcx_pages) { ret = -ENOMEM; goto free_tdvpr; } for (i = 0; i < kvm_tdx->td.tdcx_nr_pages; i++) { - page = alloc_page(GFP_KERNEL); + page = alloc_page(GFP_KERNEL_ACCOUNT); if (!page) { ret = -ENOMEM; goto free_tdcx; From f2ca2b5326211bd38490f0497eb583721ce0bbc0 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 23 Apr 2026 09:26:27 -0700 Subject: [PATCH 0611/2758] KVM: x86: Ensure vendor's exit handler runs before fastpath userspace exits commit 0ffedf43910e44b76c2c1db4e9fbf12b268190c1 upstream. Move the handling of fastpath userspace exits into vendor code to ensure KVM runs vendor specific operations that need to run before userspace gains control of the vCPU. E.g. for VMX (and soon to be for SVM as well), KVM needs to flush the PML buffer prior to exiting to userspace, otherwise any memory written by the final KVM_RUN might never be flagged as dirty. Note, waiting to snapshot CR0 and CR3 until svm_handle_exit() is flawed in general, as that risks consuming stale state in a fastpath handler. That will be addressed in a future change. Fixes: f7f39c50edb9 ("KVM: x86: Exit to userspace if fastpath triggers one on instruction skip") Cc: stable@vger.kernel.org Cc: Nikunj A. Dadhania Reviewed-by: Nikunj A. Dadhania Reviewed-by: Kai Huang Link: https://patch.msgid.link/20260423162628.490962-2-seanjc@google.com Signed-off-by: Sean Christopherson Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/svm/svm.c | 3 +++ arch/x86/kvm/vmx/vmx.c | 3 +++ arch/x86/kvm/x86.c | 3 --- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index e02a38da5296e..2544c551e702f 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -3663,6 +3663,9 @@ static int svm_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath) vcpu->arch.cr3 = svm->vmcb->save.cr3; } + if (unlikely(exit_fastpath == EXIT_FASTPATH_EXIT_USERSPACE)) + return 0; + if (is_guest_mode(vcpu)) { int vmexit; diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 3759dd336dfc9..f389ac9fcb33b 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -6697,6 +6697,9 @@ static int __vmx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath) if (enable_pml && !is_guest_mode(vcpu)) vmx_flush_pml_buffer(vcpu); + if (unlikely(exit_fastpath == EXIT_FASTPATH_EXIT_USERSPACE)) + return 0; + /* * KVM should never reach this point with a pending nested VM-Enter. * More specifically, short-circuiting VM-Entry to emulate L2 due to diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 485df779ae67e..88f0e3fd8919c 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -11578,9 +11578,6 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) if (vcpu->arch.apic_attention) kvm_lapic_sync_from_vapic(vcpu); - if (unlikely(exit_fastpath == EXIT_FASTPATH_EXIT_USERSPACE)) - return 0; - r = kvm_x86_call(handle_exit)(vcpu, exit_fastpath); return r; From f3a98d5881b9bd4807f49156143565f6aabcef1e Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Tue, 2 Jun 2026 10:09:19 -0700 Subject: [PATCH 0612/2758] KVM: guest_memfd: Treat memslot binding offset+size as unsigned values commit eba85fee7fc6cf28fec38a5bf3c378bef9a79ca6 upstream. When binding a memslot to a guest_memfd file, treat the offset and size as unsigned values to fix a bug where the sum of the two can result in a false negative when checking for overflow against the size of the file. Passing unsigned values also avoids relying on somewhat obscure checks in other flows for safety, and tracks the offset and size as they are intended to be tracked, as unsigned values. On 64-bit kernels, the number of pages a memslot contains and thus the size (and offset) of its guest_memfd binding are unsigned 64-bit values. Taking the offset+size as an loff_t instead of a uoff_t inadvertently converts the unsigned value to a signed value if the offset and/or size is massive. Locally storing the offset and size as signed values is benign in and of itself (though even that is *extremely* difficult to discern), but operating on their sum is not. For the offset, KVM explicitly checks against a negative value, which might seem like a bug as KVM could incorrectly reject a legitimate binding, but that's not actually the case as KVM_CREATE_GUEST_MEMFD takes a signed value for its size, i.e. a would-be-negative offset is also greater than the maximum possible size of any guest_memfd file. Regarding the size, while KVM lacks an explicit check for a negative value, i.e. seemingly has a flawed overflow check, KVM restricts the number of pages in a single memslot to the largest positive signed 32-bit value: if (id < KVM_USER_MEM_SLOTS && (mem->memory_size >> PAGE_SHIFT) > KVM_MEM_MAX_NR_PAGES) return -EINVAL; and so that maximum "size" will ever be is 0x7fffffff000. The sum of the two is, however, problematic. While the size is restricted by KVM's memslot logic, the offset is not, i.e. the offset is completely unchecked until the "offset + size > i_size_read(inode)" check. If the offset is the (nearly) largest possible _positive_ value, then adding size to the offset can result in a signed, negative 64-bit value. When compared against the size of the file (guaranteed to be positive), the negative sum is always smaller, and KVM incorrectly allows the absurd offset. Opportunistically add missing includes in kvm_mm.h (instead of relying on its parents). Fixes: a7800aa80ea4 ("KVM: Add KVM_CREATE_GUEST_MEMFD ioctl() for guest-specific backing memory") Cc: stable@vger.kernel.org Cc: Ackerley Tng Reviewed-by: Michael Roth Reviewed-by: Ackerley Tng Link: https://patch.msgid.link/20260602170921.1304394-2-seanjc@google.com Signed-off-by: Sean Christopherson Signed-off-by: Greg Kroah-Hartman --- virt/kvm/guest_memfd.c | 8 ++++---- virt/kvm/kvm_mm.h | 7 +++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c index 07d8db344872b..3694d5405a5e3 100644 --- a/virt/kvm/guest_memfd.c +++ b/virt/kvm/guest_memfd.c @@ -640,15 +640,16 @@ int kvm_gmem_create(struct kvm *kvm, struct kvm_create_guest_memfd *args) } int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot, - unsigned int fd, loff_t offset) + unsigned int fd, uoff_t offset) { - loff_t size = slot->npages << PAGE_SHIFT; + uoff_t size = slot->npages << PAGE_SHIFT; unsigned long start, end; struct gmem_file *f; struct inode *inode; struct file *file; int r = -EINVAL; + BUILD_BUG_ON(sizeof(gpa_t) != sizeof(offset)); BUILD_BUG_ON(sizeof(gfn_t) != sizeof(slot->gmem.pgoff)); file = fget(fd); @@ -664,8 +665,7 @@ int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot, inode = file_inode(file); - if (offset < 0 || !PAGE_ALIGNED(offset) || - offset + size > i_size_read(inode)) + if (!PAGE_ALIGNED(offset) || offset + size > i_size_read(inode)) goto err; filemap_invalidate_lock(inode->i_mapping); diff --git a/virt/kvm/kvm_mm.h b/virt/kvm/kvm_mm.h index 9fcc5d5b7f8d0..7510ca915dd1a 100644 --- a/virt/kvm/kvm_mm.h +++ b/virt/kvm/kvm_mm.h @@ -3,6 +3,9 @@ #ifndef __KVM_MM_H__ #define __KVM_MM_H__ 1 +#include +#include + /* * Architectures can choose whether to use an rwlock or spinlock * for the mmu_lock. These macros, for use in common code @@ -72,7 +75,7 @@ int kvm_gmem_init(struct module *module); void kvm_gmem_exit(void); int kvm_gmem_create(struct kvm *kvm, struct kvm_create_guest_memfd *args); int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot, - unsigned int fd, loff_t offset); + unsigned int fd, uoff_t offset); void kvm_gmem_unbind(struct kvm_memory_slot *slot); #else static inline int kvm_gmem_init(struct module *module) @@ -82,7 +85,7 @@ static inline int kvm_gmem_init(struct module *module) static inline void kvm_gmem_exit(void) {}; static inline int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot, - unsigned int fd, loff_t offset) + unsigned int fd, uoff_t offset) { WARN_ON_ONCE(1); return -EIO; From cd1067ccc0dbc18890a74db116d00a4bc3c7f2f7 Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Tue, 2 Jun 2026 16:54:46 -0700 Subject: [PATCH 0613/2758] KVM: arm64: Don't leak PFN when kvm_translate_vncr() races MMU notifier commit 9f76b039a72d7e06374aa96862f0232ed53f7787 upstream. In the case that kvm_translate_vncr() races with an MMU notifier the early return does not release a reference on the faulted in PFN. Add the necessary call to kvm_release_faultin_page() for the unused PFN. Cc: stable@vger.kernel.org Fixes: 069a05e535496 ("KVM: arm64: nv: Handle VNCR_EL2-triggered faults") Reported-by: Sashiko (local):gemini-3.1-pro Signed-off-by: Oliver Upton Link: https://patch.msgid.link/20260602235450.103057-2-oupton@kernel.org Signed-off-by: Marc Zyngier Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/nested.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 2fe6f8e617256..682e1c8437fb1 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -1327,8 +1327,10 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem) } scoped_guard(write_lock, &vcpu->kvm->mmu_lock) { - if (mmu_invalidate_retry(vcpu->kvm, mmu_seq)) + if (mmu_invalidate_retry(vcpu->kvm, mmu_seq)) { + kvm_release_faultin_page(vcpu->kvm, page, true, false); return -EAGAIN; + } vt->gva = va; vt->hpa = pfn << PAGE_SHIFT; From 18bd476ef4a1094d27c7084069f4c1e177e309d8 Mon Sep 17 00:00:00 2001 From: Mikhail Gavrilov Date: Sun, 15 Mar 2026 04:27:22 +0500 Subject: [PATCH 0614/2758] udmabuf: fix DMA direction mismatch in release_udmabuf() commit fb7b1a0ab25a6077d26cb3829e31743972d4f31d upstream. begin_cpu_udmabuf() maps the sg_table with the caller-provided direction (e.g., DMA_TO_DEVICE for a write-only sync), and caches it in ubuf->sg for reuse. However, release_udmabuf() always unmaps this sg_table with a hardcoded DMA_BIDIRECTIONAL, regardless of the direction that was originally used for the mapping. With CONFIG_DMA_API_DEBUG=y this produces: DMA-API: misc udmabuf: device driver frees DMA memory with different direction [device address=0x000000044a123000] [size=4096 bytes] [mapped with DMA_TO_DEVICE] [unmapped with DMA_BIDIRECTIONAL] The issue was found during video playback when GStreamer performed a write-only DMA_BUF_IOCTL_SYNC on a udmabuf. It can be reproduced with CONFIG_DMA_API_DEBUG=y by creating a udmabuf from a memfd, performing a write-only sync (DMA_BUF_SYNC_WRITE without DMA_BUF_SYNC_READ), and closing the file descriptor. Fix this by storing the DMA direction used when the sg_table is first created in begin_cpu_udmabuf(), and passing that same direction to put_sg_table() in release_udmabuf(). Fixes: 284562e1f348 ("udmabuf: implement begin_cpu_access/end_cpu_access hooks") Cc: stable@vger.kernel.org Signed-off-by: Mikhail Gavrilov Reviewed-by: Vivek Kasireddy Signed-off-by: Vivek Kasireddy Link: https://patch.msgid.link/20260314232722.15555-1-mikhail.v.gavrilov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/dma-buf/udmabuf.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c index 94b8ecb892bb1..d0836febefdd1 100644 --- a/drivers/dma-buf/udmabuf.c +++ b/drivers/dma-buf/udmabuf.c @@ -40,6 +40,7 @@ struct udmabuf { struct folio **pinned_folios; struct sg_table *sg; + enum dma_data_direction sg_dir; struct miscdevice *device; pgoff_t *offsets; }; @@ -235,7 +236,7 @@ static void release_udmabuf(struct dma_buf *buf) struct device *dev = ubuf->device->this_device; if (ubuf->sg) - put_sg_table(dev, ubuf->sg, DMA_BIDIRECTIONAL); + put_sg_table(dev, ubuf->sg, ubuf->sg_dir); deinit_udmabuf(ubuf); kfree(ubuf); @@ -253,6 +254,8 @@ static int begin_cpu_udmabuf(struct dma_buf *buf, if (IS_ERR(ubuf->sg)) { ret = PTR_ERR(ubuf->sg); ubuf->sg = NULL; + } else { + ubuf->sg_dir = direction; } } else { dma_sync_sgtable_for_cpu(dev, ubuf->sg, direction); From 0449a6583c0ee76778d314e4e82f166fc97fa9d8 Mon Sep 17 00:00:00 2001 From: Mikhail Gavrilov Date: Tue, 31 Mar 2026 11:16:57 +0500 Subject: [PATCH 0615/2758] dma-buf/udmabuf: skip redundant cpu sync to fix cacheline EEXIST warning commit 504e2b4ab97a51d56d966cd36d0997ad30b65b2d upstream. When CONFIG_DMA_API_DEBUG_SG is enabled, importing a udmabuf into a DRM driver (e.g. amdgpu for video playback in GNOME Videos / Showtime) triggers a spurious warning: DMA-API: amdgpu 0000:03:00.0: cacheline tracking EEXIST, \ overlapping mappings aren't supported WARNING: kernel/dma/debug.c:619 at add_dma_entry+0x473/0x5f0 The call chain is: amdgpu_cs_ioctl -> amdgpu_ttm_backend_bind -> dma_buf_map_attachment -> [udmabuf] map_udmabuf -> get_sg_table -> dma_map_sgtable(dev, sg, direction, 0) // attrs=0 -> debug_dma_map_sg -> add_dma_entry -> EEXIST This happens because udmabuf builds a per-page scatter-gather list via sg_set_folio(). When begin_cpu_udmabuf() has already created an sg table mapped for the misc device, and an importer such as amdgpu maps the same pages for its own device via map_udmabuf(), the DMA debug infrastructure sees two active mappings whose physical addresses share cacheline boundaries and warns about the overlap. The DMA_ATTR_SKIP_CPU_SYNC flag suppresses this check in add_dma_entry() because it signals that no CPU cache maintenance is performed at map/unmap time, making the cacheline overlap harmless. All other major dma-buf exporters already pass this flag: - drm_gem_map_dma_buf() passes DMA_ATTR_SKIP_CPU_SYNC - amdgpu_dma_buf_map() passes DMA_ATTR_SKIP_CPU_SYNC The CPU sync at map/unmap time is also redundant for udmabuf: begin_cpu_udmabuf() and end_cpu_udmabuf() already perform explicit cache synchronization via dma_sync_sgtable_for_cpu/device() when CPU access is requested through the dma-buf interface. Pass DMA_ATTR_SKIP_CPU_SYNC to dma_map_sgtable() and dma_unmap_sgtable() in udmabuf to suppress the spurious warning and skip the redundant sync. Fixes: 284562e1f348 ("udmabuf: implement begin_cpu_access/end_cpu_access hooks") Cc: stable@vger.kernel.org Signed-off-by: Mikhail Gavrilov Acked-by: Vivek Kasireddy Signed-off-by: Vivek Kasireddy Link: https://patch.msgid.link/20260331061657.79983-1-mikhail.v.gavrilov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/dma-buf/udmabuf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c index d0836febefdd1..2f94be1df525a 100644 --- a/drivers/dma-buf/udmabuf.c +++ b/drivers/dma-buf/udmabuf.c @@ -163,7 +163,7 @@ static struct sg_table *get_sg_table(struct device *dev, struct dma_buf *buf, sg_set_folio(sgl, ubuf->folios[i], PAGE_SIZE, ubuf->offsets[i]); - ret = dma_map_sgtable(dev, sg, direction, 0); + ret = dma_map_sgtable(dev, sg, direction, DMA_ATTR_SKIP_CPU_SYNC); if (ret < 0) goto err_map; return sg; @@ -178,7 +178,7 @@ static struct sg_table *get_sg_table(struct device *dev, struct dma_buf *buf, static void put_sg_table(struct device *dev, struct sg_table *sg, enum dma_data_direction direction) { - dma_unmap_sgtable(dev, sg, direction, 0); + dma_unmap_sgtable(dev, sg, direction, DMA_ATTR_SKIP_CPU_SYNC); sg_free_table(sg); kfree(sg); } From 40eedc4253dbda0b29b7961200534dfcecb48ace Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 22 May 2026 14:13:57 -0400 Subject: [PATCH 0616/2758] svcrdma: wake sq waiters when the transport closes commit e5248a7426030db1e126363f72afdb3b71339a5c upstream. Threads parked in svc_rdma_sq_wait() on sc_sq_ticket_wait or sc_send_wait can hang indefinitely in TASK_UNINTERRUPTIBLE state across transport teardown, pinning svc_xprt references and blocking svc_rdma_free(). The close path sets XPT_CLOSE before invoking xpo_detach and both wait_event predicates include an XPT_CLOSE term, but the predicates are re-evaluated only on wakeup. sc_sq_ticket_wait has no completion-driven wake path; it is advanced solely by the chained ticket handoff inside svc_rdma_sq_wait() itself. Without an explicit wake at close, parked threads never observe XPT_CLOSE, hold their svc_xprt_get reference forever, and svc_rdma_free() blocks on xpt_ref dropping to zero. Two close entry points reach this transport. Local teardown runs svc_rdma_detach() from svc_handle_xprt() -> svc_delete_xprt() -> xpo_detach() on a worker thread. A remote disconnect arrives at svc_rdma_cma_handler(), which calls svc_xprt_deferred_close(): that sets XPT_CLOSE and enqueues the transport but does not access either RDMA waitqueue, so a worker already parked in svc_rdma_sq_wait() never re-evaluates its predicate. With every worker parked on this transport, no thread is available to run the local teardown either, and the wake site there is unreachable. Introduce svc_rdma_xprt_deferred_close(), a thin svcrdma wrapper that calls svc_xprt_deferred_close() and then wakes both sc_sq_ticket_wait and sc_send_wait. Convert the svcrdma producers that called svc_xprt_deferred_close() directly: svc_rdma_cma_handler(), qp_event_handler(), svc_rdma_post_send_err(), svc_rdma_wc_send(), the sendto drop path, the rw completion error paths, and the recvfrom flush and read-list error paths. Wake both waitqueues from svc_rdma_detach() as well. The synchronous svc_xprt_close() path (backchannel ENOTCONN, device removal via svc_rdma_xprt_done) reaches detach without flowing through svc_xprt_deferred_close() and therefore does not invoke the new helper. Fixes: ccc89b9d1ed2 ("svcrdma: Add fair queuing for Send Queue access") Cc: stable@vger.kernel.org Assisted-by: kres (claude-opus-4-7) Signed-off-by: Chris Mason [ cel: add svc_rdma_xprt_deferred_close() to complete the fix ] Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- include/linux/sunrpc/svc_rdma.h | 1 + net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 4 ++-- net/sunrpc/xprtrdma/svc_rdma_rw.c | 6 ++--- net/sunrpc/xprtrdma/svc_rdma_sendto.c | 6 ++--- net/sunrpc/xprtrdma/svc_rdma_transport.c | 30 ++++++++++++++++++++++-- 5 files changed, 37 insertions(+), 10 deletions(-) diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index df6e08aaad570..290ec5043aa9a 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -328,6 +328,7 @@ extern int svc_rdma_result_payload(struct svc_rqst *rqstp, unsigned int offset, unsigned int length); /* svc_rdma_transport.c */ +extern void svc_rdma_xprt_deferred_close(struct svcxprt_rdma *rdma); extern struct svc_xprt_class svc_rdma_class; #ifdef CONFIG_SUNRPC_BACKCHANNEL extern struct svc_xprt_class svc_rdma_bc_class; diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index f8a0638eb095d..96594a0f8de6c 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c @@ -377,7 +377,7 @@ static void svc_rdma_wc_receive(struct ib_cq *cq, struct ib_wc *wc) trace_svcrdma_wc_recv_err(wc, &ctxt->rc_cid); dropped: svc_rdma_recv_ctxt_put(rdma, ctxt); - svc_xprt_deferred_close(&rdma->sc_xprt); + svc_rdma_xprt_deferred_close(rdma); } /** @@ -1001,7 +1001,7 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp) if (ret == -EINVAL) svc_rdma_send_error(rdma_xprt, ctxt, ret); svc_rdma_recv_ctxt_put(rdma_xprt, ctxt); - svc_xprt_deferred_close(xprt); + svc_rdma_xprt_deferred_close(rdma_xprt); return ret; } return 0; diff --git a/net/sunrpc/xprtrdma/svc_rdma_rw.c b/net/sunrpc/xprtrdma/svc_rdma_rw.c index 402e2ceca4ff7..0c6f4dfecd50b 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_rw.c +++ b/net/sunrpc/xprtrdma/svc_rdma_rw.c @@ -313,7 +313,7 @@ static void svc_rdma_reply_done(struct ib_cq *cq, struct ib_wc *wc) trace_svcrdma_wc_reply_err(wc, &cc->cc_cid); } - svc_xprt_deferred_close(&rdma->sc_xprt); + svc_rdma_xprt_deferred_close(rdma); } /** @@ -345,7 +345,7 @@ static void svc_rdma_write_done(struct ib_cq *cq, struct ib_wc *wc) * some of the outgoing RPC message. Signal the loss * to the client by closing the connection. */ - svc_xprt_deferred_close(&rdma->sc_xprt); + svc_rdma_xprt_deferred_close(rdma); } /** @@ -390,7 +390,7 @@ static void svc_rdma_wc_read_done(struct ib_cq *cq, struct ib_wc *wc) */ svc_rdma_cc_release(rdma, cc, DMA_FROM_DEVICE); svc_rdma_recv_ctxt_put(rdma, ctxt); - svc_xprt_deferred_close(&rdma->sc_xprt); + svc_rdma_xprt_deferred_close(rdma); } /* diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c index 8b3f0c8c14b25..88ad6bbed69b8 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c +++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c @@ -395,7 +395,7 @@ int svc_rdma_post_send_err(struct svcxprt_rdma *rdma, int sqecount, int ret) { trace_svcrdma_sq_post_err(rdma, cid, ret); - svc_xprt_deferred_close(&rdma->sc_xprt); + svc_rdma_xprt_deferred_close(rdma); /* If even one WR was posted, a Send completion will * return the reserved SQ slots. @@ -437,7 +437,7 @@ static void svc_rdma_wc_send(struct ib_cq *cq, struct ib_wc *wc) else trace_svcrdma_wc_send_flush(wc, &ctxt->sc_cid); svc_rdma_send_ctxt_put(rdma, ctxt); - svc_xprt_deferred_close(&rdma->sc_xprt); + svc_rdma_xprt_deferred_close(rdma); } /** @@ -1158,7 +1158,7 @@ int svc_rdma_sendto(struct svc_rqst *rqstp) svc_rdma_send_ctxt_put(rdma, sctxt); drop_connection: trace_svcrdma_send_err(rqstp, ret); - svc_xprt_deferred_close(&rdma->sc_xprt); + svc_rdma_xprt_deferred_close(rdma); return -ENOTCONN; } diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index f18bc60d9f4ff..246f332db2f75 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -98,10 +98,27 @@ struct svc_xprt_class svc_rdma_class = { .xcl_ident = XPRT_TRANSPORT_RDMA, }; +/** + * svc_rdma_xprt_deferred_close - Close an RDMA transport (deferred) + * @rdma: transport to close + */ +void svc_rdma_xprt_deferred_close(struct svcxprt_rdma *rdma) +{ + svc_xprt_deferred_close(&rdma->sc_xprt); + + /* Release parked sc_sq_ticket_wait and sc_send_wait waiters. + * Once XPT_CLOSE is observed each returns -ENOTCONN. + */ + wake_up_all(&rdma->sc_sq_ticket_wait); + wake_up_all(&rdma->sc_send_wait); +} + /* QP event handler */ static void qp_event_handler(struct ib_event *event, void *context) { struct svc_xprt *xprt = context; + struct svcxprt_rdma *rdma = + container_of(xprt, struct svcxprt_rdma, sc_xprt); trace_svcrdma_qp_error(event, (struct sockaddr *)&xprt->xpt_remote); switch (event->event) { @@ -119,7 +136,7 @@ static void qp_event_handler(struct ib_event *event, void *context) case IB_EVENT_QP_ACCESS_ERR: case IB_EVENT_DEVICE_FATAL: default: - svc_xprt_deferred_close(xprt); + svc_rdma_xprt_deferred_close(rdma); break; } } @@ -340,7 +357,7 @@ static int svc_rdma_cma_handler(struct rdma_cm_id *cma_id, svc_xprt_enqueue(xprt); break; case RDMA_CM_EVENT_DISCONNECTED: - svc_xprt_deferred_close(xprt); + svc_rdma_xprt_deferred_close(rdma); break; default: break; @@ -597,6 +614,15 @@ static void svc_rdma_detach(struct svc_xprt *xprt) container_of(xprt, struct svcxprt_rdma, sc_xprt); rdma_disconnect(rdma->sc_cm_id); + + /* + * Most close paths go through svc_rdma_xprt_deferred_close(), + * which wakes the SQ waitqueues. svc_xprt_close() reaches + * detach without that helper, so wake any threads parked in + * svc_rdma_sq_wait() here as well. + */ + wake_up_all(&rdma->sc_sq_ticket_wait); + wake_up_all(&rdma->sc_send_wait); } /** From 284d5ba931a58025c51fcaef0857299bcf6b8b7a Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Sat, 6 Jun 2026 22:24:56 -0400 Subject: [PATCH 0617/2758] Revert "svcrdma: Use contiguous pages for RDMA Read sink buffers" commit a39f0ce0c9da20986b429e2db3e4e8739035d61b upstream. Jonathan Flynn reports that commit 18755b8c2f24 ("svcrdma: Use contiguous pages for RDMA Read sink buffers") regresses NFS/RDMA WRITE throughput from 73.9 GiB/s to 30.3 GiB/s on a 128-core single-NUMA-node server driving dual 400Gb/s links with 640 nfsd threads. Server CPU utilization rises from 8.5% to 76%, with roughly three quarters of all cycles spent spinning on zone->lock. The sink buffers are allocated as high-order page blocks, split into single pages so each sub-page carries an independent refcount, and later released one page at a time through folio batches. The per-CPU page caches cannot satisfy an allocation stream whose alloc order differs from its free order, so every sink buffer page makes a round trip through the buddy allocator's free lists, serialized on the zone lock of the single NUMA node. The rq_pages entries that the split pages displace, bulk-allocated moments earlier by svc_alloc_arg(), are freed without ever being used, doubling the allocator traffic. The regression cannot be addressed trivially. Revert the commit now; a reworked approach can return in an upcoming merge window. Reported-by: Jonathan Flynn Reported-by: Mike Snitzer Closes: https://lore.kernel.org/linux-nfs/aiHlPmeZq3WgMwoJ@kernel.org/ Closes: https://lore.kernel.org/linux-nfs/3cb119b4b2a8aada30c0c60286778a54@mail.gmail.com/ Fixes: 18755b8c2f24 ("svcrdma: Use contiguous pages for RDMA Read sink buffers") Cc: stable@vger.kernel.org Tested-by: Jonathan Flynn Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- net/sunrpc/xprtrdma/svc_rdma_rw.c | 223 ------------------------------ 1 file changed, 223 deletions(-) diff --git a/net/sunrpc/xprtrdma/svc_rdma_rw.c b/net/sunrpc/xprtrdma/svc_rdma_rw.c index 0c6f4dfecd50b..73d17ac83911d 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_rw.c +++ b/net/sunrpc/xprtrdma/svc_rdma_rw.c @@ -754,216 +754,6 @@ int svc_rdma_prepare_reply_chunk(struct svcxprt_rdma *rdma, return xdr->len; } -/* - * Cap contiguous RDMA Read sink allocations at order-4. - * Higher orders risk allocation failure under - * __GFP_NORETRY, which would negate the benefit of the - * contiguous fast path. - */ -#define SVC_RDMA_CONTIG_MAX_ORDER 4 - -/** - * svc_rdma_alloc_read_pages - Allocate physically contiguous pages - * @nr_pages: number of pages needed - * @order: on success, set to the allocation order - * - * Attempts a higher-order allocation, falling back to smaller orders. - * The returned pages are split immediately so each sub-page has its - * own refcount and can be freed independently. - * - * Returns a pointer to the first page on success, or NULL if even - * order-1 allocation fails. - */ -static struct page * -svc_rdma_alloc_read_pages(unsigned int nr_pages, unsigned int *order) -{ - unsigned int o; - struct page *page; - - o = min(get_order(nr_pages << PAGE_SHIFT), - SVC_RDMA_CONTIG_MAX_ORDER); - - while (o >= 1) { - page = alloc_pages(GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN, - o); - if (page) { - split_page(page, o); - *order = o; - return page; - } - o--; - } - return NULL; -} - -/* - * svc_rdma_fill_contig_bvec - Replace rq_pages with a contiguous allocation - * @rqstp: RPC transaction context - * @head: context for ongoing I/O - * @bv: bvec entry to fill - * @pages_left: number of data pages remaining in the segment - * @len_left: bytes remaining in the segment - * - * On success, fills @bv with a bvec spanning the contiguous range and - * advances rc_curpage/rc_page_count. Returns the byte length covered, - * or zero if the allocation failed or would overrun rq_maxpages. - */ -static unsigned int -svc_rdma_fill_contig_bvec(struct svc_rqst *rqstp, - struct svc_rdma_recv_ctxt *head, - struct bio_vec *bv, unsigned int pages_left, - unsigned int len_left) -{ - unsigned int order, npages, chunk_pages, chunk_len, i; - struct page *page; - - page = svc_rdma_alloc_read_pages(pages_left, &order); - if (!page) - return 0; - npages = 1 << order; - - if (head->rc_curpage + npages > rqstp->rq_maxpages) { - for (i = 0; i < npages; i++) - __free_page(page + i); - return 0; - } - - /* - * Replace rq_pages[] entries with pages from the contiguous - * allocation. If npages exceeds chunk_pages, the extra pages - * stay in rq_pages[] for later reuse or normal rqst teardown. - */ - for (i = 0; i < npages; i++) { - svc_rqst_page_release(rqstp, - rqstp->rq_pages[head->rc_curpage + i]); - rqstp->rq_pages[head->rc_curpage + i] = page + i; - } - - chunk_pages = min(npages, pages_left); - chunk_len = min_t(unsigned int, chunk_pages << PAGE_SHIFT, len_left); - bvec_set_page(bv, page, chunk_len, 0); - head->rc_page_count += chunk_pages; - head->rc_curpage += chunk_pages; - return chunk_len; -} - -/* - * svc_rdma_fill_page_bvec - Add a single rq_page to the bvec array - * @head: context for ongoing I/O - * @ctxt: R/W context whose bvec array is being filled - * @cur: page to add - * @bvec_idx: pointer to current bvec index, not advanced on merge - * @len_left: bytes remaining in the segment - * - * If @cur is physically contiguous with the preceding bvec, it is - * merged by extending that bvec's length. Otherwise a new bvec - * entry is created. Returns the byte length covered. - */ -static unsigned int -svc_rdma_fill_page_bvec(struct svc_rdma_recv_ctxt *head, - struct svc_rdma_rw_ctxt *ctxt, struct page *cur, - unsigned int *bvec_idx, unsigned int len_left) -{ - unsigned int chunk_len = min_t(unsigned int, PAGE_SIZE, len_left); - - head->rc_page_count++; - head->rc_curpage++; - - if (*bvec_idx > 0) { - struct bio_vec *prev = &ctxt->rw_bvec[*bvec_idx - 1]; - - if (page_to_phys(prev->bv_page) + prev->bv_offset + - prev->bv_len == page_to_phys(cur)) { - prev->bv_len += chunk_len; - return chunk_len; - } - } - - bvec_set_page(&ctxt->rw_bvec[*bvec_idx], cur, chunk_len, 0); - (*bvec_idx)++; - return chunk_len; -} - -/** - * svc_rdma_build_read_segment_contig - Build RDMA Read WR with contiguous pages - * @rqstp: RPC transaction context - * @head: context for ongoing I/O - * @segment: co-ordinates of remote memory to be read - * - * Greedily allocates higher-order pages to cover the segment, - * building one bvec per contiguous chunk. Each allocation is - * split so sub-pages have independent refcounts. When a - * higher-order allocation fails, remaining pages are covered - * individually, merging adjacent pages into the preceding bvec - * when they are physically contiguous. The split sub-pages - * replace entries in rq_pages[] so downstream cleanup is - * unchanged. - * - * Returns: - * %0: the Read WR was constructed successfully - * %-ENOMEM: allocation failed - * %-EIO: a DMA mapping error occurred - */ -static int svc_rdma_build_read_segment_contig(struct svc_rqst *rqstp, - struct svc_rdma_recv_ctxt *head, - const struct svc_rdma_segment *segment) -{ - struct svcxprt_rdma *rdma = svc_rdma_rqst_rdma(rqstp); - struct svc_rdma_chunk_ctxt *cc = &head->rc_cc; - unsigned int nr_data_pages, bvec_idx; - struct svc_rdma_rw_ctxt *ctxt; - unsigned int len_left; - int ret; - - nr_data_pages = PAGE_ALIGN(segment->rs_length) >> PAGE_SHIFT; - if (head->rc_curpage + nr_data_pages > rqstp->rq_maxpages) - return -ENOMEM; - - ctxt = svc_rdma_get_rw_ctxt(rdma, nr_data_pages); - if (!ctxt) - return -ENOMEM; - - bvec_idx = 0; - len_left = segment->rs_length; - while (len_left) { - unsigned int pages_left = PAGE_ALIGN(len_left) >> PAGE_SHIFT; - unsigned int chunk_len = 0; - - if (pages_left >= 2) - chunk_len = svc_rdma_fill_contig_bvec(rqstp, head, - &ctxt->rw_bvec[bvec_idx], - pages_left, len_left); - if (chunk_len) { - bvec_idx++; - } else { - struct page *cur = - rqstp->rq_pages[head->rc_curpage]; - chunk_len = svc_rdma_fill_page_bvec(head, ctxt, cur, - &bvec_idx, - len_left); - } - - len_left -= chunk_len; - } - - ctxt->rw_nents = bvec_idx; - - head->rc_pageoff = offset_in_page(segment->rs_length); - if (head->rc_pageoff) - head->rc_curpage--; - - ret = svc_rdma_rw_ctx_init(rdma, ctxt, segment->rs_offset, - segment->rs_handle, segment->rs_length, - DMA_FROM_DEVICE); - if (ret < 0) - return -EIO; - percpu_counter_inc(&svcrdma_stat_read); - - list_add(&ctxt->rw_list, &cc->cc_rwctxts); - cc->cc_sqecount += ret; - return 0; -} - /** * svc_rdma_build_read_segment - Build RDMA Read WQEs to pull one RDMA segment * @rqstp: RPC transaction context @@ -990,14 +780,6 @@ static int svc_rdma_build_read_segment(struct svc_rqst *rqstp, if (check_add_overflow(head->rc_pageoff, len, &total)) return -EINVAL; nr_bvec = PAGE_ALIGN(total) >> PAGE_SHIFT; - - if (head->rc_pageoff == 0 && nr_bvec >= 2) { - ret = svc_rdma_build_read_segment_contig(rqstp, head, - segment); - if (ret != -ENOMEM) - return ret; - } - ctxt = svc_rdma_get_rw_ctxt(rdma, nr_bvec); if (!ctxt) return -ENOMEM; @@ -1343,11 +1125,6 @@ static void svc_rdma_clear_rqst_pages(struct svc_rqst *rqstp, { unsigned int i; - /* - * Move only pages containing RPC data into rc_pages[]. Pages - * from a contiguous allocation that were not used for the - * payload remain in rq_pages[] for subsequent reuse. - */ for (i = 0; i < head->rc_page_count; i++) { head->rc_pages[i] = rqstp->rq_pages[i]; rqstp->rq_pages[i] = NULL; From fb2c0eab51ae5b02d2bae7d67c2cfbec39b57231 Mon Sep 17 00:00:00 2001 From: Sebastian Alba Vives Date: Mon, 18 May 2026 13:07:41 -0600 Subject: [PATCH 0618/2758] fpga: dfl-afu: validate DMA mapping length in afu_dma_map_region() commit fc3b071a7c8dc0f5d56defddf6e6fd5aaa3e1e27 upstream. afu_ioctl_dma_map() accepts a 64-bit length from userspace via DFL_FPGA_PORT_DMA_MAP ioctl without an upper bound check. The value is passed to afu_dma_pin_pages() where npages is derived as length >> PAGE_SHIFT and passed to pin_user_pages_fast() which takes int nr_pages, causing implicit truncation if length is very large. Validate map.length at the ioctl entry point before calling afu_dma_map_region(), rejecting values whose page count exceeds INT_MAX. Fixes: fa8dda1edef9 ("fpga: dfl: afu: add DFL_FPGA_PORT_DMA_MAP/UNMAP ioctls support") Cc: stable@vger.kernel.org Signed-off-by: Sebastian Alba Vives Reviewed-by: Xu Yilun Link: https://lore.kernel.org/r/20260518190742.61426-3-sebasjosue84@gmail.com Signed-off-by: Xu Yilun Signed-off-by: Greg Kroah-Hartman --- drivers/fpga/dfl-afu-main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/fpga/dfl-afu-main.c b/drivers/fpga/dfl-afu-main.c index 3bf8e7338dbef..097a97eeea66c 100644 --- a/drivers/fpga/dfl-afu-main.c +++ b/drivers/fpga/dfl-afu-main.c @@ -723,6 +723,9 @@ afu_ioctl_dma_map(struct dfl_feature_dev_data *fdata, void __user *arg) if (map.argsz < minsz || map.flags) return -EINVAL; + if (map.length >> PAGE_SHIFT > (u64)INT_MAX) + return -EINVAL; + ret = afu_dma_map_region(fdata, map.user_addr, map.length, &map.iova); if (ret) return ret; From 3d760ca230b04dfc6faf212002aa508ac2675f84 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 16:37:06 +0200 Subject: [PATCH 0619/2758] i2c: core: fix irq domain leak on adapter registration failure commit 8ce19524e4cc2462685f596a6402fbd8fb984ab2 upstream. Make sure to tear down the host notify irq domain on adapter registration failure to avoid leaking it. This issue was flagged by Sashiko when reviewing another adapter registration fix. Fixes: 4d5538f5882a ("i2c: use an IRQ to report Host Notify events, not alert") Cc: stable@vger.kernel.org # 4.10 Cc: Benjamin Tissoires Signed-off-by: Johan Hovold Signed-off-by: Wolfram Sang Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/i2c-core-base.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index add940a7542ef..235c8f0f406fb 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -1578,7 +1578,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap) if (res) { pr_err("adapter '%s': can't register device (%d)\n", adap->name, res); put_device(&adap->dev); - goto out_list; + goto err_remove_irq_domain; } adap->debugfs = debugfs_create_dir(dev_name(&adap->dev), i2c_debugfs_root); @@ -1614,6 +1614,8 @@ static int i2c_register_adapter(struct i2c_adapter *adap) init_completion(&adap->dev_released); device_unregister(&adap->dev); wait_for_completion(&adap->dev_released); +err_remove_irq_domain: + i2c_host_notify_irq_teardown(adap); out_list: mutex_lock(&core_lock); idr_remove(&i2c_adapter_idr, adap->nr); From 034e307428119b67f5f18a35e4f4e7d15a2b9774 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 16:37:08 +0200 Subject: [PATCH 0620/2758] i2c: core: fix NULL-deref on adapter registration failure commit 2295d2bb101faa663fbc45fadbb3fec45f107441 upstream. If adapter registration ever fails the release callback would trigger a NULL-pointer dereference as the completion struct has not been initialised. Note that before the offending commit this would instead have resulted in a minor memory leak of the adapter name. Fixes: 3f8c4f5e9a57 ("i2c: core: fix reference leak in i2c_register_adapter()") Cc: stable@vger.kernel.org Cc: Joe Hattori Signed-off-by: Johan Hovold Signed-off-by: Wolfram Sang Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/i2c-core-base.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 235c8f0f406fb..b3b31ec73a087 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -1577,8 +1577,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap) res = device_add(&adap->dev); if (res) { pr_err("adapter '%s': can't register device (%d)\n", adap->name, res); - put_device(&adap->dev); - goto err_remove_irq_domain; + goto err_put_adap; } adap->debugfs = debugfs_create_dir(dev_name(&adap->dev), i2c_debugfs_root); @@ -1611,10 +1610,12 @@ static int i2c_register_adapter(struct i2c_adapter *adap) out_reg: i2c_deregister_clients(adap); debugfs_remove_recursive(adap->debugfs); + device_del(&adap->dev); +err_put_adap: init_completion(&adap->dev_released); - device_unregister(&adap->dev); + put_device(&adap->dev); wait_for_completion(&adap->dev_released); -err_remove_irq_domain: + i2c_host_notify_irq_teardown(adap); out_list: mutex_lock(&core_lock); From 76402d37a5de1e697fec4665f2e9e7af76a4ec4b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 16:37:09 +0200 Subject: [PATCH 0621/2758] i2c: core: fix adapter probe deferral loop commit 158efa411c57111d87bf265a3776614f32d70007 upstream. Drivers must not probe defer after having registered devices as that will trigger a probe loop if the devices bind to a driver (cf. commit fbc35b45f9f6 ("Add documentation on meaning of -EPROBE_DEFER")). Move the recovery initialisation, where the GPIO lookup may fail, before registering the adapter to prevent this. Fixes: 75820314de26 ("i2c: core: add generic I2C GPIO recovery") Cc: stable@vger.kernel.org # 5.9 Cc: Codrin Ciubotariu Signed-off-by: Johan Hovold Signed-off-by: Wolfram Sang Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/i2c-core-base.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index b3b31ec73a087..62fa97ace5152 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -1561,6 +1561,10 @@ static int i2c_register_adapter(struct i2c_adapter *adap) adap->dev.type = &i2c_adapter_type; device_initialize(&adap->dev); + res = i2c_init_recovery(adap); + if (res == -EPROBE_DEFER) + goto err_put_adap; + /* * This adapter can be used as a parent immediately after device_add(), * setup runtime-pm (especially ignore-children) before hand. @@ -1586,10 +1590,6 @@ static int i2c_register_adapter(struct i2c_adapter *adap) if (res) goto out_reg; - res = i2c_init_recovery(adap); - if (res == -EPROBE_DEFER) - goto out_reg; - dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name); /* create pre-declared device nodes */ From 036d554f532b6db7bd233908ee0ff617d4d09989 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 16:37:10 +0200 Subject: [PATCH 0622/2758] i2c: core: fix adapter debugfs creation commit 07d5fb537928aad4369aaff0cbae73ba38a719af upstream. Clients can be registered from bus notifier callbacks so the debugfs directory needs to be created before registering the adapter as clients use that directory as their debugfs parent. Move debugfs creation before adapter registration to avoid having clients create their debugfs directories in the debugfs root (which is also more likely to fail due to name collisions). Note that failure to allocate the adapter name must now be handled explicitly as debugfs_create_dir() cannot handle a NULL name (unlike device_add() which returns an error). Fixes: 73febd775bdb ("i2c: create debugfs entry per adapter") Cc: stable@vger.kernel.org # 6.8 Cc: Wolfram Sang Signed-off-by: Johan Hovold Signed-off-by: Wolfram Sang Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/i2c-core-base.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 62fa97ace5152..623ab97159bf7 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -1556,7 +1556,10 @@ static int i2c_register_adapter(struct i2c_adapter *adap) goto out_list; } - dev_set_name(&adap->dev, "i2c-%d", adap->nr); + res = dev_set_name(&adap->dev, "i2c-%d", adap->nr); + if (res) + goto err_remove_irq_domain; + adap->dev.bus = &i2c_bus_type; adap->dev.type = &i2c_adapter_type; device_initialize(&adap->dev); @@ -1578,14 +1581,14 @@ static int i2c_register_adapter(struct i2c_adapter *adap) idr_replace(&i2c_adapter_idr, adap, adap->nr); mutex_unlock(&core_lock); + adap->debugfs = debugfs_create_dir(dev_name(&adap->dev), i2c_debugfs_root); + res = device_add(&adap->dev); if (res) { pr_err("adapter '%s': can't register device (%d)\n", adap->name, res); - goto err_put_adap; + goto err_remove_debugfs; } - adap->debugfs = debugfs_create_dir(dev_name(&adap->dev), i2c_debugfs_root); - res = i2c_setup_smbus_alert(adap); if (res) goto out_reg; @@ -1609,13 +1612,14 @@ static int i2c_register_adapter(struct i2c_adapter *adap) out_reg: i2c_deregister_clients(adap); - debugfs_remove_recursive(adap->debugfs); device_del(&adap->dev); +err_remove_debugfs: + debugfs_remove_recursive(adap->debugfs); err_put_adap: init_completion(&adap->dev_released); put_device(&adap->dev); wait_for_completion(&adap->dev_released); - +err_remove_irq_domain: i2c_host_notify_irq_teardown(adap); out_list: mutex_lock(&core_lock); From 35dbd1f1f603401155cbd3a180bb18e3a3b675b8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 16:37:13 +0200 Subject: [PATCH 0623/2758] i2c: core: fix adapter deregistration race commit b1a58ed9eab146b36f41a55db8f5d7ce9fdedf3f upstream. Adapters can be looked up by their id using i2c_get_adapter() which takes a reference to the embedded struct device. Remove the adapter from the IDR before tearing it down during deregistration (and on registration failure) to make sure its resources are not accessed after having been freed (e.g. the device name). Fixes: 35fc37f81881 ("i2c: Limit core locking to the necessary sections") Cc: stable@vger.kernel.org # 2.6.31 Cc: Jean Delvare Signed-off-by: Johan Hovold Signed-off-by: Wolfram Sang Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/i2c-core-base.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 623ab97159bf7..8bea9fc6888fb 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -1586,7 +1586,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap) res = device_add(&adap->dev); if (res) { pr_err("adapter '%s': can't register device (%d)\n", adap->name, res); - goto err_remove_debugfs; + goto err_replace_id; } res = i2c_setup_smbus_alert(adap); @@ -1613,7 +1613,10 @@ static int i2c_register_adapter(struct i2c_adapter *adap) out_reg: i2c_deregister_clients(adap); device_del(&adap->dev); -err_remove_debugfs: +err_replace_id: + mutex_lock(&core_lock); + idr_replace(&i2c_adapter_idr, NULL, adap->nr); + mutex_unlock(&core_lock); debugfs_remove_recursive(adap->debugfs); err_put_adap: init_completion(&adap->dev_released); @@ -1802,6 +1805,8 @@ void i2c_del_adapter(struct i2c_adapter *adap) /* First make sure that this adapter was ever added */ mutex_lock(&core_lock); found = idr_find(&i2c_adapter_idr, adap->nr); + if (found == adap) + idr_replace(&i2c_adapter_idr, NULL, adap->nr); mutex_unlock(&core_lock); if (found != adap) { pr_debug("attempting to delete unregistered adapter [%s]\n", adap->name); From d8c97bde82248bd90cc693a715b761c4eb6aed17 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 18 Jun 2026 16:49:34 +0200 Subject: [PATCH 0624/2758] i2c: mpc: Fix timeout calculations commit 2e9a7f68329be41792c0b123c28e6c53c2fa2249 upstream. At first glance the harmless cleanup of the driver does nothing bad. However, as the operator precedence list states the '*' (multiplication) and '/' division operators have order 5 with left-to-right associativity the *= has order 17 and associativity right-to-left. It wouldn't be a problem to replace foo = foo * HZ / 1000000; with foo *= HZ / 1000000; if HZ constant is in Hertz. The problem is that in the Linux kernel HZ is defined in jiffy units, which is order of magnitude smaller than a million. That's why operator precedence has a crucial role here. Fix the regression by reverting pre-optimized calculations. Fixes: be40a3ae719f ("i2c: mpc: Use of_property_read_u32 instead of of_get_property") Signed-off-by: Andy Shevchenko Cc: # v6.4+ Reviewed-by: Chris Packham Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260618144934.3249950-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-mpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index 28c5c5c1fb7a6..a21fa45bd64c4 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -844,7 +844,7 @@ static int fsl_i2c_probe(struct platform_device *op) "fsl,timeout", &mpc_ops.timeout); if (!result) { - mpc_ops.timeout *= HZ / 1000000; + mpc_ops.timeout = mpc_ops.timeout * HZ / 1000000; if (mpc_ops.timeout < 5) mpc_ops.timeout = 5; } else { From cb037e697da0ba76d1d6cc6718d85b316429f8d7 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Wed, 10 Jun 2026 11:05:13 +0800 Subject: [PATCH 0625/2758] i2c: davinci: Unregister cpufreq notifier on probe failure commit e43f32816a1b1fe5a86279411626fe3a9be56d45 upstream. davinci_i2c_probe() registers a cpufreq transition notifier before adding the I2C adapter. If i2c_add_numbered_adapter() fails, the probe error path releases the device resources without unregistering the notifier. Add a dedicated error path to unregister the cpufreq notifier after i2c_add_numbered_adapter() fails. Fixes: 82c0de11b734 ("i2c: davinci: Add cpufreq support") Signed-off-by: Haoxiang Li Cc: # v2.6.36+ Reviewed-by: Bartosz Golaszewski Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260610030513.2651018-1-haoxiang_li2024@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-davinci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index 66c23535656b5..0617f416cb0b5 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -818,12 +818,14 @@ static int davinci_i2c_probe(struct platform_device *pdev) adap->nr = pdev->id; r = i2c_add_numbered_adapter(adap); if (r) - goto err_unuse_clocks; + goto err_cpufreq; pm_runtime_put_autosuspend(dev->dev); return 0; +err_cpufreq: + i2c_davinci_cpufreq_deregister(dev); err_unuse_clocks: pm_runtime_dont_use_autosuspend(dev->dev); pm_runtime_put_sync(dev->dev); From 1f0ab044e1063f4ad99e70557a1208cffd514ef6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20Rodr=C3=ADguez?= Date: Thu, 11 Jun 2026 12:48:56 +0200 Subject: [PATCH 0626/2758] i2c: stm32f7: truncate clock period instead of rounding it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 111bb7f9f4a90b32e495d70a607c67b137f3074a upstream. stm32f7_i2c_compute_timing() derives the I2C clock source period (i2cclk) with DIV_ROUND_CLOSEST, which may round it up. When the period is overestimated, all timings computed from it (SCLDEL, SDADEL, SCLL, SCLH) come out shorter on the wire than calculated, and the resulting bus rate can exceed the requested speed, violating the I2C specification minimums for tLOW and tHIGH. For example, with a 104.45 MHz clock source (e.g. PCLK1, the reset-default I2C clock source on STM32MP1), i2cclk is rounded from 9.574 ns up to 10 ns. Requesting a 400 kHz fast mode bus with 72/27 ns rise/fall times and no analog/digital filters then produces an actual bus rate of 415.6 kHz with tLOW = 1254 ns, violating both the 400 kHz maximum rate and the 1300 ns tLOW minimum of the specification. Truncate the period instead, so that it can only be underestimated. The error then falls on the safe side: the programmed timings come out slightly longer than computed and the bus runs marginally below the target rate (375.3 kHz in the example above) while meeting the specification. i2cbus is left rounded-to-closest: it is only used as the target of the clk_error comparison and is never multiplied into the programmed timings, so nearest rounding remains accurate there. Fixes: aeb068c57214 ("i2c: i2c-stm32f7: add driver") Signed-off-by: Guillermo Rodríguez Cc: # v4.14+ Acked-by: Alain Volmat Reviewed-by: Pierre-Yves MORDRET Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260611104857.242153-1-guille.rodriguez@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-stm32f7.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c index 067af255bd22a..0ddd8a1d0e4d0 100644 --- a/drivers/i2c/busses/i2c-stm32f7.c +++ b/drivers/i2c/busses/i2c-stm32f7.c @@ -464,8 +464,13 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev, { struct stm32f7_i2c_spec *specs; u32 p_prev = STM32F7_PRESC_MAX; - u32 i2cclk = DIV_ROUND_CLOSEST(NSEC_PER_SEC, - setup->clock_src); + /* + * Truncate instead of rounding to closest: if the clock period is + * overestimated, the computed SCL timings will come out shorter on + * the wire, which can push the bus above the target rate and below + * the spec's tLOW/tHIGH minimums. + */ + u32 i2cclk = NSEC_PER_SEC / setup->clock_src; u32 i2cbus = DIV_ROUND_CLOSEST(NSEC_PER_SEC, setup->speed_freq); u32 clk_error_prev = i2cbus; From 5800647d19d3f1f747fda4dc67e55d6afa6ee119 Mon Sep 17 00:00:00 2001 From: Carlos Song Date: Mon, 25 May 2026 11:14:50 +0800 Subject: [PATCH 0627/2758] i2c: imx-lpi2c: mark I2C adapter when hardware is powered down commit 218cfe364b55b2768221629bd4a69ad190b7fbbc upstream. On some i.MX platforms, certain I2C client drivers keep a periodic workqueue which continues to trigger I2C transfers. During system suspend/resume, there exists a time window between: - suspend_noirq and the system entering suspend - the system starting to resume and resume_noirq In this window, the I2C controller resources such as clock and pinctrl may already be disabled or not yet restored. If a workqueue triggers an I2C transfer in this period, the driver attempts to access I2C registers while the hardware resources are unavailable, which may lead to system hang. Mark the I2C adapter as suspended during noirq suspend and block new transfers until resume, ensuring that I2C transfers are only issued when hardware resources are available. Fixes: 1ee867e465c1 ("i2c: imx-lpi2c: add target mode support") Signed-off-by: Carlos Song Cc: # v6.14+ Acked-by: Mukesh Savaliya Reviewed-by: Frank Li Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260525031450.3183421-1-carlos.song@oss.nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-imx-lpi2c.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-imx-lpi2c.c index cd4da50c4dd96..e6c24a9d934d7 100644 --- a/drivers/i2c/busses/i2c-imx-lpi2c.c +++ b/drivers/i2c/busses/i2c-imx-lpi2c.c @@ -1646,7 +1646,18 @@ static int __maybe_unused lpi2c_runtime_resume(struct device *dev) static int __maybe_unused lpi2c_suspend_noirq(struct device *dev) { - return pm_runtime_force_suspend(dev); + struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev); + int ret; + + i2c_mark_adapter_suspended(&lpi2c_imx->adapter); + + ret = pm_runtime_force_suspend(dev); + if (ret) { + i2c_mark_adapter_resumed(&lpi2c_imx->adapter); + return ret; + } + + return 0; } static int __maybe_unused lpi2c_resume_noirq(struct device *dev) @@ -1666,6 +1677,8 @@ static int __maybe_unused lpi2c_resume_noirq(struct device *dev) if (lpi2c_imx->target) lpi2c_imx_target_init(lpi2c_imx); + i2c_mark_adapter_resumed(&lpi2c_imx->adapter); + return 0; } From 00904687b9c5527d569d9a1ca72119823e735a61 Mon Sep 17 00:00:00 2001 From: Mingyu Wang <25181214217@stu.xidian.edu.cn> Date: Tue, 12 May 2026 17:35:34 +0800 Subject: [PATCH 0628/2758] i2c: i801: fix hardware state machine corruption in error path commit 10dd1a736d557e310a77117832874729a0175d57 upstream. A severe livelock and subsequent Hung Task panic were observed in the i2c-i801 driver during concurrent Fuzzing. The crash is caused by an unconditional hardware register cleanup in the error handling path of i801_access(). When i801_check_pre() fails (e.g., returning -EBUSY because the SMBus controller is actively used by BIOS/ACPI), the kernel does not actually acquire the hardware ownership. However, the code jumps to the 'out' label and executes: iowrite8(SMBHSTSTS_INUSE_STS | STATUS_FLAGS, SMBHSTSTS(priv)); This forcefully clears the INUSE_STS lock and resets the hardware status flags without owning the controller. Doing so interrupts ongoing BIOS/ACPI transactions and totally corrupts the SMBus hardware state machine. Consequently, all subsequent i801_access() calls fail at the pre-check stage, triggering an endless stream of "SMBus is busy, can't use it!" error logs. Over a slow serial console, this printk flood monopolizes the CPU (Console Livelock), starving other processes trying to acquire the mmap_lock down_read semaphore, ultimately triggering the hung task watchdog. Fix this by moving the 'out' label below the hardware register cleanup. If i801_check_pre() fails, we safely bypass the iowrite8() and only release the software locks (pm_runtime and mutex), strictly adhering to the rule of not releasing resources that were never acquired. Fixes: 1f760b87e54c ("i2c: i801: Call i801_check_pre() from i801_access()") Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> Cc: # v6.3+ Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260512093534.348655-1-w15303746062@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-i801.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 32a3cef02c7b5..b29c99ed38839 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -931,13 +931,13 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr, */ if (hwpec) iowrite8(ioread8(SMBAUXCTL(priv)) & ~SMBAUXCTL_CRC, SMBAUXCTL(priv)); -out: /* * Unlock the SMBus device for use by BIOS/ACPI, * and clear status flags if not done already. */ iowrite8(SMBHSTSTS_INUSE_STS | STATUS_FLAGS, SMBHSTSTS(priv)); +out: pm_runtime_put_autosuspend(&priv->pci_dev->dev); mutex_unlock(&priv->acpi_lock); return ret; From 43d61346c0402e43a09c8996eed54c293a14ed72 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Wed, 10 Jun 2026 16:41:16 -0700 Subject: [PATCH 0629/2758] Input: synaptics-rmi4 - unregister function handlers on physical driver registration failure commit 6251f7d3472c0409e30f8d6a24f10d33d12e3f9a upstream. If rmi_register_physical_driver() fails, the current error path unregisters only the RMI bus. The function handlers registered earlier remain registered with the driver core. Add a separate error path to unregister the function handlers before unregistering the bus in this failure case. Fixes: 2b6a321da9a2 ("Input: synaptics-rmi4 - add support for Synaptics RMI4 devices") Signed-off-by: Haoxiang Li Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260610064633.2837084-1-haoxiang_li2024@163.com Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/rmi4/rmi_bus.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c index 687cb987bc137..ade57e2a72014 100644 --- a/drivers/input/rmi4/rmi_bus.c +++ b/drivers/input/rmi4/rmi_bus.c @@ -455,11 +455,13 @@ static int __init rmi_bus_init(void) if (error) { pr_err("%s: error registering the RMI physical driver: %d\n", __func__, error); - goto err_unregister_bus; + goto err_unregister_function_handlers; } return 0; +err_unregister_function_handlers: + rmi_unregister_function_handlers(); err_unregister_bus: bus_unregister(&rmi_bus_type); return error; From 64fb0e1161ccc6b9e48b8df61f07d3c34c01ec42 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sun, 14 Jun 2026 00:36:11 -0500 Subject: [PATCH 0630/2758] Input: synaptics-rmi4 - bound the F3A keymap to the GPIO count commit 57c10915f2c16c90e0d46ad00876bf39ece40fc2 upstream. rmi_f3a_initialize() takes the GPIO count from the device query register (f3a->gpio_count = buf & RMI_F3A_GPIO_COUNT, range 0..127). rmi_f3a_map_gpios() then allocates gpio_key_map with min(gpio_count, TRACKSTICK_RANGE_END) == at most 6 entries, but rmi_f3a_attention() iterates the full gpio_count and dereferences gpio_key_map[i], and input->keycodemax is set to the full gpio_count while input->keycode points at the 6-entry allocation. A device that reports gpio_count > 6 therefore causes an out-of-bounds read of gpio_key_map[] on every attention interrupt, and out-of-bounds accesses through the input core's default keymap ioctls: EVIOCGKEYCODE reads past the buffer (leaking adjacent slab memory to user space) and EVIOCSKEYCODE writes a caller-controlled value past it, for any process able to open the evdev node, since input_default_getkeycode() and input_default_setkeycode() only bound the index against keycodemax. Size the keymap for the full gpio_count. The mapping loop is unchanged: it still assigns only the first min(gpio_count, TRACKSTICK_RANGE_END) entries; the remaining slots stay KEY_RESERVED (devm_kcalloc zero-fills) and are skipped when reporting. Fixes: 9e4c596bfd00 ("Input: synaptics-rmi4 - add support for F3A") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Link: https://patch.msgid.link/20260614-b4-disp-818d6bda-v1-1-cf39a3615085@proton.me Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/rmi4/rmi_f3a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/rmi4/rmi_f3a.c b/drivers/input/rmi4/rmi_f3a.c index 0e8baed84dbb5..a0777644eef07 100644 --- a/drivers/input/rmi4/rmi_f3a.c +++ b/drivers/input/rmi4/rmi_f3a.c @@ -132,7 +132,7 @@ static int rmi_f3a_map_gpios(struct rmi_function *fn, struct f3a_data *f3a, int button_count = min_t(u8, f3a->gpio_count, TRACKSTICK_RANGE_END); f3a->gpio_key_map = devm_kcalloc(&fn->dev, - button_count, + f3a->gpio_count, sizeof(f3a->gpio_key_map[0]), GFP_KERNEL); if (!f3a->gpio_key_map) { From bfe622efecd4ff0a792d0ecd1a8dce535a902f50 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sun, 14 Jun 2026 00:36:12 -0500 Subject: [PATCH 0631/2758] Input: synaptics-rmi4 - bound the F30 keymap to the GPIO/LED count commit d577e46785d45484b2ab7e7309c49b18764bf56c upstream. rmi_f30_map_gpios() allocates gpioled_key_map with min(gpioled_count, TRACKSTICK_RANGE_END) == at most 6 entries, but rmi_f30_attention() iterates the full f30->gpioled_count (device query register, range 0..31) and dereferences gpioled_key_map[i], and input->keycodemax is set to the full gpioled_count while input->keycode points at the 6-entry allocation. A device that reports gpioled_count > 6 with GPIO support enabled therefore causes an out-of-bounds read on the attention interrupt and out-of-bounds read/write through the EVIOCGKEYCODE/EVIOCSKEYCODE ioctls, which bound the index only against keycodemax. This is the same defect as the F3A handler, which was copied from F30. Size the keymap for the full gpioled_count; the mapping loop still assigns only the first min(gpioled_count, TRACKSTICK_RANGE_END) entries. Fixes: 3e64fcbdbd10 ("Input: synaptics-rmi4 - limit the range of what GPIOs are buttons") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Link: https://patch.msgid.link/20260614-b4-disp-818d6bda-v1-2-cf39a3615085@proton.me Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/rmi4/rmi_f30.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/rmi4/rmi_f30.c b/drivers/input/rmi4/rmi_f30.c index 35045f161dc2f..b2155c8e20e75 100644 --- a/drivers/input/rmi4/rmi_f30.c +++ b/drivers/input/rmi4/rmi_f30.c @@ -233,7 +233,7 @@ static int rmi_f30_map_gpios(struct rmi_function *fn, int button_count = min_t(u8, f30->gpioled_count, TRACKSTICK_RANGE_END); f30->gpioled_key_map = devm_kcalloc(&fn->dev, - button_count, + f30->gpioled_count, sizeof(f30->gpioled_key_map[0]), GFP_KERNEL); if (!f30->gpioled_key_map) { From 6bac57d8fe2a077b8a85b4140eeb7999078158eb Mon Sep 17 00:00:00 2001 From: Ranjan Kumar Date: Mon, 22 Jun 2026 22:31:05 -0700 Subject: [PATCH 0632/2758] Input: elan_i2c - prevent division by zero and arithmetic underflow commit df2b818fa009c10ff6ba875a1663ff001cda9558 upstream. The Elan I2C touchpad driver queries the device for its physical dimensions and trace counts to calculate the device resolution and width. However, if the device firmware or device tree provides invalid zero values for x_traces or y_traces, it results in a fatal division-by-zero exception leading to a kernel panic during device probe. Add checks to ensure these parameters are non-zero before performing the division. If invalid trace values are detected, fall back to a safe default of 1. Additionally, prevent an arithmetic underflow in the touch reporting logic. Previously, if the calculated or fallback width was smaller than ETP_FWIDTH_REDUCE (90), the subtraction would underflow, resulting in a massive unsigned integer being reported to userspace. Clamp the adjusted width to a minimum of 0 to safely handle small physical dimensions and fallback scenarios. Completing the probe with safe fallback values ensures the sysfs nodes are created, keeping the firmware update path intact so a recovery firmware can be flashed to the device. Fixes: 6696777c6506 ("Input: add driver for Elan I2C/SMbus touchpad") Fixes: e3a9a1290688 ("Input: elan_i2c - do not query the info if they are provided") Signed-off-by: Ranjan Kumar Link: https://patch.msgid.link/20260612060339.3829666-1-kumarranja@chromium.org Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/mouse/elan_i2c_core.c | 36 ++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index 5cba02a156ce4..edb0a28d25aae 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -428,8 +428,17 @@ static int elan_query_device_parameters(struct elan_tp_data *data) if (error) return error; } - data->width_x = data->max_x / x_traces; - data->width_y = data->max_y / y_traces; + + if (!x_traces || !y_traces) { + dev_warn(&client->dev, + "invalid trace numbers: x=%u, y=%u\n", + x_traces, y_traces); + data->width_x = 1; + data->width_y = 1; + } else { + data->width_x = data->max_x / x_traces; + data->width_y = data->max_y / y_traces; + } if (device_property_read_u32(&client->dev, "touchscreen-x-mm", &x_mm) || @@ -443,8 +452,16 @@ static int elan_query_device_parameters(struct elan_tp_data *data) data->x_res = elan_convert_resolution(hw_x_res, data->pattern); data->y_res = elan_convert_resolution(hw_y_res, data->pattern); } else { - data->x_res = (data->max_x + 1) / x_mm; - data->y_res = (data->max_y + 1) / y_mm; + if (unlikely(x_mm == 0 || y_mm == 0)) { + dev_warn(&client->dev, + "invalid physical dimensions: x_mm=%u, y_mm=%u\n", + x_mm, y_mm); + data->x_res = 1; + data->y_res = 1; + } else { + data->x_res = (data->max_x + 1) / x_mm; + data->y_res = (data->max_y + 1) / y_mm; + } } if (device_property_read_bool(&client->dev, "elan,clickpad")) @@ -956,6 +973,7 @@ static void elan_report_contact(struct elan_tp_data *data, int contact_num, if (data->report_features & ETP_FEATURE_REPORT_MK) { unsigned int mk_x, mk_y, area_x, area_y; + int adj_width_x, adj_width_y; u8 mk_data = high_precision ? packet[ETP_MK_DATA_OFFSET + contact_num] : finger_data[3]; @@ -967,8 +985,14 @@ static void elan_report_contact(struct elan_tp_data *data, int contact_num, * To avoid treating large finger as palm, let's reduce * the width x and y per trace. */ - area_x = mk_x * (data->width_x - ETP_FWIDTH_REDUCE); - area_y = mk_y * (data->width_y - ETP_FWIDTH_REDUCE); + + adj_width_x = data->width_x > ETP_FWIDTH_REDUCE ? + data->width_x - ETP_FWIDTH_REDUCE : 0; + adj_width_y = data->width_y > ETP_FWIDTH_REDUCE ? + data->width_y - ETP_FWIDTH_REDUCE : 0; + + area_x = mk_x * adj_width_x; + area_y = mk_y * adj_width_y; input_report_abs(input, ABS_TOOL_WIDTH, mk_x); input_report_abs(input, ABS_MT_TOUCH_MAJOR, From 2a67668690129953e898923260a2dd1c7c196495 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Fri, 12 Jun 2026 21:10:33 -0500 Subject: [PATCH 0633/2758] Input: goodix - clamp the device-reported contact count commit 5ed62a96e06be4e94b8296b7932afee550a70e04 upstream. goodix_ts_read_input_report() copies the number of touch points reported by the device into an on-stack buffer u8 point_data[2 + GOODIX_MAX_CONTACT_SIZE * GOODIX_MAX_CONTACTS]; which is sized for at most GOODIX_MAX_CONTACTS (10) contacts. The only runtime check bounds the per-interrupt count against ts->max_touch_num, but that value is taken verbatim from a 4-bit field of the device configuration block and is never clamped: ts->max_touch_num = ts->config[MAX_CONTACTS_LOC] & 0x0f; The nibble can be 0..15, so a malfunctioning, malicious or counterfeit controller (or an attacker tampering with the I2C bus) can advertise up to 15 contacts. goodix_ts_read_input_report() then accepts a touch_num of up to 15 and the second goodix_i2c_read() writes ts->contact_size * (touch_num - 1) bytes past the one-contact header into point_data - up to 30 bytes (45 with the 9-byte report format) beyond the 92-byte buffer: a stack out-of-bounds write. Clamp max_touch_num to GOODIX_MAX_CONTACTS, the number of contacts point_data[] is sized for, when reading it from the configuration. Fixes: a7ac7c95d468 ("Input: goodix - use max touch number from device config") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Reviewed-by: Hans de Goede Link: https://patch.msgid.link/20260612-b4-disp-6844625d-v1-1-df0aed080c9d@proton.me Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/touchscreen/goodix.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index f8798d11ec030..17fcfe45988c2 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -1057,7 +1057,8 @@ static void goodix_read_config(struct goodix_ts_data *ts) } ts->int_trigger_type = ts->config[TRIGGER_LOC] & 0x03; - ts->max_touch_num = ts->config[MAX_CONTACTS_LOC] & 0x0f; + ts->max_touch_num = min(ts->config[MAX_CONTACTS_LOC] & 0x0f, + GOODIX_MAX_CONTACTS); x_max = get_unaligned_le16(&ts->config[RESOLUTION_LOC]); y_max = get_unaligned_le16(&ts->config[RESOLUTION_LOC + 2]); From a40250f97c312e000e3616c9074022311a0efbc3 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Mon, 22 Jun 2026 20:47:50 -0700 Subject: [PATCH 0634/2758] Input: iforce - bound the device-reported force-feedback effect index commit 0e9943d2e4c63496b6ca84bc66fd3c71d40558e2 upstream. iforce_process_packet() handles a status report (packet id 0x02) by taking a force-feedback effect index straight from the device wire and using it to address the per-effect state array: i = data[1] & 0x7f; if (data[1] & 0x80) { if (!test_and_set_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) ... } else if (test_and_clear_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) { ... } The index is masked only with 0x7f, so it ranges 0..127, but core_effects[] holds only IFORCE_EFFECTS_MAX (32) entries. For an index of 32..127 the test_and_set_bit()/test_and_clear_bit() is an out-of-bounds single-bit read-modify-write past the array. core_effects[] is the second-to-last member of struct iforce, so the write lands in the trailing members and beyond the embedding kzalloc()'d iforce_serio / iforce_usb object. data[1] is unvalidated device payload on both transports (the USB interrupt endpoint and serio), and the status path is not gated on force feedback being present, so a malicious or counterfeit device can set or clear a bit at an attacker-chosen offset past the object. Reject an out-of-range index instead of indexing with it. Bound against the array dimension IFORCE_EFFECTS_MAX rather than dev->ff->max_effects so the check guarantees memory safety regardless of how many effects the device registered. A legitimate "effect started/stopped" status always carries an index below IFORCE_EFFECTS_MAX, so well-formed devices are unaffected; the neighbouring mark_core_as_ready() loop is already bounded and is left untouched. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Bryam Vargas Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260613-b4-disp-4828d263-v1-1-02320e1a89dd@proton.me Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/joystick/iforce/iforce-packets.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/input/joystick/iforce/iforce-packets.c b/drivers/input/joystick/iforce/iforce-packets.c index fd1cd731d781a..effa76bfd8f9a 100644 --- a/drivers/input/joystick/iforce/iforce-packets.c +++ b/drivers/input/joystick/iforce/iforce-packets.c @@ -186,14 +186,18 @@ void iforce_process_packet(struct iforce *iforce, /* Check if an effect was just started or stopped */ i = data[1] & 0x7f; - if (data[1] & 0x80) { - if (!test_and_set_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) { - /* Report play event */ - input_report_ff_status(dev, i, FF_STATUS_PLAYING); + if (i < IFORCE_EFFECTS_MAX) { + if (data[1] & 0x80) { + if (!test_and_set_bit(FF_CORE_IS_PLAYED, + iforce->core_effects[i].flags)) { + /* Report play event */ + input_report_ff_status(dev, i, FF_STATUS_PLAYING); + } + } else if (test_and_clear_bit(FF_CORE_IS_PLAYED, + iforce->core_effects[i].flags)) { + /* Report stop event */ + input_report_ff_status(dev, i, FF_STATUS_STOPPED); } - } else if (test_and_clear_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) { - /* Report stop event */ - input_report_ff_status(dev, i, FF_STATUS_STOPPED); } for (j = 3; j < len; j += 2) From a747c4eb02656afdbd92eea83b88e92715a23977 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 22 Jun 2026 22:35:18 -0700 Subject: [PATCH 0635/2758] Input: mms114 - fix touch indexing for MMS134S and MMS136 commit a6ac4e24c1a8a533bb61035184fdcc7eede4cc8d upstream. The MMS134S and MMS136 touch controllers have an event size of 6 bytes rather than 8 bytes. When __mms114_read_reg() reads the touch data packet from the device into the touch buffer, the events are packed tightly at 6-byte intervals. However, the driver iterates through the events using standard C array indexing (touch[index]), where each element is sizeof(struct mms114_touch) (8 bytes) apart. As a result, any touch events beyond the first one are read from incorrect offsets and parsed improperly. Fix this by explicitly calculating the byte offset for each touch event based on the device's specific event size. Fixes: 53fefdd1d3a3 ("Input: mms114 - support MMS136") Fixes: ab108678195f ("Input: mms114 - support MMS134S") Reported-by: sashiko-bot@kernel.org Assisted-by: Antigravity:gemini-3.5-flash Reviewed-by: Bryam Vargas Link: https://patch.msgid.link/20260616050912.1531241-1-dmitry.torokhov@gmail.com Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/touchscreen/mms114.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c index af462086a65ca..0570e946b8766 100644 --- a/drivers/input/touchscreen/mms114.c +++ b/drivers/input/touchscreen/mms114.c @@ -217,7 +217,9 @@ static irqreturn_t mms114_interrupt(int irq, void *dev_id) struct mms114_data *data = dev_id; struct i2c_client *client = data->client; struct mms114_touch touch[MMS114_MAX_TOUCH]; + struct mms114_touch *t; int packet_size; + int event_size; int touch_size; int index; int error; @@ -228,9 +230,11 @@ static irqreturn_t mms114_interrupt(int irq, void *dev_id) /* MMS136 has slightly different event size */ if (data->type == TYPE_MMS134S || data->type == TYPE_MMS136) - touch_size = packet_size / MMS136_EVENT_SIZE; + event_size = MMS136_EVENT_SIZE; else - touch_size = packet_size / MMS114_EVENT_SIZE; + event_size = MMS114_EVENT_SIZE; + + touch_size = packet_size / event_size; error = __mms114_read_reg(data, MMS114_INFORMATION, packet_size, (u8 *)touch); @@ -238,18 +242,20 @@ static irqreturn_t mms114_interrupt(int irq, void *dev_id) goto out; for (index = 0; index < touch_size; index++) { - switch (touch[index].type) { + t = (struct mms114_touch *)((u8 *)touch + index * event_size); + + switch (t->type) { case MMS114_TYPE_TOUCHSCREEN: - mms114_process_mt(data, touch + index); + mms114_process_mt(data, t); break; case MMS114_TYPE_TOUCHKEY: - mms114_process_touchkey(data, touch + index); + mms114_process_touchkey(data, t); break; default: dev_err(&client->dev, "Wrong touch type (%d)\n", - touch[index].type); + t->type); break; } } From 2691b68f9b03a1cb0a79e986cfa108e41ec3b909 Mon Sep 17 00:00:00 2001 From: Kris Bahnsen Date: Thu, 7 May 2026 16:49:43 +0000 Subject: [PATCH 0636/2758] Input: ads7846 - don't use scratch for tx_buf when clearing register commit 856668312685d9a8f32d49a135a89c429d309f81 upstream. The workaround for XPT2046 clears the command register, giving the touchscreen controller a NOP. The change incorrectly re-uses the req->scratch variable which is used as rx_buf for xfer[5], so by the time xfer[6] occurs, the contents of req->scratch may not be 0. It was found that the touchscreen controller can end up in a completely unresponsive state due to it being given a command the driver does not expect. Instead, rely on the spi_transfer behavior of tx_buf being NULL to transmit all 0 bits and use the scratch variable for the rx_buf for both the 1 byte command to and 2 byte response from the controller. Also relocates the scratch member of struct ser_req to force it into a different cache line to prevent any potential issues of DMA stepping on unrelated data in other struct members due to sharing the same cache line. This change was tested on real TSC2046 and ADS7843 controllers, but not the XPT2046 the workaround was originally created for. Confirming that the original modification to clear the command register does not impact either real controller. Fixes: 781a07da9bb94 ("Input: ads7846 - add dummy command register clearing cycle") Cc: stable@vger.kernel.org Co-developed-by: Mark Featherston Signed-off-by: Mark Featherston Signed-off-by: Kris Bahnsen Link: https://patch.msgid.link/20260507164943.760009-1-kris@embeddedTS.com Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/touchscreen/ads7846.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 4b39f7212d35c..1ae1ae42582af 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -325,7 +325,6 @@ struct ser_req { u8 ref_on; u8 command; u8 ref_off; - u16 scratch; struct spi_message msg; struct spi_transfer xfer[8]; /* @@ -333,6 +332,7 @@ struct ser_req { * transfer buffers to live in their own cache lines. */ __be16 sample ____cacheline_aligned; + u16 scratch; }; struct ads7845_ser_req { @@ -403,8 +403,7 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) spi_message_add_tail(&req->xfer[5], &req->msg); /* clear the command register */ - req->scratch = 0; - req->xfer[6].tx_buf = &req->scratch; + req->xfer[6].rx_buf = &req->scratch; req->xfer[6].len = 1; spi_message_add_tail(&req->xfer[6], &req->msg); From a8d87184576c889759e3aab899799a482f1e1a5b Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sat, 13 Jun 2026 20:07:20 -0500 Subject: [PATCH 0637/2758] Input: touchwin - reset the packet index on every complete packet commit 478cdd736f2ce3114f90e775d7358136d3977b94 upstream. tw_interrupt() accumulates each non-zero serial byte into a fixed three-byte buffer with a running index that is only reset once a full packet has been received *and* the device's two Y bytes agree: tw->data[tw->idx++] = data; if (tw->idx == TW_LENGTH && tw->data[1] == tw->data[2]) { ... tw->idx = 0; } The reset is gated on tw->data[1] == tw->data[2], a value the device controls. A malicious, malfunctioning or counterfeit Touchwindow peripheral can stream non-zero bytes whose 2nd and 3rd bytes differ: the index reaches TW_LENGTH without the equality holding, is never reset, and keeps growing, so tw->data[tw->idx++] walks off the end of the three-byte array and the rest of the heap-allocated struct tw, one attacker-chosen byte at a time -- an unbounded, device-driven heap out-of-bounds write. Reset the index on every completed packet and report an event only when the two Y bytes match, like the other serio touchscreen drivers do. Fixes: 11ea3173d5f2 ("Input: add driver for Touchwin serial touchscreens") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Link: https://patch.msgid.link/20260613-b4-disp-69921bfd-v1-1-82c036899959@proton.me Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/touchscreen/touchwin.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/input/touchscreen/touchwin.c b/drivers/input/touchscreen/touchwin.c index 099fd88e65d84..3ed33378dfcd8 100644 --- a/drivers/input/touchscreen/touchwin.c +++ b/drivers/input/touchscreen/touchwin.c @@ -63,12 +63,15 @@ static irqreturn_t tw_interrupt(struct serio *serio, if (data) { /* touch */ tw->touched = 1; tw->data[tw->idx++] = data; - /* verify length and that the two Y's are the same */ - if (tw->idx == TW_LENGTH && tw->data[1] == tw->data[2]) { - input_report_abs(dev, ABS_X, tw->data[0]); - input_report_abs(dev, ABS_Y, tw->data[1]); - input_report_key(dev, BTN_TOUCH, 1); - input_sync(dev); + /* a full packet ends the accumulation, valid or not */ + if (tw->idx == TW_LENGTH) { + /* report only if the two Y's are the same */ + if (tw->data[1] == tw->data[2]) { + input_report_abs(dev, ABS_X, tw->data[0]); + input_report_abs(dev, ABS_Y, tw->data[1]); + input_report_key(dev, BTN_TOUCH, 1); + input_sync(dev); + } tw->idx = 0; } } else if (tw->touched) { /* untouch */ From f3d5e77b27fded71dcb97f409262bf0abba0410e Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sun, 14 Jun 2026 14:19:43 -0700 Subject: [PATCH 0638/2758] Input: mms114 - reject an oversized device packet size commit 66725039f7090afe14c31bd259e2059a68f04023 upstream. mms114_interrupt() reads a packet of touch data from the device into a fixed-size on-stack buffer struct mms114_touch touch[MMS114_MAX_TOUCH]; which holds MMS114_MAX_TOUCH (10) events of MMS114_EVENT_SIZE (8) bytes, i.e. 80 bytes. The length of the I2C read into it is taken verbatim from the device: packet_size = mms114_read_reg(data, MMS114_PACKET_SIZE); if (packet_size <= 0) goto out; ... error = __mms114_read_reg(data, MMS114_INFORMATION, packet_size, (u8 *)touch); packet_size is a single device register byte (0x0F) and the only check is the lower bound packet_size <= 0; it is never bounded against the size of touch[]. A malfunctioning, malicious or counterfeit controller (or an attacker tampering with the I2C bus) can report a packet_size of up to 255, so __mms114_read_reg() writes up to 175 bytes past the end of touch[] on the IRQ-thread stack: a stack out-of-bounds write that can overwrite the stack canary, saved registers and the return address. A well-formed device never reports more than the buffer holds, so reject an oversized packet and drop the report, consistent with the handler's other error paths, rather than reading past the buffer. Fixes: 07b8481d4aff ("Input: add MELFAS mms114 touchscreen driver") Signed-off-by: Bryam Vargas Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260612-b4-disp-dc4b8dc4-v1-1-d7cb0a828d92@proton.me Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/touchscreen/mms114.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c index 0570e946b8766..fb618d134638f 100644 --- a/drivers/input/touchscreen/mms114.c +++ b/drivers/input/touchscreen/mms114.c @@ -228,6 +228,12 @@ static irqreturn_t mms114_interrupt(int irq, void *dev_id) if (packet_size <= 0) goto out; + if (packet_size > sizeof(touch)) { + dev_err(&client->dev, "Invalid packet size %d (max %zu)\n", + packet_size, sizeof(touch)); + goto out; + } + /* MMS136 has slightly different event size */ if (data->type == TYPE_MMS134S || data->type == TYPE_MMS136) event_size = MMS136_EVENT_SIZE; From b75371bc87aed335daf3699160879ab64e285d34 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Wed, 24 Jun 2026 17:47:39 +0800 Subject: [PATCH 0639/2758] Input: gscps2 - advance receive buffer write index commit d86d4f8cbb5a55a3b9b86f7b5ab8c4cdda600a3f upstream. Commit 44f920069911 ("Input: gscps2 - use guard notation when acquiring spinlock") moved the receive loop into gscps2_read_data() and gscps2_report_data(). While moving the code, it preserved the writes to buffer[ps2port->append], but omitted the following producer index update from the original loop: ps2port->append = (ps2port->append + 1) & BUFFER_SIZE; As a result, append never advances. Since gscps2_report_data() only reports bytes while act != append, the receive buffer always appears empty and no keyboard or mouse data reaches the serio core. Restore the omitted index update. Fixes: 44f920069911 ("Input: gscps2 - use guard notation when acquiring spinlock") Cc: stable@vger.kernel.org # 6.13+ Signed-off-by: Xu Rao Link: https://patch.msgid.link/460B5655BA580C60+20260624094739.850306-1-raoxu@uniontech.com Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/serio/gscps2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/serio/gscps2.c b/drivers/input/serio/gscps2.c index 22b2f57fd91f5..bf9b993f57330 100644 --- a/drivers/input/serio/gscps2.c +++ b/drivers/input/serio/gscps2.c @@ -219,6 +219,7 @@ static void gscps2_read_data(struct gscps2port *ps2port) ps2port->buffer[ps2port->append].str = status; ps2port->buffer[ps2port->append].data = gscps2_readb_input(ps2port->addr); + ps2port->append = (ps2port->append + 1) & BUFFER_SIZE; } while (true); } From 2914e243ec9efa8b955da0d8f2d01fe5dc5ee472 Mon Sep 17 00:00:00 2001 From: Florian Fuchs Date: Mon, 29 Jun 2026 18:33:42 -0700 Subject: [PATCH 0640/2758] Input: maplemouse - fix NULL pointer dereference in open() commit ee89db004238bd0b034f2a6176e175561658750b upstream. Commit 555c765b0cc2 ("Input: mouse - drop unnecessary calls to input_set_drvdata") dropped the input_set_drvdata() call in probe because the data appeared to be unused. However, dc_mouse_open() and dc_mouse_close() were using maple_get_drvdata(to_maple_dev(&dev->dev)). This appears to be accessing the data attached to an instance of maple_device structure, while in reality this actually retrieves driver data from the input device's embedded struct device (doing invalid conversion of input device structure to maple device). After input_set_drvdata() was removed, that lookup started returning NULL and opening the input device dereferences mse->mdev. Restore input_set_drvdata() and convert open() and close() to use input_get_drvdata() so the dependency is no longer hidden. Fixes: 6b3480855aad ("maple: input: fix up maple mouse driver") Fixes: 555c765b0cc2 ("Input: mouse - drop unnecessary calls to input_set_drvdata") Signed-off-by: Florian Fuchs Link: https://patch.msgid.link/20260628230715.2982552-1-fuchsfl@gmail.com Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/mouse/maplemouse.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/input/mouse/maplemouse.c b/drivers/input/mouse/maplemouse.c index c99f7e2342191..c41182766538a 100644 --- a/drivers/input/mouse/maplemouse.c +++ b/drivers/input/mouse/maplemouse.c @@ -48,7 +48,7 @@ static void dc_mouse_callback(struct mapleq *mq) static int dc_mouse_open(struct input_dev *dev) { - struct dc_mouse *mse = maple_get_drvdata(to_maple_dev(&dev->dev)); + struct dc_mouse *mse = input_get_drvdata(dev); maple_getcond_callback(mse->mdev, dc_mouse_callback, HZ/50, MAPLE_FUNC_MOUSE); @@ -58,7 +58,7 @@ static int dc_mouse_open(struct input_dev *dev) static void dc_mouse_close(struct input_dev *dev) { - struct dc_mouse *mse = maple_get_drvdata(to_maple_dev(&dev->dev)); + struct dc_mouse *mse = input_get_drvdata(dev); maple_getcond_callback(mse->mdev, dc_mouse_callback, 0, MAPLE_FUNC_MOUSE); @@ -88,6 +88,7 @@ static int probe_maple_mouse(struct device *dev) mse->dev = input_dev; mse->mdev = mdev; + input_set_drvdata(input_dev, mse); input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE); From d5ab7e52e86e332571a4eb28bff1942344e5f2da Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 3 Jul 2026 23:01:12 -0700 Subject: [PATCH 0641/2758] Input: mms114 - fix multi-touch slot corruption commit adea84ee6cdea611146c4251d3c1616f5a09feca upstream. If the touchscreen controller reports a touch ID of 0, the driver calculates the slot ID as touch->id - 1, which underflows to UINT_MAX. This is passed to input_mt_slot() as -1. Since the input core ignores negative slot values, the active slot remains unchanged. The driver then reports the touch coordinates for the previously active slot, corrupting its state. Fix this by rejecting touch reports with ID 0. Fixes: 07b8481d4aff ("Input: add MELFAS mms114 touchscreen driver") Cc: stable@vger.kernel.org Reported-by: sashiko-bot@kernel.org Assisted-by: Antigravity:gemini-3.5-flash Link: https://patch.msgid.link/20260704060115.353049-1-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/touchscreen/mms114.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c index fb618d134638f..41859bfa28443 100644 --- a/drivers/input/touchscreen/mms114.c +++ b/drivers/input/touchscreen/mms114.c @@ -165,7 +165,7 @@ static void mms114_process_mt(struct mms114_data *data, struct mms114_touch *tou unsigned int x; unsigned int y; - if (touch->id > MMS114_MAX_TOUCH) { + if (touch->id == 0 || touch->id > MMS114_MAX_TOUCH) { dev_err(&client->dev, "Wrong touch id (%d)\n", touch->id); return; } From 2351e841951cdda59f85fad9bad2d2be53b05e49 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 29 Jun 2026 18:44:41 -0700 Subject: [PATCH 0642/2758] Input: maple_keyb - set driver data before registering input device commit 536394ec81419b67d9f4f0028812c4372397be1b upstream. Set maple driver data before calling input_register_device() to ensure that it is available if the device is opened immediately and the callback is triggered. Cc: stable@vger.kernel.org Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/keyboard/maple_keyb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/keyboard/maple_keyb.c b/drivers/input/keyboard/maple_keyb.c index 80a5181313e15..3d5538dd4f237 100644 --- a/drivers/input/keyboard/maple_keyb.c +++ b/drivers/input/keyboard/maple_keyb.c @@ -166,6 +166,8 @@ static int probe_maple_kbd(struct device *dev) kbd->dev = idev; memcpy(kbd->keycode, dc_kbd_keycode, sizeof(kbd->keycode)); + maple_set_drvdata(mdev, kbd); + idev->name = mdev->product_name; idev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); idev->keycode = kbd->keycode; @@ -190,8 +192,6 @@ static int probe_maple_kbd(struct device *dev) mdev->driver = mdrv; - maple_set_drvdata(mdev, kbd); - return error; fail_register: From 79e6fd106356200c74105cbd64f522f8f425f786 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 29 Jun 2026 22:47:34 -0700 Subject: [PATCH 0643/2758] Input: maplemouse - set driver data before registering input device commit 738f24bbbc95dd50cb4229d1ed62a05f29db2bda upstream. Set maple driver data before calling input_register_device() to ensure that it is available if the device is opened immediately and the callback is triggered. Cc: stable@vger.kernel.org Assisted-by: Antigravity:gemini-3.5-flash Tested-by: Florian Fuchs Link: https://patch.msgid.link/akNXw45L_8bxD6QV@google.com Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/mouse/maplemouse.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/input/mouse/maplemouse.c b/drivers/input/mouse/maplemouse.c index c41182766538a..0c8f7d1b02aa3 100644 --- a/drivers/input/mouse/maplemouse.c +++ b/drivers/input/mouse/maplemouse.c @@ -88,6 +88,8 @@ static int probe_maple_mouse(struct device *dev) mse->dev = input_dev; mse->mdev = mdev; + maple_set_drvdata(mdev, mse); + input_set_drvdata(input_dev, mse); input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | @@ -103,12 +105,12 @@ static int probe_maple_mouse(struct device *dev) goto fail_register; mdev->driver = mdrv; - maple_set_drvdata(mdev, mse); return error; fail_register: input_free_device(input_dev); + maple_set_drvdata(mdev, NULL); fail_nomem: kfree(mse); fail: From 2ffcdbfd1431c365ce07c20cb3a4b1100cfce534 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 29 Jun 2026 22:49:15 -0700 Subject: [PATCH 0644/2758] Input: maplecontrol - set driver data before registering input device commit fe938ee497d58c644f6910cfe6ae155f6fb3e523 upstream. Set maple driver data before calling input_register_device() to ensure that it is available if the device is opened immediately and the callback is triggered. Cc: stable@vger.kernel.org Assisted-by: Antigravity:gemini-3.5-flash Tested-by: Florian Fuchs Link: https://patch.msgid.link/akNYib9hQFNN1fA9@google.com Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/joystick/maplecontrol.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/input/joystick/maplecontrol.c b/drivers/input/joystick/maplecontrol.c index 7f36f73844a91..6293b6e8148b3 100644 --- a/drivers/input/joystick/maplecontrol.c +++ b/drivers/input/joystick/maplecontrol.c @@ -112,6 +112,8 @@ static int probe_maple_controller(struct device *dev) pad->dev = idev; pad->mdev = mdev; + maple_set_drvdata(mdev, pad); + idev->open = dc_pad_open; idev->close = dc_pad_close; @@ -146,7 +148,6 @@ static int probe_maple_controller(struct device *dev) goto fail; mdev->driver = mdrv; - maple_set_drvdata(mdev, pad); return 0; From da3e44add94b05dfde56f898421922f5cf35705f Mon Sep 17 00:00:00 2001 From: Zhenhao Wan Date: Fri, 12 Jun 2026 01:15:54 +0800 Subject: [PATCH 0645/2758] RDMA/rtrs-srv: Bound RDMA-Write length to chunk size in rdma_write_sg commit 963af8d97a8c6a117134a8d0db1415e0489200b1 upstream. When the server answers an RTRS READ, rdma_write_sg() builds the source scatter/gather entry for the IB_WR_RDMA_WRITE that returns data to the peer. Its length is taken directly from the wire descriptor: plist->length = le32_to_cpu(id->rd_msg->desc[0].len); rd_msg points into the chunk buffer that the remote peer filled via RDMA-WRITE-WITH-IMM (rtrs_srv_rdma_done() -> process_io_req() -> process_read()), so desc[0].len is attacker-controlled and, before this change, was only rejected when zero. The source address is the fixed chunk start (dma_addr[msg_id]) and the source lkey is the PD-wide local_dma_lkey, which is not tied to the chunk's MR mapping, so the verbs layer does not constrain the transfer length to max_chunk_size. msg_id and off are bounded against queue_depth and max_chunk_size in rtrs_srv_rdma_done(), but desc[0].len is a separate field that was not checked against the chunk size. A peer that advertises desc[0].len larger than max_chunk_size can make the posted RDMA write read past the chunk's mapped region. The resulting behaviour depends on the IOMMU configuration: with no IOMMU or in passthrough mode the read may extend into memory adjacent to the chunk and be returned to the peer, which can disclose host memory; with a translating IOMMU the out-of-range access is expected to fault and abort the connection. In either case the transfer exceeds what the protocol permits and is driven by a remote peer. Reject a descriptor length above max_chunk_size, mirroring the existing off >= max_chunk_size bound in rtrs_srv_rdma_done(). Legitimate clients do not exceed it: the client sets desc[0].len to its MR length, which is capped at the negotiated max_io_size (max_chunk_size - MAX_HDR_SIZE). Fixes: 9cb837480424 ("RDMA/rtrs: server: main functionality") Link: https://patch.msgid.link/r/20260612-master-v1-1-70cde5c6fdc9@gmail.com Reported-by: Yuhao Jiang Cc: stable@vger.kernel.org Signed-off-by: Zhenhao Wan Reviewed-by: Md Haris Iqbal Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/ulp/rtrs/rtrs-srv.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/ulp/rtrs/rtrs-srv.c b/drivers/infiniband/ulp/rtrs/rtrs-srv.c index 6482ad859bd10..f81e122a3ccb6 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs-srv.c +++ b/drivers/infiniband/ulp/rtrs/rtrs-srv.c @@ -225,8 +225,9 @@ static int rdma_write_sg(struct rtrs_srv_op *id) /* WR will fail with length error * if this is 0 */ - if (plist->length == 0) { - rtrs_err(s, "Invalid RDMA-Write sg list length 0\n"); + if (plist->length == 0 || plist->length > max_chunk_size) { + rtrs_err(s, "Invalid RDMA-Write sg list length %u\n", + plist->length); return -EINVAL; } From 020700a2fdc43a22aed621e898220f12557325fd Mon Sep 17 00:00:00 2001 From: Maher Sanalla Date: Tue, 9 Jun 2026 14:16:38 +0300 Subject: [PATCH 0646/2758] RDMA/core: Fix broadcast address falsely detected as local commit 942cd47faa2047b46dfd85745603eba9006973e6 upstream. When rdma_resolve_addr() is invoked with a broadcast destination on an IPoIB interface, is_dst_local() inspects the resolved route and incorrectly concludes that the address is local. As a result, the resolution fails with -ENODEV. The issue stems from using '&' to compare rt_type with RTN_LOCAL. The RTN_* values form a sequential enum, not a bitmask (RTN_LOCAL=2, RTN_BROADCAST=3). Thus, "rt_type & RTN_LOCAL" yields a non-zero result for a broadcast route as well. Replace '&' with '==' when comparing rt_type against RTN_LOCAL. Link: https://patch.msgid.link/r/20260609-fix-rdma-resolve-addr-v1-1-449b8b4e6c09@nvidia.com Cc: stable@vger.kernel.org Fixes: c31e4038c97f ("RDMA/core: Use route entry flag to decide on loopback traffic") Signed-off-by: Maher Sanalla Reviewed-by: Vlad Dumitrescu Signed-off-by: Edward Srouji Reviewed-by: Parav Pandit Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/core/addr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c index 27992c38ad902..d9306c0414b58 100644 --- a/drivers/infiniband/core/addr.c +++ b/drivers/infiniband/core/addr.c @@ -438,7 +438,7 @@ static int addr6_resolve(struct sockaddr *src_sock, static bool is_dst_local(const struct dst_entry *dst) { if (dst->ops->family == AF_INET) - return !!(dst_rtable(dst)->rt_type & RTN_LOCAL); + return dst_rtable(dst)->rt_type == RTN_LOCAL; else if (dst->ops->family == AF_INET6) return !!(dst_rt6_info(dst)->rt6i_flags & RTF_LOCAL); else From 75c93cd3c421890f49ea93f0b978b9b7bb10e5e3 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 2 Jun 2026 15:47:00 -0400 Subject: [PATCH 0647/2758] RDMA/siw: bound Read Response placement to the RREAD length commit 7d29f7e9dbd844cae4d3e559cf78324b9642fd6b upstream. In drivers/infiniband/sw/siw/siw_qp_rx.c, siw_proc_rresp() places each inbound Read Response DDP segment at sge->laddr + wqe->processed and then accumulates wqe->processed, but it never checks the running total against the sink buffer length on continuation segments. siw_check_sge() resolves and validates the sink memory only on the first fragment (the if (!*mem) branch), and siw_rresp_check_ntoh() compares the cumulative length against wqe->bytes only on the final segment (the !frx->more_ddp_segs guard). A connected siw peer that answers an outstanding RREAD with Read Response segments that keep the DDP Last flag clear, carrying more total payload than the RREAD requested, drives wqe->processed past the validated sink buffer; the next siw_rx_data() call writes out of bounds at sge->laddr + wqe->processed. siw runs iWARP over ordinary routable TCP, so the peer is the remote end of an established RDMA connection and needs no local privilege. Bound every segment before placement, exactly as siw_proc_send() and siw_proc_write() already do for their tagged and untagged paths, and terminate the connection with a base-or-bounds DDP error when the Read Response would overrun the sink buffer. This is the second receive-path length fix for this file. A separate change rejects an MPA FPDU length that underflows the per-fragment remainder in the header decode; that guard does not cover this case, because here each individual segment length is self-consistent and only the accumulated placement offset overruns the buffer. Fixes: 8b6a361b8c48 ("rdma/siw: receive path") Link: https://patch.msgid.link/r/20260602194700.2273758-1-michael.bommarito@gmail.com Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/sw/siw/siw_qp_rx.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/infiniband/sw/siw/siw_qp_rx.c b/drivers/infiniband/sw/siw/siw_qp_rx.c index 34d03584160c2..b566d163c5aab 100644 --- a/drivers/infiniband/sw/siw/siw_qp_rx.c +++ b/drivers/infiniband/sw/siw/siw_qp_rx.c @@ -844,6 +844,15 @@ int siw_proc_rresp(struct siw_qp *qp) } mem_p = *mem; + if (unlikely(wqe->processed + srx->fpdu_part_rem > wqe->bytes)) { + siw_dbg_qp(qp, "rresp len: %d + %d > %d\n", + wqe->processed, srx->fpdu_part_rem, wqe->bytes); + wqe->wc_status = SIW_WC_LOC_LEN_ERR; + siw_init_terminate(qp, TERM_ERROR_LAYER_DDP, + DDP_ETYPE_TAGGED_BUF, + DDP_ECODE_T_BASE_BOUNDS, 0); + return -EINVAL; + } bytes = min(srx->fpdu_part_rem, srx->skb_new); rv = siw_rx_data(mem_p, srx, &frx->pbl_idx, sge->laddr + wqe->processed, bytes); From 7ddcbd4dd85fcd61ee565bb122bb39c11290155a Mon Sep 17 00:00:00 2001 From: "Matthew R. Ochs" Date: Tue, 26 May 2026 08:20:21 -0700 Subject: [PATCH 0648/2758] fuse: back uncached readdir buffers with pages commit 4dd6f6d3085a84e74b0a1efec3a05ed0b5125dce upstream. Commit dabb90391028 ("fuse: increase readdir buffer size") changed fuse_readdir_uncached() to size its temporary buffer from ctx->count. This is useful for overlayfs and other in-kernel callers that use INT_MAX to indicate an unlimited directory read. The larger buffer is currently supplied as a kvec output argument. For virtiofs, kvec arguments are copied through req->argbuf, which is allocated with kmalloc(..., GFP_ATOMIC). A large uncached readdir buffer can therefore require a multi-megabyte contiguous atomic allocation before the request is queued. Avoid the large bounce-buffer allocation by backing uncached readdir output with pages and setting out_pages. Transports such as virtiofs can then pass the pages as scatter-gather entries instead of copying the output through argbuf. Map the pages with vm_map_ram() only while parsing the returned dirents. The existing parser can then continue to use a linear kernel mapping. [SzM: separate allocation of pages into a helper function] Fixes: dabb90391028 ("fuse: increase readdir buffer size") Cc: stable@vger.kernel.org Signed-off-by: Matthew R. Ochs Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/readdir.c | 85 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 67 insertions(+), 18 deletions(-) diff --git a/fs/fuse/readdir.c b/fs/fuse/readdir.c index db5ae8ec10305..c38139225a2e3 100644 --- a/fs/fuse/readdir.c +++ b/fs/fuse/readdir.c @@ -12,6 +12,7 @@ #include #include #include +#include static bool fuse_use_readdirplus(struct inode *dir, struct dir_context *ctx) { @@ -335,6 +336,43 @@ static int parse_dirplusfile(char *buf, size_t nbytes, struct file *file, return 0; } +static struct page **fuse_readdir_alloc_buf(struct fuse_args_pages *ap, size_t *bufsize) +{ + unsigned int i, nr_alloc, nr_pages = DIV_ROUND_UP(*bufsize, PAGE_SIZE); + struct page **pages = kcalloc(nr_pages, sizeof(*pages), GFP_KERNEL); + + if (!pages) + return NULL; + + nr_alloc = alloc_pages_bulk(GFP_KERNEL, nr_pages, pages); + if (!nr_alloc) + goto free_array; + + if (nr_alloc < nr_pages) { + nr_pages = nr_alloc; + *bufsize = (size_t) nr_pages << PAGE_SHIFT; + } + + ap->folios = fuse_folios_alloc(nr_pages, GFP_KERNEL, &ap->descs); + if (!ap->folios) + goto release_pages; + + for (i = 0; i < nr_pages; i++) { + ap->folios[i] = page_folio(pages[i]); + ap->descs[i].length = min_t(size_t, *bufsize - (size_t)i * PAGE_SIZE, PAGE_SIZE); + } + ap->num_folios = nr_pages; + ap->args.out_pages = true; + + return pages; + +release_pages: + release_pages(pages, nr_pages); +free_array: + kfree(pages); + return NULL; +} + static int fuse_readdir_uncached(struct file *file, struct dir_context *ctx) { int plus; @@ -343,18 +381,16 @@ static int fuse_readdir_uncached(struct file *file, struct dir_context *ctx) struct fuse_mount *fm = get_fuse_mount(inode); struct fuse_conn *fc = fm->fc; struct fuse_io_args ia = {}; - struct fuse_args *args = &ia.ap.args; + struct fuse_args_pages *ap = &ia.ap; void *buf; size_t bufsize = clamp((unsigned int) ctx->count, PAGE_SIZE, fc->max_pages << PAGE_SHIFT); u64 attr_version = 0, evict_ctr = 0; bool locked; + struct page **pages = fuse_readdir_alloc_buf(ap, &bufsize); - buf = kvmalloc(bufsize, GFP_KERNEL); - if (!buf) + if (!pages) return -ENOMEM; - args->out_args[0].value = buf; - plus = fuse_use_readdirplus(inode, ctx); if (plus) { attr_version = fuse_get_attr_version(fm->fc); @@ -364,24 +400,37 @@ static int fuse_readdir_uncached(struct file *file, struct dir_context *ctx) fuse_read_args_fill(&ia, file, ctx->pos, bufsize, FUSE_READDIR); } locked = fuse_lock_inode(inode); - res = fuse_simple_request(fm, args); + res = fuse_simple_request(fm, &ap->args); fuse_unlock_inode(inode, locked); - if (res >= 0) { - if (!res) { - struct fuse_file *ff = file->private_data; - - if (ff->open_flags & FOPEN_CACHE_DIR) - fuse_readdir_cache_end(file, ctx->pos); - } else if (plus) { - res = parse_dirplusfile(buf, res, file, ctx, attr_version, - evict_ctr); - } else { + if (res < 0) + goto out; + + if (!res) { + struct fuse_file *ff = file->private_data; + + if (ff->open_flags & FOPEN_CACHE_DIR) + fuse_readdir_cache_end(file, ctx->pos); + goto out; + } + + buf = vm_map_ram(pages, ap->num_folios, -1); + if (!buf) { + res = -ENOMEM; + } else { + if (plus) + res = parse_dirplusfile(buf, res, file, ctx, attr_version, evict_ctr); + else res = parse_dirfile(buf, res, file, ctx); - } + + vm_unmap_ram(buf, ap->num_folios); } +out: + kfree(ap->folios); + release_pages(pages, ap->num_folios); + kfree(pages); - kvfree(buf); fuse_invalidate_atime(inode); + return res; } From c78c4b242299bc581e4987e5c2786c6f4760c516 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Wed, 10 Jun 2026 00:37:18 +0000 Subject: [PATCH 0649/2758] fuse: avoid 32-bit prune notification count wrap commit 54243797cedf55447b4c5d560e8cd709900061ae upstream. FUSE_NOTIFY_PRUNE validates the nodeid payload length with: size - sizeof(outarg) != outarg.count * sizeof(u64) On 32-bit kernels, size_t is also 32 bits, so the daemon-controlled count multiplication can wrap. A prune notification with count 0x20000000 and no nodeid payload passes the check, enters the copy loop, and asks the device copy path to read nodeids that are not present in the userspace write buffer. In QEMU this reaches the fuse_copy_fill() BUG_ON(!err) path. Validate the payload length with array_size() instead. That accepts exactly the same valid messages, but avoids wrapping arithmetic before the copy loop consumes the count. Assisted-by: Codex:gpt-5.5-cyber-preview Fixes: 3f29d59e92a9 ("fuse: add prune notification") Cc: stable@vger.kernel.org Signed-off-by: Samuel Moelius Reviewed-by: Joanne Koong Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev.c | 2 +- fs/fuse/notify.c | 434 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 435 insertions(+), 1 deletion(-) create mode 100644 fs/fuse/notify.c diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 71f9fcefc0ebe..970b8619ff042 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -2072,7 +2072,7 @@ static int fuse_notify_prune(struct fuse_conn *fc, unsigned int size, if (err) return err; - if (size - sizeof(outarg) != outarg.count * sizeof(u64)) + if (size - sizeof(outarg) != array_size(outarg.count, sizeof(u64))) return -EINVAL; for (; outarg.count; outarg.count -= num) { diff --git a/fs/fuse/notify.c b/fs/fuse/notify.c new file mode 100644 index 0000000000000..7cd63502e124b --- /dev/null +++ b/fs/fuse/notify.c @@ -0,0 +1,434 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "dev.h" +#include "fuse_i.h" +#include + +static int fuse_notify_poll(struct fuse_conn *fc, unsigned int size, + struct fuse_copy_state *cs) +{ + struct fuse_notify_poll_wakeup_out outarg; + int err; + + if (size != sizeof(outarg)) + return -EINVAL; + + err = fuse_copy_one(cs, &outarg, sizeof(outarg)); + if (err) + return err; + + fuse_copy_finish(cs); + return fuse_notify_poll_wakeup(fc, &outarg); +} + +static int fuse_notify_inval_inode(struct fuse_conn *fc, unsigned int size, + struct fuse_copy_state *cs) +{ + struct fuse_notify_inval_inode_out outarg; + int err; + + if (size != sizeof(outarg)) + return -EINVAL; + + err = fuse_copy_one(cs, &outarg, sizeof(outarg)); + if (err) + return err; + fuse_copy_finish(cs); + + down_read(&fc->killsb); + err = fuse_reverse_inval_inode(fc, outarg.ino, + outarg.off, outarg.len); + up_read(&fc->killsb); + return err; +} + +static int fuse_notify_inval_entry(struct fuse_conn *fc, unsigned int size, + struct fuse_copy_state *cs) +{ + struct fuse_notify_inval_entry_out outarg; + int err; + char *buf; + struct qstr name; + + if (size < sizeof(outarg)) + return -EINVAL; + + err = fuse_copy_one(cs, &outarg, sizeof(outarg)); + if (err) + return err; + + if (outarg.namelen > fc->name_max) + return -ENAMETOOLONG; + + err = -EINVAL; + if (size != sizeof(outarg) + outarg.namelen + 1) + return -EINVAL; + + buf = kzalloc(outarg.namelen + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + name.name = buf; + name.len = outarg.namelen; + err = fuse_copy_one(cs, buf, outarg.namelen + 1); + if (err) + goto err; + fuse_copy_finish(cs); + buf[outarg.namelen] = 0; + + down_read(&fc->killsb); + err = fuse_reverse_inval_entry(fc, outarg.parent, 0, &name, outarg.flags); + up_read(&fc->killsb); +err: + kfree(buf); + return err; +} + +static int fuse_notify_delete(struct fuse_conn *fc, unsigned int size, + struct fuse_copy_state *cs) +{ + struct fuse_notify_delete_out outarg; + int err; + char *buf; + struct qstr name; + + if (size < sizeof(outarg)) + return -EINVAL; + + err = fuse_copy_one(cs, &outarg, sizeof(outarg)); + if (err) + return err; + + if (outarg.namelen > fc->name_max) + return -ENAMETOOLONG; + + if (size != sizeof(outarg) + outarg.namelen + 1) + return -EINVAL; + + buf = kzalloc(outarg.namelen + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + name.name = buf; + name.len = outarg.namelen; + err = fuse_copy_one(cs, buf, outarg.namelen + 1); + if (err) + goto err; + fuse_copy_finish(cs); + buf[outarg.namelen] = 0; + + down_read(&fc->killsb); + err = fuse_reverse_inval_entry(fc, outarg.parent, outarg.child, &name, 0); + up_read(&fc->killsb); +err: + kfree(buf); + return err; +} + +static int fuse_notify_store(struct fuse_conn *fc, unsigned int size, + struct fuse_copy_state *cs) +{ + struct fuse_notify_store_out outarg; + struct inode *inode; + struct address_space *mapping; + u64 nodeid; + int err; + unsigned int num; + loff_t file_size; + loff_t pos; + loff_t end; + + if (size < sizeof(outarg)) + return -EINVAL; + + err = fuse_copy_one(cs, &outarg, sizeof(outarg)); + if (err) + return err; + + if (size - sizeof(outarg) != outarg.size) + return -EINVAL; + + if (outarg.offset >= MAX_LFS_FILESIZE) + return -EINVAL; + + nodeid = outarg.nodeid; + pos = outarg.offset; + num = min(outarg.size, MAX_LFS_FILESIZE - pos); + + down_read(&fc->killsb); + + err = -ENOENT; + inode = fuse_ilookup(fc, nodeid, NULL); + if (!inode) + goto out_up_killsb; + + mapping = inode->i_mapping; + file_size = i_size_read(inode); + end = pos + num; + if (end > file_size) { + file_size = end; + fuse_write_update_attr(inode, file_size, num); + } + + while (num) { + struct folio *folio; + unsigned int folio_offset; + unsigned int nr_bytes; + pgoff_t index = pos >> PAGE_SHIFT; + + folio = filemap_grab_folio(mapping, index); + err = PTR_ERR(folio); + if (IS_ERR(folio)) + goto out_iput; + + folio_offset = offset_in_folio(folio, pos); + nr_bytes = min(num, folio_size(folio) - folio_offset); + + err = fuse_copy_folio(cs, &folio, folio_offset, nr_bytes, 0); + if (!folio_test_uptodate(folio) && !err && folio_offset == 0 && + (nr_bytes == folio_size(folio) || file_size == end)) { + folio_zero_segment(folio, nr_bytes, folio_size(folio)); + folio_mark_uptodate(folio); + } + folio_unlock(folio); + folio_put(folio); + + if (err) + goto out_iput; + + pos += nr_bytes; + num -= nr_bytes; + } + + err = 0; + +out_iput: + iput(inode); +out_up_killsb: + up_read(&fc->killsb); + return err; +} + +struct fuse_retrieve_args { + struct fuse_args_pages ap; + struct fuse_notify_retrieve_in inarg; +}; + +static void fuse_retrieve_end(struct fuse_args *args, int error) +{ + struct fuse_retrieve_args *ra = + container_of(args, typeof(*ra), ap.args); + + release_pages(ra->ap.folios, ra->ap.num_folios); + kfree(ra); +} + +static int fuse_retrieve(struct fuse_mount *fm, struct inode *inode, + struct fuse_notify_retrieve_out *outarg) +{ + int err; + struct address_space *mapping = inode->i_mapping; + loff_t file_size; + unsigned int num; + unsigned int offset; + size_t total_len = 0; + unsigned int num_pages; + struct fuse_conn *fc = fm->fc; + struct fuse_retrieve_args *ra; + size_t args_size = sizeof(*ra); + struct fuse_args_pages *ap; + struct fuse_args *args; + loff_t pos = outarg->offset; + + offset = offset_in_page(pos); + file_size = i_size_read(inode); + + num = min(outarg->size, fc->max_write); + if (pos > file_size) + num = 0; + else if (num > file_size - pos) + num = file_size - pos; + + num_pages = DIV_ROUND_UP(num + offset, PAGE_SIZE); + num_pages = min(num_pages, fc->max_pages); + num = min(num, num_pages << PAGE_SHIFT); + + args_size += num_pages * (sizeof(ap->folios[0]) + sizeof(ap->descs[0])); + + ra = kzalloc(args_size, GFP_KERNEL); + if (!ra) + return -ENOMEM; + + ap = &ra->ap; + ap->folios = (void *) (ra + 1); + ap->descs = (void *) (ap->folios + num_pages); + + args = &ap->args; + args->nodeid = outarg->nodeid; + args->opcode = FUSE_NOTIFY_REPLY; + args->in_numargs = 3; + args->in_pages = true; + args->end = fuse_retrieve_end; + + while (num && ap->num_folios < num_pages) { + struct folio *folio; + unsigned int folio_offset; + unsigned int nr_bytes; + pgoff_t index = pos >> PAGE_SHIFT; + + folio = filemap_get_folio(mapping, index); + if (IS_ERR(folio)) + break; + + folio_offset = offset_in_folio(folio, pos); + nr_bytes = min(folio_size(folio) - folio_offset, num); + + ap->folios[ap->num_folios] = folio; + ap->descs[ap->num_folios].offset = folio_offset; + ap->descs[ap->num_folios].length = nr_bytes; + ap->num_folios++; + + pos += nr_bytes; + num -= nr_bytes; + total_len += nr_bytes; + } + ra->inarg.offset = outarg->offset; + ra->inarg.size = total_len; + fuse_set_zero_arg0(args); + args->in_args[1].size = sizeof(ra->inarg); + args->in_args[1].value = &ra->inarg; + args->in_args[2].size = total_len; + + err = fuse_simple_notify_reply(fm, args, outarg->notify_unique); + if (err) + fuse_retrieve_end(args, err); + + return err; +} + +static int fuse_notify_retrieve(struct fuse_conn *fc, unsigned int size, + struct fuse_copy_state *cs) +{ + struct fuse_notify_retrieve_out outarg; + struct fuse_mount *fm; + struct inode *inode; + u64 nodeid; + int err; + + if (size != sizeof(outarg)) + return -EINVAL; + + err = fuse_copy_one(cs, &outarg, sizeof(outarg)); + if (err) + return err; + + fuse_copy_finish(cs); + + if (outarg.offset >= MAX_LFS_FILESIZE) + return -EINVAL; + + down_read(&fc->killsb); + err = -ENOENT; + nodeid = outarg.nodeid; + + inode = fuse_ilookup(fc, nodeid, &fm); + if (inode) { + err = fuse_retrieve(fm, inode, &outarg); + iput(inode); + } + up_read(&fc->killsb); + + return err; +} + +static int fuse_notify_resend(struct fuse_conn *fc) +{ + fuse_chan_resend(fc->chan); + return 0; +} + +/* + * Increments the fuse connection epoch. This will result of dentries from + * previous epochs to be invalidated. Additionally, if inval_wq is set, a work + * queue is scheduled to trigger the invalidation. + */ +static int fuse_notify_inc_epoch(struct fuse_conn *fc) +{ + atomic_inc(&fc->epoch); + if (inval_wq) + schedule_work(&fc->epoch_work); + + return 0; +} + +static int fuse_notify_prune(struct fuse_conn *fc, unsigned int size, + struct fuse_copy_state *cs) +{ + struct fuse_notify_prune_out outarg; + const unsigned int batch = 512; + u64 *nodeids __free(kfree) = kmalloc(sizeof(u64) * batch, GFP_KERNEL); + unsigned int num, i; + int err; + + if (!nodeids) + return -ENOMEM; + + if (size < sizeof(outarg)) + return -EINVAL; + + err = fuse_copy_one(cs, &outarg, sizeof(outarg)); + if (err) + return err; + + if (size - sizeof(outarg) != array_size(outarg.count, sizeof(u64))) + return -EINVAL; + + for (; outarg.count; outarg.count -= num) { + num = min(batch, outarg.count); + err = fuse_copy_one(cs, nodeids, num * sizeof(u64)); + if (err) + return err; + + scoped_guard(rwsem_read, &fc->killsb) { + for (i = 0; i < num; i++) + fuse_try_prune_one_inode(fc, nodeids[i]); + } + } + return 0; +} + +int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, + unsigned int size, struct fuse_copy_state *cs) +{ + switch (code) { + case FUSE_NOTIFY_POLL: + return fuse_notify_poll(fc, size, cs); + + case FUSE_NOTIFY_INVAL_INODE: + return fuse_notify_inval_inode(fc, size, cs); + + case FUSE_NOTIFY_INVAL_ENTRY: + return fuse_notify_inval_entry(fc, size, cs); + + case FUSE_NOTIFY_STORE: + return fuse_notify_store(fc, size, cs); + + case FUSE_NOTIFY_RETRIEVE: + return fuse_notify_retrieve(fc, size, cs); + + case FUSE_NOTIFY_DELETE: + return fuse_notify_delete(fc, size, cs); + + case FUSE_NOTIFY_RESEND: + return fuse_notify_resend(fc); + + case FUSE_NOTIFY_INC_EPOCH: + return fuse_notify_inc_epoch(fc); + + case FUSE_NOTIFY_PRUNE: + return fuse_notify_prune(fc, size, cs); + + default: + return -EINVAL; + } +} From a37a64ebc9d70a1829ad65e88dfb24fb82cb6d20 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 10 Jun 2026 12:57:21 +0200 Subject: [PATCH 0650/2758] Revert "fuse: fix conversion of fuse_reverse_inval_entry() to start_removing()" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit c12bbaf7c2d19325913ffd7002e580e63952d9e2 upstream. This reverts commit cab012375122304a6343c1ed09404e5143b9dc01. Commit c9ba789dad15 ("VFS: introduce start_creating_noperm() and start_removing_noperm()") caused a regression in FUSE_NOTIFY_INVAL_ENTRY, which failed to invalidate negative dentries. This manifests in the filesystem returning -ENOENT for operations on an existing file. Fixing it properly while still keeping the start_removing* infrastructure would add much additional complexity. Instead revert to the original simple implementation. The start_removing* infrastructure is needed in VFS to abstract the filesystem locking. However filesystem code can still safely use the raw locking primitives without affacting other filesystems. This is part one of the revert. Reported-by: Артем Лабазов <123321artyom@gmail.com> Closes: https://lore.kernel.org/all/CAFbF8N7++zopZuEcsKRxBV_sgOGCbzCY0hOyMw1SiGAtuzGhyQ@mail.gmail.com/ Fixes: c9ba789dad15 ("VFS: introduce start_creating_noperm() and start_removing_noperm()") Cc: stable@vger.kernel.org # 6.19 Cc: NeilBrown Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dir.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index b658b6baf72fe..6b05846c9bed2 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1587,8 +1587,8 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid, { int err = -ENOTDIR; struct inode *parent; - struct dentry *dir = NULL; - struct dentry *entry = NULL; + struct dentry *dir; + struct dentry *entry; parent = fuse_ilookup(fc, parent_nodeid, NULL); if (!parent) @@ -1601,19 +1601,11 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid, dir = d_find_alias(parent); if (!dir) goto put_parent; - while (!entry) { - struct dentry *child = try_lookup_noperm(name, dir); - if (!child || IS_ERR(child)) - goto put_parent; - entry = start_removing_dentry(dir, child); - dput(child); - if (IS_ERR(entry)) - goto put_parent; - if (!d_same_name(entry, dir, name)) { - end_removing(entry); - entry = NULL; - } - } + + entry = start_removing_noperm(dir, name); + dput(dir); + if (IS_ERR(entry)) + goto put_parent; fuse_dir_changed(parent); if (!(flags & FUSE_EXPIRE_ONLY)) @@ -1651,7 +1643,6 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid, end_removing(entry); put_parent: - dput(dir); iput(parent); return err; } From 81b1045c401ccf4d876522d13868a82826dd82e5 Mon Sep 17 00:00:00 2001 From: Alberto Ruiz Date: Wed, 8 Apr 2026 17:23:40 +0200 Subject: [PATCH 0651/2758] fuse: fix device node leak in cuse_process_init_reply() commit 9fa4f7a53406430ee9982f2f636a15b338185122 upstream. If device_add() succeeds during CUSE initialization but a subsequent step (cdev_alloc() or cdev_add()) fails, the error path calls put_device() without first calling device_del(). This leaks the devtmpfs entry created by device_add(), leaving a stale /dev/ node that persists until reboot. Since the cuse_conn is never linked into cuse_conntbl on the failure path, cuse_channel_release() sees cc->dev == NULL and skips device_unregister(), so no other code path cleans up the node. This has several consequences: - The device name is permanently poisoned: any subsequent attempt to create a CUSE device with the same name hits the stale sysfs entry, device_add() fails, and the new device is aborted. - The collision manifests as ENODEV returned to userspace with no dmesg diagnostic, making it very difficult to debug. - The failure is self-perpetuating: once a name is leaked, all future attempts with that name fail identically. Fix this by introducing an err_dev label that calls device_del() to undo device_add() before falling through to err_unlock. The existing err_unlock path from a device_add() failure correctly skips device_del() since the device was never added. Testing instructions can be found at the lore link below. Link: https://lore.kernel.org/all/20260408-wip-cuse-leak-fix-v1-0-1c028d575e97@redhat.com/ Signed-off-by: Alberto Ruiz Fixes: 151060ac1314 ("CUSE: implement CUSE - Character device in Userspace") Cc: stable@vger.kernel.org Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/cuse.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c index 174333633471b..3d38828af41fc 100644 --- a/fs/fuse/cuse.c +++ b/fs/fuse/cuse.c @@ -391,7 +391,7 @@ static void cuse_process_init_reply(struct fuse_mount *fm, rc = -ENOMEM; cdev = cdev_alloc(); if (!cdev) - goto err_unlock; + goto err_dev; cdev->owner = THIS_MODULE; cdev->ops = &cuse_frontend_fops; @@ -417,6 +417,8 @@ static void cuse_process_init_reply(struct fuse_mount *fm, err_cdev: cdev_del(cdev); +err_dev: + device_del(dev); err_unlock: mutex_unlock(&cuse_lock); put_device(dev); From 779b7f1fcdeec6c4a700e99fb6b15fc013d1f00b Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 10 Jun 2026 13:02:53 +0200 Subject: [PATCH 0652/2758] fuse: do not use start_removing_noperm() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 6e1b235627bb1172d27c1b2ea7bf53e67dbced8d upstream. Revert the fuse part of commit c9ba789dad15 ("VFS: introduce start_creating_noperm() and start_removing_noperm()"). Commit c9ba789dad15 ("VFS: introduce start_creating_noperm() and start_removing_noperm()") caused a regression in FUSE_NOTIFY_INVAL_ENTRY, which failed to invalidate negative dentries. This manifests in the filesystem returning -ENOENT for operations on an existing file. Fixing it properly while still keeping the start_removing* infrastructure would add much additional complexity. Instead revert to the original simple implementation. The start_removing* infrastructure is needed in VFS to abstract the filesystem locking. However filesystem code can still safely use the raw locking primitives without affacting other filesystems. This is part two of the revert. Reported-by: Артем Лабазов <123321artyom@gmail.com> Closes: https://lore.kernel.org/all/CAFbF8N7++zopZuEcsKRxBV_sgOGCbzCY0hOyMw1SiGAtuzGhyQ@mail.gmail.com/ Fixes: c9ba789dad15 ("VFS: introduce start_creating_noperm() and start_removing_noperm()") Cc: stable@vger.kernel.org # 6.19 Cc: NeilBrown Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dir.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 6b05846c9bed2..97751bacb79ce 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1594,25 +1594,27 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid, if (!parent) return -ENOENT; + inode_lock_nested(parent, I_MUTEX_PARENT); if (!S_ISDIR(parent->i_mode)) - goto put_parent; + goto unlock; err = -ENOENT; dir = d_find_alias(parent); if (!dir) - goto put_parent; + goto unlock; - entry = start_removing_noperm(dir, name); + name->hash = full_name_hash(dir, name->name, name->len); + entry = d_lookup(dir, name); dput(dir); - if (IS_ERR(entry)) - goto put_parent; + if (!entry) + goto unlock; fuse_dir_changed(parent); if (!(flags & FUSE_EXPIRE_ONLY)) d_invalidate(entry); fuse_invalidate_entry_cache(entry); - if (child_nodeid != 0) { + if (child_nodeid != 0 && d_really_is_positive(entry)) { inode_lock(d_inode(entry)); if (get_node_id(d_inode(entry)) != child_nodeid) { err = -ENOENT; @@ -1640,9 +1642,10 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid, } else { err = 0; } + dput(entry); - end_removing(entry); - put_parent: + unlock: + inode_unlock(parent); iput(parent); return err; } From 65a1c2551f7e16085acbb54aedde1feaa559ba7a Mon Sep 17 00:00:00 2001 From: Joanne Koong Date: Mon, 18 May 2026 22:28:07 -0700 Subject: [PATCH 0653/2758] fuse: re-lock request before returning from fuse_ref_folio() commit b5befa80fdbe287a98480effed9564712924add5 upstream. fuse_ref_folio() unlocks the request but does not re-lock it before returning. fuse_chan_abort() can end the request and the async end callback (eg fuse_writepage_free()) can free the args while the subsequent copy chain logic after fuse_ref_folio() accesses them, leading to use-after-free issues. Fix this by locking the request in fuse_ref_folio() before returning. Fixes: c3021629a0d8 ("fuse: support splice() reading from fuse device") Cc: stable@vger.kernel.org Signed-off-by: Joanne Koong Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 970b8619ff042..bb5cfe1e7a8df 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1106,7 +1106,7 @@ static int fuse_ref_folio(struct fuse_copy_state *cs, struct folio *folio, cs->nr_segs++; cs->len = 0; - return 0; + return lock_request(cs->req); } /* From 1f3f4060e6560828b55f8246758d289ca05959ac Mon Sep 17 00:00:00 2001 From: Joanne Koong Date: Wed, 8 Apr 2026 10:25:10 -0700 Subject: [PATCH 0654/2758] fuse: fix io-uring background queue dispatch on request completion commit 31da059891bd3be9c6e59280b8e1777ead90db34 upstream. When a background request completes via the io_uring path, the background queue gets flushed to dispatch pending background requests, but this is done before the connection-level background counters (fc->num_background, fc->active_background) are properly accounted, which may reduce effective queue depth to one. The connection-level counters are decremented in fuse_request_end(), but flush_bg_queue() flushes the /dev/fuse path queue (fc->bg_queue), not the io_uring per-queue bg one, which means pending uring background requests on the queue are never dispatched in this path. Fix this by accounting the connection-level background counters first before flushing the queue's background queue. Since fuse_request_bg_finish() clears FR_BACKGROUND, fuse_request_end() will skip the background cleanup branch entirely, which avoids any double-decrements; it will call the wake_up(&req->waitq) branch but this is effectively a no-op as background requests have no waiters on req->waitq. Reviewed-by: Bernd Schubert Fixes: 857b0263f30e ("fuse: Allow to queue bg requests through io-uring") Cc: stable@vger.kernel.org Signed-off-by: Joanne Koong Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev.c | 41 ++++++++++++++++++++++++----------------- fs/fuse/dev_uring.c | 1 + fs/fuse/fuse_dev_i.h | 1 + 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index bb5cfe1e7a8df..17a735a710481 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -447,6 +447,29 @@ static void flush_bg_queue(struct fuse_conn *fc) } } +void fuse_request_bg_finish(struct fuse_conn *fc, struct fuse_req *req) +{ + lockdep_assert_held(&fc->bg_lock); + + clear_bit(FR_BACKGROUND, &req->flags); + if (fc->num_background == fc->max_background) { + fc->blocked = 0; + wake_up(&fc->blocked_waitq); + } else if (!fc->blocked) { + /* + * Wake up next waiter, if any. It's okay to use + * waitqueue_active(), as we've already synced up + * fc->blocked with waiters with the wake_up() call + * above. + */ + if (waitqueue_active(&fc->blocked_waitq)) + wake_up(&fc->blocked_waitq); + } + + fc->num_background--; + fc->active_background--; +} + /* * This function is called when a request is finished. Either a reply * has arrived or it was aborted (and not yet sent) or some error @@ -479,23 +502,7 @@ void fuse_request_end(struct fuse_req *req) WARN_ON(test_bit(FR_SENT, &req->flags)); if (test_bit(FR_BACKGROUND, &req->flags)) { spin_lock(&fc->bg_lock); - clear_bit(FR_BACKGROUND, &req->flags); - if (fc->num_background == fc->max_background) { - fc->blocked = 0; - wake_up(&fc->blocked_waitq); - } else if (!fc->blocked) { - /* - * Wake up next waiter, if any. It's okay to use - * waitqueue_active(), as we've already synced up - * fc->blocked with waiters with the wake_up() call - * above. - */ - if (waitqueue_active(&fc->blocked_waitq)) - wake_up(&fc->blocked_waitq); - } - - fc->num_background--; - fc->active_background--; + fuse_request_bg_finish(fc, req); flush_bg_queue(fc); spin_unlock(&fc->bg_lock); } else { diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c index 7b9822e8837bc..ae916733f18a5 100644 --- a/fs/fuse/dev_uring.c +++ b/fs/fuse/dev_uring.c @@ -90,6 +90,7 @@ static void fuse_uring_req_end(struct fuse_ring_ent *ent, struct fuse_req *req, if (test_bit(FR_BACKGROUND, &req->flags)) { queue->active_background--; spin_lock(&fc->bg_lock); + fuse_request_bg_finish(fc, req); fuse_uring_flush_bg(queue); spin_unlock(&fc->bg_lock); } diff --git a/fs/fuse/fuse_dev_i.h b/fs/fuse/fuse_dev_i.h index 910f883cd090f..1853940fe80a5 100644 --- a/fs/fuse/fuse_dev_i.h +++ b/fs/fuse/fuse_dev_i.h @@ -77,6 +77,7 @@ unsigned int fuse_req_hash(u64 unique); struct fuse_req *fuse_request_find(struct fuse_pqueue *fpq, u64 unique); void fuse_dev_end_requests(struct list_head *head); +void fuse_request_bg_finish(struct fuse_conn *fc, struct fuse_req *req); void fuse_copy_init(struct fuse_copy_state *cs, bool write, struct iov_iter *iter); From 1ec674d3d0ed83ffaa9363f4622f0bef7190d130 Mon Sep 17 00:00:00 2001 From: Joanne Koong Date: Wed, 22 Apr 2026 09:31:23 -0700 Subject: [PATCH 0655/2758] fuse: don't block in fuse_get_dev() for non-sync_init case commit 9f6f44aa5a58aaae0838126dc09460c3e1f56ccb upstream. Commit a8dd5f1b73bc ("fuse: create fuse_dev on /dev/fuse open instead of mount") changed behavior so that fuse_get_dev() now unconditionally blocks waiting for a connection, even in the case where sync_init was not set. Previously, non-sync_init opens returned -EPERM immediately. Restore the previous behavior of returning -EPERM. Fixes: a8dd5f1b73bc ("fuse: create fuse_dev on /dev/fuse open instead of mount") Reported-by: Mark Brown Closes: https://lore.kernel.org/all/3c9f8396-41f4-4c88-b883-34bede72b427@sirena.org.uk/ Cc: Signed-off-by: Joanne Koong Tested-by: Mark Brown Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 17a735a710481..80b1cd4e11985 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1560,9 +1560,15 @@ struct fuse_dev *fuse_get_dev(struct file *file) struct fuse_dev *fud = fuse_file_to_fud(file); int err; - err = wait_event_interruptible(fuse_dev_waitq, fuse_dev_fc_get(fud) != NULL); - if (err) - return ERR_PTR(err); + if (unlikely(!fuse_dev_fc_get(fud))) { + /* only block waiting for mount if sync init was requested */ + if (!fud->sync_init) + return ERR_PTR(-EPERM); + + err = wait_event_interruptible(fuse_dev_waitq, fuse_dev_fc_get(fud) != NULL); + if (err) + return ERR_PTR(err); + } return fud; } From 893479015cb6442fd389d3b553ab3036c9541715 Mon Sep 17 00:00:00 2001 From: Ji'an Zhou Date: Tue, 9 Jun 2026 09:58:51 +0000 Subject: [PATCH 0656/2758] fuse: clear intr_entry in fuse_resend and fuse_remove_pending_req commit f8fce75fedf73ac72aa09163deb8f4291fdcaad2 upstream. When fuse_resend() moves a request from fpq->processing back to fiq->pending, it sets FR_PENDING and clears FR_SENT but does not remove the requests intr_entry from fiq->interrupts. If the request had FR_INTERRUPTED set from a prior signal, intr_entry remains dangling on fiq->interrupts. When the requesting task then receives a fatal signal, fuse_remove_pending_req() sees FR_PENDING=1, removes the request from fiq->pending and frees it via the refcount path, also without cleaning intr_entry. The stale intr_entry causes use-after-free when fuse_read_interrupt() iterates fiq->interrupts: - list_del_init(&req->intr_entry) -> UAF write on freed slab - req->in.h.unique -> UAF read, data leaked to userspace Remove intr_entry from fiq->interrupts in fuse_resend() for interrupted requests before they are placed back on fiq->pending. Add a WARN_ON if the intr_entry is not empty on request destruction. Fixes: 760eac73f9f6 ("fuse: Introduce a new notification type for resend pending requests") Cc: stable@vger.kernel.org # 6.9 Signed-off-by: Ji'an Zhou Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 80b1cd4e11985..5aea3f4d30306 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -148,6 +148,7 @@ static struct fuse_req *fuse_request_alloc(struct fuse_mount *fm, gfp_t flags) static void fuse_request_free(struct fuse_req *req) { + WARN_ON(!list_empty(&req->intr_entry)); kmem_cache_free(fuse_req_cachep, req); } @@ -2041,6 +2042,14 @@ static void fuse_resend(struct fuse_conn *fc) fuse_dev_end_requests(&to_queue); return; } + /* + * Remove interrupt entries for resent requests to prevent stale + * intr_entry on fiq->interrupts after the request is re-queued. + */ + list_for_each_entry(req, &to_queue, list) { + if (test_bit(FR_INTERRUPTED, &req->flags)) + list_del_init(&req->intr_entry); + } /* iq and pq requests are both oldest to newest */ list_splice(&to_queue, &fiq->pending); fuse_dev_wake_and_unlock(fiq); From fe604c08d874648a69187f6380e5c7858627dc04 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Fri, 5 Jun 2026 12:27:06 -0700 Subject: [PATCH 0657/2758] fuse-uring: fix EFAULT clobber in fuse_uring_commit commit 3a0a8bc51a13951c5141262bf770eeea3e0b6228 upstream. copy_from_user() returns the number of bytes not copied as an unsigned residual on failure (1..sizeof(struct fuse_out_header)). fuse_uring_commit stores that residual in ssize_t err, sets req->out.h.error to -EFAULT, then jumps to out: with err still holding the positive residual. err = copy_from_user(&req->out.h, &ent->headers->in_out, sizeof(req->out.h)); if (err) { req->out.h.error = -EFAULT; goto out; /* err is the positive residual */ } ... out: fuse_uring_req_end(ent, req, err); fuse_uring_req_end() then runs if (error) req->out.h.error = error; which overwrites the just-assigned -EFAULT with the positive residual. FUSE callers such as fuse_simple_request() test err < 0 to detect failure, so the positive value is interpreted as success and the caller proceeds with an uninitialised or partial req->out.args. Fix by assigning err = -EFAULT in the failure branch before jumping to out, so fuse_uring_req_end() receives a negative errno and sets req->out.h.error to -EFAULT. Fixes: c090c8abae4b ("fuse: Add io-uring sqe commit and fetch support") Cc: stable@vger.kernel.org Reviewed-by: Joanne Koong Assisted-by: kres (claude-opus-4-7) Signed-off-by: Chris Mason Reviewed-by: Bernd Schubert Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev_uring.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c index ae916733f18a5..288c947fd2cfa 100644 --- a/fs/fuse/dev_uring.c +++ b/fs/fuse/dev_uring.c @@ -814,14 +814,11 @@ static void fuse_uring_commit(struct fuse_ring_ent *ent, struct fuse_req *req, { struct fuse_ring *ring = ent->queue->ring; struct fuse_conn *fc = ring->fc; - ssize_t err = 0; + ssize_t err = -EFAULT; - err = copy_from_user(&req->out.h, &ent->headers->in_out, - sizeof(req->out.h)); - if (err) { - req->out.h.error = -EFAULT; + if (copy_from_user(&req->out.h, &ent->headers->in_out, + sizeof(req->out.h))) goto out; - } err = fuse_uring_out_header_has_err(&req->out.h, req, fc); if (err) { From d01a09b442cb786cd44ccc7c84d57e2856d6737c Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Fri, 5 Jun 2026 12:27:07 -0700 Subject: [PATCH 0658/2758] fuse-uring: fix data races on ring->ready commit 46725a0056c884cf58a6897f222892807327d82d upstream. On weakly-ordered architectures, the store to fiq->ops can be reordered past the store to ring->ready, allowing a CPU that sees ring->ready == true via fuse_uring_ready() to dispatch requests through a stale fiq->ops pointer. Upgrade the store to smp_store_release() and the load in fuse_uring_ready() to smp_load_acquire() so that the preceding WRITE_ONCE(fiq->ops, ...) is visible to any CPU that observes ring->ready == true. Additionally, fuse_uring_do_register() publishes ring->ready with WRITE_ONCE() but the fast-path check reads it with a plain load. This is a marked-vs-unmarked access that KCSAN will flag. Wrap it in READ_ONCE() to mark it without adding unnecessary ordering. Also wrap the fc->ring load in fuse_uring_ready() in READ_ONCE() to prevent the compiler from reloading it between the NULL check and the dereference. Fixes: c2c9af9a0b13 ("fuse: Allow to queue fg requests through io-uring") Cc: stable@vger.kernel.org Reviewed-by: Joanne Koong Assisted-by: kres (claude-opus-4-7) Signed-off-by: Chris Mason Reviewed-by: Bernd Schubert Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev_uring.c | 4 ++-- fs/fuse/dev_uring_i.h | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c index 288c947fd2cfa..5981d3369cb9d 100644 --- a/fs/fuse/dev_uring.c +++ b/fs/fuse/dev_uring.c @@ -989,12 +989,12 @@ static void fuse_uring_do_register(struct fuse_ring_ent *ent, fuse_uring_ent_avail(ent, queue); spin_unlock(&queue->lock); - if (!ring->ready) { + if (!READ_ONCE(ring->ready)) { bool ready = is_ring_ready(ring, queue->qid); if (ready) { WRITE_ONCE(fiq->ops, &fuse_io_uring_ops); - WRITE_ONCE(ring->ready, true); + smp_store_release(&ring->ready, true); wake_up_all(&fc->blocked_waitq); } } diff --git a/fs/fuse/dev_uring_i.h b/fs/fuse/dev_uring_i.h index 51a563922ce14..531233e03e546 100644 --- a/fs/fuse/dev_uring_i.h +++ b/fs/fuse/dev_uring_i.h @@ -169,7 +169,9 @@ static inline void fuse_uring_wait_stopped_queues(struct fuse_conn *fc) static inline bool fuse_uring_ready(struct fuse_conn *fc) { - return fc->ring && fc->ring->ready; + struct fuse_ring *ring = READ_ONCE(fc->ring); + + return ring && smp_load_acquire(&ring->ready); } #else /* CONFIG_FUSE_IO_URING */ From e8afc85acdf329361b2d8df2ad9b52364686235f Mon Sep 17 00:00:00 2001 From: Joanne Koong Date: Mon, 8 Jun 2026 12:21:49 -0700 Subject: [PATCH 0659/2758] fuse-uring: fix moving cancelled entry to ent_in_userspace list commit 198f45eeb9f78b2a2d6d8be95e4e43468eb2c6bc upstream. fuse_uring_cancel() moves entries that are available (these have no reqs attached) to the ent_in_userspace list. ent_list_request_expired() checks the first entry on ent_in_userspace and dereferences ent->fuse_req unconditionally, which will crash on a cancelled entry that was moved to this list. Fix this by freeing the entry and dropping queue_refs directly in fuse_uring_cancel(). This is safe because cancel is the cancel handler itself - after io_uring_cmd_done(), no more cancels will be dispatched for this command, and teardown serializes with cancel via queue->lock. Since cancel now decrements queue_refs, fuse_uring_abort() must no longer gate fuse_uring_abort_end_requests() on queue_refs > 0, as cancelled entries may have already dropped queue_refs while requests are still queued. Remove the gate so abort always flushes requests and stops queues. Reported-by: Heechan Kang Tested-by: Heechan Kang Reviewed-by: Bernd Schubert Fixes: 4fea593e625c ("fuse: optimize over-io-uring request expiration check") Cc: stable@vger.kernel.org Suggested-by: Jian Huang Li Suggested-by: Horst Birthelmer Signed-off-by: Joanne Koong Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev_uring.c | 6 ++++-- fs/fuse/dev_uring_i.h | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c index 5981d3369cb9d..05c3c07bc487f 100644 --- a/fs/fuse/dev_uring.c +++ b/fs/fuse/dev_uring.c @@ -508,8 +508,7 @@ static void fuse_uring_cancel(struct io_uring_cmd *cmd, queue = ent->queue; spin_lock(&queue->lock); if (ent->state == FRRS_AVAILABLE) { - ent->state = FRRS_USERSPACE; - list_move_tail(&ent->list, &queue->ent_in_userspace); + list_del_init(&ent->list); need_cmd_done = true; ent->cmd = NULL; } @@ -518,6 +517,9 @@ static void fuse_uring_cancel(struct io_uring_cmd *cmd, if (need_cmd_done) { /* no queue lock to avoid lock order issues */ io_uring_cmd_done(cmd, -ENOTCONN, issue_flags); + kfree(ent); + if (atomic_dec_and_test(&queue->ring->queue_refs)) + wake_up_all(&queue->ring->stop_waitq); } } diff --git a/fs/fuse/dev_uring_i.h b/fs/fuse/dev_uring_i.h index 531233e03e546..054ad4fdd7491 100644 --- a/fs/fuse/dev_uring_i.h +++ b/fs/fuse/dev_uring_i.h @@ -152,10 +152,10 @@ static inline void fuse_uring_abort(struct fuse_conn *fc) if (ring == NULL) return; - if (atomic_read(&ring->queue_refs) > 0) { - fuse_uring_abort_end_requests(ring); + fuse_uring_abort_end_requests(ring); + + if (atomic_read(&ring->queue_refs) > 0) fuse_uring_stop_queues(ring); - } } static inline void fuse_uring_wait_stopped_queues(struct fuse_conn *fc) From 4f45f276d5b4412eade6f74f2e37f3adba0473ed Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Mon, 8 Jun 2026 17:28:55 -0700 Subject: [PATCH 0660/2758] fuse-uring: end fuse_req on io-uring cancel task work commit bea4fe98204b6ce7eb8e29f7bf867dd7619b3ddd upstream. When io_uring delivers task work with tw.cancel set (PF_EXITING, PF_KTHREAD fallback, or percpu_ref_is_dying on the ring context), fuse_uring_send_in_task() takes the cancel branch, assigns -ECANCELED, and falls through to fuse_uring_send(). That path only flips the entry to FRRS_USERSPACE and completes the io_uring cmd; it never discharges the ring entry's owning reference to the fuse_req that fuse_uring_add_req_to_ring_ent() handed it at dispatch time. fuse_uring_send_in_task() tw.cancel == true err = -ECANCELED fuse_uring_send(ent, cmd, err, issue_flags) ent->state = FRRS_USERSPACE list_move(&ent->list, &queue->ent_in_userspace) ent->cmd = NULL io_uring_cmd_done(-ECANCELED) /* ent->fuse_req still set, req still hashed */ The fuse_req stays linked on fpq->processing[hash] and fuse_request_end() is never invoked. The originating syscall thread blocks in D-state in request_wait_answer() until fuse_abort_conn() runs, which can be the entire connection lifetime. For FR_BACKGROUND requests fc->num_background is never decremented either, so repeated cancels inflate the counter until max_background is hit and all later background ops stall. tw.cancel does not imply a connection abort (e.g. a single io_uring worker thread exits while the fuse connection stays up), so this cannot be left for fuse_abort_conn() to clean up. Ending the req but still routing the entry through fuse_uring_send() is not enough: that leaves a req-less entry on ent_in_userspace, and ent_list_request_expired() dereferences ent->fuse_req unconditionally on the head of that list, which would then NULL-deref. Fix the cancel branch to release the entry directly. Remove it from the queue, complete the io_uring cmd, end the fuse_req, free the entry, and drop its queue_refs (waking the teardown waiter if it was the last). Fixes: c2c9af9a0b13 ("fuse: Allow to queue fg requests through io-uring") Cc: stable@vger.kernel.org Reviewed-by: Joanne Koong Assisted-by: kres (claude-opus-4-7) Signed-off-by: Chris Mason Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev_uring.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c index 05c3c07bc487f..4ecb1d0ae59ff 100644 --- a/fs/fuse/dev_uring.c +++ b/fs/fuse/dev_uring.c @@ -1225,11 +1225,21 @@ static void fuse_uring_send_in_task(struct io_tw_req tw_req, io_tw_token_t tw) fuse_uring_next_fuse_req(ent, queue, issue_flags); return; } + fuse_uring_send(ent, cmd, err, issue_flags); } else { err = -ECANCELED; - } - fuse_uring_send(ent, cmd, err, issue_flags); + spin_lock(&queue->lock); + list_del_init(&ent->list); + spin_unlock(&queue->lock); + + io_uring_cmd_done(cmd, err, issue_flags); + + fuse_uring_req_end(ent, ent->fuse_req, err); + kfree(ent); + if (atomic_dec_and_test(&queue->ring->queue_refs)) + wake_up_all(&queue->ring->stop_waitq); + } } static struct fuse_ring_queue *fuse_uring_task_to_queue(struct fuse_ring *ring) From 95d7f50aff2a5f71557263ff25b97b2951f32bf8 Mon Sep 17 00:00:00 2001 From: Bernd Schubert Date: Mon, 8 Jun 2026 23:03:43 +0200 Subject: [PATCH 0661/2758] fuse-uring: Avoid use-after-free in fuse_uring_async_stop_queues commit d351da75066955144515cb2f9aa959f24a04287a upstream. fuse_uring_async_stop_queues() might run when the last reference on ring->queue_refs was already dropped. In order to avoid an early destruction a reference on struct fuse_conn is now taken before starting fuse_uring_async_stop_queues() and that reference is only released when that delayed work queue terminates. Fixes: 4a9bfb9b6850 ("fuse: {io-uring} Handle teardown of ring entries") Cc: stable@kernel.org # 6.14 Reported-by: Berkant Koc Signed-off-by: Bernd Schubert Reviewed-by: Joanne Koong Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev_uring.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c index 4ecb1d0ae59ff..63b4de66295cd 100644 --- a/fs/fuse/dev_uring.c +++ b/fs/fuse/dev_uring.c @@ -467,6 +467,7 @@ static void fuse_uring_async_stop_queues(struct work_struct *work) FUSE_URING_TEARDOWN_INTERVAL); } else { wake_up_all(&ring->stop_waitq); + fuse_conn_put(ring->chan->conn); } } @@ -478,6 +479,7 @@ void fuse_uring_stop_queues(struct fuse_ring *ring) fuse_uring_teardown_all_queues(ring); if (atomic_read(&ring->queue_refs) > 0) { + fuse_conn_get(ring->chan->conn); ring->teardown_time = jiffies; INIT_DELAYED_WORK(&ring->async_teardown_work, fuse_uring_async_stop_queues); From 4021a3a79eee551d95fe1e1e7c1b195d34ba8c08 Mon Sep 17 00:00:00 2001 From: Bernd Schubert Date: Mon, 8 Jun 2026 23:03:44 +0200 Subject: [PATCH 0662/2758] fuse-uring: Avoid queue->stopped races and set/read that value under lock commit b70a3aca16934c196f92abb17b01c1647b9bb63c upstream. There are several readers of queue->stopped that check the value under lock, but fuse_uring_commit_fetch() did not and actually the value was not set under the lock in fuse_uring_abort_end_requests() either. Especially in fuse_uring_commit_fetch it is important to check under a lock, because due to races 'struct fuse_req' might be freed with fuse_request_end, but another thread/cpu might already do teardown work. Cc: stable@kernel.org # 6.14 Fixes: 4a9bfb9b6850fec ("fuse: {io-uring} Handle teardown of ring entries") Reported-by: Berkant Koc Reported-by: xlabai Signed-off-by: Bernd Schubert Reviewed-by: Joanne Koong Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev_uring.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c index 63b4de66295cd..dbfc894a4743a 100644 --- a/fs/fuse/dev_uring.c +++ b/fs/fuse/dev_uring.c @@ -131,10 +131,9 @@ void fuse_uring_abort_end_requests(struct fuse_ring *ring) if (!queue) continue; - queue->stopped = true; - WARN_ON_ONCE(ring->fc->max_background != UINT_MAX); spin_lock(&queue->lock); + queue->stopped = true; spin_lock(&fc->bg_lock); fuse_uring_flush_bg(queue); spin_unlock(&fc->bg_lock); @@ -467,7 +466,7 @@ static void fuse_uring_async_stop_queues(struct work_struct *work) FUSE_URING_TEARDOWN_INTERVAL); } else { wake_up_all(&ring->stop_waitq); - fuse_conn_put(ring->chan->conn); + fuse_conn_put(ring->fc); } } @@ -479,7 +478,7 @@ void fuse_uring_stop_queues(struct fuse_ring *ring) fuse_uring_teardown_all_queues(ring); if (atomic_read(&ring->queue_refs) > 0) { - fuse_conn_get(ring->chan->conn); + fuse_conn_get(ring->fc); ring->teardown_time = jiffies; INIT_DELAYED_WORK(&ring->async_teardown_work, fuse_uring_async_stop_queues); @@ -900,10 +899,15 @@ static int fuse_uring_commit_fetch(struct io_uring_cmd *cmd, int issue_flags, return err; fpq = &queue->fpq; - if (!READ_ONCE(fc->connected) || READ_ONCE(queue->stopped)) + if (!READ_ONCE(fc->connected)) return err; spin_lock(&queue->lock); + if (unlikely(queue->stopped)) { + spin_unlock(&queue->lock); + return err; + } + /* Find a request based on the unique ID of the fuse request * This should get revised, as it needs a hash calculation and list * search. And full struct fuse_pqueue is needed (memory overhead). From a635f427d57e2012102ae4886b48d8955c59fb86 Mon Sep 17 00:00:00 2001 From: Bernd Schubert Date: Mon, 8 Jun 2026 23:03:45 +0200 Subject: [PATCH 0663/2758] fuse-uring: make a fuse_req on SQE commit only findable after memcpy commit 1efd3d474fc0ba74dfd984249bca78807d739812 upstream. Bad userspace might try to trick us and send commit SQEs request unique / commit-id of requests that are not even send to fuse-server (io_uring_cmd_done() not called) yet. fuse_uring_commit_fetch() ends the fuse request when the ring entry has a wrong state, but that could have caused a use-after-free with the memcpy operations in fuse_uring_send_in_task(). In order to avoid such races the call of fuse_uring_add_to_pq() is moved after the copy operations and just before completing the io-uring request - malicious userspace cannot find the request anymore until all prepration work in fuse-client/kernel is completed. This also moves fuse_uring_add_to_pq() a bit up in the code to avoid a forward declaration. Also not with a preparation commit, to make it easier to back port to older kernels. Reported-by: xlabai Reported-by: Berkant Koc Fixes: c090c8abae4b6b ("fuse: Add io-uring sqe commit and fetch support") Cc: stable@kernel.org # 6.14 Signed-off-by: Bernd Schubert Reviewed-by: Joanne Koong Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev_uring.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c index dbfc894a4743a..695293bdbac0f 100644 --- a/fs/fuse/dev_uring.c +++ b/fs/fuse/dev_uring.c @@ -714,6 +714,19 @@ static int fuse_uring_prepare_send(struct fuse_ring_ent *ent, return err; } +/* Used to find the request on SQE commit */ +static void fuse_uring_add_to_pq(struct fuse_ring_ent *ent) +{ + struct fuse_ring_queue *queue = ent->queue; + struct fuse_pqueue *fpq = &queue->fpq; + unsigned int hash; + struct fuse_req *req = ent->fuse_req; + + req->ring_entry = ent; + hash = fuse_req_hash(req->in.h.unique); + list_move_tail(&req->list, &fpq->processing[hash]); +} + /* * Write data to the ring buffer and send the request to userspace, * userspace will read it @@ -736,6 +749,7 @@ static int fuse_uring_send_next_to_ring(struct fuse_ring_ent *ent, ent->cmd = NULL; ent->state = FRRS_USERSPACE; list_move_tail(&ent->list, &queue->ent_in_userspace); + fuse_uring_add_to_pq(ent); spin_unlock(&queue->lock); io_uring_cmd_done(cmd, 0, issue_flags); @@ -753,19 +767,6 @@ static void fuse_uring_ent_avail(struct fuse_ring_ent *ent, ent->state = FRRS_AVAILABLE; } -/* Used to find the request on SQE commit */ -static void fuse_uring_add_to_pq(struct fuse_ring_ent *ent, - struct fuse_req *req) -{ - struct fuse_ring_queue *queue = ent->queue; - struct fuse_pqueue *fpq = &queue->fpq; - unsigned int hash; - - req->ring_entry = ent; - hash = fuse_req_hash(req->in.h.unique); - list_move_tail(&req->list, &fpq->processing[hash]); -} - /* * Assign a fuse queue entry to the given entry */ @@ -783,10 +784,13 @@ static void fuse_uring_add_req_to_ring_ent(struct fuse_ring_ent *ent, } clear_bit(FR_PENDING, &req->flags); + + /* Until fuse_uring_add_to_pq() the req is not attached to any list */ + list_del_init(&req->list); + ent->fuse_req = req; ent->state = FRRS_FUSE_REQ; list_move_tail(&ent->list, &queue->ent_w_req_queue); - fuse_uring_add_to_pq(ent, req); } /* Fetch the next fuse request if available */ @@ -1207,6 +1211,7 @@ static void fuse_uring_send(struct fuse_ring_ent *ent, struct io_uring_cmd *cmd, ent->state = FRRS_USERSPACE; list_move_tail(&ent->list, &queue->ent_in_userspace); ent->cmd = NULL; + fuse_uring_add_to_pq(ent); spin_unlock(&queue->lock); io_uring_cmd_done(cmd, ret, issue_flags); From 0a7f33010c0e4cd92937e088a54350381fd0fbf2 Mon Sep 17 00:00:00 2001 From: Joanne Koong Date: Tue, 9 Jun 2026 14:36:58 -0700 Subject: [PATCH 0664/2758] fuse-uring: remove request-less entries from ent_w_req_queue to fix NULL deref commit 1c57a69be962d459c5e705f5cb4355b841b3461c upstream. If a copy into the userspace ring buffer fails, a request will be terminated and fuse_uring_req_end() will set ent->fuse_req to NULL but it will leave the entry on ent_w_req_queue in FRRS_FUSE_REQ state. This can lead to a NULL deref if the request expiration logic scans ent_w_req_queue in the window before the entry is moved off it. Fix this by taking the entry off ent_w_req_queue and changing its state from FRRS_FUSE_REQ to FRRS_INVALID before terminating the request. Fixes: 4fea593e625c ("fuse: optimize over-io-uring request expiration check") Cc: stable@kernel.org Signed-off-by: Joanne Koong Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev_uring.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c index 695293bdbac0f..4fde6336cf5ce 100644 --- a/fs/fuse/dev_uring.c +++ b/fs/fuse/dev_uring.c @@ -706,10 +706,20 @@ static int fuse_uring_prepare_send(struct fuse_ring_ent *ent, int err; err = fuse_uring_copy_to_ring(ent, req); - if (!err) + if (!err) { set_bit(FR_SENT, &req->flags); - else + } else { + /* + * Copying the request failed. Remove the entry from the + * ent_w_req_queue list and terminate the request + */ + spin_lock(&ent->queue->lock); + list_del_init(&ent->list); + ent->state = FRRS_INVALID; + spin_unlock(&ent->queue->lock); + fuse_uring_req_end(ent, req, err); + } return err; } From ae8f855a28e082b079ad6e3aedef2371f367bde0 Mon Sep 17 00:00:00 2001 From: Rong Zhang Date: Wed, 27 May 2026 01:49:23 +0800 Subject: [PATCH 0665/2758] ALSA: doc: usb-audio: Add doc for QUIRK_FLAG_IFB_SILENCE_ON_EMPTY commit 06363f96e3d6b54ff7b5d2ce85cab95bd5e874b0 upstream. QUIRK_FLAG_IFB_SILENCE_ON_EMPTY was introduced into usb-audio before without appropriate documentation, so add it. Fixes: a23812004228 ("ALSA: usb-audio: add IFB_SILENCE_ON_EMPTY quirk for Behringer Flow 8") Signed-off-by: Rong Zhang Link: https://patch.msgid.link/20260527-uac-quirk-get-cur-vol-v1-1-e9362b712e5e@rong.moe Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- Documentation/sound/alsa-configuration.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/sound/alsa-configuration.rst b/Documentation/sound/alsa-configuration.rst index f75f087639415..4b30cd63c5a5a 100644 --- a/Documentation/sound/alsa-configuration.rst +++ b/Documentation/sound/alsa-configuration.rst @@ -2383,6 +2383,12 @@ quirk_flags ``V(x) = k * x``; ``dB(x) = 20 * log10(x)``. Overrides bit 24 * bit 28: ``mixer_capture_linear_vol`` Similar to bit 27 but for capture streams. Overrides bit 25 + * bit 29: ``ifb_silence_on_empty`` + In implicit feedback mode, when an entire capture URB returns with + all iso_frame_desc[i].status != 0 (bytes==0), do not silently return + from snd_usb_handle_sync_urb. Instead fall through and enqueue a + packet_info containing only size-0 packets, so the OUT ring keeps + moving (emits silence). Needed by Behringer Flow 8 (1397:050c). This module supports multiple devices, autoprobe and hotplugging. From 07e454687b132454d4bcfd8bb7ca25172221506f Mon Sep 17 00:00:00 2001 From: Mikhail Gavrilov Date: Tue, 16 Jun 2026 12:09:14 +0500 Subject: [PATCH 0666/2758] timekeeping: Register default clocksource before taking tk_core.lock commit 8fa30821180a9a19e78e9f4df1c0ba710252801e upstream. Commit f24df84cbe05 ("time/jiffies: Register jiffies clocksource before usage") moved the jiffies clocksource registration into clocksource_default_clock(), so that it is registered lazily on the first call. __clocksource_register() acquires clocksource_mutex, but the first caller is timekeeping_init(), which invokes clocksource_default_clock() while holding tk_core.lock, a raw spinlock. Acquiring a sleeping mutex while holding a raw spinlock is invalid. The default clocksource only has to be registered before tk_setup_internals() consumes its mult/shift/maxadj. Neither clocksource_default_clock(), the ->enable() callback, nor the registration itself need tk_core.lock, so fetch and enable the clock before acquiring the lock. This preserves the "register before usage" ordering while keeping clocksource_mutex out of the raw spinlock section. clocksource_default_clock() has a second caller, clocksource_done_booting(), which invokes it with clocksource_mutex already held. That path avoids a recursive lock because timekeeping_init() has already run and set cs_jiffies_registered, so the registration is skipped there. This change does not alter that; it only fixes the invalid wait context in timekeeping_init(). Fixes: f24df84cbe05 ("time/jiffies: Register jiffies clocksource before usage") Signed-off-by: Mikhail Gavrilov Signed-off-by: Thomas Gleixner Reported-by: Breno Leitao Reported-by: Oleg Nesterov Reviewed-by: Breno Leitao Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260616070914.65818-1-mikhail.v.gavrilov@gmail.com Signed-off-by: Greg Kroah-Hartman --- kernel/time/timekeeping.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index c493a40103055..eea51450e4181 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -1980,13 +1980,14 @@ void __init timekeeping_init(void) */ wall_to_mono = timespec64_sub(boot_offset, wall_time); + clock = clocksource_default_clock(); + if (clock->enable) + clock->enable(clock); + guard(raw_spinlock_irqsave)(&tk_core.lock); ntp_init(); - clock = clocksource_default_clock(); - if (clock->enable) - clock->enable(clock); tk_setup_internals(tks, clock); tk_set_xtime(tks, &wall_time); From 25e2ac320c3da984015841747cca164fdfed366c Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 12 Jun 2026 10:21:09 -0400 Subject: [PATCH 0667/2758] Bluetooth: 6lowpan: Fix using chan->conn as indication to no remote netdev commit d38eaf611839b85ade3dd3db309dbc8aaaaf0095 upstream. b66774b48dd9 ("Bluetooth: L2CAP: Fix UAF in channel timeout by holding conn ref") don't reset the chan->conn to NULL anymore making the bt# netdev not be remove once the last l2cap_chan_del is removed. Instead of restoring the original behavior this remove the logic of keeping the interface after the last channel is removed because it never worked as intended and the l2cap_chan_del always detach its l2cap_conn which results in always removing the channel anyway. Fixes: b66774b48dd9 ("Bluetooth: L2CAP: Fix UAF in channel timeout by holding conn ref") Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/6lowpan.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 960a19b3e26da..4dbbc44a950d5 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -777,20 +777,10 @@ static void chan_close_cb(struct l2cap_chan *chan) struct lowpan_btle_dev *dev = NULL; struct lowpan_peer *peer; int err = -ENOENT; - bool last = false, remove = true; + bool last = false; BT_DBG("chan %p conn %p", chan, chan->conn); - if (chan->conn && chan->conn->hcon) { - if (!is_bt_6lowpan(chan->conn->hcon)) - return; - - /* If conn is set, then the netdev is also there and we should - * not remove it. - */ - remove = false; - } - spin_lock(&devices_lock); list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) { @@ -817,10 +807,8 @@ static void chan_close_cb(struct l2cap_chan *chan) ifdown(dev->netdev); - if (remove) { - INIT_WORK(&entry->delete_netdev, delete_netdev); - schedule_work(&entry->delete_netdev); - } + INIT_WORK(&entry->delete_netdev, delete_netdev); + schedule_work(&entry->delete_netdev); } else { spin_unlock(&devices_lock); } From 57cba95f0e97c6f6e45e6731da30aff091bd7460 Mon Sep 17 00:00:00 2001 From: Shoichiro Miyamoto Date: Sat, 11 Jul 2026 22:33:26 +0900 Subject: [PATCH 0668/2758] smb: client: reject overlapping data areas in SMB2 responses commit 8986c932905ea508d66da421eb2eb6e676ace1fe upstream. Commit 53b7c271f06b ("smb: client: restrict implied bcc[0] exemption to responses without data area") restricted the implied bcc[0] length exception to responses without a data area. However, the overlap handling in __smb2_calc_size() clears data_length, which can make an invalid response appear to have no data area and so qualify for the exception. Track data area overlap separately and reject such responses before applying the length compatibility exceptions. Fixes: 53b7c271f06b ("smb: client: restrict implied bcc[0] exemption to responses without data area") Cc: stable@vger.kernel.org Signed-off-by: Shoichiro Miyamoto Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2misc.c | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/fs/smb/client/smb2misc.c b/fs/smb/client/smb2misc.c index 6270b33147d25..9068175e57cd0 100644 --- a/fs/smb/client/smb2misc.c +++ b/fs/smb/client/smb2misc.c @@ -19,7 +19,8 @@ #include "nterr.h" #include "cached_dir.h" -static unsigned int __smb2_calc_size(void *buf, bool *have_data); +static unsigned int __smb2_calc_size(void *buf, bool *have_data, + bool *data_area_overlap); static int check_smb2_hdr(struct smb2_hdr *shdr, __u64 mid) @@ -148,6 +149,7 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len, __u32 calc_len; /* calculated length */ __u64 mid; bool have_data; + bool data_area_overlap; /* If server is a channel, select the primary channel */ pserver = SERVER_IS_CHAN(server) ? server->primary_server : server; @@ -232,7 +234,12 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len, } have_data = false; - calc_len = __smb2_calc_size(buf, &have_data); + data_area_overlap = false; + calc_len = __smb2_calc_size(buf, &have_data, &data_area_overlap); + + /* Reject responses whose data area overlaps the fixed area. */ + if (data_area_overlap) + return 1; /* For SMB2_IOCTL, OutputOffset and OutputLength are optional, so might * be 0, and not a real miscalculation */ @@ -416,14 +423,15 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *shdr) } /* - * Calculate the size of the SMB message based on the fixed header - * portion, the number of word parameters and the data portion of the message. - * If have_data is non-NULL, it is set to true when a non-empty data area was - * found (data_length > 0), allowing callers to distinguish the implied bcc[0] - * case (no data area) from an overreported data length. + * Calculate the size of the SMB message based on the fixed header, fixed + * parameter area, and variable data area. + * + * If have_data is not NULL, it is set when a non-empty data area is found. + * If data_area_overlap is not NULL, it is set when the data area overlaps + * the fixed area. */ static unsigned int -__smb2_calc_size(void *buf, bool *have_data) +__smb2_calc_size(void *buf, bool *have_data, bool *data_area_overlap) { struct smb2_pdu *pdu = buf; struct smb2_hdr *shdr = &pdu->hdr; @@ -432,6 +440,11 @@ __smb2_calc_size(void *buf, bool *have_data) /* Structure Size has already been checked to make sure it is 64 */ int len = le16_to_cpu(shdr->StructureSize); + if (have_data) + *have_data = false; + if (data_area_overlap) + *data_area_overlap = false; + /* * StructureSize2, ie length of fixed parameter area has already * been checked to make sure it is the correct length. @@ -454,7 +467,10 @@ __smb2_calc_size(void *buf, bool *have_data) if (offset + 1 < len) { cifs_dbg(VFS, "data area offset %d overlaps SMB2 header %d\n", offset + 1, len); + if (data_area_overlap) + *data_area_overlap = true; data_length = 0; + goto calc_size_exit; } else { len = offset + data_length; } @@ -469,7 +485,7 @@ __smb2_calc_size(void *buf, bool *have_data) unsigned int smb2_calc_size(void *buf) { - return __smb2_calc_size(buf, NULL); + return __smb2_calc_size(buf, NULL, NULL); } /* Note: caller must free return buffer */ From 0bc4d4a97302deae1275c0043dafe44b7321bb90 Mon Sep 17 00:00:00 2001 From: Andrey Albershteyn Date: Thu, 9 Jul 2026 17:24:07 +0200 Subject: [PATCH 0669/2758] xfs: fix null pointer dereference in tracepoint commit 9202ee546b0cd71004eed7598546efe4660097da upstream. If dfp is not NULL we exit early here, when dfp is NULL it's allocated in xfs_defer_alloc() but not assigned. The tracepoint tries to dereference members of dfp struct. Signed-off-by: Andrey Albershteyn Cc: stable@vger.kernel.org # v6.8 Fixes: 3f3cec031099c3 ("xfs: force small EFIs for reaping btree extents") Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/libxfs/xfs_defer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c index c6909716b0415..89501e8bd2f84 100644 --- a/fs/xfs/libxfs/xfs_defer.c +++ b/fs/xfs/libxfs/xfs_defer.c @@ -878,7 +878,7 @@ xfs_defer_add_barrier( if (dfp) return; - xfs_defer_alloc(&tp->t_dfops, &xfs_barrier_defer_type); + dfp = xfs_defer_alloc(&tp->t_dfops, &xfs_barrier_defer_type); trace_xfs_defer_add_item(tp->t_mountp, dfp, NULL); } From cccbabeb9a18fcb978d76d6047f2b59214aa7749 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Thu, 2 Jul 2026 09:20:00 -0700 Subject: [PATCH 0670/2758] xfs: fail recovery on a committed log item with no regions commit 2094dab19d45c487285617b7b68913d0cc0c1211 upstream. If the first op of a transaction is a bare transaction header (len == sizeof(struct xfs_trans_header)), xlog_recover_add_to_trans() adds an item but no region, leaving it on r_itemq with ri_cnt == 0 and ri_buf == NULL. The header can be split across op records, so later ops may still add regions; the item is only invalid if the transaction commits with none. The runtime commit path never emits such a transaction, so this only happens on a crafted log. It came from an AI-assisted code audit of the recovery parser. xlog_recover_reorder_trans() calls ITEM_TYPE() on the item, which reads *(unsigned short *)item->ri_buf[0].iov_base and faults on the NULL ri_buf. Reject it there, before the commit handlers that also read ri_buf[0]. KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] RIP: 0010:xlog_recover_reorder_trans (fs/xfs/xfs_log_recover.c:1836) xlog_recover_commit_trans (fs/xfs/xfs_log_recover.c:2043) xlog_recover_process_data (fs/xfs/xfs_log_recover.c:2501) xlog_do_recovery_pass (fs/xfs/xfs_log_recover.c:3244) xlog_recover (fs/xfs/xfs_log_recover.c:3493) xfs_log_mount (fs/xfs/xfs_log.c:618) xfs_mountfs (fs/xfs/xfs_mount.c:1034) xfs_fs_fill_super (fs/xfs/xfs_super.c:1938) vfs_get_tree (fs/super.c:1695) path_mount (fs/namespace.c:4161) __x64_sys_mount (fs/namespace.c:4367) Fixes: 89cebc847729 ("xfs: validate transaction header length on log recovery") Cc: stable@vger.kernel.org # v4.3 Reported-by: Xiang Mei Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Weiming Shi Reviewed-by: Christoph Hellwig Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/xfs_log_recover.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 5f984bf5698a8..7f60d33f5d49f 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -1907,6 +1907,15 @@ xlog_recover_reorder_trans( list_for_each_entry_safe(item, n, &sort_list, ri_list) { enum xlog_recover_reorder fate = XLOG_REORDER_ITEM_LIST; + /* a committed item with no regions has a NULL ri_buf[0] */ + if (!item->ri_cnt || !item->ri_buf) { + xfs_warn(log->l_mp, + "%s: committed log item has no regions", + __func__); + error = -EFSCORRUPTED; + break; + } + item->ri_ops = xlog_find_item_ops(item); if (!item->ri_ops) { xfs_warn(log->l_mp, From 44f891bc088958399eec27f7604928694aa35581 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:03:44 -0700 Subject: [PATCH 0671/2758] xfs: resample the data fork mapping after cycling ILOCK commit 2f4acd0fcd862e22eab45690ec2c08c80b6ef2e7 upstream. xfs_reflink_fill_{cow_hole,delalloc} are both presented with an inode, a data fork mapping, and a cow fork mapping. Unfortunately, these two helpers cycle the ILOCK to grab a transaction, which means that the mappings are stale as soon as we reacquire the ILOCK. Currently we refresh the cow fork mapping by re-calling xfs_find_trim_cow_extent, but we don't refresh the data fork mapping beforehand, which means that the xfs_bmap_trim_cow in that function queries the refcount btree about the wrong physical blocks and returns an inaccurate value in *shared. If *shared is now false, the directio write proceeds with a stale data fork mapping. Fix this by querying the data fork mapping if the sequence counter changes across the ILOCK cycle. Cc: hch@lst.de Cc: stable@vger.kernel.org # v4.11 Fixes: 3c68d44a2b49a0 ("xfs: allocate direct I/O COW blocks in iomap_begin") Signed-off-by: "Darrick J. Wong" Reviewed-by: Christoph Hellwig Reviewed-by: Carlos Maiolino Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/xfs_reflink.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c index 251dec48f0e38..e7db02579f288 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c @@ -440,6 +440,7 @@ xfs_reflink_fill_cow_hole( struct xfs_mount *mp = ip->i_mount; struct xfs_trans *tp; xfs_filblks_t resaligned; + unsigned int seq_before = READ_ONCE(ip->i_df.if_seq); unsigned int dblocks = 0, rblocks = 0; int nimaps; int error; @@ -465,6 +466,22 @@ xfs_reflink_fill_cow_hole( *lockmode = XFS_ILOCK_EXCL; + /* + * The data fork mapping may have changed while we dropped the ILOCK + * (a racing O_DIRECT writer under IOLOCK_SHARED can complete a full + * CoW cycle including xfs_reflink_end_cow(), which remaps this offset + * and drops the refcount of the old shared block). Re-read it so the + * shared-status recheck below and the caller's in-place iomap both + * operate on the current mapping rather than a stale physical block. + */ + if (seq_before != READ_ONCE(ip->i_df.if_seq)) { + nimaps = 1; + error = xfs_bmapi_read(ip, imap->br_startoff, + imap->br_blockcount, imap, &nimaps, 0); + if (error) + goto out_trans_cancel; + } + error = xfs_find_trim_cow_extent(ip, imap, cmap, shared, &found); if (error || !*shared) goto out_trans_cancel; @@ -511,6 +528,8 @@ xfs_reflink_fill_delalloc( bool found; do { + unsigned int seq_before = READ_ONCE(ip->i_df.if_seq); + xfs_iunlock(ip, *lockmode); *lockmode = 0; @@ -521,6 +540,23 @@ xfs_reflink_fill_delalloc( *lockmode = XFS_ILOCK_EXCL; + /* + * The data fork mapping may have changed while we dropped the + * ILOCK (a racing O_DIRECT writer under IOLOCK_SHARED can + * complete a full CoW cycle including xfs_reflink_end_cow(), + * which remaps this offset and drops the refcount of the old + * shared block). Re-read it so the shared-status recheck + * below and the caller's in-place iomap both operate on the + * current mapping rather than a stale physical block. + */ + if (seq_before != READ_ONCE(ip->i_df.if_seq)) { + nimaps = 1; + error = xfs_bmapi_read(ip, imap->br_startoff, + imap->br_blockcount, imap, &nimaps, 0); + if (error) + goto out_trans_cancel; + } + error = xfs_find_trim_cow_extent(ip, imap, cmap, shared, &found); if (error || !*shared) From 2b14fe1e0924c6b901f4256456342569c5397abe Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:04:55 -0700 Subject: [PATCH 0672/2758] xfs: don't wrap around quota ids in dqiterate commit d766e4e5e85d829629c3ba503802fe1303d7b591 upstream. LOLLM noticed that q_id is an unsigned 32-bit variable. If it happens to be set to XFS_DQ_ID_MAX due to a filesystem that actually has a dquot for ID_MAX, then this addition will truncate to zero and the iteration starts over. Fix this by casting to u64. Cc: stable@vger.kernel.org # v6.8 Fixes: 21d7500929c8a0 ("xfs: improve dquot iteration for scrub") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/scrub/dqiterate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/dqiterate.c b/fs/xfs/scrub/dqiterate.c index 10950e4bd4c3c..079dc4e691a01 100644 --- a/fs/xfs/scrub/dqiterate.c +++ b/fs/xfs/scrub/dqiterate.c @@ -205,7 +205,7 @@ xchk_dquot_iter( if (error) return error; - cursor->id = dq->q_id + 1; + cursor->id = (uint64_t)dq->q_id + 1; *dqpp = dq; return 1; } From b19e5b47424b7704e00c1547df82811c4d9e9e41 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:05:57 -0700 Subject: [PATCH 0673/2758] xfs: grab rtrmap btree when checking rgsuper commit ea6e2d9de25d2095845e0cdf2274e581fd40d636 upstream. LOLLM noticed that we aren't grabbing the rtrmap btree when we check the realtime group superblock. As a result, none of the cross-referencing checks have ever run. Fix this. Cc: stable@vger.kernel.org # v6.14 Fixes: 428e4884656db9 ("xfs: allow queued realtime intents to drain before scrubbing") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/scrub/rgsuper.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/fs/xfs/scrub/rgsuper.c b/fs/xfs/scrub/rgsuper.c index 482f899a518a8..eaa8a7b4c6d0e 100644 --- a/fs/xfs/scrub/rgsuper.c +++ b/fs/xfs/scrub/rgsuper.c @@ -23,6 +23,8 @@ int xchk_setup_rgsuperblock( struct xfs_scrub *sc) { + if (xchk_need_intent_drain(sc)) + xchk_fsgates_enable(sc, XCHK_FSGATES_DRAIN); return xchk_trans_alloc(sc, 0); } @@ -43,6 +45,7 @@ xchk_rgsuperblock( struct xfs_scrub *sc) { xfs_rgnumber_t rgno = sc->sm->sm_agno; + unsigned int flags; int error; /* @@ -63,7 +66,12 @@ xchk_rgsuperblock( if (!xchk_xref_process_error(sc, 0, 0, &error)) return error; - error = xchk_rtgroup_lock(sc, &sc->sr, XFS_RTGLOCK_BITMAP_SHARED); + if (xfs_has_rtrmapbt(sc->mp)) + flags = XFS_RTGLOCK_BITMAP | XFS_RTGLOCK_RMAP; + else + flags = XFS_RTGLOCK_BITMAP_SHARED; + + error = xchk_rtgroup_lock(sc, &sc->sr, flags); if (error) return error; From 5da68d6c927a17b5cd89d7ae9ec4c9ec92220bfe Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:05:26 -0700 Subject: [PATCH 0674/2758] xfs: use the rt version of the cow staging checker commit 881f2eb0fcdea2f46a50fa6a892fe9e3ccf19e01 upstream. LOLLM also noticed that xchk_rtrmapbt_xref ought to be using the rtdev version of the "is this a cow extent?" helper function, not the datadev one. Cc: stable@vger.kernel.org # v6.14 Fixes: 91683bb3f264c0 ("xfs: cross-reference checks with the rt refcount btree") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/scrub/rtrmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/rtrmap.c b/fs/xfs/scrub/rtrmap.c index 8b1a8389d32f0..36ee9d633a28d 100644 --- a/fs/xfs/scrub/rtrmap.c +++ b/fs/xfs/scrub/rtrmap.c @@ -209,7 +209,7 @@ xchk_rtrmapbt_xref( xfs_rgbno_to_rtb(sc->sr.rtg, irec->rm_startblock), irec->rm_blockcount); if (irec->rm_owner == XFS_RMAP_OWN_COW) - xchk_xref_is_cow_staging(sc, irec->rm_startblock, + xchk_xref_is_rt_cow_staging(sc, irec->rm_startblock, irec->rm_blockcount); else xchk_rtrmapbt_xref_rtrefc(sc, irec); From 261c7a32f63734408c907ca212058db6d91841f8 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:06:12 -0700 Subject: [PATCH 0675/2758] xfs: set xfarray killable sort correctly commit 540ddc626245f12f56326ee0c1601f71ebb41d64 upstream. LOLLM noticed that we *disable* interruptible sorts when the KILLABLE flag is set. This is backwards. Fix the incorrect logic, and rename the variable to make the connection more obvious. Cc: stable@vger.kernel.org # v6.10 Fixes: 271557de7cbfde ("xfs: reduce the rate of cond_resched calls inside scrub") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/scrub/scrub.h | 6 +++--- fs/xfs/scrub/xfarray.c | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/fs/xfs/scrub/scrub.h b/fs/xfs/scrub/scrub.h index a3f1abc913903..6d7d3523b71f2 100644 --- a/fs/xfs/scrub/scrub.h +++ b/fs/xfs/scrub/scrub.h @@ -11,7 +11,7 @@ struct xfs_scrub; struct xchk_relax { unsigned long next_resched; unsigned int resched_nr; - bool interruptible; + bool killable; }; /* Yield to the scheduler at most 10x per second. */ @@ -21,7 +21,7 @@ struct xchk_relax { (struct xchk_relax){ \ .next_resched = XCHK_RELAX_NEXT, \ .resched_nr = 0, \ - .interruptible = true, \ + .killable = true, \ } /* @@ -45,7 +45,7 @@ static inline int xchk_maybe_relax(struct xchk_relax *widget) widget->next_resched = XCHK_RELAX_NEXT; } - if (widget->interruptible && fatal_signal_pending(current)) + if (widget->killable && fatal_signal_pending(current)) return -EINTR; return 0; diff --git a/fs/xfs/scrub/xfarray.c b/fs/xfs/scrub/xfarray.c index c7c4a71b6fa7c..2ce24bfe4c0fa 100644 --- a/fs/xfs/scrub/xfarray.c +++ b/fs/xfs/scrub/xfarray.c @@ -487,8 +487,7 @@ xfarray_sortinfo_alloc( xfarray_sortinfo_lo(si)[0] = 0; xfarray_sortinfo_hi(si)[0] = array->nr - 1; si->relax = INIT_XCHK_RELAX; - if (flags & XFARRAY_SORT_KILLABLE) - si->relax.interruptible = false; + si->relax.killable = !!(flags & XFARRAY_SORT_KILLABLE); trace_xfarray_sort(si, nr_bytes); *infop = si; From b7e9edbed70505d82662ce4bc4ef6c804aefeeac Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:06:43 -0700 Subject: [PATCH 0676/2758] xfs: handle non-inode owners for rtrmap record checking commit 353a5900bc85234e7df05caac37698042dc26348 upstream. LOLLM noticed that two helper functions in the rtrmapbt scrub code don't actually handle non-inode owners correctly -- CoW staging extents and rgsuperblock extents are not shareable, but they are mergeable. Fix these two helpers. Cc: stable@vger.kernel.org # v6.14 Fixes: 2d9a3e98053e8c ("xfs: allow overlapping rtrmapbt records for shared data extents") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/scrub/rtrmap.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/xfs/scrub/rtrmap.c b/fs/xfs/scrub/rtrmap.c index 36ee9d633a28d..8c9247923a624 100644 --- a/fs/xfs/scrub/rtrmap.c +++ b/fs/xfs/scrub/rtrmap.c @@ -87,6 +87,9 @@ xchk_rtrmapbt_is_shareable( return false; if (irec->rm_flags & XFS_RMAP_UNWRITTEN) return false; + if (irec->rm_owner == XFS_RMAP_OWN_COW || + irec->rm_owner == XFS_RMAP_OWN_FS) + return false; return true; } @@ -146,6 +149,9 @@ xchk_rtrmap_mergeable( return false; if (r1->rm_flags != r2->rm_flags) return false; + if (r1->rm_owner == XFS_RMAP_OWN_COW || + r1->rm_owner == XFS_RMAP_OWN_FS) + return true; return r1->rm_offset + r1->rm_blockcount == r2->rm_offset; } From cb1f92cb29cc0a3ca8b1727ea9130bd31ccb704c Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:07:15 -0700 Subject: [PATCH 0677/2758] xfs: clamp timestamp nanoseconds correctly commit 15e38a9366b31d3d61081ead115f1dff59379e24 upstream. LOLLM noticed an off-by-one error in the nsec clamping; fix that so that we never have tv_nsec == 1e9. Cc: stable@vger.kernel.org # v6.8 Fixes: 2d295fe65776d1 ("xfs: repair inode records") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/scrub/inode_repair.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/inode_repair.c b/fs/xfs/scrub/inode_repair.c index 9738b9ce3f2df..5cdd9c821ab58 100644 --- a/fs/xfs/scrub/inode_repair.c +++ b/fs/xfs/scrub/inode_repair.c @@ -1753,7 +1753,7 @@ xrep_clamp_timestamp( struct xfs_inode *ip, struct timespec64 *ts) { - ts->tv_nsec = clamp_t(long, ts->tv_nsec, 0, NSEC_PER_SEC); + ts->tv_nsec = clamp_t(long, ts->tv_nsec, 0, NSEC_PER_SEC - 1); *ts = timestamp_truncate(*ts, VFS_I(ip)); } From 704a6ba079f09f07d6dfefe4cb6d3bb85e90cd26 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:06:59 -0700 Subject: [PATCH 0678/2758] xfs: fully check the parent handle when it points to the rootdir commit ba150ce63453ccd74bae1404c1dfedbd01ecfd55 upstream. LOLLM noticed that the directory tree path checking declares the path to be ok if the inumber in the parent pointer reaches the root directory. Unfortunately, it neglects to check that the generation is correct. Fix that by moving the generation check up. Cc: stable@vger.kernel.org # v6.10 Fixes: 928b721a11789a ("xfs: teach online scrub to find directory tree structure problems") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/scrub/dirtree.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/fs/xfs/scrub/dirtree.c b/fs/xfs/scrub/dirtree.c index 143114718d75c..19f014e2ece6a 100644 --- a/fs/xfs/scrub/dirtree.c +++ b/fs/xfs/scrub/dirtree.c @@ -383,6 +383,14 @@ xchk_dirpath_step_up( goto out_scanlock; } + /* The handle encoded in the parent pointer must match. */ + if (VFS_I(dp)->i_generation != be32_to_cpu(dl->pptr_rec.p_gen)) { + trace_xchk_dirpath_badgen(dl->sc, dp, path->path_nr, + path->nr_steps, &dl->xname, &dl->pptr_rec); + error = -EFSCORRUPTED; + goto out_scanlock; + } + /* We've reached the root directory; the path is ok. */ if (parent_ino == dl->root_ino) { xchk_dirpath_set_outcome(dl, path, XCHK_DIRPATH_OK); @@ -411,14 +419,6 @@ xchk_dirpath_step_up( goto out_scanlock; } - /* The handle encoded in the parent pointer must match. */ - if (VFS_I(dp)->i_generation != be32_to_cpu(dl->pptr_rec.p_gen)) { - trace_xchk_dirpath_badgen(dl->sc, dp, path->path_nr, - path->nr_steps, &dl->xname, &dl->pptr_rec); - error = -EFSCORRUPTED; - goto out_scanlock; - } - /* Parent pointer must point up to a directory. */ if (!S_ISDIR(VFS_I(dp)->i_mode)) { trace_xchk_dirpath_nondir_parent(dl->sc, dp, path->path_nr, From 19fa8bc0df48e84f45900ea5cdda7682af0f9e80 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:07:30 -0700 Subject: [PATCH 0679/2758] xfs: don't zap bmbt forks if they are MAXLEVELS tall commit 59c462b0f5cfa107794228051724b34ae9334168 upstream. LOLLM noticed a discrepancy between the bmbt level checks in the libxfs bmbt code vs. the inode repair code. We do actually allow a bmbt root that proclaims to have a height of XFS_BM_MAXLEVELS. Cc: stable@vger.kernel.org # v6.8 Fixes: e744cef2060559 ("xfs: zap broken inode forks") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/scrub/inode_repair.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/inode_repair.c b/fs/xfs/scrub/inode_repair.c index 5cdd9c821ab58..2ac66163263b5 100644 --- a/fs/xfs/scrub/inode_repair.c +++ b/fs/xfs/scrub/inode_repair.c @@ -921,7 +921,7 @@ xrep_dinode_bad_bmbt_fork( if (nrecs == 0 || xfs_bmdr_space_calc(nrecs) > dfork_size) return true; - if (level == 0 || level >= XFS_BM_MAXLEVELS(sc->mp, whichfork)) + if (level == 0 || level > XFS_BM_MAXLEVELS(sc->mp, whichfork)) return true; dmxr = xfs_bmdr_maxrecs(dfork_size, 0); From 4d281a74eed85ff1dd40c9b8ccda380b28fa7898 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:06:28 -0700 Subject: [PATCH 0680/2758] xfs: fix off-by-one error when calling xchk_xref_has_rt_owner commit 5d72a68f2007ba2a968d6bf47dba3f4620bd182e upstream. LOLLM noticed an off-by-one error when computing the length of the rtrmap to cross-check. Cc: stable@vger.kernel.org # v6.14 Fixes: 037a44d8277adf ("xfs: cross-reference the realtime rmapbt") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/scrub/rtbitmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/rtbitmap.c b/fs/xfs/scrub/rtbitmap.c index b3a4972d423e2..88b438d8fd89e 100644 --- a/fs/xfs/scrub/rtbitmap.c +++ b/fs/xfs/scrub/rtbitmap.c @@ -258,7 +258,7 @@ xchk_rtbitmap( * the last free extent we saw and the last possible extent in the rt * group. */ - last_rgbno = rtg->rtg_extents * mp->m_sb.sb_rextsize - 1; + last_rgbno = rtg->rtg_extents * mp->m_sb.sb_rextsize; if (rtb->next_free_rgbno < last_rgbno) xchk_xref_has_rt_owner(sc, rtb->next_free_rgbno, last_rgbno - rtb->next_free_rgbno); From 5394c215efde29316728a96620c82168a9074c17 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:05:41 -0700 Subject: [PATCH 0681/2758] xfs: write the rg superblock when fixing it commit 9af789fa274bc14b907f811f79fa988a6fc6d4e7 upstream. The rtgroup superblock fixer should write the rtgroup superblock. LOLLM noticed this, oops. :/ Cc: stable@vger.kernel.org # v6.13 Fixes: 1433f8f9cead37 ("xfs: repair realtime group superblock") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/scrub/rgsuper.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/xfs/scrub/rgsuper.c b/fs/xfs/scrub/rgsuper.c index eaa8a7b4c6d0e..2bd2c0351b35f 100644 --- a/fs/xfs/scrub/rgsuper.c +++ b/fs/xfs/scrub/rgsuper.c @@ -88,9 +88,13 @@ int xrep_rgsuperblock( struct xfs_scrub *sc) { + struct xfs_buf *sb_bp; + ASSERT(rtg_rgno(sc->sr.rtg) == 0); + sb_bp = xfs_trans_getsb(sc->tp); xfs_log_sb(sc->tp); + xfs_log_rtsb(sc->tp, sb_bp); return 0; } #endif /* CONFIG_XFS_ONLINE_REPAIR */ From 63940a3adc7d9e2b85b6953f6305f2830ca73cc5 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:05:10 -0700 Subject: [PATCH 0682/2758] xfs: use rtrefcount btree cursor in xchk_xref_is_rt_cow_staging commit ee248157da501f0c02688fb64e5359f2832b0b01 upstream. LOLLM points out that we pass the wrong btree cursor here. We want the rtrefcount btree cursor, not the non-rt one. This is fairly benign since it only affects tracing data. Cc: stable@vger.kernel.org # v6.14 Fixes: 91683bb3f264c0 ("xfs: cross-reference checks with the rt refcount btree") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/scrub/rtrefcount.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/rtrefcount.c b/fs/xfs/scrub/rtrefcount.c index 4951cc2717885..5f1d5ce7c268e 100644 --- a/fs/xfs/scrub/rtrefcount.c +++ b/fs/xfs/scrub/rtrefcount.c @@ -607,7 +607,7 @@ xchk_xref_is_rt_cow_staging( /* CoW lookup returned a shared extent record? */ if (rc.rc_domain != XFS_REFC_DOMAIN_COW) - xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0); + xchk_btree_xref_set_corrupt(sc, sc->sr.refc_cur, 0); /* Must be at least as long as what was passed in */ if (rc.rc_blockcount < len) From 7a5cef0db4795d9d453a12e0f61b5b7634fc4d40 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 18 Jul 2026 16:55:52 +0200 Subject: [PATCH 0683/2758] Linux 7.1.4 Link: https://lore.kernel.org/r/20260716133047.772246337@linuxfoundation.org Tested-by: Ronald Warsow Tested-by: Brett A C Sheffield Tested-by: Florian Fainelli Tested-by: Justin M. Forbes Tested-by: Peter Schneider Tested-by: Salvatore Bonaccorso Tested-by: Miguel Ojeda Tested-by: Takeshi Ogasawara Tested-by: Ron Economos Tested-by: Shuah Khan Tested-by: Pavel Machek (CIP) Link: https://lore.kernel.org/r/20260717101912.682013354@linuxfoundation.org Tested-by: Brett A C Sheffield Tested-by: Ronald Warsow Tested-by: Florian Fainelli Tested-by: Pavel Machek (CIP) Tested-by: Peter Schneider Tested-by: Takeshi Ogasawara Tested-by: Barry K. Nathan Tested-by: Mark Brown Tested-by: Miguel Ojeda Signed-off-by: Greg Kroah-Hartman --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 11b44c8a72235..b3496dd21312c 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 7 PATCHLEVEL = 1 -SUBLEVEL = 3 +SUBLEVEL = 4 EXTRAVERSION = NAME = Baby Opossum Posse From b05defc41b27c7d0c05c45f67bf5b91c28f93669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammet=20Kaan=20KILIN=C3=87?= Date: Thu, 16 Jul 2026 02:58:36 +0000 Subject: [PATCH 0684/2758] crypto: algif_skcipher - force synchronous processing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AIO/async path in skcipher_recvmsg() passes the socket-wide ctx->iv directly into the skcipher request. After io_submit() the socket lock is dropped and the request is processed asynchronously by a worker (e.g. cryptd), which dereferences ctx->iv only later. A concurrent sendmsg(ALG_SET_IV) on the same socket can overwrite ctx->iv inside this window, so the in-flight request runs under an attacker-controlled IV. For CTR and other stream modes this causes IV/keystream reuse and allows an unprivileged user to recover the plaintext of a concurrent operation. Snapshotting ctx->iv into per-request storage for the async path is not sufficient here. For ciphers with statesize == 0 - which includes cbc and ctr - skcipher_prepare_alg() installs skcipher_noimport()/ skcipher_noexport(), so ctx->state carries nothing and the MSG_MORE inter-chunk IV chaining is carried solely by the in-place req->iv writeback. A snapshot redirects that writeback into per-request memory that af_alg_free_resources() releases on completion, so AIO + MSG_MORE with cbc/ctr would silently produce wrong output. Writing the IV back from the completion callback instead is not possible either: that would require lock_sock() there, but the callback can run in softirq/atomic context, so it must not sleep. Make the operation synchronous instead. ctx->iv is then only ever dereferenced under the socket lock held by recvmsg(), which removes the race, and the req->iv writeback lands in ctx->iv as before, which keeps MSG_MORE chaining intact for statesize == 0 ciphers. The ctx->state import/export path is unchanged for ciphers that do have state. This is equivalent to the upstream resolution: commit fcc77d33a34c ("net: Remove support for AIO on sockets") removed the AIO socket path across net/ entirely, producing the same end state for this file - algif_skcipher never processes an AIO request asynchronously. After this patch, _skcipher_recvmsg() matches mainline's crypto/algif_skcipher.c as it stands today, including the same now-dead -EIOCBQUEUED check. This patch deviates from that commit deliberately: rather than removing AIO socket support tree-wide, which would be far too invasive for stable, it removes only the AIO branch in crypto/algif_skcipher.c. io_submit() now completes synchronously, which is valid for the AIO interface; AF_ALG async is rarely used in practice. The -EIOCBQUEUED check in skcipher_recvmsg() is now dead but harmless, and is left alone to keep the fix minimal. Fixes: e870456d8e7c ("crypto: algif_skcipher - overhaul memory management") Cc: Reported-by: Muhammet Kaan KILINÇ Signed-off-by: Muhammet Kaan KILINÇ Signed-off-by: Sasha Levin --- crypto/algif_skcipher.c | 75 +++++++++++++---------------------------- 1 file changed, 24 insertions(+), 51 deletions(-) diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c index ba0a17fd95aca..35ebc3e0201b0 100644 --- a/crypto/algif_skcipher.c +++ b/crypto/algif_skcipher.c @@ -79,20 +79,6 @@ static int algif_skcipher_export(struct sock *sk, struct skcipher_request *req) return err; } -static void algif_skcipher_done(void *data, int err) -{ - struct af_alg_async_req *areq = data; - struct sock *sk = areq->sk; - - if (err) - goto out; - - err = algif_skcipher_export(sk, &areq->cra_u.skcipher_req); - -out: - af_alg_async_cb(data, err); -} - static int _skcipher_recvmsg(struct socket *sock, struct msghdr *msg, size_t ignored, int flags) { @@ -171,43 +157,30 @@ static int _skcipher_recvmsg(struct socket *sock, struct msghdr *msg, cflags |= CRYPTO_SKCIPHER_REQ_CONT; } - if (msg->msg_iocb && !is_sync_kiocb(msg->msg_iocb)) { - /* AIO operation */ - sock_hold(sk); - areq->iocb = msg->msg_iocb; - - /* Remember output size that will be generated. */ - areq->outlen = len; - - skcipher_request_set_callback(&areq->cra_u.skcipher_req, - cflags | - CRYPTO_TFM_REQ_MAY_SLEEP, - algif_skcipher_done, areq); - err = ctx->enc ? - crypto_skcipher_encrypt(&areq->cra_u.skcipher_req) : - crypto_skcipher_decrypt(&areq->cra_u.skcipher_req); - - /* AIO operation in progress */ - if (err == -EINPROGRESS) - return -EIOCBQUEUED; - - sock_put(sk); - } else { - /* Synchronous operation */ - skcipher_request_set_callback(&areq->cra_u.skcipher_req, - cflags | - CRYPTO_TFM_REQ_MAY_SLEEP | - CRYPTO_TFM_REQ_MAY_BACKLOG, - crypto_req_done, &ctx->wait); - err = crypto_wait_req(ctx->enc ? - crypto_skcipher_encrypt(&areq->cra_u.skcipher_req) : - crypto_skcipher_decrypt(&areq->cra_u.skcipher_req), - &ctx->wait); - - if (!err) - err = algif_skcipher_export( - sk, &areq->cra_u.skcipher_req); - } + /* + * Force synchronous processing. The async (AIO) path passed the + * socket-wide ctx->iv into the request, which the worker + * dereferenced after the socket lock had been dropped, letting a + * concurrent sendmsg(ALG_SET_IV) inject an attacker IV. Mainline + * removed the AIO socket path in commit fcc77d33a34c ("net: Remove + * support for AIO on sockets"); the minimal stable fix is to always + * complete synchronously, so ctx->iv is only ever dereferenced under + * the socket lock. This also keeps the IV chaining intact: for + * ciphers with statesize == 0 (e.g. ctr, cbc) the chained IV is + * carried by the req->iv writeback into ctx->iv, which is only + * consistent on the synchronous path. + */ + skcipher_request_set_callback(&areq->cra_u.skcipher_req, + cflags | + CRYPTO_TFM_REQ_MAY_SLEEP | + CRYPTO_TFM_REQ_MAY_BACKLOG, + crypto_req_done, &ctx->wait); + err = crypto_wait_req(ctx->enc ? + crypto_skcipher_encrypt(&areq->cra_u.skcipher_req) : + crypto_skcipher_decrypt(&areq->cra_u.skcipher_req), + &ctx->wait); + if (!err) + err = algif_skcipher_export(sk, &areq->cra_u.skcipher_req); free: af_alg_free_resources(areq); From 7fd4077dc92b91b1b844333c0a06bb9e286db10a Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Thu, 4 Jun 2026 14:03:07 +0800 Subject: [PATCH 0685/2758] iommu/vt-d: Clear Present bit before tearing down scalable-mode context entry [ Upstream commit f46452c3df7a8d8a5addc0926e76ef19ea7da0a0 ] device_pasid_table_teardown() zeroes the 128-bit scalable-mode context entry with context_clear_entry() while the Present bit is still set. This creates a window where the hardware can fetch a torn entry, with some fields already zeroed while Present is still set, leading to unpredictable behavior or spurious faults. The context-cache invalidation is issued only after the entry has been zeroed, and intel_pasid_free_table() then frees the PASID directory pages, so the IOMMU can keep walking a stale Present=1 entry that points at freed memory. While x86 provides strong write ordering, the compiler may reorder the two 64-bit writes to the entry, and the hardware fetch is not guaranteed to be atomic with respect to multiple CPU writes. Commit c1e4f1dccbe9d ("iommu/vt-d: Clear Present bit before tearing down context entry") fixed this exact pattern in domain_context_clear_one() and the copied-context path, but device_pasid_table_teardown() was not converted. Align it with the "Guidance to Software for Invalidations" in the VT-d spec, Section 6.5.3.3, using the same ownership handshake as the sibling fix: clear only the Present bit, flush it to the IOMMU, perform the context-cache invalidation, and only then zero the rest of the entry. Fixes: 81e921fd32161 ("iommu/vt-d: Fix NULL domain on device release") Signed-off-by: Michael Bommarito Assisted-by: Claude:claude-opus-4-7 Link: https://lore.kernel.org/r/20260528025557.3209367-1-michael.bommarito@gmail.com Signed-off-by: Lu Baolu Signed-off-by: Joerg Roedel Signed-off-by: Sasha Levin --- drivers/iommu/intel/pasid.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/intel/pasid.c b/drivers/iommu/intel/pasid.c index 89541b74ab8ca..40910dc7363b1 100644 --- a/drivers/iommu/intel/pasid.c +++ b/drivers/iommu/intel/pasid.c @@ -748,10 +748,12 @@ static void device_pasid_table_teardown(struct device *dev, u8 bus, u8 devfn) } did = context_domain_id(context); - context_clear_entry(context); + context_clear_present(context); __iommu_flush_cache(iommu, context, sizeof(*context)); spin_unlock(&iommu->lock); intel_context_flush_no_pasid(info, context, did); + context_clear_entry(context); + __iommu_flush_cache(iommu, context, sizeof(*context)); } static int pci_pasid_table_teardown(struct pci_dev *pdev, u16 alias, void *data) From 9c8086d9511189c34dfa3f9e3a03f2bee12f56a5 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 16 Jul 2026 20:50:58 -0700 Subject: [PATCH 0686/2758] crypto: sun4i-ss - Remove insecure and unused rng_alg commit b2c41fa9dd8fc740c489e060b199165771f268d1 upstream. Remove sun4i_ss_rng, as it is insecure and unused: - It has multiple vulnerabilities. sun4i_ss_prng_seed() is missing locking and has a buffer overflow. sun4i_ss_prng_generate() fails to fill the entire buffer with cryptographic random bytes, because it rounds the destination length down and also doesn't actually wait for the hardware to be ready before pulling bytes from it. - No user of this code is known. It's usable only theoretically via the "rng" algorithm type of AF_ALG. But userspace actually just uses the actual Linux RNG (/dev/random etc) instead. And rng_algs don't contribute entropy to the actual Linux RNG either. (This may have been confused with hwrng, which does contribute entropy.) The sun4i_ss_prng_seed() buffer overflow was reported by Tianchu Chen and discovered by Atuin - Automated Vulnerability Discovery Engine There's no point in fixing all these vulnerabilities individually when this is unused code, so let's just remove it. Fixes: b8ae5c7387ad ("crypto: sun4i-ss - support the Security System PRNG") Cc: stable@vger.kernel.org Reported-by: Tianchu Chen Closes: https://lore.kernel.org/r/af749a8447bd7f0e9dd26ca6c87e9c6afecb09d9@linux.dev/ Acked-by: Corentin LABBE Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- arch/arm/configs/sunxi_defconfig | 1 - drivers/crypto/allwinner/Kconfig | 8 --- drivers/crypto/allwinner/sun4i-ss/Makefile | 1 - .../crypto/allwinner/sun4i-ss/sun4i-ss-core.c | 36 ---------- .../crypto/allwinner/sun4i-ss/sun4i-ss-prng.c | 69 ------------------- drivers/crypto/allwinner/sun4i-ss/sun4i-ss.h | 20 ------ 6 files changed, 135 deletions(-) delete mode 100644 drivers/crypto/allwinner/sun4i-ss/sun4i-ss-prng.c diff --git a/arch/arm/configs/sunxi_defconfig b/arch/arm/configs/sunxi_defconfig index a83d29fed1756..f4b8d8f7dbefb 100644 --- a/arch/arm/configs/sunxi_defconfig +++ b/arch/arm/configs/sunxi_defconfig @@ -170,7 +170,6 @@ CONFIG_ROOT_NFS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ISO8859_1=y CONFIG_CRYPTO_DEV_SUN4I_SS=y -CONFIG_CRYPTO_DEV_SUN4I_SS_PRNG=y CONFIG_CRYPTO_DEV_SUN8I_CE=y CONFIG_CRYPTO_DEV_SUN8I_SS=y CONFIG_DMA_CMA=y diff --git a/drivers/crypto/allwinner/Kconfig b/drivers/crypto/allwinner/Kconfig index 7270e5fbc5738..1048f8e95ba80 100644 --- a/drivers/crypto/allwinner/Kconfig +++ b/drivers/crypto/allwinner/Kconfig @@ -25,14 +25,6 @@ config CRYPTO_DEV_SUN4I_SS To compile this driver as a module, choose M here: the module will be called sun4i-ss. -config CRYPTO_DEV_SUN4I_SS_PRNG - bool "Support for Allwinner Security System PRNG" - depends on CRYPTO_DEV_SUN4I_SS - select CRYPTO_RNG - help - Select this option if you want to provide kernel-side support for - the Pseudo-Random Number Generator found in the Security System. - config CRYPTO_DEV_SUN4I_SS_DEBUG bool "Enable sun4i-ss stats" depends on CRYPTO_DEV_SUN4I_SS diff --git a/drivers/crypto/allwinner/sun4i-ss/Makefile b/drivers/crypto/allwinner/sun4i-ss/Makefile index c0a2797d31682..06a9ae81f9f80 100644 --- a/drivers/crypto/allwinner/sun4i-ss/Makefile +++ b/drivers/crypto/allwinner/sun4i-ss/Makefile @@ -1,4 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_CRYPTO_DEV_SUN4I_SS) += sun4i-ss.o sun4i-ss-y += sun4i-ss-core.o sun4i-ss-hash.o sun4i-ss-cipher.o -sun4i-ss-$(CONFIG_CRYPTO_DEV_SUN4I_SS_PRNG) += sun4i-ss-prng.o diff --git a/drivers/crypto/allwinner/sun4i-ss/sun4i-ss-core.c b/drivers/crypto/allwinner/sun4i-ss/sun4i-ss-core.c index 58a76e2ba64e2..35ef0930e77f1 100644 --- a/drivers/crypto/allwinner/sun4i-ss/sun4i-ss-core.c +++ b/drivers/crypto/allwinner/sun4i-ss/sun4i-ss-core.c @@ -213,23 +213,6 @@ static struct sun4i_ss_alg_template ss_algs[] = { } } }, -#ifdef CONFIG_CRYPTO_DEV_SUN4I_SS_PRNG -{ - .type = CRYPTO_ALG_TYPE_RNG, - .alg.rng = { - .base = { - .cra_name = "stdrng", - .cra_driver_name = "sun4i_ss_rng", - .cra_priority = 300, - .cra_ctxsize = 0, - .cra_module = THIS_MODULE, - }, - .generate = sun4i_ss_prng_generate, - .seed = sun4i_ss_prng_seed, - .seedsize = SS_SEED_LEN / BITS_PER_BYTE, - } -}, -#endif }; static int sun4i_ss_debugfs_show(struct seq_file *seq, void *v) @@ -247,12 +230,6 @@ static int sun4i_ss_debugfs_show(struct seq_file *seq, void *v) ss_algs[i].stat_req, ss_algs[i].stat_opti, ss_algs[i].stat_fb, ss_algs[i].stat_bytes); break; - case CRYPTO_ALG_TYPE_RNG: - seq_printf(seq, "%s %s reqs=%lu tsize=%lu\n", - ss_algs[i].alg.rng.base.cra_driver_name, - ss_algs[i].alg.rng.base.cra_name, - ss_algs[i].stat_req, ss_algs[i].stat_bytes); - break; case CRYPTO_ALG_TYPE_AHASH: seq_printf(seq, "%s %s reqs=%lu\n", ss_algs[i].alg.hash.halg.base.cra_driver_name, @@ -471,13 +448,6 @@ static int sun4i_ss_probe(struct platform_device *pdev) goto error_alg; } break; - case CRYPTO_ALG_TYPE_RNG: - err = crypto_register_rng(&ss_algs[i].alg.rng); - if (err) { - dev_err(ss->dev, "Fail to register %s\n", - ss_algs[i].alg.rng.base.cra_name); - } - break; } } @@ -497,9 +467,6 @@ static int sun4i_ss_probe(struct platform_device *pdev) case CRYPTO_ALG_TYPE_AHASH: crypto_unregister_ahash(&ss_algs[i].alg.hash); break; - case CRYPTO_ALG_TYPE_RNG: - crypto_unregister_rng(&ss_algs[i].alg.rng); - break; } } error_pm: @@ -520,9 +487,6 @@ static void sun4i_ss_remove(struct platform_device *pdev) case CRYPTO_ALG_TYPE_AHASH: crypto_unregister_ahash(&ss_algs[i].alg.hash); break; - case CRYPTO_ALG_TYPE_RNG: - crypto_unregister_rng(&ss_algs[i].alg.rng); - break; } } diff --git a/drivers/crypto/allwinner/sun4i-ss/sun4i-ss-prng.c b/drivers/crypto/allwinner/sun4i-ss/sun4i-ss-prng.c deleted file mode 100644 index 491fcb7b81b40..0000000000000 --- a/drivers/crypto/allwinner/sun4i-ss/sun4i-ss-prng.c +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -#include "sun4i-ss.h" - -int sun4i_ss_prng_seed(struct crypto_rng *tfm, const u8 *seed, - unsigned int slen) -{ - struct sun4i_ss_alg_template *algt; - struct rng_alg *alg = crypto_rng_alg(tfm); - - algt = container_of(alg, struct sun4i_ss_alg_template, alg.rng); - memcpy(algt->ss->seed, seed, slen); - - return 0; -} - -int sun4i_ss_prng_generate(struct crypto_rng *tfm, const u8 *src, - unsigned int slen, u8 *dst, unsigned int dlen) -{ - struct sun4i_ss_alg_template *algt; - struct rng_alg *alg = crypto_rng_alg(tfm); - int i, err; - u32 v; - u32 *data = (u32 *)dst; - const u32 mode = SS_OP_PRNG | SS_PRNG_CONTINUE | SS_ENABLED; - size_t len; - struct sun4i_ss_ctx *ss; - unsigned int todo = (dlen / 4) * 4; - - algt = container_of(alg, struct sun4i_ss_alg_template, alg.rng); - ss = algt->ss; - - err = pm_runtime_resume_and_get(ss->dev); - if (err < 0) - return err; - - if (IS_ENABLED(CONFIG_CRYPTO_DEV_SUN4I_SS_DEBUG)) { - algt->stat_req++; - algt->stat_bytes += todo; - } - - spin_lock_bh(&ss->slock); - - writel(mode, ss->base + SS_CTL); - - while (todo > 0) { - /* write the seed */ - for (i = 0; i < SS_SEED_LEN / BITS_PER_LONG; i++) - writel(ss->seed[i], ss->base + SS_KEY0 + i * 4); - - /* Read the random data */ - len = min_t(size_t, SS_DATA_LEN / BITS_PER_BYTE, todo); - readsl(ss->base + SS_TXFIFO, data, len / 4); - data += len / 4; - todo -= len; - - /* Update the seed */ - for (i = 0; i < SS_SEED_LEN / BITS_PER_LONG; i++) { - v = readl(ss->base + SS_KEY0 + i * 4); - ss->seed[i] = v; - } - } - - writel(0, ss->base + SS_CTL); - spin_unlock_bh(&ss->slock); - - pm_runtime_put(ss->dev); - - return 0; -} diff --git a/drivers/crypto/allwinner/sun4i-ss/sun4i-ss.h b/drivers/crypto/allwinner/sun4i-ss/sun4i-ss.h index 6c5d4aa6453c7..f7d1c79ac677d 100644 --- a/drivers/crypto/allwinner/sun4i-ss/sun4i-ss.h +++ b/drivers/crypto/allwinner/sun4i-ss/sun4i-ss.h @@ -31,8 +31,6 @@ #include #include #include -#include -#include #define SS_CTL 0x00 #define SS_KEY0 0x04 @@ -62,10 +60,6 @@ /* SS_CTL configuration values */ -/* PRNG generator mode - bit 15 */ -#define SS_PRNG_ONESHOT (0 << 15) -#define SS_PRNG_CONTINUE (1 << 15) - /* IV mode for hash */ #define SS_IV_ARBITRARY (1 << 14) @@ -94,14 +88,10 @@ #define SS_OP_3DES (2 << 4) #define SS_OP_SHA1 (3 << 4) #define SS_OP_MD5 (4 << 4) -#define SS_OP_PRNG (5 << 4) /* Data end bit - bit 2 */ #define SS_DATA_END (1 << 2) -/* PRNG start bit - bit 1 */ -#define SS_PRNG_START (1 << 1) - /* SS Enable bit - bit 0 */ #define SS_DISABLED (0 << 0) #define SS_ENABLED (1 << 0) @@ -128,9 +118,6 @@ #define SS_RXFIFO_EMP_INT_ENABLE (1 << 2) #define SS_TXFIFO_AVA_INT_ENABLE (1 << 0) -#define SS_SEED_LEN 192 -#define SS_DATA_LEN 160 - /* * struct ss_variant - Describe SS hardware variant * @sha1_in_be: The SHA1 digest is given by SS in BE, and so need to be inverted. @@ -151,9 +138,6 @@ struct sun4i_ss_ctx { char buf[4 * SS_RX_MAX];/* buffer for linearize SG src */ char bufo[4 * SS_TX_MAX]; /* buffer for linearize SG dst */ spinlock_t slock; /* control the use of the device */ -#ifdef CONFIG_CRYPTO_DEV_SUN4I_SS_PRNG - u32 seed[SS_SEED_LEN / BITS_PER_LONG]; -#endif struct dentry *dbgfs_dir; struct dentry *dbgfs_stats; }; @@ -164,7 +148,6 @@ struct sun4i_ss_alg_template { union { struct skcipher_alg crypto; struct ahash_alg hash; - struct rng_alg rng; } alg; struct sun4i_ss_ctx *ss; unsigned long stat_req; @@ -231,6 +214,3 @@ int sun4i_ss_des_setkey(struct crypto_skcipher *tfm, const u8 *key, unsigned int keylen); int sun4i_ss_des3_setkey(struct crypto_skcipher *tfm, const u8 *key, unsigned int keylen); -int sun4i_ss_prng_generate(struct crypto_rng *tfm, const u8 *src, - unsigned int slen, u8 *dst, unsigned int dlen); -int sun4i_ss_prng_seed(struct crypto_rng *tfm, const u8 *seed, unsigned int slen); From 7f6c5fe1633272c926361b73d83dfc6ae01b1504 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Mon, 16 Mar 2026 11:58:22 -0400 Subject: [PATCH 0687/2758] media: uvcvideo: Fix deadlock if uvc_status_stop is called from async_ctrl.work [ Upstream commit 6d27f92c54ce28cfbd2a8a479a96d6f4a781b7d2 ] If a UVC camera has an asynchronous control, uvc_status_stop may be called from async_ctrl.work: uvc_ctrl_status_event_work() uvc_ctrl_status_event() uvc_ctrl_clear_handle() uvc_pm_put() uvc_status_put() uvc_status_stop() cancel_work_sync() This will cause a deadlock, since cancel_work_sync will wait for uvc_ctrl_status_event_work to complete before returning. Fix this by returning early from uvc_status_stop if we are currently in the work function. flush_status now remains false until uvc_status_start is called again, ensuring that uvc_ctrl_status_event_work won't resubmit the URB. Fixes: a32d9c41bdb8 ("media: uvcvideo: Make power management granular") Cc: stable@vger.kernel.org Closes: https://lore.kernel.org/all/6733bdfb-3e88-479f-8956-ab09c04c433e@linux.dev/ Signed-off-by: Sean Anderson Link: https://patch.msgid.link/20260316155823.1855434-1-sean.anderson@linux.dev Reviewed-by: Ricardo Ribalda Tested-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil Signed-off-by: Sasha Levin --- drivers/media/usb/uvc/uvc_status.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c index 65f5356bebb39..b632cf5e3fe9b 100644 --- a/drivers/media/usb/uvc/uvc_status.c +++ b/drivers/media/usb/uvc/uvc_status.c @@ -316,6 +316,16 @@ static int uvc_status_start(struct uvc_device *dev, gfp_t flags) if (!dev->int_urb) return 0; + /* + * If the previous uvc_status_stop() call was from the async work, + * the work may still be running. Wait for it to finish before we submit + * the urb. + */ + flush_work(&dev->async_ctrl.work); + + /* Clear the flush status if we were previously stopped. */ + smp_store_release(&dev->flush_status, false); + return usb_submit_urb(dev->int_urb, flags); } @@ -336,6 +346,15 @@ static void uvc_status_stop(struct uvc_device *dev) */ smp_store_release(&dev->flush_status, true); + /* + * If we are called from the event work function, the URB is guaranteed + * to not be in flight as it has completed and has not been resubmitted. + * There's no need to cancel the work (which would deadlock), or to kill + * the URB. + */ + if (current_work() == &w->work) + return; + /* * Cancel any pending asynchronous work. If any status event was queued, * process it synchronously. @@ -354,15 +373,6 @@ static void uvc_status_stop(struct uvc_device *dev) */ if (cancel_work_sync(&w->work)) uvc_ctrl_status_event(w->chain, w->ctrl, w->data); - - /* - * From this point, there are no events on the queue and the status URB - * is dead. No events will be queued until uvc_status_start() is called. - * The barrier is needed to make sure that flush_status is visible to - * uvc_ctrl_status_event_work() when uvc_status_start() will be called - * again. - */ - smp_store_release(&dev->flush_status, false); } int uvc_status_resume(struct uvc_device *dev) From 63c5a6766701b6c27545cdc23bb5ecf2e36d269d Mon Sep 17 00:00:00 2001 From: Eckhart Mohr Date: Wed, 8 Jul 2026 15:21:06 +0200 Subject: [PATCH 0688/2758] ALSA: hda/realtek: Add quirk for TongFang X6xx45xU commit d595255241e5fec0c94adeebf2565524398e37c5 upstream. Fix microphone detection on built in headphone jack for some devices. Signed-off-by: Eckhart Mohr Cc: stable@vger.kernel.org Signed-off-by: Werner Sembach Link: https://patch.msgid.link/20260708132135.102680-1-wse@tuxedocomputers.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 1f6fdbb45fc75..0c4866cf517a2 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7840,6 +7840,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1d05, 0x300f, "TongFang X6AR5xxY", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1d05, 0x3019, "TongFang X6FR5xxY", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1d05, 0x3031, "TongFang X6AR55xU", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1d05, 0x3034, "TongFang X6xx45xU", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1d17, 0x3288, "Haier Boyue G42", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS), SND_PCI_QUIRK(0x1d72, 0x1602, "RedmiBook", ALC255_FIXUP_XIAOMI_HEADSET_MIC), SND_PCI_QUIRK(0x1d72, 0x1701, "XiaomiNotebook Pro", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE), From 30469715fbfefe12e36f0c72da7f5531c669e1f4 Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Mon, 13 Jul 2026 18:03:29 +0800 Subject: [PATCH 0689/2758] ALSA: hda: conexant: Remove mic bias threshold override commit f52524da7084c1a54683ae9fbc73e93fff19dd64 upstream. Remove the mic bias current comparator threshold override (NID 0x1c, verb 0x320, value 0x010) from Conexant codec driver. This override was originally intended to support volume up/down controls on headsets with inline remote controls, but it causes microphone detection failures on some headsets with impedance less than 1k ohm. After consulting with the vendor's engineers, it was confirmed that this setting is board-specific and should be handled by BIOS/firmware rather than the generic codec driver, especially since inline remote support is not currently implemented. Fixes: 7aeb25908648 ("ALSA: hda/conexant: Fix headset auto detect fail in cx8070 and SN6140") Cc: stable@vger.kernel.org Signed-off-by: Zhang Heng Link: https://patch.msgid.link/20260713100329.306892-1-zhangheng@kylinos.cn Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/hda/codecs/conexant.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/sound/hda/codecs/conexant.c b/sound/hda/codecs/conexant.c index e3b6aaabe3a9c..9c6142ab57214 100644 --- a/sound/hda/codecs/conexant.c +++ b/sound/hda/codecs/conexant.c @@ -162,9 +162,6 @@ static void cx_fixup_headset_recog(struct hda_codec *codec) { unsigned int mic_present; - /* fix some headset type recognize fail issue, such as EDIFIER headset */ - /* set micbias output current comparator threshold from 66% to 55%. */ - snd_hda_codec_write(codec, 0x1c, 0, 0x320, 0x010); /* set OFF voltage for DFET from -1.2V to -0.8V, set headset micbias register * value adjustment trim from 2.2K ohms to 2.0K ohms. */ From ba68c540dafb73bb1ae01b5901ed58b76311528f Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Tue, 7 Jul 2026 21:24:19 +0800 Subject: [PATCH 0690/2758] ALSA: hda: Fix cached processing coefficient verbs commit f67be28fdf8b5d31ac1cc1152bb17250f9f8f513 upstream. Intel HD Audio defines Coefficient Index and Processing Coefficient as separate audio widget controls in the Audio Widget Verb Definitions: Coefficient Index selects the coefficient slot, while Processing Coefficient accesses the value at the selected slot. hda_reg_read_coef() selects the slot with AC_VERB_SET_COEF_INDEX, but then uses AC_VERB_GET_COEF_INDEX for the value read. That reads back the selected index instead of the coefficient value. hda_reg_write_coef() has the same issue and builds the value write from AC_VERB_GET_COEF_INDEX instead of AC_VERB_SET_PROC_COEF. This only affects the regmap coefficient cache path used by codecs that set codec->cache_coef. Direct coefficient helpers already use the normal SET_COEF_INDEX followed by GET_PROC_COEF or SET_PROC_COEF sequence, which is likely why this has not been noticed widely. Use AC_VERB_GET_PROC_COEF for cached coefficient reads and AC_VERB_SET_PROC_COEF for cached coefficient writes. Fixes: 40ba66a702b8 ("ALSA: hda - Add cache support for COEF read/write") Cc: stable@vger.kernel.org Signed-off-by: Xu Rao Link: https://patch.msgid.link/DB9023BF2920BA99+20260707132419.1731342-1-raoxu@uniontech.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/hda/core/regmap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/hda/core/regmap.c b/sound/hda/core/regmap.c index e7b866fc52c15..d6eb17aa9e086 100644 --- a/sound/hda/core/regmap.c +++ b/sound/hda/core/regmap.c @@ -214,7 +214,7 @@ static int hda_reg_read_coef(struct hdac_device *codec, unsigned int reg, err = snd_hdac_exec_verb(codec, verb, 0, NULL); if (err < 0) return err; - verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8); + verb = (reg & ~0xfffff) | (AC_VERB_GET_PROC_COEF << 8); return snd_hdac_exec_verb(codec, verb, 0, val); } @@ -232,7 +232,7 @@ static int hda_reg_write_coef(struct hdac_device *codec, unsigned int reg, err = snd_hdac_exec_verb(codec, verb, 0, NULL); if (err < 0) return err; - verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8) | + verb = (reg & ~0xfffff) | (AC_VERB_SET_PROC_COEF << 8) | (val & 0xffff); return snd_hdac_exec_verb(codec, verb, 0, NULL); } From 9ba2fd1433170ed95fc98c51cb7171e3a29e498a Mon Sep 17 00:00:00 2001 From: Damien Laine Date: Sun, 12 Jul 2026 23:37:08 +0200 Subject: [PATCH 0691/2758] ALSA: hda/realtek: Fix speakers on Legion Pro 7 16ARX8H with codec SSID 17aa:38a7 commit d35dfb6329accfe1cfa0b57e35214b5cbbe0f9ae upstream. Some units of the Lenovo Legion Pro 7 16ARX8H (82WS) report codec subsystem ID 17aa:38a7 instead of 17aa:38a8. Since only 38a8 has a codec SSID quirk, these machines fall through to the PCI SSID match 17aa:386f (Legion Pro 7i 16IAX7) and get ALC287_FIXUP_CS35L41_I2C_2, which probes the Cirrus amplifiers of the Intel variant. The TI TAS2781 amplifier (ACPI TIAS2781:00) present on this AMD variant is never bound and the internal speakers remain silent. Add a codec SSID quirk for 17aa:38a7 pointing to ALC287_FIXUP_TAS2781_I2C, mirroring the existing 38a8 entry. Tested on a Legion Pro 7 16ARX8H (82WS, BIOS LPCN62WW): with the codec SSID overridden to 17aa:38a8 via the HDA patch loader, the TAS2781 amplifier binds and the internal speakers work. Cc: Signed-off-by: Damien Laine Link: https://patch.msgid.link/20260712213708.1835469-1-damien.laine@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 0c4866cf517a2..bc12c9a57858e 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7723,6 +7723,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { HDA_CODEC_QUIRK(0x17aa, 0x386e, "Legion Y9000X 2022 IAH7", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x386e, "Yoga Pro 7 14ARP8", ALC285_FIXUP_SPEAKER2_TO_DAC1), HDA_CODEC_QUIRK(0x17aa, 0x38a8, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ + HDA_CODEC_QUIRK(0x17aa, 0x38a7, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ SND_PCI_QUIRK(0x17aa, 0x386f, "Legion Pro 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x3870, "Lenovo Yoga 7 14ARB7", ALC287_FIXUP_YOGA7_14ARB7_I2C), SND_PCI_QUIRK(0x17aa, 0x3877, "Lenovo Legion 7 Slim 16ARHA7", ALC287_FIXUP_CS35L41_I2C_2), From ca4c918226845ea3ea2c60e538f7e5920d25207a Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 13 May 2026 11:49:21 +0000 Subject: [PATCH 0692/2758] media: uvcvideo: Use hw timestaming if the clock buffer is full commit ede7de6e6b3db552d10ac50557d69c50d1b08486 upstream. In some situations, even with a full clock buffer, it does not contain 250msec of data. This results in the driver jumping back from software to hardware timestapsing creating a nasty artifact in the video. If the clock buffer is full, use it to calculate the timestamp instead of defaulting to software stamps, the reduced accuracy is less visible than jumping from one timestamping mechanism to the other. Fixes: 6243c83be6ee8 ("media: uvcvideo: Allow hw clock updates with buffers not full") Cc: stable@vger.kernel.org Reviewed-by: Hans de Goede Tested-by: Yunke Cao Signed-off-by: Ricardo Ribalda Link: https://patch.msgid.link/20260513-uvc-hwtimestamp-v3-2-7a64838b0b02@chromium.org Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/uvc/uvc_video.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index f6c8e32237968..b6a2e2acb3e0f 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -833,15 +833,22 @@ void uvc_video_clock_update(struct uvc_streaming *stream, y2 += 2048 << 16; /* - * Have at least 1/4 of a second of timestamps before we - * try to do any calculation. Otherwise we do not have enough - * precision. This value was determined by running Android CTS - * on different devices. + * If the buffer is not full, we want to gather at least 1/4th of + * timestamps before using HW timestamping. We do this to avoid jitter + * on the initial frames. + * + * If the buffer is full we would use it regardless of how much data + * it represents. This could be solved with an infinite big circular + * buffer, but RAM is expensive these days, specially the infinitely + * big. + * + * The value of 1/4th of a second was determined by running Android's + * CTS on different devices. * * dev_sof runs at 1KHz, and we have a fixed point precision of * 16 bits. */ - if ((y2 - y1) < ((1000 / 4) << 16)) + if (clock->size != clock->count && (y2 - y1) < ((1000 / 4) << 16)) goto done; y = (u64)(y2 - y1) * (1ULL << 31) + (u64)y1 * (u64)x2 From b058d9a21694466e83d4dc3213a94397c4d87ab8 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 17 Apr 2026 05:19:29 +0000 Subject: [PATCH 0693/2758] media: uvcvideo: Avoid partial metadata buffers commit a15b773fe4ffa450b56347cc506b2d1405600f5d upstream. If the metadata queue that is empty receives a new buffer while we are in the middle of processing a frame, the first metadata buffer will contain partial information. Avoid this by tracking the state of the metadata buffer and making sure that it is in sync with the data buffer. Now that we are at it, make sure that we skip buffers of size 1 or 0. They are not allowed by the spec... but it is a simple check to add and better be safe than sorry. Fixes: 088ead255245 ("media: uvcvideo: Add a metadata device node") Cc: stable@vger.kernel.org Signed-off-by: Ricardo Ribalda Link: https://patch.msgid.link/20260417-uvc-meta-partial-v2-2-31d274af7d2d@chromium.org Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/uvc/uvc_video.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index b6a2e2acb3e0f..987f79ff41545 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -1156,7 +1156,9 @@ static void uvc_video_stats_stop(struct uvc_streaming *stream) * uvc_video_decode_end will never be called with a NULL buffer. */ static int uvc_video_decode_start(struct uvc_streaming *stream, - struct uvc_buffer *buf, const u8 *data, int len) + struct uvc_buffer *buf, + struct uvc_buffer *meta_buf, + const u8 *data, int len) { u8 header_len; u8 fid; @@ -1229,6 +1231,8 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, /* TODO: Handle PTS and SCR. */ buf->state = UVC_BUF_STATE_ACTIVE; + if (meta_buf) + meta_buf->state = UVC_BUF_STATE_ACTIVE; } /* @@ -1431,7 +1435,7 @@ static void uvc_video_decode_meta(struct uvc_streaming *stream, ktime_t time; const u8 *scr; - if (!meta_buf || length == 2) + if (length <= 2 || !meta_buf || meta_buf->state != UVC_BUF_STATE_ACTIVE) return; has_pts = mem[1] & UVC_STREAM_PTS; @@ -1548,7 +1552,7 @@ static void uvc_video_decode_isoc(struct uvc_urb *uvc_urb, /* Decode the payload header. */ mem = urb->transfer_buffer + urb->iso_frame_desc[i].offset; do { - ret = uvc_video_decode_start(stream, buf, mem, + ret = uvc_video_decode_start(stream, buf, meta_buf, mem, urb->iso_frame_desc[i].actual_length); if (ret == -EAGAIN) uvc_video_next_buffers(stream, &buf, &meta_buf); @@ -1597,7 +1601,8 @@ static void uvc_video_decode_bulk(struct uvc_urb *uvc_urb, */ if (stream->bulk.header_size == 0 && !stream->bulk.skip_payload) { do { - ret = uvc_video_decode_start(stream, buf, mem, len); + ret = uvc_video_decode_start(stream, buf, meta_buf, mem, + len); if (ret == -EAGAIN) uvc_video_next_buffers(stream, &buf, &meta_buf); } while (ret == -EAGAIN); From c082b0ed2db6d6f9e369f1855cfbef78cc7bb205 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 23 Mar 2026 09:53:53 +0000 Subject: [PATCH 0694/2758] media: uvcvideo: Fix buffer sequence in frame gaps commit 2f24ac8dd87983a55f0498898f34a5f2b735b802 upstream. In UVC, the FID flips with every frame. For every FID flip, we increase the stream sequence number. Now, if a FID flips multiple times and there is no data transferred between the flips, the buffer sequence number will be set to the value of the stream sequence number after the first flip. Userspace uses the buffer sequence number to determine if there have been missing frames. With the current behaviour, userspace will think that the gap is in the wrong location. This patch modifies uvc_video_decode_start() to provide the correct buffer sequence number and timestamp. Cc: stable@kernel.org Fixes: 650b95feee35 ("[media] uvcvideo: Generate discontinuous sequence numbers when frames are lost") Signed-off-by: Ricardo Ribalda Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/uvc/uvc_video.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 987f79ff41545..81328522cc832 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -1185,6 +1185,19 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, stream->sequence++; if (stream->sequence) uvc_video_stats_update(stream); + + /* + * On a FID flip initialize sequence number and timestamp. + * + * The driver already takes care of injecting FID flips for + * UVC_QUIRK_STREAM_NO_FID and UVC_QUIRK_MJPEG_NO_EOF. + */ + if (buf) { + buf->buf.field = V4L2_FIELD_NONE; + buf->buf.sequence = stream->sequence; + buf->buf.vb2_buf.timestamp = + ktime_to_ns(uvc_video_get_time()); + } } uvc_video_clock_decode(stream, buf, data, len); @@ -1225,10 +1238,6 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, return -ENODATA; } - buf->buf.field = V4L2_FIELD_NONE; - buf->buf.sequence = stream->sequence; - buf->buf.vb2_buf.timestamp = ktime_to_ns(uvc_video_get_time()); - /* TODO: Handle PTS and SCR. */ buf->state = UVC_BUF_STATE_ACTIVE; if (meta_buf) From 40aaf991dc0a7928b72500d3119c974956aa8216 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 13 May 2026 11:49:20 +0000 Subject: [PATCH 0695/2758] media: uvcvideo: Fix dev_sof filtering in hw timestamp commit edc1917599c5339aedc83135cade66517e0a2972 upstream. To avoid filling the clock circular buffer with duplicated data we only add it if the new value sof is different than the last added sof. The issue is that we compare the unprocess sof with the processed sof. If there is a sof_offset, or UVC_QUIRK_INVALID_DEVICE_SOF is enabled, the comparison will not work as expected. This patch moves the comparison to the right place. Fixes: 141270bd95d4 ("media: uvcvideo: Refactor clock circular buffer") Cc: stable@vger.kernel.org Reviewed-by: Hans de Goede Tested-by: Yunke Cao Signed-off-by: Ricardo Ribalda Link: https://patch.msgid.link/20260513-uvc-hwtimestamp-v3-1-7a64838b0b02@chromium.org Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/uvc/uvc_video.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 81328522cc832..ce1cec0978178 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -583,16 +583,7 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, if (!has_scr) return; - /* - * To limit the amount of data, drop SCRs with an SOF identical to the - * previous one. This filtering is also needed to support UVC 1.5, where - * all the data packets of the same frame contains the same SOF. In that - * case only the first one will match the host_sof. - */ sample.dev_sof = get_unaligned_le16(&data[header_size - 2]); - if (sample.dev_sof == stream->clock.last_sof) - return; - sample.dev_stc = get_unaligned_le32(&data[header_size - 6]); /* @@ -664,6 +655,16 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, } sample.dev_sof = (sample.dev_sof + stream->clock.sof_offset) & 2047; + + /* + * To limit the amount of data, drop SCRs with an SOF identical to the + * previous one. This filtering is also needed to support UVC 1.5, where + * all the data packets of the same frame contains the same SOF. In that + * case only the first one will match the host_sof. + */ + if (sample.dev_sof == stream->clock.last_sof) + return; + uvc_video_clock_add_sample(&stream->clock, &sample); stream->clock.last_sof = sample.dev_sof; } From 600e379bd5891ff1d530418b5a0513c981157aa8 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 13 May 2026 11:49:23 +0000 Subject: [PATCH 0696/2758] media: uvcvideo: Do not add clock samples with small sof delta commit ba649fff36c1fe68489a86d00285224907edd436 upstream. Some UVC 1.1 cameras running in fast isochronous mode tend to spam the USB host with a lot of empty packets. These packets contain clock information and are added to the clock buffer but do not add any accuracy to the calculation. In fact, it is quite the opposite, in our calculations, only the first and the last timestamp is used, and we only have 32 slots. Ignore the samples that will produce less than MIN_HW_TIMESTAMP_DIFF data. Fixes: 141270bd95d4 ("media: uvcvideo: Refactor clock circular buffer") Cc: stable@vger.kernel.org Tested-by: Yunke Cao Reviewed-by: Hans de Goede Signed-off-by: Ricardo Ribalda Link: https://patch.msgid.link/20260513-uvc-hwtimestamp-v3-4-7a64838b0b02@chromium.org Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/uvc/uvc_video.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index ce1cec0978178..fa7aacacc638a 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -537,6 +537,15 @@ static void uvc_video_clock_add_sample(struct uvc_clock *clock, spin_unlock_irqrestore(&clock->lock, flags); } +static inline u16 sof_diff(u16 a, u16 b) +{ + /* + * Because the result is modulo 2048 (via & 2047), we do not need a + * special case for a < b. + */ + return (a - b) & 2047; +} + static void uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, const u8 *data, int len) @@ -657,12 +666,13 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, sample.dev_sof = (sample.dev_sof + stream->clock.sof_offset) & 2047; /* - * To limit the amount of data, drop SCRs with an SOF identical to the + * To limit the amount of data, drop SCRs with an SOF similar to the * previous one. This filtering is also needed to support UVC 1.5, where * all the data packets of the same frame contains the same SOF. In that * case only the first one will match the host_sof. */ - if (sample.dev_sof == stream->clock.last_sof) + if (sof_diff(sample.dev_sof, stream->clock.last_sof) <= + (UVC_MIN_HW_TIMESTAMP_DIFF / stream->clock.size)) return; uvc_video_clock_add_sample(&stream->clock, &sample); From f3970948369e41569a566fb37615c64e397661c8 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 13 May 2026 11:49:22 +0000 Subject: [PATCH 0697/2758] media: uvcvideo: Relax the constrains for interpolating the hw clock commit 1719d78f832dda8dd3f09a867ba74e05d6f11308 upstream. In the initial version we set the min value to 250msec. Looks like 100msec can also provide a good value. Now that we are at it, add a macro to make it cleaner. Fixes: 6243c83be6ee8 ("media: uvcvideo: Allow hw clock updates with buffers not full") Cc: stable@vger.kernel.org Reviewed-by: Hans de Goede Tested-by: Yunke Cao Signed-off-by: Ricardo Ribalda Link: https://patch.msgid.link/20260513-uvc-hwtimestamp-v3-3-7a64838b0b02@chromium.org Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/uvc/uvc_video.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index fa7aacacc638a..6422affb143f9 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -494,6 +494,13 @@ static int uvc_commit_video(struct uvc_streaming *stream, * Clocks and timestamps */ +/* + * The accuracy of the hardware timestamping depends on having enough data to + * interpolate between the different clock domains. This value is sof cycles, + * this is, milliseconds. + */ +#define UVC_MIN_HW_TIMESTAMP_DIFF 100 + static inline ktime_t uvc_video_get_time(void) { if (uvc_clock_param == CLOCK_MONOTONIC) @@ -853,13 +860,13 @@ void uvc_video_clock_update(struct uvc_streaming *stream, * buffer, but RAM is expensive these days, specially the infinitely * big. * - * The value of 1/4th of a second was determined by running Android's - * CTS on different devices. + * The value of UVC_MIN_HW_TIMESTAMP_DIFF was determined by running + * Android's CTS on different devices. * - * dev_sof runs at 1KHz, and we have a fixed point precision of - * 16 bits. + * y1 and y2 are dev_sof with a fixed point precision of 16 bits. */ - if (clock->size != clock->count && (y2 - y1) < ((1000 / 4) << 16)) + if (clock->size != clock->count && + (y2 - y1) < (UVC_MIN_HW_TIMESTAMP_DIFF << 16)) goto done; y = (u64)(y2 - y1) * (1ULL << 31) + (u64)y1 * (u64)x2 From cf5f54c018967394f54ea196a98f4f016f70eb89 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 23 Mar 2026 09:53:52 +0000 Subject: [PATCH 0698/2758] media: uvcvideo: Fix sequence number when no EOF commit f078966ca1fb1b3865d8e6bbe2705cfd277fc637 upstream. If the driver could not detect the EOF, the sequence number is increased twice: 1) When we enter uvc_video_decode_start() with the old buffer and FID has flipped => We return -EAGAIN and last_fid is not flipped 2) When we enter uvc_video_decode_start() with the new buffer. Fix this issue by moving the new frame detection logic earlier in uvc_video_decode_start(). This also has some nice side affects: - The error status from the new packet will no longer get propagated to the previous frame-buffer. - uvc_video_clock_decode() will no longer update the previous frame buf->stf with info from the new packet. - uvc_video_clock_decode() and uvc_video_stats_decode() will no longer get called twice for the same packet. Cc: stable@kernel.org Fixes: 650b95feee35 ("[media] uvcvideo: Generate discontinuous sequence numbers when frames are lost") Reported-by: Hans de Goede Closes: https://lore.kernel.org/linux-media/CANiDSCuj4cPuB5_v2xyvAagA5FjoN8V5scXiFFOeD3aKDMqkCg@mail.gmail.com/T/#me39fb134e8c2c085567a31548c3403eb639625e4 Signed-off-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/uvc/uvc_video.c | 92 ++++++++++++++++--------------- 1 file changed, 47 insertions(+), 45 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 6422affb143f9..aa3cb941762e3 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -1195,6 +1195,53 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, header_len = data[0]; fid = data[1] & UVC_STREAM_FID; + /* + * Mark the buffer as done if we're at the beginning of a new frame. + * End of frame detection is better implemented by checking the EOF + * bit (FID bit toggling is delayed by one frame compared to the EOF + * bit), but some devices don't set the bit at end of frame (and the + * last payload can be lost anyway). We thus must check if the FID has + * been toggled. + * + * stream->last_fid is initialized to -1, and buf->bytesused to 0, + * so the first isochronous frame will never trigger an end of frame + * detection. + * + * Empty buffers (bytesused == 0) don't trigger end of frame detection + * as it doesn't make sense to return an empty buffer. This also + * avoids detecting end of frame conditions at FID toggling if the + * previous payload had the EOF bit set. + */ + if (fid != stream->last_fid && buf && buf->bytesused != 0) { + uvc_dbg(stream->dev, FRAME, + "Frame complete (FID bit toggled)\n"); + buf->state = UVC_BUF_STATE_READY; + + return -EAGAIN; + } + + /* + * Some cameras, when running two parallel streams (one MJPEG alongside + * another non-MJPEG stream), are known to lose the EOF packet for a frame. + * We can detect the end of a frame by checking for a new SOI marker, as + * the SOI always lies on the packet boundary between two frames for + * these devices. + */ + if (stream->dev->quirks & UVC_QUIRK_MJPEG_NO_EOF && + (stream->cur_format->fcc == V4L2_PIX_FMT_MJPEG || + stream->cur_format->fcc == V4L2_PIX_FMT_JPEG) && + buf && buf->bytesused != 0) { + const u8 *packet = data + header_len; + + if (len >= header_len + 2 && + packet[0] == 0xff && packet[1] == JPEG_MARKER_SOI) { + buf->state = UVC_BUF_STATE_READY; + buf->error = 1; + stream->last_fid ^= UVC_STREAM_FID; + return -EAGAIN; + } + } + /* * Increase the sequence number regardless of any buffer states, so * that discontinuous sequence numbers always indicate lost frames. @@ -1262,51 +1309,6 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, meta_buf->state = UVC_BUF_STATE_ACTIVE; } - /* - * Mark the buffer as done if we're at the beginning of a new frame. - * End of frame detection is better implemented by checking the EOF - * bit (FID bit toggling is delayed by one frame compared to the EOF - * bit), but some devices don't set the bit at end of frame (and the - * last payload can be lost anyway). We thus must check if the FID has - * been toggled. - * - * stream->last_fid is initialized to -1, so the first isochronous - * frame will never trigger an end of frame detection. - * - * Empty buffers (bytesused == 0) don't trigger end of frame detection - * as it doesn't make sense to return an empty buffer. This also - * avoids detecting end of frame conditions at FID toggling if the - * previous payload had the EOF bit set. - */ - if (fid != stream->last_fid && buf->bytesused != 0) { - uvc_dbg(stream->dev, FRAME, - "Frame complete (FID bit toggled)\n"); - buf->state = UVC_BUF_STATE_READY; - return -EAGAIN; - } - - /* - * Some cameras, when running two parallel streams (one MJPEG alongside - * another non-MJPEG stream), are known to lose the EOF packet for a frame. - * We can detect the end of a frame by checking for a new SOI marker, as - * the SOI always lies on the packet boundary between two frames for - * these devices. - */ - if (stream->dev->quirks & UVC_QUIRK_MJPEG_NO_EOF && - (stream->cur_format->fcc == V4L2_PIX_FMT_MJPEG || - stream->cur_format->fcc == V4L2_PIX_FMT_JPEG)) { - const u8 *packet = data + header_len; - - if (len >= header_len + 2 && - packet[0] == 0xff && packet[1] == JPEG_MARKER_SOI && - buf->bytesused != 0) { - buf->state = UVC_BUF_STATE_READY; - buf->error = 1; - stream->last_fid ^= UVC_STREAM_FID; - return -EAGAIN; - } - } - stream->last_fid = fid; return header_len; From 7b3027722ab87d33ff28b967dd353ac76998e255 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 25 Dec 2025 18:36:12 +0800 Subject: [PATCH 0699/2758] dt-bindings: media: sun4i-a10-video-engine: Add interconnect properties commit 018f2dc3a42098d3b04edd1480adc51d268adb14 upstream. The Allwinner video engine sits behind the MBUS that is represented as an interconnect. Make sure that the interconnect properties are valid in the binding. Fixes: d41662e52a03 ("media: dt-bindings: media: allwinner,sun4i-a10-video-engine: Add R40 compatible") Cc: stable@vger.kernel.org Signed-off-by: Chen-Yu Tsai Acked-by: Rob Herring (Arm) Acked-by: Jernej Skrabec Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- .../media/allwinner,sun4i-a10-video-engine.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml b/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml index 541325f900a1d..01f2afa023f04 100644 --- a/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml +++ b/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml @@ -63,6 +63,16 @@ properties: CMA pool to use for buffers allocation instead of the default CMA pool. + # FIXME: This should be made required eventually once every SoC will + # have the MBUS declared. + interconnects: + maxItems: 1 + + # FIXME: This should be made required eventually once every SoC will + # have the MBUS declared. + interconnect-names: + const: dma-mem + required: - compatible - reg From 85af1567f67a90dc3bc401ac9d1402e9512e7e84 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Tue, 9 Jun 2026 14:26:40 +0800 Subject: [PATCH 0700/2758] dt-bindings: power: imx93: Add MIPI PHY power domain commit 6aa38ef0eab32df5409b72d62509de6ba09cea50 upstream. Add MIPI PHY power domain for shared PHY resources used by both MIPI DSI and CSI blocks. Signed-off-by: Guoniu Zhou Reviewed-by: Frank Li Acked-by: Krzysztof Kozlowski Reviewed-by: Peng Fan Fixes: e9aa77d413c9 ("soc: imx: add i.MX93 media blk ctrl driver") Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- include/dt-bindings/power/fsl,imx93-power.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/dt-bindings/power/fsl,imx93-power.h b/include/dt-bindings/power/fsl,imx93-power.h index 17f9f015bf7df..071221fe5c57d 100644 --- a/include/dt-bindings/power/fsl,imx93-power.h +++ b/include/dt-bindings/power/fsl,imx93-power.h @@ -11,5 +11,6 @@ #define IMX93_MEDIABLK_PD_PXP 2 #define IMX93_MEDIABLK_PD_LCDIF 3 #define IMX93_MEDIABLK_PD_ISI 4 +#define IMX93_MEDIABLK_PD_MIPI_PHY 5 #endif From e3b42e326f4f30efcdc90bad5f5ba37e1cd91a47 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Mon, 6 Jul 2026 20:03:32 +0200 Subject: [PATCH 0701/2758] serial: msm: Disable DMA for kernel console UART commit 22dd2777e6c180e1c945b00f6d18550979436324 upstream. At the moment, concurrent writes from userspace and the kernel to the console can trigger a race condition that results in an infinite loop of the same messages printed over and over again. This is most likely to happen during system startup or shutdown when the init system starts/stops a large number of system services that interact with various kernel code. When userspace writes to the TTY device, the driver initiates an asynchronous DMA transfer and releases the port lock. At the same moment, the kernel printk path might grab the port lock and re-configure the UART controller for PIO, without waiting for the DMA operation to complete. It seems like this collision results in zero progress being reported for the DMA engine, so the same text is printed to the console over and over again. For the kernel console, we want a reliable output path that will be functional even during crashes etc. So rather than implementing complex code to synchronize the kernel console write routines with the userspace DMA write routines, simply disable DMA for the console UART instance. Similar checks exist in many other serial drivers, e.g. 8250_port.c, imx.c, sh-sci.c etc. Cc: stable Fixes: 3a878c430fd6 ("tty: serial: msm: Add TX DMA support") Signed-off-by: Stephan Gerhold Acked-by: Konrad Dybcio Link: https://patch.msgid.link/20260706-serial-msm-console-dma-collision-v1-1-3179b8cb1d89@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/msm_serial.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 2e999cb9c9744..bfa44b01c3e97 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -1228,7 +1228,8 @@ static int msm_startup(struct uart_port *port) data |= MSM_UART_MR1_AUTO_RFR_LEVEL0 & rfr_level; msm_write(port, data, MSM_UART_MR1); - if (msm_port->is_uartdm) { + /* Disable DMA for console to prevent PIO/DMA collisions */ + if (msm_port->is_uartdm && !uart_console(port)) { msm_request_tx_dma(msm_port, msm_port->uart.mapbase); msm_request_rx_dma(msm_port, msm_port->uart.mapbase); } From 229815484518a34d731675143d59c435428bb932 Mon Sep 17 00:00:00 2001 From: Tapio Reijonen Date: Mon, 15 Jun 2026 06:38:40 +0000 Subject: [PATCH 0702/2758] serial: max310x: implement gpio_chip::get_direction() commit a483b1a91b33b7533280e7c3efd2bc1275caef18 upstream. It's strongly recommended for GPIO drivers to always implement the .get_direction() callback - even when the direction is tracked in software. The GPIO core emits a warning when the callback is missing and a user reads the direction of a line, e.g. via /sys/kernel/debug/gpio. The MAX310X keeps the GPIO direction in the GPIOCFG register (a set bit selects output), which the existing direction_input/output callbacks already program, so the current direction can be read back directly. Fixes: f65444187a66 ("serial: New serial driver MAX310X") Cc: stable Signed-off-by: Tapio Reijonen Reviewed-by: Linus Walleij Reviewed-by: Bartosz Golaszewski Reviewed-by: Hugo Villeneuve Link: https://patch.msgid.link/20260615-b4-serial-max310x-gpio-get-direction-v2-1-4704ba2b181a@vaisala.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index ac7d3f197c3a5..09b9ab57d2b44 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -1212,6 +1212,17 @@ static int max310x_gpio_set(struct gpio_chip *chip, unsigned int offset, return 0; } +static int max310x_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + struct max310x_port *s = gpiochip_get_data(chip); + struct uart_port *port = &s->p[offset / 4].port; + unsigned int val; + + val = max310x_port_read(port, MAX310X_GPIOCFG_REG); + + return val & BIT(offset % 4) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; +} + static int max310x_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { struct max310x_port *s = gpiochip_get_data(chip); @@ -1421,6 +1432,7 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty s->gpio.owner = THIS_MODULE; s->gpio.parent = dev; s->gpio.label = devtype->name; + s->gpio.get_direction = max310x_gpio_get_direction; s->gpio.direction_input = max310x_gpio_direction_input; s->gpio.get = max310x_gpio_get; s->gpio.direction_output= max310x_gpio_direction_output; From df1fbb25afc1c82af934c1c0326a9c0ec07f7ad8 Mon Sep 17 00:00:00 2001 From: Matthias Feser Date: Tue, 26 May 2026 07:35:09 +0000 Subject: [PATCH 0703/2758] serial: 8250_omap: clear rx_running on zero-length DMA completes commit 061b627ba534230a18ec4d7251562af12325d06a upstream. On AM33xx RX DMA only triggers when the FIFO reaches the configured threshold (typically 48 bytes). For smaller bursts no DMA request is issued and the FIFO is drained by RX timeout. In this case __dma_rx_do_complete() can legitimately see count == 0. The current code exits early in this case and does not clear dma->rx_running, leaving the DMA state inconsistent. This can prevent RX DMA from restarting and may cause omap_8250_rx_dma_flush() to fail, marking DMA as broken. Fix this by clearing dma->rx_running once the DMA transfer has completed or been terminated, even if no data was transferred. Fixes: a5fd8945a478 ("serial: 8250: 8250_omap.c: Clear DMA RX running status only after DMA termination is done") Cc: stable Signed-off-by: Matthias Feser Reviewed-by: Moteen Shah Link: https://patch.msgid.link/BE3P281MB55155F2F5795E411F5A65282EE0B2@BE3P281MB5515.DEUP281.PROD.OUTLOOK.COM Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_omap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index c552c6b9a037f..3c7775df27eff 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -944,11 +944,12 @@ static void __dma_rx_do_complete(struct uart_8250_port *p) dev_err(p->port.dev, "teardown incomplete\n"); } } + + dma->rx_running = 0; if (!count) goto out; ret = tty_insert_flip_string(tty_port, dma->rx_buf, count); - dma->rx_running = 0; p->port.icount.rx += ret; p->port.icount.buf_overrun += count - ret; out: From 1741378a7a83dfd8e53a9196730df709b903cd33 Mon Sep 17 00:00:00 2001 From: Li Daming Date: Tue, 9 Jun 2026 15:09:09 +0100 Subject: [PATCH 0704/2758] rxrpc: serialize kernel accept preallocation with socket teardown commit dc175389b18c29a5303ee83169ec653adfae3e17 upstream. rxrpc_kernel_charge_accept() reads rx->backlog without any socket/backlog synchronization and passes that raw pointer into rxrpc_service_prealloc_one(). A concurrent rxrpc_discard_prealloc() sets rx->backlog = NULL and frees the backlog rings, so a kernel preallocation worker can keep using a freed struct rxrpc_backlog while updating *_backlog_head/tail and array slots. Serialize the state check and backlog lookup with the socket lock, and reject kernel preallocation once teardown has disabled listening or discarded the service backlog. Fixes: 00e907127e6f ("rxrpc: Preallocate peers, conns and calls for incoming service requests") Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Signed-off-by: Li Daming Signed-off-by: Ren Wei Signed-off-by: David Howells cc: Marc Dionne cc: Jeffrey Altman cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260609140911.838677-6-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/rxrpc/call_accept.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c index ee2d1319e69a8..47824120f1da1 100644 --- a/net/rxrpc/call_accept.c +++ b/net/rxrpc/call_accept.c @@ -471,13 +471,26 @@ int rxrpc_kernel_charge_accept(struct socket *sock, rxrpc_notify_rx_t notify_rx, unsigned long user_call_ID, gfp_t gfp, unsigned int debug_id) { - struct rxrpc_sock *rx = rxrpc_sk(sock->sk); - struct rxrpc_backlog *b = rx->backlog; + struct rxrpc_backlog *b; + struct rxrpc_sock *rx; + struct sock *sk; + int ret; - if (sock->sk->sk_state == RXRPC_CLOSE) - return -ESHUTDOWN; + sk = sock->sk; + rx = rxrpc_sk(sk); + + lock_sock(sk); + if (sk->sk_state != RXRPC_SERVER_LISTENING || !rx->backlog) { + ret = -ESHUTDOWN; + goto out; + } + + b = rx->backlog; + ret = rxrpc_service_prealloc_one(rx, b, notify_rx, user_call_ID, + gfp, debug_id); - return rxrpc_service_prealloc_one(rx, b, notify_rx, user_call_ID, - gfp, debug_id); +out: + release_sock(sk); + return ret; } EXPORT_SYMBOL(rxrpc_kernel_charge_accept); From a962bc8508592c4d51092edac68579bd8b18fe44 Mon Sep 17 00:00:00 2001 From: Jeffrey Altman Date: Tue, 9 Jun 2026 15:09:05 +0100 Subject: [PATCH 0705/2758] rxrpc: rxrpc_verify_data ensure rx_dec_buffer alloc commit 16c8ae9735c5bd7e54dd7478d6348e0fc860842d upstream. rxrpc_recvmsg_data() calls rxrpc_verify_data() whenever the rxrpc_call.rx_dec_buffer is unallocated and assumes that upon successful return that rx_dec_buffer must be allocated. However, rxrpc_verify_data() does not request an allocation if the rxrpc_skb_priv.len is zero. In addition, failure to allocate rx_dec_buffer will result in a call to skb_copy_bits() with a NULL destination which can trigger a NULL pointer dereference. To prevent these issues rxrpc_verify_data() is modified to always attempt to allocate the rxrpc_call.rx_dec_buffer if it is NULL. This issue was identified with assistance of a private sashiko instance. Fixes: d2bc90cf6c75cb ("rxrpc: Fix DATA decrypt vs splice() by copying data to buffer in recvmsg") Reported-by: Simon Horman Signed-off-by: Jeffrey Altman Signed-off-by: David Howells cc: Jiayuan Chen cc: Marc Dionne cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260609140911.838677-2-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/rxrpc/recvmsg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index c940600117a48..a3cf5358f16ed 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -161,7 +161,7 @@ static int rxrpc_verify_data(struct rxrpc_call *call, struct sk_buff *skb) struct rxrpc_skb_priv *sp = rxrpc_skb(skb); int ret; - if (sp->len > call->rx_dec_bsize) { + if (sp->len > call->rx_dec_bsize || !call->rx_dec_buffer) { /* Make sure we can hold a 1412-byte jumbo subpacket and make * sure that the buffer size is aligned to a crypto blocksize. */ From 5f470cc883416fea6d3bce18ef96bf91dd49ffc3 Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Tue, 9 Jun 2026 15:09:06 +0100 Subject: [PATCH 0706/2758] rxrpc: Don't move a peeked OOB message onto the pending queue commit 5801cff7d5d7b4e9d877dfb627b23eb63167f02c upstream. rxrpc_recvmsg_oob() takes a received oob message off recvmsg_oobq and, if a response is needed, moves it onto the pending_oobq tree. However, only the unlink from recvmsg_oobq is guarded by MSG_PEEK; the move onto pending_oobq always runs. As a result, reading a challenge with MSG_PEEK leaves the skb on recvmsg_oobq while also adding it to pending_oobq. Since struct sk_buff's rbnode shares storage with its next and prev pointers, rb_insert_color() overwrites the list linkage, and the skb, which holds a single reference, becomes reachable from both queues at once. When the socket is closed both queues are drained in turn. While draining recvmsg_oobq, __skb_unlink() follows the next and prev pointers that rbnode has overwritten and writes to a bad address. Also, as the skb holds a single reference but is freed from each queue, both the skb and the connection reference it holds are released twice. This leads to memory corruption and to a use-after-free caused by the connection refcount underflow. MSG_PEEK does not consume the message from the queue, so only unlink it from recvmsg_oobq and then move it onto pending_oobq or free it when the message is actually consumed. Fixes: 5800b1cf3fd8 ("rxrpc: Allow CHALLENGEs to the passed to the app for a RESPONSE") Signed-off-by: Hyunwoo Kim Signed-off-by: David Howells cc: Marc Dionne cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260609140911.838677-3-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/rxrpc/recvmsg.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index a3cf5358f16ed..82614cbdb60f5 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -262,12 +262,13 @@ static int rxrpc_recvmsg_oob(struct socket *sock, struct msghdr *msg, break; } - if (!(flags & MSG_PEEK)) + if (!(flags & MSG_PEEK)) { skb_unlink(skb, &rx->recvmsg_oobq); - if (need_response) - rxrpc_add_pending_oob(rx, skb); - else - rxrpc_free_skb(skb, rxrpc_skb_put_oob); + if (need_response) + rxrpc_add_pending_oob(rx, skb); + else + rxrpc_free_skb(skb, rxrpc_skb_put_oob); + } return ret; } From 83bb0ed050e2ad9453d8ef50e4d6e624eac6eed8 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 9 Jun 2026 15:09:07 +0100 Subject: [PATCH 0707/2758] rxrpc: Fix UAF in rxgk_issue_challenge() commit 107a4cb0d47e735830f852d83970d5c81f8e1e08 upstream. Fix rxgk_issue_challenge() to free the page containing the challenge content after invoking the tracepoint as the whdr passed to the tracepoint points into the page just freed. Fixes: 9d1d2b59341f ("rxrpc: rxgk: Implement the yfs-rxgk security class (GSSAPI)") Reported-by: Marc Dionne Signed-off-by: David Howells cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260609140911.838677-4-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/rxrpc/rxgk.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c index a1ee102abae18..77a67ace1d248 100644 --- a/net/rxrpc/rxgk.c +++ b/net/rxrpc/rxgk.c @@ -687,16 +687,17 @@ static int rxgk_issue_challenge(struct rxrpc_connection *conn) ret = do_udp_sendmsg(conn->local->socket, &msg, len); if (ret > 0) rxrpc_peer_mark_tx(conn->peer); - __free_page(page); if (ret < 0) { trace_rxrpc_tx_fail(conn->debug_id, serial, ret, rxrpc_tx_point_rxgk_challenge); + __free_page(page); return -EAGAIN; } trace_rxrpc_tx_packet(conn->debug_id, whdr, rxrpc_tx_point_rxgk_challenge); + __free_page(page); _leave(" = 0"); return 0; } From ea5aca7a64e111558ff7ffc3ef8b67444ca9b840 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 24 Jun 2026 17:38:16 +0100 Subject: [PATCH 0708/2758] rxrpc: Fix socket notification race commit e66f8f32f50116670dbbee5bc9e692cd2cd0c8f8 upstream. There's a race between rxrpc_recvmsg() and rxrpc_notify_socket(), whereby the latter's attempt to avoid disabling interrupts and taking the socket's recvmsg_lock if the call is already queued may happen simultaneously with the former's discarding of a call that has nothing queued. Fix this by removing the shortcut. Note that this only affects userspace's use of AF_RXRPC; the AFS filesystem driver doesn't use the socket queue. Fixes: 248f219cb8bc ("rxrpc: Rewrite the data and ack handling code") Link: https://sashiko.dev/#/patchset/20260616155749.2125907-1-dhowells%40redhat.com Signed-off-by: David Howells cc: Marc Dionne cc: Jeffrey Altman cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260624163819.3017002-10-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/rxrpc/recvmsg.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index 82614cbdb60f5..d17c81aac6aa8 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -27,8 +27,6 @@ void rxrpc_notify_socket(struct rxrpc_call *call) _enter("%d", call->debug_id); - if (!list_empty(&call->recvmsg_link)) - return; if (test_bit(RXRPC_CALL_RELEASED, &call->flags)) { rxrpc_see_call(call, rxrpc_call_see_notify_released); return; From 04c7103dc0923cc6ac95b97aa66bab70da7ace84 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 24 Jun 2026 17:38:17 +0100 Subject: [PATCH 0709/2758] rxrpc: Fix leak of released call in recvmsg(MSG_PEEK) commit 4bdb9e471f5b1ac9cbe4add5de7ff085a0ec303c upstream. Fix rxrpc_recvmsg() to also drop the ref it holds on an already-released call if MSG_PEEK is in force (the function holds a ref on the call irrespective of whether MSG_PEEK is specified or not). Fixes: 962fb1f651c2 ("rxrpc: Fix recv-recv race of completed call") Link: https://sashiko.dev/#/patchset/20260616155749.2125907-1-dhowells%40redhat.com Signed-off-by: David Howells cc: Marc Dionne cc: Jeffrey Altman cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260624163819.3017002-11-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/rxrpc/recvmsg.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index d17c81aac6aa8..d1ce15929bb85 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -528,8 +528,7 @@ int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, if (test_bit(RXRPC_CALL_RELEASED, &call->flags)) { rxrpc_see_call(call, rxrpc_call_see_already_released); mutex_unlock(&call->user_mutex); - if (!(flags & MSG_PEEK)) - rxrpc_put_call(call, rxrpc_call_put_recvmsg); + rxrpc_put_call(call, rxrpc_call_put_recvmsg); goto try_again; } From da371b003a4a44f44741275d5e8dc74181cbb017 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 24 Jun 2026 17:38:15 +0100 Subject: [PATCH 0710/2758] rxrpc: Fix potential infinite loop in rxrpc_recvmsg() commit 67a0332f442ef07713cd2d9c13d59db0f1c23648 upstream. Fix the wait in rxrpc_recvmsg() also take check the oob queue. Fixes: 5800b1cf3fd8 ("rxrpc: Allow CHALLENGEs to the passed to the app for a RESPONSE") Link: https://sashiko.dev/#/patchset/20260616155749.2125907-1-dhowells%40redhat.com Signed-off-by: David Howells cc: Marc Dionne cc: Jeffrey Altman cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260624163819.3017002-9-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/rxrpc/recvmsg.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index d1ce15929bb85..fb49563dc9271 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -436,7 +436,8 @@ int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, return -EAGAIN; } - if (list_empty(&rx->recvmsg_q)) { + if (list_empty(&rx->recvmsg_q) && + skb_queue_empty_lockless(&rx->recvmsg_oobq)) { ret = -EWOULDBLOCK; if (timeo == 0) { call = NULL; From af107968bc3a4cda67fc54b1a2d3ef25ab050c4e Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 24 Jun 2026 17:38:18 +0100 Subject: [PATCH 0711/2758] rxrpc: Fix rxrpc_rotate_tx_rotate() to check there's something to rotate commit a5462da5a349fc7f17ad5ebd899380260d03e7ed upstream. Fix rxrpc_rotate_tx_rotate() to check that there's something in the transmission buffer to be rotated before it attempts to rotate anything. Fixes: b341a0263b1b ("rxrpc: Implement progressive transmission queue struct") Link: https://sashiko.dev/#/patchset/20260618134802.2477777-1-dhowells%40redhat.com Signed-off-by: David Howells cc: Marc Dionne cc: Jeffrey Altman cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260624163819.3017002-12-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/rxrpc/input.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index ce761466b02d6..614c2c9cd387b 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -236,6 +236,9 @@ static bool rxrpc_rotate_tx_window(struct rxrpc_call *call, rxrpc_seq_t to, call->acks_lowest_nak = to; } + if (after(seq, to)) + return false; + /* We may have a left over fully-consumed buffer at the front that we * couldn't drop before (rotate_and_keep below). */ From e0ca365f2101384bb061b4e5292faf1e6bd4c245 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 24 Jun 2026 17:38:14 +0100 Subject: [PATCH 0712/2758] rxrpc: Fix oob challenge leak in cleanup after notification failure commit 092275882aec4a70ba55c3efb66fff947c81656a upstream. Fix rxrpc_notify_socket_oob() to return an indication of failure in the event that it failed to queue a packet and fix rxrpc_post_challenge() to clean up the connection ref in such an event. Fixes: 5800b1cf3fd8 ("rxrpc: Allow CHALLENGEs to the passed to the app for a RESPONSE") Link: https://sashiko.dev/#/patchset/20260616155749.2125907-1-dhowells%40redhat.com Signed-off-by: David Howells cc: Marc Dionne cc: Jeffrey Altman cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260624163819.3017002-8-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/rxrpc/ar-internal.h | 4 ++-- net/rxrpc/conn_event.c | 9 +++++++-- net/rxrpc/oob.c | 7 +++++-- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 98f2165159d72..ead3419f08b76 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -1355,9 +1355,9 @@ static inline struct rxrpc_net *rxrpc_net(struct net *net) } /* - * out_of_band.c + * oob.c */ -void rxrpc_notify_socket_oob(struct rxrpc_call *call, struct sk_buff *skb); +bool rxrpc_notify_socket_oob(struct rxrpc_call *call, struct sk_buff *skb); void rxrpc_add_pending_oob(struct rxrpc_sock *rx, struct sk_buff *skb); int rxrpc_sendmsg_oob(struct rxrpc_sock *rx, struct msghdr *msg, size_t len); diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c index c96ca615b787c..611c790bc6d0c 100644 --- a/net/rxrpc/conn_event.c +++ b/net/rxrpc/conn_event.c @@ -436,7 +436,7 @@ static bool rxrpc_post_challenge(struct rxrpc_connection *conn, struct rxrpc_skb_priv *sp = rxrpc_skb(skb); struct rxrpc_call *call = NULL; struct rxrpc_sock *rx; - bool respond = false; + bool respond = false, queued = false; sp->chall.conn = rxrpc_get_connection(conn, rxrpc_conn_get_challenge_input); @@ -472,8 +472,13 @@ static bool rxrpc_post_challenge(struct rxrpc_connection *conn, } if (call) - rxrpc_notify_socket_oob(call, skb); + queued = rxrpc_notify_socket_oob(call, skb); rcu_read_unlock(); + if (call && !queued) { + rxrpc_put_connection(conn, rxrpc_conn_put_challenge_input); + sp->chall.conn = NULL; + return false; + } if (!call) rxrpc_post_packet_to_conn(conn, skb); diff --git a/net/rxrpc/oob.c b/net/rxrpc/oob.c index 05ca9c1faa577..8a7fccb62f1af 100644 --- a/net/rxrpc/oob.c +++ b/net/rxrpc/oob.c @@ -32,11 +32,12 @@ struct rxrpc_oob_params { * Post an out-of-band message for attention by the socket or kernel service * associated with a reference call. */ -void rxrpc_notify_socket_oob(struct rxrpc_call *call, struct sk_buff *skb) +bool rxrpc_notify_socket_oob(struct rxrpc_call *call, struct sk_buff *skb) { struct rxrpc_skb_priv *sp = rxrpc_skb(skb); struct rxrpc_sock *rx; struct sock *sk; + bool queued = false; rcu_read_lock(); @@ -49,6 +50,7 @@ void rxrpc_notify_socket_oob(struct rxrpc_call *call, struct sk_buff *skb) skb->skb_mstamp_ns = rx->oob_id_counter++; rxrpc_get_skb(skb, rxrpc_skb_get_post_oob); skb_queue_tail(&rx->recvmsg_oobq, skb); + queued = true; trace_rxrpc_notify_socket(call->debug_id, sp->hdr.serial); if (rx->app_ops) @@ -56,11 +58,12 @@ void rxrpc_notify_socket_oob(struct rxrpc_call *call, struct sk_buff *skb) } spin_unlock_irq(&rx->recvmsg_lock); - if (!rx->app_ops && !sock_flag(sk, SOCK_DEAD)) + if (queued && !rx->app_ops && !sock_flag(sk, SOCK_DEAD)) sk->sk_data_ready(sk); } rcu_read_unlock(); + return queued; } /* From 523cb585672ae681b2d3b0d620aec5313774e2e2 Mon Sep 17 00:00:00 2001 From: Wyatt Feng Date: Wed, 24 Jun 2026 17:38:08 +0100 Subject: [PATCH 0713/2758] rxrpc: Fix ACKALL packet handling commit 9b6ce594808580b2a19e6e1aa459ef56c0153ac1 upstream. rxrpc_input_ackall() accepts ACKALL packets without checking whether the call is in a state that can legitimately have outstanding transmit buffers. A forged ACKALL can therefore reach a new service call in RXRPC_CALL_SERVER_RECV_REQUEST before any reply packets have been queued. In that state call->tx_top is zero and call->tx_queue is NULL, so rxrpc_rotate_tx_window() dereferences a NULL txqueue and triggers a null-pointer dereference. Fix the handling of ACKALL packets by the following means: (1) Add two new call states: RXRPC_CALL_CLIENT_PRE_SEND which indicates that the client call is connected, but nothing has been transmitted as yet; and RXRPC_CALL_CLIENT_AWAIT_ACK, which indicates that everything has been transmitted at least once, but we're now waiting for the stuff remaining in the Tx buffer to be ACK'd (retransmissions may still happen). The RXRPC_CALL_CLIENT_PRE_SEND state is set when the call is assigned a channel and transitions to RXRPC_CALL_CLIENT_SEND_REQUEST when the first packet is transmitted. RXRPC_CALL_CLIENT_AWAIT_REPLY is then narrowed in scope to indicate that all Tx packets have been ACK'd and we're now waiting for the reply to be received. (2) As per Wyatt Feng's original patch[1], the ACKALL handler then checks that the call state is one in which there might be stuff in the Tx buffer to ACK, but now this includes AWAIT_ACK rather than AWAIT_REPLY. ACKALL packets are ignored if received in the wrong state. Note that unlike Wyatt Feng's patch, it's no longer necessary to check to see if the Tx buffer exists as this the state set now covers this. (3) Make the ACKALL handler use call->tx_transmitted rather than call->tx_top as the former is explicitly the highest packet seq number transmitted, whereas the latter has a looser definition. Thanks to Jeffrey Altman for a description of the history of the ACKALL packet[1]. Fixes: b341a0263b1b ("rxrpc: Implement progressive transmission queue struct") Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Zhengchuan Liang Reported-by: Xin Liu Signed-off-by: Wyatt Feng Co-developed-by: David Howells Signed-off-by: David Howells cc: Ren Wei cc: Marc Dionne cc: linux-afs@lists.infradead.org Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20260616155749.2125907-2-dhowells@redhat.com/ [1] Link: https://lore.kernel.org/r/c0fd4fec-1576-4070-b31e-a37d5506f5ed@auristor.com/ [2] Reviewed-by: Jeffrey Altman Link: https://patch.msgid.link/20260624163819.3017002-2-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/rxrpc/ar-internal.h | 2 ++ net/rxrpc/call_event.c | 5 ++++- net/rxrpc/call_object.c | 2 ++ net/rxrpc/conn_client.c | 2 +- net/rxrpc/input.c | 23 +++++++++++++++++++---- net/rxrpc/sendmsg.c | 3 ++- 6 files changed, 30 insertions(+), 7 deletions(-) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index ead3419f08b76..d2b31d15851b9 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -650,7 +650,9 @@ enum rxrpc_call_event { enum rxrpc_call_state { RXRPC_CALL_UNINITIALISED, RXRPC_CALL_CLIENT_AWAIT_CONN, /* - client waiting for connection to become available */ + RXRPC_CALL_CLIENT_PRE_SEND, /* - client is connected, but hasn't sent anything yet */ RXRPC_CALL_CLIENT_SEND_REQUEST, /* - client sending request phase */ + RXRPC_CALL_CLIENT_AWAIT_ACK, /* - client awaiting ACKs of request */ RXRPC_CALL_CLIENT_AWAIT_REPLY, /* - client awaiting reply */ RXRPC_CALL_CLIENT_RECV_REPLY, /* - client receiving reply phase */ RXRPC_CALL_SERVER_PREALLOC, /* - service preallocation */ diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c index fec59d9338b9f..21be9c86d7a74 100644 --- a/net/rxrpc/call_event.c +++ b/net/rxrpc/call_event.c @@ -178,7 +178,7 @@ static void rxrpc_close_tx_phase(struct rxrpc_call *call) switch (__rxrpc_call_state(call)) { case RXRPC_CALL_CLIENT_SEND_REQUEST: - rxrpc_set_call_state(call, RXRPC_CALL_CLIENT_AWAIT_REPLY); + rxrpc_set_call_state(call, RXRPC_CALL_CLIENT_AWAIT_ACK); break; case RXRPC_CALL_SERVER_SEND_REPLY: rxrpc_set_call_state(call, RXRPC_CALL_SERVER_AWAIT_ACK); @@ -244,6 +244,8 @@ static void rxrpc_transmit_fresh_data(struct rxrpc_call *call, unsigned int limi break; } while (req.n < limit && before(seq, send_top)); + if (__rxrpc_call_state(call) == RXRPC_CALL_CLIENT_PRE_SEND) + rxrpc_set_call_state(call, RXRPC_CALL_CLIENT_SEND_REQUEST); if (txb->flags & RXRPC_LAST_PACKET) { rxrpc_close_tx_phase(call); tq = NULL; @@ -267,6 +269,7 @@ void rxrpc_transmit_some_data(struct rxrpc_call *call, unsigned int limit, fallthrough; case RXRPC_CALL_SERVER_SEND_REPLY: + case RXRPC_CALL_CLIENT_PRE_SEND: case RXRPC_CALL_CLIENT_SEND_REQUEST: if (!rxrpc_tx_window_space(call)) return; diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index fcb9d38bb5214..817ed9acb91e6 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -18,7 +18,9 @@ const char *const rxrpc_call_states[NR__RXRPC_CALL_STATES] = { [RXRPC_CALL_UNINITIALISED] = "Uninit ", [RXRPC_CALL_CLIENT_AWAIT_CONN] = "ClWtConn", + [RXRPC_CALL_CLIENT_PRE_SEND] = "ClPreSnd", [RXRPC_CALL_CLIENT_SEND_REQUEST] = "ClSndReq", + [RXRPC_CALL_CLIENT_AWAIT_ACK] = "ClAwtAck", [RXRPC_CALL_CLIENT_AWAIT_REPLY] = "ClAwtRpl", [RXRPC_CALL_CLIENT_RECV_REPLY] = "ClRcvRpl", [RXRPC_CALL_SERVER_PREALLOC] = "SvPrealc", diff --git a/net/rxrpc/conn_client.c b/net/rxrpc/conn_client.c index 9b757798deddb..48519f0de1853 100644 --- a/net/rxrpc/conn_client.c +++ b/net/rxrpc/conn_client.c @@ -449,7 +449,7 @@ static void rxrpc_activate_one_channel(struct rxrpc_connection *conn, trace_rxrpc_connect_call(call); call->tx_last_sent = ktime_get_real(); rxrpc_start_call_timer(call); - rxrpc_set_call_state(call, RXRPC_CALL_CLIENT_SEND_REQUEST); + rxrpc_set_call_state(call, RXRPC_CALL_CLIENT_PRE_SEND); wake_up(&call->waitq); } diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 614c2c9cd387b..5fee309d0e479 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -181,7 +181,8 @@ void rxrpc_congestion_degrade(struct rxrpc_call *call) if (call->cong_ca_state != RXRPC_CA_SLOW_START && call->cong_ca_state != RXRPC_CA_CONGEST_AVOIDANCE) return; - if (__rxrpc_call_state(call) == RXRPC_CALL_CLIENT_AWAIT_REPLY) + if (__rxrpc_call_state(call) == RXRPC_CALL_CLIENT_AWAIT_ACK || + __rxrpc_call_state(call) == RXRPC_CALL_CLIENT_AWAIT_REPLY) return; rtt = ns_to_ktime(call->srtt_us * (NSEC_PER_USEC / 8)); @@ -359,6 +360,7 @@ static void rxrpc_end_tx_phase(struct rxrpc_call *call, bool reply_begun, switch (__rxrpc_call_state(call)) { case RXRPC_CALL_CLIENT_SEND_REQUEST: + case RXRPC_CALL_CLIENT_AWAIT_ACK: case RXRPC_CALL_CLIENT_AWAIT_REPLY: if (reply_begun) { rxrpc_set_call_state(call, RXRPC_CALL_CLIENT_RECV_REPLY); @@ -697,6 +699,7 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb) switch (__rxrpc_call_state(call)) { case RXRPC_CALL_CLIENT_SEND_REQUEST: + case RXRPC_CALL_CLIENT_AWAIT_ACK: case RXRPC_CALL_CLIENT_AWAIT_REPLY: /* Received data implicitly ACKs all of the request * packets we sent when we're acting as a client. @@ -1157,10 +1160,12 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) if (hard_ack + 1 == 0) return rxrpc_proto_abort(call, 0, rxrpc_eproto_ackr_zero); - /* Ignore ACKs unless we are or have just been transmitting. */ + /* Ignore ACKs unless we are transmitting or are waiting for + * acknowledgement of the packets we've just been transmitting. + */ switch (__rxrpc_call_state(call)) { case RXRPC_CALL_CLIENT_SEND_REQUEST: - case RXRPC_CALL_CLIENT_AWAIT_REPLY: + case RXRPC_CALL_CLIENT_AWAIT_ACK: case RXRPC_CALL_SERVER_SEND_REPLY: case RXRPC_CALL_SERVER_AWAIT_ACK: break; @@ -1218,7 +1223,17 @@ static void rxrpc_input_ackall(struct rxrpc_call *call, struct sk_buff *skb) { struct rxrpc_ack_summary summary = { 0 }; - if (rxrpc_rotate_tx_window(call, call->tx_top, &summary)) + switch (__rxrpc_call_state(call)) { + case RXRPC_CALL_CLIENT_SEND_REQUEST: + case RXRPC_CALL_CLIENT_AWAIT_ACK: + case RXRPC_CALL_SERVER_SEND_REPLY: + case RXRPC_CALL_SERVER_AWAIT_ACK: + break; + default: + return; + } + + if (rxrpc_rotate_tx_window(call, call->tx_transmitted, &summary)) rxrpc_end_tx_phase(call, false, rxrpc_eproto_unexpected_ackall); } diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c index c35de4fd75e31..ed2c9a51005ad 100644 --- a/net/rxrpc/sendmsg.c +++ b/net/rxrpc/sendmsg.c @@ -366,7 +366,8 @@ static int rxrpc_send_data(struct rxrpc_sock *rx, if (state >= RXRPC_CALL_COMPLETE) goto maybe_error; ret = -EPROTO; - if (state != RXRPC_CALL_CLIENT_SEND_REQUEST && + if (state != RXRPC_CALL_CLIENT_PRE_SEND && + state != RXRPC_CALL_CLIENT_SEND_REQUEST && state != RXRPC_CALL_SERVER_ACK_REQUEST && state != RXRPC_CALL_SERVER_SEND_REPLY) { /* Request phase complete for this client call */ From e220ae559e5a0fc33e41ec6a348ea50387cb8b0f Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 24 Jun 2026 17:38:13 +0100 Subject: [PATCH 0714/2758] rxrpc: Fix the reception of a reply packet before data transmission commit a58e33405acd2584e730c1da72635f822ada6b49 upstream. Fix rxrpc_receiving_reply() to handle the reception of an apparent reply DATA packet before rxrpc has had a chance to send any request DATA packets on a client call by checking to see if the call has been exposed yet by sending the first packet. Without this, rxrpc_rotate_tx_window() might oops. Also fix rxrpc_rotate_tx_window() to handle the Tx queue being empty by changing the do...while loop into a while loop, just in case a call is abnormally terminated by an early reply before the last request packet is transmitted. Fixes: b341a0263b1b ("rxrpc: Implement progressive transmission queue struct") Link: https://sashiko.dev/#/patchset/20260616155749.2125907-1-dhowells%40redhat.com Signed-off-by: David Howells cc: Marc Dionne cc: Jeffrey Altman cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260624163819.3017002-7-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/rxrpc/input.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 5fee309d0e479..73cafe6bfa9f5 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -251,7 +251,7 @@ static bool rxrpc_rotate_tx_window(struct rxrpc_call *call, rxrpc_seq_t to, tq = call->tx_queue; } - do { + while (before_eq(seq, to)) { unsigned int ix = seq - call->tx_qbase; _debug("tq=%x seq=%x i=%d f=%x", tq->qbase, seq, ix, tq->bufs[ix]->flags); @@ -321,8 +321,7 @@ static bool rxrpc_rotate_tx_window(struct rxrpc_call *call, rxrpc_seq_t to, break; } } - - } while (before_eq(seq, to)); + } if (trace) trace_rxrpc_rack_update(call, summary); @@ -397,6 +396,14 @@ static bool rxrpc_receiving_reply(struct rxrpc_call *call) trace_rxrpc_timer_can(call, rxrpc_timer_trace_delayed_ack); } + /* Deal with an apparent reply coming in before we've got the request + * queued or transmitted. + */ + if (!test_bit(RXRPC_CALL_EXPOSED, &call->flags)) { + rxrpc_proto_abort(call, top, rxrpc_eproto_early_reply); + return false; + } + if (!test_bit(RXRPC_CALL_TX_LAST, &call->flags)) { if (!rxrpc_rotate_tx_window(call, top, &summary)) { rxrpc_proto_abort(call, top, rxrpc_eproto_early_reply); From 4d61b096fd744b61ba8e8601fe8c99c3afce74ed Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 24 Jun 2026 17:38:09 +0100 Subject: [PATCH 0715/2758] rxrpc: Fix leak of connection from OOB challenge commit 4b28876e78fd60979afa91fd2ec6ad9cc8b7a6d0 upstream. Fix leak of connection object from OOB challenge queue when response is provided by userspace. Fixes: 5800b1cf3fd8 ("rxrpc: Allow CHALLENGEs to the passed to the app for a RESPONSE") Link: https://sashiko.dev/#/patchset/20260609140911.838677-1-dhowells%40redhat.com Signed-off-by: David Howells cc: Marc Dionne cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260624163819.3017002-3-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/rxrpc/oob.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/rxrpc/oob.c b/net/rxrpc/oob.c index 8a7fccb62f1af..c80ee2487d094 100644 --- a/net/rxrpc/oob.c +++ b/net/rxrpc/oob.c @@ -213,6 +213,11 @@ static int rxrpc_respond_to_oob(struct rxrpc_sock *rx, break; } + switch (skb->mark) { + case RXRPC_OOB_CHALLENGE: + rxrpc_put_connection(sp->chall.conn, rxrpc_conn_put_oob); + break; + } rxrpc_free_skb(skb, rxrpc_skb_put_oob); return ret; } From 1297ae6aeebc3863cb437e42a1bc4cd6173e43d9 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 24 Jun 2026 17:38:10 +0100 Subject: [PATCH 0716/2758] rxrpc: Fix double unlock in rxrpc_recvmsg() commit a2f299b4d5510147fa8629a6aba2869bbcc88aea upstream. Fix a double unlock in rxrpc_recvmsg() when dealing with OOB messages. Fixes: 5800b1cf3fd8 ("rxrpc: Allow CHALLENGEs to the passed to the app for a RESPONSE") Link: https://sashiko.dev/#/patchset/20260609140911.838677-1-dhowells%40redhat.com Signed-off-by: David Howells cc: Marc Dionne cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260624163819.3017002-4-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/rxrpc/recvmsg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index fb49563dc9271..efcba4b2e74f0 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -470,7 +470,7 @@ int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, release_sock(&rx->sk); if (ret == -EAGAIN) goto try_again; - goto error_no_call; + goto error_trace; } /* Find the next call and dequeue it if we're not just peeking. If we From eec89c5f8e1adc1de4824042ef75f62aed804153 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 9 Jun 2026 15:09:08 +0100 Subject: [PATCH 0717/2758] afs: Fix netns teardown to cancel the preallocation charger commit 47694fbc9d24ab6bf210f91e8efe06a10a478064 upstream. Fix the teardown of an afs network namespace to make sure it cancels the work item that keeps the preallocated rxrpc call/conn/peer queue charged before incoming calls are disabled (i.e. listen 0). Also, if net->live is false because the afs netns is being deleted, make afs_charge_preallocation() skip charging and make afs_rx_new_call() avoid requeuing the charger. (This was found by AI review). Fixes: 00e907127e6f ("rxrpc: Preallocate peers, conns and calls for incoming service requests") Reported-by: Simon Horman Signed-off-by: David Howells cc: Li Daming cc: Ren Wei cc: Marc Dionne cc: Jeffrey Altman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260609140911.838677-5-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- fs/afs/rxrpc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index 588f8de51167f..d5cfd24e815bd 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -127,6 +127,7 @@ void afs_close_socket(struct afs_net *net) { _enter(""); + cancel_work_sync(&net->charge_preallocation_work); kernel_listen(net->socket, 0); flush_workqueue(afs_async_calls); @@ -742,7 +743,7 @@ void afs_charge_preallocation(struct work_struct *work) container_of(work, struct afs_net, charge_preallocation_work); struct afs_call *call = net->spare_incoming_call; - for (;;) { + while (READ_ONCE(net->live)) { if (!call) { call = afs_alloc_call(net, &afs_RXCMxxxx, GFP_KERNEL); if (!call) @@ -792,7 +793,8 @@ static void afs_rx_new_call(struct sock *sk, struct rxrpc_call *rxcall, if (!call->server) trace_afs_cm_no_server(call, rxrpc_kernel_remote_srx(call->peer)); - queue_work(afs_wq, &net->charge_preallocation_work); + if (net->live) + queue_work(afs_wq, &net->charge_preallocation_work); } /* From 70b2842734d831c908474779bb8a76daf55f782c Mon Sep 17 00:00:00 2001 From: Matvey Kovalev Date: Mon, 22 Jun 2026 10:08:37 +0100 Subject: [PATCH 0718/2758] afs: fix NULL pointer dereference in afs_get_tree() commit 0b70716081c6462be9b2928ad736d0d527b09678 upstream. afs_alloc_sbi() uses kzalloc for memory allocation. And, if ctx->dyn_root is not null, as->cell and as->volume are null. In trace_afs_get_tree() they are dereferenced. KASAN error message: KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] CPU: 2 PID: 18478 Comm: syz-executor.7 Not tainted 5.10.246-syzkaller #0 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014 RIP: 0010:perf_trace_afs_get_tree+0x1d9/0x550 include/trace/events/afs.h:1365 Call Trace: trace_afs_get_tree include/trace/events/afs.h:1365 [inline] afs_get_tree+0x922/0x1350 fs/afs/super.c:599 vfs_get_tree+0x8e/0x300 fs/super.c:1572 do_new_mount fs/namespace.c:3011 [inline] path_mount+0x14a5/0x2220 fs/namespace.c:3341 do_mount fs/namespace.c:3354 [inline] __do_sys_mount fs/namespace.c:3562 [inline] __se_sys_mount fs/namespace.c:3539 [inline] __x64_sys_mount+0x283/0x300 fs/namespace.c:3539 do_syscall_64+0x33/0x50 arch/x86/entry/common.c:46 entry_SYSCALL_64_after_hwframe+0x67/0xd1 Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: 80548b03991f5 ("afs: Add more tracepoints") Cc: stable@vger.kernel.org Signed-off-by: Matvey Kovalev Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-4-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/afs/super.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/afs/super.c b/fs/afs/super.c index 942f3e9800d7d..dec091e569c42 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -587,7 +587,8 @@ static int afs_get_tree(struct fs_context *fc) } fc->root = dget(sb->s_root); - trace_afs_get_tree(as->cell, as->volume); + if (!ctx->dyn_root) + trace_afs_get_tree(as->cell, as->volume); _leave(" = 0 [%p]", sb); return 0; From 0bd5f2786a878148190b4c7c259d01313d5f2357 Mon Sep 17 00:00:00 2001 From: Nan Li Date: Mon, 22 Jun 2026 10:08:35 +0100 Subject: [PATCH 0719/2758] afs: handle CB.InitCallBackState3 requests without a server record commit f3cf725cd284b7912d5522babb44721bf38c8887 upstream. The cache manager callback path now attaches the server record to an incoming call through the rxrpc peer's app data. That association is not guaranteed to exist for every callback request, and most callback handlers already tolerate that case. Make CB.InitCallBackState3 follow the same pattern by checking whether a server record was attached before using it. If the peer is not mapped to a server record, trace the request and ignore it, matching the existing behaviour for other unmatched callback requests. This keeps the callback handler consistent with the rest of the cache manager service and avoids depending on peer state that may not be available for a given request. Fixes: 40e8b52fe8c8 ("afs: Use the per-peer app data provided by rxrpc") Cc: stable@kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Signed-off-by: Nan Li Signed-off-by: Ren Wei Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-2-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/afs/cmservice.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/afs/cmservice.c b/fs/afs/cmservice.c index 5540ae1cad599..263c60c811a58 100644 --- a/fs/afs/cmservice.c +++ b/fs/afs/cmservice.c @@ -364,6 +364,11 @@ static int afs_deliver_cb_init_call_back_state3(struct afs_call *call) if (!afs_check_call_state(call, AFS_CALL_SV_REPLYING)) return afs_io_error(call, afs_io_error_cm_reply); + if (!call->server) { + trace_afs_cm_no_server_u(call, call->request); + return 0; + } + if (memcmp(call->request, &call->server->_uuid, sizeof(call->server->_uuid)) != 0) { pr_notice("Callback UUID does not match fileserver UUID\n"); trace_afs_cm_no_server_u(call, call->request); From c6a1bc2fbd14844c2050cf615a6f3537f547fcb0 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 24 Jun 2026 17:38:11 +0100 Subject: [PATCH 0720/2758] afs: Fix further netns teardown to cancel the preallocation charger commit 2daf8ac812c3d78c642fe7652f62e29df5e3da20 upstream. When an afs network namespace is torn down, it cancels and waits for the work item that keeps the preallocated rxrpc call/conn/peer queue charged before disabling incoming (i.e. listen 0), but there's a small window in which it can be requeued by an incoming call wending through the I/O thread. Fix this by cancelling the charger work item again after reducing the listen backlog to zero. Fixes: 47694fbc9d24 ("afs: Fix netns teardown to cancel the preallocation charger") Reported-by: Jakub Kicinski Signed-off-by: David Howells Link: https://sashiko.dev/#/patchset/20260609140911.838677-1-dhowells%40redhat.com cc: Li Daming cc: Ren Wei cc: Marc Dionne cc: Jeffrey Altman cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260624163819.3017002-5-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- fs/afs/rxrpc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index d5cfd24e815bd..6714a189d58fb 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -128,8 +128,13 @@ void afs_close_socket(struct afs_net *net) _enter(""); cancel_work_sync(&net->charge_preallocation_work); + /* Future work items should now see ->live is false. */ + kernel_listen(net->socket, 0); + + /* Make sure work items are no longer running. */ flush_workqueue(afs_async_calls); + cancel_work_sync(&net->charge_preallocation_work); if (net->spare_incoming_call) { afs_put_call(net->spare_incoming_call); From d14e96ddd616c8a9fff3b5bab3bf4af0cfc30ec4 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 24 Jun 2026 17:38:12 +0100 Subject: [PATCH 0721/2758] afs: Fix uncancelled rxrpc OOB message handler commit a4057e58b07005d0fe0491bdbf1868c1491909ee upstream. Fix AFS to cancel its OOB message processing (typically to respond to security challenges). Also move OOB message processing to afs_wq so that it's also waited for and make the OOB handler just return if the net namespace is no longer live. Fixes: 5800b1cf3fd8 ("rxrpc: Allow CHALLENGEs to the passed to the app for a RESPONSE") Link: https://sashiko.dev/#/patchset/20260609140911.838677-1-dhowells%40redhat.com Signed-off-by: David Howells cc: Li Daming cc: Ren Wei cc: Marc Dionne cc: Jeffrey Altman cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260624163819.3017002-6-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- fs/afs/cm_security.c | 3 ++- fs/afs/rxrpc.c | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/afs/cm_security.c b/fs/afs/cm_security.c index edcbd249d2024..103168c70dd4d 100644 --- a/fs/afs/cm_security.c +++ b/fs/afs/cm_security.c @@ -101,7 +101,8 @@ void afs_process_oob_queue(struct work_struct *work) struct sk_buff *oob; enum rxrpc_oob_type type; - while ((oob = rxrpc_kernel_dequeue_oob(net->socket, &type))) { + while (READ_ONCE(net->live) && + (oob = rxrpc_kernel_dequeue_oob(net->socket, &type))) { switch (type) { case RXRPC_OOB_CHALLENGE: afs_respond_to_challenge(oob); diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index 6714a189d58fb..d82916657a3dd 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -128,6 +128,7 @@ void afs_close_socket(struct afs_net *net) _enter(""); cancel_work_sync(&net->charge_preallocation_work); + cancel_work_sync(&net->rx_oob_work); /* Future work items should now see ->live is false. */ kernel_listen(net->socket, 0); @@ -148,6 +149,7 @@ void afs_close_socket(struct afs_net *net) kernel_sock_shutdown(net->socket, SHUT_RDWR); flush_workqueue(afs_async_calls); + cancel_work_sync(&net->rx_oob_work); net->socket->sk->sk_user_data = NULL; sock_release(net->socket); key_put(net->fs_cm_token_key); @@ -989,5 +991,6 @@ static void afs_rx_notify_oob(struct sock *sk, struct sk_buff *oob) { struct afs_net *net = sk->sk_user_data; - schedule_work(&net->rx_oob_work); + if (READ_ONCE(net->live)) + queue_work(afs_wq, &net->rx_oob_work); } From b134ad2f7c06b3c1098dcc95008e2045ff4b49b2 Mon Sep 17 00:00:00 2001 From: Ian Bridges Date: Wed, 24 Jun 2026 16:11:36 -0500 Subject: [PATCH 0722/2758] fbcon: fix NULL pointer dereference for a console without vc_data commit 5fae9a928482d4845bca169a3a098789203a1ca4 upstream. fbcon_new_modelist() runs when a framebuffer's modelist changes. For each console mapped to it with fb_display[i].mode set, it reads vc_cons[i].d and passes the vc_num to fbcon_set_disp(). This assumes a console with a mode set has a vc_data, but it can be NULL. fbcon_set_disp() sets fb_display[i].mode before it checks vc_data, and fbcon_deinit() leaves the mode set after the vc_data is freed. fbcon_new_modelist() then dereferences the NULL vc_data. Keep fb_display[i].mode set only while the console has a vc_data. Check vc_data before setting the mode in fbcon_set_disp(), and clear the mode in fbcon_deinit(). The existing mode check in fbcon_new_modelist() then skips such consoles. Reported-by: syzbot+42525d636f430fd5d983@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=42525d636f430fd5d983 Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Ian Bridges Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/core/fbcon.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 3f4a850d0e5b3..4b66a74619602 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -1274,6 +1274,7 @@ static void fbcon_deinit(struct vc_data *vc) int idx; fbcon_free_font(p); + p->mode = NULL; idx = con2fb_map[vc->vc_num]; if (idx == -1) @@ -1445,14 +1446,14 @@ static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var, p = &fb_display[unit]; - if (var_to_display(p, var, info)) - return; - vc = vc_cons[unit].d; if (!vc) return; + if (var_to_display(p, var, info)) + return; + default_mode = vc->vc_display_fg; svc = *default_mode; t = &fb_display[svc->vc_num]; From 18f901565ec18c56ccf89e1bd2cbc8abf6b0340b Mon Sep 17 00:00:00 2001 From: Jiacheng Yu Date: Thu, 14 May 2026 17:19:18 +0800 Subject: [PATCH 0723/2758] fbcon: Use correct type for vc_resize() return value commit 84202754fb1727dc3ee87f47104e4162ecc8ba3a upstream. The return value of vc_resize() is int, but fbcon_set_disp() stores it in an unsigned long variable. While the !ret check happens to work correctly by coincidence (negative values become large positive values), the types should match. Use int instead. Eliminates the following W=3 warning: drivers/video/fbdev/core/fbcon.c: In function 'fbcon_set_disp': drivers/video/fbdev/core/fbcon.c:1494:14: warning: implicit conversion from 'int' to 'unsigned long' [-Wconversion] Fixes: af0db3c1f898 ("fbdev: Fix vmalloc out-of-bounds write in fast_imageblit") Cc: stable@vger.kernel.org # v6.17+ Signed-off-by: Jiacheng Yu Reviewed-by: Thomas Zimmermann Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/core/fbcon.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 4b66a74619602..666293f0864f3 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -1441,8 +1441,7 @@ static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var, struct vc_data **default_mode, *vc; struct vc_data *svc; struct fbcon_par *par = info->fbcon_par; - int rows, cols; - unsigned long ret = 0; + int rows, cols, ret; p = &fb_display[unit]; From 3be3d05bb5acf91b759a24cbd0b171571d392ae2 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Tue, 10 Mar 2026 00:25:30 +0800 Subject: [PATCH 0724/2758] soc: fsl: qe_ports_ic: Add missing cleanup on device removal [ Upstream commit 7dad18a179741dbad9f40799e549fa9111987c0c ] Add a devm action handler to properly clean up the irq_domain and chained handler when the device is removed. Fixes: f0bcd784e1b7 ("soc: fsl: qe: Add an interrupt controller for QUICC Engine Ports") Signed-off-by: Felix Gu Link: https://lore.kernel.org/r/20260310-qe_ports_ic-v1-1-608293026561@gmail.com Signed-off-by: Christophe Leroy (CS GROUP) Signed-off-by: Sasha Levin --- drivers/soc/fsl/qe/qe_ports_ic.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/soc/fsl/qe/qe_ports_ic.c b/drivers/soc/fsl/qe/qe_ports_ic.c index 8e2107e2cde5b..5e3fae19f3141 100644 --- a/drivers/soc/fsl/qe/qe_ports_ic.c +++ b/drivers/soc/fsl/qe/qe_ports_ic.c @@ -17,6 +17,7 @@ struct qepic_data { void __iomem *reg; struct irq_domain *host; + int irq; }; static void qepic_mask(struct irq_data *d) @@ -92,11 +93,18 @@ static const struct irq_domain_ops qepic_host_ops = { .map = qepic_host_map, }; +static void qepic_remove(void *res) +{ + struct qepic_data *data = res; + + irq_set_chained_handler_and_data(data->irq, NULL, NULL); + irq_domain_remove(data->host); +} + static int qepic_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct qepic_data *data; - int irq; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) @@ -106,17 +114,18 @@ static int qepic_probe(struct platform_device *pdev) if (IS_ERR(data->reg)) return PTR_ERR(data->reg); - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; + data->irq = platform_get_irq(pdev, 0); + if (data->irq < 0) + return data->irq; data->host = irq_domain_add_linear(dev->of_node, 32, &qepic_host_ops, data); if (!data->host) return -ENODEV; - irq_set_chained_handler_and_data(irq, qepic_cascade, data); + irq_set_chained_handler_and_data(data->irq, qepic_cascade, data); + + return devm_add_action_or_reset(dev, qepic_remove, data); - return 0; } static const struct of_device_id qepic_match[] = { From 5eabaad3008f98f7029a8c6e44933ca52df7e8b9 Mon Sep 17 00:00:00 2001 From: Stafford Horne Date: Sat, 21 Mar 2026 09:22:05 +0000 Subject: [PATCH 0725/2758] openrisc: mm: Fix section mismatch between map_page and __set_fixmap [ Upstream commit 431400d49cac4bac944fc2d989921003314667ae ] This warning was reported by the kernel test robot: WARNING: modpost: vmlinux: section mismatch in reference: __set_fixmap+0x84 (section: .text.unlikely) -> map_page.isra.0 (section: .init.text) With commit 4735037b5d9b ("openrisc: Add text patching API support") the __set_fixmap function was moved out of the .init.text section. However, the map_page helper that it uses was not moved. This was not noticed on gcc 15.1.0 where map_page gets inlined unlike lkp@intel.com which uses gcc 10.5.0. Fix this by also moving the map_page helper function out of the init section. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202603211503.E8mMETO3-lkp@intel.com/ Fixes: 4735037b5d9b ("openrisc: Add text patching API support") Signed-off-by: Stafford Horne Signed-off-by: Sasha Levin --- arch/openrisc/mm/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/openrisc/mm/init.c b/arch/openrisc/mm/init.c index 89d8c6df88551..db7c844faeeb6 100644 --- a/arch/openrisc/mm/init.c +++ b/arch/openrisc/mm/init.c @@ -193,7 +193,7 @@ void __init mem_init(void) return; } -static int __init map_page(unsigned long va, phys_addr_t pa, pgprot_t prot) +static int map_page(unsigned long va, phys_addr_t pa, pgprot_t prot) { p4d_t *p4d; pud_t *pud; From 70df8141a83c8dbcee2cd611229c1b6cd3f29a28 Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Thu, 5 Feb 2026 16:40:37 +0800 Subject: [PATCH 0726/2758] clocksource/drivers/sun5i: Handle error returns from devm_reset_control_get_optional_exclusive() [ Upstream commit fed9f727cc3f91dde8278961269419083502b40e ] The devm_reset_control_get_optional_exclusive() function may return an ERR_PTR in case of genuine reset control acquisition errors, not just NULL which indicates the legitimate absence of an optional reset. Add an IS_ERR() check after the call in sun5i_timer_probe(). On error, return the error code to ensure proper failure handling rather than proceeding with invalid pointers. Fixes: 7e5bac610d2f ("clocksource/drivers/sun5i: Convert to platform device driver") Signed-off-by: Chen Ni Signed-off-by: Daniel Lezcano Acked-by: Chen-Yu Tsai Link: https://patch.msgid.link/20260205084037.3661261-1-nichen@iscas.ac.cn Signed-off-by: Sasha Levin --- drivers/clocksource/timer-sun5i.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/clocksource/timer-sun5i.c b/drivers/clocksource/timer-sun5i.c index f827d3f98f60e..d7e012992170b 100644 --- a/drivers/clocksource/timer-sun5i.c +++ b/drivers/clocksource/timer-sun5i.c @@ -286,6 +286,9 @@ static int sun5i_timer_probe(struct platform_device *pdev) } rstc = devm_reset_control_get_optional_exclusive(dev, NULL); + if (IS_ERR(rstc)) + return dev_err_probe(dev, PTR_ERR(rstc), + "failed to get reset\n"); if (rstc) reset_control_deassert(rstc); From 5b24e0903c58cdfd8ebfecb6ac5c5bacfd06e4e7 Mon Sep 17 00:00:00 2001 From: Max Zhen Date: Wed, 25 Mar 2026 18:06:42 -0700 Subject: [PATCH 0727/2758] accel/amdxdna: Fix leak when pinning ubuf pages [ Upstream commit d946347edc4f0a7b846325323d77e936a4c90d0f ] When pin_user_pages_fast() returns fewer pages than requested, the pages that were successfully pinned are not released, leading to a leak. Fix this by unpinning any partially pinned pages before returning failure. Fixes: bd72d4acda10 ("accel/amdxdna: Support user space allocated buffer") Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Max Zhen Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260326010642.2596525-1-lizhi.hou@amd.com Signed-off-by: Sasha Levin --- drivers/accel/amdxdna/amdxdna_ubuf.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/accel/amdxdna/amdxdna_ubuf.c b/drivers/accel/amdxdna/amdxdna_ubuf.c index 85390e3cc9f98..0e0cd69cd1fbf 100644 --- a/drivers/accel/amdxdna/amdxdna_ubuf.c +++ b/drivers/accel/amdxdna/amdxdna_ubuf.c @@ -146,13 +146,17 @@ struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev, ret = pin_user_pages_fast(va_ent[i].vaddr, npages, FOLL_WRITE | FOLL_LONGTERM, &ubuf->pages[start]); - if (ret < 0 || ret != npages) { - ret = -ENOMEM; + if (ret >= 0) { + start += ret; + if (ret != npages) { + XDNA_ERR(xdna, "Partially pinned pages %d/%u", ret, npages); + ret = -ENOMEM; + goto destroy_pages; + } + } else { XDNA_ERR(xdna, "Failed to pin pages ret %d", ret); goto destroy_pages; } - - start += ret; } exp_info.ops = &amdxdna_ubuf_dmabuf_ops; From 195ab348af4b2bdf5988f9397059317bb68fd797 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 10 Mar 2026 00:44:29 +0200 Subject: [PATCH 0728/2758] drm/rockchip: inno-hdmi: Switch to drmm_kzalloc() [ Upstream commit 3cc50e7f73fcf79f28660b9d91566b13cb62e520 ] Driver makes use of drmm_encoder_init() to initialize the encoder and automatically handle the cleanup by registering drm_encoder_cleanup() with drmm_add_action(). However, the internal structure containing the encoder part gets allocated with devm_kzalloc(), which happens while component_bind_all() is being called from Rockchip DRM driver. The component framework further ensures it is deallocated as part of releasing all the resources claimed during bind, which is triggered from component_unbind_all(). When the reference to the DRM device gets eventually dropped via drm_dev_put() in rockchip_drm_unbind(), drmm_encoder_alloc_release() attempts to access the now released encoder structure, leading to use-after-free. Ensure driver's internal structure is still reachable on encoder cleanup by switching from a device-managed allocation to a drm-managed one. Fixes: 969325a2597e ("drm/rockchip: inno-hdmi: Convert to drm bridge") Signed-off-by: Cristian Ciocaltea Signed-off-by: Heiko Stuebner Link: https://patch.msgid.link/20260310-drm-rk-fixes-v2-1-645ecfb43f49@collabora.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/rockchip/inno_hdmi-rockchip.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/rockchip/inno_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/inno_hdmi-rockchip.c index 97c20500f7901..28e6fb09aae73 100644 --- a/drivers/gpu/drm/rockchip/inno_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/inno_hdmi-rockchip.c @@ -14,6 +14,7 @@ #include #include +#include #include #include "rockchip_drm_drv.h" @@ -90,7 +91,7 @@ static int inno_hdmi_rockchip_bind(struct device *dev, struct device *master, vo const struct inno_hdmi_plat_data *plat_data; int ret; - hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); + hdmi = drmm_kzalloc(drm, sizeof(*hdmi), GFP_KERNEL); if (!hdmi) return -ENOMEM; From 6b0b92d1110eccccbd5ba2949bd2b9fb6ea5fc12 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 10 Mar 2026 00:44:30 +0200 Subject: [PATCH 0729/2758] drm/rockchip: dw_dp: Switch to drmm_kzalloc() [ Upstream commit ed9da8d23020352ad24c528db09b5acdd78b81fd ] Driver makes use of drmm_encoder_init() to initialize the encoder and automatically handle the cleanup by registering drm_encoder_cleanup() with drmm_add_action(). However, the internal structure containing the encoder part gets allocated with devm_kzalloc(), which happens while component_bind_all() is being called from Rockchip DRM driver. The component framework further ensures it is deallocated as part of releasing all the resources claimed during bind, which is triggered from component_unbind_all(). When the reference to the DRM device gets eventually dropped via drm_dev_put() in rockchip_drm_unbind(), drmm_encoder_alloc_release() attempts to access the now released encoder structure, leading to use-after-free. Ensure driver's internal structure is still reachable on encoder cleanup by switching from a device-managed allocation to a drm-managed one. Fixes: d68ba7bac955 ("drm/rockchip: Add RK3588 DPTX output support") Signed-off-by: Cristian Ciocaltea Signed-off-by: Heiko Stuebner Link: https://patch.msgid.link/20260310-drm-rk-fixes-v2-2-645ecfb43f49@collabora.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/rockchip/dw_dp-rockchip.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c index dac3d202971ed..532af476d250a 100644 --- a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -82,7 +83,7 @@ static int dw_dp_rockchip_bind(struct device *dev, struct device *master, void * struct drm_connector *connector; int ret; - dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL); + dp = drmm_kzalloc(drm_dev, sizeof(*dp), GFP_KERNEL); if (!dp) return -ENOMEM; From 33bc9fba3ae05863fabe3436ddca2ca4f4939c5e Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 10 Mar 2026 00:44:31 +0200 Subject: [PATCH 0730/2758] drm/rockchip: dw_dp: Fix null-ptr-deref in dw_dp_remove() [ Upstream commit 9456381d8b60bb7dd42f2f04afe5ee4ce6e0bc12 ] Attempting to access driver data in the platform driver ->remove() callback may lead to a null pointer dereference since there is no guaranty that the component ->bind() callback invoking platform_set_drvdata() was executed. A common scenario is when Rockchip DRM driver didn't manage to run component_bind_all() because of an (unrelated) error causing early return from rockchip_drm_bind(). Drop the unnecessary call to platform_get_drvdata() and, instead, reference the target device structure via platform_device. Fixes: d68ba7bac955 ("drm/rockchip: Add RK3588 DPTX output support") Signed-off-by: Cristian Ciocaltea Signed-off-by: Heiko Stuebner Link: https://patch.msgid.link/20260310-drm-rk-fixes-v2-3-645ecfb43f49@collabora.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/rockchip/dw_dp-rockchip.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c index 532af476d250a..8945a245398ca 100644 --- a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c @@ -133,9 +133,7 @@ static int dw_dp_probe(struct platform_device *pdev) static void dw_dp_remove(struct platform_device *pdev) { - struct rockchip_dw_dp *dp = platform_get_drvdata(pdev); - - component_del(dp->dev, &dw_dp_rockchip_component_ops); + component_del(&pdev->dev, &dw_dp_rockchip_component_ops); } static const struct dw_dp_plat_data rk3588_dp_plat_data = { From 13e587aa74a5f80f67f4671b36828227666fd011 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Fri, 27 Feb 2026 14:31:10 +0100 Subject: [PATCH 0731/2758] drm/rockchip: Test for imported buffers with drm_gem_is_imported() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 9fc0da81916250f343599a4dd259f097196bf0fb ] Instead of testing import_attach for imported GEM buffers, invoke drm_gem_is_imported() to do the test. The test itself does not change. Signed-off-by: Thomas Zimmermann Cc: Sandy Huang Cc: Heiko Stübner Cc: Andy Yan Cc: linux-rockchip@lists.infradead.org Fixes: b57aa47d39e9 ("drm/gem: Test for imported GEM buffers with helper") Closes: https://lore.kernel.org/dri-devel/38d09d34.4354.196379aa560.Coremail.andyshrk@163.com/ Signed-off-by: Heiko Stuebner Link: https://patch.msgid.link/20260227133113.235940-11-tzimmermann@suse.de Signed-off-by: Sasha Levin --- drivers/gpu/drm/rockchip/rockchip_drm_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c index 09d14a072d274..b188539dca0b2 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c @@ -334,7 +334,7 @@ void rockchip_gem_free_object(struct drm_gem_object *obj) struct rockchip_drm_private *private = drm->dev_private; struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj); - if (obj->import_attach) { + if (drm_gem_is_imported(obj)) { if (private->domain) { rockchip_gem_iommu_unmap(rk_obj); } else { From 3e61215a443b45c7134c816ca80ab02ec1083055 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 11 Mar 2026 11:14:43 +0200 Subject: [PATCH 0732/2758] drm/tidss: Drop extra drm_mode_config_reset() call [ Upstream commit f468fef38716f667e805e0fa2c497c6b9c325bb9 ] We are calling drm_mode_config_reset() twice at probe time. There's no reason for this and the second call can be removed, reducing work at probe time slightly. Fixes: 32a1795f57ee ("drm/tidss: New driver for TI Keystone platform Display SubSystem") Acked-by: Maxime Ripard Link: https://patch.msgid.link/20260311-tidss-minor-fixes-v2-1-cb4479784458@ideasonboard.com Signed-off-by: Tomi Valkeinen Signed-off-by: Sasha Levin --- drivers/gpu/drm/tidss/tidss_kms.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/tidss/tidss_kms.c b/drivers/gpu/drm/tidss/tidss_kms.c index 8bb93194e5ac6..b4779c09a1bfa 100644 --- a/drivers/gpu/drm/tidss/tidss_kms.c +++ b/drivers/gpu/drm/tidss/tidss_kms.c @@ -287,8 +287,6 @@ int tidss_modeset_init(struct tidss_device *tidss) if (ret) return ret; - drm_mode_config_reset(ddev); - dev_dbg(tidss->dev, "%s done\n", __func__); return 0; From 63f443384979563f16f70420f4fa85bba31238ca Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Wed, 25 Mar 2026 16:16:08 -0700 Subject: [PATCH 0733/2758] drm/gpusvm: Reject VMAs with VM_IO or VM_PFNMAP when creating SVM ranges MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit b82a225e57a334335a21462b75ee2223bc6efe6d ] VMAs marked with VM_IO or VM_PFNMAP are not backed by struct page objects, which GPUSVM requires in order to operate correctly. In particular, get_pages() relies on hmm_range_fault() to resolve struct pages for the target range. Attempting to create an SVM range on such VMAs results in repeated get_pages() failures and can lead to an infinite loop inside a driver’s page‑fault handler. Prevent this by rejecting ranges on VM_IO or VM_PFNMAP VMAs and returning -EIO. Fixes: 99624bdff867 ("drm/gpusvm: Add support for GPU Shared Virtual Memory") Signed-off-by: Matthew Brost Reviewed-by: Himal Prasad Ghimiray Link: https://patch.msgid.link/20260325231608.25581-1-matthew.brost@intel.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/drm_gpusvm.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/drm_gpusvm.c b/drivers/gpu/drm/drm_gpusvm.c index 4b928fda5b127..7993e85c05661 100644 --- a/drivers/gpu/drm/drm_gpusvm.c +++ b/drivers/gpu/drm/drm_gpusvm.c @@ -1065,6 +1065,11 @@ drm_gpusvm_range_find_or_insert(struct drm_gpusvm *gpusvm, goto err_notifier_remove; } + if (vas->vm_flags & (VM_IO | VM_PFNMAP)) { + err = -EIO; + goto err_notifier_remove; + } + range = drm_gpusvm_range_find(notifier, fault_addr, fault_addr + 1); if (range) goto out_mmunlock; From 95a1c14bdf450e0cc5268ebe33332af5bb7ebcd6 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Mon, 30 Mar 2026 09:37:00 -0700 Subject: [PATCH 0734/2758] accel/amdxdna: Create shared functions for AIE2 and AIE4 [ Upstream commit 5a55a5da1f01274c07359b09abec952ec9f05105 ] The AIE4 platform uses a mailbox management channel mechanism similar to AIE2 to communicate with the firmware. Create aie.h and aie.c and move the functions and structures that can be shared by both platforms from the AIE2-specific files into these common files. This allows AIE2 and AIE4 to reuse the same implementation and reduces code duplication. Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260330163705.3153647-2-lizhi.hou@amd.com Stable-dep-of: 6e87001fe19f ("accel/amdxdna: Adjust size for copy_to_user()") Signed-off-by: Sasha Levin --- drivers/accel/amdxdna/Makefile | 1 + drivers/accel/amdxdna/aie.c | 89 +++++++++++++++ drivers/accel/amdxdna/aie.h | 31 ++++++ drivers/accel/amdxdna/aie2_ctx.c | 4 +- drivers/accel/amdxdna/aie2_error.c | 12 +-- drivers/accel/amdxdna/aie2_message.c | 138 +++++++++--------------- drivers/accel/amdxdna/aie2_pci.c | 107 ++++++------------ drivers/accel/amdxdna/aie2_pci.h | 26 +---- drivers/accel/amdxdna/aie2_pm.c | 6 +- drivers/accel/amdxdna/aie2_smu.c | 22 ++-- drivers/accel/amdxdna/amdxdna_pci_drv.h | 8 ++ drivers/accel/amdxdna/npu1_regs.c | 4 +- drivers/accel/amdxdna/npu4_regs.c | 4 +- drivers/accel/amdxdna/npu5_regs.c | 2 +- drivers/accel/amdxdna/npu6_regs.c | 2 +- 15 files changed, 246 insertions(+), 210 deletions(-) create mode 100644 drivers/accel/amdxdna/aie.c create mode 100644 drivers/accel/amdxdna/aie.h diff --git a/drivers/accel/amdxdna/Makefile b/drivers/accel/amdxdna/Makefile index cf9bf19dedb9e..5c7911554c46e 100644 --- a/drivers/accel/amdxdna/Makefile +++ b/drivers/accel/amdxdna/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only amdxdna-y := \ + aie.o \ aie2_ctx.o \ aie2_error.o \ aie2_message.o \ diff --git a/drivers/accel/amdxdna/aie.c b/drivers/accel/amdxdna/aie.c new file mode 100644 index 0000000000000..4b3d4493128e9 --- /dev/null +++ b/drivers/accel/amdxdna/aie.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026, Advanced Micro Devices, Inc. + */ + +#include + +#include "aie.h" +#include "amdxdna_mailbox_helper.h" +#include "amdxdna_mailbox.h" +#include "amdxdna_pci_drv.h" + +void aie_dump_mgmt_chann_debug(struct aie_device *aie) +{ + struct amdxdna_dev *xdna = aie->xdna; + + XDNA_DBG(xdna, "i2x tail 0x%x", aie->mgmt_i2x.mb_tail_ptr_reg); + XDNA_DBG(xdna, "i2x head 0x%x", aie->mgmt_i2x.mb_head_ptr_reg); + XDNA_DBG(xdna, "i2x ringbuf 0x%x", aie->mgmt_i2x.rb_start_addr); + XDNA_DBG(xdna, "i2x rsize 0x%x", aie->mgmt_i2x.rb_size); + XDNA_DBG(xdna, "x2i tail 0x%x", aie->mgmt_x2i.mb_tail_ptr_reg); + XDNA_DBG(xdna, "x2i head 0x%x", aie->mgmt_x2i.mb_head_ptr_reg); + XDNA_DBG(xdna, "x2i ringbuf 0x%x", aie->mgmt_x2i.rb_start_addr); + XDNA_DBG(xdna, "x2i rsize 0x%x", aie->mgmt_x2i.rb_size); + XDNA_DBG(xdna, "x2i chann index 0x%x", aie->mgmt_chan_idx); + XDNA_DBG(xdna, "mailbox protocol major 0x%x", aie->mgmt_prot_major); + XDNA_DBG(xdna, "mailbox protocol minor 0x%x", aie->mgmt_prot_minor); +} + +void aie_destroy_chann(struct aie_device *aie, struct mailbox_channel **chann) +{ + struct amdxdna_dev *xdna = aie->xdna; + + drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock)); + + if (!*chann) + return; + + xdna_mailbox_stop_channel(*chann); + xdna_mailbox_free_channel(*chann); + *chann = NULL; +} + +int aie_send_mgmt_msg_wait(struct aie_device *aie, struct xdna_mailbox_msg *msg) +{ + struct amdxdna_dev *xdna = aie->xdna; + struct xdna_notify *hdl = msg->handle; + int ret; + + drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock)); + + if (!aie->mgmt_chann) + return -ENODEV; + + ret = xdna_send_msg_wait(xdna, aie->mgmt_chann, msg); + if (ret == -ETIME) + aie_destroy_chann(aie, &aie->mgmt_chann); + + if (!ret && *hdl->status) { + XDNA_ERR(xdna, "command opcode 0x%x failed, status 0x%x", + msg->opcode, *hdl->data); + ret = -EINVAL; + } + + return ret; +} + +int aie_check_protocol(struct aie_device *aie, u32 fw_major, u32 fw_minor) +{ + const struct amdxdna_fw_feature_tbl *feature; + bool found = false; + + for (feature = aie->xdna->dev_info->fw_feature_tbl; + feature->major; feature++) { + if (feature->major != fw_major) + continue; + if (fw_minor < feature->min_minor) + continue; + if (feature->max_minor > 0 && fw_minor > feature->max_minor) + continue; + + aie->feature_mask |= feature->features; + + /* firmware version matches one of the driver support entry */ + found = true; + } + + return found ? 0 : -EOPNOTSUPP; +} diff --git a/drivers/accel/amdxdna/aie.h b/drivers/accel/amdxdna/aie.h new file mode 100644 index 0000000000000..1bea14b79c7c9 --- /dev/null +++ b/drivers/accel/amdxdna/aie.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2026, Advanced Micro Devices, Inc. + */ +#ifndef _AIE_H_ +#define _AIE_H_ + +#include "amdxdna_pci_drv.h" +#include "amdxdna_mailbox.h" + +struct aie_device { + struct amdxdna_dev *xdna; + struct mailbox_channel *mgmt_chann; + struct xdna_mailbox_chann_res mgmt_x2i; + struct xdna_mailbox_chann_res mgmt_i2x; + u32 mgmt_chan_idx; + u32 mgmt_prot_major; + u32 mgmt_prot_minor; + unsigned long feature_mask; +}; + +#define DECLARE_AIE_MSG(name, op) \ + DECLARE_XDNA_MSG_COMMON(name, op, -1) +#define AIE_FEATURE_ON(aie, feature) test_bit(feature, &(aie)->feature_mask) + +void aie_dump_mgmt_chann_debug(struct aie_device *aie); +void aie_destroy_chann(struct aie_device *aie, struct mailbox_channel **chann); +int aie_send_mgmt_msg_wait(struct aie_device *aie, struct xdna_mailbox_msg *msg); +int aie_check_protocol(struct aie_device *aie, u32 fw_major, u32 fw_minor); + +#endif /* _AIE_H_ */ diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c index eed3d0ec54133..2dc69900ca010 100644 --- a/drivers/accel/amdxdna/aie2_ctx.c +++ b/drivers/accel/amdxdna/aie2_ctx.c @@ -515,7 +515,7 @@ static int aie2_alloc_resource(struct amdxdna_hwctx *hwctx) struct alloc_requests *xrs_req; int ret; - if (AIE2_FEATURE_ON(xdna->dev_handle, AIE2_TEMPORAL_ONLY)) { + if (AIE_FEATURE_ON(&xdna->dev_handle->aie, AIE2_TEMPORAL_ONLY)) { hwctx->num_unused_col = xdna->dev_handle->total_col - hwctx->num_col; hwctx->num_col = xdna->dev_handle->total_col; return aie2_create_context(xdna->dev_handle, hwctx); @@ -552,7 +552,7 @@ static void aie2_release_resource(struct amdxdna_hwctx *hwctx) struct amdxdna_dev *xdna = hwctx->client->xdna; int ret; - if (AIE2_FEATURE_ON(xdna->dev_handle, AIE2_TEMPORAL_ONLY)) { + if (AIE_FEATURE_ON(&xdna->dev_handle->aie, AIE2_TEMPORAL_ONLY)) { ret = aie2_destroy_context(xdna->dev_handle, hwctx); if (ret && ret != -ENODEV) XDNA_ERR(xdna, "Destroy temporal only context failed, ret %d", ret); diff --git a/drivers/accel/amdxdna/aie2_error.c b/drivers/accel/amdxdna/aie2_error.c index 58abb59b61535..9d20e956c020e 100644 --- a/drivers/accel/amdxdna/aie2_error.c +++ b/drivers/accel/amdxdna/aie2_error.c @@ -249,12 +249,12 @@ static u32 aie2_error_backtrack(struct amdxdna_dev_hdl *ndev, void *err_info, u3 enum aie_error_category cat; cat = aie_get_error_category(err->row, err->event_id, err->mod_type); - XDNA_ERR(ndev->xdna, "Row: %d, Col: %d, module %d, event ID %d, category %d", + XDNA_ERR(ndev->aie.xdna, "Row: %d, Col: %d, module %d, event ID %d, category %d", err->row, err->col, err->mod_type, err->event_id, cat); if (err->col >= 32) { - XDNA_WARN(ndev->xdna, "Invalid column number"); + XDNA_WARN(ndev->aie.xdna, "Invalid column number"); break; } @@ -294,7 +294,7 @@ static void aie2_error_worker(struct work_struct *err_work) e = container_of(err_work, struct async_event, work); - xdna = e->ndev->xdna; + xdna = e->ndev->aie.xdna; if (e->resp.status == MAX_AIE2_STATUS_CODE) return; @@ -329,7 +329,7 @@ static void aie2_error_worker(struct work_struct *err_work) void aie2_error_async_events_free(struct amdxdna_dev_hdl *ndev) { - struct amdxdna_dev *xdna = ndev->xdna; + struct amdxdna_dev *xdna = ndev->aie.xdna; struct async_events *events; events = ndev->async_events; @@ -344,7 +344,7 @@ void aie2_error_async_events_free(struct amdxdna_dev_hdl *ndev) int aie2_error_async_events_alloc(struct amdxdna_dev_hdl *ndev) { - struct amdxdna_dev *xdna = ndev->xdna; + struct amdxdna_dev *xdna = ndev->aie.xdna; u32 total_col = ndev->total_col; u32 total_size = ASYNC_BUF_SIZE * total_col; struct async_events *events; @@ -402,7 +402,7 @@ int aie2_error_async_events_alloc(struct amdxdna_dev_hdl *ndev) int aie2_get_array_async_error(struct amdxdna_dev_hdl *ndev, struct amdxdna_drm_get_array *args) { - struct amdxdna_dev *xdna = ndev->xdna; + struct amdxdna_dev *xdna = ndev->aie.xdna; drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock)); diff --git a/drivers/accel/amdxdna/aie2_message.c b/drivers/accel/amdxdna/aie2_message.c index a1c546c3e81c3..ccf87b1aa1ccc 100644 --- a/drivers/accel/amdxdna/aie2_message.c +++ b/drivers/accel/amdxdna/aie2_message.c @@ -16,6 +16,7 @@ #include #include +#include "aie.h" #include "aie2_msg_priv.h" #include "aie2_pci.h" #include "amdxdna_ctx.h" @@ -24,38 +25,12 @@ #include "amdxdna_mailbox_helper.h" #include "amdxdna_pci_drv.h" -#define DECLARE_AIE2_MSG(name, op) \ - DECLARE_XDNA_MSG_COMMON(name, op, MAX_AIE2_STATUS_CODE) - #define EXEC_MSG_OPS(xdna) ((xdna)->dev_handle->exec_msg_ops) -static int aie2_send_mgmt_msg_wait(struct amdxdna_dev_hdl *ndev, - struct xdna_mailbox_msg *msg) -{ - struct amdxdna_dev *xdna = ndev->xdna; - struct xdna_notify *hdl = msg->handle; - int ret; - - if (!ndev->mgmt_chann) - return -ENODEV; - - ret = xdna_send_msg_wait(xdna, ndev->mgmt_chann, msg); - if (ret == -ETIME) - aie2_destroy_mgmt_chann(ndev); - - if (!ret && *hdl->status != AIE2_STATUS_SUCCESS) { - XDNA_ERR(xdna, "command opcode 0x%x failed, status 0x%x", - msg->opcode, *hdl->data); - ret = -EINVAL; - } - - return ret; -} - void *aie2_alloc_msg_buffer(struct amdxdna_dev_hdl *ndev, u32 *size, dma_addr_t *dma_addr) { - struct amdxdna_dev *xdna = ndev->xdna; + struct amdxdna_dev *xdna = ndev->aie.xdna; void *vaddr; int order; @@ -79,7 +54,7 @@ void *aie2_alloc_msg_buffer(struct amdxdna_dev_hdl *ndev, u32 *size, void aie2_free_msg_buffer(struct amdxdna_dev_hdl *ndev, size_t size, void *cpu_addr, dma_addr_t dma_addr) { - struct amdxdna_dev *xdna = ndev->xdna; + struct amdxdna_dev *xdna = ndev->aie.xdna; if (amdxdna_iova_on(xdna)) { amdxdna_iommu_free(xdna, size, cpu_addr, dma_addr); @@ -91,12 +66,12 @@ void aie2_free_msg_buffer(struct amdxdna_dev_hdl *ndev, size_t size, int aie2_suspend_fw(struct amdxdna_dev_hdl *ndev) { - DECLARE_AIE2_MSG(suspend, MSG_OP_SUSPEND); + DECLARE_AIE_MSG(suspend, MSG_OP_SUSPEND); int ret; - ret = aie2_send_mgmt_msg_wait(ndev, &msg); + ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); if (ret) { - XDNA_ERR(ndev->xdna, "Failed to suspend fw, ret %d", ret); + XDNA_ERR(ndev->aie.xdna, "Failed to suspend fw, ret %d", ret); return ret; } @@ -105,22 +80,22 @@ int aie2_suspend_fw(struct amdxdna_dev_hdl *ndev) int aie2_resume_fw(struct amdxdna_dev_hdl *ndev) { - DECLARE_AIE2_MSG(suspend, MSG_OP_RESUME); + DECLARE_AIE_MSG(suspend, MSG_OP_RESUME); - return aie2_send_mgmt_msg_wait(ndev, &msg); + return aie_send_mgmt_msg_wait(&ndev->aie, &msg); } int aie2_set_runtime_cfg(struct amdxdna_dev_hdl *ndev, u32 type, u64 value) { - DECLARE_AIE2_MSG(set_runtime_cfg, MSG_OP_SET_RUNTIME_CONFIG); + DECLARE_AIE_MSG(set_runtime_cfg, MSG_OP_SET_RUNTIME_CONFIG); int ret; req.type = type; req.value = value; - ret = aie2_send_mgmt_msg_wait(ndev, &msg); + ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); if (ret) { - XDNA_ERR(ndev->xdna, "Failed to set runtime config, ret %d", ret); + XDNA_ERR(ndev->aie.xdna, "Failed to set runtime config, ret %d", ret); return ret; } @@ -129,13 +104,13 @@ int aie2_set_runtime_cfg(struct amdxdna_dev_hdl *ndev, u32 type, u64 value) int aie2_get_runtime_cfg(struct amdxdna_dev_hdl *ndev, u32 type, u64 *value) { - DECLARE_AIE2_MSG(get_runtime_cfg, MSG_OP_GET_RUNTIME_CONFIG); + DECLARE_AIE_MSG(get_runtime_cfg, MSG_OP_GET_RUNTIME_CONFIG); int ret; req.type = type; - ret = aie2_send_mgmt_msg_wait(ndev, &msg); + ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); if (ret) { - XDNA_ERR(ndev->xdna, "Failed to get runtime config, ret %d", ret); + XDNA_ERR(ndev->aie.xdna, "Failed to get runtime config, ret %d", ret); return ret; } @@ -145,20 +120,20 @@ int aie2_get_runtime_cfg(struct amdxdna_dev_hdl *ndev, u32 type, u64 *value) int aie2_assign_mgmt_pasid(struct amdxdna_dev_hdl *ndev, u16 pasid) { - DECLARE_AIE2_MSG(assign_mgmt_pasid, MSG_OP_ASSIGN_MGMT_PASID); + DECLARE_AIE_MSG(assign_mgmt_pasid, MSG_OP_ASSIGN_MGMT_PASID); req.pasid = pasid; - return aie2_send_mgmt_msg_wait(ndev, &msg); + return aie_send_mgmt_msg_wait(&ndev->aie, &msg); } int aie2_query_aie_version(struct amdxdna_dev_hdl *ndev, struct aie_version *version) { - DECLARE_AIE2_MSG(aie_version_info, MSG_OP_QUERY_AIE_VERSION); - struct amdxdna_dev *xdna = ndev->xdna; + DECLARE_AIE_MSG(aie_version_info, MSG_OP_QUERY_AIE_VERSION); + struct amdxdna_dev *xdna = ndev->aie.xdna; int ret; - ret = aie2_send_mgmt_msg_wait(ndev, &msg); + ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); if (ret) return ret; @@ -173,10 +148,10 @@ int aie2_query_aie_version(struct amdxdna_dev_hdl *ndev, struct aie_version *ver int aie2_query_aie_metadata(struct amdxdna_dev_hdl *ndev, struct aie_metadata *metadata) { - DECLARE_AIE2_MSG(aie_tile_info, MSG_OP_QUERY_AIE_TILE_INFO); + DECLARE_AIE_MSG(aie_tile_info, MSG_OP_QUERY_AIE_TILE_INFO); int ret; - ret = aie2_send_mgmt_msg_wait(ndev, &msg); + ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); if (ret) return ret; @@ -211,10 +186,10 @@ int aie2_query_aie_metadata(struct amdxdna_dev_hdl *ndev, struct aie_metadata *m int aie2_query_firmware_version(struct amdxdna_dev_hdl *ndev, struct amdxdna_fw_ver *fw_ver) { - DECLARE_AIE2_MSG(firmware_version, MSG_OP_GET_FIRMWARE_VERSION); + DECLARE_AIE_MSG(firmware_version, MSG_OP_GET_FIRMWARE_VERSION); int ret; - ret = aie2_send_mgmt_msg_wait(ndev, &msg); + ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); if (ret) return ret; @@ -228,12 +203,12 @@ int aie2_query_firmware_version(struct amdxdna_dev_hdl *ndev, static int aie2_destroy_context_req(struct amdxdna_dev_hdl *ndev, u32 id) { - DECLARE_AIE2_MSG(destroy_ctx, MSG_OP_DESTROY_CONTEXT); - struct amdxdna_dev *xdna = ndev->xdna; + DECLARE_AIE_MSG(destroy_ctx, MSG_OP_DESTROY_CONTEXT); + struct amdxdna_dev *xdna = ndev->aie.xdna; int ret; req.context_id = id; - ret = aie2_send_mgmt_msg_wait(ndev, &msg); + ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); if (ret && ret != -ENODEV) XDNA_WARN(xdna, "Destroy context failed, ret %d", ret); else if (ret == -ENODEV) @@ -245,7 +220,7 @@ static int aie2_destroy_context_req(struct amdxdna_dev_hdl *ndev, u32 id) static u32 aie2_get_context_priority(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwctx) { - if (!AIE2_FEATURE_ON(ndev, AIE2_PREEMPT)) + if (!AIE_FEATURE_ON(&ndev->aie, AIE2_PREEMPT)) return PRIORITY_HIGH; switch (hwctx->qos.priority) { @@ -264,8 +239,8 @@ static u32 aie2_get_context_priority(struct amdxdna_dev_hdl *ndev, int aie2_create_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwctx) { - DECLARE_AIE2_MSG(create_ctx, MSG_OP_CREATE_CONTEXT); - struct amdxdna_dev *xdna = ndev->xdna; + DECLARE_AIE_MSG(create_ctx, MSG_OP_CREATE_CONTEXT); + struct amdxdna_dev *xdna = ndev->aie.xdna; struct xdna_mailbox_chann_res x2i; struct xdna_mailbox_chann_res i2x; struct cq_pair *cq_pair; @@ -280,7 +255,7 @@ int aie2_create_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwct req.pasid = amdxdna_pasid_on(hwctx->client) ? hwctx->client->pasid : 0; req.context_priority = aie2_get_context_priority(ndev, hwctx); - ret = aie2_send_mgmt_msg_wait(ndev, &msg); + ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); if (ret) return ret; @@ -344,7 +319,7 @@ int aie2_create_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwct int aie2_destroy_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwctx) { - struct amdxdna_dev *xdna = ndev->xdna; + struct amdxdna_dev *xdna = ndev->aie.xdna; int ret; if (!hwctx->priv->mbox_chann) @@ -363,14 +338,14 @@ int aie2_destroy_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwc int aie2_map_host_buf(struct amdxdna_dev_hdl *ndev, u32 context_id, u64 addr, u64 size) { - DECLARE_AIE2_MSG(map_host_buffer, MSG_OP_MAP_HOST_BUFFER); - struct amdxdna_dev *xdna = ndev->xdna; + DECLARE_AIE_MSG(map_host_buffer, MSG_OP_MAP_HOST_BUFFER); + struct amdxdna_dev *xdna = ndev->aie.xdna; int ret; req.context_id = context_id; req.buf_addr = addr; req.buf_size = size; - ret = aie2_send_mgmt_msg_wait(ndev, &msg); + ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); if (ret) return ret; @@ -392,8 +367,8 @@ static int amdxdna_hwctx_col_map(struct amdxdna_hwctx *hwctx, void *arg) int aie2_query_status(struct amdxdna_dev_hdl *ndev, char __user *buf, u32 size, u32 *cols_filled) { - DECLARE_AIE2_MSG(aie_column_info, MSG_OP_QUERY_COL_STATUS); - struct amdxdna_dev *xdna = ndev->xdna; + DECLARE_AIE_MSG(aie_column_info, MSG_OP_QUERY_COL_STATUS); + struct amdxdna_dev *xdna = ndev->aie.xdna; u32 buf_sz = size, aie_bitmap = 0; struct amdxdna_client *client; dma_addr_t dma_addr; @@ -415,7 +390,7 @@ int aie2_query_status(struct amdxdna_dev_hdl *ndev, char __user *buf, req.aie_bitmap = aie_bitmap; drm_clflush_virt_range(buff_addr, size); /* device can access */ - ret = aie2_send_mgmt_msg_wait(ndev, &msg); + ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); if (ret) { XDNA_ERR(xdna, "Error during NPU query, status %d", ret); goto fail; @@ -446,8 +421,8 @@ int aie2_query_telemetry(struct amdxdna_dev_hdl *ndev, char __user *buf, u32 size, struct amdxdna_drm_query_telemetry_header *header) { - DECLARE_AIE2_MSG(get_telemetry, MSG_OP_GET_TELEMETRY); - struct amdxdna_dev *xdna = ndev->xdna; + DECLARE_AIE_MSG(get_telemetry, MSG_OP_GET_TELEMETRY); + struct amdxdna_dev *xdna = ndev->aie.xdna; dma_addr_t dma_addr; u32 buf_sz = size; u8 *addr; @@ -465,7 +440,7 @@ int aie2_query_telemetry(struct amdxdna_dev_hdl *ndev, req.type = header->type; drm_clflush_virt_range(addr, size); /* device can access */ - ret = aie2_send_mgmt_msg_wait(ndev, &msg); + ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); if (ret) { XDNA_ERR(xdna, "Query telemetry failed, status %d", ret); goto free_buf; @@ -506,8 +481,8 @@ int aie2_register_asyn_event_msg(struct amdxdna_dev_hdl *ndev, dma_addr_t addr, req.buf_addr = addr; req.buf_size = size; - XDNA_DBG(ndev->xdna, "Register addr 0x%llx size 0x%x", addr, size); - return xdna_mailbox_send_msg(ndev->mgmt_chann, &msg, TX_TIMEOUT); + XDNA_DBG(ndev->aie.xdna, "Register addr 0x%llx size 0x%x", addr, size); + return xdna_mailbox_send_msg(ndev->aie.mgmt_chann, &msg, TX_TIMEOUT); } int aie2_config_cu(struct amdxdna_hwctx *hwctx, @@ -866,7 +841,6 @@ static int aie2_init_exec_req(void *req, struct amdxdna_gem_obj *cmd_abo, int ret; u32 op; - op = amdxdna_cmd_get_op(cmd_abo); switch (op) { case ERT_START_CU: @@ -915,12 +889,12 @@ aie2_cmdlist_fill_slot(void *slot, struct amdxdna_gem_obj *cmd_abo, ret = EXEC_MSG_OPS(xdna)->fill_dpu_slot(cmd_abo, slot, size); break; case ERT_START_NPU_PREEMPT: - if (!AIE2_FEATURE_ON(xdna->dev_handle, AIE2_PREEMPT)) + if (!AIE_FEATURE_ON(&xdna->dev_handle->aie, AIE2_PREEMPT)) return -EOPNOTSUPP; ret = EXEC_MSG_OPS(xdna)->fill_preempt_slot(cmd_abo, slot, size); break; case ERT_START_NPU_PREEMPT_ELF: - if (!AIE2_FEATURE_ON(xdna->dev_handle, AIE2_PREEMPT)) + if (!AIE_FEATURE_ON(&xdna->dev_handle->aie, AIE2_PREEMPT)) return -EOPNOTSUPP; ret = EXEC_MSG_OPS(xdna)->fill_elf_slot(cmd_abo, slot, size); break; @@ -935,26 +909,12 @@ aie2_cmdlist_fill_slot(void *slot, struct amdxdna_gem_obj *cmd_abo, void aie2_msg_init(struct amdxdna_dev_hdl *ndev) { - if (AIE2_FEATURE_ON(ndev, AIE2_NPU_COMMAND)) + if (AIE_FEATURE_ON(&ndev->aie, AIE2_NPU_COMMAND)) ndev->exec_msg_ops = &npu_exec_message_ops; else ndev->exec_msg_ops = &legacy_exec_message_ops; } -void aie2_destroy_mgmt_chann(struct amdxdna_dev_hdl *ndev) -{ - struct amdxdna_dev *xdna = ndev->xdna; - - drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock)); - - if (!ndev->mgmt_chann) - return; - - xdna_mailbox_stop_channel(ndev->mgmt_chann); - xdna_mailbox_free_channel(ndev->mgmt_chann); - ndev->mgmt_chann = NULL; -} - static inline struct amdxdna_gem_obj * aie2_cmdlist_get_cmd_buf(struct amdxdna_sched_job *job) { @@ -1199,14 +1159,14 @@ int aie2_config_debug_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job * int aie2_query_app_health(struct amdxdna_dev_hdl *ndev, u32 context_id, struct app_health_report *report) { - DECLARE_AIE2_MSG(get_app_health, MSG_OP_GET_APP_HEALTH); - struct amdxdna_dev *xdna = ndev->xdna; + DECLARE_AIE_MSG(get_app_health, MSG_OP_GET_APP_HEALTH); + struct amdxdna_dev *xdna = ndev->aie.xdna; struct app_health_report *buf; dma_addr_t dma_addr; u32 buf_size; int ret; - if (!AIE2_FEATURE_ON(ndev, AIE2_APP_HEALTH)) { + if (!AIE_FEATURE_ON(&ndev->aie, AIE2_APP_HEALTH)) { XDNA_DBG(xdna, "App health feature not supported"); return -EOPNOTSUPP; } @@ -1223,7 +1183,7 @@ int aie2_query_app_health(struct amdxdna_dev_hdl *ndev, u32 context_id, req.buf_size = buf_size; drm_clflush_virt_range(buf, sizeof(*report)); - ret = aie2_send_mgmt_msg_wait(ndev, &msg); + ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); if (ret) { XDNA_ERR(xdna, "Get app health failed, ret %d status 0x%x", ret, resp.status); goto free_buf; diff --git a/drivers/accel/amdxdna/aie2_pci.c b/drivers/accel/amdxdna/aie2_pci.c index 4500b9ccb02e0..ebdac0eaa365e 100644 --- a/drivers/accel/amdxdna/aie2_pci.c +++ b/drivers/accel/amdxdna/aie2_pci.c @@ -60,45 +60,6 @@ struct mgmt_mbox_chann_info { __u32 rsvd[4]; }; -static int aie2_check_protocol(struct amdxdna_dev_hdl *ndev, u32 fw_major, u32 fw_minor) -{ - const struct aie2_fw_feature_tbl *feature; - bool found = false; - - for (feature = ndev->priv->fw_feature_tbl; feature->major; feature++) { - if (feature->major != fw_major) - continue; - if (fw_minor < feature->min_minor) - continue; - if (feature->max_minor > 0 && fw_minor > feature->max_minor) - continue; - - ndev->feature_mask |= feature->features; - - /* firmware version matches one of the driver support entry */ - found = true; - } - - return found ? 0 : -EOPNOTSUPP; -} - -static void aie2_dump_chann_info_debug(struct amdxdna_dev_hdl *ndev) -{ - struct amdxdna_dev *xdna = ndev->xdna; - - XDNA_DBG(xdna, "i2x tail 0x%x", ndev->mgmt_i2x.mb_tail_ptr_reg); - XDNA_DBG(xdna, "i2x head 0x%x", ndev->mgmt_i2x.mb_head_ptr_reg); - XDNA_DBG(xdna, "i2x ringbuf 0x%x", ndev->mgmt_i2x.rb_start_addr); - XDNA_DBG(xdna, "i2x rsize 0x%x", ndev->mgmt_i2x.rb_size); - XDNA_DBG(xdna, "x2i tail 0x%x", ndev->mgmt_x2i.mb_tail_ptr_reg); - XDNA_DBG(xdna, "x2i head 0x%x", ndev->mgmt_x2i.mb_head_ptr_reg); - XDNA_DBG(xdna, "x2i ringbuf 0x%x", ndev->mgmt_x2i.rb_start_addr); - XDNA_DBG(xdna, "x2i rsize 0x%x", ndev->mgmt_x2i.rb_size); - XDNA_DBG(xdna, "x2i chann index 0x%x", ndev->mgmt_chan_idx); - XDNA_DBG(xdna, "mailbox protocol major 0x%x", ndev->mgmt_prot_major); - XDNA_DBG(xdna, "mailbox protocol minor 0x%x", ndev->mgmt_prot_minor); -} - static int aie2_get_mgmt_chann_info(struct amdxdna_dev_hdl *ndev) { struct mgmt_mbox_chann_info info_regs; @@ -128,13 +89,13 @@ static int aie2_get_mgmt_chann_info(struct amdxdna_dev_hdl *ndev) reg[i] = readl(ndev->sram_base + off + i * sizeof(u32)); if (info_regs.magic != MGMT_MBOX_MAGIC) { - XDNA_ERR(ndev->xdna, "Invalid mbox magic 0x%x", info_regs.magic); + XDNA_ERR(ndev->aie.xdna, "Invalid mbox magic 0x%x", info_regs.magic); ret = -EINVAL; goto done; } - i2x = &ndev->mgmt_i2x; - x2i = &ndev->mgmt_x2i; + i2x = &ndev->aie.mgmt_i2x; + x2i = &ndev->aie.mgmt_x2i; i2x->mb_head_ptr_reg = AIE2_MBOX_OFF(ndev, info_regs.i2x_head); i2x->mb_tail_ptr_reg = AIE2_MBOX_OFF(ndev, info_regs.i2x_tail); @@ -146,14 +107,15 @@ static int aie2_get_mgmt_chann_info(struct amdxdna_dev_hdl *ndev) x2i->rb_start_addr = AIE2_SRAM_OFF(ndev, info_regs.x2i_buf); x2i->rb_size = info_regs.x2i_buf_sz; - ndev->mgmt_chan_idx = info_regs.msi_id; - ndev->mgmt_prot_major = info_regs.prot_major; - ndev->mgmt_prot_minor = info_regs.prot_minor; + ndev->aie.mgmt_chan_idx = info_regs.msi_id; + ndev->aie.mgmt_prot_major = info_regs.prot_major; + ndev->aie.mgmt_prot_minor = info_regs.prot_minor; - ret = aie2_check_protocol(ndev, ndev->mgmt_prot_major, ndev->mgmt_prot_minor); + ret = aie_check_protocol(&ndev->aie, ndev->aie.mgmt_prot_major, + ndev->aie.mgmt_prot_minor); done: - aie2_dump_chann_info_debug(ndev); + aie_dump_mgmt_chann_debug(&ndev->aie); /* Must clear address at FW_ALIVE_OFF */ writel(0, SRAM_GET_ADDR(ndev, FW_ALIVE_OFF)); @@ -173,13 +135,14 @@ int aie2_runtime_cfg(struct amdxdna_dev_hdl *ndev, continue; if (cfg->feature_mask && - bitmap_subset(&cfg->feature_mask, &ndev->feature_mask, AIE2_FEATURE_MAX)) + bitmap_subset(&cfg->feature_mask, &ndev->aie.feature_mask, + AIE2_FEATURE_MAX)) continue; value = val ? *val : cfg->value; ret = aie2_set_runtime_cfg(ndev, cfg->type, value); if (ret) { - XDNA_ERR(ndev->xdna, "Set type %d value %d failed", + XDNA_ERR(ndev->aie.xdna, "Set type %d value %d failed", cfg->type, value); return ret; } @@ -194,13 +157,13 @@ static int aie2_xdna_reset(struct amdxdna_dev_hdl *ndev) ret = aie2_suspend_fw(ndev); if (ret) { - XDNA_ERR(ndev->xdna, "Suspend firmware failed"); + XDNA_ERR(ndev->aie.xdna, "Suspend firmware failed"); return ret; } ret = aie2_resume_fw(ndev); if (ret) { - XDNA_ERR(ndev->xdna, "Resume firmware failed"); + XDNA_ERR(ndev->aie.xdna, "Resume firmware failed"); return ret; } @@ -213,19 +176,19 @@ static int aie2_mgmt_fw_init(struct amdxdna_dev_hdl *ndev) ret = aie2_runtime_cfg(ndev, AIE2_RT_CFG_INIT, NULL); if (ret) { - XDNA_ERR(ndev->xdna, "Runtime config failed"); + XDNA_ERR(ndev->aie.xdna, "Runtime config failed"); return ret; } ret = aie2_assign_mgmt_pasid(ndev, 0); if (ret) { - XDNA_ERR(ndev->xdna, "Can not assign PASID"); + XDNA_ERR(ndev->aie.xdna, "Can not assign PASID"); return ret; } ret = aie2_xdna_reset(ndev); if (ret) { - XDNA_ERR(ndev->xdna, "Reset firmware failed"); + XDNA_ERR(ndev->aie.xdna, "Reset firmware failed"); return ret; } @@ -236,21 +199,21 @@ static int aie2_mgmt_fw_query(struct amdxdna_dev_hdl *ndev) { int ret; - ret = aie2_query_firmware_version(ndev, &ndev->xdna->fw_ver); + ret = aie2_query_firmware_version(ndev, &ndev->aie.xdna->fw_ver); if (ret) { - XDNA_ERR(ndev->xdna, "query firmware version failed"); + XDNA_ERR(ndev->aie.xdna, "query firmware version failed"); return ret; } ret = aie2_query_aie_version(ndev, &ndev->version); if (ret) { - XDNA_ERR(ndev->xdna, "Query AIE version failed"); + XDNA_ERR(ndev->aie.xdna, "Query AIE version failed"); return ret; } ret = aie2_query_aie_metadata(ndev, &ndev->metadata); if (ret) { - XDNA_ERR(ndev->xdna, "Query AIE metadata failed"); + XDNA_ERR(ndev->aie.xdna, "Query AIE metadata failed"); return ret; } @@ -262,8 +225,8 @@ static int aie2_mgmt_fw_query(struct amdxdna_dev_hdl *ndev) static void aie2_mgmt_fw_fini(struct amdxdna_dev_hdl *ndev) { if (aie2_suspend_fw(ndev)) - XDNA_ERR(ndev->xdna, "Suspend_fw failed"); - XDNA_DBG(ndev->xdna, "Firmware suspended"); + XDNA_ERR(ndev->aie.xdna, "Suspend_fw failed"); + XDNA_DBG(ndev->aie.xdna, "Firmware suspended"); } static int aie2_xrs_load(void *cb_arg, struct xrs_action_load *action) @@ -331,7 +294,7 @@ static void aie2_hw_stop(struct amdxdna_dev *xdna) aie2_runtime_cfg(ndev, AIE2_RT_CFG_CLK_GATING, NULL); aie2_mgmt_fw_fini(ndev); - aie2_destroy_mgmt_chann(ndev); + aie_destroy_chann(&ndev->aie, &ndev->aie.mgmt_chann); drmm_kfree(&xdna->ddev, ndev->mbox); ndev->mbox = NULL; aie2_psp_stop(ndev->psp_hdl); @@ -374,8 +337,8 @@ static int aie2_hw_start(struct amdxdna_dev *xdna) goto disable_dev; } - ndev->mgmt_chann = xdna_mailbox_alloc_channel(ndev->mbox); - if (!ndev->mgmt_chann) { + ndev->aie.mgmt_chann = xdna_mailbox_alloc_channel(ndev->mbox); + if (!ndev->aie.mgmt_chann) { XDNA_ERR(xdna, "failed to alloc channel"); ret = -ENODEV; goto disable_dev; @@ -399,17 +362,17 @@ static int aie2_hw_start(struct amdxdna_dev *xdna) goto stop_psp; } - mgmt_mb_irq = pci_irq_vector(pdev, ndev->mgmt_chan_idx); + mgmt_mb_irq = pci_irq_vector(pdev, ndev->aie.mgmt_chan_idx); if (mgmt_mb_irq < 0) { ret = mgmt_mb_irq; XDNA_ERR(xdna, "failed to alloc irq vector, ret %d", ret); goto stop_psp; } - xdna_mailbox_intr_reg = ndev->mgmt_i2x.mb_head_ptr_reg + 4; - ret = xdna_mailbox_start_channel(ndev->mgmt_chann, - &ndev->mgmt_x2i, - &ndev->mgmt_i2x, + xdna_mailbox_intr_reg = ndev->aie.mgmt_i2x.mb_head_ptr_reg + 4; + ret = xdna_mailbox_start_channel(ndev->aie.mgmt_chann, + &ndev->aie.mgmt_x2i, + &ndev->aie.mgmt_i2x, xdna_mailbox_intr_reg, mgmt_mb_irq); if (ret) { @@ -448,14 +411,14 @@ static int aie2_hw_start(struct amdxdna_dev *xdna) stop_fw: aie2_suspend_fw(ndev); - xdna_mailbox_stop_channel(ndev->mgmt_chann); + xdna_mailbox_stop_channel(ndev->aie.mgmt_chann); stop_psp: aie2_psp_stop(ndev->psp_hdl); fini_smu: aie2_smu_fini(ndev); free_channel: - xdna_mailbox_free_channel(ndev->mgmt_chann); - ndev->mgmt_chann = NULL; + xdna_mailbox_free_channel(ndev->aie.mgmt_chann); + ndev->aie.mgmt_chann = NULL; disable_dev: pci_disable_device(pdev); @@ -521,7 +484,7 @@ static int aie2_init(struct amdxdna_dev *xdna) return -ENOMEM; ndev->priv = xdna->dev_info->dev_priv; - ndev->xdna = xdna; + ndev->aie.xdna = xdna; for (i = 0; i < ARRAY_SIZE(npu_fw); i++) { fw_full_path = kasprintf(GFP_KERNEL, "%s%s", ndev->priv->fw_path, npu_fw[i]); diff --git a/drivers/accel/amdxdna/aie2_pci.h b/drivers/accel/amdxdna/aie2_pci.h index efcf4be035f04..90fb0aafaf406 100644 --- a/drivers/accel/amdxdna/aie2_pci.h +++ b/drivers/accel/amdxdna/aie2_pci.h @@ -10,6 +10,7 @@ #include #include +#include "aie.h" #include "aie2_msg_priv.h" #include "amdxdna_mailbox.h" @@ -20,7 +21,7 @@ #define AIE2_DEVM_BASE 0x4000000 #define AIE2_DEVM_SIZE SZ_64M -#define NDEV2PDEV(ndev) (to_pci_dev((ndev)->xdna->ddev.dev)) +#define NDEV2PDEV(ndev) (to_pci_dev((ndev)->aie.xdna->ddev.dev)) #define AIE2_SRAM_OFF(ndev, addr) ((addr) - (ndev)->priv->sram_dev_addr) #define AIE2_MBOX_OFF(ndev, addr) ((addr) - (ndev)->priv->mbox_dev_addr) @@ -45,7 +46,7 @@ ({ \ typeof(ndev) _ndev = (ndev); \ ((_ndev)->priv->mbox_size) ? (_ndev)->priv->mbox_size : \ - pci_resource_len(NDEV2PDEV(_ndev), (_ndev)->xdna->dev_info->mbox_bar); \ + pci_resource_len(NDEV2PDEV(_ndev), (_ndev)->aie.xdna->dev_info->mbox_bar); \ }) #if IS_ENABLED(CONFIG_AMD_PMF) @@ -203,23 +204,16 @@ struct aie2_exec_msg_ops { }; struct amdxdna_dev_hdl { - struct amdxdna_dev *xdna; + struct aie_device aie; const struct amdxdna_dev_priv *priv; void __iomem *sram_base; void __iomem *smu_base; void __iomem *mbox_base; struct psp_device *psp_hdl; - struct xdna_mailbox_chann_res mgmt_x2i; - struct xdna_mailbox_chann_res mgmt_i2x; - u32 mgmt_chan_idx; - u32 mgmt_prot_major; - u32 mgmt_prot_minor; - u32 total_col; struct aie_version version; struct aie_metadata metadata; - unsigned long feature_mask; struct aie2_exec_msg_ops *exec_msg_ops; /* power management and clock*/ @@ -237,7 +231,6 @@ struct amdxdna_dev_hdl { /* Mailbox and the management channel */ struct mailbox *mbox; - struct mailbox_channel *mgmt_chann; struct async_events *async_events; enum aie2_dev_status dev_status; @@ -266,21 +259,12 @@ enum aie2_fw_feature { AIE2_FEATURE_MAX }; -struct aie2_fw_feature_tbl { - u64 features; - u32 major; - u32 max_minor; - u32 min_minor; -}; - #define AIE2_ALL_FEATURES GENMASK_ULL(AIE2_FEATURE_MAX - 1, AIE2_NPU_COMMAND) -#define AIE2_FEATURE_ON(ndev, feature) test_bit(feature, &(ndev)->feature_mask) struct amdxdna_dev_priv { const char *fw_path; const struct rt_config *rt_config; const struct dpm_clk_freq *dpm_clk_tbl; - const struct aie2_fw_feature_tbl *fw_feature_tbl; #define COL_ALIGN_NONE 0 #define COL_ALIGN_NATURE 1 @@ -306,7 +290,7 @@ extern const struct dpm_clk_freq npu1_dpm_clk_table[]; extern const struct dpm_clk_freq npu4_dpm_clk_table[]; extern const struct rt_config npu1_default_rt_cfg[]; extern const struct rt_config npu4_default_rt_cfg[]; -extern const struct aie2_fw_feature_tbl npu4_fw_feature_table[]; +extern const struct amdxdna_fw_feature_tbl npu4_fw_feature_table[]; /* aie2_smu.c */ int aie2_smu_init(struct amdxdna_dev_hdl *ndev); diff --git a/drivers/accel/amdxdna/aie2_pm.c b/drivers/accel/amdxdna/aie2_pm.c index 29bd4403a94d4..5ec6728d04fd5 100644 --- a/drivers/accel/amdxdna/aie2_pm.c +++ b/drivers/accel/amdxdna/aie2_pm.c @@ -31,14 +31,14 @@ int aie2_pm_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level) { int ret; - ret = amdxdna_pm_resume_get_locked(ndev->xdna); + ret = amdxdna_pm_resume_get_locked(ndev->aie.xdna); if (ret) return ret; ret = ndev->priv->hw_ops.set_dpm(ndev, dpm_level); if (!ret) ndev->dpm_level = dpm_level; - amdxdna_pm_suspend_put(ndev->xdna); + amdxdna_pm_suspend_put(ndev->aie.xdna); return ret; } @@ -81,7 +81,7 @@ int aie2_pm_init(struct amdxdna_dev_hdl *ndev) int aie2_pm_set_mode(struct amdxdna_dev_hdl *ndev, enum amdxdna_power_mode_type target) { - struct amdxdna_dev *xdna = ndev->xdna; + struct amdxdna_dev *xdna = ndev->aie.xdna; u32 clk_gating, dpm_level; int ret; diff --git a/drivers/accel/amdxdna/aie2_smu.c b/drivers/accel/amdxdna/aie2_smu.c index d8c31924e501b..727637dac3a8d 100644 --- a/drivers/accel/amdxdna/aie2_smu.c +++ b/drivers/accel/amdxdna/aie2_smu.c @@ -46,7 +46,7 @@ static int aie2_smu_exec(struct amdxdna_dev_hdl *ndev, u32 reg_cmd, ret = readx_poll_timeout(readl, SMU_REG(ndev, SMU_RESP_REG), resp, resp, AIE2_INTERVAL, AIE2_TIMEOUT); if (ret) { - XDNA_ERR(ndev->xdna, "smu cmd %d timed out", reg_cmd); + XDNA_ERR(ndev->aie.xdna, "smu cmd %d timed out", reg_cmd); return ret; } @@ -54,7 +54,7 @@ static int aie2_smu_exec(struct amdxdna_dev_hdl *ndev, u32 reg_cmd, *out = readl(SMU_REG(ndev, SMU_OUT_REG)); if (resp != SMU_RESULT_OK) { - XDNA_ERR(ndev->xdna, "smu cmd %d failed, 0x%x", reg_cmd, resp); + XDNA_ERR(ndev->aie.xdna, "smu cmd %d failed, 0x%x", reg_cmd, resp); return -EINVAL; } @@ -69,7 +69,7 @@ int npu1_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level) ret = aie2_smu_exec(ndev, AIE2_SMU_SET_MPNPUCLK_FREQ, ndev->priv->dpm_clk_tbl[dpm_level].npuclk, &freq); if (ret) { - XDNA_ERR(ndev->xdna, "Set npu clock to %d failed, ret %d\n", + XDNA_ERR(ndev->aie.xdna, "Set npu clock to %d failed, ret %d\n", ndev->priv->dpm_clk_tbl[dpm_level].npuclk, ret); return ret; } @@ -78,7 +78,7 @@ int npu1_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level) ret = aie2_smu_exec(ndev, AIE2_SMU_SET_HCLK_FREQ, ndev->priv->dpm_clk_tbl[dpm_level].hclk, &freq); if (ret) { - XDNA_ERR(ndev->xdna, "Set h clock to %d failed, ret %d\n", + XDNA_ERR(ndev->aie.xdna, "Set h clock to %d failed, ret %d\n", ndev->priv->dpm_clk_tbl[dpm_level].hclk, ret); return ret; } @@ -87,7 +87,7 @@ int npu1_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level) ndev->max_tops = 2 * ndev->total_col; ndev->curr_tops = ndev->max_tops * freq / 1028; - XDNA_DBG(ndev->xdna, "MP-NPU clock %d, H clock %d\n", + XDNA_DBG(ndev->aie.xdna, "MP-NPU clock %d, H clock %d\n", ndev->npuclk_freq, ndev->hclk_freq); return 0; @@ -99,14 +99,14 @@ int npu4_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level) ret = aie2_smu_exec(ndev, AIE2_SMU_SET_HARD_DPMLEVEL, dpm_level, NULL); if (ret) { - XDNA_ERR(ndev->xdna, "Set hard dpm level %d failed, ret %d ", + XDNA_ERR(ndev->aie.xdna, "Set hard dpm level %d failed, ret %d ", dpm_level, ret); return ret; } ret = aie2_smu_exec(ndev, AIE2_SMU_SET_SOFT_DPMLEVEL, dpm_level, NULL); if (ret) { - XDNA_ERR(ndev->xdna, "Set soft dpm level %d failed, ret %d", + XDNA_ERR(ndev->aie.xdna, "Set soft dpm level %d failed, ret %d", dpm_level, ret); return ret; } @@ -116,7 +116,7 @@ int npu4_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level) ndev->max_tops = NPU4_DPM_TOPS(ndev, ndev->max_dpm_level); ndev->curr_tops = NPU4_DPM_TOPS(ndev, dpm_level); - XDNA_DBG(ndev->xdna, "MP-NPU clock %d, H clock %d\n", + XDNA_DBG(ndev->aie.xdna, "MP-NPU clock %d, H clock %d\n", ndev->npuclk_freq, ndev->hclk_freq); return 0; @@ -132,13 +132,13 @@ int aie2_smu_init(struct amdxdna_dev_hdl *ndev) */ ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_OFF, 0, NULL); if (ret) { - XDNA_ERR(ndev->xdna, "Access power failed, ret %d", ret); + XDNA_ERR(ndev->aie.xdna, "Access power failed, ret %d", ret); return ret; } ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_ON, 0, NULL); if (ret) { - XDNA_ERR(ndev->xdna, "Power on failed, ret %d", ret); + XDNA_ERR(ndev->aie.xdna, "Power on failed, ret %d", ret); return ret; } @@ -152,5 +152,5 @@ void aie2_smu_fini(struct amdxdna_dev_hdl *ndev) ndev->priv->hw_ops.set_dpm(ndev, 0); ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_OFF, 0, NULL); if (ret) - XDNA_ERR(ndev->xdna, "Power off failed, ret %d", ret); + XDNA_ERR(ndev->aie.xdna, "Power off failed, ret %d", ret); } diff --git a/drivers/accel/amdxdna/amdxdna_pci_drv.h b/drivers/accel/amdxdna/amdxdna_pci_drv.h index 0661749917d68..5e0bf565a1ae6 100644 --- a/drivers/accel/amdxdna/amdxdna_pci_drv.h +++ b/drivers/accel/amdxdna/amdxdna_pci_drv.h @@ -66,6 +66,13 @@ struct amdxdna_dev_ops { int (*get_array)(struct amdxdna_client *client, struct amdxdna_drm_get_array *args); }; +struct amdxdna_fw_feature_tbl { + u64 features; + u32 major; + u32 max_minor; + u32 min_minor; +}; + /* * struct amdxdna_dev_info - Device hardware information * Record device static information, like reg, mbox, PSP, SMU bar index @@ -83,6 +90,7 @@ struct amdxdna_dev_info { size_t dev_mem_size; char *vbnv; const struct amdxdna_dev_priv *dev_priv; + const struct amdxdna_fw_feature_tbl *fw_feature_tbl; const struct amdxdna_dev_ops *ops; }; diff --git a/drivers/accel/amdxdna/npu1_regs.c b/drivers/accel/amdxdna/npu1_regs.c index 1320e924e5482..2ea7568a2e995 100644 --- a/drivers/accel/amdxdna/npu1_regs.c +++ b/drivers/accel/amdxdna/npu1_regs.c @@ -65,7 +65,7 @@ const struct dpm_clk_freq npu1_dpm_clk_table[] = { { 0 } }; -static const struct aie2_fw_feature_tbl npu1_fw_feature_table[] = { +static const struct amdxdna_fw_feature_tbl npu1_fw_feature_table[] = { { .major = 5, .min_minor = 7 }, { .features = BIT_U64(AIE2_NPU_COMMAND), .major = 5, .min_minor = 8 }, { 0 } @@ -75,7 +75,6 @@ static const struct amdxdna_dev_priv npu1_dev_priv = { .fw_path = "amdnpu/1502_00/", .rt_config = npu1_default_rt_cfg, .dpm_clk_tbl = npu1_dpm_clk_table, - .fw_feature_tbl = npu1_fw_feature_table, .col_align = COL_ALIGN_NONE, .mbox_dev_addr = NPU1_MBOX_BAR_BASE, .mbox_size = 0, /* Use BAR size */ @@ -120,5 +119,6 @@ const struct amdxdna_dev_info dev_npu1_info = { .vbnv = "RyzenAI-npu1", .device_type = AMDXDNA_DEV_TYPE_KMQ, .dev_priv = &npu1_dev_priv, + .fw_feature_tbl = npu1_fw_feature_table, .ops = &aie2_ops, }; diff --git a/drivers/accel/amdxdna/npu4_regs.c b/drivers/accel/amdxdna/npu4_regs.c index 619bff042e52c..9689c56c83beb 100644 --- a/drivers/accel/amdxdna/npu4_regs.c +++ b/drivers/accel/amdxdna/npu4_regs.c @@ -88,7 +88,7 @@ const struct dpm_clk_freq npu4_dpm_clk_table[] = { { 0 } }; -const struct aie2_fw_feature_tbl npu4_fw_feature_table[] = { +const struct amdxdna_fw_feature_tbl npu4_fw_feature_table[] = { { .major = 6, .min_minor = 12 }, { .features = BIT_U64(AIE2_NPU_COMMAND), .major = 6, .min_minor = 15 }, { .features = BIT_U64(AIE2_PREEMPT), .major = 6, .min_minor = 12 }, @@ -102,7 +102,6 @@ static const struct amdxdna_dev_priv npu4_dev_priv = { .fw_path = "amdnpu/17f0_10/", .rt_config = npu4_default_rt_cfg, .dpm_clk_tbl = npu4_dpm_clk_table, - .fw_feature_tbl = npu4_fw_feature_table, .col_align = COL_ALIGN_NATURE, .mbox_dev_addr = NPU4_MBOX_BAR_BASE, .mbox_size = 0, /* Use BAR size */ @@ -147,5 +146,6 @@ const struct amdxdna_dev_info dev_npu4_info = { .vbnv = "RyzenAI-npu4", .device_type = AMDXDNA_DEV_TYPE_KMQ, .dev_priv = &npu4_dev_priv, + .fw_feature_tbl = npu4_fw_feature_table, .ops = &aie2_ops, /* NPU4 can share NPU1's callback */ }; diff --git a/drivers/accel/amdxdna/npu5_regs.c b/drivers/accel/amdxdna/npu5_regs.c index c0ac5daf32ee5..98ee8780f3f5c 100644 --- a/drivers/accel/amdxdna/npu5_regs.c +++ b/drivers/accel/amdxdna/npu5_regs.c @@ -66,7 +66,6 @@ static const struct amdxdna_dev_priv npu5_dev_priv = { .fw_path = "amdnpu/17f0_11/", .rt_config = npu4_default_rt_cfg, .dpm_clk_tbl = npu4_dpm_clk_table, - .fw_feature_tbl = npu4_fw_feature_table, .col_align = COL_ALIGN_NATURE, .mbox_dev_addr = NPU5_MBOX_BAR_BASE, .mbox_size = 0, /* Use BAR size */ @@ -111,5 +110,6 @@ const struct amdxdna_dev_info dev_npu5_info = { .vbnv = "RyzenAI-npu5", .device_type = AMDXDNA_DEV_TYPE_KMQ, .dev_priv = &npu5_dev_priv, + .fw_feature_tbl = npu4_fw_feature_table, .ops = &aie2_ops, }; diff --git a/drivers/accel/amdxdna/npu6_regs.c b/drivers/accel/amdxdna/npu6_regs.c index ce591ed0d4832..31400cca5ec4c 100644 --- a/drivers/accel/amdxdna/npu6_regs.c +++ b/drivers/accel/amdxdna/npu6_regs.c @@ -66,7 +66,6 @@ static const struct amdxdna_dev_priv npu6_dev_priv = { .fw_path = "amdnpu/17f0_10/", .rt_config = npu4_default_rt_cfg, .dpm_clk_tbl = npu4_dpm_clk_table, - .fw_feature_tbl = npu4_fw_feature_table, .col_align = COL_ALIGN_NATURE, .mbox_dev_addr = NPU6_MBOX_BAR_BASE, .mbox_size = 0, /* Use BAR size */ @@ -112,5 +111,6 @@ const struct amdxdna_dev_info dev_npu6_info = { .vbnv = "RyzenAI-npu6", .device_type = AMDXDNA_DEV_TYPE_KMQ, .dev_priv = &npu6_dev_priv, + .fw_feature_tbl = npu4_fw_feature_table, .ops = &aie2_ops, }; From 097e57195ef813735c8b714d6503bf3ad0742515 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Thu, 2 Apr 2026 10:41:48 -0700 Subject: [PATCH 0735/2758] accel/amdxdna: Adjust size for copy_to_user() [ Upstream commit 6e87001fe19f251e2ae14373bc76554358a13df2 ] The amount of data returned to user space should be limited by the buffer size provided by the application. If the buffer is smaller than the data size, return only the portion that fits instead of failing. Fixes: 850d71f6bf4c ("accel/amdxdna: Add query functions") Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260402174148.3527757-1-lizhi.hou@amd.com Signed-off-by: Sasha Levin --- drivers/accel/amdxdna/aie2_error.c | 5 ++- drivers/accel/amdxdna/aie2_message.c | 20 ++++++---- drivers/accel/amdxdna/aie2_pci.c | 59 ++++++++++++++++------------ 3 files changed, 50 insertions(+), 34 deletions(-) diff --git a/drivers/accel/amdxdna/aie2_error.c b/drivers/accel/amdxdna/aie2_error.c index 9d20e956c020e..70007b4363cd0 100644 --- a/drivers/accel/amdxdna/aie2_error.c +++ b/drivers/accel/amdxdna/aie2_error.c @@ -406,8 +406,11 @@ int aie2_get_array_async_error(struct amdxdna_dev_hdl *ndev, struct amdxdna_drm_ drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock)); + if (!args->num_element) + return -EINVAL; + args->num_element = 1; - args->element_size = sizeof(ndev->last_async_err); + args->element_size = min(args->element_size, sizeof(ndev->last_async_err)); if (copy_to_user(u64_to_user_ptr(args->buffer), &ndev->last_async_err, args->element_size)) return -EFAULT; diff --git a/drivers/accel/amdxdna/aie2_message.c b/drivers/accel/amdxdna/aie2_message.c index ccf87b1aa1ccc..0a619e55902b5 100644 --- a/drivers/accel/amdxdna/aie2_message.c +++ b/drivers/accel/amdxdna/aie2_message.c @@ -369,12 +369,13 @@ int aie2_query_status(struct amdxdna_dev_hdl *ndev, char __user *buf, { DECLARE_AIE_MSG(aie_column_info, MSG_OP_QUERY_COL_STATUS); struct amdxdna_dev *xdna = ndev->aie.xdna; - u32 buf_sz = size, aie_bitmap = 0; + u32 buf_sz, aie_bitmap = 0; struct amdxdna_client *client; dma_addr_t dma_addr; u8 *buff_addr; int ret; + buf_sz = ndev->metadata.cols * ndev->metadata.size; buff_addr = aie2_alloc_msg_buffer(ndev, &buf_sz, &dma_addr); if (IS_ERR(buff_addr)) return PTR_ERR(buff_addr); @@ -398,13 +399,14 @@ int aie2_query_status(struct amdxdna_dev_hdl *ndev, char __user *buf, XDNA_DBG(xdna, "Query NPU status completed"); - if (size < resp.size) { + if (buf_sz < resp.size) { ret = -EINVAL; - XDNA_ERR(xdna, "Bad buffer size. Available: %u. Needs: %u", size, resp.size); + XDNA_ERR(xdna, "Bad buffer size. Available: %u. Needs: %u", buf_sz, resp.size); goto fail; } - if (copy_to_user(buf, buff_addr, resp.size)) { + size = min(size, resp.size); + if (copy_to_user(buf, buff_addr, size)) { ret = -EFAULT; XDNA_ERR(xdna, "Failed to copy NPU status to user space"); goto fail; @@ -424,13 +426,14 @@ int aie2_query_telemetry(struct amdxdna_dev_hdl *ndev, DECLARE_AIE_MSG(get_telemetry, MSG_OP_GET_TELEMETRY); struct amdxdna_dev *xdna = ndev->aie.xdna; dma_addr_t dma_addr; - u32 buf_sz = size; + u32 buf_sz; u8 *addr; int ret; if (header->type >= MAX_TELEMETRY_TYPE) return -EINVAL; + buf_sz = min(size, SZ_4M); addr = aie2_alloc_msg_buffer(ndev, &buf_sz, &dma_addr); if (IS_ERR(addr)) return PTR_ERR(addr); @@ -446,13 +449,14 @@ int aie2_query_telemetry(struct amdxdna_dev_hdl *ndev, goto free_buf; } - if (size < resp.size) { + if (buf_sz < resp.size) { ret = -EINVAL; - XDNA_ERR(xdna, "Bad buffer size. Available: %u. Needs: %u", size, resp.size); + XDNA_ERR(xdna, "Bad buffer size. Available: %u. Needs: %u", buf_sz, resp.size); goto free_buf; } - if (copy_to_user(buf, addr, resp.size)) { + size = min(size, resp.size); + if (copy_to_user(buf, addr, size)) { ret = -EFAULT; XDNA_ERR(xdna, "Failed to copy telemetry to user space"); goto free_buf; diff --git a/drivers/accel/amdxdna/aie2_pci.c b/drivers/accel/amdxdna/aie2_pci.c index ebdac0eaa365e..a7b923005ab7c 100644 --- a/drivers/accel/amdxdna/aie2_pci.c +++ b/drivers/accel/amdxdna/aie2_pci.c @@ -607,23 +607,19 @@ static void aie2_fini(struct amdxdna_dev *xdna) static int aie2_get_aie_status(struct amdxdna_client *client, struct amdxdna_drm_get_info *args) { - struct amdxdna_drm_query_aie_status status; + struct amdxdna_drm_query_aie_status status = {}; struct amdxdna_dev *xdna = client->xdna; struct amdxdna_dev_hdl *ndev; + u32 buf_sz; int ret; ndev = xdna->dev_handle; - if (copy_from_user(&status, u64_to_user_ptr(args->buffer), sizeof(status))) { + buf_sz = min(args->buffer_size, sizeof(status)); + if (copy_from_user(&status, u64_to_user_ptr(args->buffer), buf_sz)) { XDNA_ERR(xdna, "Failed to copy AIE request into kernel"); return -EFAULT; } - if (ndev->metadata.cols * ndev->metadata.size < status.buffer_size) { - XDNA_ERR(xdna, "Invalid buffer size. Given Size: %u. Need Size: %u.", - status.buffer_size, ndev->metadata.cols * ndev->metadata.size); - return -EINVAL; - } - ret = aie2_query_status(ndev, u64_to_user_ptr(status.buffer), status.buffer_size, &status.cols_filled); if (ret) { @@ -631,7 +627,7 @@ static int aie2_get_aie_status(struct amdxdna_client *client, return ret; } - if (copy_to_user(u64_to_user_ptr(args->buffer), &status, sizeof(status))) { + if (copy_to_user(u64_to_user_ptr(args->buffer), &status, buf_sz)) { XDNA_ERR(xdna, "Failed to copy AIE request info to user space"); return -EFAULT; } @@ -646,6 +642,7 @@ static int aie2_get_aie_metadata(struct amdxdna_client *client, struct amdxdna_dev *xdna = client->xdna; struct amdxdna_dev_hdl *ndev; int ret = 0; + u32 buf_sz; ndev = xdna->dev_handle; meta = kzalloc_obj(*meta); @@ -677,7 +674,8 @@ static int aie2_get_aie_metadata(struct amdxdna_client *client, meta->shim.lock_count = ndev->metadata.shim.lock_count; meta->shim.event_reg_count = ndev->metadata.shim.event_reg_count; - if (copy_to_user(u64_to_user_ptr(args->buffer), meta, sizeof(*meta))) + buf_sz = min(args->buffer_size, sizeof(*meta)); + if (copy_to_user(u64_to_user_ptr(args->buffer), meta, buf_sz)) ret = -EFAULT; kfree(meta); @@ -690,12 +688,14 @@ static int aie2_get_aie_version(struct amdxdna_client *client, struct amdxdna_drm_query_aie_version version; struct amdxdna_dev *xdna = client->xdna; struct amdxdna_dev_hdl *ndev; + u32 buf_sz; ndev = xdna->dev_handle; version.major = ndev->version.major; version.minor = ndev->version.minor; - if (copy_to_user(u64_to_user_ptr(args->buffer), &version, sizeof(version))) + buf_sz = min(args->buffer_size, sizeof(version)); + if (copy_to_user(u64_to_user_ptr(args->buffer), &version, buf_sz)) return -EFAULT; return 0; @@ -706,13 +706,15 @@ static int aie2_get_firmware_version(struct amdxdna_client *client, { struct amdxdna_drm_query_firmware_version version; struct amdxdna_dev *xdna = client->xdna; + u32 buf_sz; version.major = xdna->fw_ver.major; version.minor = xdna->fw_ver.minor; version.patch = xdna->fw_ver.sub; version.build = xdna->fw_ver.build; - if (copy_to_user(u64_to_user_ptr(args->buffer), &version, sizeof(version))) + buf_sz = min(args->buffer_size, sizeof(version)); + if (copy_to_user(u64_to_user_ptr(args->buffer), &version, buf_sz)) return -EFAULT; return 0; @@ -724,11 +726,13 @@ static int aie2_get_power_mode(struct amdxdna_client *client, struct amdxdna_drm_get_power_mode mode = {}; struct amdxdna_dev *xdna = client->xdna; struct amdxdna_dev_hdl *ndev; + u32 buf_sz; ndev = xdna->dev_handle; mode.power_mode = ndev->pw_mode; - if (copy_to_user(u64_to_user_ptr(args->buffer), &mode, sizeof(mode))) + buf_sz = min(args->buffer_size, sizeof(mode)); + if (copy_to_user(u64_to_user_ptr(args->buffer), &mode, buf_sz)) return -EFAULT; return 0; @@ -741,6 +745,7 @@ static int aie2_get_clock_metadata(struct amdxdna_client *client, struct amdxdna_dev *xdna = client->xdna; struct amdxdna_dev_hdl *ndev; int ret = 0; + u32 buf_sz; ndev = xdna->dev_handle; clock = kzalloc_obj(*clock); @@ -753,7 +758,8 @@ static int aie2_get_clock_metadata(struct amdxdna_client *client, snprintf(clock->h_clock.name, sizeof(clock->h_clock.name), "H Clock"); clock->h_clock.freq_mhz = ndev->hclk_freq; - if (copy_to_user(u64_to_user_ptr(args->buffer), clock, sizeof(*clock))) + buf_sz = min(args->buffer_size, sizeof(*clock)); + if (copy_to_user(u64_to_user_ptr(args->buffer), clock, buf_sz)) ret = -EFAULT; kfree(clock); @@ -779,12 +785,14 @@ static int aie2_get_sensors(struct amdxdna_client *client, scnprintf(sensor.label, sizeof(sensor.label), "Total Power"); scnprintf(sensor.units, sizeof(sensor.units), "mW"); + if (args->buffer_size < sizeof(sensor)) + goto out; + if (copy_to_user(u64_to_user_ptr(args->buffer), &sensor, sizeof(sensor))) return -EFAULT; + args->buffer_size -= sizeof(sensor); sensors_count++; - if (args->buffer_size <= sensors_count * sizeof(sensor)) - goto out; for (i = 0; i < min_t(u32, ndev->total_col, 8); i++) { memset(&sensor, 0, sizeof(sensor)); @@ -794,13 +802,15 @@ static int aie2_get_sensors(struct amdxdna_client *client, scnprintf(sensor.label, sizeof(sensor.label), "Column %d Utilization", i); scnprintf(sensor.units, sizeof(sensor.units), "%%"); + if (args->buffer_size < sizeof(sensor)) + goto out; + if (copy_to_user(u64_to_user_ptr(args->buffer) + sensors_count * sizeof(sensor), &sensor, sizeof(sensor))) return -EFAULT; + args->buffer_size -= sizeof(sensor); sensors_count++; - if (args->buffer_size <= sensors_count * sizeof(sensor)) - goto out; } out: @@ -896,6 +906,7 @@ static int aie2_query_resource_info(struct amdxdna_client *client, const struct amdxdna_dev_priv *priv; struct amdxdna_dev_hdl *ndev; struct amdxdna_dev *xdna; + u32 buf_sz; xdna = client->xdna; ndev = xdna->dev_handle; @@ -907,7 +918,8 @@ static int aie2_query_resource_info(struct amdxdna_client *client, res_info.npu_tops_curr = ndev->curr_tops; res_info.npu_task_curr = ndev->hwctx_num; - if (copy_to_user(u64_to_user_ptr(args->buffer), &res_info, sizeof(res_info))) + buf_sz = min(args->buffer_size, sizeof(res_info)); + if (copy_to_user(u64_to_user_ptr(args->buffer), &res_info, buf_sz)) return -EFAULT; return 0; @@ -943,12 +955,7 @@ static int aie2_get_telemetry(struct amdxdna_client *client, XDNA_ERR(xdna, "Invalid buffer size"); return -EINVAL; } - telemetry_data_sz = args->buffer_size - header_sz; - if (telemetry_data_sz > SZ_4M) { - XDNA_ERR(xdna, "Buffer size is too big, %d", telemetry_data_sz); - return -EINVAL; - } header = kzalloc(header_sz, GFP_KERNEL); if (!header) @@ -989,6 +996,7 @@ static int aie2_get_preempt_state(struct amdxdna_client *client, struct amdxdna_drm_attribute_state state = {}; struct amdxdna_dev *xdna = client->xdna; struct amdxdna_dev_hdl *ndev; + u32 buf_sz; ndev = xdna->dev_handle; if (args->param == DRM_AMDXDNA_GET_FORCE_PREEMPT_STATE) @@ -996,7 +1004,8 @@ static int aie2_get_preempt_state(struct amdxdna_client *client, else if (args->param == DRM_AMDXDNA_GET_FRAME_BOUNDARY_PREEMPT_STATE) state.state = ndev->frame_boundary_preempt; - if (copy_to_user(u64_to_user_ptr(args->buffer), &state, sizeof(state))) + buf_sz = min(args->buffer_size, sizeof(state)); + if (copy_to_user(u64_to_user_ptr(args->buffer), &state, buf_sz)) return -EFAULT; return 0; From afc9ebbb0a9bb76c2becafb36804b3b2a5f2d751 Mon Sep 17 00:00:00 2001 From: Max Zhen Date: Mon, 6 Apr 2026 14:14:03 -0700 Subject: [PATCH 0736/2758] accel/amdxdna: Handle DETACH_DEBUG_BO through config_debug_bo path [ Upstream commit f844177c6811fd322aa84ed5c32f0e39743446c2 ] Route DETACH_DEBUG_BO through aie2_config_debug_bo() the same way as ATTACH_DEBUG_BO. The scheduler switch in aie2_sched_job_run() already handles ATTACH_DEBUG_BO with aie2_config_debug_bo(), but DETACH_DEBUG_BO was not included in that path. Add an explicit fallthrough so both attach and detach operations use the same handler. This fixes debug BO detach handling by ensuring the detach command is processed by the expected configuration path. Fixes: 7ea046838021 ("accel/amdxdna: Support firmware debug buffer") Signed-off-by: Max Zhen Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260406211403.4011988-1-lizhi.hou@amd.com Signed-off-by: Sasha Levin --- drivers/accel/amdxdna/aie2_ctx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c index 2dc69900ca010..d719000c45325 100644 --- a/drivers/accel/amdxdna/aie2_ctx.c +++ b/drivers/accel/amdxdna/aie2_ctx.c @@ -360,6 +360,7 @@ aie2_sched_job_run(struct drm_sched_job *sched_job) ret = aie2_sync_bo(hwctx, job, aie2_sched_drvcmd_resp_handler); break; case ATTACH_DEBUG_BO: + case DETACH_DEBUG_BO: ret = aie2_config_debug_bo(hwctx, job, aie2_sched_drvcmd_resp_handler); break; default: From 032645be8d88ae575d84fbe6e56db67ae5ba9580 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Mon, 13 Apr 2026 11:02:38 -0700 Subject: [PATCH 0737/2758] accel/amdxdna: Fix iommu_map_sgtable() return value handling [ Upstream commit 1bcfa4c4e88a554d1b6f98f4e3f886288581cbb4 ] iommu_map_sgtable() returns negative error codes on failure, but the result is stored in an unsigned variable. This prevents proper error detection. Change the variable type to ssize_t so negative error values can be handled correctly. Fixes: ece3e8980907 ("accel/amdxdna: Allow forcing IOVA-based DMA via module parameter") Reported-by: Dan Carpenter Closes: https://lore.kernel.org/all/adk7kOUBwIyYnX1M@stanley.mountain/ Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Wendy Liang Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260413180238.668441-1-lizhi.hou@amd.com Signed-off-by: Sasha Levin --- drivers/accel/amdxdna/amdxdna_iommu.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/accel/amdxdna/amdxdna_iommu.c b/drivers/accel/amdxdna/amdxdna_iommu.c index 4626434d4180c..2676cfcfabee0 100644 --- a/drivers/accel/amdxdna/amdxdna_iommu.c +++ b/drivers/accel/amdxdna/amdxdna_iommu.c @@ -40,7 +40,7 @@ int amdxdna_iommu_map_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo) struct sg_table *sgt; dma_addr_t dma_addr; struct iova *iova; - size_t size; + ssize_t size; if (abo->type != AMDXDNA_BO_DEV_HEAP && abo->type != AMDXDNA_BO_SHMEM) return 0; @@ -65,7 +65,14 @@ int amdxdna_iommu_map_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo) size = iommu_map_sgtable(xdna->domain, dma_addr, sgt, IOMMU_READ | IOMMU_WRITE); + if (size < 0) { + XDNA_ERR(xdna, "iommu_map_sgtable failed: %zd", size); + __free_iova(&xdna->iovad, iova); + return size; + } + if (size < abo->mem.size) { + iommu_unmap(xdna->domain, dma_addr, size); __free_iova(&xdna->iovad, iova); return -ENXIO; } From ebfc332033a46e3c35223909c5e4dde24f643945 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Mon, 13 Apr 2026 11:18:43 -0700 Subject: [PATCH 0738/2758] accel/amdxdna: Fix order of canceled mailbox messages [ Upstream commit c83ad8ea6b0a53f1ed61ae0b4b9606bdfe338b33 ] Mailbox message IDs are allocated cyclically. When destroying a mailbox channel, pending messages are canceled starting from message ID 0. This results in an incorrect cancellation order when the ID of the last posted message wraps around and is smaller than the ID of the first posted message. Fix this by canceling pending messages starting from the next available message ID, ensuring the correct ordering across wraparound. Fixes: a37d78470bcc ("accel/amdxdna: Replace idr api with xarray") Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260413181843.670796-1-lizhi.hou@amd.com Signed-off-by: Sasha Levin --- drivers/accel/amdxdna/amdxdna_mailbox.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/accel/amdxdna/amdxdna_mailbox.c b/drivers/accel/amdxdna/amdxdna_mailbox.c index e681a090752df..d3639a2ea2395 100644 --- a/drivers/accel/amdxdna/amdxdna_mailbox.c +++ b/drivers/accel/amdxdna/amdxdna_mailbox.c @@ -538,7 +538,9 @@ void xdna_mailbox_stop_channel(struct mailbox_channel *mb_chann) drain_workqueue(mb_chann->work_q); /* We can clean up and release resources */ - xa_for_each(&mb_chann->chan_xa, msg_id, mb_msg) + xa_for_each_start(&mb_chann->chan_xa, msg_id, mb_msg, mb_chann->next_msgid) + mailbox_release_msg(mb_chann, mb_msg); + xa_for_each_range(&mb_chann->chan_xa, msg_id, mb_msg, 0, mb_chann->next_msgid - 1) mailbox_release_msg(mb_chann, mb_msg); xa_destroy(&mb_chann->chan_xa); From 4e01fc9a5bc49b04fad403ffa71299b35e132ca8 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Wed, 15 Apr 2026 09:32:07 +0100 Subject: [PATCH 0739/2758] dma-fence: Fix potential tracepoint null pointer dereferences MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit e94b9f01543cc6a83538c2c2cc645a424d3015ca ] Trace_dma_fence_signaled, trace_dma_fence_wait_end and trace_dma_fence_destroy can all currently dereference a null fence->ops pointer after it has been reset on fence signalling. Lets use the safe string getters for most tracepoints to avoid this class of a problem, while for the signal tracepoint we move it to before ops are cleared to avoid losing the driver and timeline name information. Apart from moving it we also need to add a new tracepoint class to bypass the safe name getters since the signaled bit is already set. For dma_fence_init we also need to use the new tracepoint class since the rcu read lock is not held there, and we can do the same for the enable signaling since there we are certain the fence cannot be signaled while we are holding the lock and have even validated the fence->ops. Signed-off-by: Tvrtko Ursulin Fixes: 541c8f2468b9 ("dma-buf: detach fence ops on signal v3") Cc: Christian König Cc: Philipp Stanner Cc: Boris Brezillon Cc: linux-media@vger.kernel.org Cc: linaro-mm-sig@lists.linaro.org Reviewed-by: Christian König Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260415083207.40513-2-tvrtko.ursulin@igalia.com Signed-off-by: Sasha Levin --- drivers/dma-buf/dma-fence.c | 3 ++- include/trace/events/dma_fence.h | 40 +++++++++++++++++++++++++++----- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c index a2aa82f4eedd4..b3bfa6943a8e1 100644 --- a/drivers/dma-buf/dma-fence.c +++ b/drivers/dma-buf/dma-fence.c @@ -363,6 +363,8 @@ void dma_fence_signal_timestamp_locked(struct dma_fence *fence, &fence->flags))) return; + trace_dma_fence_signaled(fence); + /* * When neither a release nor a wait operation is specified set the ops * pointer to NULL to allow the fence structure to become independent @@ -377,7 +379,6 @@ void dma_fence_signal_timestamp_locked(struct dma_fence *fence, fence->timestamp = timestamp; set_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &fence->flags); - trace_dma_fence_signaled(fence); list_for_each_entry_safe(cur, tmp, &cb_list, node) { INIT_LIST_HEAD(&cur->node); diff --git a/include/trace/events/dma_fence.h b/include/trace/events/dma_fence.h index 3abba45c0601a..5b10a9e06fb4e 100644 --- a/include/trace/events/dma_fence.h +++ b/include/trace/events/dma_fence.h @@ -9,12 +9,40 @@ struct dma_fence; +DECLARE_EVENT_CLASS(dma_fence, + + TP_PROTO(struct dma_fence *fence), + + TP_ARGS(fence), + + TP_STRUCT__entry( + __string(driver, dma_fence_driver_name(fence)) + __string(timeline, dma_fence_timeline_name(fence)) + __field(unsigned int, context) + __field(unsigned int, seqno) + ), + + TP_fast_assign( + __assign_str(driver); + __assign_str(timeline); + __entry->context = fence->context; + __entry->seqno = fence->seqno; + ), + + TP_printk("driver=%s timeline=%s context=%u seqno=%u", + __get_str(driver), __get_str(timeline), __entry->context, + __entry->seqno) +); + /* * Safe only for call sites which are guaranteed to not race with fence - * signaling,holding the fence->lock and having checked for not signaled, or the - * signaling path itself. + * signaling, holding the fence->lock and having checked for not signaled, or + * the signaling path itself. + * + * TODO: Remove the need for this event class when drivers switch to independent + * fences. */ -DECLARE_EVENT_CLASS(dma_fence, +DECLARE_EVENT_CLASS(dma_fence_ops, TP_PROTO(struct dma_fence *fence), @@ -46,7 +74,7 @@ DEFINE_EVENT(dma_fence, dma_fence_emit, TP_ARGS(fence) ); -DEFINE_EVENT(dma_fence, dma_fence_init, +DEFINE_EVENT(dma_fence_ops, dma_fence_init, TP_PROTO(struct dma_fence *fence), @@ -60,14 +88,14 @@ DEFINE_EVENT(dma_fence, dma_fence_destroy, TP_ARGS(fence) ); -DEFINE_EVENT(dma_fence, dma_fence_enable_signal, +DEFINE_EVENT(dma_fence_ops, dma_fence_enable_signal, TP_PROTO(struct dma_fence *fence), TP_ARGS(fence) ); -DEFINE_EVENT(dma_fence, dma_fence_signaled, +DEFINE_EVENT(dma_fence_ops, dma_fence_signaled, TP_PROTO(struct dma_fence *fence), From 6ddaf49a1088f7bd7eef278d7fcb3a2eceb4b458 Mon Sep 17 00:00:00 2001 From: Max Zhen Date: Tue, 14 Apr 2026 09:56:24 -0700 Subject: [PATCH 0740/2758] accel/amdxdna: Fix fatal_error_info layout in firmware interface [ Upstream commit 2d1e82f1224f3109481bb0ed3683e54b09b978e3 ] Adjust struct fatal_error_info to match the expected driver/firmware interface layout. The structure is used to retrieve debug information from firmware when a command becomes stuck on the device. The reserved field currently uses 128 u32 entries, which makes the structure larger than intended and causes the layout to no longer match the firmware definition. Reduce the reserved array size from 128 to 127 entries so the structure matches the expected interface format. Fixes: 25854131c04a ("accel/amdxdna: Support retrieving hardware context debug information") Signed-off-by: Max Zhen Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260414165625.788853-1-lizhi.hou@amd.com Signed-off-by: Sasha Levin --- drivers/accel/amdxdna/aie2_msg_priv.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/accel/amdxdna/aie2_msg_priv.h b/drivers/accel/amdxdna/aie2_msg_priv.h index f18e89a39e357..4a01d73209bbf 100644 --- a/drivers/accel/amdxdna/aie2_msg_priv.h +++ b/drivers/accel/amdxdna/aie2_msg_priv.h @@ -460,7 +460,7 @@ struct fatal_error_info { __u32 exception_pc; /* Program Counter at the time of the exception */ __u32 app_module; /* Error module name */ __u32 task_index; /* Index of the task in which the error occurred */ - __u32 reserved[128]; + __u32 reserved[127]; }; struct app_health_report { From 966e13ccadc87e3010285a80f80c99d4259e47ea Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Thu, 16 Apr 2026 21:37:23 +0800 Subject: [PATCH 0741/2758] accel/amdxdna: Fix memory leak in amdxdna_iommu_alloc() [ Upstream commit 432fafdc9a3122a7bee5b2bfd23dcf2dc262a3d7 ] In amdxdna_iommu_alloc(), if iommu_map() fails after successfully allocating both iova and cpu_addr, the code jumps to free_iova which only frees the iova, leaking the allocated pages. Fixes: ece3e8980907 ("accel/amdxdna: Allow forcing IOVA-based DMA via module parameter") Signed-off-by: Felix Gu Reviewed-by: Lizhi Hou Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260416-amdxdna-v1-1-30c13008365c@gmail.com Signed-off-by: Sasha Levin --- drivers/accel/amdxdna/amdxdna_iommu.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/accel/amdxdna/amdxdna_iommu.c b/drivers/accel/amdxdna/amdxdna_iommu.c index 2676cfcfabee0..5a9f06183487f 100644 --- a/drivers/accel/amdxdna/amdxdna_iommu.c +++ b/drivers/accel/amdxdna/amdxdna_iommu.c @@ -117,10 +117,12 @@ void *amdxdna_iommu_alloc(struct amdxdna_dev *xdna, size_t size, dma_addr_t *dma iova_align(&xdna->iovad, size), IOMMU_READ | IOMMU_WRITE, GFP_KERNEL); if (ret) - goto free_iova; + goto free_cpu_addr; return cpu_addr; +free_cpu_addr: + free_pages((unsigned long)cpu_addr, get_order(size)); free_iova: __free_iova(&xdna->iovad, iova); return ERR_PTR(ret); From 3a555d9e92d598948a8e8bef5996b242b9743a99 Mon Sep 17 00:00:00 2001 From: Jonathan Cavitt Date: Fri, 30 Jan 2026 19:19:54 +0000 Subject: [PATCH 0742/2758] drm/gpuvm: Do not prepare NULL objects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 88f059f6f6f589bd78e2ceb05a1339a8c10b8626 ] Statis analysis issue: drm_gpuvm_prepare_range issues an exec_object_prepare call to all drm_gem_objects mapped between addr and addr + range. However, it is possible (albeit very unlikely) that the objects found through drm_gpuvm_for_each_va_range (as connected to va->gem) are NULL, as seen in other functions such as drm_gpuva_link and drm_gpuva_unlink_defer. Do not prepare NULL objects. Fixes: 50c1a36f594b ("drm/gpuvm: track/lock/validate external/evicted objects") Signed-off-by: Jonathan Cavitt Cc: Matthew Brost Cc: Thomas Hellström Reviewed-by: Krzysztof Karas Link: https://patch.msgid.link/20260130191953.61718-2-jonathan.cavitt@intel.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/drm_gpuvm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/drm_gpuvm.c b/drivers/gpu/drm/drm_gpuvm.c index 44acfe4120d24..f56719e9f4350 100644 --- a/drivers/gpu/drm/drm_gpuvm.c +++ b/drivers/gpu/drm/drm_gpuvm.c @@ -1322,6 +1322,9 @@ drm_gpuvm_prepare_range(struct drm_gpuvm *gpuvm, struct drm_exec *exec, drm_gpuvm_for_each_va_range(va, gpuvm, addr, end) { struct drm_gem_object *obj = va->gem.obj; + if (unlikely(!obj)) + continue; + ret = exec_prepare_obj(exec, obj, num_fences); if (ret) return ret; From 83d6d375f357ed2702b28d739511aad56d485de7 Mon Sep 17 00:00:00 2001 From: Max Zhen Date: Thu, 16 Apr 2026 13:11:06 -0700 Subject: [PATCH 0743/2758] accel/amdxdna: Guard management mailbox channel cleanup against NULL pointer [ Upstream commit 506255d46bdb93a281cf39e72abbca124f5c7a1b ] The management mailbox channel cleanup helpers can be called from error handling paths when mgmt_chann has already been destroyed. Add NULL checks to xdna_mailbox_free_channel() and xdna_mailbox_stop_channel() so the cleanup path safely returns instead of dereferencing a NULL mailbox channel pointer. Fixes: b87f920b9344 ("accel/amdxdna: Support hardware mailbox") Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Max Zhen Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260416201106.1046072-1-lizhi.hou@amd.com Signed-off-by: Sasha Levin --- drivers/accel/amdxdna/amdxdna_mailbox.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/accel/amdxdna/amdxdna_mailbox.c b/drivers/accel/amdxdna/amdxdna_mailbox.c index d3639a2ea2395..13c20bb3890c9 100644 --- a/drivers/accel/amdxdna/amdxdna_mailbox.c +++ b/drivers/accel/amdxdna/amdxdna_mailbox.c @@ -485,6 +485,9 @@ struct mailbox_channel *xdna_mailbox_alloc_channel(struct mailbox *mb) void xdna_mailbox_free_channel(struct mailbox_channel *mb_chann) { + if (!mb_chann) + return; + destroy_workqueue(mb_chann->work_q); kfree(mb_chann); } @@ -531,6 +534,9 @@ void xdna_mailbox_stop_channel(struct mailbox_channel *mb_chann) struct mailbox_msg *mb_msg; unsigned long msg_id; + if (!mb_chann) + return; + /* Disable an irq and wait. This might sleep. */ free_irq(mb_chann->msix_irq, mb_chann); From 50d468733e5ec9ed3ec1ecfce6b4c7c312630412 Mon Sep 17 00:00:00 2001 From: Werner Kasselman Date: Tue, 14 Apr 2026 05:08:42 +0000 Subject: [PATCH 0744/2758] drm/amdgpu: fix integer overflow in amdgpu_gem_align_pitch() [ Upstream commit fc3659f178d4a65599167d5a648bbeef4b0d4446 ] amdgpu_gem_align_pitch() is passed u32 width and cpp from dumb buffer creation but uses signed int internally. The round-up add and the aligned * cpp multiplication can overflow, returning zero or a negative pitch. A zero pitch propagates to a zero-sized GEM object allocation that reaches userspace via DRM_IOCTL_MODE_CREATE_DUMB. Switch the helper to unsigned int and use check_add_overflow() / check_mul_overflow() so wraparound returns zero. Reject a zero pitch or size in amdgpu_mode_dumb_create() rather than allocating a zero- byte BO. Fixes: 8e911ab770f7 ("drm: amdgpu: Replace drm_fb_get_bpp_depth() with drm_format_plane_cpp()") Signed-off-by: Werner Kasselman Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index fe6d988e7f245..1120f8225ac02 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -27,6 +27,7 @@ */ #include #include +#include #include #include #include @@ -1228,13 +1229,14 @@ int amdgpu_gem_list_handles_ioctl(struct drm_device *dev, void *data, return ret; } -static int amdgpu_gem_align_pitch(struct amdgpu_device *adev, - int width, - int cpp, - bool tiled) +static unsigned int amdgpu_gem_align_pitch(struct amdgpu_device *adev, + unsigned int width, + unsigned int cpp, + bool tiled) { - int aligned = width; - int pitch_mask = 0; + unsigned int aligned = width; + unsigned int pitch_mask = 0; + unsigned int pitch; switch (cpp) { case 1: @@ -1249,9 +1251,12 @@ static int amdgpu_gem_align_pitch(struct amdgpu_device *adev, break; } - aligned += pitch_mask; + if (check_add_overflow(aligned, pitch_mask, &aligned)) + return 0; aligned &= ~pitch_mask; - return aligned * cpp; + if (check_mul_overflow(aligned, cpp, &pitch)) + return 0; + return pitch; } int amdgpu_mode_dumb_create(struct drm_file *file_priv, @@ -1278,8 +1283,12 @@ int amdgpu_mode_dumb_create(struct drm_file *file_priv, args->pitch = amdgpu_gem_align_pitch(adev, args->width, DIV_ROUND_UP(args->bpp, 8), 0); + if (!args->pitch) + return -EINVAL; args->size = (u64)args->pitch * args->height; args->size = ALIGN(args->size, PAGE_SIZE); + if (!args->size) + return -EINVAL; domain = amdgpu_bo_get_preferred_domain(adev, amdgpu_display_supported_domains(adev, flags)); r = amdgpu_gem_object_create(adev, args->size, 0, domain, flags, From 4bab3a7e66db9b6311586c4178e68eabbb15b29e Mon Sep 17 00:00:00 2001 From: Alex Sierra Date: Tue, 7 Apr 2026 17:22:44 -0500 Subject: [PATCH 0745/2758] drm/amdkfd: fix redundant MQD iterations in GFX v12.1 [ Upstream commit 9315a1e2bdf1ba4aace856cabcb5f9f3a5c09202 ] The init_mqd_v12_1 function and its sub-call update_mqd_v12_1 both independently iterate over XCC-specific MQDs. This nested iteration is redundant and can cause MQDs in different queues to be overwritten. This patch removes the duplicate loop logic to prevent queue corruption. Fixes: 01bbc4a4b947 ("drm/amdkfd: Add MQD manager for GFX 12.1.0") Signed-off-by: Alex Sierra Reviewed-by: Felix Kuehling Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c index c90c0d99b1e3f..475589b924e90 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c @@ -32,6 +32,10 @@ #include "amdgpu_amdkfd.h" #include "kfd_device_queue_manager.h" +static void update_mqd(struct mqd_manager *mm, void *mqd, + struct queue_properties *q, + struct mqd_update_info *minfo); + static inline struct v12_1_compute_mqd *get_mqd(void *mqd) { return (struct v12_1_compute_mqd *)mqd; @@ -215,7 +219,7 @@ static void init_mqd(struct mqd_manager *mm, void **mqd, *mqd = m; if (gart_addr) *gart_addr = addr; - mm->update_mqd(mm, m, q, NULL); + update_mqd(mm, m, q, NULL); } static int load_mqd(struct mqd_manager *mm, void *mqd, From dfc7b5b5599472277e71e5bd2712740651c7c5be Mon Sep 17 00:00:00 2001 From: Werner Kasselman Date: Wed, 15 Apr 2026 22:13:52 +0000 Subject: [PATCH 0746/2758] drm/radeon: fix integer overflow in radeon_align_pitch() [ Upstream commit ce3b24eb3ee8f82de851535f516bf21f83e82259 ] radeon_align_pitch() has the same kind of overflow issue as the old amdgpu helper: both the alignment round-up add and the final 'aligned * cpp' calculation can overflow signed int. If that wraps, radeon_mode_dumb_create() can end up returning an invalid pitch or creating a zero-sized dumb buffer. Fix this by using check_add_overflow() for the alignment round-up and check_mul_overflow() for the final pitch calculation, returning 0 on overflow. Also reject zero pitch and size in radeon_mode_dumb_create(). Found via AST-based call-graph analysis using sqry. Fixes: ff72145badb8 ("drm: dumb scanout create/mmap for intel/radeon (v3)") Signed-off-by: Werner Kasselman Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/radeon/radeon_gem.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c index 20fc87409f2e4..8ce180e22d1d1 100644 --- a/drivers/gpu/drm/radeon/radeon_gem.c +++ b/drivers/gpu/drm/radeon/radeon_gem.c @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -812,6 +813,7 @@ int radeon_align_pitch(struct radeon_device *rdev, int width, int cpp, bool tile int aligned = width; int align_large = (ASIC_IS_AVIVO(rdev)) || tiled; int pitch_mask = 0; + int pitch; switch (cpp) { case 1: @@ -826,9 +828,12 @@ int radeon_align_pitch(struct radeon_device *rdev, int width, int cpp, bool tile break; } - aligned += pitch_mask; + if (check_add_overflow(aligned, pitch_mask, &aligned)) + return 0; aligned &= ~pitch_mask; - return aligned * cpp; + if (check_mul_overflow(aligned, cpp, &pitch)) + return 0; + return pitch; } int radeon_mode_dumb_create(struct drm_file *file_priv, @@ -842,8 +847,12 @@ int radeon_mode_dumb_create(struct drm_file *file_priv, args->pitch = radeon_align_pitch(rdev, args->width, DIV_ROUND_UP(args->bpp, 8), 0); + if (!args->pitch) + return -EINVAL; args->size = (u64)args->pitch * args->height; args->size = ALIGN(args->size, PAGE_SIZE); + if (!args->size) + return -EINVAL; r = radeon_gem_object_create(rdev, args->size, 0, RADEON_GEM_DOMAIN_VRAM, 0, From ccc42187fc2d0720947a63ce1b9e399d2c068ff4 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Thu, 16 Apr 2026 15:55:37 -0400 Subject: [PATCH 0747/2758] drm/radeon: fix memory leak in radeon_ring_restore() on lock failure [ Upstream commit 82f1d6042611d45b8b9de423bbcb4e0ced9ec62b ] radeon_ring_restore() takes ownership of the data buffer allocated by radeon_ring_backup(). The caller (radeon_gpu_reset()) only frees it in the non-restore branch; in the restore branch it relies on radeon_ring_restore() to free it. If radeon_ring_lock() fails, the function returned early without calling kvfree(data), leaking the ring backup buffer on every GPU reset that fails at the lock stage. During repeated GPU resets this causes cumulative kernel memory exhaustion. Free data before returning the error. Fixes: 55d7c22192be ("drm/radeon: implement ring saving on reset v4") Signed-off-by: Yuho Choi Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/radeon/radeon_ring.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c index 581ae20c46e4b..a5dff072c1ac0 100644 --- a/drivers/gpu/drm/radeon/radeon_ring.c +++ b/drivers/gpu/drm/radeon/radeon_ring.c @@ -356,8 +356,10 @@ int radeon_ring_restore(struct radeon_device *rdev, struct radeon_ring *ring, /* restore the saved ring content */ r = radeon_ring_lock(rdev, ring, size); - if (r) + if (r) { + kvfree(data); return r; + } for (i = 0; i < size; ++i) { radeon_ring_write(ring, data[i]); From c521733289b7cb12a0fd2d6e59ca0aea9bfecad8 Mon Sep 17 00:00:00 2001 From: Aaron Tomlin Date: Sat, 18 Apr 2026 23:09:44 -0400 Subject: [PATCH 0748/2758] libbpf: Report error when a negative kprobe offset is specified [ Upstream commit ad35d8018669fd2eea76e3f74eb050fd3d2fb690 ] In attach_kprobe(), the parsing logic uses sscanf() to extract the target function name and offset from the section definition. Currently, if a user specifies a negative offset (e.g., SEC("kprobe/func+-100")), the input is not explicitly caught and reported as an error. This commit updates the logic to explicitly notify the user when a negative integer is provided. To facilitate this check, the offset variable is changed from unsigned long to long so that sscanf() can accurately capture a negative input for evaluation. If a negative offset is detected, the loader will now print an informative warning stating that the offset must be non-negative, and return -EINVAL. Additionally, free(func) is called in this new error path to prevent a memory leak, as the function name string is dynamically allocated by sscanf(). Fixes: e3f9bc35ea7e9 ("libbpf: Allow decimal offset for kprobes") Signed-off-by: Aaron Tomlin Acked-by: Mykyta Yatsenko Link: https://lore.kernel.org/bpf/20260419030944.1423642-1-atomlin@atomlin.com Signed-off-by: Kumar Kartikeya Dwivedi Signed-off-by: Sasha Levin --- tools/lib/bpf/libbpf.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 3a80a018fc7d5..83aae7a39d36d 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -12280,7 +12280,7 @@ bpf_program__attach_kprobe_multi_opts(const struct bpf_program *prog, static int attach_kprobe(const struct bpf_program *prog, long cookie, struct bpf_link **link) { DECLARE_LIBBPF_OPTS(bpf_kprobe_opts, opts); - unsigned long offset = 0; + long offset = 0; const char *func_name; char *func; int n; @@ -12302,6 +12302,13 @@ static int attach_kprobe(const struct bpf_program *prog, long cookie, struct bpf pr_warn("kprobe name is invalid: %s\n", func_name); return -EINVAL; } + + if (offset < 0) { + free(func); + pr_warn("kprobe offset must be a non-negative integer: %li\n", offset); + return -EINVAL; + } + if (opts.retprobe && offset != 0) { free(func); pr_warn("kretprobes do not support offset specification\n"); From 0ac0063afc55c8e4a56b7b415b5e8030295d6432 Mon Sep 17 00:00:00 2001 From: Matt Bobrowski Date: Mon, 20 Apr 2026 09:37:34 +0000 Subject: [PATCH 0749/2758] selftests/bpf: Fix off-by-one in bpf_cpumask_populate related selftest [ Upstream commit 0aa6378695b8c67146130812f635f07c4898f171 ] The test_populate test uses >= instead of > when checking if the runtime nr_cpus exceeds the bit capacity of a cpumask_t. On a system where the physical CPU core count perfectly matches the CONFIG_NR_CPUS upper bound (e.g. nr_cpus = 512 and CONFIG_NR_CPUS = 512), the condition nr_cpus >= CPUMASK_TEST_MASKLEN * 8 evaluates to true (512 >= 512). This incorrectly causes the test to fail with an error value of 3. A 512-bit cpumask_t provides enough bits (indices 0 through 511) to represent 512 CPUs. The subsequent bpf_for(i, 0, nr_cpus) loop iterates up to nr_cpus - 1 (511), which perfectly aligns with the maximum valid index of the bitmask. Change the condition to nr_cpus > CPUMASK_TEST_MASKLEN * 8 to fix the false positive failure on these systems. Fixes: 918ba2636d4e ("selftests: bpf: add bpf_cpumask_populate selftests") Signed-off-by: Matt Bobrowski Acked-by: Paul Chaignon Link: https://lore.kernel.org/bpf/20260420093734.2400330-1-mattbobrowski@google.com Signed-off-by: Kumar Kartikeya Dwivedi Signed-off-by: Sasha Levin --- tools/testing/selftests/bpf/progs/cpumask_success.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/progs/cpumask_success.c b/tools/testing/selftests/bpf/progs/cpumask_success.c index 0e04c31b91c0c..774706e7b058b 100644 --- a/tools/testing/selftests/bpf/progs/cpumask_success.c +++ b/tools/testing/selftests/bpf/progs/cpumask_success.c @@ -866,7 +866,7 @@ int BPF_PROG(test_populate, struct task_struct *task, u64 clone_flags) * access NR_CPUS, the upper bound for nr_cpus, so we infer * it from the size of cpumask_t. */ - if (nr_cpus < 0 || nr_cpus >= CPUMASK_TEST_MASKLEN * 8) { + if (nr_cpus < 0 || nr_cpus > CPUMASK_TEST_MASKLEN * 8) { err = 3; goto out; } From eb99b69b743ee8aeb4017e0689ea2e049120ebf4 Mon Sep 17 00:00:00 2001 From: Nick Hu Date: Sun, 19 Apr 2026 23:18:55 -0700 Subject: [PATCH 0750/2758] dt-bindings: timer: Remove sifive,fine-ctr-bits property [ Upstream commit 1ccca10755107bc8c649937d1ba69651d1ef9da2 ] The counter width can be inferred from the compatible string, making the explicit "sifive,fine-ctr-bits" property redundant. Remove the property to simplify the bindings. Fixes: 0f920690a82c ("dt-bindings: timer: Add SiFive CLINT2") Suggested-by: Conor Dooley Signed-off-by: Nick Hu Signed-off-by: Daniel Lezcano Acked-by: Conor Dooley Link: https://lore.kernel.org/linux-riscv/20260330-relative-hardened-5ce35fe1ef57@spud/ Link: https://patch.msgid.link/20260419-clintv2-remove-fine-ctr-v1-1-7527f4d45850@sifive.com Signed-off-by: Sasha Levin --- .../devicetree/bindings/timer/sifive,clint.yaml | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/Documentation/devicetree/bindings/timer/sifive,clint.yaml b/Documentation/devicetree/bindings/timer/sifive,clint.yaml index 3c16b260db040..051edb1da0d73 100644 --- a/Documentation/devicetree/bindings/timer/sifive,clint.yaml +++ b/Documentation/devicetree/bindings/timer/sifive,clint.yaml @@ -72,22 +72,6 @@ properties: minItems: 1 maxItems: 4095 - sifive,fine-ctr-bits: - maximum: 15 - description: The width in bits of the fine counter. - -if: - properties: - compatible: - contains: - const: sifive,clint2 -then: - required: - - sifive,fine-ctr-bits -else: - properties: - sifive,fine-ctr-bits: false - additionalProperties: false required: From 585f673e728516088dcf768f24a121789ae97227 Mon Sep 17 00:00:00 2001 From: Aaradhana Sahu Date: Tue, 14 Apr 2026 11:58:29 +0530 Subject: [PATCH 0751/2758] wifi: ath12k: Fix invalid IRQ requests during AHB probe [ Upstream commit 590182b72213ef04977ab0b16b8dadfcfd25ff73 ] ath12k_ahb_config_ext_irq() iterates over ATH12K_EXT_IRQ_NUM_MAX (16) entries while checking TX ring masks, but the tcl_to_wbm_rbm_map array contains only DP_TCL_NUM_RING_MAX (4) valid elements. When the iterator (j) is greater than or equal to DP_TCL_NUM_RING_MAX, it accesses tcl_to_wbm_rbm_map[j] out of bounds. This results in reading uninitialized memory for wbm_ring_num, causing the driver to evaluate incorrect BIT() conditions and request IRQs for rings that do not have an assigned interrupt line or device tree entry. This leads to request_irq() failures with -ENXIO or -EINVAL during ath12k AHB probe. Fix this by splitting the loop into two separate loops: one iterating over DP_TCL_NUM_RING_MAX for TX ring, and another iterating over ATH12K_EXT_IRQ_NUM_MAX for remaining IRQ entries. Also add a bounds check for num_irq. Tested-on: IPQ5332 hw1.0 AHB WLAN.WBE.1.6-01275-QCAHKSWPL_SILICONZ-1 Fixes: 6cee30f0da75 ("wifi: ath12k: add AHB driver support for IPQ5332") Signed-off-by: Aaradhana Sahu Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260414062829.2371761-1-aaradhana.sahu@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath12k/ahb.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/ahb.c b/drivers/net/wireless/ath/ath12k/ahb.c index 2dcf0a52e4c15..30733a244454e 100644 --- a/drivers/net/wireless/ath/ath12k/ahb.c +++ b/drivers/net/wireless/ath/ath12k/ahb.c @@ -583,31 +583,36 @@ static int ath12k_ahb_config_ext_irq(struct ath12k_base *ab) netif_napi_add(irq_grp->napi_ndev, &irq_grp->napi, ath12k_ahb_ext_grp_napi_poll); - for (j = 0; j < ATH12K_EXT_IRQ_NUM_MAX; j++) { - /* For TX ring, ensure that the ring mask and the - * tcl_to_wbm_rbm_map point to the same ring number. - */ + for (j = 0; j < DP_TCL_NUM_RING_MAX; j++) { if (ring_mask->tx[i] & - BIT(ab->hal.tcl_to_wbm_rbm_map[j].wbm_ring_num)) { + BIT(ab->hal.tcl_to_wbm_rbm_map[j].wbm_ring_num) && + num_irq < ATH12K_EXT_IRQ_NUM_MAX) { irq_grp->irqs[num_irq++] = wbm2host_tx_completions_ring1 - j; } + } - if (ring_mask->rx[i] & BIT(j)) { + for (j = 0; j < ATH12K_EXT_IRQ_NUM_MAX; j++) { + if (ring_mask->rx[i] & BIT(j) && + num_irq < ATH12K_EXT_IRQ_NUM_MAX) { irq_grp->irqs[num_irq++] = reo2host_destination_ring1 - j; } - if (ring_mask->rx_err[i] & BIT(j)) + if (ring_mask->rx_err[i] & BIT(j) && + num_irq < ATH12K_EXT_IRQ_NUM_MAX) irq_grp->irqs[num_irq++] = reo2host_exception; - if (ring_mask->rx_wbm_rel[i] & BIT(j)) + if (ring_mask->rx_wbm_rel[i] & BIT(j) && + num_irq < ATH12K_EXT_IRQ_NUM_MAX) irq_grp->irqs[num_irq++] = wbm2host_rx_release; - if (ring_mask->reo_status[i] & BIT(j)) + if (ring_mask->reo_status[i] & BIT(j) && + num_irq < ATH12K_EXT_IRQ_NUM_MAX) irq_grp->irqs[num_irq++] = reo2host_status; - if (ring_mask->rx_mon_dest[i] & BIT(j)) + if (ring_mask->rx_mon_dest[i] & BIT(j) && + num_irq < ATH12K_EXT_IRQ_NUM_MAX) irq_grp->irqs[num_irq++] = rxdma2host_monitor_destination_mac1; } From 5da57e99987bc9f96150e872cc49d6ca31159541 Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Fri, 17 Apr 2026 10:33:17 +0200 Subject: [PATCH 0752/2758] libbpf: Fix deduplication of typedef with base definitions [ Upstream commit 0831b110eb4591e4ad8c5fd0d8f0f3f9979a5ff5 ] When deduplicating definitions for a module, typedef defined in the base are not removed. This is because the hash used for base types differs from the one used in the deduplication logic in btf_dedup_struct_type. This was introduced by the referenced commit when moving the typedef deduplication logic handling from btf_dedup_ref_type to btf_dedup_struct_type, as this also changed the hash logic (btf_hash_common to btf_hash_typedef). This also impacts other types referencing those typedef (e.g. const). In my test, the BTF section size of the openvswitch module went from 31KB to 45KB. Fixes: 3781413465df ("libbpf: Fix BTF dedup to support recursive typedef definitions"). Signed-off-by: Antoine Tenart Signed-off-by: Andrii Nakryiko Tested-by: Alan Maguire Reviewed-by: Alan Maguire Link: https://lore.kernel.org/bpf/20260417083319.32716-1-atenart@kernel.org Signed-off-by: Sasha Levin --- tools/lib/bpf/btf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c index ceb57b46a8782..771aeaa0262b2 100644 --- a/tools/lib/bpf/btf.c +++ b/tools/lib/bpf/btf.c @@ -4578,12 +4578,14 @@ static int btf_dedup_prep(struct btf_dedup *d) case BTF_KIND_RESTRICT: case BTF_KIND_PTR: case BTF_KIND_FWD: - case BTF_KIND_TYPEDEF: case BTF_KIND_FUNC: case BTF_KIND_FLOAT: case BTF_KIND_TYPE_TAG: h = btf_hash_common(t); break; + case BTF_KIND_TYPEDEF: + h = btf_hash_typedef(t); + break; case BTF_KIND_INT: case BTF_KIND_DECL_TAG: h = btf_hash_int_decl_tag(t); From a59eb502641a6f792ac7d261a93bc718554d548a Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Tue, 17 Mar 2026 20:58:07 -0400 Subject: [PATCH 0753/2758] drm/amd/pm: remove trailing semicolon from AMDGPU_PM_POLICY_ATTR macro [ Upstream commit 0d892afb52d2b940d891b6b52dc9f04debef2d81 ] macros should not include a trailing semicolon as per kernel coding style (checkpatch.pl warning). move the semicolon from the macro definition to the invocation sites instead. checkpatch.pl logs: WARNING: macros should not use a trailing semicolon +#define AMDGPU_PM_POLICY_ATTR(_name, _id) \ + static struct amdgpu_pm_policy_attr pm_policy_attr_##_name = { \ + .dev_attr = __ATTR(_name, 0644, amdgpu_get_pm_policy_attr, \ + amdgpu_set_pm_policy_attr), \ + .id = PP_PM_POLICY_##_id, \ + }; Fixes: 4d154b1ca580 ("drm/amd/pm: Add support for DPM policies") Signed-off-by: Yang Wang Acked-by: Alex Deucher Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/pm/amdgpu_pm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c index 736304e73ca4d..024bfdb7c1571 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c @@ -2505,12 +2505,12 @@ static ssize_t amdgpu_set_pm_policy_attr(struct device *dev, .dev_attr = __ATTR(_name, 0644, amdgpu_get_pm_policy_attr, \ amdgpu_set_pm_policy_attr), \ .id = PP_PM_POLICY_##_id, \ - }; + } #define AMDGPU_PM_POLICY_ATTR_VAR(_name) pm_policy_attr_##_name.dev_attr.attr -AMDGPU_PM_POLICY_ATTR(soc_pstate, SOC_PSTATE) -AMDGPU_PM_POLICY_ATTR(xgmi_plpd, XGMI_PLPD) +AMDGPU_PM_POLICY_ATTR(soc_pstate, SOC_PSTATE); +AMDGPU_PM_POLICY_ATTR(xgmi_plpd, XGMI_PLPD); static struct attribute *pm_policy_attrs[] = { &AMDGPU_PM_POLICY_ATTR_VAR(soc_pstate), From af6a34c41683067a314d2b58b39edecb2e5e4ac6 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Fri, 17 Apr 2026 00:27:54 +0800 Subject: [PATCH 0754/2758] spi: atcspi200: fix use-after-free when driver unbind [ Upstream commit 565bdf45125a05aa8f622f58f598283f46ba43f4 ] DMA resource is initialized after SPI controller registration. So when driver unbind, this can trigger a use-after-free when DMA is torn down while the controller is still alive and triggers DMA transfers. Fixes: 34e3815ea459 ("spi: atcspi200: Add ATCSPI200 SPI controller driver") Signed-off-by: Felix Gu Link: https://patch.msgid.link/20260417-atcspi-v1-1-854831667d63@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-atcspi200.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi-atcspi200.c b/drivers/spi/spi-atcspi200.c index 3832d9db3cbf8..c5cf1aa2d6743 100644 --- a/drivers/spi/spi-atcspi200.c +++ b/drivers/spi/spi-atcspi200.c @@ -575,12 +575,6 @@ static int atcspi_probe(struct platform_device *pdev) if (ret) goto free_controller; - ret = devm_spi_register_controller(&pdev->dev, host); - if (ret) { - dev_err_probe(spi->dev, ret, - "Failed to register SPI controller\n"); - goto free_controller; - } spi->use_dma = false; if (ATCSPI_DMA_SUPPORT) { ret = atcspi_configure_dma(spi); @@ -591,6 +585,13 @@ static int atcspi_probe(struct platform_device *pdev) spi->use_dma = true; } + ret = devm_spi_register_controller(&pdev->dev, host); + if (ret) { + dev_err_probe(spi->dev, ret, + "Failed to register SPI controller\n"); + goto free_controller; + } + return 0; free_controller: From e93b36361458258b87e7c458d5628cff53bf39ca Mon Sep 17 00:00:00 2001 From: Gregory Bell Date: Fri, 17 Apr 2026 11:41:21 -0400 Subject: [PATCH 0755/2758] selftests/bpf: Use local type for flow_offload_tuple_rhash in xdp_flowtable [ Upstream commit ac985e7bf840e34a8dafe0808cc571fd85896c30 ] Define flow_offload_tuple_rhash___local and use it in place of the forward-declared kernel type for the bpf_xdp_flow_lookup kfunc return type and tuplehash variable. This is consistent with how bpf_flowtable_opts___local is already handled in the same file and avoids relying on a forward declaration of the struct. Fixes: eeb23b54e447 ("selftests/bpf: fix compilation failure when CONFIG_NF_FLOW_TABLE=m") Signed-off-by: Gregory Bell Link: https://lore.kernel.org/r/20260417154122.2558890-2-grbell@redhat.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- tools/testing/selftests/bpf/progs/xdp_flowtable.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/xdp_flowtable.c b/tools/testing/selftests/bpf/progs/xdp_flowtable.c index 7fdc7b23ee749..e67daa02749d5 100644 --- a/tools/testing/selftests/bpf/progs/xdp_flowtable.c +++ b/tools/testing/selftests/bpf/progs/xdp_flowtable.c @@ -15,7 +15,10 @@ struct bpf_flowtable_opts___local { s32 error; }; -struct flow_offload_tuple_rhash * +struct flow_offload_tuple_rhash___local { +}; + +struct flow_offload_tuple_rhash___local * bpf_xdp_flow_lookup(struct xdp_md *, struct bpf_fib_lookup *, struct bpf_flowtable_opts___local *, u32) __ksym; @@ -67,7 +70,7 @@ int xdp_flowtable_do_lookup(struct xdp_md *ctx) { void *data_end = (void *)(long)ctx->data_end; struct bpf_flowtable_opts___local opts = {}; - struct flow_offload_tuple_rhash *tuplehash; + struct flow_offload_tuple_rhash___local *tuplehash; struct bpf_fib_lookup tuple = { .ifindex = ctx->ingress_ifindex, }; From 313849b13e054b60abc43095f48d2cf1cdf1df2d Mon Sep 17 00:00:00 2001 From: Gregory Bell Date: Fri, 17 Apr 2026 11:41:22 -0400 Subject: [PATCH 0756/2758] selftests/bpf: Use local type for bpf_fou_encap in test_tunnel_kern [ Upstream commit afb0450be061907a0f5d36bd8b010ca30eda3d3b ] Replace the forward-declared struct bpf_fou_encap with the existing bpf_fou_encap___local type in the bpf_skb_set_fou_encap and bpf_skb_get_fou_encap declarations. This removes the need for the forward declaration and the explicit casts at each call. Fixes: d17f9b370df6 ("selftests/bpf: Fix compilation failure when CONFIG_NET_FOU!=y") Signed-off-by: Gregory Bell Link: https://lore.kernel.org/r/20260417154122.2558890-3-grbell@redhat.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- .../testing/selftests/bpf/progs/test_tunnel_kern.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/test_tunnel_kern.c b/tools/testing/selftests/bpf/progs/test_tunnel_kern.c index 32127f1cd6872..30f1de458669d 100644 --- a/tools/testing/selftests/bpf/progs/test_tunnel_kern.c +++ b/tools/testing/selftests/bpf/progs/test_tunnel_kern.c @@ -6,6 +6,7 @@ * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. */ +#define BPF_NO_KFUNC_PROTOTYPES #include "vmlinux.h" #include #include @@ -36,12 +37,10 @@ enum bpf_fou_encap_type___local { FOU_BPF_ENCAP_GUE___local, }; -struct bpf_fou_encap; - int bpf_skb_set_fou_encap(struct __sk_buff *skb_ctx, - struct bpf_fou_encap *encap, int type) __ksym; + struct bpf_fou_encap___local *encap, int type) __ksym; int bpf_skb_get_fou_encap(struct __sk_buff *skb_ctx, - struct bpf_fou_encap *encap) __ksym; + struct bpf_fou_encap___local *encap) __ksym; struct xfrm_state * bpf_xdp_get_xfrm_state(struct xdp_md *ctx, struct bpf_xfrm_state_opts *opts, u32 opts__sz) __ksym; @@ -781,7 +780,7 @@ int ipip_gue_set_tunnel(struct __sk_buff *skb) encap.sport = 0; encap.dport = bpf_htons(5555); - ret = bpf_skb_set_fou_encap(skb, (struct bpf_fou_encap *)&encap, + ret = bpf_skb_set_fou_encap(skb, &encap, bpf_core_enum_value(enum bpf_fou_encap_type___local, FOU_BPF_ENCAP_GUE___local)); if (ret < 0) { @@ -820,7 +819,7 @@ int ipip_fou_set_tunnel(struct __sk_buff *skb) encap.sport = 0; encap.dport = bpf_htons(5555); - ret = bpf_skb_set_fou_encap(skb, (struct bpf_fou_encap *)&encap, + ret = bpf_skb_set_fou_encap(skb, &encap, FOU_BPF_ENCAP_FOU___local); if (ret < 0) { log_err(ret); @@ -843,7 +842,7 @@ int ipip_encap_get_tunnel(struct __sk_buff *skb) return TC_ACT_SHOT; } - ret = bpf_skb_get_fou_encap(skb, (struct bpf_fou_encap *)&encap); + ret = bpf_skb_get_fou_encap(skb, &encap); if (ret < 0) { log_err(ret); return TC_ACT_SHOT; From 342db93e484471216a960b24dc66723fd7003614 Mon Sep 17 00:00:00 2001 From: Baolin Liu Date: Fri, 24 Apr 2026 17:06:54 +0800 Subject: [PATCH 0757/2758] Documentation: proc: fix section numbering in table of contents [ Upstream commit 97a7bd8c2c58a6d820df563d1d0f68aafec45477 ] Commit e24ccaaf7ec4 ("block: remove last remaining traces of IDE documentation") removed the IDE section but left its table of contents entry behind. Fix the stale entry and renumber the following sections. Fixes: e24ccaaf7ec4 ("block: remove last remaining traces of IDE documentation") Signed-off-by: Baolin Liu Acked-by: Randy Dunlap Signed-off-by: Jonathan Corbet Message-ID: <20260424090654.19229-1-liubaolin12138@163.com> Signed-off-by: Sasha Levin --- Documentation/filesystems/proc.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Documentation/filesystems/proc.rst b/Documentation/filesystems/proc.rst index db6167befb7b2..3c6e4f52f3e4e 100644 --- a/Documentation/filesystems/proc.rst +++ b/Documentation/filesystems/proc.rst @@ -23,13 +23,13 @@ fixes/update part 1.1 Stefani Seibold June 9 2009 1 Collecting System Information 1.1 Process-Specific Subdirectories 1.2 Kernel data - 1.3 IDE devices in /proc/ide - 1.4 Networking info in /proc/net - 1.5 SCSI info - 1.6 Parallel port info in /proc/parport - 1.7 TTY info in /proc/tty - 1.8 Miscellaneous kernel statistics in /proc/stat - 1.9 Ext4 file system parameters + 1.3 Networking info in /proc/net + 1.4 SCSI info + 1.5 Parallel port info in /proc/parport + 1.6 TTY info in /proc/tty + 1.7 Miscellaneous kernel statistics in /proc/stat + 1.8 Ext4 file system parameters + 1.9 /proc/consoles - Shows registered system consoles 2 Modifying System Parameters From d4275810ed6a95454fc74fccf65ee50c6dd7ecfa Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Wed, 4 Mar 2026 23:00:42 +0200 Subject: [PATCH 0758/2758] arm64: dts: rockchip: Fix vdec register blocks order on RK3576 [ Upstream commit 63fd2f6aa402a105fa22ede6f3c35dafd64827a5 ] When building device trees for the RK3576 based boards, DTC shows the following complaint: rk3576.dtsi:1282.30-1304.5: Warning (simple_bus_reg): /soc/video-codec@27b00000: simple-bus unit address format error, expected "27b00100" Since the video decoder support for the aforementioned SoC in mainline driver and devicetrees hasn't been released yet (just landed in v7.0-rc1), fix the issue by providing the register blocks using the 'link,function,cache' listing, which follows the address-based order as shown in the vendor's datasheet and, implicitly, ensures the unit address points to the primary register range. Fixes: da0de806d8b4 ("arm64: dts: rockchip: Add the vdpu383 Video Decoder on rk3576") Signed-off-by: Cristian Ciocaltea Link: https://patch.msgid.link/20260304-vdec-reg-order-rk3576-v5-3-7006fad42c3a@collabora.com Signed-off-by: Heiko Stuebner Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/rockchip/rk3576.dtsi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3576.dtsi b/arch/arm64/boot/dts/rockchip/rk3576.dtsi index 28175d8200d57..e12a2a0cfb891 100644 --- a/arch/arm64/boot/dts/rockchip/rk3576.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3576.dtsi @@ -1281,10 +1281,10 @@ vdec: video-codec@27b00000 { compatible = "rockchip,rk3576-vdec"; - reg = <0x0 0x27b00100 0x0 0x500>, - <0x0 0x27b00000 0x0 0x100>, + reg = <0x0 0x27b00000 0x0 0x100>, + <0x0 0x27b00100 0x0 0x500>, <0x0 0x27b00600 0x0 0x100>; - reg-names = "function", "link", "cache"; + reg-names = "link", "function", "cache"; interrupts = ; clocks = <&cru ACLK_RKVDEC_ROOT>, <&cru HCLK_RKVDEC>, <&cru ACLK_RKVDEC_ROOT_BAK>, <&cru CLK_RKVDEC_CORE>, From d07a6ee87fd995f8f4d54d182ff05e50e2d4cb57 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Wed, 4 Mar 2026 23:00:43 +0200 Subject: [PATCH 0759/2758] arm64: dts: rockchip: Update vdec register blocks order on RK3588 [ Upstream commit b481c11cd20a114c4df35f3b1ecd28b05e622067 ] With the introduction of the RK3588 SoC, three register blocks have been provided for the video decoder unit instead of just one, which are further referenced in the vendor's datasheet by 'link table', 'function' and 'cache'. The former is present at the top of the listing, starting at video decoder unit base address, but the binding got this wrong initially, i.e. the 'function' block got listed before the 'link' one. Since the video decoder support for the aforementioned SoC in mainline driver and devicetrees hasn't been released yet (just landed in v7.0-rc1), address the problem by providing the register blocks for vdec0 & vdec1 nodes using the 'link,function,cache' listing, which ensures the unit address points to the primary register range. This aligns with a similar fix for RK3576, where DTC also complained about the bus address format. Fixes: f61731bd6062 ("arm64: dts: rockchip: Add the vdpu381 Video Decoders on RK3588") Signed-off-by: Cristian Ciocaltea Link: https://patch.msgid.link/20260304-vdec-reg-order-rk3576-v5-4-7006fad42c3a@collabora.com Signed-off-by: Heiko Stuebner Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/rockchip/rk3588-base.dtsi | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi index 4fb8888c281c8..d4dc057e31f0e 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi @@ -1355,10 +1355,10 @@ vdec0: video-codec@fdc38000 { compatible = "rockchip,rk3588-vdec"; - reg = <0x0 0xfdc38100 0x0 0x500>, - <0x0 0xfdc38000 0x0 0x100>, + reg = <0x0 0xfdc38000 0x0 0x100>, + <0x0 0xfdc38100 0x0 0x500>, <0x0 0xfdc38600 0x0 0x100>; - reg-names = "function", "link", "cache"; + reg-names = "link", "function", "cache"; interrupts = ; clocks = <&cru ACLK_RKVDEC0>, <&cru HCLK_RKVDEC0>, <&cru CLK_RKVDEC0_CA>, <&cru CLK_RKVDEC0_CORE>, <&cru CLK_RKVDEC0_HEVC_CA>; @@ -1387,10 +1387,10 @@ vdec1: video-codec@fdc40000 { compatible = "rockchip,rk3588-vdec"; - reg = <0x0 0xfdc40100 0x0 0x500>, - <0x0 0xfdc40000 0x0 0x100>, + reg = <0x0 0xfdc40000 0x0 0x100>, + <0x0 0xfdc40100 0x0 0x500>, <0x0 0xfdc40600 0x0 0x100>; - reg-names = "function", "link", "cache"; + reg-names = "link", "function", "cache"; interrupts = ; clocks = <&cru ACLK_RKVDEC1>, <&cru HCLK_RKVDEC1>, <&cru CLK_RKVDEC1_CA>, <&cru CLK_RKVDEC1_CORE>, <&cru CLK_RKVDEC1_HEVC_CA>; From f63a5ae90d614917591cb4f09e85a0871e2b5b1f Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Tue, 21 Apr 2026 11:45:05 +0200 Subject: [PATCH 0760/2758] arm64: dts: rockchip: fix Ethernet PHY not found on PX30 Cobra [ Upstream commit 6598ed3586a4b1cc79423666e66b9861631a6c7e ] When not passing the PHY ID with an ethernet-phy-idX.Y compatible property, the MDIO bus will attempt to auto-detect the PHY by reading its registers and then probing the appropriate driver. For this to work, the PHY needs to be in a working state. Unfortunately, the net subsystem doesn't control the PHY reset GPIO when attempting to auto-detect the PHY. This means the PHY needs to be in a working state when entering the Linux kernel. This historically has been the case for this device, but only because the bootloader was taking care of initializing the Ethernet controller even when not using it. We're attempting to support the removal of the network stack in the bootloader, which means the Linux kernel will be entered with the PHY still in reset and now Ethernet doesn't work anymore. The devices in the field only ever had a TI DP83825, so let's simply bypass the auto-detection mechanism entirely by passing the appropriate PHY IDs via the compatible. Fixes: bb510ddc9d3e ("arm64: dts: rockchip: add px30-cobra base dtsi and board variants") Signed-off-by: Quentin Schulz Link: https://patch.msgid.link/20260421-px30-eth-phy-v2-1-68c375b120fd@cherry.de Signed-off-by: Heiko Stuebner Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/rockchip/px30-cobra.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/rockchip/px30-cobra.dtsi b/arch/arm64/boot/dts/rockchip/px30-cobra.dtsi index b7e669d8ba4d1..add917af5de78 100644 --- a/arch/arm64/boot/dts/rockchip/px30-cobra.dtsi +++ b/arch/arm64/boot/dts/rockchip/px30-cobra.dtsi @@ -397,7 +397,7 @@ &mdio { dp83825: ethernet-phy@0 { - compatible = "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id2000.a140"; reg = <0x0>; pinctrl-names = "default"; pinctrl-0 = <&phy_rst>; From aaea3af1fbf1bebd056d81a58cbcabe98154f9dc Mon Sep 17 00:00:00 2001 From: Diederik de Haas Date: Wed, 1 Apr 2026 15:11:38 +0200 Subject: [PATCH 0761/2758] arm64: dts: rockchip: Fix gmac0 reset pin for NanoPi R5S [ Upstream commit c83c4a09d4c01c91d6c52d6d4d77a06892a3e83b ] According to the NanoPi R5S 2204 schematic on page 6, GPIO0_C4 is for GMAC0_INT/PMEB_GPIO0_C4, while GPIO0_C5 is for GMAC0_RSTn_GPIO0_C5. While the 'reset-gpios' property was set correctly, the corresponding pinctrl didn't match that. Next to fixing the pinctrl definition, also change the node name and phandle to match what is used in the schematic. Fixes: c6629b9a6738 ("arm64: dts: rockchip: Add FriendlyElec Nanopi R5S") Signed-off-by: Diederik de Haas Link: https://patch.msgid.link/20260401131551.734456-2-diederik@cknow-tech.com Signed-off-by: Heiko Stuebner Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/rockchip/rk3568-nanopi-r5s.dts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3568-nanopi-r5s.dts b/arch/arm64/boot/dts/rockchip/rk3568-nanopi-r5s.dts index 718d1a2da8e56..90ce6f0e1dcff 100644 --- a/arch/arm64/boot/dts/rockchip/rk3568-nanopi-r5s.dts +++ b/arch/arm64/boot/dts/rockchip/rk3568-nanopi-r5s.dts @@ -98,7 +98,7 @@ rgmii_phy0: ethernet-phy@1 { compatible = "ethernet-phy-ieee802.3-c22"; reg = <1>; - pinctrl-0 = <ð_phy0_reset_pin>; + pinctrl-0 = <&gmac0_rstn_gpio0_c5_pin>; pinctrl-names = "default"; }; }; @@ -132,8 +132,8 @@ &pinctrl { gmac0 { - eth_phy0_reset_pin: eth-phy0-reset-pin { - rockchip,pins = <0 RK_PC4 RK_FUNC_GPIO &pcfg_pull_up>; + gmac0_rstn_gpio0_c5_pin: gmac0-rstn-gpio0-c5-pin { + rockchip,pins = <0 RK_PC5 RK_FUNC_GPIO &pcfg_pull_up>; }; }; From 342828cf47812e5d95afa1c36c06523155ccf624 Mon Sep 17 00:00:00 2001 From: Kamlesh Kumar Date: Fri, 24 Apr 2026 17:09:46 +0530 Subject: [PATCH 0762/2758] ima: Fix sigv3 signature handling for EVM_IMA_XATTR_DIGSIG [ Upstream commit 398ee113f15c1e8e62535e54f22fb4db340c7835 ] ima_get_hash_algo() only recognizes version 2 signatures when the xattr type is EVM_IMA_XATTR_DIGSIG. Since sigv3 signatures also use EVM_IMA_XATTR_DIGSIG as the xattr type, version 3 must be accepted as well to correctly determine the hash algorithm. Additionally, ima_validate_rule() does not include IMA_SIGV3_REQUIRED in the allowed flags bitmask for MODULE_CHECK, KEXEC_KERNEL_CHECK, and KEXEC_INITRAMFS_CHECK hook functions. As a result, policy rules with "appraise_type=sigv3" are rejected for these functions. Add version 3 to the accepted versions in ima_get_hash_algo() for EVM_IMA_XATTR_DIGSIG, and add IMA_SIGV3_REQUIRED to the allowed flags for MODULE_CHECK, KEXEC_KERNEL_CHECK, and KEXEC_INITRAMFS_CHECK in ima_validate_rule(). Signed-off-by: Kamlesh Kumar Tested-by: Stefan Berger Fixes: de4c44a7f559 ("ima: add support to require IMA sigv3 signatures") Signed-off-by: Mimi Zohar Signed-off-by: Sasha Levin --- security/integrity/ima/ima_appraise.c | 5 +++-- security/integrity/ima/ima_policy.c | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index de963b9f36344..2dd231567710a 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -195,8 +195,9 @@ enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_value, return sig->hash_algo; case EVM_IMA_XATTR_DIGSIG: sig = (typeof(sig))xattr_value; - if (sig->version != 2 || xattr_len <= sizeof(*sig) - || sig->hash_algo >= HASH_ALGO__LAST) + if ((sig->version != 2 && sig->version != 3) || + xattr_len <= sizeof(*sig) || + sig->hash_algo >= HASH_ALGO__LAST) return ima_hash_algo; return sig->hash_algo; case IMA_XATTR_DIGEST_NG: diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index f7f940a769223..b1c010e8eb138 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -1313,7 +1313,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry) IMA_GID | IMA_EGID | IMA_FGROUP | IMA_DIGSIG_REQUIRED | IMA_PERMIT_DIRECTIO | IMA_MODSIG_ALLOWED | - IMA_CHECK_BLACKLIST | IMA_VALIDATE_ALGOS)) + IMA_CHECK_BLACKLIST | IMA_VALIDATE_ALGOS | + IMA_SIGV3_REQUIRED)) return false; break; From dc0f024484b0875266f8ffdbf346db948627a80f Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 25 Feb 2026 13:23:21 +0100 Subject: [PATCH 0763/2758] dt-bindings: net: bluetooth: qualcomm: Fix WCN6855 regulator names [ Upstream commit 63b35a29d7ad2cafb0076f8b002b30fb74df053a ] Commit 5f4f954bba12 ("dt-bindings: bluetooth: bring the HW description closer to reality for wcn6855") changed the vddrfa1p7-supply to 1p8 for whatever reason. The schematics footprint for this chip definitely says 7 on the input leg and the driver still expects 1p7. Bring it back. Fixes: 5f4f954bba12 ("dt-bindings: bluetooth: bring the HW description closer to reality for wcn6855") Reviewed-by: Krzysztof Kozlowski Reviewed-by: Abel Vesa Reviewed-by: Bartosz Golaszewski Acked-by: Dmitry Baryshkov Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260225-topic-wcn6855_pmu_dtbdings-v3-1-576ec5c4e631@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- .../devicetree/bindings/net/bluetooth/qcom,wcn6855-bt.yaml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6855-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6855-bt.yaml index 45630067d3c8e..0beda26ae8bb4 100644 --- a/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6855-bt.yaml +++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6855-bt.yaml @@ -50,9 +50,6 @@ properties: description: VDD_RFA_1P7 supply regulator handle deprecated: true - vddrfa1p8-supply: - description: VDD_RFA_1P8 supply regulator handle - vddrfacmn-supply: description: VDD_RFA_CMN supply regulator handle @@ -68,7 +65,7 @@ required: - vddbtcmx-supply - vddrfa0p8-supply - vddrfa1p2-supply - - vddrfa1p8-supply + - vddrfa1p7-supply - vddrfacmn-supply - vddwlcx-supply - vddwlmx-supply @@ -91,7 +88,7 @@ examples: vddbtcmx-supply = <&vreg_pmu_btcmx_0p8>; vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; - vddrfa1p8-supply = <&vreg_pmu_rfa_1p7>; + vddrfa1p7-supply = <&vreg_pmu_rfa_1p7>; vddrfacmn-supply = <&vreg_pmu_rfa_cmn_0p8>; vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; vddwlmx-supply = <&vreg_pmu_wlmx_0p8>; From f0dbf9e5d82e87b4fd2500ed934504778d2db4fa Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 23 Apr 2026 07:54:17 -0700 Subject: [PATCH 0764/2758] x86/bug: Add printf() validation to HAVE_ARCH_BUG_FORMAT_ARGS WARNs [ Upstream commit 40c4b47f41b95dff743c841536cb64014e65ef0c ] Add explicit printf() validation for x86-64's newfangled WARN implementation, as most (all?) compilers fail to detect basic formatting issues without the annotation. E.g. even goofs like printing a u64 as a string aren't detected: WARN_ONCE(1, "Bad message, %s", vcpu->arch.last_guest_tsc); 32-bit x86 doesn't support HAVE_ARCH_BUG_FORMAT_ARGS and uses generic implementations that provide printf() validation. This means there's now a big blind spot is code that is strictly x86-64. Inconveniently, new features are also frequently x86-64-only. Fix the blind 64-bit blind spot. [ dhansen: changelog tweaks to flesh out the 64-bit-only details ] Fixes: 5b472b6e5bd9 ("x86_64/bug: Implement __WARN_printf()") Fixes: 11bb4944f014 ("x86/bug: Implement WARN_ONCE()") Signed-off-by: Sean Christopherson Signed-off-by: Dave Hansen Acked-by: Dave Hansen Link: https://lore.kernel.org/all/adc1IrD8uqWdaOKv@yzhao56-desk.sh.intel.com Link: https://patch.msgid.link/20260423145419.459988-2-seanjc@google.com Signed-off-by: Sasha Levin --- arch/x86/include/asm/bug.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/x86/include/asm/bug.h b/arch/x86/include/asm/bug.h index 80c1696d8d597..bf3c802654d18 100644 --- a/arch/x86/include/asm/bug.h +++ b/arch/x86/include/asm/bug.h @@ -153,6 +153,7 @@ struct arch_va_list { struct sysv_va_list args; }; extern void *__warn_args(struct arch_va_list *args, struct pt_regs *regs); +static __always_inline __printf(1, 2) void __WARN_validate_printf(const char *fmt, ...) { } #endif /* __ASSEMBLER__ */ #define __WARN_bug_entry(flags, format) ({ \ @@ -172,6 +173,7 @@ extern void *__warn_args(struct arch_va_list *args, struct pt_regs *regs); #define __WARN_print_arg(flags, format, arg...) \ do { \ int __flags = (flags) | BUGFLAG_WARNING | BUGFLAG_ARGS ; \ + __WARN_validate_printf(format, ## arg); \ static_call_mod(WARN_trap)(__WARN_bug_entry(__flags, format), ## arg); \ asm (""); /* inhibit tail-call optimization */ \ } while (0) From 2a51c2954ac6a0678c6ae03b2b69c40b2b3cf937 Mon Sep 17 00:00:00 2001 From: Alexander Koskovich Date: Mon, 23 Mar 2026 13:54:42 +0000 Subject: [PATCH 0765/2758] arm64: dts: qcom: milos: Reduce rmtfs_mem size to 2.5MiB [ Upstream commit 1f820571ca7c64d3fd5bd9bf653b571cb3350703 ] The rmtfs_mem region is currently sized at 6MiB but the default for milos downstream is 2.5MiB. This causes remoteproc crashes on devices that expect the smaller size: modem_ac.c:281:Access Control Error: Could not protect the region specified:Start:e1f00000 End:e2180000, PID:1 Reduce the default to 2.5MiB to match the QCOM downstream config, and override the size for FP6. Fixes: d9d59d105f98 ("arm64: dts: qcom: Add initial Milos dtsi") Reviewed-by: Luca Weiss Reviewed-by: Konrad Dybcio Signed-off-by: Alexander Koskovich Link: https://lore.kernel.org/r/20260323-asteroids-v2-1-1a35fa9e178a@pm.me Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/milos-fairphone-fp6.dts | 5 +++++ arch/arm64/boot/dts/qcom/milos.dtsi | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/milos-fairphone-fp6.dts b/arch/arm64/boot/dts/qcom/milos-fairphone-fp6.dts index c1899db46e714..5dc7c950e60d6 100644 --- a/arch/arm64/boot/dts/qcom/milos-fairphone-fp6.dts +++ b/arch/arm64/boot/dts/qcom/milos-fairphone-fp6.dts @@ -786,6 +786,11 @@ status = "okay"; }; +&rmtfs_mem { + /* Increase the size from 2.5 MiB to 6 MiB */ + reg = <0x0 0xe1f00000 0x0 0x600000>; +}; + &sdhc_2 { cd-gpios = <&tlmm 65 GPIO_ACTIVE_HIGH>; diff --git a/arch/arm64/boot/dts/qcom/milos.dtsi b/arch/arm64/boot/dts/qcom/milos.dtsi index a6e463f3885dc..c4e373872e8c6 100644 --- a/arch/arm64/boot/dts/qcom/milos.dtsi +++ b/arch/arm64/boot/dts/qcom/milos.dtsi @@ -642,7 +642,7 @@ rmtfs_mem: rmtfs@e1f00000 { compatible = "qcom,rmtfs-mem"; - reg = <0x0 0xe1f00000 0x0 0x600000>; + reg = <0x0 0xe1f00000 0x0 0x280000>; no-map; qcom,client-id = <1>; From be1d8901ac74cab7bfc3e39e638b36850cc412a6 Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Thu, 2 Apr 2026 00:39:36 +0200 Subject: [PATCH 0766/2758] arm64: dts: qcom: sdm845-oneplus: Drop address from framebuffer node [ Upstream commit b379bb1470d864659ae9522b72f241a15255dce6 ] This node has no 'reg' property, so it shouldn't have a unit address (after '@') either Fixes: b0d5c96e860c ("arm64: dts: qcom: sdm845-oneplus: Add framebuffer") Reviewed-by: Dmitry Baryshkov Signed-off-by: David Heidelberg Link: https://lore.kernel.org/r/20260402-beryllium-fb-v4-2-46170004da28@ixit.cz Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sdm845-oneplus-common.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sdm845-oneplus-common.dtsi b/arch/arm64/boot/dts/qcom/sdm845-oneplus-common.dtsi index 6b7378cf4d493..b0b9baf01ec29 100644 --- a/arch/arm64/boot/dts/qcom/sdm845-oneplus-common.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm845-oneplus-common.dtsi @@ -72,7 +72,7 @@ stdout-path = "serial0:115200n8"; - framebuffer: framebuffer@9d400000 { + framebuffer: framebuffer { compatible = "simple-framebuffer"; memory-region = <&cont_splash_mem>; From 460e2fb9f8da6754b40948a9bfed4ebf30d78b4e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 5 Apr 2026 15:39:28 +0200 Subject: [PATCH 0767/2758] arm64: dts: qcom: glymur: Fix USB simple_bus_reg warning [ Upstream commit cd66b6d256f94e40922941e14d7f9390d35d072b ] Correct the unit address of USB node in Qualcomm Glymur SoC DTSI to fix W=1 DTC warning: glymur.dtsi:4027.23-4093.5: Warning (simple_bus_reg): /soc@0/usb@a2f8800: simple-bus unit address format error, expected "a200000" Fixes: 4eee57dd4df9 ("arm64: dts: qcom: glymur: Add USB related nodes") Reviewed-by: Dmitry Baryshkov Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260405-dts-qcom-w-1-fixes-v2-1-1f2c7b74a93f@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/glymur.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/glymur.dtsi b/arch/arm64/boot/dts/qcom/glymur.dtsi index 82436984485d4..ab2a6b577bc9d 100644 --- a/arch/arm64/boot/dts/qcom/glymur.dtsi +++ b/arch/arm64/boot/dts/qcom/glymur.dtsi @@ -4016,7 +4016,7 @@ }; }; - usb_hs: usb@a2f8800 { + usb_hs: usb@a200000 { compatible = "qcom,glymur-dwc3", "qcom,snps-dwc3"; reg = <0x0 0x0a200000 0x0 0xfc100>; From 92b4e73fd38136c1cfbf3920d0456015ebf65136 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 5 Apr 2026 15:39:29 +0200 Subject: [PATCH 0768/2758] arm64: dts: qcom: glymur: Fix cache and SRAM simple_bus_reg warnings [ Upstream commit 46eccc1034c3740b07b58c125190bbb99247c9de ] Correct the unit address of cache controller and SRAM nodes in Qualcomm Glymur SoC DTSI to fix W=1 DTC warnings: glymur.dtsi:5876.36-5908.5: Warning (simple_bus_reg): /soc@0/system-cache-controller@20400000: simple-bus unit address format error, expected "21800000" glymur.dtsi:5917.23-5934.5: Warning (simple_bus_reg): /soc@0/sram@81e08000: simple-bus unit address format error, expected "81e08600" Fixes: 41b6e8db400c ("arm64: dts: qcom: Introduce Glymur base dtsi") Reviewed-by: Dmitry Baryshkov Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260405-dts-qcom-w-1-fixes-v2-2-1f2c7b74a93f@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/glymur.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/glymur.dtsi b/arch/arm64/boot/dts/qcom/glymur.dtsi index ab2a6b577bc9d..7283d45ca259a 100644 --- a/arch/arm64/boot/dts/qcom/glymur.dtsi +++ b/arch/arm64/boot/dts/qcom/glymur.dtsi @@ -5865,7 +5865,7 @@ #interconnect-cells = <2>; }; - system-cache-controller@20400000 { + system-cache-controller@21800000 { compatible = "qcom,glymur-llcc"; reg = <0x0 0x21800000 0x0 0x100000>, <0x0 0x21a00000 0x0 0x100000>, @@ -5906,7 +5906,7 @@ #interconnect-cells = <2>; }; - imem: sram@81e08000 { + imem: sram@81e08600 { compatible = "mmio-sram"; reg = <0x0 0x81e08600 0x0 0x300>; From 4b47923d5310bb43ccde57dff2507340210c1a15 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 5 Apr 2026 15:39:30 +0200 Subject: [PATCH 0769/2758] arm64: dts: qcom: ipq5424: Fix USB simple_bus_reg warnings [ Upstream commit 864fde494aa1dd26c68254661f2ce973e9f03832 ] Correct the unit address of USB nodes in Qualcomm IPQ5424 SoC DTSI to fix W=1 DTC warnings: ipq5424.dtsi:642.22-693.5: Warning (simple_bus_reg): /soc@0/usb2@1e00000: simple-bus unit address format error, expected "1ef8800" ipq5424.dtsi:733.22-786.5: Warning (simple_bus_reg): /soc@0/usb3@8a00000: simple-bus unit address format error, expected "8af8800" Fixes: 113d52bdc820 ("arm64: dts: qcom: ipq5424: Add USB controller and phy nodes") Reviewed-by: Dmitry Baryshkov Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260405-dts-qcom-w-1-fixes-v2-3-1f2c7b74a93f@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/ipq5424.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/ipq5424.dtsi b/arch/arm64/boot/dts/qcom/ipq5424.dtsi index f20cda4290949..876bf6a8b8ff0 100644 --- a/arch/arm64/boot/dts/qcom/ipq5424.dtsi +++ b/arch/arm64/boot/dts/qcom/ipq5424.dtsi @@ -639,7 +639,7 @@ status = "disabled"; }; - usb2: usb2@1e00000 { + usb2: usb2@1ef8800 { compatible = "qcom,ipq5424-dwc3", "qcom,dwc3"; reg = <0 0x01ef8800 0 0x400>; #address-cells = <2>; @@ -730,7 +730,7 @@ status = "disabled"; }; - usb3: usb3@8a00000 { + usb3: usb3@8af8800 { compatible = "qcom,ipq5424-dwc3", "qcom,dwc3"; reg = <0 0x08af8800 0 0x400>; From 1d066a4d678d2018b34790826329db8f8201d666 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 5 Apr 2026 15:39:31 +0200 Subject: [PATCH 0770/2758] arm64: dts: qcom: sc8180x: Fix phy simple_bus_reg warning [ Upstream commit f319a5fc998e29699d325af0e461721b3768eeec ] Correct the unit address of phy node in Qualcomm SC8180x SoC DTSI to fix W=1 DTC warning: sc8180x.dtsi:2650.31-2695.5: Warning (simple_bus_reg): /soc@0/phy@88ee000: simple-bus unit address format error, expected "88ed000" Fixes: 35e3a9c1afce ("arm64: dts: qcom: sc8180x: switch USB+DP QMP PHYs to new bindings") Reviewed-by: Dmitry Baryshkov Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260405-dts-qcom-w-1-fixes-v2-4-1f2c7b74a93f@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sc8180x.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sc8180x.dtsi b/arch/arm64/boot/dts/qcom/sc8180x.dtsi index f45deb188c6c0..e87e82fa73e9c 100644 --- a/arch/arm64/boot/dts/qcom/sc8180x.dtsi +++ b/arch/arm64/boot/dts/qcom/sc8180x.dtsi @@ -2647,7 +2647,7 @@ status = "disabled"; }; - usb_sec_qmpphy: phy@88ee000 { + usb_sec_qmpphy: phy@88ed000 { compatible = "qcom,sc8180x-qmp-usb3-dp-phy"; reg = <0 0x088ed000 0 0x3000>; From 0ca70d4382981bb7079352647b09978968b16b33 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 5 Apr 2026 15:39:32 +0200 Subject: [PATCH 0771/2758] arm64: dts: qcom: sdm845-mezzanine: Fix camss ports unit_address_vs_reg warning [ Upstream commit f20a82aacd7f381084391a8d1f0f58defa91974c ] Add necessary properties for ports node in SDM845 DB845c Navigation mezzanine overlay to fix W=1 DTC warning: sdm845-db845c-navigation-mezzanine.dtso:19.10-24.5: Warning (unit_address_vs_reg): /fragment@0/__overlay__/ports/port@0: node has a unit name, but no reg or ranges property Fixes: 30df676a31b7 ("arm64: dts: qcom: sdm845-db845c-navigation-mezzanine: Convert mezzanine riser to dtso") Reviewed-by: Dmitry Baryshkov Reviewed-by: David Heidelberg Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260405-dts-qcom-w-1-fixes-v2-5-1f2c7b74a93f@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- .../boot/dts/qcom/sdm845-db845c-navigation-mezzanine.dtso | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/sdm845-db845c-navigation-mezzanine.dtso b/arch/arm64/boot/dts/qcom/sdm845-db845c-navigation-mezzanine.dtso index dbe1911d8e470..678a17c805f74 100644 --- a/arch/arm64/boot/dts/qcom/sdm845-db845c-navigation-mezzanine.dtso +++ b/arch/arm64/boot/dts/qcom/sdm845-db845c-navigation-mezzanine.dtso @@ -16,7 +16,12 @@ status = "okay"; ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + csiphy0_ep: endpoint { data-lanes = <0 1 2 3>; remote-endpoint = <&ov8856_ep>; From c72381bc70f545ab09f94cb4dcbe50cc19a49bf6 Mon Sep 17 00:00:00 2001 From: Kamal Wadhwa Date: Mon, 6 Apr 2026 19:05:55 +0530 Subject: [PATCH 0772/2758] arm64: dts: qcom: fix temp-alarm probe failure for PMH0104 on Glymur [ Upstream commit e8fae9152e100b4bb30d38b25bc9c81623e8e91e ] The temp-alarm driver probe is failing for the pmh0104 PMICs on glymur. [ 3.999713] spmi-temp-alarm c426000.spmi:pmic@8:temp-alarm@a00: error -ENODEV: failed to register sensor [ 4.015066] spmi-temp-alarm c426000.spmi:pmic@9:temp-alarm@a00: error -ENODEV: failed to register sensor [ 4.033908] spmi-temp-alarm c437000.spmi:pmic@b:temp-alarm@a00: error -ENODEV: failed to register sensor This happens because thermal zone associated with the temp alarm was defined under the thermal zones parent node which had a typo (used `_` in place of `-`). Correct the typo to fix probe failure. Fixes: 41b6e8db400c ("arm64: dts: qcom: Introduce Glymur base dtsi") Signed-off-by: Kamal Wadhwa Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260406-glymur-pmh0104-temp-alarm-fix-v1-1-4441b7b01f85@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/pmh0104-glymur.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/pmh0104-glymur.dtsi b/arch/arm64/boot/dts/qcom/pmh0104-glymur.dtsi index 7a1e5f355c175..6b4747025b9f8 100644 --- a/arch/arm64/boot/dts/qcom/pmh0104-glymur.dtsi +++ b/arch/arm64/boot/dts/qcom/pmh0104-glymur.dtsi @@ -7,7 +7,7 @@ #include /{ - thermal_zones { + thermal-zones { pmh0104_i0_thermal: pmh0104-i0-thermal { polling-delay-passive = <100>; thermal-sensors = <&pmh0104_i_e0_temp_alarm>; From 2c112ae55546ff240c2bcf097a1208c88cb4b8d2 Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Fri, 3 Apr 2026 15:58:48 +0200 Subject: [PATCH 0773/2758] arm64: dts: qcom: sdm845-shift-axolotl: Correct touchscreen sleep state [ Upstream commit 0689aa70fe28ba13eb1d8b10d50e08157ade9670 ] There is no suspend state in the mainline kernel, use the sleep state intended for this purpose. Fixes: 45882459159d ("arm64: dts: qcom: sdm845: add device tree for SHIFT6mq") Signed-off-by: David Heidelberg Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260403-oneplus-nfc-v3-3-fbdce57d63c1@ixit.cz Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sdm845-shift-axolotl.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sdm845-shift-axolotl.dts b/arch/arm64/boot/dts/qcom/sdm845-shift-axolotl.dts index 7d81198bc499c..b5fc93f0315fd 100644 --- a/arch/arm64/boot/dts/qcom/sdm845-shift-axolotl.dts +++ b/arch/arm64/boot/dts/qcom/sdm845-shift-axolotl.dts @@ -448,7 +448,7 @@ pinctrl-0 = <&ts_int_active &ts_reset_active>; pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>; - pinctrl-names = "default", "suspend"; + pinctrl-names = "default", "sleep"; touchscreen-size-x = <1080>; touchscreen-size-y = <2160>; From b3c7ccb1a93cbb5a9a13196abf608ffa2516817e Mon Sep 17 00:00:00 2001 From: Edward Adam Davis Date: Thu, 16 Apr 2026 11:37:53 +0800 Subject: [PATCH 0774/2758] hfsplus: Remove the duplicate attr inode dirty marking action [ Upstream commit 7a41fd2b32e5908f19a68732008d581c167279dd ] Syzbot reported a null-ptr-deref in [1]. If the attributes file is not loaded during system mount, a trigger occurs [1] when setxattr is executed in userspace. Remove the first mark attr inode dirty operation. [1] KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f] Call Trace: hfsplus_setxattr+0x124/0x340 fs/hfsplus/xattr.c:555 hfsplus_trusted_setxattr+0x40/0x60 fs/hfsplus/xattr_trusted.c:30 __vfs_setxattr+0x43c/0x480 fs/xattr.c:218 __vfs_setxattr_noperm+0x12d/0x660 fs/xattr.c:252 vfs_setxattr+0x163/0x360 fs/xattr.c:339 do_setxattr fs/xattr.c:654 [inline] Reported-by: syzbot+bc70a12e438dadba4fb4@syzkaller.appspotmail.com Fixes: ee8422d00b7c ("hfsplus: fix potential Allocation File corruption after fsync") Closes: https://syzkaller.appspot.com/bug?extid=bc70a12e438dadba4fb4 Signed-off-by: Edward Adam Davis Reviewed-by: Viacheslav Dubeyko Signed-off-by: Viacheslav Dubeyko Link: https://lore.kernel.org/r/tencent_A8D47429765566CC3C8B378496D036664A09@qq.com Signed-off-by: Viacheslav Dubeyko Signed-off-by: Sasha Levin --- fs/hfsplus/xattr.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/hfsplus/xattr.c b/fs/hfsplus/xattr.c index 452a1f9becb2d..21a1c196c71f2 100644 --- a/fs/hfsplus/xattr.c +++ b/fs/hfsplus/xattr.c @@ -317,7 +317,6 @@ static int hfsplus_create_attributes_file(struct super_block *sb) next_node++; } - hfsplus_mark_inode_dirty(HFSPLUS_ATTR_TREE_I(sb), HFSPLUS_I_ATTR_DIRTY); hfsplus_mark_inode_dirty(attr_file, HFSPLUS_I_ATTR_DIRTY); sbi->attr_tree = hfs_btree_open(sb, HFSPLUS_ATTR_CNID); From 306265eb9384d2c224f1b14d83a29323f0eab6d1 Mon Sep 17 00:00:00 2001 From: Edward Adam Davis Date: Fri, 17 Apr 2026 07:44:02 +0800 Subject: [PATCH 0775/2758] hfsplus: Add a sanity check for btree node size [ Upstream commit 3f95e2661574ff13f099dd13456751933c280628 ] Syzbot reported an uninit-value bug in [1] with a corrupted HFS+ image, during the file system mounting process, specifically while loading the catalog, a corrupted node_size value of 1 caused the rec_off argument passed to hfs_bnode_read_u16() (within hfs_bnode_find()) to be excessively large. Consequently, the function failed to return a valid value to initialize the off variable, triggering the bug [1]. Every node starts from BTree node descriptor: struct hfs_bnode_desc. So, the size of node cannot be lesser than that. However, technical specification declares that: "The node size (which is expressed in bytes) must be power of two, from 512 through 32,768, inclusive." Add a check for btree node size base on technical specification. [1] BUG: KMSAN: uninit-value in hfsplus_bnode_find+0x141c/0x1600 fs/hfsplus/bnode.c:584 hfsplus_bnode_find+0x141c/0x1600 fs/hfsplus/bnode.c:584 hfsplus_btree_open+0x169a/0x1e40 fs/hfsplus/btree.c:382 hfsplus_fill_super+0x111f/0x2770 fs/hfsplus/super.c:553 get_tree_bdev_flags+0x6e6/0x920 fs/super.c:1694 get_tree_bdev+0x38/0x50 fs/super.c:1717 hfsplus_get_tree+0x35/0x40 fs/hfsplus/super.c:709 vfs_get_tree+0xb3/0x5d0 fs/super.c:1754 fc_mount fs/namespace.c:1193 [inline] Fixes: 8ad2c6a36ac4 ("hfsplus: validate b-tree node 0 bitmap at mount time") Reported-by: syzbot+217eb327242d08197efb@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=217eb327242d08197efb Signed-off-by: Edward Adam Davis Reviewed-by: Viacheslav Dubeyko Signed-off-by: Viacheslav Dubeyko Link: https://lore.kernel.org/r/tencent_5ED373437A697F83A4A446B771577626CD05@qq.com Signed-off-by: Viacheslav Dubeyko Signed-off-by: Sasha Levin --- fs/hfsplus/btree.c | 2 ++ include/linux/hfs_common.h | 1 + 2 files changed, 3 insertions(+) diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c index 761c74ccd6531..394542a47e600 100644 --- a/fs/hfsplus/btree.c +++ b/fs/hfsplus/btree.c @@ -365,6 +365,8 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id) } size = tree->node_size; + if (size < HFSPLUS_NODE_MINSZ || size > HFSPLUS_NODE_MXSZ) + goto fail_page; if (!is_power_of_2(size)) goto fail_page; if (!tree->node_count) diff --git a/include/linux/hfs_common.h b/include/linux/hfs_common.h index 07dfc39630ab9..45fb4c9ff9f5e 100644 --- a/include/linux/hfs_common.h +++ b/include/linux/hfs_common.h @@ -513,6 +513,7 @@ struct hfs_btree_header_rec { /* HFS+ BTree misc info */ #define HFSPLUS_TREE_HEAD 0 #define HFSPLUS_NODE_MXSZ 32768 +#define HFSPLUS_NODE_MINSZ 512 #define HFSPLUS_ATTR_TREE_NODE_SIZE 8192 #define HFSPLUS_BTREE_HDR_NODE_RECS_COUNT 3 #define HFSPLUS_BTREE_HDR_MAP_REC_INDEX 2 /* Map (bitmap) record in Header node */ From d707dd37c6dcc16bc3106c17d14e1f94faa2c898 Mon Sep 17 00:00:00 2001 From: Louis Kotze Date: Tue, 14 Apr 2026 14:27:28 +0200 Subject: [PATCH 0776/2758] wifi: cfg80211: fix grammar in MLO group key error message [ Upstream commit 9dcc1af3bbf5441f71a1e51b8d81bdf38a249607 ] The error message emitted by nl80211_validate_key_link_id() when a group key install on an MLO wdev is missing the link ID reads "link ID must for MLO group key", which is missing the words "be set". This makes the error harder to grep and parse in userspace logs, and is reported verbatim by wpa_supplicant via its nl80211 extack relay, e.g.: wpa_supplicant: nl80211: kernel reports: link ID must for MLO group key The sibling error strings in the same helper already use grammatical phrasing ("link ID not allowed for pairwise key", "invalid link ID for MLO group key", "link ID not allowed for non-MLO group key"). Fix this one to match. No functional change. Fixes: e7a7b84e3317 ("wifi: cfg80211: Add link_id parameter to various key operations for MLO") Signed-off-by: Louis Kotze Link: https://patch.msgid.link/20260414122728.92234-1-loukot@gmail.com Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/wireless/nl80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 76c537a6e8b52..21e43e08b5d79 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5112,7 +5112,7 @@ static int nl80211_validate_key_link_id(struct genl_info *info, if (wdev->valid_links) { if (link_id == -1) { GENL_SET_ERR_MSG(info, - "link ID must for MLO group key"); + "link ID must be set for MLO group key"); return -EINVAL; } if (!(wdev->valid_links & BIT(link_id))) { From 4e7d9b05bf64cdaced78a299f69ac2fe05dc37e2 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Wed, 1 Apr 2026 11:29:41 +0100 Subject: [PATCH 0777/2758] arm64: tegra: Fix Tegra234 MGBE PTP clock [ Upstream commit 8f0cc929a4bad534c5a860a53d88912cf16d9c9c ] The Tegra MGBE PTP clock is incorrectly named as 'ptp-ref' and not 'ptp_ref' and this causing the initialisation of the PTP clock to fail. The device-tree binding doc for the device and the Tegra MGBE driver have been updated to use the correct name and so update the device-tree for Tegra234 as well. Fixes: 610cdf3186bc ("arm64: tegra: Add MGBE nodes on Tegra234") Signed-off-by: Jon Hunter Reviewed-by: Krzysztof Kozlowski Signed-off-by: Thierry Reding Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/nvidia/tegra234.dtsi | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/nvidia/tegra234.dtsi b/arch/arm64/boot/dts/nvidia/tegra234.dtsi index 04a95b6658caa..18220cdac9f9b 100644 --- a/arch/arm64/boot/dts/nvidia/tegra234.dtsi +++ b/arch/arm64/boot/dts/nvidia/tegra234.dtsi @@ -3605,7 +3605,7 @@ <&bpmp TEGRA234_CLK_MGBE0_RX_PCS_M>, <&bpmp TEGRA234_CLK_MGBE0_RX_PCS>, <&bpmp TEGRA234_CLK_MGBE0_TX_PCS>; - clock-names = "mgbe", "mac", "mac-divider", "ptp-ref", "rx-input-m", + clock-names = "mgbe", "mac", "mac-divider", "ptp_ref", "rx-input-m", "rx-input", "tx", "eee-pcs", "rx-pcs-input", "rx-pcs-m", "rx-pcs", "tx-pcs"; resets = <&bpmp TEGRA234_RESET_MGBE0_MAC>, @@ -3647,7 +3647,7 @@ <&bpmp TEGRA234_CLK_MGBE1_RX_PCS_M>, <&bpmp TEGRA234_CLK_MGBE1_RX_PCS>, <&bpmp TEGRA234_CLK_MGBE1_TX_PCS>; - clock-names = "mgbe", "mac", "mac-divider", "ptp-ref", "rx-input-m", + clock-names = "mgbe", "mac", "mac-divider", "ptp_ref", "rx-input-m", "rx-input", "tx", "eee-pcs", "rx-pcs-input", "rx-pcs-m", "rx-pcs", "tx-pcs"; resets = <&bpmp TEGRA234_RESET_MGBE1_MAC>, @@ -3689,7 +3689,7 @@ <&bpmp TEGRA234_CLK_MGBE2_RX_PCS_M>, <&bpmp TEGRA234_CLK_MGBE2_RX_PCS>, <&bpmp TEGRA234_CLK_MGBE2_TX_PCS>; - clock-names = "mgbe", "mac", "mac-divider", "ptp-ref", "rx-input-m", + clock-names = "mgbe", "mac", "mac-divider", "ptp_ref", "rx-input-m", "rx-input", "tx", "eee-pcs", "rx-pcs-input", "rx-pcs-m", "rx-pcs", "tx-pcs"; resets = <&bpmp TEGRA234_RESET_MGBE2_MAC>, @@ -3731,7 +3731,7 @@ <&bpmp TEGRA234_CLK_MGBE3_RX_PCS_M>, <&bpmp TEGRA234_CLK_MGBE3_RX_PCS>, <&bpmp TEGRA234_CLK_MGBE3_TX_PCS>; - clock-names = "mgbe", "mac", "mac-divider", "ptp-ref", "rx-input-m", + clock-names = "mgbe", "mac", "mac-divider", "ptp_ref", "rx-input-m", "rx-input", "tx", "eee-pcs", "rx-pcs-input", "rx-pcs-m", "rx-pcs", "tx-pcs"; resets = <&bpmp TEGRA234_RESET_MGBE3_MAC>, From c6cb2aa31fc246ee548acfec470b8b48749e900a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 10 Apr 2026 13:10:48 +0200 Subject: [PATCH 0778/2758] dt-bindings: pinctrl: nvidia,tegra234: Add missing required block [ Upstream commit 61b5deb5a968f7a82124f9b2591a6a151ed7273a ] Binding should require 'reg' property, because address space cannot be missing in the hardware and is already needed by the Linux drivers. Require also 'compatible' by convention, although it is not strictly necessary. Fixes: 857982138b79 ("dt-bindings: pinctrl: Document Tegra234 pin controllers") Signed-off-by: Krzysztof Kozlowski Acked-by: Rob Herring (Arm) Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- .../bindings/pinctrl/nvidia,tegra234-pinmux-aon.yaml | 4 ++++ .../devicetree/bindings/pinctrl/nvidia,tegra234-pinmux.yaml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux-aon.yaml b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux-aon.yaml index db8224dfba2c1..56fb9cf763ef7 100644 --- a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux-aon.yaml +++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux-aon.yaml @@ -58,6 +58,10 @@ patternProperties: drive_soc_gpio27_pee6, drive_ao_retention_n_pee2, drive_vcomp_alert_pee1, drive_hdmi_cec_pgg0 ] +required: + - compatible + - reg + unevaluatedProperties: false examples: diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux.yaml b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux.yaml index f5a3a881dec4f..bd305a34eee2b 100644 --- a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux.yaml +++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux.yaml @@ -115,6 +115,10 @@ patternProperties: drive_sdmmc1_dat2_pj4, drive_sdmmc1_dat1_pj3, drive_sdmmc1_dat0_pj2 ] +required: + - compatible + - reg + unevaluatedProperties: false examples: From ff77834b70c0144862412506b87abeadc51d2313 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Mon, 20 Apr 2026 18:55:55 +0800 Subject: [PATCH 0779/2758] pinctrl: pinconf-generic: fix properties bitmap leak in parse_fw_cfg() [ Upstream commit 352f5621b8c472bf328f452446bce85c00837223 ] In parse_fw_cfg(), if fwnode_property_match_property_string() fails with -ENOENT, the code returns directly and leaks the bitmap. Use __free(bitmap) for automatic cleanup to fix the leak. Fixes: 9c105255108b ("pinctrl: pinconf-generic: perform basic checks on pincfg properties") Signed-off-by: Felix Gu Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/pinconf-generic.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/pinctrl/pinconf-generic.c b/drivers/pinctrl/pinconf-generic.c index 64ed283097887..0771491216280 100644 --- a/drivers/pinctrl/pinconf-generic.c +++ b/drivers/pinctrl/pinconf-generic.c @@ -225,10 +225,9 @@ static int parse_fw_cfg(struct fwnode_handle *fwnode, unsigned int count, unsigned long *cfg, unsigned int *ncfg) { - unsigned long *properties; int i, test; - properties = bitmap_zalloc(count, GFP_KERNEL); + unsigned long *properties __free(bitmap) = bitmap_zalloc(count, GFP_KERNEL); for (i = 0; i < count; i++) { u32 val; @@ -263,7 +262,6 @@ static int parse_fw_cfg(struct fwnode_handle *fwnode, if (ret) { pr_err("%pfw: conflicting setting detected for %s\n", fwnode, par->property); - bitmap_free(properties); return -EINVAL; } } @@ -295,7 +293,6 @@ static int parse_fw_cfg(struct fwnode_handle *fwnode, pr_err("%pfw: cannot have multiple drive configurations\n", fwnode); - bitmap_free(properties); return 0; } From cb6311f25a096621ac7ffd91b50d1bb1cfb63a96 Mon Sep 17 00:00:00 2001 From: Srinivasan Shanmugam Date: Thu, 23 Apr 2026 18:29:46 +0530 Subject: [PATCH 0780/2758] drm/amdkfd: Validate CRIU-restored IDs before idr_alloc [ Upstream commit 85043dd49c2f51a37b22618168e3ae59ab92f0d6 ] The KFD CRIU restore flow restores previously saved object IDs from userspace. For event restore: kfd_criu_restore_event() -> create_signal_event() / create_other_event() -> allocate_event_notification_slot() -> idr_alloc(..., *restore_id, *restore_id + 1, ...) For BO restore: criu_restore_memory_of_gpu() -> idr_alloc(..., bo_priv->idr_handle, ...) In both cases, the restored ID comes from userspace-provided CRIU data. idr_alloc() expects the ID range values to fit within signed int limits. If a restored ID is larger than INT_MAX, it can trigger a WARN in the IDR layer. A kernel WARN is undesirable because it prints a warning trace and may cause a panic or reboot on systems with panic_on_warn enabled. Smatch reported these paths as allowing unchecked userspace values to reach idr_alloc(). Add INT_MAX validation before using restored IDs in: - kfd_criu_restore_event() - criu_restore_memory_of_gpu() If the restored ID is invalid, return -EINVAL. This prevents invalid restore data from reaching the IDR layer and avoids WARN-triggering paths, while keeping valid restore behavior unchanged. Fixes: 40e8a766a761 ("drm/amdkfd: CRIU checkpoint and restore events") Reported-by: Dan Carpenter Cc: Felix Kuehling Cc: David Yat Sin Cc: Rajneesh Bhardwaj Signed-off-by: Srinivasan Shanmugam Reviewed-by: David Yat Sin Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdkfd/kfd_chardev.c | 3 +++ drivers/gpu/drm/amd/amdkfd/kfd_events.c | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index 8785f7810157e..78068b2c96856 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -2362,6 +2362,9 @@ static int criu_restore_memory_of_gpu(struct kfd_process_device *pdd, const bool criu_resume = true; u64 offset; + if (bo_priv->idr_handle > INT_MAX) + return -EINVAL; + if (bo_bucket->alloc_flags & KFD_IOC_ALLOC_MEM_FLAGS_DOORBELL) { if (bo_bucket->size != kfd_doorbell_process_slice(pdd->dev->kfd)) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_events.c index e65b323aafbf3..81900b49d9d5b 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c @@ -483,6 +483,11 @@ int kfd_criu_restore_event(struct file *devkfd, } *priv_data_offset += sizeof(*ev_priv); + if (ev_priv->event_id > INT_MAX) { + ret = -EINVAL; + goto exit; + } + if (ev_priv->user_handle) { ret = kfd_kmap_event_page(p, ev_priv->user_handle); if (ret) From 9b0f4082a09760939588135d60a8e9cc994bfa3e Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Sat, 18 Apr 2026 18:22:18 +0200 Subject: [PATCH 0781/2758] driver core: use READ_ONCE() for dev->driver in dev_has_sync_state() [ Upstream commit e9506871a8ea304cde48ff4a57226df2aadddae3 ] dev_has_sync_state() reads dev->driver twice without holding device_lock() -- once for the NULL check and once to dereference ->sync_state. Some callers only hold device_links_write_lock, which doesn't prevent a concurrent unbind from clearing dev->driver via device_unbind_cleanup(). Fix it by reading dev->driver exactly once with READ_ONCE(), pairing with the WRITE_ONCE() in device_set_driver(). Link: https://lore.kernel.org/driver-core/DHW8QPU1VU1F.3P6PH69HLFBYC@kernel.org/ Fixes: ac338acf514e ("driver core: Add dev_has_sync_state()") Reviewed-by: Rafael J. Wysocki (Intel) Acked-by: Greg Kroah-Hartman Reviewed-by: Saravana Kannan Link: https://patch.msgid.link/20260418162221.1121873-1-dakr@kernel.org Signed-off-by: Danilo Krummrich Signed-off-by: Sasha Levin --- include/linux/device.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/linux/device.h b/include/linux/device.h index 9c8fde6a3d866..79a4a6549be8e 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -1065,9 +1065,12 @@ static inline void device_lock_assert(struct device *dev) static inline bool dev_has_sync_state(struct device *dev) { + struct device_driver *drv; + if (!dev) return false; - if (dev->driver && dev->driver->sync_state) + drv = READ_ONCE(dev->driver); + if (drv && drv->sync_state) return true; if (dev->bus && dev->bus->sync_state) return true; From c1907b9a4fa9cb33d11a9af138374dd2e93f7a93 Mon Sep 17 00:00:00 2001 From: Christos Longros Date: Sun, 29 Mar 2026 09:38:57 +0200 Subject: [PATCH 0782/2758] wifi: rtw89: fix wrong pci_get_drvdata type in AER handlers [ Upstream commit 7068c379cf9aa8afe4dce4d9d82390187aa9c4d0 ] rtw89 stores an ieee80211_hw pointer via pci_set_drvdata() at probe time, but io_error_detected() and io_resume() retrieve it as a net_device pointer. This causes netif_device_detach/attach to operate on an ieee80211_hw struct, reading and writing at wrong offsets. The adjacent io_slot_reset() already does it correctly. Use ieee80211_stop_queues/wake_queues instead, consistent with every other queue stop/start path in the driver. Tested on RTL8852CE by calling the handlers from a test module before and after the fix. Fixes: 16e3d93c6183 ("wifi: rtw89: pci: add PCI Express error handling") Signed-off-by: Christos Longros Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260329073857.113081-1-chris.longros@gmail.com Signed-off-by: Sasha Levin --- drivers/net/wireless/realtek/rtw89/pci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index 43c61b3dc969f..64554eb35a72c 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -4624,9 +4624,9 @@ EXPORT_SYMBOL(rtw89_pm_ops); static pci_ers_result_t rtw89_pci_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state) { - struct net_device *netdev = pci_get_drvdata(pdev); + struct ieee80211_hw *hw = pci_get_drvdata(pdev); - netif_device_detach(netdev); + ieee80211_stop_queues(hw); return PCI_ERS_RESULT_NEED_RESET; } @@ -4643,12 +4643,12 @@ static pci_ers_result_t rtw89_pci_io_slot_reset(struct pci_dev *pdev) static void rtw89_pci_io_resume(struct pci_dev *pdev) { - struct net_device *netdev = pci_get_drvdata(pdev); + struct ieee80211_hw *hw = pci_get_drvdata(pdev); /* ack any pending wake events, disable PME */ pci_enable_wake(pdev, PCI_D0, 0); - netif_device_attach(netdev); + ieee80211_wake_queues(hw); } const struct pci_error_handlers rtw89_pci_err_handler = { From d7920797f721f944861f3c48ffb2b73f84b631fe Mon Sep 17 00:00:00 2001 From: Chin-Yen Lee Date: Mon, 13 Apr 2026 14:59:26 +0800 Subject: [PATCH 0783/2758] wifi: rtw88: fix wrong pci_get_drvdata type in AER handlers [ Upstream commit 706183dbef4a79d120d4e928f693bea50df496f8 ] rtw88 stores an ieee80211_hw pointer via pci_set_drvdata() at probe time, but io_error_detected() and io_resume() retrieve it as a net_device pointer. This causes netif_device_detach/attach to operate on an ieee80211_hw struct, reading and writing at wrong offsets. Use ieee80211_stop_queues/wake_queues instead, consistent with every other queue stop/start path in the driver. Fixes: cdb82c80b934 ("wifi: rtw88: pci: add PCI Express error handling") Signed-off-by: Chin-Yen Lee Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260413065926.17027-1-pkshih@realtek.com Signed-off-by: Sasha Levin --- drivers/net/wireless/realtek/rtw88/pci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c index bba370ad510cf..c56beacbb1b03 100644 --- a/drivers/net/wireless/realtek/rtw88/pci.c +++ b/drivers/net/wireless/realtek/rtw88/pci.c @@ -1711,9 +1711,9 @@ static void rtw_pci_napi_deinit(struct rtw_dev *rtwdev) static pci_ers_result_t rtw_pci_io_err_detected(struct pci_dev *pdev, pci_channel_state_t state) { - struct net_device *netdev = pci_get_drvdata(pdev); + struct ieee80211_hw *hw = pci_get_drvdata(pdev); - netif_device_detach(netdev); + ieee80211_stop_queues(hw); return PCI_ERS_RESULT_NEED_RESET; } @@ -1730,12 +1730,12 @@ static pci_ers_result_t rtw_pci_io_slot_reset(struct pci_dev *pdev) static void rtw_pci_io_resume(struct pci_dev *pdev) { - struct net_device *netdev = pci_get_drvdata(pdev); + struct ieee80211_hw *hw = pci_get_drvdata(pdev); /* ack any pending wake events, disable PME */ pci_enable_wake(pdev, PCI_D0, 0); - netif_device_attach(netdev); + ieee80211_wake_queues(hw); } const struct pci_error_handlers rtw_pci_err_handler = { From 140fa699cfd0e18ab2f1497acd951fb3548610f5 Mon Sep 17 00:00:00 2001 From: Shin-Yi Lin Date: Mon, 20 Apr 2026 11:40:41 +0800 Subject: [PATCH 0784/2758] wifi: rtw89: Correct data type for scan index to avoid infinite loop [ Upstream commit 08fdcb529df6df3562dd2b0035f88dd5be8b3c68 ] A kernel soft lockup was observed during Wi-Fi scanning on the 6GHz band. The CPU becomes stuck in rtw89_hw_scan_add_chan_ax for over 20 seconds, leading to a system panic. RIP points to 0f b6 c3 (movzbl %bl, %eax), which zero-extends the low 8 bits of RBX into RAX. RBX (the counter i) has reached a huge value: 0x137466a1. watchdog: BUG: soft lockup - CPU#2 stuck for 26s! [kworker/u16:4:6124] Workqueue: events_unbound cfg80211_wiphy_work [cfg80211] RIP: 0010:rtw89_hw_scan_add_chan_ax+0xb3/0x6e0 [rtw89_core] Code: a0 48 89 45 a8 44 89 6d 9c 44 89 75 98 eb 29 66 66 2e 0f 1f 84 00 00 00 00 00 66 66 2e 0f 1f 84 00 00 00 00 00 66 90 83 c3 01 <0f> b6 c3 41 3b 44 24 74 0f 83 0b 02 00 00 0f b6 c3 48 8d 14 80 49 RSP: 0018:ffffcb48cbaa39f8 EFLAGS: 00000202 RAX: 0000000000000005 RBX: 00000000137466a1 RCX: 0000000000000000 RDX: ffff89ffc9d851a8 RSI: 0000000000004f0d RDI: 0000000096af0130 RBP: ffffcb48cbaa3a60 R08: 0000000000000000 R09: ffff8a00b7502080 R10: ffff8a00b75ff600 R11: 0000000000000000 R12: ffff89ffc7553870 R13: ffff8a00b7ac8f19 R14: ffff8a00b75020d8 R15: ffff89ffc3d54d80 FS: 0000000000000000(0000) GS:ffff8a014f962000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007558d7f9f4c4 CR3: 0000000178040001 CR4: 00000000001706f0 Call Trace: rtw89_hw_scan_prep_chan_list_ax+0x8a/0x400 [rtw89_core] rtw89_hw_scan_start+0x546/0x8a0 [rtw89_core] ? rtw89_fw_h2c_default_cmac_tbl+0x13c/0x1f0 [rtw89_core] rtw89_ops_hw_scan+0xae/0x120 [rtw89_core] drv_hw_scan+0xbb/0x180 [mac80211] __ieee80211_start_scan+0x2fc/0x750 [mac80211] ieee80211_request_scan+0xe/0x20 [mac80211] ieee80211_scan+0x123/0x190 [mac80211] rdev_scan+0x40/0x110 [cfg80211] cfg80211_scan_6ghz+0x5a1/0xa30 [cfg80211] By objdump with source: for (i = 0; i < req->n_6ghz_params; i++) { 5fbc0: 83 c3 01 add $0x1,%ebx --> i++ 5fbc3: 0f b6 c3 movzbl %bl,%eax --> get counter fbc6: 41 3b 44 24 74 cmp 0x74(%r12),%eax * RBX: 00000000137466a1 -> %bl = a1 -> EAX = 000000a1 (161) Fixes: c6aa9a9c4725 ("wifi: rtw89: add RNR support for 6 GHz scan") Signed-off-by: Shin-Yi Lin Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-7-pkshih@realtek.com Signed-off-by: Sasha Levin --- drivers/net/wireless/realtek/rtw89/fw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 17704f054727a..089c9071b58f5 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -8318,7 +8318,7 @@ static int rtw89_update_6ghz_rnr_chan_ax(struct rtw89_dev *rtwdev, struct sk_buff *skb; bool found; int ret = 0; - u8 i; + u32 i; if (!req->n_6ghz_params) return 0; From 1554fa522f16ec7c5c342ad33fe734eeb6eb2452 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Tue, 21 Apr 2026 11:14:34 +0000 Subject: [PATCH 0785/2758] wifi: rtw88: fix OOB read from firmware RX descriptor exceeding DMA buffer [ Upstream commit 6e76e9ed273dfb4b3333a5ebbb94958cc5752ab6 ] In rtw_pci_rx_napi(), new_len is computed as the sum of pkt_len (14-bit descriptor field, max 16383) and pkt_offset (drv_info_sz + shift, both firmware-controlled). The result can exceed RTK_PCI_RX_BUF_SIZE (11478), causing an out-of-bounds read from the pre-allocated DMA buffer when skb_put_data copies new_len bytes. The USB transport already validates this (rtw_usb_rx_data_put checks against RTW_USB_MAX_RECVBUF_SZ); the PCIe path does not. Add a check that new_len does not exceed the DMA buffer size. Fixes: e3037485c68e ("rtw88: new Realtek 802.11ac driver") Signed-off-by: Tristan Madani Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260421111434.3389674-1-tristmd@gmail.com Signed-off-by: Sasha Levin --- drivers/net/wireless/realtek/rtw88/pci.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c index c56beacbb1b03..c2bf44e880cf2 100644 --- a/drivers/net/wireless/realtek/rtw88/pci.c +++ b/drivers/net/wireless/realtek/rtw88/pci.c @@ -1077,6 +1077,11 @@ static u32 rtw_pci_rx_napi(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci, * discard the frame if none available */ new_len = pkt_stat.pkt_len + pkt_offset; + if (unlikely(new_len > RTK_PCI_RX_BUF_SIZE)) { + rtw_dbg(rtwdev, RTW_DBG_RX, + "oversized RX packet: %u\n", new_len); + goto next_rp; + } new = dev_alloc_skb(new_len); if (WARN_ONCE(!new, "rx routine starvation\n")) goto next_rp; From 920101305e7601a33b9e01019f4ca526af2526ae Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Tue, 21 Apr 2026 11:14:42 +0000 Subject: [PATCH 0786/2758] wifi: rtw89: add bounds check on firmware mac_id in link lookup [ Upstream commit 6d88244bb129755acca696f9227200f4a2d106a6 ] The mac_id field in RX descriptors is 8 bits wide (0-255), but assoc_link_on_macid[] has only RTW89_MAX_MAC_ID_NUM (128) entries. While the driver currently assigns mac_id values below 128, the descriptor value comes from firmware and is not validated before use as an array index. Add a defensive bounds check in rtw89_assoc_link_rcu_dereference() to guard against out-of-range firmware values. Fixes: 144c6cd24b35 ("wifi: rtw89: 8922a: configure AP_LINK_PS if FW supports") Signed-off-by: Tristan Madani Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260421111442.3395411-1-tristmd@gmail.com Signed-off-by: Sasha Levin --- drivers/net/wireless/realtek/rtw89/core.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index fd29dbbb120d2..05adf20a65be1 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -6492,6 +6492,9 @@ static inline void rtw89_assoc_link_clr(struct rtw89_sta_link *rtwsta_link) static inline struct rtw89_sta_link * rtw89_assoc_link_rcu_dereference(struct rtw89_dev *rtwdev, u8 macid) { + if (unlikely(macid >= RTW89_MAX_MAC_ID_NUM)) + return NULL; + return rcu_dereference(rtwdev->assoc_link_on_macid[macid]); } From b0dad1ac5e2f725c9a131060b873f8757c82c3fa Mon Sep 17 00:00:00 2001 From: Xingjing Deng Date: Fri, 6 Mar 2026 02:17:09 +0000 Subject: [PATCH 0787/2758] kconfig: fix potential NULL pointer dereference in conf_askvalue [ Upstream commit b9d21c32dca2167a614e66c9e27999b9e1c33d55 ] In conf_askvalue(), the 'def' argument (retrieved via sym_get_string_value) can be NULL. While current call sites ensure that 'def' is valid, calling printf("%s\n", def) is technically undefined behavior and could lead to a segmentation fault on certain libc implementations if the function were called with a NULL pointer in the future. Improve the robustness of conf_askvalue() by providing an empty string as a fallback. Additionally, remove the redundant re-initialization of the 'line' buffer inside the !sym_is_changeable(sym) block, as it is already properly initialized at the function entry. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Xingjing Deng Reviewed-by: Nathan Chancellor Link: https://patch.msgid.link/20260306021709.27068-1-micro6947@gmail.com Signed-off-by: Nathan Chancellor Signed-off-by: Sasha Levin --- scripts/kconfig/conf.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c index a7b44cd8ae140..c368bec5ab601 100644 --- a/scripts/kconfig/conf.c +++ b/scripts/kconfig/conf.c @@ -297,9 +297,7 @@ static int conf_askvalue(struct symbol *sym, const char *def) line[1] = 0; if (!sym_is_changeable(sym)) { - printf("%s\n", def); - line[0] = '\n'; - line[1] = 0; + printf("%s\n", def ?: ""); return 0; } @@ -307,7 +305,7 @@ static int conf_askvalue(struct symbol *sym, const char *def) case oldconfig: case syncconfig: if (sym_has_value(sym)) { - printf("%s\n", def); + printf("%s\n", def ?: ""); return 0; } /* fall through */ From d349aa118ff5e4cab88424ce1b16e08edd70f8bb Mon Sep 17 00:00:00 2001 From: Prasanna Kumar T S M Date: Thu, 19 Mar 2026 23:03:06 -0700 Subject: [PATCH 0788/2758] soc: xilinx: Fix race condition in event registration [ Upstream commit fb445935338405110baca8f541a2df3b4cb8d712 ] The zynqmp_power driver registers handlers for suspend and subsystem restart events using register_event(). However, the work structures (zynqmp_pm_init_suspend_work and zynqmp_pm_init_restart_work) used by these handlers were allocated and initialized after the registration call. This created a race window where, if the firmware triggered an event immediately after registration but before allocation, the callback (suspend_event_callback or subsystem_restart_event_callback) would dereference a NULL pointer in work_pending(), leading to a crash. Fix this by allocating and initializing the work structures before registering the events. Fixes: fcf544ac6439 ("soc: xilinx: Add cb event for subsystem restart") Signed-off-by: Prasanna Kumar T S M Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/20260320060306.1540928-1-ptsm@linux.microsoft.com Signed-off-by: Sasha Levin --- drivers/soc/xilinx/zynqmp_power.c | 43 ++++++++++++------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/drivers/soc/xilinx/zynqmp_power.c b/drivers/soc/xilinx/zynqmp_power.c index 9085db1b480aa..9dd938bd01d84 100644 --- a/drivers/soc/xilinx/zynqmp_power.c +++ b/drivers/soc/xilinx/zynqmp_power.c @@ -303,18 +303,18 @@ static int zynqmp_pm_probe(struct platform_device *pdev) * is not available to use) or -ENODEV(Xilinx Event Manager not compiled), * then use ipi-mailbox or interrupt method. */ + zynqmp_pm_init_suspend_work = devm_kzalloc(&pdev->dev, + sizeof(struct zynqmp_pm_work_struct), + GFP_KERNEL); + if (!zynqmp_pm_init_suspend_work) + return -ENOMEM; + + INIT_WORK(&zynqmp_pm_init_suspend_work->callback_work, + zynqmp_pm_init_suspend_work_fn); + ret = register_event(&pdev->dev, PM_INIT_SUSPEND_CB, 0, 0, false, suspend_event_callback); if (!ret) { - zynqmp_pm_init_suspend_work = devm_kzalloc(&pdev->dev, - sizeof(struct zynqmp_pm_work_struct), - GFP_KERNEL); - if (!zynqmp_pm_init_suspend_work) - return -ENOMEM; - - INIT_WORK(&zynqmp_pm_init_suspend_work->callback_work, - zynqmp_pm_init_suspend_work_fn); - ret = zynqmp_pm_get_family_info(&pm_family_code); if (ret < 0) return ret; @@ -326,14 +326,6 @@ static int zynqmp_pm_probe(struct platform_device *pdev) else return -ENODEV; - ret = register_event(&pdev->dev, PM_NOTIFY_CB, node_id, EVENT_SUBSYSTEM_RESTART, - false, subsystem_restart_event_callback); - if (ret) { - dev_err(&pdev->dev, "Failed to Register with Xilinx Event manager %d\n", - ret); - return ret; - } - zynqmp_pm_init_restart_work = devm_kzalloc(&pdev->dev, sizeof(struct zynqmp_pm_work_struct), GFP_KERNEL); @@ -342,19 +334,18 @@ static int zynqmp_pm_probe(struct platform_device *pdev) INIT_WORK(&zynqmp_pm_init_restart_work->callback_work, zynqmp_pm_subsystem_restart_work_fn); + + ret = register_event(&pdev->dev, PM_NOTIFY_CB, node_id, EVENT_SUBSYSTEM_RESTART, + false, subsystem_restart_event_callback); + if (ret) { + dev_err(&pdev->dev, "Failed to Register with Xilinx Event manager %d\n", + ret); + return ret; + } } else if (ret != -EACCES && ret != -ENODEV) { dev_err(&pdev->dev, "Failed to Register with Xilinx Event manager %d\n", ret); return ret; } else if (of_property_present(pdev->dev.of_node, "mboxes")) { - zynqmp_pm_init_suspend_work = - devm_kzalloc(&pdev->dev, - sizeof(struct zynqmp_pm_work_struct), - GFP_KERNEL); - if (!zynqmp_pm_init_suspend_work) - return -ENOMEM; - - INIT_WORK(&zynqmp_pm_init_suspend_work->callback_work, - zynqmp_pm_init_suspend_work_fn); client = devm_kzalloc(&pdev->dev, sizeof(*client), GFP_KERNEL); if (!client) return -ENOMEM; From b5d2240c2c7bc8370e7ba54a2e1fe3919f55aee5 Mon Sep 17 00:00:00 2001 From: Prasanna Kumar T S M Date: Thu, 19 Mar 2026 23:04:45 -0700 Subject: [PATCH 0789/2758] soc: xilinx: Shutdown and free rx mailbox channel [ Upstream commit fdee7c66c0d7b6869c36b9f9a915abf29ab5b550 ] A mbox rx channel is requested using mbox_request_channel_byname() in probe. In remove callback, the rx mailbox channel is cleaned up when the rx_chan is NULL due to incorrect condition check. The mailbox channel is not shutdown and it can receive messages even after the device removal. This leads to use after free. Also the channel resources are not freed. Fix this by checking the rx_chan correctly. Fixes: ffdbae28d9d1a ("drivers: soc: xilinx: Use mailbox IPI callback") Signed-off-by: Prasanna Kumar T S M Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/20260320060445.1541017-1-ptsm@linux.microsoft.com Signed-off-by: Sasha Levin --- drivers/soc/xilinx/zynqmp_power.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/soc/xilinx/zynqmp_power.c b/drivers/soc/xilinx/zynqmp_power.c index 9dd938bd01d84..370e61ac47d88 100644 --- a/drivers/soc/xilinx/zynqmp_power.c +++ b/drivers/soc/xilinx/zynqmp_power.c @@ -389,8 +389,10 @@ static void zynqmp_pm_remove(struct platform_device *pdev) { sysfs_remove_file(&pdev->dev.kobj, &dev_attr_suspend_mode.attr); - if (!rx_chan) + if (rx_chan) { mbox_free_channel(rx_chan); + rx_chan = NULL; + } } static const struct of_device_id pm_of_match[] = { From 0cc7c35a481014cbe4d0c89f6801305e87bbe04a Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Mon, 27 Apr 2026 10:11:46 +0800 Subject: [PATCH 0790/2758] pinctrl: mediatek: eint: Drop base from mtk_eint_chip_write_mask() [ Upstream commit 3ca99eed042620d12315e9272ed3ef260ca29877 ] When support for multiple EINT base addresses was added in commit 3ef9f710efcb ("pinctrl: mediatek: Add EINT support for multiple addresses"), mtk_eint_chip_write_mask() was changed to write interrupt masks for all base addresses in one call. However the "base" parameter was left around and now causes sparse warnings: mtk-eint.c:428:44: warning: incorrect type in argument 2 (different address spaces) mtk-eint.c:428:44: expected void [noderef] __iomem *base mtk-eint.c:428:44: got void [noderef] __iomem **base mtk-eint.c:436:44: warning: incorrect type in argument 2 (different address spaces) mtk-eint.c:436:44: expected void [noderef] __iomem *base mtk-eint.c:436:44: got void [noderef] __iomem **base Since the "base" parameter is no longer needed, just drop it. Fixes: 3ef9f710efcb ("pinctrl: mediatek: Add EINT support for multiple addresses") Cc: Hao Chang Cc: Qingliang Li Signed-off-by: Chen-Yu Tsai Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/mediatek/mtk-eint.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pinctrl/mediatek/mtk-eint.c b/drivers/pinctrl/mediatek/mtk-eint.c index 2a3c04eedc5f3..47ac92ea98c2c 100644 --- a/drivers/pinctrl/mediatek/mtk-eint.c +++ b/drivers/pinctrl/mediatek/mtk-eint.c @@ -246,7 +246,7 @@ static int mtk_eint_irq_set_wake(struct irq_data *d, unsigned int on) } static void mtk_eint_chip_write_mask(const struct mtk_eint *eint, - void __iomem *base, unsigned int **buf) + unsigned int **buf) { int inst, port, port_num; void __iomem *reg; @@ -425,7 +425,7 @@ static void mtk_eint_irq_handler(struct irq_desc *desc) int mtk_eint_do_suspend(struct mtk_eint *eint) { - mtk_eint_chip_write_mask(eint, eint->base, eint->wake_mask); + mtk_eint_chip_write_mask(eint, eint->wake_mask); return 0; } @@ -433,7 +433,7 @@ EXPORT_SYMBOL_GPL(mtk_eint_do_suspend); int mtk_eint_do_resume(struct mtk_eint *eint) { - mtk_eint_chip_write_mask(eint, eint->base, eint->cur_mask); + mtk_eint_chip_write_mask(eint, eint->cur_mask); return 0; } From 336d4c8cd9b1646060ee690c881d465dfc09c6c0 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Wed, 15 Apr 2026 22:23:43 +0000 Subject: [PATCH 0791/2758] wifi: ath9k: fix OOB access from firmware tx status queue ID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 7ce2f118a2389e8f0a64068c6fe7cc7d40639be0 ] ath_tx_edma_tasklet() accesses sc->tx.txq[ts.qid] where ts.qid is a 4-bit hardware field (0-15), but the txq array only has ATH9K_NUM_TX_QUEUES (10) entries. A qid >= 10 causes an OOB array access. Add a bounds check on ts.qid before using it as an array index. Fixes: fce041beb03f ("ath9k: unify edma and non-edma tx code, improve tx fifo handling") Signed-off-by: Tristan Madani Acked-by: Toke Høiland-Jørgensen Link: https://patch.msgid.link/20260415222343.1540564-1-tristmd@gmail.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath9k/xmit.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 0ac9212e42f75..957646b2df4de 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -2746,6 +2746,11 @@ void ath_tx_edma_tasklet(struct ath_softc *sc) continue; } + if (ts.qid >= ATH9K_NUM_TX_QUEUES) { + ath_dbg(common, XMIT, "invalid qid %d\n", ts.qid); + continue; + } + txq = &sc->tx.txq[ts.qid]; ath_txq_lock(sc, txq); From 53dd29e8aeb2a1b5f079178551836b85fc6b53df Mon Sep 17 00:00:00 2001 From: Wei Zhang Date: Fri, 3 Apr 2026 21:30:50 -0700 Subject: [PATCH 0792/2758] wifi: ath11k: cancel SSR work items during PCI shutdown [ Upstream commit 8c79aac429b583301f387374ff37c59be671df87 ] A reboot can crash the kernel if it overlaps with WLAN firmware crash recovery (SSR). The crash is a NULL pointer dereference in the MHI teardown path while freeing DMA-backed MHI contexts. Simplified trace: dma_free_attrs mhi_deinit_dev_ctxt [mhi] ath11k_pci_power_down [ath11k_pci] ath11k_pci_shutdown [ath11k_pci] device_shutdown kernel_restart On the host side, SSR is driven by the MHI RDDM callback, which queues reset_work to perform device recovery. reset_work power-cycles the device by calling ath11k_hif_power_down() followed by ath11k_hif_power_up(). The power-down phase deinitializes MHI and frees DMA resources. Shutdown/reboot runs fully asynchronously with this RDDM-driven SSR recovery flow. As a result, the shutdown path (ath11k_pci_shutdown() -> ath11k_pci_power_down()) can race with the SSR recovery sequence. Fix this by canceling SSR-related work items during PCI shutdown, marking the device as unregistering, and serializing the RDDM callback path that checks and queues reset_work. This ensures that no new SSR recovery work can be queued once teardown has started, and that any in-flight recovery work is fully synchronized before device power-down, preventing MHI teardown and DMA resource freeing from running more than once. Note: This issue only affects PCI/MHI-based devices. AHB-based ath11k devices do not queue reset_work in normal SSR flows. Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-04866.5-QCAHSPSWPL_V1_V2_SILICONZ_IOE-1 Fixes: 13da397f884d ("ath11k: add support for device recovery for QCA6390/WCN6855") Fixes: 5edbb148bc57 ("wifi: ath11k: Add firmware coredump collection support") Signed-off-by: Wei Zhang Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260404043050.3433754-1-wei.zhang@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath11k/mhi.c | 4 +++- drivers/net/wireless/ath/ath11k/pci.c | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath11k/mhi.c b/drivers/net/wireless/ath/ath11k/mhi.c index f994233df2bb9..a6c9ff112c68f 100644 --- a/drivers/net/wireless/ath/ath11k/mhi.c +++ b/drivers/net/wireless/ath/ath11k/mhi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include @@ -282,8 +282,10 @@ static void ath11k_mhi_op_status_cb(struct mhi_controller *mhi_cntrl, break; } + spin_lock_bh(&ab->base_lock); if (!(test_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags))) queue_work(ab->workqueue_aux, &ab->reset_work); + spin_unlock_bh(&ab->base_lock); break; default: diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c index 7114eca8810db..35bb9e7a63a20 100644 --- a/drivers/net/wireless/ath/ath11k/pci.c +++ b/drivers/net/wireless/ath/ath11k/pci.c @@ -1210,6 +1210,14 @@ static void ath11k_pci_shutdown(struct pci_dev *pdev) struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); ath11k_pci_set_irq_affinity_hint(ab_pci, NULL); + + spin_lock_bh(&ab->base_lock); + set_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags); + spin_unlock_bh(&ab->base_lock); + + cancel_work_sync(&ab->reset_work); + cancel_work_sync(&ab->dump_work); + ath11k_pci_power_down(ab, false); } From 54ebf6af5805f7984d22f380a28bed48c34ce009 Mon Sep 17 00:00:00 2001 From: Jihed Chaibi Date: Wed, 25 Mar 2026 23:34:11 +0100 Subject: [PATCH 0793/2758] ARM: dts: am335x-sl50: Fix audio bitclock and frame master endpoint [ Upstream commit 2bc564f46b00dc4f4331fc337277ff3f5fac8a4e ] The cpu_endpoint in mcasp0 specifies the TLV320AIC3106 codec as the bitclock and frame master, but the phandles point to the codec's port node (codec_port) rather than its endpoint node (codec_endpoint). audio-graph-card calls simple_util_parse_daifmt() with ep_codec set to the endpoint node (codec_endpoint). The function resolves the bitclock-master phandle and checks whether it equals ep_codec. Since codec_port is the parent of codec_endpoint, not the endpoint itself, the comparison always evaluates to false. This causes the mcasp0 CPU side to be silently configured as bitclock and frame master instead of the codec, which is the opposite of the intended configuration. Fix by pointing bitclock-master and frame-master to codec_endpoint. Fixes: e5f89dbdebc5 ("ARM: dts: am335x-sl50: use audio-graph-card for sound") Signed-off-by: Jihed Chaibi Link: https://patch.msgid.link/20260325223411.123666-1-jihed.chaibi.dev@gmail.com Signed-off-by: Kevin Hilman (TI) Signed-off-by: Sasha Levin --- arch/arm/boot/dts/ti/omap/am335x-sl50.dts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/ti/omap/am335x-sl50.dts b/arch/arm/boot/dts/ti/omap/am335x-sl50.dts index 1dc4e344efd63..c5259eb7d21c7 100644 --- a/arch/arm/boot/dts/ti/omap/am335x-sl50.dts +++ b/arch/arm/boot/dts/ti/omap/am335x-sl50.dts @@ -558,8 +558,8 @@ remote-endpoint = <&codec_endpoint>; dai-format = "dsp_b"; - bitclock-master = <&codec_port>; - frame-master = <&codec_port>; + bitclock-master = <&codec_endpoint>; + frame-master = <&codec_endpoint>; bitclock-inversion; clocks = <&audio_mclk>; }; From eb49026306b4fdf32946a66211d51f65928894d2 Mon Sep 17 00:00:00 2001 From: Aleksandr Loktionov Date: Thu, 30 Apr 2026 23:37:20 -0700 Subject: [PATCH 0794/2758] ixgbe: fix unaligned u32 access in ixgbe_update_flash_X550() [ Upstream commit eae23ec14a9c83b6af9bd616f3b86163688e2688 ] ixgbe_host_interface_command() treats its buffer as a u32 array. The local buffer we pass in was a union of byte-sized fields, which gives it 1-byte alignment on the stack. On strict-align architectures this can cause unaligned 32-bit accesses. Add a u32 member to union ixgbe_hic_hdr2 so the object is 4-byte aligned, and pass the u32 member when calling ixgbe_host_interface_command(). No functional change on x86; prevents unaligned accesses on architectures that enforce natural alignment. Fixes: 49425dfc7451 ("ixgbe: Add support for x550em_a 10G MAC type") Signed-off-by: Aleksandr Loktionov Reviewed-by: Jedrzej Jagielski Reviewed-by: Paul Menzel Fixes: 6a14ee0cfb19 ("ixgbe: Add X550 support function pointers") Tested-by: Rinitha S Signed-off-by: Jacob Keller Link: https://patch.msgid.link/20260430-jk-iwl-net-next-2026-04-30-v1-9-6f27ae1cd073@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/ixgbe/ixgbe_type.h | 1 + drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 61f2ef67defdd..eb5bf3b6bbb52 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -2798,6 +2798,7 @@ struct ixgbe_hic_hdr2_rsp { }; union ixgbe_hic_hdr2 { + u32 buf[1]; struct ixgbe_hic_hdr2_req req; struct ixgbe_hic_hdr2_rsp rsp; }; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index 76d2fa3ef5182..4a0ccbf448a2a 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -1228,7 +1228,7 @@ static int ixgbe_update_flash_X550(struct ixgbe_hw *hw) buffer.req.buf_lenl = FW_SHADOW_RAM_DUMP_LEN; buffer.req.checksum = FW_DEFAULT_CHECKSUM; - status = ixgbe_host_interface_command(hw, &buffer, sizeof(buffer), + status = ixgbe_host_interface_command(hw, buffer.buf, sizeof(buffer), IXGBE_HI_COMMAND_TIMEOUT, false); return status; } From ffb693966d967addb53eff9805b1af2fe3d987bb Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Mon, 27 Apr 2026 15:17:09 +0200 Subject: [PATCH 0795/2758] Documentation/rv: Replace stale website link [ Upstream commit 49cbd359e4a7501e9d6694c072031d9ae6b2d1a5 ] The sched monitor page was linking to Daniel's website which is now down. The main purpose of the link was to point to a source for the models from the original author and that can be found also in his published paper. Replace the link with a reference to Daniel's "A thread synchronization model for the PREEMPT_RT Linux kernel" which can be found online and includes the models definitions as well as the work behind them (not the original patches but since they're based on a 5.0 kernel and are mostly included upstream, there's little value in keeping them in the docs). Fixes: 03abeaa63c08 ("Documentation/rv: Add docs for the sched monitors") Signed-off-by: Gabriele Monaco Acked-by: Matteo Martelli Tested-by: Matteo Martelli Tested-by: Randy Dunlap Acked-by: Randy Dunlap Signed-off-by: Jonathan Corbet Message-ID: <20260427131709.170505-2-gmonaco@redhat.com> Signed-off-by: Sasha Levin --- Documentation/trace/rv/monitor_sched.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Documentation/trace/rv/monitor_sched.rst b/Documentation/trace/rv/monitor_sched.rst index 0b96d6e147c65..d3ba7edc202f6 100644 --- a/Documentation/trace/rv/monitor_sched.rst +++ b/Documentation/trace/rv/monitor_sched.rst @@ -36,7 +36,7 @@ Specifications -------------- The specifications included in sched are currently a work in progress, adapting the ones -defined in by Daniel Bristot in [1]. +defined by Daniel Bristot in [1]_. Currently we included the following: @@ -365,4 +365,7 @@ constraints when processing the events:: References ---------- -[1] - https://bristot.me/linux-task-model +.. [1] Daniel Bristot de Oliveira et al.: + `A thread synchronization model for the PREEMPT_RT Linux kernel + `_, + J. Syst. Archit., 2020. From a4f833321e513e43bd19053a211c8d1d10888621 Mon Sep 17 00:00:00 2001 From: Yingjie Gao Date: Thu, 2 Apr 2026 15:16:17 +0800 Subject: [PATCH 0796/2758] watchdog: sp5100_tco: Use EFCH MMIO for newer Hygon FCH [ Upstream commit 4f675f036cd5e9cfbe6df5f24f8338158af96a4b ] Commit 009637de1f65 ("watchdog: sp5100_tco: support Hygon FCH/SCH (Server Controller Hub)") added Hygon vendor matching to the efch layout selection, but newer Hygon 0x790b SMBus devices still need the efch_mmio path. The efch_mmio path enables EFCH_PM_DECODEEN_WDT_TMREN before probing the watchdog MMIO block. If firmware leaves that bit clear and the driver picks the legacy efch path instead, probe falls back to the alternate window and fails with "Watchdog hardware is disabled". Select efch_mmio for Hygon 0x790b devices with revision 0x51 or later, matching the equivalent AMD behavior and allowing the watchdog to initialize on those systems. Fixes: 009637de1f65 ("watchdog: sp5100_tco: support Hygon FCH/SCH (Server Controller Hub)") Signed-off-by: Yingjie Gao Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20260402071617.634563-2-gaoyingjie@uniontech.com Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/watchdog/sp5100_tco.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c index 2bd3dc25cb030..7e99c3b1f3676 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c @@ -92,7 +92,8 @@ static enum tco_reg_layout tco_reg_layout(struct pci_dev *dev) dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS && dev->revision < 0x40) { return sp5100; - } else if (dev->vendor == PCI_VENDOR_ID_AMD && + } else if ((dev->vendor == PCI_VENDOR_ID_AMD || + dev->vendor == PCI_VENDOR_ID_HYGON) && sp5100_tco_pci->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS && sp5100_tco_pci->revision >= AMD_ZEN_SMBUS_PCI_REV) { return efch_mmio; From 6e8ea05b73f6f031f9a8a0018693a4f29b442e48 Mon Sep 17 00:00:00 2001 From: Balakrishnan Sambath Date: Mon, 2 Mar 2026 17:03:09 +0530 Subject: [PATCH 0797/2758] watchdog: sama5d4_wdt: Fix WDDIS detection on SAM9X60 and SAMA7G5 [ Upstream commit e8bc610b14a99d24b0916635102cc52ba41def9b ] The driver hardcoded AT91_WDT_WDDIS (bit 15) in wdt_enabled and the probe initial state readout. SAM9X60 and SAMA7G5 use bit 12 (AT91_SAM9X60_WDDIS), causing incorrect WDDIS detection. Introduce a per-device wddis_mask field to select the correct WDDIS bit based on the compatible string. Fixes: 266da53c35fc ("watchdog: sama5d4: readout initial state") Co-developed-by: Andrei Simion Signed-off-by: Andrei Simion Signed-off-by: Balakrishnan Sambath Reviewed-by: Alexandre Belloni Link: https://lore.kernel.org/r/20260302113310.133989-2-balakrishnan.s@microchip.com Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/watchdog/sama5d4_wdt.c | 48 +++++++++++++++------------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c index 13e72918338a2..704b786cc2ec6 100644 --- a/drivers/watchdog/sama5d4_wdt.c +++ b/drivers/watchdog/sama5d4_wdt.c @@ -30,6 +30,7 @@ struct sama5d4_wdt { void __iomem *reg_base; u32 mr; u32 ir; + u32 wddis_mask; unsigned long last_ping; bool need_irq; bool sam9x60_support; @@ -48,7 +49,10 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -#define wdt_enabled (!(wdt->mr & AT91_WDT_WDDIS)) +static inline bool wdt_enabled(struct sama5d4_wdt *wdt) +{ + return !(wdt->mr & wdt->wddis_mask); +} #define wdt_read(wdt, field) \ readl_relaxed((wdt)->reg_base + (field)) @@ -81,12 +85,9 @@ static int sama5d4_wdt_start(struct watchdog_device *wdd) { struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); - if (wdt->sam9x60_support) { + if (wdt->sam9x60_support) writel_relaxed(wdt->ir, wdt->reg_base + AT91_SAM9X60_IER); - wdt->mr &= ~AT91_SAM9X60_WDDIS; - } else { - wdt->mr &= ~AT91_WDT_WDDIS; - } + wdt->mr &= ~wdt->wddis_mask; wdt_write(wdt, AT91_WDT_MR, wdt->mr); return 0; @@ -96,12 +97,9 @@ static int sama5d4_wdt_stop(struct watchdog_device *wdd) { struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); - if (wdt->sam9x60_support) { + if (wdt->sam9x60_support) writel_relaxed(wdt->ir, wdt->reg_base + AT91_SAM9X60_IDR); - wdt->mr |= AT91_SAM9X60_WDDIS; - } else { - wdt->mr |= AT91_WDT_WDDIS; - } + wdt->mr |= wdt->wddis_mask; wdt_write(wdt, AT91_WDT_MR, wdt->mr); return 0; @@ -117,7 +115,7 @@ static int sama5d4_wdt_ping(struct watchdog_device *wdd) } static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd, - unsigned int timeout) + unsigned int timeout) { struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); u32 value = WDT_SEC2TICKS(timeout); @@ -140,8 +138,8 @@ static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd, * If the watchdog is enabled, then the timeout can be updated. Else, * wait that the user enables it. */ - if (wdt_enabled) - wdt_write(wdt, AT91_WDT_MR, wdt->mr & ~AT91_WDT_WDDIS); + if (wdt_enabled(wdt)) + wdt_write(wdt, AT91_WDT_MR, wdt->mr & ~wdt->wddis_mask); wdd->timeout = timeout; @@ -184,10 +182,7 @@ static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt) { const char *tmp; - if (wdt->sam9x60_support) - wdt->mr = AT91_SAM9X60_WDDIS; - else - wdt->mr = AT91_WDT_WDDIS; + wdt->mr = wdt->wddis_mask; if (!of_property_read_string(np, "atmel,watchdog-type", &tmp) && !strcmp(tmp, "software")) @@ -213,15 +208,11 @@ static int sama5d4_wdt_init(struct sama5d4_wdt *wdt) * If the watchdog is already running, we can safely update it. * Else, we have to disable it properly. */ - if (!wdt_enabled) { + if (!wdt_enabled(wdt)) { reg = wdt_read(wdt, AT91_WDT_MR); - if (wdt->sam9x60_support && (!(reg & AT91_SAM9X60_WDDIS))) - wdt_write_nosleep(wdt, AT91_WDT_MR, - reg | AT91_SAM9X60_WDDIS); - else if (!wdt->sam9x60_support && - (!(reg & AT91_WDT_WDDIS))) + if (!(reg & wdt->wddis_mask)) wdt_write_nosleep(wdt, AT91_WDT_MR, - reg | AT91_WDT_WDDIS); + reg | wdt->wddis_mask); } if (wdt->sam9x60_support) { @@ -273,6 +264,9 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) of_device_is_compatible(dev->of_node, "microchip,sama7g5-wdt")) wdt->sam9x60_support = true; + wdt->wddis_mask = wdt->sam9x60_support ? AT91_SAM9X60_WDDIS + : AT91_WDT_WDDIS; + watchdog_set_drvdata(wdd, wdt); regs = devm_platform_ioremap_resource(pdev, 0); @@ -306,8 +300,8 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) watchdog_init_timeout(wdd, wdt_timeout, dev); reg = wdt_read(wdt, AT91_WDT_MR); - if (!(reg & AT91_WDT_WDDIS)) { - wdt->mr &= ~AT91_WDT_WDDIS; + if (!(reg & wdt->wddis_mask)) { + wdt->mr &= ~wdt->wddis_mask; set_bit(WDOG_HW_RUNNING, &wdd->status); } From ccaf1a97e15589b6f1748199ab23254c108b8057 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Mon, 23 Feb 2026 19:59:20 +0800 Subject: [PATCH 0798/2758] watchdog: sprd_wdt: Remove redundant sprd_wdt_disable() on register failure [ Upstream commit 96b3cfc3b8ad0524d12fed1e08bc5df3ff345f64 ] The driver uses devm_add_action_or_reset() to register sprd_wdt_disable() as a managed cleanup action. When devm_watchdog_register_device() fails, the devm core will invoke the cleanup action automatically. The explicit sprd_wdt_disable() call in the error path is therefore redundant and results in adouble cleanup. Fixes: 78d9bfad2e89 ("watchdog: sprd_wdt: Convert to use device managed functions and other improvements") Signed-off-by: Felix Gu Reviewed-by: Guenter Roeck Reviewed-by: Baolin Wang Link: https://lore.kernel.org/r/20260223-sprd_wdt-v1-1-2e71f9a76ecb@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/watchdog/sprd_wdt.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/watchdog/sprd_wdt.c b/drivers/watchdog/sprd_wdt.c index 4e689b6ff1418..aacf04616fefe 100644 --- a/drivers/watchdog/sprd_wdt.c +++ b/drivers/watchdog/sprd_wdt.c @@ -320,10 +320,9 @@ static int sprd_wdt_probe(struct platform_device *pdev) watchdog_init_timeout(&wdt->wdd, 0, dev); ret = devm_watchdog_register_device(dev, &wdt->wdd); - if (ret) { - sprd_wdt_disable(wdt); + if (ret) return ret; - } + platform_set_drvdata(pdev, wdt); return 0; From 984c22a861eef904ed2e369d16ace0d559b98999 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Tue, 7 Apr 2026 01:14:40 +0300 Subject: [PATCH 0799/2758] media: cedrus: Fix failure to clean up hardware on probe failure [ Upstream commit f0a22f1d602ed499a192284de5e811a73421f0c7 ] If V4L2 device fails to register, then SRAM still be claimed and as a result driver will not be able to probe again. cedrus 1c0e000.video-codec: Failed to claim SRAM cedrus 1c0e000.video-codec: Failed to probe hardware cedrus 1c0e000.video-codec: probe with driver cedrus failed with error -16 cedrus_hw_remove undoes everything that was previously done by cedrus_hw_probe, such as disabling runtime power management and releasing the claimed SRAM and reserved memory region. Signed-off-by: Samuel Holland Signed-off-by: Andrey Skvortsov Fixes: 50e761516f2b ("media: platform: Add Cedrus VPU decoder driver") Acked-by: Paul Kocialkowski Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil Signed-off-by: Sasha Levin --- drivers/staging/media/sunxi/cedrus/cedrus.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c index 6600245dff0e2..27e43af6c7bd0 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus.c @@ -476,7 +476,7 @@ static int cedrus_probe(struct platform_device *pdev) ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); if (ret) { dev_err(&pdev->dev, "Failed to register V4L2 device\n"); - return ret; + goto err_hw; } vfd = &dev->vfd; @@ -537,6 +537,8 @@ static int cedrus_probe(struct platform_device *pdev) v4l2_m2m_release(dev->m2m_dev); err_v4l2: v4l2_device_unregister(&dev->v4l2_dev); +err_hw: + cedrus_hw_remove(dev); return ret; } From f4de1cc282e578d703b9a5da8a8a416c11f5b39f Mon Sep 17 00:00:00 2001 From: Nas Chung Date: Wed, 15 Apr 2026 18:25:21 +0900 Subject: [PATCH 0800/2758] media: v4l2-common: Add YUV24 format info [ Upstream commit 968b741872914a15363af0daf24ed2e82ec355f3 ] The YUV24 format is missing an entry in the v4l2_format_info(). The YUV24 format is the packed YUV 4:4:4 formats with 8 bits per component. Fixes: 0376a51fbe5e ("media: v4l: Add packed YUV444 24bpp pixel format") Signed-off-by: Nas Chung Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil Signed-off-by: Sasha Levin --- drivers/media/v4l2-core/v4l2-common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 554c591e11133..55bcd5975d9fc 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -281,6 +281,7 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) { .format = V4L2_PIX_FMT_Y212, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, { .format = V4L2_PIX_FMT_Y216, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, { .format = V4L2_PIX_FMT_YUV48_12, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_YUV24, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_MT2110T, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2, .block_w = { 16, 8, 0, 0 }, .block_h = { 32, 16, 0, 0 }}, { .format = V4L2_PIX_FMT_MT2110R, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2, From 48213cbfa6dd3d3719859e1cb9a1bc2e7a45c25f Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Tue, 31 Mar 2026 14:01:25 +0800 Subject: [PATCH 0801/2758] drm: verisilicon: call atomic helper's plane state check even if no CRTC [ Upstream commit eae3903e33797a28d0d37e693d4314d58338918e ] The `drm_atomic_helper_check_plane_state()` helper function needs to be called even if the plane is bound to no CRTCs. Remove the early return in the primary plane's atomic_check, and use NULL for crtc_state in this situation. Fixes: dbf21777caa8 ("drm: verisilicon: add a driver for Verisilicon display controllers") Signed-off-by: Icenowy Zheng Reviewed-by: Thomas Zimmermann Signed-off-by: Thomas Zimmermann Link: https://patch.msgid.link/20260331060126.1291966-4-zhengxingda@iscas.ac.cn Signed-off-by: Sasha Levin --- drivers/gpu/drm/verisilicon/vs_primary_plane.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/verisilicon/vs_primary_plane.c b/drivers/gpu/drm/verisilicon/vs_primary_plane.c index e8fcb5958615c..a383b70f1ea0f 100644 --- a/drivers/gpu/drm/verisilicon/vs_primary_plane.c +++ b/drivers/gpu/drm/verisilicon/vs_primary_plane.c @@ -26,14 +26,10 @@ static int vs_primary_plane_atomic_check(struct drm_plane *plane, struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); struct drm_crtc *crtc = new_plane_state->crtc; - struct drm_crtc_state *crtc_state; + struct drm_crtc_state *crtc_state = NULL; - if (!crtc) - return 0; - - crtc_state = drm_atomic_get_new_crtc_state(state, crtc); - if (WARN_ON(!crtc_state)) - return -EINVAL; + if (crtc) + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state, From 3255998ad0106df5a2943ba1c146e7f3c6e9c9d6 Mon Sep 17 00:00:00 2001 From: Ashish Mhetre Date: Thu, 30 Apr 2026 09:52:01 +0000 Subject: [PATCH 0802/2758] memory: tegra: Wire up system sleep PM ops [ Upstream commit 2411c8d1e3e09910e94bab0d0a2c071fbc8a9e7b ] The tegra-mc platform driver does not register any dev_pm_ops, so the SoC-specific ->resume() is never invoked (e.g. tegra186_mc_resume) on system wake. On Tegra186 and later this means MC client Stream-ID override registers are not reprogrammed, and clients behind the ARM SMMU fault on the first DMA after resume. Register a dev_pm_ops on the tegra-mc driver and route the system resume callback into mc->soc->ops->resume() so the existing SID restore path runs again on wake. No suspend callback is needed as the resume path reprograms all MC state from the static SoC tables, so there is nothing to save. Fixes: fe3b082a6eb8 ("memory: tegra: Add SID override programming for MC clients") Signed-off-by: Ashish Mhetre Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20260430095202.1167651-3-amhetre@nvidia.com Signed-off-by: Krzysztof Kozlowski Signed-off-by: Sasha Levin --- drivers/memory/tegra/mc.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index d620660da3311..64e41338cdf2d 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -1010,10 +1011,23 @@ static void tegra_mc_sync_state(struct device *dev) icc_sync_state(dev); } +static int tegra_mc_resume(struct device *dev) +{ + struct tegra_mc *mc = dev_get_drvdata(dev); + + if (mc->soc->ops && mc->soc->ops->resume) + mc->soc->ops->resume(mc); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(tegra_mc_pm_ops, NULL, tegra_mc_resume); + static struct platform_driver tegra_mc_driver = { .driver = { .name = "tegra-mc", .of_match_table = tegra_mc_of_match, + .pm = pm_sleep_ptr(&tegra_mc_pm_ops), .suppress_bind_attrs = true, .sync_state = tegra_mc_sync_state, }, From db3172a18cce1e6ac48bdc052788547a3977c177 Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Wed, 8 Apr 2026 10:49:19 -0400 Subject: [PATCH 0803/2758] objtool/klp: Fix is_uncorrelated_static_local() for Clang [ Upstream commit 84c304a534b844703d3437811e8f072166e3f116 ] For naming function-local static locals, GCC uses ., e.g. __already_done.15, while Clang uses . with optional ., e.g. create_worker.__already_done.111 The existing is_uncorrelated_static_local() check only matches the GCC convention where the variable name is a prefix. Handle both cases by checking for a prefix match (GCC) and by checking after the first dot separator (Clang). Fixes: dd590d4d57eb ("objtool/klp: Introduce klp diff subcommand for diffing object files") Signed-off-by: Joe Lawrence Acked-by: Song Liu Reviewed-by: Miroslav Benes Signed-off-by: Josh Poimboeuf Signed-off-by: Sasha Levin --- tools/objtool/klp-diff.c | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c index 0b0d1503851ff..b1b068e9b4c70 100644 --- a/tools/objtool/klp-diff.c +++ b/tools/objtool/klp-diff.c @@ -242,16 +242,17 @@ static struct symbol *next_file_symbol(struct elf *elf, struct symbol *sym) static bool is_uncorrelated_static_local(struct symbol *sym) { static const char * const vars[] = { - "__already_done.", - "__func__.", - "__key.", - "__warned.", - "_entry.", - "_entry_ptr.", - "_rs.", - "descriptor.", - "CSWTCH.", + "__already_done", + "__func__", + "__key", + "__warned", + "_entry", + "_entry_ptr", + "_rs", + "descriptor", + "CSWTCH", }; + const char *dot; if (!is_object_sym(sym) || !is_local_sym(sym)) return false; @@ -259,8 +260,20 @@ static bool is_uncorrelated_static_local(struct symbol *sym) if (!strcmp(sym->sec->name, ".data.once")) return true; + dot = strchr(sym->name, '.'); + if (!dot) + return false; + for (int i = 0; i < ARRAY_SIZE(vars); i++) { - if (strstarts(sym->name, vars[i])) + size_t len = strlen(vars[i]); + + /* GCC: . */ + if (strstarts(sym->name, vars[i]) && (sym->name[len] == '.')) + return true; + + /* Clang: .[.] */ + if (strstarts(dot + 1, vars[i]) && + (dot[1 + len] == '.' || dot[1 + len] == '\0')) return true; } From a19f83ec8699784082baff132db881f893d5019f Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Sat, 11 Apr 2026 21:11:24 -0700 Subject: [PATCH 0804/2758] objtool/klp: Fix .data..once static local non-correlation [ Upstream commit ff529864e738f447ff4c019956319930fc274a23 ] While there was once a section named .data.once, it has since been renamed to .data..once with commit dbefa1f31a91 ("Rename .data.once to .data..once to fix resetting WARN*_ONCE"). Fix it. Fixes: dd590d4d57eb ("objtool/klp: Introduce klp diff subcommand for diffing object files") Acked-by: Song Liu Reviewed-by: Miroslav Benes Signed-off-by: Josh Poimboeuf Signed-off-by: Sasha Levin --- tools/objtool/klp-diff.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c index b1b068e9b4c70..cb26c1c92a74a 100644 --- a/tools/objtool/klp-diff.c +++ b/tools/objtool/klp-diff.c @@ -257,7 +257,8 @@ static bool is_uncorrelated_static_local(struct symbol *sym) if (!is_object_sym(sym) || !is_local_sym(sym)) return false; - if (!strcmp(sym->sec->name, ".data.once")) + /* WARN_ONCE, etc */ + if (!strcmp(sym->sec->name, ".data..once")) return true; dot = strchr(sym->name, '.'); From 9dea59f904b30811ef5c3c58c909010255947d6d Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Tue, 24 Mar 2026 17:34:16 -0400 Subject: [PATCH 0805/2758] objtool/klp: Fix create_fake_symbols() skipping entsize-based sections [ Upstream commit 3de711fba73ad93b8b3fbe09cf681cefed5d573d ] create_fake_symbols() has two phases: creating symbols from ANNOTATE_DATA_SPECIAL entries, and a fallback that uses sh_entsize for special sections like .static_call_sites. When .discard.annotate_data is absent, the function returns early, skipping the entsize fallback and silently allowing unsupported module-local static call keys through. Fix it by jumping to the entsize phase instead of returning early. Fixes: dd590d4d57eb ("objtool/klp: Introduce klp diff subcommand for diffing object files") Assisted-by: Claude:claude-4-opus Signed-off-by: Joe Lawrence Acked-by: Song Liu Reviewed-by: Miroslav Benes Signed-off-by: Josh Poimboeuf Signed-off-by: Sasha Levin --- tools/objtool/klp-diff.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c index cb26c1c92a74a..18e40a7f86431 100644 --- a/tools/objtool/klp-diff.c +++ b/tools/objtool/klp-diff.c @@ -1348,7 +1348,7 @@ static int create_fake_symbols(struct elf *elf) sec = find_section_by_name(elf, ".discard.annotate_data"); if (!sec || !sec->rsec) - return 0; + goto entsize; for_each_reloc(sec->rsec, reloc) { unsigned long offset, size; @@ -1380,7 +1380,7 @@ static int create_fake_symbols(struct elf *elf) /* * 2) Make symbols for sh_entsize, and simple arrays of pointers: */ - +entsize: for_each_sec(elf, sec) { unsigned int entry_size; unsigned long offset; From 283caf6959f4d5c7d3082208edc315283b3080f4 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Thu, 9 Apr 2026 18:34:50 -0700 Subject: [PATCH 0806/2758] objtool/klp: Fix handling of zero-length .altinstr_replacement sections [ Upstream commit c4c02d4450b5b7e2fbde578252f71a5697180112 ] When a section is empty (e.g. only zero-length alternative replacements), there are no symbols to convert a section symbol reference to. Skip the reloc instead of erroring out. Fixes: dd590d4d57eb ("objtool/klp: Introduce klp diff subcommand for diffing object files") Acked-by: Song Liu Reviewed-by: Miroslav Benes Signed-off-by: Josh Poimboeuf Signed-off-by: Sasha Levin --- tools/objtool/klp-diff.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c index 18e40a7f86431..8aec5f5702c73 100644 --- a/tools/objtool/klp-diff.c +++ b/tools/objtool/klp-diff.c @@ -993,6 +993,13 @@ static int convert_reloc_secsym_to_sym(struct elf *elf, struct reloc *reloc) /* No dedicated section; find the symbol manually */ sym = find_symbol_containing(sec, arch_adjusted_addend(reloc)); if (!sym) { + /* + * This is presumably an .altinstr_replacement section which is + * empty due to it only having zero-length replacement(s). + */ + if (!sec_size(sec)) + return 1; + /* * This can happen for special section references to weak code * whose symbol has been stripped by the linker. @@ -1253,6 +1260,7 @@ static int clone_sym_relocs(struct elfs *e, struct symbol *patched_sym) for_each_reloc(patched_rsec, patched_reloc) { unsigned long offset; + int ret; if (reloc_offset(patched_reloc) < start || reloc_offset(patched_reloc) >= end) @@ -1266,12 +1274,15 @@ static int clone_sym_relocs(struct elfs *e, struct symbol *patched_sym) !strcmp(patched_reloc->sym->sec->name, ".altinstr_aux")) continue; - if (convert_reloc_sym(e->patched, patched_reloc)) { + ret = convert_reloc_sym(e->patched, patched_reloc); + if (ret < 0) { ERROR_FUNC(patched_rsec->base, reloc_offset(patched_reloc), "failed to convert reloc sym '%s' to its proper format", patched_reloc->sym->name); return -1; } + if (ret > 0) + continue; offset = out_sym->offset + (reloc_offset(patched_reloc) - patched_sym->offset); From e9a4ea48396e74d1eec05506c23aae0ed764d529 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Thu, 9 Apr 2026 18:35:01 -0700 Subject: [PATCH 0807/2758] objtool/klp: Fix cloning of zero-length section symbols [ Upstream commit 98377f3ba7c02d6eb34e203c9f9823bc62b0d231 ] Fix NULL dereference when cloning a symbol from an empty section. sec->data is only populated for sections with non-zero size. Fixes: dd590d4d57eb ("objtool/klp: Introduce klp diff subcommand for diffing object files") Acked-by: Song Liu Reviewed-by: Miroslav Benes Signed-off-by: Josh Poimboeuf Signed-off-by: Sasha Levin --- tools/objtool/klp-diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c index 8aec5f5702c73..67fdc43a74337 100644 --- a/tools/objtool/klp-diff.c +++ b/tools/objtool/klp-diff.c @@ -669,7 +669,7 @@ static struct symbol *__clone_symbol(struct elf *elf, struct symbol *patched_sym size_t size; /* bss doesn't have data */ - if (patched_sym->sec->data->d_buf) + if (patched_sym->sec->data && patched_sym->sec->data->d_buf) data = patched_sym->sec->data->d_buf + patched_sym->offset; if (is_sec_sym(patched_sym)) From cf0fac29f54073e61caa9ea4d8dc618a8333b092 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Fri, 3 Apr 2026 14:53:32 -0700 Subject: [PATCH 0808/2758] objtool/klp: Fix extraction of text annotations for alternatives [ Upstream commit 62a7a01fde87c99926cd7e9670b4226c4c79ebaf ] Objtool is failing to extract text annotations which reference .altinstr_replacement instructions: 1) Alternative replacement fake symbols are NOTYPE rather than FUNC, and they don't have sym->included set, thus they aren't recognized by should_keep_special_sym(). 2) .discard.annotate_insn gets processed before .altinstr_replacement, so the referenced (fake) symbols don't have clones yet. Fix the first issue by checking for a valid clone instead of sym->included and by accepting NOTYPE symbols when processing .discard.annotate_insn. Fix the second issue by deferring text annotation processing until after the other special sections have been cloned. Fixes: dd590d4d57eb ("objtool/klp: Introduce klp diff subcommand for diffing object files") Acked-by: Song Liu Reviewed-by: Miroslav Benes Signed-off-by: Josh Poimboeuf Signed-off-by: Sasha Levin --- tools/objtool/klp-diff.c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c index 67fdc43a74337..61abf5ceb650e 100644 --- a/tools/objtool/klp-diff.c +++ b/tools/objtool/klp-diff.c @@ -1425,6 +1425,7 @@ static int create_fake_symbols(struct elf *elf) /* Keep a special section entry if it references an included function */ static bool should_keep_special_sym(struct elf *elf, struct symbol *sym) { + bool annotate_insn = !strcmp(sym->sec->name, ".discard.annotate_insn"); struct reloc *reloc; if (is_sec_sym(sym) || !sym->sec->rsec) @@ -1434,7 +1435,16 @@ static bool should_keep_special_sym(struct elf *elf, struct symbol *sym) if (convert_reloc_sym(elf, reloc)) continue; - if (is_func_sym(reloc->sym) && reloc->sym->included) + if (!reloc->sym->clone || is_undef_sym(reloc->sym->clone)) + continue; + + /* + * Keep special section references to cloned functions. + * In some cases annotate_insn can also reference cloned alt + * replacement fake symbols; keep those references as well. + */ + if (is_func_sym(reloc->sym) || + (annotate_insn && is_notype_sym(reloc->sym))) return true; } @@ -1578,15 +1588,28 @@ static int clone_special_section(struct elfs *e, struct section *patched_sec) /* Extract only the needed bits from special sections */ static int clone_special_sections(struct elfs *e) { - struct section *patched_sec; + struct section *sec, *annotate_insn = NULL; - for_each_sec(e->patched, patched_sec) { - if (is_special_section(patched_sec)) { - if (clone_special_section(e, patched_sec)) + for_each_sec(e->patched, sec) { + if (is_special_section(sec)) { + if (!strcmp(sec->name, ".discard.annotate_insn")) { + annotate_insn = sec; + continue; + } + if (clone_special_section(e, sec)) return -1; } } + /* + * Do .discard.annotate_insn last, it can reference other special + * sections (alt replacements) so they need to be cloned first. + */ + if (annotate_insn) { + if (clone_special_section(e, annotate_insn)) + return -1; + } + return 0; } From 4220dd46c38e7e0de613eef21f44a72be0e316ac Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 20 Apr 2026 22:59:12 -0700 Subject: [PATCH 0809/2758] objtool/klp: Fix relocation conversion failures for R_X86_64_NONE [ Upstream commit 3787e82a4e3a0a04aeb5543580ee90bed3a36e55 ] Objtool has some hacks which NOP out certain calls/jumps and replace their relocations with R_X86_64_NONE. The klp-diff relocation extraction code will error out when trying to copy these relocations due to their negative addend, which would only makes sense for a PC-relative branch instruction. Just ignore them. Fixes: dd590d4d57eb ("objtool/klp: Introduce klp diff subcommand for diffing object files") Acked-by: Song Liu Reviewed-by: Miroslav Benes Signed-off-by: Josh Poimboeuf Signed-off-by: Sasha Levin --- tools/objtool/klp-diff.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c index 61abf5ceb650e..ef038f1336667 100644 --- a/tools/objtool/klp-diff.c +++ b/tools/objtool/klp-diff.c @@ -1020,6 +1020,9 @@ static int convert_reloc_secsym_to_sym(struct elf *elf, struct reloc *reloc) */ static int convert_reloc_sym(struct elf *elf, struct reloc *reloc) { + if (reloc_type(reloc) == R_NONE) + return 1; + if (is_reloc_allowed(reloc)) return 0; From a5ad716141904c4b87bb55b0f7a9e5acbd9f8d8d Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Sun, 12 Apr 2026 22:41:23 -0700 Subject: [PATCH 0810/2758] objtool: Replace iterator callback with for_each_sym_by_mangled_name() [ Upstream commit 0333b7399587ee0aaa863ed0d13a00a6c7c64068 ] Convert the callback-based iterate_sym_by_demangled_name() with a new for_each_sym_by_demangled_name() macro. This eliminates the callback struct/function and makes the code more compact and readable. Acked-by: Song Liu Reviewed-by: Miroslav Benes Signed-off-by: Josh Poimboeuf Stable-dep-of: a375e327b63e ("objtool: Fix reloc hash collision in find_reloc_by_dest_range()") Signed-off-by: Sasha Levin --- tools/objtool/elf.c | 68 ++++++++--------------------- tools/objtool/include/objtool/elf.h | 32 ++++++++++++-- tools/objtool/klp-diff.c | 42 ++++++------------ 3 files changed, 60 insertions(+), 82 deletions(-) diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index f3df2bde119fc..dc39132f71c14 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -27,27 +27,16 @@ static ssize_t demangled_name_len(const char *name); -static inline u32 str_hash(const char *str) -{ - return jhash(str, strlen(str), 0); -} - -static inline u32 str_hash_demangled(const char *str) +u32 str_hash_demangled(const char *str) { return jhash(str, demangled_name_len(str), 0); } -#define __elf_table(name) (elf->name##_hash) -#define __elf_bits(name) (elf->name##_bits) - -#define __elf_table_entry(name, key) \ - __elf_table(name)[hash_min(key, __elf_bits(name))] - #define elf_hash_add(name, node, key) \ ({ \ struct elf_hash_node *__node = node; \ - __node->next = __elf_table_entry(name, key); \ - __elf_table_entry(name, key) = __node; \ + __node->next = __elf_table_entry(elf, name, key); \ + __elf_table_entry(elf, name, key) = __node; \ }) static inline void __elf_hash_del(struct elf_hash_node *node, @@ -69,30 +58,20 @@ static inline void __elf_hash_del(struct elf_hash_node *node, } #define elf_hash_del(name, node, key) \ - __elf_hash_del(node, &__elf_table_entry(name, key)) - -#define elf_list_entry(ptr, type, member) \ -({ \ - typeof(ptr) __ptr = (ptr); \ - __ptr ? container_of(__ptr, type, member) : NULL; \ -}) - -#define elf_hash_for_each_possible(name, obj, member, key) \ - for (obj = elf_list_entry(__elf_table_entry(name, key), typeof(*obj), member); \ - obj; \ - obj = elf_list_entry(obj->member.next, typeof(*(obj)), member)) + __elf_hash_del(node, &__elf_table_entry(elf, name, key)) #define elf_alloc_hash(name, size) \ ({ \ - __elf_bits(name) = max(10, ilog2(size)); \ - __elf_table(name) = mmap(NULL, sizeof(struct elf_hash_node *) << __elf_bits(name), \ + __elf_bits(elf, name) = max(10, ilog2(size)); \ + __elf_table(elf, name) = mmap(NULL, \ + sizeof(struct elf_hash_node *) << __elf_bits(elf, name), \ PROT_READ|PROT_WRITE, \ MAP_PRIVATE|MAP_ANON, -1, 0); \ - if (__elf_table(name) == (void *)-1L) { \ + if (__elf_table(elf, name) == (void *)-1L) { \ ERROR_GLIBC("mmap fail " #name); \ - __elf_table(name) = NULL; \ + __elf_table(elf, name) = NULL; \ } \ - __elf_table(name); \ + __elf_table(elf, name); \ }) static inline unsigned long __sym_start(struct symbol *s) @@ -141,7 +120,7 @@ struct section *find_section_by_name(const struct elf *elf, const char *name) { struct section *sec; - elf_hash_for_each_possible(section_name, sec, name_hash, str_hash(name)) { + elf_hash_for_each_possible(elf, section_name, sec, name_hash, str_hash(name)) { if (!strcmp(sec->name, name)) return sec; } @@ -154,7 +133,7 @@ static struct section *find_section_by_index(struct elf *elf, { struct section *sec; - elf_hash_for_each_possible(section, sec, hash, idx) { + elf_hash_for_each_possible(elf, section, sec, hash, idx) { if (sec->idx == idx) return sec; } @@ -166,7 +145,7 @@ static struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx) { struct symbol *sym; - elf_hash_for_each_possible(symbol, sym, hash, idx) { + elf_hash_for_each_possible(elf, symbol, sym, hash, idx) { if (sym->idx == idx) return sym; } @@ -285,7 +264,7 @@ struct symbol *find_symbol_by_name(const struct elf *elf, const char *name) { struct symbol *sym; - elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash(name)) { + elf_hash_for_each_possible(elf, symbol_name, sym, name_hash, str_hash(name)) { if (!strcmp(sym->name, name)) return sym; } @@ -300,7 +279,7 @@ static struct symbol *find_local_symbol_by_file_and_name(const struct elf *elf, { struct symbol *sym; - elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash_demangled(name)) { + elf_hash_for_each_possible(elf, symbol_name, sym, name_hash, str_hash_demangled(name)) { if (sym->bind == STB_LOCAL && sym->file == file && !strcmp(sym->name, name)) { return sym; @@ -314,7 +293,7 @@ struct symbol *find_global_symbol_by_name(const struct elf *elf, const char *nam { struct symbol *sym; - elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash_demangled(name)) { + elf_hash_for_each_possible(elf, symbol_name, sym, name_hash, str_hash_demangled(name)) { if (!strcmp(sym->name, name) && !is_local_sym(sym)) return sym; } @@ -322,19 +301,6 @@ struct symbol *find_global_symbol_by_name(const struct elf *elf, const char *nam return NULL; } -void iterate_global_symbol_by_demangled_name(const struct elf *elf, - const char *demangled_name, - void (*process)(struct symbol *sym, void *data), - void *data) -{ - struct symbol *sym; - - elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash(demangled_name)) { - if (!strcmp(sym->demangled_name, demangled_name) && !is_local_sym(sym)) - process(sym, data); - } -} - struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec, unsigned long offset, unsigned int len) { @@ -347,7 +313,7 @@ struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *se return NULL; for_offset_range(o, offset, offset + len) { - elf_hash_for_each_possible(reloc, reloc, hash, + elf_hash_for_each_possible(elf, reloc, reloc, hash, sec_offset_hash(rsec, o)) { if (reloc->sec != rsec) continue; diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h index 25573e5af76ef..b142984eb9b5a 100644 --- a/tools/objtool/include/objtool/elf.h +++ b/tools/objtool/include/objtool/elf.h @@ -21,6 +21,13 @@ #define SEC_NAME_LEN 1024 #define SYM_NAME_LEN 512 +static inline u32 str_hash(const char *str) +{ + return jhash(str, strlen(str), 0); +} + +u32 str_hash_demangled(const char *str); + #define bswap_if_needed(elf, val) __bswap_if_needed(&elf->ehdr, val) #ifdef LIBELF_USE_DEPRECATED @@ -130,6 +137,23 @@ struct elf { struct symbol *symbol_data; }; +#define __elf_table(elf, name) ((elf)->name##_hash) +#define __elf_bits(elf, name) ((elf)->name##_bits) + +#define __elf_table_entry(elf, name, key) \ + __elf_table(elf, name)[hash_min(key, __elf_bits(elf, name))] + +#define elf_list_entry(ptr, type, member) \ +({ \ + typeof(ptr) __ptr = (ptr); \ + __ptr ? container_of(__ptr, type, member) : NULL; \ +}) + +#define elf_hash_for_each_possible(elf, name, obj, member, key) \ + for (obj = elf_list_entry(__elf_table_entry(elf, name, key), typeof(*obj), member); \ + obj; \ + obj = elf_list_entry(obj->member.next, typeof(*(obj)), member)) + struct elf *elf_open_read(const char *name, int flags); struct elf *elf_create_file(GElf_Ehdr *ehdr, const char *name); @@ -186,9 +210,6 @@ struct symbol *find_func_by_offset(struct section *sec, unsigned long offset); struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset); struct symbol *find_symbol_by_name(const struct elf *elf, const char *name); struct symbol *find_global_symbol_by_name(const struct elf *elf, const char *name); -void iterate_global_symbol_by_demangled_name(const struct elf *elf, const char *demangled_name, - void (*process)(struct symbol *sym, void *data), - void *data); struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset); int find_symbol_hole_containing(const struct section *sec, unsigned long offset); struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, unsigned long offset); @@ -468,6 +489,11 @@ static inline void set_sym_next_reloc(struct reloc *reloc, struct reloc *next) #define for_each_sym_continue(elf, sym) \ list_for_each_entry_continue(sym, &elf->symbols, global_list) +#define for_each_sym_by_demangled_name(elf, name, sym) \ + elf_hash_for_each_possible(elf, symbol_name, sym, name_hash, \ + str_hash(name)) \ + if (strcmp(sym->demangled_name, name)) {} else + #define rsec_next_reloc(rsec, reloc) \ reloc_idx(reloc) < sec_num_entries(rsec) - 1 ? reloc + 1 : NULL diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c index ef038f1336667..d0b3d1eef0521 100644 --- a/tools/objtool/klp-diff.c +++ b/tools/objtool/klp-diff.c @@ -46,11 +46,6 @@ static const struct option klp_diff_options[] = { static DEFINE_HASHTABLE(exports, 15); -static inline u32 str_hash(const char *str) -{ - return jhash(str, strlen(str), 0); -} - static char *escape_str(const char *orig) { size_t len = 0; @@ -370,22 +365,6 @@ static bool dont_correlate(struct symbol *sym) strstarts(sym->name, "__initcall__"); } -struct process_demangled_name_data { - struct symbol *ret; - int count; -}; - -static void process_demangled_name(struct symbol *sym, void *d) -{ - struct process_demangled_name_data *data = d; - - if (sym->twin) - return; - - data->count++; - data->ret = sym; -} - /* * When there is no full name match, try match demangled_name. This would * match original foo.llvm.123 to patched foo.llvm.456. @@ -397,16 +376,23 @@ static void process_demangled_name(struct symbol *sym, void *d) static int find_global_symbol_by_demangled_name(struct elf *elf, struct symbol *sym, struct symbol **out_sym) { - struct process_demangled_name_data data = {}; + struct symbol *sym2, *result = NULL; + int count = 0; + + for_each_sym_by_demangled_name(elf, sym->demangled_name, sym2) { + if (is_local_sym(sym2) || sym2->twin) + continue; - iterate_global_symbol_by_demangled_name(elf, sym->demangled_name, - process_demangled_name, - &data); - if (data.count > 1) { - ERROR("Multiple (%d) correlation candidates for %s", data.count, sym->name); + count++; + result = sym2; + } + + if (count > 1) { + ERROR("Multiple (%d) correlation candidates for %s", count, sym->name); return -1; } - *out_sym = data.ret; + + *out_sym = result; return 0; } From 7c558e3d80daf9351573ace236bfa9b695fe20a3 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 21 Apr 2026 07:21:19 -0700 Subject: [PATCH 0811/2758] objtool: Fix reloc hash collision in find_reloc_by_dest_range() [ Upstream commit a375e327b63e0da29b82a92b569bfdf4628fa38a ] In find_reloc_by_dest_range(), hash collisions can cause a high-offset relocation to appear when probing a low-offset hash bucket. Only return early when the best match found so far genuinely belongs to the current bucket (its offset is within the bucket's stride range). Otherwise, continue scanning later buckets which may contain lower-offset matches. This ensures the first reloc in the range gets returned. Fixes: 74b873e49d92 ("objtool: Optimize find_rela_by_dest_range()") Acked-by: Peter Zijlstra (Intel) Acked-by: Song Liu Reviewed-by: Miroslav Benes Signed-off-by: Josh Poimboeuf Signed-off-by: Sasha Levin --- tools/objtool/elf.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index dc39132f71c14..58631d62011d6 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -301,8 +301,9 @@ struct symbol *find_global_symbol_by_name(const struct elf *elf, const char *nam return NULL; } +/* If there are multiple matches, return the first one in the range */ struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec, - unsigned long offset, unsigned int len) + unsigned long offset, unsigned int len) { struct reloc *reloc, *r = NULL; struct section *rsec; @@ -324,11 +325,11 @@ struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *se r = reloc; } } - if (r) + if (r && (reloc_offset(r) & OFFSET_STRIDE_MASK) == o) return r; } - return NULL; + return r; } struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, unsigned long offset) From 653421ada35c03085fd5066e1ad041ae6dad3fdd Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Thu, 2 Apr 2026 19:47:10 -0700 Subject: [PATCH 0812/2758] klp-build: Fix hang on out-of-date .config [ Upstream commit cc39ccce7d5bc623100f07dcda070cef1bf690f6 ] If .config is out of date with the kernel source, 'make syncconfig' hangs while waiting for user input on new config options. Detect the mismatch and return an error. Fixes: 6f93f7b06810 ("livepatch/klp-build: Fix inconsistent kernel version") Acked-by: Song Liu Signed-off-by: Josh Poimboeuf Signed-off-by: Sasha Levin --- scripts/livepatch/klp-build | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build index 0ad7e66313142..e19d93b78fcba 100755 --- a/scripts/livepatch/klp-build +++ b/scripts/livepatch/klp-build @@ -306,7 +306,12 @@ set_kernelversion() { stash_file "$file" - kernelrelease="$(cd "$SRC" && make syncconfig &>/dev/null && make -s kernelrelease)" + if [[ -n "$(make -s listnewconfig 2>/dev/null)" ]]; then + die ".config mismatch, check your .config or run 'make olddefconfig'" + fi + make syncconfig &>/dev/null || die "make syncconfig failed" + + kernelrelease="$(make -s kernelrelease)" [[ -z "$kernelrelease" ]] && die "failed to get kernel version" sed -i "2i echo $kernelrelease; exit 0" scripts/setlocalversion From 4165b2f6491681d2921a0a47b26cf3263e02b3d1 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Fri, 10 Apr 2026 21:49:56 -0700 Subject: [PATCH 0813/2758] klp-build: Fix checksum comparison for changed offsets [ Upstream commit ba77fe55781a2464f68b6c13b4b31d05abd2abcf ] The klp-build -f/--show-first-changed feature uses diff to compare checksum log lines between original and patched objects. However, diff compares entire lines, including the offset field. When a function is at a different section offset, the offset field differs even though the instruction checksum is identical, causing the wrong instruction to be printed. Only compare the checksum field when looking for the first changed instruction. Also print both the original and patched offsets when they differ. Fixes: 78be9facfb5e ("livepatch/klp-build: Add --show-first-changed option to show function divergence") Acked-by: Song Liu Signed-off-by: Josh Poimboeuf Signed-off-by: Sasha Levin --- scripts/livepatch/klp-build | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build index e19d93b78fcba..8f0ea56f26400 100755 --- a/scripts/livepatch/klp-build +++ b/scripts/livepatch/klp-build @@ -727,13 +727,29 @@ diff_checksums() { ) for func in ${funcs[$file]}; do - diff <( grep0 -E "^DEBUG: .*checksum: $func " "$orig_log" | sed "s|$ORIG_DIR/||") \ - <( grep0 -E "^DEBUG: .*checksum: $func " "$patched_log" | sed "s|$PATCHED_DIR/||") \ - | gawk '/^< DEBUG: / { - gsub(/:/, "") - printf "%s: %s: %s\n", $3, $5, $6 - exit - }' || true + local -a orig patched + paste <(grep0 -E "^DEBUG: .*checksum: $func " "$orig_log") \ + <(grep0 -E "^DEBUG: .*checksum: $func " "$patched_log") | + while IFS= read -r line; do + read -ra orig <<< "${line%%$'\t'*}" + read -ra patched <<< "${line#*$'\t'}" + + if [[ ${#patched[@]} -eq 0 ]]; then + printf "%s: %s: %s (removed)\n" "${orig[1]%:}" "${orig[3]}" "${orig[-2]}" + break + elif [[ ${#orig[@]} -eq 0 ]]; then + printf "%s: %s: %s (added)\n" "${patched[1]%:}" "${patched[3]}" "${patched[-2]}" + break + fi + + [[ "${orig[-1]}" == "${patched[-1]}" ]] && continue + + printf "%s: %s: %s" "${orig[1]%:}" "${orig[3]}" "${orig[-2]}" + [[ "${orig[-2]}" != "${patched[-2]}" ]] && \ + printf " (patched: %s)" "${patched[-2]}" + printf "\n" + break + done || true done done } From f71a8795990a516a18bb95b7ed3de4a19317a652 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Thu, 2 Apr 2026 19:08:39 -0700 Subject: [PATCH 0814/2758] klp-build: Fix patch cleanup on interrupt [ Upstream commit f3048888ea62ac1c573db91e74e0dcabe058e89f ] If a build error occurs and the user hits Ctrl-C while a large patch is being reverted during cleanup, the cleanup EXIT trap gets re-triggered and tries to re-revert the already partially-reverted patch. That causes 'patch -R' to repeatedly prompt "Unreversed patch detected! Ignore -R? [n]" for each already-reverted hunk, with no way to break out. Fix it by adding '--force' to the patch revert command in revert_patch(), which causes it to silently ignore already-reverted hunks. And ignore errors, as the cleanup is always best-effort. For similar reasons, add to APPLIED_PATCHES before (rather than after) applying the patch in apply_patch() so an interrupted apply will also get cleaned up. Fixes: d36a7343f4ba ("livepatch/klp-build: switch to GNU patch and recountdiff") Acked-by: Song Liu Signed-off-by: Josh Poimboeuf Signed-off-by: Sasha Levin --- scripts/livepatch/klp-build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build index 8f0ea56f26400..ab5ea13c87004 100755 --- a/scripts/livepatch/klp-build +++ b/scripts/livepatch/klp-build @@ -381,15 +381,15 @@ apply_patch() { warn "${patch} applied with fuzz" fi - patch -d "$SRC" -p1 --no-backup-if-mismatch -r /dev/null "${extra_args[@]}" --silent < "$patch" APPLIED_PATCHES+=("$patch") + patch -d "$SRC" -p1 --no-backup-if-mismatch -r /dev/null "${extra_args[@]}" --silent < "$patch" } revert_patch() { local patch="$1" local tmp=() - patch -d "$SRC" -p1 -R --silent --no-backup-if-mismatch -r /dev/null < "$patch" + patch -d "$SRC" -p1 -R --force --no-backup-if-mismatch -r /dev/null &> /dev/null < "$patch" || true for p in "${APPLIED_PATCHES[@]}"; do [[ "$p" == "$patch" ]] && continue From c38445d726ed9d5dc89cdb6cedafdad5affde56d Mon Sep 17 00:00:00 2001 From: Damian Muszynski Date: Tue, 7 Apr 2026 12:04:26 +0200 Subject: [PATCH 0815/2758] crypto: qat - fix heartbeat error injection [ Upstream commit 2e96024632b386c86860aa78639940fc96d6fcc9 ] The current implementation of the heartbeat error injection uses adf_disable_arb_thd() to stop a specific accelerator engine thread from processing requests. This does not reliably prevent the device from generating responses. Fix the error injection by disabling the device arbiter through exit_arb() instead. This properly simulates a device failure by stopping all arbitration, which results in missing responses for sent requests. Remove the now unused adf_disable_arb_thd() function and its declaration. Fixes: e2b67859ab6e ("crypto: qat - add heartbeat error simulator") Signed-off-by: Damian Muszynski Reviewed-by: Ahsan Atta Reviewed-by: Giovanni Cabiddu Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- .../intel/qat/qat_common/adf_common_drv.h | 1 - .../qat/qat_common/adf_heartbeat_inject.c | 6 ++--- .../intel/qat/qat_common/adf_hw_arbiter.c | 25 ------------------- 3 files changed, 2 insertions(+), 30 deletions(-) diff --git a/drivers/crypto/intel/qat/qat_common/adf_common_drv.h b/drivers/crypto/intel/qat/qat_common/adf_common_drv.h index db26e0b31170e..7d7d64fce6140 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_common_drv.h +++ b/drivers/crypto/intel/qat/qat_common/adf_common_drv.h @@ -90,7 +90,6 @@ void adf_exit_aer(void); int adf_init_arb(struct adf_accel_dev *accel_dev); void adf_exit_arb(struct adf_accel_dev *accel_dev); void adf_update_ring_arb(struct adf_etr_ring_data *ring); -int adf_disable_arb_thd(struct adf_accel_dev *accel_dev, u32 ae, u32 thr); int adf_dev_get(struct adf_accel_dev *accel_dev); void adf_dev_put(struct adf_accel_dev *accel_dev); diff --git a/drivers/crypto/intel/qat/qat_common/adf_heartbeat_inject.c b/drivers/crypto/intel/qat/qat_common/adf_heartbeat_inject.c index a3b474bdef6c8..023c5f1e78b07 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_heartbeat_inject.c +++ b/drivers/crypto/intel/qat/qat_common/adf_heartbeat_inject.c @@ -64,10 +64,8 @@ int adf_heartbeat_inject_error(struct adf_accel_dev *accel_dev) if (ret) return ret; - /* Configure worker threads to stop processing any packet */ - ret = adf_disable_arb_thd(accel_dev, rand_ae, rand_thr); - if (ret) - return ret; + /* Disable arbiter to stop processing any packet */ + hw_device->exit_arb(accel_dev); /* Change HB counters memory to simulate a hang */ adf_set_hb_counters_fail(accel_dev, rand_ae, rand_thr); diff --git a/drivers/crypto/intel/qat/qat_common/adf_hw_arbiter.c b/drivers/crypto/intel/qat/qat_common/adf_hw_arbiter.c index f93d9cca70cee..dd9a31c20bc9c 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_hw_arbiter.c +++ b/drivers/crypto/intel/qat/qat_common/adf_hw_arbiter.c @@ -99,28 +99,3 @@ void adf_exit_arb(struct adf_accel_dev *accel_dev) csr_ops->write_csr_ring_srv_arb_en(csr, i, 0); } EXPORT_SYMBOL_GPL(adf_exit_arb); - -int adf_disable_arb_thd(struct adf_accel_dev *accel_dev, u32 ae, u32 thr) -{ - void __iomem *csr = accel_dev->transport->banks[0].csr_addr; - struct adf_hw_device_data *hw_data = accel_dev->hw_device; - const u32 *thd_2_arb_cfg; - struct arb_info info; - u32 ae_thr_map; - - if (ADF_AE_STRAND0_THREAD == thr || ADF_AE_STRAND1_THREAD == thr) - thr = ADF_AE_ADMIN_THREAD; - - hw_data->get_arb_info(&info); - thd_2_arb_cfg = hw_data->get_arb_mapping(accel_dev); - if (!thd_2_arb_cfg) - return -EFAULT; - - /* Disable scheduling for this particular AE and thread */ - ae_thr_map = *(thd_2_arb_cfg + ae); - ae_thr_map &= ~(GENMASK(3, 0) << (thr * BIT(2))); - - WRITE_CSR_ARB_WT2SAM(csr, info.arb_offset, info.wt2sam_offset, ae, - ae_thr_map); - return 0; -} From 377c3ce5d83fb96e20e92282f788931c5d5850e8 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Thu, 26 Mar 2026 21:12:00 +0900 Subject: [PATCH 0816/2758] lib/vsprintf: Fix to check field_width and precision [ Upstream commit 71876dffab295b6e25d4209f0424da8fc5020e12 ] Check the field_width and presition correctly. Previously it depends on the bitfield conversion from int to check out-of-range error. However, commit 938df695e98d ("vsprintf: associate the format state with the format pointer") changed those fields to int. We need to check the out-of-range correctly without bitfield conversion. Fixes: 938df695e98d ("vsprintf: associate the format state with the format pointer") Reported-by: David Laight Closes: https://lore.kernel.org/all/20260318151250.40fef0ab@pumpkin/ Signed-off-by: Masami Hiramatsu (Google) Reviewed-by: Petr Mladek Link: https://patch.msgid.link/177452712047.197965.16376597502504928495.stgit@devnote2 Signed-off-by: Petr Mladek Signed-off-by: Sasha Levin --- lib/vsprintf.c | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 9f359b31c8d1c..3c76cc5c7f9ce 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -2640,6 +2640,18 @@ static unsigned char spec_flag(unsigned char c) return (c < sizeof(spec_flag_array)) ? spec_flag_array[c] : 0; } +static void set_field_width(struct printf_spec *spec, int width) +{ + spec->field_width = clamp(width, -FIELD_WIDTH_MAX, FIELD_WIDTH_MAX); + WARN_ONCE(spec->field_width != width, "field width %d out of range", width); +} + +static void set_precision(struct printf_spec *spec, int prec) +{ + spec->precision = clamp(prec, 0, PRECISION_MAX); + WARN_ONCE(spec->precision < prec, "precision %d too large", prec); +} + /* * Helper function to decode printf style format. * Each call decode a token from the format and return the @@ -2710,7 +2722,7 @@ struct fmt format_decode(struct fmt fmt, struct printf_spec *spec) spec->field_width = -1; if (isdigit(*fmt.str)) - spec->field_width = skip_atoi(&fmt.str); + set_field_width(spec, skip_atoi(&fmt.str)); else if (unlikely(*fmt.str == '*')) { /* it's the next argument */ fmt.state = FORMAT_STATE_WIDTH; @@ -2724,9 +2736,7 @@ struct fmt format_decode(struct fmt fmt, struct printf_spec *spec) if (unlikely(*fmt.str == '.')) { fmt.str++; if (isdigit(*fmt.str)) { - spec->precision = skip_atoi(&fmt.str); - if (spec->precision < 0) - spec->precision = 0; + set_precision(spec, skip_atoi(&fmt.str)); } else if (*fmt.str == '*') { /* it's the next argument */ fmt.state = FORMAT_STATE_PRECISION; @@ -2799,24 +2809,6 @@ struct fmt format_decode(struct fmt fmt, struct printf_spec *spec) return fmt; } -static void -set_field_width(struct printf_spec *spec, int width) -{ - spec->field_width = width; - if (WARN_ONCE(spec->field_width != width, "field width %d too large", width)) { - spec->field_width = clamp(width, -FIELD_WIDTH_MAX, FIELD_WIDTH_MAX); - } -} - -static void -set_precision(struct printf_spec *spec, int prec) -{ - spec->precision = prec; - if (WARN_ONCE(spec->precision != prec, "precision %d too large", prec)) { - spec->precision = clamp(prec, 0, PRECISION_MAX); - } -} - /* * Turn a 1/2/4-byte value into a 64-bit one for printing: truncate * as necessary and deal with signedness. From c0f6acb33d46c6d4add01bcfc7376cfeea1276bc Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Thu, 30 Apr 2026 19:52:12 +0100 Subject: [PATCH 0817/2758] dts: spacemit: set console baud rate on bpif3 [ Upstream commit 24c12ca43b12c104389d9a159207d0b25779d0af ] Because the default console's baud rate is not set, defconfig kernels do not have any serial output on this platform. Set the baud rate to 115200, matching what is used by U-Boot etc on this platform. Suggested-by: Vivian Wang Fixes: d60d57ab6b2a8 ("riscv: dts: spacemit: add Banana Pi BPI-F3 board device tree") Signed-off-by: Conor Dooley Reviewed-by: Yixun Lan Link: https://lore.kernel.org/r/20260430-reword-overstep-3be08b7eab25@spud Signed-off-by: Yixun Lan Signed-off-by: Sasha Levin --- arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts index 5790d927b93db..333ac8ebf3f51 100644 --- a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts +++ b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts @@ -19,7 +19,7 @@ }; chosen { - stdout-path = "serial0"; + stdout-path = "serial0:115200n8"; }; leds { From 8addeb8537800d3f5c0caa2da76e1678a5d6a617 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Mon, 4 May 2026 22:53:26 +0800 Subject: [PATCH 0818/2758] pinctrl: sunxi: fix regulator leak in sunxi_pmx_request() error path [ Upstream commit 334f7bcbdc79dc8e5b938ac3372453a5368221cf ] In the error path of sunxi_pmx_request(), the code calls regulator_put(s_reg->regulator) to release the regulator. However, s_reg->regulator is only assigned after a successful regulator_enable(). This causes a memory leak: the regulator obtained via regulator_get() is never properly released when regulator_enable() fails. Fixes: dc1445584177 ("pinctrl: sunxi: Fix and simplify pin bank regulator handling") Signed-off-by: Felix Gu Reviewed-by: Andre Przywara Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/sunxi/pinctrl-sunxi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index d3042e0c9712e..25489beeb3125 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -925,7 +925,7 @@ static int sunxi_pmx_request(struct pinctrl_dev *pctldev, unsigned offset) return 0; out: - regulator_put(s_reg->regulator); + regulator_put(reg); return ret; } From 1b7b645d7730f47bef4d072ee7fc599a34169216 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Thu, 19 Mar 2026 16:26:35 +0000 Subject: [PATCH 0819/2758] riscv: dts: microchip: gpio controllers on mpfs need 2 interrupt cells [ Upstream commit 79b731da340f1c9703e28665d49a865aa6956278 ] The platform has variable interrupt types for GPIO interrupts, in addition to having multiple lines per GPIO controller. Two interrupt cells are required. Fixes: 528a5b1f2556d ("riscv: dts: microchip: add new peripherals to icicle kit device tree") Signed-off-by: Conor Dooley Signed-off-by: Sasha Levin --- arch/riscv/boot/dts/microchip/mpfs.dtsi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/riscv/boot/dts/microchip/mpfs.dtsi b/arch/riscv/boot/dts/microchip/mpfs.dtsi index d535d4c72763c..85d8df6437f23 100644 --- a/arch/riscv/boot/dts/microchip/mpfs.dtsi +++ b/arch/riscv/boot/dts/microchip/mpfs.dtsi @@ -499,7 +499,7 @@ reg = <0x0 0x20120000 0x0 0x1000>; interrupt-parent = <&irqmux>; interrupt-controller; - #interrupt-cells = <1>; + #interrupt-cells = <2>; interrupts = <0>, <1>, <2>, <3>, <4>, <5>, <6>, <7>, <8>, <9>, <10>, <11>, @@ -516,7 +516,7 @@ reg = <0x0 0x20121000 0x0 0x1000>; interrupt-parent = <&irqmux>; interrupt-controller; - #interrupt-cells = <1>; + #interrupt-cells = <2>; interrupts = <32>, <33>, <34>, <35>, <36>, <37>, <38>, <39>, <40>, <41>, <42>, <43>, @@ -535,7 +535,7 @@ reg = <0x0 0x20122000 0x0 0x1000>; interrupt-parent = <&irqmux>; interrupt-controller; - #interrupt-cells = <1>; + #interrupt-cells = <2>; interrupts = <64>, <65>, <66>, <67>, <68>, <69>, <70>, <71>, <72>, <73>, <74>, <75>, From 7ce97a9994aadbf9a12b23e80474afb0bc14ebb5 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Thu, 5 Mar 2026 10:30:03 +0000 Subject: [PATCH 0820/2758] riscv: dts: microchip: remove gpio hogs from beaglev-fire [ Upstream commit 7ab032833b2f0dd3b51d71e6756ebe7efc61eed4 ] sd-det-hog should be cd-gpios, but when the mmc-spi-slot was added, the "cd-" prefix was omitted and the collision with the hog was not noticed. vio-enable-hog is just a regulator that can be modelled as such. Fixes: 1088d49b62648 ("riscv: dts: microchip: enable qspi adc/mmc-spi-slot on BeagleV Fire") Signed-off-by: Conor Dooley Signed-off-by: Sasha Levin --- .../boot/dts/microchip/mpfs-beaglev-fire.dts | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/arch/riscv/boot/dts/microchip/mpfs-beaglev-fire.dts b/arch/riscv/boot/dts/microchip/mpfs-beaglev-fire.dts index 0e1b0b8d394b9..6e9653827cfe2 100644 --- a/arch/riscv/boot/dts/microchip/mpfs-beaglev-fire.dts +++ b/arch/riscv/boot/dts/microchip/mpfs-beaglev-fire.dts @@ -77,6 +77,15 @@ regulator-max-microvolt = <1200000>; }; + regulator-1v8-syzygy { + compatible = "regulator-fixed"; + gpios = <&gpio2 30 GPIO_ACTIVE_HIGH>; + regulator-name = "syzygy_1v8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + enable-active-high; + }; }; &gpio0 { @@ -118,20 +127,6 @@ "P8_PIN27", "P8_PIN28", "P8_PIN29", "P8_PIN30", "M2_W_DISABLE1", "M2_W_DISABLE2", "VIO_ENABLE", "SD_DET"; status = "okay"; - - vio-enable-hog { - gpio-hog; - gpios = <30 30>; - output-high; - line-name = "VIO_ENABLE"; - }; - - sd-det-hog { - gpio-hog; - gpios = <31 31>; - input; - line-name = "SD_DET"; - }; }; &i2c0 { @@ -316,7 +311,7 @@ mmc@1 { compatible = "mmc-spi-slot"; reg = <1>; - gpios = <&gpio2 31 1>; + cd-gpios = <&gpio2 31 GPIO_ACTIVE_LOW>; voltage-ranges = <3300 3300>; spi-max-frequency = <5000000>; disable-wp; From 123b1729bb19b6f76c62baabb06d8298d8e33a1b Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 22 Apr 2026 23:07:23 +0200 Subject: [PATCH 0821/2758] dt-bindings: vendor-prefixes: Add Displaytech Ltd. [ Upstream commit 42112cff8cb78ff6120983ba71bd14d52ce9dccd ] Add "displaytech" vendor prefix for Displaytech Ltd. . Signed-off-by: Marek Vasut Acked-by: Krzysztof Kozlowski Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260422210806.80948-1-marex@nabladev.com Signed-off-by: Sasha Levin --- Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 28784d66ae7ba..11c55b5df0e4c 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -437,6 +437,8 @@ patternProperties: description: Diodes, Inc. "^dioo,.*": description: Dioo Microcircuit Co., Ltd + "^displaytech,.*": + description: Displaytech Ltd. "^djn,.*": description: Shenzhen DJN Optronics Technology Co., Ltd "^dlc,.*": From 4b9c05d21fdc0b31ff0255f24a6301b4daa7fba7 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Thu, 16 Apr 2026 13:10:54 +0000 Subject: [PATCH 0822/2758] drm/gpuvm: take refcount on DRM device [ Upstream commit c2d72717e0a9dd01c6a1bac61a1462a8e04bd179 ] Currently GPUVM relies on the owner implicitly holding a refcount to the drm device, and it does not implicitly take a refcount on the drm device. This design is error-prone, so take a refcount on the device. Suggested-by: Danilo Krummrich Signed-off-by: Alice Ryhl Fixes: 546ca4d35dcc ("drm/gpuvm: convert WARN() to drm_WARN() variants") Link: https://patch.msgid.link/20260416-gpuvm-drm-dev-get-v1-1-f3bc06571e73@google.com Signed-off-by: Danilo Krummrich Signed-off-by: Sasha Levin --- drivers/gpu/drm/drm_gpuvm.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_gpuvm.c b/drivers/gpu/drm/drm_gpuvm.c index f56719e9f4350..83020b6cf9ba1 100644 --- a/drivers/gpu/drm/drm_gpuvm.c +++ b/drivers/gpu/drm/drm_gpuvm.c @@ -25,6 +25,7 @@ * */ +#include #include #include @@ -1117,6 +1118,7 @@ drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name, gpuvm->drm = drm; gpuvm->r_obj = r_obj; + drm_dev_get(drm); drm_gem_object_get(r_obj); drm_gpuvm_warn_check_overflow(gpuvm, start_offset, range); @@ -1160,13 +1162,15 @@ static void drm_gpuvm_free(struct kref *kref) { struct drm_gpuvm *gpuvm = container_of(kref, struct drm_gpuvm, kref); + struct drm_device *drm = gpuvm->drm; drm_gpuvm_fini(gpuvm); - if (drm_WARN_ON(gpuvm->drm, !gpuvm->ops->vm_free)) + if (drm_WARN_ON(drm, !gpuvm->ops->vm_free)) return; gpuvm->ops->vm_free(gpuvm); + drm_dev_put(drm); } /** From 73dd28e41c6eeac2f984601e8f187bf6351ffe50 Mon Sep 17 00:00:00 2001 From: Peddolla Harshavardhan Reddy Date: Mon, 20 Apr 2026 14:38:44 +0530 Subject: [PATCH 0823/2758] wifi: cfg80211: restrict LMR feedback check to TB and non-TB ranging [ Upstream commit bef9d5e378f07ccdf967b929897de84a9931d1e3 ] The lmr_feedback field is only applicable to TB and non-TB ranging. Currently, pmsr_parse_ftm() enforces lmr_feedback for all RSTA requests, incorrectly rejecting valid EDCA-based RSTA requests. Fix this by limiting the lmr_feedback requirement to TB and non-TB ranging only. Fixes: 853800c746d3 ("wifi: nl80211/cfg80211: support operating as RSTA in PMSR FTM request") Co-developed-by: Kavita Kavita Signed-off-by: Kavita Kavita Signed-off-by: Peddolla Harshavardhan Reddy Link: https://patch.msgid.link/20260420090856.2152905-2-peddolla.reddy@oss.qualcomm.com Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/wireless/pmsr.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/wireless/pmsr.c b/net/wireless/pmsr.c index d6cd0de64d1f8..0ca93fd7d46f8 100644 --- a/net/wireless/pmsr.c +++ b/net/wireless/pmsr.c @@ -195,7 +195,9 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev, return -EOPNOTSUPP; } - if (out->ftm.rsta && !out->ftm.lmr_feedback) { + if (out->ftm.rsta && + (out->ftm.non_trigger_based || out->ftm.trigger_based) && + !out->ftm.lmr_feedback) { NL_SET_ERR_MSG_ATTR(info->extack, tb[NL80211_PMSR_FTM_REQ_ATTR_RSTA], "FTM: RSTA set without LMR feedback"); From be7449e58ab6303b4e7f8182abdc2318846b5fc0 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Tue, 5 May 2026 15:53:40 +0200 Subject: [PATCH 0824/2758] drm/panel: Clean up SOFEF00 config dependencies [ Upstream commit e87e2357292f80d10e1a43cb910454313a5cb448 ] As per the config name this Display IC features a DSI command-mode interface (or the command to switch to video mode is not known/documented) and does not use any of the video-mode helper utilities, hence should not select VIDEOMODE_HELPERS. In addition it uses devm_gpiod_get() and related functions from GPIOLIB. Fixes: 5933baa36e26 ("drm/panel/samsung-sofef00: Add panel for OnePlus 6/T devices") Reviewed-by: Neil Armstrong Reviewed-by: Casey Connolly Reviewed-by: David Heidelberg Signed-off-by: Marijn Suijten Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260505-panel-clean-up-kconfig-dep-v2-1-9cc31d6e6919@ixit.cz Signed-off-by: Sasha Levin --- drivers/gpu/drm/panel/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index d592f4f4b939a..28802f86a538f 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -982,10 +982,10 @@ config DRM_PANEL_SAMSUNG_S6E8FC0 config DRM_PANEL_SAMSUNG_SOFEF00 tristate "Samsung SOFEF00 DSI panel controller" + depends on GPIOLIB depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE - select VIDEOMODE_HELPERS help Say Y or M here if you want to enable support for the Samsung AMOLED panel SOFEF00 DDIC and connected panel. From ba4be84ab919f0292736a4cf1add35653508c95f Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Tue, 5 May 2026 15:53:41 +0200 Subject: [PATCH 0825/2758] drm/panel: Clean up S6E3FC2X01 config dependencies [ Upstream commit faf497d2f1ff15756981c6e65a9c3d8bf192a969 ] As per the config name this Display IC features a DSI command-mode interface (or the command to switch to video mode is not known/documented) and does not use any of the video-mode helper utilities, hence should not select VIDEOMODE_HELPERS. In addition it uses devm_gpiod_get() and related functions from GPIOLIB. Fixes: 88148c30ef26 ("drm/panel: Add Samsung S6E3FC2X01 DDIC with AMS641RW panel") Signed-off-by: David Heidelberg Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260505-panel-clean-up-kconfig-dep-v2-2-9cc31d6e6919@ixit.cz Signed-off-by: Sasha Levin --- drivers/gpu/drm/panel/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 28802f86a538f..56b4c464ec9b3 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -890,10 +890,10 @@ config DRM_PANEL_SAMSUNG_S6D7AA0 config DRM_PANEL_SAMSUNG_S6E3FC2X01 tristate "Samsung S6E3FC2X01 DSI panel controller" + depends on GPIOLIB depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE - select VIDEOMODE_HELPERS help Say Y or M here if you want to enable support for the Samsung S6E3FC2 DDIC and connected MIPI DSI panel. From b703c1d4686212a841284ed56fe26e52a529cb6c Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Tue, 5 May 2026 15:53:42 +0200 Subject: [PATCH 0826/2758] drm/panel: Clean up S6E3HA2 config dependencies and fill help text [ Upstream commit 632a8aa96d0717aa92cb63e3939d09b896223b4c ] As per the config name this Display IC features a DSI command-mode interface (or the command to switch to video mode is not known/documented) and does not use any of the video-mode helper utilities, hence should not select VIDEOMODE_HELPERS. In addition it uses devm_gpiod_get() and related functions from GPIOLIB. Fixes: 779679d3c164 ("drm/panel: Add support for S6E3HA8 panel driver") Signed-off-by: David Heidelberg Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260505-panel-clean-up-kconfig-dep-v2-3-9cc31d6e6919@ixit.cz Signed-off-by: Sasha Levin --- drivers/gpu/drm/panel/Kconfig | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 56b4c464ec9b3..b2153e04a59af 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -910,11 +910,18 @@ config DRM_PANEL_SAMSUNG_S6E3HA2 config DRM_PANEL_SAMSUNG_S6E3HA8 tristate "Samsung S6E3HA8 DSI video mode panel" + depends on GPIOLIB depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE select DRM_DISPLAY_DSC_HELPER - select VIDEOMODE_HELPERS + help + Say Y or M here if you want to enable support for the + Samsung S6E3HA8 DDIC and connected MIPI DSI panel. + Currently supported panels: + + Samsung AMB577PX01 (found in the Samsung S9 smartphone) + config DRM_PANEL_SAMSUNG_S6E63J0X03 tristate "Samsung S6E63J0X03 DSI command mode panel" From 5aab3e1bac86f7d563e114673a1d57302bbe05c9 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Tue, 7 Apr 2026 16:36:24 +0100 Subject: [PATCH 0827/2758] riscv: dts: microchip: update pic64gx gpio interrupts to better match the SoC [ Upstream commit 4ce4a46cdb2404bcd53ca58a18aeda673f37fbe9 ] Just like PolarFire SoC, the same issues with GPIO interrupts exist in the pic64gx, due to their similarity. Yoinking from the commit message for the same change for PolarFire SoC: There are 3 GPIO controllers on this SoC, of which: - GPIO controller 0 has 14 GPIOs - GPIO controller 1 has 24 GPIOs - GPIO controller 2 has 32 GPIOs All GPIOs are capable of generating interrupts, for a total of 70. There are only 41 IRQs available however, so a configurable mux is used to ensure all GPIOs can be used for interrupt generation. 38 of the 41 interrupts are in what the documentation calls "direct mode", as they provide an exclusive connection from a GPIO to the PLIC. The 3 remaining interrupts are used to mux the interrupts which do not have a exclusive connection, one for each GPIO controller. The mux was overlooked when the bindings and driver were originally written for the GPIO controllers on Polarfire SoC, and the interrupts property in the GPIO nodes used to try and convey what the mapping was. Instead, the mux should be a device in its own right, and the GPIO controllers should be connected to it, rather than to the PLIC. Now that a binding exists for that mux, fix the inaccurate description of the interrupt controller hierarchy. Signed-off-by: Conor Dooley Stable-dep-of: 48c7771b3c79 ("riscv: dts: microchip: fix pic64gx gpio interrupt-cells") Signed-off-by: Sasha Levin --- .../dts/microchip/pic64gx-curiosity-kit.dts | 47 ++++++++++++------- arch/riscv/boot/dts/microchip/pic64gx.dtsi | 32 +++++++++++-- 2 files changed, 58 insertions(+), 21 deletions(-) diff --git a/arch/riscv/boot/dts/microchip/pic64gx-curiosity-kit.dts b/arch/riscv/boot/dts/microchip/pic64gx-curiosity-kit.dts index 2f2ccd77af30a..ed3ff03f3b11b 100644 --- a/arch/riscv/boot/dts/microchip/pic64gx-curiosity-kit.dts +++ b/arch/riscv/boot/dts/microchip/pic64gx-curiosity-kit.dts @@ -63,10 +63,6 @@ }; &gpio0 { - interrupts = <13>, <14>, <15>, <16>, - <17>, <18>, <19>, <20>, - <21>, <22>, <23>, <24>, - <25>, <26>; status ="okay"; gpio-line-names = "", "", "", "", "", "", "", "", @@ -74,12 +70,6 @@ }; &gpio1 { - interrupts = <27>, <28>, <29>, <30>, - <31>, <32>, <33>, <34>, - <35>, <36>, <37>, <38>, - <39>, <40>, <41>, <42>, - <43>, <44>, <45>, <46>, - <47>, <48>, <49>, <50>; status ="okay"; gpio-line-names = "", "", "LED1", "LED2", "LED3", "LED4", "LED5", "LED6", @@ -88,14 +78,6 @@ }; &gpio2 { - interrupts = <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>; pinctrl-names = "default"; pinctrl-0 = <&mdio1_gpio>, <&spi0_gpio>, <&can0_gpio>, <&pcie_gpio>, <&qspi_gpio>, <&uart3_gpio>, <&uart4_gpio>, <&can1_gpio>; @@ -107,6 +89,35 @@ "DIP4", "USR_IO11", "", "", "SWITCH1", "", "", ""; }; +&irqmux { + interrupt-map = <0 &plic 13>, <1 &plic 14>, <2 &plic 15>, + <3 &plic 16>, <4 &plic 17>, <5 &plic 18>, + <6 &plic 19>, <7 &plic 20>, <8 &plic 21>, + <9 &plic 22>, <10 &plic 23>, <11 &plic 24>, + <12 &plic 25>, <13 &plic 26>, + + <32 &plic 27>, <33 &plic 28>, <34 &plic 29>, + <35 &plic 30>, <36 &plic 31>, <37 &plic 32>, + <38 &plic 33>, <39 &plic 34>, <40 &plic 35>, + <41 &plic 36>, <42 &plic 37>, <43 &plic 38>, + <44 &plic 39>, <45 &plic 40>, <46 &plic 41>, + <47 &plic 42>, <48 &plic 43>, <49 &plic 44>, + <50 &plic 45>, <51 &plic 46>, <52 &plic 47>, + <53 &plic 48>, <54 &plic 49>, <55 &plic 50>, + + <64 &plic 53>, <65 &plic 53>, <66 &plic 53>, + <67 &plic 53>, <68 &plic 53>, <69 &plic 53>, + <70 &plic 53>, <71 &plic 53>, <72 &plic 53>, + <73 &plic 53>, <74 &plic 53>, <75 &plic 53>, + <76 &plic 53>, <77 &plic 53>, <78 &plic 53>, + <79 &plic 53>, <80 &plic 53>, <81 &plic 53>, + <82 &plic 53>, <83 &plic 53>, <84 &plic 53>, + <85 &plic 53>, <86 &plic 53>, <87 &plic 53>, + <88 &plic 53>, <89 &plic 53>, <90 &plic 53>, + <91 &plic 53>, <92 &plic 53>, <93 &plic 53>, + <94 &plic 53>, <95 &plic 53>; +}; + &mac0 { status = "okay"; phy-mode = "sgmii"; diff --git a/arch/riscv/boot/dts/microchip/pic64gx.dtsi b/arch/riscv/boot/dts/microchip/pic64gx.dtsi index c164d7bc270a2..ed4623b29920e 100644 --- a/arch/riscv/boot/dts/microchip/pic64gx.dtsi +++ b/arch/riscv/boot/dts/microchip/pic64gx.dtsi @@ -295,6 +295,14 @@ #size-cells = <1>; #reset-cells = <1>; + irqmux: interrupt-controller@54 { + compatible = "microchip,pic64gx-irqmux", "microchip,mpfs-irqmux"; + reg = <0x54 0x4>; + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-map-mask = <0x7f>; + }; + iomux0: pinctrl@200 { compatible = "microchip,pic64gx-pinctrl-iomux0", "microchip,mpfs-pinctrl-iomux0"; @@ -484,9 +492,13 @@ gpio0: gpio@20120000 { compatible = "microchip,pic64gx-gpio", "microchip,mpfs-gpio"; reg = <0x0 0x20120000 0x0 0x1000>; - interrupt-parent = <&plic>; + interrupt-parent = <&irqmux>; interrupt-controller; #interrupt-cells = <1>; + interrupts = <0>, <1>, <2>, <3>, + <4>, <5>, <6>, <7>, + <8>, <9>, <10>, <11>, + <12>, <13>; clocks = <&clkcfg CLK_GPIO0>; gpio-controller; #gpio-cells = <2>; @@ -497,9 +509,15 @@ gpio1: gpio@20121000 { compatible = "microchip,pic64gx-gpio", "microchip,mpfs-gpio"; reg = <0x0 0x20121000 0x0 0x1000>; - interrupt-parent = <&plic>; + interrupt-parent = <&irqmux>; interrupt-controller; #interrupt-cells = <1>; + interrupts = <32>, <33>, <34>, <35>, + <36>, <37>, <38>, <39>, + <40>, <41>, <42>, <43>, + <44>, <45>, <46>, <47>, + <48>, <49>, <50>, <51>, + <52>, <53>, <54>, <55>; clocks = <&clkcfg CLK_GPIO1>; gpio-controller; #gpio-cells = <2>; @@ -510,9 +528,17 @@ gpio2: gpio@20122000 { compatible = "microchip,pic64gx-gpio", "microchip,mpfs-gpio"; reg = <0x0 0x20122000 0x0 0x1000>; - interrupt-parent = <&plic>; + interrupt-parent = <&irqmux>; interrupt-controller; #interrupt-cells = <1>; + interrupts = <64>, <65>, <66>, <67>, + <68>, <69>, <70>, <71>, + <72>, <73>, <74>, <75>, + <76>, <77>, <78>, <79>, + <80>, <81>, <82>, <83>, + <84>, <85>, <86>, <87>, + <88>, <89>, <90>, <91>, + <92>, <93>, <94>, <95>; clocks = <&clkcfg CLK_GPIO2>; gpio-controller; #gpio-cells = <2>; From 2c74b677a82f6e45509467804330bd0c9b87978e Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Tue, 5 May 2026 11:22:50 +0100 Subject: [PATCH 0828/2758] riscv: dts: microchip: fix pic64gx gpio interrupt-cells [ Upstream commit 48c7771b3c792b153872a2eff67e4cbcb77e4054 ] As the pic64gx devicetree files got added in parallel to the GPIO interrupt-cells being fixed for PolarFire SoC, they didn't get changed to the correct values. Fix them now. Fixes: 7219d20f9f421 ("riscv: dts: microchip: add pic64gx and its curiosity kit") Signed-off-by: Conor Dooley Signed-off-by: Sasha Levin --- arch/riscv/boot/dts/microchip/pic64gx.dtsi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/riscv/boot/dts/microchip/pic64gx.dtsi b/arch/riscv/boot/dts/microchip/pic64gx.dtsi index ed4623b29920e..e6a24cc5715fa 100644 --- a/arch/riscv/boot/dts/microchip/pic64gx.dtsi +++ b/arch/riscv/boot/dts/microchip/pic64gx.dtsi @@ -494,7 +494,7 @@ reg = <0x0 0x20120000 0x0 0x1000>; interrupt-parent = <&irqmux>; interrupt-controller; - #interrupt-cells = <1>; + #interrupt-cells = <2>; interrupts = <0>, <1>, <2>, <3>, <4>, <5>, <6>, <7>, <8>, <9>, <10>, <11>, @@ -511,7 +511,7 @@ reg = <0x0 0x20121000 0x0 0x1000>; interrupt-parent = <&irqmux>; interrupt-controller; - #interrupt-cells = <1>; + #interrupt-cells = <2>; interrupts = <32>, <33>, <34>, <35>, <36>, <37>, <38>, <39>, <40>, <41>, <42>, <43>, @@ -530,7 +530,7 @@ reg = <0x0 0x20122000 0x0 0x1000>; interrupt-parent = <&irqmux>; interrupt-controller; - #interrupt-cells = <1>; + #interrupt-cells = <2>; interrupts = <64>, <65>, <66>, <67>, <68>, <69>, <70>, <71>, <72>, <73>, <74>, <75>, From 284ca8b314d7ef7851d929592d3452a79ddea83a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Duje=20Mihanovi=C4=87?= Date: Thu, 9 Apr 2026 23:17:24 +0200 Subject: [PATCH 0829/2758] arm64: dts: marvell: samsung-coreprimevelte: Increase touchscreen voltage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 4da515eca1b8de56c9e8a17866626f99d9bccbc7 ] The old 1.9V setting was found to be insufficient in certain environments (in my case cold ones), causing the touchscreen to register ghost touches and mostly ignore actual touches. Increase the voltage to 2.5V to correct the issue. Fixes: ec958b5b18c8 ("arm64: dts: samsung,coreprimevelte: add touchscreen") Acked-by: Karel Balej Signed-off-by: Duje Mihanović Signed-off-by: Sasha Levin --- .../boot/dts/marvell/mmp/pxa1908-samsung-coreprimevelte.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/marvell/mmp/pxa1908-samsung-coreprimevelte.dts b/arch/arm64/boot/dts/marvell/mmp/pxa1908-samsung-coreprimevelte.dts index b2ce5edd9c6ac..bb0a993996241 100644 --- a/arch/arm64/boot/dts/marvell/mmp/pxa1908-samsung-coreprimevelte.dts +++ b/arch/arm64/boot/dts/marvell/mmp/pxa1908-samsung-coreprimevelte.dts @@ -460,7 +460,7 @@ regulators { ldo2: ldo2 { - regulator-min-microvolt = <1900000>; + regulator-min-microvolt = <2500000>; regulator-max-microvolt = <3100000>; }; From 2f9f619d9ce0bf44db335ab97ea34158f0090e69 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 28 Apr 2026 13:06:54 -0700 Subject: [PATCH 0830/2758] ARM: dts: rockchip: Add #{address,size}-cells to Chromium-based /firmware [ Upstream commit 98461edf564a35ee00a97a64f5463eaece586546 ] Chromium/Depthcharge bootloaders may dynamically add a few device nodes to a system's DTB under a /firmware node. A typical DT looks something like the following: / { firmware { ranges; coreboot { compatible = "coreboot"; reg = <...>; ...; }; }; }; Notably, the /firmware node has an empty 'ranges', but does not have address/size-cells. Commit 6e5773d52f4a ("of/address: Fix WARN when attempting translating non-translatable addresses") started requiring #address-cells for a device's parent if we want to use the reg resource in a device node. This leads to errors like the following: [ 7.763870] coreboot_table firmware:coreboot: probe with driver coreboot_table failed with error -22 Add appropriate #{address,size}-cells to work around the problem. Note that Google has also patched the Depthcharge bootloader source to add {address,size}-cells [1], but bootloader updates are typically delivered only via Google OS updates. Not all users install Google software updates, and even if they do, Google may not produce updated binaries for all/older devices. [1] https://lore.kernel.org/all/20241209092809.GA3246424@google.com/ https://crrev.com/c/6051580 ("coreboot: Insert #address-cells and #size-cells for firmware node") Closes: https://lore.kernel.org/all/aeKlYzTiL0OB1y3g@google.com/ Fixes: 6e5773d52f4a ("of/address: Fix WARN when attempting translating non-translatable addresses") Signed-off-by: Brian Norris Reviewed-by: Douglas Anderson [On RK288-based Chromebooks there is no real other way than to load the DTB together with its kernel when running a mainline kernel and as the whole line is EOL, there also won't be any updates to the bootloader that could fix that issue there.] Link: https://patch.msgid.link/20260428200712.2660635-3-briannorris@chromium.org Signed-off-by: Heiko Stuebner Signed-off-by: Sasha Levin --- arch/arm/boot/dts/rockchip/rk3288-veyron.dtsi | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/boot/dts/rockchip/rk3288-veyron.dtsi b/arch/arm/boot/dts/rockchip/rk3288-veyron.dtsi index 2d6cf08d00f90..ca8e8e7350781 100644 --- a/arch/arm/boot/dts/rockchip/rk3288-veyron.dtsi +++ b/arch/arm/boot/dts/rockchip/rk3288-veyron.dtsi @@ -18,6 +18,11 @@ stdout-path = "serial2:115200n8"; }; + firmware { + #address-cells = <1>; + #size-cells = <1>; + }; + /* * The default coreboot on veyron devices ignores memory@0 nodes * and would instead create another memory node. From 4952ddbe87735c38f717f78768071614dd4fd7d3 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 28 Apr 2026 13:06:53 -0700 Subject: [PATCH 0831/2758] arm64: dts: rockchip: Add #{address,size}-cells to Chromium-based /firmware [ Upstream commit 0b74f1a037672980c477bbe6b3848fb5341eb4f1 ] Chromium/Depthcharge bootloaders may dynamically add a few device nodes to a system's DTB under a /firmware node. A typical DT looks something like the following: ## From a RK3399 Gru/Kevin Chromebook: # find /sys/firmware/devicetree/base/firmware /sys/firmware/devicetree/base/firmware /sys/firmware/devicetree/base/firmware/coreboot /sys/firmware/devicetree/base/firmware/coreboot/ram-code /sys/firmware/devicetree/base/firmware/coreboot/compatible /sys/firmware/devicetree/base/firmware/coreboot/board-id /sys/firmware/devicetree/base/firmware/coreboot/reg /sys/firmware/devicetree/base/firmware/coreboot/name /sys/firmware/devicetree/base/firmware/chromeos /sys/firmware/devicetree/base/firmware/chromeos/readonly-firmware-version /sys/firmware/devicetree/base/firmware/chromeos/active-ec-firmware /sys/firmware/devicetree/base/firmware/chromeos/firmware-version /sys/firmware/devicetree/base/firmware/chromeos/nonvolatile-context-storage /sys/firmware/devicetree/base/firmware/chromeos/vboot-shared-data /sys/firmware/devicetree/base/firmware/chromeos/nonvolatile-context-size /sys/firmware/devicetree/base/firmware/chromeos/nonvolatile-context-offset /sys/firmware/devicetree/base/firmware/chromeos/hardware-id /sys/firmware/devicetree/base/firmware/chromeos/compatible /sys/firmware/devicetree/base/firmware/chromeos/firmware-type /sys/firmware/devicetree/base/firmware/chromeos/fmap-offset /sys/firmware/devicetree/base/firmware/chromeos/name /sys/firmware/devicetree/base/firmware/ranges /sys/firmware/devicetree/base/firmware/name The /firmware node has an empty 'ranges', but does not have address/size-cells. Commit 6e5773d52f4a ("of/address: Fix WARN when attempting translating non-translatable addresses") started requiring #address-cells for a device's parent if we want to use the reg resource in a device node. This leads to errors like the following: [ 7.763870] coreboot_table firmware:coreboot: probe with driver coreboot_table failed with error -22 Add appropriate #{address,size}-cells to work around the problem. Note that Google has also patched the Depthcharge bootloader source to add {address,size}-cells [1], but bootloader updates are typically delivered only via Google OS updates. Not all users install Google software updates, and even if they do, Google may not produce updated binaries for all/older devices. [1] https://lore.kernel.org/all/20241209092809.GA3246424@google.com/ https://crrev.com/c/6051580 ("coreboot: Insert #address-cells and #size-cells for firmware node") Closes: https://lore.kernel.org/all/aeKlYzTiL0OB1y3g@google.com/ Fixes: 6e5773d52f4a ("of/address: Fix WARN when attempting translating non-translatable addresses") Signed-off-by: Brian Norris Reviewed-by: Douglas Anderson Reviewed-by: Chen-Yu Tsai [On RK3399-based Chromebooks there is no real other way than to load the DTB together with its kernel when running a mainline kernel and as the whole line is EOL, there also won't be any updates to the bootloader that could fix that issue there.] Link: https://patch.msgid.link/20260428200712.2660635-2-briannorris@chromium.org Signed-off-by: Heiko Stuebner Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi index 7eca1da78cffa..2f9e39671efc0 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi @@ -18,6 +18,11 @@ stdout-path = "serial2:115200n8"; }; + firmware { + #address-cells = <2>; + #size-cells = <2>; + }; + /* * Power Tree * From 358fc3d52a6413e7cc54b6e312236566ac5c887b Mon Sep 17 00:00:00 2001 From: Weixin Guo <2298701336@qq.com> Date: Mon, 27 Apr 2026 21:58:04 +0800 Subject: [PATCH 0832/2758] arm64: dts: rockchip: fix rk809 interrupt pin on rk3566-roc-pc [ Upstream commit 460bac478c5fe69054ffc60d607bba03bcaf909b ] The RK809 PMIC interrupt pin on the Firefly ROC-RK3566-PC (Station M2) is physically connected to GPIO0_A3 (RK_PA3) according to the board's schematic. Currently, the PMIC node incorrectly specifies RK_PA7 for the interrupt, which prevents the PMIC from correctly signaling interrupts. (Note that the pinctrl node 'pmic_int' correctly configures RK_PA3). Fix this by updating the interrupts property to use RK_PA3. Fixes: 30ac9b4e25d8 ("arm64: dts: rockchip: add dts for Firefly Station M2 rk3566") Signed-off-by: Weixin Guo <2298701336@qq.com> Link: https://patch.msgid.link/tencent_5035EEE630C845B1B51DEA4284DE23DCCE06@qq.com Signed-off-by: Heiko Stuebner Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/rockchip/rk3566-roc-pc.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3566-roc-pc.dts b/arch/arm64/boot/dts/rockchip/rk3566-roc-pc.dts index 7e499064e0357..985770e3a5e2c 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-roc-pc.dts +++ b/arch/arm64/boot/dts/rockchip/rk3566-roc-pc.dts @@ -245,7 +245,7 @@ compatible = "rockchip,rk809"; reg = <0x20>; interrupt-parent = <&gpio0>; - interrupts = ; + interrupts = ; clock-output-names = "rk808-clkout1", "rk808-clkout2"; assigned-clocks = <&cru I2S1_MCLKOUT_TX>; assigned-clock-parents = <&cru CLK_I2S1_8CH_TX>; From 8b35eda12ac0a234b43d1f20e2cea409a7dba9e4 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 1 Apr 2026 14:40:56 +0800 Subject: [PATCH 0833/2758] arm64: dts: imx8x-colibri: Correct SODIMM PAD settings [ Upstream commit 0a03ee38a262cab0d9754e9947d565e089a9f7a5 ] SION is BIT(30), not BIT(26). Correct it. Fixes: 7ece3cbc8b1ef ("arm64: dts: colibri-imx8x: Add atmel pinctrl groups") Signed-off-by: Peng Fan Reviewed-by: Daniel Baluta Reviewed-by: Alexander Stein Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/freescale/imx8x-colibri.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx8x-colibri.dtsi b/arch/arm64/boot/dts/freescale/imx8x-colibri.dtsi index 47895ff8cb244..2415487d3a5de 100644 --- a/arch/arm64/boot/dts/freescale/imx8x-colibri.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8x-colibri.dtsi @@ -631,12 +631,12 @@ */ pinctrl_atmel_adap: atmeladaptergrp { fsl,pins = , /* SODIMM 30 */ - ; /* SODIMM 28 */ + ; /* SODIMM 28 */ }; /* Atmel MXT touchsceen + boards with built-in Capacitive Touch Connector */ pinctrl_atmel_conn: atmelconnectorgrp { - fsl,pins = , /* SODIMM 107 */ + fsl,pins = , /* SODIMM 107 */ ; /* SODIMM 106 */ }; From b691f7477254b62c89265611bb0be56af6ad03f2 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 6 Apr 2026 08:38:11 +0200 Subject: [PATCH 0834/2758] arm64: dts: imx8mn-vhip4-evalboard-v1: Correct interrupt flags [ Upstream commit 895e7756cdc1ba6467eb37eba31c1358243f6ad9 ] GPIO_ACTIVE_x flags are not correct in the context of interrupt flags. These are simple defines so they could be used in DTS but they will not have the same meaning: 1. GPIO_ACTIVE_HIGH = 0 => IRQ_TYPE_NONE 2. GPIO_ACTIVE_LOW = 1 => IRQ_TYPE_EDGE_RISING Correct the interrupt flags, assuming the author of the code wanted the same logical behavior behind the name "ACTIVE_xxx", this is: ACTIVE_LOW => IRQ_TYPE_LEVEL_LOW Fixes: 5eb7405db99b ("arm64: dts: imx8mn: Add ifm VHIP4 EvalBoard v1 and v2") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Marek Vasut Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/freescale/imx8mn-vhip4-evalboard-v1.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mn-vhip4-evalboard-v1.dts b/arch/arm64/boot/dts/freescale/imx8mn-vhip4-evalboard-v1.dts index 5f37065bf43f3..a8f7c226a61f8 100644 --- a/arch/arm64/boot/dts/freescale/imx8mn-vhip4-evalboard-v1.dts +++ b/arch/arm64/boot/dts/freescale/imx8mn-vhip4-evalboard-v1.dts @@ -112,7 +112,7 @@ &ifm_pmic { interrupt-parent = <&gpio2>; - interrupts = <0 GPIO_ACTIVE_LOW>; + interrupts = <0 IRQ_TYPE_LEVEL_LOW>; }; &iomuxc { From 1043f1e71ceac5cc4e56ab84cbcac476f99e0c19 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 6 Apr 2026 08:38:12 +0200 Subject: [PATCH 0835/2758] arm64: dts: imx8mn-vhip4-evalboard-v2: Correct interrupt flags [ Upstream commit 7263f1acdd33169e1558064ee9a98758921f3bec ] GPIO_ACTIVE_x flags are not correct in the context of interrupt flags. These are simple defines so they could be used in DTS but they will not have the same meaning: 1. GPIO_ACTIVE_HIGH = 0 => IRQ_TYPE_NONE 2. GPIO_ACTIVE_LOW = 1 => IRQ_TYPE_EDGE_RISING Correct the interrupt flags, assuming the author of the code wanted the same logical behavior behind the name "ACTIVE_xxx", this is: ACTIVE_LOW => IRQ_TYPE_LEVEL_LOW Fixes: 5eb7405db99b ("arm64: dts: imx8mn: Add ifm VHIP4 EvalBoard v1 and v2") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Marek Vasut Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/freescale/imx8mn-vhip4-evalboard-v2.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mn-vhip4-evalboard-v2.dts b/arch/arm64/boot/dts/freescale/imx8mn-vhip4-evalboard-v2.dts index 4dadfb7f78de2..43fd4d0041ef3 100644 --- a/arch/arm64/boot/dts/freescale/imx8mn-vhip4-evalboard-v2.dts +++ b/arch/arm64/boot/dts/freescale/imx8mn-vhip4-evalboard-v2.dts @@ -99,7 +99,7 @@ &ifm_pmic { interrupt-parent = <&gpio5>; - interrupts = <17 GPIO_ACTIVE_LOW>; + interrupts = <17 IRQ_TYPE_LEVEL_LOW>; }; &iomuxc { From 958681b7bbffed784b77d8aa2c414d14e5788961 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 6 Apr 2026 08:38:13 +0200 Subject: [PATCH 0836/2758] arm64: dts: imx8mp-ab2: Correct interrupt flags [ Upstream commit eb27f9b9a4b2965f8d497a3c565cfbc2473c184b ] GPIO_ACTIVE_x flags are not correct in the context of interrupt flags. These are simple defines so they could be used in DTS but they will not have the same meaning: 1. GPIO_ACTIVE_HIGH = 0 => IRQ_TYPE_NONE 2. GPIO_ACTIVE_LOW = 1 => IRQ_TYPE_EDGE_RISING Correct the interrupt flags, assuming the author of the code wanted the same logical behavior behind the name "ACTIVE_xxx", this is: ACTIVE_LOW => IRQ_TYPE_LEVEL_LOW Fixes: bf68c18150ef ("arm64: dts: imx8mp-ab2: add support for NXP i.MX8MP audio board (version 2)") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Shengjiu Wang Reviewed-by: Daniel Baluta Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/freescale/imx8mp-ab2.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mp-ab2.dts b/arch/arm64/boot/dts/freescale/imx8mp-ab2.dts index dbbc0df0e3d1c..443e4fd5b9bfc 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-ab2.dts +++ b/arch/arm64/boot/dts/freescale/imx8mp-ab2.dts @@ -281,7 +281,7 @@ compatible = "nxp,pca9450c"; reg = <0x25>; interrupt-parent = <&gpio1>; - interrupts = <3 GPIO_ACTIVE_LOW>; + interrupts = <3 IRQ_TYPE_LEVEL_LOW>; pinctrl-0 = <&pinctrl_pmic>; regulators { From 7177583a7acc0e354c8c86302e7fb1682ee94172 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Fri, 3 Apr 2026 17:57:01 +0800 Subject: [PATCH 0837/2758] Revert "arm64: dts: imx8mm-kontron: Add support for reading SD_VSEL signal" [ Upstream commit aa907ee010e396c0c0d31d60495ace6e531ceec9 ] This reverts commit 8472751c4d96b558d60d0f6aede6b24b64bcb3c9. The board uses SDHC VSELECT to automatically switch between 1.8v and 3.3v. It does not use GPIO to control the PMIC SD_VSEL signal. The original commit intends to read back SD_VSEL value from GPIO, but it is wrong. When MUX is configured as SDHC VSELECT, it is impossible to read back the value from GPIO controller. Setting SION could only enable the input path for the mux function. It could not redirect the input to GPIO. And value "0x40000d0" is wrong, SION is BIT30, not BIT26. Fixes: 8472751c4d96b ("arm64: dts: imx8mm-kontron: Add support for reading SD_VSEL signal") Signed-off-by: Peng Fan Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/freescale/imx8mm-kontron-bl.dts | 10 +++------- .../arm64/boot/dts/freescale/imx8mm-kontron-osm-s.dtsi | 7 +++---- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mm-kontron-bl.dts b/arch/arm64/boot/dts/freescale/imx8mm-kontron-bl.dts index e756fe5db56b6..dd59af0ebaae5 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-kontron-bl.dts +++ b/arch/arm64/boot/dts/freescale/imx8mm-kontron-bl.dts @@ -254,10 +254,6 @@ status = "okay"; }; -®_nvcc_sd { - sd-vsel-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>; -}; - &uart1 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_uart1>; @@ -466,7 +462,7 @@ MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d0 MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d0 MX8MM_IOMUXC_SD2_CD_B_GPIO2_IO12 0x19 - MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x40000d0 + MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0xd0 >; }; @@ -479,7 +475,7 @@ MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d4 MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d4 MX8MM_IOMUXC_SD2_CD_B_GPIO2_IO12 0x19 - MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x40000d0 + MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0xd0 >; }; @@ -492,7 +488,7 @@ MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d6 MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d6 MX8MM_IOMUXC_SD2_CD_B_GPIO2_IO12 0x19 - MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x40000d0 + MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0xd0 >; }; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-kontron-osm-s.dtsi b/arch/arm64/boot/dts/freescale/imx8mm-kontron-osm-s.dtsi index 96987910609f1..4fb13d8ecfd45 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-kontron-osm-s.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mm-kontron-osm-s.dtsi @@ -342,7 +342,6 @@ regulator-name = "NVCC_SD (LDO5)"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <3300000>; - sd-vsel-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>; }; }; }; @@ -795,7 +794,7 @@ MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d0 /* SDIO_A_D2 */ MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d0 /* SDIO_A_D3 */ MX8MM_IOMUXC_SD2_WP_USDHC2_WP 0x400000d6 /* SDIO_A_WP */ - MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x40000090 + MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x90 >; }; @@ -808,7 +807,7 @@ MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d4 /* SDIO_A_D2 */ MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d4 /* SDIO_A_D3 */ MX8MM_IOMUXC_SD2_WP_USDHC2_WP 0x400000d6 /* SDIO_A_WP */ - MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x40000090 + MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x90 >; }; @@ -821,7 +820,7 @@ MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d6 /* SDIO_A_D2 */ MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d6 /* SDIO_A_D3 */ MX8MM_IOMUXC_SD2_WP_USDHC2_WP 0x400000d6 /* SDIO_A_WP */ - MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x40000090 + MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x90 >; }; From 1d701d9f4cb3684015c8119cfb08de68dfd162a3 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Fri, 3 Apr 2026 17:57:02 +0800 Subject: [PATCH 0838/2758] Revert "arm64: dts: imx8mp-kontron: Add support for reading SD_VSEL signal" [ Upstream commit 22465a195af370ed6311be3adee479fb7c683685 ] This reverts commit 39e4189d9d63a0b6fc15458ce0136e99ecdfb1b8. The board uses SDHC VSELECT to automatically switch between 1.8v and 3.3v. It does not use GPIO to control the PMIC SD_VSEL signal. The original commit intends to read back SD_VSEL value from GPIO, but it is wrong. When MUX is configured as SDHC VSELECT, it is impossible to read back the value from GPIO controller. Setting SION could only enable the input path for the mux function. It could not redirect the input to GPIO. Fixes: 39e4189d9d63a ("arm64: dts: imx8mp-kontron: Add support for reading SD_VSEL signal") Signed-off-by: Peng Fan Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/freescale/imx8mp-kontron-osm-s.dtsi | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mp-kontron-osm-s.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-kontron-osm-s.dtsi index bc1a261bb000e..ea69c639b30b8 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-kontron-osm-s.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp-kontron-osm-s.dtsi @@ -311,7 +311,6 @@ regulator-name = "NVCC_SD (LDO5)"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <3300000>; - sd-vsel-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>; }; }; }; @@ -815,7 +814,7 @@ MX8MP_IOMUXC_SD2_DATA1__USDHC2_DATA1 0x1d0 /* SDIO_A_D1 */ MX8MP_IOMUXC_SD2_DATA2__USDHC2_DATA2 0x1d0 /* SDIO_A_D2 */ MX8MP_IOMUXC_SD2_DATA3__USDHC2_DATA3 0x1d0 /* SDIO_A_D3 */ - MX8MP_IOMUXC_GPIO1_IO04__USDHC2_VSELECT 0x400001d0 + MX8MP_IOMUXC_GPIO1_IO04__USDHC2_VSELECT 0x1d0 >; }; @@ -827,7 +826,7 @@ MX8MP_IOMUXC_SD2_DATA1__USDHC2_DATA1 0x1d4 /* SDIO_A_D1 */ MX8MP_IOMUXC_SD2_DATA2__USDHC2_DATA2 0x1d4 /* SDIO_A_D2 */ MX8MP_IOMUXC_SD2_DATA3__USDHC2_DATA3 0x1d4 /* SDIO_A_D3 */ - MX8MP_IOMUXC_GPIO1_IO04__USDHC2_VSELECT 0x400001d0 + MX8MP_IOMUXC_GPIO1_IO04__USDHC2_VSELECT 0x1d0 >; }; @@ -839,7 +838,7 @@ MX8MP_IOMUXC_SD2_DATA1__USDHC2_DATA1 0x1d6 /* SDIO_A_D1 */ MX8MP_IOMUXC_SD2_DATA2__USDHC2_DATA2 0x1d6 /* SDIO_A_D2 */ MX8MP_IOMUXC_SD2_DATA3__USDHC2_DATA3 0x1d6 /* SDIO_A_D3 */ - MX8MP_IOMUXC_GPIO1_IO04__USDHC2_VSELECT 0x400001d0 + MX8MP_IOMUXC_GPIO1_IO04__USDHC2_VSELECT 0x1d0 >; }; From ef44dac2a37f86eeae6b88ed10a6d60b35387dfd Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Sat, 2 May 2026 03:12:59 +0000 Subject: [PATCH 0839/2758] vxlan: Fix potential null-ptr-deref in vxlan_gro_prepare_receive(). [ Upstream commit 30a45c0bffdd62350261e2f2689fdba426a33578 ] udp_tunnel_sock_release() could set sk->sk_user_data to NULL while vxlan_gro_prepare_receive() is running. Let's check if rcu_dereference_sk_user_data() is NULL after skb_gro_remcsum_init(). Fixes: 5602c48cf875 ("vxlan: change vxlan to use UDP socket GRO") Signed-off-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260502031401.3557229-7-kuniyu@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/vxlan/vxlan_core.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index b5b1253ac08ba..89397447a14d4 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -657,14 +657,18 @@ static struct vxlanhdr *vxlan_gro_prepare_receive(struct sock *sk, struct sk_buff *skb, struct gro_remcsum *grc) { - struct sk_buff *p; struct vxlanhdr *vh, *vh2; unsigned int hlen, off_vx; - struct vxlan_sock *vs = rcu_dereference_sk_user_data(sk); + struct vxlan_sock *vs; + struct sk_buff *p; __be32 flags; skb_gro_remcsum_init(grc); + vs = rcu_dereference_sk_user_data(sk); + if (!vs) + return NULL; + off_vx = skb_gro_offset(skb); hlen = off_vx + sizeof(*vh); vh = skb_gro_header(skb, hlen, off_vx); From 92be339a841f05ecebb77cd13a8fae7d0762a176 Mon Sep 17 00:00:00 2001 From: Chris Brandt Date: Fri, 1 May 2026 09:21:35 -0400 Subject: [PATCH 0840/2758] drm: renesas: rz-du: mipi_dsi: Fix return path on error [ Upstream commit 79e1afecfe1afbfd06f63bf7bbe854a88155b7bd ] In case of error, we should unwind correctly. Switching to using dmam_ instead of dma_ and moving the code earlier fixes the issue. Fixes: 6f392f371650 ("drm: renesas: rz-du: Implement MIPI DSI host transfers") Suggested-by: Pavel Machek Signed-off-by: Chris Brandt Reviewed-by: Biju Das Link: https://patch.msgid.link/20260501132135.196701-1-chris.brandt@renesas.com Signed-off-by: Biju Das Signed-off-by: Sasha Levin --- drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c index a87a301326c7a..0d0cf10225bb5 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c @@ -1441,6 +1441,11 @@ static int rzg2l_mipi_dsi_probe(struct platform_device *pdev) return dev_err_probe(dsi->dev, PTR_ERR(dsi->prstc), "failed to get prst\n"); + dsi->dcs_buf_virt = dmam_alloc_coherent(dsi->dev, RZG2L_DCS_BUF_SIZE, + &dsi->dcs_buf_phys, GFP_KERNEL); + if (!dsi->dcs_buf_virt) + return -ENOMEM; + platform_set_drvdata(pdev, dsi); pm_runtime_enable(dsi->dev); @@ -1473,11 +1478,6 @@ static int rzg2l_mipi_dsi_probe(struct platform_device *pdev) if (ret < 0) goto err_pm_disable; - dsi->dcs_buf_virt = dma_alloc_coherent(dsi->host.dev, RZG2L_DCS_BUF_SIZE, - &dsi->dcs_buf_phys, GFP_KERNEL); - if (!dsi->dcs_buf_virt) - return -ENOMEM; - return 0; err_phy: @@ -1492,8 +1492,6 @@ static void rzg2l_mipi_dsi_remove(struct platform_device *pdev) { struct rzg2l_mipi_dsi *dsi = platform_get_drvdata(pdev); - dma_free_coherent(dsi->host.dev, RZG2L_DCS_BUF_SIZE, dsi->dcs_buf_virt, - dsi->dcs_buf_phys); mipi_dsi_host_unregister(&dsi->host); pm_runtime_disable(&pdev->dev); } From 65a48d57bd282082fbc9eee4344c032b081dbc9c Mon Sep 17 00:00:00 2001 From: Zhan Xusheng Date: Wed, 29 Apr 2026 16:06:35 +0800 Subject: [PATCH 0841/2758] alarmtimer: Remove stale return description from alarm_handle_timer() [ Upstream commit ed3b3c4976686b63b28e44f9805a88abc20ff18a ] alarm_handle_timer() was converted from returning enum alarmtimer_restart to void, but the kernel-doc "Return:" line was not removed. Remove the stale description. Fixes: 2634303f8773 ("alarmtimers: Remove return value from alarm functions") Signed-off-by: Zhan Xusheng Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260429080635.166790-1-zhanxusheng@xiaomi.com Signed-off-by: Sasha Levin --- kernel/time/alarmtimer.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c index 6e173d70d8259..fe9c42c03523c 100644 --- a/kernel/time/alarmtimer.c +++ b/kernel/time/alarmtimer.c @@ -512,8 +512,6 @@ static enum alarmtimer_type clock2alarm(clockid_t clockid) * @now: time at the timer expiration * * Posix timer callback for expired alarm timers. - * - * Return: whether the timer is to be restarted */ static void alarm_handle_timer(struct alarm *alarm, ktime_t now) { From bb75bd7d9ae7672034f73ce67a57e6ac89bb39e5 Mon Sep 17 00:00:00 2001 From: Di Shen Date: Mon, 27 Apr 2026 20:00:47 +0800 Subject: [PATCH 0842/2758] OPP: Fix race between OPP addition and lookup [ Upstream commit f5e1cc9a284bff2510981643a5bca4bc4c21b81a ] A race exists between dev_pm_opp_add_dynamic() and dev_pm_opp_find_freq_exact(): CPU0 (add) CPU1 (lookup) ------------------------------- ------------------------------ _opp_add() mutex_lock() list_add(&new_opp->node, head) mutex_unlock() _opp_table_find_key() mutex_lock() dev_pm_opp_get(opp) kref_get() mutex_unlock() kref_init(&new_opp->kref) dev_pm_opp_put() kref_put_mutex() The newly added OPP is inserted into the list before its kref is initialized. A concurrent lookup can find this OPP and increment its reference count while it is still uninitialized, leading to refcount corruption and a potential premature free. Fix this by initializing ->kref and ->opp_table before making the OPP visible via list_add(). This ensures any concurrent lookup observes a fully initialized object. Fixes: 7034764a1e4a (PM / OPP: Add 'struct kref' to struct dev_pm_opp) Co-developed-by: Ling Xu Signed-off-by: Ling Xu Signed-off-by: Di Shen [ Viresh: Updated commit log ] Signed-off-by: Viresh Kumar Signed-off-by: Sasha Levin --- drivers/opp/core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index da3f5eba43419..ab0b0a2f85a17 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -2088,11 +2088,10 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, return ret; list_add(&new_opp->node, head); + new_opp->opp_table = opp_table; + kref_init(&new_opp->kref); } - new_opp->opp_table = opp_table; - kref_init(&new_opp->kref); - opp_debug_create_one(new_opp, opp_table); if (!_opp_supported_by_regulators(new_opp, opp_table)) { From d0c7bc211499cc429b01688c0ed7ba21035c9d6f Mon Sep 17 00:00:00 2001 From: "Tycho Andersen (AMD)" Date: Wed, 8 Apr 2026 08:32:56 -0600 Subject: [PATCH 0843/2758] crypto: ccp - Reverse the cleanup order in psp_dev_destroy() [ Upstream commit 4a76a164ba1617f60d1c8a2fd754466c9d9e48e9 ] Before SNP x86 shutdown [1], all HV_FIXED pages were always leaked on module unload. Now pages can be reclaimed if they are freed before SNP shutdown. The SFS driver does sfs_dev_destroy() -> snp_free_hv_fixed_pages(), marking the command buffer as free. But this happens after sev_dev_destroy() in psp_dev_destroy(), so the pages are always leaked. Rearrange psp_dev_destroy() to destroy things in the reverse order from psp_init(), so that any dependencies can be unwound accordingly. This lets SFS free the page and the subsequent SNP shutdown release it. This was identified with use of Chris Mason's review-prompts: https://github.com/masoncl/review-prompts [1]: https://lore.kernel.org/all/20260324161301.1353976-1-tycho@kernel.org/ Fixes: 648dbccc03a0 ("crypto: ccp - Add AMD Seamless Firmware Servicing (SFS) driver") Reported-by: review-prompts Assisted-by: Claude:claude-4.6-opus Suggested-by: Tom Lendacky Signed-off-by: Tycho Andersen (AMD) Reviewed-by: Ashish Kalra Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/ccp/psp-dev.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/crypto/ccp/psp-dev.c b/drivers/crypto/ccp/psp-dev.c index 5c7f7e02a7d8a..b14ce51065d5d 100644 --- a/drivers/crypto/ccp/psp-dev.c +++ b/drivers/crypto/ccp/psp-dev.c @@ -316,15 +316,15 @@ void psp_dev_destroy(struct sp_device *sp) if (!psp) return; - sev_dev_destroy(psp); + dbc_dev_destroy(psp); - tee_dev_destroy(psp); + platform_access_dev_destroy(psp); sfs_dev_destroy(psp); - dbc_dev_destroy(psp); + tee_dev_destroy(psp); - platform_access_dev_destroy(psp); + sev_dev_destroy(psp); sp_free_psp_irq(sp, psp); From af7341616b742ad2c374a90998bd650a035f694d Mon Sep 17 00:00:00 2001 From: "Tycho Andersen (AMD)" Date: Wed, 8 Apr 2026 08:32:57 -0600 Subject: [PATCH 0844/2758] crypto: ccp - Fix snp_filter_reserved_mem_regions() off-by-one [ Upstream commit 1b864b6cb213bbd7b406e9b2e98c962077f300df ] Sashiko notes: > regarding the bounds check in snp_filter_reserved_mem_regions() > called via walk_iomem_res_desc(): does the check > if ((range_list->num_elements * 16 + 8) > PAGE_SIZE) > allow an off-by-one heap buffer overflow? > > If range_list->num_elements is 255, 255 * 16 + 8 = 4088, which is <= 4096. > Writing range->base (8 bytes) fills 4088-4095, but writing range->page_count > (4 bytes) would write to 4096-4099, overflowing the kzalloc-allocated > PAGE_SIZE buffer. Fix this by accounting for the entry about to be written to, in addition to the entries that are already allocated. Fixes: 1ca5614b84ee ("crypto: ccp: Add support to initialize the AMD-SP for SEV-SNP") Reported-by: Sashiko Assisted-by: Gemini:gemini-3.1-pro-preview Link: https://sashiko.dev/#/patchset/20260324161301.1353976-1-tycho%40kernel.org Signed-off-by: Tycho Andersen (AMD) Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/ccp/sev-dev.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index 7c4dd57fabb91..b0290f8491f59 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -1328,10 +1328,11 @@ static int snp_filter_reserved_mem_regions(struct resource *rs, void *arg) size_t size; /* - * Ensure the list of HV_FIXED pages that will be passed to firmware - * do not exceed the page-sized argument buffer. + * Ensure the list of HV_FIXED pages passed to the firmware including + * the one about to be written to do not exceed the page-sized argument + * buffer. */ - if ((range_list->num_elements * sizeof(struct sev_data_range) + + if (((range_list->num_elements + 1) * sizeof(struct sev_data_range) + sizeof(struct sev_data_range_list)) > PAGE_SIZE) return -E2BIG; From 17e1aae19a06d9f6da4b46d54fa2aeab77ec0c69 Mon Sep 17 00:00:00 2001 From: "Tycho Andersen (AMD)" Date: Wed, 8 Apr 2026 08:32:58 -0600 Subject: [PATCH 0845/2758] crypto: ccp - Check for page allocation failure correctly in TIO [ Upstream commit a8d5370eef00eca132a292b1901c9914c817e385 ] Sashiko notes: > if __snp_alloc_firmware_pages() returns NULL under memory pressure, is it > safe to pass it directly to page_address()? > > On architectures without HASHED_PAGE_VIRTUAL, page_address(NULL) might > compute a deterministic but invalid, non-zero virtual address. The > subsequent if (tio_status) check would then evaluate to true, and > sev_tsm_init_locked() would dereference the invalid pointer. Indeed, page_address(NULL) will return non-NULL garbage here. Fix this by checking the page allocation itself for NULL, not the resulting virtual address. Fixes: 4be423572da1 ("crypto/ccp: Implement SEV-TIO PCIe IDE (phase1)") Reported-by: Sashiko Assisted-by: Gemini:gemini-3.1-pro-preview Link: https://sashiko.dev/#/patchset/20260324161301.1353976-1-tycho%40kernel.org Signed-off-by: Tycho Andersen (AMD) Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/ccp/sev-dev.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index b0290f8491f59..3991d4b355e60 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -1488,6 +1488,8 @@ static int __sev_snp_init_locked(int *error, unsigned int max_snp_asid) &snp_panic_notifier); if (data.tio_en) { + struct page *page; + /* * This executes with the sev_cmd_mutex held so down the stack * snp_reclaim_pages(locked=false) might be needed (which is extremely @@ -1495,12 +1497,14 @@ static int __sev_snp_init_locked(int *error, unsigned int max_snp_asid) * Instead of exporting __snp_alloc_firmware_pages(), allocate a page * for this one call here. */ - void *tio_status = page_address(__snp_alloc_firmware_pages( - GFP_KERNEL_ACCOUNT | __GFP_ZERO, 0, true)); + page = __snp_alloc_firmware_pages(GFP_KERNEL_ACCOUNT | __GFP_ZERO, + 0, true); + if (page) { + void *tio_status = page_address(page); - if (tio_status) { sev_tsm_init_locked(sev, tio_status); - __snp_free_firmware_pages(virt_to_page(tio_status), 0, true); + + __snp_free_firmware_pages(page, 0, true); } } From 9aeea4317696dc34304a58248d7f4d5912bec4dd Mon Sep 17 00:00:00 2001 From: "Tycho Andersen (AMD)" Date: Wed, 8 Apr 2026 08:32:59 -0600 Subject: [PATCH 0846/2758] crypto: ccp - Initialize data during __sev_snp_init_locked() [ Upstream commit fed613c1230277105bb512bce6e1fda8f316d178 ] Sashiko notes: > is the stack variable data left uninitialized when taking the else branch? > Since data.tio_en is later evaluated unconditionally, could stack garbage > cause it to evaluate to true, leading to erroneous attempts to allocate > pages and initialize SEV-TIO on unsupported hardware? If the firmware is too old to support SEV_INIT_EX, data is left uninitialized but used in the debug logging about whether TIO is enabled or not. Fixes: 4be423572da1 ("crypto/ccp: Implement SEV-TIO PCIe IDE (phase1)") Reported-by: Sashiko Assisted-by: Gemini:gemini-3.1-pro-preview Link: https://sashiko.dev/#/patchset/20260324161301.1353976-1-tycho%40kernel.org Signed-off-by: Tycho Andersen (AMD) Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/ccp/sev-dev.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index 3991d4b355e60..14df519ae7eea 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -1356,7 +1356,7 @@ static int __sev_snp_init_locked(int *error, unsigned int max_snp_asid) { struct sev_data_range_list *snp_range_list __free(kfree) = NULL; struct psp_device *psp = psp_master; - struct sev_data_snp_init_ex data; + struct sev_data_snp_init_ex data = {}; struct sev_device *sev; void *arg = &data; int cmd, rc = 0; @@ -1420,8 +1420,6 @@ static int __sev_snp_init_locked(int *error, unsigned int max_snp_asid) */ snp_add_hv_fixed_pages(sev, snp_range_list); - memset(&data, 0, sizeof(data)); - if (max_snp_asid) { data.ciphertext_hiding_en = 1; data.max_snp_asid = max_snp_asid; From 938ae1fb4a8f5db43b62b6206d409efbf317f0d9 Mon Sep 17 00:00:00 2001 From: Lothar Rubusch Date: Sun, 26 Apr 2026 21:29:47 +0000 Subject: [PATCH 0847/2758] crypto: atmel-sha204a - fix blocking and non-blocking rng logic [ Upstream commit 319400fc5ee15db5793aa45f854968141326effc ] The blocking and non-blocking paths were failing to provide valid entropy due to improper buffer management. Reading the buffer starting from byte 1, only fetch the 32 bytes of random data from the return message. Tested on an Atmel SHA204A device. Before (here for blocking), tests showed repeatedly reading reduced bytes. $ head -c 32 /dev/hwrng | hexdump -C 00000000 02 28 85 b3 47 40 f2 ee 00 00 00 00 00 00 00 00 |.(..G@..........| 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000020 After, the result will be similar to the following: $ head -c 32 /dev/hwrng | hexdump -C 00000000 5a fc 3f 13 14 68 fe 06 68 0a bd 04 83 6e 09 69 |Z.?..h..h....n.i| 00000010 75 ff cf 87 10 84 3b c9 c1 df ae eb 45 53 4c c3 |u.....;.....ESL.| 00000020 Fixes: da001fb651b0 ("crypto: atmel-i2c - add support for SHA204A random number generator") Suggested-by: Ard Biesheuvel Signed-off-by: Lothar Rubusch Tested-by: Thorsten Blum Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/atmel-sha204a.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/crypto/atmel-sha204a.c b/drivers/crypto/atmel-sha204a.c index 987eadae2007a..aa1db7ac2bb2a 100644 --- a/drivers/crypto/atmel-sha204a.c +++ b/drivers/crypto/atmel-sha204a.c @@ -54,8 +54,8 @@ static int atmel_sha204a_rng_read_nonblocking(struct hwrng *rng, void *data, if (rng->priv) { work_data = (struct atmel_i2c_work_data *)rng->priv; - max = min(sizeof(work_data->cmd.data), max); - memcpy(data, &work_data->cmd.data, max); + max = min(RANDOM_RSP_SIZE - CMD_OVERHEAD_SIZE, max); + memcpy(data, &work_data->cmd.data[RSP_DATA_IDX], max); rng->priv = 0; } else { work_data = kmalloc_obj(*work_data, GFP_ATOMIC); @@ -93,8 +93,8 @@ static int atmel_sha204a_rng_read(struct hwrng *rng, void *data, size_t max, if (ret) return ret; - max = min(sizeof(cmd.data), max); - memcpy(data, cmd.data, max); + max = min(RANDOM_RSP_SIZE - CMD_OVERHEAD_SIZE, max); + memcpy(data, &cmd.data[RSP_DATA_IDX], max); return max; } From b0fa9c1d6f6e4d2fe0007ddd7e8fd61a6d6aa9fd Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sat, 2 May 2026 21:09:04 +0200 Subject: [PATCH 0848/2758] crypto: ecrdsa - fix unknown OID check in ecrdsa_param_curve [ Upstream commit 2d7b2cfc59998baf5e8622a24dc28f69a5212e06 ] The ->curve_oid check in ecrdsa_param_curve() rejects the valid enum value 0 (OID_id_dsa_with_sha1), but look_up_OID() returns OID__NR on lookup failure. Compare ->curve_oid with OID__NR instead to ensure that only unknown OIDs return -EINVAL. Fixes: 0d7a78643f69 ("crypto: ecrdsa - add EC-RDSA (GOST 34.10) algorithm") Signed-off-by: Thorsten Blum Reviewed-by: Lukas Wunner Reviewed-by: Vitaly Chikunov Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- crypto/ecrdsa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/ecrdsa.c b/crypto/ecrdsa.c index 2c0602f0cd406..0cd7eb3676041 100644 --- a/crypto/ecrdsa.c +++ b/crypto/ecrdsa.c @@ -145,7 +145,7 @@ int ecrdsa_param_curve(void *context, size_t hdrlen, unsigned char tag, struct ecrdsa_ctx *ctx = context; ctx->curve_oid = look_up_OID(value, vlen); - if (!ctx->curve_oid) + if (ctx->curve_oid == OID__NR) return -EINVAL; ctx->curve = get_curve_by_oid(ctx->curve_oid); return 0; From 803591785d33cf13b6f73ce2796e8b9e6d5e6526 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Thu, 30 Apr 2026 10:36:34 -0700 Subject: [PATCH 0849/2758] crypto: asymmetric_keys - fix OOB read in pefile_digest_pe_contents [ Upstream commit f7dd32c5179d7755de18e21d5674b08f9e5cb180 ] pefile_digest_pe_contents() computes the trailing-data hash length as pelen - (hashed_bytes + certs_size). A crafted PE can make the addition exceed pelen, causing the unsigned subtraction to underflow to ~4 GiB. This is passed to crypto_shash_update() which reads out of bounds and panics on unmapped vmalloc guard pages. BUG: unable to handle page fault for address: ffffc900038d8000 Oops: Oops: 0000 [#1] SMP KASAN NOPTI RIP: 0010:sha256_blocks_generic (lib/crypto/sha256.c:152) Call Trace: __sha256_update (lib/crypto/sha256.c:208) crypto_sha256_update (crypto/sha256.c:142) verify_pefile_signature (crypto/asymmetric_keys/verify_pefile.c:436) kexec_kernel_verify_pe_sig (kernel/kexec_file.c:151) __do_sys_kexec_file_load (kernel/kexec_file.c:406) 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 Validate that the addition does not overflow and the result does not exceed pelen before the subtraction. Return -ELIBBAD on failure. Fixes: af316fc442ef ("pefile: Digest the PE binary and compare to the PKCS#7 data") Reported-by: Xiang Mei Signed-off-by: Weiming Shi Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- crypto/asymmetric_keys/verify_pefile.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c index 1f3b227ba7f22..cec99db14129a 100644 --- a/crypto/asymmetric_keys/verify_pefile.c +++ b/crypto/asymmetric_keys/verify_pefile.c @@ -305,6 +305,8 @@ static int pefile_digest_pe_contents(const void *pebuf, unsigned int pelen, if (pelen > hashed_bytes) { tmp = hashed_bytes + ctx->certs_size; + if (tmp <= hashed_bytes || pelen < tmp) + return -ELIBBAD; ret = crypto_shash_update(desc, pebuf + hashed_bytes, pelen - tmp); From d10c26bd628ae62f821818ab86433f3233e19a9c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 12 Apr 2026 19:12:48 +0200 Subject: [PATCH 0850/2758] ARM: multi_v7_defconfig: Correct QCOM_RPMH and QCOM_RPMHPD [ Upstream commit 02802b40c31d2f2cfc94716189cfaf610930c589 ] QCOM_RPMH and QCOM_RPMHPD can be build only as modules when QCOM_COMMAND_DB is module itself. Fixes: 1c25ca9bb5c5 ("ARM: multi_v7_defconfig: enable more Qualcomm drivers") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Linus Walleij Signed-off-by: Arnd Bergmann Signed-off-by: Sasha Levin --- arch/arm/configs/multi_v7_defconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index bcc9aabc12028..aad12026b20d2 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -1155,7 +1155,7 @@ CONFIG_QCOM_COMMAND_DB=m CONFIG_QCOM_GSBI=y CONFIG_QCOM_OCMEM=m CONFIG_QCOM_RMTFS_MEM=m -CONFIG_QCOM_RPMH=y +CONFIG_QCOM_RPMH=m CONFIG_QCOM_SMEM=y CONFIG_QCOM_SMD_RPM=y CONFIG_QCOM_SMP2P=y @@ -1170,7 +1170,7 @@ CONFIG_KEYSTONE_NAVIGATOR_DMA=y CONFIG_TI_PRUSS=m CONFIG_RASPBERRYPI_POWER=y CONFIG_QCOM_CPR=y -CONFIG_QCOM_RPMHPD=y +CONFIG_QCOM_RPMHPD=m CONFIG_QCOM_RPMPD=y CONFIG_ROCKCHIP_PM_DOMAINS=y CONFIG_TI_SCI_PM_DOMAINS=y From 2531c273f2e0a3010f59a8da0c14347328ec0012 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Wed, 6 May 2026 21:02:07 -0700 Subject: [PATCH 0851/2758] accel/amdxdna: Fix clflush buffer size [ Upstream commit ada61841caede131cc626f6cd28a60904296f248 ] The firmware is told the buffer is req.buf_size bytes. It may read/write the entire region. If the CPU only flushes a subset, the remaining cache lines could contain stale data, causing the device to see garbage. Fixes: 6e87001fe19f ("accel/amdxdna: Adjust size for copy_to_user()") Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260507040207.178111-1-lizhi.hou@amd.com Signed-off-by: Sasha Levin --- drivers/accel/amdxdna/aie2_message.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/accel/amdxdna/aie2_message.c b/drivers/accel/amdxdna/aie2_message.c index 0a619e55902b5..3028968066064 100644 --- a/drivers/accel/amdxdna/aie2_message.c +++ b/drivers/accel/amdxdna/aie2_message.c @@ -390,7 +390,7 @@ int aie2_query_status(struct amdxdna_dev_hdl *ndev, char __user *buf, req.num_cols = hweight32(aie_bitmap); req.aie_bitmap = aie_bitmap; - drm_clflush_virt_range(buff_addr, size); /* device can access */ + drm_clflush_virt_range(buff_addr, req.dump_buff_size); /* device can access */ ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); if (ret) { XDNA_ERR(xdna, "Error during NPU query, status %d", ret); @@ -442,7 +442,7 @@ int aie2_query_telemetry(struct amdxdna_dev_hdl *ndev, req.buf_size = buf_sz; req.type = header->type; - drm_clflush_virt_range(addr, size); /* device can access */ + drm_clflush_virt_range(addr, req.buf_size); /* device can access */ ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); if (ret) { XDNA_ERR(xdna, "Query telemetry failed, status %d", ret); @@ -1186,7 +1186,7 @@ int aie2_query_app_health(struct amdxdna_dev_hdl *ndev, u32 context_id, req.context_id = context_id; req.buf_size = buf_size; - drm_clflush_virt_range(buf, sizeof(*report)); + drm_clflush_virt_range(buf, req.buf_size); ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg); if (ret) { XDNA_ERR(xdna, "Get app health failed, ret %d status 0x%x", ret, resp.status); From 4d45250b1d22960f86d83245be188b16e456218b Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 27 Apr 2026 11:59:34 -0400 Subject: [PATCH 0852/2758] dlm: fix add msg handle in send_queue ordered [ Upstream commit d2248cb70c070f8f04762872772e155b59016f17 ] In a benchmark scenario triggering a lot of requests that triggers a lot of DLM messages on the network it can be that the mh->seq is not ordered according the oldest seq number. This ordering is required by dlm_receive_ack as "before(mh->seq, seq)" will stop to check for older sequence numbers that are ordered in the tail of "node->send_queue". The side effects of not having it correct ordered regarding "before(mh->seq, seq)" are refcounting issues and use-after free. I only was able to reproduce this issue in a experimental DLM branch and a user space DLM benchmark that uses io_uring. After changing this I don't experienced any refcounting with the sending buffer issues anymore. Fixes: 489d8e559c659 ("fs: dlm: add reliable connection if reconnect") Signed-off-by: Alexander Aring Signed-off-by: David Teigland Signed-off-by: Sasha Levin --- fs/dlm/midcomms.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/dlm/midcomms.c b/fs/dlm/midcomms.c index d54bdd8fc4f2e..64826a9b79a55 100644 --- a/fs/dlm/midcomms.c +++ b/fs/dlm/midcomms.c @@ -968,10 +968,10 @@ static void midcomms_new_msg_cb(void *data) atomic_inc(&mh->node->send_queue_cnt); spin_lock_bh(&mh->node->send_queue_lock); + /* need to be locked with list_add_tail_rcu() because list is ordered */ + mh->seq = atomic_fetch_inc(&mh->node->seq_send); list_add_tail_rcu(&mh->list, &mh->node->send_queue); spin_unlock_bh(&mh->node->send_queue_lock); - - mh->seq = atomic_fetch_inc(&mh->node->seq_send); } static struct dlm_msg *dlm_midcomms_get_msg_3_2(struct dlm_mhandle *mh, int nodeid, From 6e724f0e42cc87b0d8a9a4119a33e57f2d800f80 Mon Sep 17 00:00:00 2001 From: Shuangpeng Bai Date: Thu, 7 May 2026 11:50:21 -0400 Subject: [PATCH 0853/2758] nilfs2: fix backing_dev_info reference leak [ Upstream commit 665f192a2a11384cb7dc1be5f87d16438522a4ed ] setup_bdev_super() already initializes sb->s_bdev and takes a reference on the block device backing_dev_info when assigning sb->s_bdi. nilfs_fill_super() takes another reference to the same backing_dev_info and stores it in sb->s_bdi again. The extra reference is not paired with a matching bdi_put(), since generic_shutdown_super() releases sb->s_bdi only once. Drop the redundant bdi_get() in nilfs_fill_super(). The single reference taken by setup_bdev_super() is enough and is released during superblock shutdown. Fixes: c1e012ea9e83 ("nilfs2: use setup_bdev_super to de-duplicate the mount code") Signed-off-by: Shuangpeng Bai Acked-by: Ryusuke Konishi Reviewed-by: Viacheslav Dubeyko Signed-off-by: Viacheslav Dubeyko Signed-off-by: Sasha Levin --- fs/nilfs2/super.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 7aa5ef8606cdd..893a504cb80c8 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -1070,8 +1070,6 @@ nilfs_fill_super(struct super_block *sb, struct fs_context *fc) sb->s_time_gran = 1; sb->s_max_links = NILFS_LINK_MAX; - sb->s_bdi = bdi_get(sb->s_bdev->bd_disk->bdi); - err = load_nilfs(nilfs, sb); if (err) goto failed_nilfs; From 10662aafce4acd52278c942d0a5b3993594017b9 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 1 May 2026 09:35:05 +0300 Subject: [PATCH 0854/2758] ntb: Store original DMA address for future release [ Upstream commit da6d997ac556479c112554ab5d95cbd04683eb11 ] The DMA API requires that dma_free_attrs receive the exact dma_handle originally returned by the allocation function. Do not modify it. Fixes: fc5d1829f9bf ("NTB: transport: Try harder to alloc an aligned MW buffer") Signed-off-by: Leon Romanovsky Reviewed-by: Dave Jiang Signed-off-by: Marek Szyprowski Link: https://lore.kernel.org/r/20260501-dma-attrs-debug-v2-1-8dbac75cd501@nvidia.com Signed-off-by: Sasha Levin --- drivers/ntb/ntb_transport.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c index 7cabc82305d6b..771eb7d9f4356 100644 --- a/drivers/ntb/ntb_transport.c +++ b/drivers/ntb/ntb_transport.c @@ -223,6 +223,7 @@ struct ntb_transport_mw { void *alloc_addr; void *virt_addr; dma_addr_t dma_addr; + dma_addr_t original_dma_addr; }; struct ntb_transport_client_dev { @@ -766,7 +767,7 @@ static void ntb_free_mw(struct ntb_transport_ctx *nt, int num_mw) ntb_mw_clear_trans(nt->ndev, PIDX, num_mw); dma_free_coherent(dma_dev, mw->alloc_size, - mw->alloc_addr, mw->dma_addr); + mw->alloc_addr, mw->original_dma_addr); mw->xlat_size = 0; mw->buff_size = 0; mw->alloc_size = 0; @@ -802,6 +803,7 @@ static int ntb_alloc_mw_buffer(struct ntb_transport_mw *mw, return -ENOMEM; } virt_addr = alloc_addr; + mw->original_dma_addr = dma_addr; /* * we must ensure that the memory address allocated is BAR size From 91b00a6eb6d8a953663c8ffc52bbf9dc28de9482 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 1 May 2026 09:35:06 +0300 Subject: [PATCH 0855/2758] ntb: Use consistent DMA attributes when freeing DMA mappings [ Upstream commit 9d625aa2ebd445868955719e0abcf695b43f9318 ] The counterpart of dma_alloc_attrs() is dma_free_attrs(), which must receive the same DMA attributes used during allocation. The code previously used dma_free_coherent(), which does not accept or apply any DMA attributes. Fixes: 061a785a114f ("ntb: Force physically contiguous allocation of rx ring buffers") Signed-off-by: Leon Romanovsky Reviewed-by: Dave Jiang Signed-off-by: Marek Szyprowski Link: https://lore.kernel.org/r/20260501-dma-attrs-debug-v2-2-8dbac75cd501@nvidia.com Signed-off-by: Sasha Levin --- drivers/ntb/ntb_transport.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c index 771eb7d9f4356..f59f926d4bfaa 100644 --- a/drivers/ntb/ntb_transport.c +++ b/drivers/ntb/ntb_transport.c @@ -766,8 +766,8 @@ static void ntb_free_mw(struct ntb_transport_ctx *nt, int num_mw) return; ntb_mw_clear_trans(nt->ndev, PIDX, num_mw); - dma_free_coherent(dma_dev, mw->alloc_size, - mw->alloc_addr, mw->original_dma_addr); + dma_free_attrs(dma_dev, mw->alloc_size, mw->alloc_addr, + mw->original_dma_addr, DMA_ATTR_FORCE_CONTIGUOUS); mw->xlat_size = 0; mw->buff_size = 0; mw->alloc_size = 0; @@ -828,8 +828,8 @@ static int ntb_alloc_mw_buffer(struct ntb_transport_mw *mw, return 0; err: - dma_free_coherent(ntb_dev, mw->alloc_size, alloc_addr, dma_addr); - + dma_free_attrs(ntb_dev, mw->alloc_size, alloc_addr, dma_addr, + DMA_ATTR_FORCE_CONTIGUOUS); return rc; } From e9b1ae1625cb776a3401d3af9b0aad496dd04456 Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Thu, 19 Mar 2026 17:09:02 +0800 Subject: [PATCH 0856/2758] media: qcom: camss: vfe: fix PIX subdev naming on VFE lite [ Upstream commit c97e797a64cdfe4acecff831b4285418d2815893 ] VFE lite hardware does not provide a functional PIX path, but after the per sub-device type resource changes the PIX subdev name is still assigned unconditionally. Only assign the PIX subdev name on non-lite VFE variants to avoid exposing a misleading device name. Fixes: ae44829a4a97 ("media: qcom: camss: Add per sub-device type resources") Signed-off-by: Wenmeng Liu Reviewed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue Signed-off-by: Sasha Levin --- drivers/media/platform/qcom/camss/camss-vfe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index 5baf0e3d4bc46..2ee4f9ae0ab50 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -2053,7 +2053,7 @@ int msm_vfe_register_entities(struct vfe_device *vfe, v4l2_subdev_init(sd, &vfe_v4l2_ops); sd->internal_ops = &vfe_v4l2_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - if (i == VFE_LINE_PIX) + if (i == VFE_LINE_PIX && vfe->res->is_lite == false) snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s", MSM_VFE_NAME, vfe->id, "pix"); else From ddd6b01310a2b5d7234e5ddebec5725cbddcd910 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Tue, 28 Apr 2026 10:57:29 +0000 Subject: [PATCH 0857/2758] dts: riscv: spacemit: correct 32k clock frequency [ Upstream commit 3c350f6284d8ea5e7a9648241b2e9604f2262d42 ] The 32k oscillator's clock frequency is actually 32768Hz, so correct it. Fixes: 67072c8cd48c ("riscv: dts: spacemit: k3: add clock tree") Fixes: a6fafa64b03a ("riscv: dts: spacemit: Add clock tree for SpacemiT K1") Link: https://patch.msgid.link/20260428-06-k3-clk-osc32k-v1-1-e2378da7cb9b@kernel.org Signed-off-by: Yixun Lan Signed-off-by: Sasha Levin --- arch/riscv/boot/dts/spacemit/k1.dtsi | 2 +- arch/riscv/boot/dts/spacemit/k3.dtsi | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/riscv/boot/dts/spacemit/k1.dtsi b/arch/riscv/boot/dts/spacemit/k1.dtsi index f0bad6855c970..b0b9c19b56a09 100644 --- a/arch/riscv/boot/dts/spacemit/k1.dtsi +++ b/arch/riscv/boot/dts/spacemit/k1.dtsi @@ -333,7 +333,7 @@ osc_32k: clock-32k { compatible = "fixed-clock"; - clock-frequency = <32000>; + clock-frequency = <32768>; clock-output-names = "osc_32k"; #clock-cells = <0>; }; diff --git a/arch/riscv/boot/dts/spacemit/k3.dtsi b/arch/riscv/boot/dts/spacemit/k3.dtsi index 815debd16409b..e6faf8d8759e1 100644 --- a/arch/riscv/boot/dts/spacemit/k3.dtsi +++ b/arch/riscv/boot/dts/spacemit/k3.dtsi @@ -424,7 +424,7 @@ osc_32k: clock-32k { compatible = "fixed-clock"; - clock-frequency = <32000>; + clock-frequency = <32768>; clock-output-names = "osc_32k"; #clock-cells = <0>; }; From 8dcf3edaea35aca276501b2e9f822d94a373c2d4 Mon Sep 17 00:00:00 2001 From: Nickolay Goppen Date: Wed, 29 Apr 2026 12:30:09 +0300 Subject: [PATCH 0858/2758] arm64: dts: qcom: sdm660: set cdsp compute-cbs' regs properly [ Upstream commit 708ab9d3bc5b74eeee767c678366624d3c02a4ec ] Changing FastRPC compute-cbs' reg values to matching iommu streams solves SMMU translation errors when trying to use FastRPC on CDSP so change FastRPC compute-cbs' reg values that way Fixes: c0c32a9e3493 ("arm64: dts: qcom: sdm630/660: Add CDSP-related nodes") Reviewed-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Reviewed-by: Ekansh Gupta Signed-off-by: Nickolay Goppen Link: https://lore.kernel.org/r/20260429-qcom-sdm660-cdsp-adsp-fastrpc-dts-fix-v5-2-16bc82e622ad@mainlining.org Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sdm660.dtsi | 36 ++++++++++++++-------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/sdm660.dtsi b/arch/arm64/boot/dts/qcom/sdm660.dtsi index 3fd6dd82a9927..0fca9662c64a1 100644 --- a/arch/arm64/boot/dts/qcom/sdm660.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm660.dtsi @@ -350,57 +350,57 @@ #address-cells = <1>; #size-cells = <0>; - compute-cb@5 { + compute-cb@3 { compatible = "qcom,fastrpc-compute-cb"; - reg = <5>; + reg = <3>; iommus = <&cdsp_smmu 3>; }; - compute-cb@6 { + compute-cb@4 { compatible = "qcom,fastrpc-compute-cb"; - reg = <6>; + reg = <4>; iommus = <&cdsp_smmu 4>; }; - compute-cb@7 { + compute-cb@5 { compatible = "qcom,fastrpc-compute-cb"; - reg = <7>; + reg = <5>; iommus = <&cdsp_smmu 5>; }; - compute-cb@8 { + compute-cb@6 { compatible = "qcom,fastrpc-compute-cb"; - reg = <8>; + reg = <6>; iommus = <&cdsp_smmu 6>; }; - compute-cb@9 { + compute-cb@7 { compatible = "qcom,fastrpc-compute-cb"; - reg = <9>; + reg = <7>; iommus = <&cdsp_smmu 7>; }; - compute-cb@10 { + compute-cb@8 { compatible = "qcom,fastrpc-compute-cb"; - reg = <10>; + reg = <8>; iommus = <&cdsp_smmu 8>; }; - compute-cb@11 { + compute-cb@9 { compatible = "qcom,fastrpc-compute-cb"; - reg = <11>; + reg = <9>; iommus = <&cdsp_smmu 9>; }; - compute-cb@12 { + compute-cb@10 { compatible = "qcom,fastrpc-compute-cb"; - reg = <12>; + reg = <10>; iommus = <&cdsp_smmu 10>; }; - compute-cb@13 { + compute-cb@11 { compatible = "qcom,fastrpc-compute-cb"; - reg = <13>; + reg = <11>; iommus = <&cdsp_smmu 11>; }; }; From 6df549d0307b4d2cb58d6c478e823ed4f296bdec Mon Sep 17 00:00:00 2001 From: Nickolay Goppen Date: Wed, 29 Apr 2026 12:30:10 +0300 Subject: [PATCH 0859/2758] arm64: dts: qcom: sdm630: set adsp compute-cbs' regs properly [ Upstream commit e8e1fb5c703fc6962103ffdc60830df99351c139 ] Changing FastRPC compute-cbs' reg values to matching iommu streams solves SMMU translation errors when trying to use FastRPC on ADSP so change FastRPC compute-cbs' reg values that way Fixes: af2ce7296643 ("arm64: dts: qcom: sdm630: Add FastRPC nodes to ADSP") Reviewed-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Reviewed-by: Ekansh Gupta Signed-off-by: Nickolay Goppen Link: https://lore.kernel.org/r/20260429-qcom-sdm660-cdsp-adsp-fastrpc-dts-fix-v5-3-16bc82e622ad@mainlining.org Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sdm630.dtsi | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/sdm630.dtsi b/arch/arm64/boot/dts/qcom/sdm630.dtsi index bef3213165d66..4b47efdb57b21 100644 --- a/arch/arm64/boot/dts/qcom/sdm630.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm630.dtsi @@ -2459,27 +2459,27 @@ #address-cells = <1>; #size-cells = <0>; - compute-cb@1 { + compute-cb@3 { compatible = "qcom,fastrpc-compute-cb"; - reg = <1>; + reg = <3>; iommus = <&lpass_smmu 3>; }; - compute-cb@2 { + compute-cb@7 { compatible = "qcom,fastrpc-compute-cb"; - reg = <2>; + reg = <7>; iommus = <&lpass_smmu 7>; }; - compute-cb@3 { + compute-cb@8 { compatible = "qcom,fastrpc-compute-cb"; - reg = <3>; + reg = <8>; iommus = <&lpass_smmu 8>; }; - compute-cb@4 { + compute-cb@9 { compatible = "qcom,fastrpc-compute-cb"; - reg = <4>; + reg = <9>; iommus = <&lpass_smmu 9>; }; }; From a71475405397ab04a259da518b2ce505fe794084 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 31 Mar 2026 17:01:47 +0800 Subject: [PATCH 0860/2758] arm64: dts: qcom: lemans: Move PCIe devices into soc node [ Upstream commit 8222873cd4698627c08bffb2e40ba6f5a008fe32 ] These PCIe devices with MMIO address should be inside soc node rather than outside. Fixes: 489f14be0e0a ("arm64: dts: qcom: sa8775p: Add pcie0 and pcie1 nodes") Signed-off-by: Shawn Guo Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260331090147.18522-1-shengchao.guo@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/lemans.dtsi | 692 +++++++++++++-------------- 1 file changed, 346 insertions(+), 346 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/lemans.dtsi b/arch/arm64/boot/dts/qcom/lemans.dtsi index fe6e763518230..5dd271e7108d7 100644 --- a/arch/arm64/boot/dts/qcom/lemans.dtsi +++ b/arch/arm64/boot/dts/qcom/lemans.dtsi @@ -2694,6 +2694,352 @@ qcom,bcm-voters = <&apps_bcm_voter>; }; + pcie0: pcie@1c00000 { + compatible = "qcom,pcie-sa8775p"; + reg = <0x0 0x01c00000 0x0 0x3000>, + <0x0 0x40000000 0x0 0xf20>, + <0x0 0x40000f20 0x0 0xa8>, + <0x0 0x40001000 0x0 0x4000>, + <0x0 0x40100000 0x0 0x100000>, + <0x0 0x01c03000 0x0 0x1000>; + reg-names = "parf", "dbi", "elbi", "atu", "config", "mhi"; + device_type = "pci"; + + #address-cells = <3>; + #size-cells = <2>; + ranges = <0x01000000 0x0 0x00000000 0x0 0x40200000 0x0 0x100000>, + <0x02000000 0x0 0x40300000 0x0 0x40300000 0x0 0x1fd00000>; + bus-range = <0x00 0xff>; + + dma-coherent; + + linux,pci-domain = <0>; + num-lanes = <2>; + + interrupts = , + , + , + , + , + , + , + , + ; + interrupt-names = "msi0", + "msi1", + "msi2", + "msi3", + "msi4", + "msi5", + "msi6", + "msi7", + "global"; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc GIC_SPI 434 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 2 &intc GIC_SPI 435 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 3 &intc GIC_SPI 438 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 4 &intc GIC_SPI 439 IRQ_TYPE_LEVEL_HIGH>; + + clocks = <&gcc GCC_PCIE_0_AUX_CLK>, + <&gcc GCC_PCIE_0_CFG_AHB_CLK>, + <&gcc GCC_PCIE_0_MSTR_AXI_CLK>, + <&gcc GCC_PCIE_0_SLV_AXI_CLK>, + <&gcc GCC_PCIE_0_SLV_Q2A_AXI_CLK>; + + clock-names = "aux", + "cfg", + "bus_master", + "bus_slave", + "slave_q2a"; + + assigned-clocks = <&gcc GCC_PCIE_0_AUX_CLK>; + assigned-clock-rates = <19200000>; + + interconnects = <&pcie_anoc MASTER_PCIE_0 0 &mc_virt SLAVE_EBI1 0>, + <&gem_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_PCIE_0 0>; + interconnect-names = "pcie-mem", "cpu-pcie"; + + iommu-map = <0x0 &pcie_smmu 0x0000 0x1>, + <0x100 &pcie_smmu 0x0001 0x1>; + + resets = <&gcc GCC_PCIE_0_BCR>, + <&gcc GCC_PCIE_0_LINK_DOWN_BCR>; + reset-names = "pci", + "link_down"; + + power-domains = <&gcc PCIE_0_GDSC>; + + phys = <&pcie0_phy>; + phy-names = "pciephy"; + + eq-presets-8gts = /bits/ 16 <0x5555 0x5555>; + eq-presets-16gts = /bits/ 8 <0x55 0x55>; + + status = "disabled"; + + pcieport0: pcie@0 { + device_type = "pci"; + reg = <0x0 0x0 0x0 0x0 0x0>; + bus-range = <0x01 0xff>; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; + }; + + pcie0_ep: pcie-ep@1c00000 { + compatible = "qcom,sa8775p-pcie-ep"; + reg = <0x0 0x01c00000 0x0 0x3000>, + <0x0 0x40000000 0x0 0xf20>, + <0x0 0x40000f20 0x0 0xa8>, + <0x0 0x40001000 0x0 0x4000>, + <0x0 0x40200000 0x0 0x1fe00000>, + <0x0 0x01c03000 0x0 0x1000>, + <0x0 0x40005000 0x0 0x2000>; + reg-names = "parf", "dbi", "elbi", "atu", "addr_space", + "mmio", "dma"; + + clocks = <&gcc GCC_PCIE_0_AUX_CLK>, + <&gcc GCC_PCIE_0_CFG_AHB_CLK>, + <&gcc GCC_PCIE_0_MSTR_AXI_CLK>, + <&gcc GCC_PCIE_0_SLV_AXI_CLK>, + <&gcc GCC_PCIE_0_SLV_Q2A_AXI_CLK>; + + clock-names = "aux", + "cfg", + "bus_master", + "bus_slave", + "slave_q2a"; + + interrupts = , + , + ; + + interrupt-names = "global", "doorbell", "dma"; + + interconnects = <&pcie_anoc MASTER_PCIE_0 0 &mc_virt SLAVE_EBI1 0>, + <&gem_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_PCIE_0 0>; + interconnect-names = "pcie-mem", "cpu-pcie"; + + dma-coherent; + iommus = <&pcie_smmu 0x0000 0x7f>; + resets = <&gcc GCC_PCIE_0_BCR>; + reset-names = "core"; + power-domains = <&gcc PCIE_0_GDSC>; + phys = <&pcie0_phy>; + phy-names = "pciephy"; + num-lanes = <2>; + linux,pci-domain = <0>; + + status = "disabled"; + }; + + pcie0_phy: phy@1c04000 { + compatible = "qcom,sa8775p-qmp-gen4x2-pcie-phy"; + reg = <0x0 0x1c04000 0x0 0x2000>; + + clocks = <&gcc GCC_PCIE_0_PHY_AUX_CLK>, + <&gcc GCC_PCIE_0_CFG_AHB_CLK>, + <&gcc GCC_PCIE_CLKREF_EN>, + <&gcc GCC_PCIE_0_PHY_RCHNG_CLK>, + <&gcc GCC_PCIE_0_PIPE_CLK>, + <&gcc GCC_PCIE_0_PIPEDIV2_CLK>; + clock-names = "aux", + "cfg_ahb", + "ref", + "rchng", + "pipe", + "pipediv2"; + + assigned-clocks = <&gcc GCC_PCIE_0_PHY_RCHNG_CLK>; + assigned-clock-rates = <100000000>; + + resets = <&gcc GCC_PCIE_0_PHY_BCR>; + reset-names = "phy"; + + #clock-cells = <0>; + clock-output-names = "pcie_0_pipe_clk"; + + #phy-cells = <0>; + + status = "disabled"; + }; + + pcie1: pcie@1c10000 { + compatible = "qcom,pcie-sa8775p"; + reg = <0x0 0x01c10000 0x0 0x3000>, + <0x0 0x60000000 0x0 0xf20>, + <0x0 0x60000f20 0x0 0xa8>, + <0x0 0x60001000 0x0 0x4000>, + <0x0 0x60100000 0x0 0x100000>, + <0x0 0x01c13000 0x0 0x1000>; + reg-names = "parf", "dbi", "elbi", "atu", "config", "mhi"; + device_type = "pci"; + + #address-cells = <3>; + #size-cells = <2>; + ranges = <0x01000000 0x0 0x00000000 0x0 0x60200000 0x0 0x100000>, + <0x02000000 0x0 0x60300000 0x0 0x60300000 0x0 0x1fd00000>; + bus-range = <0x00 0xff>; + + dma-coherent; + + linux,pci-domain = <1>; + num-lanes = <4>; + + interrupts = , + , + , + , + , + , + , + , + ; + interrupt-names = "msi0", + "msi1", + "msi2", + "msi3", + "msi4", + "msi5", + "msi6", + "msi7", + "global"; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 2 &intc GIC_SPI 149 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 3 &intc GIC_SPI 150 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 4 &intc GIC_SPI 151 IRQ_TYPE_LEVEL_HIGH>; + + clocks = <&gcc GCC_PCIE_1_AUX_CLK>, + <&gcc GCC_PCIE_1_CFG_AHB_CLK>, + <&gcc GCC_PCIE_1_MSTR_AXI_CLK>, + <&gcc GCC_PCIE_1_SLV_AXI_CLK>, + <&gcc GCC_PCIE_1_SLV_Q2A_AXI_CLK>; + + clock-names = "aux", + "cfg", + "bus_master", + "bus_slave", + "slave_q2a"; + + assigned-clocks = <&gcc GCC_PCIE_1_AUX_CLK>; + assigned-clock-rates = <19200000>; + + interconnects = <&pcie_anoc MASTER_PCIE_1 0 &mc_virt SLAVE_EBI1 0>, + <&gem_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_PCIE_1 0>; + interconnect-names = "pcie-mem", "cpu-pcie"; + + iommu-map = <0x0 &pcie_smmu 0x0080 0x1>, + <0x100 &pcie_smmu 0x0081 0x1>; + + resets = <&gcc GCC_PCIE_1_BCR>, + <&gcc GCC_PCIE_1_LINK_DOWN_BCR>; + reset-names = "pci", + "link_down"; + + power-domains = <&gcc PCIE_1_GDSC>; + + phys = <&pcie1_phy>; + phy-names = "pciephy"; + + eq-presets-8gts = /bits/ 16 <0x5555 0x5555 0x5555 0x5555>; + eq-presets-16gts = /bits/ 8 <0x55 0x55 0x55 0x55>; + + status = "disabled"; + + pcie@0 { + device_type = "pci"; + reg = <0x0 0x0 0x0 0x0 0x0>; + bus-range = <0x01 0xff>; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; + }; + + pcie1_ep: pcie-ep@1c10000 { + compatible = "qcom,sa8775p-pcie-ep"; + reg = <0x0 0x01c10000 0x0 0x3000>, + <0x0 0x60000000 0x0 0xf20>, + <0x0 0x60000f20 0x0 0xa8>, + <0x0 0x60001000 0x0 0x4000>, + <0x0 0x60200000 0x0 0x1fe00000>, + <0x0 0x01c13000 0x0 0x1000>, + <0x0 0x60005000 0x0 0x2000>; + reg-names = "parf", "dbi", "elbi", "atu", "addr_space", + "mmio", "dma"; + + clocks = <&gcc GCC_PCIE_1_AUX_CLK>, + <&gcc GCC_PCIE_1_CFG_AHB_CLK>, + <&gcc GCC_PCIE_1_MSTR_AXI_CLK>, + <&gcc GCC_PCIE_1_SLV_AXI_CLK>, + <&gcc GCC_PCIE_1_SLV_Q2A_AXI_CLK>; + + clock-names = "aux", + "cfg", + "bus_master", + "bus_slave", + "slave_q2a"; + + interrupts = , + , + ; + + interrupt-names = "global", "doorbell", "dma"; + + interconnects = <&pcie_anoc MASTER_PCIE_1 0 &mc_virt SLAVE_EBI1 0>, + <&gem_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_PCIE_1 0>; + interconnect-names = "pcie-mem", "cpu-pcie"; + + dma-coherent; + iommus = <&pcie_smmu 0x80 0x7f>; + resets = <&gcc GCC_PCIE_1_BCR>; + reset-names = "core"; + power-domains = <&gcc PCIE_1_GDSC>; + phys = <&pcie1_phy>; + phy-names = "pciephy"; + num-lanes = <4>; + linux,pci-domain = <1>; + + status = "disabled"; + }; + + pcie1_phy: phy@1c14000 { + compatible = "qcom,sa8775p-qmp-gen4x4-pcie-phy"; + reg = <0x0 0x1c14000 0x0 0x4000>; + + clocks = <&gcc GCC_PCIE_1_PHY_AUX_CLK>, + <&gcc GCC_PCIE_1_CFG_AHB_CLK>, + <&gcc GCC_PCIE_CLKREF_EN>, + <&gcc GCC_PCIE_1_PHY_RCHNG_CLK>, + <&gcc GCC_PCIE_1_PIPE_CLK>, + <&gcc GCC_PCIE_1_PIPEDIV2_CLK>; + clock-names = "aux", + "cfg_ahb", + "ref", + "rchng", + "pipe", + "pipediv2"; + + assigned-clocks = <&gcc GCC_PCIE_1_PHY_RCHNG_CLK>; + assigned-clock-rates = <100000000>; + + resets = <&gcc GCC_PCIE_1_PHY_BCR>; + reset-names = "phy"; + + #clock-cells = <0>; + clock-output-names = "pcie_1_pipe_clk"; + + #phy-cells = <0>; + + status = "disabled"; + }; + ufs_mem_hc: ufshc@1d84000 { compatible = "qcom,sa8775p-ufshc", "qcom,ufshc", "jedec,ufs-2.0"; reg = <0x0 0x01d84000 0x0 0x3000>; @@ -8601,350 +8947,4 @@ }; }; }; - - pcie0: pcie@1c00000 { - compatible = "qcom,pcie-sa8775p"; - reg = <0x0 0x01c00000 0x0 0x3000>, - <0x0 0x40000000 0x0 0xf20>, - <0x0 0x40000f20 0x0 0xa8>, - <0x0 0x40001000 0x0 0x4000>, - <0x0 0x40100000 0x0 0x100000>, - <0x0 0x01c03000 0x0 0x1000>; - reg-names = "parf", "dbi", "elbi", "atu", "config", "mhi"; - device_type = "pci"; - - #address-cells = <3>; - #size-cells = <2>; - ranges = <0x01000000 0x0 0x00000000 0x0 0x40200000 0x0 0x100000>, - <0x02000000 0x0 0x40300000 0x0 0x40300000 0x0 0x1fd00000>; - bus-range = <0x00 0xff>; - - dma-coherent; - - linux,pci-domain = <0>; - num-lanes = <2>; - - interrupts = , - , - , - , - , - , - , - , - ; - interrupt-names = "msi0", - "msi1", - "msi2", - "msi3", - "msi4", - "msi5", - "msi6", - "msi7", - "global"; - #interrupt-cells = <1>; - interrupt-map-mask = <0 0 0 0x7>; - interrupt-map = <0 0 0 1 &intc GIC_SPI 434 IRQ_TYPE_LEVEL_HIGH>, - <0 0 0 2 &intc GIC_SPI 435 IRQ_TYPE_LEVEL_HIGH>, - <0 0 0 3 &intc GIC_SPI 438 IRQ_TYPE_LEVEL_HIGH>, - <0 0 0 4 &intc GIC_SPI 439 IRQ_TYPE_LEVEL_HIGH>; - - clocks = <&gcc GCC_PCIE_0_AUX_CLK>, - <&gcc GCC_PCIE_0_CFG_AHB_CLK>, - <&gcc GCC_PCIE_0_MSTR_AXI_CLK>, - <&gcc GCC_PCIE_0_SLV_AXI_CLK>, - <&gcc GCC_PCIE_0_SLV_Q2A_AXI_CLK>; - - clock-names = "aux", - "cfg", - "bus_master", - "bus_slave", - "slave_q2a"; - - assigned-clocks = <&gcc GCC_PCIE_0_AUX_CLK>; - assigned-clock-rates = <19200000>; - - interconnects = <&pcie_anoc MASTER_PCIE_0 0 &mc_virt SLAVE_EBI1 0>, - <&gem_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_PCIE_0 0>; - interconnect-names = "pcie-mem", "cpu-pcie"; - - iommu-map = <0x0 &pcie_smmu 0x0000 0x1>, - <0x100 &pcie_smmu 0x0001 0x1>; - - resets = <&gcc GCC_PCIE_0_BCR>, - <&gcc GCC_PCIE_0_LINK_DOWN_BCR>; - reset-names = "pci", - "link_down"; - - power-domains = <&gcc PCIE_0_GDSC>; - - phys = <&pcie0_phy>; - phy-names = "pciephy"; - - eq-presets-8gts = /bits/ 16 <0x5555 0x5555>; - eq-presets-16gts = /bits/ 8 <0x55 0x55>; - - status = "disabled"; - - pcieport0: pcie@0 { - device_type = "pci"; - reg = <0x0 0x0 0x0 0x0 0x0>; - bus-range = <0x01 0xff>; - - #address-cells = <3>; - #size-cells = <2>; - ranges; - }; - }; - - pcie0_ep: pcie-ep@1c00000 { - compatible = "qcom,sa8775p-pcie-ep"; - reg = <0x0 0x01c00000 0x0 0x3000>, - <0x0 0x40000000 0x0 0xf20>, - <0x0 0x40000f20 0x0 0xa8>, - <0x0 0x40001000 0x0 0x4000>, - <0x0 0x40200000 0x0 0x1fe00000>, - <0x0 0x01c03000 0x0 0x1000>, - <0x0 0x40005000 0x0 0x2000>; - reg-names = "parf", "dbi", "elbi", "atu", "addr_space", - "mmio", "dma"; - - clocks = <&gcc GCC_PCIE_0_AUX_CLK>, - <&gcc GCC_PCIE_0_CFG_AHB_CLK>, - <&gcc GCC_PCIE_0_MSTR_AXI_CLK>, - <&gcc GCC_PCIE_0_SLV_AXI_CLK>, - <&gcc GCC_PCIE_0_SLV_Q2A_AXI_CLK>; - - clock-names = "aux", - "cfg", - "bus_master", - "bus_slave", - "slave_q2a"; - - interrupts = , - , - ; - - interrupt-names = "global", "doorbell", "dma"; - - interconnects = <&pcie_anoc MASTER_PCIE_0 0 &mc_virt SLAVE_EBI1 0>, - <&gem_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_PCIE_0 0>; - interconnect-names = "pcie-mem", "cpu-pcie"; - - dma-coherent; - iommus = <&pcie_smmu 0x0000 0x7f>; - resets = <&gcc GCC_PCIE_0_BCR>; - reset-names = "core"; - power-domains = <&gcc PCIE_0_GDSC>; - phys = <&pcie0_phy>; - phy-names = "pciephy"; - num-lanes = <2>; - linux,pci-domain = <0>; - - status = "disabled"; - }; - - pcie0_phy: phy@1c04000 { - compatible = "qcom,sa8775p-qmp-gen4x2-pcie-phy"; - reg = <0x0 0x1c04000 0x0 0x2000>; - - clocks = <&gcc GCC_PCIE_0_PHY_AUX_CLK>, - <&gcc GCC_PCIE_0_CFG_AHB_CLK>, - <&gcc GCC_PCIE_CLKREF_EN>, - <&gcc GCC_PCIE_0_PHY_RCHNG_CLK>, - <&gcc GCC_PCIE_0_PIPE_CLK>, - <&gcc GCC_PCIE_0_PIPEDIV2_CLK>; - clock-names = "aux", - "cfg_ahb", - "ref", - "rchng", - "pipe", - "pipediv2"; - - assigned-clocks = <&gcc GCC_PCIE_0_PHY_RCHNG_CLK>; - assigned-clock-rates = <100000000>; - - resets = <&gcc GCC_PCIE_0_PHY_BCR>; - reset-names = "phy"; - - #clock-cells = <0>; - clock-output-names = "pcie_0_pipe_clk"; - - #phy-cells = <0>; - - status = "disabled"; - }; - - pcie1: pcie@1c10000 { - compatible = "qcom,pcie-sa8775p"; - reg = <0x0 0x01c10000 0x0 0x3000>, - <0x0 0x60000000 0x0 0xf20>, - <0x0 0x60000f20 0x0 0xa8>, - <0x0 0x60001000 0x0 0x4000>, - <0x0 0x60100000 0x0 0x100000>, - <0x0 0x01c13000 0x0 0x1000>; - reg-names = "parf", "dbi", "elbi", "atu", "config", "mhi"; - device_type = "pci"; - - #address-cells = <3>; - #size-cells = <2>; - ranges = <0x01000000 0x0 0x00000000 0x0 0x60200000 0x0 0x100000>, - <0x02000000 0x0 0x60300000 0x0 0x60300000 0x0 0x1fd00000>; - bus-range = <0x00 0xff>; - - dma-coherent; - - linux,pci-domain = <1>; - num-lanes = <4>; - - interrupts = , - , - , - , - , - , - , - , - ; - interrupt-names = "msi0", - "msi1", - "msi2", - "msi3", - "msi4", - "msi5", - "msi6", - "msi7", - "global"; - #interrupt-cells = <1>; - interrupt-map-mask = <0 0 0 0x7>; - interrupt-map = <0 0 0 1 &intc GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>, - <0 0 0 2 &intc GIC_SPI 149 IRQ_TYPE_LEVEL_HIGH>, - <0 0 0 3 &intc GIC_SPI 150 IRQ_TYPE_LEVEL_HIGH>, - <0 0 0 4 &intc GIC_SPI 151 IRQ_TYPE_LEVEL_HIGH>; - - clocks = <&gcc GCC_PCIE_1_AUX_CLK>, - <&gcc GCC_PCIE_1_CFG_AHB_CLK>, - <&gcc GCC_PCIE_1_MSTR_AXI_CLK>, - <&gcc GCC_PCIE_1_SLV_AXI_CLK>, - <&gcc GCC_PCIE_1_SLV_Q2A_AXI_CLK>; - - clock-names = "aux", - "cfg", - "bus_master", - "bus_slave", - "slave_q2a"; - - assigned-clocks = <&gcc GCC_PCIE_1_AUX_CLK>; - assigned-clock-rates = <19200000>; - - interconnects = <&pcie_anoc MASTER_PCIE_1 0 &mc_virt SLAVE_EBI1 0>, - <&gem_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_PCIE_1 0>; - interconnect-names = "pcie-mem", "cpu-pcie"; - - iommu-map = <0x0 &pcie_smmu 0x0080 0x1>, - <0x100 &pcie_smmu 0x0081 0x1>; - - resets = <&gcc GCC_PCIE_1_BCR>, - <&gcc GCC_PCIE_1_LINK_DOWN_BCR>; - reset-names = "pci", - "link_down"; - - power-domains = <&gcc PCIE_1_GDSC>; - - phys = <&pcie1_phy>; - phy-names = "pciephy"; - - eq-presets-8gts = /bits/ 16 <0x5555 0x5555 0x5555 0x5555>; - eq-presets-16gts = /bits/ 8 <0x55 0x55 0x55 0x55>; - - status = "disabled"; - - pcie@0 { - device_type = "pci"; - reg = <0x0 0x0 0x0 0x0 0x0>; - bus-range = <0x01 0xff>; - - #address-cells = <3>; - #size-cells = <2>; - ranges; - }; - }; - - pcie1_ep: pcie-ep@1c10000 { - compatible = "qcom,sa8775p-pcie-ep"; - reg = <0x0 0x01c10000 0x0 0x3000>, - <0x0 0x60000000 0x0 0xf20>, - <0x0 0x60000f20 0x0 0xa8>, - <0x0 0x60001000 0x0 0x4000>, - <0x0 0x60200000 0x0 0x1fe00000>, - <0x0 0x01c13000 0x0 0x1000>, - <0x0 0x60005000 0x0 0x2000>; - reg-names = "parf", "dbi", "elbi", "atu", "addr_space", - "mmio", "dma"; - - clocks = <&gcc GCC_PCIE_1_AUX_CLK>, - <&gcc GCC_PCIE_1_CFG_AHB_CLK>, - <&gcc GCC_PCIE_1_MSTR_AXI_CLK>, - <&gcc GCC_PCIE_1_SLV_AXI_CLK>, - <&gcc GCC_PCIE_1_SLV_Q2A_AXI_CLK>; - - clock-names = "aux", - "cfg", - "bus_master", - "bus_slave", - "slave_q2a"; - - interrupts = , - , - ; - - interrupt-names = "global", "doorbell", "dma"; - - interconnects = <&pcie_anoc MASTER_PCIE_1 0 &mc_virt SLAVE_EBI1 0>, - <&gem_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_PCIE_1 0>; - interconnect-names = "pcie-mem", "cpu-pcie"; - - dma-coherent; - iommus = <&pcie_smmu 0x80 0x7f>; - resets = <&gcc GCC_PCIE_1_BCR>; - reset-names = "core"; - power-domains = <&gcc PCIE_1_GDSC>; - phys = <&pcie1_phy>; - phy-names = "pciephy"; - num-lanes = <4>; - linux,pci-domain = <1>; - - status = "disabled"; - }; - - pcie1_phy: phy@1c14000 { - compatible = "qcom,sa8775p-qmp-gen4x4-pcie-phy"; - reg = <0x0 0x1c14000 0x0 0x4000>; - - clocks = <&gcc GCC_PCIE_1_PHY_AUX_CLK>, - <&gcc GCC_PCIE_1_CFG_AHB_CLK>, - <&gcc GCC_PCIE_CLKREF_EN>, - <&gcc GCC_PCIE_1_PHY_RCHNG_CLK>, - <&gcc GCC_PCIE_1_PIPE_CLK>, - <&gcc GCC_PCIE_1_PIPEDIV2_CLK>; - clock-names = "aux", - "cfg_ahb", - "ref", - "rchng", - "pipe", - "pipediv2"; - - assigned-clocks = <&gcc GCC_PCIE_1_PHY_RCHNG_CLK>; - assigned-clock-rates = <100000000>; - - resets = <&gcc GCC_PCIE_1_PHY_BCR>; - reset-names = "phy"; - - #clock-cells = <0>; - clock-output-names = "pcie_1_pipe_clk"; - - #phy-cells = <0>; - - status = "disabled"; - }; }; From 92908ee078096d2d9bb44d8395acc011e2cefda6 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Mon, 9 Feb 2026 03:32:17 +0200 Subject: [PATCH 0861/2758] media: iris: scale MMCX power domain on SM8250 [ Upstream commit 4170e2f25ce40b69f3827fcdabf395380095e136 ] On SM8250 most of the video clocks are powered by the MMCX domain, while the PLL is powered on by the MX domain. Extend the driver to support scaling both power domains, while keeping compatibility with the existing DTs, which define only the MX domain. Fixes: 79865252acb6 ("media: iris: enable video driver probe of SM8250 SoC") Reviewed-by: Dikshita Agarwal Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue Signed-off-by: Sasha Levin --- drivers/media/platform/qcom/iris/iris_platform_gen1.c | 2 +- drivers/media/platform/qcom/iris/iris_probe.c | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen1.c b/drivers/media/platform/qcom/iris/iris_platform_gen1.c index df8e6bf9430ed..aa71f7f53ee34 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen1.c @@ -281,7 +281,7 @@ static const struct bw_info sm8250_bw_table_dec[] = { static const char * const sm8250_pmdomain_table[] = { "venus", "vcodec0" }; -static const char * const sm8250_opp_pd_table[] = { "mx" }; +static const char * const sm8250_opp_pd_table[] = { "mx", "mmcx" }; static const struct platform_clk_data sm8250_clk_table[] = { {IRIS_AXI_CLK, "iface" }, diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index ddaacda523ecb..487eb0917c0e2 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -64,6 +64,13 @@ static int iris_init_power_domains(struct iris_core *core) return ret; ret = devm_pm_domain_attach_list(core->dev, &iris_opp_pd_data, &core->opp_pmdomain_tbl); + /* backwards compatibility for incomplete ABI SM8250 */ + if (ret == -ENODEV && + of_device_is_compatible(core->dev->of_node, "qcom,sm8250-venus")) { + iris_opp_pd_data.num_pd_names--; + ret = devm_pm_domain_attach_list(core->dev, &iris_opp_pd_data, + &core->opp_pmdomain_tbl); + } if (ret < 0) return ret; From 8d855fbb913039b841f7fabadc5fa019deb3f94b Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Mon, 9 Feb 2026 03:32:18 +0200 Subject: [PATCH 0862/2758] media: venus: scale MMCX power domain on SM8250 [ Upstream commit f45fd3dbb5f6d60a8b31d5cc4affaf1ef40163b3 ] On SM8250 most of the video clocks are powered by the MMCX domain, while the PLL is powered on by the MX domain. Extend the driver to support scaling both power domains, while keeping compatibility with the existing DTs, which define only the MX domain. Fixes: 0aeabfa29a9c ("media: venus: core: add sm8250 DT compatible and resource data") Reviewed-by: Bryan O'Donoghue Reviewed-by: Dikshita Agarwal Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue Signed-off-by: Sasha Levin --- drivers/media/platform/qcom/venus/core.c | 7 ++++++- drivers/media/platform/qcom/venus/core.h | 1 + drivers/media/platform/qcom/venus/pm_helpers.c | 8 +++++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 7e639760c41d9..00fb6806c1298 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -882,6 +882,7 @@ static const struct venus_resources sdm845_res_v2 = { .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0", "vcodec1" }, .vcodec_pmdomains_num = 3, .opp_pmdomain = (const char *[]) { "cx" }, + .opp_pmdomain_num = 1, .vcodec_num = 2, .max_load = 3110400, /* 4096x2160@90 */ .hfi_version = HFI_VERSION_4XX, @@ -933,6 +934,7 @@ static const struct venus_resources sc7180_res = { .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, .vcodec_pmdomains_num = 2, .opp_pmdomain = (const char *[]) { "cx" }, + .opp_pmdomain_num = 1, .vcodec_num = 1, .hfi_version = HFI_VERSION_4XX, .vpu_version = VPU_VERSION_AR50, @@ -991,7 +993,8 @@ static const struct venus_resources sm8250_res = { .vcodec_clks_num = 1, .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, .vcodec_pmdomains_num = 2, - .opp_pmdomain = (const char *[]) { "mx" }, + .opp_pmdomain = (const char *[]) { "mx", "mmcx" }, + .opp_pmdomain_num = 2, .vcodec_num = 1, .max_load = 7833600, .hfi_version = HFI_VERSION_6XX, @@ -1053,6 +1056,7 @@ static const struct venus_resources sc7280_res = { .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, .vcodec_pmdomains_num = 2, .opp_pmdomain = (const char *[]) { "cx" }, + .opp_pmdomain_num = 1, .vcodec_num = 1, .hfi_version = HFI_VERSION_6XX, .vpu_version = VPU_VERSION_IRIS2_1, @@ -1100,6 +1104,7 @@ static const struct venus_resources qcm2290_res = { .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, .vcodec_pmdomains_num = 2, .opp_pmdomain = (const char *[]) { "cx" }, + .opp_pmdomain_num = 1, .vcodec_num = 1, .hfi_version = HFI_VERSION_4XX, .vpu_version = VPU_VERSION_AR50_LITE, diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 7506f5d0f609a..70e7b40affa93 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -83,6 +83,7 @@ struct venus_resources { const char **vcodec_pmdomains; unsigned int vcodec_pmdomains_num; const char **opp_pmdomain; + unsigned int opp_pmdomain_num; unsigned int vcodec_num; const char * const resets[VIDC_RESETS_NUM_MAX]; unsigned int resets_num; diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c index f0269524ac70e..14a4e8311a643 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.c +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -887,7 +887,7 @@ static int vcodec_domains_get(struct venus_core *core) }; struct dev_pm_domain_attach_data opp_pd_data = { .pd_names = res->opp_pmdomain, - .num_pd_names = 1, + .num_pd_names = res->opp_pmdomain_num, .pd_flags = PD_FLAG_DEV_LINK_ON | PD_FLAG_REQUIRED_OPP, }; @@ -904,6 +904,12 @@ static int vcodec_domains_get(struct venus_core *core) /* Attach the power domain for setting performance state */ ret = devm_pm_domain_attach_list(dev, &opp_pd_data, &core->opp_pmdomain); + /* backwards compatibility for incomplete ABI SM8250 */ + if (ret == -ENODEV && + of_device_is_compatible(dev->of_node, "qcom,sm8250-venus")) { + opp_pd_data.num_pd_names--; + ret = devm_pm_domain_attach_list(dev, &opp_pd_data, &core->opp_pmdomain); + } if (ret < 0) return ret; From d2a2614d487c1b4b2680a1e231fadafca863d349 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 14 Mar 2025 20:21:48 -0700 Subject: [PATCH 0863/2758] iommu/amd: Fix a stale comment about which legacy mode is user visible [ Upstream commit 4bf53c2d0c08bbdaa32f2114281f1ddab61902bf ] Update a stale comment about which of the legacy modes is visible to the user, i.e. can be forced via amd_iommu_intr=legacy. Fixes: b74aa02d7a30 ("iommu/amd: Fix legacy interrupt remapping for x2APIC-enabled system") Signed-off-by: Sean Christopherson Reviewed-by: Vasant Hegde Reviewed-by: Wei Wang Signed-off-by: Joerg Roedel Signed-off-by: Sasha Levin --- drivers/iommu/amd/amd_iommu_types.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h index f9f7180878930..c726d115939a6 100644 --- a/drivers/iommu/amd/amd_iommu_types.h +++ b/drivers/iommu/amd/amd_iommu_types.h @@ -948,12 +948,13 @@ static inline int get_hpet_devid(int id) } enum amd_iommu_intr_mode_type { - AMD_IOMMU_GUEST_IR_LEGACY, - - /* This mode is not visible to users. It is used when - * we cannot fully enable vAPIC and fallback to only support - * legacy interrupt remapping via 128-bit IRTE. + /* + * The legacy format mode is not visible to users to prevent the user + * from crashing x2APIC systems, which for all intents and purposes + * require 128-bit IRTEs. The legacy format will be forced as needed + * when hardware doesn't support 128-bit IRTEs. */ + AMD_IOMMU_GUEST_IR_LEGACY, AMD_IOMMU_GUEST_IR_LEGACY_GA, AMD_IOMMU_GUEST_IR_VAPIC, }; From 5bd6191d0f8145e5bcca943058278cc3ffeae1b8 Mon Sep 17 00:00:00 2001 From: Luca Leonardo Scorcia Date: Tue, 5 May 2026 22:13:16 +0100 Subject: [PATCH 0864/2758] soc: mediatek: mtk-mmsys: Restore MT8167 routing masks lost during merge [ Upstream commit 7d462de9f65b002b439b1b168bf3b5579b0de48b ] The original patch that was sent to the mailing lists included the values for the route masks, but they got lost during merge: add back the full register masks where missing. Fixes: 060f7875bd23 ("soc: mediatek: mmsys: Add support for MT8167 SoC") Signed-off-by: Luca Leonardo Scorcia Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: AngeloGioacchino Del Regno Signed-off-by: Sasha Levin --- drivers/soc/mediatek/mt8167-mmsys.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/soc/mediatek/mt8167-mmsys.h b/drivers/soc/mediatek/mt8167-mmsys.h index c468926561b47..eef14083c47b5 100644 --- a/drivers/soc/mediatek/mt8167-mmsys.h +++ b/drivers/soc/mediatek/mt8167-mmsys.h @@ -10,24 +10,29 @@ #define MT8167_DISP_REG_CONFIG_DISP_RDMA0_SOUT_SEL_IN 0x06c #define MT8167_DITHER_MOUT_EN_RDMA0 0x1 +#define MT8167_DITHER_MOUT_EN_MASK 0x7 + #define MT8167_RDMA0_SOUT_DSI0 0x2 +#define MT8167_RDMA0_SOUT_MASK 0x3 + #define MT8167_DSI0_SEL_IN_RDMA0 0x1 +#define MT8167_DSI0_SEL_IN_MASK 0x3 static const struct mtk_mmsys_routes mt8167_mmsys_routing_table[] = { MMSYS_ROUTE(OVL0, COLOR0, MT8167_DISP_REG_CONFIG_DISP_OVL0_MOUT_EN, OVL0_MOUT_EN_COLOR0, OVL0_MOUT_EN_COLOR0), MMSYS_ROUTE(DITHER0, RDMA0, - MT8167_DISP_REG_CONFIG_DISP_DITHER_MOUT_EN, MT8167_DITHER_MOUT_EN_RDMA0, + MT8167_DISP_REG_CONFIG_DISP_DITHER_MOUT_EN, MT8167_DITHER_MOUT_EN_MASK, MT8167_DITHER_MOUT_EN_RDMA0), MMSYS_ROUTE(OVL0, COLOR0, MT8167_DISP_REG_CONFIG_DISP_COLOR0_SEL_IN, COLOR0_SEL_IN_OVL0, COLOR0_SEL_IN_OVL0), MMSYS_ROUTE(RDMA0, DSI0, - MT8167_DISP_REG_CONFIG_DISP_DSI0_SEL_IN, MT8167_DSI0_SEL_IN_RDMA0, + MT8167_DISP_REG_CONFIG_DISP_DSI0_SEL_IN, MT8167_DSI0_SEL_IN_MASK, MT8167_DSI0_SEL_IN_RDMA0), MMSYS_ROUTE(RDMA0, DSI0, - MT8167_DISP_REG_CONFIG_DISP_RDMA0_SOUT_SEL_IN, MT8167_RDMA0_SOUT_DSI0, + MT8167_DISP_REG_CONFIG_DISP_RDMA0_SOUT_SEL_IN, MT8167_RDMA0_SOUT_MASK, MT8167_RDMA0_SOUT_DSI0), }; From 0160edf2af51c5fde742973742c5d10497901b21 Mon Sep 17 00:00:00 2001 From: Matt Bobrowski Date: Thu, 30 Apr 2026 07:38:36 +0000 Subject: [PATCH 0865/2758] bpf: fix crash in bpf_[set|remove]_dentry_xattr for negative dentries [ Upstream commit 07410646f6ff1d23222f105ccab778957d401bbe ] bpf_set_dentry_xattr and bpf_remove_dentry_xattr BPF kfuncs attempt to lock the inode of the supplied dentry without checking if it is NULL. If a negative dentry is passed (e.g. from security_inode_create), d_inode(dentry) returns NULL, and inode_lock(inode) will cause a NULL pointer dereference. Trivially fix this by adding a NULL check for inode before attempting to lock it, returning -EINVAL if it is NULL. Additionally, drop WARN_ON(!inode) in bpf_xattr_read_permission() and bpf_xattr_write_permission(). These warnings could be triggered by passing a negative dentry to bpf_get_dentry_xattr() or the _locked variants of the xattr kfuncs, potentially causing a Denial of Service on systems with panic_on_warn enabled. Instead, simply return -EINVAL. Reported-by: Quan Sun <2022090917019@std.uestc.edu.cn> Closes: https://lore.kernel.org/bpf/1587cbf4-1293-4e25-ad24-c970836a1686@std.uestc.edu.cn/ Fixes: 56467292794b ("bpf: fs/xattr: Add BPF kfuncs to set and remove xattrs") Signed-off-by: Matt Bobrowski Link: https://patch.msgid.link/20260430073836.2894001-1-mattbobrowski@google.com Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- fs/bpf_fs_kfuncs.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/bpf_fs_kfuncs.c b/fs/bpf_fs_kfuncs.c index e4e51a1d0de28..606319dd69e80 100644 --- a/fs/bpf_fs_kfuncs.c +++ b/fs/bpf_fs_kfuncs.c @@ -100,7 +100,7 @@ static bool match_security_bpf_prefix(const char *name__str) static int bpf_xattr_read_permission(const char *name, struct inode *inode) { - if (WARN_ON(!inode)) + if (!inode) return -EINVAL; /* Allow reading xattr with user. and security.bpf. prefix */ @@ -170,7 +170,7 @@ __bpf_kfunc_end_defs(); static int bpf_xattr_write_permission(const char *name, struct inode *inode) { - if (WARN_ON(!inode)) + if (!inode) return -EINVAL; /* Only allow setting and removing security.bpf. xattrs */ @@ -289,6 +289,9 @@ __bpf_kfunc int bpf_set_dentry_xattr(struct dentry *dentry, const char *name__st struct inode *inode = d_inode(dentry); int ret; + if (!inode) + return -EINVAL; + inode_lock(inode); ret = bpf_set_dentry_xattr_locked(dentry, name__str, value_p, flags); inode_unlock(inode); @@ -314,6 +317,9 @@ __bpf_kfunc int bpf_remove_dentry_xattr(struct dentry *dentry, const char *name_ struct inode *inode = d_inode(dentry); int ret; + if (!inode) + return -EINVAL; + inode_lock(inode); ret = bpf_remove_dentry_xattr_locked(dentry, name__str); inode_unlock(inode); From 9ac7c0ae081fceec3ab0982572d3983d2a9b6bfc Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 7 Apr 2026 18:03:13 +0200 Subject: [PATCH 0866/2758] uaccess: fix ignored_trailing logic in copy_struct_to_user() [ Upstream commit 4911de3145a797389577abfdf9a5185d36cc18d7 ] Currently all callers pass ignored_trailing=NULL, but I have code that will make use of. Now it actually behaves like documented: * If @usize < @ksize, then the kernel is trying to pass userspace a newer struct than it supports. Thus we only copy the interoperable portions (@usize) and ignore the rest (but @ignored_trailing is set to %true if any of the trailing (@ksize - @usize) bytes are non-zero). Fixes: 424a55a4a908 ("uaccess: add copy_struct_to_user helper") Cc: Dmitry Safonov <0x7f454c46@gmail.com> Cc: Dmitry Safonov Cc: Francesco Ruggeri Cc: Salam Noureddine Cc: David Ahern Cc: David S. Miller Cc: Michal Luczaj Cc: David Wei Cc: Luiz Augusto von Dentz Cc: Luiz Augusto von Dentz Cc: Marcel Holtmann Cc: Xin Long Cc: Eric Dumazet Cc: Kuniyuki Iwashima Cc: Paolo Abeni Cc: Willem de Bruijn Cc: Neal Cardwell Cc: Jakub Kicinski Cc: Simon Horman Cc: Aleksa Sarai Cc: Christian Brauner CC: Kees Cook Cc: netdev@vger.kernel.org Cc: linux-bluetooth@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Stefan Metzmacher Link: https://patch.msgid.link/71f69442410c1186ed8ce6d5b4b9d4a5a70edbad.1775576651.git.metze@samba.org Reviewed-by: Aleksa Sarai Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- include/linux/uaccess.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h index 56328601218c5..09a09cc4aac27 100644 --- a/include/linux/uaccess.h +++ b/include/linux/uaccess.h @@ -510,7 +510,7 @@ copy_struct_to_user(void __user *dst, size_t usize, const void *src, return -EFAULT; } if (ignored_trailing) - *ignored_trailing = ksize < usize && + *ignored_trailing = usize < ksize && memchr_inv(src + size, 0, rest) != NULL; /* Copy the interoperable parts of the struct. */ if (copy_to_user(dst, src, size)) From 5f12ed66624b5b769276580afefda721200c6aea Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 7 Apr 2026 18:03:14 +0200 Subject: [PATCH 0867/2758] sockptr: fix usize check in copy_struct_from_sockptr() for user pointers [ Upstream commit db0493512931fe1e5a71612e6a358df1aa22d80c ] copy_struct_from_user will never hit the check_zeroed_user() call and will never return -E2BIG if new userspace passed new bits in a larger structure than the current kernel structure. As far as I can there are no critical/related uapi changes in - include/net/bluetooth/bluetooth.h and net/bluetooth/sco.c after the use of copy_struct_from_sockptr in v6.13-rc3 - include/uapi/linux/tcp.h and net/ipv4/tcp_ao.c after the use of copy_struct_from_sockptr in v6.6-rc1 So that new callers will get the correct behavior from the start. Fixes: 4954f17ddefc ("net/tcp: Introduce TCP_AO setsockopt()s") Fixes: ef84703a911f ("net/tcp: Add TCP-AO getsockopt()s") Fixes: faadfaba5e01 ("net/tcp: Add TCP_AO_REPAIR") Fixes: 3e643e4efa1e ("Bluetooth: Improve setsockopt() handling of malformed user input") Cc: Dmitry Safonov <0x7f454c46@gmail.com> Cc: Dmitry Safonov Cc: Francesco Ruggeri Cc: Salam Noureddine Cc: David Ahern Cc: David S. Miller Cc: Michal Luczaj Cc: David Wei Cc: Luiz Augusto von Dentz Cc: Luiz Augusto von Dentz Cc: Marcel Holtmann Cc: Xin Long Cc: Eric Dumazet Cc: Kuniyuki Iwashima Cc: Paolo Abeni Cc: Willem de Bruijn Cc: Neal Cardwell Cc: Jakub Kicinski Cc: Simon Horman Cc: Aleksa Sarai Cc: Christian Brauner CC: Kees Cook Cc: netdev@vger.kernel.org Cc: linux-bluetooth@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Stefan Metzmacher Link: https://patch.msgid.link/cfaedbc33ae9d36adaabf04fa79424f30ff1efdd.1775576651.git.metze@samba.org Reviewed-by: Aleksa Sarai Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- include/linux/sockptr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/sockptr.h b/include/linux/sockptr.h index 3e6c8e9d67aef..ba88f4d78c1b1 100644 --- a/include/linux/sockptr.h +++ b/include/linux/sockptr.h @@ -91,7 +91,7 @@ static inline int copy_struct_from_sockptr(void *dst, size_t ksize, size_t rest = max(ksize, usize) - size; if (!sockptr_is_kernel(src)) - return copy_struct_from_user(dst, ksize, src.user, size); + return copy_struct_from_user(dst, ksize, src.user, usize); if (usize < ksize) { memset(dst + size, 0, rest); From 2b6f1de3aa5e1545b99e6b6f17ec7b9894e9d915 Mon Sep 17 00:00:00 2001 From: Frank Wunderlich Date: Sun, 12 Apr 2026 11:23:32 +0200 Subject: [PATCH 0868/2758] arm64: dts: mediatek: mt7988a-bpi-r4pro: rework pcie gpio-hog handling [ Upstream commit e309fa232d12b969afac43a4b7d83dee025cfe63 ] The active-high property in base-dt cannot be overwritten and must be set in separate overlay. Fixes: f397471a6a8c ("arm64: dts: mediatek: mt7988: Add devicetree for BananaPi R4 Pro") Signed-off-by: Frank Wunderlich Signed-off-by: AngeloGioacchino Del Regno Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/mediatek/Makefile | 8 ++++++++ .../mt7988a-bananapi-bpi-r4-pro-cn13.dtso | 20 +++++++++++++++++++ .../mt7988a-bananapi-bpi-r4-pro-cn14.dtso | 20 +++++++++++++++++++ .../mediatek/mt7988a-bananapi-bpi-r4-pro.dtsi | 2 -- 4 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-pro-cn13.dtso create mode 100644 arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-pro-cn14.dtso diff --git a/arch/arm64/boot/dts/mediatek/Makefile b/arch/arm64/boot/dts/mediatek/Makefile index 387faa9c2a09b..a86fb313b1a9d 100644 --- a/arch/arm64/boot/dts/mediatek/Makefile +++ b/arch/arm64/boot/dts/mediatek/Makefile @@ -47,6 +47,8 @@ dtb-$(CONFIG_ARCH_MEDIATEK) += mt7988a-bananapi-bpi-r4-2g5.dtb dtb-$(CONFIG_ARCH_MEDIATEK) += mt7988a-bananapi-bpi-r4-emmc.dtbo dtb-$(CONFIG_ARCH_MEDIATEK) += mt7988a-bananapi-bpi-r4-pro-4e.dtb dtb-$(CONFIG_ARCH_MEDIATEK) += mt7988a-bananapi-bpi-r4-pro-8x.dtb +dtb-$(CONFIG_ARCH_MEDIATEK) += mt7988a-bananapi-bpi-r4-pro-cn13.dtbo +dtb-$(CONFIG_ARCH_MEDIATEK) += mt7988a-bananapi-bpi-r4-pro-cn14.dtbo dtb-$(CONFIG_ARCH_MEDIATEK) += mt7988a-bananapi-bpi-r4-pro-cn15.dtbo dtb-$(CONFIG_ARCH_MEDIATEK) += mt7988a-bananapi-bpi-r4-pro-cn18.dtbo dtb-$(CONFIG_ARCH_MEDIATEK) += mt7988a-bananapi-bpi-r4-pro-emmc.dtbo @@ -70,18 +72,24 @@ mt7988a-bananapi-bpi-r4-2g5-sd-dtbs := \ dtb-$(CONFIG_ARCH_MEDIATEK) += mt7988a-bananapi-bpi-r4-2g5-sd.dtb mt7988a-bananapi-bpi-r4-pro-8x-emmc-dtbs := \ mt7988a-bananapi-bpi-r4-pro-8x.dtb \ + mt7988a-bananapi-bpi-r4-pro-cn13.dtbo \ + mt7988a-bananapi-bpi-r4-pro-cn14.dtbo \ mt7988a-bananapi-bpi-r4-pro-emmc.dtbo dtb-$(CONFIG_ARCH_MEDIATEK) += mt7988a-bananapi-bpi-r4-pro-8x-emmc.dtb mt7988a-bananapi-bpi-r4-pro-8x-sd-dtbs := \ mt7988a-bananapi-bpi-r4-pro-8x.dtb \ + mt7988a-bananapi-bpi-r4-pro-cn13.dtbo \ + mt7988a-bananapi-bpi-r4-pro-cn14.dtbo \ mt7988a-bananapi-bpi-r4-pro-sd.dtbo dtb-$(CONFIG_ARCH_MEDIATEK) += mt7988a-bananapi-bpi-r4-pro-8x-sd.dtb mt7988a-bananapi-bpi-r4-pro-8x-sd-cn15-dtbs := \ mt7988a-bananapi-bpi-r4-pro-8x-sd.dtb \ + mt7988a-bananapi-bpi-r4-pro-cn14.dtbo \ mt7988a-bananapi-bpi-r4-pro-cn15.dtbo dtb-$(CONFIG_ARCH_MEDIATEK) += mt7988a-bananapi-bpi-r4-pro-8x-sd-cn15.dtb mt7988a-bananapi-bpi-r4-pro-8x-sd-cn18-dtbs := \ mt7988a-bananapi-bpi-r4-pro-8x-sd.dtb \ + mt7988a-bananapi-bpi-r4-pro-cn13.dtbo \ mt7988a-bananapi-bpi-r4-pro-cn18.dtbo dtb-$(CONFIG_ARCH_MEDIATEK) += mt7988a-bananapi-bpi-r4-pro-8x-sd-cn18.dtb dtb-$(CONFIG_ARCH_MEDIATEK) += mt8167-pumpkin.dtb diff --git a/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-pro-cn13.dtso b/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-pro-cn13.dtso new file mode 100644 index 0000000000000..973b76ba0cbfb --- /dev/null +++ b/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-pro-cn13.dtso @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2025 MediaTek Inc. + * Author: Frank Wunderlich + */ + +/* This enables key-m slot CN13 on pcie2(11280000 1L0) on BPI-R4-Pro */ + +/dts-v1/; +/plugin/; + +#include + +/ { + compatible = "bananapi,bpi-r4-pro", "mediatek,mt7988a"; +}; + +&{/soc/pinctrl@1001f000/pcie-2-hog} { + output-high; +}; diff --git a/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-pro-cn14.dtso b/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-pro-cn14.dtso new file mode 100644 index 0000000000000..90b2a64459c31 --- /dev/null +++ b/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-pro-cn14.dtso @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2025 MediaTek Inc. + * Author: Frank Wunderlich + */ + +/* This enables key-m slot CN14 on pcie3(11290000 1L1) on BPI-R4-Pro */ + +/dts-v1/; +/plugin/; + +#include + +/ { + compatible = "bananapi,bpi-r4-pro", "mediatek,mt7988a"; +}; + +&{/soc/pinctrl@1001f000/pcie-3-hog} { + output-high; +}; diff --git a/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-pro.dtsi b/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-pro.dtsi index a48132f094114..ff778e8305d5b 100644 --- a/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-pro.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-pro.dtsi @@ -437,14 +437,12 @@ pcie-2-hog { gpio-hog; gpios = <79 GPIO_ACTIVE_HIGH>; - output-high; }; /* 1L1 0=key-b (CN18), 1=key-m (CN14) */ pcie-3-hog { gpio-hog; gpios = <63 GPIO_ACTIVE_HIGH>; - output-high; }; pwm0_pins: pwm0-pins { From ce75126fa35b445c1978eda7db5f15d49058bb78 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 30 Apr 2026 20:07:24 +0800 Subject: [PATCH 0869/2758] arm64: dts: mediatek: mt8192-asurada: Move PCIe DMA bounce buffer to host [ Upstream commit 533275a4b5240a2bf2c592bd4536560388306d26 ] The DMA bounce buffer is attached to the PCIe host controller, i.e. all PCIe DMA transfers should use it. Move it from the PCIe (WiFi) device node down to the PCIe host controller node. Fixes: 0dca9f0b3e63 ("arm64: dts: mediatek: asurada: Enable PCIe and add WiFi") Signed-off-by: Chen-Yu Tsai Signed-off-by: AngeloGioacchino Del Regno Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/mediatek/mt8192-asurada.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/mediatek/mt8192-asurada.dtsi b/arch/arm64/boot/dts/mediatek/mt8192-asurada.dtsi index eadf1b2d156f2..95d4db2b79794 100644 --- a/arch/arm64/boot/dts/mediatek/mt8192-asurada.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8192-asurada.dtsi @@ -525,6 +525,7 @@ &pcie { pinctrl-names = "default"; pinctrl-0 = <&pcie_pins>; + memory-region = <&wifi_restricted_dma_region>; pcie0: pcie@0,0 { device_type = "pci"; @@ -539,7 +540,6 @@ wifi: wifi@0,0 { reg = <0x10000 0 0 0 0x100000>, <0x10000 0 0x100000 0 0x100000>; - memory-region = <&wifi_restricted_dma_region>; }; }; }; From 4f250d54c6efe98e7e19821ce2bf7e58248a2c7f Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Mon, 11 May 2026 05:26:55 -0700 Subject: [PATCH 0870/2758] workqueue: forbid TEST_WORKQUEUE from being built-in [ Upstream commit 1503043fd75e29ad49c7d506232e272f6951d07d ] The benchmark drives the workqueue's affinity_scope through sysfs by filp_open()'ing /sys/bus/workqueue/devices/bench_wq/affinity_scope. When CONFIG_TEST_WORKQUEUE=y, the module_init runs during kernel init before userspace has mounted sysfs, so every open returns -ENOENT and the benchmark loop spins emitting: test_workqueue: open /sys/bus/workqueue/devices/bench_wq/affinity_scope failed: -2 Mirror the TEST_BPF pattern and add "depends on m" so Kconfig will not let this be built into the kernel image, and document the reason in the help text. Fixes: 24b2e73f9700 ("workqueue: add test_workqueue benchmark module") Signed-off-by: Breno Leitao Signed-off-by: Tejun Heo Signed-off-by: Sasha Levin --- lib/Kconfig.debug | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 8ff5adcfe1e0a..040f4e077435b 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -2649,12 +2649,17 @@ config TEST_VMALLOC config TEST_WORKQUEUE tristate "Test module for stress/performance analysis of workqueue" + depends on m default n help This builds the "test_workqueue" module for benchmarking workqueue throughput under contention. Useful for evaluating affinity scope changes (e.g., cache_shard vs cache). + The test drives sysfs to switch affinity scopes, so it must be + loaded after userspace has mounted sysfs; building it in (=y) + would run module_init before /sys is available. + If unsure, say N. config TEST_BPF From 5a4cb517245032d5be55175f82f3d67895e8f5c4 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Thu, 7 May 2026 16:09:51 +0800 Subject: [PATCH 0871/2758] drm/amdgpu: fix error return code in mes_v12_1_map_test_bo [ Upstream commit 5da1e1dfd15727918aab2c1a79c019f179991e80 ] The function mes_v12_1_map_test_bo incorrectly returned 0 unconditionallyon error path, which would hide the real error code and mislead upperlayers about the failure status. Fix it by returning the correct error code 'r' instead of 0. Fixes: 44e5195fa3d4 ("drm/amdgpu/mes_v12_1: add mes self test"); Signed-off-by: Yang Wang Reviewed-by: Asad Kamal Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdgpu/mes_v12_1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c b/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c index cec8012781265..be86802fef33b 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c @@ -2016,7 +2016,7 @@ static int mes_v12_1_map_test_bo(struct amdgpu_device *adev, error: amdgpu_sync_free(&sync); - return 0; + return r; } static int mes_v12_1_test_ring(struct amdgpu_device *adev, int xcc_id, From 6d2b08731caf6b6c9f77d1992acfa953ddf5492d Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Wed, 15 Apr 2026 10:52:57 +0300 Subject: [PATCH 0872/2758] arm64: dts: qcom: glymur-crd: Drop forced host mode for USB SS0 and SS1 [ Upstream commit d9dc254945756b44fa6f61b9cbd4adf0413c6a6f ] The two USB Type-C ports on Glymur CRD are dual-role capable. Do not force their controllers into host mode. Drop the explicit 'dr_mode = "host"' properties so they can use their default OTG mode instead. Fixes: c8b63029455b ("arm64: dts: qcom: glymur-crd: Enable USB support") Signed-off-by: Abel Vesa Link: https://lore.kernel.org/r/20260415-dts-qcom-glymur-usb-role-switch-fix-v1-2-409e1a257f1f@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/glymur-crd.dts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/glymur-crd.dts b/arch/arm64/boot/dts/qcom/glymur-crd.dts index 35aaf09e4e2b4..c98dfb3941fa3 100644 --- a/arch/arm64/boot/dts/qcom/glymur-crd.dts +++ b/arch/arm64/boot/dts/qcom/glymur-crd.dts @@ -322,8 +322,6 @@ }; &usb_0 { - dr_mode = "host"; - status = "okay"; }; @@ -353,8 +351,6 @@ }; &usb_1 { - dr_mode = "host"; - status = "okay"; }; From bbd427f79dd642cc48ffd39ca3f9d4f1cc84e255 Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Wed, 15 Apr 2026 10:52:56 +0300 Subject: [PATCH 0873/2758] arm64: dts: qcom: glymur: Mark USB SS1 and SS2 as role-switch capable [ Upstream commit 347fa2fa64e1cef0c7897522896c0c66e734c82b ] Like USB SS0, the USB SS1 and SS2 controllers on Glymur also support USB role switching. Describe this by adding the 'usb-role-switch' property to both controllers. Fixes: 4eee57dd4df9 ("arm64: dts: qcom: glymur: Add USB related nodes") Signed-off-by: Abel Vesa Link: https://lore.kernel.org/r/20260415-dts-qcom-glymur-usb-role-switch-fix-v1-1-409e1a257f1f@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/glymur.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/glymur.dtsi b/arch/arm64/boot/dts/qcom/glymur.dtsi index 7283d45ca259a..780708a9d8d45 100644 --- a/arch/arm64/boot/dts/qcom/glymur.dtsi +++ b/arch/arm64/boot/dts/qcom/glymur.dtsi @@ -3920,6 +3920,8 @@ snps,dis_u2_susphy_quirk; snps,dis_enblslpm_quirk; + usb-role-switch; + status = "disabled"; ports { @@ -3993,6 +3995,8 @@ snps,dis_u2_susphy_quirk; snps,dis_enblslpm_quirk; + usb-role-switch; + status = "disabled"; ports { From 35f6814c98b987cd5b1a29b485301e888b37e461 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 27 Apr 2026 13:09:57 +0200 Subject: [PATCH 0874/2758] rhashtable: give each instance its own lockdep class [ Upstream commit 060d4e94b8d400b62453890821bd7feecd4cde2c ] syzbot reported a possible circular locking dependency between &ht->mutex and fs_reclaim: CPU0 (kswapd0) CPU1 (kworker) -------------- -------------- fs_reclaim ht->mutex shmem_evict_inode rhashtable_rehash_alloc simple_xattrs_free bucket_table_alloc(GFP_KERNEL) rhashtable_free_and_destroy __kvmalloc_node mutex_lock(&ht->mutex) might_alloc -> fs_reclaim The two halves of the splat refer to two different events on &ht->mutex. The kswapd0 path is unambiguous: shmem_evict_inode at mm/shmem.c:1429 calls simple_xattrs_free(), which calls rhashtable_free_and_destroy() on the per-inode simple_xattrs rhashtable being torn down with the inode. The previously-recorded ht->mutex -> fs_reclaim edge comes from rht_deferred_worker -> rhashtable_rehash_alloc -> bucket_table_alloc(GFP_KERNEL) -> __kvmalloc_node -> might_alloc -> fs_reclaim. That stack stops at generic library code: there is no subsystem-specific frame above rht_deferred_worker, so the splat does not identify which rhashtable's worker recorded the edge -- only that some rhashtable in the system did. Whether or not that recording happened on the same simple_xattrs ht that is now being destroyed, the predicted deadlock cannot occur: rhashtable_free_and_destroy() does cancel_work_sync(&ht->run_work) before taking ht->mutex, so the deferred worker cannot be running on the instance being torn down. If the recording was on a different rhashtable instance, the two ht->mutex acquisitions are on distinct mutex objects and cannot deadlock either. Lockdep flags a cycle regardless because mutex_init(&ht->mutex) lives on a single source line in rhashtable_init_noprof(), so every ht->mutex in the kernel shares one static lockdep class. Lockdep matches by class, not by instance, and collapses all of these into one node. Lift the lockdep key out of rhashtable_init_noprof() and into the caller. The user-visible rhashtable_init_noprof() / rhltable_init_noprof() identifiers become macros that declare a per-call-site static lock_class_key. Link: https://patch.msgid.link/20260427-work-rhashtable-lockdep-v1-1-f69e8bd91cb2@kernel.org Fixes: c6307674ed82 ("mm: kvmalloc: add non-blocking support for vmalloc") Acked-by: Michal Hocko Reported-by: syzbot+5af806780f38a5fe691f@syzkaller.appspotmail.com Closes: https://lore.kernel.org/69e798fe.050a0220.24bfd3.0032.GAE@google.com Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- include/linux/rhashtable-types.h | 22 ++++++++++++++++++---- lib/rhashtable.c | 17 ++++++++++------- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/include/linux/rhashtable-types.h b/include/linux/rhashtable-types.h index fc2f596a6df1b..57c11ec9dc645 100644 --- a/include/linux/rhashtable-types.h +++ b/include/linux/rhashtable-types.h @@ -136,12 +136,26 @@ struct rhashtable_iter { bool end_of_table; }; -int rhashtable_init_noprof(struct rhashtable *ht, - const struct rhashtable_params *params); +int __rhashtable_init_noprof(struct rhashtable *ht, + const struct rhashtable_params *params, + struct lock_class_key *key); +#define rhashtable_init_noprof(ht, params) \ +({ \ + static struct lock_class_key __key; \ + \ + __rhashtable_init_noprof(ht, params, &__key); \ +}) #define rhashtable_init(...) alloc_hooks(rhashtable_init_noprof(__VA_ARGS__)) -int rhltable_init_noprof(struct rhltable *hlt, - const struct rhashtable_params *params); +int __rhltable_init_noprof(struct rhltable *hlt, + const struct rhashtable_params *params, + struct lock_class_key *key); +#define rhltable_init_noprof(hlt, params) \ +({ \ + static struct lock_class_key __key; \ + \ + __rhltable_init_noprof(hlt, params, &__key); \ +}) #define rhltable_init(...) alloc_hooks(rhltable_init_noprof(__VA_ARGS__)) #endif /* _LINUX_RHASHTABLE_TYPES_H */ diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 04b3a808fca9f..c0ba34eadb397 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -1057,8 +1057,9 @@ static u32 rhashtable_jhash2(const void *key, u32 length, u32 seed) * .obj_hashfn = my_hash_fn, * }; */ -int rhashtable_init_noprof(struct rhashtable *ht, - const struct rhashtable_params *params) +int __rhashtable_init_noprof(struct rhashtable *ht, + const struct rhashtable_params *params, + struct lock_class_key *key) { struct bucket_table *tbl; size_t size; @@ -1068,7 +1069,7 @@ int rhashtable_init_noprof(struct rhashtable *ht, return -EINVAL; memset(ht, 0, sizeof(*ht)); - mutex_init(&ht->mutex); + mutex_init_with_key(&ht->mutex, key); spin_lock_init(&ht->lock); memcpy(&ht->p, params, sizeof(*params)); @@ -1120,7 +1121,7 @@ int rhashtable_init_noprof(struct rhashtable *ht, return 0; } -EXPORT_SYMBOL_GPL(rhashtable_init_noprof); +EXPORT_SYMBOL_GPL(__rhashtable_init_noprof); /** * rhltable_init - initialize a new hash list table @@ -1131,15 +1132,17 @@ EXPORT_SYMBOL_GPL(rhashtable_init_noprof); * * See documentation for rhashtable_init. */ -int rhltable_init_noprof(struct rhltable *hlt, const struct rhashtable_params *params) +int __rhltable_init_noprof(struct rhltable *hlt, + const struct rhashtable_params *params, + struct lock_class_key *key) { int err; - err = rhashtable_init_noprof(&hlt->ht, params); + err = __rhashtable_init_noprof(&hlt->ht, params, key); hlt->ht.rhlist = true; return err; } -EXPORT_SYMBOL_GPL(rhltable_init_noprof); +EXPORT_SYMBOL_GPL(__rhltable_init_noprof); static void rhashtable_free_one(struct rhashtable *ht, struct rhash_head *obj, void (*free_fn)(void *ptr, void *arg), From a2da42614ca75e276a60922ccbcfa42d61b14547 Mon Sep 17 00:00:00 2001 From: Hsiu Che Yu Date: Sat, 25 Apr 2026 18:16:19 +0800 Subject: [PATCH 0875/2758] rust: alloc: fix `Vec::extend_with` SAFETY comment [ Upstream commit f497aae6ded43f91b1dbf29a35d7062823635640 ] Fix an incorrect operator in the SAFETY comment, changing `<` to `<=`, since `Vec::reserve` guarantees capacity for exactly n additional elements, so the equal case should be included. Signed-off-by: Hsiu Che Yu Reviewed-by: Alexandre Courbot Fixes: 2aac4cd7dae3d ("rust: alloc: implement kernel `Vec` type") Link: https://patch.msgid.link/18fc8eee2f057a6bfbcadae156d1d0b7c40d0077.1777111268.git.yu.whisper.personal@gmail.com Signed-off-by: Danilo Krummrich Signed-off-by: Sasha Levin --- rust/kernel/alloc/kvec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs index 6438385e4322e..5c24fcf05bdfe 100644 --- a/rust/kernel/alloc/kvec.rs +++ b/rust/kernel/alloc/kvec.rs @@ -866,7 +866,7 @@ impl Vec { spare[n - 1].write(value); // SAFETY: - // - `self.len() + n < self.capacity()` due to the call to reserve above, + // - `self.len() + n <= self.capacity()` due to the call to reserve above, // - the loop and the line above initialized the next `n` elements. unsafe { self.inc_len(n) }; From d937ee3169a95410888d9ac4ee4a204396f242f3 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Fri, 8 May 2026 16:32:46 +0100 Subject: [PATCH 0876/2758] clk: scmi: Fix clock rate rounding [ Upstream commit d0c81a38d06d446d341532700b3a4a43d3b00eb1 ] While the do_div() helper used for rounding expects its divisor argument to be a 32bits quantity, the currently provided divisor parameter is a 64bit value that, as a consequence, is silently truncated and a possible source of bugs. Fix by using the proper div64_ul helper. Cc: Michael Turquette Cc: Stephen Boyd Cc: linux-clk@vger.kernel.org Fixes: 7a8655e19bdb ("clk: scmi: Fix the rounding of clock rate") Signed-off-by: Cristian Marussi Tested-by: Florian Fainelli Tested-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260508153300.2224715-2-cristian.marussi@arm.com Reviewed-by: Brian Masney Signed-off-by: Sudeep Holla Signed-off-by: Sasha Levin --- drivers/clk/clk-scmi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c index 6b286ea6f1218..b6a12f3bc123c 100644 --- a/drivers/clk/clk-scmi.c +++ b/drivers/clk/clk-scmi.c @@ -10,9 +10,9 @@ #include #include #include +#include #include #include -#include #define NOT_ATOMIC false #define ATOMIC true @@ -83,7 +83,7 @@ static int scmi_clk_determine_rate(struct clk_hw *hw, ftmp = req->rate - fmin; ftmp += clk->info->range.step_size - 1; /* to round up */ - do_div(ftmp, clk->info->range.step_size); + ftmp = div64_ul(ftmp, clk->info->range.step_size); req->rate = ftmp * clk->info->range.step_size + fmin; From 89abf027fa7c96d238811bc91946247641a95ce5 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 31 Mar 2026 18:56:46 +0200 Subject: [PATCH 0877/2758] arm64: dts: qcom: sm8750: Fix DSI1 phy reference clock rate [ Upstream commit f4d7c5875a215cd3989b59d13a9c30cec9f0a33b ] The DSI PHY CXO clock input is the SoC CXO divided by two. DSI0 already uses correct one, but DSI1 got copy-paste from SM8650. Wrong clock parent will cause incorrect DSI1 PHY PLL frequencies to be used making the DSI panel non-working, although there is no upstream user of DSI1. Fixes: 818ae2b389bc ("arm64: dts: qcom: sm8750: Add display (MDSS) with Display CC") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260331165645.233965-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sm8750.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sm8750.dtsi b/arch/arm64/boot/dts/qcom/sm8750.dtsi index 18fb52c14acd7..320aec62e462d 100644 --- a/arch/arm64/boot/dts/qcom/sm8750.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8750.dtsi @@ -3313,7 +3313,7 @@ "dsi_pll"; clocks = <&dispcc DISP_CC_MDSS_AHB_CLK>, - <&rpmhcc RPMH_CXO_CLK>; + <&bi_tcxo_div2>; clock-names = "iface", "ref"; From bf76824320bf8b536736fc0ddb15537df61134a7 Mon Sep 17 00:00:00 2001 From: Kuldeep Singh Date: Thu, 2 Apr 2026 00:05:09 +0530 Subject: [PATCH 0878/2758] arm64: dts: qcom: kodiak: Fix ICE reg size [ Upstream commit fa9c7403af64d3e56a6c20dedc60e0a55b527509 ] The ICE register region on Kodiak is currently defined as 0x8000 bytes. According to the hardware specification, the correct register size is 0x18000. Update the ICE node reg property to match the hardware. Fixes: dfd5ee7b34bb ("arm64: dts: qcom: sc7280: Add inline crypto engine") Signed-off-by: Kuldeep Singh Reviewed-by: Harshal Dev Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260402-ice_dt_reg_fix-v1-1-74e4c2129238@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/kodiak.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/kodiak.dtsi b/arch/arm64/boot/dts/qcom/kodiak.dtsi index 988ca5f7c8a0e..fdde9f065199e 100644 --- a/arch/arm64/boot/dts/qcom/kodiak.dtsi +++ b/arch/arm64/boot/dts/qcom/kodiak.dtsi @@ -2578,7 +2578,7 @@ ice: crypto@1d88000 { compatible = "qcom,sc7280-inline-crypto-engine", "qcom,inline-crypto-engine"; - reg = <0 0x01d88000 0 0x8000>; + reg = <0 0x01d88000 0 0x18000>; clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; }; From 3d7c342e478a38df98041350ccd6bae35746d286 Mon Sep 17 00:00:00 2001 From: Kuldeep Singh Date: Thu, 2 Apr 2026 00:05:10 +0530 Subject: [PATCH 0879/2758] arm64: dts: qcom: sm8450: Fix ICE reg size [ Upstream commit 7fb3d0512dacc0d09217eb45057357f00298203d ] The ICE register region size was originally described incorrectly when the ICE hardware was first introduced. The same value was later carried over unchanged when the ICE node was split out from the UFS node into its own DT entry. Correct the register size to match the hardware specification. Fixes: 276ee34a40c1 ("arm64: dts: qcom: sm8450: add Inline Crypto Engine registers and clock") Signed-off-by: Kuldeep Singh Reviewed-by: Harshal Dev Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260402-ice_dt_reg_fix-v1-2-74e4c2129238@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sm8450.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sm8450.dtsi b/arch/arm64/boot/dts/qcom/sm8450.dtsi index 03bf30b53f289..e0c37ce3042a2 100644 --- a/arch/arm64/boot/dts/qcom/sm8450.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8450.dtsi @@ -5373,7 +5373,7 @@ ice: crypto@1d88000 { compatible = "qcom,sm8450-inline-crypto-engine", "qcom,inline-crypto-engine"; - reg = <0 0x01d88000 0 0x8000>; + reg = <0 0x01d88000 0 0x18000>; clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; }; From 1c7a943d5b040ddf5dc470580cdb1e9f8712afa5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 12 May 2026 18:35:20 +0200 Subject: [PATCH 0880/2758] platform/chrome: chromeos_privacy_screen: Check ACPI_COMPANION() [ Upstream commit 8a4a217f617b1ac2f8c095f33efd67d947ddb2cf ] Every platform driver can be forced to match a device that doesn't match its list of device IDs because of device_match_driver_override(), so platform drivers that rely on the existence of a device's ACPI companion object need to verify its presence. Accordingly, add a requisite ACPI_COMPANION() check against NULL to the chromeos_privacy_screen driver. Fixes: d3c2872ae323 ("platform/chrome: Convert ChromeOS privacy-screen driver to platform") Signed-off-by: Rafael J. Wysocki Link: https://lore.kernel.org/r/3357444.5fSG56mABF@rafael.j.wysocki Signed-off-by: Tzung-Bi Shih Signed-off-by: Sasha Levin --- drivers/platform/chrome/chromeos_privacy_screen.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/platform/chrome/chromeos_privacy_screen.c b/drivers/platform/chrome/chromeos_privacy_screen.c index abc5d189a3894..407b04207de2f 100644 --- a/drivers/platform/chrome/chromeos_privacy_screen.c +++ b/drivers/platform/chrome/chromeos_privacy_screen.c @@ -104,6 +104,9 @@ static const struct drm_privacy_screen_ops chromeos_privacy_screen_ops = { static int chromeos_privacy_screen_probe(struct platform_device *pdev) { + if (!ACPI_COMPANION(&pdev->dev)) + return -ENODEV; + struct drm_privacy_screen *drm_privacy_screen = drm_privacy_screen_register(&pdev->dev, &chromeos_privacy_screen_ops, From f4061f6d56bc48f268569f3d2b664b45fa913e5e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 12 May 2026 18:35:54 +0200 Subject: [PATCH 0881/2758] platform/chrome: chromeos_tbmc: Check ACPI_COMPANION() [ Upstream commit c15dbae7c856fb53cc6ffb86c6c64ebb816d07c8 ] Every platform driver can be forced to match a device that doesn't match its list of device IDs because of device_match_driver_override(), so platform drivers that rely on the existence of a device's ACPI companion object need to verify its presence. Accordingly, add a requisite ACPI_COMPANION() check against NULL to the chromeos_tbmc driver. Fixes: a2676ead257f ("platform/chrome: chromeos_tbmc: Convert to a platform driver") Signed-off-by: Rafael J. Wysocki Link: https://lore.kernel.org/r/1875121.VLH7GnMWUR@rafael.j.wysocki Signed-off-by: Tzung-Bi Shih Signed-off-by: Sasha Levin --- drivers/platform/chrome/chromeos_tbmc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/platform/chrome/chromeos_tbmc.c b/drivers/platform/chrome/chromeos_tbmc.c index 5133806b2d958..fd756761a481f 100644 --- a/drivers/platform/chrome/chromeos_tbmc.c +++ b/drivers/platform/chrome/chromeos_tbmc.c @@ -69,9 +69,13 @@ static int chromeos_tbmc_probe(struct platform_device *pdev) { struct input_dev *idev; struct device *dev = &pdev->dev; - struct acpi_device *adev = ACPI_COMPANION(dev); + struct acpi_device *adev; int ret; + adev = ACPI_COMPANION(dev); + if (!adev) + return -ENODEV; + idev = devm_input_allocate_device(dev); if (!idev) return -ENOMEM; From bd74bb16c6be1256b323b64f24ffce71110319a0 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 12 May 2026 18:36:42 +0200 Subject: [PATCH 0882/2758] platform/chrome: wilco_ec: event: Check ACPI_COMPANION() [ Upstream commit 51dcff9796fd486d7abf01081ca62e4072789e9d ] Every platform driver can be forced to match a device that doesn't match its list of device IDs because of device_match_driver_override(), so platform drivers that rely on the existence of a device's ACPI companion object need to verify its presence. Accordingly, add a requisite ACPI_COMPANION() check against NULL to the wilco_ec event driver. Fixes: 27d58498f690 ("platform/chrome: wilco_ec: event: Convert to a platform driver") Signed-off-by: Rafael J. Wysocki Link: https://lore.kernel.org/r/2076666.usQuhbGJ8B@rafael.j.wysocki Signed-off-by: Tzung-Bi Shih Signed-off-by: Sasha Levin --- drivers/platform/chrome/wilco_ec/event.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/platform/chrome/wilco_ec/event.c b/drivers/platform/chrome/wilco_ec/event.c index b6e935badc0e9..1b5cb89839e08 100644 --- a/drivers/platform/chrome/wilco_ec/event.c +++ b/drivers/platform/chrome/wilco_ec/event.c @@ -452,8 +452,13 @@ static void hangup_device(struct event_device_data *dev_data) static int event_device_probe(struct platform_device *pdev) { struct event_device_data *dev_data; + struct acpi_device *adev; int error, minor; + adev = ACPI_COMPANION(&pdev->dev); + if (!adev) + return -ENODEV; + minor = ida_alloc_max(&event_ida, EVENT_MAX_DEV-1, GFP_KERNEL); if (minor < 0) { error = minor; @@ -494,8 +499,7 @@ static int event_device_probe(struct platform_device *pdev) goto free_dev_data; /* Install an ACPI notify handler. */ - error = acpi_dev_install_notify_handler(ACPI_COMPANION(&pdev->dev), - ACPI_DEVICE_NOTIFY, + error = acpi_dev_install_notify_handler(adev, ACPI_DEVICE_NOTIFY, event_device_notify, &pdev->dev); if (error) goto free_cdev; From 69c58f39e39aba60fa5de1bdf6251979d5cd701e Mon Sep 17 00:00:00 2001 From: Lin He Date: Sat, 9 May 2026 11:22:59 +0800 Subject: [PATCH 0883/2758] drm/hisilicon/hibmc: add updating link cap in DP detect() [ Upstream commit 7d380ef98dd469747b6df43fb0243301b0caaacf ] In the past, the link cap is updated in link training at encoder enable stage, but the hibmc_dp_mode_valid() is called before it, which will use DP link's rate and lanes. So add the hibmc_dp_update_caps() in hibmc_dp_update_caps() to avoid some potential risks. Fixes: 607805abfb74 ("drm/hisilicon/hibmc: add dp mode valid check") Signed-off-by: Lin He Signed-off-by: Yongbang Shi Acked-by: Thomas Zimmermann Signed-off-by: Thomas Zimmermann Link: https://patch.msgid.link/20260509032302.2057227-2-shiyongbang@huawei.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h | 1 + drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c | 2 +- drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h index f9ee7ebfec55c..f53dac256ee0b 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h @@ -69,5 +69,6 @@ int hibmc_dp_link_training(struct hibmc_dp_dev *dp); int hibmc_dp_serdes_init(struct hibmc_dp_dev *dp); int hibmc_dp_serdes_rate_switch(u8 rate, struct hibmc_dp_dev *dp); int hibmc_dp_serdes_set_tx_cfg(struct hibmc_dp_dev *dp, u8 train_set[HIBMC_DP_LANE_NUM_MAX]); +void hibmc_dp_update_caps(struct hibmc_dp_dev *dp); #endif diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c index 0726cb5b736e6..8c53f16db5160 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c @@ -325,7 +325,7 @@ static int hibmc_dp_link_downgrade_training_eq(struct hibmc_dp_dev *dp) return hibmc_dp_link_reduce_rate(dp); } -static void hibmc_dp_update_caps(struct hibmc_dp_dev *dp) +void hibmc_dp_update_caps(struct hibmc_dp_dev *dp) { dp->link.cap.link_rate = dp->dpcd[DP_MAX_LINK_RATE]; if (dp->link.cap.link_rate > DP_LINK_BW_8_1 || !dp->link.cap.link_rate) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c index 616821e3c933b..35dff7bfbf76f 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c @@ -41,6 +41,8 @@ static bool hibmc_dp_get_dpcd(struct hibmc_dp_dev *dp_dev) if (ret) return false; + hibmc_dp_update_caps(dp_dev); + dp_dev->is_branch = drm_dp_is_branch(dp_dev->dpcd); ret = drm_dp_read_desc(dp_dev->aux, &dp_dev->desc, dp_dev->is_branch); From 056d3482beb42f94e2a2d712324a1dd3ac06495c Mon Sep 17 00:00:00 2001 From: Lin He Date: Sat, 9 May 2026 11:23:00 +0800 Subject: [PATCH 0884/2758] drm/hisilicon/hibmc: fix no showing when no connectors connected [ Upstream commit 4110e29a7c384f1ff21d6b187b6a1772f6e17b23 ] Our chip support KVM over IP feature, so hibmc driver need to support displaying without any connectors plugged in. If no connectors are connected, the vdac connector status should be set to 'connected' to ensure proper KVM display functionality. Additionally, for previous-generation products that may lack hardware link support and thus cannot detect the monitor, the same approach should be applied to ensure VGA display functionality. * Add phys_state in the struct of dp and vdac to check physical outputs. * The 'epoch_counter' of the vdac connector is incremented when the physical status changes. For get_modes: using BMC modes for connector if no display is attached to phys VGA cable, otherwise use EDID modes by drm_connector_helper_get_modes, because KVM doesn't provide EDID reads. The polling mechanism for the KMS helper is enabled. Fixes: 4c962bc929f1 ("drm/hisilicon/hibmc: Add vga connector detect functions") Reported-by: Thomas Zimmermann Closes: https://lore.kernel.org/all/0eb5c509-2724-4c57-87ad-74e4270d5a5a@suse.de/ Signed-off-by: Lin He Signed-off-by: Yongbang Shi Reviewed-by: Thomas Zimmermann Tested-by: Thomas Zimmermann Signed-off-by: Thomas Zimmermann Link: https://patch.msgid.link/20260509032302.2057227-3-shiyongbang@huawei.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h | 1 + .../gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c | 33 ++++++++---- .../gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 3 ++ .../gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h | 1 + .../gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c | 53 +++++++++++++------ 5 files changed, 64 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h index 31316fe1ea8db..0f3662d8737e9 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h @@ -55,6 +55,7 @@ struct hibmc_dp { struct drm_dp_aux aux; struct hibmc_dp_cbar_cfg cfg; u32 irq_status; + int phys_status; }; int hibmc_dp_hw_init(struct hibmc_dp *dp); diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c index 35dff7bfbf76f..596c5bfe32d8e 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c @@ -61,27 +61,38 @@ static int hibmc_dp_detect(struct drm_connector *connector, { struct hibmc_dp *dp = to_hibmc_dp(connector); struct hibmc_dp_dev *dp_dev = dp->dp_dev; - int ret; + int ret = connector_status_disconnected; if (dp->irq_status) { - if (dp_dev->hpd_status != HIBMC_HPD_IN) - return connector_status_disconnected; + if (dp_dev->hpd_status != HIBMC_HPD_IN) { + ret = connector_status_disconnected; + goto exit; + } } - if (!hibmc_dp_get_dpcd(dp_dev)) - return connector_status_disconnected; + if (!hibmc_dp_get_dpcd(dp_dev)) { + ret = connector_status_disconnected; + goto exit; + } - if (!dp_dev->is_branch) - return connector_status_connected; + if (!dp_dev->is_branch) { + ret = connector_status_connected; + goto exit; + } if (drm_dp_read_sink_count_cap(connector, dp_dev->dpcd, &dp_dev->desc) && dp_dev->downstream_ports[0] & DP_DS_PORT_HPD) { ret = drm_dp_read_sink_count(dp_dev->aux); - if (ret > 0) - return connector_status_connected; + if (ret > 0) { + ret = connector_status_connected; + goto exit; + } } - return connector_status_disconnected; +exit: + dp->phys_status = ret; + + return ret; } static int hibmc_dp_mode_valid(struct drm_connector *connector, @@ -243,5 +254,7 @@ int hibmc_dp_init(struct hibmc_drm_private *priv) connector->polled = DRM_CONNECTOR_POLL_HPD; + dp->phys_status = connector_status_disconnected; + return 0; } diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c index 289304500ab09..481e5e62c30bf 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "hibmc_drm_drv.h" #include "hibmc_drm_regs.h" @@ -355,6 +356,8 @@ static int hibmc_load(struct drm_device *dev) /* reset all the states of crtc/plane/encoder/connector */ drm_mode_config_reset(dev); + drmm_kms_helper_poll_init(dev); + return 0; err: diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h index ca8502e2760c1..cd3a3fca1fe60 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h @@ -31,6 +31,7 @@ struct hibmc_vdac { struct drm_connector connector; struct i2c_adapter adapter; struct i2c_algo_bit_data bit_data; + int phys_status; }; struct hibmc_drm_private { diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c index 841e81f47b686..2fcfa3246fd12 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c @@ -24,28 +24,21 @@ static int hibmc_connector_get_modes(struct drm_connector *connector) { + struct drm_mode_config *mode_config = &connector->dev->mode_config; struct hibmc_vdac *vdac = to_hibmc_vdac(connector); - const struct drm_edid *drm_edid; int count; - drm_edid = drm_edid_read_ddc(connector, &vdac->adapter); - - drm_edid_connector_update(connector, drm_edid); - - if (drm_edid) { - count = drm_edid_connector_add_modes(connector); + if (vdac->phys_status == connector_status_connected) { + count = drm_connector_helper_get_modes(connector); + } else { + drm_edid_connector_update(connector, NULL); + count = drm_add_modes_noedid(connector, + mode_config->max_width, + mode_config->max_height); if (count) - goto out; + drm_set_preferred_mode(connector, 1024, 768); } - count = drm_add_modes_noedid(connector, - connector->dev->mode_config.max_width, - connector->dev->mode_config.max_height); - drm_set_preferred_mode(connector, 1024, 768); - -out: - drm_edid_free(drm_edid); - return count; } @@ -57,10 +50,34 @@ static void hibmc_connector_destroy(struct drm_connector *connector) drm_connector_cleanup(connector); } +static int hibmc_vdac_detect(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force) +{ + struct hibmc_drm_private *priv = to_hibmc_drm_private(connector->dev); + int status = drm_connector_helper_detect_from_ddc(connector, ctx, + force); + struct hibmc_vdac *vdac = to_hibmc_vdac(connector); + + if (priv->dp.phys_status == connector_status_connected) { + vdac->phys_status = status; + return status; + } + + if (status != vdac->phys_status) + ++connector->epoch_counter; + vdac->phys_status = status; + + /* When both the DP and VDAC physical status are disconnected, + * the "connected" status is returned to support KVM display. + */ + return connector_status_connected; +} + static const struct drm_connector_helper_funcs hibmc_connector_helper_funcs = { .get_modes = hibmc_connector_get_modes, - .detect_ctx = drm_connector_helper_detect_from_ddc, + .detect_ctx = hibmc_vdac_detect, }; static const struct drm_connector_funcs hibmc_connector_funcs = { @@ -130,6 +147,8 @@ int hibmc_vdac_init(struct hibmc_drm_private *priv) connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; + vdac->phys_status = connector_status_disconnected; + return 0; err: From 0fbf47903c8773dc0cb4f0e8c499d17a15135c94 Mon Sep 17 00:00:00 2001 From: Lin He Date: Sat, 9 May 2026 11:23:01 +0800 Subject: [PATCH 0885/2758] drm/hisilicon/hibmc: move display contrl config to hibmc_probe() [ Upstream commit de2a56c71f9b223922ea8fe0192d3c1fa2954608 ] If there's no VGA output, this encoder modeset won't be called, which will cause displaying data from GPU being cut off. It's actually a common display config for DP and VGA, so move the vdac encoder modeset to driver load stage. Removed invalid bit configurations from `hibmc_display_ctrl` Fixes: 5294967f4ae4 ("drm/hisilicon/hibmc: Add support for VDAC") Signed-off-by: Lin He Signed-off-by: Yongbang Shi Reviewed-by: Thomas Zimmermann Signed-off-by: Thomas Zimmermann Link: https://patch.msgid.link/20260509032302.2057227-4-shiyongbang@huawei.com Signed-off-by: Sasha Levin --- .../gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 11 ++++++++++ .../gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c | 22 ------------------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c index 481e5e62c30bf..99b36de1fe137 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c @@ -215,6 +215,15 @@ void hibmc_set_current_gate(struct hibmc_drm_private *priv, unsigned int gate) writel(gate, mmio + gate_reg); } +static void hibmc_display_ctrl(struct hibmc_drm_private *priv) +{ + u32 reg; + + reg = readl(priv->mmio + HIBMC_DISPLAY_CONTROL_HISILE); + reg |= HIBMC_DISPLAY_CONTROL_PANELDATE(1); + writel(reg, priv->mmio + HIBMC_DISPLAY_CONTROL_HISILE); +} + static void hibmc_hw_config(struct hibmc_drm_private *priv) { u32 reg; @@ -246,6 +255,8 @@ static void hibmc_hw_config(struct hibmc_drm_private *priv) reg |= HIBMC_MSCCTL_LOCALMEM_RESET(1); writel(reg, priv->mmio + HIBMC_MISC_CTRL); + + hibmc_display_ctrl(priv); } static int hibmc_hw_map(struct hibmc_drm_private *priv) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c index 2fcfa3246fd12..b9bd6d33fb0f7 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c @@ -88,26 +88,6 @@ static const struct drm_connector_funcs hibmc_connector_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; -static void hibmc_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adj_mode) -{ - u32 reg; - struct drm_device *dev = encoder->dev; - struct hibmc_drm_private *priv = to_hibmc_drm_private(dev); - - reg = readl(priv->mmio + HIBMC_DISPLAY_CONTROL_HISILE); - reg |= HIBMC_DISPLAY_CONTROL_FPVDDEN(1); - reg |= HIBMC_DISPLAY_CONTROL_PANELDATE(1); - reg |= HIBMC_DISPLAY_CONTROL_FPEN(1); - reg |= HIBMC_DISPLAY_CONTROL_VBIASEN(1); - writel(reg, priv->mmio + HIBMC_DISPLAY_CONTROL_HISILE); -} - -static const struct drm_encoder_helper_funcs hibmc_encoder_helper_funcs = { - .mode_set = hibmc_encoder_mode_set, -}; - int hibmc_vdac_init(struct hibmc_drm_private *priv) { struct drm_device *dev = &priv->dev; @@ -130,8 +110,6 @@ int hibmc_vdac_init(struct hibmc_drm_private *priv) goto err; } - drm_encoder_helper_add(encoder, &hibmc_encoder_helper_funcs); - ret = drm_connector_init_with_ddc(dev, connector, &hibmc_connector_funcs, DRM_MODE_CONNECTOR_VGA, From 817c94be630305ca86127d7361deb1a0a551e96a Mon Sep 17 00:00:00 2001 From: Lin He Date: Sat, 9 May 2026 11:23:02 +0800 Subject: [PATCH 0886/2758] drm/hisilicon/hibmc: use clock to look up the PLL value [ Upstream commit 99ef3a4f1c1813cabd50bb3e8522e85e00838bb2 ] In the past, we use width and height to look up our PLL value. But actually the actual clock check is also necessnary. There are some resolutions that width and height same, but its clock different. Add the clock check when using pll_table to determine the PLL value. Fixes: da52605eea8f ("drm/hisilicon/hibmc: Add support for display engine") Signed-off-by: Lin He Signed-off-by: Yongbang Shi Reviewed-by: Thomas Zimmermann Signed-off-by: Thomas Zimmermann Link: https://patch.msgid.link/20260509032302.2057227-5-shiyongbang@huawei.com Signed-off-by: Sasha Levin --- .../gpu/drm/hisilicon/hibmc/hibmc_drm_de.c | 80 +++++++++++-------- 1 file changed, 45 insertions(+), 35 deletions(-) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c index 89bed78f14666..db7fce4e8cc34 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c @@ -32,26 +32,43 @@ struct hibmc_display_panel_pll { struct hibmc_dislay_pll_config { u64 hdisplay; u64 vdisplay; + int clock; u32 pll1_config_value; u32 pll2_config_value; }; static const struct hibmc_dislay_pll_config hibmc_pll_table[] = { - {640, 480, CRT_PLL1_HS_25MHZ, CRT_PLL2_HS_25MHZ}, - {800, 600, CRT_PLL1_HS_40MHZ, CRT_PLL2_HS_40MHZ}, - {1024, 768, CRT_PLL1_HS_65MHZ, CRT_PLL2_HS_65MHZ}, - {1152, 864, CRT_PLL1_HS_80MHZ_1152, CRT_PLL2_HS_80MHZ}, - {1280, 768, CRT_PLL1_HS_80MHZ, CRT_PLL2_HS_80MHZ}, - {1280, 720, CRT_PLL1_HS_74MHZ, CRT_PLL2_HS_74MHZ}, - {1280, 960, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ}, - {1280, 1024, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ}, - {1440, 900, CRT_PLL1_HS_106MHZ, CRT_PLL2_HS_106MHZ}, - {1600, 900, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ}, - {1600, 1200, CRT_PLL1_HS_162MHZ, CRT_PLL2_HS_162MHZ}, - {1920, 1080, CRT_PLL1_HS_148MHZ, CRT_PLL2_HS_148MHZ}, - {1920, 1200, CRT_PLL1_HS_193MHZ, CRT_PLL2_HS_193MHZ}, + {640, 480, 25000, CRT_PLL1_HS_25MHZ, CRT_PLL2_HS_25MHZ}, + {800, 600, 40000, CRT_PLL1_HS_40MHZ, CRT_PLL2_HS_40MHZ}, + {1024, 768, 65000, CRT_PLL1_HS_65MHZ, CRT_PLL2_HS_65MHZ}, + {1152, 864, 78750, CRT_PLL1_HS_80MHZ_1152, CRT_PLL2_HS_80MHZ}, + {1280, 768, 80000, CRT_PLL1_HS_80MHZ, CRT_PLL2_HS_80MHZ}, + {1280, 720, 74375, CRT_PLL1_HS_74MHZ, CRT_PLL2_HS_74MHZ}, + {1280, 960, 108000, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ}, + {1280, 1024, 108000, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ}, + {1440, 900, 105952, CRT_PLL1_HS_106MHZ, CRT_PLL2_HS_106MHZ}, + {1600, 900, 108000, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ}, + {1600, 1200, 162500, CRT_PLL1_HS_162MHZ, CRT_PLL2_HS_162MHZ}, + {1920, 1080, 148750, CRT_PLL1_HS_148MHZ, CRT_PLL2_HS_148MHZ}, + {1920, 1200, 193750, CRT_PLL1_HS_193MHZ, CRT_PLL2_HS_193MHZ}, }; +static int hibmc_get_best_clock_idx(const struct drm_display_mode *mode) +{ + int i, diff; + + for (i = 0; i < ARRAY_SIZE(hibmc_pll_table); i++) { + if (hibmc_pll_table[i].hdisplay == mode->hdisplay && + hibmc_pll_table[i].vdisplay == mode->vdisplay) { + diff = abs(mode->clock - hibmc_pll_table[i].clock); + if (diff < mode->clock / 100) /* tolerance 1/100 */ + return i; + } + } + + return -MODE_CLOCK_RANGE; +} + static int hibmc_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_state *state) { @@ -214,19 +231,15 @@ static enum drm_mode_status hibmc_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode) { - size_t i = 0; int vrefresh = drm_mode_vrefresh(mode); if (vrefresh < 59 || vrefresh > 61) return MODE_NOCLOCK; - for (i = 0; i < ARRAY_SIZE(hibmc_pll_table); i++) { - if (hibmc_pll_table[i].hdisplay == mode->hdisplay && - hibmc_pll_table[i].vdisplay == mode->vdisplay) - return MODE_OK; - } + if (hibmc_get_best_clock_idx(mode) >= 0) + return MODE_OK; - return MODE_BAD; + return MODE_CLOCK_RANGE; } static u32 format_pll_reg(void) @@ -281,23 +294,20 @@ static void set_vclock_hisilicon(struct drm_device *dev, u64 pll) writel(val, priv->mmio + CRT_PLL1_HS); } -static void get_pll_config(u64 x, u64 y, u32 *pll1, u32 *pll2) +static void get_pll_config(struct drm_display_mode *mode, u32 *pll1, u32 *pll2) { - size_t i; - size_t count = ARRAY_SIZE(hibmc_pll_table); - - for (i = 0; i < count; i++) { - if (hibmc_pll_table[i].hdisplay == x && - hibmc_pll_table[i].vdisplay == y) { - *pll1 = hibmc_pll_table[i].pll1_config_value; - *pll2 = hibmc_pll_table[i].pll2_config_value; - return; - } + int idx; + + idx = hibmc_get_best_clock_idx(mode); + if (idx < 0) { + /* if found none, we use default value */ + *pll1 = CRT_PLL1_HS_25MHZ; + *pll2 = CRT_PLL2_HS_25MHZ; + return; } - /* if found none, we use default value */ - *pll1 = CRT_PLL1_HS_25MHZ; - *pll2 = CRT_PLL2_HS_25MHZ; + *pll1 = hibmc_pll_table[idx].pll1_config_value; + *pll2 = hibmc_pll_table[idx].pll2_config_value; } /* @@ -319,7 +329,7 @@ static u32 display_ctrl_adjust(struct drm_device *dev, x = mode->hdisplay; y = mode->vdisplay; - get_pll_config(x, y, &pll1, &pll2); + get_pll_config(mode, &pll1, &pll2); writel(pll2, priv->mmio + CRT_PLL2_HS); set_vclock_hisilicon(dev, pll1); From 38b7d63b23aa9d2ddde7688040e9426d32be4065 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 23 Apr 2026 23:30:00 +0800 Subject: [PATCH 0887/2758] evm: terminate and bound the evm_xattrs read buffer [ Upstream commit 11143a19f5b8dc8f414deab87571134f9f447313 ] evm_read_xattrs() allocates size + 1 bytes, fills them from the list of enabled xattrs, and then passes strlen(temp) to simple_read_from_buffer(). When no configured xattrs are enabled, the fill loop stores nothing and temp[0] remains uninitialized, so strlen() reads beyond initialized memory. Explicitly terminate the buffer after allocation, use snprintf() for each formatted line, and pass the accumulated length, without risk of truncation, to simple_read_from_buffer(). Fixes: fa516b66a1bf ("EVM: Allow runtime modification of the set of verified xattrs") Signed-off-by: Pengpeng Hou Reviewed-by: Roberto Sassu Signed-off-by: Mimi Zohar Signed-off-by: Sasha Levin --- security/integrity/evm/evm_secfs.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c index acd840461902f..4baf5e23bc97e 100644 --- a/security/integrity/evm/evm_secfs.c +++ b/security/integrity/evm/evm_secfs.c @@ -127,8 +127,8 @@ static ssize_t evm_read_xattrs(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { char *temp; - int offset = 0; - ssize_t rc, size = 0; + size_t offset = 0, size = 0; + ssize_t rc; struct xattr_list *xattr; if (*ppos != 0) @@ -151,16 +151,22 @@ static ssize_t evm_read_xattrs(struct file *filp, char __user *buf, return -ENOMEM; } + temp[size] = '\0'; + + /* + * No truncation possible: size is computed over the same enabled + * xattrs under xattr_list_mutex, so offset never exceeds size. + */ list_for_each_entry(xattr, &evm_config_xattrnames, list) { if (!xattr->enabled) continue; - sprintf(temp + offset, "%s\n", xattr->name); - offset += strlen(xattr->name) + 1; + offset += snprintf(temp + offset, size + 1 - offset, "%s\n", + xattr->name); } mutex_unlock(&xattr_list_mutex); - rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp)); + rc = simple_read_from_buffer(buf, count, ppos, temp, offset); kfree(temp); From 403c3703a1054a83694a00239950f6faf96f92e6 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 5 May 2026 13:36:56 +0200 Subject: [PATCH 0888/2758] thermal: hwmon: Fix critical temperature attribute removal [ Upstream commit c2114dbda05354dbcf4dfbb30a2c623e8611c43a ] Since the return value of thermal_zone_crit_temp_valid() depends on the behavior of the thermal zone .get_crit_temp() callback which may change over time in theory, thermal_remove_hwmon_sysfs() may attempt to remove a critical temperature attribute that has not been created, passing a pointer to an uninitialized attribute structure to device_remove_file(). To avoid that, set a flag in struct thermal_hwmon_temp after creating a critical temperature attribute and use the value of that flag to decide whether or not the attribute needs to be removed. Fixes: e8db5d6736a7 ("thermal: hwmon: Make the check for critical temp valid consistent") Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2437056.ElGaqSPkdT@rafael.j.wysocki Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/thermal/thermal_hwmon.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c index b624892bc6d66..597c33c8a5550 100644 --- a/drivers/thermal/thermal_hwmon.c +++ b/drivers/thermal/thermal_hwmon.c @@ -40,6 +40,7 @@ struct thermal_hwmon_temp { struct thermal_zone_device *tz; struct thermal_hwmon_attr temp_input; /* hwmon sys attr */ struct thermal_hwmon_attr temp_crit; /* hwmon sys attr */ + bool temp_crit_present; }; static LIST_HEAD(thermal_hwmon_list); @@ -191,6 +192,8 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) &temp->temp_crit.attr); if (result) goto unregister_input; + + temp->temp_crit_present = true; } mutex_lock(&thermal_hwmon_list_lock); @@ -235,7 +238,7 @@ void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz) } device_remove_file(hwmon->device, &temp->temp_input.attr); - if (thermal_zone_crit_temp_valid(tz)) + if (temp->temp_crit_present) device_remove_file(hwmon->device, &temp->temp_crit.attr); mutex_lock(&thermal_hwmon_list_lock); From 996957016bc1403c6b981dc2a0a26aae241bef71 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 5 May 2026 13:44:01 +0200 Subject: [PATCH 0889/2758] thermal: hwmon: Register a hwmon device for each thermal zone [ Upstream commit d6323469bcfbda91f0aa89b7b39ad45fe822ca5d ] The current code creates one hwmon device per thermal zone type and that device is registered under the first thermal zone of the given type. That turns out to be problematic when the thermal zone holding the hwmon device is removed. For example, say that there are two ACPI thermal zones on a system /sys/devices/virtual/thermal/thermal_zone0/ /sys/devices/virtual/thermal/thermal_zone1/ The current code registers a hwmon class device for thermal_zone0 only: /sys/devices/virtual/thermal/thermal_zone0/hwmon0/ because the type is "acpitz" for both of them, but it adds a sysfs attribute that belongs to thermal_zone1 under it: /sys/devices/virtual/thermal/thermal_zone0/hwmon0/temp2_input There is also /sys/devices/virtual/thermal/thermal_zone0/hwmon0/temp1_input which belongs to thermal_zone0. When thermal_zone0 is removed, say because the ACPI thermal driver is unbound from the underlying platform device, the removal code skips the removal of hwmon0 because of the temp2_input attribute belonging to thermal_zone1 which effectively prevents thermal_zone0 removal from making progress. To address this problem, rework the thermal hwmon code to register one hwmon device for each thermal zone, but since user space utilities produce confusing output in some cases when there are multiple hwmon devices with the same name attribute value present under thermal zones of the same type, append the thermal zone ID preceded by an underline character to the name of the hwmon device registered for that thermal zone. Link: https://lore.kernel.org/linux-pm/20260402021828.16556-1-liujia6264@gmail.com/ Fixes: f6b6b52ef7a5 ("thermal_hwmon: Pass the originating device down to hwmon_device_register_with_info") Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/3070412.e9J7NaK4W3@rafael.j.wysocki Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/thermal/thermal_hwmon.c | 151 ++++++++++---------------------- 1 file changed, 47 insertions(+), 104 deletions(-) diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c index 597c33c8a5550..223ae1571655b 100644 --- a/drivers/thermal/thermal_hwmon.c +++ b/drivers/thermal/thermal_hwmon.c @@ -19,30 +19,33 @@ #include "thermal_hwmon.h" #include "thermal_core.h" -/* hwmon sys I/F */ -/* thermal zone devices with the same type share one hwmon device */ -struct thermal_hwmon_device { - char type[THERMAL_NAME_LENGTH]; - struct device *device; - int count; - struct list_head tz_list; - struct list_head node; -}; +/* + * Needs to be large enough to hold a thermal zone type string followed by an + * underline character and a 32-bit integer in decimal representation. + */ +#define THERMAL_HWMON_NAME_LENGTH (THERMAL_NAME_LENGTH + 11) struct thermal_hwmon_attr { struct device_attribute attr; - char name[16]; }; /* one temperature input for each thermal zone */ struct thermal_hwmon_temp { - struct list_head hwmon_node; struct thermal_zone_device *tz; struct thermal_hwmon_attr temp_input; /* hwmon sys attr */ struct thermal_hwmon_attr temp_crit; /* hwmon sys attr */ bool temp_crit_present; }; +/* hwmon sys I/F */ +/* thermal zone devices with the same type share one hwmon device */ +struct thermal_hwmon_device { + char name[THERMAL_HWMON_NAME_LENGTH]; + struct device *device; + struct list_head node; + struct thermal_hwmon_temp tz_temp; +}; + static LIST_HEAD(thermal_hwmon_list); static DEFINE_MUTEX(thermal_hwmon_list_lock); @@ -88,45 +91,6 @@ temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf) return sysfs_emit(buf, "%d\n", temperature); } - -static struct thermal_hwmon_device * -thermal_hwmon_lookup_by_type(const struct thermal_zone_device *tz) -{ - struct thermal_hwmon_device *hwmon; - char type[THERMAL_NAME_LENGTH]; - - mutex_lock(&thermal_hwmon_list_lock); - list_for_each_entry(hwmon, &thermal_hwmon_list, node) { - strscpy(type, tz->type); - strreplace(type, '-', '_'); - if (!strcmp(hwmon->type, type)) { - mutex_unlock(&thermal_hwmon_list_lock); - return hwmon; - } - } - mutex_unlock(&thermal_hwmon_list_lock); - - return NULL; -} - -/* Find the temperature input matching a given thermal zone */ -static struct thermal_hwmon_temp * -thermal_hwmon_lookup_temp(const struct thermal_hwmon_device *hwmon, - const struct thermal_zone_device *tz) -{ - struct thermal_hwmon_temp *temp; - - mutex_lock(&thermal_hwmon_list_lock); - list_for_each_entry(temp, &hwmon->tz_list, hwmon_node) - if (temp->tz == tz) { - mutex_unlock(&thermal_hwmon_list_lock); - return temp; - } - mutex_unlock(&thermal_hwmon_list_lock); - - return NULL; -} - static bool thermal_zone_crit_temp_valid(struct thermal_zone_device *tz) { int temp; @@ -137,54 +101,39 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) { struct thermal_hwmon_device *hwmon; struct thermal_hwmon_temp *temp; - int new_hwmon_device = 1; int result; - hwmon = thermal_hwmon_lookup_by_type(tz); - if (hwmon) { - new_hwmon_device = 0; - goto register_sys_interface; - } - hwmon = kzalloc_obj(*hwmon); if (!hwmon) return -ENOMEM; - INIT_LIST_HEAD(&hwmon->tz_list); - strscpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH); - strreplace(hwmon->type, '-', '_'); + /* + * Append the thermal zone ID preceded by an underline character to the + * type to disambiguate the sensors command output. + */ + scnprintf(hwmon->name, THERMAL_HWMON_NAME_LENGTH, "%s_%d", tz->type, tz->id); + strreplace(hwmon->name, '-', '_'); hwmon->device = hwmon_device_register_for_thermal(&tz->device, - hwmon->type, hwmon); + hwmon->name, hwmon); if (IS_ERR(hwmon->device)) { result = PTR_ERR(hwmon->device); goto free_mem; } - register_sys_interface: - temp = kzalloc_obj(*temp); - if (!temp) { - result = -ENOMEM; - goto unregister_name; - } + temp = &hwmon->tz_temp; temp->tz = tz; - hwmon->count++; - snprintf(temp->temp_input.name, sizeof(temp->temp_input.name), - "temp%d_input", hwmon->count); - temp->temp_input.attr.attr.name = temp->temp_input.name; + temp->temp_input.attr.attr.name = "temp1_input"; temp->temp_input.attr.attr.mode = 0444; temp->temp_input.attr.show = temp_input_show; sysfs_attr_init(&temp->temp_input.attr.attr); result = device_create_file(hwmon->device, &temp->temp_input.attr); if (result) - goto free_temp_mem; + goto unregister_name; if (thermal_zone_crit_temp_valid(tz)) { - snprintf(temp->temp_crit.name, - sizeof(temp->temp_crit.name), - "temp%d_crit", hwmon->count); - temp->temp_crit.attr.attr.name = temp->temp_crit.name; + temp->temp_crit.attr.attr.name = "temp1_crit"; temp->temp_crit.attr.attr.mode = 0444; temp->temp_crit.attr.show = temp_crit_show; sysfs_attr_init(&temp->temp_crit.attr.attr); @@ -196,21 +145,17 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) temp->temp_crit_present = true; } + /* The list is needed for hwmon lookup during removal. */ mutex_lock(&thermal_hwmon_list_lock); - if (new_hwmon_device) - list_add_tail(&hwmon->node, &thermal_hwmon_list); - list_add_tail(&temp->hwmon_node, &hwmon->tz_list); + list_add_tail(&hwmon->node, &thermal_hwmon_list); mutex_unlock(&thermal_hwmon_list_lock); return 0; unregister_input: device_remove_file(hwmon->device, &temp->temp_input.attr); - free_temp_mem: - kfree(temp); unregister_name: - if (new_hwmon_device) - hwmon_device_unregister(hwmon->device); + hwmon_device_unregister(hwmon->device); free_mem: kfree(hwmon); @@ -218,39 +163,37 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) } EXPORT_SYMBOL_GPL(thermal_add_hwmon_sysfs); +static struct thermal_hwmon_device * +thermal_hwmon_lookup(const struct thermal_zone_device *tz) +{ + struct thermal_hwmon_device *hwmon; + + list_for_each_entry(hwmon, &thermal_hwmon_list, node) { + if (hwmon->tz_temp.tz == tz) + return hwmon; + } + return NULL; +} + void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz) { struct thermal_hwmon_device *hwmon; struct thermal_hwmon_temp *temp; - hwmon = thermal_hwmon_lookup_by_type(tz); - if (unlikely(!hwmon)) { - /* Should never happen... */ - dev_dbg(&tz->device, "hwmon device lookup failed!\n"); - return; - } + scoped_guard(mutex, &thermal_hwmon_list_lock) { + hwmon = thermal_hwmon_lookup(tz); + if (!hwmon) + return; - temp = thermal_hwmon_lookup_temp(hwmon, tz); - if (unlikely(!temp)) { - /* Should never happen... */ - dev_dbg(&tz->device, "temperature input lookup failed!\n"); - return; + list_del(&hwmon->node); } + temp = &hwmon->tz_temp; + device_remove_file(hwmon->device, &temp->temp_input.attr); if (temp->temp_crit_present) device_remove_file(hwmon->device, &temp->temp_crit.attr); - mutex_lock(&thermal_hwmon_list_lock); - list_del(&temp->hwmon_node); - kfree(temp); - if (!list_empty(&hwmon->tz_list)) { - mutex_unlock(&thermal_hwmon_list_lock); - return; - } - list_del(&hwmon->node); - mutex_unlock(&thermal_hwmon_list_lock); - hwmon_device_unregister(hwmon->device); kfree(hwmon); } From d85543848ef03e6b1614fa3609c14485bd24b4ff Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Wed, 13 May 2026 14:09:00 +0500 Subject: [PATCH 0890/2758] clk: scpi: Unregister child clock providers on remove [ Upstream commit b79d9b5747d961516c35ef4d5e91efa579fd3e9a ] SCPI clock providers are registered for each child node in scpi_clk_add(), but scpi_clocks_remove() unregisters the parent node on each iteration. of_clk_del_provider() matches providers by the node used at registration time, so passing the parent node leaves the child providers registered. This leaks the provider allocations and the node references held by the clock provider core. Pass the child node to of_clk_del_provider() so the remove path matches the probe path. Fixes: cd52c2a4b5c4 ("clk: add support for clocks provided by SCP(System Control Processor)") Signed-off-by: Stepan Ionichev Link: https://patch.msgid.link/20260513090900.5323-1-sozdayvek@gmail.com (sudeep.holla: Updated commit title and message a bit) Signed-off-by: Sudeep Holla Signed-off-by: Sasha Levin --- drivers/clk/clk-scpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/clk-scpi.c b/drivers/clk/clk-scpi.c index 7806569cd0d5c..24cee7c9fda6c 100644 --- a/drivers/clk/clk-scpi.c +++ b/drivers/clk/clk-scpi.c @@ -258,7 +258,7 @@ static void scpi_clocks_remove(struct platform_device *pdev) } for_each_available_child_of_node(np, child) - of_clk_del_provider(np); + of_clk_del_provider(child); } static int scpi_clocks_probe(struct platform_device *pdev) From 8a046e68f073b2420337541a194de274d4858eb5 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 13 May 2026 08:08:53 +0000 Subject: [PATCH 0891/2758] net/sched: sch_hfsc: annotate data-races in hfsc_dump_class_stats() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit e300c7d470ad2726d6abf7d11b31b5d9912d9cf0 ] hfsc_dump_class_stats() runs without qdisc spinlock being held. Add READ_ONCE()/WRITE_ONCE() annotations around: - cl->level - cl->cl_vtperiod - cl->cl_total - cl->cl_cumul Fixes: edb09eb17ed8 ("net: sched: do not acquire qdisc spinlock in qdisc/class stats dump") Signed-off-by: Eric Dumazet Reviewed-by: Toke Høiland-Jørgensen Link: https://patch.msgid.link/20260513080853.1383975-4-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/sch_hfsc.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index 83b2ca2e37fc8..55258aac6b93c 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -715,7 +715,7 @@ init_vf(struct hfsc_class *cl, unsigned int len) rtsc_min(&cl->cl_virtual, &cl->cl_fsc, cl->cl_vt, cl->cl_total); cl->cl_vtadj = 0; - cl->cl_vtperiod++; /* increment vt period */ + WRITE_ONCE(cl->cl_vtperiod, cl->cl_vtperiod + 1); /* increment vt period */ cl->cl_parentperiod = cl->cl_parent->cl_vtperiod; if (cl->cl_parent->cl_nactive == 0) cl->cl_parentperiod++; @@ -757,7 +757,7 @@ update_vf(struct hfsc_class *cl, unsigned int len, u64 cur_time) go_passive = 1; for (; cl->cl_parent != NULL; cl = cl->cl_parent) { - cl->cl_total += len; + WRITE_ONCE(cl->cl_total, cl->cl_total + len); if (!(cl->cl_flags & HFSC_FSC) || cl->cl_nactive == 0) continue; @@ -847,7 +847,7 @@ hfsc_adjust_levels(struct hfsc_class *cl) if (p->level >= level) level = p->level + 1; } - cl->level = level; + WRITE_ONCE(cl->level, level); } while ((cl = cl->cl_parent) != NULL); } @@ -1338,10 +1338,10 @@ hfsc_dump_class_stats(struct Qdisc *sch, unsigned long arg, __u32 qlen; qdisc_qstats_qlen_backlog(cl->qdisc, &qlen, &cl->qstats.backlog); - xstats.level = cl->level; - xstats.period = cl->cl_vtperiod; - xstats.work = cl->cl_total; - xstats.rtwork = cl->cl_cumul; + xstats.level = READ_ONCE(cl->level); + xstats.period = READ_ONCE(cl->cl_vtperiod); + xstats.work = READ_ONCE(cl->cl_total); + xstats.rtwork = READ_ONCE(cl->cl_cumul); if (gnet_stats_copy_basic(d, NULL, &cl->bstats, true) < 0 || gnet_stats_copy_rate_est(d, &cl->rate_est) < 0 || @@ -1452,15 +1452,15 @@ hfsc_change_qdisc(struct Qdisc *sch, struct nlattr *opt, static void hfsc_reset_class(struct hfsc_class *cl) { - cl->cl_total = 0; - cl->cl_cumul = 0; + WRITE_ONCE(cl->cl_total, 0); + WRITE_ONCE(cl->cl_cumul, 0); cl->cl_d = 0; cl->cl_e = 0; cl->cl_vt = 0; cl->cl_vtadj = 0; cl->cl_cvtmin = 0; cl->cl_cvtoff = 0; - cl->cl_vtperiod = 0; + WRITE_ONCE(cl->cl_vtperiod, 0); cl->cl_parentperiod = 0; cl->cl_f = 0; cl->cl_myf = 0; @@ -1626,7 +1626,7 @@ hfsc_dequeue(struct Qdisc *sch) bstats_update(&cl->bstats, skb); update_vf(cl, qdisc_pkt_len(skb), cur_time); if (realtime) - cl->cl_cumul += qdisc_pkt_len(skb); + WRITE_ONCE(cl->cl_cumul, cl->cl_cumul + qdisc_pkt_len(skb)); if (cl->cl_flags & HFSC_RSC) { if (cl->qdisc->q.qlen != 0) { From 4867dba229292e13fc19ed865ec1839661952ff9 Mon Sep 17 00:00:00 2001 From: Yihang Li Date: Sat, 25 Apr 2026 16:20:56 +0800 Subject: [PATCH 0892/2758] scsi: hisi_sas: Add slave_destroy interface for v3 hw [ Upstream commit 67b85a88265df19f049241d8c00571a5408f4eeb ] WARNING is triggered when executing link reset of remote PHY and rmmod SAS driver simultaneously. Following is the WARNING log: WARNING: CPU: 61 PID: 21818 at drivers/base/core.c:1347 __device_links_no_driver+0xb4/0xc0 Call trace: __device_links_no_driver+0xb4/0xc0 device_links_driver_cleanup+0xb0/0xfc __device_release_driver+0x198/0x23c device_release_driver+0x38/0x50 bus_remove_device+0x130/0x140 device_del+0x184/0x434 __scsi_remove_device+0x118/0x150 scsi_remove_target+0x1bc/0x240 sas_rphy_remove+0x90/0x94 sas_rphy_delete+0x24/0x3c sas_destruct_devices+0x64/0xa0 [libsas] sas_revalidate_domain+0xe4/0x150 [libsas] process_one_work+0x1e0/0x46c worker_thread+0x15c/0x464 kthread+0x160/0x170 ret_from_fork+0x10/0x20 ---[ end trace 71e059eb58f85d4a ]--- During SAS phy up, link->status is set to DL_STATE_AVAILABLE in device_links_driver_bound, then this setting influences __device_links_no_driver() before driver rmmod and caused WARNING. Add the slave_destroy interface to make sure link is removed after flush workque. Fixes: 16fd4a7c5917 ("scsi: hisi_sas: Add device link between SCSI devices and hisi_hba") Signed-off-by: Yihang Li Link: https://patch.msgid.link/20260425082056.2749910-1-liyihang9@huawei.com Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 14d563e82d208..213d5b5dea94f 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -2977,7 +2977,7 @@ static int sdev_configure_v3_hw(struct scsi_device *sdev, return 0; if (!device_link_add(&sdev->sdev_gendev, dev, - DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE)) { + DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE)) { if (pm_runtime_enabled(dev)) { dev_info(dev, "add device link failed, disable runtime PM for the host\n"); pm_runtime_disable(dev); @@ -2987,6 +2987,15 @@ static int sdev_configure_v3_hw(struct scsi_device *sdev, return 0; } +static void hisi_sas_sdev_destroy(struct scsi_device *sdev) +{ + struct Scsi_Host *shost = dev_to_shost(&sdev->sdev_gendev); + struct hisi_hba *hisi_hba = shost_priv(shost); + struct device *dev = hisi_hba->dev; + + device_link_remove(&sdev->sdev_gendev, dev); +} + static struct attribute *host_v3_hw_attrs[] = { &dev_attr_phy_event_threshold.attr, &dev_attr_intr_conv_v3_hw.attr, @@ -3401,6 +3410,7 @@ static const struct scsi_host_template sht_v3_hw = { .sg_tablesize = HISI_SAS_SGE_PAGE_CNT, .sg_prot_tablesize = HISI_SAS_SGE_PAGE_CNT, .sdev_init = hisi_sas_sdev_init, + .sdev_destroy = hisi_sas_sdev_destroy, .shost_groups = host_v3_hw_groups, .sdev_groups = sdev_groups_v3_hw, .tag_alloc_policy_rr = true, From d88071a6ce091272726bedda26c105b85b23705f Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Mon, 4 May 2026 15:28:12 -0700 Subject: [PATCH 0893/2758] crypto: ccp - Treat zero-length cert chain as query for blob lengths [ Upstream commit ef8c9dacda2871accd64e3eda951fef6b788b1ea ] When handling a PDH export, treat a zero-length userspace cert chain buffer as a request to query the length of the relevant blobs. Failure to account for the zero-length buffer trips a BUG_ON() when running with CONFIG_DEBUG_VIRTUAL=y due to trying to get the physical address of the ZERO_SIZE_PTR (returned by kzalloc() on the bogus allocation). kernel BUG at arch/x86/mm/physaddr.c:28 ! Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI CPU: 30 UID: 0 PID: 28580 Comm: syz.2.18 Kdump: loaded Tainted: G W 6.18.16-smp-DEV #1 NONE Tainted: [W]=WARN Hardware name: Google, Inc. Arcadia_IT_80/Arcadia_IT_80, BIOS 12.62.0-0 11/19/2025 RIP: 0010:__phys_addr+0x16a/0x180 arch/x86/mm/physaddr.c:28 RSP: 0018:ffffc9008329fc80 EFLAGS: 00010293 RAX: ffffffff8179110a RBX: 0000778000000010 RCX: ffff8884e6992600 RDX: 0000000000000000 RSI: 0000000080000010 RDI: 0000778000000010 RBP: ffffc9008329fdf0 R08: 0000000000000dc0 R09: 00000000ffffffff R10: dffffc0000000000 R11: fffffbfff126d297 R12: dffffc0000000000 R13: 1ffff92010653fc8 R14: 0000000080000010 R15: dffffc0000000000 FS: 0000555556bec9c0(0000) GS:ffff88aa4ce1c000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007fd3159e7000 CR3: 00000004fbc44000 CR4: 0000000000350ef0 Call Trace: [] sev_ioctl_do_pdh_export+0x559/0x7a0 drivers/crypto/ccp/sev-dev.c:2308 [] sev_ioctl+0x2cd/0x480 drivers/crypto/ccp/sev-dev.c:2556 [] vfs_ioctl fs/ioctl.c:52 [inline] [] __do_sys_ioctl fs/ioctl.c:598 [inline] [] __se_sys_ioctl+0xfc/0x170 fs/ioctl.c:584 [] do_syscall_x64 arch/x86/entry/syscall_64.c:64 [inline] [] do_syscall_64+0x9f/0xf40 arch/x86/entry/syscall_64.c:98 [] entry_SYSCALL_64_after_hwframe+0x76/0x7e RIP: 0033:0x7fd3158eac39 Thankfully, the bug is benign outside of CONFIG_DEBUG_VIRTUAL=y as getting the physical address is just arithmetic, and the PSP errors out before trying to write to the garbage address (which it must, otherwise querying the blob lengths would clobber memory at pfn=0). Fixes: 76a2b524a4b1 ("crypto: ccp: Implement SEV_PDH_CERT_EXPORT ioctl command") Signed-off-by: Sean Christopherson Reviewed-by: Tom Lendacky Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/ccp/sev-dev.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index 14df519ae7eea..068b901034cbc 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -2286,7 +2286,8 @@ static int sev_ioctl_do_pdh_export(struct sev_issue_cmd *argp, bool writable) /* Userspace wants to query the certificate length. */ if (!input.pdh_cert_address || !input.pdh_cert_len || - !input.cert_chain_address) + !input.cert_chain_address || + !input.cert_chain_len) goto cmd; /* Allocate a physically contiguous buffer to store the PDH blob. */ From 0b3a57d218618cb1cc78ddc9ba02c07de84b46f4 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Tue, 5 May 2026 17:02:45 +0800 Subject: [PATCH 0894/2758] crypto: af_alg - Cap AEAD AD length to 0x80000000 [ Upstream commit e4c06479d7059888adf2f22bc1ebcf053bf691a2 ] In order to prevent arithmetic overflows when checking the TX buffer size, cap the associated data length to 0x80000000. Reported-by: Yiming Qian Fixes: 400c40cf78da ("crypto: algif - add AEAD support") Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- crypto/af_alg.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crypto/af_alg.c b/crypto/af_alg.c index fce0b87c2b652..48c53f488e0fd 100644 --- a/crypto/af_alg.c +++ b/crypto/af_alg.c @@ -584,6 +584,8 @@ static int af_alg_cmsg_send(struct msghdr *msg, struct af_alg_control *con) if (cmsg->cmsg_len < CMSG_LEN(sizeof(u32))) return -EINVAL; con->aead_assoclen = *(u32 *)CMSG_DATA(cmsg); + if (con->aead_assoclen >= 0x80000000u) + return -EINVAL; break; default: From 6bafd740095fb3d0e9a00540bc8f3484c38e6f85 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Thu, 7 May 2026 19:06:08 +0500 Subject: [PATCH 0895/2758] crypto: ccp/sev-dev-tsm - bail out early when pdev->bus is NULL [ Upstream commit 930d9d36ea618a775985446a125aedeb401db522 ] dsm_create() initially checks pdev->bus when computing segment_id: u8 segment_id = pdev->bus ? pci_domain_nr(pdev->bus) : 0; But the next two lines unconditionally dereference pdev->bus via pcie_find_root_port() and especially pci_dev_id(pdev), which expands to PCI_DEVID(dev->bus->number, dev->devfn). If pdev->bus is in fact NULL, segment_id is initialised to 0 but the very next statement crashes the kernel. smatch flags this: drivers/crypto/ccp/sev-dev-tsm.c:253 dsm_create() error: we previously assumed 'pdev->bus' could be null (see line 251) Make the NULL handling consistent: if pdev->bus is NULL the device has no PCI context to work with and SEV TIO setup cannot proceed, so return -ENODEV before any of the bus-dependent lookups. The remaining initialisation now runs only on the path where pdev->bus is known to be valid. No change for callers where pdev->bus is non-NULL, which is the only case where dsm_create() did meaningful work before this change. Fixes: 4be423572da1 ("crypto/ccp: Implement SEV-TIO PCIe IDE (phase1)") Signed-off-by: Stepan Ionichev Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/ccp/sev-dev-tsm.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/crypto/ccp/sev-dev-tsm.c b/drivers/crypto/ccp/sev-dev-tsm.c index b07ae529b5910..f303d8f55991b 100644 --- a/drivers/crypto/ccp/sev-dev-tsm.c +++ b/drivers/crypto/ccp/sev-dev-tsm.c @@ -248,12 +248,19 @@ static void dsm_remove(struct pci_tsm *tsm) static int dsm_create(struct tio_dsm *dsm) { struct pci_dev *pdev = dsm->tsm.base_tsm.pdev; - u8 segment_id = pdev->bus ? pci_domain_nr(pdev->bus) : 0; - struct pci_dev *rootport = pcie_find_root_port(pdev); - u16 device_id = pci_dev_id(pdev); + struct pci_dev *rootport; + u8 segment_id; + u16 device_id; u16 root_port_id; u32 lnkcap = 0; + if (!pdev->bus) + return -ENODEV; + + segment_id = pci_domain_nr(pdev->bus); + rootport = pcie_find_root_port(pdev); + device_id = pci_dev_id(pdev); + if (pci_read_config_dword(rootport, pci_pcie_cap(rootport) + PCI_EXP_LNKCAP, &lnkcap)) return -ENODEV; From 2de04bc78f63bcf1f4487fb99c3640eacda3d7e8 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Fri, 8 May 2026 14:33:45 +0530 Subject: [PATCH 0896/2758] crypto: safexcel - Fix potential memory leak in safexcel_pci_probe() [ Upstream commit c36faca103a685590281412e47df74b550f71886 ] The memory allocated for priv in safexcel_pci_probe() is not freed in the error paths, as well as in the PCI remove function. Fix this by using device managed allocation. Fixes: 625f269a5a7a ("crypto: inside-secure - add support for PCI based FPGA development board") Signed-off-by: Abdun Nihaal Reviewed-by: Antoine Tenart Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/inside-secure/safexcel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/crypto/inside-secure/safexcel.c b/drivers/crypto/inside-secure/safexcel.c index fb4936e7afa2d..2bd8641a07b36 100644 --- a/drivers/crypto/inside-secure/safexcel.c +++ b/drivers/crypto/inside-secure/safexcel.c @@ -1893,7 +1893,7 @@ static int safexcel_pci_probe(struct pci_dev *pdev, ent->vendor, ent->device, ent->subvendor, ent->subdevice, ent->driver_data); - priv = kzalloc_obj(*priv); + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; From 160b64b41559b18a4a4c37042bfb313cdfaa4d40 Mon Sep 17 00:00:00 2001 From: Qiang Ma Date: Fri, 15 May 2026 18:26:20 +0800 Subject: [PATCH 0897/2758] spi: hisi-kunpeng: Use dev_err_probe() for host registration failure [ Upstream commit 59b991990a04b1d1ce95373983b7c8b65bdf7acc ] When the SPI core registers the Kunpeng controller it may need to acquire chip-select GPIO descriptors. If the GPIO provider has not probed yet, spi_register_controller() returns -EPROBE_DEFER. On a Kunpeng system this currently prints an alarming error even though the next deferred-probe retry succeeds: hisi-kunpeng-spi HISI03E1:00: failed to register spi host, ret=-517 hisi-kunpeng-spi HISI03E1:00: hw version:0x30 max-freq:12500 kHz Use dev_err_probe() so that -EPROBE_DEFER is reported through the deferred probe mechanism instead of as a hard error, while preserving normal error reporting for real registration failures. Fixes: c770d8631e18 ("spi: Add HiSilicon SPI Controller Driver for Kunpeng SoCs") Signed-off-by: Qiang Ma Link: https://patch.msgid.link/20260515102620.1926930-1-maqianga@uniontech.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-hisi-kunpeng.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-hisi-kunpeng.c b/drivers/spi/spi-hisi-kunpeng.c index 046bd894040b1..395214b81179e 100644 --- a/drivers/spi/spi-hisi-kunpeng.c +++ b/drivers/spi/spi-hisi-kunpeng.c @@ -520,10 +520,8 @@ static int hisi_spi_probe(struct platform_device *pdev) } ret = spi_register_controller(host); - if (ret) { - dev_err(dev, "failed to register spi host, ret=%d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to register spi host\n"); if (hisi_spi_debugfs_init(hs)) dev_info(dev, "failed to create debugfs dir\n"); From b53e561dd96595deb175d52afb6be9a3c58a6efa Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 10 May 2026 09:14:49 +0000 Subject: [PATCH 0898/2758] net/sched: add qdisc_qlen_inc() and qdisc_qlen_dec() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 863cd78652018576c60f1b73477a15bdb0ea7551 ] Helpers to increment or decrement sch->q.qlen, with appropriate WRITE_ONCE() to prevent store tearing. Add other WRITE_ONCE() when sch->q.qlen is changed. Signed-off-by: Eric Dumazet Acked-by: Toke Høiland-Jørgensen Link: https://patch.msgid.link/20260510091455.4039245-3-edumazet@google.com Signed-off-by: Jakub Kicinski Stable-dep-of: 5658bddaca41 ("net/sched: sch_dualpi2: annotate data-races in dualpi2_dump_stats()") Signed-off-by: Sasha Levin --- include/net/sch_generic.h | 32 +++++++++++++++++++++----------- net/sched/sch_api.c | 2 +- net/sched/sch_cake.c | 8 ++++---- net/sched/sch_cbs.c | 4 ++-- net/sched/sch_choke.c | 8 ++++---- net/sched/sch_drr.c | 4 ++-- net/sched/sch_dualpi2.c | 6 +++--- net/sched/sch_etf.c | 8 ++++---- net/sched/sch_ets.c | 4 ++-- net/sched/sch_fq.c | 6 +++--- net/sched/sch_fq_codel.c | 7 ++++--- net/sched/sch_fq_pie.c | 4 ++-- net/sched/sch_generic.c | 10 +++++----- net/sched/sch_hfsc.c | 4 ++-- net/sched/sch_hhf.c | 7 ++++--- net/sched/sch_htb.c | 4 ++-- net/sched/sch_mq.c | 5 +++-- net/sched/sch_mqprio.c | 18 ++++++++++-------- net/sched/sch_multiq.c | 4 ++-- net/sched/sch_netem.c | 10 +++++----- net/sched/sch_prio.c | 4 ++-- net/sched/sch_qfq.c | 6 +++--- net/sched/sch_red.c | 4 ++-- net/sched/sch_sfb.c | 4 ++-- net/sched/sch_sfq.c | 9 +++++---- net/sched/sch_skbprio.c | 4 ++-- net/sched/sch_taprio.c | 4 ++-- net/sched/sch_tbf.c | 6 +++--- net/sched/sch_teql.c | 2 +- 29 files changed, 107 insertions(+), 91 deletions(-) diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 11159a50d6a14..195e22a835662 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -542,6 +542,16 @@ static inline int qdisc_qlen(const struct Qdisc *q) return q->q.qlen; } +static inline void qdisc_qlen_inc(struct Qdisc *q) +{ + WRITE_ONCE(q->q.qlen, q->q.qlen + 1); +} + +static inline void qdisc_qlen_dec(struct Qdisc *q) +{ + WRITE_ONCE(q->q.qlen, q->q.qlen - 1); +} + static inline int qdisc_qlen_sum(const struct Qdisc *q) { __u32 qlen = q->qstats.qlen; @@ -549,9 +559,9 @@ static inline int qdisc_qlen_sum(const struct Qdisc *q) if (qdisc_is_percpu_stats(q)) { for_each_possible_cpu(i) - qlen += per_cpu_ptr(q->cpu_qstats, i)->qlen; + qlen += READ_ONCE(per_cpu_ptr(q->cpu_qstats, i)->qlen); } else { - qlen += q->q.qlen; + qlen += READ_ONCE(q->q.qlen); } return qlen; @@ -1063,7 +1073,7 @@ static inline void __qdisc_enqueue_tail(struct sk_buff *skb, qh->tail = skb; qh->head = skb; } - qh->qlen++; + WRITE_ONCE(qh->qlen, qh->qlen + 1); } static inline int qdisc_enqueue_tail(struct sk_buff *skb, struct Qdisc *sch) @@ -1081,7 +1091,7 @@ static inline void __qdisc_enqueue_head(struct sk_buff *skb, if (!qh->head) qh->tail = skb; qh->head = skb; - qh->qlen++; + WRITE_ONCE(qh->qlen, qh->qlen + 1); } static inline struct sk_buff *__qdisc_dequeue_head(struct qdisc_skb_head *qh) @@ -1090,7 +1100,7 @@ static inline struct sk_buff *__qdisc_dequeue_head(struct qdisc_skb_head *qh) if (likely(skb != NULL)) { qh->head = skb->next; - qh->qlen--; + WRITE_ONCE(qh->qlen, qh->qlen - 1); if (qh->head == NULL) qh->tail = NULL; skb->next = NULL; @@ -1105,7 +1115,7 @@ static inline struct sk_buff *qdisc_dequeue_internal(struct Qdisc *sch, bool dir skb = __skb_dequeue(&sch->gso_skb); if (skb) { - sch->q.qlen--; + qdisc_qlen_dec(sch); qdisc_qstats_backlog_dec(sch, skb); return skb; } @@ -1261,7 +1271,7 @@ static inline struct sk_buff *qdisc_peek_dequeued(struct Qdisc *sch) __skb_queue_head(&sch->gso_skb, skb); /* it's still part of the queue */ qdisc_qstats_backlog_inc(sch, skb); - sch->q.qlen++; + qdisc_qlen_inc(sch); } } @@ -1278,7 +1288,7 @@ static inline void qdisc_update_stats_at_dequeue(struct Qdisc *sch, } else { qdisc_qstats_backlog_dec(sch, skb); qdisc_bstats_update(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); } } @@ -1290,7 +1300,7 @@ static inline void qdisc_update_stats_at_enqueue(struct Qdisc *sch, this_cpu_add(sch->cpu_qstats->backlog, pkt_len); } else { sch->qstats.backlog += pkt_len; - sch->q.qlen++; + qdisc_qlen_inc(sch); } } @@ -1306,7 +1316,7 @@ static inline struct sk_buff *qdisc_dequeue_peeked(struct Qdisc *sch) qdisc_qstats_cpu_qlen_dec(sch); } else { qdisc_qstats_backlog_dec(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); } } else { skb = sch->dequeue(sch); @@ -1327,7 +1337,7 @@ static inline void __qdisc_reset_queue(struct qdisc_skb_head *qh) qh->head = NULL; qh->tail = NULL; - qh->qlen = 0; + WRITE_ONCE(qh->qlen, 0); } } diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index ed869a5ffc737..0dd3efd863938 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -805,7 +805,7 @@ void qdisc_tree_reduce_backlog(struct Qdisc *sch, int n, int len) cl = cops->find(sch, parentid); cops->qlen_notify(sch, cl); } - sch->q.qlen -= n; + WRITE_ONCE(sch->q.qlen, sch->q.qlen - n); sch->qstats.backlog -= len; __qdisc_qstats_drop(sch, drops); } diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c index 5862933be8d74..c4915073d9d1a 100644 --- a/net/sched/sch_cake.c +++ b/net/sched/sch_cake.c @@ -1612,7 +1612,7 @@ static unsigned int cake_drop(struct Qdisc *sch, struct sk_buff **to_free) cake_advance_shaper(q, b, skb, now, true); qdisc_drop_reason(skb, sch, to_free, QDISC_DROP_OVERLIMIT); - sch->q.qlen--; + qdisc_qlen_dec(sch); cake_heapify(q, 0); @@ -1822,7 +1822,7 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch, segs); flow_queue_add(flow, segs); - sch->q.qlen++; + qdisc_qlen_inc(sch); numsegs++; slen += segs->len; q->buffer_used += segs->truesize; @@ -1861,7 +1861,7 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch, qdisc_tree_reduce_backlog(sch, 1, ack_pkt_len); consume_skb(ack); } else { - sch->q.qlen++; + qdisc_qlen_inc(sch); q->buffer_used += skb->truesize; } @@ -1987,7 +1987,7 @@ static struct sk_buff *cake_dequeue_one(struct Qdisc *sch) WRITE_ONCE(b->tin_backlog, b->tin_backlog - len); sch->qstats.backlog -= len; q->buffer_used -= skb->truesize; - sch->q.qlen--; + qdisc_qlen_dec(sch); if (q->overflow_timeout) cake_heapify(q, b->overflow_idx[q->cur_flow]); diff --git a/net/sched/sch_cbs.c b/net/sched/sch_cbs.c index 0f953bd46b581..0994da70816ec 100644 --- a/net/sched/sch_cbs.c +++ b/net/sched/sch_cbs.c @@ -97,7 +97,7 @@ static int cbs_child_enqueue(struct sk_buff *skb, struct Qdisc *sch, return err; sch->qstats.backlog += len; - sch->q.qlen++; + qdisc_qlen_inc(sch); return NET_XMIT_SUCCESS; } @@ -168,7 +168,7 @@ static struct sk_buff *cbs_child_dequeue(struct Qdisc *sch, struct Qdisc *child) qdisc_qstats_backlog_dec(sch, skb); qdisc_bstats_update(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); return skb; } diff --git a/net/sched/sch_choke.c b/net/sched/sch_choke.c index 2875bcdb18a41..73d3e673dc7b1 100644 --- a/net/sched/sch_choke.c +++ b/net/sched/sch_choke.c @@ -123,7 +123,7 @@ static void choke_drop_by_idx(struct Qdisc *sch, unsigned int idx, if (idx == q->tail) choke_zap_tail_holes(q); - --sch->q.qlen; + qdisc_qlen_dec(sch); qdisc_qstats_backlog_dec(sch, skb); qdisc_tree_reduce_backlog(sch, 1, qdisc_pkt_len(skb)); qdisc_drop(skb, sch, to_free); @@ -271,7 +271,7 @@ static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch, if (sch->q.qlen < q->limit) { q->tab[q->tail] = skb; q->tail = (q->tail + 1) & q->tab_mask; - ++sch->q.qlen; + qdisc_qlen_inc(sch); qdisc_qstats_backlog_inc(sch, skb); return NET_XMIT_SUCCESS; } @@ -298,7 +298,7 @@ static struct sk_buff *choke_dequeue(struct Qdisc *sch) skb = q->tab[q->head]; q->tab[q->head] = NULL; choke_zap_head_holes(q); - --sch->q.qlen; + qdisc_qlen_dec(sch); qdisc_qstats_backlog_dec(sch, skb); qdisc_bstats_update(sch, skb); @@ -396,7 +396,7 @@ static int choke_change(struct Qdisc *sch, struct nlattr *opt, } dropped += qdisc_pkt_len(skb); qdisc_qstats_backlog_dec(sch, skb); - --sch->q.qlen; + qdisc_qlen_dec(sch); rtnl_qdisc_drop(skb, sch); } qdisc_tree_reduce_backlog(sch, oqlen - sch->q.qlen, dropped); diff --git a/net/sched/sch_drr.c b/net/sched/sch_drr.c index 01335a49e0914..925fa0cfd730c 100644 --- a/net/sched/sch_drr.c +++ b/net/sched/sch_drr.c @@ -366,7 +366,7 @@ static int drr_enqueue(struct sk_buff *skb, struct Qdisc *sch, } sch->qstats.backlog += len; - sch->q.qlen++; + qdisc_qlen_inc(sch); return err; } @@ -399,7 +399,7 @@ static struct sk_buff *drr_dequeue(struct Qdisc *sch) bstats_update(&cl->bstats, skb); qdisc_bstats_update(sch, skb); qdisc_qstats_backlog_dec(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); return skb; } diff --git a/net/sched/sch_dualpi2.c b/net/sched/sch_dualpi2.c index 1ca4d2c03e61d..9a6020238427c 100644 --- a/net/sched/sch_dualpi2.c +++ b/net/sched/sch_dualpi2.c @@ -415,7 +415,7 @@ static int dualpi2_enqueue_skb(struct sk_buff *skb, struct Qdisc *sch, dualpi2_skb_cb(skb)->apply_step = skb_apply_step(skb, q); /* Keep the overall qdisc stats consistent */ - ++sch->q.qlen; + qdisc_qlen_inc(sch); qdisc_qstats_backlog_inc(sch, skb); ++q->packets_in_l; if (!q->l_head_ts) @@ -529,7 +529,7 @@ static struct sk_buff *dequeue_packet(struct Qdisc *sch, qdisc_qstats_backlog_dec(q->l_queue, skb); /* Keep the global queue size consistent */ - --sch->q.qlen; + qdisc_qlen_dec(sch); q->memory_used -= skb->truesize; } else if (c_len) { skb = __qdisc_dequeue_head(&sch->q); @@ -887,7 +887,7 @@ static int dualpi2_change(struct Qdisc *sch, struct nlattr *opt, * l_queue on enqueue; qdisc_dequeue_internal() * handled l_queue, so we further account for sch. */ - --sch->q.qlen; + qdisc_qlen_dec(sch); qdisc_qstats_backlog_dec(sch, skb); q->memory_used -= skb->truesize; rtnl_qdisc_drop(skb, q->l_queue); diff --git a/net/sched/sch_etf.c b/net/sched/sch_etf.c index c74d778c32a1e..ada87a81da6ac 100644 --- a/net/sched/sch_etf.c +++ b/net/sched/sch_etf.c @@ -189,7 +189,7 @@ static int etf_enqueue_timesortedlist(struct sk_buff *nskb, struct Qdisc *sch, rb_insert_color_cached(&nskb->rbnode, &q->head, leftmost); qdisc_qstats_backlog_inc(sch, nskb); - sch->q.qlen++; + qdisc_qlen_inc(sch); /* Now we may need to re-arm the qdisc watchdog for the next packet. */ reset_watchdog(sch); @@ -222,7 +222,7 @@ static void timesortedlist_drop(struct Qdisc *sch, struct sk_buff *skb, qdisc_qstats_backlog_dec(sch, skb); qdisc_drop(skb, sch, &to_free); qdisc_qstats_overlimit(sch); - sch->q.qlen--; + qdisc_qlen_dec(sch); } kfree_skb_list(to_free); @@ -247,7 +247,7 @@ static void timesortedlist_remove(struct Qdisc *sch, struct sk_buff *skb) q->last = skb->tstamp; - sch->q.qlen--; + qdisc_qlen_dec(sch); } static struct sk_buff *etf_dequeue_timesortedlist(struct Qdisc *sch) @@ -426,7 +426,7 @@ static void timesortedlist_clear(struct Qdisc *sch) rb_erase_cached(&skb->rbnode, &q->head); rtnl_kfree_skbs(skb, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); } } diff --git a/net/sched/sch_ets.c b/net/sched/sch_ets.c index a4b07b661b775..c817e0a6c1465 100644 --- a/net/sched/sch_ets.c +++ b/net/sched/sch_ets.c @@ -449,7 +449,7 @@ static int ets_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch, } sch->qstats.backlog += len; - sch->q.qlen++; + qdisc_qlen_inc(sch); return err; } @@ -458,7 +458,7 @@ ets_qdisc_dequeue_skb(struct Qdisc *sch, struct sk_buff *skb) { qdisc_bstats_update(sch, skb); qdisc_qstats_backlog_dec(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); return skb; } diff --git a/net/sched/sch_fq.c b/net/sched/sch_fq.c index f2edcf872981f..1e34ac136b15c 100644 --- a/net/sched/sch_fq.c +++ b/net/sched/sch_fq.c @@ -497,7 +497,7 @@ static void fq_dequeue_skb(struct Qdisc *sch, struct fq_flow *flow, fq_erase_head(sch, flow, skb); skb_mark_not_on_list(skb); qdisc_qstats_backlog_dec(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); qdisc_bstats_update(sch, skb); } @@ -597,7 +597,7 @@ static int fq_enqueue(struct sk_buff *skb, struct Qdisc *sch, flow_queue_add(f, skb); qdisc_qstats_backlog_inc(sch, skb); - sch->q.qlen++; + qdisc_qlen_inc(sch); return NET_XMIT_SUCCESS; } @@ -801,7 +801,7 @@ static void fq_reset(struct Qdisc *sch) struct fq_flow *f; unsigned int idx; - sch->q.qlen = 0; + WRITE_ONCE(sch->q.qlen, 0); sch->qstats.backlog = 0; fq_flow_purge(&q->internal); diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c index 24db54684e8a5..5302d50f4bef6 100644 --- a/net/sched/sch_fq_codel.c +++ b/net/sched/sch_fq_codel.c @@ -178,7 +178,7 @@ static unsigned int fq_codel_drop(struct Qdisc *sch, unsigned int max_packets, q->memory_usage -= mem; sch->qstats.drops += i; sch->qstats.backlog -= len; - sch->q.qlen -= i; + WRITE_ONCE(sch->q.qlen, sch->q.qlen - i); return idx; } @@ -215,7 +215,8 @@ static int fq_codel_enqueue(struct sk_buff *skb, struct Qdisc *sch, get_codel_cb(skb)->mem_usage = skb->truesize; q->memory_usage += get_codel_cb(skb)->mem_usage; memory_limited = q->memory_usage > q->memory_limit; - if (++sch->q.qlen <= sch->limit && !memory_limited) + qdisc_qlen_inc(sch); + if (sch->q.qlen <= sch->limit && !memory_limited) return NET_XMIT_SUCCESS; prev_backlog = sch->qstats.backlog; @@ -266,7 +267,7 @@ static struct sk_buff *dequeue_func(struct codel_vars *vars, void *ctx) WRITE_ONCE(q->backlogs[flow - q->flows], q->backlogs[flow - q->flows] - qdisc_pkt_len(skb)); q->memory_usage -= get_codel_cb(skb)->mem_usage; - sch->q.qlen--; + qdisc_qlen_dec(sch); sch->qstats.backlog -= qdisc_pkt_len(skb); } return skb; diff --git a/net/sched/sch_fq_pie.c b/net/sched/sch_fq_pie.c index 7becbf5362b31..0a4eca4ab086e 100644 --- a/net/sched/sch_fq_pie.c +++ b/net/sched/sch_fq_pie.c @@ -185,7 +185,7 @@ static int fq_pie_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch, q->stats.packets_in++; q->memory_usage += skb->truesize; sch->qstats.backlog += pkt_len; - sch->q.qlen++; + qdisc_qlen_inc(sch); flow_queue_add(sel_flow, skb); if (list_empty(&sel_flow->flowchain)) { list_add_tail(&sel_flow->flowchain, &q->new_flows); @@ -263,7 +263,7 @@ static struct sk_buff *fq_pie_qdisc_dequeue(struct Qdisc *sch) skb = dequeue_head(flow); pkt_len = qdisc_pkt_len(skb); sch->qstats.backlog -= pkt_len; - sch->q.qlen--; + qdisc_qlen_dec(sch); qdisc_bstats_update(sch, skb); } diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index a93321db8fd75..e35d9c58850fa 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -118,7 +118,7 @@ static inline struct sk_buff *__skb_dequeue_bad_txq(struct Qdisc *q) qdisc_qstats_cpu_qlen_dec(q); } else { qdisc_qstats_backlog_dec(q, skb); - q->q.qlen--; + qdisc_qlen_dec(q); } } else { skb = SKB_XOFF_MAGIC; @@ -159,7 +159,7 @@ static inline void qdisc_enqueue_skb_bad_txq(struct Qdisc *q, qdisc_qstats_cpu_qlen_inc(q); } else { qdisc_qstats_backlog_inc(q, skb); - q->q.qlen++; + qdisc_qlen_inc(q); } if (lock) @@ -188,7 +188,7 @@ static inline void dev_requeue_skb(struct sk_buff *skb, struct Qdisc *q) } else { q->qstats.requeues++; qdisc_qstats_backlog_inc(q, skb); - q->q.qlen++; + qdisc_qlen_inc(q); } skb = next; @@ -294,7 +294,7 @@ static struct sk_buff *dequeue_skb(struct Qdisc *q, bool *validate, qdisc_qstats_cpu_qlen_dec(q); } else { qdisc_qstats_backlog_dec(q, skb); - q->q.qlen--; + qdisc_qlen_dec(q); } } else { skb = NULL; @@ -1059,7 +1059,7 @@ void qdisc_reset(struct Qdisc *qdisc) __skb_queue_purge(&qdisc->gso_skb); __skb_queue_purge(&qdisc->skb_bad_txq); - qdisc->q.qlen = 0; + WRITE_ONCE(qdisc->q.qlen, 0); qdisc->qstats.backlog = 0; } EXPORT_SYMBOL(qdisc_reset); diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index 55258aac6b93c..e3dd6de8f1b6e 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -1561,7 +1561,7 @@ hfsc_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff **to_free) } sch->qstats.backlog += len; - sch->q.qlen++; + qdisc_qlen_inc(sch); if (first && !cl_in_el_or_vttree(cl)) { if (cl->cl_flags & HFSC_RSC) @@ -1650,7 +1650,7 @@ hfsc_dequeue(struct Qdisc *sch) qdisc_bstats_update(sch, skb); qdisc_qstats_backlog_dec(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); return skb; } diff --git a/net/sched/sch_hhf.c b/net/sched/sch_hhf.c index 96021f52d835b..1e25b75daae2e 100644 --- a/net/sched/sch_hhf.c +++ b/net/sched/sch_hhf.c @@ -360,7 +360,7 @@ static unsigned int hhf_drop(struct Qdisc *sch, struct sk_buff **to_free) if (bucket->head) { struct sk_buff *skb = dequeue_head(bucket); - sch->q.qlen--; + qdisc_qlen_dec(sch); qdisc_qstats_backlog_dec(sch, skb); qdisc_drop(skb, sch, to_free); } @@ -400,7 +400,8 @@ static int hhf_enqueue(struct sk_buff *skb, struct Qdisc *sch, } bucket->deficit = weight * q->quantum; } - if (++sch->q.qlen <= sch->limit) + qdisc_qlen_inc(sch); + if (sch->q.qlen <= sch->limit) return NET_XMIT_SUCCESS; prev_backlog = sch->qstats.backlog; @@ -443,7 +444,7 @@ static struct sk_buff *hhf_dequeue(struct Qdisc *sch) if (bucket->head) { skb = dequeue_head(bucket); - sch->q.qlen--; + qdisc_qlen_dec(sch); qdisc_qstats_backlog_dec(sch, skb); } diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index eb12381795ce1..c22ccd8eae8c7 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -651,7 +651,7 @@ static int htb_enqueue(struct sk_buff *skb, struct Qdisc *sch, } sch->qstats.backlog += len; - sch->q.qlen++; + qdisc_qlen_inc(sch); return NET_XMIT_SUCCESS; } @@ -951,7 +951,7 @@ static struct sk_buff *htb_dequeue(struct Qdisc *sch) ok: qdisc_bstats_update(sch, skb); qdisc_qstats_backlog_dec(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); return skb; } diff --git a/net/sched/sch_mq.c b/net/sched/sch_mq.c index a0133a7b9d3b0..ec8c91d3fde04 100644 --- a/net/sched/sch_mq.c +++ b/net/sched/sch_mq.c @@ -143,10 +143,10 @@ EXPORT_SYMBOL_NS_GPL(mq_attach, "NET_SCHED_INTERNAL"); void mq_dump_common(struct Qdisc *sch, struct sk_buff *skb) { struct net_device *dev = qdisc_dev(sch); + unsigned int qlen = 0; struct Qdisc *qdisc; unsigned int ntx; - sch->q.qlen = 0; gnet_stats_basic_sync_init(&sch->bstats); memset(&sch->qstats, 0, sizeof(sch->qstats)); @@ -163,10 +163,11 @@ void mq_dump_common(struct Qdisc *sch, struct sk_buff *skb) &qdisc->bstats, false); gnet_stats_add_queue(&sch->qstats, qdisc->cpu_qstats, &qdisc->qstats); - sch->q.qlen += qdisc_qlen(qdisc); + qlen += qdisc_qlen(qdisc); spin_unlock_bh(qdisc_lock(qdisc)); } + WRITE_ONCE(sch->q.qlen, qlen); } EXPORT_SYMBOL_NS_GPL(mq_dump_common, "NET_SCHED_INTERNAL"); diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c index 002add5ce9e0a..91a92992cd24a 100644 --- a/net/sched/sch_mqprio.c +++ b/net/sched/sch_mqprio.c @@ -555,10 +555,11 @@ static int mqprio_dump(struct Qdisc *sch, struct sk_buff *skb) struct mqprio_sched *priv = qdisc_priv(sch); struct nlattr *nla = (struct nlattr *)skb_tail_pointer(skb); struct tc_mqprio_qopt opt = { 0 }; + unsigned int qlen = 0; struct Qdisc *qdisc; unsigned int ntx; - sch->q.qlen = 0; + qlen = 0; gnet_stats_basic_sync_init(&sch->bstats); memset(&sch->qstats, 0, sizeof(sch->qstats)); @@ -575,10 +576,11 @@ static int mqprio_dump(struct Qdisc *sch, struct sk_buff *skb) &qdisc->bstats, false); gnet_stats_add_queue(&sch->qstats, qdisc->cpu_qstats, &qdisc->qstats); - sch->q.qlen += qdisc_qlen(qdisc); + qlen += qdisc_qlen(qdisc); spin_unlock_bh(qdisc_lock(qdisc)); } + WRITE_ONCE(sch->q.qlen, qlen); mqprio_qopt_reconstruct(dev, &opt); opt.hw = priv->hw_offload; @@ -663,12 +665,12 @@ static int mqprio_dump_class_stats(struct Qdisc *sch, unsigned long cl, __acquires(d->lock) { if (cl >= TC_H_MIN_PRIORITY) { - int i; - __u32 qlen; - struct gnet_stats_queue qstats = {0}; - struct gnet_stats_basic_sync bstats; struct net_device *dev = qdisc_dev(sch); struct netdev_tc_txq tc = dev->tc_to_txq[cl & TC_BITMASK]; + struct gnet_stats_queue qstats = {0}; + struct gnet_stats_basic_sync bstats; + u32 qlen = 0; + int i; gnet_stats_basic_sync_init(&bstats); /* Drop lock here it will be reclaimed before touching @@ -689,11 +691,11 @@ static int mqprio_dump_class_stats(struct Qdisc *sch, unsigned long cl, &qdisc->bstats, false); gnet_stats_add_queue(&qstats, qdisc->cpu_qstats, &qdisc->qstats); - sch->q.qlen += qdisc_qlen(qdisc); + qlen += qdisc_qlen(qdisc); spin_unlock_bh(qdisc_lock(qdisc)); } - qlen = qdisc_qlen(sch) + qstats.qlen; + qlen = qlen + qstats.qlen; /* Reclaim root sleeping lock before completing stats */ if (d->lock) diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c index 9f822fee113df..4e465d11e3d75 100644 --- a/net/sched/sch_multiq.c +++ b/net/sched/sch_multiq.c @@ -76,7 +76,7 @@ multiq_enqueue(struct sk_buff *skb, struct Qdisc *sch, ret = qdisc_enqueue(skb, qdisc, to_free); if (ret == NET_XMIT_SUCCESS) { - sch->q.qlen++; + qdisc_qlen_inc(sch); return NET_XMIT_SUCCESS; } if (net_xmit_drop_count(ret)) @@ -106,7 +106,7 @@ static struct sk_buff *multiq_dequeue(struct Qdisc *sch) skb = qdisc->dequeue(qdisc); if (skb) { qdisc_bstats_update(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); return skb; } } diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 17a79fe2f0911..db44cdb47dcef 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -416,7 +416,7 @@ static void tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch) rb_insert_color(&nskb->rbnode, &q->t_root); } q->t_len++; - sch->q.qlen++; + qdisc_qlen_inc(sch); } /* netem can't properly corrupt a megapacket (like we get from GSO), so instead @@ -750,19 +750,19 @@ static struct sk_buff *netem_dequeue(struct Qdisc *sch) if (net_xmit_drop_count(err)) qdisc_qstats_drop(sch); sch->qstats.backlog -= pkt_len; - sch->q.qlen--; + qdisc_qlen_dec(sch); qdisc_tree_reduce_backlog(sch, 1, pkt_len); } goto tfifo_dequeue; } - sch->q.qlen--; + qdisc_qlen_dec(sch); goto deliver; } if (q->qdisc) { skb = q->qdisc->ops->dequeue(q->qdisc); if (skb) { - sch->q.qlen--; + qdisc_qlen_dec(sch); goto deliver; } } @@ -775,7 +775,7 @@ static struct sk_buff *netem_dequeue(struct Qdisc *sch) if (q->qdisc) { skb = q->qdisc->ops->dequeue(q->qdisc); if (skb) { - sch->q.qlen--; + qdisc_qlen_dec(sch); goto deliver; } } diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c index 9e2b9a490db23..fe42ae3d6b696 100644 --- a/net/sched/sch_prio.c +++ b/net/sched/sch_prio.c @@ -86,7 +86,7 @@ prio_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff **to_free) ret = qdisc_enqueue(skb, qdisc, to_free); if (ret == NET_XMIT_SUCCESS) { sch->qstats.backlog += len; - sch->q.qlen++; + qdisc_qlen_inc(sch); return NET_XMIT_SUCCESS; } if (net_xmit_drop_count(ret)) @@ -119,7 +119,7 @@ static struct sk_buff *prio_dequeue(struct Qdisc *sch) if (skb) { qdisc_bstats_update(sch, skb); qdisc_qstats_backlog_dec(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); return skb; } } diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c index 699e45873f861..195c434aae5f7 100644 --- a/net/sched/sch_qfq.c +++ b/net/sched/sch_qfq.c @@ -1152,12 +1152,12 @@ static struct sk_buff *qfq_dequeue(struct Qdisc *sch) if (!skb) return NULL; - sch->q.qlen--; + qdisc_qlen_dec(sch); skb = agg_dequeue(in_serv_agg, cl, len); if (!skb) { - sch->q.qlen++; + qdisc_qlen_inc(sch); return NULL; } @@ -1265,7 +1265,7 @@ static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch, _bstats_update(&cl->bstats, len, gso_segs); sch->qstats.backlog += len; - ++sch->q.qlen; + qdisc_qlen_inc(sch); agg = cl->agg; /* if the class is active, then done here */ diff --git a/net/sched/sch_red.c b/net/sched/sch_red.c index 4d0e44a2e7c66..0719590dfd73b 100644 --- a/net/sched/sch_red.c +++ b/net/sched/sch_red.c @@ -139,7 +139,7 @@ static int red_enqueue(struct sk_buff *skb, struct Qdisc *sch, ret = qdisc_enqueue(skb, child, to_free); if (likely(ret == NET_XMIT_SUCCESS)) { sch->qstats.backlog += len; - sch->q.qlen++; + qdisc_qlen_inc(sch); } else if (net_xmit_drop_count(ret)) { WRITE_ONCE(q->stats.pdrop, q->stats.pdrop + 1); @@ -166,7 +166,7 @@ static struct sk_buff *red_dequeue(struct Qdisc *sch) if (skb) { qdisc_bstats_update(sch, skb); qdisc_qstats_backlog_dec(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); } else { if (!red_is_idling(&q->vars)) red_start_of_idle_period(&q->vars); diff --git a/net/sched/sch_sfb.c b/net/sched/sch_sfb.c index d3ee8e5479b35..efd9251c3add3 100644 --- a/net/sched/sch_sfb.c +++ b/net/sched/sch_sfb.c @@ -416,7 +416,7 @@ static int sfb_enqueue(struct sk_buff *skb, struct Qdisc *sch, ret = qdisc_enqueue(skb, child, to_free); if (likely(ret == NET_XMIT_SUCCESS)) { sch->qstats.backlog += len; - sch->q.qlen++; + qdisc_qlen_inc(sch); increment_qlen(&cb, q); } else if (net_xmit_drop_count(ret)) { WRITE_ONCE(q->stats.childdrop, @@ -446,7 +446,7 @@ static struct sk_buff *sfb_dequeue(struct Qdisc *sch) if (skb) { qdisc_bstats_update(sch, skb); qdisc_qstats_backlog_dec(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); decrement_qlen(skb, q); } diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index f39822babf88b..f9807ee2cf6c7 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -302,7 +302,7 @@ static unsigned int sfq_drop(struct Qdisc *sch, struct sk_buff **to_free) len = qdisc_pkt_len(skb); WRITE_ONCE(slot->backlog, slot->backlog - len); sfq_dec(q, x); - sch->q.qlen--; + qdisc_qlen_dec(sch); qdisc_qstats_backlog_dec(sch, skb); qdisc_drop_reason(skb, sch, to_free, QDISC_DROP_OVERLIMIT); return len; @@ -456,7 +456,8 @@ sfq_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff **to_free) /* We could use a bigger initial quantum for new flows */ WRITE_ONCE(slot->allot, q->quantum); } - if (++sch->q.qlen <= q->limit) + qdisc_qlen_inc(sch); + if (sch->q.qlen <= q->limit) return NET_XMIT_SUCCESS; qlen = slot->qlen; @@ -497,7 +498,7 @@ sfq_dequeue(struct Qdisc *sch) skb = slot_dequeue_head(slot); sfq_dec(q, a); qdisc_bstats_update(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); qdisc_qstats_backlog_dec(sch, skb); WRITE_ONCE(slot->backlog, slot->backlog - qdisc_pkt_len(skb)); /* Is the slot empty? */ @@ -596,7 +597,7 @@ static void sfq_rehash(struct Qdisc *sch) WRITE_ONCE(slot->allot, q->quantum); } } - sch->q.qlen -= dropped; + WRITE_ONCE(sch->q.qlen, sch->q.qlen - dropped); qdisc_tree_reduce_backlog(sch, dropped, drop_len); } diff --git a/net/sched/sch_skbprio.c b/net/sched/sch_skbprio.c index f485f62ab721a..52abfb4015a36 100644 --- a/net/sched/sch_skbprio.c +++ b/net/sched/sch_skbprio.c @@ -93,7 +93,7 @@ static int skbprio_enqueue(struct sk_buff *skb, struct Qdisc *sch, if (prio < q->lowest_prio) q->lowest_prio = prio; - sch->q.qlen++; + qdisc_qlen_inc(sch); return NET_XMIT_SUCCESS; } @@ -145,7 +145,7 @@ static struct sk_buff *skbprio_dequeue(struct Qdisc *sch) if (unlikely(!skb)) return NULL; - sch->q.qlen--; + qdisc_qlen_dec(sch); qdisc_qstats_backlog_dec(sch, skb); qdisc_bstats_update(sch, skb); diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c index 45245157e00a6..06d93d9269187 100644 --- a/net/sched/sch_taprio.c +++ b/net/sched/sch_taprio.c @@ -574,7 +574,7 @@ static int taprio_enqueue_one(struct sk_buff *skb, struct Qdisc *sch, } qdisc_qstats_backlog_inc(sch, skb); - sch->q.qlen++; + qdisc_qlen_inc(sch); return qdisc_enqueue(skb, child, to_free); } @@ -755,7 +755,7 @@ static struct sk_buff *taprio_dequeue_from_txq(struct Qdisc *sch, int txq, qdisc_bstats_update(sch, skb); qdisc_qstats_backlog_dec(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); return skb; } diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c index f2340164f579a..25edf11a7d671 100644 --- a/net/sched/sch_tbf.c +++ b/net/sched/sch_tbf.c @@ -231,7 +231,7 @@ static int tbf_segment(struct sk_buff *skb, struct Qdisc *sch, len += seg_len; } } - sch->q.qlen += nb; + WRITE_ONCE(sch->q.qlen, sch->q.qlen + nb); sch->qstats.backlog += len; if (nb > 0) { qdisc_tree_reduce_backlog(sch, 1 - nb, prev_len - len); @@ -264,7 +264,7 @@ static int tbf_enqueue(struct sk_buff *skb, struct Qdisc *sch, } sch->qstats.backlog += len; - sch->q.qlen++; + qdisc_qlen_inc(sch); return NET_XMIT_SUCCESS; } @@ -309,7 +309,7 @@ static struct sk_buff *tbf_dequeue(struct Qdisc *sch) q->tokens = toks; q->ptokens = ptoks; qdisc_qstats_backlog_dec(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); qdisc_bstats_update(sch, skb); return skb; } diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index ec4039a201a2c..e7bbc9e5174d0 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -107,7 +107,7 @@ teql_dequeue(struct Qdisc *sch) } else { qdisc_bstats_update(sch, skb); } - sch->q.qlen = dat->q.qlen + q->q.qlen; + WRITE_ONCE(sch->q.qlen, dat->q.qlen + READ_ONCE(q->q.qlen)); return skb; } From c1e94b098328a7eb95b9b14fa8307a7f6cf7e4d4 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 14 May 2026 11:47:13 +0000 Subject: [PATCH 0899/2758] net/sched: sch_dualpi2: annotate data-races in dualpi2_dump_stats() [ Upstream commit 5658bddaca41417331d2ac4b24a9b159a839ab87 ] dualpi2_dump_stats() runs without qdisc lock held. Add missing READ_ONCE()/WRITE_ONCE() annotations. Fixes: d4de8bffbef4 ("sched: Dump configuration and statistics of dualpi2 qdisc") Signed-off-by: Eric Dumazet Cc: Vineet Agarwal Link: https://patch.msgid.link/20260514114713.4134674-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/sch_dualpi2.c | 68 ++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/net/sched/sch_dualpi2.c b/net/sched/sch_dualpi2.c index 9a6020238427c..b81a922f5e468 100644 --- a/net/sched/sch_dualpi2.c +++ b/net/sched/sch_dualpi2.c @@ -190,7 +190,7 @@ static bool skb_apply_step(struct sk_buff *skb, struct dualpi2_sched_data *q) static bool dualpi2_mark(struct dualpi2_sched_data *q, struct sk_buff *skb) { if (INET_ECN_set_ce(skb)) { - q->ecn_mark++; + WRITE_ONCE(q->ecn_mark, q->ecn_mark + 1); return true; } return false; @@ -198,7 +198,7 @@ static bool dualpi2_mark(struct dualpi2_sched_data *q, struct sk_buff *skb) static void dualpi2_reset_c_protection(struct dualpi2_sched_data *q) { - q->c_protection_credit = q->c_protection_init; + WRITE_ONCE(q->c_protection_credit, q->c_protection_init); } /* This computes the initial credit value and WRR weight for the L queue (wl) @@ -403,12 +403,12 @@ static int dualpi2_enqueue_skb(struct sk_buff *skb, struct Qdisc *sch, cb = dualpi2_skb_cb(skb); cb->ts = ktime_get_ns(); - q->memory_used += skb->truesize; + WRITE_ONCE(q->memory_used, q->memory_used + skb->truesize); if (q->memory_used > q->max_memory_used) - q->max_memory_used = q->memory_used; + WRITE_ONCE(q->max_memory_used, q->memory_used); if (qdisc_qlen(sch) > q->maxq) - q->maxq = qdisc_qlen(sch); + WRITE_ONCE(q->maxq, qdisc_qlen(sch)); if (skb_in_l_queue(skb)) { /* Apply step thresh if skb is L4S && L-queue len >= min_qlen */ @@ -417,14 +417,14 @@ static int dualpi2_enqueue_skb(struct sk_buff *skb, struct Qdisc *sch, /* Keep the overall qdisc stats consistent */ qdisc_qlen_inc(sch); qdisc_qstats_backlog_inc(sch, skb); - ++q->packets_in_l; + WRITE_ONCE(q->packets_in_l, q->packets_in_l + 1); if (!q->l_head_ts) - q->l_head_ts = cb->ts; + WRITE_ONCE(q->l_head_ts, cb->ts); return qdisc_enqueue_tail(skb, q->l_queue); } - ++q->packets_in_c; + WRITE_ONCE(q->packets_in_c, q->packets_in_c + 1); if (!q->c_head_ts) - q->c_head_ts = cb->ts; + WRITE_ONCE(q->c_head_ts, cb->ts); return qdisc_enqueue_tail(skb, sch); } @@ -530,17 +530,16 @@ static struct sk_buff *dequeue_packet(struct Qdisc *sch, /* Keep the global queue size consistent */ qdisc_qlen_dec(sch); - q->memory_used -= skb->truesize; } else if (c_len) { skb = __qdisc_dequeue_head(&sch->q); WRITE_ONCE(q->c_head_ts, head_enqueue_time(sch)); if (qdisc_qlen(q->l_queue)) *credit_change = ~((s32)q->c_protection_wl) + 1; - q->memory_used -= skb->truesize; } else { dualpi2_reset_c_protection(q); return NULL; } + WRITE_ONCE(q->memory_used, q->memory_used - skb->truesize); *credit_change *= qdisc_pkt_len(skb); qdisc_qstats_backlog_dec(sch, skb); return skb; @@ -563,7 +562,7 @@ static int do_step_aqm(struct dualpi2_sched_data *q, struct sk_buff *skb, } if (dualpi2_mark(q, skb)) - ++q->step_marks; + WRITE_ONCE(q->step_marks, q->step_marks + 1); } qdisc_bstats_update(q->l_queue, skb); return 0; @@ -599,7 +598,8 @@ static struct sk_buff *dualpi2_qdisc_dequeue(struct Qdisc *sch) continue; } - q->c_protection_credit += credit_change; + WRITE_ONCE(q->c_protection_credit, + q->c_protection_credit + credit_change); qdisc_bstats_update(sch, skb); break; } @@ -875,7 +875,7 @@ static int dualpi2_change(struct Qdisc *sch, struct nlattr *opt, WARN_ON_ONCE(1); break; } - q->memory_used -= skb->truesize; + WRITE_ONCE(q->memory_used, q->memory_used - skb->truesize); rtnl_qdisc_drop(skb, sch); } else if (qdisc_qlen(q->l_queue)) { skb = qdisc_dequeue_internal(q->l_queue, true); @@ -889,7 +889,7 @@ static int dualpi2_change(struct Qdisc *sch, struct nlattr *opt, */ qdisc_qlen_dec(sch); qdisc_qstats_backlog_dec(sch, skb); - q->memory_used -= skb->truesize; + WRITE_ONCE(q->memory_used, q->memory_used - skb->truesize); rtnl_qdisc_drop(skb, q->l_queue); qdisc_qstats_drop(sch); } else { @@ -1045,15 +1045,15 @@ static int dualpi2_dump_stats(struct Qdisc *sch, struct gnet_dump *d) struct dualpi2_sched_data *q = qdisc_priv(sch); struct tc_dualpi2_xstats st = { .prob = READ_ONCE(q->pi2_prob), - .packets_in_c = q->packets_in_c, - .packets_in_l = q->packets_in_l, - .maxq = q->maxq, - .ecn_mark = q->ecn_mark, - .credit = q->c_protection_credit, - .step_marks = q->step_marks, - .memory_used = q->memory_used, - .max_memory_used = q->max_memory_used, - .memory_limit = q->memory_limit, + .packets_in_c = READ_ONCE(q->packets_in_c), + .packets_in_l = READ_ONCE(q->packets_in_l), + .maxq = READ_ONCE(q->maxq), + .ecn_mark = READ_ONCE(q->ecn_mark), + .credit = READ_ONCE(q->c_protection_credit), + .step_marks = READ_ONCE(q->step_marks), + .memory_used = READ_ONCE(q->memory_used), + .max_memory_used = READ_ONCE(q->max_memory_used), + .memory_limit = READ_ONCE(q->memory_limit), }; u64 qc, ql; @@ -1073,16 +1073,16 @@ static void dualpi2_reset(struct Qdisc *sch) qdisc_reset_queue(sch); qdisc_reset_queue(q->l_queue); - q->c_head_ts = 0; - q->l_head_ts = 0; - q->pi2_prob = 0; - q->packets_in_c = 0; - q->packets_in_l = 0; - q->maxq = 0; - q->ecn_mark = 0; - q->step_marks = 0; - q->memory_used = 0; - q->max_memory_used = 0; + WRITE_ONCE(q->c_head_ts, 0); + WRITE_ONCE(q->l_head_ts, 0); + WRITE_ONCE(q->pi2_prob, 0); + WRITE_ONCE(q->packets_in_c, 0); + WRITE_ONCE(q->packets_in_l, 0); + WRITE_ONCE(q->maxq, 0); + WRITE_ONCE(q->ecn_mark, 0); + WRITE_ONCE(q->step_marks, 0); + WRITE_ONCE(q->memory_used, 0); + WRITE_ONCE(q->max_memory_used, 0); dualpi2_reset_c_protection(q); } From fbd521a10c60749a81b6036eb869fb2e35076a15 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 14 May 2026 09:59:32 +0000 Subject: [PATCH 0900/2758] net/sched: sch_htb: do not change sch->flags in htb_dump() [ Upstream commit 9b949fa69129e4b694ed11ee3be6d6edd4a9b8f4 ] htb_dump() runs without holding qdisc spinlock. It is illegal to touch sch->flags with non locked RMW, as concurrent readers might see intermediate wrong values. Set TCQ_F_OFFLOADED in control path (htb_init()) instead. Fixes: d03b195b5aa0 ("sch_htb: Hierarchical QoS hardware offload") Signed-off-by: Eric Dumazet Link: https://patch.msgid.link/20260514095935.3926276-2-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/sch_htb.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index c22ccd8eae8c7..f94a44fad4e9d 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -1147,6 +1147,7 @@ static int htb_init(struct Qdisc *sch, struct nlattr *opt, * parts (especially calling ndo_setup_tc) on errors. */ q->offload = true; + sch->flags |= TCQ_F_OFFLOADED; return 0; } @@ -1207,11 +1208,6 @@ static int htb_dump(struct Qdisc *sch, struct sk_buff *skb) struct nlattr *nest; struct tc_htb_glob gopt; - if (q->offload) - sch->flags |= TCQ_F_OFFLOADED; - else - sch->flags &= ~TCQ_F_OFFLOADED; - sch->qstats.overlimits = q->overlimits; /* Its safe to not acquire qdisc lock. As we hold RTNL, * no change can happen on the qdisc parameters. From 5bf2f3a739b948223fa4a2529087865afeb3a9da Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 14 May 2026 09:59:33 +0000 Subject: [PATCH 0901/2758] net/sched: sch_htb: annotate data-races (I) [ Upstream commit e54c33503bf7cebb1c1790251ce90f1252678081 ] htb_dump() runs without holding qdisc spinlock. Add missing READ_ONCE()/WRITE_ONCE() annotations around q->overlimits and q->direct_pkts. Fixes: edb09eb17ed8 ("net: sched: do not acquire qdisc spinlock in qdisc/class stats dump") Signed-off-by: Eric Dumazet Link: https://patch.msgid.link/20260514095935.3926276-3-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/sch_htb.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index f94a44fad4e9d..ccabafed3fe44 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -568,7 +568,7 @@ htb_change_class_mode(struct htb_sched *q, struct htb_class *cl, s64 *diff) if (new_mode == HTB_CANT_SEND) { cl->overlimits++; - q->overlimits++; + WRITE_ONCE(q->overlimits, q->overlimits + 1); } if (cl->prio_activity) { /* not necessary: speed optimization */ @@ -628,7 +628,7 @@ static int htb_enqueue(struct sk_buff *skb, struct Qdisc *sch, /* enqueue to helper queue */ if (q->direct_queue.qlen < q->direct_qlen) { __qdisc_enqueue_tail(skb, &q->direct_queue); - q->direct_pkts++; + WRITE_ONCE(q->direct_pkts, q->direct_pkts + 1); } else { return qdisc_drop(skb, sch, to_free); } @@ -1208,12 +1208,12 @@ static int htb_dump(struct Qdisc *sch, struct sk_buff *skb) struct nlattr *nest; struct tc_htb_glob gopt; - sch->qstats.overlimits = q->overlimits; + sch->qstats.overlimits = READ_ONCE(q->overlimits); /* Its safe to not acquire qdisc lock. As we hold RTNL, * no change can happen on the qdisc parameters. */ - gopt.direct_pkts = q->direct_pkts; + gopt.direct_pkts = READ_ONCE(q->direct_pkts); gopt.version = HTB_VER; gopt.rate2quantum = q->rate2quantum; gopt.defcls = q->defcls; From e889aa99ad3ed48bb0ddcff6475b17542532d18b Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Wed, 13 May 2026 10:55:09 +0800 Subject: [PATCH 0902/2758] ipv6: addrconf: bail out of dad_failure when state is no longer POSTDAD [ Upstream commit 627ac78f2741e2ebd2225e2e953b6964a8a9182f ] addrconf_dad_failure() transitions ifp->state from DAD to POSTDAD via addrconf_dad_end(), which drops ifp->lock on return. The lock is re-acquired after net_info_ratelimited(). A concurrent ipv6_del_addr() can take the lock in that window, set ifp->state to DEAD and run list_del_rcu(&ifp->if_list). addrconf_dad_failure() then overwrites DEAD with ERRDAD at errdad: and schedules a new dad_work. The work calls ipv6_del_addr() again, hitting the already-poisoned list entry: general protection fault: 0000 [#1] SMP NOPTI CPU: 4 PID: 217 Comm: kworker/4:1 Workqueue: ipv6_addrconf addrconf_dad_work RIP: 0010:ipv6_del_addr+0xe9/0x280 RAX: dead000000000122 Call Trace: addrconf_dad_stop+0x113/0x140 addrconf_dad_work+0x28c/0x430 process_one_work+0x1eb/0x3b0 worker_thread+0x4d/0x400 kthread+0x104/0x140 ret_from_fork+0x35/0x40 Fold the addrconf_dad_end() logic into addrconf_dad_failure() under a single ifp->lock critical section. The STABLE_PRIVACY branch temporarily drops ifp->lock around address regeneration, so at lock_errdad: verify the state is still POSTDAD before transitioning to ERRDAD; bail out otherwise to avoid overwriting a state set by another path while the lock was released. Fixes: c15b1ccadb32 ("ipv6: move DAD and addrconf_verify processing to workqueue") Signed-off-by: Linmao Li Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260513025509.3776405-1-lilinmao@kylinos.cn Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/addrconf.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index c9e5d3e48ab98..7953f5653451b 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -2168,16 +2168,18 @@ void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp) struct net *net = dev_net(idev->dev); int max_addresses; - if (addrconf_dad_end(ifp)) { + spin_lock_bh(&ifp->lock); + + if (ifp->state != INET6_IFADDR_STATE_DAD) { + spin_unlock_bh(&ifp->lock); in6_ifa_put(ifp); return; } + ifp->state = INET6_IFADDR_STATE_POSTDAD; net_info_ratelimited("%s: IPv6 duplicate address %pI6c used by %pM detected!\n", ifp->idev->dev->name, &ifp->addr, eth_hdr(skb)->h_source); - spin_lock_bh(&ifp->lock); - if (ifp->flags & IFA_F_STABLE_PRIVACY) { struct in6_addr new_addr; struct inet6_ifaddr *ifp2; @@ -2225,6 +2227,11 @@ void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp) in6_ifa_put(ifp2); lock_errdad: spin_lock_bh(&ifp->lock); + if (ifp->state != INET6_IFADDR_STATE_POSTDAD) { + spin_unlock_bh(&ifp->lock); + in6_ifa_put(ifp); + return; + } } errdad: From f88e12c95fc19f719e06ca1e9eb20fdad68ef61a Mon Sep 17 00:00:00 2001 From: Prathamesh Deshpande Date: Sun, 12 Apr 2026 02:18:49 +0100 Subject: [PATCH 0903/2758] IB/mlx5: Fix transport-domain rollback and initialize lb mutex earlier [ Upstream commit e79389115b9d27287ff6230a9750675106ed7668 ] mlx5_ib_alloc_transport_domain() allocates a transport domain and then may fail in mlx5_ib_enable_lb(). In that case, the allocated TD is leaked. Fix this by deallocating the TD when mlx5_ib_enable_lb() returns an error. Also return 0 explicitly in the no-loopback-capability success branch, and move dev->lb.mutex initialization to mlx5_ib_stage_init_init(). Fixes: 146d2f1af324 ("IB/mlx5: Allocate a Transport Domain for each ucontext") Signed-off-by: Prathamesh Deshpande Signed-off-by: Leon Romanovsky Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/main.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 61078281953d6..aa2eb64ecf15e 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -2069,9 +2069,13 @@ static int mlx5_ib_alloc_transport_domain(struct mlx5_ib_dev *dev, u32 *tdn, if ((MLX5_CAP_GEN(dev->mdev, port_type) != MLX5_CAP_PORT_TYPE_ETH) || (!MLX5_CAP_GEN(dev->mdev, disable_local_lb_uc) && !MLX5_CAP_GEN(dev->mdev, disable_local_lb_mc))) - return err; + return 0; + + err = mlx5_ib_enable_lb(dev, true, false); + if (err) + mlx5_cmd_dealloc_transport_domain(dev->mdev, *tdn, uid); - return mlx5_ib_enable_lb(dev, true, false); + return err; } static void mlx5_ib_dealloc_transport_domain(struct mlx5_ib_dev *dev, u32 tdn, @@ -4488,6 +4492,8 @@ static int mlx5_ib_stage_init_init(struct mlx5_ib_dev *dev) dev->port[i].roce.last_port_state = IB_PORT_DOWN; } + mutex_init(&dev->lb.mutex); + err = mlx5r_cmd_query_special_mkeys(dev); if (err) return err; @@ -4788,11 +4794,6 @@ static int mlx5_ib_stage_caps_init(struct mlx5_ib_dev *dev) if (err) return err; - if ((MLX5_CAP_GEN(dev->mdev, port_type) == MLX5_CAP_PORT_TYPE_ETH) && - (MLX5_CAP_GEN(dev->mdev, disable_local_lb_uc) || - MLX5_CAP_GEN(dev->mdev, disable_local_lb_mc))) - mutex_init(&dev->lb.mutex); - if (MLX5_CAP_GEN_64(dev->mdev, general_obj_types) & MLX5_GENERAL_OBJ_TYPES_CAP_VIRTIO_NET_Q) { err = mlx5_ib_init_var_region(dev); From 8efee40ef632d94fe141474bbde2c327e7791a08 Mon Sep 17 00:00:00 2001 From: Alexander Chesnokov Date: Mon, 13 Apr 2026 12:14:43 +0300 Subject: [PATCH 0904/2758] RDMA/hns: Fix arithmetic overflow in calc_hem_config() [ Upstream commit a38e4410af9ad8b7ad2217254da06cd4dc21f0ed ] If bt_num is 3 or 2, then the expressions like l0_idx * chunk_ba_num + l1_idx are computed in 32-bit arithmetic before being assigned to a u64 index field, which can lead to overflow. Cast the first operand to u64 to ensure the arithmetic is performed in 64-bit. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 2f49de21f3e9 ("RDMA/hns: Optimize mhop get flow for multi-hop addressing") Signed-off-by: Alexander Chesnokov Link: https://patch.msgid.link/20260413091527.39990-1-Alexander.Chesnokov@kaspersky.com Signed-off-by: Leon Romanovsky Signed-off-by: Sasha Levin --- drivers/infiniband/hw/hns/hns_roce_hem.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_hem.c b/drivers/infiniband/hw/hns/hns_roce_hem.c index e7c9e30ad2d8b..ccb40f8a48b72 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hem.c +++ b/drivers/infiniband/hw/hns/hns_roce_hem.c @@ -314,14 +314,14 @@ static int calc_hem_config(struct hns_roce_dev *hr_dev, bt_num = hns_roce_get_bt_num(table->type, mhop->hop_num); switch (bt_num) { case 3: - index->l1 = l0_idx * chunk_ba_num + l1_idx; + index->l1 = (u64)l0_idx * chunk_ba_num + l1_idx; index->l0 = l0_idx; - index->buf = l0_idx * chunk_ba_num * chunk_ba_num + - l1_idx * chunk_ba_num + l2_idx; + index->buf = (u64)l0_idx * chunk_ba_num * chunk_ba_num + + (u64)l1_idx * chunk_ba_num + l2_idx; break; case 2: index->l0 = l0_idx; - index->buf = l0_idx * chunk_ba_num + l1_idx; + index->buf = (u64)l0_idx * chunk_ba_num + l1_idx; break; case 1: index->buf = l0_idx; From 9619909d4869afe720904c6888a289b9ac3055b8 Mon Sep 17 00:00:00 2001 From: Prathamesh Deshpande Date: Sun, 26 Apr 2026 14:23:41 +0100 Subject: [PATCH 0905/2758] RDMA/mlx5: Fix UMR XLT cleanup on ODP populate failure [ Upstream commit 1eae35b37923cb71b0cb5136d00671440d488b9f ] mlx5r_umr_update_xlt() allocates and DMA maps an XLT buffer with mlx5r_umr_create_xlt(). The buffer is released by the common cleanup path through mlx5r_umr_unmap_free_xlt(). After mlx5_odp_populate_xlt() became fallible, its error path returned directly and skipped that cleanup. This leaks the XLT DMA mapping and buffer. If the emergency XLT page was used, it also leaves xlt_emergency_page_mutex locked. Break out of the loop so execution falls through the existing cleanup path. Fixes: 1efe8c0670d6 ("RDMA/core: Convert UMEM ODP DMA mapping to caching IOVA and page linkage") Signed-off-by: Prathamesh Deshpande Link: https://patch.msgid.link/20260426132356.22264-1-prathameshdeshpande7@gmail.com Signed-off-by: Leon Romanovsky Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/umr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/mlx5/umr.c b/drivers/infiniband/hw/mlx5/umr.c index f2139474be375..688d5246f284e 100644 --- a/drivers/infiniband/hw/mlx5/umr.c +++ b/drivers/infiniband/hw/mlx5/umr.c @@ -915,7 +915,7 @@ int mlx5r_umr_update_xlt(struct mlx5_ib_mr *mr, u64 idx, int npages, */ err = mlx5_odp_populate_xlt(xlt, idx, npages, mr, flags); if (err) - return err; + break; dma_sync_single_for_device(ddev, sg.addr, sg.length, DMA_TO_DEVICE); sg.length = ALIGN(size_to_map, MLX5_UMR_FLEX_ALIGNMENT); From 78b9589fda266c71f0f9d0c858d4fa7381a890a5 Mon Sep 17 00:00:00 2001 From: Prathamesh Deshpande Date: Tue, 28 Apr 2026 23:42:49 +0100 Subject: [PATCH 0906/2758] RDMA/mlx5: Fix devx subscribe-event unwind NULL dereference [ Upstream commit 43f8f7946814c8e5f464518246fdbc69b6e32326 ] MLX5_IB_METHOD_DEVX_SUBSCRIBE_EVENT() links event_sub into sub_list before initializing the fields used by the shared error path. If eventfd_ctx_fdget() then fails, the unwind path dereferences event_sub->ev_file in uverbs_uobject_put() and calls subscribe_event_xa_dealloc() with an unset xa_key_level1. subscribe_event_xa_alloc() creates the XA entry exactly once for a given key_level1, on the first occurrence of that key. The unwind path must therefore call subscribe_event_xa_dealloc() exactly once for it as well. Enforce that by adding devx_key_in_sub_list() and calling subscribe_event_xa_dealloc() only when the last matching pending entry is being cleaned up. Fixes: 759738537142 ("IB/mlx5: Enable subscription for device events over DEVX") Signed-off-by: Prathamesh Deshpande Link: https://patch.msgid.link/20260428224319.37682-1-prathameshdeshpande7@gmail.com Reviewed-by: Yishai Hadas Signed-off-by: Leon Romanovsky Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/devx.c | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/devx.c b/drivers/infiniband/hw/mlx5/devx.c index 645ebcc0832d7..c2ae5a1404711 100644 --- a/drivers/infiniband/hw/mlx5/devx.c +++ b/drivers/infiniband/hw/mlx5/devx.c @@ -1913,6 +1913,17 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_ASYNC_QUERY)( return err; } +static bool devx_key_in_sub_list(struct list_head *list, u32 key_level1) +{ + struct devx_event_subscription *s; + + list_for_each_entry(s, list, event_list) + if (s->xa_key_level1 == key_level1) + return true; + + return false; +} + static void subscribe_event_xa_dealloc(struct mlx5_devx_event_table *devx_event_table, u32 key_level1, @@ -2160,10 +2171,17 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_SUBSCRIBE_EVENT)( event_sub = kzalloc_obj(*event_sub); if (!event_sub) { + if (!devx_key_in_sub_list(&sub_list, key_level1)) + subscribe_event_xa_dealloc(devx_event_table, + key_level1, + obj, + obj_id); err = -ENOMEM; goto err; } + event_sub->ev_file = ev_file; + event_sub->xa_key_level1 = key_level1; list_add_tail(&event_sub->event_list, &sub_list); uverbs_uobject_get(&ev_file->uobj); if (use_eventfd) { @@ -2178,9 +2196,6 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_SUBSCRIBE_EVENT)( } event_sub->cookie = cookie; - event_sub->ev_file = ev_file; - /* May be needed upon cleanup the devx object/subscription */ - event_sub->xa_key_level1 = key_level1; event_sub->xa_key_level2 = obj_id; INIT_LIST_HEAD(&event_sub->obj_list); } @@ -2225,10 +2240,11 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_SUBSCRIBE_EVENT)( list_for_each_entry_safe(event_sub, tmp_sub, &sub_list, event_list) { list_del(&event_sub->event_list); - subscribe_event_xa_dealloc(devx_event_table, - event_sub->xa_key_level1, - obj, - obj_id); + if (!devx_key_in_sub_list(&sub_list, event_sub->xa_key_level1)) + subscribe_event_xa_dealloc(devx_event_table, + event_sub->xa_key_level1, + obj, + obj_id); if (event_sub->eventfd) eventfd_ctx_put(event_sub->eventfd); From 72497172a4799119a0282a5eb5e2b8ddcc821921 Mon Sep 17 00:00:00 2001 From: Sara Venkatesh Date: Mon, 4 May 2026 01:00:36 -0700 Subject: [PATCH 0907/2758] RDMA/srpt: fix integer overflow in immediate data length check [ Upstream commit eb4ecdf631fe00e8020bf461503cb9b7017ed796 ] imm_buf->len is a user-controlled uint32_t received from the network. Adding it to imm_data_offset without overflow checking allows a malicious initiator to send len=0xFFFFFFFF, causing req_size to wrap around to a small value, bypassing the bounds check, and subsequently passing a ~4GB length to sg_init_one(). Use check_add_overflow() to detect wrapping before the comparison. Fixes: 5dabcd0456d7 ("RDMA/srpt: Add support for immediate data") Reported-by: Carlos Bilbao (Lambda) Signed-off-by: Sara Venkatesh Link: https://patch.msgid.link/20260504080036.3482415-1-sarajvenkatesh@gmail.com Reviewed-by: Carlos Bilbao (Lambda) Reviewed-by: Bart Van Assche Signed-off-by: Leon Romanovsky Signed-off-by: Sasha Levin --- drivers/infiniband/ulp/srpt/ib_srpt.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index 9aec5d80117f1..f66cfd70c2636 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -1129,9 +1129,10 @@ static int srpt_get_desc_tbl(struct srpt_recv_ioctx *recv_ioctx, struct srp_imm_buf *imm_buf = srpt_get_desc_buf(srp_cmd); void *data = (void *)srp_cmd + imm_data_offset; uint32_t len = be32_to_cpu(imm_buf->len); - uint32_t req_size = imm_data_offset + len; + uint32_t req_size; - if (req_size > srp_max_req_size) { + if (check_add_overflow((uint32_t)imm_data_offset, len, &req_size) || + req_size > srp_max_req_size) { pr_err("Immediate data (length %d + %d) exceeds request size %d\n", imm_data_offset, len, srp_max_req_size); return -EINVAL; From 06e195936fca7c40893cdbd2ba0cac17e1966acb Mon Sep 17 00:00:00 2001 From: Junxian Huang Date: Thu, 7 May 2026 09:21:46 +0800 Subject: [PATCH 0908/2758] RDMA/hns: Initialize seqfile before creating file [ Upstream commit b4070770506ff516e21b4923afdaf7eb5da0e150 ] The debugfs file was created before seq->read and seq->data were set, leaving a small window where userspace could access an uninitialized seqfile. Move debugfs_create_file() after the assignments to avoid this issue. Also, inline the original init_debugfs_seqfile() since it is not a really necessary helper. Fixes: ca7ad04cd5d2 ("RDMA/hns: Add debugfs to hns RoCE") Signed-off-by: Junxian Huang Link: https://patch.msgid.link/20260507012148.1079712-2-huangjunxian6@hisilicon.com Signed-off-by: Leon Romanovsky Signed-off-by: Sasha Levin --- drivers/infiniband/hw/hns/hns_roce_debugfs.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_debugfs.c b/drivers/infiniband/hw/hns/hns_roce_debugfs.c index b869cdc541189..db32c5897640f 100644 --- a/drivers/infiniband/hw/hns/hns_roce_debugfs.c +++ b/drivers/infiniband/hw/hns/hns_roce_debugfs.c @@ -26,17 +26,6 @@ static const struct file_operations hns_debugfs_seqfile_fops = { .llseek = seq_lseek }; -static void init_debugfs_seqfile(struct hns_debugfs_seqfile *seq, - const char *name, struct dentry *parent, - int (*read_fn)(struct seq_file *, void *), - void *data) -{ - debugfs_create_file(name, 0400, parent, seq, &hns_debugfs_seqfile_fops); - - seq->read = read_fn; - seq->data = data; -} - static const char * const sw_stat_info[] = { [HNS_ROCE_DFX_AEQE_CNT] = "aeqe", [HNS_ROCE_DFX_CEQE_CNT] = "ceqe", @@ -76,10 +65,12 @@ static void create_sw_stat_debugfs(struct hns_roce_dev *hr_dev, { struct hns_sw_stat_debugfs *dbgfs = &hr_dev->dbgfs.sw_stat_root; - dbgfs->root = debugfs_create_dir("sw_stat", parent); + dbgfs->sw_stat.read = sw_stat_debugfs_show; + dbgfs->sw_stat.data = hr_dev; - init_debugfs_seqfile(&dbgfs->sw_stat, "sw_stat", dbgfs->root, - sw_stat_debugfs_show, hr_dev); + dbgfs->root = debugfs_create_dir("sw_stat", parent); + debugfs_create_file("sw_stat", 0400, dbgfs->root, &dbgfs->sw_stat, + &hns_debugfs_seqfile_fops); } /* debugfs for device */ From e3c2b81d6761bf2e49911838abe76daf354db7a1 Mon Sep 17 00:00:00 2001 From: Costa Shulyupin Date: Tue, 14 Apr 2026 21:52:18 +0300 Subject: [PATCH 0909/2758] tools/rtla: Fix --dump-tasks usage in timerlat [ Upstream commit 704fe8f3d97bba842f5f357e317116f1b88169e8 ] Fix --dump-task to --dump-tasks in timerlat_hist usage string and getopt_long table for consistency with timerlat_top. Add missing --dump-tasks to timerlat_top usage synopsis. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: Costa Shulyupin Fixes: 2091336b9a8b ("rtla/timerlat_hist: Add auto-analysis support") Link: https://lore.kernel.org/r/20260414185223.65353-1-costa.shul@redhat.com Signed-off-by: Tomas Glozar Signed-off-by: Sasha Levin --- tools/tracing/rtla/src/timerlat_hist.c | 4 ++-- tools/tracing/rtla/src/timerlat_top.c | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/tracing/rtla/src/timerlat_hist.c b/tools/tracing/rtla/src/timerlat_hist.c index 4b6708e333b8f..d52fd59195cd1 100644 --- a/tools/tracing/rtla/src/timerlat_hist.c +++ b/tools/tracing/rtla/src/timerlat_hist.c @@ -694,7 +694,7 @@ static void timerlat_hist_usage(void) "[-d s] [-D] [-n] [-a us] [-p us] [-i us] [-T us] [-s us] \\", " [-t [file]] [-e sys[:event]] [--filter ] [--trigger ] [-c cpu-list] [-H cpu-list]\\", " [-P priority] [-E N] [-b N] [--no-irq] [--no-thread] [--no-header] [--no-summary] \\", - " [--no-index] [--with-zeros] [--dma-latency us] [-C [cgroup_name]] [--no-aa] [--dump-task] [-u|-k]", + " [--no-index] [--with-zeros] [--dma-latency us] [-C [cgroup_name]] [--no-aa] [--dump-tasks] [-u|-k]", " [--warm-up s] [--deepest-idle-state n]", NULL, }; @@ -809,7 +809,7 @@ static struct common_params {"filter", required_argument, 0, '7'}, {"dma-latency", required_argument, 0, '8'}, {"no-aa", no_argument, 0, '9'}, - {"dump-task", no_argument, 0, '\1'}, + {"dump-tasks", no_argument, 0, '\1'}, {"warm-up", required_argument, 0, '\2'}, {"trace-buffer-size", required_argument, 0, '\3'}, {"deepest-idle-state", required_argument, 0, '\4'}, diff --git a/tools/tracing/rtla/src/timerlat_top.c b/tools/tracing/rtla/src/timerlat_top.c index 91f88bbebad9e..035abf01dbe63 100644 --- a/tools/tracing/rtla/src/timerlat_top.c +++ b/tools/tracing/rtla/src/timerlat_top.c @@ -467,7 +467,8 @@ static void timerlat_top_usage(void) static const char *const msg_start[] = { "[-q] [-a us] [-d s] [-D] [-n] [-p us] [-i us] [-T us] [-s us] \\", " [[-t [file]] [-e sys[:event]] [--filter ] [--trigger ] [-c cpu-list] [-H cpu-list]\\", - " [-P priority] [--dma-latency us] [--aa-only us] [-C [cgroup_name]] [-u|-k] [--warm-up s] [--deepest-idle-state n]", + " [-P priority] [--dma-latency us] [--aa-only us] [-C [cgroup_name]] [--dump-tasks] [-u|-k] [--warm-up s]\\", + " [--deepest-idle-state n]", NULL, }; From 9b75c38776a00189c243f762e0da6b6f83854f03 Mon Sep 17 00:00:00 2001 From: Crystal Wood Date: Tue, 12 May 2026 12:37:31 -0500 Subject: [PATCH 0910/2758] rtla: Stop the record trace on interrupt [ Upstream commit f03a59f949176ce4312cb466245d1243aaf40389 ] Before, when rtla got a signal, it stopped the main trace but not the record trace. With "--on-end trace", this can lead to save_trace_to_file() failing to keep up, especially on a debug kernel. Plus, it adds post-stoppage noise to the trace file. Signed-off-by: Crystal Wood Fixes: c73cab9dbed0 ("rtla/timerlat_hist: Stop timerlat tracer on signal") Fixes: a4dfce7559d7 ("rtla/timerlat_top: Stop timerlat tracer on signal") Fixes: 3aadb65db5d6 ("rtla/timerlat: Add action on end feature") Link: https://lore.kernel.org/r/20260512173731.2151841-1-crwood@redhat.com Signed-off-by: Tomas Glozar Signed-off-by: Sasha Levin --- tools/tracing/rtla/src/common.c | 19 +++++++++++-------- tools/tracing/rtla/src/common.h | 1 - tools/tracing/rtla/src/timerlat.c | 2 +- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/tools/tracing/rtla/src/common.c b/tools/tracing/rtla/src/common.c index bc9d01ddd1029..bfeccc6222e5b 100644 --- a/tools/tracing/rtla/src/common.c +++ b/tools/tracing/rtla/src/common.c @@ -10,7 +10,7 @@ #include "common.h" -struct trace_instance *trace_inst; +struct osnoise_tool *trace_tool; volatile int stop_tracing; int nr_cpus; @@ -21,12 +21,16 @@ static void stop_trace(int sig) * Stop requested twice in a row; abort event processing and * exit immediately */ - tracefs_iterate_stop(trace_inst->inst); + if (trace_tool) + tracefs_iterate_stop(trace_tool->trace.inst); return; } stop_tracing = 1; - if (trace_inst) - trace_instance_stop(trace_inst); + if (trace_tool) { + trace_instance_stop(&trace_tool->trace); + if (trace_tool->record) + trace_instance_stop(&trace_tool->record->trace); + } } /* @@ -255,11 +259,10 @@ int run_tool(struct tool_ops *ops, int argc, char *argv[]) tool->params = params; /* - * Save trace instance into global variable so that SIGINT can stop - * the timerlat tracer. + * Expose the tool to signal handlers so they can stop the trace. * Otherwise, rtla could loop indefinitely when overloaded. */ - trace_inst = &tool->trace; + trace_tool = tool; retval = ops->apply_config(tool); if (retval) { @@ -267,7 +270,7 @@ int run_tool(struct tool_ops *ops, int argc, char *argv[]) goto out_free; } - retval = enable_tracer_by_name(trace_inst->inst, ops->tracer); + retval = enable_tracer_by_name(tool->trace.inst, ops->tracer); if (retval) { err_msg("Failed to enable %s tracer\n", ops->tracer); goto out_free; diff --git a/tools/tracing/rtla/src/common.h b/tools/tracing/rtla/src/common.h index 8921807bda988..505babf386373 100644 --- a/tools/tracing/rtla/src/common.h +++ b/tools/tracing/rtla/src/common.h @@ -54,7 +54,6 @@ struct osnoise_context { int opt_workload; }; -extern struct trace_instance *trace_inst; extern volatile int stop_tracing; struct hist_params { diff --git a/tools/tracing/rtla/src/timerlat.c b/tools/tracing/rtla/src/timerlat.c index f8c057518d223..637f68d684f5f 100644 --- a/tools/tracing/rtla/src/timerlat.c +++ b/tools/tracing/rtla/src/timerlat.c @@ -202,7 +202,7 @@ void timerlat_analyze(struct osnoise_tool *tool, bool stopped) * If the trace did not stop with --aa-only, at least print * the max known latency. */ - max_lat = tracefs_instance_file_read(trace_inst->inst, "tracing_max_latency", NULL); + max_lat = tracefs_instance_file_read(tool->trace.inst, "tracing_max_latency", NULL); if (max_lat) { printf(" Max latency was %s\n", max_lat); free(max_lat); From 5c5994a1204743fa55e4c198ed93a6c36f21cd1f Mon Sep 17 00:00:00 2001 From: Liviu Dudau Date: Thu, 7 May 2026 11:50:46 +0100 Subject: [PATCH 0911/2758] drm/syncobj: Fix memory leak in drm_syncobj_find_fence() [ Upstream commit e5b93bd6fdb92aa5e4689715d7e8487d9ce66a38 ] Commit 18226ba52159 ("drm/syncobj: reject invalid flags in drm_syncobj_find_fence") forgot to take into account the fact that drm_syncobj_find() takes a reference to syncobj and returns early without dropping the reference, leading to memory leaks. Fixes: 18226ba52159 ("drm/syncobj: reject invalid flags in drm_syncobj_find_fence") Reported by: Sam Spencer Signed-off-by: Liviu Dudau Acked-by: Erik Kurzinger Signed-off-by: Liviu Dudau Link: https://lore.kernel.org/all/20260507144425.2488057-1-liviu.dudau@arm.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/drm_syncobj.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/drm_syncobj.c b/drivers/gpu/drm/drm_syncobj.c index 8d9fd1917c6e6..c9dbf64c0c9f2 100644 --- a/drivers/gpu/drm/drm_syncobj.c +++ b/drivers/gpu/drm/drm_syncobj.c @@ -442,13 +442,15 @@ int drm_syncobj_find_fence(struct drm_file *file_private, u64 timeout = nsecs_to_jiffies64(DRM_SYNCOBJ_WAIT_FOR_SUBMIT_TIMEOUT); int ret; - if (flags & ~DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) - return -EINVAL; - if (!syncobj) return -ENOENT; - /* Waiting for userspace with locks help is illegal cause that can + if (flags & ~DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) { + ret = -EINVAL; + goto out; + } + + /* Waiting for userspace with locks held is illegal cause that can * trivial deadlock with page faults for example. Make lockdep complain * about it early on. */ From 6c4ede3b771adbb3bf21ad4e8b8f2f6f6120c4f7 Mon Sep 17 00:00:00 2001 From: Fengnan Chang Date: Wed, 13 May 2026 17:13:49 +0800 Subject: [PATCH 0912/2758] dm: limit target bio polling to one shot [ Upstream commit 5aa0f9231cbacade065cedd8e9b5ebd067231171 ] dm_poll_bio() is the ->poll_bio() callback for a stacked dm device. The caller only knows about the dm queue, so it may decide to do a spinning poll if it thinks a single queue is being polled. Passing those flags unchanged to the mapped clone lets blk_mq_poll() spin on a target queue from inside dm_poll_bio(). With io_uring IOPOLL on a dm-stripe target this can keep a task in dm_poll_bio() -> bio_poll() -> blk_mq_poll() long enough to trigger an RCU CPU stall, before io_uring gets back to io_iopoll_check() and its need_resched() check. Keep dm's ->poll_bio() bounded by forcing one-shot polling for target bios. The caller can invoke dm_poll_bio() again if it wants to keep polling, and it also gets a chance to reap completions or reschedule between passes. Fixes: f22ecf9c14c1 ("blk-mq: delete task running check in blk_hctx_poll()") Signed-off-by: Fengnan Chang Signed-off-by: Mikulas Patocka Signed-off-by: Sasha Levin --- drivers/md/dm.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/md/dm.c b/drivers/md/dm.c index e178fe19973ea..8f44fbbcf3da2 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -2098,8 +2098,17 @@ static bool dm_poll_dm_io(struct dm_io *io, struct io_comp_batch *iob, WARN_ON_ONCE(!dm_tio_is_normal(&io->tio)); /* don't poll if the mapped io is done */ - if (atomic_read(&io->io_count) > 1) - bio_poll(&io->tio.clone, iob, flags); + if (atomic_read(&io->io_count) > 1) { + /* + * DM hides the target queues from the upper poller, which may + * decide it is safe to spin on a single stacked queue. Do not + * pass that spinning policy down to a target queue: one slow + * clone could keep the task inside dm_poll_bio() for a long + * time. Poll target bios once and let the caller decide + * whether to keep polling, reap completions or reschedule. + */ + bio_poll(&io->tio.clone, iob, flags | BLK_POLL_ONESHOT); + } /* bio_poll holds the last reference */ return atomic_read(&io->io_count) == 1; From 659a61123cbe264fbf657e9240448d2cfa2df8c5 Mon Sep 17 00:00:00 2001 From: Paul Chaignon Date: Wed, 13 May 2026 13:24:37 +0200 Subject: [PATCH 0913/2758] selftests/bpf: Override EXTRA_LDFLAGS for static builds [ Upstream commit 879daba303f7d7c3057f4d218921621e751f1912 ] When running vmtest.sh with static linking, the bpftool_map_access selftests fail. These selftests are calling the bpftool binary in tools/sbin/ directly, which results in the following error: error while loading shared libraries: libLLVM.so.21.1: cannot open shared object file: No such file or directory To fix this, we need to also build bpftool statically. That can be done by setting EXTRA_LDFLAGS=-static. Fixes: 2d96bbdfd3b5 ("selftests/bpf: convert test_bpftool_map_access.sh into test_progs framework") Signed-off-by: Paul Chaignon Reviewed-by: Jakub Sitnicki Link: https://lore.kernel.org/r/714556da329c812988010ffe53173d9152570a78.1778669303.git.paul.chaignon@gmail.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- tools/testing/selftests/bpf/README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/README.rst b/tools/testing/selftests/bpf/README.rst index 776fbe3cb8f9b..37164322a1023 100644 --- a/tools/testing/selftests/bpf/README.rst +++ b/tools/testing/selftests/bpf/README.rst @@ -77,7 +77,7 @@ In case of linker errors when running selftests, try using static linking: .. code-block:: console - $ LDLIBS=-static PKG_CONFIG='pkg-config --static' vmtest.sh + $ LDLIBS=-static EXTRA_LDFLAGS=-static PKG_CONFIG='pkg-config --static' vmtest.sh .. note:: Some distros may not support static linking. From 4aa01da6cf93d1a6a034e74a6eda0893d4496984 Mon Sep 17 00:00:00 2001 From: Roman Kvasnytskyi Date: Sat, 16 May 2026 14:06:25 +0200 Subject: [PATCH 0914/2758] selftests/bpf: Reject unsupported -k option in vmtest.sh [ Upstream commit 6df582112aa9ac9d190169abdb0e42e496659ec9 ] vmtest.sh does not document a -k option and does not handle it in the getopts case statement. However, the getopts optstring includes k, which causes the script to accept -k silently instead of reporting it as an invalid option. Remove k from the optstring so unsupported options are rejected through the existing invalid-option path. Fixes: c9709f52386d ("bpf: Helper script for running BPF presubmit tests") Signed-off-by: Roman Kvasnytskyi Acked-by: Paul Chaignon Link: https://lore.kernel.org/r/20260516120625.80839-1-roman@kvasnytskyi.net Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- tools/testing/selftests/bpf/vmtest.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/vmtest.sh b/tools/testing/selftests/bpf/vmtest.sh index 2f869daf8a06b..9ca8022853933 100755 --- a/tools/testing/selftests/bpf/vmtest.sh +++ b/tools/testing/selftests/bpf/vmtest.sh @@ -382,7 +382,7 @@ main() local exit_command="poweroff -f" local debug_shell="no" - while getopts ':hskl:id:j:' opt; do + while getopts ':hsl:id:j:' opt; do case ${opt} in l) LOCAL_ROOTFS_IMAGE="$OPTARG" From 3204e6b35e8c389493648d3ceaa0c68fd399bfee Mon Sep 17 00:00:00 2001 From: Paul Chaignon Date: Mon, 18 May 2026 18:26:35 +0200 Subject: [PATCH 0915/2758] selftests/bpf: Fix test for refinement of single-value tnum [ Upstream commit 523d2f42b406f5be2989f436b03eacebf3679835 ] This patch fixes the "bounds refinement with single-value tnum on umin" verifier selftest. This selftest was introduced in commit e6ad477d1bf8 ("selftests/bpf: Test refinement of single-value tnum") to cover the logic from __update_reg64_bounds(), introduced in commit efc11a667878 ("bpf: Improve bounds when tnum has a single possible value"). However, the test still passes if that last commit is reverted. The test is supposed to cover the case when the tnum and u64 range (or cnum64 now) overlap in a single value. __update_reg64_bounds() detects that case and refines the bounds to a known constant. However, the constants for the test were poorly chosen and the bounds get refined to a known constant even without __update_reg64_bounds(). The code is as follows: 0: call bpf_get_prandom_u32#7 ; R0=scalar() 1: r0 |= 224 ; R0=scalar(umin=umin32=224,var_off=(0xe0; 0xffffffffffffff1f)) 2: r0 &= 240 ; R0=scalar(smin=umin=smin32=umin32=224,smax=umax=smax32=umax32=240,var_off=(0xe0; 0x10)) 3: if r0 == 0xf0 goto pc+2 ; R0=224 After instruction 3, we have u64=[0xe0; 0xef] and tnum=(0xe0; 0x10). __reg_bound_offset() is able to deduce a new tnum from the u64, tnum=(0xe0; 0x0f), which combined with the existing tnum gives us a constant: 0xe0 or 224. We can easily fix this by choosing different starting bounds. If we make it u64=[0xe1; 0xf0], then __reg_bound_offset() doesn't have any impact. Fixes: e6ad477d1bf8 ("selftests/bpf: Test refinement of single-value tnum") Signed-off-by: Paul Chaignon Link: https://lore.kernel.org/r/be2dc2c3d85120286e60b3029b3338fff339f942.1779121582.git.paul.chaignon@gmail.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- .../testing/selftests/bpf/progs/verifier_bounds.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/verifier_bounds.c b/tools/testing/selftests/bpf/progs/verifier_bounds.c index c1ae013dee29c..bc431f46aafdb 100644 --- a/tools/testing/selftests/bpf/progs/verifier_bounds.c +++ b/tools/testing/selftests/bpf/progs/verifier_bounds.c @@ -1890,25 +1890,25 @@ __naked void bounds_refinement_tnum_umax(void *ctx) /* This test covers the bounds deduction when the u64 range and the tnum * overlap only at umin. After instruction 3, the ranges look as follows: * - * 0 umin=0xe00 umax=0xeff U64_MAX + * 0 umin=0xe1 umax=0xf0 U64_MAX * | [xxxxxxxxxxxxxx] | * |----------------------------|------------------------------| * | x x | tnum values * - * The verifier can therefore deduce that the R0=0xe0=224. + * The verifier can therefore deduce that the R0=0xe1=225. */ SEC("socket") __description("bounds refinement with single-value tnum on umin") -__msg("3: (15) if r0 == 0xf0 {{.*}} R0=224") +__msg("3: (15) if r0 == 0xf1 {{.*}} R0=225") __success __log_level(2) __naked void bounds_refinement_tnum_umin(void *ctx) { asm volatile(" \ call %[bpf_get_prandom_u32]; \ - r0 |= 0xe0; \ - r0 &= 0xf0; \ - if r0 == 0xf0 goto +2; \ - if r0 == 0xe0 goto +1; \ + r0 |= 0xe1; \ + r0 &= 0xf1; \ + if r0 == 0xf1 goto +2; \ + if r0 == 0xe1 goto +1; \ r10 = 0; \ exit; \ " : From 60e37bc8809915234239c96d9836ec602c817af8 Mon Sep 17 00:00:00 2001 From: Bibek Kumar Patro Date: Thu, 23 Apr 2026 15:32:30 +0530 Subject: [PATCH 0916/2758] iommu/arm-smmu-qcom: Fix fastrpc compatible string in ACTLR client match table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit a6e1618a65d453d4e739e5898c662ccb1e3de6c0 ] The qcom_smmu_actlr_client_of_match table contained "qcom,fastrpc" as the compatible string for applying ACTLR prefetch settings to FastRPC devices. However, "qcom,fastrpc" is the compatible string for the parent rpmsg channel node, which is not an IOMMU client — it carries no "iommus" property in the device tree and is never attached to an SMMU context bank. The actual IOMMU clients are the compute context bank (CB) child nodes, which use the compatible string "qcom,fastrpc-compute-cb". These nodes carry the "iommus" property and are probed by fastrpc_cb_driver via fastrpc_cb_probe(), which sets up the DMA mask and IOMMU mappings for each FastRPC session. The device tree structure is: fastrpc { compatible = "qcom,fastrpc"; /* rpmsg channel, no iommus */ ... compute-cb@3 { compatible = "qcom,fastrpc-compute-cb"; iommus = <&apps_smmu 0x1823 0x0>; /* actual IOMMU client */ }; }; Since qcom_smmu_set_actlr_dev() calls of_match_device() against the device being attached to the SMMU context bank, the "qcom,fastrpc" entry was never matching any device. As a result, the ACTLR prefetch settings (PREFETCH_DEEP | CPRE | CMTLB) were silently never applied for FastRPC compute context banks. Fix this by replacing "qcom,fastrpc" with "qcom,fastrpc-compute-cb" in the match table so that the ACTLR settings are correctly applied to the compute CB devices that are the true IOMMU clients. Assisted-by: Claude:claude-sonnet-4-6 Fixes: 3e35c3e725de ("iommu/arm-smmu: Add ACTLR data and support for qcom_smmu_500") Reviewed-by: Dmitry Baryshkov Reviewed-by: Shawn Guo Signed-off-by: Bibek Kumar Patro Signed-off-by: Will Deacon Signed-off-by: Sasha Levin --- drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c index edd41b5a3b6ac..2d006049dd610 100644 --- a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c +++ b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c @@ -39,7 +39,7 @@ static const struct of_device_id qcom_smmu_actlr_client_of_match[] = { .data = (const void *) (PREFETCH_DEEP | CPRE | CMTLB) }, { .compatible = "qcom,adreno-smmu", .data = (const void *) (PREFETCH_DEEP | CPRE | CMTLB) }, - { .compatible = "qcom,fastrpc", + { .compatible = "qcom,fastrpc-compute-cb", .data = (const void *) (PREFETCH_DEEP | CPRE | CMTLB) }, { .compatible = "qcom,qcm2290-mdss", .data = (const void *) (PREFETCH_SHALLOW | CPRE | CMTLB) }, From cc12d55ea9d8d5a5c479010fe7518554e4842f4c Mon Sep 17 00:00:00 2001 From: Vincent Guittot Date: Mon, 18 May 2026 12:23:45 +0200 Subject: [PATCH 0917/2758] sched/fair: Update util_est after updating util_avg during dequeue [ Upstream commit 6d2051403d6c93832d3058a1b275c6aef2c97f44 ] util_est_update() must be called after updating util_avg during the dequeue of a task and only when the task is not delayed dequeue. Move util_est_update() in update_load_avg(). Fixes: b55945c500c5 ("sched: Fix pick_next_task_fair() vs try_to_wake_up() race") Closes: https://lore.kernel.org/all/20260512124653.305275-1-qyousef@layalina.io/ Reported-by: Qais Yousef Reviewed-and-tested-by: Qais Yousef Signed-off-by: Vincent Guittot Signed-off-by: Peter Zijlstra (Intel) Link: https://patch.msgid.link/20260518102345.268452-1-vincent.guittot@linaro.org Signed-off-by: Sasha Levin --- kernel/sched/fair.c | 188 ++++++++++++++++++++++---------------------- 1 file changed, 92 insertions(+), 96 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 3ebec186f9823..bcc984e462272 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -4959,13 +4959,86 @@ static void detach_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *s trace_pelt_cfs_tp(cfs_rq); } +#define UTIL_EST_MARGIN (SCHED_CAPACITY_SCALE / 100) + +static inline void util_est_update(struct sched_entity *se) +{ + unsigned int ewma, dequeued, last_ewma_diff; + + if (!sched_feat(UTIL_EST)) + return; + + /* Get current estimate of utilization */ + ewma = READ_ONCE(se->avg.util_est); + + /* + * If the PELT values haven't changed since enqueue time, + * skip the util_est update. + */ + if (ewma & UTIL_AVG_UNCHANGED) + return; + + /* Get utilization at dequeue */ + dequeued = READ_ONCE(se->avg.util_avg); + + /* + * Reset EWMA on utilization increases, the moving average is used only + * to smooth utilization decreases. + */ + if (ewma <= dequeued) { + ewma = dequeued; + goto done; + } + + /* + * Skip update of task's estimated utilization when its members are + * already ~1% close to its last activation value. + */ + last_ewma_diff = ewma - dequeued; + if (last_ewma_diff < UTIL_EST_MARGIN) + goto done; + + /* + * To avoid underestimate of task utilization, skip updates of EWMA if + * we cannot grant that thread got all CPU time it wanted. + */ + if ((dequeued + UTIL_EST_MARGIN) < READ_ONCE(se->avg.runnable_avg)) + goto done; + + /* + * Update Task's estimated utilization + * + * When *p completes an activation we can consolidate another sample + * of the task size. This is done by using this value to update the + * Exponential Weighted Moving Average (EWMA): + * + * ewma(t) = w * task_util(p) + (1-w) * ewma(t-1) + * = w * task_util(p) + ewma(t-1) - w * ewma(t-1) + * = w * (task_util(p) - ewma(t-1)) + ewma(t-1) + * = w * ( -last_ewma_diff ) + ewma(t-1) + * = w * (-last_ewma_diff + ewma(t-1) / w) + * + * Where 'w' is the weight of new samples, which is configured to be + * 0.25, thus making w=1/4 ( >>= UTIL_EST_WEIGHT_SHIFT) + */ + ewma <<= UTIL_EST_WEIGHT_SHIFT; + ewma -= last_ewma_diff; + ewma >>= UTIL_EST_WEIGHT_SHIFT; +done: + ewma |= UTIL_AVG_UNCHANGED; + WRITE_ONCE(se->avg.util_est, ewma); + + trace_sched_util_est_se_tp(se); +} + /* * Optional action to be done while updating the load average */ -#define UPDATE_TG 0x1 -#define SKIP_AGE_LOAD 0x2 -#define DO_ATTACH 0x4 -#define DO_DETACH 0x8 +#define UPDATE_TG 0x01 +#define SKIP_AGE_LOAD 0x02 +#define DO_ATTACH 0x04 +#define DO_DETACH 0x08 +#define UPDATE_UTIL_EST 0x10 /* Update task and its cfs_rq load average */ static inline void update_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) @@ -5008,6 +5081,9 @@ static inline void update_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *s if (flags & UPDATE_TG) update_tg_load_avg(cfs_rq); } + + if (flags & UPDATE_UTIL_EST) + util_est_update(se); } /* @@ -5066,11 +5142,6 @@ static inline unsigned long task_util(struct task_struct *p) return READ_ONCE(p->se.avg.util_avg); } -static inline unsigned long task_runnable(struct task_struct *p) -{ - return READ_ONCE(p->se.avg.runnable_avg); -} - static inline unsigned long _task_util_est(struct task_struct *p) { return READ_ONCE(p->se.avg.util_est) & ~UTIL_AVG_UNCHANGED; @@ -5113,88 +5184,6 @@ static inline void util_est_dequeue(struct cfs_rq *cfs_rq, trace_sched_util_est_cfs_tp(cfs_rq); } -#define UTIL_EST_MARGIN (SCHED_CAPACITY_SCALE / 100) - -static inline void util_est_update(struct cfs_rq *cfs_rq, - struct task_struct *p, - bool task_sleep) -{ - unsigned int ewma, dequeued, last_ewma_diff; - - if (!sched_feat(UTIL_EST)) - return; - - /* - * Skip update of task's estimated utilization when the task has not - * yet completed an activation, e.g. being migrated. - */ - if (!task_sleep) - return; - - /* Get current estimate of utilization */ - ewma = READ_ONCE(p->se.avg.util_est); - - /* - * If the PELT values haven't changed since enqueue time, - * skip the util_est update. - */ - if (ewma & UTIL_AVG_UNCHANGED) - return; - - /* Get utilization at dequeue */ - dequeued = task_util(p); - - /* - * Reset EWMA on utilization increases, the moving average is used only - * to smooth utilization decreases. - */ - if (ewma <= dequeued) { - ewma = dequeued; - goto done; - } - - /* - * Skip update of task's estimated utilization when its members are - * already ~1% close to its last activation value. - */ - last_ewma_diff = ewma - dequeued; - if (last_ewma_diff < UTIL_EST_MARGIN) - goto done; - - /* - * To avoid underestimate of task utilization, skip updates of EWMA if - * we cannot grant that thread got all CPU time it wanted. - */ - if ((dequeued + UTIL_EST_MARGIN) < task_runnable(p)) - goto done; - - - /* - * Update Task's estimated utilization - * - * When *p completes an activation we can consolidate another sample - * of the task size. This is done by using this value to update the - * Exponential Weighted Moving Average (EWMA): - * - * ewma(t) = w * task_util(p) + (1-w) * ewma(t-1) - * = w * task_util(p) + ewma(t-1) - w * ewma(t-1) - * = w * (task_util(p) - ewma(t-1)) + ewma(t-1) - * = w * ( -last_ewma_diff ) + ewma(t-1) - * = w * (-last_ewma_diff + ewma(t-1) / w) - * - * Where 'w' is the weight of new samples, which is configured to be - * 0.25, thus making w=1/4 ( >>= UTIL_EST_WEIGHT_SHIFT) - */ - ewma <<= UTIL_EST_WEIGHT_SHIFT; - ewma -= last_ewma_diff; - ewma >>= UTIL_EST_WEIGHT_SHIFT; -done: - ewma |= UTIL_AVG_UNCHANGED; - WRITE_ONCE(p->se.avg.util_est, ewma); - - trace_sched_util_est_se_tp(&p->se); -} - static inline unsigned long get_actual_cpu_capacity(int cpu) { unsigned long capacity = arch_scale_cpu_capacity(cpu); @@ -5647,7 +5636,7 @@ static bool dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) { bool sleep = flags & DEQUEUE_SLEEP; - int action = UPDATE_TG; + int action = 0; update_curr(cfs_rq); clear_buddies(cfs_rq, se); @@ -5667,15 +5656,23 @@ dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) if (sched_feat(DELAY_DEQUEUE) && delay && !entity_eligible(cfs_rq, se)) { - update_load_avg(cfs_rq, se, 0); + if (entity_is_task(se)) + action |= UPDATE_UTIL_EST; + update_load_avg(cfs_rq, se, action); update_entity_lag(cfs_rq, se); set_delayed(se); return false; } } - if (entity_is_task(se) && task_on_rq_migrating(task_of(se))) - action |= DO_DETACH; + action = UPDATE_TG; + if (entity_is_task(se)) { + if (task_on_rq_migrating(task_of(se))) + action |= DO_DETACH; + + if (sleep && !(flags & DEQUEUE_DELAYED)) + action |= UPDATE_UTIL_EST; + } /* * When dequeuing a sched_entity, we must: @@ -7438,7 +7435,6 @@ static bool dequeue_task_fair(struct rq *rq, struct task_struct *p, int flags) if (!p->se.sched_delayed) util_est_dequeue(&rq->cfs, p); - util_est_update(&rq->cfs, p, flags & DEQUEUE_SLEEP); if (dequeue_entities(rq, &p->se, flags) < 0) return false; From 4235d57b36c3d6adc9b4c3e8ad2404d6711853d0 Mon Sep 17 00:00:00 2001 From: Kevin Brodsky Date: Mon, 27 Apr 2026 13:03:34 +0100 Subject: [PATCH 0918/2758] selftests/mm: Fix resv_sz when parsing arm64 signal frame [ Upstream commit c364aa56d6738c8759e88941d7a45a1d6b4c52d0 ] get_header() wants the size of the reserved area in struct sigcontext, but instead we pass it the size of the entire struct. This could in theory result in an out-of-bounds read (if the signal frame is malformed). Fix this using one of the existing macros from tools/testing/selftests/arm64/signal/testcases/testcases.h. This issue was reported by Sashiko on a patch that copied this portion of the code. Link: https://sashiko.dev/#/patchset/20260421144252.1440365-1-kevin.brodsky%40arm.com Fixes: f5b5ea51f78f ("selftests: mm: make protection_keys test work on arm64") Signed-off-by: Kevin Brodsky Reviewed-by: Mark Brown Signed-off-by: Will Deacon Signed-off-by: Sasha Levin --- tools/testing/selftests/mm/pkey-arm64.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/mm/pkey-arm64.h b/tools/testing/selftests/mm/pkey-arm64.h index 8e9685e03c441..c5a78a2f211d5 100644 --- a/tools/testing/selftests/mm/pkey-arm64.h +++ b/tools/testing/selftests/mm/pkey-arm64.h @@ -130,9 +130,10 @@ static inline u64 get_pkey_bits(u64 reg, int pkey) static inline void aarch64_write_signal_pkey(ucontext_t *uctxt, u64 pkey) { struct _aarch64_ctx *ctx = GET_UC_RESV_HEAD(uctxt); + size_t resv_size = GET_UCP_RESV_SIZE(uctxt); struct poe_context *poe_ctx = (struct poe_context *) get_header(ctx, POE_MAGIC, - sizeof(uctxt->uc_mcontext), NULL); + resv_size, NULL); if (poe_ctx) poe_ctx->por_el0 = pkey; } From 0a81d683e78d1b0d4856212338df0b5df3719650 Mon Sep 17 00:00:00 2001 From: Jamie Nguyen Date: Mon, 18 May 2026 13:31:16 -0700 Subject: [PATCH 0919/2758] firmware: arm_ffa: Honor partition info descriptor size [ Upstream commit 01b9cae706161a39452a2cce0f281d4369344c51 ] FFA_PARTITION_INFO_GET_REGS reports the size of each partition information descriptor in x2[63:48]. However, __ffa_partition_info_get_regs() walks the returned register payload with a hardcoded 24-byte stride (regs += 3), even though the size is already read into buf_sz. That works for the FF-A v1.1/v1.2 24-byte descriptor layout, where each descriptor consumes three registers. Newer FF-A revisions can extend the descriptor while keeping the existing fields at the front. For example, a 48-byte descriptor consumes six registers, so advancing by only three registers desynchronises the parser and can make it read subsequent entries from the middle of a descriptor. Use the advertised descriptor size to derive the register stride. Validate that the size is register-aligned, large enough for the fields parsed by the driver, and that the requested number of descriptors fits in the returned x3..x17 register window. The driver still copies only the fields it understands, but now skips over any trailing descriptor fields correctly. Fixes: ba85c644ac8d ("firmware: arm_ffa: Add support for FFA_PARTITION_INFO_GET_REGS") Suggested-by: Sudeep Holla Signed-off-by: Jamie Nguyen Link: https://patch.msgid.link/20260518203116.42624-1-jamien@nvidia.com (sudeep.holla: Minor rewordng of the commit message and subject) Signed-off-by: Sudeep Holla Signed-off-by: Sasha Levin --- drivers/firmware/arm_ffa/driver.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index b9f17fda72432..cab32cfdac423 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -324,11 +324,9 @@ __ffa_partition_info_get(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, #define PART_INFO_EXEC_CXT_MASK GENMASK(31, 16) #define PART_INFO_PROPS_MASK GENMASK(63, 32) #define FFA_PART_INFO_GET_REGS_FIRST_REG 3 -#define FFA_PART_INFO_GET_REGS_REGS_PER_DESC 3 -#define FFA_PART_INFO_GET_REGS_MAX_DESC \ - (((sizeof(ffa_value_t) / sizeof_field(ffa_value_t, a0)) - \ - FFA_PART_INFO_GET_REGS_FIRST_REG) / \ - FFA_PART_INFO_GET_REGS_REGS_PER_DESC) +#define FFA_PART_INFO_GET_REGS_MIN_REGS_PER_DESC 3 +#define FFA_PART_INFO_GET_REGS_NUM_REGS \ + (sizeof(ffa_value_t) / sizeof_field(ffa_value_t, a0)) #define PART_INFO_ID(x) ((u16)(FIELD_GET(PART_INFO_ID_MASK, (x)))) #define PART_INFO_EXEC_CXT(x) ((u16)(FIELD_GET(PART_INFO_EXEC_CXT_MASK, (x)))) #define PART_INFO_PROPERTIES(x) ((u32)(FIELD_GET(PART_INFO_PROPS_MASK, (x)))) @@ -342,7 +340,7 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, do { __le64 *regs; - int idx, nr_desc, buf_idx; + int idx, nr_desc, buf_idx, regs_per_desc, max_desc; invoke_ffa_fn((ffa_value_t){ .a0 = FFA_PARTITION_INFO_GET_REGS, @@ -365,8 +363,18 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, if (cur_idx < start_idx || cur_idx >= count) return -EINVAL; + buf_sz = PARTITION_INFO_SZ(partition_info.a2); + if (buf_sz % sizeof(*regs)) + return -EINVAL; + + regs_per_desc = buf_sz / sizeof(*regs); + if (regs_per_desc < FFA_PART_INFO_GET_REGS_MIN_REGS_PER_DESC) + return -EINVAL; + nr_desc = cur_idx - start_idx + 1; - if (nr_desc > FFA_PART_INFO_GET_REGS_MAX_DESC) + max_desc = (FFA_PART_INFO_GET_REGS_NUM_REGS - + FFA_PART_INFO_GET_REGS_FIRST_REG) / regs_per_desc; + if (nr_desc > max_desc) return -EINVAL; buf_idx = buf - buffer; @@ -374,9 +382,6 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, return -EINVAL; tag = UUID_INFO_TAG(partition_info.a2); - buf_sz = PARTITION_INFO_SZ(partition_info.a2); - if (buf_sz > sizeof(*buffer)) - buf_sz = sizeof(*buffer); regs = (void *)&partition_info.a3; for (idx = 0; idx < nr_desc; idx++, buf++) { @@ -395,7 +400,7 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, buf->exec_ctxt = PART_INFO_EXEC_CXT(val); buf->properties = PART_INFO_PROPERTIES(val); uuid_copy(&buf->uuid, &uuid_regs.uuid); - regs += 3; + regs += regs_per_desc; } start_idx = cur_idx + 1; From e618f5d1028989a568b66d06b33de986638cdbc1 Mon Sep 17 00:00:00 2001 From: Francesco Dolcini Date: Thu, 9 Apr 2026 11:33:04 +0200 Subject: [PATCH 0920/2758] arm64: dts: freescale: imx95-verdin-ivy: fix RS485 RTS polarity [ Upstream commit 9f004f2afded2870838c0ae4906c2fa9307286bb ] Fix the RS485 functionality, the RS485 RTS signal is active high on Ivy. Fixes: f33a1f9a942c ("arm64: dts: freescale: imx95-verdin: Add Ivy carrier board") Signed-off-by: Francesco Dolcini Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/freescale/imx95-verdin-ivy.dtsi | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm64/boot/dts/freescale/imx95-verdin-ivy.dtsi b/arch/arm64/boot/dts/freescale/imx95-verdin-ivy.dtsi index 8337c8b25f050..ff31f7c48cfb4 100644 --- a/arch/arm64/boot/dts/freescale/imx95-verdin-ivy.dtsi +++ b/arch/arm64/boot/dts/freescale/imx95-verdin-ivy.dtsi @@ -452,7 +452,6 @@ /* Verdin UART_2, through RS485 transceiver */ &lpuart8 { - rs485-rts-active-low; rs485-rx-during-tx; linux,rs485-enabled-at-boot-time; From 6623515c4eee4bc5e4769aefab7205e1e2b7fc7a Mon Sep 17 00:00:00 2001 From: Sherry Sun Date: Sat, 9 May 2026 09:54:09 +0800 Subject: [PATCH 0921/2758] arm64: dts: imx8dxl-evk: Remove unnecessary PCIe EP properties [ Upstream commit 538525f34158deb21b782164bde23ec07a353e4a ] For PCIe endpoint mode, only M.2 power supply needs to be ensured. On imx8dxl-evk, the M.2 power is always on and cannot be controlled, while reg_pcieb only controls the M.2 W_DISABLE1# signal. Remove the unnecessary vpcie-supply property from pcie0_ep node. Also remove reset-gpio as PCIe endpoint mode doesn't require reset control. Fixes: c1c4820b60d7 ("arm64: dts: imx8dxl-evk: Add pcie0-ep node and use unified pcie0 label") Signed-off-by: Sherry Sun Reviewed-by: Richard Zhu Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/freescale/imx8dxl-evk.dts | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx8dxl-evk.dts b/arch/arm64/boot/dts/freescale/imx8dxl-evk.dts index bc62ae5ca812d..441e009030299 100644 --- a/arch/arm64/boot/dts/freescale/imx8dxl-evk.dts +++ b/arch/arm64/boot/dts/freescale/imx8dxl-evk.dts @@ -686,8 +686,6 @@ phy-names = "pcie-phy"; pinctrl-0 = <&pinctrl_pcieb>; pinctrl-names = "default"; - reset-gpio = <&lsio_gpio4 0 GPIO_ACTIVE_LOW>; - vpcie-supply = <®_pcieb>; status = "disabled"; }; From 1b8584e7df354bf3003c38027684cb35130d9b26 Mon Sep 17 00:00:00 2001 From: Sherry Sun Date: Sat, 9 May 2026 09:54:10 +0800 Subject: [PATCH 0922/2758] arm64: dts: imx8qxp-mek: Remove unnecessary PCIe EP vpcie-supply [ Upstream commit b5ba136d535f1060322af31c0cf74d85ea19892d ] For PCIe endpoint mode, only M.2 power supply needs to be ensured. On imx8qxp-mek, the M.2 power is always on and cannot be controlled, while reg_pcieb only controls the M.2 W_DISABLE1# signal. Remove the unnecessary vpcie-supply property from pcie0_ep node. Fixes: 1c9b0c6044c2 ("arm64: dts: imx8: use common imx-pcie0-ep.dtso to enable PCI ep function") Signed-off-by: Sherry Sun Reviewed-by: Richard Zhu Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/freescale/imx8qxp-mek.dts | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm64/boot/dts/freescale/imx8qxp-mek.dts b/arch/arm64/boot/dts/freescale/imx8qxp-mek.dts index 623169f7ddb5f..c07138055229c 100644 --- a/arch/arm64/boot/dts/freescale/imx8qxp-mek.dts +++ b/arch/arm64/boot/dts/freescale/imx8qxp-mek.dts @@ -742,7 +742,6 @@ phy-names = "pcie-phy"; pinctrl-0 = <&pinctrl_pcieb>; pinctrl-names = "default"; - vpcie-supply = <®_pcieb>; status = "disabled"; }; From ba5fc6b6448a3b740c6684fb8b7cd571eaff6b86 Mon Sep 17 00:00:00 2001 From: Sherry Sun Date: Sat, 9 May 2026 09:54:11 +0800 Subject: [PATCH 0923/2758] arm64: dts: imx95-19x19-evk: Fix PCIe EP vpcie-supply [ Upstream commit 596d0f9f4fefffbf783ab26cfa90cf50f5dd6bb0 ] The vpcie-supply property should reference the regulator that controls the actual M.2 power supply, not the W_DISABLE1# signal. On imx95-19x19-evk: - reg_pcie0 controls M.2 W_DISABLE1# signal - reg_m2_pwr controls the actual M.2 power supply Fix the vpcie-supply to use reg_m2_pwr for proper power control in PCIe endpoint mode. Fixes: 58bea81052d0 ("arm64: dts: imx95: add pcie1 ep overlay file and create pcie-ep dtb files") Signed-off-by: Sherry Sun Reviewed-by: Richard Zhu Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts b/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts index 041fd838fabba..49400bc9ba919 100644 --- a/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts +++ b/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts @@ -550,7 +550,7 @@ &pcie0_ep { pinctrl-0 = <&pinctrl_pcie0>; pinctrl-names = "default"; - vpcie-supply = <®_pcie0>; + vpcie-supply = <®_m2_pwr>; status = "disabled"; }; From ba608e9b5a4e9b6b0ee6be880d733cbafad2a07b Mon Sep 17 00:00:00 2001 From: Viacheslav Dubeyko Date: Thu, 14 May 2026 12:55:19 -0700 Subject: [PATCH 0924/2758] hfs: fix incorrect inode ID assignment in hfs_new_inode() [ Upstream commit 6592287869bffee91f59363e51de5f971ec2bd9d ] The xfstests' test-case generic/003 reveals the HFS volume corruption: sudo ./check generic/003 FSTYP -- hfs PLATFORM -- Linux/x86_64 hfsplus-testing-0001 7.0.0-rc1+ #18 SMP PREEMPT_DYNAMIC Fri Mar 13 17:54:19 PDT 2026 MKFS_OPTIONS -- /dev/loop51 MOUNT_OPTIONS -- /dev/loop51 /mnt/scratch generic/003 51s ... _check_generic_filesystem: filesystem on /dev/loop51 is inconsistent sudo fsck.hfs -d /dev/loop51 ** /dev/loop51 Using cacheBlockSize=32K cacheTotalBlock=1024 cacheSize=32768K. Executing fsck_hfs (version 540.1-Linux). ** Checking HFS volume. The volume name is untitled ** Checking extents overflow file. ** Checking catalog file. ** Checking catalog hierarchy. ** Checking volume bitmap. ** Checking volume information. invalid MDB drNxtCNID Master Directory Block needs minor repair (1, 0) Verify Status: VIStat = 0x8000, ABTStat = 0x0000 EBTStat = 0x0000 CBTStat = 0x0000 CatStat = 0x00000000 ** Repairing volume. ** Rechecking volume. ** Checking HFS volume. The volume name is untitled ** Checking extents overflow file. ** Checking catalog file. ** Checking catalog hierarchy. ** Checking volume bitmap. ** Checking volume information. ** The volume untitled was repaired successfully. The reason of corruption is incorrect value of drNxtCNID (next CNID) in the MDB or superblock. The generic/003 test-case creates several new inodes: kernel: run fstests generic/003 hfs: hfs_mdb_get():179 next_id 16 hfs: hfs_mdb_get():179 next_id 16 hfs: hfs_new_inode():208 next_id 17 hfs: hfs_new_inode():208 next_id 18 hfs: hfs_mdb_commit():307 next_id 18 hfs: hfs_mdb_get():179 next_id 18 hfs: hfs_new_inode():208 next_id 19 hfs: hfs_new_inode():208 next_id 20 hfs_mdb_commit():307 next_id 20 hfs: hfs_mdb_get():179 next_id 20 hfs: hfs_new_inode():208 next_id 21 hfs: hfs_mdb_commit():307 next_id 21 hfs: hfs_mdb_get():179 next_id 21 The final assigned CNID was 21 but fsck correct it on 22. It is possible to see that the reason of the issue is incrementing the next_id value at first and assigning already incremented value to the inode->i_ino: struct inode *hfs_new_inode(...) { next_id = atomic64_inc_return(&HFS_SB(sb)->next_id); inode->i_ino = (u32)next_id; } This patch fixes the issue by assigning the decremented value to inode->i_ino. Fixes: a06ec283e125 ("hfs: add logic of correcting a next unused CNID") cc: John Paul Adrian Glaubitz cc: Yangtao Li cc: linux-fsdevel@vger.kernel.org Signed-off-by: Viacheslav Dubeyko Link: https://lore.kernel.org/r/20260514195518.354108-2-slava@dubeyko.com Signed-off-by: Viacheslav Dubeyko Signed-off-by: Sasha Levin --- fs/hfs/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index 89b33a9d46d5c..1cbba73450389 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -204,7 +204,7 @@ struct inode *hfs_new_inode(struct inode *dir, const struct qstr *name, umode_t pr_err("cannot create new inode: next CNID exceeds limit\n"); goto out_discard; } - inode->i_ino = (u32)next_id; + inode->i_ino = (u32)next_id - 1; inode->i_mode = mode; inode->i_uid = current_fsuid(); inode->i_gid = current_fsgid(); From c2fb3a440ac78abbe72ba6decbc4b20ac9a8405e Mon Sep 17 00:00:00 2001 From: Zilin Guan Date: Tue, 3 Feb 2026 16:31:34 +0000 Subject: [PATCH 0925/2758] media: atomisp: Fix memory leak in atomisp_fixed_pattern_table() [ Upstream commit 4e8156bd9517fa18c3613ea39c222c7035f0221e ] atomisp_v4l2_framebuffer_to_css_frame() allocates memory for temporary variable raw_black_frame, which must be released via ia_css_frame_free() before the function returns. However, if sh_css_set_black_frame() fails, the function returns immediately without performing this cleanup, leading to a memory leak. Fix this by assigning the return value of sh_css_set_black_frame() to ret. This ensures that the error code is propagated while allowing the execution to fall through to the ia_css_frame_free() cleanup call. The bug was originally detected on v6.13-rc1 using an experimental static analysis tool we are developing, and we have verified that the issue persists in the latest mainline kernel. The tool is based on the LLVM framework and is specifically designed to detect memory management issues. It is currently under active development and not yet publicly available. We performed build testing on x86_64 with allyesconfig. Since triggering this error path in atomisp requires specific Intel Atom ISP hardware and firmware, we were unable to perform runtime testing and instead verified the fix according to the code logic. Fixes: 85b606e02ad7 ("media: atomisp: get rid of a bunch of other wrappers") Signed-off-by: Zilin Guan Reviewed-by: Andy Shevchenko Signed-off-by: Sakari Ailus Signed-off-by: Sasha Levin --- drivers/staging/media/atomisp/pci/atomisp_cmd.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c index fec369575d883..d58aa4d594577 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c @@ -3364,10 +3364,8 @@ int atomisp_fixed_pattern_table(struct atomisp_sub_device *asd, if (ret) return ret; - if (sh_css_set_black_frame(asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream, - raw_black_frame) != 0) - return -ENOMEM; - + ret = sh_css_set_black_frame(asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream, + raw_black_frame); ia_css_frame_free(raw_black_frame); return ret; } From f614bf0a64aa1cb7444d152a96798a6bf1d49e1f Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Thu, 2 Apr 2026 20:23:19 -0400 Subject: [PATCH 0926/2758] media: atomisp: gc2235: fix UAF and memory leak [ Upstream commit 628f763aee0047ff44974388d6f70f75a763026b ] gc2235_probe() handles its error paths incorrectly. If media_entity_pads_init() fails, gc2235_remove() is called, which tears down the subdev and frees dev, but then still falls through to atomisp_register_i2c_module(). This results in use-after-free. If atomisp_register_i2c_module() fails, the media entity and control handler are left initialized and dev is leaked. gc2235_remove() unconditionally calls media_entity_cleanup() and v4l2_ctrl_handler_free(), but these are not initialized at every error path in gc2235_probe(). Replace gc2235_remove() calls in the probe error paths with explicit unwind labels that free only the resources initialized at each point of failure, in reverse order of initialization. Fixes: a49d25364dfb ("staging/atomisp: Add support for the Intel IPU v2") Signed-off-by: Yuho Choi Reviewed-by: Dan Carpenter Signed-off-by: Sakari Ailus Signed-off-by: Sasha Levin --- .../media/atomisp/i2c/atomisp-gc2235.c | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c index d3414312e1de2..998c9f46bd068 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c @@ -809,7 +809,7 @@ static int gc2235_probe(struct i2c_client *client) ret = gc2235_s_config(&dev->sd, client->irq, gcpdev); if (ret) - goto out_free; + goto err_unregister_subdev; dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; dev->pad.flags = MEDIA_PAD_FL_SOURCE; @@ -818,18 +818,16 @@ static int gc2235_probe(struct i2c_client *client) ret = v4l2_ctrl_handler_init(&dev->ctrl_handler, ARRAY_SIZE(gc2235_controls)); - if (ret) { - gc2235_remove(client); - return ret; - } + if (ret) + goto err_csi_cfg; for (i = 0; i < ARRAY_SIZE(gc2235_controls); i++) v4l2_ctrl_new_custom(&dev->ctrl_handler, &gc2235_controls[i], NULL); if (dev->ctrl_handler.error) { - gc2235_remove(client); - return dev->ctrl_handler.error; + ret = dev->ctrl_handler.error; + goto err_ctrl_handler; } /* Use same lock for controls as for everything else. */ @@ -838,14 +836,23 @@ static int gc2235_probe(struct i2c_client *client) ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad); if (ret) - gc2235_remove(client); + goto err_ctrl_handler; + + ret = atomisp_register_i2c_module(&dev->sd, gcpdev); + if (ret) + goto err_media_cleanup; - return atomisp_register_i2c_module(&dev->sd, gcpdev); + return 0; -out_free: +err_media_cleanup: + media_entity_cleanup(&dev->sd.entity); +err_ctrl_handler: + v4l2_ctrl_handler_free(&dev->ctrl_handler); +err_csi_cfg: + dev->platform_data->csi_cfg(&dev->sd, 0); +err_unregister_subdev: v4l2_device_unregister_subdev(&dev->sd); kfree(dev); - return ret; } From 3040b1d34a1f888a694a9d4ffa4b5779ef8d109b Mon Sep 17 00:00:00 2001 From: "Jose A. Perez de Azpillaga" Date: Thu, 26 Mar 2026 22:34:07 +0100 Subject: [PATCH 0927/2758] staging: media: atomisp: fix loop shadowing in ia_css_stream_destroy() [ Upstream commit 9087395a383212ab1beaefcbe3b57ed131c7823d ] The nested loop inside the IS_ISP2401 block incorrectly uses the same variable 'i' as the outer loop. This shadows the outer loop variable and causes premature termination or skipped array elements. Change the inner loop to use a new variable 'j' to prevent this. Fixes: 113401c67386 ("media: atomisp: sh_css: Removed #ifdef ISP2401 to make code generic") Signed-off-by: Jose A. Perez de Azpillaga Reviewed-by: Dan Carpenter Signed-off-by: Sakari Ailus Signed-off-by: Sasha Levin --- drivers/staging/media/atomisp/pci/sh_css.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c index 584751516b696..3d1903a988048 100644 --- a/drivers/staging/media/atomisp/pci/sh_css.c +++ b/drivers/staging/media/atomisp/pci/sh_css.c @@ -8192,7 +8192,7 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config, int ia_css_stream_destroy(struct ia_css_stream *stream) { - int i; + int i, j; int err = 0; IA_CSS_ENTER_PRIVATE("stream = %p", stream); @@ -8223,10 +8223,10 @@ ia_css_stream_destroy(struct ia_css_stream *stream) sp_pipeline_input_terminal = &sh_css_sp_group.pipe_io[sp_thread_id].input; - for (i = 0; i < IA_CSS_STREAM_MAX_ISYS_STREAM_PER_CH; i++) { + for (j = 0; j < IA_CSS_STREAM_MAX_ISYS_STREAM_PER_CH; j++) { ia_css_isys_stream_h isys_stream = - &sp_pipeline_input_terminal->context.virtual_input_system_stream[i]; - if (stream->config.isys_config[i].valid && isys_stream->valid) + &sp_pipeline_input_terminal->context.virtual_input_system_stream[j]; + if (stream->config.isys_config[j].valid && isys_stream->valid) ia_css_isys_stream_destroy(isys_stream); } } From 311efbb24eddce7a0ff291f377d2b6974baf6e6f Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Tue, 19 May 2026 06:12:35 +0200 Subject: [PATCH 0928/2758] riscv: dts: spacemit: set console baud rate on Milk-V Jupiter [ Upstream commit 4f97acb4f744f516bbe976da8282c0fe213216ff ] Because the default console's baud rate is not set, defconfig kernels do not have any serial output on this platform. Set the baud rate to 115200, matching what is used by U-Boot etc on this platform. See-also: 24c12ca43b12c ("dts: spacemit: set console baud rate on bpif3") Fixes: 5b90a3d6092d9 ("riscv: dts: spacemit: Add Milk-V Jupiter board device tree") Signed-off-by: Aurelien Jarno Reviewed-by: Yixun Lan Link: https://patch.msgid.link/20260519041458.3287843-2-aurelien@aurel32.net Signed-off-by: Yixun Lan Signed-off-by: Sasha Levin --- arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts b/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts index afaad59e6bce2..db98dbfadf00f 100644 --- a/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts +++ b/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts @@ -20,7 +20,7 @@ }; chosen { - stdout-path = "serial0"; + stdout-path = "serial0:115200n8"; }; leds { From 9a454b0a5017c306e7793d0a51ec05386ef32aba Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Tue, 19 May 2026 06:12:39 +0200 Subject: [PATCH 0929/2758] riscv: dts: spacemit: fix uboot partition offset on Milk-V Jupiter [ Upstream commit 6edd9a0d32e1ef81133b8cb5b3bb3157a44da4d1 ] Correct the uboot partition node name to match its actual offset. Fixes: 2829823956f0 ("riscv: dts: spacemit: enable QSPI and add SPI NOR on Milk-V Jupiter") Signed-off-by: Aurelien Jarno Reviewed-by: Yixun Lan Link: https://patch.msgid.link/20260519041458.3287843-6-aurelien@aurel32.net Signed-off-by: Yixun Lan Signed-off-by: Sasha Levin --- arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts b/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts index db98dbfadf00f..c47bfb1597306 100644 --- a/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts +++ b/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts @@ -367,7 +367,7 @@ opensbi@70000 { reg = <0x70000 0x30000>; }; - uboot@a00000 { + uboot@a0000 { reg = <0xa0000 0x760000>; }; }; From 5d0b8fcea617ef5be3fe44574551e1f8d996fc85 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Tue, 2 Sep 2025 18:20:53 +0100 Subject: [PATCH 0930/2758] firmware: smccc: Fix Arm SMCCC SOC_ID name call [ Upstream commit 70492cfce2a4d41e87bf46989028a90f4bc6b38f ] Commit 5f9c23abc477 ("firmware: smccc: Support optional Arm SMCCC SOC_ID name") introduced the SOC_ID name string call, which reports a human readable string describing the SoC, as returned by firmware. The SMCCC spec v1.6 describes this feature as AArch64 only, since we rely on 8 characters to be transmitted per register. Consequently the SMCCC call must use the AArch64 calling convention, which requires bit 30 of the FID to be set. The spec is a bit confusing here, since it mentions that in the parameter description ("2: SoC name (optionally implemented for SMC64 calls, ..."), but still prints the FID explicitly as 0x80000002. But as this FID is using the SMC32 calling convention (correct for the other two calls), it will not match what any SMCCC conformant firmware is expecting, so any call would return NOT_SUPPORTED. Add a 64-bit version of the ARCH_SOC_ID FID macro, and use that for the SoC name version of the call to fix the issue. Fixes: 5f9c23abc477 ("firmware: smccc: Support optional Arm SMCCC SOC_ID name") Signed-off-by: Andre Przywara Link: https://patch.msgid.link/20250902172053.304911-1-andre.przywara@arm.com Signed-off-by: Sudeep Holla Signed-off-by: Sasha Levin --- drivers/firmware/smccc/soc_id.c | 2 +- include/linux/arm-smccc.h | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/smccc/soc_id.c b/drivers/firmware/smccc/soc_id.c index 2f7475e66b3ca..a909d5e6dee50 100644 --- a/drivers/firmware/smccc/soc_id.c +++ b/drivers/firmware/smccc/soc_id.c @@ -60,7 +60,7 @@ static char __init *smccc_soc_name_init(void) * to the ARM_SMCCC_ARCH_SOC_ID function. Fetch it if * available. */ - args.a0 = ARM_SMCCC_ARCH_SOC_ID; + args.a0 = ARM_SMCCC_ARCH_SOC_ID64; args.a1 = 2; /* SOC_ID name */ arm_smccc_1_2_invoke(&args, &res); diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h index e7195750d21bb..4de81848fe2ee 100644 --- a/include/linux/arm-smccc.h +++ b/include/linux/arm-smccc.h @@ -90,6 +90,11 @@ ARM_SMCCC_SMC_32, \ 0, 2) +#define ARM_SMCCC_ARCH_SOC_ID64 \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_64, \ + 0, 2) + #define ARM_SMCCC_ARCH_WORKAROUND_1 \ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ ARM_SMCCC_SMC_32, \ From 923bf9f5b7fbf4f0b2403b05c74e00671f90fb82 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Sun, 17 May 2026 20:02:40 +0100 Subject: [PATCH 0931/2758] firmware: arm_scmi: Read sensor config as 32-bit value [ Upstream commit f6fe7c3c007df18afd289ff2d98c692fae9ab085 ] The SENSOR_CONFIG_GET response contains a 32-bit sensor_config field, and the xfer is initialized with a 4-byte RX buffer. Reading it with get_unaligned_le64() can consume bytes past the returned payload. Use get_unaligned_le32() to match the protocol layout and the allocated response size. Fixes: 7b83c5f41088 ("firmware: arm_scmi: Add SCMI v3.0 sensor configuration support") Link: https://patch.msgid.link/20260517-scmi_fixes-v1-1-d86daec4defd@kernel.org Reviewed-by: Cristian Marussi Signed-off-by: Sudeep Holla Signed-off-by: Sasha Levin --- drivers/firmware/arm_scmi/sensors.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c index 882d55f987d22..836c294a9f42a 100644 --- a/drivers/firmware/arm_scmi/sensors.c +++ b/drivers/firmware/arm_scmi/sensors.c @@ -793,7 +793,7 @@ static int scmi_sensor_config_get(const struct scmi_protocol_handle *ph, if (!ret) { struct scmi_sensor_info *s = si->sensors + sensor_id; - *sensor_config = get_unaligned_le64(t->rx.buf); + *sensor_config = get_unaligned_le32(t->rx.buf); s->sensor_config = *sensor_config; } From 9730135f28dee325e32f80a7cdf00c5659ead5b2 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Tue, 19 May 2026 10:07:38 +0800 Subject: [PATCH 0932/2758] media: synopsys: Fix IPI using hardcoded datatype [ Upstream commit 8ba166ff7c921da610376156d7c0a5fc985fa983 ] The imx93_csi2rx_dphy_ipi_enable() function configures the IPI datatype using csi2->formats->csi_dt, which is initialized during probe but never updated in set_fmt(). This causes the IPI to always use the probe-time default datatype, ignoring the actual media bus format negotiated at runtime. When userspace requests a different format, the IPI hardware is configured with the wrong datatype, resulting in incorrect image output. Fix by updating csi2->formats in the set_fmt callback to reflect the currently negotiated format, ensuring the IPI configuration matches the runtime datatype. Fixes: ec40b431f0ab ("media: synopsys: csi2rx: add i.MX93 support") Reviewed-by: Frank Li Signed-off-by: Guoniu Zhou Signed-off-by: Sakari Ailus Signed-off-by: Sasha Levin --- drivers/media/platform/synopsys/dw-mipi-csi2rx.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c index 02eb4a6cafade..0b80e84983f9e 100644 --- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c +++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c @@ -311,7 +311,7 @@ dw_mipi_csi2rx_find_format(struct dw_mipi_csi2rx_device *csi2, u32 mbus_code) WARN_ON(csi2->formats_num == 0); for (unsigned int i = 0; i < csi2->formats_num; i++) { - const struct dw_mipi_csi2rx_format *format = &csi2->formats[i]; + const struct dw_mipi_csi2rx_format *format = &formats[i]; if (format->code == mbus_code) return format; @@ -433,7 +433,7 @@ dw_mipi_csi2rx_enum_mbus_code(struct v4l2_subdev *sd, if (code->index >= csi2->formats_num) return -EINVAL; - code->code = csi2->formats[code->index].code; + code->code = formats[code->index].code; return 0; default: return -EINVAL; @@ -470,6 +470,17 @@ static int dw_mipi_csi2rx_set_fmt(struct v4l2_subdev *sd, *src = *sink; + /* Store the CSIS format descriptor for active formats. */ + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + csi2->formats = fmt ? : + dw_mipi_csi2rx_find_format(csi2, default_format.code); + + if (!csi2->formats) { + dev_err(csi2->dev, "Failed to find valid format\n"); + return -EINVAL; + } + } + return 0; } From ba7f353f3a152b174dde99f8c1518a3e54a61388 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 20 May 2026 15:07:01 +0200 Subject: [PATCH 0933/2758] sysfs: clamp show() return value in sysfs_kf_read() [ Upstream commit 454257f6d124a92342dcbb7710c03dd6ef96c731 ] sysfs_kf_seq_show() defends against buggy show() callbacks that return larger than PAGE_SIZE by clamping the value and printing a warning. sysfs_kf_read(), the prealloc variant, has no such defense. The only current in-tree user of __ATTR_PREALLOC is drivers/md/md.c, whose show() callbacks are well-behaved, so this is hardening against future drivers doing foolish things and out-of-tree code doing even more foolish things. Cc: NeilBrown Cc: Tejun Heo Fixes: 2b75869bba67 ("sysfs/kernfs: allow attributes to request write buffer be pre-allocated.") Assisted-by: gregkh_clanker_t1000 Reviewed-by: Rafael J. Wysocki (Intel) Reviewed-by: Danilo Krummrich Link: https://patch.msgid.link/2026052000-drove-unicycle-d61b@gregkh Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- fs/sysfs/file.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 5709cede1d756..25b44fe171a32 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -120,6 +120,10 @@ static ssize_t sysfs_kf_read(struct kernfs_open_file *of, char *buf, len = ops->show(kobj, of->kn->priv, buf); if (len < 0) return len; + if (len >= (ssize_t)PAGE_SIZE) { + printk("fill_read_buffer: %pS returned bad count\n", ops->show); + len = PAGE_SIZE - 1; + } if (pos) { if (len <= pos) return 0; From 6003b1a0bc343b18fb097b54ba83100f433ed959 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 2 May 2026 22:25:08 -0700 Subject: [PATCH 0934/2758] bitops: use common function parameter names [ Upstream commit 8a51b2e874f47a6094353b59ecae421f0968fe3a ] Fix the function prototypes to use the common parameter name 'addr' instead of 'p' (common to arch-specific implementations of these functions). This avoids the kernel-doc warnings: Warning: include/asm-generic/bitops/lock.h:19 function parameter 'p' not described in 'arch_test_and_set_bit_lock' Warning: include/asm-generic/bitops/lock.h:41 function parameter 'p' not described in 'arch_clear_bit_unlock' Warning: include/asm-generic/bitops/lock.h:59 function parameter 'p' not described in 'arch___clear_bit_unlock' Fixes: 84c6591103db ("locking/atomics, asm-generic/bitops/lock.h: Rewrite using atomic_fetch_*()") Signed-off-by: Randy Dunlap Signed-off-by: Yury Norov Signed-off-by: Sasha Levin --- include/asm-generic/bitops/lock.h | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/include/asm-generic/bitops/lock.h b/include/asm-generic/bitops/lock.h index 14d4ec8c5152d..ffb73b6129e73 100644 --- a/include/asm-generic/bitops/lock.h +++ b/include/asm-generic/bitops/lock.h @@ -16,16 +16,16 @@ * It can be used to implement bit locks. */ static __always_inline int -arch_test_and_set_bit_lock(unsigned int nr, volatile unsigned long *p) +arch_test_and_set_bit_lock(unsigned int nr, volatile unsigned long *addr) { long old; unsigned long mask = BIT_MASK(nr); - p += BIT_WORD(nr); - if (READ_ONCE(*p) & mask) + addr += BIT_WORD(nr); + if (READ_ONCE(*addr) & mask) return 1; - old = raw_atomic_long_fetch_or_acquire(mask, (atomic_long_t *)p); + old = raw_atomic_long_fetch_or_acquire(mask, (atomic_long_t *)addr); return !!(old & mask); } @@ -38,10 +38,10 @@ arch_test_and_set_bit_lock(unsigned int nr, volatile unsigned long *p) * This operation is atomic and provides release barrier semantics. */ static __always_inline void -arch_clear_bit_unlock(unsigned int nr, volatile unsigned long *p) +arch_clear_bit_unlock(unsigned int nr, volatile unsigned long *addr) { - p += BIT_WORD(nr); - raw_atomic_long_fetch_andnot_release(BIT_MASK(nr), (atomic_long_t *)p); + addr += BIT_WORD(nr); + raw_atomic_long_fetch_andnot_release(BIT_MASK(nr), (atomic_long_t *)addr); } /** @@ -56,14 +56,14 @@ arch_clear_bit_unlock(unsigned int nr, volatile unsigned long *p) * See for example x86's implementation. */ static inline void -arch___clear_bit_unlock(unsigned int nr, volatile unsigned long *p) +arch___clear_bit_unlock(unsigned int nr, volatile unsigned long *addr) { unsigned long old; - p += BIT_WORD(nr); - old = READ_ONCE(*p); + addr += BIT_WORD(nr); + old = READ_ONCE(*addr); old &= ~BIT_MASK(nr); - raw_atomic_long_set_release((atomic_long_t *)p, old); + raw_atomic_long_set_release((atomic_long_t *)addr, old); } #ifndef arch_xor_unlock_is_negative_byte From b22cebba03efb16c13ecdb3b76bb43ed3503de0a Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 14 May 2026 17:15:15 +0800 Subject: [PATCH 0935/2758] regulator: dt-bindings: mt6359: Drop regulator-name pattern restrictions [ Upstream commit cdc517688ffa2c30a64a20b558a9ecbf046c70f1 ] The name of the regulator should match what the board design specifies for the power rail. There should be no limitations on what the name can be, and they definitely don't always follow the PMIC's own names. Drop the restrictions on regulator-name. Fixes: 8771456635d5 ("dt-bindings: regulator: Add document for MT6359 regulator") Reviewed-by: Krzysztof Kozlowski Signed-off-by: Chen-Yu Tsai Link: https://patch.msgid.link/20260514091520.2718987-3-wenst@chromium.org Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- .../bindings/regulator/mt6359-regulator.yaml | 43 ------------------- 1 file changed, 43 deletions(-) diff --git a/Documentation/devicetree/bindings/regulator/mt6359-regulator.yaml b/Documentation/devicetree/bindings/regulator/mt6359-regulator.yaml index fe4ac9350ba03..ac925334ae833 100644 --- a/Documentation/devicetree/bindings/regulator/mt6359-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/mt6359-regulator.yaml @@ -18,84 +18,41 @@ patternProperties: "^buck_v(s1|gpu11|modem|pu|core|s2|pa|proc2|proc1|core_sshub)$": type: object $ref: regulator.yaml# - - properties: - regulator-name: - pattern: "^v(s1|gpu11|modem|pu|core|s2|pa|proc2|proc1|core_sshub)$" - unevaluatedProperties: false "^ldo_v(ibr|rf12|usb|camio|efuse|xo22)$": type: object $ref: regulator.yaml# - - properties: - regulator-name: - pattern: "^v(ibr|rf12|usb|camio|efuse|xo22)$" - unevaluatedProperties: false "^ldo_v(rfck|emc|a12|a09|ufs|bbck)$": type: object $ref: regulator.yaml# - - properties: - regulator-name: - pattern: "^v(rfck|emc|a12|a09|ufs|bbck)$" - unevaluatedProperties: false "^ldo_vcn(18|13|33_1_bt|13_1_wifi|33_2_bt|33_2_wifi)$": type: object $ref: regulator.yaml# - - properties: - regulator-name: - pattern: "^vcn(18|13|33_1_bt|13_1_wifi|33_2_bt|33_2_wifi)$" - unevaluatedProperties: false "^ldo_vsram_(proc2|others|md|proc1|others_sshub)$": type: object $ref: regulator.yaml# - - properties: - regulator-name: - pattern: "^vsram_(proc2|others|md|proc1|others_sshub)$" - unevaluatedProperties: false "^ldo_v(fe|bif|io)28$": type: object $ref: regulator.yaml# - - properties: - regulator-name: - pattern: "^v(fe|bif|io)28$" - unevaluatedProperties: false "^ldo_v(aud|io|aux|rf|m)18$": type: object $ref: regulator.yaml# - - properties: - regulator-name: - pattern: "^v(aud|io|aux|rf|m)18$" - unevaluatedProperties: false "^ldo_vsim[12]$": type: object $ref: regulator.yaml# - - properties: - regulator-name: - pattern: "^vsim[12]$" - - required: - - regulator-name - unevaluatedProperties: false additionalProperties: false From 0451ddd2cf16452a30adb512dfea00d57d970202 Mon Sep 17 00:00:00 2001 From: Daniel Palmer Date: Wed, 20 May 2026 20:19:31 +0900 Subject: [PATCH 0936/2758] tools/nolibc: getopt: Fix potential out of bounds access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 136ca91411b0b637e862eb7b1cce2a56853edd17 ] Running clang-tidy on a program that uses getopt() from nolibc this warning appears: getopt.h:80:6: warning: Out of bound access to memory after the end of the string literal [clang-analyzer-security.ArrayBound] 80 | if (optstring[i] == ':') { This looks like a very unlikely case that an argument inside of argv is being changed between getopt() calls. Adding a check for d becoming 0 in the guard after the loop stops getopt() getting far enough to access beyond the end of the array and seems to correct the issue. Fixes: bae3cd708e8a ("tools/nolibc: add getopt()") Assisted-by: Claude:claude-4.6-sonnet # reproducer Signed-off-by: Daniel Palmer Link: https://patch.msgid.link/20260520111931.1027758-1-daniel@thingy.jp [Thomas: clean up commit message a bit] Signed-off-by: Thomas Weißschuh Signed-off-by: Sasha Levin --- tools/include/nolibc/getopt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/include/nolibc/getopt.h b/tools/include/nolibc/getopt.h index 87565e3b6a339..3ad140f692dfe 100644 --- a/tools/include/nolibc/getopt.h +++ b/tools/include/nolibc/getopt.h @@ -71,7 +71,7 @@ int getopt(int argc, char * const argv[], const char *optstring) d = optstring[i++]; } while (d && d != c); - if (d != c || c == ':') { + if (!d || d != c || c == ':') { optopt = c; if (optstring[0] != ':' && opterr) fprintf(stderr, "%s: unrecognized option: %c\n", argv[0], *optchar); From 8d33d107c1308ac02c4e0e1331852cb951667516 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Thu, 14 May 2026 13:04:44 -0300 Subject: [PATCH 0937/2758] vfio: selftests: Fix out-of-tree build with make O= [ Upstream commit a21b864bd951e452922dbd66747da24daca2b04f ] The test programs are compiled via a static pattern rule that requires intermediate .o files: $(TEST_GEN_PROGS): %: %.o $(LIBVFIO_O) After lib.mk prefixes TEST_GEN_PROGS with $(OUTPUT), this creates dependencies on .o files in the output directory (e.g. $(OUTPUT)/vfio_dma_mapping_test.o). However, there is no rule to compile these .o files from the source directory .c files when OUTPUT differs from the source directory. Add an explicit chain of pattern rules: $(OUTPUT)/% -> $(OUTPUT)/%.o -> %.c Following the same pattern already used in libvfio.mk for the library objects. Fixes: 19faf6fd969c ("vfio: selftests: Add a helper library for VFIO selftests") Reviewed-by: David Matlack Signed-off-by: Jason Gunthorpe Link: https://lore.kernel.org/r/0-v2-4ccc247e6aff+1d93-vfio_st_make_o_jgg@nvidia.com Signed-off-by: Alex Williamson Signed-off-by: Sasha Levin --- tools/testing/selftests/vfio/Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/vfio/Makefile b/tools/testing/selftests/vfio/Makefile index 0684932d91bfc..a19b75742342a 100644 --- a/tools/testing/selftests/vfio/Makefile +++ b/tools/testing/selftests/vfio/Makefile @@ -27,10 +27,13 @@ CFLAGS += $(EXTRA_CFLAGS) LDFLAGS += -pthread -$(TEST_GEN_PROGS): %: %.o $(LIBVFIO_O) +$(TEST_GEN_PROGS): $(OUTPUT)/%: $(OUTPUT)/%.o $(LIBVFIO_O) $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $< $(LIBVFIO_O) $(LDLIBS) -o $@ TEST_GEN_PROGS_O = $(patsubst %, %.o, $(TEST_GEN_PROGS)) +$(TEST_GEN_PROGS_O): $(OUTPUT)/%.o: %.c + $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@ + TEST_DEP_FILES = $(patsubst %.o, %.d, $(TEST_GEN_PROGS_O) $(LIBVFIO_O)) -include $(TEST_DEP_FILES) From 1203f9ea8fd4a343f9e27b5be9d1d2075b244fc5 Mon Sep 17 00:00:00 2001 From: David Matlack Date: Tue, 28 Apr 2026 23:27:06 +0000 Subject: [PATCH 0938/2758] vfio: selftests: Allow builds when ARCH=x86 [ Upstream commit 75182529687e204b2bd046a3f7b1c2acb5031032 ] Allow builds when ARCH=x86 since the top-level Makefile can set ARCH=x86 even for 64-bit x86 builds. Note that ARCH=x86 could also indicate a native build on a 32-bit x86 host. However, it doesn't seem like anyone is building selftests natively on 32-bit x86 hosts these days since KVM selftests allow ARCH=x86 and fail to compile on 32-bit x86. If someone reports an issue on 32-bit native builds we can harden the KVM and VFIO selftests to explicitly check 64-bit (see the discussion in the Closes link below). Fixes: a55d4bbbe644 ("vfio: selftests: only build tests on arm64 and x86_64") Reported-by: Jason Gunthorpe Closes: https://lore.kernel.org/kvm/20260427231217.GA1670652@nvidia.com/ Signed-off-by: David Matlack Reviewed-by: Jason Gunthorpe Link: https://lore.kernel.org/r/20260428232707.2139059-1-dmatlack@google.com Signed-off-by: Alex Williamson Signed-off-by: Sasha Levin --- tools/testing/selftests/vfio/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/vfio/Makefile b/tools/testing/selftests/vfio/Makefile index a19b75742342a..0a4cfd1a6c7ed 100644 --- a/tools/testing/selftests/vfio/Makefile +++ b/tools/testing/selftests/vfio/Makefile @@ -1,6 +1,6 @@ ARCH ?= $(shell uname -m) -ifeq (,$(filter $(ARCH),aarch64 arm64 x86_64)) +ifeq (,$(filter $(ARCH),aarch64 arm64 x86 x86_64)) # Do nothing on unsupported architectures include ../lib.mk else From 29cb06dabcd6e03e3d2104429d5d9de1eacead40 Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Wed, 20 May 2026 19:15:47 +0900 Subject: [PATCH 0939/2758] nilfs2: Fix return in nilfs_mkdir [ Upstream commit e5925f33e4fa9ee313d481557607adce8e30ed2e ] Return NULL instead of passing zero to ERR_PTR. Fixes smatch warning: - fs/nilfs2/namei.c:261 nilfs_mkdir() warn: passing zero to 'ERR_PTR' Fixes: 88d5baf69082 ("Change inode_operations.mkdir to return struct dentry *") Signed-off-by: Hongling Zeng Signed-off-by: Ryusuke Konishi Signed-off-by: Viacheslav Dubeyko Signed-off-by: Sasha Levin --- fs/nilfs2/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c index 40ac679ec56e4..e2fe95de3d71c 100644 --- a/fs/nilfs2/namei.c +++ b/fs/nilfs2/namei.c @@ -258,7 +258,7 @@ static struct dentry *nilfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, else nilfs_transaction_abort(dir->i_sb); - return ERR_PTR(err); + return err ? ERR_PTR(err) : NULL; out_fail: drop_nlink(inode); From e143d5b150af2b030c8df201f3c14c68902337df Mon Sep 17 00:00:00 2001 From: GuoHan Zhao Date: Mon, 27 Apr 2026 09:21:28 +0800 Subject: [PATCH 0940/2758] vfio/xe: avoid duplicate reset in xe_vfio_pci_reset_done MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit b9285405c5f6144f4444f97bf3048d865e11cc1d ] xe_vfio_pci_reset_done() sets deferred_reset and, when it manages to acquire state_mutex itself, hands the cleanup off to xe_vfio_pci_state_mutex_unlock(). That helper already clears deferred_reset and runs xe_vfio_pci_reset() before dropping the mutex. Calling xe_vfio_pci_reset() again right afterwards repeats the reset handling unnecessarily. Fixes: 1f5556ec8b9e ("vfio/xe: Add device specific vfio_pci driver variant for Intel graphics") Signed-off-by: GuoHan Zhao Reviewed-by: Kevin Tian Acked-by: Michał Winiarski Link: https://lore.kernel.org/r/20260427012128.117051-1-zhaoguohan@kylinos.cn Signed-off-by: Alex Williamson Signed-off-by: Sasha Levin --- drivers/vfio/pci/xe/main.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/vfio/pci/xe/main.c b/drivers/vfio/pci/xe/main.c index 4ecadbbfd86ec..cbff5af385ef5 100644 --- a/drivers/vfio/pci/xe/main.c +++ b/drivers/vfio/pci/xe/main.c @@ -135,8 +135,6 @@ static void xe_vfio_pci_reset_done(struct pci_dev *pdev) } spin_unlock(&xe_vdev->reset_lock); xe_vfio_pci_state_mutex_unlock(xe_vdev); - - xe_vfio_pci_reset(xe_vdev); } static const struct pci_error_handlers xe_vfio_pci_err_handlers = { From 32064f2dfe6db8f69d5bd655332cb427dc451e30 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 19 May 2026 09:46:17 +0000 Subject: [PATCH 0941/2758] net/sched: sch_drr: annotate data-races around cl->deficit [ Upstream commit c67b104fd7982162885c5e43057ca761006748e2 ] drr_dump_class_stats() runs without qdisc spinlock held. Add missing READ_ONCE()/WRITE_ONCE() annotations around cl->deficit. Fixes: edb09eb17ed8 ("net: sched: do not acquire qdisc spinlock in qdisc/class stats dump") Signed-off-by: Eric Dumazet Link: https://patch.msgid.link/20260519094618.2632073-2-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/sch_drr.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/sched/sch_drr.c b/net/sched/sch_drr.c index 925fa0cfd730c..2eedd3a4322ad 100644 --- a/net/sched/sch_drr.c +++ b/net/sched/sch_drr.c @@ -270,7 +270,7 @@ static int drr_dump_class_stats(struct Qdisc *sch, unsigned long arg, memset(&xstats, 0, sizeof(xstats)); if (qlen) - xstats.deficit = cl->deficit; + xstats.deficit = READ_ONCE(cl->deficit); if (gnet_stats_copy_basic(d, NULL, &cl->bstats, true) < 0 || gnet_stats_copy_rate_est(d, &cl->rate_est) < 0 || @@ -362,7 +362,7 @@ static int drr_enqueue(struct sk_buff *skb, struct Qdisc *sch, if (!cl_is_active(cl)) { list_add_tail(&cl->alist, &q->active); - cl->deficit = cl->quantum; + WRITE_ONCE(cl->deficit, cl->quantum); } sch->qstats.backlog += len; @@ -389,7 +389,7 @@ static struct sk_buff *drr_dequeue(struct Qdisc *sch) len = qdisc_pkt_len(skb); if (len <= cl->deficit) { - cl->deficit -= len; + WRITE_ONCE(cl->deficit, cl->deficit - len); skb = qdisc_dequeue_peeked(cl->qdisc); if (unlikely(skb == NULL)) goto out; @@ -403,7 +403,7 @@ static struct sk_buff *drr_dequeue(struct Qdisc *sch) return skb; } - cl->deficit += cl->quantum; + WRITE_ONCE(cl->deficit, cl->deficit + cl->quantum); list_move_tail(&cl->alist, &q->active); } out: From 1ccb75113f766dac52c444528ea04dc7b611a0de Mon Sep 17 00:00:00 2001 From: Nishanth Sampath Kumar Date: Wed, 20 May 2026 08:11:09 -0700 Subject: [PATCH 0942/2758] regmap-i2c: fix sparse warning in regmap_smbus_word_write_reg16 [ Upstream commit 34808ac8ddafc3e2c2a59e84eaab0a410e7a0fdc ] i2c_smbus_write_word_data() expects a plain u16, but cpu_to_le16() returns __le16 (a sparse-restricted endian type), causing: drivers/base/regmap/regmap-i2c.c:340: sparse: incorrect type in argument 3 (different base types) expected unsigned short [usertype] value got restricted __le16 [usertype] SMBus already defines byte ordering internally, so cpu_to_le16() is wrong here. Replace it with a plain (u16) cast. Fixes: bad4bd28abf4 ("regmap-i2c: add SMBus byte/word reg16 bus for adapters lacking I2C_FUNC_I2C") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202605161621.mY5zFh4D-lkp@intel.com/ Signed-off-by: Nishanth Sampath Kumar Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/base/regmap/regmap-i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c index 31e30dfced192..51a04961faf7b 100644 --- a/drivers/base/regmap/regmap-i2c.c +++ b/drivers/base/regmap/regmap-i2c.c @@ -337,7 +337,7 @@ static int regmap_smbus_word_write_reg16(void *context, const void *data, val = ((u8 *)data)[2]; return i2c_smbus_write_word_data(i2c, addr_hi, - cpu_to_le16(((u16)val << 8) | addr_lo)); + ((u16)val << 8) | addr_lo); } static const struct regmap_bus regmap_smbus_byte_word_reg16 = { From c27c778c44107e3869e8c20e022b37fa0750c5e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:11 +0200 Subject: [PATCH 0943/2758] media: rockchip: rga: fix too small buffer size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 65017e26c065d1240b0512c15867ef1128034fd8 ] Fix the command buffer size being only a quarter of the actual size. The RGA_CMDBUF_SIZE macro was potentially intended to specify the length of the cmdbuf u32 array pointer. But as it's used to specify the size of the allocation, which is counted in bytes. Therefore adjust the macro size to bytes as it better matches the variable name and adjust it's users accordingly. As the command buffer is relatively small, it probably didn't caused an issue due to being smaller than a single page. Fixes: f7e7b48e6d79 ("[media] rockchip/rga: v4l2 m2m support") Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil Signed-off-by: Sasha Levin --- drivers/media/platform/rockchip/rga/rga-hw.c | 2 +- drivers/media/platform/rockchip/rga/rga-hw.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index 43ed742a16492..d1618bb247501 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -414,7 +414,7 @@ static void rga_cmd_set(struct rga_ctx *ctx, { struct rockchip_rga *rga = ctx->rga; - memset(rga->cmdbuf_virt, 0, RGA_CMDBUF_SIZE * 4); + memset(rga->cmdbuf_virt, 0, RGA_CMDBUF_SIZE); rga_cmd_set_src_addr(ctx, src->dma_desc_pa); /* diff --git a/drivers/media/platform/rockchip/rga/rga-hw.h b/drivers/media/platform/rockchip/rga/rga-hw.h index cc6bd7f5b0300..2b8537a5fd0d7 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.h +++ b/drivers/media/platform/rockchip/rga/rga-hw.h @@ -6,7 +6,7 @@ #ifndef __RGA_HW_H__ #define __RGA_HW_H__ -#define RGA_CMDBUF_SIZE 0x20 +#define RGA_CMDBUF_SIZE 0x80 /* Hardware limits */ #define MAX_WIDTH 8192 From 11bb771f3c55b4cef3879e69da4ddecd6085569c Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 15 May 2026 11:59:15 +0200 Subject: [PATCH 0944/2758] firmware: arm_scmi: Fix OOB in scmi_power_name_get() [ Upstream commit f9ef3f66f4b18078e464b7606f9497e4dbeb9905 ] scmi_power_name_get() does not validate the domain number passed by the external caller, which may lead to an out-of-bounds access. Fix this by returning "unknown" for invalid domains, like scmi_reset_name_get() does. Fixes: 76a6550990e296a7 ("firmware: arm_scmi: add initial support for power protocol") Signed-off-by: Geert Uytterhoeven Reviewed-by: Cristian Marussi Link: https://patch.msgid.link/75caae28bdffb55199a0bc6cac5df112a966c608.1778838987.git.geert+renesas@glider.be Signed-off-by: Sudeep Holla Signed-off-by: Sasha Levin --- drivers/firmware/arm_scmi/power.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c index bb5062ab8280e..28ef63a4ecc2e 100644 --- a/drivers/firmware/arm_scmi/power.c +++ b/drivers/firmware/arm_scmi/power.c @@ -204,8 +204,12 @@ scmi_power_name_get(const struct scmi_protocol_handle *ph, u32 domain) { struct scmi_power_info *pi = ph->get_priv(ph); - struct power_dom_info *dom = pi->dom_info + domain; + struct power_dom_info *dom; + + if (domain >= pi->num_domains) + return "unknown"; + dom = pi->dom_info + domain; return dom->name; } From 37d4d0f75a385ae1d5aa1ba2b02c15e47a774a14 Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Fri, 15 May 2026 16:22:38 +0300 Subject: [PATCH 0945/2758] arm64: dts: qcom: eliza-mtp: Fix the debug UART index [ Upstream commit 179d11c1c0cfaddcfc984edc3c1863ab600f679f ] The Eliza MTP debug UART is QUPv3 WRAP2 SE5. The existing DTS labels it as uart14, but the serial-engine index for this block is actually 13. Rename the SoC UART label and pinctrl state to uart13 and update the MTP alias and node reference accordingly. Fixes: af20af39fc09 ("arm64: dts: qcom: Introduce Eliza Soc base dtsi") Fixes: 2a5d4fc6f3f7 ("arm64: dts: qcom: eliza: Enable Eliza MTP board support") Reviewed-by: Konrad Dybcio Signed-off-by: Abel Vesa Link: https://lore.kernel.org/r/20260515-eliza-dts-fix-debug-uart-and-more-support-v2-1-5ad3da81b9d3@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/eliza-mtp.dts | 4 ++-- arch/arm64/boot/dts/qcom/eliza.dtsi | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/eliza-mtp.dts b/arch/arm64/boot/dts/qcom/eliza-mtp.dts index 90f629800cb02..4708df8eb4a40 100644 --- a/arch/arm64/boot/dts/qcom/eliza-mtp.dts +++ b/arch/arm64/boot/dts/qcom/eliza-mtp.dts @@ -15,7 +15,7 @@ chassis-type = "handset"; aliases { - serial0 = &uart14; + serial0 = &uart13; }; chosen { @@ -382,7 +382,7 @@ <118 1>; /* NFC Secure I/O */ }; -&uart14 { +&uart13 { compatible = "qcom,geni-debug-uart"; status = "okay"; diff --git a/arch/arm64/boot/dts/qcom/eliza.dtsi b/arch/arm64/boot/dts/qcom/eliza.dtsi index 7e97361a5dc58..9edfee5e1dde5 100644 --- a/arch/arm64/boot/dts/qcom/eliza.dtsi +++ b/arch/arm64/boot/dts/qcom/eliza.dtsi @@ -639,7 +639,7 @@ #size-cells = <2>; ranges; - uart14: serial@894000 { + uart13: serial@894000 { compatible = "qcom,geni-uart"; reg = <0x0 0x00894000 0x0 0x4000>; @@ -655,7 +655,7 @@ interconnect-names = "qup-core", "qup-config"; - pinctrl-0 = <&qup_uart14_default>; + pinctrl-0 = <&qup_uart13_default>; pinctrl-names = "default"; status = "disabled"; @@ -1338,7 +1338,7 @@ gpio-ranges = <&tlmm 0 0 184>; wakeup-parent = <&pdc>; - qup_uart14_default: qup-uart14-default-state { + qup_uart13_default: qup-uart13-default-state { /* TX, RX */ pins = "gpio18", "gpio19"; function = "qup2_se5"; From 2f0854496cd652efb638604fdaa68f0470f8ef36 Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Thu, 16 Apr 2026 17:29:20 +0530 Subject: [PATCH 0946/2758] arm64: dts: qcom: kaanapali: Add power-domain and iface clk for ice node [ Upstream commit 11b48f6d5ed505ced9cd3645d6615279198a7a54 ] Qualcomm in-line crypto engine (ICE) platform driver specifies and votes for its own resources. Before accessing ICE hardware during probe, to avoid potential unclocked register access issues (when clk_ignore_unused is not passed on the kernel command line), in addition to the 'core' clock the 'iface' clock should also be turned on by the driver. This can only be done if the GCC_UFS_PHY_GDSC power domain is enabled. Specify both the GCC_UFS_PHY_GDSC power domain and the 'iface' clock in the ICE node for kaanapali. Fixes: 2eeb5767d53f4 ("arm64: dts: qcom: Introduce Kaanapali SoC") Reviewed-by: Konrad Dybcio Reviewed-by: Kuldeep Singh Signed-off-by: Harshal Dev Link: https://lore.kernel.org/r/20260416-qcom_ice_power_and_clk_vote-v5-3-5ccf5d7e2846@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/kaanapali.dtsi | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/kaanapali.dtsi b/arch/arm64/boot/dts/qcom/kaanapali.dtsi index 7cc326aa1a1aa..14e362a4899b6 100644 --- a/arch/arm64/boot/dts/qcom/kaanapali.dtsi +++ b/arch/arm64/boot/dts/qcom/kaanapali.dtsi @@ -2538,7 +2538,11 @@ "qcom,inline-crypto-engine"; reg = <0x0 0x01d88000 0x0 0x18000>; - clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; + clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>, + <&gcc GCC_UFS_PHY_AHB_CLK>; + clock-names = "core", + "iface"; + power-domains = <&gcc GCC_UFS_PHY_GDSC>; }; tcsr_mutex: hwlock@1f40000 { From 6b3443bf37a86cdf0c59789e862ab6ebe1d10e26 Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Thu, 16 Apr 2026 17:29:21 +0530 Subject: [PATCH 0947/2758] arm64: dts: qcom: lemans: Add power-domain and iface clk for ice node [ Upstream commit 04566e287b35fde9fd129db5fdf6a96e336af55c ] Qualcomm in-line crypto engine (ICE) platform driver specifies and votes for its own resources. Before accessing ICE hardware during probe, to avoid potential unclocked register access issues (when clk_ignore_unused is not passed on the kernel command line), in addition to the 'core' clock the 'iface' clock should also be turned on by the driver. This can only be done if the UFS_PHY_GDSC power domain is enabled. Specify both the UFS_PHY_GDSC power domain and the 'iface' clock in the ICE node for lemans. Fixes: 96272ba7103d4 ("arm64: dts: qcom: sa8775p: enable the inline crypto engine") Reviewed-by: Konrad Dybcio Reviewed-by: Kuldeep Singh Signed-off-by: Harshal Dev Reviewed-by: Bartosz Golaszewski Link: https://lore.kernel.org/r/20260416-qcom_ice_power_and_clk_vote-v5-4-5ccf5d7e2846@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/lemans.dtsi | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/lemans.dtsi b/arch/arm64/boot/dts/qcom/lemans.dtsi index 5dd271e7108d7..bf3b13be3af0a 100644 --- a/arch/arm64/boot/dts/qcom/lemans.dtsi +++ b/arch/arm64/boot/dts/qcom/lemans.dtsi @@ -3104,7 +3104,11 @@ compatible = "qcom,sa8775p-inline-crypto-engine", "qcom,inline-crypto-engine"; reg = <0x0 0x01d88000 0x0 0x18000>; - clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; + clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>, + <&gcc GCC_UFS_PHY_AHB_CLK>; + clock-names = "core", + "iface"; + power-domains = <&gcc UFS_PHY_GDSC>; }; cryptobam: dma-controller@1dc4000 { From 0f3146ce9a6df80c101c34e3071950a8a1d8030f Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Thu, 16 Apr 2026 17:29:22 +0530 Subject: [PATCH 0948/2758] arm64: dts: qcom: monaco: Add power-domain and iface clk for ice node [ Upstream commit 68d5d9701a7ab1b1f9c76feaa3a24ca716f03f0b ] Qualcomm in-line crypto engine (ICE) platform driver specifies and votes for its own resources. Before accessing ICE hardware during probe, to avoid potential unclocked register access issues (when clk_ignore_unused is not passed on the kernel command line), in addition to the 'core' clock the 'iface' clock should also be turned on by the driver. This can only be done if the GCC_UFS_PHY_GDSC power domain is enabled. Specify both the GCC_UFS_PHY_GDSC power domain and the 'iface' clock in the ICE node for monaco. Fixes: cc9d29aad876d ("arm64: dts: qcom: qcs8300: enable the inline crypto engine") Reviewed-by: Konrad Dybcio Reviewed-by: Kuldeep Singh Signed-off-by: Harshal Dev Link: https://lore.kernel.org/r/20260416-qcom_ice_power_and_clk_vote-v5-5-5ccf5d7e2846@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/monaco.dtsi | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/monaco.dtsi b/arch/arm64/boot/dts/qcom/monaco.dtsi index 7b1d57460f1e6..fa13210fc539a 100644 --- a/arch/arm64/boot/dts/qcom/monaco.dtsi +++ b/arch/arm64/boot/dts/qcom/monaco.dtsi @@ -2737,7 +2737,11 @@ compatible = "qcom,qcs8300-inline-crypto-engine", "qcom,inline-crypto-engine"; reg = <0x0 0x01d88000 0x0 0x18000>; - clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; + clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>, + <&gcc GCC_UFS_PHY_AHB_CLK>; + clock-names = "core", + "iface"; + power-domains = <&gcc GCC_UFS_PHY_GDSC>; }; crypto: crypto@1dfa000 { From 3f93ebf4cba256763b133c71cadc8044e1dde160 Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Thu, 16 Apr 2026 17:29:23 +0530 Subject: [PATCH 0949/2758] arm64: dts: qcom: sc7180: Add power-domain and iface clk for ice node [ Upstream commit 7cd7271ac525e4eadd22734f418219f247638f43 ] Qualcomm in-line crypto engine (ICE) platform driver specifies and votes for its own resources. Before accessing ICE hardware during probe, to avoid potential unclocked register access issues (when clk_ignore_unused is not passed on the kernel command line), in addition to the 'core' clock the 'iface' clock should also be turned on by the driver. This can only be done if the UFS_PHY_GDSC power domain is enabled. Specify both the UFS_PHY_GDSC power domain and the 'iface' clock in the ICE node for sc7180. Fixes: 858536d9dc946 ("arm64: dts: qcom: sc7180: Add UFS nodes") Reviewed-by: Konrad Dybcio Reviewed-by: Kuldeep Singh Signed-off-by: Harshal Dev Link: https://lore.kernel.org/r/20260416-qcom_ice_power_and_clk_vote-v5-6-5ccf5d7e2846@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sc7180.dtsi | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sc7180.dtsi b/arch/arm64/boot/dts/qcom/sc7180.dtsi index a4b17564469ee..94a699cc26889 100644 --- a/arch/arm64/boot/dts/qcom/sc7180.dtsi +++ b/arch/arm64/boot/dts/qcom/sc7180.dtsi @@ -1605,7 +1605,11 @@ compatible = "qcom,sc7180-inline-crypto-engine", "qcom,inline-crypto-engine"; reg = <0 0x01d90000 0 0x8000>; - clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; + clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>, + <&gcc GCC_UFS_PHY_AHB_CLK>; + clock-names = "core", + "iface"; + power-domains = <&gcc UFS_PHY_GDSC>; }; ipa: ipa@1e40000 { From abfa2bad440e2eeab1ba189daf1dffc98d66d72a Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Thu, 16 Apr 2026 17:29:24 +0530 Subject: [PATCH 0950/2758] arm64: dts: qcom: kodiak: Add power-domain and iface clk for ice node [ Upstream commit cca53c338ad87edc4b46d2d82730fd8ca01a164f ] Qualcomm in-line crypto engine (ICE) platform driver specifies and votes for its own resources. Before accessing ICE hardware during probe, to avoid potential unclocked register access issues (when clk_ignore_unused is not passed on the kernel command line), in addition to the 'core' clock the 'iface' clock should also be turned on by the driver. This can only be done if the GCC_UFS_PHY_GDSC power domain is enabled. Specify both the GCC_UFS_PHY_GDSC power domain and the 'iface' clock in the ICE node for kodiak. Fixes: dfd5ee7b34bb7 ("arm64: dts: qcom: sc7280: Add inline crypto engine") Reviewed-by: Konrad Dybcio Reviewed-by: Kuldeep Singh Tested-by: Kuldeep Singh Signed-off-by: Harshal Dev Link: https://lore.kernel.org/r/20260416-qcom_ice_power_and_clk_vote-v5-7-5ccf5d7e2846@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/kodiak.dtsi | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/kodiak.dtsi b/arch/arm64/boot/dts/qcom/kodiak.dtsi index fdde9f065199e..522b2b61ce7d2 100644 --- a/arch/arm64/boot/dts/qcom/kodiak.dtsi +++ b/arch/arm64/boot/dts/qcom/kodiak.dtsi @@ -2579,7 +2579,11 @@ compatible = "qcom,sc7280-inline-crypto-engine", "qcom,inline-crypto-engine"; reg = <0 0x01d88000 0 0x18000>; - clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; + clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>, + <&gcc GCC_UFS_PHY_AHB_CLK>; + clock-names = "core", + "iface"; + power-domains = <&gcc GCC_UFS_PHY_GDSC>; }; cryptobam: dma-controller@1dc4000 { From 38ef0ab2a49e498ec555945bd0a1beb89039fee3 Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Thu, 16 Apr 2026 17:29:25 +0530 Subject: [PATCH 0951/2758] arm64: dts: qcom: sm8450: Add power-domain and iface clk for ice node [ Upstream commit 3a5cb1ccbfb3141862b28f24cd5050083233aae7 ] Qualcomm in-line crypto engine (ICE) platform driver specifies and votes for its own resources. Before accessing ICE hardware during probe, to avoid potential unclocked register access issues (when clk_ignore_unused is not passed on the kernel command line), in addition to the 'core' clock the 'iface' clock should also be turned on by the driver. This can only be done if the UFS_PHY_GDSC power domain is enabled. Specify both the UFS_PHY_GDSC power domain and the 'iface' clock in the ICE node for sm8450. Fixes: 86b0aef435851 ("arm64: dts: qcom: sm8450: Use standalone ICE node for UFS") Reviewed-by: Konrad Dybcio Reviewed-by: Kuldeep Singh Signed-off-by: Harshal Dev Link: https://lore.kernel.org/r/20260416-qcom_ice_power_and_clk_vote-v5-8-5ccf5d7e2846@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sm8450.dtsi | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sm8450.dtsi b/arch/arm64/boot/dts/qcom/sm8450.dtsi index e0c37ce3042a2..47b028259d910 100644 --- a/arch/arm64/boot/dts/qcom/sm8450.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8450.dtsi @@ -5374,7 +5374,11 @@ compatible = "qcom,sm8450-inline-crypto-engine", "qcom,inline-crypto-engine"; reg = <0 0x01d88000 0 0x18000>; - clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; + clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>, + <&gcc GCC_UFS_PHY_AHB_CLK>; + clock-names = "core", + "iface"; + power-domains = <&gcc UFS_PHY_GDSC>; }; cryptobam: dma-controller@1dc4000 { From e873e3cad2a96a2e8fc915c6fa2b3153440dae46 Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Thu, 16 Apr 2026 17:29:26 +0530 Subject: [PATCH 0952/2758] arm64: dts: qcom: sm8550: Add power-domain and iface clk for ice node [ Upstream commit 52696dbbe7bbe0c8fc8c17133ffb5133b8cf37a6 ] Qualcomm in-line crypto engine (ICE) platform driver specifies and votes for its own resources. Before accessing ICE hardware during probe, to avoid potential unclocked register access issues (when clk_ignore_unused is not passed on the kernel command line), in addition to the 'core' clock the 'iface' clock should also be turned on by the driver. This can only be done if the UFS_PHY_GDSC power domain is enabled. Specify both the UFS_PHY_GDSC power domain and the 'iface' clock in the ICE node for sm8550. Fixes: b8630c48b43fc ("arm64: dts: qcom: sm8550: Add the Inline Crypto Engine node") Reviewed-by: Konrad Dybcio Reviewed-by: Kuldeep Singh Signed-off-by: Harshal Dev Link: https://lore.kernel.org/r/20260416-qcom_ice_power_and_clk_vote-v5-9-5ccf5d7e2846@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sm8550.dtsi | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sm8550.dtsi b/arch/arm64/boot/dts/qcom/sm8550.dtsi index 912525e9bca6f..fe46a5d41fe07 100644 --- a/arch/arm64/boot/dts/qcom/sm8550.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8550.dtsi @@ -2465,7 +2465,11 @@ "qcom,inline-crypto-engine"; reg = <0 0x01d88000 0 0x18000>; - clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; + clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>, + <&gcc GCC_UFS_PHY_AHB_CLK>; + clock-names = "core", + "iface"; + power-domains = <&gcc UFS_PHY_GDSC>; }; tcsr_mutex: hwlock@1f40000 { From dd0004304a6261ff2d43bce2c420582924ea6427 Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Thu, 16 Apr 2026 17:29:27 +0530 Subject: [PATCH 0953/2758] arm64: dts: qcom: sm8650: Add power-domain and iface clk for ice node [ Upstream commit c62b084d5d1564f808408a2f7d4c514e57cd4106 ] Qualcomm in-line crypto engine (ICE) platform driver specifies and votes for its own resources. Before accessing ICE hardware during probe, to avoid potential unclocked register access issues (when clk_ignore_unused is not passed on the kernel command line), in addition to the 'core' clock the 'iface' clock should also be turned on by the driver. This can only be done if the UFS_PHY_GDSC power domain is enabled. Specify both the UFS_PHY_GDSC power domain and the 'iface' clock in the ICE node for sm8650. Fixes: 10e0246712951 ("arm64: dts: qcom: sm8650: add interconnect dependent device nodes") Reviewed-by: Konrad Dybcio Reviewed-by: Kuldeep Singh Signed-off-by: Harshal Dev Link: https://lore.kernel.org/r/20260416-qcom_ice_power_and_clk_vote-v5-10-5ccf5d7e2846@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sm8650.dtsi | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sm8650.dtsi b/arch/arm64/boot/dts/qcom/sm8650.dtsi index 1604bc8cff373..e2d98cf6adca8 100644 --- a/arch/arm64/boot/dts/qcom/sm8650.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8650.dtsi @@ -4081,7 +4081,11 @@ "qcom,inline-crypto-engine"; reg = <0 0x01d88000 0 0x18000>; - clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; + clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>, + <&gcc GCC_UFS_PHY_AHB_CLK>; + clock-names = "core", + "iface"; + power-domains = <&gcc UFS_PHY_GDSC>; }; cryptobam: dma-controller@1dc4000 { From dbbb33fe49fac0a88f0cc83ad0114339a21b4ebe Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Thu, 16 Apr 2026 17:29:28 +0530 Subject: [PATCH 0954/2758] arm64: dts: qcom: sm8750: Add power-domain and iface clk for ice node [ Upstream commit 081ac792f0ea6d27a4b130c70cfd7544efee8137 ] Qualcomm in-line crypto engine (ICE) platform driver specifies and votes for its own resources. Before accessing ICE hardware during probe, to avoid potential unclocked register access issues (when clk_ignore_unused is not passed on the kernel command line), in addition to the 'core' clock the 'iface' clock should also be turned on by the driver. This can only be done if the GCC_UFS_PHY_GDSC power domain is enabled. Specify both the GCC_UFS_PHY_GDSC power domain and the 'iface' clock in the ICE node for sm8750. Fixes: b1dac789c650a ("arm64: dts: qcom: sm8750: Add ICE nodes") Reviewed-by: Konrad Dybcio Reviewed-by: Kuldeep Singh Signed-off-by: Harshal Dev Link: https://lore.kernel.org/r/20260416-qcom_ice_power_and_clk_vote-v5-11-5ccf5d7e2846@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sm8750.dtsi | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sm8750.dtsi b/arch/arm64/boot/dts/qcom/sm8750.dtsi index 320aec62e462d..2d2f029c2d6fe 100644 --- a/arch/arm64/boot/dts/qcom/sm8750.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8750.dtsi @@ -2086,7 +2086,11 @@ "qcom,inline-crypto-engine"; reg = <0x0 0x01d88000 0x0 0x18000>; - clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; + clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>, + <&gcc GCC_UFS_PHY_AHB_CLK>; + clock-names = "core", + "iface"; + power-domains = <&gcc GCC_UFS_PHY_GDSC>; }; cryptobam: dma-controller@1dc4000 { From cf334620036ad2250e008c7e7bb6646a3938b7b0 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 30 Apr 2026 12:33:50 +0800 Subject: [PATCH 0955/2758] tracing: Bound synthetic-field strings with seq_buf [ Upstream commit f07883450eb14d1cf020b55d9f3a7ec5683bcd26 ] The synthetic field helpers build a prefixed synthetic variable name and a generated hist command in fixed MAX_FILTER_STR_VAL buffers. The current code appends those strings with raw strcat(), so long key lists, field names, or saved filters can run past the end of the staging buffers. Build both strings with seq_buf and propagate -E2BIG if either the synthetic variable name or the generated command exceeds MAX_FILTER_STR_VAL. This keeps the existing tracing-side limit while using the helper intended for bounded command construction. Cc: Mathieu Desnoyers Cc: Tom Zanussi Link: https://patch.msgid.link/20260430043350.57928-1-pengpeng@iscas.ac.cn Fixes: 02205a6752f2 ("tracing: Add support for 'field variables'") Acked-by: Masami Hiramatsu (Google) Reviewed-by: Tom Zanussi Signed-off-by: Pengpeng Hou [ sdr: Moved struct seq_buf *s for upside-down x-mas tree formatting ] Signed-off-by: Steven Rostedt Signed-off-by: Sasha Levin --- kernel/trace/trace_events_hist.c | 41 ++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c index eb2c2bc8bc3d5..9701650c89b2f 100644 --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -2967,13 +2968,22 @@ find_synthetic_field_var(struct hist_trigger_data *target_hist_data, { struct hist_field *event_var; char *synthetic_name; + struct seq_buf s; synthetic_name = kzalloc(MAX_FILTER_STR_VAL, GFP_KERNEL); if (!synthetic_name) return ERR_PTR(-ENOMEM); - strcpy(synthetic_name, "synthetic_"); - strcat(synthetic_name, field_name); + seq_buf_init(&s, synthetic_name, MAX_FILTER_STR_VAL); + seq_buf_printf(&s, "synthetic_%s", field_name); + + /* Terminate synthetic_name with a NUL. */ + seq_buf_str(&s); + + if (seq_buf_has_overflowed(&s)) { + kfree(synthetic_name); + return ERR_PTR(-E2BIG); + } event_var = find_event_var(target_hist_data, system, event_name, synthetic_name); @@ -3019,6 +3029,7 @@ create_field_var_hist(struct hist_trigger_data *target_hist_data, struct hist_field *key_field; struct hist_field *event_var; char *saved_filter; + struct seq_buf s; char *cmd; int ret; @@ -3063,28 +3074,34 @@ create_field_var_hist(struct hist_trigger_data *target_hist_data, return ERR_PTR(-ENOMEM); } + seq_buf_init(&s, cmd, MAX_FILTER_STR_VAL); + /* Use the same keys as the compatible histogram */ - strcat(cmd, "keys="); + seq_buf_puts(&s, "keys="); for_each_hist_key_field(i, hist_data) { key_field = hist_data->fields[i]; if (!first) - strcat(cmd, ","); - strcat(cmd, key_field->field->name); + seq_buf_putc(&s, ','); + seq_buf_puts(&s, key_field->field->name); first = false; } /* Create the synthetic field variable specification */ - strcat(cmd, ":synthetic_"); - strcat(cmd, field_name); - strcat(cmd, "="); - strcat(cmd, field_name); + seq_buf_printf(&s, ":synthetic_%s=%s", field_name, field_name); /* Use the same filter as the compatible histogram */ saved_filter = find_trigger_filter(hist_data, file); - if (saved_filter) { - strcat(cmd, " if "); - strcat(cmd, saved_filter); + if (saved_filter) + seq_buf_printf(&s, " if %s", saved_filter); + + /* Terminate cmd with a NUL. */ + seq_buf_str(&s); + + if (seq_buf_has_overflowed(&s)) { + kfree(cmd); + kfree(var_hist); + return ERR_PTR(-E2BIG); } var_hist->cmd = kstrdup(cmd, GFP_KERNEL); From 6ead900324537b52e09a7f1d3bfa5e82574d2c8e Mon Sep 17 00:00:00 2001 From: Ritesh Kumar Date: Wed, 28 Jan 2026 17:18:50 +0530 Subject: [PATCH 0956/2758] arm64: dts: qcom: lemans: Add eDP ref clock for eDP PHYs [ Upstream commit 4bd073e00fd79c0aead74ad64ade48c904221245 ] The eDP PHY nodes on lemans were missing the reference clock voting. This initially went unnoticed because the clock was implicitly enabled by the UFS PHY driver, and the eDP PHY happened to rely on that. After commit 77d2fa54a945 ("scsi: ufs: qcom : Refactor phy_power_on/off calls"), the UFS driver no longer keeps the reference clock enabled. As a result, the eDP PHY fails to power on. To fix this, add eDP reference clock for eDP PHYs on lemans chipset ensuring reference clock is enabled. Fixes: e1e3e5673f8d7 ("arm64: dts: qcom: sa8775p: add DisplayPort device nodes") Signed-off-by: Ritesh Kumar Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260128114853.2543416-3-quic_riteshk@quicinc.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/lemans.dtsi | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/lemans.dtsi b/arch/arm64/boot/dts/qcom/lemans.dtsi index bf3b13be3af0a..3cd8d68cc4f91 100644 --- a/arch/arm64/boot/dts/qcom/lemans.dtsi +++ b/arch/arm64/boot/dts/qcom/lemans.dtsi @@ -5658,9 +5658,11 @@ <0x0 0x0aec2000 0x0 0x1c8>; clocks = <&dispcc0 MDSS_DISP_CC_MDSS_DPTX0_AUX_CLK>, - <&dispcc0 MDSS_DISP_CC_MDSS_AHB_CLK>; + <&dispcc0 MDSS_DISP_CC_MDSS_AHB_CLK>, + <&gcc GCC_EDP_REF_CLKREF_EN>; clock-names = "aux", - "cfg_ahb"; + "cfg_ahb", + "ref"; #clock-cells = <1>; #phy-cells = <0>; @@ -5677,9 +5679,11 @@ <0x0 0x0aec5000 0x0 0x1c8>; clocks = <&dispcc0 MDSS_DISP_CC_MDSS_DPTX1_AUX_CLK>, - <&dispcc0 MDSS_DISP_CC_MDSS_AHB_CLK>; + <&dispcc0 MDSS_DISP_CC_MDSS_AHB_CLK>, + <&gcc GCC_EDP_REF_CLKREF_EN>; clock-names = "aux", - "cfg_ahb"; + "cfg_ahb", + "ref"; #clock-cells = <1>; #phy-cells = <0>; From 2dbb61fd473bcce82ec0aea4dbe07760e862b361 Mon Sep 17 00:00:00 2001 From: Gopikrishna Garmidi Date: Mon, 18 May 2026 02:52:53 -0700 Subject: [PATCH 0957/2758] arm64: dts: qcom: glymur: Fix wrong interrupt number for i2c19 [ Upstream commit ecabfe832b817bd1c1fdb8841d7bc706bf621ef1 ] The i2c19 node at 0x88c000 uses GIC SPI 584, but that interrupt belongs to the neighboring i2c18/spi18 node at 0x888000. The correct interrupt for i2c19 is GIC SPI 585, as used by its sibling nodes spi19 and uart19 which share the same register base and clock. Fixes: 41b6e8db400c ("arm64: dts: qcom: Introduce Glymur base dtsi") Signed-off-by: Gopikrishna Garmidi Reviewed-by: Abel Vesa Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260518-glymur-fix-i2c19-irq-v1-1-7d5968bd9b2b@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/glymur.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/glymur.dtsi b/arch/arm64/boot/dts/qcom/glymur.dtsi index 780708a9d8d45..63df60b7e010e 100644 --- a/arch/arm64/boot/dts/qcom/glymur.dtsi +++ b/arch/arm64/boot/dts/qcom/glymur.dtsi @@ -968,7 +968,7 @@ i2c19: i2c@88c000 { compatible = "qcom,geni-i2c"; reg = <0x0 0x0088c000 0x0 0x4000>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP2_S3_CLK>; clock-names = "se"; interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS From 4b23284c347dbe19104ac879c7fd89b213d13401 Mon Sep 17 00:00:00 2001 From: Joel Selvaraj Date: Wed, 29 Apr 2026 15:16:53 +0200 Subject: [PATCH 0958/2758] arm64: dts: qcom: sdm845-xiaomi-beryllium: Correct IPA FW path [ Upstream commit f3cd85f60c5eb2d817af6c62465018dd941ce4f3 ] The path was accidentally reverted back to old while refactoring of the device-tree. Fixes: 5bde31dc7b17 ("arm64: dts: qcom: sdm845-xiaomi-beryllium: Add placeholders and sort") Signed-off-by: Joel Selvaraj Signed-off-by: David Heidelberg Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260429-beryllium-ipa-fix-v1-1-816326ba9047@ixit.cz Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sdm845-xiaomi-beryllium-common.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sdm845-xiaomi-beryllium-common.dtsi b/arch/arm64/boot/dts/qcom/sdm845-xiaomi-beryllium-common.dtsi index 1298485c42142..22354968aeaa3 100644 --- a/arch/arm64/boot/dts/qcom/sdm845-xiaomi-beryllium-common.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm845-xiaomi-beryllium-common.dtsi @@ -271,7 +271,7 @@ &ipa { qcom,gsi-loader = "self"; memory-region = <&ipa_fw_mem>; - firmware-name = "qcom/sdm845/beryllium/ipa_fws.mbn"; + firmware-name = "qcom/sdm845/Xiaomi/beryllium/ipa_fws.mbn"; status = "okay"; }; From d60a8ec640b318b916d71fe3f2f78fba3a827e23 Mon Sep 17 00:00:00 2001 From: Baokun Li Date: Thu, 21 May 2026 17:50:15 +0800 Subject: [PATCH 0959/2758] writeback: drop now-unnecessary rcu_barrier() in cgroup_writeback_umount() [ Upstream commit e90a6d668e26e00a72df2d09c173b563468f09c9 ] Commit e1b849cfa6b6 ("writeback: Avoid contention on wb->list_lock when switching inodes") replaced the queue_rcu_work() based scheduling of inode wb switches with a plain queue_work(). Since then no switcher goes through call_rcu(), so rcu_barrier() in cgroup_writeback_umount() has no callbacks of its own to wait for. It still drains unrelated call_rcu() callbacks from other subsystems on busy systems, which incidentally slows umount down; drop it. Fixes: e1b849cfa6b6 ("writeback: Avoid contention on wb->list_lock when switching inodes") Reviewed-by: Jan Kara Signed-off-by: Baokun Li Link: https://patch.msgid.link/20260521095016.2791354-3-libaokun@linux.alibaba.com Acked-by: Tejun Heo Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/fs-writeback.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 6766de9f9d75b..325a30cc35bfb 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -1248,11 +1248,6 @@ void cgroup_writeback_umount(struct super_block *sb) * will then drain it. */ synchronize_rcu(); - /* - * Use rcu_barrier() to wait for all pending callbacks to - * ensure that all in-flight wb switches are in the workqueue. - */ - rcu_barrier(); flush_workqueue(isw_wq); } } From 0dcc7fec47da3a4cb9589cfa492e5a02a19d43fa Mon Sep 17 00:00:00 2001 From: Conor Kotwasinski Date: Thu, 16 Apr 2026 09:43:15 -0400 Subject: [PATCH 0960/2758] kernfs: fix suspicious RCU usage in kernfs_put() [ Upstream commit 0fdde3f2aeadcf8d090ee3edee0aad73fb91f690 ] Commit 741c10b096bc ("kernfs: Use RCU to access kernfs_node::name.") converted the WARN_ONCE() in kernfs_put() to read kn->name and parent->name via rcu_dereference(), but kernfs_put() has callers that hold neither kernfs_rwsem nor the RCU read lock. The inode eviction path driven by memory reclaim is one such case: kernfs_put+0x53/0x60 fs/kernfs/dir.c:602 evict+0x3c2/0xad0 fs/inode.c:846 iput_final fs/inode.c:1966 [inline] iput.part.0+0x605/0xf50 fs/inode.c:2015 iput+0x35/0x40 fs/inode.c:1981 dentry_unlink_inode+0x2a1/0x490 fs/dcache.c:467 __dentry_kill+0x1d0/0x600 fs/dcache.c:670 shrink_dentry_list+0x180/0x5e0 fs/dcache.c:1174 prune_dcache_sb+0xea/0x150 fs/dcache.c:1256 super_cache_scan+0x328/0x550 fs/super.c:223 ... kswapd+0x556/0xba0 mm/vmscan.c:7343 lockdep complains with "suspicious RCU usage" whenever the WARN fires from such a context. Wrap the rcu_dereference() calls in an RCU read-side critical section. Gate on the active-ref check so the lock is only taken when the WARN is about to fire. Note that this does not address the underlying imbalance in kn->active that triggers the WARN. Fixes: 741c10b096bc ("kernfs: Use RCU to access kernfs_node::name.") Reported-by: syzbot+0dfe499ea713e0a15bec@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=0dfe499ea713e0a15bec Signed-off-by: Conor Kotwasinski Acked-by: Tejun Heo Link: https://patch.msgid.link/20260416134315.1474726-1-conorkotwasinski2024@u.northwestern.edu Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- fs/kernfs/dir.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 4f9ade82b08ab..e88b71607f1e5 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -597,10 +597,13 @@ void kernfs_put(struct kernfs_node *kn) */ parent = kernfs_parent(kn); - WARN_ONCE(atomic_read(&kn->active) != KN_DEACTIVATED_BIAS, - "kernfs_put: %s/%s: released with incorrect active_ref %d\n", - parent ? rcu_dereference(parent->name) : "", - rcu_dereference(kn->name), atomic_read(&kn->active)); + if (atomic_read(&kn->active) != KN_DEACTIVATED_BIAS) { + guard(rcu)(); + WARN_ONCE(1, + "kernfs_put: %s/%s: released with incorrect active_ref %d\n", + parent ? rcu_dereference(parent->name) : "", + rcu_dereference(kn->name), atomic_read(&kn->active)); + } if (kernfs_type(kn) == KERNFS_LINK) kernfs_put(kn->symlink.target_kn); From f3d760cab86c7b2274d86f5aa5e32175b20bc583 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Thu, 14 May 2026 22:14:55 +0500 Subject: [PATCH 0961/2758] device property: fix fwnode reference leak in fwnode_graph_get_endpoint_by_id() [ Upstream commit 9582485a65eacfd7245ec7f0a9d7e2c34749d669 ] When called with FWNODE_GRAPH_ENDPOINT_NEXT, the function walks every endpoint under the requested port and, for any endpoint whose ID is greater than or equal to the requested one, may store a fwnode reference in best_ep via fwnode_handle_get(). If a later iteration finds an exact-ID match, the function returns that endpoint directly without dropping the reference held by best_ep, leaking it. Drop the saved candidate before returning the exact-match endpoint. This affects callers that use FWNODE_GRAPH_ENDPOINT_NEXT to ask for the next endpoint with ID >= the requested one (used by a number of media drivers, e.g. imx7/8, sun6i CSI, omap3isp, xilinx-csi2, stm32-csi). Each leak retains a fwnode reference until reboot/unbind. Fixes: 0fcc2bdc8aff ("device property: Add fwnode_graph_get_endpoint_by_id()") Signed-off-by: Stepan Ionichev Link: https://patch.msgid.link/20260514171455.27271-1-sozdayvek@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/base/property.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/base/property.c b/drivers/base/property.c index 8e0148a37fffb..e08eadd66f4f9 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -1277,8 +1277,10 @@ fwnode_graph_get_endpoint_by_id(const struct fwnode_handle *fwnode, if (fwnode_ep.port != port) continue; - if (fwnode_ep.id == endpoint) + if (fwnode_ep.id == endpoint) { + fwnode_handle_put(best_ep); return ep; + } if (!endpoint_next) continue; From ce052e4fa3bfe08e365ab627c6c8ab866c9ae44c Mon Sep 17 00:00:00 2001 From: Zhang Yuwei Date: Fri, 10 Apr 2026 10:44:48 +0800 Subject: [PATCH 0962/2758] driver core: Use mod_delayed_work to prevent lost deferred probe work [ Upstream commit 1137838865bfc9a7cd5869c1dc5c22aa45ec12c8 ] The deferred_probe_timeout_work may be permanently and unexpectedly canceled when deferred_probe_extend_timeout() executes concurrently. Starting with deferred_probe_timeout_work pending, the problem can occur after the following sequence: CPU0 CPU1 deferred_probe_extend_timeout -> cancel_delayed_work() => true deferred_probe_extend_timeout -> cancel_delayed_work() -> __cancel_work() -> try_grab_pending() -> schedule_delayed_work() -> queue_delayed_work_on() (Since the pending bit is grabbed, it just returns without queuing) -> set_work_pool_and_clear_pending() (This __cancel_work() returns false and the work will never be queued again) The root cause is that the WORK_STRUCT_PENDING_BIT of the work_struct is set temporarily in __cancel_work() (via try_grab_pending()). This transient state prevents the work_struct from being successfully queued by another CPU. To fix this, replace the original non-atomic cancel and schedule mechanism with mod_delayed_work(). This ensures the modification is handled atomically and guarantees that the work is not lost. Fixes: 2b28a1a84a0e ("driver core: Extend deferred probe timeout on driver registration") Signed-off-by: Zhang Yuwei Link: https://patch.msgid.link/20260410024448.387231-1-zhangyuwei20@huawei.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/base/dd.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 1dc1e3528043c..3523a5d8ec503 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -323,12 +323,10 @@ void deferred_probe_extend_timeout(void) * If the work hasn't been queued yet or if the work expired, don't * start a new one. */ - if (cancel_delayed_work(&deferred_probe_timeout_work)) { - schedule_delayed_work(&deferred_probe_timeout_work, - driver_deferred_probe_timeout * HZ); + if (mod_delayed_work(system_wq, &deferred_probe_timeout_work, + driver_deferred_probe_timeout)) pr_debug("Extended deferred probe timeout by %d secs\n", driver_deferred_probe_timeout); - } } /** From cc8041e16efdd2990c908e611c16b251ea65ce2e Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Mon, 11 May 2026 17:57:48 +0200 Subject: [PATCH 0963/2758] Revert "treewide: Fix probing of devices in DT overlays" [ Upstream commit aaf08c52df9a19148731d4a3cfd85d98455db901 ] This reverts commit 1a50d9403fb90cbe4dea0ec9fd0351d2ecbd8924. While the commit fixed fw_devlink overlay handling for one case, it broke it for another case. So revert it and redo the fix in a separate patch. Fixes: 1a50d9403fb9 ("treewide: Fix probing of devices in DT overlays") Reported-by: Herve Codina Closes: https://lore.kernel.org/lkml/CAMuHMdXEnSD4rRJ-o90x4OprUacN_rJgyo8x6=9F9rZ+-KzjOg@mail.gmail.com/ Closes: https://lore.kernel.org/all/20240221095137.616d2aaa@bootlin.com/ Closes: https://lore.kernel.org/lkml/20240312151835.29ef62a0@bootlin.com/ Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/lkml/20240411235623.1260061-2-saravanak@google.com/ [Herve: Fix conflicts due to f72e77c33e4b ("device property: Make modifications of fwnode "flags" thread safe")] Signed-off-by: Herve Codina Acked-by: Mark Brown Acked-by: Rob Herring (Arm) Acked-by: Wolfram Sang # for I2C Link: https://patch.msgid.link/20260511155755.34428-2-herve.codina@bootlin.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/bus/imx-weim.c | 6 ------ drivers/i2c/i2c-core-of.c | 5 ----- drivers/of/dynamic.c | 1 - drivers/of/platform.c | 5 ----- drivers/spi/spi.c | 5 ----- 5 files changed, 22 deletions(-) diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c index f735e0462c55e..87070155b0572 100644 --- a/drivers/bus/imx-weim.c +++ b/drivers/bus/imx-weim.c @@ -327,12 +327,6 @@ static int of_weim_notify(struct notifier_block *nb, unsigned long action, "Failed to setup timing for '%pOF'\n", rd->dn); if (!of_node_check_flag(rd->dn, OF_POPULATED)) { - /* - * Clear the flag before adding the device so that - * fw_devlink doesn't skip adding consumers to this - * device. - */ - fwnode_clear_flag(&rd->dn->fwnode, FWNODE_FLAG_NOT_DEVICE); if (!of_platform_device_create(rd->dn, NULL, &pdev->dev)) { dev_err(&pdev->dev, "Failed to create child device '%pOF'\n", diff --git a/drivers/i2c/i2c-core-of.c b/drivers/i2c/i2c-core-of.c index 354a88d0599e3..30b48a428c0be 100644 --- a/drivers/i2c/i2c-core-of.c +++ b/drivers/i2c/i2c-core-of.c @@ -176,11 +176,6 @@ static int of_i2c_notify(struct notifier_block *nb, unsigned long action, return NOTIFY_OK; } - /* - * Clear the flag before adding the device so that fw_devlink - * doesn't skip adding consumers to this device. - */ - fwnode_clear_flag(&rd->dn->fwnode, FWNODE_FLAG_NOT_DEVICE); client = of_i2c_register_device(adap, rd->dn); if (IS_ERR(client)) { dev_err(&adap->dev, "failed to create client for '%pOF'\n", diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index ade288372101b..aa450425ec1e6 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -225,7 +225,6 @@ static void __of_attach_node(struct device_node *np) np->sibling = np->parent->child; np->parent->child = np; of_node_clear_flag(np, OF_DETACHED); - fwnode_set_flag(&np->fwnode, FWNODE_FLAG_NOT_DEVICE); raw_spin_unlock_irqrestore(&devtree_lock, flags); diff --git a/drivers/of/platform.c b/drivers/of/platform.c index a42224f9d1a88..53bca8c6f7810 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -744,11 +744,6 @@ static int of_platform_notify(struct notifier_block *nb, if (of_node_check_flag(rd->dn, OF_POPULATED)) return NOTIFY_OK; - /* - * Clear the flag before adding the device so that fw_devlink - * doesn't skip adding consumers to this device. - */ - fwnode_clear_flag(&rd->dn->fwnode, FWNODE_FLAG_NOT_DEVICE); /* pdev_parent may be NULL when no bus platform device */ pdev_parent = of_find_device_by_node(parent); pdev = of_platform_device_create(rd->dn, NULL, diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 104279858f567..889e1eecc7573 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -5003,11 +5003,6 @@ static int of_spi_notify(struct notifier_block *nb, unsigned long action, return NOTIFY_OK; } - /* - * Clear the flag before adding the device so that fw_devlink - * doesn't skip adding consumers to this device. - */ - fwnode_clear_flag(&rd->dn->fwnode, FWNODE_FLAG_NOT_DEVICE); spi = of_register_spi_device(ctlr, rd->dn); put_device(&ctlr->dev); From baea6404c0e96319ea28c6959dc2c387a5356bd6 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Mon, 11 May 2026 17:57:49 +0200 Subject: [PATCH 0964/2758] of: dynamic: Fix overlayed devices not probing because of fw_devlink [ Upstream commit 81e7c6befa36cecdcbf7244393bd67e8f8c59bf5 ] When an overlay is applied, if the target device has already probed successfully and bound to a device, then some of the fw_devlink logic that ran when the device was probed needs to be rerun. This allows newly created dangling consumers of the overlayed device tree nodes to be moved to become consumers of the target device. [Herve: Add the call to driver_deferred_probe_trigger()] [Herve: Use fwnode_test_flag() to test fwnode flags value] Fixes: 1a50d9403fb9 ("treewide: Fix probing of devices in DT overlays") Reported-by: Herve Codina Closes: https://lore.kernel.org/lkml/CAMuHMdXEnSD4rRJ-o90x4OprUacN_rJgyo8x6=9F9rZ+-KzjOg@mail.gmail.com/ Closes: https://lore.kernel.org/all/20240221095137.616d2aaa@bootlin.com/ Closes: https://lore.kernel.org/lkml/20240312151835.29ef62a0@bootlin.com/ Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/lkml/20240411235623.1260061-3-saravanak@google.com/ [Herve: Rebase on top of recent kernel] Signed-off-by: Herve Codina Tested-by: Kalle Niemi Tested-by: Geert Uytterhoeven Acked-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260511155755.34428-3-herve.codina@bootlin.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/base/core.c | 83 +++++++++++++++++++++++++++++++++++++----- drivers/of/overlay.c | 15 ++++++++ include/linux/fwnode.h | 1 + 3 files changed, 90 insertions(+), 9 deletions(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index bd2ddf2aab505..478aa3fbf1e83 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -235,6 +235,79 @@ static void __fw_devlink_pickup_dangling_consumers(struct fwnode_handle *fwnode, __fw_devlink_pickup_dangling_consumers(child, new_sup); } +static void fw_devlink_pickup_dangling_consumers(struct device *dev) +{ + struct fwnode_handle *child; + + guard(mutex)(&fwnode_link_lock); + + fwnode_for_each_available_child_node(dev->fwnode, child) + __fw_devlink_pickup_dangling_consumers(child, dev->fwnode); + __fw_devlink_link_to_consumers(dev); +} + +/** + * fw_devlink_refresh_fwnode - Recheck the tree under this firmware node + * @fwnode: The fwnode under which the fwnode tree has changed + * + * This function is mainly meant to adjust the supplier/consumer dependencies + * after a fwnode tree overlay has occurred. + */ +void fw_devlink_refresh_fwnode(struct fwnode_handle *fwnode) +{ + struct device *dev; + + /* + * Find the closest ancestor fwnode that has been converted to a device + * that can bind to a driver (bus device). + */ + fwnode_handle_get(fwnode); + do { + if (fwnode_test_flag(fwnode, FWNODE_FLAG_NOT_DEVICE)) + continue; + + dev = get_dev_from_fwnode(fwnode); + if (!dev) + continue; + + if (dev->bus) + break; + + put_device(dev); + } while ((fwnode = fwnode_get_next_parent(fwnode))); + + /* + * If none of the ancestor fwnodes have (yet) been converted to a device + * that can bind to a driver, there's nothing to fix up. + */ + if (!fwnode) + return; + + WARN(device_is_bound(dev) && dev->links.status != DL_DEV_DRIVER_BOUND, + "Don't multithread overlaying and probing the same device!\n"); + + /* + * If the device has already bound to a driver, then we need to redo + * some of the work that was done after the device was bound to a + * driver. If the device hasn't bound to a driver, running things too + * soon would incorrectly pick up consumers that it shouldn't. + */ + if (dev->links.status == DL_DEV_DRIVER_BOUND) { + fw_devlink_pickup_dangling_consumers(dev); + /* + * Some of dangling consumers could have been put previously in + * the deferred probe list due to the unavailability of their + * suppliers. Those consumers have been picked up and some of + * their suppliers links have been updated. Time to re-try their + * probe sequence. + */ + driver_deferred_probe_trigger(); + } + + put_device(dev); + fwnode_handle_put(fwnode); +} + static DEFINE_MUTEX(device_links_lock); DEFINE_STATIC_SRCU(device_links_srcu); @@ -1312,16 +1385,8 @@ void device_links_driver_bound(struct device *dev) * child firmware node. */ if (dev->fwnode && dev->fwnode->dev == dev) { - struct fwnode_handle *child; - fwnode_links_purge_suppliers(dev->fwnode); - - guard(mutex)(&fwnode_link_lock); - - fwnode_for_each_available_child_node(dev->fwnode, child) - __fw_devlink_pickup_dangling_consumers(child, - dev->fwnode); - __fw_devlink_link_to_consumers(dev); + fw_devlink_pickup_dangling_consumers(dev); } device_remove_file(dev, &dev_attr_waiting_for_supplier); diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index c1c5686fc7b19..4e45f3414c2c1 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -185,6 +185,15 @@ static int overlay_notify(struct overlay_changeset *ovcs, return 0; } +static void overlay_fw_devlink_refresh(struct overlay_changeset *ovcs) +{ + for (int i = 0; i < ovcs->count; i++) { + struct device_node *np = ovcs->fragments[i].target; + + fw_devlink_refresh_fwnode(of_fwnode_handle(np)); + } +} + /* * The values of properties in the "/__symbols__" node are paths in * the ovcs->overlay_root. When duplicating the properties, the paths @@ -951,6 +960,12 @@ static int of_overlay_apply(struct overlay_changeset *ovcs, pr_err("overlay apply changeset entry notify error %d\n", ret); /* notify failure is not fatal, continue */ + /* + * Needs to happen after changeset notify to give the listeners a chance + * to finish creating all the devices they need to create. + */ + overlay_fw_devlink_refresh(ovcs); + ret_tmp = overlay_notify(ovcs, OF_OVERLAY_POST_APPLY); if (ret_tmp) if (!ret) diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index 31df7608737e7..5e9cef1e4d448 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -251,6 +251,7 @@ int fwnode_link_add(struct fwnode_handle *con, struct fwnode_handle *sup, u8 flags); void fwnode_links_purge(struct fwnode_handle *fwnode); void fw_devlink_purge_absent_suppliers(struct fwnode_handle *fwnode); +void fw_devlink_refresh_fwnode(struct fwnode_handle *fwnode); bool fw_devlink_is_strict(void); #endif From fccbae82cb294484597edb179e262d8b6cf11f4f Mon Sep 17 00:00:00 2001 From: Aleksander Jan Bajkowski Date: Sat, 16 May 2026 14:26:51 +0200 Subject: [PATCH 0965/2758] crypto: eip93 - fix reset ring register definition [ Upstream commit 09e6b79b8ce388993aec9ac91b1cb2c181c27bd9 ] This patch fixes a descriptor ring reset. This causes a hang in the driver's unload/load sequence. Fixes: 9739f5f93b78 ("crypto: eip93 - Add Inside Secure SafeXcel EIP-93 crypto engine support") Suggested-by: Benjamin Larsson Signed-off-by: Aleksander Jan Bajkowski Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/inside-secure/eip93/eip93-regs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/crypto/inside-secure/eip93/eip93-regs.h b/drivers/crypto/inside-secure/eip93/eip93-regs.h index 96285ca6fbbe8..96d28c6651bdb 100644 --- a/drivers/crypto/inside-secure/eip93/eip93-regs.h +++ b/drivers/crypto/inside-secure/eip93/eip93-regs.h @@ -103,7 +103,7 @@ #define EIP93_PE_TARGET_COMMAND_NO_RDR_MODE FIELD_PREP(EIP93_PE_CONFIG_PE_MODE, 0x2) #define EIP93_PE_TARGET_COMMAND_WITH_RDR_MODE FIELD_PREP(EIP93_PE_CONFIG_PE_MODE, 0x1) #define EIP93_PE_DIRECT_HOST_MODE FIELD_PREP(EIP93_PE_CONFIG_PE_MODE, 0x0) -#define EIP93_PE_CONFIG_RST_RING BIT(2) +#define EIP93_PE_CONFIG_RST_RING BIT(1) #define EIP93_PE_CONFIG_RST_PE BIT(0) #define EIP93_REG_PE_STATUS 0x104 #define EIP93_REG_PE_BUF_THRESH 0x10c From e88dd4d9c7b348549fffcde226f9aca45d894144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Thu, 14 May 2026 10:52:35 -0300 Subject: [PATCH 0966/2758] ASoC: mediatek: mt8189: Fix probe resource cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 5404599c3292a12dfc6a3b604cebb5d51064f553 ] The MT8189 AFE probe assigns reserved memory with of_reserved_mem_device_init(), but only releases that assignment from .remove(). If probe fails after the reserved memory has been assigned, the assignment record is left behind. The probe path also uses pm_runtime_get_sync() without checking its return value. If runtime resume fails, pm_runtime_get_sync() leaves the usage count incremented and the driver continues initialization without the device being resumed. Use pm_runtime_resume_and_get() so resume errors abort probe without leaking a PM usage count. Finally, component registration failure currently jumps to a label that drops a runtime PM reference even though the temporary probe reference was already released. Return the component registration error directly, and do not drop an unmatched PM reference from .remove(). Fixes: 7eb153585598 ("ASoC: mediatek: mt8189: add platform driver") Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260514-asoc-mt8189-probe-cleanup-v1-1-ded733363281@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/mediatek/mt8189/mt8189-afe-pcm.c | 38 ++++++++++++++++------ 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/sound/soc/mediatek/mt8189/mt8189-afe-pcm.c b/sound/soc/mediatek/mt8189/mt8189-afe-pcm.c index 24b0c78815f61..77cf2b604f6ce 100644 --- a/sound/soc/mediatek/mt8189/mt8189-afe-pcm.c +++ b/sound/soc/mediatek/mt8189/mt8189-afe-pcm.c @@ -2351,9 +2351,13 @@ static int mt8189_afe_runtime_resume(struct device *dev) static int mt8189_afe_component_probe(struct snd_soc_component *component) { struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + int ret; /* enable clock for regcache get default value from hw */ - pm_runtime_get_sync(afe->dev); + ret = pm_runtime_resume_and_get(afe->dev); + if (ret) + return dev_err_probe(afe->dev, ret, "failed to resume device\n"); + mtk_afe_add_sub_dai_control(component); pm_runtime_put_sync(afe->dev); @@ -2417,6 +2421,11 @@ static const struct reg_sequence mt8189_cg_patch[] = { { AUDIO_TOP_CON4, 0x361c }, }; +static void mt8189_afe_release_reserved_mem(void *data) +{ + of_reserved_mem_device_release(data); +} + static int mt8189_afe_pcm_dev_probe(struct platform_device *pdev) { int ret, i; @@ -2431,8 +2440,15 @@ static int mt8189_afe_pcm_dev_probe(struct platform_device *pdev) return ret; ret = of_reserved_mem_device_init(dev); - if (ret) + if (ret) { dev_warn(dev, "failed to assign memory region: %d\n", ret); + } else { + ret = devm_add_action_or_reset(dev, + mt8189_afe_release_reserved_mem, + dev); + if (ret) + return ret; + } afe = devm_kzalloc(dev, sizeof(*afe), GFP_KERNEL); if (!afe) @@ -2533,18 +2549,22 @@ static int mt8189_afe_pcm_dev_probe(struct platform_device *pdev) dev_pm_syscore_device(dev, true); /* enable clock for regcache get default value from hw */ - pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); + if (ret) + return dev_err_probe(dev, ret, "failed to resume device\n"); afe->regmap = devm_regmap_init_mmio(dev, afe->base_addr, &mt8189_afe_regmap_config); - if (IS_ERR(afe->regmap)) - return PTR_ERR(afe->regmap); + if (IS_ERR(afe->regmap)) { + ret = PTR_ERR(afe->regmap); + goto err_pm_put; + } ret = regmap_register_patch(afe->regmap, mt8189_cg_patch, ARRAY_SIZE(mt8189_cg_patch)); if (ret < 0) { dev_err(dev, "Failed to apply cg patch\n"); - goto err_pm_disable; + goto err_pm_put; } regmap_read(afe->regmap, AFE_IRQ_MCU_EN, &tmp_reg); @@ -2563,12 +2583,12 @@ static int mt8189_afe_pcm_dev_probe(struct platform_device *pdev) afe->num_dai_drivers); if (ret) { dev_err(dev, "afe component err: %d\n", ret); - goto err_pm_disable; + return ret; } return 0; -err_pm_disable: +err_pm_put: pm_runtime_put_sync(dev); return ret; } @@ -2578,14 +2598,12 @@ static void mt8189_afe_pcm_dev_remove(struct platform_device *pdev) struct mtk_base_afe *afe = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; - pm_runtime_put_sync(dev); if (!pm_runtime_status_suspended(dev)) mt8189_afe_runtime_suspend(dev); mt8189_afe_disable_main_clock(afe); /* disable afe clock */ mt8189_afe_disable_reg_rw_clk(afe); - of_reserved_mem_device_release(dev); } static const struct of_device_id mt8189_afe_pcm_dt_match[] = { From a5fc66d9899c19a7d4b3e86b9483c874381c945d Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 7 May 2026 16:02:57 +0300 Subject: [PATCH 0967/2758] drm/msm/mdss: correct UBWC programming sequences [ Upstream commit 78de481c75c34623f450abf0f4604344f9396593 ] The UBWC registers in the MDSS region are not dependent on the UBWC version (it is an invalid assumption we inherited from the vendor SDE driver). Instead they are dependent only on the MDSS core revision. Rework UBWC programming to follow MDSS revision and to use required (aka encoder) UBWC version instead of the ubwc_dec_version. Fixes: d68db6069a8e ("drm/msm/mdss: convert UBWC setup to use match data") Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/723384/ Link: https://lore.kernel.org/r/20260507-ubwc-rework-v4-1-c19593d20c1d@oss.qualcomm.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/msm_mdss.c | 115 ++++++++++++--------------------- 1 file changed, 41 insertions(+), 74 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_mdss.c b/drivers/gpu/drm/msm/msm_mdss.c index 90c3fa0681a06..4a6acd468bc84 100644 --- a/drivers/gpu/drm/msm/msm_mdss.c +++ b/drivers/gpu/drm/msm/msm_mdss.c @@ -166,22 +166,19 @@ static int _msm_mdss_irq_domain_add(struct msm_mdss *msm_mdss) return 0; } -static void msm_mdss_setup_ubwc_dec_20(struct msm_mdss *msm_mdss) +static void msm_mdss_4x_setup_ubwc(struct msm_mdss *msm_mdss) { const struct qcom_ubwc_cfg_data *data = msm_mdss->mdss_data; - u32 value = MDSS_UBWC_STATIC_UBWC_SWIZZLE(data->ubwc_swizzle) | + u32 value = MDSS_UBWC_STATIC_UBWC_SWIZZLE(data->ubwc_swizzle & 0x1) | MDSS_UBWC_STATIC_HIGHEST_BANK_BIT(data->highest_bank_bit - 13); - if (data->ubwc_bank_spread) - value |= MDSS_UBWC_STATIC_UBWC_BANK_SPREAD; - if (data->ubwc_enc_version == UBWC_1_0) value |= MDSS_UBWC_STATIC_UBWC_MIN_ACC_LEN(1); writel_relaxed(value, msm_mdss->mmio + REG_MDSS_UBWC_STATIC); } -static void msm_mdss_setup_ubwc_dec_30(struct msm_mdss *msm_mdss) +static void msm_mdss_5x_setup_ubwc(struct msm_mdss *msm_mdss) { const struct qcom_ubwc_cfg_data *data = msm_mdss->mdss_data; u32 value = MDSS_UBWC_STATIC_UBWC_SWIZZLE(data->ubwc_swizzle & 0x1) | @@ -199,11 +196,12 @@ static void msm_mdss_setup_ubwc_dec_30(struct msm_mdss *msm_mdss) writel_relaxed(value, msm_mdss->mmio + REG_MDSS_UBWC_STATIC); } -static void msm_mdss_setup_ubwc_dec_40(struct msm_mdss *msm_mdss) +static void msm_mdss_6x_setup_ubwc(struct msm_mdss *msm_mdss) { const struct qcom_ubwc_cfg_data *data = msm_mdss->mdss_data; u32 value = MDSS_UBWC_STATIC_UBWC_SWIZZLE(data->ubwc_swizzle) | MDSS_UBWC_STATIC_HIGHEST_BANK_BIT(data->highest_bank_bit - 13); + u32 ver, prediction_mode; if (data->ubwc_bank_spread) value |= MDSS_UBWC_STATIC_UBWC_BANK_SPREAD; @@ -211,45 +209,42 @@ static void msm_mdss_setup_ubwc_dec_40(struct msm_mdss *msm_mdss) if (data->macrotile_mode) value |= MDSS_UBWC_STATIC_MACROTILE_MODE; - writel_relaxed(value, msm_mdss->mmio + REG_MDSS_UBWC_STATIC); - - if (data->ubwc_enc_version == UBWC_3_0) { - writel_relaxed(1, msm_mdss->mmio + REG_MDSS_UBWC_CTRL_2); - writel_relaxed(0, msm_mdss->mmio + REG_MDSS_UBWC_PREDICTION_MODE); - } else { - if (data->ubwc_dec_version == UBWC_4_3) - writel_relaxed(3, msm_mdss->mmio + REG_MDSS_UBWC_CTRL_2); - else - writel_relaxed(2, msm_mdss->mmio + REG_MDSS_UBWC_CTRL_2); - writel_relaxed(1, msm_mdss->mmio + REG_MDSS_UBWC_PREDICTION_MODE); - } -} - -static void msm_mdss_setup_ubwc_dec_50(struct msm_mdss *msm_mdss) -{ - const struct qcom_ubwc_cfg_data *data = msm_mdss->mdss_data; - u32 value = MDSS_UBWC_STATIC_UBWC_SWIZZLE(data->ubwc_swizzle) | - MDSS_UBWC_STATIC_HIGHEST_BANK_BIT(data->highest_bank_bit - 13); - - if (data->ubwc_bank_spread) - value |= MDSS_UBWC_STATIC_UBWC_BANK_SPREAD; - - if (data->macrotile_mode) - value |= MDSS_UBWC_STATIC_MACROTILE_MODE; + if (data->ubwc_enc_version == UBWC_1_0) + value |= MDSS_UBWC_STATIC_UBWC_MIN_ACC_LEN(1); writel_relaxed(value, msm_mdss->mmio + REG_MDSS_UBWC_STATIC); - if (data->ubwc_dec_version == UBWC_6_0) - writel_relaxed(5, msm_mdss->mmio + REG_MDSS_UBWC_CTRL_2); + if (data->ubwc_enc_version < UBWC_4_0) + prediction_mode = 0; else - writel_relaxed(4, msm_mdss->mmio + REG_MDSS_UBWC_CTRL_2); - - writel_relaxed(1, msm_mdss->mmio + REG_MDSS_UBWC_PREDICTION_MODE); + prediction_mode = 1; + + if (data->ubwc_enc_version >= UBWC_6_0) + ver = 5; + else if (data->ubwc_enc_version >= UBWC_5_0) + ver = 4; + else if (data->ubwc_enc_version >= UBWC_4_3) + ver = 3; + else if (data->ubwc_enc_version >= UBWC_4_0) + ver = 2; + else if (data->ubwc_enc_version >= UBWC_3_0) + ver = 1; + else /* UBWC 1.0 and 2.0 */ + ver = 0; + + writel_relaxed(ver, msm_mdss->mmio + REG_MDSS_UBWC_CTRL_2); + writel_relaxed(prediction_mode, msm_mdss->mmio + REG_MDSS_UBWC_PREDICTION_MODE); } +#define MDSS_HW_VER(major, minor, step) \ + ((((major) & 0xf) << 28) | \ + (((minor) & 0xfff) << 16) | \ + ((step) & 0xffff)) + static int msm_mdss_enable(struct msm_mdss *msm_mdss) { int ret, i; + u32 hw_rev; /* * Several components have AXI clocks that can only be turned on if @@ -283,43 +278,15 @@ static int msm_mdss_enable(struct msm_mdss *msm_mdss) if (msm_mdss->is_mdp5 || !msm_mdss->mdss_data) return 0; - /* - * ubwc config is part of the "mdss" region which is not accessible - * from the rest of the driver. hardcode known configurations here - * - * Decoder version can be read from the UBWC_DEC_HW_VERSION reg, - * UBWC_n and the rest of params comes from hw data. - */ - switch (msm_mdss->mdss_data->ubwc_dec_version) { - case 0: /* no UBWC */ - case UBWC_1_0: - /* do nothing */ - break; - case UBWC_2_0: - msm_mdss_setup_ubwc_dec_20(msm_mdss); - break; - case UBWC_3_0: - msm_mdss_setup_ubwc_dec_30(msm_mdss); - break; - case UBWC_4_0: - case UBWC_4_3: - msm_mdss_setup_ubwc_dec_40(msm_mdss); - break; - case UBWC_5_0: - msm_mdss_setup_ubwc_dec_50(msm_mdss); - break; - case UBWC_6_0: - msm_mdss_setup_ubwc_dec_50(msm_mdss); - break; - default: - dev_err(msm_mdss->dev, "Unsupported UBWC decoder version %x\n", - msm_mdss->mdss_data->ubwc_dec_version); - dev_err(msm_mdss->dev, "HW_REV: 0x%x\n", - readl_relaxed(msm_mdss->mmio + REG_MDSS_HW_VERSION)); - dev_err(msm_mdss->dev, "UBWC_DEC_HW_VERSION: 0x%x\n", - readl_relaxed(msm_mdss->mmio + REG_MDSS_UBWC_DEC_HW_VERSION)); - break; - } + hw_rev = readl_relaxed(msm_mdss->mmio + REG_MDSS_HW_VERSION); + + if (hw_rev >= MDSS_HW_VER(6, 0, 0)) + msm_mdss_6x_setup_ubwc(msm_mdss); + else if (hw_rev >= MDSS_HW_VER(5, 0, 0)) + msm_mdss_5x_setup_ubwc(msm_mdss); + else if (hw_rev >= MDSS_HW_VER(4, 0, 0)) + msm_mdss_4x_setup_ubwc(msm_mdss); + /* else UBWC 1.0 or none, no params to program */ return ret; } From 6cb9796036814a7321dde93a29d81c6cf1200207 Mon Sep 17 00:00:00 2001 From: Pengjie Zhang Date: Mon, 18 May 2026 21:34:57 +0800 Subject: [PATCH 0968/2758] cpufreq: Documentation: fix sampling_down_factor range [ Upstream commit 85524e651d20944399322d46fb97960337831d43 ] The ondemand governor implementation accepts sampling_down_factor values from 1 to 100000 via MAX_SAMPLING_DOWN_FACTOR, but the documentation in admin-guide/pm/cpufreq.rst still says the valid range is 1 to 100. Update the documentation to match the actual code. Fixes: 2a0e49279850 ("cpufreq: User/admin documentation update and consolidation") Reviewed-by: Zhongqiu Han Signed-off-by: Pengjie Zhang Link: https://patch.msgid.link/20260518133457.2408463-1-zhangpengjie2@huawei.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- Documentation/admin-guide/pm/cpufreq.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/admin-guide/pm/cpufreq.rst b/Documentation/admin-guide/pm/cpufreq.rst index dbe6d23a5d671..fdca59c955dcc 100644 --- a/Documentation/admin-guide/pm/cpufreq.rst +++ b/Documentation/admin-guide/pm/cpufreq.rst @@ -516,7 +516,7 @@ This governor exposes the following tunables: of those tasks above 0 and set this attribute to 1. ``sampling_down_factor`` - Temporary multiplier, between 1 (default) and 100 inclusive, to apply to + Temporary multiplier, between 1 (default) and 100000 inclusive, to apply to the ``sampling_rate`` value if the CPU load goes above ``up_threshold``. This causes the next execution of the governor's worker routine (after From 7f0c81b8839128bd20b832ab5b97642c88cb9764 Mon Sep 17 00:00:00 2001 From: Lifeng Zheng Date: Fri, 22 May 2026 09:49:10 +0530 Subject: [PATCH 0969/2758] cpufreq: conservative: Simplify frequency limit handling [ Upstream commit 3494dff89779b73a6c70481c982e0e96d336454a ] cs_dbs_update() performs explicit checks against policy->min/max before updating the target frequency. These checks are redundant as __cpufreq_driver_target() already clamps the requested frequency to the valid policy limits. Remove the unnecessary boundary checks and simplify the update logic. This also fixes an issue introduced by commit 00bfe05889e9 ("cpufreq: conservative: Decrease frequency faster for deferred updates"), where stale target comparisons could cause frequency updates to be skipped entirely after deferred adjustments. Closes: https://lore.kernel.org/all/20260421123545.1745998-1-zhenglifeng1@huawei.com/ Fixes: 00bfe05889e9 ("cpufreq: conservative: Decrease frequency faster for deferred updates") Signed-off-by: Lifeng Zheng Co-developed-by: Viresh Kumar Signed-off-by: Viresh Kumar Reviewed-by: Zhongqiu Han Link: https://patch.msgid.link/292e6d937890f135e30ec0d2107eaad47cb9a976.1779423281.git.viresh.kumar@linaro.org Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/cpufreq/cpufreq_conservative.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index df01d33993d82..0b32ae28ec857 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -103,10 +103,6 @@ static unsigned int cs_dbs_update(struct cpufreq_policy *policy) if (load > dbs_data->up_threshold) { dbs_info->down_skip = 0; - /* if we are already at full speed then break out early */ - if (requested_freq == policy->max) - goto out; - requested_freq += freq_step; if (requested_freq > policy->max) requested_freq = policy->max; @@ -124,13 +120,7 @@ static unsigned int cs_dbs_update(struct cpufreq_policy *policy) /* Check for frequency decrease */ if (load < cs_tuners->down_threshold) { - /* - * if we cannot reduce the frequency anymore, break out early - */ - if (requested_freq == policy->min) - goto out; - - if (requested_freq > freq_step) + if (requested_freq > policy->min + freq_step) requested_freq -= freq_step; else requested_freq = policy->min; From 0d7beef36455275855965cdf37818140d88e4491 Mon Sep 17 00:00:00 2001 From: Ronaldo Nunez Date: Fri, 22 May 2026 16:13:48 -0300 Subject: [PATCH 0970/2758] pwm: imx27: Fix variable truncation in .apply() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit dc9e08fdbcc3eb08a1d2b868b535081c44425e27 ] Fix a variable truncation when calculating period in microseconds as part of the solution for the ERR051198 in .apply() callback. Example scenario: - Period of 3us (PWMPR = 196 and prescaler = 1) - Expected value in tmp: 198000000000 (NSEC_PER_SEC * (196 + 2) * 1) - Actual value is 431504384 (truncation to u32) Signed-off-by: Ronaldo Nunez Reviewed-by: Frank Li Link: https://patch.msgid.link/20260522191348.6227-1-rnunez@baylibre.com Fixes: a25351e4c774 ("pwm: imx27: Workaround of the pwm output bug when decrease the duty cycle") Signed-off-by: Uwe Kleine-König Signed-off-by: Sasha Levin --- drivers/pwm/pwm-imx27.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-imx27.c b/drivers/pwm/pwm-imx27.c index 3d34cdc4a3a51..c8b801fcb5251 100644 --- a/drivers/pwm/pwm-imx27.c +++ b/drivers/pwm/pwm-imx27.c @@ -200,7 +200,7 @@ static void pwm_imx27_wait_fifo_slot(struct pwm_chip *chip, static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state) { - unsigned long period_cycles, duty_cycles, prescale, period_us, tmp; + unsigned long period_cycles, duty_cycles, prescale, period_us; struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); unsigned long long c; unsigned long long clkrate; @@ -208,6 +208,7 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm, int val; int ret; u32 cr; + u64 tmp; clkrate = clk_get_rate(imx->clks[PWM_IMX27_PER].clk); c = clkrate * state->period; @@ -249,6 +250,11 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm, val = readl(imx->mmio_base + MX3_PWMPR); val = val >= MX3_PWMPR_MAX ? MX3_PWMPR_MAX : val; cr = readl(imx->mmio_base + MX3_PWMCR); + + /* + * tmp stores period in nanoseconds. Result fits in u64 since + * val <= 0xfffe and prescaler in [1, 0x1000]. + */ tmp = NSEC_PER_SEC * (u64)(val + 2) * MX3_PWMCR_PRESCALER_GET(cr); tmp = DIV_ROUND_UP_ULL(tmp, clkrate); period_us = DIV_ROUND_UP_ULL(tmp, 1000); From c3549f8c833d154af9f12661a4dcd581df5a8c63 Mon Sep 17 00:00:00 2001 From: Shiraz Saleem Date: Tue, 12 May 2026 02:40:56 -0700 Subject: [PATCH 0971/2758] RDMA/mana_ib: Use ib_get_eth_speed for reporting port speed [ Upstream commit d28654518c8db5d06d27bd3211c0e9a70c18f7c2 ] Replace hardcoded IB_WIDTH_4X/IB_SPEED_EDR with ib_get_eth_speed() to report the actual link speed in mana_ib_query_port(). Fixes: 4bda1d5332ec ("RDMA/mana_ib: Implement port parameters") Link: https://patch.msgid.link/r/20260512094056.264827-1-kotaranov@linux.microsoft.com Signed-off-by: Shiraz Saleem Signed-off-by: Konstantin Taranov Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mana/main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/mana/main.c b/drivers/infiniband/hw/mana/main.c index afc2fc124fee3..307ae01bf26f3 100644 --- a/drivers/infiniband/hw/mana/main.c +++ b/drivers/infiniband/hw/mana/main.c @@ -600,8 +600,7 @@ int mana_ib_query_port(struct ib_device *ibdev, u32 port, props->phys_state = IB_PORT_PHYS_STATE_DISABLED; } - props->active_width = IB_WIDTH_4X; - props->active_speed = IB_SPEED_EDR; + ib_get_eth_speed(ibdev, port, &props->active_speed, &props->active_width); props->pkey_tbl_len = 1; if (mana_ib_is_rnic(dev)) { props->gid_tbl_len = 16; From 059f9953b9270e9223d8b0bd9cab07b646714ce7 Mon Sep 17 00:00:00 2001 From: Daniel Palmer Date: Fri, 22 May 2026 18:07:26 +0900 Subject: [PATCH 0972/2758] tools/nolibc: stackprotector: Avoid stalling program startup if crng is not init yet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit b882d807fa443b529ae8bf917d7b640a8d555437 ] We are using the getrandom syscall to get a random seed for the stack protector canary but we are calling it with no flags which means it'll block until there is some real randomness to return. This means that if the crng is not ready yet program startup will block and if you are unlucky that could be for a long time and look like the program has crashed. Even if the call to getrandom does not yield any random data, we will still initialize the canary. Fixes: 7188d4637e95 ("tools/nolibc: add support for stack protector") Signed-off-by: Daniel Palmer Acked-by: Willy Tarreau Link: https://patch.msgid.link/20260522090726.726985-1-daniel@thingy.jp Signed-off-by: Thomas Weißschuh Signed-off-by: Sasha Levin --- tools/include/nolibc/stackprotector.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/include/nolibc/stackprotector.h b/tools/include/nolibc/stackprotector.h index ae8b1d3a374dc..4d1aa5371c77c 100644 --- a/tools/include/nolibc/stackprotector.h +++ b/tools/include/nolibc/stackprotector.h @@ -42,7 +42,8 @@ uintptr_t __stack_chk_guard; static __no_stack_protector void __stack_chk_init(void) { - __nolibc_syscall3(__NR_getrandom, &__stack_chk_guard, sizeof(__stack_chk_guard), 0); + __nolibc_syscall3(__NR_getrandom, &__stack_chk_guard, sizeof(__stack_chk_guard), + GRND_INSECURE | GRND_NONBLOCK); /* a bit more randomness in case getrandom() fails, ensure the guard is never 0 */ if (__stack_chk_guard != (uintptr_t) &__stack_chk_guard) __stack_chk_guard ^= (uintptr_t) &__stack_chk_guard; From c49533d4f9950628731f740e721b820e1733762b Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 1 Mar 2026 17:49:39 +0300 Subject: [PATCH 0973/2758] bus: sunxi-rsb: Always check register address validity [ Upstream commit 61192938a5870ac36edae81e4775b680dcf02c61 ] The register address was already validated for read operations in regmap_sunxi_rsb_reg_read before being truncated to a u8. Write operations have the same set of possible addresses, and the address is being truncated from u32 to u8 here as well, so the same check is needed. Signed-off-by: Samuel Holland Signed-off-by: Andrey Skvortsov Fixes: d787dcdb9c8f ("bus: sunxi-rsb: Add driver for Allwinner Reduced Serial Bus") Reviewed-by: Chen-Yu Tsai Reviewed-by: Jernej Skrabec Link: https://patch.msgid.link/20260301144939.1832806-1-andrej.skvortzov@gmail.com Signed-off-by: Chen-Yu Tsai Signed-off-by: Sasha Levin --- drivers/bus/sunxi-rsb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/bus/sunxi-rsb.c b/drivers/bus/sunxi-rsb.c index b4f2c64ac1810..daf0ea563e477 100644 --- a/drivers/bus/sunxi-rsb.c +++ b/drivers/bus/sunxi-rsb.c @@ -445,6 +445,9 @@ static int regmap_sunxi_rsb_reg_write(void *context, unsigned int reg, struct sunxi_rsb_ctx *ctx = context; struct sunxi_rsb_device *rdev = ctx->rdev; + if (reg > 0xff) + return -EINVAL; + return sunxi_rsb_write(rdev->rsb, rdev->rtaddr, reg, &val, ctx->size); } From d3b53f0eda26e442ff97016ad4a0724db8fd3781 Mon Sep 17 00:00:00 2001 From: Han Gao Date: Wed, 20 May 2026 00:40:07 +0800 Subject: [PATCH 0974/2758] pinctrl: spacemit: fix NULL check in spacemit_pin_set_config [ Upstream commit 09c816e5c4d3a8d6d6e4b7537433e5e98505d934 ] spacemit_pin_set_config() looks up the per-pin descriptor with spacemit_get_pin() then checks the wrong variable for failure: const struct spacemit_pin *spin = spacemit_get_pin(pctrl, pin); ... if (!pin) return -EINVAL; reg = spacemit_pin_to_reg(pctrl, spin->pin); pin is an unsigned int pin id, where 0 (GPIO_0 / gmac0_rxdv on K3) is a valid pin, so rejecting it here drops the PAD config write for the first pin of every group. On K3 Pico-ITX the GMAC RGMII group lists pin 0 as its first entry, so its drive-strength / bias configuration was silently ignored. The intended guard is against spacemit_get_pin() returning NULL when the pin id isn't in the SoC's pin table. Check spin instead, which both restores PAD setup for pin 0 and prevents a NULL deref on spin->pin. Fixes: a83c29e1d145 ("pinctrl: spacemit: add support for SpacemiT K1 SoC") Signed-off-by: Han Gao Reviewed-by: Troy Mitchell Reviewed-by: Yixun Lan Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/spacemit/pinctrl-k1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/spacemit/pinctrl-k1.c b/drivers/pinctrl/spacemit/pinctrl-k1.c index b0be62b1c8161..95024e2bb5a58 100644 --- a/drivers/pinctrl/spacemit/pinctrl-k1.c +++ b/drivers/pinctrl/spacemit/pinctrl-k1.c @@ -795,7 +795,7 @@ static int spacemit_pin_set_config(struct spacemit_pinctrl *pctrl, void __iomem *reg; unsigned int mux; - if (!pin) + if (!spin) return -EINVAL; reg = spacemit_pin_to_reg(pctrl, spin->pin); From 335fafb690c655ee81c62b45a7b0ec98c38f42bd Mon Sep 17 00:00:00 2001 From: Chancel Liu Date: Thu, 7 May 2026 10:36:52 +0900 Subject: [PATCH 0975/2758] ASoC: dapm: Fix widget lookup with prefixed names across DAPM contexts [ Upstream commit 8468c8aafe8b5807e5acba2f8aa96d0b3ce0c248 ] Currently dapm_find_widget() manually constructs a prefixed widget name based on the provided DAPM context and compares it using strcmp(). This happens to work in most cases because callers usually know which DAPM context the target widget belongs to and pass in the matching DAPM context. However, this assumption breaks when search_other_contexts is enabled. In such cases, callers may intentionally pass a different DAPM context, while searching for a widget that actually belongs to another DAPM context. For example, when searching for a "DAC" widget, the widget belongs to the codec DAPM and be registered with a codec prefix, while the caller passes card->dapm and intends to search across all DAPM contexts. The current implementation incorrectly applies the caller card DAPM causing the lookup to fail even though the widget exists on the card. Improve the matching strategy to support both use cases: 1. When the caller provides a fully qualified name with prefix, perform exact string matching. This preserves the ability to use prefixes for disambiguation. 2. When the caller provides a bare widget name without prefix, try exact matching first, then fall back to prefix-stripped comparison using snd_soc_dapm_widget_name_cmp(). To determine whether the pin name includes a prefix, a new helper function snd_soc_dapm_pin_has_prefix() is introduced. It checks if the pin name starts with any known component prefix on the card. This fixes widget lookup failures when searching across different DAPM contexts while maintaining backward compatibility for explicitly prefixed lookups. Fixes: ae4fc532244b ("ASoC: dapm: use component prefix when checking widget names") Signed-off-by: Chancel Liu Assisted-by: Cody:Claude-4.5-Sonnet Link: https://patch.msgid.link/20260507013654.2945915-2-chancel.liu@nxp.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- include/sound/soc-dapm.h | 1 + sound/soc/soc-dapm.c | 49 ++++++++++++++++++++++++++++++---------- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 4f8fb7622a139..c1e4f467efda7 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -685,6 +685,7 @@ int snd_soc_dapm_sync_unlocked(struct snd_soc_dapm_context *dapm); int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin); int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, const char *pin); int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, const char *pin); +bool snd_soc_dapm_pin_has_prefix(struct snd_soc_card *card, const char *pin); void snd_soc_dapm_mark_endpoints_dirty(struct snd_soc_card *card); /* dapm path query */ diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index d6192204e613d..a26771c8e6ee6 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2906,20 +2906,18 @@ static struct snd_soc_dapm_widget *dapm_find_widget( { struct snd_soc_dapm_widget *w; struct snd_soc_dapm_widget *fallback = NULL; - char prefixed_pin[80]; - const char *pin_name; - const char *prefix = dapm_prefix(dapm); - - if (prefix) { - snprintf(prefixed_pin, sizeof(prefixed_pin), "%s %s", - prefix, pin); - pin_name = prefixed_pin; - } else { - pin_name = pin; - } + bool pin_has_prefix = snd_soc_dapm_pin_has_prefix(dapm->card, pin); + bool match; for_each_card_widgets(dapm->card, w) { - if (!strcmp(w->name, pin_name)) { + match = false; + + if (!strcmp(pin, w->name)) + match = true; + else if (!pin_has_prefix && !snd_soc_dapm_widget_name_cmp(w, pin)) + match = true; + + if (match) { if (w->dapm == dapm) return w; else @@ -4872,6 +4870,33 @@ int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, } EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend); +/** + * snd_soc_dapm_pin_has_prefix - check if given pin has a known prefix + * @card: card to be checked + * @pin: pin name + * + * Returns true if given pin has a known prefix + */ +bool snd_soc_dapm_pin_has_prefix(struct snd_soc_card *card, const char *pin) +{ + struct snd_soc_component *component; + const char *prefix; + size_t prefix_len; + + for_each_card_components(card, component) { + prefix = component->name_prefix; + if (!prefix) + continue; + + prefix_len = strlen(prefix); + if (!strncmp(pin, prefix, prefix_len) && pin[prefix_len] == ' ') + return true; + } + + return false; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_pin_has_prefix); + /** * snd_soc_dapm_free - free dapm resources * @dapm: DAPM context From abd27a977b419d584efa659488c22d2306987b29 Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Tue, 12 May 2026 18:38:52 +0000 Subject: [PATCH 0976/2758] RDMA/irdma: Fix out-of-bounds write in irdma_copy_user_pgaddrs [ Upstream commit 5ebb3ed757be3e04cf803026004aa0beaeb13e9b ] The irdma_copy_user_pgaddrs function loops through all of the umem DMA blocks to populate the PBLEs and will stop when either the last DMA block is reached or palloc->total_cnt is reached. The issue is that the logic for checking palloc->total_cnt would only work for non-zero values. When irdma_setup_pbles is called with lvl==0, it calls irdma_copy_user_pgaddrs with palloc->total_cnt==0, which means the only way to break out of the loop is to reach the last umem DMA block, which means it could end up going beyond the fixed size of 4 iwmr->pgaddrmem array that is used in the lvl==0 case. In the case of QP/CQ/SRQ rings, the value of lvl is determined by a separate input (for example, req.cq_pages in the case of a CQ). So, we must perform explicit checking to ensure we don't overflow the pgaddrmem array if the user provides a umem that consists of more blocks than their provided req.cq_pages. Fixes: b48c24c2d710 ("RDMA/irdma: Implement device supported verb APIs") Link: https://patch.msgid.link/r/20260512183852.614045-1-jmoroni@google.com Signed-off-by: Jacob Moroni Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/irdma/verbs.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index 8cd4275328052..661f2e0299ef7 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -2781,10 +2781,11 @@ static inline u64 *irdma_next_pbl_addr(u64 *pbl, struct irdma_pble_info **pinfo, * irdma_copy_user_pgaddrs - copy user page address to pble's os locally * @iwmr: iwmr for IB's user page addresses * @pbl: ple pointer to save 1 level or 0 level pble + * @pbl_len: Max number of PBL entries to populate * @level: indicated level 0, 1 or 2 */ static void irdma_copy_user_pgaddrs(struct irdma_mr *iwmr, u64 *pbl, - enum irdma_pble_level level) + u32 pbl_len, enum irdma_pble_level level) { struct ib_umem *region = iwmr->region; struct irdma_pbl *iwpbl = &iwmr->iwpbl; @@ -2792,7 +2793,9 @@ static void irdma_copy_user_pgaddrs(struct irdma_mr *iwmr, u64 *pbl, struct irdma_pble_info *pinfo; struct ib_block_iter biter; u32 idx = 0; - u32 pbl_cnt = 0; + + if (!pbl_len) + return; pinfo = (level == PBLE_LEVEL_1) ? NULL : palloc->level2.leaf; @@ -2801,7 +2804,7 @@ static void irdma_copy_user_pgaddrs(struct irdma_mr *iwmr, u64 *pbl, rdma_umem_for_each_dma_block(region, &biter, iwmr->page_size) { *pbl = rdma_block_iter_dma_address(&biter); - if (++pbl_cnt == palloc->total_cnt) + if (!--pbl_len) break; pbl = irdma_next_pbl_addr(pbl, &pinfo, &idx); } @@ -2877,6 +2880,7 @@ static int irdma_setup_pbles(struct irdma_pci_f *rf, struct irdma_mr *iwmr, u64 *pbl; int status; enum irdma_pble_level level = PBLE_LEVEL_1; + u32 pbl_len; if (lvl) { status = irdma_get_pble(rf->pble_rsrc, palloc, iwmr->page_cnt, @@ -2884,16 +2888,18 @@ static int irdma_setup_pbles(struct irdma_pci_f *rf, struct irdma_mr *iwmr, if (status) return status; + pbl_len = palloc->total_cnt; iwpbl->pbl_allocated = true; level = palloc->level; pinfo = (level == PBLE_LEVEL_1) ? &palloc->level1 : palloc->level2.leaf; pbl = pinfo->addr; } else { + pbl_len = IRDMA_MAX_SAVED_PHY_PGADDR; pbl = iwmr->pgaddrmem; } - irdma_copy_user_pgaddrs(iwmr, pbl, level); + irdma_copy_user_pgaddrs(iwmr, pbl, pbl_len, level); if (lvl) iwmr->pgaddrmem[0] = *pbl; From 3525987a392536f31a484833af258971af63b24c Mon Sep 17 00:00:00 2001 From: Zhu Yanjun Date: Fri, 15 May 2026 02:25:37 +0200 Subject: [PATCH 0977/2758] RDMA/rxe: Fix a use-after-free problem in rxe_mmap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 35744ab3d03c5fca8c1752f53fc8fc674e14c561 ] rxe_mmap() removes a rxe_mmap_info struct from the pending_mmaps list and releases pending_lock while the struct's kref is still at 1: list_del_init(&ip->pending_mmaps); spin_unlock_bh(&rxe->pending_lock); /* ref == 1, no lock held */ ret = remap_vmalloc_range(vma, ip->obj, 0); /* walks PTEs */ [...] rxe_vma_open(vma); /* kref_get, ref → 2 */ remap_vmalloc_range_partial() walks PTEs without any lock. A concurrent DESTROY_CQ ioctl on another CPU calls: kref_put(&q->ip->ref, rxe_mmap_release) /* ref 1→0 */ vfree(ip->obj) /* clears vmalloc PTEs mid-walk */ kfree(ip) /* frees rxe_mmap_info */ This yields: 1. Kernel crash, vmalloc_to_page() returns NULL when vfree wins the per-PTE race -> vm_insert_page(NULL) → GPF in validate_page_before_insert 2. Page UAF, vmalloc_to_page() reads a stale PTE before vfree clears it. User VMA holds a PTE to a free'd page which might eventually get reallocated later by vmalloc which allows the attacker to get a clean page-level UAF. It is worth noting that even though a page-level UAF is possible given the strong primitive, it is statistically very difficult to achieve given the very short time window (after the last insert_page and before the kref_get). The call trace are as below: Oops: general protection fault, probably for non-canonical address 0xdffffc0000000001: 0000 [#1] SMP KASAN NOPTI KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f] CPU: 0 UID: 1000 PID: 413 Comm: poc Not tainted 7.0.0-rc5-dirty #28 PREEMPT(lazy) Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014 RIP: 0010:validate_page_before_insert+0x32/0x300 Code: e5 41 57 41 56 49 89 fe 41 55 41 54 53 48 89 f3 e8 93 b5 a3 ff 48 8d 7b 08 48 b8 00 00 00 00 00 fc ff df 48 89 fa 48 c1 ea 03 <80> 3c 02 00 0f 85 7b 02 00 00 4c 8b 63 08 31 ff 4d 89 e5 41 83 e5 RSP: 0018:ffff88811b15f2f0 EFLAGS: 00000202 RAX: dffffc0000000000 RBX: 0000000000000000 RCX: 0000000000000000 RDX: 0000000000000001 RSI: 0000000000000000 RDI: 0000000000000008 RBP: ffff88811b15f318 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000000 R12: ffff8881181eee00 R13: 0000000000000000 R14: ffff8881181eee00 R15: ffff8881181eee20 FS: 00007b1e000f76c0(0000) GS:ffff8884268e0000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007b1e00a24ac0 CR3: 0000000116eb3000 CR4: 00000000000006f0 Call Trace: insert_page+0x8f/0x190 ? __pfx_insert_page+0x10/0x10 ? kasan_save_alloc_info+0x38/0x60 vm_insert_page+0x2e7/0x400 remap_vmalloc_range_partial+0x212/0x3e0 remap_vmalloc_range+0x6e/0xb0 ? __kasan_check_write+0x14/0x30 rxe_mmap+0x2e9/0x5d0 ib_uverbs_mmap+0x1ad/0x2c0 __mmap_region+0x12c2/0x2ad0 ? __pfx___mmap_region+0x10/0x10 ? __sanitizer_cov_trace_switch+0x58/0xb0 ? mas_prev_slot+0x360/0x39c0 ? __sanitizer_cov_trace_switch+0x58/0xb0 ? mas_next_slot+0x1e5b/0x2f40 ? __sanitizer_cov_trace_cmp8+0x18/0x30 ? unmapped_area_topdown+0x4dd/0x610 ? kfree+0x1b1/0x440 ? free_cpumask_var+0x16/0x30 ? __kasan_slab_free+0x7d/0xa0 ? __sanitizer_cov_trace_cmp8+0x18/0x30 mmap_region+0x2e6/0x3c0 do_mmap+0xa3e/0x12a0 ? __pfx_do_mmap+0x10/0x10 ? __kasan_check_write+0x14/0x30 ? down_write_killable+0xba/0x160 ? __pfx_down_write_killable+0x10/0x10 ? __sanitizer_cov_trace_cmp4+0x16/0x30 vm_mmap_pgoff+0x2d4/0x4a0 ? __pfx_vm_mmap_pgoff+0x10/0x10 ? fget+0x1bf/0x270 ksys_mmap_pgoff+0x40c/0x690 ? __sanitizer_cov_trace_const_cmp4+0x16/0x30 ? __pfx_ksys_mmap_pgoff+0x10/0x10 ? __kasan_check_write+0x14/0x30 ? _raw_spin_trylock+0xbb/0x130 ? __pfx__raw_spin_trylock+0x10/0x10 __x64_sys_mmap+0x135/0x1e0 x64_sys_call+0x1c14/0x2790 do_syscall_64+0xd2/0x1050 ? rcu_core+0x352/0x7d0 ? rcu_core_si+0xe/0x20 ? handle_softirqs+0x1aa/0x650 ? __sanitizer_cov_trace_cmp4+0x16/0x30 ? fpregs_assert_state_consistent+0xe1/0x160 ? irqentry_exit+0xb1/0x670 entry_SYSCALL_64_after_hwframe+0x76/0x7e Link: https://patch.msgid.link/r/20260515002537.6209-1-yanjun.zhu@linux.dev Reported-and-tested-by: nasm Suggested-by: nasm Fixes: 8700e3e7c485 ("Soft RoCE driver") Signed-off-by: Zhu Yanjun Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/sw/rxe/rxe_mmap.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/sw/rxe/rxe_mmap.c b/drivers/infiniband/sw/rxe/rxe_mmap.c index db380302149e5..7f723a2f37005 100644 --- a/drivers/infiniband/sw/rxe/rxe_mmap.c +++ b/drivers/infiniband/sw/rxe/rxe_mmap.c @@ -93,18 +93,31 @@ int rxe_mmap(struct ib_ucontext *context, struct vm_area_struct *vma) goto done; found_it: + /* + * Increment refcount and check whether it is being freed atm while + * holding lock to prevent UAF + */ + if (!kref_get_unless_zero(&ip->ref)) { + spin_unlock_bh(&rxe->pending_lock); + ret = -ENXIO; + goto done; + } + list_del_init(&ip->pending_mmaps); spin_unlock_bh(&rxe->pending_lock); + vma->vm_ops = &rxe_vm_ops; + vma->vm_private_data = ip; + ret = remap_vmalloc_range(vma, ip->obj, 0); if (ret) { + vma->vm_private_data = NULL; + vma->vm_ops = NULL; + kref_put(&ip->ref, rxe_mmap_release); rxe_dbg_dev(rxe, "err %d from remap_vmalloc_range\n", ret); goto done; } - vma->vm_ops = &rxe_vm_ops; - vma->vm_private_data = ip; - rxe_vma_open(vma); done: return ret; } From e874d99092a0307c56d260922e7c4dca7373f790 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Mon, 18 May 2026 10:19:10 +0800 Subject: [PATCH 0978/2758] IB/mlx4: Fix refcount leak in add_port() error path [ Upstream commit 9a8826fdfbcd7ed2ccf745f5d54208358d939def ] After kobject_init_and_add(), the lifetime of the embedded struct kobject is expected to be managed through the kobject core reference counting. In add_port(), failure paths after kobject_init_and_add() must not free struct mlx4_port directly, because the embedded kobject is then managed by the kobject core. Freeing it directly leaves the kobject reference counting unbalanced and can lead to incorrect lifetime handling. Allocate the pkey and gid attribute arrays before kobject_init_and_add(), so failures before kobject initialization can be handled by directly freeing the allocated memory. Once kobject_init_and_add() has been called, unwind later failures by removing any successfully created sysfs groups, calling kobject_del(), and then releasing the embedded kobject with kobject_put(). Fixes: c1e7e466120b ("IB/mlx4: Add iov directory in sysfs under the ib device") Link: https://patch.msgid.link/r/20260518021910.972900-1-lgs201920130244@gmail.com Signed-off-by: Guangshuo Li Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx4/sysfs.c | 45 ++++++++++++++++++------------ 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/drivers/infiniband/hw/mlx4/sysfs.c b/drivers/infiniband/hw/mlx4/sysfs.c index b8fa4ecfc9610..e688ad66a895f 100644 --- a/drivers/infiniband/hw/mlx4/sysfs.c +++ b/drivers/infiniband/hw/mlx4/sysfs.c @@ -636,12 +636,6 @@ static int add_port(struct mlx4_ib_dev *dev, int port_num, int slave) p->port_num = port_num; p->slave = slave; - ret = kobject_init_and_add(&p->kobj, &port_type, - kobject_get(dev->dev_ports_parent[slave]), - "%d", port_num); - if (ret) - goto err_alloc; - p->pkey_group.name = "pkey_idx"; p->pkey_group.attrs = alloc_group_attrs(show_port_pkey, @@ -649,13 +643,9 @@ static int add_port(struct mlx4_ib_dev *dev, int port_num, int slave) dev->dev->caps.pkey_table_len[port_num]); if (!p->pkey_group.attrs) { ret = -ENOMEM; - goto err_alloc; + goto err_free_port; } - ret = sysfs_create_group(&p->kobj, &p->pkey_group); - if (ret) - goto err_free_pkey; - p->gid_group.name = "gid_idx"; p->gid_group.attrs = alloc_group_attrs(show_port_gid_idx, NULL, 1); if (!p->gid_group.attrs) { @@ -663,28 +653,47 @@ static int add_port(struct mlx4_ib_dev *dev, int port_num, int slave) goto err_free_pkey; } + ret = kobject_init_and_add(&p->kobj, &port_type, + kobject_get(dev->dev_ports_parent[slave]), + "%d", port_num); + if (ret) + goto err_put; + + ret = sysfs_create_group(&p->kobj, &p->pkey_group); + if (ret) + goto err_del; + ret = sysfs_create_group(&p->kobj, &p->gid_group); if (ret) - goto err_free_gid; + goto err_remove_pkey; ret = add_vf_smi_entries(p); if (ret) - goto err_free_gid; + goto err_remove_gid; list_add_tail(&p->kobj.entry, &dev->pkeys.pkey_port_list[slave]); return 0; -err_free_gid: - kfree(p->gid_group.attrs[0]); - kfree(p->gid_group.attrs); +err_remove_gid: + sysfs_remove_group(&p->kobj, &p->gid_group); + +err_remove_pkey: + sysfs_remove_group(&p->kobj, &p->pkey_group); + +err_del: + kobject_del(&p->kobj); + +err_put: + kobject_put(dev->dev_ports_parent[slave]); + kobject_put(&p->kobj); + return ret; err_free_pkey: for (i = 0; i < dev->dev->caps.pkey_table_len[port_num]; ++i) kfree(p->pkey_group.attrs[i]); kfree(p->pkey_group.attrs); -err_alloc: - kobject_put(dev->dev_ports_parent[slave]); +err_free_port: kfree(p); return ret; } From 1e56f8886d64288cf88d80af44c7b9148aeaa98b Mon Sep 17 00:00:00 2001 From: Lianfa Weng Date: Wed, 20 May 2026 13:57:58 +0800 Subject: [PATCH 0979/2758] RDMA/hns: Fix warning in poll cq direct mode [ Upstream commit 3f19c2a3852e6ba75f3e92dd5edc4e07f3d07f4a ] CQs allocated by ib_alloc_cq() always have a comp_handler. Though in direct mode this handler is never expected to be called, it is still called when the driver is reset, triggering the following WARN_ONCE(): Call trace: ib_cq_completion_direct+0x38/0x60 hns_roce_cq_completion+0x54/0x90 (hns_roce_hw_v2] hns_roce_handle_device_err+Ox1c8/0x340 [hns_roce_hw_v2] hns_roce_hw_v2_uninit_instance.constprop.0+0x34/0x70 [hns_roce_hw_v2] hns_roce_hw_v2_reset_notify+0xc4/0xe0 [hns_roce_hw_v2] hclge_notify_roce_client+0x60/0xbc [hclge] hclge_reset_rebuild+0x48/0x34c [hclge] hclge_reset_subtask+0xcc/0xec [hclge] hclge_reset_service_task+0x80/0x160 [hclge] hclge_service_task+0x50/0x80 (hclge] process_one_work+0x1cc/0x4d0 worker_thread+0x154/0x414 kthread+0x104/0x144 ret_from_fork+0x10/0x18 Fixes: f295e4cece5c ("RDMA/hns: Delete unnecessary callback functions for cq") Link: https://patch.msgid.link/r/20260520055759.2354037-3-huangjunxian6@hisilicon.com Signed-off-by: Lianfa Weng Signed-off-by: Junxian Huang Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/hns/hns_roce_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_main.c b/drivers/infiniband/hw/hns/hns_roce_main.c index 0dbe99aab6ad2..1e54710354542 100644 --- a/drivers/infiniband/hw/hns/hns_roce_main.c +++ b/drivers/infiniband/hw/hns/hns_roce_main.c @@ -1113,7 +1113,7 @@ static void check_and_get_armed_cq(struct list_head *cq_list, struct ib_cq *cq) unsigned long flags; spin_lock_irqsave(&hr_cq->lock, flags); - if (cq->comp_handler) { + if (cq->comp_handler && hr_cq->ib_cq.poll_ctx != IB_POLL_DIRECT) { if (!hr_cq->is_armed) { hr_cq->is_armed = 1; list_add_tail(&hr_cq->node, cq_list); From f3c9e84268e9fa613d40d6c138fb6969c35879b5 Mon Sep 17 00:00:00 2001 From: Lianfa Weng Date: Wed, 20 May 2026 13:57:59 +0800 Subject: [PATCH 0980/2758] RDMA/hns: Fix log flood after cmd_mbox failure [ Upstream commit bbd97d71e53e551890e4115ad9de46b5f2ac0858 ] hns_roce_cmd_mbox() is the command interface between driver and hardware. When hardware is abnormal, the unlimited error printings after hns_roce_cmd_mbox() failure will cause log flood and even system crash. Replace ibdev_err() and ibdev_warn() with their ratelimited versions in the error handling path after hns_roce_cmd_mbox() (and its wrappers hns_roce_create_hw_ctx/hns_roce_destroy_hw_ctx) fails. Fixes: 9a4435375cd1 ("IB/hns: Add driver files for hns RoCE driver") Link: https://patch.msgid.link/r/20260520055759.2354037-4-huangjunxian6@hisilicon.com Signed-off-by: Lianfa Weng Signed-off-by: Junxian Huang Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/hns/hns_roce_cq.c | 6 +++--- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 18 +++++++++--------- drivers/infiniband/hw/hns/hns_roce_mr.c | 6 +++--- drivers/infiniband/hw/hns/hns_roce_srq.c | 2 +- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_cq.c b/drivers/infiniband/hw/hns/hns_roce_cq.c index 621568e114054..f7e220ab42198 100644 --- a/drivers/infiniband/hw/hns/hns_roce_cq.c +++ b/drivers/infiniband/hw/hns/hns_roce_cq.c @@ -174,9 +174,9 @@ static int hns_roce_create_cqc(struct hns_roce_dev *hr_dev, ret = hns_roce_create_hw_ctx(hr_dev, mailbox, HNS_ROCE_CMD_CREATE_CQC, hr_cq->cqn); if (ret) - ibdev_err(ibdev, - "failed to send create cmd for CQ(0x%lx), ret = %d.\n", - hr_cq->cqn, ret); + ibdev_err_ratelimited(ibdev, + "failed to send create cmd for CQ(0x%lx), ret = %d.\n", + hr_cq->cqn, ret); hns_roce_free_cmd_mailbox(hr_dev, mailbox); diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index fa36700d0db2b..8810fa0b65973 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -6192,9 +6192,9 @@ static int hns_roce_v2_modify_srq(struct ib_srq *ibsrq, HNS_ROCE_CMD_MODIFY_SRQC, srq->srqn); hns_roce_free_cmd_mailbox(hr_dev, mailbox); if (ret) - ibdev_err(&hr_dev->ib_dev, - "failed to handle cmd of modifying SRQ, ret = %d.\n", - ret); + ibdev_err_ratelimited(&hr_dev->ib_dev, + "failed to handle cmd of modifying SRQ, ret = %d.\n", + ret); } out: @@ -6220,9 +6220,9 @@ static int hns_roce_v2_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr) ret = hns_roce_cmd_mbox(hr_dev, 0, mailbox->dma, HNS_ROCE_CMD_QUERY_SRQC, srq->srqn); if (ret) { - ibdev_err(&hr_dev->ib_dev, - "failed to process cmd of querying SRQ, ret = %d.\n", - ret); + ibdev_err_ratelimited(&hr_dev->ib_dev, + "failed to process cmd of querying SRQ, ret = %d.\n", + ret); goto out; } @@ -6328,9 +6328,9 @@ static int hns_roce_v2_query_mpt(struct hns_roce_dev *hr_dev, u32 key, ret = hns_roce_cmd_mbox(hr_dev, 0, mailbox->dma, HNS_ROCE_CMD_QUERY_MPT, key_to_hw_index(key)); if (ret) { - ibdev_err(&hr_dev->ib_dev, - "failed to process cmd when querying MPT, ret = %d.\n", - ret); + ibdev_err_ratelimited(&hr_dev->ib_dev, + "failed to process cmd when querying MPT, ret = %d.\n", + ret); goto err_mailbox; } diff --git a/drivers/infiniband/hw/hns/hns_roce_mr.c b/drivers/infiniband/hw/hns/hns_roce_mr.c index 25bfd3970f5b6..c13d29cd0897b 100644 --- a/drivers/infiniband/hw/hns/hns_roce_mr.c +++ b/drivers/infiniband/hw/hns/hns_roce_mr.c @@ -173,7 +173,7 @@ static int hns_roce_mr_enable(struct hns_roce_dev *hr_dev, ret = hns_roce_create_hw_ctx(hr_dev, mailbox, HNS_ROCE_CMD_CREATE_MPT, mtpt_idx & (hr_dev->caps.num_mtpts - 1)); if (ret) { - dev_err(dev, "failed to create mpt, ret = %d.\n", ret); + dev_err_ratelimited(dev, "failed to create mpt, ret = %d.\n", ret); goto err_page; } @@ -319,7 +319,7 @@ struct ib_mr *hns_roce_rereg_user_mr(struct ib_mr *ibmr, int flags, u64 start, ret = hns_roce_destroy_hw_ctx(hr_dev, HNS_ROCE_CMD_DESTROY_MPT, mtpt_idx); if (ret) - ibdev_warn(ib_dev, "failed to destroy MPT, ret = %d.\n", ret); + ibdev_warn_ratelimited(ib_dev, "failed to destroy MPT, ret = %d.\n", ret); mr->enabled = 0; mr->iova = virt_addr; @@ -350,7 +350,7 @@ struct ib_mr *hns_roce_rereg_user_mr(struct ib_mr *ibmr, int flags, u64 start, ret = hns_roce_create_hw_ctx(hr_dev, mailbox, HNS_ROCE_CMD_CREATE_MPT, mtpt_idx); if (ret) { - ibdev_err(ib_dev, "failed to create MPT, ret = %d.\n", ret); + ibdev_err_ratelimited(ib_dev, "failed to create MPT, ret = %d.\n", ret); goto free_cmd_mbox; } diff --git a/drivers/infiniband/hw/hns/hns_roce_srq.c b/drivers/infiniband/hw/hns/hns_roce_srq.c index 8b94cbdfa54df..8feb5851067d8 100644 --- a/drivers/infiniband/hw/hns/hns_roce_srq.c +++ b/drivers/infiniband/hw/hns/hns_roce_srq.c @@ -103,7 +103,7 @@ static int hns_roce_create_srqc(struct hns_roce_dev *hr_dev, ret = hns_roce_create_hw_ctx(hr_dev, mailbox, HNS_ROCE_CMD_CREATE_SRQ, srq->srqn); if (ret) - ibdev_err(ibdev, "failed to config SRQC, ret = %d.\n", ret); + ibdev_err_ratelimited(ibdev, "failed to config SRQC, ret = %d.\n", ret); err_mbox: hns_roce_free_cmd_mailbox(hr_dev, mailbox); From 53b70c86e249a1c3a61aa78a84831300546d54a3 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 21 May 2026 16:06:48 +0200 Subject: [PATCH 0981/2758] ACPI: PAD: Fix teardown ordering in acpi_pad_remove() [ Upstream commit 5776575d9a7e50e77c45e0ab0271c000496f341d ] The ACPI notify handler installed by acpi_pad_probe() needs to be removed before calling acpi_pad_idle_cpus() in acpi_pad_remove() so it doesn't schedule idle time injection on some CPUs again. Fixes: 8e0af5141ab9 ("ACPI: create Processor Aggregator Device driver") Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2064153.usQuhbGJ8B@rafael.j.wysocki Signed-off-by: Sasha Levin --- drivers/acpi/acpi_pad.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/acpi_pad.c b/drivers/acpi/acpi_pad.c index ec94b09bb7471..bff702835cfeb 100644 --- a/drivers/acpi/acpi_pad.c +++ b/drivers/acpi/acpi_pad.c @@ -435,12 +435,12 @@ static int acpi_pad_probe(struct platform_device *pdev) static void acpi_pad_remove(struct platform_device *pdev) { + acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), + ACPI_DEVICE_NOTIFY, acpi_pad_notify); + mutex_lock(&isolated_cpus_lock); acpi_pad_idle_cpus(0); mutex_unlock(&isolated_cpus_lock); - - acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), - ACPI_DEVICE_NOTIFY, acpi_pad_notify); } static const struct acpi_device_id pad_device_ids[] = { From 2734eb69a79601cb49be2bd36829c41aadc8beeb Mon Sep 17 00:00:00 2001 From: Tao Cui Date: Wed, 20 May 2026 18:45:46 +0800 Subject: [PATCH 0982/2758] RDMA/counter: Fix incorrect port index in rdma_counter_init() error cleanup [ Upstream commit b86fd95805a7bd4c5b9465c9e7f75e45bbe7eb6f ] The error cleanup loop in rdma_counter_init() iterates with variable 'i' but accesses dev->port_data[port] instead of dev->port_data[i]. This causes the failed port's hstats to be freed multiple times while leaking hstats of previously initialized ports. Fixes: 56594ae1d250 ("RDMA/core: Annotate destroy of mutex to ensure that it is released as unlocked") Link: https://patch.msgid.link/r/20260520104546.1776253-3-cuitao@kylinos.cn Signed-off-by: Tao Cui Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/core/counters.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/core/counters.c b/drivers/infiniband/core/counters.c index c3aa6d7fc66b6..5dad5d77ce274 100644 --- a/drivers/infiniband/core/counters.c +++ b/drivers/infiniband/core/counters.c @@ -661,7 +661,7 @@ void rdma_counter_init(struct ib_device *dev) fail: for (i = port; i >= rdma_start_port(dev); i--) { - port_counter = &dev->port_data[port].port_counter; + port_counter = &dev->port_data[i].port_counter; rdma_free_hw_stats_struct(port_counter->hstats); port_counter->hstats = NULL; mutex_destroy(&port_counter->lock); From 5a2119f4a98555c363911511d4cbe74a4757de7e Mon Sep 17 00:00:00 2001 From: Xianwei Zhao Date: Mon, 18 May 2026 08:26:20 +0000 Subject: [PATCH 0983/2758] pinctrl: meson: amlogic-a4: fix gpio output glitch [ Upstream commit 45ad4de324cb1bba88e568b5bef633a79d926aed ] When the system transitions from bootloader to kernel, the GPIO is expected to keep driving high. However, the Linux kernel first configures the pin direction and then sets the output value. This may cause a brief low-level glitch on the GPIO line, which can be problematic for regulator control. By configuring the output value before switching the pin direction to output, the glitch can be avoided. This commit fixes the issue by swapping the configuration order. Fixes: 6e9be3abb78c ("pinctrl: Add driver support for Amlogic SoCs") Signed-off-by: Xianwei Zhao Reviewed-by: Neil Armstrong Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/meson/pinctrl-amlogic-a4.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/pinctrl/meson/pinctrl-amlogic-a4.c b/drivers/pinctrl/meson/pinctrl-amlogic-a4.c index 35d27626a336b..1bd58fbbd26ac 100644 --- a/drivers/pinctrl/meson/pinctrl-amlogic-a4.c +++ b/drivers/pinctrl/meson/pinctrl-amlogic-a4.c @@ -548,11 +548,11 @@ static int aml_pinconf_set_output_drive(struct aml_pinctrl *info, { int ret; - ret = aml_pinconf_set_output(info, pin, true); + ret = aml_pinconf_set_drive(info, pin, high); if (ret) return ret; - return aml_pinconf_set_drive(info, pin, high); + return aml_pinconf_set_output(info, pin, true); } static int aml_pinconf_set(struct pinctrl_dev *pcdev, unsigned int pin, @@ -921,15 +921,14 @@ static int aml_gpio_direction_output(struct gpio_chip *chip, unsigned int gpio, unsigned int bit, reg; int ret; - aml_gpio_calc_reg_and_bit(bank, AML_REG_DIR, gpio, ®, &bit); - ret = regmap_update_bits(bank->reg_gpio, reg, BIT(bit), 0); + aml_gpio_calc_reg_and_bit(bank, AML_REG_OUT, gpio, ®, &bit); + ret = regmap_update_bits(bank->reg_gpio, reg, BIT(bit), + value ? BIT(bit) : 0); if (ret < 0) return ret; - aml_gpio_calc_reg_and_bit(bank, AML_REG_OUT, gpio, ®, &bit); - - return regmap_update_bits(bank->reg_gpio, reg, BIT(bit), - value ? BIT(bit) : 0); + aml_gpio_calc_reg_and_bit(bank, AML_REG_DIR, gpio, ®, &bit); + return regmap_update_bits(bank->reg_gpio, reg, BIT(bit), 0); } static int aml_gpio_set(struct gpio_chip *chip, unsigned int gpio, int value) From 5e6bc20316d0f97d5d3b07c1f444d27a3c3745ef Mon Sep 17 00:00:00 2001 From: Jiakai Xu Date: Sat, 23 May 2026 02:23:14 +0000 Subject: [PATCH 0984/2758] PM: sleep: Use complete() in device_pm_sleep_init() [ Upstream commit 3855941f1e4069182c895d5093c5fa589f5b38bd ] Replace complete_all() with complete() in device_pm_sleep_init() to allow it to be called in atomic contexts without triggering a false-positive WARNING from lockdep_assert_RT_in_threaded_ctx() when CONFIG_PROVE_RAW_LOCK_NESTING is enabled. device_pm_sleep_init() may be called during device initialization while holding a raw_spinlock (e.g., from within device_initialize()), and complete_all() is unsafe in atomic contexts on PREEMPT_RT kernels. complete(), which is safe to call from any context, is sufficient here. complete_all() sets the completion count to UINT_MAX/2 (permanently signaled), while complete() increments it by 1. Since no threads can be waiting during device initialization, both are functionally equivalent. The completion is always reinitialized via reinit_completion() in dpm_clear_async_state() before each suspend/resume cycle. However, changing to complete() introduces a potential deadlock for devices with no PM support (dev->power.no_pm = true). Such devices are never added to the dpm_list and never go through dpm_clear_async_state(), so their completion is never reinitialized. A parent device waiting on a no_pm child across multiple suspend phases would consume the single-use token in the first phase and block forever in the second. Fix this by adding an early return in dpm_wait() when dev->power.no_pm is set, since no_pm devices do not participate in system suspend/resume. Fixes: 152e1d592071 ("PM: Prevent waiting forever on asynchronous resume after failing suspend") Signed-off-by: Jiakai Xu [ rjw: Subject adjustment ] Link: https://patch.msgid.link/20260523022314.2657232-1-xujiakai24@mails.ucas.ac.cn Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/base/power/main.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index e1b550664babb..ed48c292f5757 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -115,7 +115,7 @@ void device_pm_sleep_init(struct device *dev) dev->power.is_noirq_suspended = false; dev->power.is_late_suspended = false; init_completion(&dev->power.completion); - complete_all(&dev->power.completion); + complete(&dev->power.completion); dev->power.wakeup = NULL; INIT_LIST_HEAD(&dev->power.entry); } @@ -252,6 +252,10 @@ static void dpm_wait(struct device *dev, bool async) if (!dev) return; + /* Devices with no PM support don't use the completion. */ + if (dev->power.no_pm) + return; + if (async || (pm_async_enabled && dev->power.async_suspend)) wait_for_completion(&dev->power.completion); } From cf49b4e37fda345d50eb82a1d511531d08e0347f Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Sat, 2 May 2026 00:14:20 +0100 Subject: [PATCH 0985/2758] MIPS: Fix big-endian stack argument fetching in o32 wrapper [ Upstream commit 8e0780d30b1b51248e42895b7acc7a20f147b40b ] Fix an issue in call_o32() where the upper 32-bit half of incoming n64 stack arguments is fetched and used for outgoing o32 stack arguments on big-endian platforms. This code was adapted from arch/mips/dec/prom/call_o32.S which was meant for a little-endian platform only and therefore using 32-bit loads from 64-bit stack slot locations holding incoming stack arguments resulted in correct values being retrieved for data that is expected to be 32-bit. This works on little-endian platforms where the lower 32-bit half of the 64-bit value is located at every 64-bit stack slot location. However on big-endian platforms the lower 32-bit half is instead located at offset 4 from every 64-bit stack slot location. So to fix the issue the offset of 4 would have to be used on big-endian platforms only, or alternatively a 64-bit load from the 64-bit stack slot location can be used across the board, as the subsequent 32-bit store to the corresponding outgoing stack argument slot will correctly truncate the value and cause no unpredictable result. We already take advantage of this architectural feature for the incoming arguments held in $a6 and $a7 registers, since the o32 wrapper does not know how many incoming arguments there are and consequently propagates incoming data which may not be 32-bit. Since this code is generally supposed to be used with the stack located in cached memory there is no extra overhead expected for 64-bit loads as opposed to 32-bit ones, so pick this variant for code simplicity. Fixes: 231a35d37293 ("[MIPS] RM: Collected changes") Signed-off-by: Maciej W. Rozycki Signed-off-by: Thomas Bogendoerfer Signed-off-by: Sasha Levin --- arch/mips/fw/lib/call_o32.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/fw/lib/call_o32.S b/arch/mips/fw/lib/call_o32.S index ee856709e0b60..77533cfbdfc10 100644 --- a/arch/mips/fw/lib/call_o32.S +++ b/arch/mips/fw/lib/call_o32.S @@ -74,7 +74,7 @@ NESTED(call_o32, O32_FRAMESZ, ra) PTR_LA t1,6*O32_SZREG(fp) li t2,O32_ARGC-6 1: - lw t3,(t0) + ld t3,(t0) REG_ADDU t0,SZREG sw t3,(t1) REG_SUBU t2,1 From fa403d9617310b34361317072806a3f55e333827 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Mon, 4 May 2026 21:14:00 +0100 Subject: [PATCH 0986/2758] MIPS: DEC: Remove do_IRQ() call indirection [ Upstream commit 35554eadc1b127bb5294504a4ac8f3a5dd9e299d ] As from commit 8f99a1626535 ("MIPS: Tracing: Add IRQENTRY_EXIT section for MIPS") do_IRQ() is not a macro anymore and can be invoked directly from assembly code, as a tail call. Remove the dec_irq_dispatch() stub then and the indirection previously introduced with commit 187933f23679 ("[MIPS] do_IRQ cleanup"), improving performance by reducing the number of control flow changes and the overall instruction count, while fixing a compiler's complaint about a missing prototype for said stub: arch/mips/dec/setup.c:780:25: warning: no previous prototype for 'dec_irq_dispatch' [-Wmissing-prototypes] 780 | asmlinkage unsigned int dec_irq_dispatch(unsigned int irq) | ^~~~~~~~~~~~~~~~ (which gets promoted to a compilation error with CONFIG_WERROR). Fixes: 8f99a1626535 ("MIPS: Tracing: Add IRQENTRY_EXIT section for MIPS") Signed-off-by: Maciej W. Rozycki Signed-off-by: Thomas Bogendoerfer Signed-off-by: Sasha Levin --- arch/mips/dec/int-handler.S | 2 +- arch/mips/dec/setup.c | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/arch/mips/dec/int-handler.S b/arch/mips/dec/int-handler.S index 011d1d678840a..a0b439c90488f 100644 --- a/arch/mips/dec/int-handler.S +++ b/arch/mips/dec/int-handler.S @@ -277,7 +277,7 @@ srlv t3,t1,t2 handle_it: - j dec_irq_dispatch + j do_IRQ nop #if defined(CONFIG_32BIT) && defined(CONFIG_MIPS_FP_SUPPORT) diff --git a/arch/mips/dec/setup.c b/arch/mips/dec/setup.c index 87f0a1436bf9c..abe42616498db 100644 --- a/arch/mips/dec/setup.c +++ b/arch/mips/dec/setup.c @@ -776,9 +776,3 @@ void __init arch_init_irq(void) pr_err("Failed to register halt interrupt\n"); } } - -asmlinkage unsigned int dec_irq_dispatch(unsigned int irq) -{ - do_IRQ(irq); - return 0; -} From 9a092e2d344fd60076a5a23bdb081a74395bc883 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 7 May 2026 21:27:24 -0700 Subject: [PATCH 0987/2758] mips: ralink: mt7621: add missing __iomem [ Upstream commit 4ddaf88aadd3bd09c2eb3734c53d2864af6b144e ] raw_readl and writel calls expect pointers annotated with __iomem. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild/202211060456.cnV6IK6G-lkp@intel.com/ Fixes: cc19db8b312a ("MIPS: ralink: mt7621: do memory detection on KSEG1") Signed-off-by: Rosen Penev Reviewed-by: Sergio Paracuellos Signed-off-by: Thomas Bogendoerfer Signed-off-by: Sasha Levin --- arch/mips/ralink/mt7621.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/ralink/mt7621.c b/arch/mips/ralink/mt7621.c index a4bdda8541c07..ae7b8cfedd5f7 100644 --- a/arch/mips/ralink/mt7621.c +++ b/arch/mips/ralink/mt7621.c @@ -63,7 +63,7 @@ phys_addr_t mips_cpc_default_phys_base(void) static bool __init mt7621_addr_wraparound_test(phys_addr_t size) { - void *dm = (void *)KSEG1ADDR(&detect_magic); + void __iomem *dm = (void __iomem *)KSEG1ADDR(&detect_magic); if (CPHYSADDR(dm + size) >= MT7621_LOWMEM_MAX_SIZE) return true; From 4f65b01eb631b9307ca6c7e7f4aec29460eaeacf Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Fri, 8 May 2026 20:57:27 -0700 Subject: [PATCH 0988/2758] mips: n64: add __iomem for writel call [ Upstream commit 579f5329d5df2dbcf4bb5ef398701c2501d24892 ] sparse: incorrect type in argument 2 (different address spaces) @@ expected void volatile [noderef] __iomem *mem @@ got unsigned int [usertype] * Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202105261445.AcvPd2EE-lkp@intel.com/ Fixes: baec970aa5ba ("mips: Add N64 machine type") Signed-off-by: Rosen Penev Signed-off-by: Thomas Bogendoerfer Signed-off-by: Sasha Levin --- arch/mips/n64/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/n64/init.c b/arch/mips/n64/init.c index dfbd864f46670..66ec28ab41f32 100644 --- a/arch/mips/n64/init.c +++ b/arch/mips/n64/init.c @@ -50,7 +50,7 @@ void __init prom_init(void) #define W 320 #define H 240 -#define REG_BASE ((u32 *) CKSEG1ADDR(0x4400000)) +#define REG_BASE ((u32 __iomem *) CKSEG1ADDR(0x4400000)) static void __init n64rdp_write_reg(const u8 reg, const u32 value) { From 3890afc64cb5afd321eab9206b773ab9c6aa7bcb Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 25 May 2026 03:23:21 +0200 Subject: [PATCH 0989/2758] driver core: Fix missing jiffies conversion in deferred_probe_extend_timeout() [ Upstream commit f9e6da99fe49277979798a1c3b9790ae10aaa18a ] mod_delayed_work() takes jiffies, not seconds. Thus, restore the dropped conversion. While at it, fix incorrect indentation. Fixes: 1137838865bf ("driver core: Use mod_delayed_work to prevent lost deferred probe work") Tested-by: Biju Das Tested-by: Geert Uytterhoeven Reviewed-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20260525012340.3860581-1-dakr@kernel.org Signed-off-by: Danilo Krummrich Signed-off-by: Sasha Levin --- drivers/base/dd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 3523a5d8ec503..6ced7272b97e3 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -324,7 +324,7 @@ void deferred_probe_extend_timeout(void) * start a new one. */ if (mod_delayed_work(system_wq, &deferred_probe_timeout_work, - driver_deferred_probe_timeout)) + secs_to_jiffies(driver_deferred_probe_timeout))) pr_debug("Extended deferred probe timeout by %d secs\n", driver_deferred_probe_timeout); } From 4f72ebf36e72cb28c659f54340f92f81cf7b1deb Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 25 May 2026 03:23:22 +0200 Subject: [PATCH 0990/2758] driver core: Guard deferred probe timeout extension with delayed_work_pending() [ Upstream commit 557495bc879013c3d5e21d667e987e7ce3a514de ] mod_delayed_work() unconditionally queues the work even when it wasn't previously pending, which can fire the timeout prematurely or restart it after it already fired. Add a delayed_work_pending() guard to restore the originally intended semantics. Premature firing calls fw_devlink_drivers_done() before all built-in drivers have registered, causing fw_devlink to prematurely relax device links for suppliers whose drivers haven't loaded yet. Fixes: 1137838865bf ("driver core: Use mod_delayed_work to prevent lost deferred probe work") Tested-by: Geert Uytterhoeven Reviewed-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20260525012340.3860581-2-dakr@kernel.org Signed-off-by: Danilo Krummrich Signed-off-by: Sasha Levin --- drivers/base/dd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 6ced7272b97e3..f64175afefc91 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -323,7 +323,8 @@ void deferred_probe_extend_timeout(void) * If the work hasn't been queued yet or if the work expired, don't * start a new one. */ - if (mod_delayed_work(system_wq, &deferred_probe_timeout_work, + if (delayed_work_pending(&deferred_probe_timeout_work) && + mod_delayed_work(system_wq, &deferred_probe_timeout_work, secs_to_jiffies(driver_deferred_probe_timeout))) pr_debug("Extended deferred probe timeout by %d secs\n", driver_deferred_probe_timeout); From d9aa1b2d79524802da5a840e6c867e13203b1e5d Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 19 May 2026 09:47:06 +0100 Subject: [PATCH 0991/2758] arm64: tegra: Fix address of Tegra264 main GPIO controller [ Upstream commit 7f2eeae12690aaee6195f85ed129a29c17f156d1 ] The 64-bit address of the main GPIO controller on Tegra264 is 0x810c300000. The main GPIO controller was incorrectly added under the bus@0 node instead of the bus@8100000000 node breaking the boot on Tegra264. Fix this by moving to main GPIO controller node under bus@8100000000. Fixes: c70e6bc11d20 ("arm64: tegra: Add Tegra264 GPIO controllers") Signed-off-by: Jon Hunter Signed-off-by: Thierry Reding Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/nvidia/tegra264.dtsi | 88 ++++++++++++------------ 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/arch/arm64/boot/dts/nvidia/tegra264.dtsi b/arch/arm64/boot/dts/nvidia/tegra264.dtsi index 06d8357bdf527..2d8e7e37830ff 100644 --- a/arch/arm64/boot/dts/nvidia/tegra264.dtsi +++ b/arch/arm64/boot/dts/nvidia/tegra264.dtsi @@ -3277,50 +3277,6 @@ status = "disabled"; }; - gpio_main: gpio@c300000 { - compatible = "nvidia,tegra264-gpio"; - reg = <0x00 0x0c300000 0x0 0x4000>, - <0x00 0x0c310000 0x0 0x4000>; - reg-names = "security", "gpio"; - wakeup-parent = <&pmc>; - interrupts = , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - ; - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - }; - serial@c4e0000 { compatible = "nvidia,tegra264-utc"; reg = <0x0 0x0c4e0000 0x0 0x8000>, @@ -3586,6 +3542,50 @@ status = "disabled"; }; + gpio_main: gpio@c300000 { + compatible = "nvidia,tegra264-gpio"; + reg = <0x00 0x0c300000 0x0 0x4000>, + <0x00 0x0c310000 0x0 0x4000>; + reg-names = "security", "gpio"; + wakeup-parent = <&pmc>; + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + i2c14: i2c@c410000 { compatible = "nvidia,tegra264-i2c"; reg = <0x00 0x0c410000 0x0 0x10000>; From 71304ef1ffe188725e7030356d0be5b2cfca76cf Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:25 +0200 Subject: [PATCH 0992/2758] mtd: spi-nor: debugfs: Fix the flags list [ Upstream commit 829dff83597615208aedf0f5abb3878b47f2314d ] As mentioned above the spi_nor_option_flags enumeration in core.h, this list should be kept in sync with the one in the core. Add the missing flag. Fixes: 6a42bc97ccda ("mtd: spi-nor: core: Allow specifying the byte order in Octal DTR mode") Reviewed-by: Michael Walle Signed-off-by: Miquel Raynal Signed-off-by: Pratyush Yadav Signed-off-by: Sasha Levin --- drivers/mtd/spi-nor/debugfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/spi-nor/debugfs.c b/drivers/mtd/spi-nor/debugfs.c index 14ba1680c3154..c0bd8f1149a51 100644 --- a/drivers/mtd/spi-nor/debugfs.c +++ b/drivers/mtd/spi-nor/debugfs.c @@ -29,6 +29,7 @@ static const char *const snor_f_names[] = { SNOR_F_NAME(RWW), SNOR_F_NAME(ECC), SNOR_F_NAME(NO_WP), + SNOR_F_NAME(SWAP16), }; #undef SNOR_F_NAME From 6d5dbd41ae1d5fbe28aa17f00c2a1aaecb041992 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:27 +0200 Subject: [PATCH 0993/2758] mtd: spi-nor: Drop duplicate Kconfig dependency [ Upstream commit a6470e2162e9c3779a4bd6ff3bed1b81d796e46e ] I do not think the MTD dependency is needed twice. This is likely a duplicate coming from a former rebase when the spi-nor core got cleaned up a while ago. Remove the extra line. Fixes: b35b9a10362d ("mtd: spi-nor: Move m25p80 code in spi-nor.c") Signed-off-by: Miquel Raynal Reviewed-by: Michael Walle Signed-off-by: Pratyush Yadav Signed-off-by: Sasha Levin --- drivers/mtd/spi-nor/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 24cd25de2b8b7..fd05a24d64a96 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only menuconfig MTD_SPI_NOR tristate "SPI NOR device support" - depends on MTD depends on MTD && SPI_MASTER select SPI_MEM help From e392eafcc2f843f14b532a8984edf86027033064 Mon Sep 17 00:00:00 2001 From: Ripan Deuri Date: Wed, 20 May 2026 00:58:15 +0530 Subject: [PATCH 0994/2758] wifi: ath12k: fix error unwind on arch_init() failure in PCI probe [ Upstream commit d5c336161088c588f85da64f48ba6deead194afd ] When arch_init() fails in ath12k_pci_probe(), the code jumps to err_pci_msi_free, leaking resources in teardown. Redirect the failure path to err_free_irq so teardown matches the setup order. Compile-tested only. Fixes: 614c23e24ee8 ("wifi: ath12k: Support arch-specific DP device allocation") Signed-off-by: Ripan Deuri Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260519192815.3911324-1-ripan.deuri@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath12k/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c index 375277ca2b892..d9a22d6afbb02 100644 --- a/drivers/net/wireless/ath/ath12k/pci.c +++ b/drivers/net/wireless/ath/ath12k/pci.c @@ -1639,7 +1639,7 @@ static int ath12k_pci_probe(struct pci_dev *pdev, ret = ab_pci->device_family_ops->arch_init(ab); if (ret) { ath12k_err(ab, "PCI arch_init failed %d\n", ret); - goto err_pci_msi_free; + goto err_free_irq; } ret = ath12k_core_init(ab); From daf480c6fe154e2d03ef693fec8b2487adc3744c Mon Sep 17 00:00:00 2001 From: Zhongqiu Han Date: Sun, 19 Apr 2026 21:26:53 +0800 Subject: [PATCH 0995/2758] cpufreq: governor: Fix data races on per-CPU idle/nice baselines [ Upstream commit 91f5d698478f3d07230cf9ca4dfaf67e0316a53d ] gov_update_cpu_data() resets per-CPU prev_cpu_idle for every CPU in the governed domain, and conditionally resets prev_cpu_nice when ignore_nice_load is set. It is called from sysfs store callbacks (e.g. ignore_nice_load_store) which run under attr_set->update_lock, held by the surrounding governor_store(). Concurrently, dbs_work_handler() calls gov->gov_dbs_update() (which calls dbs_update()) under policy_dbs->update_mutex. dbs_update() both reads and writes the same prev_cpu_idle / prev_cpu_nice fields. The potential race path is: Path A (sysfs write, holds attr_set->update_lock only): governor_store() mutex_lock(&attr_set->update_lock) ignore_nice_load_store() dbs_data->ignore_nice_load = input gov_update_cpu_data(dbs_data) list_for_each_entry(policy_dbs, ...) for_each_cpu(j, ...) j_cdbs->prev_cpu_idle = get_cpu_idle_time(...) /* write */ j_cdbs->prev_cpu_nice = kcpustat_field(...) /* write */ mutex_unlock(&attr_set->update_lock) Path B (work queue, holds policy_dbs->update_mutex only): dbs_work_handler() mutex_lock(&policy_dbs->update_mutex) gov->gov_dbs_update(policy) dbs_update() for_each_cpu(j, policy->cpus) idle_time = cur - j_cdbs->prev_cpu_idle /* read */ j_cdbs->prev_cpu_idle = cur_idle_time /* write */ idle_time += cur_nice - j_cdbs->prev_cpu_nice /* read */ j_cdbs->prev_cpu_nice = cur_nice /* write */ mutex_unlock(&policy_dbs->update_mutex) Because attr_set->update_lock and policy_dbs->update_mutex are two completely independent locks, the two paths are not mutually exclusive. This results in a data race on cpu_dbs_info.prev_cpu_idle and cpu_dbs_info.prev_cpu_nice. Fix this by also acquiring policy_dbs->update_mutex in gov_update_cpu_data() for each policy, so that path A participates in the mutual exclusion already established by dbs_work_handler(). Also update the function comment to accurately reflect the two-level locking contract. Additionally, cpufreq_dbs_governor_start() initializes prev_cpu_idle using io_busy read from dbs_data->io_is_busy without holding policy_dbs->update_mutex. A concurrent io_is_busy_store() can update io_is_busy and call gov_update_cpu_data(), which writes prev_cpu_idle with the new value under the mutex. cpufreq_dbs_governor_start() then overwrites prev_cpu_idle with the stale io_busy value, leaving the baseline inconsistent with the tunable. Fix this by reading io_busy inside the mutex. The root of this race dates back to the original ondemand/conservative governors. Before commit ee88415caf73 ("[CPUFREQ] Cleanup locking in conservative governor") and commit 5a75c82828e7 ("[CPUFREQ] Cleanup locking in ondemand governor"), all accesses to prev_cpu_idle and prev_cpu_nice in cpufreq_governor_dbs() (path X), store_ignore_nice_load() /io_is_busy_store() (path Y), and do_dbs_timer() (path Z) were serialised by the same dbs_mutex, so no race existed. Those two commits switched do_dbs_timer() from dbs_mutex to a per-policy/per-cpu timer_mutex to reduce lock contention, but left path Y (store) still holding dbs_mutex. As a result, path Y (store) and path Z (do_dbs_timer) no longer shared a common lock, introducing a potential race on prev_cpu_idle/prev_cpu_nice between path Y (store) and dbs_check_cpu(). Commit 326c86deaed54a ("[CPUFREQ] Remove unneeded locks") then removed dbs_mutex from store_ignore_nice_load()/io_is_busy_store() entirely, introducing an additional potential race between path Y (now lockless) and cpufreq_governor_dbs() (path X, still holding dbs_mutex), while the race between path Y and path Z remained. Fixes: ee88415caf736b ("[CPUFREQ] Cleanup locking in conservative governor") Fixes: 5a75c82828e7c0 ("[CPUFREQ] Cleanup locking in ondemand governor") Fixes: 326c86deaed54a ("[CPUFREQ] Remove unneeded locks") Signed-off-by: Zhongqiu Han Link: https://patch.msgid.link/20260419132655.3800673-2-zhongqiu.han@oss.qualcomm.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/cpufreq/cpufreq_governor.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c index 86f35e4519142..fc6f705c5a9c7 100644 --- a/drivers/cpufreq/cpufreq_governor.c +++ b/drivers/cpufreq/cpufreq_governor.c @@ -90,7 +90,8 @@ EXPORT_SYMBOL_GPL(sampling_rate_store); * (that may be a single policy or a bunch of them if governor tunables are * system-wide). * - * Call under the @dbs_data mutex. + * Call under the @dbs_data->attr_set.update_lock. The per-policy + * update_mutex is acquired and released internally for each policy. */ void gov_update_cpu_data(struct dbs_data *dbs_data) { @@ -99,6 +100,7 @@ void gov_update_cpu_data(struct dbs_data *dbs_data) list_for_each_entry(policy_dbs, &dbs_data->attr_set.policy_list, list) { unsigned int j; + mutex_lock(&policy_dbs->update_mutex); for_each_cpu(j, policy_dbs->policy->cpus) { struct cpu_dbs_info *j_cdbs = &per_cpu(cpu_dbs, j); @@ -107,6 +109,7 @@ void gov_update_cpu_data(struct dbs_data *dbs_data) if (dbs_data->ignore_nice_load) j_cdbs->prev_cpu_nice = kcpustat_field(&kcpustat_cpu(j), CPUTIME_NICE, j); } + mutex_unlock(&policy_dbs->update_mutex); } } EXPORT_SYMBOL_GPL(gov_update_cpu_data); @@ -527,8 +530,9 @@ int cpufreq_dbs_governor_start(struct cpufreq_policy *policy) sampling_rate = dbs_data->sampling_rate; ignore_nice = dbs_data->ignore_nice_load; - io_busy = dbs_data->io_is_busy; + mutex_lock(&policy_dbs->update_mutex); + io_busy = dbs_data->io_is_busy; for_each_cpu(j, policy->cpus) { struct cpu_dbs_info *j_cdbs = &per_cpu(cpu_dbs, j); @@ -541,6 +545,7 @@ int cpufreq_dbs_governor_start(struct cpufreq_policy *policy) if (ignore_nice) j_cdbs->prev_cpu_nice = kcpustat_field(&kcpustat_cpu(j), CPUTIME_NICE, j); } + mutex_unlock(&policy_dbs->update_mutex); gov->start(policy); From 97f35b0a523d1668a24e204372134e62289a320f Mon Sep 17 00:00:00 2001 From: Zhongqiu Han Date: Sun, 19 Apr 2026 21:26:54 +0800 Subject: [PATCH 0996/2758] cpufreq: governor: Fix stale prev_cpu_nice spike when enabling ignore_nice_load [ Upstream commit 24fc5870808dea4290e9563746cb5f2146043c6c ] When ignore_nice_load is toggled from 0 to 1 via sysfs, dbs_update() may run concurrently and observe the new tunable value while prev_cpu_nice still holds a stale baseline, producing a spurious massive idle_time that results in an incorrect CPU load value. The race can be illustrated with two concurrent paths: Path A (sysfs write, holds attr_set->update_lock): governor_store() mutex_lock(&attr_set->update_lock) ignore_nice_load_store() dbs_data->ignore_nice_load = 1 /* (A1) */ gov_update_cpu_data(dbs_data) mutex_lock(&policy_dbs->update_mutex) /* (A2) */ j_cdbs->prev_cpu_nice = kcpustat_field(...) mutex_unlock(&policy_dbs->update_mutex) mutex_unlock(&attr_set->update_lock) Path B (work queue, wins the race between A1 and A2): dbs_work_handler() mutex_lock(&policy_dbs->update_mutex) /* acquired before A2 */ dbs_update() ignore_nice = dbs_data->ignore_nice_load /* sees new value: 1 */ cur_nice = kcpustat_field(...) idle_time += div_u64(cur_nice - j_cdbs->prev_cpu_nice, ..) /* stale */ j_cdbs->prev_cpu_nice = cur_nice mutex_unlock(&policy_dbs->update_mutex) Fix this by unconditionally sampling cur_nice and advancing prev_cpu_nice in dbs_update() on every call, regardless of ignore_nice. With prev_cpu_nice always reflecting the most recent sample, enabling ignore_nice_load can never produce a stale-baseline spike: the delta will always be the nice time accumulated in the last sampling interval, not since boot. The additional kcpustat_field() call per CPU per sample is negligible given that the sampling path already reads idle and load accounting. To keep prev_cpu_nice handling consistent with the always-tracking semantics introduced above: - gov_update_cpu_data() unconditionally resets prev_cpu_nice alongside prev_cpu_idle, so both baselines share the same timestamp when io_is_busy changes. This prevents an interval mismatch between idle_time and nice_delta on the next dbs_update() when ignore_nice_load is enabled. - cpufreq_dbs_governor_start() unconditionally initializes prev_cpu_nice so the baseline is always valid from the first dbs_update() call; remove the ignore_nice guard and the now-unused ignore_nice variable. Fixes: ee88415caf736b ("[CPUFREQ] Cleanup locking in conservative governor") Fixes: 5a75c82828e7c0 ("[CPUFREQ] Cleanup locking in ondemand governor") Fixes: 326c86deaed54a ("[CPUFREQ] Remove unneeded locks") Signed-off-by: Zhongqiu Han Link: https://patch.msgid.link/20260419132655.3800673-3-zhongqiu.han@oss.qualcomm.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/cpufreq/cpufreq_governor.c | 33 +++++++++++++++++++----------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c index fc6f705c5a9c7..8a85bd32defe7 100644 --- a/drivers/cpufreq/cpufreq_governor.c +++ b/drivers/cpufreq/cpufreq_governor.c @@ -92,6 +92,12 @@ EXPORT_SYMBOL_GPL(sampling_rate_store); * * Call under the @dbs_data->attr_set.update_lock. The per-policy * update_mutex is acquired and released internally for each policy. + * + * Note: prev_cpu_nice is reset here unconditionally alongside prev_cpu_idle. + * When io_is_busy changes, both baselines must be advanced to the same + * timestamp so that the next dbs_update() computes idle_time and nice_delta + * over the same interval, preventing an artificially inflated idle_time when + * ignore_nice_load is enabled. */ void gov_update_cpu_data(struct dbs_data *dbs_data) { @@ -106,8 +112,7 @@ void gov_update_cpu_data(struct dbs_data *dbs_data) j_cdbs->prev_cpu_idle = get_cpu_idle_time(j, &j_cdbs->prev_update_time, dbs_data->io_is_busy); - if (dbs_data->ignore_nice_load) - j_cdbs->prev_cpu_nice = kcpustat_field(&kcpustat_cpu(j), CPUTIME_NICE, j); + j_cdbs->prev_cpu_nice = kcpustat_field(&kcpustat_cpu(j), CPUTIME_NICE, j); } mutex_unlock(&policy_dbs->update_mutex); } @@ -121,6 +126,7 @@ unsigned int dbs_update(struct cpufreq_policy *policy) unsigned int ignore_nice = dbs_data->ignore_nice_load; unsigned int max_load = 0, idle_periods = UINT_MAX; unsigned int sampling_rate, io_busy, j; + u64 cur_nice; /* * Sometimes governors may use an additional multiplier to increase @@ -167,12 +173,18 @@ unsigned int dbs_update(struct cpufreq_policy *policy) j_cdbs->prev_cpu_idle = cur_idle_time; - if (ignore_nice) { - u64 cur_nice = kcpustat_field(&kcpustat_cpu(j), CPUTIME_NICE, j); - + /* + * Always sample cur_nice and advance prev_cpu_nice, regardless + * of ignore_nice. This keeps prev_cpu_nice current so that + * enabling ignore_nice_load via sysfs never produces a + * stale-baseline spike (the delta will be at most one sampling + * interval of accumulated nice time, not since boot). + */ + cur_nice = kcpustat_field(&kcpustat_cpu(j), CPUTIME_NICE, j); + if (ignore_nice) idle_time += div_u64(cur_nice - j_cdbs->prev_cpu_nice, NSEC_PER_USEC); - j_cdbs->prev_cpu_nice = cur_nice; - } + + j_cdbs->prev_cpu_nice = cur_nice; if (unlikely(!time_elapsed)) { /* @@ -519,7 +531,7 @@ int cpufreq_dbs_governor_start(struct cpufreq_policy *policy) struct dbs_governor *gov = dbs_governor_of(policy); struct policy_dbs_info *policy_dbs = policy->governor_data; struct dbs_data *dbs_data = policy_dbs->dbs_data; - unsigned int sampling_rate, ignore_nice, j; + unsigned int sampling_rate, j; unsigned int io_busy; if (!policy->cur) @@ -529,7 +541,6 @@ int cpufreq_dbs_governor_start(struct cpufreq_policy *policy) policy_dbs->rate_mult = 1; sampling_rate = dbs_data->sampling_rate; - ignore_nice = dbs_data->ignore_nice_load; mutex_lock(&policy_dbs->update_mutex); io_busy = dbs_data->io_is_busy; @@ -541,9 +552,7 @@ int cpufreq_dbs_governor_start(struct cpufreq_policy *policy) * Make the first invocation of dbs_update() compute the load. */ j_cdbs->prev_load = 0; - - if (ignore_nice) - j_cdbs->prev_cpu_nice = kcpustat_field(&kcpustat_cpu(j), CPUTIME_NICE, j); + j_cdbs->prev_cpu_nice = kcpustat_field(&kcpustat_cpu(j), CPUTIME_NICE, j); } mutex_unlock(&policy_dbs->update_mutex); From b1ff4649fa5a1170b8da47303b07ed0e8f4968d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Tue, 26 May 2026 09:48:26 -0300 Subject: [PATCH 0997/2758] ALSA: xen-front: Reset event channel state on stream clear MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 9cd81152373c560b8aa8299b0705c4db82b103b7 ] xen_snd_front_evtchnl_pair_clear() resets evt_next_id for both channels. That is correct for the request channel, where evt_next_id is used to allocate the next request id. It is wrong for the event channel: incoming events are validated against evt_id, and evt_id is incremented by evtchnl_interrupt_evt(). This leaves the expected event id from the previous stream instance. A backend that restarts event ids for a reopened stream can then have valid current-position events dropped until the stale frontend id catches up. Reset evt_id for the event channel. Also advance the event-page consumer to the current producer while clearing the stream, so obsolete events queued for the previous stream instance are not delivered to the next ALSA runtime. Fixes: 1cee559351a7 ("ALSA: xen-front: Implement ALSA virtual sound driver") Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260526-alsa-xen-event-channel-fixes-v1-1-91d3a6a50778@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/xen/xen_snd_front_evtchnl.c | 8 ++++++-- sound/xen/xen_snd_front_evtchnl.h | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/sound/xen/xen_snd_front_evtchnl.c b/sound/xen/xen_snd_front_evtchnl.c index bc03f71bf16e3..09e4c1d056363 100644 --- a/sound/xen/xen_snd_front_evtchnl.c +++ b/sound/xen/xen_snd_front_evtchnl.c @@ -456,7 +456,11 @@ void xen_snd_front_evtchnl_pair_clear(struct xen_snd_front_evtchnl_pair *evt_pai } scoped_guard(mutex, &evt_pair->evt.ring_io_lock) { - evt_pair->evt.evt_next_id = 0; + evt_pair->evt.evt_id = 0; + /* Drop obsolete events queued for the previous stream instance. */ + evt_pair->evt.u.evt.page->in_cons = + evt_pair->evt.u.evt.page->in_prod; + /* Ensure the consumer index is visible before stream reuse. */ + virt_wmb(); } } - diff --git a/sound/xen/xen_snd_front_evtchnl.h b/sound/xen/xen_snd_front_evtchnl.h index 3675fba705647..8400261ac4660 100644 --- a/sound/xen/xen_snd_front_evtchnl.h +++ b/sound/xen/xen_snd_front_evtchnl.h @@ -37,9 +37,9 @@ struct xen_snd_front_evtchnl { /* State of the event channel. */ enum xen_snd_front_evtchnl_state state; enum xen_snd_front_evtchnl_type type; - /* Either response id or incoming event id. */ + /* Current response id or next expected incoming event id. */ u16 evt_id; - /* Next request id or next expected event id. */ + /* Next request id. */ u16 evt_next_id; /* Shared ring access lock. */ struct mutex ring_io_lock; From 05f62f9d6ab404979b9e3349637a6e00a12a6ff7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Tue, 26 May 2026 09:48:27 -0300 Subject: [PATCH 0998/2758] ALSA: xen-front: Connect event channel after stream prepare MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 3624f0bd4af15a820b1bd88b489980fa9fd61b7a ] The request channel must be connected from ALSA .open(), because hw-rule queries and the stream open request use it. The event channel is different: XENSND_EVT_CUR_POS handling uses ALSA runtime buffer and period geometry, and the corresponding Xen stream parameters are not submitted to the backend until .prepare() sends XENSND_OP_OPEN. Currently .open() connects both channels. A backend current-position event, or a stale event queued for an earlier stream instance, can therefore reach xen_snd_front_alsa_handle_cur_pos() before runtime->buffer_size and runtime->period_size are valid. Add a per-channel connection helper, connect only the request channel in .open(), connect the event channel after a successful stream prepare, and disconnect it before stream close/free. Re-check the event-channel state after taking ring_io_lock so disconnecting the event channel synchronizes against a threaded IRQ that passed the initial lockless state test. Keep defensive runtime geometry checks in the position handler. Fixes: 1cee559351a7 ("ALSA: xen-front: Implement ALSA virtual sound driver") Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260526-alsa-xen-event-channel-fixes-v1-2-91d3a6a50778@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/xen/xen_snd_front_alsa.c | 17 ++++++++++++----- sound/xen/xen_snd_front_evtchnl.c | 20 +++++++++++++------- sound/xen/xen_snd_front_evtchnl.h | 2 ++ 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/sound/xen/xen_snd_front_alsa.c b/sound/xen/xen_snd_front_alsa.c index dc626480123ac..a6dd196f73d66 100644 --- a/sound/xen/xen_snd_front_alsa.c +++ b/sound/xen/xen_snd_front_alsa.c @@ -378,7 +378,7 @@ static int alsa_open(struct snd_pcm_substream *substream) stream_clear(stream); - xen_snd_front_evtchnl_pair_set_connected(stream->evt_pair, true); + xen_snd_front_evtchnl_set_connected(&stream->evt_pair->req, true); ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, alsa_hw_rule, stream, @@ -498,6 +498,8 @@ static int alsa_hw_free(struct snd_pcm_substream *substream) struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); int ret; + xen_snd_front_evtchnl_set_connected(&stream->evt_pair->evt, false); + ret = xen_snd_front_stream_close(&stream->evt_pair->req); stream_free(stream); return ret; @@ -532,6 +534,7 @@ static int alsa_prepare(struct snd_pcm_substream *substream) return ret; stream->is_open = true; + xen_snd_front_evtchnl_set_connected(&stream->evt_pair->evt, true); } return 0; @@ -571,20 +574,24 @@ void xen_snd_front_alsa_handle_cur_pos(struct xen_snd_front_evtchnl *evtchnl, { struct snd_pcm_substream *substream = evtchnl->u.evt.substream; struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); + struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t delta, new_hw_ptr, cur_frame; - cur_frame = bytes_to_frames(substream->runtime, pos_bytes); + if (!runtime->buffer_size || !runtime->period_size) + return; + + cur_frame = bytes_to_frames(runtime, pos_bytes); delta = cur_frame - stream->be_cur_frame; stream->be_cur_frame = cur_frame; new_hw_ptr = (snd_pcm_uframes_t)atomic_read(&stream->hw_ptr); - new_hw_ptr = (new_hw_ptr + delta) % substream->runtime->buffer_size; + new_hw_ptr = (new_hw_ptr + delta) % runtime->buffer_size; atomic_set(&stream->hw_ptr, (int)new_hw_ptr); stream->out_frames += delta; - if (stream->out_frames > substream->runtime->period_size) { - stream->out_frames %= substream->runtime->period_size; + if (stream->out_frames > runtime->period_size) { + stream->out_frames %= runtime->period_size; snd_pcm_period_elapsed(substream); } } diff --git a/sound/xen/xen_snd_front_evtchnl.c b/sound/xen/xen_snd_front_evtchnl.c index 09e4c1d056363..17a30452c0cca 100644 --- a/sound/xen/xen_snd_front_evtchnl.c +++ b/sound/xen/xen_snd_front_evtchnl.c @@ -94,6 +94,9 @@ static irqreturn_t evtchnl_interrupt_evt(int irq, void *dev_id) guard(mutex)(&channel->ring_io_lock); + if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED)) + return IRQ_HANDLED; + prod = page->in_prod; /* Ensure we see ring contents up to prod. */ virt_rmb(); @@ -430,8 +433,8 @@ int xen_snd_front_evtchnl_publish_all(struct xen_snd_front_info *front_info) return ret; } -void xen_snd_front_evtchnl_pair_set_connected(struct xen_snd_front_evtchnl_pair *evt_pair, - bool is_connected) +void xen_snd_front_evtchnl_set_connected(struct xen_snd_front_evtchnl *channel, + bool is_connected) { enum xen_snd_front_evtchnl_state state; @@ -440,13 +443,16 @@ void xen_snd_front_evtchnl_pair_set_connected(struct xen_snd_front_evtchnl_pair else state = EVTCHNL_STATE_DISCONNECTED; - scoped_guard(mutex, &evt_pair->req.ring_io_lock) { - evt_pair->req.state = state; + scoped_guard(mutex, &channel->ring_io_lock) { + channel->state = state; } +} - scoped_guard(mutex, &evt_pair->evt.ring_io_lock) { - evt_pair->evt.state = state; - } +void xen_snd_front_evtchnl_pair_set_connected(struct xen_snd_front_evtchnl_pair *evt_pair, + bool is_connected) +{ + xen_snd_front_evtchnl_set_connected(&evt_pair->req, is_connected); + xen_snd_front_evtchnl_set_connected(&evt_pair->evt, is_connected); } void xen_snd_front_evtchnl_pair_clear(struct xen_snd_front_evtchnl_pair *evt_pair) diff --git a/sound/xen/xen_snd_front_evtchnl.h b/sound/xen/xen_snd_front_evtchnl.h index 8400261ac4660..f6ebdb09c0298 100644 --- a/sound/xen/xen_snd_front_evtchnl.h +++ b/sound/xen/xen_snd_front_evtchnl.h @@ -77,6 +77,8 @@ void xen_snd_front_evtchnl_free_all(struct xen_snd_front_info *front_info); int xen_snd_front_evtchnl_publish_all(struct xen_snd_front_info *front_info); void xen_snd_front_evtchnl_flush(struct xen_snd_front_evtchnl *evtchnl); +void xen_snd_front_evtchnl_set_connected(struct xen_snd_front_evtchnl *channel, + bool is_connected); void xen_snd_front_evtchnl_pair_set_connected(struct xen_snd_front_evtchnl_pair *evt_pair, bool is_connected); From 7aad70cabd8f34cf11a9593fcd3f2ac3f5496943 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 26 May 2026 17:28:41 +0200 Subject: [PATCH 0999/2758] ALSA: seq: oss: Fix UAF at handling events with embedded SysEx data [ Upstream commit 7c349b4f2a603202fb8c363bd2774a22ac2fddf3 ] The OSS sequencer processes the input MIDI bytes into a sequencer event to be dispatched later (in snd_seq_oss_midi_putc() called from snd_seq_oss_process_event()). When it's a SysEx data, the event record contains data.ext.ptr pointer to the original SysEx bytes, and the referred data is copied into the pool afterwards at dispatching. The problem is that, if the sequencer port gets closed concurrently before the dispatch, the OSS sequencer core also releases the resources (in snd_seq_oss_midi_check_exit_port()), while the pending event may hold a stale pointer, eventually leading to a UAF at a later dispatch. Fortunately, there is already a refcounting mechanism (snd_use_lock_t) for the OSS MIDI device access, and for addressing the issue above, we just need to extend the refcount until the event gets dispatched. This patch extends snd_seq_oss_process_event() to give back the refcount object, which is in turn released after calling the sequencer dispatcher with the given event in the caller side. According to the original report, KASAN report as below: KASAN slab-use-after-free in snd_seq_event_dup+0x40c/0x470 RIP: 0033:0x7f2cb66a6340 Read of size 6 Call trace: dump_stack_lvl+0x73/0xb0 (?:?) print_report+0xd1/0x650 (?:?) srso_alias_return_thunk+0x5/0xfbef5 (?:?) __virt_addr_valid+0x1a7/0x340 (?:?) kasan_complete_mode_report_info+0x64/0x200 (?:?) kasan_report+0xf7/0x130 (?:?) snd_seq_event_dup+0x40c/0x470 (?:?) kasan_check_range+0x10c/0x1c0 (?:?) __asan_memcpy+0x27/0x70 (?:?) snd_seq_event_dup+0x9/0x470 (?:?) snd_seq_client_enqueue_event+0x139/0x240 (?:?) _raw_spin_unlock_irqrestore+0x4b/0x60 (?:?) snd_seq_kernel_client_enqueue+0x102/0x120 (?:?) snd_seq_oss_write+0x416/0x4e0 (?:?) apparmor_file_permission+0x20/0x30 (?:?) odev_write+0x3b/0x60 (?:?) vfs_write+0x1ce/0x850 (?:?) lock_release+0xc8/0x2a0 (?:?) __kasan_check_write+0x18/0x20 (?:?) __mutex_unlock_slowpath+0x129/0x510 (?:?) ksys_write+0xe1/0x180 (?:?) mutex_unlock+0x16/0x20 (?:?) odev_ioctl+0x65/0xc0 (?:?) __x64_sys_write+0x46/0x60 (?:?) x64_sys_call+0x7d/0x20d0 (?:?) do_syscall_64+0xc1/0x360 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f (?:?) Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-and-tested-by: Zhang Cen Closes: https://lore.kernel.org/20260521233900.478153-1-rollkingzzc@gmail.com Link: https://patch.msgid.link/20260526152843.617503-1-tiwai@suse.de Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/core/seq/oss/seq_oss_event.c | 6 ++++-- sound/core/seq/oss/seq_oss_event.h | 3 ++- sound/core/seq/oss/seq_oss_ioctl.c | 5 ++++- sound/core/seq/oss/seq_oss_midi.c | 6 +++++- sound/core/seq/oss/seq_oss_midi.h | 2 +- sound/core/seq/oss/seq_oss_rw.c | 5 ++++- 6 files changed, 20 insertions(+), 7 deletions(-) diff --git a/sound/core/seq/oss/seq_oss_event.c b/sound/core/seq/oss/seq_oss_event.c index 76fb81077eef7..122735862044d 100644 --- a/sound/core/seq/oss/seq_oss_event.c +++ b/sound/core/seq/oss/seq_oss_event.c @@ -39,8 +39,10 @@ static int set_echo_event(struct seq_oss_devinfo *dp, union evrec *rec, struct s */ int -snd_seq_oss_process_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev) +snd_seq_oss_process_event(struct seq_oss_devinfo *dp, union evrec *q, + struct snd_seq_event *ev, snd_use_lock_t **lockp) { + *lockp = NULL; switch (q->s.code) { case SEQ_EXTENDED: return extended_event(dp, q, ev); @@ -69,7 +71,7 @@ snd_seq_oss_process_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd if (snd_seq_oss_midi_open(dp, q->s.dev, SNDRV_SEQ_OSS_FILE_WRITE)) break; if (snd_seq_oss_midi_filemode(dp, q->s.dev) & SNDRV_SEQ_OSS_FILE_WRITE) - return snd_seq_oss_midi_putc(dp, q->s.dev, q->s.parm1, ev); + return snd_seq_oss_midi_putc(dp, q->s.dev, q->s.parm1, ev, lockp); break; case SEQ_ECHO: diff --git a/sound/core/seq/oss/seq_oss_event.h b/sound/core/seq/oss/seq_oss_event.h index b4f723949a170..a4524e51d0e9d 100644 --- a/sound/core/seq/oss/seq_oss_event.h +++ b/sound/core/seq/oss/seq_oss_event.h @@ -91,7 +91,8 @@ union evrec { #define ev_is_long(ev) ((ev)->s.code >= 128) #define ev_length(ev) ((ev)->s.code >= 128 ? LONG_EVENT_SIZE : SHORT_EVENT_SIZE) -int snd_seq_oss_process_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev); +int snd_seq_oss_process_event(struct seq_oss_devinfo *dp, union evrec *q, + struct snd_seq_event *ev, snd_use_lock_t **lockp); int snd_seq_oss_process_timer_event(struct seq_oss_timer *rec, union evrec *q); int snd_seq_oss_event_input(struct snd_seq_event *ev, int direct, void *private_data, int atomic, int hop); diff --git a/sound/core/seq/oss/seq_oss_ioctl.c b/sound/core/seq/oss/seq_oss_ioctl.c index ccf682689ec95..ce7a69d52b308 100644 --- a/sound/core/seq/oss/seq_oss_ioctl.c +++ b/sound/core/seq/oss/seq_oss_ioctl.c @@ -45,14 +45,17 @@ static int snd_seq_oss_oob_user(struct seq_oss_devinfo *dp, void __user *arg) { unsigned char ev[8]; struct snd_seq_event tmpev; + snd_use_lock_t *lock = NULL; if (copy_from_user(ev, arg, 8)) return -EFAULT; memset(&tmpev, 0, sizeof(tmpev)); snd_seq_oss_fill_addr(dp, &tmpev, dp->addr.client, dp->addr.port); tmpev.time.tick = 0; - if (! snd_seq_oss_process_event(dp, (union evrec *)ev, &tmpev)) { + if (!snd_seq_oss_process_event(dp, (union evrec *)ev, &tmpev, &lock)) { snd_seq_oss_dispatch(dp, &tmpev, 0, 0); + if (lock) + snd_use_lock_free(lock); } return 0; } diff --git a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c index b50a49ca42ff3..70f94df651446 100644 --- a/sound/core/seq/oss/seq_oss_midi.c +++ b/sound/core/seq/oss/seq_oss_midi.c @@ -593,7 +593,8 @@ send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, struct seq * non-zero : invalid - ignored */ int -snd_seq_oss_midi_putc(struct seq_oss_devinfo *dp, int dev, unsigned char c, struct snd_seq_event *ev) +snd_seq_oss_midi_putc(struct seq_oss_devinfo *dp, int dev, unsigned char c, + struct snd_seq_event *ev, snd_use_lock_t **lockp) { struct seq_oss_midi *mdev __free(seq_oss_midi) = get_mididev(dp, dev); @@ -602,6 +603,9 @@ snd_seq_oss_midi_putc(struct seq_oss_devinfo *dp, int dev, unsigned char c, stru return -ENODEV; if (snd_midi_event_encode_byte(mdev->coder, c, ev)) { snd_seq_oss_fill_addr(dp, ev, mdev->client, mdev->port); + /* the caller must release this later */ + *lockp = &mdev->use_lock; + snd_use_lock_use(*lockp); return 0; } return -EINVAL; diff --git a/sound/core/seq/oss/seq_oss_midi.h b/sound/core/seq/oss/seq_oss_midi.h index bcc1683773dfb..4819d4170bf6d 100644 --- a/sound/core/seq/oss/seq_oss_midi.h +++ b/sound/core/seq/oss/seq_oss_midi.h @@ -26,7 +26,7 @@ void snd_seq_oss_midi_open_all(struct seq_oss_devinfo *dp, int file_mode); int snd_seq_oss_midi_close(struct seq_oss_devinfo *dp, int dev); void snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev); int snd_seq_oss_midi_putc(struct seq_oss_devinfo *dp, int dev, unsigned char c, - struct snd_seq_event *ev); + struct snd_seq_event *ev, snd_use_lock_t **lockp); int snd_seq_oss_midi_input(struct snd_seq_event *ev, int direct, void *private); int snd_seq_oss_midi_filemode(struct seq_oss_devinfo *dp, int dev); int snd_seq_oss_midi_make_info(struct seq_oss_devinfo *dp, int dev, struct midi_info *inf); diff --git a/sound/core/seq/oss/seq_oss_rw.c b/sound/core/seq/oss/seq_oss_rw.c index 307ef98c44c7b..111c792bc72ca 100644 --- a/sound/core/seq/oss/seq_oss_rw.c +++ b/sound/core/seq/oss/seq_oss_rw.c @@ -153,6 +153,7 @@ insert_queue(struct seq_oss_devinfo *dp, union evrec *rec, struct file *opt) { int rc = 0; struct snd_seq_event event; + snd_use_lock_t *lock = NULL; /* if this is a timing event, process the current time */ if (snd_seq_oss_process_timer_event(dp->timer, rec)) @@ -164,7 +165,7 @@ insert_queue(struct seq_oss_devinfo *dp, union evrec *rec, struct file *opt) event.type = SNDRV_SEQ_EVENT_NOTEOFF; snd_seq_oss_fill_addr(dp, &event, dp->addr.client, dp->addr.port); - if (snd_seq_oss_process_event(dp, rec, &event)) + if (snd_seq_oss_process_event(dp, rec, &event, &lock)) return 0; /* invalid event - no need to insert queue */ event.time.tick = snd_seq_oss_timer_cur_tick(dp->timer); @@ -173,6 +174,8 @@ insert_queue(struct seq_oss_devinfo *dp, union evrec *rec, struct file *opt) else rc = snd_seq_kernel_client_enqueue(dp->cseq, &event, opt, !is_nonblock_mode(dp->file_mode)); + if (lock) + snd_use_lock_free(lock); return rc; } From d6fd2afb137f52bf00c5210cc44d08ed54dcffb4 Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Wed, 27 May 2026 14:29:48 +0800 Subject: [PATCH 1000/2758] ALSA: seq: midi: Serialize output teardown with event_input [ Upstream commit ef7607ab1c8adc6258fb1b27d08e26aecdc18a58 ] event_process_midi() borrows msynth->output_rfile.output and then passes the substream to dump_midi() and snd_rawmidi_kernel_write() without synchronizing with the output open/close transition. midisynth_use() also publishes output_rfile before snd_rawmidi_output_params() has finished. The last midisynth_unuse() can therefore release the same rawmidi file and free substream->runtime before snd_rawmidi_kernel_write1() takes its runtime buffer reference. That leaves the event_input path using a stale substream or runtime and can end in a NULL-deref or use-after-free. Fix this with two pieces of synchronization. Keep a short IRQ-safe spinlock only for publishing or clearing output_rfile and for pairing the output snapshot with an snd_use_lock_t reference. Once event_process_midi() has taken that in-flight reference, it drops the spinlock before calling snd_seq_dump_var_event(), dump_midi(), or snd_rawmidi_kernel_write(). midisynth_unuse() now detaches the visible rawmidi file under the same spinlock, waits for the in-flight writers to drain, and only then drains and releases the saved file. midisynth_use() likewise opens into a local snd_rawmidi_file and publishes it only after snd_rawmidi_output_params() succeeds. The buggy scenario involves two paths, with each column showing the order within that path: event_input path: last unuse path: 1. event_process_midi() snapshots 1. midisynth_unuse() starts output_rfile.output. tearing down output_rfile. 2. dump_midi() reaches 2. snd_rawmidi_kernel_release() snd_rawmidi_kernel_write() closes the output file. before runtime is pinned. 3. close_substream() frees 3. The callback keeps using substream->runtime. the borrowed substream. Validation reproduced this kernel report: KASAN null-ptr-deref in snd_rawmidi_kernel_write1+0x56/0x360 RIP: 0033:0x7fde7dd0837f RIP: 0010:snd_rawmidi_kernel_write1+0x56/0x360 Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Assisted-by: Codex:gpt-5.5 Signed-off-by: Zhang Cen Link: https://patch.msgid.link/20260527062948.3614025-1-rollkingzzc@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/core/seq/seq_midi.c | 55 +++++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c index ca3f5fc309927..2eb12199c92f9 100644 --- a/sound/core/seq/seq_midi.c +++ b/sound/core/seq/seq_midi.c @@ -24,6 +24,7 @@ Possible options for midisynth module: #include #include #include +#include "seq_lock.h" MODULE_AUTHOR("Frank van de Pol , Jaroslav Kysela "); MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI synth."); @@ -42,6 +43,8 @@ struct seq_midisynth { int device; int subdevice; struct snd_rawmidi_file input_rfile; + spinlock_t output_lock; /* protects output_rfile publication */ + snd_use_lock_t output_use_lock; /* in-flight event_input users */ struct snd_rawmidi_file output_rfile; int seq_client; int seq_port; @@ -125,31 +128,42 @@ static int event_process_midi(struct snd_seq_event *ev, int direct, struct seq_midisynth *msynth = private_data; unsigned char msg[10]; /* buffer for constructing midi messages */ struct snd_rawmidi_substream *substream; + int err = 0; int len; if (snd_BUG_ON(!msynth)) return -EINVAL; - substream = msynth->output_rfile.output; - if (substream == NULL) - return -ENODEV; + + scoped_guard(spinlock_irqsave, &msynth->output_lock) { + substream = msynth->output_rfile.output; + if (!substream) + return -ENODEV; + snd_use_lock_use(&msynth->output_use_lock); + } + if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { /* special case, to save space */ if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) { /* invalid event */ pr_debug("ALSA: seq_midi: invalid sysex event flags = 0x%x\n", ev->flags); - return 0; + goto out; } snd_seq_dump_var_event(ev, __dump_midi, substream); snd_midi_event_reset_decode(msynth->parser); } else { - if (msynth->parser == NULL) - return -EIO; + if (!msynth->parser) { + err = -EIO; + goto out; + } len = snd_midi_event_decode(msynth->parser, msg, sizeof(msg), ev); if (len < 0) - return 0; + goto out; if (dump_midi(substream, msg, len) < 0) snd_midi_event_reset_decode(msynth->parser); } - return 0; + +out: + snd_use_lock_free(&msynth->output_use_lock); + return err; } @@ -163,6 +177,8 @@ static int snd_seq_midisynth_new(struct seq_midisynth *msynth, msynth->card = card; msynth->device = device; msynth->subdevice = subdevice; + spin_lock_init(&msynth->output_lock); + snd_use_lock_init(&msynth->output_use_lock); return 0; } @@ -215,12 +231,13 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info { int err; struct seq_midisynth *msynth = private_data; + struct snd_rawmidi_file rfile = {}; struct snd_rawmidi_params params; /* open midi port */ err = snd_rawmidi_kernel_open(msynth->rmidi, msynth->subdevice, SNDRV_RAWMIDI_LFLG_OUTPUT, - &msynth->output_rfile); + &rfile); if (err < 0) { pr_debug("ALSA: seq_midi: midi output open failed!!!\n"); return err; @@ -229,12 +246,14 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info params.avail_min = 1; params.buffer_size = output_buffer_size; params.no_active_sensing = 1; - err = snd_rawmidi_output_params(msynth->output_rfile.output, ¶ms); + err = snd_rawmidi_output_params(rfile.output, ¶ms); if (err < 0) { - snd_rawmidi_kernel_release(&msynth->output_rfile); + snd_rawmidi_kernel_release(&rfile); return err; } snd_midi_event_reset_decode(msynth->parser); + scoped_guard(spinlock_irqsave, &msynth->output_lock) + msynth->output_rfile = rfile; return 0; } @@ -242,11 +261,19 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info static int midisynth_unuse(void *private_data, struct snd_seq_port_subscribe *info) { struct seq_midisynth *msynth = private_data; + struct snd_rawmidi_file rfile = {}; - if (snd_BUG_ON(!msynth->output_rfile.output)) + scoped_guard(spinlock_irqsave, &msynth->output_lock) { + rfile = msynth->output_rfile; + msynth->output_rfile = (struct snd_rawmidi_file){}; + } + + if (snd_BUG_ON(!rfile.output)) return -EINVAL; - snd_rawmidi_drain_output(msynth->output_rfile.output); - return snd_rawmidi_kernel_release(&msynth->output_rfile); + + snd_use_lock_sync(&msynth->output_use_lock); + snd_rawmidi_drain_output(rfile.output); + return snd_rawmidi_kernel_release(&rfile); } /* delete given midi synth port */ From 6824e4fdb2f718f7f1d79e5aece81cc969dd9404 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Tue, 26 May 2026 10:01:08 +0200 Subject: [PATCH 1001/2758] selftests: Fix Makefile target for nsfs [ Upstream commit 77d1a2d2318fa96f8a662c9ad6647abedcd22734 ] The kselftests for nsfs where moved under filesystem/ with commit cae73d3bdce5 ("seltests: move nsfs into filesystems subfolder"). However, the kselftest TARGETS declaration was not adjusted. Since the kselftest Makefile ignores errors unless no target builds, the invalid target declaration can easily be missed. Fix this by adjusting the TARGETS accordingly. Fixes: cae73d3bdce5 ("seltests: move nsfs into filesystems subfolder") Signed-off-by: Florian Schmaus Link: https://patch.msgid.link/20260526-kselftest-nsfs-v1-1-7b042ebe42d6@geekplace.eu Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- tools/testing/selftests/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 6e59b8f63e416..641a180fb35f4 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -37,6 +37,7 @@ TARGETS += filesystems/fat TARGETS += filesystems/overlayfs TARGETS += filesystems/statmount TARGETS += filesystems/mount-notify +TARGETS += filesystems/nsfs TARGETS += filesystems/fuse TARGETS += filesystems/move_mount TARGETS += filesystems/empty_mntns @@ -85,7 +86,6 @@ TARGETS += net/ppp TARGETS += net/rds TARGETS += net/tcp_ao TARGETS += nolibc -TARGETS += nsfs TARGETS += pci_endpoint TARGETS += pcie_bwctrl TARGETS += perf_events From 4b6e404257b8a63d0446785bf925ef4f31e495d6 Mon Sep 17 00:00:00 2001 From: Joey Lu Date: Mon, 11 May 2026 11:17:49 +0800 Subject: [PATCH 1002/2758] pinctrl: nuvoton: ma35d1: fix MFP register offset and pin table [ Upstream commit ce4e27247ca643645c6d3043aad1c1c67bf7fdac ] Each GPIO bank has two 32-bit MFP registers: MFPL covering pins 0-7 at the bank base offset, and MFPH covering pins 8-15 at base offset+4. ma35_pinctrl_parse_groups() computed the register address without accounting for this split, so any pin with an index >= 8 within its bank was written to the wrong register. Also fix the pin descriptor table in pinctrl-ma35d1.c: switch from sequential to 16-per-bank pin numbering, add missing PC8-PC11 pins and their mux options, and remove the duplicate PN10-PN15 entries. Fixes: f805e356313b ("pinctrl: nuvoton: Add ma35d1 pinctrl and GPIO driver") Signed-off-by: Joey Lu Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/nuvoton/pinctrl-ma35.c | 3 +- drivers/pinctrl/nuvoton/pinctrl-ma35d1.c | 470 +++++++++++++---------- 2 files changed, 263 insertions(+), 210 deletions(-) diff --git a/drivers/pinctrl/nuvoton/pinctrl-ma35.c b/drivers/pinctrl/nuvoton/pinctrl-ma35.c index f01344201628f..dafa85c105a13 100644 --- a/drivers/pinctrl/nuvoton/pinctrl-ma35.c +++ b/drivers/pinctrl/nuvoton/pinctrl-ma35.c @@ -1014,7 +1014,8 @@ static int ma35_pinctrl_parse_groups(struct fwnode_handle *fwnode, struct group_ grp->data = pin; for (i = 0, j = 0; i < count; i += 3, j++) { - pin->offset = elems[i] * MA35_MFP_REG_SZ_PER_BANK + MA35_MFP_REG_BASE; + pin->offset = elems[i] * MA35_MFP_REG_SZ_PER_BANK + MA35_MFP_REG_BASE + + (elems[i + 1] >= 8 ? 4 : 0); pin->shift = (elems[i + 1] * MA35_MFP_BITS_PER_PORT) % 32; pin->muxval = elems[i + 2]; pin->configs = configs; diff --git a/drivers/pinctrl/nuvoton/pinctrl-ma35d1.c b/drivers/pinctrl/nuvoton/pinctrl-ma35d1.c index eafa06ca08791..9d4627c80a52c 100644 --- a/drivers/pinctrl/nuvoton/pinctrl-ma35d1.c +++ b/drivers/pinctrl/nuvoton/pinctrl-ma35d1.c @@ -113,6 +113,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x0, "GPA14"), MA35_MUX(0x2, "UART7_RXD"), MA35_MUX(0x3, "CAN3_RXD"), + MA35_MUX(0x4, "USBHL3_DM"), MA35_MUX(0x6, "NAND_nWP"), MA35_MUX(0x7, "EBI_AD14"), MA35_MUX(0x9, "EBI_ADR14")), @@ -123,6 +124,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x3, "UART6_RXD"), MA35_MUX(0x4, "I2C4_SDA"), MA35_MUX(0x5, "CAN2_RXD"), + MA35_MUX(0x6, "USBHL0_DM"), MA35_MUX(0x7, "EBI_ALE"), MA35_MUX(0x9, "QEI0_A"), MA35_MUX(0xb, "TM1"), @@ -187,6 +189,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x1, "EPWM2_CH5"), MA35_MUX(0x2, "UART2_RXD"), MA35_MUX(0x3, "CAN0_RXD"), + MA35_MUX(0x4, "USBHL2_DM"), MA35_MUX(0x5, "SPI0_MOSI"), MA35_MUX(0x6, "EBI_MCLK"), MA35_MUX(0x7, "CCAP1_VSYNC"), @@ -202,6 +205,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x1, "EPWM2_BRAKE1"), MA35_MUX(0x2, "UART2_TXD"), MA35_MUX(0x3, "CAN0_TXD"), + MA35_MUX(0x4, "USBHL2_DP"), MA35_MUX(0x5, "SPI0_MISO"), MA35_MUX(0x6, "I2S1_MCLK"), MA35_MUX(0x7, "CCAP1_SFIELD"), @@ -220,6 +224,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x4, "I2C3_SDA"), MA35_MUX(0x5, "CAN2_RXD"), MA35_MUX(0x6, "I2S1_LRCK"), + MA35_MUX(0x7, "USBHL1_DM"), MA35_MUX(0x8, "ADC0_CH4"), MA35_MUX(0x9, "EBI_ADR16"), MA35_MUX(0xe, "ECAP2_IC0")), @@ -231,6 +236,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x4, "I2C3_SCL"), MA35_MUX(0x5, "CAN2_TXD"), MA35_MUX(0x6, "I2S1_BCLK"), + MA35_MUX(0x7, "USBHL1_DP"), MA35_MUX(0x8, "ADC0_CH5"), MA35_MUX(0x9, "EBI_ADR17"), MA35_MUX(0xe, "ECAP2_IC1")), @@ -239,6 +245,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x1, "EPWM2_CH2"), MA35_MUX(0x2, "UART4_RXD"), MA35_MUX(0x3, "CAN1_RXD"), + MA35_MUX(0x4, "USBHL3_DM"), MA35_MUX(0x5, "I2C4_SDA"), MA35_MUX(0x6, "I2S1_DI"), MA35_MUX(0x8, "ADC0_CH6"), @@ -249,6 +256,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x1, "EPWM2_CH3"), MA35_MUX(0x2, "UART4_TXD"), MA35_MUX(0x3, "CAN1_TXD"), + MA35_MUX(0x4, "USBHL3_DP"), MA35_MUX(0x5, "I2C4_SCL"), MA35_MUX(0x6, "I2S1_DO"), MA35_MUX(0x8, "ADC0_CH7"), @@ -264,10 +272,12 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_PIN(34, PC2, 0x90, 0x8, MA35_MUX(0x0, "GPC2"), MA35_MUX(0x3, "CAN0_RXD"), + MA35_MUX(0x4, "USBHL4_DM"), MA35_MUX(0x6, "SD0_DAT0/eMMC0_DAT0")), MA35_PIN(35, PC3, 0x90, 0xc, MA35_MUX(0x0, "GPC3"), MA35_MUX(0x3, "CAN0_TXD"), + MA35_MUX(0x4, "USBHL4_DP"), MA35_MUX(0x6, "SD0_DAT1/eMMC0_DAT1")), MA35_PIN(36, PC4, 0x90, 0x10, MA35_MUX(0x0, "GPC4"), @@ -280,65 +290,100 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_PIN(38, PC6, 0x90, 0x18, MA35_MUX(0x0, "GPC6"), MA35_MUX(0x3, "CAN1_RXD"), + MA35_MUX(0x4, "USBHL5_DM"), MA35_MUX(0x6, "SD0_nCD")), MA35_PIN(39, PC7, 0x90, 0x1c, MA35_MUX(0x0, "GPC7"), MA35_MUX(0x3, "CAN1_TXD"), + MA35_MUX(0x4, "USBHL5_DP"), MA35_MUX(0x6, "SD0_WP")), - MA35_PIN(40, PC12, 0x94, 0x10, + MA35_PIN(40, PC8, 0x94, 0x0, + MA35_MUX(0x0, "GPC8"), + MA35_MUX(0x1, "EPWM2_CH0"), + MA35_MUX(0x2, "UART10_nCTS"), + MA35_MUX(0x3, "UART9_RXD"), + MA35_MUX(0x4, "I2C0_SDA"), + MA35_MUX(0x5, "SPI1_SS0"), + MA35_MUX(0x6, "SD0_DAT4/eMMC0_DAT4")), + MA35_PIN(41, PC9, 0x94, 0x4, + MA35_MUX(0x0, "GPC9"), + MA35_MUX(0x1, "EPWM2_CH1"), + MA35_MUX(0x2, "UART10_nRTS"), + MA35_MUX(0x3, "UART9_TXD"), + MA35_MUX(0x4, "I2C0_SCL"), + MA35_MUX(0x5, "SPI1_CLK"), + MA35_MUX(0x6, "SD0_DAT5/eMMC0_DAT5")), + MA35_PIN(42, PC10, 0x94, 0x8, + MA35_MUX(0x0, "GPC10"), + MA35_MUX(0x1, "EPWM2_CH2"), + MA35_MUX(0x2, "UART10_RXD"), + MA35_MUX(0x3, "CAN2_RXD"), + MA35_MUX(0x4, "USBHL0_DM"), + MA35_MUX(0x5, "SPI1_MOSI"), + MA35_MUX(0x6, "SD0_DAT6/eMMC0_DAT6")), + MA35_PIN(43, PC11, 0x94, 0xc, + MA35_MUX(0x0, "GPC11"), + MA35_MUX(0x1, "EPWM2_CH3"), + MA35_MUX(0x2, "UART10_TXD"), + MA35_MUX(0x3, "CAN2_TXD"), + MA35_MUX(0x4, "USBHL0_DP"), + MA35_MUX(0x5, "SPI1_MISO"), + MA35_MUX(0x6, "SD0_DAT7/eMMC0_DAT7")), + MA35_PIN(44, PC12, 0x94, 0x10, MA35_MUX(0x0, "GPC12"), MA35_MUX(0x2, "UART12_nCTS"), MA35_MUX(0x3, "UART11_RXD"), MA35_MUX(0x6, "LCM_DATA16")), - MA35_PIN(41, PC13, 0x94, 0x14, + MA35_PIN(45, PC13, 0x94, 0x14, MA35_MUX(0x0, "GPC13"), MA35_MUX(0x2, "UART12_nRTS"), MA35_MUX(0x3, "UART11_TXD"), MA35_MUX(0x6, "LCM_DATA17")), - MA35_PIN(42, PC14, 0x94, 0x18, + MA35_PIN(46, PC14, 0x94, 0x18, MA35_MUX(0x0, "GPC14"), MA35_MUX(0x2, "UART12_RXD"), MA35_MUX(0x6, "LCM_DATA18")), - MA35_PIN(43, PC15, 0x94, 0x1c, + MA35_PIN(47, PC15, 0x94, 0x1c, MA35_MUX(0x0, "GPC15"), MA35_MUX(0x2, "UART12_TXD"), MA35_MUX(0x6, "LCM_DATA19"), MA35_MUX(0x7, "LCM_MPU_TE"), MA35_MUX(0x8, "LCM_MPU_VSYNC")), - MA35_PIN(44, PD0, 0x98, 0x0, + MA35_PIN(48, PD0, 0x98, 0x0, MA35_MUX(0x0, "GPD0"), MA35_MUX(0x2, "UART3_nCTS"), MA35_MUX(0x3, "UART4_RXD"), MA35_MUX(0x5, "QSPI0_SS0")), - MA35_PIN(45, PD1, 0x98, 0x4, + MA35_PIN(49, PD1, 0x98, 0x4, MA35_MUX(0x0, "GPD1"), MA35_MUX(0x2, "UART3_nRTS"), MA35_MUX(0x3, "UART4_TXD"), MA35_MUX(0x5, "QSPI0_CLK")), - MA35_PIN(46, PD2, 0x98, 0x8, + MA35_PIN(50, PD2, 0x98, 0x8, MA35_MUX(0x0, "GPD2"), MA35_MUX(0x2, "UART3_RXD"), MA35_MUX(0x5, "QSPI0_MOSI0")), - MA35_PIN(47, PD3, 0x98, 0xc, + MA35_PIN(51, PD3, 0x98, 0xc, MA35_MUX(0x0, "GPD3"), MA35_MUX(0x2, "UART3_TXD"), MA35_MUX(0x5, "QSPI0_MISO0")), - MA35_PIN(48, PD4, 0x98, 0x10, + MA35_PIN(52, PD4, 0x98, 0x10, MA35_MUX(0x0, "GPD4"), MA35_MUX(0x2, "UART1_nCTS"), MA35_MUX(0x3, "UART2_RXD"), MA35_MUX(0x4, "I2C2_SDA"), MA35_MUX(0x5, "QSPI0_MOSI1")), - MA35_PIN(49, PD5, 0x98, 0x14, + MA35_PIN(53, PD5, 0x98, 0x14, MA35_MUX(0x0, "GPD5"), MA35_MUX(0x2, "UART1_nRTS"), MA35_MUX(0x3, "UART2_TXD"), MA35_MUX(0x4, "I2C2_SCL"), MA35_MUX(0x5, "QSPI0_MISO1")), - MA35_PIN(50, PD6, 0x98, 0x18, + MA35_PIN(54, PD6, 0x98, 0x18, MA35_MUX(0x0, "GPD6"), MA35_MUX(0x1, "EPWM0_SYNC_IN"), MA35_MUX(0x2, "UART1_RXD"), + MA35_MUX(0x4, "USBHL3_DM"), MA35_MUX(0x5, "QSPI1_MOSI1"), MA35_MUX(0x6, "I2C0_SDA"), MA35_MUX(0x7, "I2S0_MCLK"), @@ -346,10 +391,11 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "EBI_AD5"), MA35_MUX(0xa, "SPI3_SS1"), MA35_MUX(0xb, "TRACE_CLK")), - MA35_PIN(51, PD7, 0x98, 0x1c, + MA35_PIN(55, PD7, 0x98, 0x1c, MA35_MUX(0x0, "GPD7"), MA35_MUX(0x1, "EPWM0_SYNC_OUT"), MA35_MUX(0x2, "UART1_TXD"), + MA35_MUX(0x4, "USBHL3_DP"), MA35_MUX(0x5, "QSPI1_MISO1"), MA35_MUX(0x6, "I2C0_SCL"), MA35_MUX(0x7, "I2S1_MCLK"), @@ -357,7 +403,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "EBI_AD6"), MA35_MUX(0xa, "SC1_nCD"), MA35_MUX(0xb, "EADC0_ST")), - MA35_PIN(52, PD8, 0x9c, 0x0, + MA35_PIN(56, PD8, 0x9c, 0x0, MA35_MUX(0x0, "GPD8"), MA35_MUX(0x1, "EPWM0_BRAKE0"), MA35_MUX(0x2, "UART16_nCTS"), @@ -368,7 +414,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "EBI_AD7"), MA35_MUX(0xa, "SC1_CLK"), MA35_MUX(0xb, "TM0")), - MA35_PIN(53, PD9, 0x9c, 0x4, + MA35_PIN(57, PD9, 0x9c, 0x4, MA35_MUX(0x0, "GPD9"), MA35_MUX(0x1, "EPWM0_BRAKE1"), MA35_MUX(0x2, "UART16_nRTS"), @@ -379,7 +425,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "EBI_AD8"), MA35_MUX(0xa, "SC1_DAT"), MA35_MUX(0xb, "TM0_EXT")), - MA35_PIN(54, PD10, 0x9c, 0x8, + MA35_PIN(58, PD10, 0x9c, 0x8, MA35_MUX(0x0, "GPD10"), MA35_MUX(0x1, "EPWM1_BRAKE0"), MA35_MUX(0x2, "UART16_RXD"), @@ -389,7 +435,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "EBI_AD9"), MA35_MUX(0xa, "SC1_RST"), MA35_MUX(0xb, "TM2")), - MA35_PIN(55, PD11, 0x9c, 0xc, + MA35_PIN(59, PD11, 0x9c, 0xc, MA35_MUX(0x0, "GPD11"), MA35_MUX(0x1, "EPWM1_BRAKE1"), MA35_MUX(0x2, "UART16_TXD"), @@ -399,7 +445,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "EBI_AD10"), MA35_MUX(0xa, "SC1_PWR"), MA35_MUX(0xb, "TM2_EXT")), - MA35_PIN(56, PD12, 0x9c, 0x10, + MA35_PIN(60, PD12, 0x9c, 0x10, MA35_MUX(0x0, "GPD12"), MA35_MUX(0x1, "EPWM0_BRAKE0"), MA35_MUX(0x2, "UART11_TXD"), @@ -412,7 +458,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xb, "TM5"), MA35_MUX(0xc, "I2S1_LRCK"), MA35_MUX(0xd, "INT1")), - MA35_PIN(57, PD13, 0x9c, 0x14, + MA35_PIN(61, PD13, 0x9c, 0x14, MA35_MUX(0x0, "GPD13"), MA35_MUX(0x1, "EPWM0_BRAKE1"), MA35_MUX(0x2, "UART11_RXD"), @@ -424,11 +470,12 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "ECAP0_IC0"), MA35_MUX(0xb, "TM5_EXT"), MA35_MUX(0xc, "I2S1_BCLK")), - MA35_PIN(58, PD14, 0x9c, 0x18, + MA35_PIN(62, PD14, 0x9c, 0x18, MA35_MUX(0x0, "GPD14"), MA35_MUX(0x1, "EPWM0_SYNC_IN"), MA35_MUX(0x2, "UART11_nCTS"), MA35_MUX(0x3, "CAN3_RXD"), + MA35_MUX(0x4, "USBHL5_DM"), MA35_MUX(0x6, "TRACE_DATA2"), MA35_MUX(0x7, "EBI_MCLK"), MA35_MUX(0x8, "EBI_AD6"), @@ -436,116 +483,117 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xb, "TM6"), MA35_MUX(0xc, "I2S1_DI"), MA35_MUX(0xd, "INT3")), - MA35_PIN(59, PD15, 0x9c, 0x1c, + MA35_PIN(63, PD15, 0x9c, 0x1c, MA35_MUX(0x0, "GPD15"), MA35_MUX(0x1, "EPWM0_SYNC_OUT"), MA35_MUX(0x2, "UART11_nRTS"), MA35_MUX(0x3, "CAN3_TXD"), + MA35_MUX(0x4, "USBHL5_DP"), MA35_MUX(0x6, "TRACE_DATA3"), MA35_MUX(0x7, "EBI_ALE"), MA35_MUX(0x8, "EBI_AD7"), MA35_MUX(0x9, "ECAP0_IC2"), MA35_MUX(0xb, "TM6_EXT"), MA35_MUX(0xc, "I2S1_DO")), - MA35_PIN(60, PE0, 0xa0, 0x0, + MA35_PIN(64, PE0, 0xa0, 0x0, MA35_MUX(0x0, "GPE0"), MA35_MUX(0x2, "UART9_nCTS"), MA35_MUX(0x3, "UART8_RXD"), MA35_MUX(0x7, "CCAP1_DATA0"), MA35_MUX(0x8, "RGMII0_MDC"), MA35_MUX(0x9, "RMII0_MDC")), - MA35_PIN(61, PE1, 0xa0, 0x4, + MA35_PIN(65, PE1, 0xa0, 0x4, MA35_MUX(0x0, "GPE1"), MA35_MUX(0x2, "UART9_nRTS"), MA35_MUX(0x3, "UART8_TXD"), MA35_MUX(0x7, "CCAP1_DATA1"), MA35_MUX(0x8, "RGMII0_MDIO"), MA35_MUX(0x9, "RMII0_MDIO")), - MA35_PIN(62, PE2, 0xa0, 0x8, + MA35_PIN(66, PE2, 0xa0, 0x8, MA35_MUX(0x0, "GPE2"), MA35_MUX(0x2, "UART9_RXD"), MA35_MUX(0x7, "CCAP1_DATA2"), MA35_MUX(0x8, "RGMII0_TXCTL"), MA35_MUX(0x9, "RMII0_TXEN")), - MA35_PIN(63, PE3, 0xa0, 0xc, + MA35_PIN(67, PE3, 0xa0, 0xc, MA35_MUX(0x0, "GPE3"), MA35_MUX(0x2, "UART9_TXD"), MA35_MUX(0x7, "CCAP1_DATA3"), MA35_MUX(0x8, "RGMII0_TXD0"), MA35_MUX(0x9, "RMII0_TXD0")), - MA35_PIN(64, PE4, 0xa0, 0x10, + MA35_PIN(68, PE4, 0xa0, 0x10, MA35_MUX(0x0, "GPE4"), MA35_MUX(0x2, "UART4_nCTS"), MA35_MUX(0x3, "UART3_RXD"), MA35_MUX(0x7, "CCAP1_DATA4"), MA35_MUX(0x8, "RGMII0_TXD1"), MA35_MUX(0x9, "RMII0_TXD1")), - MA35_PIN(65, PE5, 0xa0, 0x14, + MA35_PIN(69, PE5, 0xa0, 0x14, MA35_MUX(0x0, "GPE5"), MA35_MUX(0x2, "UART4_nRTS"), MA35_MUX(0x3, "UART3_TXD"), MA35_MUX(0x7, "CCAP1_DATA5"), MA35_MUX(0x8, "RGMII0_RXCLK"), MA35_MUX(0x9, "RMII0_REFCLK")), - MA35_PIN(66, PE6, 0xa0, 0x18, + MA35_PIN(70, PE6, 0xa0, 0x18, MA35_MUX(0x0, "GPE6"), MA35_MUX(0x2, "UART4_RXD"), MA35_MUX(0x7, "CCAP1_DATA6"), MA35_MUX(0x8, "RGMII0_RXCTL"), MA35_MUX(0x9, "RMII0_CRSDV")), - MA35_PIN(67, PE7, 0xa0, 0x1c, + MA35_PIN(71, PE7, 0xa0, 0x1c, MA35_MUX(0x0, "GPE7"), MA35_MUX(0x2, "UART4_TXD"), MA35_MUX(0x7, "CCAP1_DATA7"), MA35_MUX(0x8, "RGMII0_RXD0"), MA35_MUX(0x9, "RMII0_RXD0")), - MA35_PIN(68, PE8, 0xa4, 0x0, + MA35_PIN(72, PE8, 0xa4, 0x0, MA35_MUX(0x0, "GPE8"), MA35_MUX(0x2, "UART13_nCTS"), MA35_MUX(0x3, "UART12_RXD"), MA35_MUX(0x7, "CCAP1_SCLK"), MA35_MUX(0x8, "RGMII0_RXD1"), MA35_MUX(0x9, "RMII0_RXD1")), - MA35_PIN(69, PE9, 0xa4, 0x4, + MA35_PIN(73, PE9, 0xa4, 0x4, MA35_MUX(0x0, "GPE9"), MA35_MUX(0x2, "UART13_nRTS"), MA35_MUX(0x3, "UART12_TXD"), MA35_MUX(0x7, "CCAP1_PIXCLK"), MA35_MUX(0x8, "RGMII0_RXD2"), MA35_MUX(0x9, "RMII0_RXERR")), - MA35_PIN(70, PE10, 0xa4, 0x8, + MA35_PIN(74, PE10, 0xa4, 0x8, MA35_MUX(0x0, "GPE10"), MA35_MUX(0x2, "UART15_nCTS"), MA35_MUX(0x3, "UART14_RXD"), MA35_MUX(0x5, "SPI1_SS0"), MA35_MUX(0x7, "CCAP1_HSYNC"), MA35_MUX(0x8, "RGMII0_RXD3")), - MA35_PIN(71, PE11, 0xa4, 0xc, + MA35_PIN(75, PE11, 0xa4, 0xc, MA35_MUX(0x0, "GPE11"), MA35_MUX(0x2, "UART15_nRTS"), MA35_MUX(0x3, "UART14_TXD"), MA35_MUX(0x5, "SPI1_CLK"), MA35_MUX(0x7, "CCAP1_VSYNC"), MA35_MUX(0x8, "RGMII0_TXCLK")), - MA35_PIN(72, PE12, 0xa4, 0x10, + MA35_PIN(76, PE12, 0xa4, 0x10, MA35_MUX(0x0, "GPE12"), MA35_MUX(0x2, "UART15_RXD"), MA35_MUX(0x5, "SPI1_MOSI"), MA35_MUX(0x7, "CCAP1_DATA8"), MA35_MUX(0x8, "RGMII0_TXD2")), - MA35_PIN(73, PE13, 0xa4, 0x14, + MA35_PIN(77, PE13, 0xa4, 0x14, MA35_MUX(0x0, "GPE13"), MA35_MUX(0x2, "UART15_TXD"), MA35_MUX(0x5, "SPI1_MISO"), MA35_MUX(0x7, "CCAP1_DATA9"), MA35_MUX(0x8, "RGMII0_TXD3")), - MA35_PIN(74, PE14, 0xa4, 0x18, + MA35_PIN(78, PE14, 0xa4, 0x18, MA35_MUX(0x0, "GPE14"), MA35_MUX(0x1, "UART0_TXD")), - MA35_PIN(75, PE15, 0xa4, 0x1c, + MA35_PIN(79, PE15, 0xa4, 0x1c, MA35_MUX(0x0, "GPE15"), MA35_MUX(0x1, "UART0_RXD")), - MA35_PIN(76, PF0, 0xa8, 0x0, + MA35_PIN(80, PF0, 0xa8, 0x0, MA35_MUX(0x0, "GPF0"), MA35_MUX(0x2, "UART2_nCTS"), MA35_MUX(0x3, "UART1_RXD"), @@ -553,7 +601,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "RGMII1_MDC"), MA35_MUX(0x9, "RMII1_MDC"), MA35_MUX(0xe, "KPI_COL0")), - MA35_PIN(77, PF1, 0xa8, 0x4, + MA35_PIN(81, PF1, 0xa8, 0x4, MA35_MUX(0x0, "GPF1"), MA35_MUX(0x2, "UART2_nRTS"), MA35_MUX(0x3, "UART1_TXD"), @@ -561,21 +609,21 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "RGMII1_MDIO"), MA35_MUX(0x9, "RMII1_MDIO"), MA35_MUX(0xe, "KPI_COL1")), - MA35_PIN(78, PF2, 0xa8, 0x8, + MA35_PIN(82, PF2, 0xa8, 0x8, MA35_MUX(0x0, "GPF2"), MA35_MUX(0x2, "UART2_RXD"), MA35_MUX(0x6, "RGMII0_TXD2"), MA35_MUX(0x8, "RGMII1_TXCTL"), MA35_MUX(0x9, "RMII1_TXEN"), MA35_MUX(0xe, "KPI_COL2")), - MA35_PIN(79, PF3, 0xa8, 0xc, + MA35_PIN(83, PF3, 0xa8, 0xc, MA35_MUX(0x0, "GPF3"), MA35_MUX(0x2, "UART2_TXD"), MA35_MUX(0x6, "RGMII0_TXD3"), MA35_MUX(0x8, "RGMII1_TXD0"), MA35_MUX(0x9, "RMII1_TXD0"), MA35_MUX(0xe, "KPI_COL3")), - MA35_PIN(80, PF4, 0xa8, 0x10, + MA35_PIN(84, PF4, 0xa8, 0x10, MA35_MUX(0x0, "GPF4"), MA35_MUX(0x2, "UART11_nCTS"), MA35_MUX(0x3, "UART10_RXD"), @@ -583,9 +631,10 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x5, "SPI1_SS0"), MA35_MUX(0x8, "RGMII1_TXD1"), MA35_MUX(0x9, "RMII1_TXD1"), + MA35_MUX(0xc, "USBHL0_DM"), MA35_MUX(0xd, "CAN2_RXD"), MA35_MUX(0xe, "KPI_ROW0")), - MA35_PIN(81, PF5, 0xa8, 0x14, + MA35_PIN(85, PF5, 0xa8, 0x14, MA35_MUX(0x0, "GPF5"), MA35_MUX(0x2, "UART11_nRTS"), MA35_MUX(0x3, "UART10_TXD"), @@ -593,9 +642,10 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x5, "SPI1_CLK"), MA35_MUX(0x8, "RGMII1_RXCLK"), MA35_MUX(0x9, "RMII1_REFCLK"), + MA35_MUX(0xc, "USBHL0_DP"), MA35_MUX(0xd, "CAN2_TXD"), MA35_MUX(0xe, "KPI_ROW1")), - MA35_PIN(82, PF6, 0xa8, 0x18, + MA35_PIN(86, PF6, 0xa8, 0x18, MA35_MUX(0x0, "GPF6"), MA35_MUX(0x2, "UART11_RXD"), MA35_MUX(0x4, "I2S0_DI"), @@ -605,7 +655,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "I2C4_SDA"), MA35_MUX(0xd, "SC0_CLK"), MA35_MUX(0xe, "KPI_ROW2")), - MA35_PIN(83, PF7, 0xa8, 0x1c, + MA35_PIN(87, PF7, 0xa8, 0x1c, MA35_MUX(0x0, "GPF7"), MA35_MUX(0x2, "UART11_TXD"), MA35_MUX(0x4, "I2S0_DO"), @@ -615,7 +665,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "I2C4_SCL"), MA35_MUX(0xd, "SC0_DAT"), MA35_MUX(0xe, "KPI_ROW3")), - MA35_PIN(84, PF8, 0xac, 0x0, + MA35_PIN(88, PF8, 0xac, 0x0, MA35_MUX(0x0, "GPF8"), MA35_MUX(0x2, "UART13_RXD"), MA35_MUX(0x4, "I2C5_SDA"), @@ -624,7 +674,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "RMII1_RXD1"), MA35_MUX(0xd, "SC0_RST"), MA35_MUX(0xe, "KPI_COL4")), - MA35_PIN(85, PF9, 0xac, 0x4, + MA35_PIN(89, PF9, 0xac, 0x4, MA35_MUX(0x0, "GPF9"), MA35_MUX(0x2, "UART13_TXD"), MA35_MUX(0x4, "I2C5_SCL"), @@ -633,7 +683,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "RMII1_RXERR"), MA35_MUX(0xd, "SC0_PWR"), MA35_MUX(0xe, "KPI_COL5")), - MA35_PIN(86, PF10, 0xac, 0x8, + MA35_PIN(90, PF10, 0xac, 0x8, MA35_MUX(0x0, "GPF10"), MA35_MUX(0x2, "UART13_nCTS"), MA35_MUX(0x5, "I2S0_LRCK"), @@ -641,7 +691,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "RGMII1_RXD3"), MA35_MUX(0x9, "SC0_CLK"), MA35_MUX(0xe, "KPI_COL6")), - MA35_PIN(87, PF11, 0xac, 0xc, + MA35_PIN(91, PF11, 0xac, 0xc, MA35_MUX(0x0, "GPF11"), MA35_MUX(0x2, "UART13_nRTS"), MA35_MUX(0x5, "I2S0_BCLK"), @@ -649,21 +699,21 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "RGMII1_TXCLK"), MA35_MUX(0x9, "SC0_DAT"), MA35_MUX(0xe, "KPI_COL7")), - MA35_PIN(88, PF12, 0xac, 0x10, + MA35_PIN(92, PF12, 0xac, 0x10, MA35_MUX(0x0, "GPF12"), MA35_MUX(0x5, "I2S0_DI"), MA35_MUX(0x6, "SPI1_MOSI"), MA35_MUX(0x8, "RGMII1_TXD2"), MA35_MUX(0x9, "SC0_RST"), MA35_MUX(0xe, "KPI_ROW4")), - MA35_PIN(89, PF13, 0xac, 0x14, + MA35_PIN(93, PF13, 0xac, 0x14, MA35_MUX(0x0, "GPF13"), MA35_MUX(0x5, "I2S0_DO"), MA35_MUX(0x6, "SPI1_MISO"), MA35_MUX(0x8, "RGMII1_TXD3"), MA35_MUX(0x9, "SC0_PWR"), MA35_MUX(0xe, "KPI_ROW5")), - MA35_PIN(90, PF14, 0xac, 0x18, + MA35_PIN(94, PF14, 0xac, 0x18, MA35_MUX(0x0, "GPF14"), MA35_MUX(0x1, "EPWM2_BRAKE0"), MA35_MUX(0x2, "EADC0_ST"), @@ -679,10 +729,10 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xd, "SPI1_SS1"), MA35_MUX(0xe, "QEI2_INDEX"), MA35_MUX(0xf, "I2S0_MCLK")), - MA35_PIN(91, PF15, 0xac, 0x1c, + MA35_PIN(95, PF15, 0xac, 0x1c, MA35_MUX(0x0, "GPF15"), MA35_MUX(0x1, "HSUSB0_VBUSVLD")), - MA35_PIN(92, PG0, 0xb0, 0x0, + MA35_PIN(96, PG0, 0xb0, 0x0, MA35_MUX(0x0, "GPG0"), MA35_MUX(0x1, "EPWM0_CH0"), MA35_MUX(0x2, "UART7_TXD"), @@ -696,19 +746,20 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xc, "CLKO"), MA35_MUX(0xd, "INT0"), MA35_MUX(0xf, "EBI_ADR15")), - MA35_PIN(93, PG1, 0xb0, 0x4, + MA35_PIN(97, PG1, 0xb0, 0x4, MA35_MUX(0x0, "GPG1"), MA35_MUX(0x1, "EPWM0_CH3"), MA35_MUX(0x2, "UART9_nRTS"), MA35_MUX(0x3, "UART6_TXD"), MA35_MUX(0x4, "I2C4_SCL"), MA35_MUX(0x5, "CAN2_TXD"), + MA35_MUX(0x6, "USBHL0_DP"), MA35_MUX(0x7, "EBI_nCS0"), MA35_MUX(0x9, "QEI0_B"), MA35_MUX(0xb, "TM1_EXT"), MA35_MUX(0xe, "RGMII1_PPS"), MA35_MUX(0xf, "RMII1_PPS")), - MA35_PIN(94, PG2, 0xb0, 0x8, + MA35_PIN(98, PG2, 0xb0, 0x8, MA35_MUX(0x0, "GPG2"), MA35_MUX(0x1, "EPWM0_CH4"), MA35_MUX(0x2, "UART9_RXD"), @@ -719,7 +770,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "QEI0_A"), MA35_MUX(0xb, "TM3"), MA35_MUX(0xd, "INT1")), - MA35_PIN(95, PG3, 0xb0, 0xc, + MA35_PIN(99, PG3, 0xb0, 0xc, MA35_MUX(0x0, "GPG3"), MA35_MUX(0x1, "EPWM0_CH5"), MA35_MUX(0x2, "UART9_TXD"), @@ -731,7 +782,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "QEI0_B"), MA35_MUX(0xb, "TM3_EXT"), MA35_MUX(0xc, "I2S1_MCLK")), - MA35_PIN(96, PG4, 0xb0, 0x10, + MA35_PIN(100, PG4, 0xb0, 0x10, MA35_MUX(0x0, "GPG4"), MA35_MUX(0x1, "EPWM1_CH0"), MA35_MUX(0x2, "UART5_nCTS"), @@ -745,7 +796,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xb, "TM4"), MA35_MUX(0xd, "INT2"), MA35_MUX(0xe, "ECAP1_IC2")), - MA35_PIN(97, PG5, 0xb0, 0x14, + MA35_PIN(101, PG5, 0xb0, 0x14, MA35_MUX(0x0, "GPG5"), MA35_MUX(0x1, "EPWM1_CH1"), MA35_MUX(0x2, "UART5_nRTS"), @@ -757,7 +808,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "I2S1_DI"), MA35_MUX(0xa, "SC1_DAT"), MA35_MUX(0xb, "TM4_EXT")), - MA35_PIN(98, PG6, 0xb0, 0x18, + MA35_PIN(102, PG6, 0xb0, 0x18, MA35_MUX(0x0, "GPG6"), MA35_MUX(0x1, "EPWM1_CH2"), MA35_MUX(0x2, "UART5_RXD"), @@ -769,7 +820,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "SC1_RST"), MA35_MUX(0xb, "TM7"), MA35_MUX(0xd, "INT3")), - MA35_PIN(99, PG7, 0xb0, 0x1c, + MA35_PIN(103, PG7, 0xb0, 0x1c, MA35_MUX(0x0, "GPG7"), MA35_MUX(0x1, "EPWM1_CH3"), MA35_MUX(0x2, "UART5_TXD"), @@ -780,27 +831,29 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "I2S1_LRCK"), MA35_MUX(0xa, "SC1_PWR"), MA35_MUX(0xb, "TM7_EXT")), - MA35_PIN(100, PG8, 0xb4, 0x0, + MA35_PIN(104, PG8, 0xb4, 0x0, MA35_MUX(0x0, "GPG8"), MA35_MUX(0x1, "EPWM1_CH4"), MA35_MUX(0x2, "UART12_RXD"), MA35_MUX(0x3, "CAN3_RXD"), + MA35_MUX(0x4, "USBHL4_DM"), MA35_MUX(0x5, "SPI2_SS0"), MA35_MUX(0x6, "LCM_VSYNC"), MA35_MUX(0x7, "I2C3_SDA"), MA35_MUX(0xc, "EBI_AD7"), MA35_MUX(0xd, "EBI_nCS0")), - MA35_PIN(101, PG9, 0xb4, 0x4, + MA35_PIN(105, PG9, 0xb4, 0x4, MA35_MUX(0x0, "GPG9"), MA35_MUX(0x1, "EPWM1_CH5"), MA35_MUX(0x2, "UART12_TXD"), MA35_MUX(0x3, "CAN3_TXD"), + MA35_MUX(0x4, "USBHL4_DP"), MA35_MUX(0x5, "SPI2_CLK"), MA35_MUX(0x6, "LCM_HSYNC"), MA35_MUX(0x7, "I2C3_SCL"), MA35_MUX(0xc, "EBI_AD8"), MA35_MUX(0xd, "EBI_nCS1")), - MA35_PIN(102, PG10, 0xb4, 0x8, + MA35_PIN(106, PG10, 0xb4, 0x8, MA35_MUX(0x0, "GPG10"), MA35_MUX(0x2, "UART12_nRTS"), MA35_MUX(0x3, "UART13_TXD"), @@ -808,7 +861,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x6, "LCM_CLK"), MA35_MUX(0xc, "EBI_AD9"), MA35_MUX(0xd, "EBI_nWRH")), - MA35_PIN(103, PG11, 0xb4, 0xc, + MA35_PIN(107, PG11, 0xb4, 0xc, MA35_MUX(0x0, "GPG11"), MA35_MUX(0x3, "JTAG_TDO"), MA35_MUX(0x5, "I2S0_MCLK"), @@ -816,93 +869,93 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x7, "EBI_nWRH"), MA35_MUX(0x8, "EBI_nCS1"), MA35_MUX(0xa, "EBI_AD0")), - MA35_PIN(104, PG12, 0xb4, 0x10, + MA35_PIN(108, PG12, 0xb4, 0x10, MA35_MUX(0x0, "GPG12"), MA35_MUX(0x3, "JTAG_TCK/SW_CLK"), MA35_MUX(0x5, "I2S0_LRCK"), MA35_MUX(0x7, "EBI_nWRL"), MA35_MUX(0xa, "EBI_AD1")), - MA35_PIN(105, PG13, 0xb4, 0x14, + MA35_PIN(109, PG13, 0xb4, 0x14, MA35_MUX(0x0, "GPG13"), MA35_MUX(0x3, "JTAG_TMS/SW_DIO"), MA35_MUX(0x5, "I2S0_BCLK"), MA35_MUX(0x7, "EBI_MCLK"), MA35_MUX(0xa, "EBI_AD2")), - MA35_PIN(106, PG14, 0xb4, 0x18, + MA35_PIN(110, PG14, 0xb4, 0x18, MA35_MUX(0x0, "GPG14"), MA35_MUX(0x3, "JTAG_TDI"), MA35_MUX(0x5, "I2S0_DI"), MA35_MUX(0x6, "NAND_nCS1"), MA35_MUX(0x7, "EBI_ALE"), MA35_MUX(0xa, "EBI_AD3")), - MA35_PIN(107, PG15, 0xb4, 0x1c, + MA35_PIN(111, PG15, 0xb4, 0x1c, MA35_MUX(0x0, "GPG15"), MA35_MUX(0x3, "JTAG_nTRST"), MA35_MUX(0x5, "I2S0_DO"), MA35_MUX(0x7, "EBI_nCS0"), MA35_MUX(0xa, "EBI_AD4")), - MA35_PIN(108, PH0, 0xb8, 0x0, + MA35_PIN(112, PH0, 0xb8, 0x0, MA35_MUX(0x0, "GPH0"), MA35_MUX(0x2, "UART8_nCTS"), MA35_MUX(0x3, "UART7_RXD"), MA35_MUX(0x6, "LCM_DATA8")), - MA35_PIN(109, PH1, 0xb8, 0x4, + MA35_PIN(113, PH1, 0xb8, 0x4, MA35_MUX(0x0, "GPH1"), MA35_MUX(0x2, "UART8_nRTS"), MA35_MUX(0x3, "UART7_TXD"), MA35_MUX(0x6, "LCM_DATA9")), - MA35_PIN(110, PH2, 0xb8, 0x8, + MA35_PIN(114, PH2, 0xb8, 0x8, MA35_MUX(0x0, "GPH2"), MA35_MUX(0x2, "UART8_RXD"), MA35_MUX(0x6, "LCM_DATA10")), - MA35_PIN(111, PH3, 0xb8, 0xc, + MA35_PIN(115, PH3, 0xb8, 0xc, MA35_MUX(0x0, "GPH3"), MA35_MUX(0x2, "UART8_TXD"), MA35_MUX(0x6, "LCM_DATA11")), - MA35_PIN(112, PH4, 0xb8, 0x10, + MA35_PIN(116, PH4, 0xb8, 0x10, MA35_MUX(0x0, "GPH4"), MA35_MUX(0x2, "UART10_nCTS"), MA35_MUX(0x3, "UART9_RXD"), MA35_MUX(0x6, "LCM_DATA12")), - MA35_PIN(113, PH5, 0xb8, 0x14, + MA35_PIN(117, PH5, 0xb8, 0x14, MA35_MUX(0x0, "GPH5"), MA35_MUX(0x2, "UART10_nRTS"), MA35_MUX(0x3, "UART9_TXD"), MA35_MUX(0x6, "LCM_DATA13")), - MA35_PIN(114, PH6, 0xb8, 0x18, + MA35_PIN(118, PH6, 0xb8, 0x18, MA35_MUX(0x0, "GPH6"), MA35_MUX(0x2, "UART10_RXD"), MA35_MUX(0x6, "LCM_DATA14")), - MA35_PIN(115, PH7, 0xb8, 0x1c, + MA35_PIN(119, PH7, 0xb8, 0x1c, MA35_MUX(0x0, "GPH7"), MA35_MUX(0x2, "UART10_TXD"), MA35_MUX(0x6, "LCM_DATA15")), - MA35_PIN(116, PH8, 0xbc, 0x0, + MA35_PIN(120, PH8, 0xbc, 0x0, MA35_MUX(0x0, "GPH8"), MA35_MUX(0x6, "TAMPER0")), - MA35_PIN(117, PH9, 0xbc, 0x4, + MA35_PIN(121, PH9, 0xbc, 0x4, MA35_MUX(0x0, "GPH9"), MA35_MUX(0x4, "CLK_32KOUT"), MA35_MUX(0x6, "TAMPER1")), - MA35_PIN(118, PH12, 0xbc, 0x10, + MA35_PIN(124, PH12, 0xbc, 0x10, MA35_MUX(0x0, "GPH12"), MA35_MUX(0x2, "UART14_nCTS"), MA35_MUX(0x3, "UART13_RXD"), MA35_MUX(0x6, "LCM_DATA20")), - MA35_PIN(119, PH13, 0xbc, 0x14, + MA35_PIN(125, PH13, 0xbc, 0x14, MA35_MUX(0x0, "GPH13"), MA35_MUX(0x2, "UART14_nRTS"), MA35_MUX(0x3, "UART13_TXD"), MA35_MUX(0x6, "LCM_DATA21")), - MA35_PIN(120, PH14, 0xbc, 0x18, + MA35_PIN(126, PH14, 0xbc, 0x18, MA35_MUX(0x0, "GPH14"), MA35_MUX(0x2, "UART14_RXD"), MA35_MUX(0x6, "LCM_DATA22")), - MA35_PIN(121, PH15, 0xbc, 0x1c, + MA35_PIN(127, PH15, 0xbc, 0x1c, MA35_MUX(0x0, "GPH15"), MA35_MUX(0x2, "UART14_TXD"), MA35_MUX(0x6, "LCM_DATA23")), - MA35_PIN(122, PI0, 0xc0, 0x0, + MA35_PIN(128, PI0, 0xc0, 0x0, MA35_MUX(0x0, "GPI0"), MA35_MUX(0x1, "EPWM0_CH0"), MA35_MUX(0x2, "UART12_nCTS"), @@ -913,7 +966,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "EBI_ADR0"), MA35_MUX(0xb, "TM0"), MA35_MUX(0xc, "ECAP1_IC0")), - MA35_PIN(123, PI1, 0xc0, 0x4, + MA35_PIN(129, PI1, 0xc0, 0x4, MA35_MUX(0x0, "GPI1"), MA35_MUX(0x1, "EPWM0_CH1"), MA35_MUX(0x2, "UART12_nRTS"), @@ -924,26 +977,28 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "EBI_ADR1"), MA35_MUX(0xb, "TM0_EXT"), MA35_MUX(0xc, "ECAP1_IC1")), - MA35_PIN(124, PI2, 0xc0, 0x8, + MA35_PIN(130, PI2, 0xc0, 0x8, MA35_MUX(0x0, "GPI2"), MA35_MUX(0x1, "EPWM0_CH2"), MA35_MUX(0x2, "UART12_RXD"), MA35_MUX(0x3, "CAN0_RXD"), + MA35_MUX(0x4, "USBHL2_DM"), MA35_MUX(0x5, "SPI3_MOSI"), MA35_MUX(0x7, "SC0_DAT"), MA35_MUX(0x8, "EBI_ADR2"), MA35_MUX(0xb, "TM1"), MA35_MUX(0xc, "ECAP1_IC2")), - MA35_PIN(125, PI3, 0xc0, 0xc, + MA35_PIN(131, PI3, 0xc0, 0xc, MA35_MUX(0x0, "GPI3"), MA35_MUX(0x1, "EPWM0_CH3"), MA35_MUX(0x2, "UART12_TXD"), MA35_MUX(0x3, "CAN0_TXD"), + MA35_MUX(0x4, "USBHL2_DP"), MA35_MUX(0x5, "SPI3_MISO"), MA35_MUX(0x7, "SC0_RST"), MA35_MUX(0x8, "EBI_ADR3"), MA35_MUX(0xb, "TM1_EXT")), - MA35_PIN(126, PI4, 0xc0, 0x10, + MA35_PIN(132, PI4, 0xc0, 0x10, MA35_MUX(0x0, "GPI4"), MA35_MUX(0x1, "EPWM0_CH4"), MA35_MUX(0x2, "UART14_nCTS"), @@ -953,7 +1008,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x6, "I2S1_LRCK"), MA35_MUX(0x8, "EBI_ADR4"), MA35_MUX(0xd, "INT0")), - MA35_PIN(127, PI5, 0xc0, 0x14, + MA35_PIN(133, PI5, 0xc0, 0x14, MA35_MUX(0x0, "GPI5"), MA35_MUX(0x1, "EPWM0_CH5"), MA35_MUX(0x2, "UART14_nRTS"), @@ -962,65 +1017,67 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x6, "I2S1_BCLK"), MA35_MUX(0x8, "EBI_ADR5"), MA35_MUX(0xd, "INT1")), - MA35_PIN(128, PI6, 0xc0, 0x18, + MA35_PIN(134, PI6, 0xc0, 0x18, MA35_MUX(0x0, "GPI6"), MA35_MUX(0x1, "EPWM0_BRAKE0"), MA35_MUX(0x2, "UART14_RXD"), MA35_MUX(0x3, "CAN1_RXD"), + MA35_MUX(0x4, "USBHL3_DM"), MA35_MUX(0x6, "I2S1_DI"), MA35_MUX(0x8, "EBI_ADR6"), MA35_MUX(0xc, "QEI1_INDEX"), MA35_MUX(0xd, "INT2")), - MA35_PIN(129, PI7, 0xc0, 0x1c, + MA35_PIN(135, PI7, 0xc0, 0x1c, MA35_MUX(0x0, "GPI7"), MA35_MUX(0x1, "EPWM0_BRAKE1"), MA35_MUX(0x2, "UART14_TXD"), MA35_MUX(0x3, "CAN1_TXD"), + MA35_MUX(0x4, "USBHL3_DP"), MA35_MUX(0x6, "I2S1_DO"), MA35_MUX(0x8, "EBI_ADR7"), MA35_MUX(0xc, "ECAP0_IC0"), MA35_MUX(0xd, "INT3")), - MA35_PIN(130, PI8, 0xc4, 0x0, + MA35_PIN(136, PI8, 0xc4, 0x0, MA35_MUX(0x0, "GPI8"), MA35_MUX(0x2, "UART4_nCTS"), MA35_MUX(0x3, "UART3_RXD"), MA35_MUX(0x6, "LCM_DATA0"), MA35_MUX(0xc, "EBI_AD11")), - MA35_PIN(131, PI9, 0xc4, 0x4, + MA35_PIN(137, PI9, 0xc4, 0x4, MA35_MUX(0x0, "GPI9"), MA35_MUX(0x2, "UART4_nRTS"), MA35_MUX(0x3, "UART3_TXD"), MA35_MUX(0x6, "LCM_DATA1"), MA35_MUX(0xc, "EBI_AD12")), - MA35_PIN(132, PI10, 0xc4, 0x8, + MA35_PIN(138, PI10, 0xc4, 0x8, MA35_MUX(0x0, "GPI10"), MA35_MUX(0x2, "UART4_RXD"), MA35_MUX(0x6, "LCM_DATA2"), MA35_MUX(0xc, "EBI_AD13")), - MA35_PIN(133, PI11, 0xC4, 0xc, + MA35_PIN(139, PI11, 0xC4, 0xc, MA35_MUX(0x0, "GPI11"), MA35_MUX(0x2, "UART4_TXD"), MA35_MUX(0x6, "LCM_DATA3"), MA35_MUX(0xc, "EBI_AD14")), - MA35_PIN(134, PI12, 0xc4, 0x10, + MA35_PIN(140, PI12, 0xc4, 0x10, MA35_MUX(0x0, "GPI12"), MA35_MUX(0x2, "UART6_nCTS"), MA35_MUX(0x3, "UART5_RXD"), MA35_MUX(0x6, "LCM_DATA4")), - MA35_PIN(135, PI13, 0xc4, 0x14, + MA35_PIN(141, PI13, 0xc4, 0x14, MA35_MUX(0x0, "GPI13"), MA35_MUX(0x2, "UART6_nRTS"), MA35_MUX(0x3, "UART5_TXD"), MA35_MUX(0x6, "LCM_DATA5")), - MA35_PIN(136, PI14, 0xc4, 0x18, + MA35_PIN(142, PI14, 0xc4, 0x18, MA35_MUX(0x0, "GPI14"), MA35_MUX(0x2, "UART6_RXD"), MA35_MUX(0x6, "LCM_DATA6")), - MA35_PIN(137, PI15, 0xc4, 0x1c, + MA35_PIN(143, PI15, 0xc4, 0x1c, MA35_MUX(0x0, "GPI15"), MA35_MUX(0x2, "UART6_TXD"), MA35_MUX(0x6, "LCM_DATA7")), - MA35_PIN(138, PJ0, 0xc8, 0x0, + MA35_PIN(144, PJ0, 0xc8, 0x0, MA35_MUX(0x0, "GPJ0"), MA35_MUX(0x1, "EPWM1_BRAKE0"), MA35_MUX(0x2, "UART8_nCTS"), @@ -1034,7 +1091,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "EBI_ADR16"), MA35_MUX(0xb, "EBI_nCS0"), MA35_MUX(0xc, "EBI_AD7")), - MA35_PIN(139, PJ1, 0xc8, 0x4, + MA35_PIN(145, PJ1, 0xc8, 0x4, MA35_MUX(0x0, "GPJ1"), MA35_MUX(0x1, "EPWM1_BRAKE1"), MA35_MUX(0x2, "UART8_nRTS"), @@ -1048,11 +1105,12 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "EBI_ADR17"), MA35_MUX(0xb, "EBI_nCS1"), MA35_MUX(0xc, "EBI_AD8")), - MA35_PIN(140, PJ2, 0xc8, 0x8, + MA35_PIN(146, PJ2, 0xc8, 0x8, MA35_MUX(0x0, "GPJ2"), MA35_MUX(0x1, "EPWM1_CH4"), MA35_MUX(0x2, "UART8_RXD"), MA35_MUX(0x3, "CAN1_RXD"), + MA35_MUX(0x4, "USBHL5_DM"), MA35_MUX(0x5, "SPI2_MOSI"), MA35_MUX(0x6, "eMMC1_DAT6"), MA35_MUX(0x7, "I2S0_DI"), @@ -1061,11 +1119,12 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "EBI_ADR18"), MA35_MUX(0xb, "EBI_nWRH"), MA35_MUX(0xc, "EBI_AD9")), - MA35_PIN(141, PJ3, 0xc8, 0xc, + MA35_PIN(147, PJ3, 0xc8, 0xc, MA35_MUX(0x0, "GPJ3"), MA35_MUX(0x1, "EPWM1_CH5"), MA35_MUX(0x2, "UART8_TXD"), MA35_MUX(0x3, "CAN1_TXD"), + MA35_MUX(0x4, "USBHL5_DP"), MA35_MUX(0x5, "SPI2_MISO"), MA35_MUX(0x6, "eMMC1_DAT7"), MA35_MUX(0x7, "I2S0_DO"), @@ -1074,39 +1133,43 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "EBI_ADR19"), MA35_MUX(0xb, "EBI_nWRL"), MA35_MUX(0xc, "EBI_AD10")), - MA35_PIN(142, PJ4, 0xc8, 0x10, + MA35_PIN(148, PJ4, 0xc8, 0x10, MA35_MUX(0x0, "GPJ4"), MA35_MUX(0x4, "I2C3_SDA"), MA35_MUX(0x6, "SD1_WP")), - MA35_PIN(143, PJ5, 0xc8, 0x14, + MA35_PIN(149, PJ5, 0xc8, 0x14, MA35_MUX(0x0, "GPJ5"), MA35_MUX(0x4, "I2C3_SCL"), MA35_MUX(0x6, "SD1_nCD")), - MA35_PIN(144, PJ6, 0xc8, 0x18, + MA35_PIN(150, PJ6, 0xc8, 0x18, MA35_MUX(0x0, "GPJ6"), MA35_MUX(0x3, "CAN3_RXD"), + MA35_MUX(0x4, "USBHL0_DM"), MA35_MUX(0x6, "SD1_CMD/eMMC1_CMD")), - MA35_PIN(145, PJ7, 0xc8, 0x1c, + MA35_PIN(151, PJ7, 0xc8, 0x1c, MA35_MUX(0x0, "GPJ7"), MA35_MUX(0x3, "CAN3_TXD"), + MA35_MUX(0x4, "USBHL0_DP"), MA35_MUX(0x6, "SD1_CLK/eMMC1_CLK")), - MA35_PIN(146, PJ8, 0xcc, 0x0, + MA35_PIN(152, PJ8, 0xcc, 0x0, MA35_MUX(0x0, "GPJ8"), MA35_MUX(0x4, "I2C4_SDA"), MA35_MUX(0x6, "SD1_DAT0/eMMC1_DAT0")), - MA35_PIN(147, PJ9, 0xcc, 0x4, + MA35_PIN(153, PJ9, 0xcc, 0x4, MA35_MUX(0x0, "GPJ9"), MA35_MUX(0x4, "I2C4_SCL"), MA35_MUX(0x6, "SD1_DAT1/eMMC1_DAT1")), - MA35_PIN(148, PJ10, 0xcc, 0x8, + MA35_PIN(154, PJ10, 0xcc, 0x8, MA35_MUX(0x0, "GPJ10"), MA35_MUX(0x3, "CAN0_RXD"), + MA35_MUX(0x4, "USBHL1_DM"), MA35_MUX(0x6, "SD1_DAT2/eMMC1_DAT2")), - MA35_PIN(149, PJ11, 0xcc, 0xc, + MA35_PIN(155, PJ11, 0xcc, 0xc, MA35_MUX(0x0, "GPJ11"), MA35_MUX(0x3, "CAN0_TXD"), + MA35_MUX(0x4, "USBHL1_DP"), MA35_MUX(0x6, "SD1_DAT3/eMMC1_DAT3")), - MA35_PIN(150, PJ12, 0xcc, 0x10, + MA35_PIN(156, PJ12, 0xcc, 0x10, MA35_MUX(0x0, "GPJ12"), MA35_MUX(0x1, "EPWM1_CH2"), MA35_MUX(0x2, "UART2_nCTS"), @@ -1117,7 +1180,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "EBI_ADR12"), MA35_MUX(0xb, "TM2"), MA35_MUX(0xc, "QEI0_INDEX")), - MA35_PIN(151, PJ13, 0xcc, 0x14, + MA35_PIN(157, PJ13, 0xcc, 0x14, MA35_MUX(0x0, "GPJ13"), MA35_MUX(0x1, "EPWM1_CH3"), MA35_MUX(0x2, "UART2_nRTS"), @@ -1127,27 +1190,29 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x7, "SC1_DAT"), MA35_MUX(0x8, "EBI_ADR13"), MA35_MUX(0xb, "TM2_EXT")), - MA35_PIN(152, PJ14, 0xcc, 0x18, + MA35_PIN(158, PJ14, 0xcc, 0x18, MA35_MUX(0x0, "GPJ14"), MA35_MUX(0x1, "EPWM1_CH4"), MA35_MUX(0x2, "UART2_RXD"), MA35_MUX(0x3, "CAN3_RXD"), + MA35_MUX(0x4, "USBHL5_DM"), MA35_MUX(0x5, "SPI3_MISO"), MA35_MUX(0x7, "SC1_RST"), MA35_MUX(0x8, "EBI_ADR14"), MA35_MUX(0xb, "TM3")), - MA35_PIN(153, PJ15, 0xcc, 0x1c, + MA35_PIN(159, PJ15, 0xcc, 0x1c, MA35_MUX(0x0, "GPJ15"), MA35_MUX(0x1, "EPWM1_CH5"), MA35_MUX(0x2, "UART2_TXD"), MA35_MUX(0x3, "CAN3_TXD"), + MA35_MUX(0x4, "USBHL5_DP"), MA35_MUX(0x5, "SPI3_CLK"), MA35_MUX(0x6, "EADC0_ST"), MA35_MUX(0x7, "SC1_PWR"), MA35_MUX(0x8, "EBI_ADR15"), MA35_MUX(0xb, "TM3_EXT"), MA35_MUX(0xd, "INT1")), - MA35_PIN(154, PK0, 0xd0, 0x0, + MA35_PIN(160, PK0, 0xd0, 0x0, MA35_MUX(0x0, "GPK0"), MA35_MUX(0x1, "EPWM0_SYNC_IN"), MA35_MUX(0x2, "UART16_nCTS"), @@ -1157,7 +1222,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "EBI_ADR8"), MA35_MUX(0xb, "TM7"), MA35_MUX(0xc, "ECAP0_IC1")), - MA35_PIN(155, PK1, 0xd0, 0x4, + MA35_PIN(161, PK1, 0xd0, 0x4, MA35_MUX(0x0, "GPK1"), MA35_MUX(0x1, "EPWM0_SYNC_OUT"), MA35_MUX(0x2, "UART16_nRTS"), @@ -1167,25 +1232,27 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "EBI_ADR9"), MA35_MUX(0xb, "TM7_EXT"), MA35_MUX(0xc, "ECAP0_IC2")), - MA35_PIN(156, PK2, 0xd0, 0x8, + MA35_PIN(162, PK2, 0xd0, 0x8, MA35_MUX(0x0, "GPK2"), MA35_MUX(0x1, "EPWM1_CH0"), MA35_MUX(0x2, "UART16_RXD"), MA35_MUX(0x3, "CAN2_RXD"), + MA35_MUX(0x4, "USBHL4_DM"), MA35_MUX(0x5, "SPI3_I2SMCLK"), MA35_MUX(0x7, "SC0_PWR"), MA35_MUX(0x8, "EBI_ADR10"), MA35_MUX(0xc, "QEI0_A")), - MA35_PIN(157, PK3, 0xd0, 0xc, + MA35_PIN(163, PK3, 0xd0, 0xc, MA35_MUX(0x0, "GPK3"), MA35_MUX(0x1, "EPWM1_CH1"), MA35_MUX(0x2, "UART16_TXD"), MA35_MUX(0x3, "CAN2_TXD"), + MA35_MUX(0x4, "USBHL4_DP"), MA35_MUX(0x5, "SPI3_SS1"), MA35_MUX(0x7, "SC1_nCD"), MA35_MUX(0x8, "EBI_ADR11"), MA35_MUX(0xc, "QEI0_B")), - MA35_PIN(158, PK4, 0xd0, 0x10, + MA35_PIN(164, PK4, 0xd0, 0x10, MA35_MUX(0x0, "GPK4"), MA35_MUX(0x2, "UART12_nCTS"), MA35_MUX(0x3, "UART13_RXD"), @@ -1193,7 +1260,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x6, "LCM_DEN"), MA35_MUX(0xc, "EBI_AD10"), MA35_MUX(0xd, "EBI_nWRL")), - MA35_PIN(159, PK5, 0xd0, 0x14, + MA35_PIN(165, PK5, 0xd0, 0x14, MA35_MUX(0x0, "GPK5"), MA35_MUX(0x1, "EPWM1_CH1"), MA35_MUX(0x2, "UART12_nRTS"), @@ -1205,28 +1272,30 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "EADC0_ST"), MA35_MUX(0xb, "TM8_EXT"), MA35_MUX(0xd, "INT1")), - MA35_PIN(160, PK6, 0xd0, 0x18, + MA35_PIN(166, PK6, 0xd0, 0x18, MA35_MUX(0x0, "GPK6"), MA35_MUX(0x1, "EPWM1_CH2"), MA35_MUX(0x2, "UART12_RXD"), MA35_MUX(0x3, "CAN0_RXD"), + MA35_MUX(0x4, "USBHL4_DM"), MA35_MUX(0x5, "SPI2_MOSI"), MA35_MUX(0x7, "I2S1_BCLK"), MA35_MUX(0x8, "SC0_RST"), MA35_MUX(0xb, "TM6"), MA35_MUX(0xd, "INT2")), - MA35_PIN(161, PK7, 0xd0, 0x1c, + MA35_PIN(167, PK7, 0xd0, 0x1c, MA35_MUX(0x0, "GPK7"), MA35_MUX(0x1, "EPWM1_CH3"), MA35_MUX(0x2, "UART12_TXD"), MA35_MUX(0x3, "CAN0_TXD"), + MA35_MUX(0x4, "USBHL4_DP"), MA35_MUX(0x5, "SPI2_MISO"), MA35_MUX(0x7, "I2S1_LRCK"), MA35_MUX(0x8, "SC0_PWR"), MA35_MUX(0x9, "CLKO"), MA35_MUX(0xb, "TM6_EXT"), MA35_MUX(0xd, "INT3")), - MA35_PIN(162, PK8, 0xd4, 0x0, + MA35_PIN(168, PK8, 0xd4, 0x0, MA35_MUX(0x0, "GPK8"), MA35_MUX(0x1, "EPWM1_CH0"), MA35_MUX(0x4, "I2C3_SDA"), @@ -1237,25 +1306,27 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "EBI_ADR15"), MA35_MUX(0xb, "TM8"), MA35_MUX(0xc, "QEI1_INDEX")), - MA35_PIN(163, PK9, 0xd4, 0x4, + MA35_PIN(169, PK9, 0xd4, 0x4, MA35_MUX(0x0, "GPK9"), MA35_MUX(0x4, "I2C3_SCL"), MA35_MUX(0x6, "CCAP0_SCLK"), MA35_MUX(0x8, "EBI_AD0"), MA35_MUX(0xa, "EBI_ADR0")), - MA35_PIN(164, PK10, 0xd4, 0x8, + MA35_PIN(170, PK10, 0xd4, 0x8, MA35_MUX(0x0, "GPK10"), MA35_MUX(0x3, "CAN1_RXD"), + MA35_MUX(0x4, "USBHL3_DM"), MA35_MUX(0x6, "CCAP0_PIXCLK"), MA35_MUX(0x8, "EBI_AD1"), MA35_MUX(0xa, "EBI_ADR1")), - MA35_PIN(165, PK11, 0xd4, 0xc, + MA35_PIN(171, PK11, 0xd4, 0xc, MA35_MUX(0x0, "GPK11"), MA35_MUX(0x3, "CAN1_TXD"), + MA35_MUX(0x4, "USBHL3_DP"), MA35_MUX(0x6, "CCAP0_HSYNC"), MA35_MUX(0x8, "EBI_AD2"), MA35_MUX(0xa, "EBI_ADR2")), - MA35_PIN(166, PK12, 0xd4, 0x10, + MA35_PIN(172, PK12, 0xd4, 0x10, MA35_MUX(0x0, "GPK12"), MA35_MUX(0x1, "EPWM2_CH0"), MA35_MUX(0x2, "UART1_nCTS"), @@ -1266,7 +1337,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "SC0_CLK"), MA35_MUX(0xb, "TM10"), MA35_MUX(0xd, "INT2")), - MA35_PIN(167, PK13, 0xd4, 0x14, + MA35_PIN(173, PK13, 0xd4, 0x14, MA35_MUX(0x0, "GPK13"), MA35_MUX(0x1, "EPWM2_CH1"), MA35_MUX(0x2, "UART1_nRTS"), @@ -1276,28 +1347,30 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x6, "SPI1_CLK"), MA35_MUX(0x8, "SC0_DAT"), MA35_MUX(0xb, "TM10_EXT")), - MA35_PIN(168, PK14, 0xd4, 0x18, + MA35_PIN(174, PK14, 0xd4, 0x18, MA35_MUX(0x0, "GPK14"), MA35_MUX(0x1, "EPWM2_CH2"), MA35_MUX(0x2, "UART1_RXD"), MA35_MUX(0x3, "CAN3_RXD"), + MA35_MUX(0x4, "USBHL4_DM"), MA35_MUX(0x5, "I2S0_DI"), MA35_MUX(0x6, "SPI1_MOSI"), MA35_MUX(0x8, "SC0_RST"), MA35_MUX(0xa, "I2C5_SDA"), MA35_MUX(0xb, "TM11"), MA35_MUX(0xd, "INT3")), - MA35_PIN(169, PK15, 0xd4, 0x1c, + MA35_PIN(175, PK15, 0xd4, 0x1c, MA35_MUX(0x0, "GPK15"), MA35_MUX(0x1, "EPWM2_CH3"), MA35_MUX(0x2, "UART1_TXD"), MA35_MUX(0x3, "CAN3_TXD"), + MA35_MUX(0x4, "USBHL4_DP"), MA35_MUX(0x5, "I2S0_DO"), MA35_MUX(0x6, "SPI1_MISO"), MA35_MUX(0x8, "SC0_PWR"), MA35_MUX(0xa, "I2C5_SCL"), MA35_MUX(0xb, "TM11_EXT")), - MA35_PIN(170, PL0, 0xd8, 0x0, + MA35_PIN(176, PL0, 0xd8, 0x0, MA35_MUX(0x0, "GPL0"), MA35_MUX(0x1, "EPWM1_CH0"), MA35_MUX(0x2, "UART11_nCTS"), @@ -1310,7 +1383,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "SC1_CLK"), MA35_MUX(0xb, "TM5"), MA35_MUX(0xc, "QEI1_A")), - MA35_PIN(171, PL1, 0xd8, 0x4, + MA35_PIN(177, PL1, 0xd8, 0x4, MA35_MUX(0x0, "GPL1"), MA35_MUX(0x1, "EPWM1_CH1"), MA35_MUX(0x2, "UART11_nRTS"), @@ -1323,11 +1396,12 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "SC1_DAT"), MA35_MUX(0xb, "TM5_EXT"), MA35_MUX(0xc, "QEI1_B")), - MA35_PIN(172, PL2, 0xd8, 0x8, + MA35_PIN(178, PL2, 0xd8, 0x8, MA35_MUX(0x0, "GPL2"), MA35_MUX(0x1, "EPWM1_CH2"), MA35_MUX(0x2, "UART11_RXD"), MA35_MUX(0x3, "CAN3_RXD"), + MA35_MUX(0x4, "USBHL4_DM"), MA35_MUX(0x5, "SPI2_SS0"), MA35_MUX(0x6, "QSPI1_SS1"), MA35_MUX(0x7, "I2S0_DI"), @@ -1335,11 +1409,12 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "SC1_RST"), MA35_MUX(0xb, "TM7"), MA35_MUX(0xc, "QEI1_INDEX")), - MA35_PIN(173, PL3, 0xd8, 0xc, + MA35_PIN(179, PL3, 0xd8, 0xc, MA35_MUX(0x0, "GPL3"), MA35_MUX(0x1, "EPWM1_CH3"), MA35_MUX(0x2, "UART11_TXD"), MA35_MUX(0x3, "CAN3_TXD"), + MA35_MUX(0x4, "USBHL4_DP"), MA35_MUX(0x5, "SPI2_CLK"), MA35_MUX(0x6, "QSPI1_CLK"), MA35_MUX(0x7, "I2S0_DO"), @@ -1347,7 +1422,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "SC1_PWR"), MA35_MUX(0xb, "TM7_EXT"), MA35_MUX(0xc, "ECAP0_IC0")), - MA35_PIN(174, PL4, 0xd8, 0x10, + MA35_PIN(180, PL4, 0xd8, 0x10, MA35_MUX(0x0, "GPL4"), MA35_MUX(0x1, "EPWM1_CH4"), MA35_MUX(0x2, "UART2_nCTS"), @@ -1360,7 +1435,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "SC1_nCD"), MA35_MUX(0xb, "TM9"), MA35_MUX(0xc, "ECAP0_IC1")), - MA35_PIN(175, PL5, 0xd8, 0x14, + MA35_PIN(181, PL5, 0xd8, 0x14, MA35_MUX(0x0, "GPL5"), MA35_MUX(0x1, "EPWM1_CH5"), MA35_MUX(0x2, "UART2_nRTS"), @@ -1373,28 +1448,30 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "SC0_nCD"), MA35_MUX(0xb, "TM9_EXT"), MA35_MUX(0xc, "ECAP0_IC2")), - MA35_PIN(176, PL6, 0xd8, 0x18, + MA35_PIN(182, PL6, 0xd8, 0x18, MA35_MUX(0x0, "GPL6"), MA35_MUX(0x1, "EPWM0_CH0"), MA35_MUX(0x2, "UART2_RXD"), MA35_MUX(0x3, "CAN0_RXD"), + MA35_MUX(0x4, "USBHL5_DM"), MA35_MUX(0x6, "QSPI1_MOSI1"), MA35_MUX(0x7, "TRACE_CLK"), MA35_MUX(0x8, "EBI_AD5"), MA35_MUX(0xb, "TM3"), MA35_MUX(0xc, "ECAP1_IC0"), MA35_MUX(0xd, "INT0")), - MA35_PIN(177, PL7, 0xd8, 0x1c, + MA35_PIN(183, PL7, 0xd8, 0x1c, MA35_MUX(0x0, "GPL7"), MA35_MUX(0x1, "EPWM0_CH1"), MA35_MUX(0x2, "UART2_TXD"), MA35_MUX(0x3, "CAN0_TXD"), + MA35_MUX(0x4, "USBHL5_DP"), MA35_MUX(0x6, "QSPI1_MISO1"), MA35_MUX(0x8, "EBI_AD6"), MA35_MUX(0xb, "TM3_EXT"), MA35_MUX(0xc, "ECAP1_IC1"), MA35_MUX(0xd, "INT1")), - MA35_PIN(178, PL8, 0xdc, 0x0, + MA35_PIN(184, PL8, 0xdc, 0x0, MA35_MUX(0x0, "GPL8"), MA35_MUX(0x1, "EPWM0_CH2"), MA35_MUX(0x2, "UART14_nCTS"), @@ -1408,7 +1485,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xb, "TM4"), MA35_MUX(0xc, "ECAP1_IC2"), MA35_MUX(0xd, "INT2")), - MA35_PIN(179, PL9, 0xdc, 0x4, + MA35_PIN(185, PL9, 0xdc, 0x4, MA35_MUX(0x0, "GPL9"), MA35_MUX(0x1, "EPWM0_CH3"), MA35_MUX(0x2, "UART14_nRTS"), @@ -1422,11 +1499,12 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xb, "TM4_EXT"), MA35_MUX(0xc, "QEI0_A"), MA35_MUX(0xd, "INT3")), - MA35_PIN(180, PL10, 0xdc, 0x8, + MA35_PIN(186, PL10, 0xdc, 0x8, MA35_MUX(0x0, "GPL10"), MA35_MUX(0x1, "EPWM0_CH4"), MA35_MUX(0x2, "UART14_RXD"), MA35_MUX(0x3, "CAN3_RXD"), + MA35_MUX(0x4, "USBHL2_DM"), MA35_MUX(0x5, "SPI3_MOSI"), MA35_MUX(0x6, "EPWM0_CH5"), MA35_MUX(0x7, "I2S1_DI"), @@ -1434,11 +1512,12 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "SC0_RST"), MA35_MUX(0xb, "EBI_nWRH"), MA35_MUX(0xc, "QEI0_B")), - MA35_PIN(181, PL11, 0xdc, 0xc, + MA35_PIN(187, PL11, 0xdc, 0xc, MA35_MUX(0x0, "GPL11"), MA35_MUX(0x1, "EPWM0_CH5"), MA35_MUX(0x2, "UART14_TXD"), MA35_MUX(0x3, "CAN3_TXD"), + MA35_MUX(0x4, "USBHL2_DP"), MA35_MUX(0x5, "SPI3_MISO"), MA35_MUX(0x6, "EPWM1_CH5"), MA35_MUX(0x7, "I2S1_DO"), @@ -1446,7 +1525,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "SC0_PWR"), MA35_MUX(0xb, "EBI_nWRL"), MA35_MUX(0xc, "QEI0_INDEX")), - MA35_PIN(182, PL12, 0xdc, 0x10, + MA35_PIN(188, PL12, 0xdc, 0x10, MA35_MUX(0x0, "GPL12"), MA35_MUX(0x1, "EPWM0_SYNC_IN"), MA35_MUX(0x2, "UART7_nCTS"), @@ -1463,7 +1542,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xd, "EBI_AD11"), MA35_MUX(0xe, "RGMII0_PPS"), MA35_MUX(0xf, "RMII0_PPS")), - MA35_PIN(183, PL13, 0xdc, 0x14, + MA35_PIN(189, PL13, 0xdc, 0x14, MA35_MUX(0x0, "GPL13"), MA35_MUX(0x1, "EPWM0_SYNC_OUT"), MA35_MUX(0x2, "UART7_nRTS"), @@ -1480,7 +1559,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xd, "EBI_AD12"), MA35_MUX(0xe, "RGMII1_PPS"), MA35_MUX(0xf, "RMII1_PPS")), - MA35_PIN(184, PL14, 0xdc, 0x18, + MA35_PIN(190, PL14, 0xdc, 0x18, MA35_MUX(0x0, "GPL14"), MA35_MUX(0x1, "EPWM0_CH2"), MA35_MUX(0x2, "UART7_RXD"), @@ -1492,7 +1571,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xb, "TM2"), MA35_MUX(0xc, "INT0"), MA35_MUX(0xd, "EBI_AD13")), - MA35_PIN(185, PL15, 0xdc, 0x1c, + MA35_PIN(191, PL15, 0xdc, 0x1c, MA35_MUX(0x0, "GPL15"), MA35_MUX(0x1, "EPWM0_CH1"), MA35_MUX(0x2, "UART7_TXD"), @@ -1505,86 +1584,92 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xb, "TM2_EXT"), MA35_MUX(0xc, "INT2"), MA35_MUX(0xd, "EBI_AD14")), - MA35_PIN(186, PM0, 0xe0, 0x0, + MA35_PIN(192, PM0, 0xe0, 0x0, MA35_MUX(0x0, "GPM0"), MA35_MUX(0x4, "I2C4_SDA"), MA35_MUX(0x6, "CCAP0_VSYNC"), MA35_MUX(0x8, "EBI_AD3"), MA35_MUX(0xa, "EBI_ADR3")), - MA35_PIN(187, PM1, 0xe0, 0x4, + MA35_PIN(193, PM1, 0xe0, 0x4, MA35_MUX(0x0, "GPM1"), MA35_MUX(0x4, "I2C4_SCL"), MA35_MUX(0x5, "SPI3_I2SMCLK"), MA35_MUX(0x6, "CCAP0_SFIELD"), MA35_MUX(0x8, "EBI_AD4"), MA35_MUX(0xa, "EBI_ADR4")), - MA35_PIN(188, PM2, 0xe0, 0x8, + MA35_PIN(194, PM2, 0xe0, 0x8, MA35_MUX(0x0, "GPM2"), MA35_MUX(0x3, "CAN3_RXD"), + MA35_MUX(0x4, "USBHL0_DM"), MA35_MUX(0x6, "CCAP0_DATA0"), MA35_MUX(0x8, "EBI_AD5"), MA35_MUX(0xa, "EBI_ADR5")), - MA35_PIN(189, PM3, 0xe0, 0xc, + MA35_PIN(195, PM3, 0xe0, 0xc, MA35_MUX(0x0, "GPM3"), MA35_MUX(0x3, "CAN3_TXD"), + MA35_MUX(0x4, "USBHL0_DP"), MA35_MUX(0x6, "CCAP0_DATA1"), MA35_MUX(0x8, "EBI_AD6"), MA35_MUX(0xa, "EBI_ADR6")), - MA35_PIN(190, PM4, 0xe0, 0x10, + MA35_PIN(196, PM4, 0xe0, 0x10, MA35_MUX(0x0, "GPM4"), MA35_MUX(0x4, "I2C5_SDA"), MA35_MUX(0x6, "CCAP0_DATA2"), MA35_MUX(0x8, "EBI_AD7"), MA35_MUX(0xa, "EBI_ADR7")), - MA35_PIN(191, PM5, 0xe0, 0x14, + MA35_PIN(197, PM5, 0xe0, 0x14, MA35_MUX(0x0, "GPM5"), MA35_MUX(0x4, "I2C5_SCL"), MA35_MUX(0x6, "CCAP0_DATA3"), MA35_MUX(0x8, "EBI_AD8"), MA35_MUX(0xa, "EBI_ADR8")), - MA35_PIN(192, PM6, 0xe0, 0x18, + MA35_PIN(198, PM6, 0xe0, 0x18, MA35_MUX(0x0, "GPM6"), MA35_MUX(0x3, "CAN0_RXD"), + MA35_MUX(0x4, "USBHL1_DM"), MA35_MUX(0x6, "CCAP0_DATA4"), MA35_MUX(0x8, "EBI_AD9"), MA35_MUX(0xa, "EBI_ADR9")), - MA35_PIN(193, PM7, 0xe0, 0x1c, + MA35_PIN(199, PM7, 0xe0, 0x1c, MA35_MUX(0x0, "GPM7"), MA35_MUX(0x3, "CAN0_TXD"), + MA35_MUX(0x4, "USBHL1_DP"), MA35_MUX(0x6, "CCAP0_DATA5"), MA35_MUX(0x8, "EBI_AD10"), MA35_MUX(0xa, "EBI_ADR10")), - MA35_PIN(194, PM8, 0xe4, 0x0, + MA35_PIN(200, PM8, 0xe4, 0x0, MA35_MUX(0x0, "GPM8"), MA35_MUX(0x4, "I2C0_SDA"), MA35_MUX(0x6, "CCAP0_DATA6"), MA35_MUX(0x8, "EBI_AD11"), MA35_MUX(0xa, "EBI_ADR11")), - MA35_PIN(195, PM9, 0xe4, 0x4, + MA35_PIN(201, PM9, 0xe4, 0x4, MA35_MUX(0x0, "GPM9"), MA35_MUX(0x4, "I2C0_SCL"), MA35_MUX(0x6, "CCAP0_DATA7"), MA35_MUX(0x8, "EBI_AD12"), MA35_MUX(0xa, "EBI_ADR12")), - MA35_PIN(196, PM10, 0xe4, 0x8, + MA35_PIN(202, PM10, 0xe4, 0x8, MA35_MUX(0x0, "GPM10"), MA35_MUX(0x1, "EPWM1_CH2"), MA35_MUX(0x3, "CAN2_RXD"), + MA35_MUX(0x4, "USBHL4_DM"), MA35_MUX(0x5, "SPI3_SS0"), MA35_MUX(0x6, "CCAP0_DATA8"), MA35_MUX(0x7, "SPI2_I2SMCLK"), MA35_MUX(0x8, "EBI_AD13"), MA35_MUX(0xa, "EBI_ADR13")), - MA35_PIN(197, PM11, 0xe4, 0xc, + MA35_PIN(203, PM11, 0xe4, 0xc, MA35_MUX(0x0, "GPM11"), MA35_MUX(0x1, "EPWM1_CH3"), MA35_MUX(0x3, "CAN2_TXD"), + MA35_MUX(0x4, "USBHL4_DP"), MA35_MUX(0x5, "SPI3_SS1"), MA35_MUX(0x6, "CCAP0_DATA9"), MA35_MUX(0x7, "SPI2_SS1"), MA35_MUX(0x8, "EBI_AD14"), MA35_MUX(0xa, "EBI_ADR14")), - MA35_PIN(198, PM12, 0xe4, 0x10, + MA35_PIN(204, PM12, 0xe4, 0x10, MA35_MUX(0x0, "GPM12"), MA35_MUX(0x1, "EPWM1_CH4"), MA35_MUX(0x2, "UART10_nCTS"), @@ -1595,7 +1680,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "EBI_AD8"), MA35_MUX(0x9, "I2S1_MCLK"), MA35_MUX(0xb, "TM8")), - MA35_PIN(199, PM13, 0xe4, 0x14, + MA35_PIN(205, PM13, 0xe4, 0x14, MA35_MUX(0x0, "GPM13"), MA35_MUX(0x1, "EPWM1_CH5"), MA35_MUX(0x2, "UART10_nRTS"), @@ -1605,99 +1690,66 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "EBI_AD9"), MA35_MUX(0x9, "ECAP1_IC0"), MA35_MUX(0xb, "TM8_EXT")), - MA35_PIN(200, PM14, 0xe4, 0x18, + MA35_PIN(206, PM14, 0xe4, 0x18, MA35_MUX(0x0, "GPM14"), MA35_MUX(0x1, "EPWM1_BRAKE0"), MA35_MUX(0x2, "UART10_RXD"), MA35_MUX(0x3, "TRACE_DATA2"), MA35_MUX(0x4, "CAN2_RXD"), + MA35_MUX(0x5, "USBHL3_DM"), MA35_MUX(0x6, "I2C3_SDA"), MA35_MUX(0x8, "EBI_AD10"), MA35_MUX(0x9, "ECAP1_IC1"), MA35_MUX(0xb, "TM10"), MA35_MUX(0xd, "INT1")), - MA35_PIN(201, PM15, 0xe4, 0x1c, + MA35_PIN(207, PM15, 0xe4, 0x1c, MA35_MUX(0x0, "GPM15"), MA35_MUX(0x1, "EPWM1_BRAKE1"), MA35_MUX(0x2, "UART10_TXD"), MA35_MUX(0x3, "TRACE_DATA3"), MA35_MUX(0x4, "CAN2_TXD"), + MA35_MUX(0x5, "USBHL3_DP"), MA35_MUX(0x6, "I2C3_SCL"), MA35_MUX(0x8, "EBI_AD11"), MA35_MUX(0x9, "ECAP1_IC2"), MA35_MUX(0xb, "TM10_EXT"), MA35_MUX(0xd, "INT2")), - MA35_PIN(202, PN0, 0xe8, 0x0, + MA35_PIN(208, PN0, 0xe8, 0x0, MA35_MUX(0x0, "GPN0"), MA35_MUX(0x4, "I2C2_SDA"), MA35_MUX(0x6, "CCAP1_DATA0")), - MA35_PIN(203, PN1, 0xe8, 0x4, + MA35_PIN(209, PN1, 0xe8, 0x4, MA35_MUX(0x0, "GPN1"), MA35_MUX(0x4, "I2C2_SCL"), MA35_MUX(0x6, "CCAP1_DATA1")), - MA35_PIN(204, PN2, 0xe8, 0x8, + MA35_PIN(210, PN2, 0xe8, 0x8, MA35_MUX(0x0, "GPN2"), MA35_MUX(0x3, "CAN0_RXD"), + MA35_MUX(0x4, "USBHL0_DM"), MA35_MUX(0x6, "CCAP1_DATA2")), - MA35_PIN(205, PN3, 0xe8, 0xc, + MA35_PIN(211, PN3, 0xe8, 0xc, MA35_MUX(0x0, "GPN3"), MA35_MUX(0x3, "CAN0_TXD"), + MA35_MUX(0x4, "USBHL0_DP"), MA35_MUX(0x6, "CCAP1_DATA3")), - MA35_PIN(206, PN4, 0xe8, 0x10, + MA35_PIN(212, PN4, 0xe8, 0x10, MA35_MUX(0x0, "GPN4"), MA35_MUX(0x4, "I2C1_SDA"), MA35_MUX(0x6, "CCAP1_DATA4")), - MA35_PIN(207, PN5, 0xe8, 0x14, + MA35_PIN(213, PN5, 0xe8, 0x14, MA35_MUX(0x0, "GPN5"), MA35_MUX(0x4, "I2C1_SCL"), MA35_MUX(0x6, "CCAP1_DATA5")), - MA35_PIN(208, PN6, 0xe8, 0x18, + MA35_PIN(214, PN6, 0xe8, 0x18, MA35_MUX(0x0, "GPN6"), MA35_MUX(0x3, "CAN1_RXD"), + MA35_MUX(0x4, "USBHL1_DM"), MA35_MUX(0x6, "CCAP1_DATA6")), - MA35_PIN(209, PN7, 0xe8, 0x1c, + MA35_PIN(215, PN7, 0xe8, 0x1c, MA35_MUX(0x0, "GPN7"), MA35_MUX(0x3, "CAN1_TXD"), + MA35_MUX(0x4, "USBHL1_DP"), MA35_MUX(0x6, "CCAP1_DATA7")), - MA35_PIN(210, PN10, 0xec, 0x8, - MA35_MUX(0x0, "GPN10"), - MA35_MUX(0x3, "CAN2_RXD"), - MA35_MUX(0x6, "CCAP1_SCLK")), - MA35_PIN(211, PN11, 0xec, 0xc, - MA35_MUX(0x0, "GPN11"), - MA35_MUX(0x3, "CAN2_TXD"), - MA35_MUX(0x6, "CCAP1_PIXCLK")), - MA35_PIN(212, PN12, 0xec, 0x10, - MA35_MUX(0x0, "GPN12"), - MA35_MUX(0x2, "UART6_nCTS"), - MA35_MUX(0x3, "UART12_RXD"), - MA35_MUX(0x4, "I2C5_SDA"), - MA35_MUX(0x6, "CCAP1_HSYNC")), - MA35_PIN(213, PN13, 0xec, 0x14, - MA35_MUX(0x0, "GPN13"), - MA35_MUX(0x2, "UART6_nRTS"), - MA35_MUX(0x3, "UART12_TXD"), - MA35_MUX(0x4, "I2C5_SCL"), - MA35_MUX(0x6, "CCAP1_VSYNC")), - MA35_PIN(214, PN14, 0xec, 0x18, - MA35_MUX(0x0, "GPN14"), - MA35_MUX(0x2, "UART6_RXD"), - MA35_MUX(0x3, "CAN3_RXD"), - MA35_MUX(0x5, "SPI1_SS1"), - MA35_MUX(0x6, "CCAP1_SFIELD"), - MA35_MUX(0x7, "SPI1_I2SMCLK")), - MA35_PIN(215, PN15, 0xec, 0x1c, - MA35_MUX(0x0, "GPN15"), - MA35_MUX(0x1, "EPWM2_CH4"), - MA35_MUX(0x2, "UART6_TXD"), - MA35_MUX(0x3, "CAN3_TXD"), - MA35_MUX(0x5, "I2S0_MCLK"), - MA35_MUX(0x6, "SPI1_SS1"), - MA35_MUX(0x7, "SPI1_I2SMCLK"), - MA35_MUX(0x8, "SC0_nCD"), - MA35_MUX(0x9, "EADC0_ST"), - MA35_MUX(0xa, "CLKO"), - MA35_MUX(0xb, "TM6")), MA35_PIN(216, PN8, 0xec, 0x0, MA35_MUX(0x0, "GPN8"), MA35_MUX(0x1, "EPWM2_CH4"), From 6d497a3f2fa6f28aba2330ae2a83945baa5253a1 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 8 May 2026 15:34:52 +0100 Subject: [PATCH 1003/2758] pinctrl: cs42l43: Fix leaked pm reference on error path [ Upstream commit 1cb73b83ab6dec8159d0280345a46fbb282c378f ] Returning directly if the regmap_update_bits() fails causes a pm runtime reference to be leaked, let things run to the end of the function instead. Fixes: e52c741907fb ("pinctrl: cirrus: cs42l43: use new GPIO line value setter callbacks") Signed-off-by: Charles Keepax Reviewed-by: Bartosz Golaszewski Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/cirrus/pinctrl-cs42l43.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/pinctrl/cirrus/pinctrl-cs42l43.c b/drivers/pinctrl/cirrus/pinctrl-cs42l43.c index 227c37c360e19..3cc1835206077 100644 --- a/drivers/pinctrl/cirrus/pinctrl-cs42l43.c +++ b/drivers/pinctrl/cirrus/pinctrl-cs42l43.c @@ -499,12 +499,10 @@ static int cs42l43_gpio_set(struct gpio_chip *chip, unsigned int offset, ret = regmap_update_bits(priv->regmap, CS42L43_GPIO_CTRL1, BIT(shift), value << shift); - if (ret) - return ret; pm_runtime_put(priv->dev); - return 0; + return ret; } static int cs42l43_gpio_direction_out(struct gpio_chip *chip, From adde45911310511b298f479dea0a743683e0eefb Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 8 May 2026 15:34:53 +0100 Subject: [PATCH 1004/2758] pinctrl: cs42l43: Fix polarity on debounce [ Upstream commit 9da52ee80aee3ab3c69208bd1cfbb4be01371214 ] The debounce bit sets a bypass on the debounce rather than enabling it, as such the current polarity of the debounce is set incorrectly. Invert the polarity to correct this. Fixes: d5282a539297 ("pinctrl: cs42l43: Add support for the cs42l43") Signed-off-by: Charles Keepax Reviewed-by: Bartosz Golaszewski Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/cirrus/pinctrl-cs42l43.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/cirrus/pinctrl-cs42l43.c b/drivers/pinctrl/cirrus/pinctrl-cs42l43.c index 3cc1835206077..305233fc19876 100644 --- a/drivers/pinctrl/cirrus/pinctrl-cs42l43.c +++ b/drivers/pinctrl/cirrus/pinctrl-cs42l43.c @@ -343,7 +343,7 @@ static int cs42l43_pin_set_db(struct cs42l43_pin *priv, unsigned int pin, return regmap_update_bits(priv->regmap, CS42L43_GPIO_CTRL2, CS42L43_GPIO1_DEGLITCH_BYP_MASK << pin, - !!us << pin); + !us << pin); } static int cs42l43_pin_config_get(struct pinctrl_dev *pctldev, From 673fb7374b1b45836673ba908cc91fd165460933 Mon Sep 17 00:00:00 2001 From: Jia He Date: Tue, 19 May 2026 09:39:37 +0000 Subject: [PATCH 1005/2758] init/initramfs_test: wait_for_initramfs() before running [ Upstream commit ec3f4e0443a61e68092ac07111f16dd4ca89ddb4 ] initramfs_test_extract() and friends call unpack_to_rootfs() from a kunit kthread while do_populate_rootfs() may still be running asynchronously from rootfs_initcall. unpack_to_rootfs() keeps its parser state in module-static variables (victim, byte_count, state, this_header, header_buf, name_buf, ...), so the two writers corrupt each other. On arm64 v7.0-rc5+ this oopses early in boot: Unable to handle kernel paging request at virtual address ffff80018f9f0ffc pc : do_reset+0x3c/0x98 Call trace: do_reset initramfs_test_extract kunit_try_run_case Initramfs unpacking failed: junk within compressed archive do_reset() faults because 'victim' was overwritten by the boot-time unpacker; the boot unpacker meanwhile logs the bogus "junk within compressed archive" on the real initrd because the test wrecked its state machine. Add a .suite_init callback that calls wait_for_initramfs() so the async unpack is quiescent before the first case runs. suite_init runs once per suite rather than before every individual test case. Fixes: 83c0b27266ec ("initramfs_test: kunit tests for initramfs unpacking") Signed-off-by: Jia He Link: https://patch.msgid.link/20260519093937.1064628-1-justin.he@arm.com Reviewed-by: David Disseldorp Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- init/initramfs_test.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/init/initramfs_test.c b/init/initramfs_test.c index 2ce38d9a8fd0f..a4c06ec343689 100644 --- a/init/initramfs_test.c +++ b/init/initramfs_test.c @@ -3,7 +3,9 @@ #include #include #include +#include #include +#include #include #include #include "initramfs_internal.h" @@ -510,8 +512,21 @@ static struct kunit_case __refdata initramfs_test_cases[] = { {}, }; -static struct kunit_suite initramfs_test_suite = { +static int __init initramfs_test_init(struct kunit_suite *suite) +{ + /* + * unpack_to_rootfs() uses module-static state (victim, byte_count, + * state, ...). The boot-time async do_populate_rootfs() may still be + * running, so wait for it to finish before we call unpack_to_rootfs() + * from the test thread, otherwise the two writers race and crash. + */ + wait_for_initramfs(); + return 0; +} + +static struct kunit_suite __refdata initramfs_test_suite = { .name = "initramfs", + .suite_init = initramfs_test_init, .test_cases = initramfs_test_cases, }; kunit_test_init_section_suites(&initramfs_test_suite); From 5fbe83a374f09561a0f0c1f4aa021501ffd681eb Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Tue, 26 May 2026 17:22:22 +0800 Subject: [PATCH 1006/2758] nvmet-tcp: fix page fragment cache leak in error path [ Upstream commit 4dae393956093c807212918fd91a8fc70df15338 ] In nvmet_tcp_alloc_queue(), when a connection is closed during the allocation process (e.g., nvmet_tcp_set_queue_sock() returns -ENOTCONN), the error handling jumps to out_destroy_sq and then to out_ida_remove without draining the page fragment cache. Although nvmet_tcp_free_cmd() is called in some error paths to release individual page fragments, the underlying page cache reference held by queue->pf_cache is never released. The first allocation using pf_cache is the call to nvmet_tcp_alloc_cmd() for queue->connect, which happens after ida_alloc() returns successfully. This results in a page leak each time a connection fails during allocation, which could lead to memory exhaustion over time if connections are repeatedly opened and closed. Fix this by calling page_frag_cache_drain() before freeing the queue structure in the out_ida_remove label. Fixes: 872d26a391da ("nvmet-tcp: add NVMe over TCP target driver") Reviewed-by: Christoph Hellwig Signed-off-by: Geliang Tang Signed-off-by: Keith Busch Signed-off-by: Sasha Levin --- drivers/nvme/target/tcp.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c index 20f150d17a962..57a6da696fa0b 100644 --- a/drivers/nvme/target/tcp.c +++ b/drivers/nvme/target/tcp.c @@ -1999,6 +1999,12 @@ static void nvmet_tcp_alloc_queue(struct nvmet_tcp_port *port, nvmet_tcp_free_cmd(&queue->connect); out_ida_remove: ida_free(&nvmet_tcp_queue_ida, queue->idx); + /* + * Drain the page fragment cache if any allocations were done. + * The first allocation using pf_cache is nvmet_tcp_alloc_cmd() + * for queue->connect after ida_alloc(). + */ + page_frag_cache_drain(&queue->pf_cache); out_sock: fput(queue->sock->file); out_free_queue: From 22aa70f9a0544643ec37d442b6fcb1833d804462 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Tue, 26 May 2026 17:28:05 +0800 Subject: [PATCH 1007/2758] nvmet-tcp: check return value of nvmet_tcp_set_queue_sock [ Upstream commit 7ef789703e2b91775dcb36b2efa46325be31a2a0 ] The return value of nvmet_tcp_set_queue_sock() is currently ignored in nvmet_tcp_tls_handshake_done(). If it fails (e.g., due to the socket not being in TCP_ESTABLISHED state), the socket callbacks will not be properly set, leading to queue and socket leakage. Fix this by capturing the return value and calling nvmet_tcp_schedule_release_queue() on failure to ensure proper cleanup. Fixes: 675b453e0241 ("nvmet-tcp: enable TLS handshake upcall") Reviewed-by: Hannes Reinecke Reviewed-by: Chaitanya Kulkarni Signed-off-by: Geliang Tang Signed-off-by: Keith Busch Signed-off-by: Sasha Levin --- drivers/nvme/target/tcp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c index 57a6da696fa0b..15c52f1f95f1b 100644 --- a/drivers/nvme/target/tcp.c +++ b/drivers/nvme/target/tcp.c @@ -1844,10 +1844,11 @@ static void nvmet_tcp_tls_handshake_done(void *data, int status, if (!status) status = nvmet_tcp_tls_key_lookup(queue, peerid); + if (!status) + status = nvmet_tcp_set_queue_sock(queue); + if (status) nvmet_tcp_schedule_release_queue(queue); - else - nvmet_tcp_set_queue_sock(queue); kref_put(&queue->kref, nvmet_tcp_release_queue); } From 7173a741fed73de6247384056fe92e582ba12507 Mon Sep 17 00:00:00 2001 From: Nilay Shroff Date: Wed, 27 May 2026 11:50:00 +0530 Subject: [PATCH 1008/2758] nvme-multipath: fix flex array size in struct nvme_ns_head [ Upstream commit 001e57554de81aa79c25c18fd53911d8a415c304 ] struct nvme_ns_head contains a flexible array member, current_path[], which is indexed using the NUMA node ID: head->current_path[numa_node_id()] The structure is currently allocated as: size = sizeof(struct nvme_ns_head) + (num_possible_nodes() * sizeof(struct nvme_ns *)); head = kzalloc(size, GFP_KERNEL); This allocation assumes that NUMA node IDs are sequential and densely packed from 0 .. num_possible_nodes() - 1. While this assumption holds on many systems, it is not always true on some architectures such as powerpc. On some powerpc systems, NUMA node IDs can be sparse. For example: NUMA: NUMA node(s): 6 NUMA node0 CPU(s): 80-159 NUMA node8 CPU(s): 0-79 NUMA node252 CPU(s): NUMA node253 CPU(s): NUMA node254 CPU(s): NUMA node255 CPU(s): That is, the possible/online NUMA node IDs are: 0, 8, 252, 253, 254, 255 In this case: num_possible_nodes() = 6 So memory is allocated for only 6 entries in current_path[]. However, the array is later indexed using the actual NUMA node ID. As a result, accesses such as: head->current_path[8] or head->current_path[252] goes out of bounds, leading to the following KASAN splat: ================================================================== BUG: KASAN: slab-out-of-bounds in nvme_mpath_revalidate_paths+0x22c/0x290 [nvme_core] Write of size 8 at addr c00020003bda35b8 by task kworker/u641:2/1997 CPU: 1 UID: 0 PID: 1997 Comm: kworker/u641:2 Not tainted 7.1.0-rc5-dirty #14 PREEMPT(lazy) Hardware name: 8335-GTH POWER9 0x4e1202 opal:skiboot-v6.5.3-35-g1851b2a06 PowerNV Workqueue: async async_run_entry_fn Call Trace: [c000200037fa7510] [c0000000021c23d4] dump_stack_lvl+0x88/0xdc (unreliable) [c000200037fa7540] [c0000000009fda90] print_report+0x22c/0x67c [c000200037fa7630] [c0000000009fd508] kasan_report+0x108/0x220 [c000200037fa7740] [c0000000009fff48] __asan_store8+0xe8/0x120 [c000200037fa7760] [c008000018e76474] nvme_mpath_revalidate_paths+0x22c/0x290 [nvme_core] [c000200037fa7800] [c008000018e6556c] nvme_update_ns_info+0x4a4/0x5e0 [nvme_core] [c000200037fa7a50] [c008000018e66270] nvme_alloc_ns+0x6d8/0x1a70 [nvme_core] [c000200037fa7c20] [c008000018e679fc] nvme_scan_ns+0x3f4/0x630 [nvme_core] [c000200037fa7d10] [c00000000031f22c] async_run_entry_fn+0x9c/0x3a0 [c000200037fa7db0] [c0000000002fa544] process_one_work+0x414/0xa10 [c000200037fa7ec0] [c0000000002fbf00] worker_thread+0x320/0x640 [c000200037fa7f80] [c00000000030d0f8] kthread+0x278/0x290 [c000200037fa7fe0] [c00000000000ded8] start_kernel_thread+0x14/0x18 Allocated by task 1997 on cpu 1 at 35.928317s: The buggy address belongs to the object at c00020003bda3000 which belongs to the cache kmalloc-rnd-15-2k of size 2048 The buggy address is located 16 bytes to the right of allocated 1448-byte region [c00020003bda3000, c00020003bda35a8) The buggy address belongs to the physical page: Memory state around the buggy address: c00020003bda3480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 c00020003bda3500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >c00020003bda3580: 00 00 00 00 00 fc fc fc fc fc fc fc fc fc fc fc ^ c00020003bda3600: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc c00020003bda3680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ================================================================== Fix this by allocating the flexible array using nr_node_ids instead of num_possible_nodes(). Since nr_node_ids represents the maximum possible NUMA node IDs, indexing current_path[] using numa_node_id() becomes safe even on systems with sparse node IDs. Fixes: f333444708f8 ("nvme: take node locality into account when selecting a path") Tested-by: Mukesh Kumar Chaurasiya (IBM) Reviewed-by: Mukesh Kumar Chaurasiya (IBM) Reviewed-by: Hannes Reinecke Reviewed-by: John Garry Reviewed-by: Christoph Hellwig Signed-off-by: Nilay Shroff Signed-off-by: Keith Busch Signed-off-by: Sasha Levin --- drivers/nvme/host/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index c3032d6ad6b1e..96809227a0e22 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -3926,7 +3926,7 @@ static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ctrl *ctrl, int ret = -ENOMEM; #ifdef CONFIG_NVME_MULTIPATH - size += num_possible_nodes() * sizeof(struct nvme_ns *); + size += nr_node_ids * sizeof(struct nvme_ns *); #endif head = kzalloc(size, GFP_KERNEL); From fa9b6accd1ad7af6914b409c6b003c417724c299 Mon Sep 17 00:00:00 2001 From: Mateusz Nowicki Date: Sat, 23 May 2026 08:28:16 +0000 Subject: [PATCH 1009/2758] nvme-pci: fix out-of-bounds access in nvme_setup_descriptor_pools [ Upstream commit a192b8cfa447e1b3701a13434a31c392b2e7ed29 ] nvme_setup_descriptor_pools() indexes dev->descriptor_pools[] using the numa_node forwarded from hctx->numa_node by its single caller, nvme_init_hctx_common(). On a non-NUMA kernel hctx->numa_node is NUMA_NO_NODE (-1). Because the parameter was declared 'unsigned', the value becomes UINT_MAX and the index walks off the array (sized to nr_node_ids), faulting during nvme_alloc_ns() and leaving the namespace without a /dev node. Reproduces on any NVMe controller probed by a CONFIG_NUMA=n kernel: BUG: unable to handle page fault for address: ffff889101603d38 RIP: 0010:nvme_init_hctx_common+0x5a/0x190 [nvme] Call Trace: nvme_init_hctx+0x10/0x20 [nvme] nvme_alloc_ns+0x9e/0xa10 [nvme_core] nvme_scan_ns+0x301/0x3b0 [nvme_core] nvme_scan_ns_async+0x23/0x30 [nvme_core] Switch the parameter to int and fall back to node 0 when it is NUMA_NO_NODE; node 0 is always present. Fixes: d977506f8863 ("nvme-pci: make PRP list DMA pools per-NUMA-node") Link: https://lore.kernel.org/r/20260309062840.2937858-2-iam@sung-woo.kim Reported-by: Sung-woo Kim Reviewed-by: Christoph Hellwig Signed-off-by: Mateusz Nowicki Signed-off-by: Keith Busch Signed-off-by: Sasha Levin --- drivers/nvme/host/pci.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index b5f8462006789..0de6844ca56c0 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -587,11 +587,16 @@ static bool nvme_dbbuf_update_and_check_event(u16 value, __le32 *dbbuf_db, } static struct nvme_descriptor_pools * -nvme_setup_descriptor_pools(struct nvme_dev *dev, unsigned numa_node) +nvme_setup_descriptor_pools(struct nvme_dev *dev, int numa_node) { - struct nvme_descriptor_pools *pools = &dev->descriptor_pools[numa_node]; + struct nvme_descriptor_pools *pools; size_t small_align = NVME_SMALL_POOL_SIZE; + if (numa_node == NUMA_NO_NODE) + numa_node = 0; + + pools = &dev->descriptor_pools[numa_node]; + if (pools->small) return pools; /* already initialized */ From 2898b3f3dc4faf1aeea43219b91118f4fcf26872 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Sun, 24 May 2026 08:19:56 -0700 Subject: [PATCH 1010/2758] workqueue: drop spurious '*' from print_worker_info() fn declaration [ Upstream commit 611583a76ea97991b0f65ec1ff099eac7fe0bae4 ] print_worker_info() declares its local 'fn' as work_func_t * but worker->current_func has type work_func_t (a function pointer). The extra level of indirection is wrong and only happens to be harmless today because every supported Linux architecture has sizeof(work_func_t) == sizeof(work_func_t *): copy_from_kernel_nofault() reads the correct number of bytes by accident, and %ps still resolves the printed address because the stored value is the function address regardless of declared type. On any future ABI where sizeof(void (*)()) differs from sizeof(void *), the nofault copy would transfer the wrong number of bytes and the subsequent %ps would print an incorrect address. Match the field type so the intent is explicit and the code does not silently rely on equal pointer sizes. Fixes: 3d1cb2059d93 ("workqueue: include workqueue info when printing debug dump of a worker task") Signed-off-by: Breno Leitao Signed-off-by: Tejun Heo Signed-off-by: Sasha Levin --- kernel/workqueue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 33b721a9af022..bd79b3b5ca878 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -6310,7 +6310,7 @@ EXPORT_SYMBOL_GPL(set_worker_desc); */ void print_worker_info(const char *log_lvl, struct task_struct *task) { - work_func_t *fn = NULL; + work_func_t fn = NULL; char name[WQ_NAME_LEN] = { }; char desc[WORKER_DESC_LEN] = { }; struct pool_workqueue *pwq = NULL; From fc920c0659cdea5afd8721c1155a51564859e075 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 26 May 2026 14:55:28 +0000 Subject: [PATCH 1011/2758] ipv6: guard against possible NULL deref in __in6_dev_stats_get() [ Upstream commit 507541c2a8eeb76c02bd2511958f73a8cfa3e1bc ] dev_get_by_index_rcu() could return NULL if the original physical device is unregistered. Found by Sashiko. Fixes: e1ae5c2ea478 ("vrf: Increment Icmp6InMsgs on the original netdev") Signed-off-by: Eric Dumazet Cc: Stephen Suryaputra Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260526145529.3587126-2-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- include/net/addrconf.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 9e96776945e5f..539bbbe54b14e 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -369,8 +369,11 @@ static inline struct inet6_dev *__in6_dev_get_rtnl_net(const struct net_device * static inline struct inet6_dev *__in6_dev_stats_get(const struct net_device *dev, const struct sk_buff *skb) { - if (netif_is_l3_master(dev)) + if (netif_is_l3_master(dev)) { dev = dev_get_by_index_rcu(dev_net(dev), inet6_iif(skb)); + if (!dev) + return NULL; + } return __in6_dev_get(dev); } From 3fa6fb5d771c992ebedbfa7331c6bcc6f33f89b7 Mon Sep 17 00:00:00 2001 From: Jiayuan Chen Date: Tue, 26 May 2026 10:55:29 +0800 Subject: [PATCH 1012/2758] net/sched: cls_bpf: prevent unbounded recursion in offload rollback [ Upstream commit 27db54b90bcc7c37867fe664107fa25ea6a116e4 ] Quan Sun reported [1] a stack overflow in cls_bpf_offload_cmd(). Reproducer on netdevsim: add a skip_sw cls_bpf filter, set the bpf_tc_accept debugfs knob to 0, then `tc filter replace`. The replace calls tc_setup_cb_replace() which fails. cls_bpf_offload_cmd() then swaps prog/oldprog and recursively calls itself to roll back. But bpf_tc_accept=0 makes the rollback fail too, which triggers yet another rollback frame with the same arguments, and so on until the stack is exhausted. bpf_tc_accept is just a convenient knob for the reproducer. Any driver whose tc_setup_cb_replace() fails twice in a row can hit the same loop, so this is not a netdevsim-only issue. Two ways to fix it: 1) Have the rollback call tc_setup_cb_add() on oldprog instead of re-entering cls_bpf_offload_cmd(). 2) Mark the rollback frame with a flag and skip a second-level rollback from inside it. Go with (2). It is the smaller change and keeps the original behaviour: the rollback still goes through tc_setup_cb_replace(), so the driver gets one real chance to restore its state. If that attempt also fails, we just return the original error instead of recursing. [1]: https://lore.kernel.org/bpf/ce5a6005-3c5e-4696-9e05-eba9461dc860@std.uestc.edu.cn/T/#u Fixes: 102740bd9436 ("cls_bpf: fix offload assumptions after callback conversion") Reviewed-by: Jakub Kicinski Signed-off-by: Jiayuan Chen Link: https://patch.msgid.link/20260526025529.24382-1-jiayuan.chen@linux.dev Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/cls_bpf.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index 9a346b6221b35..001d8c4ebfedc 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -142,7 +142,8 @@ static bool cls_bpf_is_ebpf(const struct cls_bpf_prog *prog) static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog, struct cls_bpf_prog *oldprog, - struct netlink_ext_ack *extack) + struct netlink_ext_ack *extack, + bool is_rollback) { struct tcf_block *block = tp->chain->block; struct tc_cls_bpf_offload cls_bpf = {}; @@ -177,7 +178,8 @@ static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog, &oldprog->in_hw_count, true); if (prog && err) { - cls_bpf_offload_cmd(tp, oldprog, prog, extack); + if (!is_rollback) + cls_bpf_offload_cmd(tp, oldprog, prog, extack, true); return err; } @@ -208,7 +210,7 @@ static int cls_bpf_offload(struct tcf_proto *tp, struct cls_bpf_prog *prog, if (!prog && !oldprog) return 0; - return cls_bpf_offload_cmd(tp, prog, oldprog, extack); + return cls_bpf_offload_cmd(tp, prog, oldprog, extack, false); } static void cls_bpf_stop_offload(struct tcf_proto *tp, @@ -217,7 +219,7 @@ static void cls_bpf_stop_offload(struct tcf_proto *tp, { int err; - err = cls_bpf_offload_cmd(tp, NULL, prog, extack); + err = cls_bpf_offload_cmd(tp, NULL, prog, extack, false); if (err) pr_err("Stopping hardware offload failed: %d\n", err); } From cda919720774fa7dd01b1c11812645caf195ddbd Mon Sep 17 00:00:00 2001 From: Vasant Hegde Date: Sun, 17 May 2026 12:29:25 +0000 Subject: [PATCH 1013/2758] iommu/amd: Fix premature break in init_iommu_one() [ Upstream commit 283d245468a2b61c41aa8b582f25ed5615d1c304 ] In init_iommu_one(), when processing IOMMU EFR attributes, the code checks whether GASUP is enabled. If GASUP is not enabled, the code falls back to legacy guest IR mode and then breaks out of the switch statement. This break incorrectly skips the subsequent initialization steps that follow the GASUP check. These initializations are independent of GASUP support and must always be performed. Fix this by replacing the early break with a conditional else block, ensuring that the XTSUP check is only skipped when GASUP is not available. Fixes: a44092e326d4 ("iommu/amd: Use IVHD EFR for early initialization of IOMMU features") Reported-by: Sudheer Dantuluri Tested-by: Dheeraj Kumar Srivastava Signed-off-by: Vasant Hegde Signed-off-by: Joerg Roedel Signed-off-by: Sasha Levin --- drivers/iommu/amd/init.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c index 3bdb380d23e9a..9a846dcd03063 100644 --- a/drivers/iommu/amd/init.c +++ b/drivers/iommu/amd/init.c @@ -1939,12 +1939,11 @@ static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h, /* XT and GAM require GA mode. */ if ((h->efr_reg & (0x1 << IOMMU_EFR_GASUP_SHIFT)) == 0) { amd_iommu_guest_ir = AMD_IOMMU_GUEST_IR_LEGACY; - break; + } else { + if (h->efr_reg & BIT(IOMMU_EFR_XTSUP_SHIFT)) + amd_iommu_xt_mode = IRQ_REMAP_X2APIC_MODE; } - if (h->efr_reg & BIT(IOMMU_EFR_XTSUP_SHIFT)) - amd_iommu_xt_mode = IRQ_REMAP_X2APIC_MODE; - if (h->efr_attr & BIT(IOMMU_IVHD_ATTR_HATDIS_SHIFT)) { pr_warn_once("Host Address Translation is not supported.\n"); amd_iommu_hatdis = true; From 42ba8cc3047d8ac3d1d4ccdc10d846be9786d91b Mon Sep 17 00:00:00 2001 From: Jackie Dong Date: Wed, 27 May 2026 21:03:53 +0800 Subject: [PATCH 1014/2758] ALSA: hda/realtek:ALC269 fixup for Yoga Pro 7 15ASH11 mic mute LED [ Upstream commit 17065203e1bc7e7f2786998d532cd93a06265156 ] Lenovo Yoga Pro 7 15ASH11 with AMD RYZEN AI MAX+ 388 (Strix Halo, ACP 7.0) uses Realtek ALC287 series codec. The ALC269_FIXUP_LENOVO_XPAD_ACPI in alc269_fixup_vendor_tbl[] can load lenovo_wmi_hotkey_utilities module by default in this laptop, but the driver doesn't control mic mute LED. If users run below command and the mic mute LED can work normally. $sudo echo 'Capture Switch' >/sys/class/sound/ctl-led/mic/card1/attach After added the SND_PCI_QUIRK quirk special for Lenovo Yoga Pro 7 15ASH11, the mic mute LED works well. Fixes: 83dca2530fb3 ("ALSA: hda/realtek: ALC269 fixup for Lenovo Yoga Pro 7 15ASH111 audio") Signed-off-by: Jackie Dong Link: https://patch.msgid.link/20260527130353.5658-1-xy-jackie@139.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/hda/codecs/realtek/alc269.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index bc12c9a57858e..5b385a82e7dc4 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -4076,6 +4076,7 @@ enum { ALC287_FIXUP_YOGA7_14ITL_SPEAKERS, ALC298_FIXUP_LENOVO_C940_DUET7, ALC287_FIXUP_LENOVO_YOGA_BOOK_9I, + ALC287_FIXUP_LENOVO_YOGA_PRO7, ALC287_FIXUP_13S_GEN2_SPEAKERS, ALC256_FIXUP_SET_COEF_DEFAULTS, ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE, @@ -6101,6 +6102,13 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK, }, + [ALC287_FIXUP_LENOVO_YOGA_PRO7] = { + .type = HDA_FIXUP_FUNC, + /* Reuse the DAC routing selected for ThinkPad X1 Gen7 */ + .v.func = alc285_fixup_thinkpad_x1_gen7, + .chained = true, + .chain_id = ALC269_FIXUP_LENOVO_XPAD_ACPI, + }, [ALC623_FIXUP_LENOVO_THINKSTATION_P340] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_no_shutup, @@ -7771,7 +7779,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x38df, "Y990 YG DUAL", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38f9, "Thinkbook 16P Gen5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x38fa, "Thinkbook 16P Gen5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), - SND_PCI_QUIRK(0x17aa, 0x38fc, "Lenovo Yoga Pro 7 15ASH11", ALC245_FIXUP_BASS_HP_DAC), + SND_PCI_QUIRK(0x17aa, 0x38fc, "Lenovo Yoga Pro 7 15ASH11", ALC287_FIXUP_LENOVO_YOGA_PRO7), SND_PCI_QUIRK(0x17aa, 0x38fd, "ThinkBook plus Gen5 Hybrid", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI), SND_PCI_QUIRK(0x17aa, 0x390d, "Lenovo Yoga Pro 7 14ASP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), From 690c798e73dc5f8ab13067c686bd30ef1c603dfc Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 28 Feb 2026 01:05:44 +0100 Subject: [PATCH 1015/2758] dt-bindings: vendor-prefixes: Add Verbatim Corporation [ Upstream commit 4b8c63c6a2811c23995c5d8cc8bb63d26caab299 ] Verbatim Corporation has manufactured a few electronics items over the years. Link: https://en.wikipedia.org/wiki/Verbatim_(company) Acked-by: Krzysztof Kozlowski Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 11c55b5df0e4c..ac83310259a91 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -1785,6 +1785,8 @@ patternProperties: description: Variscite Ltd. "^vdl,.*": description: Van der Laan b.v. + "^verbatim,.*": + description: Verbatim Corporation "^verisilicon,.*": description: VeriSilicon Microelectronics (Shanghai) Co., Ltd. "^vertexcom,.*": From 78fcd6c3da86a3bf3799172c160841f02d9b8a71 Mon Sep 17 00:00:00 2001 From: Tomas Glozar Date: Tue, 26 May 2026 12:25:20 +0200 Subject: [PATCH 1016/2758] rtla/actions: Restore continue flag in actions_perform() [ Upstream commit dd520daffbc901f10d49a51a58313547d417b506 ] Currently, actions_perform() only ever sets the continue flag (when performing the continue action), but never resets it. That leads to RTLA continuing tracing even if the continue action was not performed in the current iteration. For example, the following command: $ rtla timerlat hist -T 100 --on-threshold shell,command=' echo Spike! if [ -f /tmp/a ] then exit 1 else touch /tmp/a fi' --on-threshold continue should print Spike! at most once, because after hitting the threshold for the first time, /tmp/a exists, the shell action will fail, and the continue action is not performed. However, unless /tmp/a exists before the measurement, it will print Spike! until stopped, as the continue flag stays set. Set the continue flag to false in the beginning of actions_perform() to make RTLA continue only if the action was actually performed. Fixes: 8d933d5c89e8 ("rtla/timerlat: Add continue action") Link: https://lore.kernel.org/r/20260526102523.2662391-1-tglozar@redhat.com [ correct Fixes tag to include 12 characters of hash ] Signed-off-by: Tomas Glozar Signed-off-by: Sasha Levin --- tools/tracing/rtla/src/actions.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/tracing/rtla/src/actions.c b/tools/tracing/rtla/src/actions.c index b0d68b5de08db..bf13d9d68f169 100644 --- a/tools/tracing/rtla/src/actions.c +++ b/tools/tracing/rtla/src/actions.c @@ -247,6 +247,8 @@ actions_perform(struct actions *self) int pid, retval; const struct action *action; + self->continue_flag = false; + for_each_action(self, action) { switch (action->type) { case ACTION_TRACE_OUTPUT: From 3055292b8eed69553b389a609197a241df47e68e Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Sun, 17 May 2026 12:14:49 +0300 Subject: [PATCH 1017/2758] drm/tegra: gr2d/gr3d: Initialize address register map before HOST1X client is registered [ Upstream commit c4ef5ba1131346159e31f4ef858525cf377380a6 ] The host1x_client_register() function is called just prior to register map initialization loop, making the device available to userspace. This may result in userspace attempting to submits a job before the register map is initialized. Address this by moving register initialization before host1x client registration. Acked-by: Mikko Perttunen Signed-off-by: Svyatoslav Ryhel Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260517091450.46728-2-clamor95@gmail.com Stable-dep-of: ace01e2af387 ("drm/tegra: gr2d/gr3d: Contain PM in the gr*d_probe/gr*d_remove") Signed-off-by: Sasha Levin --- drivers/gpu/drm/tegra/gr2d.c | 8 ++++---- drivers/gpu/drm/tegra/gr3d.c | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/tegra/gr2d.c b/drivers/gpu/drm/tegra/gr2d.c index 21f4dd0fa6aff..e4148b034af74 100644 --- a/drivers/gpu/drm/tegra/gr2d.c +++ b/drivers/gpu/drm/tegra/gr2d.c @@ -276,16 +276,16 @@ static int gr2d_probe(struct platform_device *pdev) if (err) return err; + /* initialize address register map */ + for (i = 0; i < ARRAY_SIZE(gr2d_addr_regs); i++) + set_bit(gr2d_addr_regs[i], gr2d->addr_regs); + err = host1x_client_register(&gr2d->client.base); if (err < 0) { dev_err(dev, "failed to register host1x client: %d\n", err); return err; } - /* initialize address register map */ - for (i = 0; i < ARRAY_SIZE(gr2d_addr_regs); i++) - set_bit(gr2d_addr_regs[i], gr2d->addr_regs); - return 0; } diff --git a/drivers/gpu/drm/tegra/gr3d.c b/drivers/gpu/drm/tegra/gr3d.c index 42e9656ab80c9..47b0c6c56bfd0 100644 --- a/drivers/gpu/drm/tegra/gr3d.c +++ b/drivers/gpu/drm/tegra/gr3d.c @@ -506,6 +506,10 @@ static int gr3d_probe(struct platform_device *pdev) if (err) return err; + /* initialize address register map */ + for (i = 0; i < ARRAY_SIZE(gr3d_addr_regs); i++) + set_bit(gr3d_addr_regs[i], gr3d->addr_regs); + err = host1x_client_register(&gr3d->client.base); if (err < 0) { dev_err(&pdev->dev, "failed to register host1x client: %d\n", @@ -513,10 +517,6 @@ static int gr3d_probe(struct platform_device *pdev) return err; } - /* initialize address register map */ - for (i = 0; i < ARRAY_SIZE(gr3d_addr_regs); i++) - set_bit(gr3d_addr_regs[i], gr3d->addr_regs); - return 0; } From b3df1a2891325cd34b0059eaf0781df6c0f94cc9 Mon Sep 17 00:00:00 2001 From: Ion Agorria Date: Sun, 17 May 2026 12:14:50 +0300 Subject: [PATCH 1018/2758] drm/tegra: gr2d/gr3d: Contain PM in the gr*d_probe/gr*d_remove [ Upstream commit ace01e2af3871343d700fb60c6f64d8f8e3180e1 ] The current power management configuration causes GR2G/GR3D to malfunction after resume. Reconfigure all PM actions to be handled within the GR*D probe and remove operations to address this. Fixes: 62fa0a985e2c ("drm/tegra: Enable runtime PM during probe") Acked-by: Mikko Perttunen Signed-off-by: Ion Agorria Signed-off-by: Svyatoslav Ryhel Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260517091450.46728-3-clamor95@gmail.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/tegra/gr2d.c | 13 ++++++------- drivers/gpu/drm/tegra/gr3d.c | 13 ++++++------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/tegra/gr2d.c b/drivers/gpu/drm/tegra/gr2d.c index e4148b034af74..892e3450b281e 100644 --- a/drivers/gpu/drm/tegra/gr2d.c +++ b/drivers/gpu/drm/tegra/gr2d.c @@ -100,9 +100,6 @@ static int gr2d_exit(struct host1x_client *client) if (err < 0) return err; - pm_runtime_dont_use_autosuspend(client->dev); - pm_runtime_force_suspend(client->dev); - host1x_client_iommu_detach(client); host1x_syncpt_put(client->syncpts[0]); host1x_channel_put(gr2d->channel); @@ -280,12 +277,18 @@ static int gr2d_probe(struct platform_device *pdev) for (i = 0; i < ARRAY_SIZE(gr2d_addr_regs); i++) set_bit(gr2d_addr_regs[i], gr2d->addr_regs); + pm_runtime_enable(dev); + err = host1x_client_register(&gr2d->client.base); if (err < 0) { + pm_runtime_disable(dev); dev_err(dev, "failed to register host1x client: %d\n", err); return err; } + pm_runtime_use_autosuspend(dev); + pm_runtime_set_autosuspend_delay(dev, 500); + return 0; } @@ -367,10 +370,6 @@ static int __maybe_unused gr2d_runtime_resume(struct device *dev) goto disable_clk; } - pm_runtime_enable(dev); - pm_runtime_use_autosuspend(dev); - pm_runtime_set_autosuspend_delay(dev, 500); - return 0; disable_clk: diff --git a/drivers/gpu/drm/tegra/gr3d.c b/drivers/gpu/drm/tegra/gr3d.c index 47b0c6c56bfd0..388e47943d5ec 100644 --- a/drivers/gpu/drm/tegra/gr3d.c +++ b/drivers/gpu/drm/tegra/gr3d.c @@ -109,9 +109,6 @@ static int gr3d_exit(struct host1x_client *client) if (err < 0) return err; - pm_runtime_dont_use_autosuspend(client->dev); - pm_runtime_force_suspend(client->dev); - host1x_client_iommu_detach(client); host1x_syncpt_put(client->syncpts[0]); host1x_channel_put(gr3d->channel); @@ -510,13 +507,19 @@ static int gr3d_probe(struct platform_device *pdev) for (i = 0; i < ARRAY_SIZE(gr3d_addr_regs); i++) set_bit(gr3d_addr_regs[i], gr3d->addr_regs); + pm_runtime_enable(&pdev->dev); + err = host1x_client_register(&gr3d->client.base); if (err < 0) { + pm_runtime_disable(&pdev->dev); dev_err(&pdev->dev, "failed to register host1x client: %d\n", err); return err; } + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, 500); + return 0; } @@ -578,10 +581,6 @@ static int __maybe_unused gr3d_runtime_resume(struct device *dev) goto disable_clk; } - pm_runtime_enable(dev); - pm_runtime_use_autosuspend(dev); - pm_runtime_set_autosuspend_delay(dev, 500); - return 0; disable_clk: From df63c76f9c8d881ca7bce1aecfba512328d0527d Mon Sep 17 00:00:00 2001 From: Mikko Perttunen Date: Fri, 15 May 2026 11:34:51 +0900 Subject: [PATCH 1019/2758] gpu: host1x: Allow entries in BO caches to be freed [ Upstream commit 3cbf5e3c46e66d9b3b6b91099bb720c6cb1be3bc ] When a buffer object is pinned via host1x_bo_pin() with a cache, the resulting mapping is kept in the cache so it can be reused on subsequent pins. Each mapping held a reference to the underlying host1x_bo (taken in tegra_bo_pin / gather_bo_pin), so as long as a mapping was cached, the bo itself could not be freed. However, the only way to remove the cached mapping was through the free path of the buffer object. This meant that if a bo got cached, it could never get freed again. Resolve the circularity by holding a weak reference to the bo from the cache side. This is done by having the .pin callbacks not bump the bo's refcount -- instead the common Host1x bo code does so, except for the cache reference. Also move the remove-cache-mapping-on-free code into a common function inside Host1x code. This is only called from the TegraDRM GEM buffers since those are the only ones that can be cached at the moment. Reported-by: Aaron Kling Fixes: 1f39b1dfa53c ("drm/tegra: Implement buffer object cache") Signed-off-by: Mikko Perttunen Tested-by: Aaron Kling Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260515-host1x-bocache-leak-v1-1-a0375f68aeab@nvidia.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/tegra/gem.c | 13 ++------ drivers/gpu/drm/tegra/submit.c | 3 +- drivers/gpu/host1x/bus.c | 60 +++++++++++++++++++++++++++++++++- include/linux/host1x.h | 7 ++++ 4 files changed, 69 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index d2bae88ad545f..2377e2b76397a 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -69,7 +69,7 @@ static struct host1x_bo_mapping *tegra_bo_pin(struct device *dev, struct host1x_ return ERR_PTR(-ENOMEM); kref_init(&map->ref); - map->bo = host1x_bo_get(bo); + map->bo = bo; map->direction = direction; map->dev = dev; @@ -170,7 +170,6 @@ static void tegra_bo_unpin(struct host1x_bo_mapping *map) kfree(map->sgt); } - host1x_bo_put(map->bo); kfree(map); } @@ -509,17 +508,9 @@ static struct tegra_bo *tegra_bo_import(struct drm_device *drm, void tegra_bo_free_object(struct drm_gem_object *gem) { struct tegra_drm *tegra = gem->dev->dev_private; - struct host1x_bo_mapping *mapping, *tmp; struct tegra_bo *bo = to_tegra_bo(gem); - /* remove all mappings of this buffer object from any caches */ - list_for_each_entry_safe(mapping, tmp, &bo->base.mappings, list) { - if (mapping->cache) - host1x_bo_unpin(mapping); - else - dev_err(gem->dev->dev, "mapping %p stale for device %s\n", mapping, - dev_name(mapping->dev)); - } + host1x_bo_clear_cached_mappings(&bo->base); if (tegra->domain) { tegra_bo_iommu_unmap(tegra, bo); diff --git a/drivers/gpu/drm/tegra/submit.c b/drivers/gpu/drm/tegra/submit.c index 3009b8b9e6197..e5841857c9378 100644 --- a/drivers/gpu/drm/tegra/submit.c +++ b/drivers/gpu/drm/tegra/submit.c @@ -76,7 +76,7 @@ gather_bo_pin(struct device *dev, struct host1x_bo *bo, enum dma_data_direction return ERR_PTR(-ENOMEM); kref_init(&map->ref); - map->bo = host1x_bo_get(bo); + map->bo = bo; map->direction = direction; map->dev = dev; @@ -117,7 +117,6 @@ static void gather_bo_unpin(struct host1x_bo_mapping *map) dma_unmap_sgtable(map->dev, map->sgt, map->direction, 0); sg_free_table(map->sgt); kfree(map->sgt); - host1x_bo_put(map->bo); kfree(map); } diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c index f814eb4941c02..772e05a7b45b3 100644 --- a/drivers/gpu/host1x/bus.c +++ b/drivers/gpu/host1x/bus.c @@ -887,6 +887,20 @@ int host1x_client_resume(struct host1x_client *client) } EXPORT_SYMBOL(host1x_client_resume); +/** + * host1x_bo_pin() - Create a DMA mapping for the buffer object + * @dev: Device onto which DMA map to + * @bo: Buffer object to map + * @dir: DMA direction + * @cache: Cache in which to store mapping, or NULL + * + * Creates a DMA mapping pointing to @bo for @dev. The refcount of @bo is incremented + * until host1x_bo_unpin is called. + * + * If @cache is specified, the mapping is also stored in the cache and not released + * until @bo is freed (refcount drops to zero). This improves performance when a buffer + * is pinned and unpinned frequently as in the case of display use. + */ struct host1x_bo_mapping *host1x_bo_pin(struct device *dev, struct host1x_bo *bo, enum dma_data_direction dir, struct host1x_bo_cache *cache) @@ -899,6 +913,7 @@ struct host1x_bo_mapping *host1x_bo_pin(struct device *dev, struct host1x_bo *bo list_for_each_entry(mapping, &cache->mappings, entry) { if (mapping->bo == bo && mapping->direction == dir) { kref_get(&mapping->ref); + host1x_bo_get(bo); goto unlock; } } @@ -908,6 +923,8 @@ struct host1x_bo_mapping *host1x_bo_pin(struct device *dev, struct host1x_bo *bo if (IS_ERR(mapping)) goto unlock; + host1x_bo_get(bo); + spin_lock(&mapping->bo->lock); list_add_tail(&mapping->list, &bo->mappings); spin_unlock(&mapping->bo->lock); @@ -918,7 +935,12 @@ struct host1x_bo_mapping *host1x_bo_pin(struct device *dev, struct host1x_bo *bo list_add_tail(&mapping->entry, &cache->mappings); - /* bump reference count to track the copy in the cache */ + /* + * Bump the mapping reference count to track the mapping in the cache, + * but do not bump the BO's refcount. This allows the BO to still get freed, + * triggering the release of the cache mapping through + * host1x_bo_clear_cached_mappings. + */ kref_get(&mapping->ref); } @@ -948,9 +970,17 @@ static void __host1x_bo_unpin(struct kref *ref) mapping->bo->ops->unpin(mapping); } +/** + * host1x_bo_unpin() - Release an established DMA mapping of a buffer object + * @mapping: Mapping to release + * + * Unmaps the given @mapping, unless it is cached. Decreases the refcount on + * the underlying buffer object. + */ void host1x_bo_unpin(struct host1x_bo_mapping *mapping) { struct host1x_bo_cache *cache = mapping->cache; + struct host1x_bo *bo = mapping->bo; if (cache) mutex_lock(&cache->lock); @@ -959,5 +989,33 @@ void host1x_bo_unpin(struct host1x_bo_mapping *mapping) if (cache) mutex_unlock(&cache->lock); + + host1x_bo_put(bo); } EXPORT_SYMBOL(host1x_bo_unpin); + +/** + * host1x_bo_clear_cached_mappings() - Remove all cached mappings pointing at a bo + * @bo: Buffer object to release mappings of + * + * Drops references to any mappings pointing to @bo left in any caches. This must + * be called by any host1x_bo implementers that may be pinned with caching enabled + * before freeing the bo. + */ +void host1x_bo_clear_cached_mappings(struct host1x_bo *bo) +{ + struct host1x_bo_mapping *mapping, *tmp; + struct host1x_bo_cache *cache; + + list_for_each_entry_safe(mapping, tmp, &bo->mappings, list) { + cache = mapping->cache; + if (WARN_ON(!cache)) + continue; + + mutex_lock(&mapping->cache->lock); + WARN_ON(kref_read(&mapping->ref) != 1); + __host1x_bo_unpin(&mapping->ref); + mutex_unlock(&mapping->cache->lock); + } +} +EXPORT_SYMBOL(host1x_bo_clear_cached_mappings); diff --git a/include/linux/host1x.h b/include/linux/host1x.h index 1f5f55917d1cd..a7a6757831362 100644 --- a/include/linux/host1x.h +++ b/include/linux/host1x.h @@ -143,6 +143,12 @@ static inline struct host1x_bo_mapping *to_host1x_bo_mapping(struct kref *ref) return container_of(ref, struct host1x_bo_mapping, ref); } +/** + * struct host1x_bo_ops - operations implemented by a host1x_bo provider + * + * @pin: create a DMA mapping. Implementation must not touch the bo's refcount. + * @unpin: destroy a DMA mapping. Implementation must not touch the bo's refcount. + */ struct host1x_bo_ops { struct host1x_bo *(*get)(struct host1x_bo *bo); void (*put)(struct host1x_bo *bo); @@ -181,6 +187,7 @@ struct host1x_bo_mapping *host1x_bo_pin(struct device *dev, struct host1x_bo *bo enum dma_data_direction dir, struct host1x_bo_cache *cache); void host1x_bo_unpin(struct host1x_bo_mapping *map); +void host1x_bo_clear_cached_mappings(struct host1x_bo *bo); static inline void *host1x_bo_mmap(struct host1x_bo *bo) { From 1efaea78135e61a91f69a3661f14451c66bea6c0 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 21 Apr 2026 09:29:05 +0200 Subject: [PATCH 1020/2758] drm/tegra: fbdev: Do not assign to struct drm_fb_helper.info [ Upstream commit d23bd83f3e47a928e783c0d6a004737519dc77dc ] That field already contains the value being assigned. No need to do this twice. Signed-off-by: Thomas Zimmermann Fixes: 63c971af4036 ("drm/fb-helper: Allocate and release fb_info in single place") Cc: linux-tegra@vger.kernel.org Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260421073646.144712-2-tzimmermann@suse.de Signed-off-by: Sasha Levin --- drivers/gpu/drm/tegra/fbdev.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/tegra/fbdev.c b/drivers/gpu/drm/tegra/fbdev.c index 8f40882aa76ec..19e39fa54bfae 100644 --- a/drivers/gpu/drm/tegra/fbdev.c +++ b/drivers/gpu/drm/tegra/fbdev.c @@ -110,7 +110,6 @@ int tegra_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, helper->funcs = &tegra_fbdev_helper_funcs; helper->fb = fb; - helper->info = info; info->fbops = &tegra_fb_ops; From 461192271ea621a8090bdf313c379678856575fe Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Thu, 16 Apr 2026 20:09:29 +0800 Subject: [PATCH 1021/2758] gpu: host1x: mipi: Fix device_node reference leak in tegra_mipi_request() [ Upstream commit b217fb77ca4fd0cc267329500b291c0ad8f8b211 ] In tegra_mipi_request(), when provider.np is not equal with args.np, it returns without calling of_node_put(args.np), causing a reference leak. Convert to use the existing goto out pattern to ensure proper cleanup. Fixes: 767598d447aa ("gpu: host1x: mipi: Update tegra_mipi_request() to be node based") Signed-off-by: Felix Gu Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260416-mipi-v1-1-9c027175abdf@gmail.com Signed-off-by: Sasha Levin --- drivers/gpu/host1x/mipi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/host1x/mipi.c b/drivers/gpu/host1x/mipi.c index 01513b775d899..9886814239817 100644 --- a/drivers/gpu/host1x/mipi.c +++ b/drivers/gpu/host1x/mipi.c @@ -114,8 +114,10 @@ struct tegra_mipi_device *tegra_mipi_request(struct device *device, if (err < 0) return ERR_PTR(err); - if (provider.np != args.np) - return ERR_PTR(-ENODEV); + if (provider.np != args.np) { + err = -ENODEV; + goto out; + } mipidev = kzalloc_obj(*mipidev); if (!mipidev) { From 499603f530d2b7aac24be413a8f5279ae9b11826 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Wed, 28 Jan 2026 00:43:10 +0800 Subject: [PATCH 1022/2758] drm/tegra: dc: Fix device node reference leak in tegra_dc_has_output() [ Upstream commit 63a3998d792ab5c45304bf879e385a31fa923b61 ] The of_for_each_phandle() macro increments the reference count of the device node it iterates over. If the loop exits early, the reference must be released manually. In tegra_dc_has_output(), the function returns true immediately when a match is found, failing to release the current node's reference. Fix this by adding a call to of_node_put() before returning from the loop. Fixes: c57997bce423 ("drm/tegra: sor: Add Tegra186 support") Signed-off-by: Felix Gu Acked-by: Mikko Perttunen Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260128-dc-v1-1-a88205826301@gmail.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/tegra/dc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 06370b7e0e567..c6734a87ef0af 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -101,8 +101,10 @@ bool tegra_dc_has_output(struct tegra_dc *dc, struct device *dev) int err; of_for_each_phandle(&it, err, np, "nvidia,outputs", NULL, 0) - if (it.node == dev->of_node) + if (it.node == dev->of_node) { + of_node_put(it.node); return true; + } return false; } From e024c7993d839503d6c1f0044b8fc537c30300e9 Mon Sep 17 00:00:00 2001 From: Mikko Perttunen Date: Tue, 21 Apr 2026 13:02:36 +0900 Subject: [PATCH 1023/2758] gpu: host1x: Fix iommu_map_sgtable() return value check [ Upstream commit 18f74762013a4b6aa6f905c4459e0f506f9c5c7b ] Commit "iommu: return full error code from iommu_map_sg[_atomic]()" changed iommu_map_sgtable() to return an ssize_t and negative values in error cases, rather than a size_t and a zero. pin_job() also was incorrectly assigning to 'int', which could cause overflows into negative values. Update pin_job() to correctly check for errors from iommu_map_sgtable. Fixes: ad8f36e4b6b1 ("iommu: return full error code from iommu_map_sg[_atomic]()") Signed-off-by: Mikko Perttunen Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260421-iommu_map_sgtable-return-v1-1-fb484c07d2a1@nvidia.com Signed-off-by: Sasha Levin --- drivers/gpu/host1x/job.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/host1x/job.c b/drivers/gpu/host1x/job.c index 3ed49e1fd9332..70bda32f1ff48 100644 --- a/drivers/gpu/host1x/job.c +++ b/drivers/gpu/host1x/job.c @@ -235,6 +235,8 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job) } if (host->domain) { + ssize_t map_err; + for_each_sgtable_sg(map->sgt, sg, j) gather_size += sg->length; @@ -248,11 +250,11 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job) goto put; } - err = iommu_map_sgtable(host->domain, iova_dma_addr(&host->iova, alloc), - map->sgt, IOMMU_READ); - if (err == 0) { + map_err = iommu_map_sgtable(host->domain, iova_dma_addr(&host->iova, alloc), + map->sgt, IOMMU_READ); + if (map_err < 0) { __free_iova(&host->iova, alloc); - err = -EINVAL; + err = map_err; goto put; } From 401319f74bcf5e2289dacfb00d2c1a10466b541e Mon Sep 17 00:00:00 2001 From: Mikko Perttunen Date: Tue, 21 Apr 2026 13:02:37 +0900 Subject: [PATCH 1024/2758] drm/tegra: Fix iommu_map_sgtable() return value check [ Upstream commit b3f349517de8f4469c385ebb7bfdfcc148790c0f ] Commit "iommu: return full error code from iommu_map_sg[_atomic]()" changed iommu_map_sgtable() to return an ssize_t and negative values in error cases, rather than a size_t and a zero. Update tegra_bo_iommu_map() to correctly check for errors from iommu_map_sgtable. Fixes: ad8f36e4b6b1 ("iommu: return full error code from iommu_map_sg[_atomic]()") Signed-off-by: Mikko Perttunen Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260421-iommu_map_sgtable-return-v1-2-fb484c07d2a1@nvidia.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/tegra/gem.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index 2377e2b76397a..436394e048125 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -234,6 +234,7 @@ static const struct host1x_bo_ops tegra_bo_ops = { static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo) { int prot = IOMMU_READ | IOMMU_WRITE; + ssize_t size; int err; if (bo->mm) @@ -255,13 +256,15 @@ static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo) bo->iova = bo->mm->start; - bo->size = iommu_map_sgtable(tegra->domain, bo->iova, bo->sgt, prot); - if (!bo->size) { + size = iommu_map_sgtable(tegra->domain, bo->iova, bo->sgt, prot); + if (size < 0) { dev_err(tegra->drm->dev, "failed to map buffer\n"); - err = -ENOMEM; + err = size; goto remove; } + bo->size = size; + mutex_unlock(&tegra->mm_lock); return 0; From 1b244c3ef4a49407303408909ffd99ea6017aed4 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 30 Apr 2026 17:38:35 -0500 Subject: [PATCH 1025/2758] drm/nouveau/bios: specify correct display fuse register for Ampere and Ada [ Upstream commit c1cf2d5db80ce91a85855bbaf4da85ff603e089a ] The NV_FUSE_STATUS_OPT_DISPLAY register is used to determine whether the GPU has display hardware. The current code that normally reads this register is instead hard-coded to check for GA100 vs later GPUs. Since this function is called only on pre-Hopper GPUs, and this if-statement applies only to GA100 and later, the check works because GA100 is the only non-display Ampere and Ada GPU. However, there actually is a register that can be read, so we should use it. Fixes: a34632482f1e ("drm/nouveau/bios/ga10[024]: initial support") Signed-off-by: Timur Tabi Reviewed-by: Lyude Paul Link: https://patch.msgid.link/20260430223838.2530778-8-ttabi@nvidia.com Signed-off-by: Danilo Krummrich Signed-off-by: Sasha Levin --- drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c index d5411d176e3a8..0d9e6cdd61193 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c @@ -65,13 +65,14 @@ pramin_init(struct nvkm_bios *bios, const char *name) /* we can't get the bios image pointer without PDISP */ if (device->card_type >= GA100) - addr = device->chipset == 0x170; /*XXX: find the fuse reg for this */ + addr = nvkm_rd32(device, 0x820c04); else if (device->card_type >= GM100) addr = nvkm_rd32(device, 0x021c04); else if (device->card_type >= NV_C0) addr = nvkm_rd32(device, 0x022500); + if (addr & 0x00000001) { nvkm_debug(subdev, "... display disabled\n"); return ERR_PTR(-ENODEV); From 63e5aa348fd41afac57243f69d03e4408c2bfd20 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Fri, 22 May 2026 16:13:53 -0400 Subject: [PATCH 1026/2758] libbpf: Harden parse_vma_segs() path parsing [ Upstream commit fee9a38174f4c6454fb1fbaf2b9b5a1cca9070d0 ] parse_vma_segs() in tools/lib/bpf/usdt.c parses /proc//maps with two widthless scansets, "%s" into mode[16] and "%[^\n]" into line[4096]. A VMA name in maps is not limited to that local buffer; a deeply nested backing path can produce a maps record long enough to overflow the stack buffer. Bound both scansets to the declared buffer sizes ("%15s" for mode[16] and "%4095[^\n]" for line[4096]) and drain any residue past line[4094] with "%*[^\n]" before the trailing "\n". Without the drain, the residue of an over-long record would stay in the stream and break the next "%zx-%zx" parse, so the loop would exit early and silently skip later maps records. Also stop using sscanf(..., "%s") to peel the /proc//root prefix from lib_path. Parse the pid and prefix length with "%n", check for the following slash, and copy the remainder with libbpf_strlcpy(). That removes a second unbounded stack write and preserves paths containing spaces. Fixes: 74cc6311cec9 ("libbpf: Add USDT notes parsing and resolution logic") Signed-off-by: Michael Bommarito Signed-off-by: Andrii Nakryiko Reviewed-by: Emil Tsalapatis Link: https://lore.kernel.org/bpf/20260522201353.1454653-1-michael.bommarito@gmail.com Signed-off-by: Sasha Levin --- tools/lib/bpf/usdt.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tools/lib/bpf/usdt.c b/tools/lib/bpf/usdt.c index e3710933fd52a..57fb82bb81b58 100644 --- a/tools/lib/bpf/usdt.c +++ b/tools/lib/bpf/usdt.c @@ -468,10 +468,10 @@ static int parse_elf_segs(Elf *elf, const char *path, struct elf_seg **segs, siz static int parse_vma_segs(int pid, const char *lib_path, struct elf_seg **segs, size_t *seg_cnt) { - char path[PATH_MAX], line[PATH_MAX], mode[16]; + char path[PATH_MAX], line[4096], mode[16]; size_t seg_start, seg_end, seg_off; struct elf_seg *seg; - int tmp_pid, i, err; + int tmp_pid, n, i, err; FILE *f; *seg_cnt = 0; @@ -480,8 +480,13 @@ static int parse_vma_segs(int pid, const char *lib_path, struct elf_seg **segs, * /proc//root/. They will be reported as just / in * /proc//maps. */ - if (sscanf(lib_path, "/proc/%d/root%s", &tmp_pid, path) == 2 && pid == tmp_pid) + /* %n is not counted in sscanf() return value, so initialize it. */ + n = 0; + if (sscanf(lib_path, "/proc/%d/root%n", &tmp_pid, &n) == 1 && + n > 0 && pid == tmp_pid && lib_path[n] == '/') { + libbpf_strlcpy(path, lib_path + n, sizeof(path)); goto proceed; + } if (!realpath(lib_path, path)) { pr_warn("usdt: failed to get absolute path of '%s' (err %s), using path as is...\n", @@ -504,8 +509,11 @@ static int parse_vma_segs(int pid, const char *lib_path, struct elf_seg **segs, * 7f5c6f5d1000-7f5c6f5d3000 rw-p 001c7000 08:04 21238613 /usr/lib64/libc-2.17.so * 7f5c6f5d3000-7f5c6f5d8000 rw-p 00000000 00:00 0 * 7f5c6f5d8000-7f5c6f5d9000 r-xp 00000000 103:01 362990598 /data/users/andriin/linux/tools/bpf/usdt/libhello_usdt.so + * + * Some VMA names can be longer than the local buffer. Bound the + * writes, but still consume the rest of the line. */ - while (fscanf(f, "%zx-%zx %s %zx %*s %*d%[^\n]\n", + while (fscanf(f, "%zx-%zx %15s %zx %*s %*d%4095[^\n]%*[^\n]\n", &seg_start, &seg_end, mode, &seg_off, line) == 5) { void *tmp; From a1253892dd66dbee17815e7373b2e551343dbd8a Mon Sep 17 00:00:00 2001 From: Siddharth Nayyar Date: Wed, 20 May 2026 09:40:44 +0000 Subject: [PATCH 1027/2758] bpftool: Fix typo in struct_ops map FD generation for light skeleton [ Upstream commit be4c6c7bc42952b71188894933946b410deadcfe ] When generating light skeletons for BPF programs containing struct_ops maps, bpftool incorrectly outputs a stray literal 't' instead of a tab character for the map file descriptor member in the links structure. This causes a compilation error when the generated light skeleton is used. Correct the format string by replacing 't' with '\t'. Fixes: 08ac454e258e ("libbpf: Auto-attach struct_ops BPF maps in BPF skeleton") Signed-off-by: Siddharth Nayyar Signed-off-by: Andrii Nakryiko Acked-by: Quentin Monnet Link: https://lore.kernel.org/bpf/20260520-struct_ops_gen_typo_fix-v1-1-4dee3771da46@google.com Signed-off-by: Sasha Levin --- tools/bpf/bpftool/gen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c index 2f9e10752e288..d6040b52d4e1b 100644 --- a/tools/bpf/bpftool/gen.c +++ b/tools/bpf/bpftool/gen.c @@ -1399,7 +1399,7 @@ static int do_skeleton(int argc, char **argv) continue; if (use_loader) - printf("t\tint %s_fd;\n", ident); + printf("\t\tint %s_fd;\n", ident); else printf("\t\tstruct bpf_link *%s;\n", ident); } From b6c9331d84ca1fde11468a89567df081415b103d Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Sat, 23 May 2026 16:27:21 +0000 Subject: [PATCH 1028/2758] libbpf: Fix UAF in strset__add_str() [ Upstream commit b23705e6afb6ac4ae6d220dcb35975698667dd76 ] strset_add_str_mem() might reallocate the strset data buffer in order to accommodate the provided string 's'. However, if 's' points to a string already present in the buffer, it becomes dangling after the realloc. This leads to a use-after-free when attempting to memcpy() the string into the new buffer. One scenario that triggers this problematic path is when resolve_btfids attempts to patch kfunc prototypes using existing BTF parameter names: | resolve_btfids: function bpf_list_push_back_impl already exists in BTF | Segmentation fault (core dumped) Compiling resolve_btfids with fsanitize=address generates a detailed report of the UAF: | ================================================================= | ERROR: AddressSanitizer: heap-use-after-free on address 0x7f4c4a500bd4 | ==1507892==ERROR: AddressSanitizer: heap-use-after-free on address 0x7f4c4a500bd4 at pc 0x55d25155a2a8 bp 0x7ffcef879060 sp 0x7ffcef878818 | READ of size 5 at 0x7f4c4a500bd4 thread T0 | #0 0x55d25155a2a7 in memcpy (tools/bpf/resolve_btfids/resolve_btfids+0xcf2a7) | #1 0x55d2515d708e in strset__add_str tools/lib/bpf/strset.c:162:2 | #2 0x55d2515c730b in btf__add_str tools/lib/bpf/btf.c:2109:8 | #3 0x55d2515c9020 in btf__add_func_param tools/lib/bpf/btf.c:3108:14 | #4 0x55d25159f0b5 in process_kfunc_with_implicit_args tools/bpf/resolve_btfids/main.c:1196:9 | #5 0x55d25159e004 in btf2btf tools/bpf/resolve_btfids/main.c:1229:9 | #6 0x55d25159cee7 in main tools/bpf/resolve_btfids/main.c:1535:6 | #7 0x7f4c78e29f76 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 | #8 0x7f4c78e2a026 in __libc_start_main csu/../csu/libc-start.c:360:3 | #9 0x55d2514bb860 in _start (tools/bpf/resolve_btfids/resolve_btfids+0x30860) | | 0x7f4c4a500bd4 is located 13268 bytes inside of 2829000-byte region [0x7f4c4a4fd800,0x7f4c4a7b02c8) | freed by thread T0 here: | #0 0x55d25155b700 in realloc (tools/bpf/resolve_btfids/resolve_btfids+0xd0700) | #1 0x55d2515c426c in libbpf_reallocarray tools/lib/bpf/./libbpf_internal.h:220:9 | #2 0x55d2515c426c in libbpf_add_mem tools/lib/bpf/btf.c:224:13 | | previously allocated by thread T0 here: | #0 0x55d25155b2e3 in malloc (tools/bpf/resolve_btfids/resolve_btfids+0xd02e3) | #1 0x55d2515d6e7d in strset__new tools/lib/bpf/strset.c:58:20 While resolve_btfids could be refactored to avoid this call path, let's instead fix this issue at the source in strset__add_str() and avoid similar scenarios. Let's check if set->strs_data was reallocated and whether 's' points to an internal string within the old strset buffer. In such case, 's' is reconstructed to point to the new buffer. While already here, also fix strset__find_str() which suffers from the same problem by factoring out the common operations into a new helper function strset_str_append(). Fixes: 90d76d3ececc ("libbpf: Extract internal set-of-strings datastructure APIs") Suggested-by: Andrii Nakryiko Suggested-by: Mykyta Yatsenko Signed-off-by: Carlos Llamas Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20260523162722.2718940-1-cmllamas@google.com Signed-off-by: Sasha Levin --- tools/lib/bpf/strset.c | 62 ++++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/tools/lib/bpf/strset.c b/tools/lib/bpf/strset.c index 2464bcbd04e03..ace73c6b3d62b 100644 --- a/tools/lib/bpf/strset.c +++ b/tools/lib/bpf/strset.c @@ -107,6 +107,41 @@ static void *strset_add_str_mem(struct strset *set, size_t add_sz) set->strs_data_len, set->strs_data_max_len, add_sz); } +static long strset_str_append(struct strset *set, const char *s) +{ + uintptr_t old_data = (uintptr_t)set->strs_data; + size_t old_data_len = set->strs_data_len; + uintptr_t old_s = (uintptr_t)s; + long len = strlen(s) + 1; + void *p; + + /* + * Hashmap keys are always offsets within set->strs_data, so to even + * look up some string from the "outside", we need to first append it + * at the end, so that it can be addressed with an offset. Luckily, + * until set->strs_data_len is incremented, that string is just a piece + * of garbage for the rest of the code, so no harm, no foul. On the + * other hand, if the string is unique, it's already appended and + * ready to be used, only a simple set->strs_data_len increment away. + */ + p = strset_add_str_mem(set, len); + if (!p) + return -ENOMEM; + + /* + * The set->strs_data might have reallocated and if 's' pointed + * to an internal string within the old buffer, then it became + * dangling and needs to be reconstructed before the copy. + */ + if (old_data && old_data != (uintptr_t)set->strs_data && + old_s >= old_data && old_s < old_data + old_data_len) + s = set->strs_data + (old_s - old_data); + + memcpy(p, s, len); + + return len; +} + /* Find string offset that corresponds to a given string *s*. * Returns: * - >0 offset into string data, if string is found; @@ -116,16 +151,12 @@ static void *strset_add_str_mem(struct strset *set, size_t add_sz) int strset__find_str(struct strset *set, const char *s) { long old_off, new_off, len; - void *p; - /* see strset__add_str() for why we do this */ - len = strlen(s) + 1; - p = strset_add_str_mem(set, len); - if (!p) - return -ENOMEM; + len = strset_str_append(set, s); + if (len < 0) + return len; new_off = set->strs_data_len; - memcpy(p, s, len); if (hashmap__find(set->strs_hash, new_off, &old_off)) return old_off; @@ -142,24 +173,13 @@ int strset__find_str(struct strset *set, const char *s) int strset__add_str(struct strset *set, const char *s) { long old_off, new_off, len; - void *p; int err; - /* Hashmap keys are always offsets within set->strs_data, so to even - * look up some string from the "outside", we need to first append it - * at the end, so that it can be addressed with an offset. Luckily, - * until set->strs_data_len is incremented, that string is just a piece - * of garbage for the rest of the code, so no harm, no foul. On the - * other hand, if the string is unique, it's already appended and - * ready to be used, only a simple set->strs_data_len increment away. - */ - len = strlen(s) + 1; - p = strset_add_str_mem(set, len); - if (!p) - return -ENOMEM; + len = strset_str_append(set, s); + if (len < 0) + return len; new_off = set->strs_data_len; - memcpy(p, s, len); /* Now attempt to add the string, but only if the string with the same * contents doesn't exist already (HASHMAP_ADD strategy). If such From ac77e295b05b097984e5a8052c53389916313321 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 28 Apr 2026 13:06:55 -0700 Subject: [PATCH 1029/2758] ARM: tegra: Add #{address,size}-cells to Chromium-based /firmware [ Upstream commit 1fe27b10dc97c85821dfae1e4e6f9db4472287aa ] Chromium/Depthcharge bootloaders may dynamically add a few device nodes to a system's DTB under a /firmware node. A typical DT looks something like the following: / { firmware { ranges; coreboot { compatible = "coreboot"; reg = <...>; ...; }; }; }; Notably, the /firmware node has an empty 'ranges', but does not have address/size-cells. Commit 6e5773d52f4a ("of/address: Fix WARN when attempting translating non-translatable addresses") started requiring #address-cells for a device's parent if we want to use the reg resource in a device node. This leads to errors like the following: [ 7.763870] coreboot_table firmware:coreboot: probe with driver coreboot_table failed with error -22 Add appropriate #{address,size}-cells to work around the problem. Note that Google has also patched the Depthcharge bootloader source to add {address,size}-cells [1], but bootloader updates are typically delivered only via Google OS updates. Not all users install Google software updates, and even if they do, Google may not produce updated binaries for all/older devices. [1] https://lore.kernel.org/all/20241209092809.GA3246424@google.com/ https://crrev.com/c/6051580 ("coreboot: Insert #address-cells and #size-cells for firmware node") Closes: https://lore.kernel.org/all/aeKlYzTiL0OB1y3g@google.com/ Fixes: 6e5773d52f4a ("of/address: Fix WARN when attempting translating non-translatable addresses") Signed-off-by: Brian Norris Reviewed-by: Douglas Anderson Signed-off-by: Thierry Reding Signed-off-by: Sasha Levin --- arch/arm/boot/dts/nvidia/tegra124-nyan.dtsi | 5 +++++ arch/arm/boot/dts/nvidia/tegra124-venice2.dts | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/arch/arm/boot/dts/nvidia/tegra124-nyan.dtsi b/arch/arm/boot/dts/nvidia/tegra124-nyan.dtsi index 974c76f007db4..89a749cb89338 100644 --- a/arch/arm/boot/dts/nvidia/tegra124-nyan.dtsi +++ b/arch/arm/boot/dts/nvidia/tegra124-nyan.dtsi @@ -14,6 +14,11 @@ stdout-path = "serial0:115200n8"; }; + firmware { + #address-cells = <1>; + #size-cells = <1>; + }; + /* * Note that recent version of the device tree compiler (starting with * version 1.4.2) warn about this node containing a reg property, but diff --git a/arch/arm/boot/dts/nvidia/tegra124-venice2.dts b/arch/arm/boot/dts/nvidia/tegra124-venice2.dts index df98dc2a67b85..059ee6c5b13cd 100644 --- a/arch/arm/boot/dts/nvidia/tegra124-venice2.dts +++ b/arch/arm/boot/dts/nvidia/tegra124-venice2.dts @@ -18,6 +18,11 @@ stdout-path = "serial0:115200n8"; }; + firmware { + #address-cells = <1>; + #size-cells = <1>; + }; + memory@80000000 { reg = <0x0 0x80000000 0x0 0x80000000>; }; From da5715a1cc3329442d87104560310218a5ce0b77 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 28 Apr 2026 13:06:58 -0700 Subject: [PATCH 1030/2758] arm64: tegra: Add #{address,size}-cells to Chromium-based /firmware [ Upstream commit f0fbedccae9e16624977cca02216ab2399f5a3ab ] Chromium/Depthcharge bootloaders may dynamically add a few device nodes to a system's DTB under a /firmware node. A typical DT looks something like the following: / { firmware { ranges; coreboot { compatible = "coreboot"; reg = <...>; ...; }; }; }; Notably, the /firmware node has an empty 'ranges', but does not have address/size-cells. Commit 6e5773d52f4a ("of/address: Fix WARN when attempting translating non-translatable addresses") started requiring #address-cells for a device's parent if we want to use the reg resource in a device node. This leads to errors like the following: [ 7.763870] coreboot_table firmware:coreboot: probe with driver coreboot_table failed with error -22 Add appropriate #{address,size}-cells to work around the problem. Note that Google has also patched the Depthcharge bootloader source to add {address,size}-cells [1], but bootloader updates are typically delivered only via Google OS updates. Not all users install Google software updates, and even if they do, Google may not produce updated binaries for all/older devices. [1] https://lore.kernel.org/all/20241209092809.GA3246424@google.com/ https://crrev.com/c/6051580 ("coreboot: Insert #address-cells and #size-cells for firmware node") Closes: https://lore.kernel.org/all/aeKlYzTiL0OB1y3g@google.com/ Fixes: 6e5773d52f4a ("of/address: Fix WARN when attempting translating non-translatable addresses") Signed-off-by: Brian Norris Reviewed-by: Douglas Anderson Signed-off-by: Thierry Reding Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/nvidia/tegra132-norrin.dts | 5 +++++ arch/arm64/boot/dts/nvidia/tegra210-smaug.dts | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/arch/arm64/boot/dts/nvidia/tegra132-norrin.dts b/arch/arm64/boot/dts/nvidia/tegra132-norrin.dts index 683ac124523b3..1f5222d43e62c 100644 --- a/arch/arm64/boot/dts/nvidia/tegra132-norrin.dts +++ b/arch/arm64/boot/dts/nvidia/tegra132-norrin.dts @@ -18,6 +18,11 @@ stdout-path = "serial0:115200n8"; }; + firmware { + #address-cells = <2>; + #size-cells = <2>; + }; + memory@80000000 { device_type = "memory"; reg = <0x0 0x80000000 0x0 0x80000000>; diff --git a/arch/arm64/boot/dts/nvidia/tegra210-smaug.dts b/arch/arm64/boot/dts/nvidia/tegra210-smaug.dts index f0b8c2c80aa50..a6d31650245d3 100644 --- a/arch/arm64/boot/dts/nvidia/tegra210-smaug.dts +++ b/arch/arm64/boot/dts/nvidia/tegra210-smaug.dts @@ -25,6 +25,11 @@ stdout-path = "serial0:115200n8"; }; + firmware { + #address-cells = <2>; + #size-cells = <2>; + }; + memory@80000000 { device_type = "memory"; reg = <0x0 0x80000000 0x0 0xc0000000>; From 146580b2cef4a02b068a74ed54e7f097bc3637d6 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Tue, 26 May 2026 02:04:41 +0200 Subject: [PATCH 1031/2758] rust: devres: add 'static bound to Devres [ Upstream commit 016267b521b18529c977c9eca9597a1669c3d73c ] Devres::new() registers a callback with the C devres subsystem via devres_node_add(). If the Devres is leaked (e.g. via core::mem::forget(), which is safe), its Drop impl never runs, and the devres release callback will revoke the inner Revocable on device unbind, which drops T in place. If T contains non-'static references, those may be dangling by that point. Add a 'static bound to prevent storing types with borrowed data in Devres. Fixes: 76c01ded724b ("rust: add devres abstraction") Reviewed-by: Alexandre Courbot Reviewed-by: Eliot Courtney Link: https://patch.msgid.link/20260526000447.350558-1-dakr@kernel.org Signed-off-by: Danilo Krummrich Signed-off-by: Sasha Levin --- rust/kernel/devres.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs index 9e5f93aed20cf..2e258d31a45ca 100644 --- a/rust/kernel/devres.rs +++ b/rust/kernel/devres.rs @@ -122,7 +122,7 @@ struct Inner { /// # Ok(()) /// # } /// ``` -pub struct Devres { +pub struct Devres { dev: ARef, inner: Arc>, } @@ -184,7 +184,7 @@ mod base { } } -impl Devres { +impl Devres { /// Creates a new [`Devres`] instance of the given `data`. /// /// The `data` encapsulated within the returned `Devres` instance' `data` will be @@ -349,7 +349,7 @@ unsafe impl Send for Devres {} // SAFETY: `Devres` can be shared with any task, if `T: Sync`. unsafe impl Sync for Devres {} -impl Drop for Devres { +impl Drop for Devres { fn drop(&mut self) { // SAFETY: When `drop` runs, it is guaranteed that nobody is accessing the revocable data // anymore, hence it is safe not to wait for the grace period to finish. From 30ea97affbcce236c57287c82dc6ef7a29c63b76 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Mon, 23 Feb 2026 12:15:16 -0800 Subject: [PATCH 1032/2758] dax/kmem: account for partial discontiguous resource upon removal [ Upstream commit 8aa442cfce79e2d69e72fc8e0c0864ac2971149d ] When dev_dax_kmem_probe() partially succeeds (at least one range is mapped) but a subsequent range fails request_mem_region() or add_memory_driver_managed(), the probe silently continues, ultimately returning success, but with the corresponding range resource NULL'ed out. dev_dax_kmem_remove() iterates over all dax_device ranges regardless of if the underlying resource exists. When remove_memory() is called later, it returns 0 because the memory was never added which causes dev_dax_kmem_remove() to incorrectly assume the (nonexistent) resource can be removed and attempts cleanup on a NULL pointer. Fix this by skipping these ranges altogether, noting that these cases are considered success, such that the cleanup is still reached when all actually-added ranges are successfully removed. Link: https://lore.kernel.org/20260223201516.1517657-1-dave@stgolabs.net Fixes: 60e93dc097f7 ("device-dax: add dis-contiguous resource support") Signed-off-by: Davidlohr Bueso Reviewed-by: Ben Cheatham Reviewed-by: Alison Schofield Reviewed-by: Jonathan Cameron Cc: Dan Williams Cc: Dave Jiang Cc: Vishal Verma Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- drivers/dax/kmem.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/dax/kmem.c b/drivers/dax/kmem.c index 2cc8749bc8711..a18e2b968e4da 100644 --- a/drivers/dax/kmem.c +++ b/drivers/dax/kmem.c @@ -227,6 +227,12 @@ static void dev_dax_kmem_remove(struct dev_dax *dev_dax) if (rc) continue; + /* range was never added during probe */ + if (!data->res[i]) { + success++; + continue; + } + rc = remove_memory(range.start, range_len(&range)); if (rc == 0) { remove_resource(data->res[i]); From fd134f19825d373fd5188500f9861729aba2f605 Mon Sep 17 00:00:00 2001 From: Josh Law Date: Tue, 24 Mar 2026 22:32:09 +0000 Subject: [PATCH 1033/2758] lib/base64: validate before writing in decode tail path [ Upstream commit cae29a5787e38ce7ec7727a87ac7e393a85cb1ef ] Patch series "lib/base64: decode fixes", v2. Two small fixes for lib/base64.c: 1. base64_decode() writes a decoded byte to the output buffer before validating the input in the trailing-bytes path. Move the validity checks before any writes so dst is untouched on invalid input. 2. The @padding kernel-doc for base64_decode() was copy-pasted from base64_encode() and describes the wrong direction. This patch (of 2): The trailing-bytes path in base64_decode() writes a decoded byte to the output buffer before checking whether the input characters are valid. If the input is malformed, garbage is written to dst before the function returns -1. Move the validity checks before any writes so the output buffer is left untouched on invalid input. Link: https://lore.kernel.org/20260324223210.47676-1-objecting@objecting.org Link: https://lore.kernel.org/20260324223210.47676-2-objecting@objecting.org Fixes: 9c7d3cf94d33 ("lib/base64: rework encode/decode for speed and stricter validation") Signed-off-by: Josh Law Reviewed-by: Andrew Morton Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- lib/base64.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/base64.c b/lib/base64.c index 41961a444028b..20dacee25f650 100644 --- a/lib/base64.c +++ b/lib/base64.c @@ -168,15 +168,16 @@ int base64_decode(const char *src, int srclen, u8 *dst, bool padding, enum base6 return -1; val = (base64_rev_tables[s[0]] << 12) | (base64_rev_tables[s[1]] << 6); - *bp++ = val >> 10; if (srclen == 2) { if (val & 0x800003ff) return -1; + *bp++ = val >> 10; } else { val |= base64_rev_tables[s[2]]; if (val & 0x80000003) return -1; + *bp++ = val >> 10; *bp++ = val >> 2; } return bp - dst; From 34f692ba3f755ad6ba598e01dfd39df96482cffc Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Fri, 24 Apr 2026 22:08:55 -0400 Subject: [PATCH 1034/2758] rust: uaccess: use INLINE_COPY_TO_USER to guard copy_to_user() [ Upstream commit f829d4d911cc296b32d14436a8a517e907228475 ] Patch series "uaccess: unify inline vs outline copy_{from,to}_user() selection", v2. The kernel allows arches to select between inline and outline implementations of the copy_{from,to}_user() by defining individual INLINE_COPY_FROM_USER and INLINE_COPY_TO_USER, correspondingly. However, all arches enable or disable them always together. Without the real use-case for one helper being inlined while the other outlined, having independent controls is excessive and error prone. The first patch of the series fixes rust/uaccess coppy_to_user() wrapper guarded with INLINE_COPY_FROM_USER. The 2nd patch switches codebase to the unified INLINE_COPY_USER. And the last patch cleans up ifdefery in the include/linux/uaccess.h This patch (of 3): The copy_to_user() rust helper is only needed when the main kernel inlines the function. It is controlled by INLINE_COPY_TO_USER, but the rust helper is protected with INLINE_COPY_FROM_USER. Fix that. Link: https://lore.kernel.org/20260425020857.356850-1-ynorov@nvidia.com Link: https://lore.kernel.org/20260425020857.356850-2-ynorov@nvidia.com Fixes: d99dc586ca7c7 ("uaccess: decouple INLINE_COPY_FROM_USER and CONFIG_RUST") Signed-off-by: Yury Norov Reported-by: Christophe Leroy (CS GROUP) Closes: https://lore.kernel.org/all/746c9c50-20c4-4dc9-a539-bf1310ff9414@kernel.org/ Cc: Alice Ryhl Cc: Mathieu Desnoyers Cc: Peter Zijlstra Cc: Randy Dunlap Cc: Viktor Malik Cc: Arnd Bergmann Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- rust/helpers/uaccess.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rust/helpers/uaccess.c b/rust/helpers/uaccess.c index d9625b9ee0466..aff22f16ab388 100644 --- a/rust/helpers/uaccess.c +++ b/rust/helpers/uaccess.c @@ -20,7 +20,9 @@ unsigned long rust_helper__copy_from_user(void *to, const void __user *from, uns { return _inline_copy_from_user(to, from, n); } +#endif +#ifdef INLINE_COPY_TO_USER __rust_helper unsigned long rust_helper__copy_to_user(void __user *to, const void *from, unsigned long n) { From 86c1d7f3e548af9c7e7dbcf5d1bfc2bb52e5aa4e Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Fri, 24 Apr 2026 22:08:56 -0400 Subject: [PATCH 1035/2758] uaccess: unify inline vs outline copy_{from,to}_user() selection [ Upstream commit c02be2ad2b88c67c5d7c06b6aa7083b5b40e1077 ] The kernel allows arches to select between inline and outline implementations of the copy_{from,to}_user() by defining individual INLINE_COPY_FROM_USER and INLINE_COPY_TO_USER, correspondingly. However, all arches enable or disable them always together. Without the real use-case for one helper being inlined while the other outlined, having independent controls is excessive and error prone. Switch the codebase to the single unified INLINE_COPY_USER control. Link: https://lore.kernel.org/20260425020857.356850-3-ynorov@nvidia.com Signed-off-by: Yury Norov Tested-by: Alice Ryhl Cc: Arnd Bergmann Cc: Christophe Leroy (CS GROUP) Cc: Mathieu Desnoyers Cc: Peter Zijlstra Cc: Randy Dunlap Cc: Viktor Malik Signed-off-by: Andrew Morton Stable-dep-of: bd99fcfc6219 ("uaccess: minimize INLINE_COPY_USER-related ifdefery") Signed-off-by: Sasha Levin --- arch/arc/include/asm/uaccess.h | 3 +-- arch/arm/include/asm/uaccess.h | 3 +-- arch/arm64/include/asm/uaccess.h | 3 +-- arch/hexagon/include/asm/uaccess.h | 3 +-- arch/loongarch/include/asm/uaccess.h | 3 +-- arch/m68k/include/asm/uaccess.h | 3 +-- arch/microblaze/include/asm/uaccess.h | 3 +-- arch/mips/include/asm/uaccess.h | 3 +-- arch/nios2/include/asm/uaccess.h | 3 +-- arch/openrisc/include/asm/uaccess.h | 3 +-- arch/parisc/include/asm/uaccess.h | 3 +-- arch/s390/include/asm/uaccess.h | 3 +-- arch/sh/include/asm/uaccess.h | 3 +-- arch/sparc/include/asm/uaccess_32.h | 3 +-- arch/sparc/include/asm/uaccess_64.h | 3 +-- arch/um/include/asm/uaccess.h | 3 +-- arch/xtensa/include/asm/uaccess.h | 3 +-- include/asm-generic/uaccess.h | 3 +-- include/linux/uaccess.h | 12 ++++++------ lib/usercopy.c | 4 +--- rust/helpers/uaccess.c | 4 +--- 21 files changed, 26 insertions(+), 48 deletions(-) diff --git a/arch/arc/include/asm/uaccess.h b/arch/arc/include/asm/uaccess.h index 1e8809ea000a3..6df2209541ac0 100644 --- a/arch/arc/include/asm/uaccess.h +++ b/arch/arc/include/asm/uaccess.h @@ -628,8 +628,7 @@ static inline unsigned long __clear_user(void __user *to, unsigned long n) return res; } -#define INLINE_COPY_TO_USER -#define INLINE_COPY_FROM_USER +#define INLINE_COPY_USER #define __clear_user __clear_user diff --git a/arch/arm/include/asm/uaccess.h b/arch/arm/include/asm/uaccess.h index d6ae80b5df36f..1593cf3b98008 100644 --- a/arch/arm/include/asm/uaccess.h +++ b/arch/arm/include/asm/uaccess.h @@ -616,8 +616,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n) } #define __clear_user(addr, n) (memset((void __force *)addr, 0, n), 0) #endif -#define INLINE_COPY_TO_USER -#define INLINE_COPY_FROM_USER +#define INLINE_COPY_USER static inline unsigned long __must_check clear_user(void __user *to, unsigned long n) { diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h index b0c83a08dda97..9f5bd9c69c249 100644 --- a/arch/arm64/include/asm/uaccess.h +++ b/arch/arm64/include/asm/uaccess.h @@ -456,8 +456,7 @@ do { \ unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u8, label); \ } while (0) -#define INLINE_COPY_TO_USER -#define INLINE_COPY_FROM_USER +#define INLINE_COPY_USER extern unsigned long __must_check __arch_clear_user(void __user *to, unsigned long n); static inline unsigned long __must_check __clear_user(void __user *to, unsigned long n) diff --git a/arch/hexagon/include/asm/uaccess.h b/arch/hexagon/include/asm/uaccess.h index bff77efc0d9a9..1aecf60ec4f5a 100644 --- a/arch/hexagon/include/asm/uaccess.h +++ b/arch/hexagon/include/asm/uaccess.h @@ -26,8 +26,7 @@ unsigned long raw_copy_from_user(void *to, const void __user *from, unsigned long n); unsigned long raw_copy_to_user(void __user *to, const void *from, unsigned long n); -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER __kernel_size_t __clear_user_hexagon(void __user *dest, unsigned long count); #define __clear_user(a, s) __clear_user_hexagon((a), (s)) diff --git a/arch/loongarch/include/asm/uaccess.h b/arch/loongarch/include/asm/uaccess.h index 438269313e78c..428f373feabf1 100644 --- a/arch/loongarch/include/asm/uaccess.h +++ b/arch/loongarch/include/asm/uaccess.h @@ -292,8 +292,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n) return __copy_user((__force void *)to, from, n); } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER /* * __clear_user: - Zero a block of memory in user space, with less checking. diff --git a/arch/m68k/include/asm/uaccess.h b/arch/m68k/include/asm/uaccess.h index 64914872a5c98..31d133faa45ef 100644 --- a/arch/m68k/include/asm/uaccess.h +++ b/arch/m68k/include/asm/uaccess.h @@ -377,8 +377,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n) return __constant_copy_to_user(to, from, n); return __generic_copy_to_user(to, from, n); } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER #define __get_kernel_nofault(dst, src, type, err_label) \ do { \ diff --git a/arch/microblaze/include/asm/uaccess.h b/arch/microblaze/include/asm/uaccess.h index 3aab2f17e0462..afa0dd8d013fb 100644 --- a/arch/microblaze/include/asm/uaccess.h +++ b/arch/microblaze/include/asm/uaccess.h @@ -250,8 +250,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n) { return __copy_tofrom_user(to, (__force const void __user *)from, n); } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER /* * Copy a null terminated string from userspace. diff --git a/arch/mips/include/asm/uaccess.h b/arch/mips/include/asm/uaccess.h index c0cede273c7c0..f00c36676b737 100644 --- a/arch/mips/include/asm/uaccess.h +++ b/arch/mips/include/asm/uaccess.h @@ -433,8 +433,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n) return __cu_len_r; } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER extern __kernel_size_t __bzero(void __user *addr, __kernel_size_t size); diff --git a/arch/nios2/include/asm/uaccess.h b/arch/nios2/include/asm/uaccess.h index 6ccc9a232c239..5e6e05cc6efc7 100644 --- a/arch/nios2/include/asm/uaccess.h +++ b/arch/nios2/include/asm/uaccess.h @@ -57,8 +57,7 @@ extern unsigned long raw_copy_from_user(void *to, const void __user *from, unsigned long n); extern unsigned long raw_copy_to_user(void __user *to, const void *from, unsigned long n); -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER extern long strncpy_from_user(char *__to, const char __user *__from, long __len); diff --git a/arch/openrisc/include/asm/uaccess.h b/arch/openrisc/include/asm/uaccess.h index d6500a374e183..db934ebc0069f 100644 --- a/arch/openrisc/include/asm/uaccess.h +++ b/arch/openrisc/include/asm/uaccess.h @@ -218,8 +218,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long size) { return __copy_tofrom_user((__force void *)to, from, size); } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER extern unsigned long __clear_user(void __user *addr, unsigned long size); diff --git a/arch/parisc/include/asm/uaccess.h b/arch/parisc/include/asm/uaccess.h index 6c531d2c847eb..0d17f81c8b270 100644 --- a/arch/parisc/include/asm/uaccess.h +++ b/arch/parisc/include/asm/uaccess.h @@ -197,7 +197,6 @@ unsigned long __must_check raw_copy_to_user(void __user *dst, const void *src, unsigned long len); unsigned long __must_check raw_copy_from_user(void *dst, const void __user *src, unsigned long len); -#define INLINE_COPY_TO_USER -#define INLINE_COPY_FROM_USER +#define INLINE_COPY_USER #endif /* __PARISC_UACCESS_H */ diff --git a/arch/s390/include/asm/uaccess.h b/arch/s390/include/asm/uaccess.h index dff035372601e..a9f32c53f699b 100644 --- a/arch/s390/include/asm/uaccess.h +++ b/arch/s390/include/asm/uaccess.h @@ -30,8 +30,7 @@ void debug_user_asce(int exit); #define uaccess_kmsan_or_inline __always_inline #endif -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER static uaccess_kmsan_or_inline __must_check unsigned long raw_copy_from_user(void *to, const void __user *from, unsigned long size) diff --git a/arch/sh/include/asm/uaccess.h b/arch/sh/include/asm/uaccess.h index a79609eb14be4..02e7a066538ec 100644 --- a/arch/sh/include/asm/uaccess.h +++ b/arch/sh/include/asm/uaccess.h @@ -95,8 +95,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n) { return __copy_user((__force void *)to, from, n); } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER /* * Clear the area and return remaining number of bytes diff --git a/arch/sparc/include/asm/uaccess_32.h b/arch/sparc/include/asm/uaccess_32.h index 43284b6ec46a6..5542d5b32994f 100644 --- a/arch/sparc/include/asm/uaccess_32.h +++ b/arch/sparc/include/asm/uaccess_32.h @@ -190,8 +190,7 @@ static inline unsigned long raw_copy_from_user(void *to, const void __user *from return __copy_user((__force void __user *) to, from, n); } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER static inline unsigned long __clear_user(void __user *addr, unsigned long size) { diff --git a/arch/sparc/include/asm/uaccess_64.h b/arch/sparc/include/asm/uaccess_64.h index b825a5dd0210e..e2989cfba626d 100644 --- a/arch/sparc/include/asm/uaccess_64.h +++ b/arch/sparc/include/asm/uaccess_64.h @@ -231,8 +231,7 @@ unsigned long __must_check raw_copy_from_user(void *to, unsigned long __must_check raw_copy_to_user(void __user *to, const void *from, unsigned long size); -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER unsigned long __must_check raw_copy_in_user(void __user *to, const void __user *from, diff --git a/arch/um/include/asm/uaccess.h b/arch/um/include/asm/uaccess.h index 0df9ea4abda83..4417c8b1d37a6 100644 --- a/arch/um/include/asm/uaccess.h +++ b/arch/um/include/asm/uaccess.h @@ -27,8 +27,7 @@ static inline int __access_ok(const void __user *ptr, unsigned long size); #define __access_ok __access_ok #define __clear_user __clear_user -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER #include diff --git a/arch/xtensa/include/asm/uaccess.h b/arch/xtensa/include/asm/uaccess.h index 56aec6d504fee..6538a29a2bbd4 100644 --- a/arch/xtensa/include/asm/uaccess.h +++ b/arch/xtensa/include/asm/uaccess.h @@ -237,8 +237,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n) prefetch(from); return __xtensa_copy_user((__force void *)to, from, n); } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER /* * We need to return the number of bytes not cleared. Our memset() diff --git a/include/asm-generic/uaccess.h b/include/asm-generic/uaccess.h index b276f783494c4..4569045e7139f 100644 --- a/include/asm-generic/uaccess.h +++ b/include/asm-generic/uaccess.h @@ -91,8 +91,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n) memcpy((void __force *)to, from, n); return 0; } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER #endif /* CONFIG_UACCESS_MEMCPY */ /* diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h index 09a09cc4aac27..add9ceea7b1ed 100644 --- a/include/linux/uaccess.h +++ b/include/linux/uaccess.h @@ -84,7 +84,7 @@ * the 6 functions (copy_{to,from}_user(), __copy_{to,from}_user_inatomic()) * that are used instead. Out of those, __... ones are inlined. Plain * copy_{to,from}_user() might or might not be inlined. If you want them - * inlined, have asm/uaccess.h define INLINE_COPY_{TO,FROM}_USER. + * inlined, have asm/uaccess.h define INLINE_COPY_USER. * * NOTE: only copy_from_user() zero-pads the destination in case of short copy. * Neither __copy_from_user() nor __copy_from_user_inatomic() zero anything @@ -157,7 +157,7 @@ __copy_to_user(void __user *to, const void *from, unsigned long n) } /* - * Architectures that #define INLINE_COPY_TO_USER use this function + * Architectures that #define INLINE_COPY_USER use this function * directly in the normal copy_to/from_user(), the other ones go * through an extern _copy_to/from_user(), which expands the same code * here. @@ -190,7 +190,7 @@ _inline_copy_from_user(void *to, const void __user *from, unsigned long n) memset(to + (n - res), 0, res); return res; } -#ifndef INLINE_COPY_FROM_USER +#ifndef INLINE_COPY_USER extern __must_check unsigned long _copy_from_user(void *, const void __user *, unsigned long); #endif @@ -207,7 +207,7 @@ _inline_copy_to_user(void __user *to, const void *from, unsigned long n) } return n; } -#ifndef INLINE_COPY_TO_USER +#ifndef INLINE_COPY_USER extern __must_check unsigned long _copy_to_user(void __user *, const void *, unsigned long); #endif @@ -217,7 +217,7 @@ copy_from_user(void *to, const void __user *from, unsigned long n) { if (!check_copy_size(to, n, false)) return n; -#ifdef INLINE_COPY_FROM_USER +#ifdef INLINE_COPY_USER return _inline_copy_from_user(to, from, n); #else return _copy_from_user(to, from, n); @@ -230,7 +230,7 @@ copy_to_user(void __user *to, const void *from, unsigned long n) if (!check_copy_size(from, n, true)) return n; -#ifdef INLINE_COPY_TO_USER +#ifdef INLINE_COPY_USER return _inline_copy_to_user(to, from, n); #else return _copy_to_user(to, from, n); diff --git a/lib/usercopy.c b/lib/usercopy.c index b00a3a957de6b..e2f0bf104a591 100644 --- a/lib/usercopy.c +++ b/lib/usercopy.c @@ -12,15 +12,13 @@ /* out-of-line parts */ -#if !defined(INLINE_COPY_FROM_USER) +#if !defined(INLINE_COPY_USER) unsigned long _copy_from_user(void *to, const void __user *from, unsigned long n) { return _inline_copy_from_user(to, from, n); } EXPORT_SYMBOL(_copy_from_user); -#endif -#if !defined(INLINE_COPY_TO_USER) unsigned long _copy_to_user(void __user *to, const void *from, unsigned long n) { return _inline_copy_to_user(to, from, n); diff --git a/rust/helpers/uaccess.c b/rust/helpers/uaccess.c index aff22f16ab388..6e59cc9c665cc 100644 --- a/rust/helpers/uaccess.c +++ b/rust/helpers/uaccess.c @@ -14,15 +14,13 @@ rust_helper_copy_to_user(void __user *to, const void *from, unsigned long n) return copy_to_user(to, from, n); } -#ifdef INLINE_COPY_FROM_USER +#ifdef INLINE_COPY_USER __rust_helper unsigned long rust_helper__copy_from_user(void *to, const void __user *from, unsigned long n) { return _inline_copy_from_user(to, from, n); } -#endif -#ifdef INLINE_COPY_TO_USER __rust_helper unsigned long rust_helper__copy_to_user(void __user *to, const void *from, unsigned long n) { From 9d27b6f190bd41739e7301472cc21903208cd59b Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Fri, 24 Apr 2026 22:08:57 -0400 Subject: [PATCH 1036/2758] uaccess: minimize INLINE_COPY_USER-related ifdefery [ Upstream commit bd99fcfc6219ebe36ae4d0bf5333b5ecc17b53df ] Now that we've got the same config selecting inline vs outline copy_to_user() and copy_from_user(), we can simplify the corresponding logic in the uaccess.h. Link: https://lore.kernel.org/20260425020857.356850-4-ynorov@nvidia.com Fixes: 1f9a8286bc0c ("uaccess: always export _copy_[from|to]_user with CONFIG_RUST") Signed-off-by: Yury Norov Tested-by: Alice Ryhl Cc: Arnd Bergmann Cc: Christophe Leroy (CS GROUP) Cc: Mathieu Desnoyers Cc: Peter Zijlstra Cc: Randy Dunlap Cc: Viktor Malik Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- include/linux/uaccess.h | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h index add9ceea7b1ed..9d39b640f0674 100644 --- a/include/linux/uaccess.h +++ b/include/linux/uaccess.h @@ -190,10 +190,6 @@ _inline_copy_from_user(void *to, const void __user *from, unsigned long n) memset(to + (n - res), 0, res); return res; } -#ifndef INLINE_COPY_USER -extern __must_check unsigned long -_copy_from_user(void *, const void __user *, unsigned long); -#endif static inline __must_check unsigned long _inline_copy_to_user(void __user *to, const void *from, unsigned long n) @@ -207,7 +203,13 @@ _inline_copy_to_user(void __user *to, const void *from, unsigned long n) } return n; } -#ifndef INLINE_COPY_USER +#ifdef INLINE_COPY_USER +# define _copy_to_user _inline_copy_to_user +# define _copy_from_user _inline_copy_from_user +#else +extern __must_check unsigned long +_copy_from_user(void *, const void __user *, unsigned long); + extern __must_check unsigned long _copy_to_user(void __user *, const void *, unsigned long); #endif @@ -217,11 +219,7 @@ copy_from_user(void *to, const void __user *from, unsigned long n) { if (!check_copy_size(to, n, false)) return n; -#ifdef INLINE_COPY_USER - return _inline_copy_from_user(to, from, n); -#else return _copy_from_user(to, from, n); -#endif } static __always_inline unsigned long __must_check @@ -229,12 +227,7 @@ copy_to_user(void __user *to, const void *from, unsigned long n) { if (!check_copy_size(from, n, true)) return n; - -#ifdef INLINE_COPY_USER - return _inline_copy_to_user(to, from, n); -#else return _copy_to_user(to, from, n); -#endif } #ifndef copy_mc_to_kernel From dc28a14f3e4546b2b06098d24177942540581a5f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 8 May 2026 10:51:56 +0300 Subject: [PATCH 1037/2758] rapidio/tsi721: prevent a bad dereference in tsi721_db_dpc() [ Upstream commit fc15e3a30ddd950f009c76765331783b9af94a87 ] With a list_for_each() loop, if we don't find the item we are looking for in the list, then the loop exits with the iterator, which is "dbell" in this loop, pointing to invalid memory. This code uses the "found" variable to determine if we have found the doorbell we are looking for or not. However, the problem that the "found" variable needs to be set to false at the start of each iteration, otherwise after the first correct doorbell, then everything is marked as found. Reset the "found" to false at the start of the iteration and move the variable inside the loop. Link: https://lore.kernel.org/af2WHMZiqMwdYveO@stanley.mountain Fixes: 48618fb4e522 ("RapidIO: add mport driver for Tsi721 bridge") Signed-off-by: Dan Carpenter Cc: Alexandre Bounine Cc: Chul Kim Cc: Matt Porter Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- drivers/rapidio/devices/tsi721.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/rapidio/devices/tsi721.c b/drivers/rapidio/devices/tsi721.c index 66331e67cf4ef..71b87bf8c31d8 100644 --- a/drivers/rapidio/devices/tsi721.c +++ b/drivers/rapidio/devices/tsi721.c @@ -394,7 +394,6 @@ static void tsi721_db_dpc(struct work_struct *work) idb_work); struct rio_mport *mport; struct rio_dbell *dbell; - int found = 0; u32 wr_ptr, rd_ptr; u64 *idb_entry; u32 regval; @@ -412,6 +411,8 @@ static void tsi721_db_dpc(struct work_struct *work) rd_ptr = ioread32(priv->regs + TSI721_IDQ_RP(IDB_QUEUE)) % IDB_QSIZE; while (wr_ptr != rd_ptr) { + int found = 0; + idb_entry = (u64 *)(priv->idb_base + (TSI721_IDB_ENTRY_SIZE * rd_ptr)); rd_ptr++; From 5b33f99f3e48465bd93219495381a5aef4fa967f Mon Sep 17 00:00:00 2001 From: ZhengYuan Huang Date: Tue, 12 May 2026 10:41:15 +0800 Subject: [PATCH 1038/2758] ocfs2: don't BUG_ON an invalid journal dinode [ Upstream commit c0438198c28b1d22c272751af5e717c11d9fa8dd ] [BUG] A fuzzed OCFS2 image can corrupt the current slot journal dinode while mount is still in progress. The mount path first reports the invalid journal block and then crashes in shutdown: kernel BUG at fs/ocfs2/journal.c:1034! Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI RIP: 0010:ocfs2_journal_toggle_dirty+0x2d6/0x340 fs/ocfs2/journal.c:1034 Call Trace: ocfs2_journal_shutdown+0x414/0xc30 fs/ocfs2/journal.c:1116 ocfs2_mount_volume fs/ocfs2/super.c:1785 [inline] ocfs2_fill_super+0x30a9/0x3cd0 fs/ocfs2/super.c:1083 get_tree_bdev_flags+0x38b/0x640 fs/super.c:1698 get_tree_bdev+0x24/0x40 fs/super.c:1721 ocfs2_get_tree+0x21/0x30 fs/ocfs2/super.c:1184 vfs_get_tree+0x9a/0x370 fs/super.c:1758 fc_mount fs/namespace.c:1199 [inline] do_new_mount_fc fs/namespace.c:3642 [inline] do_new_mount fs/namespace.c:3718 [inline] path_mount+0x5b8/0x1ea0 fs/namespace.c:4028 do_mount fs/namespace.c:4041 [inline] __do_sys_mount fs/namespace.c:4229 [inline] __se_sys_mount fs/namespace.c:4206 [inline] __x64_sys_mount+0x282/0x320 fs/namespace.c:4206 ... [CAUSE] ocfs2_journal_toggle_dirty() used to return -EIO when journal->j_bh no longer contained a valid dinode, because the startup and shutdown paths already handled that failure. Commit 10995aa2451a ("ocfs2: Morph the haphazard OCFS2_IS_VALID_DINODE() checks.") changed the check to a BUG_ON() under the assumption that the journal dinode had already been validated. That turns an unexpected invalid journal dinode during mount teardown into a kernel crash instead of a normal mount failure. [FIX] Replace the BUG_ON() with WARN_ON() and return -EIO. This keeps the invariant warning for debugging, but restores the original behavior of failing startup or shutdown cleanly instead of panicking the kernel. Link: https://lore.kernel.org/20260512024115.4036371-1-gality369@gmail.com Fixes: 10995aa2451a ("ocfs2: Morph the haphazard OCFS2_IS_VALID_DINODE() checks.") Signed-off-by: ZhengYuan Huang Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- fs/ocfs2/journal.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index f9bf3bac085db..fc54cc798ce35 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -1022,11 +1022,8 @@ static int ocfs2_journal_toggle_dirty(struct ocfs2_super *osb, struct ocfs2_dinode *fe; fe = (struct ocfs2_dinode *)bh->b_data; - - /* The journal bh on the osb always comes from ocfs2_journal_init() - * and was validated there inside ocfs2_inode_lock_full(). It's a - * code bug if we mess it up. */ - BUG_ON(!OCFS2_IS_VALID_DINODE(fe)); + if (WARN_ON(!OCFS2_IS_VALID_DINODE(fe))) + return -EIO; flags = le32_to_cpu(fe->id1.journal1.ij_flags); if (dirty) From 3c220eccfb3bf549af0aadd42fe451fc9c99ac1e Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Mon, 18 May 2026 13:23:40 +0900 Subject: [PATCH 1039/2758] ocfs2: kill osb->system_file_mutex lock [ Upstream commit f5b1910e23f1233c8d4185268b2e659df2bc5dbf ] Commit 43b10a20372d ("ocfs2: avoid system inode ref confusion by adding mutex lock") tried to avoid a refcount leak caused by allowing multiple threads to call igrab(inode). But addition of osb->system_file_mutex made locking dependency complicated and is causing lockdep to warn about possibility of AB-BA deadlock. Since _ocfs2_get_system_file_inode() returns the same inode for the same input arguments, we don't need to serialize _ocfs2_get_system_file_inode(). What we need to make sure is that igrab(inode) is called for only once(). Therefore, replace osb->system_file_mutex with cmpxchg()-based locking. Link: https://lore.kernel.org/fea8d1fd-afb0-4302-a560-c202e2ef7afd@I-love.SAKURA.ne.jp Fixes: 43b10a20372d ("ocfs2: avoid system inode ref confusion by adding mutex lock") Signed-off-by: Tetsuo Handa Reviewed-by: Heming Zhao Acked-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- fs/ocfs2/ocfs2.h | 2 -- fs/ocfs2/super.c | 2 -- fs/ocfs2/sysfile.c | 9 +++------ 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h index 7b50e03dfa664..62cad6522c7a3 100644 --- a/fs/ocfs2/ocfs2.h +++ b/fs/ocfs2/ocfs2.h @@ -494,8 +494,6 @@ struct ocfs2_super struct rb_root osb_rf_lock_tree; struct ocfs2_refcount_tree *osb_ref_tree_lru; - struct mutex system_file_mutex; - /* * OCFS2 needs to schedule several different types of work which * require cluster locking, disk I/O, recovery waits, etc. Since these diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index b875f01c97564..6dd45c2153f88 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -1997,8 +1997,6 @@ static int ocfs2_initialize_super(struct super_block *sb, spin_lock_init(&osb->osb_xattr_lock); ocfs2_init_steal_slots(osb); - mutex_init(&osb->system_file_mutex); - atomic_set(&osb->alloc_stats.moves, 0); atomic_set(&osb->alloc_stats.local_data, 0); atomic_set(&osb->alloc_stats.bitmap_data, 0); diff --git a/fs/ocfs2/sysfile.c b/fs/ocfs2/sysfile.c index d53a6cc866bef..67e492f4b828b 100644 --- a/fs/ocfs2/sysfile.c +++ b/fs/ocfs2/sysfile.c @@ -98,11 +98,9 @@ struct inode *ocfs2_get_system_file_inode(struct ocfs2_super *osb, } else arr = get_local_system_inode(osb, type, slot); - mutex_lock(&osb->system_file_mutex); if (arr && ((inode = *arr) != NULL)) { /* get a ref in addition to the array ref */ inode = igrab(inode); - mutex_unlock(&osb->system_file_mutex); BUG_ON(!inode); return inode; @@ -112,11 +110,10 @@ struct inode *ocfs2_get_system_file_inode(struct ocfs2_super *osb, inode = _ocfs2_get_system_file_inode(osb, type, slot); /* add one more if putting into array for first time */ - if (arr && inode) { - *arr = igrab(inode); - BUG_ON(!*arr); + if (inode && arr && !*arr && !cmpxchg(&(*arr), NULL, inode)) { + inode = igrab(inode); + BUG_ON(!inode); } - mutex_unlock(&osb->system_file_mutex); return inode; } From 4b409fb6b630cd4ecd190182fa99fcfca2675116 Mon Sep 17 00:00:00 2001 From: Weili Qian Date: Mon, 18 May 2026 22:29:54 +0800 Subject: [PATCH 1040/2758] crypto: hisilicon/qm - disable error report before flr [ Upstream commit e71dc5602b9a29027f6aedd5990d3e8c4f638c8c ] Before function level reset, driver first disable device error report and then waits for the device reset to complete. However, when the error is recovered, the error bits will be enabled again, resulting in invalid disable. It is modified to detect that there is no error before disable error report, and then do FLR. Fixes: 7ce396fa12a9 ("crypto: hisilicon - add FLR support") Signed-off-by: Weili Qian Signed-off-by: Zongyu Wu Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/hisilicon/qm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/crypto/hisilicon/qm.c b/drivers/crypto/hisilicon/qm.c index 3ca47e2a9719f..26e2ccb70c2ae 100644 --- a/drivers/crypto/hisilicon/qm.c +++ b/drivers/crypto/hisilicon/qm.c @@ -4917,8 +4917,6 @@ void hisi_qm_reset_prepare(struct pci_dev *pdev) u32 delay = 0; int ret; - hisi_qm_dev_err_uninit(pf_qm); - /* * Check whether there is an ECC mbit error, If it occurs, need to * wait for soft reset to fix it. @@ -4935,6 +4933,8 @@ void hisi_qm_reset_prepare(struct pci_dev *pdev) return; } + hisi_qm_dev_err_uninit(pf_qm); + /* PF obtains the information of VF by querying the register. */ if (qm->fun_type == QM_HW_PF) qm_cmd_uninit(qm); From 27bddeff3d10b3538e99a6dbf9b2c54bbd7ea6a1 Mon Sep 17 00:00:00 2001 From: Aleksander Jan Bajkowski Date: Mon, 18 May 2026 23:24:59 +0200 Subject: [PATCH 1041/2758] crypto: inside-secure/eip93 - Add check for devm_request_threaded_irq [ Upstream commit 85a61bf9145d4097c740ffcf3aa832d930a8913b ] As the potential failure of the devm_request_threaded_irq(), it should be better to check the return value and return error if fails. Fixes: 9739f5f93b78 ("crypto: eip93 - Add Inside Secure SafeXcel EIP-93 crypto engine support") Signed-off-by: Aleksander Jan Bajkowski Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/inside-secure/eip93/eip93-main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/crypto/inside-secure/eip93/eip93-main.c b/drivers/crypto/inside-secure/eip93/eip93-main.c index 7dccfdeb7b11c..276839e1a515f 100644 --- a/drivers/crypto/inside-secure/eip93/eip93-main.c +++ b/drivers/crypto/inside-secure/eip93/eip93-main.c @@ -433,6 +433,8 @@ static int eip93_crypto_probe(struct platform_device *pdev) ret = devm_request_threaded_irq(eip93->dev, eip93->irq, eip93_irq_handler, NULL, IRQF_ONESHOT, dev_name(eip93->dev), eip93); + if (ret) + return ret; eip93->ring = devm_kcalloc(eip93->dev, 1, sizeof(*eip93->ring), GFP_KERNEL); if (!eip93->ring) From ec49878f2780a985b9efbd68121979c493f4afbd Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Tue, 19 May 2026 12:22:18 +0800 Subject: [PATCH 1042/2758] crypto: tegra - Fix dma_free_coherent size error [ Upstream commit 03215b8457784540acc741e6331e355b62c6c8ab ] When freeing a coherent DMA buffer, the size must match the value that was used during the allocation. Unfortunately the size field in the tegra driver gets overwritten by this point so it no longer matches and creates a warning. Fix this by saving a copy of the size on the stack. Note that the ccm function actually mixes up the inbuf and outbuf sizes, but it doesn't matter because the two sizes are actually equal. Fixes: 1cb328da4e8f ("crypto: tegra - Do not use fixed size buffers") Reporeted-by: Patrick Talbert Signed-off-by: Herbert Xu Reviewed-by: Vladislav Dronov Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/tegra/tegra-se-aes.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/crypto/tegra/tegra-se-aes.c b/drivers/crypto/tegra/tegra-se-aes.c index 30c78afe3dea6..5086e7f140c30 100644 --- a/drivers/crypto/tegra/tegra-se-aes.c +++ b/drivers/crypto/tegra/tegra-se-aes.c @@ -1201,6 +1201,7 @@ static int tegra_ccm_do_one_req(struct crypto_engine *engine, void *areq) struct crypto_aead *tfm = crypto_aead_reqtfm(req); struct tegra_aead_ctx *ctx = crypto_aead_ctx(tfm); struct tegra_se *se = ctx->se; + unsigned int bufsize; int ret; ret = tegra_ccm_crypt_init(req, se, rctx); @@ -1210,14 +1211,15 @@ static int tegra_ccm_do_one_req(struct crypto_engine *engine, void *areq) rctx->key_id = ctx->key_id; /* Allocate buffers required */ - rctx->inbuf.size = rctx->assoclen + rctx->authsize + rctx->cryptlen + 100; - rctx->inbuf.buf = dma_alloc_coherent(ctx->se->dev, rctx->inbuf.size, + bufsize = rctx->assoclen + rctx->authsize + rctx->cryptlen + 100; + rctx->inbuf.size = bufsize; + rctx->inbuf.buf = dma_alloc_coherent(ctx->se->dev, bufsize, &rctx->inbuf.addr, GFP_KERNEL); if (!rctx->inbuf.buf) goto out_finalize; - rctx->outbuf.size = rctx->assoclen + rctx->authsize + rctx->cryptlen + 100; - rctx->outbuf.buf = dma_alloc_coherent(ctx->se->dev, rctx->outbuf.size, + rctx->outbuf.size = bufsize; + rctx->outbuf.buf = dma_alloc_coherent(ctx->se->dev, bufsize, &rctx->outbuf.addr, GFP_KERNEL); if (!rctx->outbuf.buf) { ret = -ENOMEM; @@ -1254,11 +1256,11 @@ static int tegra_ccm_do_one_req(struct crypto_engine *engine, void *areq) } out: - dma_free_coherent(ctx->se->dev, rctx->inbuf.size, + dma_free_coherent(ctx->se->dev, bufsize, rctx->outbuf.buf, rctx->outbuf.addr); out_free_inbuf: - dma_free_coherent(ctx->se->dev, rctx->outbuf.size, + dma_free_coherent(ctx->se->dev, bufsize, rctx->inbuf.buf, rctx->inbuf.addr); if (tegra_key_is_reserved(rctx->key_id)) @@ -1278,6 +1280,7 @@ static int tegra_gcm_do_one_req(struct crypto_engine *engine, void *areq) struct crypto_aead *tfm = crypto_aead_reqtfm(req); struct tegra_aead_ctx *ctx = crypto_aead_ctx(tfm); struct tegra_aead_reqctx *rctx = aead_request_ctx(req); + unsigned int bufsize; int ret; rctx->src_sg = req->src; @@ -1296,16 +1299,17 @@ static int tegra_gcm_do_one_req(struct crypto_engine *engine, void *areq) rctx->key_id = ctx->key_id; /* Allocate buffers required */ - rctx->inbuf.size = rctx->assoclen + rctx->authsize + rctx->cryptlen; - rctx->inbuf.buf = dma_alloc_coherent(ctx->se->dev, rctx->inbuf.size, + bufsize = rctx->assoclen + rctx->authsize + rctx->cryptlen; + rctx->inbuf.size = bufsize; + rctx->inbuf.buf = dma_alloc_coherent(ctx->se->dev, bufsize, &rctx->inbuf.addr, GFP_KERNEL); if (!rctx->inbuf.buf) { ret = -ENOMEM; goto out_finalize; } - rctx->outbuf.size = rctx->assoclen + rctx->authsize + rctx->cryptlen; - rctx->outbuf.buf = dma_alloc_coherent(ctx->se->dev, rctx->outbuf.size, + rctx->outbuf.size = bufsize; + rctx->outbuf.buf = dma_alloc_coherent(ctx->se->dev, bufsize, &rctx->outbuf.addr, GFP_KERNEL); if (!rctx->outbuf.buf) { ret = -ENOMEM; @@ -1342,11 +1346,11 @@ static int tegra_gcm_do_one_req(struct crypto_engine *engine, void *areq) ret = tegra_gcm_do_verify(ctx->se, rctx); out: - dma_free_coherent(ctx->se->dev, rctx->outbuf.size, + dma_free_coherent(ctx->se->dev, bufsize, rctx->outbuf.buf, rctx->outbuf.addr); out_free_inbuf: - dma_free_coherent(ctx->se->dev, rctx->inbuf.size, + dma_free_coherent(ctx->se->dev, bufsize, rctx->inbuf.buf, rctx->inbuf.addr); if (tegra_key_is_reserved(rctx->key_id)) From 20360e125a8dd411b0cc2660cb70f58590740818 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Wed, 20 May 2026 10:51:14 +0800 Subject: [PATCH 1043/2758] crypto: tegra - Return ENOMEM when input buffer allocation fails for ccm [ Upstream commit 690a5f9e5c972a580565ce544ed1627ccf1e84de ] Ensure the ENOMEM error value is set when the input buffer allocation fails in tegra_ccm_do_one_req. Fixes: 1e245948ca0c ("crypto: tegra - finalize crypto req on error") Reported-by: Vladislav Dronov Signed-off-by: Herbert Xu Reviewed-by: Vladislav Dronov Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/tegra/tegra-se-aes.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/crypto/tegra/tegra-se-aes.c b/drivers/crypto/tegra/tegra-se-aes.c index 5086e7f140c30..9094c03e991f6 100644 --- a/drivers/crypto/tegra/tegra-se-aes.c +++ b/drivers/crypto/tegra/tegra-se-aes.c @@ -1215,16 +1215,15 @@ static int tegra_ccm_do_one_req(struct crypto_engine *engine, void *areq) rctx->inbuf.size = bufsize; rctx->inbuf.buf = dma_alloc_coherent(ctx->se->dev, bufsize, &rctx->inbuf.addr, GFP_KERNEL); + ret = -ENOMEM; if (!rctx->inbuf.buf) goto out_finalize; rctx->outbuf.size = bufsize; rctx->outbuf.buf = dma_alloc_coherent(ctx->se->dev, bufsize, &rctx->outbuf.addr, GFP_KERNEL); - if (!rctx->outbuf.buf) { - ret = -ENOMEM; + if (!rctx->outbuf.buf) goto out_free_inbuf; - } if (!ctx->key_id) { ret = tegra_key_submit_reserved_aes(ctx->se, ctx->key, From 6945ad6db8d939c668f50e8ba2af24e50bcc4fe7 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Thu, 21 May 2026 17:43:01 +1000 Subject: [PATCH 1044/2758] crypto: ccp/tsm - Enable the root port after the endpoint [ Upstream commit 50e506201bab875545c7a9443bd7e1c71804e553 ] The PCIe r7.0, chapter "6.33.8 Other IDE Rules" mandates if selective IDE is enabled for config requersts, a stream must be enabled on the endpoint before enabling it on the rootport: === For Selective IDE, the Stream must not be used until it has been enabled in both Partner Ports. For cases where one of the Partner Ports is a Root Port and Selective IDE for Configuration Requests is enabled, the other Partner Port must be enabled prior to the Root Port. For other scenarios, the mechanisms to satisfy this requirement are implementation-specific. === Do what the spec says. Fixes: 4be423572da1 ("crypto/ccp: Implement SEV-TIO PCIe IDE (phase1)") Signed-off-by: Alexey Kardashevskiy Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/ccp/sev-dev-tsm.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/crypto/ccp/sev-dev-tsm.c b/drivers/crypto/ccp/sev-dev-tsm.c index f303d8f55991b..46f2539d2d5ab 100644 --- a/drivers/crypto/ccp/sev-dev-tsm.c +++ b/drivers/crypto/ccp/sev-dev-tsm.c @@ -58,13 +58,13 @@ static int stream_enable(struct pci_ide *ide) struct pci_dev *rp = pcie_find_root_port(ide->pdev); int ret; - ret = pci_ide_stream_enable(rp, ide); - if (ret) + ret = pci_ide_stream_enable(ide->pdev, ide); + if (ret && ret != -ENXIO) return ret; - ret = pci_ide_stream_enable(ide->pdev, ide); - if (ret) - pci_ide_stream_disable(rp, ide); + ret = pci_ide_stream_enable(rp, ide); + if (ret && ret != -ENXIO) + pci_ide_stream_disable(ide->pdev, ide); return ret; } From 32af7d581353a6c35d6664468786cd159772af3d Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Tue, 26 May 2026 12:05:02 +0200 Subject: [PATCH 1045/2758] sched/deadline: Reject debugfs dl_server writes for offline CPUs [ Upstream commit 4043f549841619a01999bf5d4e0b7931ef87f6cc ] Writing runtime or period via the per-CPU dl_server debugfs files (/sys/kernel/debug/sched/{fair,ext}_server/cpu*/{runtime,period}) on an offline CPU can trigger two distinct kernel issues: 1) Divide-by-zero in dl_server_apply_params(): Oops: divide error: 0000 [#1] SMP NOPTI RIP: 0010:dl_server_apply_params+0x239/0x3a0 Call Trace: sched_server_write_common.isra.0+0x21a/0x3c0 full_proxy_write+0x78/0xd0 vfs_write+0xe7/0x6e0 Both __dl_sub() and __dl_add() divide by cpus internally, which can be 0 once the CPU has been removed from any active root-domain span (this has been latent since the debugfs interface was introduced). 2) WARN_ON_ONCE in dl_server_start(): WARNING: kernel/sched/deadline.c:1805 at dl_server_start+0x232/0x270 Commit ee6e44dfe6e5 ("sched/deadline: Stop dl_server before CPU goes offline") added this check to catch enqueueing the server on an offline rq. There's no meaningful semantics for re-configuring the per-CPU dl_server bandwidth while the CPU is offline, so simply reject the write with -EBUSY so userspace gets a clear error. Closes: https://lore.kernel.org/all/20260526092228.3B6891F00A3A@smtp.kernel.org/ Fixes: d741f297bcea ("sched/fair: Fair server interface") Reported-by: Sashiko Signed-off-by: Andrea Righi Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Juri Lelli Tested-by: abaci-kreproducer Link: https://patch.msgid.link/20260526100502.575774-1-arighi@nvidia.com Signed-off-by: Sasha Levin --- kernel/sched/debug.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c index 74c1617cf6523..6f74bde684376 100644 --- a/kernel/sched/debug.c +++ b/kernel/sched/debug.c @@ -373,6 +373,9 @@ static ssize_t sched_server_write_common(struct file *filp, const char __user *u return -EINVAL; } + if (!cpu_online(cpu_of(rq))) + return -EBUSY; + update_rq_clock(rq); dl_server_stop(dl_se); retval = dl_server_apply_params(dl_se, runtime, period, 0); From 20dbc65b23c26486e995c8b703b51f579a162b5e Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Sun, 24 May 2026 13:33:29 +0300 Subject: [PATCH 1046/2758] drm/msm/dp: fix HPD state status bit shift value [ Upstream commit 2e6c2e81d81251623c458a60e2a57447dcbc988e ] The HPD state status is the 3 most significant bits, not 4 bits of the HPD_INT_STATUS register. Fix the bit shift macro so that the correct bits are returned in msm_dp_aux_is_link_connected(). Fixes: 19e52bcb27c2 ("drm/msm/dp: return correct connection status after suspend") Signed-off-by: Jessica Zhang Reviewed-by: Konrad Dybcio Tested-by: Val Packett # x1e80100-dell-latitude-7455 Tested-by: Yongxing Mou # Hamoa IOT EVK, QCS8300 Ride Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/727611/ Link: https://lore.kernel.org/r/20260524-hpd-refactor-v6-1-cf3ab488dd7b@oss.qualcomm.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/dp/dp_reg.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h index 7c44d4e2cf139..3689642b7fc06 100644 --- a/drivers/gpu/drm/msm/dp/dp_reg.h +++ b/drivers/gpu/drm/msm/dp/dp_reg.h @@ -68,8 +68,8 @@ #define DP_DP_IRQ_HPD_INT_ACK (0x00000002) #define DP_DP_HPD_REPLUG_INT_ACK (0x00000004) #define DP_DP_HPD_UNPLUG_INT_ACK (0x00000008) -#define DP_DP_HPD_STATE_STATUS_BITS_MASK (0x0000000F) -#define DP_DP_HPD_STATE_STATUS_BITS_SHIFT (0x1C) +#define DP_DP_HPD_STATE_STATUS_BITS_MASK (0x00000007) +#define DP_DP_HPD_STATE_STATUS_BITS_SHIFT (0x1D) #define REG_DP_DP_HPD_INT_MASK (0x0000000C) #define DP_DP_HPD_PLUG_INT_MASK (0x00000001) From e80925b3fd9c2e68f61aeee0c633805e3f0bb400 Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Sun, 24 May 2026 13:33:30 +0300 Subject: [PATCH 1047/2758] drm/msm/dp: Fix the ISR_* enum values [ Upstream commit 3fbfdc3b1d48cc115a86953e5df0c76cd2efc42b ] The ISR_HPD_* enum should represent values that can be read from the REG_DP_DP_HPD_INT_STATUS register. Swap ISR_HPD_IO_GLITCH_COUNT and ISR_HPD_REPLUG_COUNT to map them correctly to register values. While we are at it, correct the spelling for ISR_HPD_REPLUG_COUNT. Fixes: 8ede2ecc3e5e ("drm/msm/dp: Add DP compliance tests on Snapdragon Chipsets") Signed-off-by: Jessica Zhang Reviewed-by: Konrad Dybcio Tested-by: Val Packett # x1e80100-dell-latitude-7455 Tested-by: Yongxing Mou # Hamoa IOT EVK, QCS8300 Ride Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/727602/ Link: https://lore.kernel.org/r/20260524-hpd-refactor-v6-2-cf3ab488dd7b@oss.qualcomm.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/dp/dp_display.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index d2124d6254855..cf2415635c426 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -38,9 +38,9 @@ enum { ISR_DISCONNECTED, ISR_CONNECT_PENDING, ISR_CONNECTED, - ISR_HPD_REPLUG_COUNT, + ISR_HPD_IO_GLITCH_COUNT, ISR_IRQ_HPD_PULSE_COUNT, - ISR_HPD_LO_GLITH_COUNT, + ISR_HPD_REPLUG_COUNT, }; /* event thread connection state */ From 75c70913d30c3b3c395a4efa0d8c0e9c90553fa7 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Fri, 15 May 2026 09:32:30 +0000 Subject: [PATCH 1048/2758] firmware: samsung: acpm: Add devm_acpm_get_by_phandle helper [ Upstream commit fc13a4b9b9c8cb0b8e5ba54b21712d00f810496c ] Introduce devm_acpm_get_by_phandle() to standardize how consumer drivers acquire a handle to the ACPM IPC interface. Enforce the use of the "samsung,acpm-ipc" property name across the SoC and simplify the boilerplate code in client drivers. The first consumer of this helper is the Exynos ACPM Thermal Management Unit (TMU) driver. The TMU utilizes a hybrid management approach: direct register access from the Application Processor (AP) is restricted to the interrupt pending (INTPEND) registers for event identification. High-level functional tasks, such as sensor initialization, threshold programming, and temperature reads, are delegated to the ACPM firmware via this IPC interface. Signed-off-by: Tudor Ambarus Reviewed-by: Peter Griffin Link: https://patch.msgid.link/20260515-acpm-tmu-helpers-v2-6-8ca011d5a965@linaro.org Signed-off-by: Krzysztof Kozlowski Stable-dep-of: 7b661285aa75 ("firmware: samsung: acpm: remove compile-testing stubs") Signed-off-by: Sasha Levin --- drivers/firmware/samsung/exynos-acpm.c | 23 +++++++++++++++++++ .../firmware/samsung/exynos-acpm-protocol.h | 6 +++++ 2 files changed, 29 insertions(+) diff --git a/drivers/firmware/samsung/exynos-acpm.c b/drivers/firmware/samsung/exynos-acpm.c index 19db3674a28f3..65949e7fd430d 100644 --- a/drivers/firmware/samsung/exynos-acpm.c +++ b/drivers/firmware/samsung/exynos-acpm.c @@ -819,6 +819,29 @@ struct acpm_handle *devm_acpm_get_by_node(struct device *dev, } EXPORT_SYMBOL_GPL(devm_acpm_get_by_node); +/** + * devm_acpm_get_by_phandle - Resource managed lookup of the standardized + * "samsung,acpm-ipc" handle. + * @dev: consumer device + * + * Return: pointer to handle on success, ERR_PTR(-errno) otherwise. + */ +struct acpm_handle *devm_acpm_get_by_phandle(struct device *dev) +{ + struct acpm_handle *handle; + struct device_node *np; + + np = of_parse_phandle(dev->of_node, "samsung,acpm-ipc", 0); + if (!np) + return ERR_PTR(-ENODEV); + + handle = devm_acpm_get_by_node(dev, np); + of_node_put(np); + + return handle; +} +EXPORT_SYMBOL_GPL(devm_acpm_get_by_phandle); + static const struct acpm_match_data acpm_gs101 = { .initdata_base = ACPM_GS101_INITDATA_BASE, .acpm_clk_dev_name = "gs101-acpm-clk", diff --git a/include/linux/firmware/samsung/exynos-acpm-protocol.h b/include/linux/firmware/samsung/exynos-acpm-protocol.h index 13f17dc4443b8..596d17f0384f4 100644 --- a/include/linux/firmware/samsung/exynos-acpm-protocol.h +++ b/include/linux/firmware/samsung/exynos-acpm-protocol.h @@ -52,6 +52,7 @@ struct device; #if IS_ENABLED(CONFIG_EXYNOS_ACPM_PROTOCOL) struct acpm_handle *devm_acpm_get_by_node(struct device *dev, struct device_node *np); +struct acpm_handle *devm_acpm_get_by_phandle(struct device *dev); #else static inline struct acpm_handle *devm_acpm_get_by_node(struct device *dev, @@ -59,6 +60,11 @@ static inline struct acpm_handle *devm_acpm_get_by_node(struct device *dev, { return NULL; } + +static inline struct acpm_handle *devm_acpm_get_by_phandle(struct device *dev) +{ + return ERR_PTR(-ENODEV); +} #endif #endif /* __EXYNOS_ACPM_PROTOCOL_H */ From f0115af6746eaee0232cefb6f38048a14c06cffb Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 29 May 2026 15:43:31 +0200 Subject: [PATCH 1049/2758] firmware: samsung: acpm: remove compile-testing stubs [ Upstream commit 7b661285aa7507eab79efff0a418445157db4141 ] Sashiko reported an inconsistent use of NULL vs ERR_PTR() returns in the stub helpers in xynos-acpm-protocol.h. Since this only happens on dead code for COMPILE_TEST=y, this is not really a bug though. Having stub functions that return NULL is a common way to define optional interfaces, where callers still work when the feature is disabled, though this clearly does not work for acpm because some callers have a NULL pointer dereference when compile testing. Since CONFIG_EXYNOS_ACPM_PROTOCOL already supports compile-testing itself, and all (both) drivers using it clearly require the support, so this just simplifies the option space without losing any build coverage. Remove the stub functions entirely and adjust the one Kconfig dependency to require EXYNOS_ACPM_PROTOCOL unconditionally. Fixes: 6837c006d4e7 ("firmware: exynos-acpm: add empty method to allow compile test") Closes: https://sashiko.dev/#/patchset/20260420-acpm-tmu-v3-0-3dc8e93f0b26%40linaro.org Link: https://lore.kernel.org/all/a7994860-24a3-4f87-84bf-109ed653dda4@linaro.org/ Reviewed-by: Tudor Ambarus Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20260529134454.2147446-1-arnd@kernel.org [krzk: Rebase on difference in devm_acpm_get_by_node()] Signed-off-by: Krzysztof Kozlowski Signed-off-by: Sasha Levin --- drivers/clk/samsung/Kconfig | 2 +- .../linux/firmware/samsung/exynos-acpm-protocol.h | 14 -------------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/drivers/clk/samsung/Kconfig b/drivers/clk/samsung/Kconfig index 70a8b82a0136b..198d8b6212893 100644 --- a/drivers/clk/samsung/Kconfig +++ b/drivers/clk/samsung/Kconfig @@ -97,7 +97,7 @@ config EXYNOS_CLKOUT config EXYNOS_ACPM_CLK tristate "Clock driver controlled via ACPM interface" - depends on EXYNOS_ACPM_PROTOCOL || (COMPILE_TEST && !EXYNOS_ACPM_PROTOCOL) + depends on EXYNOS_ACPM_PROTOCOL help This driver provides support for clocks that are controlled by firmware that implements the ACPM interface. diff --git a/include/linux/firmware/samsung/exynos-acpm-protocol.h b/include/linux/firmware/samsung/exynos-acpm-protocol.h index 596d17f0384f4..24eacc7c16d25 100644 --- a/include/linux/firmware/samsung/exynos-acpm-protocol.h +++ b/include/linux/firmware/samsung/exynos-acpm-protocol.h @@ -49,22 +49,8 @@ struct acpm_handle { struct device; -#if IS_ENABLED(CONFIG_EXYNOS_ACPM_PROTOCOL) struct acpm_handle *devm_acpm_get_by_node(struct device *dev, struct device_node *np); struct acpm_handle *devm_acpm_get_by_phandle(struct device *dev); -#else - -static inline struct acpm_handle *devm_acpm_get_by_node(struct device *dev, - struct device_node *np) -{ - return NULL; -} - -static inline struct acpm_handle *devm_acpm_get_by_phandle(struct device *dev) -{ - return ERR_PTR(-ENODEV); -} -#endif #endif /* __EXYNOS_ACPM_PROTOCOL_H */ From 93657584450d83c5eb49d81ae6863892b0e1d386 Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Mon, 27 Apr 2026 12:08:59 +0530 Subject: [PATCH 1050/2758] drm/msm/a8xx: Make a8xx_recover IFPC safe [ Upstream commit e7c45d9838b7487c1846a0202c649336b2f5bf87 ] Similar to a6xx_recover(), check the GX power domain status before accessing mmio in GX domain a8xx_recover(). Fixes: 288a93200892 ("drm/msm/adreno: Introduce A8x GPU Support") Signed-off-by: Akhil P Oommen Reviewed-by: Konrad Dybcio Signed-off-by: Taniya Das Patchwork: https://patchwork.freedesktop.org/patch/720977/ Message-ID: <20260427-gfx-clk-fixes-v2-5-797e54b3d464@oss.qualcomm.com> Signed-off-by: Rob Clark Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c index ccfccc45133fd..9b99ec5ceeb58 100644 --- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c @@ -886,17 +886,22 @@ void a8xx_recover(struct msm_gpu *gpu) adreno_dump_info(gpu); - if (hang_debug) - a8xx_dump(gpu); - /* * To handle recovery specific sequences during the rpm suspend we are * about to trigger */ a6xx_gpu->hung = true; - /* Halt SQE first */ - gpu_write(gpu, REG_A8XX_CP_SQE_CNTL, 3); + if (adreno_gpu->funcs->gx_is_on(adreno_gpu)) { + /* + * Sometimes crashstate capture is skipped, so SQE should be + * halted here again + */ + gpu_write(gpu, REG_A8XX_CP_SQE_CNTL, 3); + + if (hang_debug) + a8xx_dump(gpu); + } pm_runtime_dont_use_autosuspend(&gpu->pdev->dev); From cbaa5e2d04aad0aa2a4c83d105c3b419ede2c2a4 Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Fri, 22 May 2026 15:41:57 +0530 Subject: [PATCH 1051/2758] drm/msm/a8xx: Fix RSCC offset [ Upstream commit 586a34dafc08c4fa68d1b4673de24cd84d09df05 ] In A8xx, the RSCC block is part of GPU's register space. Update the virtual base address of rscc to point to the correct address. Fixes: 50e8a557d8d3 ("drm/msm/a8xx: Add support for A8x GMU") Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/727117/ Message-ID: <20260522-glymur-gpu-dt-v5-1-562c406b210c@oss.qualcomm.com> Signed-off-by: Rob Clark Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/adreno/a6xx_gmu.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c index 1b44b9e21ad86..cab4c46c6cf2e 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c @@ -2357,7 +2357,12 @@ int a6xx_gmu_init(struct a6xx_gpu *a6xx_gpu, struct device_node *node) goto err_mmio; } } else if (adreno_is_a8xx(adreno_gpu)) { - gmu->rscc = gmu->mmio + 0x19000; + /* + * On a8xx , RSCC lives at GPU base + 0x50000, which falls + * inside the GPU's kgsl_3d0_reg_memory range rather than the + * GMU's. + */ + gmu->rscc = gpu->mmio + 0x50000; } else { gmu->rscc = gmu->mmio + 0x23000; } From a341302c5126ef96bb45286224d67ef60a24281e Mon Sep 17 00:00:00 2001 From: Qiuxu Zhuo Date: Fri, 3 Apr 2026 13:40:27 +0800 Subject: [PATCH 1052/2758] EDAC/igen6: Fix call trace due to missing release() [ Upstream commit ab1f9d466c7d83ab0d2a529e07984e53b5960dcd ] When unloading the igen6_edac driver, there is a call trace: Device '(null)' does not have a release() function, it is broken and must be fixed. See Documentation/core-api/kobject.rst. WARNING: drivers/base/core.c:2567 at device_release+0x84/0x90, CPU#5: rmmod/127209 ... RIP: 0010:device_release+0x84/0x90 Call Trace: kobject_put+0x8c/0x220 put_device+0x17/0x30 igen6_unregister_mcis+0xa2/0xe0 [igen6_edac] igen6_remove+0x82/0xb0 [igen6_edac] ... Fix the call trace by providing empty release() functions for the memory controller devices. Fixes: 10590a9d4f23 ("EDAC/igen6: Add EDAC driver for Intel client SoCs using IBECC") Signed-off-by: Qiuxu Zhuo Signed-off-by: Tony Luck Link: https://patch.msgid.link/20260403054029.3950383-2-qiuxu.zhuo@intel.com Signed-off-by: Sasha Levin --- drivers/edac/igen6_edac.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/edac/igen6_edac.c b/drivers/edac/igen6_edac.c index fcb8ab44cba55..0bf9cf349d0b4 100644 --- a/drivers/edac/igen6_edac.c +++ b/drivers/edac/igen6_edac.c @@ -1296,6 +1296,11 @@ static bool igen6_imc_absent(void __iomem *window) return readl(window + MAD_INTER_CHANNEL_OFFSET) == ~0; } +static void imc_release(struct device *dev) +{ + /* Nothing to do, the 'imc' owns the 'dev' and will also release it. */ +} + static int igen6_register_mci(int mc, void __iomem *window, struct pci_dev *pdev) { struct edac_mc_layer layers[2]; @@ -1334,6 +1339,7 @@ static int igen6_register_mci(int mc, void __iomem *window, struct pci_dev *pdev mci->pvt_info = &igen6_pvt->imc[mc]; imc = mci->pvt_info; + imc->dev.release = imc_release; device_initialize(&imc->dev); /* * EDAC core uses mci->pdev(pointer of structure device) as From 63bbfadcbaf778eded12ec249daf77d303d9cd80 Mon Sep 17 00:00:00 2001 From: Qiuxu Zhuo Date: Fri, 3 Apr 2026 13:40:28 +0800 Subject: [PATCH 1053/2758] EDAC/igen6: Fix memory topology parsing for Panther Lake-H SoCs [ Upstream commit 114bfa24eacb688488caa2e459358a1b9b89b16d ] Panther Lake-H SoC memory controller registers for memory topology have been updated, but the current igen6_edac driver still uses old generation ones to incorrectly parse memory topology. Fix the issue by adding memory topology parsing function pointers to the 'struct res_config' and creating a new configuration structure for Panther Lake-H SoCs to enable igen6_edac to parse memory correctly. Fixes: 0be9f1af3902 ("EDAC/igen6: Add Intel Panther Lake-H SoCs support") Fixes: 4c36e6106997 ("EDAC/igen6: Add more Intel Panther Lake-H SoCs support") Signed-off-by: Qiuxu Zhuo Signed-off-by: Tony Luck Link: https://patch.msgid.link/20260403054029.3950383-3-qiuxu.zhuo@intel.com Signed-off-by: Sasha Levin --- drivers/edac/igen6_edac.c | 373 +++++++++++++++++++++++++++++++------- include/linux/edac.h | 3 + 2 files changed, 307 insertions(+), 69 deletions(-) diff --git a/drivers/edac/igen6_edac.c b/drivers/edac/igen6_edac.c index 0bf9cf349d0b4..f849e3299593f 100644 --- a/drivers/edac/igen6_edac.c +++ b/drivers/edac/igen6_edac.c @@ -122,6 +122,20 @@ #define MEM_SLICE_HASH_MASK(v) (GET_BITFIELD(v, 6, 19) << 6) #define MEM_SLICE_HASH_LSB_MASK_BIT(v) GET_BITFIELD(v, 24, 26) +struct igen6_imc { + int mc; + struct mem_ctl_info *mci; + struct pci_dev *pdev; + struct device dev; + void __iomem *window; + u64 size; + u64 ch_s_size; + int ch_l_map; + u64 dimm_s_size[NUM_CHANNELS]; + u64 dimm_l_size[NUM_CHANNELS]; + int dimm_l_map[NUM_CHANNELS]; +}; + static struct res_config { bool machine_check; /* The number of present memory controllers. */ @@ -134,12 +148,29 @@ static struct res_config { u64 reg_touud_mask; /* IBECC error log */ u64 reg_eccerrlog_addr_mask; + /* MEMSS_PMA_CR registers. */ + u32 reg_mem_config_offset; + u32 reg_mem_config_ddr_type_mask; + /* Memory controller registers. */ + u32 reg_mad_inter_size_mask[NUM_CHANNELS]; + u64 reg_mad_inter_size_granularity; + u32 reg_mad_intra_rank_mask[NUM_DIMMS]; + u32 reg_mad_intra_width_mask[NUM_DIMMS]; + u32 reg_mad_intra_density_mask[NUM_DIMMS]; u32 imc_base; u32 cmf_base; u32 cmf_size; u32 ms_hash_offset; u32 ibecc_base; u32 ibecc_error_log_offset; + /* Get memory type. */ + enum mem_type (*get_mem_type)(struct igen6_imc *imc); + /* Get DRAM chip type. */ + enum dev_type (*get_dev_type)(struct igen6_imc *imc, int chan, int dimm_l); + /* Set imc->ch_{s_size,l_map}. */ + void (*set_chan_params)(struct igen6_imc *imc); + /* Set imc->dimm_{l_size,s_size,l_map}[chan]. */ + void (*set_dimm_params)(struct igen6_imc *imc, int chan); bool (*ibecc_available)(struct pci_dev *pdev); /* Extract error address logged in IBECC */ u64 (*err_addr)(u64 ecclog); @@ -149,22 +180,9 @@ static struct res_config { u64 (*err_addr_to_imc_addr)(u64 eaddr, int mc); } *res_cfg; -struct igen6_imc { - int mc; - struct mem_ctl_info *mci; - struct pci_dev *pdev; - struct device dev; - void __iomem *window; - u64 size; - u64 ch_s_size; - int ch_l_map; - u64 dimm_s_size[NUM_CHANNELS]; - u64 dimm_l_size[NUM_CHANNELS]; - int dimm_l_map[NUM_CHANNELS]; -}; - static struct igen6_pvt { struct igen6_imc imc[NUM_IMC]; + void __iomem *memss_pma_cr; u64 ms_hash; u64 ms_s_size; int ms_l_map; @@ -500,6 +518,119 @@ static u64 rpl_p_err_addr(u64 ecclog) return field_get(res_cfg->reg_eccerrlog_addr_mask, ecclog); } +static enum mem_type ptl_h_get_mem_type(struct igen6_imc *imc) +{ + u32 mtype, val; + + val = readl(igen6_pvt->memss_pma_cr + res_cfg->reg_mem_config_offset); + mtype = field_get(res_cfg->reg_mem_config_ddr_type_mask, val); + + edac_dbg(2, "mtype %u (reg 0x%x)\n", mtype, val); + + switch (mtype) { + case 1: + return MEM_DDR5; + case 2: + return MEM_LPDDR5; + case 3: + return MEM_LPDDR4; + default: + return MEM_UNKNOWN; + } +} + +static enum dev_type ptl_h_get_dev_type(struct igen6_imc *imc, int chan, int dimm) +{ + u32 width, val; + + val = readl(imc->window + MAD_INTRA_CH0_OFFSET + chan * 4); + width = field_get(res_cfg->reg_mad_intra_width_mask[dimm], val); + + switch (width) { + case 1: + return DEV_X8; + default: + return DEV_X16; + } +} + +static u64 ptl_h_get_chan_size(struct igen6_imc *imc, int chan) +{ + u32 val = readl(imc->window + MAD_INTER_CHANNEL_OFFSET); + + return field_get(res_cfg->reg_mad_inter_size_mask[chan], val) * + res_cfg->reg_mad_inter_size_granularity; +} + +static u64 ptl_h_get_dimm_size(struct igen6_imc *imc, int chan, int dimm) +{ + u32 val = readl(imc->window + MAD_INTRA_CH0_OFFSET + chan * 4); + u32 ranks = 1 << field_get(res_cfg->reg_mad_intra_rank_mask[dimm], val); + /* DRAM device density in Gb */ + u64 density = field_get(res_cfg->reg_mad_intra_density_mask[dimm], val) * 4; + + enum mem_type mtype = ptl_h_get_mem_type(imc); + enum dev_type dtype = ptl_h_get_dev_type(imc, chan, dimm); + u64 sub_ch_width, dev_num; + + switch (mtype) { + case MEM_DDR5: + sub_ch_width = 32; + break; + case MEM_LPDDR5: + case MEM_LPDDR4: + sub_ch_width = 16; + break; + default: + sub_ch_width = 0; + } + + switch (dtype) { + case DEV_X8: + dev_num = sub_ch_width / 8; + break; + case DEV_X16: + dev_num = sub_ch_width / 16; + break; + default: + dev_num = 0; + } + + edac_dbg(2, "ranks %d, density %lluGb, sub_ch_width %llu, dev_num %llu (reg 0x%x)\n", ranks, density, sub_ch_width, dev_num, val); + + return ((dev_num * density / 8) * ranks) << 30; +} + +static void ptl_h_set_chan_params(struct igen6_imc *imc) +{ + u64 ch0_size = ptl_h_get_chan_size(imc, 0); + u64 ch1_size = ptl_h_get_chan_size(imc, 1); + + if (ch0_size <= ch1_size) { + imc->ch_s_size = ch0_size; + imc->ch_l_map = 1; + } else { + imc->ch_s_size = ch1_size; + imc->ch_l_map = 0; + } +} + +static void ptl_h_set_dimm_params(struct igen6_imc *imc, int chan) +{ + u64 dimm0_size = ptl_h_get_dimm_size(imc, chan, 0); + u64 dimm1_size = ptl_h_get_dimm_size(imc, chan, 1); + + if (dimm0_size <= dimm1_size) { + imc->dimm_s_size[chan] = dimm0_size; + imc->dimm_l_size[chan] = dimm1_size; + imc->dimm_l_map[chan] = 1; + } else { + imc->dimm_s_size[chan] = dimm1_size; + imc->dimm_l_size[chan] = dimm0_size; + imc->dimm_l_map[chan] = 0; + } +} + static struct res_config ehl_cfg = { .num_imc = 1, .reg_mchbar_mask = GENMASK_ULL(38, 16), @@ -622,6 +753,36 @@ static struct res_config mtl_p_cfg = { .err_addr_to_imc_addr = adl_err_addr_to_imc_addr, }; +static struct res_config ptl_h_cfg = { + .machine_check = true, + .num_imc = 2, + .reg_mchbar_mask = GENMASK_ULL(41, 17), + .reg_tom_mask = GENMASK_ULL(41, 20), + .reg_touud_mask = GENMASK_ULL(41, 20), + .reg_eccerrlog_addr_mask = GENMASK_ULL(38, 5), + .reg_mem_config_offset = 0x13d04, + .reg_mem_config_ddr_type_mask = GENMASK(8, 6), + .reg_mad_inter_size_mask[0] = GENMASK(15, 8), + .reg_mad_inter_size_mask[1] = GENMASK(23, 16), + .reg_mad_inter_size_granularity = BIT_ULL(29), + .reg_mad_intra_rank_mask[0] = BIT(7), + .reg_mad_intra_rank_mask[1] = BIT(15), + .reg_mad_intra_width_mask[0] = BIT(6), + .reg_mad_intra_width_mask[1] = BIT(14), + .reg_mad_intra_density_mask[0] = GENMASK(3, 0), + .reg_mad_intra_density_mask[1] = GENMASK(11, 8), + .imc_base = 0xd800, + .ibecc_base = 0xd400, + .ibecc_error_log_offset = 0x170, + .get_mem_type = ptl_h_get_mem_type, + .get_dev_type = ptl_h_get_dev_type, + .set_chan_params = ptl_h_set_chan_params, + .set_dimm_params = ptl_h_set_dimm_params, + .ibecc_available = mtl_p_ibecc_available, + .err_addr_to_sys_addr = adl_err_addr_to_sys_addr, + .err_addr_to_imc_addr = adl_err_addr_to_imc_addr, +}; + static struct res_config wcl_cfg = { .machine_check = true, .num_imc = 1, @@ -689,46 +850,34 @@ static struct pci_device_id igen6_pci_tbl[] = { { PCI_VDEVICE(INTEL, DID_ARL_UH_SKU1), (kernel_ulong_t)&mtl_p_cfg }, { PCI_VDEVICE(INTEL, DID_ARL_UH_SKU2), (kernel_ulong_t)&mtl_p_cfg }, { PCI_VDEVICE(INTEL, DID_ARL_UH_SKU3), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU1), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU2), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU3), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU4), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU5), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU6), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU7), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU8), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU9), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU10), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU11), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU12), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU13), (kernel_ulong_t)&mtl_p_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU1), (kernel_ulong_t)&ptl_h_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU2), (kernel_ulong_t)&ptl_h_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU3), (kernel_ulong_t)&ptl_h_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU4), (kernel_ulong_t)&ptl_h_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU5), (kernel_ulong_t)&ptl_h_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU6), (kernel_ulong_t)&ptl_h_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU7), (kernel_ulong_t)&ptl_h_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU8), (kernel_ulong_t)&ptl_h_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU9), (kernel_ulong_t)&ptl_h_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU10), (kernel_ulong_t)&ptl_h_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU11), (kernel_ulong_t)&ptl_h_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU12), (kernel_ulong_t)&ptl_h_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU13), (kernel_ulong_t)&ptl_h_cfg }, { PCI_VDEVICE(INTEL, DID_WCL_SKU1), (kernel_ulong_t)&wcl_cfg }, { }, }; MODULE_DEVICE_TABLE(pci, igen6_pci_tbl); -static enum dev_type get_width(int dimm_l, u32 mad_dimm) +static enum mem_type get_mem_type(struct igen6_imc *imc) { - u32 w = dimm_l ? MAD_DIMM_CH_DLW(mad_dimm) : - MAD_DIMM_CH_DSW(mad_dimm); + u32 val; - switch (w) { - case 0: - return DEV_X8; - case 1: - return DEV_X16; - case 2: - return DEV_X32; - default: - return DEV_UNKNOWN; - } -} + if (res_cfg->get_mem_type) + return res_cfg->get_mem_type(imc); -static enum mem_type get_memory_type(u32 mad_inter) -{ - u32 t = MAD_INTER_CHANNEL_DDR_TYPE(mad_inter); + val = readl(imc->window + MAD_INTER_CHANNEL_OFFSET); - switch (t) { + switch (MAD_INTER_CHANNEL_DDR_TYPE(val)) { case 0: return MEM_DDR4; case 1: @@ -744,6 +893,73 @@ static enum mem_type get_memory_type(u32 mad_inter) } } +static bool large_dimm(struct igen6_imc *imc, int chan, int dimm) +{ + return dimm == imc->dimm_l_map[chan]; +} + +static enum dev_type get_dev_type(struct igen6_imc *imc, int chan, int dimm) +{ + u32 width, val; + + if (res_cfg->get_dev_type) + return res_cfg->get_dev_type(imc, chan, dimm); + + val = readl(imc->window + MAD_DIMM_CH0_OFFSET + chan * 4); + width = large_dimm(imc, chan, dimm) ? MAD_DIMM_CH_DLW(val) : + MAD_DIMM_CH_DSW(val); + + switch (width) { + case 0: + return DEV_X8; + case 1: + return DEV_X16; + case 2: + return DEV_X32; + default: + return DEV_UNKNOWN; + } +} + +static u64 get_dimm_size(struct igen6_imc *imc, int chan, int dimm) +{ + if (large_dimm(imc, chan, dimm)) + return imc->dimm_l_size[chan]; + + return imc->dimm_s_size[chan]; +} + +static void set_chan_params(struct igen6_imc *imc) +{ + u32 val; + + if (res_cfg->set_chan_params) { + res_cfg->set_chan_params(imc); + return; + } + + val = readl(imc->window + MAD_INTER_CHANNEL_OFFSET); + imc->ch_s_size = MAD_INTER_CHANNEL_CH_S_SIZE(val); + imc->ch_l_map = MAD_INTER_CHANNEL_CH_L_MAP(val); +} + +static void set_dimm_params(struct igen6_imc *imc, int chan) +{ + u32 val; + + if (res_cfg->set_dimm_params) { + res_cfg->set_dimm_params(imc, chan); + return; + } + + val = readl(imc->window + MAD_INTRA_CH0_OFFSET + chan * 4); + imc->dimm_l_map[chan] = MAD_INTRA_CH_DIMM_L_MAP(val); + + val = readl(imc->window + MAD_DIMM_CH0_OFFSET + chan * 4); + imc->dimm_l_size[chan] = MAD_DIMM_CH_DIMM_L_SIZE(val); + imc->dimm_s_size[chan] = MAD_DIMM_CH_DIMM_S_SIZE(val); +} + static int decode_chan_idx(u64 addr, u64 mask, int intlv_bit) { u64 hash_addr = addr & mask, hash = 0; @@ -1084,7 +1300,6 @@ static bool igen6_check_ecc(struct igen6_imc *imc) static int igen6_get_dimm_config(struct mem_ctl_info *mci) { struct igen6_imc *imc = mci->pvt_info; - u32 mad_inter, mad_intra, mad_dimm; int i, j, ndimms, mc = imc->mc; struct dimm_info *dimm; enum mem_type mtype; @@ -1094,33 +1309,20 @@ static int igen6_get_dimm_config(struct mem_ctl_info *mci) edac_dbg(2, "\n"); - mad_inter = readl(imc->window + MAD_INTER_CHANNEL_OFFSET); - mtype = get_memory_type(mad_inter); + mtype = get_mem_type(imc); ecc = igen6_check_ecc(imc); - imc->ch_s_size = MAD_INTER_CHANNEL_CH_S_SIZE(mad_inter); - imc->ch_l_map = MAD_INTER_CHANNEL_CH_L_MAP(mad_inter); + set_chan_params(imc); for (i = 0; i < NUM_CHANNELS; i++) { - mad_intra = readl(imc->window + MAD_INTRA_CH0_OFFSET + i * 4); - mad_dimm = readl(imc->window + MAD_DIMM_CH0_OFFSET + i * 4); - - imc->dimm_l_size[i] = MAD_DIMM_CH_DIMM_L_SIZE(mad_dimm); - imc->dimm_s_size[i] = MAD_DIMM_CH_DIMM_S_SIZE(mad_dimm); - imc->dimm_l_map[i] = MAD_INTRA_CH_DIMM_L_MAP(mad_intra); + set_dimm_params(imc, i); imc->size += imc->dimm_s_size[i]; imc->size += imc->dimm_l_size[i]; ndimms = 0; for (j = 0; j < NUM_DIMMS; j++) { dimm = edac_get_dimm(mci, i, j, 0); - - if (j ^ imc->dimm_l_map[i]) { - dtype = get_width(0, mad_dimm); - dsize = imc->dimm_s_size[i]; - } else { - dtype = get_width(1, mad_dimm); - dsize = imc->dimm_l_size[i]; - } + dtype = get_dev_type(imc, i, j); + dsize = get_dimm_size(imc, i, j); if (!dsize) continue; @@ -1223,6 +1425,39 @@ static void igen6_debug_setup(void) {} static void igen6_debug_teardown(void) {} #endif +static struct igen6_pvt *igen6_pvt_setup(struct pci_dev *pdev) +{ + void __iomem *memss_pma_cr; + struct igen6_pvt *pvt; + u64 mchbar; + int rc; + + pvt = kzalloc_obj(*igen6_pvt); + if (!pvt) + return NULL; + + rc = get_mchbar(pdev, &mchbar); + if (rc) { + kfree(pvt); + return NULL; + } + + memss_pma_cr = ioremap(mchbar, MCHBAR_SIZE * 2); + if (!memss_pma_cr) { + kfree(pvt); + return NULL; + } + pvt->memss_pma_cr = memss_pma_cr; + + return pvt; +} + +static void igen6_pvt_release(struct igen6_pvt *pvt) +{ + iounmap(pvt->memss_pma_cr); + kfree(pvt); +} + static int igen6_pci_setup(struct pci_dev *pdev, u64 *mchbar) { union { @@ -1555,12 +1790,12 @@ static int igen6_probe(struct pci_dev *pdev, const struct pci_device_id *ent) edac_dbg(2, "\n"); - igen6_pvt = kzalloc_obj(*igen6_pvt); + res_cfg = (struct res_config *)ent->driver_data; + + igen6_pvt = igen6_pvt_setup(pdev); if (!igen6_pvt) return -ENOMEM; - res_cfg = (struct res_config *)ent->driver_data; - rc = igen6_pci_setup(pdev, &mchbar); if (rc) goto fail; @@ -1609,7 +1844,7 @@ static int igen6_probe(struct pci_dev *pdev, const struct pci_device_id *ent) fail2: igen6_unregister_mcis(); fail: - kfree(igen6_pvt); + igen6_pvt_release(igen6_pvt); return rc; } @@ -1624,7 +1859,7 @@ static void igen6_remove(struct pci_dev *pdev) flush_work(&ecclog_work); gen_pool_destroy(ecclog_pool); igen6_unregister_mcis(); - kfree(igen6_pvt); + igen6_pvt_release(igen6_pvt); } static struct pci_driver igen6_driver = { diff --git a/include/linux/edac.h b/include/linux/edac.h index deba46b3ee25d..e6b4e51130e5f 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -184,6 +184,7 @@ static inline char *mc_event_error_type(const unsigned int err_type) * @MEM_DDR5: Unbuffered DDR5 RAM * @MEM_RDDR5: Registered DDR5 RAM * @MEM_LRDDR5: Load-Reduced DDR5 memory. + * @MEM_LPDDR5: Low-Power DDR5 memory. * @MEM_NVDIMM: Non-volatile RAM * @MEM_WIO2: Wide I/O 2. * @MEM_HBM2: High bandwidth Memory Gen 2. @@ -216,6 +217,7 @@ enum mem_type { MEM_DDR5, MEM_RDDR5, MEM_LRDDR5, + MEM_LPDDR5, MEM_NVDIMM, MEM_WIO2, MEM_HBM2, @@ -247,6 +249,7 @@ enum mem_type { #define MEM_FLAG_DDR5 BIT(MEM_DDR5) #define MEM_FLAG_RDDR5 BIT(MEM_RDDR5) #define MEM_FLAG_LRDDR5 BIT(MEM_LRDDR5) +#define MEM_FLAG_LPDDR5 BIT(MEM_LPDDR5) #define MEM_FLAG_NVDIMM BIT(MEM_NVDIMM) #define MEM_FLAG_WIO2 BIT(MEM_WIO2) #define MEM_FLAG_HBM2 BIT(MEM_HBM2) From c6e1cab54a06cd6481adf21630ddd97b43f69edd Mon Sep 17 00:00:00 2001 From: zhoumin Date: Thu, 26 Mar 2026 17:14:03 +0800 Subject: [PATCH 1054/2758] EDAC/{skx_common,skx}: Fix UBSAN shift-out-of-bounds in skx_get_dimm_info [ Upstream commit c63ed6e1f5fe648a4a099b6717f679999be482ef ] When the skx_get_dimm_attr() helper returns -EINVAL, skx_get_dimm_info() does not validate these return values before using them in a shift operation: size = ((1ull << (rows + cols + ranks)) * banks) >> (20 - 3); If all three values are -22, the shift exponent becomes -66, triggering a UBSAN shift-out-of-bounds error: UBSAN: shift-out-of-bounds in drivers/edac/skx_common.c shift exponent -66 is negative Fixes: 88a242c98740 ("EDAC, skx_common: Separate common code out from skx_edac") Signed-off-by: zhoumin Signed-off-by: Tony Luck Link: https://patch.msgid.link/tencent_2A0CC835A18366643CBD2865B169948AB409@qq.com Signed-off-by: Sasha Levin --- drivers/edac/skx_common.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/edac/skx_common.c b/drivers/edac/skx_common.c index a9557c8344bc1..f15de0ea96c87 100644 --- a/drivers/edac/skx_common.c +++ b/drivers/edac/skx_common.c @@ -466,6 +466,9 @@ int skx_get_dimm_info(u32 mtr, u32 mcmtr, u32 amap, struct dimm_info *dimm, rows = numrow(mtr); cols = imc->hbm_mc ? 6 : numcol(mtr); + if (ranks < 0 || rows < 0 || cols < 0) + return 0; + if (imc->hbm_mc) { banks = 32; mtype = MEM_HBM2; From 558626dc9d73fcd0810b7ae565aca83a00524726 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 11 Apr 2026 15:02:35 +0200 Subject: [PATCH 1055/2758] arm64: dts: st: Fix SAI addresses on stm32mp251 [ Upstream commit fba4a31a7f3b6b29b01c83180f83e7ed4c398738 ] The second field of SAI register addresses should be within 0x3f0 bytes from the start of the SAI register addresses, the second field describes the ID registers which are at that addrses. Currently, the second field does not match RM, fix it. Fixes: bf26d75a95f1 ("arm64: dts: st: add sai support on stm32mp251") Signed-off-by: Marek Vasut Reviewed-by: Olivier Moysan Link: https://lore.kernel.org/r/20260411130300.19603-1-marex@nabladev.com Signed-off-by: Alexandre Torgue Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/st/stm32mp251.dtsi | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/st/stm32mp251.dtsi b/arch/arm64/boot/dts/st/stm32mp251.dtsi index 673fbc5632e69..9c63fdb5a885a 100644 --- a/arch/arm64/boot/dts/st/stm32mp251.dtsi +++ b/arch/arm64/boot/dts/st/stm32mp251.dtsi @@ -1202,7 +1202,7 @@ sai1: sai@40290000 { compatible = "st,stm32mp25-sai"; - reg = <0x40290000 0x4>, <0x4029a3f0 0x10>; + reg = <0x40290000 0x4>, <0x402903f0 0x10>; ranges = <0 0x40290000 0x400>; #address-cells = <1>; #size-cells = <1>; @@ -1236,7 +1236,7 @@ sai2: sai@402a0000 { compatible = "st,stm32mp25-sai"; - reg = <0x402a0000 0x4>, <0x402aa3f0 0x10>; + reg = <0x402a0000 0x4>, <0x402a03f0 0x10>; ranges = <0 0x402a0000 0x400>; #address-cells = <1>; #size-cells = <1>; @@ -1270,7 +1270,7 @@ sai3: sai@402b0000 { compatible = "st,stm32mp25-sai"; - reg = <0x402b0000 0x4>, <0x402ba3f0 0x10>; + reg = <0x402b0000 0x4>, <0x402b03f0 0x10>; ranges = <0 0x402b0000 0x400>; #address-cells = <1>; #size-cells = <1>; @@ -1362,7 +1362,7 @@ sai4: sai@40340000 { compatible = "st,stm32mp25-sai"; - reg = <0x40340000 0x4>, <0x4034a3f0 0x10>; + reg = <0x40340000 0x4>, <0x403403f0 0x10>; ranges = <0 0x40340000 0x400>; #address-cells = <1>; #size-cells = <1>; From 31a19a68c83b39ed14c6e401387886aae68ddee0 Mon Sep 17 00:00:00 2001 From: Jinseok Kim Date: Wed, 27 May 2026 19:39:26 +0900 Subject: [PATCH 1056/2758] arm: dts: bcm2711: Fix typo in gpio-line-names [ Upstream commit 245552af869d35ae27cac2121696dd630f15b3bf ] Replace "RGMIO_MDC" with "RGMII_MDC" in gpio-line-names. Signed-off-by: Jinseok Kim Link: https://lore.kernel.org/r/20260527103930.2973-1-always.starving0@gmail.com Fixes: cd87c180b301 ("ARM: dts: bcm2711-rpi-4-b: Add SoC GPIO labels") Fixes: ea93ada05c9e ("ARM: dts: Add Raspberry Pi Compute Module 4 IO Board") Signed-off-by: Florian Fainelli Signed-off-by: Sasha Levin --- arch/arm/boot/dts/broadcom/bcm2711-rpi-4-b.dts | 2 +- arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4-io.dts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/broadcom/bcm2711-rpi-4-b.dts b/arch/arm/boot/dts/broadcom/bcm2711-rpi-4-b.dts index 353bb50ce5425..5469fa663526f 100644 --- a/arch/arm/boot/dts/broadcom/bcm2711-rpi-4-b.dts +++ b/arch/arm/boot/dts/broadcom/bcm2711-rpi-4-b.dts @@ -110,7 +110,7 @@ "GPIO26", "GPIO27", "RGMII_MDIO", - "RGMIO_MDC", + "RGMII_MDC", /* Used by BT module */ "CTS0", /* 30 */ "RTS0", diff --git a/arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4-io.dts b/arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4-io.dts index 6bc77dd48c0d9..1f4ebec68370c 100644 --- a/arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4-io.dts +++ b/arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4-io.dts @@ -56,7 +56,7 @@ "GPIO26", "GPIO27", "RGMII_MDIO", - "RGMIO_MDC", + "RGMII_MDC", /* Used by BT module */ "CTS0", "RTS0", From 168670d9c56ec80c9ac836fcf583d9dfc1dfe50a Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 29 May 2026 15:43:10 +0200 Subject: [PATCH 1057/2758] RDMA/umem: Add ib_umem_is_contiguous() stub for !CONFIG_INFINIBAND_USER_MEM [ Upstream commit 2cc10972f5f4f123e5a7658824db4f7b5abfc410 ] ib_umem_is_contiguous() is defined under #ifdef CONFIG_INFINIBAND_USER_MEM, but the #else branch lacks a stub. Add the missing inline to fix potential broken build. Fixes: c897c2c8b8e8 ("RDMA/core: Add umem "is_contiguous" and "start_dma_addr" helpers") Link: https://patch.msgid.link/r/20260529134312.2836341-15-jiri@resnulli.us Signed-off-by: Jiri Pirko Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- include/rdma/ib_umem.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/rdma/ib_umem.h b/include/rdma/ib_umem.h index 49172098a8de1..3e0c2c356d142 100644 --- a/include/rdma/ib_umem.h +++ b/include/rdma/ib_umem.h @@ -185,6 +185,10 @@ static inline unsigned long ib_umem_find_best_pgoff(struct ib_umem *umem, { return 0; } +static inline bool ib_umem_is_contiguous(struct ib_umem *umem) +{ + return false; +} static inline struct ib_umem_dmabuf *ib_umem_dmabuf_get(struct ib_device *device, unsigned long offset, From b9800d7953d119bcc068c74587d48e4ba0313629 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Mon, 18 May 2026 21:50:39 +0000 Subject: [PATCH 1058/2758] RDMA/rxe: Fix TOCTOU heap overflow in get_srq_wqe [ Upstream commit 22b8fbded65b8c441b634a185f8da67657df6c50 ] get_srq_wqe() reads wqe->dma.num_sge from the shared receive queue buffer, which is mapped into userspace. It validates num_sge against max_sge, but then re-reads the same field to calculate the memcpy size. A concurrent userspace thread can modify num_sge between validation and use, causing a heap buffer overflow when copying the WQE into qp->resp.srq_wqe. Read num_sge into a local variable and use it for both the bounds check and the size calculation. Fixes: 8700e3e7c485 ("Soft RoCE driver") Link: https://patch.msgid.link/r/20260518215040.1598586-2-tristan@talencesecurity.com Signed-off-by: Tristan Madani Reviewed-by: Zhu Yanjun Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/sw/rxe/rxe_resp.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/sw/rxe/rxe_resp.c b/drivers/infiniband/sw/rxe/rxe_resp.c index 9cb2f6fbf2dd6..8a0a973963601 100644 --- a/drivers/infiniband/sw/rxe/rxe_resp.c +++ b/drivers/infiniband/sw/rxe/rxe_resp.c @@ -264,6 +264,7 @@ static enum resp_states get_srq_wqe(struct rxe_qp *qp) struct rxe_recv_wqe *wqe; struct ib_event ev; unsigned int count; + unsigned int num_sge; size_t size; unsigned long flags; @@ -279,12 +280,13 @@ static enum resp_states get_srq_wqe(struct rxe_qp *qp) } /* don't trust user space data */ - if (unlikely(wqe->dma.num_sge > srq->rq.max_sge)) { + num_sge = wqe->dma.num_sge; + if (unlikely(num_sge > srq->rq.max_sge)) { spin_unlock_irqrestore(&srq->rq.consumer_lock, flags); rxe_dbg_qp(qp, "invalid num_sge in SRQ entry\n"); return RESPST_ERR_MALFORMED_WQE; } - size = sizeof(*wqe) + wqe->dma.num_sge*sizeof(struct rxe_sge); + size = sizeof(*wqe) + num_sge * sizeof(struct rxe_sge); memcpy(&qp->resp.srq_wqe, wqe, size); qp->resp.wqe = &qp->resp.srq_wqe.wqe; From a211b7904aed365e4e4f08a48ec6e6dd1ea7b16b Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Mon, 18 May 2026 21:50:40 +0000 Subject: [PATCH 1059/2758] RDMA/rxe: Copy WQE to local buffer in non-SRQ receive path [ Upstream commit d6ab440240a04b8737ee4c7bb21af9182e451733 ] For non-SRQ QPs, the responder reads WQE fields directly from the shared queue buffer mapped into userspace. This allows a malicious user to modify fields like num_sge or sge entries while the kernel is processing the WQE, leading to out-of-bounds reads in rxe_resp_check_length() and copy_data(). Introduce get_recv_wqe() that validates num_sge and copies the WQE to a kernel-local buffer before processing, matching the approach already used for SRQ WQEs in get_srq_wqe(). The srq_wqe buffer is reused since SRQ and non-SRQ paths are mutually exclusive per QP. Fixes: 8700e3e7c485 ("Soft RoCE driver") Link: https://patch.msgid.link/r/20260518215040.1598586-3-tristan@talencesecurity.com Signed-off-by: Tristan Madani Reviewed-by: Zhu Yanjun Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/sw/rxe/rxe_resp.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/sw/rxe/rxe_resp.c b/drivers/infiniband/sw/rxe/rxe_resp.c index 8a0a973963601..e0294b7c95693 100644 --- a/drivers/infiniband/sw/rxe/rxe_resp.c +++ b/drivers/infiniband/sw/rxe/rxe_resp.c @@ -310,6 +310,29 @@ static enum resp_states get_srq_wqe(struct rxe_qp *qp) return RESPST_CHK_LENGTH; } +static enum resp_states rxe_get_recv_wqe(struct rxe_qp *qp) +{ + struct rxe_queue *q = qp->rq.queue; + struct rxe_recv_wqe *wqe; + unsigned int num_sge; + size_t size; + + wqe = queue_head(q, QUEUE_TYPE_FROM_CLIENT); + if (!wqe) + return RESPST_ERR_RNR; + + num_sge = wqe->dma.num_sge; + if (unlikely(num_sge > qp->rq.max_sge)) { + rxe_dbg_qp(qp, "invalid num_sge in recv WQE\n"); + return RESPST_ERR_MALFORMED_WQE; + } + size = sizeof(*wqe) + num_sge * sizeof(struct rxe_sge); + memcpy(&qp->resp.srq_wqe, wqe, size); + + qp->resp.wqe = &qp->resp.srq_wqe.wqe; + return RESPST_CHK_LENGTH; +} + static enum resp_states check_resource(struct rxe_qp *qp, struct rxe_pkt_info *pkt) { @@ -330,9 +353,7 @@ static enum resp_states check_resource(struct rxe_qp *qp, if (srq) return get_srq_wqe(qp); - qp->resp.wqe = queue_head(qp->rq.queue, - QUEUE_TYPE_FROM_CLIENT); - return (qp->resp.wqe) ? RESPST_CHK_LENGTH : RESPST_ERR_RNR; + return rxe_get_recv_wqe(qp); } return RESPST_CHK_LENGTH; From f6db9ffab324344197b28dadf3f3c326d7edd4ce Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Mon, 18 May 2026 20:58:16 +0000 Subject: [PATCH 1060/2758] dts: riscv: spacemit: k3: Fix I/O power settings [ Upstream commit 3ea695eb111fc5a7ffddd21d5c3fee6d82560413 ] SpacemiT K3 SoC support dual-voltage I/O power domain, while initially configure to 3.3v, and need to access register from APBC space to switch to 1.8v domain. Fix the GMAC0's I/O pins 1.8v switch failure that will result a broken ethernet driver. Fixes: d8944577496b ("riscv: dts: spacemit: k3: add pinctrl support") Reported-by: Han Gao Link: https://patch.msgid.link/20260518-07-dts-pinctrl-io-power-v1-1-abe19c14a726@kernel.org Signed-off-by: Yixun Lan Signed-off-by: Sasha Levin --- arch/riscv/boot/dts/spacemit/k3.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/boot/dts/spacemit/k3.dtsi b/arch/riscv/boot/dts/spacemit/k3.dtsi index e6faf8d8759e1..719850aa7aac5 100644 --- a/arch/riscv/boot/dts/spacemit/k3.dtsi +++ b/arch/riscv/boot/dts/spacemit/k3.dtsi @@ -803,6 +803,7 @@ clocks = <&syscon_apbc CLK_APBC_AIB>, <&syscon_apbc CLK_APBC_AIB_BUS>; clock-names = "func", "bus"; + spacemit,apbc = <&syscon_apbc>; }; uart10: serial@d401f000 { From c399eccc9729d7c26f5206a8df3000dd85ced79b Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 7 Apr 2026 01:15:52 +0300 Subject: [PATCH 1061/2758] Revert "media: venus: hfi_platform: Correct supported codecs for sc7280" [ Upstream commit 3eb9ba0da0ab73e135f93ffccf07381ba11f100e ] This reverts commit c0ab2901fc68 ("media: venus: hfi_platform: Correct supported codecs for sc7280"). The codecs might be deprecated, but they still work (somewhat) perfectly and don't cause any issues with the rest of the system. Reenable VP8 codecs by reverting the offending commit. Tested with fluster: |Test|FFmpeg-VP8-v4l2m2m|GStreamer-VP8-V4L2| |TOTAL|50/61|50/61| |TOTAL TIME|12.171s|11.824s| Fixes: c0ab2901fc68 ("media: venus: hfi_platform: Correct supported codecs for sc7280") Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue Signed-off-by: Sasha Levin --- .../media/platform/qcom/venus/hfi_parser.c | 6 ++--- .../media/platform/qcom/venus/hfi_platform.c | 24 ------------------- .../media/platform/qcom/venus/hfi_platform.h | 2 -- 3 files changed, 2 insertions(+), 30 deletions(-) diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c index 92765f9c88730..c4cf6cd50a9a0 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.c +++ b/drivers/media/platform/qcom/venus/hfi_parser.c @@ -268,7 +268,6 @@ static int hfi_platform_parser(struct venus_core *core, struct venus_inst *inst) const struct hfi_plat_caps *caps = NULL; u32 enc_codecs, dec_codecs, count = 0; unsigned int entries; - int ret; plat = hfi_platform_get(core->res->hfi_version); if (!plat) @@ -277,9 +276,8 @@ static int hfi_platform_parser(struct venus_core *core, struct venus_inst *inst) if (inst) return 0; - ret = hfi_platform_get_codecs(core, &enc_codecs, &dec_codecs, &count); - if (ret) - return ret; + if (plat->codecs) + plat->codecs(core, &enc_codecs, &dec_codecs, &count); if (plat->capabilities) caps = plat->capabilities(core, &entries); diff --git a/drivers/media/platform/qcom/venus/hfi_platform.c b/drivers/media/platform/qcom/venus/hfi_platform.c index cde7f93045ac4..f19572ab1d161 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform.c +++ b/drivers/media/platform/qcom/venus/hfi_platform.c @@ -2,9 +2,7 @@ /* * Copyright (c) 2020, The Linux Foundation. All rights reserved. */ -#include #include "hfi_platform.h" -#include "core.h" const struct hfi_platform *hfi_platform_get(enum hfi_version version) { @@ -73,25 +71,3 @@ hfi_platform_get_codec_lp_freq(struct venus_core *core, return freq; } - -int -hfi_platform_get_codecs(struct venus_core *core, u32 *enc_codecs, - u32 *dec_codecs, u32 *count) -{ - const struct hfi_platform *plat; - - plat = hfi_platform_get(core->res->hfi_version); - if (!plat) - return -EINVAL; - - if (plat->codecs) - plat->codecs(core, enc_codecs, dec_codecs, count); - - if (IS_IRIS2_1(core)) { - *enc_codecs &= ~HFI_VIDEO_CODEC_VP8; - *dec_codecs &= ~HFI_VIDEO_CODEC_VP8; - } - - return 0; -} - diff --git a/drivers/media/platform/qcom/venus/hfi_platform.h b/drivers/media/platform/qcom/venus/hfi_platform.h index 5e4f8013a6b1d..a0b6d19f3e1a0 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform.h +++ b/drivers/media/platform/qcom/venus/hfi_platform.h @@ -74,6 +74,4 @@ unsigned long hfi_platform_get_codec_vsp_freq(struct venus_core *core, unsigned long hfi_platform_get_codec_lp_freq(struct venus_core *core, enum hfi_version version, u32 codec, u32 session_type); -int hfi_platform_get_codecs(struct venus_core *core, u32 *enc_codecs, - u32 *dec_codecs, u32 *count); #endif From f035a50db2515d4f33518e35eac6db5111008866 Mon Sep 17 00:00:00 2001 From: Renjiang Han Date: Tue, 31 Mar 2026 10:07:07 +0530 Subject: [PATCH 1062/2758] media: qcom: venus: drop extra padding in NV12 raw size calculation [ Upstream commit e1c9adabb268cc5d56723b7df1da49e59070f309 ] get_framesize_raw_nv12() currently adds SZ_4K to the UV plane size and an additional SZ_8K to the total buffer size. This inflates the calculated sizeimage and leads userspace to over-allocate buffers without a clear requirement. Remove the extra SZ_4K/SZ_8K padding and compute the NV12 size as the sum of Y and UV planes, keeping the final ALIGN(size, SZ_4K) intact. Fixes: e1cb72de702ad ("media: venus: helpers: move frame size calculations on common place") Signed-off-by: Renjiang Han Reviewed-by: Dikshita Agarwal Signed-off-by: Bryan O'Donoghue Signed-off-by: Sasha Levin --- drivers/media/platform/qcom/venus/helpers.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 747c388fe25fa..59eee3dd9e06c 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -954,8 +954,8 @@ static u32 get_framesize_raw_nv12(u32 width, u32 height) uv_sclines = ALIGN(((height + 1) >> 1), 16); y_plane = y_stride * y_sclines; - uv_plane = uv_stride * uv_sclines + SZ_4K; - size = y_plane + uv_plane + SZ_8K; + uv_plane = uv_stride * uv_sclines; + size = y_plane + uv_plane; return ALIGN(size, SZ_4K); } From 309f922634ec382a8beb47873cb22985b0b74825 Mon Sep 17 00:00:00 2001 From: Renjiang Han Date: Tue, 31 Mar 2026 10:07:08 +0530 Subject: [PATCH 1063/2758] media: qcom: venus: relax encoder frame/blur dimension steps on v4 [ Upstream commit 35428ae3a6a40912f1f7b47bb6c65f1a63d0b8f8 ] Encoder HFI capabilities on v4 advertise a 16-pixel step for frame and blur dimensions. This is overly restrictive and can cause userspace caps negotiation to fail even for valid resolutions. Relax the advertised step size to 1 and keep alignment enforcement in buffer layout and size calculations. Fixes: 8b88cabef404e ("media: venus: hfi_plat_v4: Populate codecs and capabilities for v4") Signed-off-by: Renjiang Han Reviewed-by: Dikshita Agarwal Signed-off-by: Bryan O'Donoghue Signed-off-by: Sasha Levin --- .../platform/qcom/venus/hfi_platform_v4.c | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/media/platform/qcom/venus/hfi_platform_v4.c b/drivers/media/platform/qcom/venus/hfi_platform_v4.c index cda888b56b5d4..e0b3652bb4409 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform_v4.c +++ b/drivers/media/platform/qcom/venus/hfi_platform_v4.c @@ -136,8 +136,8 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_H264, .domain = VIDC_SESSION_TYPE_ENC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, @@ -173,8 +173,8 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_HEVC, .domain = VIDC_SESSION_TYPE_ENC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, @@ -195,8 +195,8 @@ static const struct hfi_plat_caps caps[] = { .caps[19] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, .caps[20] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, .caps[21] = {HFI_CAPABILITY_ROTATION, 1, 4, 90}, - .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16}, - .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16}, + .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 1}, + .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 1}, .num_caps = 24, .pl[0] = {HFI_HEVC_PROFILE_MAIN, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, .pl[1] = {HFI_HEVC_PROFILE_MAIN10, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, @@ -210,8 +210,8 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_VP8, .domain = VIDC_SESSION_TYPE_ENC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, @@ -229,8 +229,8 @@ static const struct hfi_plat_caps caps[] = { .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 127, 1}, .caps[17] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1}, .caps[18] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, - .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16}, - .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16}, + .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 1}, + .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 1}, .caps[21] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, .caps[22] = {HFI_CAPABILITY_ROTATION, 1, 4, 90}, .num_caps = 23, From 4d94725aef73e83102eac726bc92ec4498042733 Mon Sep 17 00:00:00 2001 From: Renjiang Han Date: Tue, 31 Mar 2026 10:07:09 +0530 Subject: [PATCH 1064/2758] media: qcom: venus: relax encoder frame/blur step size on v6 [ Upstream commit c53e0550288b2e08b984b24035c471941b7820c7 ] Encoder HFI capabilities on v6 enforce a 16-pixel step for frame and blur dimensions, which does not reflect actual hardware requirements and can reject valid userspace configurations. Relax the step size to 1 while leaving min/max limits unchanged. Fixes: 869d77e706290 ("media: venus: hfi_plat_v6: Populate capabilities for v6") Signed-off-by: Renjiang Han Reviewed-by: Dikshita Agarwal Signed-off-by: Bryan O'Donoghue Signed-off-by: Sasha Levin --- .../media/platform/qcom/venus/hfi_platform_v6.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/qcom/venus/hfi_platform_v6.c b/drivers/media/platform/qcom/venus/hfi_platform_v6.c index d8568c08cc361..fb8d10ab34043 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform_v6.c +++ b/drivers/media/platform/qcom/venus/hfi_platform_v6.c @@ -173,8 +173,8 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_HEVC, .domain = VIDC_SESSION_TYPE_ENC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 8192, 16}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 8192, 16}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 8192, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 8192, 1}, .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 138240, 1}, .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 160000000, 1}, .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, @@ -195,8 +195,8 @@ static const struct hfi_plat_caps caps[] = { .caps[19] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, .caps[20] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, .caps[21] = {HFI_CAPABILITY_ROTATION, 1, 4, 90}, - .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16}, - .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16}, + .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 1}, + .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 1}, .num_caps = 24, .pl[0] = {HFI_HEVC_PROFILE_MAIN, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, .pl[1] = {HFI_HEVC_PROFILE_MAIN10, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, @@ -210,8 +210,8 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_VP8, .domain = VIDC_SESSION_TYPE_ENC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 4096, 16}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 4096, 16}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 4096, 1}, .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 36864, 1}, .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 74000000, 1}, .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, @@ -229,8 +229,8 @@ static const struct hfi_plat_caps caps[] = { .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 127, 1}, .caps[17] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1}, .caps[18] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, - .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16}, - .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16}, + .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 1}, + .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 1}, .caps[21] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, .caps[22] = {HFI_CAPABILITY_ROTATION, 1, 4, 90}, .num_caps = 23, From 3dcce74e455d1489fed4272cfc65271ff8dc9daa Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Tue, 5 May 2026 15:37:21 +0200 Subject: [PATCH 1065/2758] amba: use generic driver_override infrastructure [ Upstream commit 1947229f5f2a8d4ecf8c971aca68a1242bb7b37c ] When a driver is probed through __driver_attach(), the bus' match() callback is called without the device lock held, thus accessing the driver_override field without a lock, which can cause a UAF. Fix this by using the driver-core driver_override infrastructure taking care of proper locking internally. Note that calling match() from __driver_attach() without the device lock held is intentional. [1] Link: https://lore.kernel.org/driver-core/DGRGTIRHA62X.3RY09D9SOK77P@kernel.org/ [1] Reported-by: Gui-Dong Han Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220789 Fixes: 3cf385713460 ("ARM: 8256/1: driver coamba: add device binding path 'driver_override'") Reviewed-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20260505133935.3772495-2-dakr@kernel.org Signed-off-by: Danilo Krummrich Signed-off-by: Sasha Levin --- drivers/amba/bus.c | 37 ++++++------------------------------- include/linux/amba/bus.h | 5 ----- 2 files changed, 6 insertions(+), 36 deletions(-) diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index 6d479caf89cbd..d721d64a98580 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -82,33 +82,6 @@ static void amba_put_disable_pclk(struct amba_device *pcdev) } -static ssize_t driver_override_show(struct device *_dev, - struct device_attribute *attr, char *buf) -{ - struct amba_device *dev = to_amba_device(_dev); - ssize_t len; - - device_lock(_dev); - len = sprintf(buf, "%s\n", dev->driver_override); - device_unlock(_dev); - return len; -} - -static ssize_t driver_override_store(struct device *_dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct amba_device *dev = to_amba_device(_dev); - int ret; - - ret = driver_set_override(_dev, &dev->driver_override, buf, count); - if (ret) - return ret; - - return count; -} -static DEVICE_ATTR_RW(driver_override); - #define amba_attr_func(name,fmt,arg...) \ static ssize_t name##_show(struct device *_dev, \ struct device_attribute *attr, char *buf) \ @@ -126,7 +99,6 @@ amba_attr_func(resource, "\t%016llx\t%016llx\t%016lx\n", static struct attribute *amba_dev_attrs[] = { &dev_attr_id.attr, &dev_attr_resource.attr, - &dev_attr_driver_override.attr, NULL, }; ATTRIBUTE_GROUPS(amba_dev); @@ -209,10 +181,11 @@ static int amba_match(struct device *dev, const struct device_driver *drv) { struct amba_device *pcdev = to_amba_device(dev); const struct amba_driver *pcdrv = to_amba_driver(drv); + int ret; mutex_lock(&pcdev->periphid_lock); if (!pcdev->periphid) { - int ret = amba_read_periphid(pcdev); + ret = amba_read_periphid(pcdev); /* * Returning any error other than -EPROBE_DEFER from bus match @@ -230,8 +203,9 @@ static int amba_match(struct device *dev, const struct device_driver *drv) mutex_unlock(&pcdev->periphid_lock); /* When driver_override is set, only bind to the matching driver */ - if (pcdev->driver_override) - return !strcmp(pcdev->driver_override, drv->name); + ret = device_match_driver_override(dev, drv); + if (ret >= 0) + return ret; return amba_lookup(pcdrv->id_table, pcdev) != NULL; } @@ -436,6 +410,7 @@ static const struct dev_pm_ops amba_pm = { const struct bus_type amba_bustype = { .name = "amba", .dev_groups = amba_dev_groups, + .driver_override = true, .match = amba_match, .uevent = amba_uevent, .probe = amba_probe, diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h index 9946276aff737..6c54d5c0d21f7 100644 --- a/include/linux/amba/bus.h +++ b/include/linux/amba/bus.h @@ -71,11 +71,6 @@ struct amba_device { unsigned int cid; struct amba_cs_uci_id uci; unsigned int irq[AMBA_NR_IRQS]; - /* - * Driver name to force a match. Do not set directly, because core - * frees it. Use driver_set_override() to set or clear it. - */ - const char *driver_override; }; struct amba_driver { From 3666f28016719f1476373b9b1c689d835e95718d Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Tue, 5 May 2026 15:37:22 +0200 Subject: [PATCH 1066/2758] cdx: use generic driver_override infrastructure [ Upstream commit d541aa1897f67f4f14c805785bff894bcc61dca1 ] When a driver is probed through __driver_attach(), the bus' match() callback is called without the device lock held, thus accessing the driver_override field without a lock, which can cause a UAF. Fix this by using the driver-core driver_override infrastructure taking care of proper locking internally. Note that calling match() from __driver_attach() without the device lock held is intentional. [1] Link: https://lore.kernel.org/driver-core/DGRGTIRHA62X.3RY09D9SOK77P@kernel.org/ [1] Reported-by: Gui-Dong Han Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220789 Fixes: 2959ab247061 ("cdx: add the cdx bus driver") Reviewed-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20260505133935.3772495-3-dakr@kernel.org Signed-off-by: Danilo Krummrich Signed-off-by: Sasha Levin --- drivers/cdx/cdx.c | 40 +++++-------------------------------- include/linux/cdx/cdx_bus.h | 4 ---- 2 files changed, 5 insertions(+), 39 deletions(-) diff --git a/drivers/cdx/cdx.c b/drivers/cdx/cdx.c index 9196dc50a48db..d3d2302472621 100644 --- a/drivers/cdx/cdx.c +++ b/drivers/cdx/cdx.c @@ -156,8 +156,6 @@ static int cdx_unregister_device(struct device *dev, } else { cdx_destroy_res_attr(cdx_dev, MAX_CDX_DEV_RESOURCES); debugfs_remove_recursive(cdx_dev->debugfs_dir); - kfree(cdx_dev->driver_override); - cdx_dev->driver_override = NULL; } /* @@ -268,6 +266,7 @@ static int cdx_bus_match(struct device *dev, const struct device_driver *drv) const struct cdx_driver *cdx_drv = to_cdx_driver(drv); const struct cdx_device_id *found_id = NULL; const struct cdx_device_id *ids; + int ret; if (cdx_dev->is_bus) return false; @@ -275,7 +274,8 @@ static int cdx_bus_match(struct device *dev, const struct device_driver *drv) ids = cdx_drv->match_id_table; /* When driver_override is set, only bind to the matching driver */ - if (cdx_dev->driver_override && strcmp(cdx_dev->driver_override, drv->name)) + ret = device_match_driver_override(dev, drv); + if (ret == 0) return false; found_id = cdx_match_id(ids, cdx_dev); @@ -289,7 +289,7 @@ static int cdx_bus_match(struct device *dev, const struct device_driver *drv) */ if (!found_id->override_only) return true; - if (cdx_dev->driver_override) + if (ret > 0) return true; ids = found_id + 1; @@ -453,36 +453,6 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(modalias); -static ssize_t driver_override_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cdx_device *cdx_dev = to_cdx_device(dev); - int ret; - - if (WARN_ON(dev->bus != &cdx_bus_type)) - return -EINVAL; - - ret = driver_set_override(dev, &cdx_dev->driver_override, buf, count); - if (ret) - return ret; - - return count; -} - -static ssize_t driver_override_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct cdx_device *cdx_dev = to_cdx_device(dev); - ssize_t len; - - device_lock(dev); - len = sysfs_emit(buf, "%s\n", cdx_dev->driver_override); - device_unlock(dev); - return len; -} -static DEVICE_ATTR_RW(driver_override); - static ssize_t enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -552,7 +522,6 @@ static struct attribute *cdx_dev_attrs[] = { &dev_attr_class.attr, &dev_attr_revision.attr, &dev_attr_modalias.attr, - &dev_attr_driver_override.attr, NULL, }; @@ -646,6 +615,7 @@ ATTRIBUTE_GROUPS(cdx_bus); const struct bus_type cdx_bus_type = { .name = "cdx", + .driver_override = true, .match = cdx_bus_match, .probe = cdx_probe, .remove = cdx_remove, diff --git a/include/linux/cdx/cdx_bus.h b/include/linux/cdx/cdx_bus.h index b1ba97f6c9ad6..f54770f110bc8 100644 --- a/include/linux/cdx/cdx_bus.h +++ b/include/linux/cdx/cdx_bus.h @@ -137,9 +137,6 @@ struct cdx_controller { * @enabled: is this bus enabled * @msi_dev_id: MSI Device ID associated with CDX device * @num_msi: Number of MSI's supported by the device - * @driver_override: driver name to force a match; do not set directly, - * because core frees it; use driver_set_override() to - * set or clear it. * @irqchip_lock: lock to synchronize irq/msi configuration * @msi_write_pending: MSI write pending for this device */ @@ -165,7 +162,6 @@ struct cdx_device { bool enabled; u32 msi_dev_id; u32 num_msi; - const char *driver_override; struct mutex irqchip_lock; bool msi_write_pending; }; From 2fb010ea7ca98cd09a6d365260b769f627e73a76 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Tue, 5 May 2026 15:37:23 +0200 Subject: [PATCH 1067/2758] Drivers: hv: vmbus: use generic driver_override infrastructure [ Upstream commit 331d8900121a1d74ecd45cd2db742ddcb5a0a565 ] When a driver is probed through __driver_attach(), the bus' match() callback is called without the device lock held, thus accessing the driver_override field without a lock, which can cause a UAF. Fix this by using the driver-core driver_override infrastructure taking care of proper locking internally. Note that calling match() from __driver_attach() without the device lock held is intentional. [1] Tested-by: Michael Kelley Reviewed-by: Michael Kelley Link: https://lore.kernel.org/driver-core/DGRGTIRHA62X.3RY09D9SOK77P@kernel.org/ [1] Reported-by: Gui-Dong Han Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220789 Fixes: d765edbb301c ("vmbus: add driver_override support") Reviewed-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20260505133935.3772495-4-dakr@kernel.org Signed-off-by: Danilo Krummrich Signed-off-by: Sasha Levin --- drivers/hv/vmbus_drv.c | 43 ++++++++++-------------------------------- include/linux/hyperv.h | 5 ----- 2 files changed, 10 insertions(+), 38 deletions(-) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index b80a35c778ab4..23206640c6139 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -548,34 +548,6 @@ static ssize_t device_show(struct device *dev, } static DEVICE_ATTR_RO(device); -static ssize_t driver_override_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct hv_device *hv_dev = device_to_hv_device(dev); - int ret; - - ret = driver_set_override(dev, &hv_dev->driver_override, buf, count); - if (ret) - return ret; - - return count; -} - -static ssize_t driver_override_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct hv_device *hv_dev = device_to_hv_device(dev); - ssize_t len; - - device_lock(dev); - len = sysfs_emit(buf, "%s\n", hv_dev->driver_override); - device_unlock(dev); - - return len; -} -static DEVICE_ATTR_RW(driver_override); - /* Set up per device attributes in /sys/bus/vmbus/devices/ */ static struct attribute *vmbus_dev_attrs[] = { &dev_attr_id.attr, @@ -606,7 +578,6 @@ static struct attribute *vmbus_dev_attrs[] = { &dev_attr_channel_vp_mapping.attr, &dev_attr_vendor.attr, &dev_attr_device.attr, - &dev_attr_driver_override.attr, NULL, }; @@ -718,9 +689,11 @@ static const struct hv_vmbus_device_id *hv_vmbus_get_id(const struct hv_driver * { const guid_t *guid = &dev->dev_type; const struct hv_vmbus_device_id *id; + int ret; - /* When driver_override is set, only bind to the matching driver */ - if (dev->driver_override && strcmp(dev->driver_override, drv->name)) + /* If a driver override is set, only bind to the matching driver */ + ret = device_match_driver_override(&dev->device, &drv->driver); + if (ret == 0) return NULL; /* Look at the dynamic ids first, before the static ones */ @@ -728,8 +701,11 @@ static const struct hv_vmbus_device_id *hv_vmbus_get_id(const struct hv_driver * if (!id) id = hv_vmbus_dev_match(drv->id_table, guid); - /* driver_override will always match, send a dummy id */ - if (!id && dev->driver_override) + /* + * If there's a matching driver override, this function should succeed, + * thus return a dummy device ID if no matching ID is found. + */ + if (!id && ret > 0) id = &vmbus_device_null; return id; @@ -1031,6 +1007,7 @@ static const struct dev_pm_ops vmbus_pm = { /* The one and only one */ static const struct bus_type hv_bus = { .name = "vmbus", + .driver_override = true, .match = vmbus_match, .shutdown = vmbus_shutdown, .remove = vmbus_remove, diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 734b7ef98f4d2..9de2c8d6037aa 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1272,11 +1272,6 @@ struct hv_device { u16 device_id; struct device device; - /* - * Driver name to force a match. Do not set directly, because core - * frees it. Use driver_set_override() to set or clear it. - */ - const char *driver_override; struct vmbus_channel *channel; struct kset *channels_kset; From 62797f0d044ce0573ebdddcf77d2fadffdf3e177 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Tue, 5 May 2026 15:37:24 +0200 Subject: [PATCH 1068/2758] rpmsg: use generic driver_override infrastructure [ Upstream commit 55ced13c42921714e90f8fae94b6ed803330dc6a ] When a driver is probed through __driver_attach(), the bus' match() callback is called without the device lock held, thus accessing the driver_override field without a lock, which can cause a UAF. Fix this by using the driver-core driver_override infrastructure taking care of proper locking internally. Note that calling match() from __driver_attach() without the device lock held is intentional. [1] Link: https://lore.kernel.org/driver-core/DGRGTIRHA62X.3RY09D9SOK77P@kernel.org/ [1] Reported-by: Gui-Dong Han Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220789 Fixes: e95060478244 ("rpmsg: Introduce a driver override mechanism") Reviewed-by: Mathieu Poirier Reviewed-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20260505133935.3772495-5-dakr@kernel.org Signed-off-by: Danilo Krummrich Signed-off-by: Sasha Levin --- drivers/rpmsg/qcom_glink_native.c | 2 -- drivers/rpmsg/rpmsg_core.c | 43 +++++-------------------------- drivers/rpmsg/virtio_rpmsg_bus.c | 1 - include/linux/rpmsg.h | 4 --- 4 files changed, 7 insertions(+), 43 deletions(-) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 401a4ece0c977..d9d4468e4cbdf 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -1626,7 +1626,6 @@ static void qcom_glink_rpdev_release(struct device *dev) { struct rpmsg_device *rpdev = to_rpmsg_device(dev); - kfree(rpdev->driver_override); kfree(rpdev); } @@ -1862,7 +1861,6 @@ static void qcom_glink_device_release(struct device *dev) /* Release qcom_glink_alloc_channel() reference */ kref_put(&channel->refcount, qcom_glink_channel_release); - kfree(rpdev->driver_override); kfree(rpdev); } diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c index e7f7831d37f89..c56f69c22e420 100644 --- a/drivers/rpmsg/rpmsg_core.c +++ b/drivers/rpmsg/rpmsg_core.c @@ -358,33 +358,6 @@ rpmsg_show_attr(src, src, "0x%x\n"); rpmsg_show_attr(dst, dst, "0x%x\n"); rpmsg_show_attr(announce, announce ? "true" : "false", "%s\n"); -static ssize_t driver_override_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct rpmsg_device *rpdev = to_rpmsg_device(dev); - int ret; - - ret = driver_set_override(dev, &rpdev->driver_override, buf, count); - if (ret) - return ret; - - return count; -} - -static ssize_t driver_override_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct rpmsg_device *rpdev = to_rpmsg_device(dev); - ssize_t len; - - device_lock(dev); - len = sysfs_emit(buf, "%s\n", rpdev->driver_override); - device_unlock(dev); - return len; -} -static DEVICE_ATTR_RW(driver_override); - static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -405,7 +378,6 @@ static struct attribute *rpmsg_dev_attrs[] = { &dev_attr_dst.attr, &dev_attr_src.attr, &dev_attr_announce.attr, - &dev_attr_driver_override.attr, NULL, }; ATTRIBUTE_GROUPS(rpmsg_dev); @@ -424,9 +396,11 @@ static int rpmsg_dev_match(struct device *dev, const struct device_driver *drv) const struct rpmsg_driver *rpdrv = to_rpmsg_driver(drv); const struct rpmsg_device_id *ids = rpdrv->id_table; unsigned int i; + int ret; - if (rpdev->driver_override) - return !strcmp(rpdev->driver_override, drv->name); + ret = device_match_driver_override(dev, drv); + if (ret >= 0) + return ret; if (ids) for (i = 0; ids[i].name[0]; i++) @@ -535,6 +509,7 @@ static const struct bus_type rpmsg_bus = { .name = "rpmsg", .match = rpmsg_dev_match, .dev_groups = rpmsg_dev_groups, + .driver_override = true, .uevent = rpmsg_uevent, .probe = rpmsg_dev_probe, .remove = rpmsg_dev_remove, @@ -560,11 +535,9 @@ int rpmsg_register_device_override(struct rpmsg_device *rpdev, device_initialize(dev); if (driver_override) { - ret = driver_set_override(dev, &rpdev->driver_override, - driver_override, - strlen(driver_override)); + ret = device_set_driver_override(dev, driver_override); if (ret) { - dev_err(dev, "device_set_override failed: %d\n", ret); + dev_err(dev, "device_set_driver_override() failed: %d\n", ret); put_device(dev); return ret; } @@ -573,8 +546,6 @@ int rpmsg_register_device_override(struct rpmsg_device *rpdev, ret = device_add(dev); if (ret) { dev_err(dev, "device_add failed: %d\n", ret); - kfree(rpdev->driver_override); - rpdev->driver_override = NULL; put_device(dev); } diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c index 5ae15111fb4f1..1b8bb05924af7 100644 --- a/drivers/rpmsg/virtio_rpmsg_bus.c +++ b/drivers/rpmsg/virtio_rpmsg_bus.c @@ -374,7 +374,6 @@ static void virtio_rpmsg_release_device(struct device *dev) struct rpmsg_device *rpdev = to_rpmsg_device(dev); struct virtio_rpmsg_channel *vch = to_virtio_rpmsg_channel(rpdev); - kfree(rpdev->driver_override); kfree(vch); } diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h index 83266ce146420..2e40eb54155e0 100644 --- a/include/linux/rpmsg.h +++ b/include/linux/rpmsg.h @@ -41,9 +41,6 @@ struct rpmsg_channel_info { * rpmsg_device - device that belong to the rpmsg bus * @dev: the device struct * @id: device id (used to match between rpmsg drivers and devices) - * @driver_override: driver name to force a match; do not set directly, - * because core frees it; use driver_set_override() to - * set or clear it. * @src: local address * @dst: destination address * @ept: the rpmsg endpoint of this channel @@ -53,7 +50,6 @@ struct rpmsg_channel_info { struct rpmsg_device { struct device dev; struct rpmsg_device_id id; - const char *driver_override; u32 src; u32 dst; struct rpmsg_endpoint *ept; From 4932ed4d1bba38e51962073dbbe124789ddd58e8 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 14 May 2026 14:53:06 +0200 Subject: [PATCH 1069/2758] arm64: dts: renesas: r8a78000: Fix GIC-720AE View 1 Redistributor description MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 54613573ff1495b928e2841b257c081a58566901 ] The Renesas R-Car X5H (R8A78000) SoC contains Arm CoreLink GIC-720AE Generic Interrupt Controller with Multi View capability. Firmware has access to configuration View 0, Linux kernel has access to View 1. The Arm CoreLink GIC-720AE Generic Interrupt Controller Technical Reference Manual, currently latest r2p1 [1], chapter "5. Programmers model for GIC-720AE", subchapter "5.4 Redistributor registers for control and physical LPIs summary", part "5.4.3 GICR_TYPER, Redistributor Type Register", "Table 5-50: GICR_TYPER bit descriptions" on page 200, clarifies register "GICR_TYPER" bit 4 "Last" behavior in Multi View setup as follows: Last Last Redistributor: 0 ... This Redistributor is not the last Redistributor on the chip. 1 ... This Redistributor is the last Redistributor on the chip. When GICD_CFGID.VIEW == 1, for views 1, 2, or 3 this bit always returns 1. On this SoC, GICD_CFGID.VIEW is 1 and the Linux kernel has access to View 1, therefore Linux kernel GICv3 driver will interpret register "GICR_TYPER" bit 4 "Last" = 1 in the first Redistributor in continuous Redistributor page as that first Redistributor being the one and only Redistributor and will stop processing the continuous Redistributor page further. This will prevent the other Redistributors from being recognized by the system and used for other PEs. Because the hardware indicates that the continuous Redistributor page is not continuous for View 1, 2, or 3, describe every Redistributor separately in the DT. This makes all Redistributors for all cores accessible in Linux. [1] https://documentation-service.arm.com/static/69ef3c1cd35efd294e335c43 Arm® CoreLink™ GIC-720AE Generic Interrupt Controller Revision: r2p1 / Issue 12 / 102666_0201_12_en Fixes: 63500d12cf76 ("arm64: dts: renesas: Add R8A78000 SoC support") Acked-by: Marc Zyngier Signed-off-by: Marek Vasut Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260514125328.20954-1-marek.vasut+renesas@mailbox.org Signed-off-by: Geert Uytterhoeven Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/renesas/r8a78000.dtsi | 36 +++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/renesas/r8a78000.dtsi b/arch/arm64/boot/dts/renesas/r8a78000.dtsi index 3ec1b53d27828..73be51787265c 100644 --- a/arch/arm64/boot/dts/renesas/r8a78000.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a78000.dtsi @@ -689,8 +689,40 @@ #interrupt-cells = <3>; #address-cells = <0>; interrupt-controller; - reg = <0 0x39000000 0 0x10000>, - <0 0x39080000 0 0x800000>; + #redistributor-regions = <32>; + reg = <0x0 0x39000000 0x0 0x10000>, + <0x0 0x39080000 0x0 0x40000>, + <0x0 0x390c0000 0x0 0x40000>, + <0x0 0x39100000 0x0 0x40000>, + <0x0 0x39140000 0x0 0x40000>, + <0x0 0x39180000 0x0 0x40000>, + <0x0 0x391c0000 0x0 0x40000>, + <0x0 0x39200000 0x0 0x40000>, + <0x0 0x39240000 0x0 0x40000>, + <0x0 0x39280000 0x0 0x40000>, + <0x0 0x392c0000 0x0 0x40000>, + <0x0 0x39300000 0x0 0x40000>, + <0x0 0x39340000 0x0 0x40000>, + <0x0 0x39380000 0x0 0x40000>, + <0x0 0x393c0000 0x0 0x40000>, + <0x0 0x39400000 0x0 0x40000>, + <0x0 0x39440000 0x0 0x40000>, + <0x0 0x39480000 0x0 0x40000>, + <0x0 0x394c0000 0x0 0x40000>, + <0x0 0x39500000 0x0 0x40000>, + <0x0 0x39540000 0x0 0x40000>, + <0x0 0x39580000 0x0 0x40000>, + <0x0 0x395c0000 0x0 0x40000>, + <0x0 0x39600000 0x0 0x40000>, + <0x0 0x39640000 0x0 0x40000>, + <0x0 0x39680000 0x0 0x40000>, + <0x0 0x396c0000 0x0 0x40000>, + <0x0 0x39700000 0x0 0x40000>, + <0x0 0x39740000 0x0 0x40000>, + <0x0 0x39780000 0x0 0x40000>, + <0x0 0x397c0000 0x0 0x40000>, + <0x0 0x39800000 0x0 0x40000>, + <0x0 0x39840000 0x0 0x40000>; interrupts = ; }; From 00a6d9771de05b03f7efe82033a3dded4a16caaf Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 17 May 2026 18:31:25 +0200 Subject: [PATCH 1070/2758] arm64: dts: renesas: ironhide: Describe all reserved memory [ Upstream commit 5250b3b1ad99d216b31c98b2b321f1dee362a1b5 ] Fully describe all available DRAM in the DT, and describe regions which are not accessible because they are used by firmware in reserved-memory nodes. Replace the first memory bank memory@60600000 with memory@40000000 and a 518 MiB long reserved-memory no-map subnode. This memory region is used by other cores in the system. Reserve 32 kiB of memory at 0x8c100000 for parameters shared by IPL, SCP, TFA BL31 and TEE. Reserve 512 kiB of memory at 0x8c200000 for TFA BL31. The upcoming upstream TFA 2.15 BL31 uses memory from 0x8c200000..0x8c242fff; rounding up to 512 kiB is slight future-proofing. Reserve 32 MiB of memory at 0x8c400000 for OPTEE-OS, which is the entire OPTEE-OS TZ protected DRAM area. Neither TFA BL31 nor OPTEE-OS modify the DT passed to Linux in any way with any new reserved-memory {} nodes to reserve memory areas used by the TFA BL31 or OPTEE-OS to prevent the next stage from using those areas, which lets Linux use all of the available DRAM as it is described in the DT that was passed in by U-Boot, including the areas that are newly utilized by TFA BL31 or OPTEE-OS. In case of high DRAM utilization, unless the memory used by TFA BL31 or OPTEE-OS is properly reserved, Linux may use and corrupt the memory used by TFA BL31 or OPTEE-OS, which would lead to the system becoming unresponsive. Fixes: ad142a4ef710 ("arm64: dts: renesas: r8a78000: Add initial Ironhide board support") Signed-off-by: Marek Vasut Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260517163212.18016-1-marek.vasut+renesas@mailbox.org Signed-off-by: Geert Uytterhoeven Signed-off-by: Sasha Levin --- .../boot/dts/renesas/r8a78000-ironhide.dts | 35 +++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts b/arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts index a721734fbd5d0..ed027a6c356ed 100644 --- a/arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts +++ b/arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts @@ -20,10 +20,9 @@ stdout-path = "serial0:1843200n8"; }; - memory@60600000 { + memory@40000000 { device_type = "memory"; - /* first 518MiB is reserved for other purposes. */ - reg = <0x0 0x60600000 0x0 0x5fa00000>; + reg = <0x0 0x40000000 0x0 0x80000000>; }; memory@1080000000 { @@ -65,6 +64,36 @@ device_type = "memory"; reg = <0x1e 0x00000000 0x1 0x00000000>; }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + /* First 518 MiB is reserved for other purposes. */ + firmware@40000000 { + reg = <0x0 0x40000000 0x0 0x20600000>; + no-map; + }; + + /* Parameters set by IPL. */ + parameters@8c100000 { + reg = <0x0 0x8c100000 0x0 0x00008000>; + no-map; + }; + + /* TFA BL31. */ + tfa-bl31@8c200000 { + reg = <0x0 0x8c200000 0x0 0x00080000>; + no-map; + }; + + /* TEE TZ DRAM. */ + tee@8c400000 { + reg = <0x0 0x8c400000 0x0 0x02000000>; + no-map; + }; + }; }; &extal_clk { From eb04e3e9c14ed15914f5fd2eae8b6435f54f095f Mon Sep 17 00:00:00 2001 From: Chen Cheng Date: Fri, 15 May 2026 17:30:19 +0800 Subject: [PATCH 1071/2758] md/raid10: reset read_slot when reusing r10bio for discard [ Upstream commit 6b8a26af065ddc93de2aa5c9f0df98dce9723442 ] put_all_bios() always drops devs[i].bio, but it only drops devs[i].repl_bio when r10_bio->read_slot < 0. If discard reuses an r10bio that was previously used for a read, read_slot can still be non-negative, and discard cleanup can skip bio_put() on repl_bio. Reset read_slot to -1 when preparing an r10bio for discard so the replacement bio is always released correctly. Fixes: d30588b2731f ("md/raid10: improve raid10 discard request") Signed-off-by: Chen Cheng Reviewed-by: Xiao Ni Link: https://patch.msgid.link/20260515093019.3436882-1-chencheng@fnnas.com Signed-off-by: Yu Kuai Signed-off-by: Sasha Levin --- drivers/md/raid10.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 39085e7dd6d26..7dc2a5a127e89 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1727,6 +1727,7 @@ static int raid10_handle_discard(struct mddev *mddev, struct bio *bio) r10_bio->mddev = mddev; r10_bio->state = 0; r10_bio->sectors = 0; + r10_bio->read_slot = -1; memset(r10_bio->devs, 0, sizeof(r10_bio->devs[0]) * geo->raid_disks); wait_blocked_dev(mddev, r10_bio); From 38777f014c37972868f1733a651c85288bb8a757 Mon Sep 17 00:00:00 2001 From: Abd-Alrhman Masalkhi Date: Fri, 1 May 2026 13:46:49 +0200 Subject: [PATCH 1072/2758] md/raid1,raid10: fix deadlock in read error recovery path [ Upstream commit 7b15c24f805339a585cfe7d72f446b7e88b9bcc0 ] raid1d and raid10d may resubmit a split md cloned bio while handling a read error. In this case, resubmitting the bio can lead to a deadlock if the array is suspended before md_handle_request() acquires an active_io reference via percpu_ref_tryget_live(). Since the cloned bio already holds an active_io reference, trying to acquire another reference via percpu_ref_tryget_live() can lead to a deadlock while the array is suspended. Fix this by using percpu_ref_get() for md cloned bios. Fixes: bb2a9acefaf9 ("md/raid1: switch to use md_account_bio() for io accounting") Fixes: 820455238366 ("md/raid10: switch to use md_account_bio() for io accounting") Signed-off-by: Abd-Alrhman Masalkhi Reviewed-by: Xiao Ni Reviewed-by: Yu Kuai Link: https://patch.msgid.link/20260501114652.590037-2-abd.masalkhi@gmail.com Signed-off-by: Yu Kuai Signed-off-by: Sasha Levin --- drivers/md/md.c | 25 ++++++++++++++++--------- drivers/md/md.h | 5 +++++ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/drivers/md/md.c b/drivers/md/md.c index 8b568eee87433..518d1d9c22031 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -395,17 +395,24 @@ static bool is_suspended(struct mddev *mddev, struct bio *bio) bool md_handle_request(struct mddev *mddev, struct bio *bio) { check_suspended: - if (is_suspended(mddev, bio)) { - /* Bail out if REQ_NOWAIT is set for the bio */ - if (bio->bi_opf & REQ_NOWAIT) { - bio_wouldblock_error(bio); - return true; + if (unlikely(md_cloned_bio(mddev, bio))) { + /* + * This bio is an MD cloned bio and already holds an + * active_io reference, so percpu_ref_get() is safe here. + */ + percpu_ref_get(&mddev->active_io); + } else { + if (is_suspended(mddev, bio)) { + /* Bail out if REQ_NOWAIT is set for the bio */ + if (bio->bi_opf & REQ_NOWAIT) { + bio_wouldblock_error(bio); + return true; + } + wait_event(mddev->sb_wait, !is_suspended(mddev, bio)); } - wait_event(mddev->sb_wait, !is_suspended(mddev, bio)); + if (!percpu_ref_tryget_live(&mddev->active_io)) + goto check_suspended; } - if (!percpu_ref_tryget_live(&mddev->active_io)) - goto check_suspended; - if (!mddev->pers->make_request(mddev, bio)) { percpu_ref_put(&mddev->active_io); if (mddev_is_dm(mddev) && mddev->pers->prepare_suspend) diff --git a/drivers/md/md.h b/drivers/md/md.h index 52c3780860464..aca5c06e5fe31 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -1042,6 +1042,11 @@ void mddev_update_io_opt(struct mddev *mddev, unsigned int nr_stripes); extern const struct block_device_operations md_fops; +static inline bool md_cloned_bio(struct mddev *mddev, struct bio *bio) +{ + return bio->bi_pool == &mddev->io_clone_set; +} + /* * MD devices can be used undeneath by DM, in which case ->gendisk is NULL. */ From 20fb582c92fd64e5c8bd83c6176264b2794603b7 Mon Sep 17 00:00:00 2001 From: Abd-Alrhman Masalkhi Date: Fri, 1 May 2026 13:46:50 +0200 Subject: [PATCH 1073/2758] md/raid1,raid10: fix error-path detection with md_cloned_bio() [ Upstream commit 811545e0926d02a6a0b1a1258bb5544777c164d4 ] Detect the error path using md_cloned_bio() instead of relying on r1_bio in raid1 or r10_bio->read_slot in raid10, which may be NULL or -1 after splitting and resubmitting a failed bio. As a result, the error path may not be recognized and memory allocations can incorrectly use GFP_NOIO instead of (GFP_NOIO | __GFP_HIGH), which can lead to a deadlock under memory pressure. Fixes: 689389a06ce7 ("md/raid1: simplify handle_read_error().") Fixes: 545250f24809 ("md/raid10: simplify handle_read_error()") Signed-off-by: Abd-Alrhman Masalkhi Reviewed-by: Xiao Ni Link: https://patch.msgid.link/20260501114652.590037-3-abd.masalkhi@gmail.com Signed-off-by: Yu Kuai Signed-off-by: Sasha Levin --- drivers/md/raid1.c | 13 ++++++++++--- drivers/md/raid10.c | 20 ++++++++++++++------ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 64d970e2ef50f..22458df5069e9 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1343,11 +1343,18 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio, bool r1bio_existed = !!r1_bio; /* - * If r1_bio is set, we are blocking the raid1d thread - * so there is a tiny risk of deadlock. So ask for + * An md cloned bio indicates we are in the error path. + * This is more reliable than checking r1_bio, which might + * be NULL even in the error path if a failed bio was split. + */ + bool err_path = md_cloned_bio(mddev, bio); + + /* + * If we are in the error path, we are blocking the raid1d + * thread so there is a tiny risk of deadlock. So ask for * emergency memory if needed. */ - gfp_t gfp = r1_bio ? (GFP_NOIO | __GFP_HIGH) : GFP_NOIO; + gfp_t gfp = err_path ? (GFP_NOIO | __GFP_HIGH) : GFP_NOIO; /* * Still need barrier for READ in case that whole diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 7dc2a5a127e89..b38a0ccd4661a 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1155,7 +1155,20 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio, char b[BDEVNAME_SIZE]; int slot = r10_bio->read_slot; struct md_rdev *err_rdev = NULL; - gfp_t gfp = GFP_NOIO; + + /* + * An md cloned bio indicates we are in the error path. + * This is more reliable than checking slot, which might + * be -1 even in the error path if a failed bio was split. + */ + bool err_path = md_cloned_bio(mddev, bio); + + /* + * If we are in the error path, we are blocking the raid10d + * thread so there is a tiny risk of deadlock. So ask for + * emergency memory if needed. + */ + gfp_t gfp = err_path ? (GFP_NOIO | __GFP_HIGH) : GFP_NOIO; if (slot >= 0 && r10_bio->devs[slot].rdev) { /* @@ -1166,11 +1179,6 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio, * we lose the device name in error messages. */ int disk; - /* - * As we are blocking raid10, it is a little safer to - * use __GFP_HIGH. - */ - gfp = GFP_NOIO | __GFP_HIGH; disk = r10_bio->devs[slot].devnum; err_rdev = conf->mirrors[disk].rdev; From bb8d7ea74206b419aa05d7773a15a923b668c9f0 Mon Sep 17 00:00:00 2001 From: Abd-Alrhman Masalkhi Date: Fri, 1 May 2026 13:46:51 +0200 Subject: [PATCH 1074/2758] md/raid1,raid10: fix bio accounting for split md cloned bios [ Upstream commit ba976e3501111d11c550848b3b7341a73035f582 ] Use md_cloned_bio() to control bio accounting instead of relying on r1bio_existed in raid1 or the io_accounting flag in raid10. The previous logic does not reliably reflect whether a bio is an md cloned bio. When a failed bio is split and resubmitted via bio_submit_split_bioset() on the error path, this can lead to either double accounting for md cloned bios, or missing accounting for bios returned from bio_submit_split_bioset() Fix this by using md_cloned_bio() to detect md cloned bios and skip accounting accordingly. Fixes: bb2a9acefaf9 ("md/raid1: switch to use md_account_bio() for io accounting") Fixes: 820455238366 ("md/raid10: switch to use md_account_bio() for io accounting") Signed-off-by: Abd-Alrhman Masalkhi Reviewed-by: Xiao Ni Link: https://patch.msgid.link/20260501114652.590037-4-abd.masalkhi@gmail.com Signed-off-by: Yu Kuai Signed-off-by: Sasha Levin --- drivers/md/raid1.c | 2 +- drivers/md/raid10.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 22458df5069e9..603aa09088f05 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1418,7 +1418,7 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio, } r1_bio->read_disk = rdisk; - if (!r1bio_existed) { + if (likely(!md_cloned_bio(mddev, bio))) { md_account_bio(mddev, &bio); r1_bio->master_bio = bio; } diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index b38a0ccd4661a..5bd7698e0a1b5 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1146,7 +1146,7 @@ static bool regular_request_wait(struct mddev *mddev, struct r10conf *conf, } static void raid10_read_request(struct mddev *mddev, struct bio *bio, - struct r10bio *r10_bio, bool io_accounting) + struct r10bio *r10_bio) { struct r10conf *conf = mddev->private; struct bio *read_bio; @@ -1226,7 +1226,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio, } slot = r10_bio->read_slot; - if (io_accounting) { + if (likely(!md_cloned_bio(mddev, bio))) { md_account_bio(mddev, &bio); r10_bio->master_bio = bio; } @@ -1552,7 +1552,7 @@ static void __make_request(struct mddev *mddev, struct bio *bio, int sectors) conf->geo.raid_disks); if (bio_data_dir(bio) == READ) - raid10_read_request(mddev, bio, r10_bio, true); + raid10_read_request(mddev, bio, r10_bio); else raid10_write_request(mddev, bio, r10_bio); } @@ -2867,7 +2867,7 @@ static void handle_read_error(struct mddev *mddev, struct r10bio *r10_bio) rdev_dec_pending(rdev, mddev); r10_bio->state = 0; - raid10_read_request(mddev, r10_bio->master_bio, r10_bio, false); + raid10_read_request(mddev, r10_bio->master_bio, r10_bio); /* * allow_barrier after re-submit to ensure no sync io * can be issued while regular io pending. From 731485617bf862f1289c3f40ed1f800d0475826f Mon Sep 17 00:00:00 2001 From: Abd-Alrhman Masalkhi Date: Sat, 30 May 2026 15:14:11 +0000 Subject: [PATCH 1075/2758] raid1: fix nr_pending leak in REQ_ATOMIC bad-block error path [ Upstream commit 909d9dc3b5730c8ed7b764c68bc788342df2a07b ] In raid1_write_request(), each per-mirror loop iteration begins by incrementing rdev->nr_pending. If a REQ_ATOMIC write encounters a badblock within the requested range, the code jumps to err_handle without dropping the reference taken for the current mirror. err_handle's cleanup loop will only decrements for k < i and r1_bio->bios[k] is non-NULL. The current slot is therefore skipped, leaving its nr_pending reference leaked permanently. The reference prevents the rdev from ever being removed, since raid1_remove_conf() refuses to remove an rdev with nr_pending > 0. Fix this by calling rdev_dec_pending() before jumping to err_handle. Fixes: f2a38abf5f1c ("md/raid1: Atomic write support") Signed-off-by: Abd-Alrhman Masalkhi Link: https://patch.msgid.link/20260530151411.4119-1-abd.masalkhi@gmail.com Signed-off-by: Yu Kuai Signed-off-by: Sasha Levin --- drivers/md/raid1.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 603aa09088f05..1f22250334bca 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1603,8 +1603,10 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, * complexity of supporting that is not worth * the benefit. */ - if (bio->bi_opf & REQ_ATOMIC) + if (bio->bi_opf & REQ_ATOMIC) { + rdev_dec_pending(rdev, mddev); goto err_handle; + } good_sectors = first_bad - r1_bio->sector; if (good_sectors < max_sectors) From d3d630e8a7f3421bc2d204b87bdff35b4432a8e3 Mon Sep 17 00:00:00 2001 From: Yuyang Huang Date: Sun, 31 May 2026 15:55:59 +0800 Subject: [PATCH 1076/2758] bpf: fix BPF_PROG_QUERY OOB write and cgroup backward compat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 21c4b99b27f3f85b89256e81b3e997dec0a460d0 ] BPF_PROG_QUERY writes back the 'query.revision' field unconditionally to userspace. If userspace passes a smaller 'bpf_attr' structure (e.g. 40 bytes, which was the layout before the addition of 'query.revision'), the kernel performs an out-of-bounds write. Fix this by propagating the user-provided attribute size 'uattr_size' down to the cgroup query handlers, and conditionally skipping writing the revision field to userspace when the provided buffer size is insufficient. query.revision in bpf_mprog_query is structurally identical to the cgroup case: a late tail field, written unconditionally. But the backward-compat hazard is not the same. The min-historical-size test is per command, and bpf_mprog_query only serves attach types that were born with revision in the struct: - tcx_prog_query -> BPF_TCX_INGRESS/EGRESS - netkit_prog_query -> BPF_NETKIT_PRIMARY/PEER tcx, netkit, the revision field, and bpf_mprog_query itself all landed in the same v6.6 merge window (053c8e1f235d added the mprog query API + revision; tcx in e420bed02507, netkit in 35dfaad7188c). There has never been a tcx/netkit BPF_PROG_QUERY userspace that doesn't know about revision. So for these commands the minimum legitimate struct already covers offset 56-64 — no old binary can be broken here. Contrast with cgroup: BPF_PROG_QUERY on cgroup attach types shipped in 2017; revision write-back was bolted on years later (120933984460). That path has a real population of pre-revision callers. Fixes: 120933984460 ("bpf: Implement mprog API on top of existing cgroup progs") Cc: Maciej Żenczykowski Cc: Lorenzo Colitti Signed-off-by: Yuyang Huang Link: https://lore.kernel.org/r/20260531075600.4058207-2-yuyanghuang@google.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- include/linux/bpf-cgroup.h | 5 +++-- kernel/bpf/cgroup.c | 13 +++++++------ kernel/bpf/syscall.c | 6 +++--- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/include/linux/bpf-cgroup.h b/include/linux/bpf-cgroup.h index b2e79c2b41d51..4d0cc65976a14 100644 --- a/include/linux/bpf-cgroup.h +++ b/include/linux/bpf-cgroup.h @@ -421,7 +421,7 @@ int cgroup_bpf_prog_detach(const union bpf_attr *attr, enum bpf_prog_type ptype); int cgroup_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog); int cgroup_bpf_prog_query(const union bpf_attr *attr, - union bpf_attr __user *uattr); + union bpf_attr __user *uattr, u32 uattr_size); const struct bpf_func_proto * cgroup_common_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog); @@ -452,7 +452,8 @@ static inline int cgroup_bpf_link_attach(const union bpf_attr *attr, } static inline int cgroup_bpf_prog_query(const union bpf_attr *attr, - union bpf_attr __user *uattr) + union bpf_attr __user *uattr, + u32 uattr_size) { return -EINVAL; } diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index f4eefdacd4533..89ea605457906 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -1208,7 +1208,7 @@ static int cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog, /* Must be called with cgroup_mutex held to avoid races. */ static int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr, - union bpf_attr __user *uattr) + union bpf_attr __user *uattr, u32 uattr_size) { __u32 __user *prog_attach_flags = u64_to_user_ptr(attr->query.prog_attach_flags); bool effective_query = attr->query.query_flags & BPF_F_QUERY_EFFECTIVE; @@ -1259,7 +1259,8 @@ static int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr, return -EFAULT; if (!effective_query && from_atype == to_atype) revision = cgrp->bpf.revisions[from_atype]; - if (copy_to_user(&uattr->query.revision, &revision, sizeof(revision))) + if (uattr_size >= offsetofend(union bpf_attr, query.revision) && + copy_to_user(&uattr->query.revision, &revision, sizeof(revision))) return -EFAULT; if (attr->query.prog_cnt == 0 || !prog_ids || !total_cnt) /* return early if user requested only program count + flags */ @@ -1312,12 +1313,12 @@ static int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr, } static int cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr, - union bpf_attr __user *uattr) + union bpf_attr __user *uattr, u32 uattr_size) { int ret; cgroup_lock(); - ret = __cgroup_bpf_query(cgrp, attr, uattr); + ret = __cgroup_bpf_query(cgrp, attr, uattr, uattr_size); cgroup_unlock(); return ret; } @@ -1520,7 +1521,7 @@ int cgroup_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog) } int cgroup_bpf_prog_query(const union bpf_attr *attr, - union bpf_attr __user *uattr) + union bpf_attr __user *uattr, u32 uattr_size) { struct cgroup *cgrp; int ret; @@ -1529,7 +1530,7 @@ int cgroup_bpf_prog_query(const union bpf_attr *attr, if (IS_ERR(cgrp)) return PTR_ERR(cgrp); - ret = cgroup_bpf_query(cgrp, attr, uattr); + ret = cgroup_bpf_query(cgrp, attr, uattr, uattr_size); cgroup_put(cgrp); return ret; diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 630d530782fe8..d1274486a564e 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -4654,7 +4654,7 @@ static int bpf_prog_detach(const union bpf_attr *attr) #define BPF_PROG_QUERY_LAST_FIELD query.revision static int bpf_prog_query(const union bpf_attr *attr, - union bpf_attr __user *uattr) + union bpf_attr __user *uattr, u32 uattr_size) { if (!bpf_net_capable()) return -EPERM; @@ -4693,7 +4693,7 @@ static int bpf_prog_query(const union bpf_attr *attr, case BPF_CGROUP_GETSOCKOPT: case BPF_CGROUP_SETSOCKOPT: case BPF_LSM_CGROUP: - return cgroup_bpf_prog_query(attr, uattr); + return cgroup_bpf_prog_query(attr, uattr, uattr_size); case BPF_LIRC_MODE2: return lirc_prog_query(attr, uattr); case BPF_FLOW_DISSECTOR: @@ -6286,7 +6286,7 @@ static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size) err = bpf_prog_detach(&attr); break; case BPF_PROG_QUERY: - err = bpf_prog_query(&attr, uattr.user); + err = bpf_prog_query(&attr, uattr.user, size); break; case BPF_PROG_TEST_RUN: err = bpf_prog_test_run(&attr, uattr.user); From 7d177b1f7974b133e1a014e018e6fa05ba102c7b Mon Sep 17 00:00:00 2001 From: Yuyang Huang Date: Sun, 31 May 2026 15:56:00 +0800 Subject: [PATCH 1077/2758] selftests/bpf: add verification for BPF_PROG_QUERY attr size boundaries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 5add3a4ad1a3bc15404e8bd338813ed0a636f5c9 ] Add a new selftest to verify that the BPF syscall (specifically BPF_PROG_QUERY) correctly handles different user-declared attribute sizes. Specifically, verify that: - For cgroup queries, a query with a size that covers 'prog_cnt' but is smaller than 'revision' (OLD_QUERY_SIZE) succeeds, but does not write to 'revision' (verifying backward compatibility). - A query with full size (FULL_QUERY_SIZE) succeeds and writes both 'prog_cnt' and 'revision'. Fixes: 120933984460 ("bpf: Implement mprog API on top of existing cgroup progs") Cc: Maciej Żenczykowski Cc: Lorenzo Colitti Signed-off-by: Yuyang Huang Link: https://lore.kernel.org/r/20260531075600.4058207-3-yuyanghuang@google.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- .../selftests/bpf/prog_tests/bpf_attr_size.c | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/bpf_attr_size.c diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_attr_size.c b/tools/testing/selftests/bpf/prog_tests/bpf_attr_size.c new file mode 100644 index 0000000000000..32159dc64da8b --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/bpf_attr_size.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2026 Google LLC */ +#include +#include +#include +#include +#include +#include "cgroup_skb_direct_packet_access.skel.h" + +#define OLD_QUERY_SIZE offsetofend(union bpf_attr, query.prog_cnt) +#define FULL_QUERY_SIZE offsetofend(union bpf_attr, query.revision) + +static void test_query_size_boundaries(void) +{ + struct cgroup_skb_direct_packet_access *skel; + struct bpf_link *link = NULL; + union bpf_attr attr; + int cg_fd = -1; + int err; + + skel = cgroup_skb_direct_packet_access__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel_load")) + return; + + cg_fd = test__join_cgroup("/attr_size_cg"); + if (!ASSERT_GE(cg_fd, 0, "join_cgroup")) + goto cleanup; + + link = bpf_program__attach_cgroup(skel->progs.direct_packet_access, + cg_fd); + if (!ASSERT_OK_PTR(link, "cg_attach")) + goto cleanup; + + memset(&attr, 0, sizeof(attr)); + attr.query.target_fd = cg_fd; + attr.query.attach_type = BPF_CGROUP_INET_INGRESS; + attr.query.revision = 0xdeadbeefdeadbeefULL; + + err = syscall(__NR_bpf, BPF_PROG_QUERY, &attr, OLD_QUERY_SIZE); + if (ASSERT_OK(err, "query_old_size")) { + ASSERT_EQ(attr.query.prog_cnt, 1, "prog_cnt_written_old"); + ASSERT_EQ(attr.query.revision, 0xdeadbeefdeadbeefULL, + "revision_not_written_old"); + } + + memset(&attr, 0, sizeof(attr)); + attr.query.target_fd = cg_fd; + attr.query.attach_type = BPF_CGROUP_INET_INGRESS; + + err = syscall(__NR_bpf, BPF_PROG_QUERY, &attr, FULL_QUERY_SIZE); + if (!ASSERT_OK(err, "query_full_size")) + goto cleanup; + + ASSERT_EQ(attr.query.prog_cnt, 1, "prog_cnt_written"); + ASSERT_GT(attr.query.revision, 0, "revision_written"); + +cleanup: + if (link) + bpf_link__destroy(link); + if (cg_fd >= 0) + close(cg_fd); + cgroup_skb_direct_packet_access__destroy(skel); +} + +void test_bpf_attr_size(void) +{ + if (test__start_subtest("query_size_boundaries")) + test_query_size_boundaries(); +} From 271616cd0fe7ad591eaa253c8e8aade3af03d237 Mon Sep 17 00:00:00 2001 From: David Matlack Date: Thu, 23 Apr 2026 17:40:28 +0000 Subject: [PATCH 1078/2758] liveupdate: Use refcount_t for FLB reference counts [ Upstream commit d2850ff2f8c5acda4c1097aa53c006a75b091f2c ] Use refcount_t instead of a raw integer to keep track of references on incoming and outgoing FLBs. Using refcount_t provides protection from overflow, underflow, and other issues. Fixes: cab056f2aae7 ("liveupdate: luo_flb: introduce File-Lifecycle-Bound global state") Signed-off-by: David Matlack Reviewed-by: Samiullah Khawaja Reviewed-by: Pasha Tatashin Link: https://lore.kernel.org/r/20260423174032.3140399-2-dmatlack@google.com Signed-off-by: Pasha Tatashin Signed-off-by: Mike Rapoport (Microsoft) Signed-off-by: Sasha Levin --- include/linux/liveupdate.h | 3 ++- kernel/liveupdate/luo_flb.c | 22 ++++++++++------------ 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/include/linux/liveupdate.h b/include/linux/liveupdate.h index 30c5a39ff9e9c..8d3bbc35c828b 100644 --- a/include/linux/liveupdate.h +++ b/include/linux/liveupdate.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -175,7 +176,7 @@ struct liveupdate_flb_ops { * @retrieved: True once the FLB's retrieve() callback has run. */ struct luo_flb_private_state { - long count; + refcount_t count; u64 data; void *obj; struct mutex lock; diff --git a/kernel/liveupdate/luo_flb.c b/kernel/liveupdate/luo_flb.c index 00f5494812c4a..59c5f31ab7674 100644 --- a/kernel/liveupdate/luo_flb.c +++ b/kernel/liveupdate/luo_flb.c @@ -111,7 +111,7 @@ static int luo_flb_file_preserve_one(struct liveupdate_flb *flb) struct luo_flb_private *private = luo_flb_get_private(flb); scoped_guard(mutex, &private->outgoing.lock) { - if (!private->outgoing.count) { + if (!refcount_read(&private->outgoing.count)) { struct liveupdate_flb_op_args args = {0}; int err; @@ -126,8 +126,10 @@ static int luo_flb_file_preserve_one(struct liveupdate_flb *flb) } private->outgoing.data = args.data; private->outgoing.obj = args.obj; + refcount_set(&private->outgoing.count, 1); + } else { + refcount_inc(&private->outgoing.count); } - private->outgoing.count++; } return 0; @@ -138,8 +140,7 @@ static void luo_flb_file_unpreserve_one(struct liveupdate_flb *flb) struct luo_flb_private *private = luo_flb_get_private(flb); scoped_guard(mutex, &private->outgoing.lock) { - private->outgoing.count--; - if (!private->outgoing.count) { + if (refcount_dec_and_test(&private->outgoing.count)) { struct liveupdate_flb_op_args args = {0}; args.flb = flb; @@ -178,7 +179,7 @@ static int luo_flb_retrieve_one(struct liveupdate_flb *flb) for (int i = 0; i < fh->header_ser->count; i++) { if (!strcmp(fh->ser[i].name, flb->compatible)) { private->incoming.data = fh->ser[i].data; - private->incoming.count = fh->ser[i].count; + refcount_set(&private->incoming.count, fh->ser[i].count); found = true; break; } @@ -208,12 +209,8 @@ static int luo_flb_retrieve_one(struct liveupdate_flb *flb) static void luo_flb_file_finish_one(struct liveupdate_flb *flb) { struct luo_flb_private *private = luo_flb_get_private(flb); - u64 count; - scoped_guard(mutex, &private->incoming.lock) - count = --private->incoming.count; - - if (!count) { + if (refcount_dec_and_test(&private->incoming.count)) { struct liveupdate_flb_op_args args = {0}; if (!private->incoming.retrieved) { @@ -652,12 +649,13 @@ void luo_flb_serialize(void) guard(rwsem_read)(&luo_register_rwlock); list_private_for_each_entry(gflb, &luo_flb_global.list, private.list) { struct luo_flb_private *private = luo_flb_get_private(gflb); + long count = refcount_read(&private->outgoing.count); - if (private->outgoing.count > 0) { + if (count > 0) { strscpy(fh->ser[i].name, gflb->compatible, sizeof(fh->ser[i].name)); fh->ser[i].data = private->outgoing.data; - fh->ser[i].count = private->outgoing.count; + fh->ser[i].count = count; i++; } } From 725ada0273a0f48a67fd59cc518d7f8055fe3d5c Mon Sep 17 00:00:00 2001 From: David Matlack Date: Thu, 23 Apr 2026 17:40:29 +0000 Subject: [PATCH 1079/2758] liveupdate: Reference count incoming FLB data [ Upstream commit d8e47bd066d7e626f9f45d416182d585b7e18b9b ] Increment the incoming FLB refcount in liveupdate_flb_get_incoming() so that the FLB structure cannot be freed while the caller is actively using it. Add an additional liveupdate_flb_put_incoming() function so the caller can explicitly indicate when it is done using the FLB data. During a Live Update, a subsystem might need to hold onto the incoming File-Lifecycle-Bound (FLB) data for an extended period, such as during device enumeration. Incrementing the reference count guarantees that the data remains valid and accessible until the subsystem releases it, preventing future use-after-free bugs. Fixes: cab056f2aae7 ("liveupdate: luo_flb: introduce File-Lifecycle-Bound global state") Signed-off-by: David Matlack Reviewed-by: Samiullah Khawaja Reviewed-by: Pasha Tatashin Link: https://lore.kernel.org/r/20260423174032.3140399-3-dmatlack@google.com Signed-off-by: Pasha Tatashin Signed-off-by: Mike Rapoport (Microsoft) Signed-off-by: Sasha Levin --- include/linux/liveupdate.h | 6 ++++++ kernel/liveupdate/luo_flb.c | 32 +++++++++++++++++--------------- lib/tests/liveupdate.c | 3 +++ 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/include/linux/liveupdate.h b/include/linux/liveupdate.h index 8d3bbc35c828b..88722e5caf020 100644 --- a/include/linux/liveupdate.h +++ b/include/linux/liveupdate.h @@ -240,6 +240,8 @@ void liveupdate_unregister_flb(struct liveupdate_file_handler *fh, struct liveupdate_flb *flb); int liveupdate_flb_get_incoming(struct liveupdate_flb *flb, void **objp); +void liveupdate_flb_put_incoming(struct liveupdate_flb *flb); + int liveupdate_flb_get_outgoing(struct liveupdate_flb *flb, void **objp); #else /* CONFIG_LIVEUPDATE */ @@ -280,6 +282,10 @@ static inline int liveupdate_flb_get_incoming(struct liveupdate_flb *flb, return -EOPNOTSUPP; } +static inline void liveupdate_flb_put_incoming(struct liveupdate_flb *flb) +{ +} + static inline int liveupdate_flb_get_outgoing(struct liveupdate_flb *flb, void **objp) { diff --git a/kernel/liveupdate/luo_flb.c b/kernel/liveupdate/luo_flb.c index 59c5f31ab7674..8f5c5dd01cd04 100644 --- a/kernel/liveupdate/luo_flb.c +++ b/kernel/liveupdate/luo_flb.c @@ -165,7 +165,7 @@ static int luo_flb_retrieve_one(struct liveupdate_flb *flb) bool found = false; int err; - guard(mutex)(&private->incoming.lock); + lockdep_assert_held(&private->incoming.lock); if (private->incoming.finished) return -ENODATA; @@ -206,12 +206,14 @@ static int luo_flb_retrieve_one(struct liveupdate_flb *flb) return 0; } -static void luo_flb_file_finish_one(struct liveupdate_flb *flb) +void liveupdate_flb_put_incoming(struct liveupdate_flb *flb) { struct luo_flb_private *private = luo_flb_get_private(flb); + struct liveupdate_flb_op_args args = {0}; - if (refcount_dec_and_test(&private->incoming.count)) { - struct liveupdate_flb_op_args args = {0}; + scoped_guard(mutex, &private->incoming.lock) { + if (!refcount_dec_and_test(&private->incoming.count)) + return; if (!private->incoming.retrieved) { int err = luo_flb_retrieve_one(flb); @@ -220,16 +222,14 @@ static void luo_flb_file_finish_one(struct liveupdate_flb *flb) return; } - scoped_guard(mutex, &private->incoming.lock) { - args.flb = flb; - args.obj = private->incoming.obj; - flb->ops->finish(&args); + args.flb = flb; + args.obj = private->incoming.obj; + flb->ops->finish(&args); - private->incoming.data = 0; - private->incoming.obj = NULL; - private->incoming.finished = true; - module_put(flb->ops->owner); - } + private->incoming.data = 0; + private->incoming.obj = NULL; + private->incoming.finished = true; + module_put(flb->ops->owner); } } @@ -312,7 +312,7 @@ void luo_flb_file_finish(struct liveupdate_file_handler *fh) guard(rwsem_read)(&luo_register_rwlock); list_for_each_entry_reverse(iter, flb_list, list) - luo_flb_file_finish_one(iter->flb); + liveupdate_flb_put_incoming(iter->flb); } static void luo_flb_unregister_one(struct liveupdate_file_handler *fh, @@ -509,6 +509,8 @@ int liveupdate_flb_get_incoming(struct liveupdate_flb *flb, void **objp) if (!liveupdate_enabled()) return -EOPNOTSUPP; + guard(mutex)(&private->incoming.lock); + if (!private->incoming.obj) { int err = luo_flb_retrieve_one(flb); @@ -516,7 +518,7 @@ int liveupdate_flb_get_incoming(struct liveupdate_flb *flb, void **objp) return err; } - guard(mutex)(&private->incoming.lock); + refcount_inc(&private->incoming.count); *objp = private->incoming.obj; return 0; diff --git a/lib/tests/liveupdate.c b/lib/tests/liveupdate.c index e4b0ecbee32fe..4c08a7c6fb788 100644 --- a/lib/tests/liveupdate.c +++ b/lib/tests/liveupdate.c @@ -105,6 +105,9 @@ static void liveupdate_test_init(void) pr_err("liveupdate_flb_get_incoming for %s failed: %pe\n", flb->compatible, ERR_PTR(err)); } + + if (!err) + liveupdate_flb_put_incoming(flb); } initialized = true; } From a8f448943e1a9c8e1f2fb4aa2302309cf5eeefdd Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 29 May 2026 11:41:17 +0200 Subject: [PATCH 1080/2758] libbpf: Skip hash computation when loader generation failed [ Upstream commit 3c5e2f1a85844abbb65df4694f5ebad0a13e219c ] bpf_gen__finish() calls compute_sha_update_offsets() gated only on the gen_hash option, without first consulting gen->error. On a failed generation this is buggy: a failed realloc_data_buf() sets gen->data_start to NULL (leaving gen->data_cur dangling), so compute_sha_update_offsets() runs libbpf_sha256() over a NULL buffer with a bogus length; a failed realloc_insn_buf() likewise sets gen->insn_start to NULL and the hash immediates get patched through that NULL base. The computed program is discarded in either case, since the following "if (!gen->error)" block does not publish opts->insns once an error is set. Thus, skip the hash pass when generation has already failed. Fixes: ea923080c145 ("libbpf: Embed and verify the metadata hash in the loader") Reported-by: sashiko Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/r/20260529094119.307264-2-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- tools/lib/bpf/gen_loader.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/lib/bpf/gen_loader.c b/tools/lib/bpf/gen_loader.c index 9478b8f78f260..e2fe841a56698 100644 --- a/tools/lib/bpf/gen_loader.c +++ b/tools/lib/bpf/gen_loader.c @@ -398,13 +398,12 @@ int bpf_gen__finish(struct bpf_gen *gen, int nr_progs, int nr_maps) blob_fd_array_off(gen, i)); emit(gen, BPF_MOV64_IMM(BPF_REG_0, 0)); emit(gen, BPF_EXIT_INSN()); - if (OPTS_GET(gen->opts, gen_hash, false)) - compute_sha_update_offsets(gen); - - pr_debug("gen: finish %s\n", errstr(gen->error)); if (!gen->error) { struct gen_loader_opts *opts = gen->opts; + if (OPTS_GET(opts, gen_hash, false)) + compute_sha_update_offsets(gen); + opts->insns = gen->insn_start; opts->insns_sz = gen->insn_cur - gen->insn_start; opts->data = gen->data_start; @@ -419,6 +418,7 @@ int bpf_gen__finish(struct bpf_gen *gen, int nr_progs, int nr_maps) bpf_insn_bswap(insn++); } } + pr_debug("gen: finish %s\n", errstr(gen->error)); return gen->error; } From 61d0ebb5a0457fcac19c95519909db6207034cfd Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 29 May 2026 18:28:29 +0200 Subject: [PATCH 1081/2758] libbpf: Skip endianness swap when loader generation failed [ Upstream commit 41300d032a1b1d91a3ed996ad21905463e344beb ] bpf_gen__prog_load() byte-swaps the program insns and the {func,line}_info and CO-RE relo blobs in place for cross-endian targets. The blob offsets come from add_data(), which returns 0 on failure: realloc_data_buf() either frees and NULLs gen->data_start (realloc OOM) or returns early on an already-latched gen->error, leaving a stale, possibly too-small buffer. Neither bswap site checked for this. With gen->swapped_endian set and a failed generation, "gen->data_start + off" becomes NULL + 0. Guard the same way via !gen->error so they are skipped once generation has failed. Fixes: 8ca3323dce43 ("libbpf: Support creating light skeleton of either endianness") Reported-by: sashiko Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/r/20260529162829.315921-1-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- tools/lib/bpf/gen_loader.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/lib/bpf/gen_loader.c b/tools/lib/bpf/gen_loader.c index e2fe841a56698..e63a01101e2d8 100644 --- a/tools/lib/bpf/gen_loader.c +++ b/tools/lib/bpf/gen_loader.c @@ -1053,7 +1053,7 @@ void bpf_gen__prog_load(struct bpf_gen *gen, prog_idx, prog_type, insns_off, insn_cnt, license_off); /* convert blob insns to target endianness */ - if (gen->swapped_endian) { + if (gen->swapped_endian && !gen->error) { struct bpf_insn *insn = gen->data_start + insns_off; int i; @@ -1091,7 +1091,7 @@ void bpf_gen__prog_load(struct bpf_gen *gen, sizeof(struct bpf_core_relo)); /* convert all info blobs to target endianness */ - if (gen->swapped_endian) + if (gen->swapped_endian && !gen->error) info_blob_bswap(gen, func_info, line_info, core_relos, load_attr); libbpf_strlcpy(attr.prog_name, prog_name, sizeof(attr.prog_name)); From 2ad182a15bbc3a84ee3595cbece0e4c85de7818d Mon Sep 17 00:00:00 2001 From: Pasha Tatashin Date: Wed, 27 May 2026 20:27:33 +0000 Subject: [PATCH 1082/2758] liveupdate: skip serialization for context-preserving kexec [ Upstream commit 5eff62b051fbdb686e885c1468301d964f2e3d66 ] A preserve_context kexec returns to the current kernel, which is unrelated to live update where the state is passed to the next kernel. Skip liveupdate_reboot() in this case to avoid serialization and prevent sessions from being left in a frozen state upon return. Fixes: db8bed8082dc ("kexec: call liveupdate_reboot() before kexec") Reported-by: Oskar Gerlicz Kowalczuk Reviewed-by: Pratyush Yadav (Google) Acked-by: Mike Rapoport (Microsoft) Signed-off-by: Pasha Tatashin Link: https://patch.msgid.link/20260527202737.1345192-2-pasha.tatashin@soleen.com Signed-off-by: Mike Rapoport (Microsoft) Signed-off-by: Sasha Levin --- kernel/kexec_core.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c index a43d2da0fe3e7..dc770b9a6d053 100644 --- a/kernel/kexec_core.c +++ b/kernel/kexec_core.c @@ -1146,9 +1146,11 @@ int kernel_kexec(void) goto Unlock; } - error = liveupdate_reboot(); - if (error) - goto Unlock; + if (!kexec_image->preserve_context) { + error = liveupdate_reboot(); + if (error) + goto Unlock; + } #ifdef CONFIG_KEXEC_JUMP if (kexec_image->preserve_context) { From d944170607b872a1f93713c555ad3f0efde3a9b8 Mon Sep 17 00:00:00 2001 From: Pasha Tatashin Date: Wed, 27 May 2026 20:27:34 +0000 Subject: [PATCH 1083/2758] liveupdate: fix TOCTOU race in luo_session_retrieve() [ Upstream commit d3ae9e7fddb4036f50003d7fa1ef52801fdb961b ] Extend the scope of the rwsem_read lock in luo_session_retrieve() to overlap with the acquisition of the session mutex. This prevents a concurrent thread from releasing and freeing the session between the lookup and the mutex lock. Fixes: 0153094d03df ("liveupdate: luo_session: add sessions support") Acked-by: Mike Rapoport (Microsoft) Reviewed-by: Pratyush Yadav (Google) Signed-off-by: Pasha Tatashin Link: https://patch.msgid.link/20260527202737.1345192-3-pasha.tatashin@soleen.com Signed-off-by: Mike Rapoport (Microsoft) Signed-off-by: Sasha Levin --- kernel/liveupdate/luo_session.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c index ec7aebc15a801..85af0963d66ae 100644 --- a/kernel/liveupdate/luo_session.c +++ b/kernel/liveupdate/luo_session.c @@ -419,12 +419,11 @@ int luo_session_retrieve(const char *name, struct file **filep) struct luo_session *it; int err; - scoped_guard(rwsem_read, &sh->rwsem) { - list_for_each_entry(it, &sh->list, list) { - if (!strncmp(it->name, name, sizeof(it->name))) { - session = it; - break; - } + guard(rwsem_read)(&sh->rwsem); + list_for_each_entry(it, &sh->list, list) { + if (!strncmp(it->name, name, sizeof(it->name))) { + session = it; + break; } } From bcb49f7d0a283f0c38ae951be623127856991313 Mon Sep 17 00:00:00 2001 From: Pasha Tatashin Date: Wed, 27 May 2026 20:27:35 +0000 Subject: [PATCH 1084/2758] liveupdate: block session mutations during reboot [ Upstream commit bb1328be35bf43c88288c5c31ceb45181b574c0c ] During the reboot() syscall, user processes may still be running concurrently and attempting to mutate sessions (e.g., creating, retrieving, or releasing sessions). To prevent this, introduce luo_session_serialize_rwsem to synchronize mutations with the serialization process. All session mutation operations (create, retrieve, release, ioctl) take the read lock. The serialization process (luo_session_serialize) takes the write lock and holds it indefinitely on success. This effectively freezes the LUO session subsystem during the transition to the new kernel. If serialization fails, the lock is released to allow recovery. Fixes: 0153094d03df ("liveupdate: luo_session: add sessions support") Reported-by: Oskar Gerlicz Kowalczuk Acked-by: Mike Rapoport (Microsoft) Signed-off-by: Pasha Tatashin Reviewed-by: Pratyush Yadav (Google) Link: https://patch.msgid.link/20260527202737.1345192-4-pasha.tatashin@soleen.com Signed-off-by: Mike Rapoport (Microsoft) Signed-off-by: Sasha Levin --- kernel/liveupdate/luo_session.c | 51 +++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c index 85af0963d66ae..5486e834c4514 100644 --- a/kernel/liveupdate/luo_session.c +++ b/kernel/liveupdate/luo_session.c @@ -46,6 +46,38 @@ * 4. Retrieval: A userspace agent in the new kernel can then call * `luo_session_retrieve()` with a session name to get a new file * descriptor and access the preserved state. + * + * Locking: + * + * The LUO session subsystem uses a three-tier locking hierarchy to ensure thread + * safety and prevent deadlocks during concurrent session mutations and kexec + * serialization: + * + * 1. `luo_session_serialize_rwsem` (global rwsem): + * Protects session mutations (creation, retrieval, release, and ioctls) + * against the serialization process during reboot. + * + * - Readers: Taken by any path modifying or accessing session state (e.g., + * `luo_session_create()`, `luo_session_retrieve()`, `luo_session_release()`, + * and `luo_session_ioctl()`). + * - Writer: Taken by the serialization process (`luo_session_serialize()`) + * during reboot. On success, the write lock is held indefinitely to freeze + * the subsystem. On failure, it is released to allow recovery. + * + * 2. `luo_session_header->rwsem` (per-list rwsem): + * Synchronizes list-level operations for the incoming and outgoing session headers. + * + * - Writer: Taken during list mutation operations (inserting or removing a + * session from the list). + * - Reader: Taken when traversing the list (e.g., retrieving a session by name). + * + * 3. `luo_session->mutex` (per-session mutex): + * Protects the internal state and file sets of an individual session. It is + * acquired during per-session operations such as preserving, retrieving, + * or freezing files. + * + * Lock Hierarchy: + * `luo_session_serialize_rwsem` -> `luo_session_header->rwsem` -> `luo_session->mutex` */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -75,6 +107,8 @@ sizeof(struct luo_session_header_ser)) / \ sizeof(struct luo_session_ser)) +static DECLARE_RWSEM(luo_session_serialize_rwsem); + /** * struct luo_session_header - Header struct for managing LUO sessions. * @count: The number of sessions currently tracked in the @list. @@ -205,6 +239,7 @@ static int luo_session_release(struct inode *inodep, struct file *filep) struct luo_session *session = filep->private_data; struct luo_session_header *sh; + guard(rwsem_read)(&luo_session_serialize_rwsem); /* If retrieved is set, it means this session is from incoming list */ if (session->retrieved) { int err = luo_session_finish_one(session); @@ -354,6 +389,7 @@ static long luo_session_ioctl(struct file *filep, unsigned int cmd, if (ret) return ret; + guard(rwsem_read)(&luo_session_serialize_rwsem); return op->execute(session, &ucmd); } @@ -393,14 +429,17 @@ int luo_session_create(const char *name, struct file **filep) if (IS_ERR(session)) return PTR_ERR(session); + down_read(&luo_session_serialize_rwsem); err = luo_session_insert(&luo_session_global.outgoing, session); if (err) goto err_free; - scoped_guard(mutex, &session->mutex) - err = luo_session_getfile(session, filep); + mutex_lock(&session->mutex); + err = luo_session_getfile(session, filep); + mutex_unlock(&session->mutex); if (err) goto err_remove; + up_read(&luo_session_serialize_rwsem); return 0; @@ -408,6 +447,7 @@ int luo_session_create(const char *name, struct file **filep) luo_session_remove(&luo_session_global.outgoing, session); err_free: luo_session_free(session); + up_read(&luo_session_serialize_rwsem); return err; } @@ -419,6 +459,7 @@ int luo_session_retrieve(const char *name, struct file **filep) struct luo_session *it; int err; + guard(rwsem_read)(&luo_session_serialize_rwsem); guard(rwsem_read)(&sh->rwsem); list_for_each_entry(it, &sh->list, list) { if (!strncmp(it->name, name, sizeof(it->name))) { @@ -591,7 +632,8 @@ int luo_session_serialize(void) int i = 0; int err; - guard(rwsem_write)(&sh->rwsem); + down_write(&luo_session_serialize_rwsem); + down_write(&sh->rwsem); list_for_each_entry(session, &sh->list, list) { err = luo_session_freeze_one(session, &sh->ser[i]); if (err) @@ -602,6 +644,7 @@ int luo_session_serialize(void) i++; } sh->header_ser->count = sh->count; + up_write(&sh->rwsem); return 0; @@ -611,6 +654,8 @@ int luo_session_serialize(void) luo_session_unfreeze_one(session, &sh->ser[i]); memset(sh->ser[i].name, 0, sizeof(sh->ser[i].name)); } + up_write(&sh->rwsem); + up_write(&luo_session_serialize_rwsem); return err; } From f7fca3db635022429382cf26e5b08b33558e375d Mon Sep 17 00:00:00 2001 From: Pasha Tatashin Date: Wed, 27 May 2026 20:27:36 +0000 Subject: [PATCH 1085/2758] liveupdate: fix u-a-f in luo_file_unpreserve_files() and luo_file_finish() [ Upstream commit 291dcd37c8c8f8f8e1bccc92228f44bf371762a8 ] In luo_file_unpreserve_files() and luo_file_finish(), reorder module_put() and xa_erase() to ensure the file handler module remains pinned while its operations are being accessed. Specifically, luo_get_id() dereferences fh->ops->get_id, so the module reference must be held until after xa_erase() (which calls luo_get_id) completes. For luo_file_finish(), this requires moving the module_put() call out of the luo_file_finish_one() helper and into the main loop of luo_file_finish() itself. Fixes: 00d0b372374f ("liveupdate: prevent double management of files") Acked-by: Mike Rapoport (Microsoft) Reviewed-by: Pratyush Yadav (Google) Signed-off-by: Pasha Tatashin Link: https://patch.msgid.link/20260527202737.1345192-5-pasha.tatashin@soleen.com Signed-off-by: Mike Rapoport (Microsoft) Signed-off-by: Sasha Levin --- kernel/liveupdate/luo_file.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c index a0a419085e283..208987502f73a 100644 --- a/kernel/liveupdate/luo_file.c +++ b/kernel/liveupdate/luo_file.c @@ -385,10 +385,11 @@ void luo_file_unpreserve_files(struct luo_file_set *file_set) args.private_data = luo_file->private_data; luo_file->fh->ops->unpreserve(&args); luo_flb_file_unpreserve(luo_file->fh); - module_put(luo_file->fh->ops->owner); xa_erase(&luo_preserved_files, luo_get_id(luo_file->fh, luo_file->file)); + module_put(luo_file->fh->ops->owner); + list_del(&luo_file->list); file_set->count--; @@ -677,7 +678,6 @@ static void luo_file_finish_one(struct luo_file_set *file_set, luo_file->fh->ops->finish(&args); luo_flb_file_finish(luo_file->fh); - module_put(luo_file->fh->ops->owner); } /** @@ -738,6 +738,7 @@ int luo_file_finish(struct luo_file_set *file_set) luo_get_id(luo_file->fh, luo_file->file)); fput(luo_file->file); } + module_put(luo_file->fh->ops->owner); list_del(&luo_file->list); file_set->count--; mutex_destroy(&luo_file->mutex); From 134e0bb5a0c2a0541c050462d6f8c0ef10d4ea3e Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Fri, 24 Apr 2026 18:42:01 +0800 Subject: [PATCH 1086/2758] ext4: fix LOGFLUSH shutdown ordering to allow ordered-mode data writeback [ Upstream commit d99748ef1695ce17eaf51c64b7a06952fa7cddab ] In EXT4_GOING_FLAGS_LOGFLUSH mode, the EXT4_FLAGS_SHUTDOWN flag was set before calling ext4_force_commit(). This caused ordered-mode data writeback (triggered by journal commit) to fail with -EIO, since ext4_do_writepages() checks for the shutdown flag. The journal would then be aborted prematurely before the commit could succeed. Fix this by calling ext4_force_commit() first, then setting the shutdown flag, so that pending data can be written back correctly. Note that moving ext4_force_commit() before setting the shutdown flag creates a small window in which new writes may occur and generate new journal transactions. When the journal is subsequently aborted, the new transactions will not be able to write to disk. This is intentional because LOGFLUSH's semantics are to flush pre-existing journal entries before shutdown, not to guarantee atomicity for writes that race with the ioctl. Fixes: 783d94854499 ("ext4: add EXT4_IOC_GOINGDOWN ioctl") Signed-off-by: Zhang Yi Reviewed-by: Baokun Li Reviewed-by: Jan Kara Link: https://patch.msgid.link/20260424104201.1930823-1-yi.zhang@huaweicloud.com Signed-off-by: Theodore Ts'o Signed-off-by: Sasha Levin --- fs/ext4/ioctl.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 1d0c3d4bdf472..110e3fb194ec6 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -830,11 +830,17 @@ int ext4_force_shutdown(struct super_block *sb, u32 flags) bdev_thaw(sb->s_bdev); break; case EXT4_GOING_FLAGS_LOGFLUSH: + /* + * Call ext4_force_commit() before setting EXT4_FLAGS_SHUTDOWN. + * This is because in data=ordered mode, journal commit + * triggers data writeback which fails if shutdown is already + * set, causing the journal to be aborted prematurely before + * the commit succeeds. + */ + (void) ext4_force_commit(sb); set_bit(EXT4_FLAGS_SHUTDOWN, &sbi->s_ext4_flags); - if (sbi->s_journal && !is_journal_aborted(sbi->s_journal)) { - (void) ext4_force_commit(sb); + if (sbi->s_journal && !is_journal_aborted(sbi->s_journal)) jbd2_journal_abort(sbi->s_journal, -ESHUTDOWN); - } break; case EXT4_GOING_FLAGS_NOLOGFLUSH: set_bit(EXT4_FLAGS_SHUTDOWN, &sbi->s_ext4_flags); From 422bb0227d2f88a50ee5ea4aae9dc19d0186ac42 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Fri, 22 May 2026 20:40:48 +0800 Subject: [PATCH 1087/2758] spi: atmel: fix DMA channel and bounce buffer leaks [ Upstream commit bd7e9843ec95bffe2643c901dd625f0bab32e639 ] The original code set use_dma to false when dma_alloc_coherent() for bounce buffers failed, but DMA channels acquired earlier via atmel_spi_configure_dma() were never freed. When devm_request_irq() or clk_prepare_enable() failed later in probe, the driver also did not release DMA channels or bounce buffers already allocated. The out_free_dma error path released DMA channels but did not free the bounce buffers. Fix by moving bounce buffer allocation into atmel_spi_configure_dma() and registering the devres cleanup for DMA channels and bounce buffers. Fixes: a9889ed62d06 ("spi: atmel: Implements transfers with bounce buffer") Signed-off-by: Felix Gu Link: https://patch.msgid.link/20260522-atmel-v3-1-23f8c6e6aa43@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-atmel.c | 133 ++++++++++++++++++++-------------------- 1 file changed, 68 insertions(+), 65 deletions(-) diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 42db85d7ff8e1..397bc819e7161 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -559,6 +559,38 @@ static int atmel_spi_dma_slave_config(struct atmel_spi *as, u8 bits_per_word) return err; } +static void atmel_spi_release_dma(void *data) +{ + struct spi_controller *host = data; + struct atmel_spi *as = spi_controller_get_devdata(host); + struct device *dev = &as->pdev->dev; + + if (host->dma_tx) { + dma_release_channel(host->dma_tx); + host->dma_tx = NULL; + } + + if (host->dma_rx) { + dma_release_channel(host->dma_rx); + host->dma_rx = NULL; + } + + if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) { + if (as->addr_tx_bbuf) { + dma_free_coherent(dev, SPI_MAX_DMA_XFER, + as->addr_tx_bbuf, + as->dma_addr_tx_bbuf); + as->addr_tx_bbuf = NULL; + } + if (as->addr_rx_bbuf) { + dma_free_coherent(dev, SPI_MAX_DMA_XFER, + as->addr_rx_bbuf, + as->dma_addr_rx_bbuf); + as->addr_rx_bbuf = NULL; + } + } +} + static int atmel_spi_configure_dma(struct spi_controller *host, struct atmel_spi *as) { @@ -569,7 +601,8 @@ static int atmel_spi_configure_dma(struct spi_controller *host, if (IS_ERR(host->dma_tx)) { err = PTR_ERR(host->dma_tx); dev_dbg(dev, "No TX DMA channel, DMA is disabled\n"); - goto error_clear; + host->dma_tx = NULL; + return err; } host->dma_rx = dma_request_chan(dev, "rx"); @@ -580,26 +613,45 @@ static int atmel_spi_configure_dma(struct spi_controller *host, * requested tx channel. */ dev_dbg(dev, "No RX DMA channel, DMA is disabled\n"); - goto error; + host->dma_rx = NULL; + goto err_release_dma; } err = atmel_spi_dma_slave_config(as, 8); if (err) - goto error; + goto err_release_dma; + + if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) { + as->addr_tx_bbuf = dma_alloc_coherent(dev, SPI_MAX_DMA_XFER, + &as->dma_addr_tx_bbuf, + GFP_KERNEL | GFP_DMA); + if (!as->addr_tx_bbuf) { + err = -ENOMEM; + goto err_release_dma; + } + + as->addr_rx_bbuf = dma_alloc_coherent(dev, SPI_MAX_DMA_XFER, + &as->dma_addr_rx_bbuf, + GFP_KERNEL | GFP_DMA); + if (!as->addr_rx_bbuf) { + err = -ENOMEM; + goto err_release_dma; + } + } + + err = devm_add_action_or_reset(dev, atmel_spi_release_dma, host); + if (err) + return err; dev_info(&as->pdev->dev, - "Using %s (tx) and %s (rx) for DMA transfers\n", - dma_chan_name(host->dma_tx), - dma_chan_name(host->dma_rx)); + "Using %s (tx) and %s (rx) for DMA transfers\n", + dma_chan_name(host->dma_tx), dma_chan_name(host->dma_rx)); return 0; -error: - if (!IS_ERR(host->dma_rx)) - dma_release_channel(host->dma_rx); - if (!IS_ERR(host->dma_tx)) - dma_release_channel(host->dma_tx); -error_clear: - host->dma_tx = host->dma_rx = NULL; + +err_release_dma: + atmel_spi_release_dma(host); + return err; } @@ -611,18 +663,6 @@ static void atmel_spi_stop_dma(struct spi_controller *host) dmaengine_terminate_all(host->dma_tx); } -static void atmel_spi_release_dma(struct spi_controller *host) -{ - if (host->dma_rx) { - dma_release_channel(host->dma_rx); - host->dma_rx = NULL; - } - if (host->dma_tx) { - dma_release_channel(host->dma_tx); - host->dma_tx = NULL; - } -} - /* This function is called by the DMA driver from tasklet context */ static void dma_callback(void *data) { @@ -1585,30 +1625,6 @@ static int atmel_spi_probe(struct platform_device *pdev) as->use_pdc = true; } - if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) { - as->addr_rx_bbuf = dma_alloc_coherent(&pdev->dev, - SPI_MAX_DMA_XFER, - &as->dma_addr_rx_bbuf, - GFP_KERNEL | GFP_DMA); - if (!as->addr_rx_bbuf) { - as->use_dma = false; - } else { - as->addr_tx_bbuf = dma_alloc_coherent(&pdev->dev, - SPI_MAX_DMA_XFER, - &as->dma_addr_tx_bbuf, - GFP_KERNEL | GFP_DMA); - if (!as->addr_tx_bbuf) { - as->use_dma = false; - dma_free_coherent(&pdev->dev, SPI_MAX_DMA_XFER, - as->addr_rx_bbuf, - as->dma_addr_rx_bbuf); - } - } - if (!as->use_dma) - dev_info(host->dev.parent, - " can not allocate dma coherent memory\n"); - } - if (as->caps.has_dma_support && !as->use_dma) dev_info(&pdev->dev, "Atmel SPI Controller using PIO only\n"); @@ -1668,13 +1684,10 @@ static int atmel_spi_probe(struct platform_device *pdev) out_free_dma: pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); - - if (as->use_dma) - atmel_spi_release_dma(host); - spi_writel(as, CR, SPI_BIT(SWRST)); spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */ - clk_disable_unprepare(as->gclk); + if (as->gclk) + clk_disable_unprepare(as->gclk); out_disable_clk: clk_disable_unprepare(clk); out_free_irq: @@ -1695,18 +1708,8 @@ static void atmel_spi_remove(struct platform_device *pdev) spi_unregister_controller(host); /* reset the hardware and block queue progress */ - if (as->use_dma) { + if (as->use_dma) atmel_spi_stop_dma(host); - atmel_spi_release_dma(host); - if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) { - dma_free_coherent(&pdev->dev, SPI_MAX_DMA_XFER, - as->addr_tx_bbuf, - as->dma_addr_tx_bbuf); - dma_free_coherent(&pdev->dev, SPI_MAX_DMA_XFER, - as->addr_rx_bbuf, - as->dma_addr_rx_bbuf); - } - } spin_lock_irq(&as->lock); spi_writel(as, CR, SPI_BIT(SWRST)); From 7a2ac292a070f8d2d0691ad983d9cfd49f5be653 Mon Sep 17 00:00:00 2001 From: Carlos Song Date: Mon, 25 May 2026 14:29:28 +0800 Subject: [PATCH 1088/2758] spi: imx: replace dmaengine_terminate_all() with dmaengine_terminate_sync() [ Upstream commit 4503b2fe761c2bfd33ed043d9b9deec0d1eb40e0 ] dmaengine_terminate_all() has been deprecated, so replace it with dmaengine_terminate_sync(). Fixes: ba9b28652c75 ("spi: imx: enable DMA mode for target operation") Fixes: a450c8b77f92 ("spi: imx: handle DMA submission errors with dma_submit_error()") Signed-off-by: Carlos Song Link: https://patch.msgid.link/20260525062928.3191821-1-carlos.song@oss.nxp.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-imx.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 480d1e8b281f1..ae9912905c67a 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -1774,8 +1774,8 @@ static int spi_imx_dma_submit(struct spi_imx_data *spi_imx, transfer_timeout); if (!time_left) { dev_err(spi_imx->dev, "I/O Error in DMA TX\n"); - dmaengine_terminate_all(controller->dma_tx); - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_rx); return -ETIMEDOUT; } @@ -1784,7 +1784,7 @@ static int spi_imx_dma_submit(struct spi_imx_data *spi_imx, if (!time_left) { dev_err(&controller->dev, "I/O Error in DMA RX\n"); spi_imx->devtype_data->reset(spi_imx); - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_rx); return -ETIMEDOUT; } } else { @@ -1793,15 +1793,15 @@ static int spi_imx_dma_submit(struct spi_imx_data *spi_imx, if (wait_for_completion_interruptible(&spi_imx->dma_tx_completion) || READ_ONCE(spi_imx->target_aborted)) { dev_dbg(spi_imx->dev, "I/O Error in DMA TX interrupted\n"); - dmaengine_terminate_all(controller->dma_tx); - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_rx); return -EINTR; } if (wait_for_completion_interruptible(&spi_imx->dma_rx_completion) || READ_ONCE(spi_imx->target_aborted)) { dev_dbg(spi_imx->dev, "I/O Error in DMA RX interrupted\n"); - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_rx); return -EINTR; } @@ -1818,9 +1818,9 @@ static int spi_imx_dma_submit(struct spi_imx_data *spi_imx, return 0; dmaengine_terminate_tx: - dmaengine_terminate_all(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_tx); dmaengine_terminate_rx: - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_rx); return -EINVAL; } From 48ea89e4efaa6e93acfc4b1605efd3df8e0a1fe9 Mon Sep 17 00:00:00 2001 From: John Madieu Date: Mon, 25 May 2026 11:02:14 +0000 Subject: [PATCH 1089/2758] ASoC: rsnd: Fix RSND_SOC_MASK width to single nibble [ Upstream commit c0758279367e9d82eb7d7b4959718d7d32e96b7d ] RSND_SOC_MASK was defined as (0xFF << 4), spanning bits 4-11. This is wider than needed since only nibble B (bits 7:4) is used for SoC identifiers. Narrow it to (0xF << 4) to match the intended single-nibble allocation and prevent overlap with bits 8-11 which will be used by upcoming RZ series flags. No functional change, since the only current user (RSND_SOC_E) fits within a single nibble. Fixes: ba164a49f8f7 ("ASoC: rsnd: src: Avoid a potential deadlock") Signed-off-by: John Madieu Acked-by: Kuninori Morimoto Link: https://patch.msgid.link/20260525110230.4014435-3-john.madieu.xa@bp.renesas.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/renesas/rcar/rsnd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/renesas/rcar/rsnd.h b/sound/soc/renesas/rcar/rsnd.h index 04c70690f7a25..3e666125959b4 100644 --- a/sound/soc/renesas/rcar/rsnd.h +++ b/sound/soc/renesas/rcar/rsnd.h @@ -624,7 +624,7 @@ struct rsnd_priv { #define RSND_GEN2 (2 << 0) #define RSND_GEN3 (3 << 0) #define RSND_GEN4 (4 << 0) -#define RSND_SOC_MASK (0xFF << 4) +#define RSND_SOC_MASK (0xF << 4) #define RSND_SOC_E (1 << 4) /* E1/E2/E3 */ /* From ef2422b9e8bec07f6881349291889409bc0d4160 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 24 Mar 2026 11:18:12 -0400 Subject: [PATCH 1090/2758] NFSD: Fix delegation reference leak in nfsd4_revoke_states [ Upstream commit 625981c8f3da0cc2d236d7b46c39dd75554b8276 ] When revoking delegation state, nfsd4_revoke_states() takes an extra reference on the stid before calling unhash_delegation_locked(). If unhash_delegation_locked() returns false (the delegation was already unhashed by a concurrent path), dp is set to NULL and revoke_delegation() is skipped, but the extra reference is never released. Each occurrence permanently pins the stid in memory. The leaked reference also prevents nfs4_put_stid() from decrementing cl_admin_revoked, leaving the counter permanently inflated. Drop the extra reference in the failure path. Fixes: 8dd91e8d31fe ("nfsd: fix race between laundromat and free_stateid") Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Sasha Levin --- fs/nfsd/nfs4state.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index d875e98d4dcbd..2696a9fef0291 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1376,7 +1376,8 @@ static void destroy_delegation(struct nfs4_delegation *dp) * stateid or it's called from a laundromat thread (nfsd4_landromat()) that * determined that this specific state has expired and needs to be revoked * (both mark state with the appropriate stid sc_status mode). It is also - * assumed that a reference was taken on the @dp state. + * assumed that a reference was taken on the @dp state. This function + * consumes that reference. * * If this function finds that the @dp state is SC_STATUS_FREED it means * that a FREE_STATEID operation for this stateid has been processed and @@ -1839,6 +1840,10 @@ void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb) mutex_unlock(&stp->st_mutex); break; case SC_TYPE_DELEG: + /* Extra reference guards against concurrent + * FREE_STATEID; revoke_delegation() consumes + * it, otherwise release it directly. + */ refcount_inc(&stid->sc_count); dp = delegstateid(stid); spin_lock(&nn->deleg_lock); @@ -1848,6 +1853,8 @@ void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb) spin_unlock(&nn->deleg_lock); if (dp) revoke_delegation(dp); + else + nfs4_put_stid(stid); break; case SC_TYPE_LAYOUT: ls = layoutstateid(stid); From 6128c4dede9770c85bcabb72f970dd9f3da65691 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Wed, 13 May 2026 23:40:04 -0400 Subject: [PATCH 1091/2758] ARM: imx3: Fix CCM node reference leak [ Upstream commit 36d46348eb5fc4bc505cd2290ddd70c25fbe6bb3 ] of_find_compatible_node() returns a referenced device node. The i.MX31 and i.MX35 early init paths use the node to map the CCM registers with of_iomap(), but never drop the node reference. Release the node after the mapping is created. Fixes: 2cf98d12958c ("ARM: imx3: Retrieve the CCM base address from devicetree") Signed-off-by: Yuho Choi Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- arch/arm/mach-imx/mm-imx3.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/mach-imx/mm-imx3.c b/arch/arm/mach-imx/mm-imx3.c index 0788c5cc7f9e6..9b0b014d7fe27 100644 --- a/arch/arm/mach-imx/mm-imx3.c +++ b/arch/arm/mach-imx/mm-imx3.c @@ -106,6 +106,7 @@ void __init imx31_init_early(void) arm_pm_idle = imx31_idle; np = of_find_compatible_node(NULL, NULL, "fsl,imx31-ccm"); mx3_ccm_base = of_iomap(np, 0); + of_node_put(np); BUG_ON(!mx3_ccm_base); } #endif /* ifdef CONFIG_SOC_IMX31 */ @@ -143,6 +144,7 @@ void __init imx35_init_early(void) arch_ioremap_caller = imx3_ioremap_caller; np = of_find_compatible_node(NULL, NULL, "fsl,imx35-ccm"); mx3_ccm_base = of_iomap(np, 0); + of_node_put(np); BUG_ON(!mx3_ccm_base); } #endif /* ifdef CONFIG_SOC_IMX35 */ From eac7ec8a153806bcc7933a9761b779df1dac3457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20Neusch=C3=A4fer?= Date: Thu, 26 Mar 2026 15:03:48 +0100 Subject: [PATCH 1092/2758] HID: wiimote: Fix table layout and whitespace errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 12b7731995ca577d86e02196e99ba9c126f47282 ] Some tab characters snuck into the data layout table for turntable extensions, which resulted in the table only looking right at a tabstop of 4, which is uncommon in the kernel. Change them to the equivalent amount of spaces, which should look correct in any editor. While at it, also fix the other whitespace errors (trailing spaces at end of line) introduced in the same commit. Fixes: 05086f3db530b3 ("HID: wiimote: Add support for the DJ Hero turntable") Reviewed-by: David Rheinsberg Signed-off-by: J. Neuschäfer Signed-off-by: Benjamin Tissoires Signed-off-by: Sasha Levin --- drivers/hid/hid-wiimote-modules.c | 58 +++++++++++++++---------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c index dbccdfa639167..dccb78bb3afd6 100644 --- a/drivers/hid/hid-wiimote-modules.c +++ b/drivers/hid/hid-wiimote-modules.c @@ -2403,7 +2403,7 @@ static const struct wiimod_ops wiimod_guitar = { .in_ext = wiimod_guitar_in_ext, }; -/* +/* * Turntable * DJ Hero came with a Turntable Controller that was plugged in * as an extension. @@ -2439,15 +2439,15 @@ static const __u16 wiimod_turntable_map[] = { static void wiimod_turntable_in_ext(struct wiimote_data *wdata, const __u8 *ext) { __u8 be, cs, sx, sy, ed, rtt, rbg, rbr, rbb, ltt, lbg, lbr, lbb, bp, bm; - /* + /* * Byte | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | *------+------+-----+-----+-----+-----+------+------+--------+ - * 0 | RTT<4:3> | SX <5:0> | - * 1 | RTT<2:1> | SY <5:0> | + * 0 | RTT<4:3> | SX <5:0> | + * 1 | RTT<2:1> | SY <5:0> | *------+------+-----+-----+-----+-----+------+------+--------+ * 2 |RTT<0>| ED<4:3> | CS<3:0> | RTT<5> | *------+------+-----+-----+-----+-----+------+------+--------+ - * 3 | ED<2:0> | LTT<4:0> | + * 3 | ED<2:0> | LTT<4:0> | *------+------+-----+-----+-----+-----+------+------+--------+ * 4 | 0 | 0 | LBR | B- | 0 | B+ | RBR | LTT<5> | *------+------+-----+-----+-----+-----+------+------+--------+ @@ -2458,20 +2458,20 @@ static void wiimod_turntable_in_ext(struct wiimote_data *wdata, const __u8 *ext) * With Motion+ enabled, it will look like this: * Byte | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | *------+------+-----+-----+-----+-----+------+------+--------+ - * 1 | RTT<4:3> | SX <5:1> | 0 | - * 2 | RTT<2:1> | SY <5:1> | 0 | + * 1 | RTT<4:3> | SX <5:1> | 0 | + * 2 | RTT<2:1> | SY <5:1> | 0 | *------+------+-----+-----+-----+-----+------+------+--------+ * 3 |RTT<0>| ED<4:3> | CS<3:0> | RTT<5> | *------+------+-----+-----+-----+-----+------+------+--------+ - * 4 | ED<2:0> | LTT<4:0> | + * 4 | ED<2:0> | LTT<4:0> | *------+------+-----+-----+-----+-----+------+------+--------+ * 5 | 0 | 0 | LBR | B- | 0 | B+ | RBR | XXXX | *------+------+-----+-----+-----+-----+------+------+--------+ * 6 | LBB | 0 | RBG | BE | LBG | RBB | XXXX | XXXX | *------+------+-----+-----+-----+-----+------+------+--------+ */ - - be = !(ext[5] & 0x10); + + be = !(ext[5] & 0x10); cs = ((ext[2] & 0x1e)); sx = ext[0] & 0x3f; sy = ext[1] & 0x3f; @@ -2499,32 +2499,32 @@ static void wiimod_turntable_in_ext(struct wiimote_data *wdata, const __u8 *ext) input_report_abs(wdata->extension.input, ABS_HAT1X, ltt); input_report_abs(wdata->extension.input, ABS_HAT2X, cs); input_report_abs(wdata->extension.input, ABS_HAT3X, ed); - input_report_key(wdata->extension.input, - wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_G_RIGHT], + input_report_key(wdata->extension.input, + wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_G_RIGHT], rbg); input_report_key(wdata->extension.input, wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_R_RIGHT], rbr); - input_report_key(wdata->extension.input, - wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_B_RIGHT], + input_report_key(wdata->extension.input, + wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_B_RIGHT], rbb); - input_report_key(wdata->extension.input, - wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_G_LEFT], + input_report_key(wdata->extension.input, + wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_G_LEFT], lbg); - input_report_key(wdata->extension.input, - wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_R_LEFT], + input_report_key(wdata->extension.input, + wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_R_LEFT], lbr); - input_report_key(wdata->extension.input, - wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_B_LEFT], + input_report_key(wdata->extension.input, + wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_B_LEFT], lbb); - input_report_key(wdata->extension.input, - wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_EUPHORIA], + input_report_key(wdata->extension.input, + wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_EUPHORIA], be); - input_report_key(wdata->extension.input, - wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_PLUS], + input_report_key(wdata->extension.input, + wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_PLUS], bp); - input_report_key(wdata->extension.input, - wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_MINUS], + input_report_key(wdata->extension.input, + wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_MINUS], bm); input_sync(wdata->extension.input); @@ -2557,7 +2557,7 @@ static void wiimod_turntable_close(struct input_dev *dev) static int wiimod_turntable_probe(const struct wiimod_ops *ops, struct wiimote_data *wdata) { - int ret, i; + int ret, i; wdata->extension.input = input_allocate_device(); if (!wdata->extension.input) @@ -2594,9 +2594,9 @@ static int wiimod_turntable_probe(const struct wiimod_ops *ops, input_set_abs_params(wdata->extension.input, ABS_HAT1X, -8, 8, 0, 0); input_set_abs_params(wdata->extension.input, - ABS_HAT2X, 0, 31, 1, 1); + ABS_HAT2X, 0, 31, 1, 1); input_set_abs_params(wdata->extension.input, - ABS_HAT3X, 0, 7, 0, 0); + ABS_HAT3X, 0, 7, 0, 0); ret = input_register_device(wdata->extension.input); if (ret) goto err_free; From 6a39b2e950aeca3b62146a4deb66937555f1c772 Mon Sep 17 00:00:00 2001 From: Kwan Lai Chee Hou Date: Thu, 7 May 2026 09:53:35 +0800 Subject: [PATCH 1093/2758] wifi: ath12k: fix incorrect HT/VHT/HE/EHT MCS reporting in monitor mode [ Upstream commit 10085a654a4c2331d5f0cdc20bfc839a49fbb886 ] In monitor mode, the driver incorrectly assigns the legacy rate to the rate_idx field of the radiotap header for HT/VHT/HE/EHT frames, ignoring the actual MCS value parsed from the hardware. This causes packet analyzers (like Wireshark) to display incorrect MCS values (e.g., legacy base rates instead of the true MCS). Fix this by assigning ppdu_info->mcs as the default rate_mcs in ath12k_dp_mon_fill_rx_rate(), and remove rate_idx assignments in ath12k_dp_mon_update_radiotap() to preserve the previously calculated MCS values (including the HT NSS offset). Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ Fixes: 5393dcb45209 ("wifi: ath12k: change the status update in the monitor Rx") Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220864 Signed-off-by: Kwan Lai Chee Hou Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260507015336.14636-1-laicheehou9@gmail.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath12k/dp_mon.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index 737287a9aa462..44c5cff75f169 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -115,13 +115,14 @@ ath12k_dp_mon_fill_rx_rate(struct ath12k_pdev_dp *dp_pdev, bool is_cck; pkt_type = ppdu_info->preamble_type; - rate_mcs = ppdu_info->rate; + rate_mcs = ppdu_info->mcs; nss = ppdu_info->nss; sgi = ppdu_info->gi; switch (pkt_type) { case RX_MSDU_START_PKT_TYPE_11A: case RX_MSDU_START_PKT_TYPE_11B: + rate_mcs = ppdu_info->rate; is_cck = (pkt_type == RX_MSDU_START_PKT_TYPE_11B); if (rx_status->band < NUM_NL80211_BANDS) { struct ath12k *ar = ath12k_pdev_dp_to_ar(dp_pdev); @@ -471,13 +472,10 @@ void ath12k_dp_mon_update_radiotap(struct ath12k_pdev_dp *dp_pdev, rxs->encoding = RX_ENC_HE; ptr = skb_push(mon_skb, sizeof(struct ieee80211_radiotap_he)); ath12k_dp_mon_rx_update_radiotap_he(ppduinfo, ptr); - rxs->rate_idx = ppduinfo->rate; } else if (ppduinfo->vht_flags) { rxs->encoding = RX_ENC_VHT; - rxs->rate_idx = ppduinfo->rate; } else if (ppduinfo->ht_flags) { rxs->encoding = RX_ENC_HT; - rxs->rate_idx = ppduinfo->rate; } else { struct ath12k *ar; From 5187a92099778501f21af76ed6c8415d128e7298 Mon Sep 17 00:00:00 2001 From: Miaoqing Pan Date: Tue, 12 May 2026 10:11:08 +0800 Subject: [PATCH 1094/2758] wifi: ath12k: fix memory leak in ath12k_wifi7_dp_rx_h_verify_tkip_mic() [ Upstream commit 98d4f92ab6a1af2ea2ab590d7e2801b203110981 ] In ath12k_wifi7_dp_rx_h_verify_tkip_mic(), the call to ath12k_dp_rx_check_nwifi_hdr_len_valid() may return false when the NWIFI header length is invalid, causing the function to abort early with -EINVAL. When this happens, the error propagates to ath12k_wifi7_dp_rx_h_defrag(), which clears first_frag by setting it to NULL. As a result, the corresponding MSDU is no longer referenced by the defragmentation path and is never freed. This leads to a memory leak for the affected MSDU on this error path. Proper cleanup is required to ensure the MSDU is released when header validation fails during TKIP MIC verification. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Fixes: 9a0dddfb30f1 ("wifi: ath12k: Fix invalid data access in ath12k_dp_rx_h_undecap_nwifi") Signed-off-by: Miaoqing Pan Reviewed-by: Tamizh Chelvam Raja Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260512021108.2031651-1-miaoqing.pan@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c index 945680b3ebdfc..a5e290edaa898 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c @@ -1028,8 +1028,10 @@ static int ath12k_wifi7_dp_rx_h_verify_tkip_mic(struct ath12k_pdev_dp *dp_pdev, skb_pull(msdu, hal_rx_desc_sz); if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(dp, msdu, - rx_info))) + rx_info))) { + dev_kfree_skb_any(msdu); return -EINVAL; + } ath12k_dp_rx_h_ppdu(dp_pdev, rx_info); ath12k_dp_rx_h_undecap(dp_pdev, msdu, HAL_ENCRYPT_TYPE_TKIP_MIC, true, From e0140e094b196d92f2a4ce167ee73cbfab000290 Mon Sep 17 00:00:00 2001 From: Wei Zhang Date: Mon, 11 May 2026 21:49:04 -0700 Subject: [PATCH 1095/2758] wifi: ath12k: fix inconsistent arvif state in vdev_create error paths [ Upstream commit c972636efc63f0f43d725b59805dd1ae5bc4b31e ] ath12k_mac_vdev_create() has three error path issues that leave arvif in an inconsistent state: 1. When ath12k_wmi_vdev_create() fails, the function returns directly without clearing arvif->ar, which was already set before the WMI call. Subsequent code checking arvif->ar to determine vdev readiness will see a non-NULL value despite no vdev existing in firmware. 2. When ath12k_wmi_send_peer_delete_cmd() fails in err_peer_del, the code jumped to err: skipping the DP peer cleanup and vdev rollback, leaving num_created_vdevs, vdev maps and arvif list membership live. 3. When ath12k_wait_for_peer_delete_done() fails, the code jumped to err_vdev_del: skipping the DP peer cleanup. Fix by changing the ath12k_wmi_vdev_create() failure to goto err instead of returning directly, routing both err_peer_del failure paths through err_dp_peer_del: for proper DP peer and vdev rollback, and consolidating the arvif state cleanup at err:. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Fixes: 477cabfdb776 ("wifi: ath12k: modify link arvif creation and removal for MLO") Signed-off-by: Wei Zhang Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260512044906.1735821-2-wei.zhang@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath12k/mac.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 2cff9485c95af..6a3aaa1ba47ac 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -10293,7 +10293,7 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) if (ret) { ath12k_warn(ab, "failed to create WMI vdev %d: %d\n", arvif->vdev_id, ret); - return ret; + goto err; } ar->num_created_vdevs++; @@ -10440,13 +10440,13 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) if (ret) { ath12k_warn(ar->ab, "failed to delete peer vdev_id %d addr %pM\n", arvif->vdev_id, arvif->bssid); - goto err; + goto err_dp_peer_del; } ret = ath12k_wait_for_peer_delete_done(ar, arvif->vdev_id, arvif->bssid); if (ret) - goto err_vdev_del; + goto err_dp_peer_del; ar->num_peers--; } @@ -10463,8 +10463,6 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) ath12k_wmi_vdev_delete(ar, arvif->vdev_id); ar->num_created_vdevs--; - arvif->is_created = false; - arvif->ar = NULL; ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id); ab->free_vdev_map |= 1LL << arvif->vdev_id; ab->free_vdev_stats_id_map &= ~(1LL << arvif->vdev_stats_id); @@ -10473,6 +10471,7 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) spin_unlock_bh(&ar->data_lock); err: + arvif->is_created = false; arvif->ar = NULL; return ret; } From 5f5be2aa3b6d730c51dd4f8b432f2ad72823e63f Mon Sep 17 00:00:00 2001 From: Wei Zhang Date: Mon, 11 May 2026 21:49:05 -0700 Subject: [PATCH 1096/2758] wifi: ath12k: fix NULL deref in change_sta_links for unready link [ Upstream commit 47809a7c8348bc4a332ccc26a37c7145a5f609f8 ] _ieee80211_set_active_links() calls _ieee80211_link_use_channel() for each newly-added link and WARN_ON_ONCE()s if it fails. The call uses assign_on_failure=true, which allows mac80211 to continue despite driver failures, but when a mac80211-level channel validation fails (e.g., combinations check, DFS, or no available radio), drv_assign_vif_chanctx() is never reached. Since ath12k_mac_vdev_create() is only called from that path, arvif->is_created remains false and arvif->ar remains NULL for the failed link. The subsequent drv_change_sta_links() call reaches ath12k_mac_op_change_sta_links(), which allocates an arsta and sets ahsta->links_map |= BIT(link_id) for the broken link before checking whether the link is ready. When the vdev was never created, only station_add() is skipped, but the link remains in links_map. Any subsequent operation iterating links_map and dereferencing arvif->ar without a NULL check will crash. Two observed examples are NULL deref in ath12k_mac_ml_station_remove() on disconnect and in ath12k_mac_op_set_key() when wpa_supplicant installs PTK keys. BUG: Unable to handle kernel NULL pointer dereference at 0x00000000 pc : ath12k_mac_station_post_remove+0x40/0xe8 [ath12k] Call trace: ath12k_mac_station_post_remove+0x40/0xe8 [ath12k] ath12k_mac_op_sta_state+0xb60/0x1720 [ath12k] drv_sta_state+0x100/0xbd8 [mac80211] __sta_info_destroy_part2+0x148/0x178 [mac80211] ieee80211_set_disassoc+0x500/0x678 [mac80211] BUG: Unable to handle kernel NULL pointer dereference at 0x00000000 pc : ath12k_mac_op_set_key+0x1f8/0x2c0 [ath12k] Call trace: ath12k_mac_op_set_key+0x1f8/0x2c0 [ath12k] drv_set_key+0x70/0x100 [mac80211] ieee80211_key_enable_hw_accel+0x78/0x260 [mac80211] ieee80211_add_key+0x16c/0x2ac [mac80211] nl80211_new_key+0x138/0x280 [cfg80211] Fix this by checking arvif->is_created before calling ath12k_mac_alloc_assign_link_sta(). This prevents the broken link from entering links_map, so all subsequent operations iterating the bitmap are protected. The reliability of arvif->is_created across all error paths is ensured by the preceding patch. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Fixes: a27fa6148dac ("wifi: ath12k: support change_sta_links() mac80211 op") Signed-off-by: Wei Zhang Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260512044906.1735821-3-wei.zhang@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath12k/mac.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 6a3aaa1ba47ac..74af291350241 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -8054,16 +8054,16 @@ int ath12k_mac_op_change_sta_links(struct ieee80211_hw *hw, continue; arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); - arsta = ath12k_mac_alloc_assign_link_sta(ah, ahsta, ahvif, link_id); + if (!arvif || !arvif->is_created) + continue; - if (!arvif || !arsta) { + arsta = ath12k_mac_alloc_assign_link_sta(ah, ahsta, ahvif, link_id); + if (!arsta) { ath12k_hw_warn(ah, "Failed to alloc/assign link sta"); continue; } ar = arvif->ar; - if (!ar) - continue; ret = ath12k_mac_station_add(ar, arvif, arsta); if (ret) { From d2306ad238c665d4d39e0aa8b44953c2df60728f Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Thu, 21 May 2026 10:33:29 -0700 Subject: [PATCH 1097/2758] ata: libata: Fix ata_exec_internal() [ Upstream commit aa0ae1c35f7b3e9afed2324bed5f5c87ad55b92c ] Some but not all ata_exec_internal() calls happen from the context of the ATA error handler. Commit c0c362b60e25 ("libata: implement cross-port EH exclusion") added ata_eh_release() and ata_eh_acquire() calls in ata_exec_internal(). Calling these functions is necessary if the caller holds the eh_mutex but is not allowed if the caller doesn't hold that mutex. Fix this by only calling ata_eh_release() and ata_eh_acquire() if the caller holds the eh_mutex. An example of an indirect caller of ata_exec_internal() that does not hold the eh_mutex is ata_host_register(). Fixes: c0c362b60e25 ("libata: implement cross-port EH exclusion") Signed-off-by: Bart Van Assche Reviewed-by: Niklas Cassel Reviewed-by: Damien Le Moal Reviewed-by: Hannes Reinecke Signed-off-by: Niklas Cassel Signed-off-by: Sasha Levin --- drivers/ata/libata-core.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 3d0027ec33c2a..3e19a00a92393 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -1540,6 +1540,7 @@ unsigned int ata_exec_internal(struct ata_device *dev, struct ata_taskfile *tf, { struct ata_link *link = dev->link; struct ata_port *ap = link->ap; + const bool owns_eh_mutex = ap->host->eh_owner == current; u8 command = tf->command; struct ata_queued_cmd *qc; struct scatterlist sgl; @@ -1617,11 +1618,25 @@ unsigned int ata_exec_internal(struct ata_device *dev, struct ata_taskfile *tf, } } - ata_eh_release(ap); + if (owns_eh_mutex) { + /* + * To prevent that the compiler complains about the + * ata_eh_release() call below. + */ + __acquire(&ap->host->eh_mutex); + ata_eh_release(ap); + } rc = wait_for_completion_timeout(&wait, msecs_to_jiffies(timeout)); - ata_eh_acquire(ap); + if (owns_eh_mutex) { + ata_eh_acquire(ap); + /* + * To prevent that the compiler complains about the above + * ata_eh_acquire() call. + */ + __release(&ap->host->eh_mutex); + } ata_sff_flush_pio_task(ap); From c04e59bcaf6f91f6eefa38a6e7e57ee5a00b44f5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 1 Jun 2026 18:55:15 +0200 Subject: [PATCH 1098/2758] ACPI: button: Fix lid_device value leak past driver removal [ Upstream commit f8600e0d1ac60e6eac34bc9c7e8cf78f7a4c368f ] Static variable lid_device is set when the ACPI button driver probes the last lid device (under the assumptions that there will be only one lid device in the system) and never cleared, but in principle it should be reset when the driver unbinds from the lid device pointed to by it. Address that and add locking that is needed to clear and set that variable safely. Fixes: 7e12715ecc47 ("ACPI button: provide lid status functions") Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/6281379.lOV4Wx5bFT@rafael.j.wysocki Signed-off-by: Sasha Levin --- drivers/acpi/button.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index d80276368b810..5df470eea7540 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -182,7 +182,6 @@ struct acpi_button { bool gpe_enabled; }; -static struct acpi_device *lid_device; static long lid_init_state = -1; static unsigned long lid_report_interval __read_mostly = 500; @@ -378,9 +377,29 @@ static int acpi_button_remove_fs(struct acpi_button *button) return 0; } +static struct acpi_device *lid_device; +static DEFINE_MUTEX(acpi_lid_lock); + +static void acpi_lid_save(struct acpi_device *adev) +{ + guard(mutex)(&acpi_lid_lock); + + lid_device = adev; +} + +static void acpi_lid_forget(struct acpi_device *adev) +{ + guard(mutex)(&acpi_lid_lock); + + if (lid_device == adev) + lid_device = NULL; +} + /* Driver Interface */ int acpi_lid_open(void) { + guard(mutex)(&acpi_lid_lock); + if (!lid_device) return -ENODEV; @@ -674,7 +693,7 @@ static int acpi_button_probe(struct platform_device *pdev) * This assumes there's only one lid device, or if there are * more we only care about the last one... */ - lid_device = device; + acpi_lid_save(device); } pr_info("%s [%s]\n", name, acpi_device_bid(device)); @@ -696,6 +715,9 @@ static void acpi_button_remove(struct platform_device *pdev) struct acpi_button *button = platform_get_drvdata(pdev); struct acpi_device *adev = button->adev; + if (button->type == ACPI_BUTTON_TYPE_LID) + acpi_lid_forget(adev); + switch (adev->device_type) { case ACPI_BUS_TYPE_POWER_BUTTON: acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, From 2cbc214a7917f9294d43b7178d8de6d1596bdd4a Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Mon, 25 May 2026 00:01:58 -0400 Subject: [PATCH 1099/2758] ARM: imx31: Fix IIM mapping leak in revision check [ Upstream commit ccb4b54b8ecf1ebafef96d538cd6c5c8455bb390 ] mx31_read_cpu_rev() maps the IIM registers with of_iomap() to read the silicon revision, but returns without unmapping the MMIO mapping. Keep the normalized revision value in a local variable and route the return path through iounmap() after the revision register has been read. Fixes: 3172225d45bd ("ARM: imx31: Retrieve the IIM base address from devicetree") Signed-off-by: Yuho Choi Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- arch/arm/mach-imx/cpu-imx31.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-imx/cpu-imx31.c b/arch/arm/mach-imx/cpu-imx31.c index 35c544924e509..e81ef9e36a1fa 100644 --- a/arch/arm/mach-imx/cpu-imx31.c +++ b/arch/arm/mach-imx/cpu-imx31.c @@ -36,6 +36,7 @@ static int mx31_read_cpu_rev(void) void __iomem *iim_base; struct device_node *np; u32 i, srev; + int rev = IMX_CHIP_REVISION_UNKNOWN; np = of_find_compatible_node(NULL, NULL, "fsl,imx31-iim"); iim_base = of_iomap(np, 0); @@ -48,13 +49,17 @@ static int mx31_read_cpu_rev(void) for (i = 0; i < ARRAY_SIZE(mx31_cpu_type); i++) if (srev == mx31_cpu_type[i].srev) { + rev = mx31_cpu_type[i].rev; imx_print_silicon_rev(mx31_cpu_type[i].name, mx31_cpu_type[i].rev); - return mx31_cpu_type[i].rev; + goto out; } imx_print_silicon_rev("i.MX31", IMX_CHIP_REVISION_UNKNOWN); - return IMX_CHIP_REVISION_UNKNOWN; + +out: + iounmap(iim_base); + return rev; } int mx31_revision(void) From 6034e3d19b98b3d28a2f55c62182caf460c168cf Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 19 May 2026 10:35:26 -0700 Subject: [PATCH 1100/2758] x86/cpu: Keep the PROCESSOR_SELECT menu together [ Upstream commit 1df61a8b2d01c560822a0421f2a76af7fda34c1f ] Having a stray kconfig symbol in the middle of the PROCESSOR_SELECT menu (this symbol plus its dependent symbols) causes the menu dependencies not to be displayed correctly in "make {menu,n,g,x}config". Move the BROADCAST_TLB_FLUSH symbol away from the PROCESSOR_SELECT menu so that the list of processors is displayed correctly. Fixes: 767ae437a32d ("x86/mm: Add INVLPGB feature and Kconfig entry") Signed-off-by: Randy Dunlap Signed-off-by: Borislav Petkov (AMD) Link: https://patch.msgid.link/20260519173526.10985-1-rdunlap@infradead.org Signed-off-by: Sasha Levin --- arch/x86/Kconfig.cpu | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/x86/Kconfig.cpu b/arch/x86/Kconfig.cpu index d7ba9219cb47d..df003a42d25a6 100644 --- a/arch/x86/Kconfig.cpu +++ b/arch/x86/Kconfig.cpu @@ -334,10 +334,6 @@ menuconfig PROCESSOR_SELECT This lets you choose what x86 vendor support code your kernel will include. -config BROADCAST_TLB_FLUSH - def_bool y - depends on CPU_SUP_AMD && 64BIT - config CPU_SUP_INTEL default y bool "Support Intel processors" if PROCESSOR_SELECT @@ -458,3 +454,7 @@ config CPU_SUP_VORTEX_32 makes the kernel a tiny bit smaller. If unsure, say N. + +config BROADCAST_TLB_FLUSH + def_bool y + depends on CPU_SUP_AMD && 64BIT From 4eafa810b042d985ec6bbf5b514414e73cee6f6f Mon Sep 17 00:00:00 2001 From: Alison Schofield Date: Wed, 27 May 2026 19:16:22 -0700 Subject: [PATCH 1101/2758] nvdimm/btt: Handle preemption in BTT lane acquisition [ Upstream commit 8d4b989d9c9afe5f185aa5853b666fc4617afe9e ] BTT lanes serialize access to per-lane metadata and workspace state during BTT I/O. The btt-check unit test reports data mismatches during BTT writes due to a race in lane acquisition that can lead to silent data corruption. The existing lane model uses a spinlock together with a per-CPU recursion count. That recursion model stopped being valid after BTT lanes became preemptible: another task can run on the same CPU, observe a non-zero recursion count, bypass locking, and use the same lane concurrently. BTT lanes are also held across arena_write_bytes() calls. That path reaches nsio_rw_bytes(), which flushes writes with nvdimm_flush(). Some provider flush callbacks can sleep, making a spinlock the wrong primitive for the lane lifetime. Replace the spinlock-based recursion model with a dynamically allocated per-lane mutex array and take the lane lock unconditionally. Add might_sleep() to catch any future atomic-context caller. Found with the ndctl unit test btt-check.sh. Fixes: 36c75ce3bd29 ("nd_btt: Make BTT lanes preemptible") Assisted-by: Claude-Sonnet:4.5 Tested-by: Aboorva Devarajan Reviewed-by: Aboorva Devarajan Reviewed-by: Vishal Verma Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260528021625.618462-1-alison.schofield@intel.com Signed-off-by: Alison Schofield Signed-off-by: Sasha Levin --- Documentation/driver-api/nvdimm/btt.rst | 5 +- drivers/nvdimm/nd.h | 11 ++--- drivers/nvdimm/region_devs.c | 66 +++++++++---------------- 3 files changed, 29 insertions(+), 53 deletions(-) diff --git a/Documentation/driver-api/nvdimm/btt.rst b/Documentation/driver-api/nvdimm/btt.rst index 2d8269f834bd6..d29fab95f1494 100644 --- a/Documentation/driver-api/nvdimm/btt.rst +++ b/Documentation/driver-api/nvdimm/btt.rst @@ -161,9 +161,8 @@ process:: nlanes = min(nfree, num_cpus) A lane number is obtained at the start of any IO, and is used for indexing into -all the on-disk and in-memory data structures for the duration of the IO. If -there are more CPUs than the max number of available lanes, than lanes are -protected by spinlocks. +all the on-disk and in-memory data structures for the duration of the IO. Lanes +are protected by mutexes. d. In-memory data structure: Read Tracking Table (RTT) diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index b199eea3260ef..197e5368c0a46 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h @@ -365,11 +365,6 @@ unsigned sizeof_namespace_label(struct nvdimm_drvdata *ndd); for (res = (ndd)->dpa.child, next = res ? res->sibling : NULL; \ res; res = next, next = next ? next->sibling : NULL) -struct nd_percpu_lane { - int count; - spinlock_t lock; -}; - enum nd_label_flags { ND_LABEL_REAP, }; @@ -400,6 +395,10 @@ struct nd_mapping { struct nvdimm_drvdata *ndd; }; +struct nd_lane { + struct mutex lock; /* serialize lane access */ +} ____cacheline_aligned_in_smp; + struct nd_region { struct device dev; struct ida ns_ida; @@ -420,7 +419,7 @@ struct nd_region { struct kernfs_node *bb_state; struct badblocks bb; struct nd_interleave_set *nd_set; - struct nd_percpu_lane __percpu *lane; + struct nd_lane *lane; int (*flush)(struct nd_region *nd_region, struct bio *bio); struct nd_mapping mapping[] __counted_by(ndr_mappings); }; diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c index e35c2e18518f0..5e079d61cbaa3 100644 --- a/drivers/nvdimm/region_devs.c +++ b/drivers/nvdimm/region_devs.c @@ -192,7 +192,9 @@ static void nd_region_release(struct device *dev) put_device(&nvdimm->dev); } - free_percpu(nd_region->lane); + for (i = 0; i < nd_region->num_lanes; i++) + mutex_destroy(&nd_region->lane[i].lock); + kfree(nd_region->lane); if (!test_bit(ND_REGION_CXL, &nd_region->flags)) memregion_free(nd_region->id); kfree(nd_region); @@ -904,52 +906,30 @@ void nd_region_advance_seeds(struct nd_region *nd_region, struct device *dev) * nd_region_acquire_lane - allocate and lock a lane * @nd_region: region id and number of lanes possible * - * A lane correlates to a BLK-data-window and/or a log slot in the BTT. - * We optimize for the common case where there are 256 lanes, one - * per-cpu. For larger systems we need to lock to share lanes. For now - * this implementation assumes the cost of maintaining an allocator for - * free lanes is on the order of the lock hold time, so it implements a - * static lane = cpu % num_lanes mapping. + * A lane correlates to a log slot in the BTT. Lanes are shared across + * CPUs using a static lane = cpu % num_lanes mapping, with a per-lane + * mutex to serialize access. * - * In the case of a BTT instance on top of a BLK namespace a lane may be - * acquired recursively. We lock on the first instance. - * - * In the case of a BTT instance on top of PMEM, we only acquire a lane - * for the BTT metadata updates. + * Callers must be in sleepable context. The only in-tree caller is + * BTT's ->submit_bio handler (btt_read_pg / btt_write_pg). */ unsigned int nd_region_acquire_lane(struct nd_region *nd_region) + __acquires(&nd_region->lane[lane].lock) { - unsigned int cpu, lane; - - migrate_disable(); - cpu = smp_processor_id(); - if (nd_region->num_lanes < nr_cpu_ids) { - struct nd_percpu_lane *ndl_lock, *ndl_count; + unsigned int lane; - lane = cpu % nd_region->num_lanes; - ndl_count = per_cpu_ptr(nd_region->lane, cpu); - ndl_lock = per_cpu_ptr(nd_region->lane, lane); - if (ndl_count->count++ == 0) - spin_lock(&ndl_lock->lock); - } else - lane = cpu; + might_sleep(); + lane = raw_smp_processor_id() % nd_region->num_lanes; + mutex_lock(&nd_region->lane[lane].lock); return lane; } EXPORT_SYMBOL(nd_region_acquire_lane); void nd_region_release_lane(struct nd_region *nd_region, unsigned int lane) + __releases(&nd_region->lane[lane].lock) { - if (nd_region->num_lanes < nr_cpu_ids) { - unsigned int cpu = smp_processor_id(); - struct nd_percpu_lane *ndl_lock, *ndl_count; - - ndl_count = per_cpu_ptr(nd_region->lane, cpu); - ndl_lock = per_cpu_ptr(nd_region->lane, lane); - if (--ndl_count->count == 0) - spin_unlock(&ndl_lock->lock); - } - migrate_enable(); + mutex_unlock(&nd_region->lane[lane].lock); } EXPORT_SYMBOL(nd_region_release_lane); @@ -1019,17 +999,16 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus, goto err_id; } - nd_region->lane = alloc_percpu(struct nd_percpu_lane); + nd_region->num_lanes = ndr_desc->num_lanes; + if (!nd_region->num_lanes) + goto err_percpu; + nd_region->lane = kcalloc(nd_region->num_lanes, + sizeof(*nd_region->lane), GFP_KERNEL); if (!nd_region->lane) goto err_percpu; - for (i = 0; i < nr_cpu_ids; i++) { - struct nd_percpu_lane *ndl; - - ndl = per_cpu_ptr(nd_region->lane, i); - spin_lock_init(&ndl->lock); - ndl->count = 0; - } + for (i = 0; i < nd_region->num_lanes; i++) + mutex_init(&nd_region->lane[i].lock); for (i = 0; i < ndr_desc->num_mappings; i++) { struct nd_mapping_desc *mapping = &ndr_desc->mapping[i]; @@ -1046,7 +1025,6 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus, } nd_region->provider_data = ndr_desc->provider_data; nd_region->nd_set = ndr_desc->nd_set; - nd_region->num_lanes = ndr_desc->num_lanes; nd_region->flags = ndr_desc->flags; nd_region->ro = ro; nd_region->numa_node = ndr_desc->numa_node; From ea03b98f3fc26556ac699a023e2c1e603e875dd8 Mon Sep 17 00:00:00 2001 From: Neal Cardwell Date: Sun, 31 May 2026 11:35:57 -0700 Subject: [PATCH 1102/2758] tcp_bbr: fix SPDX-License-Identifier to be GPL-2.0 OR BSD-3-Clause [ Upstream commit 4490516d2109e105daf732681435c5c075b5d61b ] Since TCP BBR congestion control was introduced in commit 0f8782ea1497 ("tcp_bbr: add BBR congestion control") it has always been offered as "Dual BSD/GPL": MODULE_LICENSE("Dual BSD/GPL"); A GPL-2.0-only SPDX header was erroneously added in the recent commit 2ed4b46b4fc7 ("net: Add SPDX ids to some source files"). This commit revises the tcp_bbr.c SPDX-License-Identifier to note that this file is licensed as "GPL-2.0 OR BSD-3-Clause". Fixes: 2ed4b46b4fc7 ("net: Add SPDX ids to some source files") Signed-off-by: Neal Cardwell Cc: Yuchung Cheng Cc: Van Jacobson Cc: Soheil Hassas Yeganeh Reviewed-by: Tim Bird Link: https://patch.msgid.link/20260531183558.2337381-1-ncardwell.sw@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv4/tcp_bbr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/tcp_bbr.c b/net/ipv4/tcp_bbr.c index aec7805b1d376..82378a2bfd1e1 100644 --- a/net/ipv4/tcp_bbr.c +++ b/net/ipv4/tcp_bbr.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* Bottleneck Bandwidth and RTT (BBR) congestion control * * BBR congestion control computes the sending rate based on the delivery From 1f04d24e4bf134e5115d6a53862511436c6ae2ed Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 13 May 2026 19:42:36 +0200 Subject: [PATCH 1103/2758] scsi: Revert "scsi: Fix sas_user_scan() to handle wildcard and multi-channel scans" [ Upstream commit 8c292e89bd831c8a13e92f3429ef66bbe0b83677 ] This reverts commit 37c4e72b0651e7697eb338cd1fb09feef472cc1a. Said commit causes excessive resource usage and even system freeze with some controllers, e.g. smartpqi and hisi_sas. The justification provided by the patch authors [1] was supporting a special mode of the mpi3mr and mpt3sas, so-called "Tri-mode", in which NVMe drives are exposed as SCSI devices on a separate channel. While that's useful for these drivers, it seems wrong to cause major breakage for other drivers for the sake of this feature. [1] https://lore.kernel.org/linux-scsi/CAFdVvOwjy+2ORJ6uJkspiLTPF05481U7gcS4QohFOFGPqAs8ig@mail.gmail.com/ Fixes: 37c4e72b0651 ("scsi: Fix sas_user_scan() to handle wildcard and multi-channel scans") Signed-off-by: Martin Wilck Cc: Don Brace Cc: storagedev@microchip.com Cc: Ranjan Kumar Cc: Sathya Prakash Veerichetty Cc: Kashyap Desai Cc: Sumit Saxena Cc: mpi3mr-linuxdrv.pdl@broadcom.com Cc: MPT-FusionLinux.pdl@broadcom.com Cc: Yihang Li Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260513174236.430465-3-mwilck@suse.com Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/scsi/scsi_scan.c | 2 +- drivers/scsi/scsi_transport_sas.c | 60 +++++++------------------------ 2 files changed, 13 insertions(+), 49 deletions(-) diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index ef22a4228b855..b118ed0bf53f8 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -1910,7 +1910,7 @@ int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel, return 0; } -EXPORT_SYMBOL(scsi_scan_host_selected); + static void scsi_sysfs_add_devices(struct Scsi_Host *shost) { struct scsi_device *sdev; diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index 13412702188e4..d8f2377b017fe 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -40,8 +40,6 @@ #include #include "scsi_sas_internal.h" -#include "scsi_priv.h" - struct sas_host_attrs { struct list_head rphy_list; struct mutex lock; @@ -1685,22 +1683,6 @@ int scsi_is_sas_rphy(const struct device *dev) } EXPORT_SYMBOL(scsi_is_sas_rphy); -static void scan_channel_zero(struct Scsi_Host *shost, uint id, u64 lun) -{ - struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); - struct sas_rphy *rphy; - - list_for_each_entry(rphy, &sas_host->rphy_list, list) { - if (rphy->identify.device_type != SAS_END_DEVICE || - rphy->scsi_target_id == -1) - continue; - - if (id == SCAN_WILD_CARD || id == rphy->scsi_target_id) { - scsi_scan_target(&rphy->dev, 0, rphy->scsi_target_id, - lun, SCSI_SCAN_MANUAL); - } - } -} /* * SCSI scan helper @@ -1710,41 +1692,23 @@ static int sas_user_scan(struct Scsi_Host *shost, uint channel, uint id, u64 lun) { struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); - int res = 0; - int i; - - switch (channel) { - case 0: - mutex_lock(&sas_host->lock); - scan_channel_zero(shost, id, lun); - mutex_unlock(&sas_host->lock); - break; - - case SCAN_WILD_CARD: - mutex_lock(&sas_host->lock); - scan_channel_zero(shost, id, lun); - mutex_unlock(&sas_host->lock); + struct sas_rphy *rphy; - for (i = 1; i <= shost->max_channel; i++) { - res = scsi_scan_host_selected(shost, i, id, lun, - SCSI_SCAN_MANUAL); - if (res) - goto exit_scan; - } - break; + mutex_lock(&sas_host->lock); + list_for_each_entry(rphy, &sas_host->rphy_list, list) { + if (rphy->identify.device_type != SAS_END_DEVICE || + rphy->scsi_target_id == -1) + continue; - default: - if (channel <= shost->max_channel) { - res = scsi_scan_host_selected(shost, channel, id, lun, - SCSI_SCAN_MANUAL); - } else { - res = -EINVAL; + if ((channel == SCAN_WILD_CARD || channel == 0) && + (id == SCAN_WILD_CARD || id == rphy->scsi_target_id)) { + scsi_scan_target(&rphy->dev, 0, rphy->scsi_target_id, + lun, SCSI_SCAN_MANUAL); } - break; } + mutex_unlock(&sas_host->lock); -exit_scan: - return res; + return 0; } From 7c58ace08f180f8e249e714d1623388362f9d807 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 1 Jun 2026 17:02:42 +0200 Subject: [PATCH 1104/2758] bpf: Reject exclusive maps as inner maps in map-in-map [ Upstream commit 9a3c3c49c333760c8944dadacbe114c1884546ef ] An exclusive map (created with excl_prog_hash) is bound to a single program by hash: check_map_prog_compatibility() refuses to load any program whose digest does not match map->excl_prog_sha. That check only runs for maps a program references directly, i.e. its used_maps. A map reached at runtime through a map-of-maps is never in used_maps, and bpf_map_meta_equal() does not consider excl_prog_sha, so an exclusive map can be inserted into a non-exclusive outer map and then looked up and mutated by an unrelated program, bypassing the exclusivity guarantee. For the signed loader this defeats the metadata map exclusivity check added in the signed loader: the cached map->sha[] is validated against the signed hash while another program on a hostile host rewrites the frozen map's contents through the outer map. Fixes: baefdbdf6812 ("bpf: Implement exclusive map creation") Reported-by: sashiko Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/r/20260601150248.394863-2-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- kernel/bpf/map_in_map.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/map_in_map.c b/kernel/bpf/map_in_map.c index 645bd30bc9a9d..d2cbab4bdf644 100644 --- a/kernel/bpf/map_in_map.c +++ b/kernel/bpf/map_in_map.c @@ -20,7 +20,8 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd) /* Does not support >1 level map-in-map */ if (inner_map->inner_map_meta) return ERR_PTR(-EINVAL); - + if (inner_map->excl_prog_sha) + return ERR_PTR(-ENOTSUPP); if (!inner_map->ops->map_meta_equal) return ERR_PTR(-ENOTSUPP); @@ -101,6 +102,8 @@ void *bpf_map_fd_get_ptr(struct bpf_map *map, inner_map = __bpf_map_get(f); if (IS_ERR(inner_map)) return inner_map; + if (inner_map->excl_prog_sha) + return ERR_PTR(-ENOTSUPP); inner_map_meta = map->inner_map_meta; if (inner_map_meta->ops->map_meta_equal(inner_map_meta, inner_map)) From 0dad5adeb34b6f78a883e8faa6a1c947a240e7c9 Mon Sep 17 00:00:00 2001 From: KP Singh Date: Mon, 1 Jun 2026 17:02:44 +0200 Subject: [PATCH 1105/2758] libbpf: Reject non-exclusive metadata maps in the signed loader [ Upstream commit 0fb6c9ed6493b4af01be8bb0a384574eba7df636 ] The loader verifies map->sha against the metadata hash in its instructions. map->sha is calculated when BPF_OBJ_GET_INFO_BY_FD is called on the frozen map. While the map is frozen, the /signed loader/ must also ensure the map is exclusive, as, without exclusivity (which a hostile host could just omit when loading the loader), another BPF program with map access can mutate the contents afterwards, so the check passes on stale data. With the extra check as part of the signed loader, it now refuses to move on with map->sha validation if the host set it up wrongly. Fixes: fb2b0e290147 ("libbpf: Update light skeleton for signing") Signed-off-by: KP Singh Co-developed-by: Daniel Borkmann Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/r/20260601150248.394863-4-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- include/linux/bpf.h | 1 + kernel/bpf/syscall.c | 7 +++++++ tools/lib/bpf/gen_loader.c | 17 +++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index cd191c5fdb0a5..487f4653d8a66 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -295,6 +295,7 @@ struct bpf_map_owner { struct bpf_map { u8 sha[SHA256_DIGEST_SIZE]; + u32 excl; const struct bpf_map_ops *ops; struct bpf_map *inner_map_meta; #ifdef CONFIG_SECURITY diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index d1274486a564e..2edda1844ec7f 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1572,6 +1572,13 @@ static int map_create(union bpf_attr *attr, bpfptr_t uattr) err = -EFAULT; goto free_map; } + + /* See libbpf: emit_signature_match() */ + BUILD_BUG_ON(offsetof(struct bpf_map, excl) != SHA256_DIGEST_SIZE); + BUILD_BUG_ON(!__same_type(map->excl, u32)); + BUILD_BUG_ON(offsetof(struct bpf_map, sha) != 0); + BUILD_BUG_ON(!__same_type(map->sha, u8[SHA256_DIGEST_SIZE])); + map->excl = 1; } else if (attr->excl_prog_hash_size) { err = -EINVAL; goto free_map; diff --git a/tools/lib/bpf/gen_loader.c b/tools/lib/bpf/gen_loader.c index e63a01101e2d8..91790dc82eb6c 100644 --- a/tools/lib/bpf/gen_loader.c +++ b/tools/lib/bpf/gen_loader.c @@ -585,6 +585,23 @@ static void emit_signature_match(struct bpf_gen *gen) __s64 off; int i; + /* + * Reject if the metadata map is not exclusive. Without exclusivity + * the cached map->sha[] verified above can be stale: another BPF + * program with map access could have mutated the contents between + * BPF_OBJ_GET_INFO_BY_FD and loader execution. + */ + emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX, + 0, 0, 0, 0)); + emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, SHA256_DIGEST_LENGTH)); + off = -(gen->insn_cur - gen->insn_start - gen->cleanup_label) / 8 - 2; + if (is_simm16(off)) { + emit(gen, BPF_MOV64_IMM(BPF_REG_7, -EINVAL)); + emit(gen, BPF_JMP_IMM(BPF_JNE, BPF_REG_2, 1, off)); + } else { + gen->error = -ERANGE; + } + for (i = 0; i < SHA256_DWORD_SIZE; i++) { emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX, 0, 0, 0, 0)); From b544115380a052d45ffc345f07c91755506b7a21 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 1 Jun 2026 17:02:45 +0200 Subject: [PATCH 1106/2758] libbpf: Skip initial_value override on signed loaders [ Upstream commit 61e084152328867fe2279cc790573aae39959cd5 ] bpf_gen__map_update_elem() emits code that, when the host-supplied loader ctx provides a non-NULL map_desc[idx].initial_value, overwrites the blob value with bytes read from the host (bpf_copy_from_user / bpf_probe_read_kernel) before the BPF_MAP_UPDATE_ELEM that populates the program's .data/.rodata/.bss maps. This override runs after emit_signature_match() has validated map->sha[], and initial_value is part of neither the signed loader instructions nor the hashed data blob. For a signed loader this lets an untrusted host substitute global-variable contents into a program whose code carries a valid signature, thus weakening what the signature attests to. The blob already contains the signer-provided value (added via add_data() and covered by the embedded, signed hash), so simply skip emitting the override for signed loaders (gen_hash). Runtime initialization stays available for the unsigned light-skeleton path as before. The jump offsets within the override block are internal to it, so guarding the whole block leaves them unchanged. Fixes: ea923080c145 ("libbpf: Embed and verify the metadata hash in the loader") Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/r/20260601150248.394863-5-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- tools/lib/bpf/gen_loader.c | 39 +++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/tools/lib/bpf/gen_loader.c b/tools/lib/bpf/gen_loader.c index 91790dc82eb6c..0cb14e030b8be 100644 --- a/tools/lib/bpf/gen_loader.c +++ b/tools/lib/bpf/gen_loader.c @@ -1186,27 +1186,36 @@ void bpf_gen__map_update_elem(struct bpf_gen *gen, int map_idx, void *pvalue, value = add_data(gen, pvalue, value_size); key = add_data(gen, &zero, sizeof(zero)); - /* if (map_desc[map_idx].initial_value) { + /* + * if (map_desc[map_idx].initial_value) { * if (ctx->flags & BPF_SKEL_KERNEL) * bpf_probe_read_kernel(value, value_size, initial_value); * else * bpf_copy_from_user(value, value_size, initial_value); * } + * + * The runtime initial_value comes from the host-supplied loader + * ctx and would overwrite the blob value after emit_signature_match() + * has already validated map->sha[]. For a signed loader (gen_hash) + * the attested blob value must be authoritative, so skip the override + * and leave the hashed value in place. */ - emit(gen, BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_6, - sizeof(struct bpf_loader_ctx) + - sizeof(struct bpf_map_desc) * map_idx + - offsetof(struct bpf_map_desc, initial_value))); - emit(gen, BPF_JMP_IMM(BPF_JEQ, BPF_REG_3, 0, 8)); - emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE, - 0, 0, 0, value)); - emit(gen, BPF_MOV64_IMM(BPF_REG_2, value_size)); - emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, - offsetof(struct bpf_loader_ctx, flags))); - emit(gen, BPF_JMP_IMM(BPF_JSET, BPF_REG_0, BPF_SKEL_KERNEL, 2)); - emit(gen, BPF_EMIT_CALL(BPF_FUNC_copy_from_user)); - emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, 1)); - emit(gen, BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel)); + if (!OPTS_GET(gen->opts, gen_hash, false)) { + emit(gen, BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_6, + sizeof(struct bpf_loader_ctx) + + sizeof(struct bpf_map_desc) * map_idx + + offsetof(struct bpf_map_desc, initial_value))); + emit(gen, BPF_JMP_IMM(BPF_JEQ, BPF_REG_3, 0, 8)); + emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE, + 0, 0, 0, value)); + emit(gen, BPF_MOV64_IMM(BPF_REG_2, value_size)); + emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, + offsetof(struct bpf_loader_ctx, flags))); + emit(gen, BPF_JMP_IMM(BPF_JSET, BPF_REG_0, BPF_SKEL_KERNEL, 2)); + emit(gen, BPF_EMIT_CALL(BPF_FUNC_copy_from_user)); + emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, 1)); + emit(gen, BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel)); + } map_update_attr = add_data(gen, &attr, attr_size); pr_debug("gen: map_update_elem: idx %d, value: off %d size %d, attr: off %d size %d\n", From 0777038191f8dcd154611cc13ab5d56313ccf96a Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 1 Jun 2026 17:02:46 +0200 Subject: [PATCH 1107/2758] libbpf: Skip max_entries override on signed loaders [ Upstream commit 60214435b365ecdd40b2f96d4e54564b5c927645 ] bpf_gen__map_create() lets the host-supplied loader ctx override a map's max_entries at runtime (map_desc[idx].max_entries, when non-zero). This is how the light skeleton sizes maps to the target machine, but it happens after emit_signature_match() and is covered by neither the signed loader instructions nor the hashed blob. For a signed loader this means an untrusted host can re-dimension the program's maps, outside what the signature attests to. Gate the override on gen_hash so signed loaders use the signer-provided max_entries baked into the blob. Fixes: ea923080c145 ("libbpf: Embed and verify the metadata hash in the loader") Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/r/20260601150248.394863-6-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- tools/lib/bpf/gen_loader.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tools/lib/bpf/gen_loader.c b/tools/lib/bpf/gen_loader.c index 0cb14e030b8be..40004aa3af71b 100644 --- a/tools/lib/bpf/gen_loader.c +++ b/tools/lib/bpf/gen_loader.c @@ -545,13 +545,22 @@ void bpf_gen__map_create(struct bpf_gen *gen, default: break; } - /* conditionally update max_entries */ - if (map_idx >= 0) + + /* + * Conditionally update max_entries from the host-supplied loader + * ctx. This sizes the map at runtime, but for a signed loader + * (gen_hash) it would let an untrusted host re-dimension the + * program's maps after emit_signature_match(), outside what the + * signature attests to. Keep the signer-provided max_entries + * baked into the blob in that case. + */ + if (map_idx >= 0 && !OPTS_GET(gen->opts, gen_hash, false)) move_ctx2blob(gen, attr_field(map_create_attr, max_entries), 4, sizeof(struct bpf_loader_ctx) + sizeof(struct bpf_map_desc) * map_idx + offsetof(struct bpf_map_desc, max_entries), true /* check that max_entries != 0 */); + /* emit MAP_CREATE command */ emit_sys_bpf(gen, BPF_MAP_CREATE, map_create_attr, attr_size); debug_ret(gen, "map_create %s idx %d type %d value_size %d value_btf_id %d", From 1538da12ed88fd8f1272d419992a1309593af39f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 30 May 2026 22:45:48 +0300 Subject: [PATCH 1108/2758] scsi: pm8001: Fix error code in non_fatal_log_show() [ Upstream commit 1b6f03b7ae9ee27054c55bb55a69d05555a78516 ] The non_fatal_log_show() function is supposed to return negative error codes on failure. But because the error codes are saved in a u32 and then cast to signed long, they end up being high positive values instead of negative. Remove the intermediary u32 variable to fix this bug. Fixes: dba2cc03b9db ("scsi: pm80xx: sysfs attribute for non fatal dump") Signed-off-by: Dan Carpenter Acked-by: Jack Wang Link: https://patch.msgid.link/ahs-bEsBJH0KhnsX@stanley.mountain Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/scsi/pm8001/pm8001_ctl.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/scsi/pm8001/pm8001_ctl.c b/drivers/scsi/pm8001/pm8001_ctl.c index cbfda8c04e956..c10854ec44c7b 100644 --- a/drivers/scsi/pm8001/pm8001_ctl.c +++ b/drivers/scsi/pm8001/pm8001_ctl.c @@ -588,10 +588,7 @@ static DEVICE_ATTR(fatal_log, S_IRUGO, pm8001_ctl_fatal_log_show, NULL); static ssize_t non_fatal_log_show(struct device *cdev, struct device_attribute *attr, char *buf) { - u32 count; - - count = pm80xx_get_non_fatal_dump(cdev, attr, buf); - return count; + return pm80xx_get_non_fatal_dump(cdev, attr, buf); } static DEVICE_ATTR_RO(non_fatal_log); From a8c524e2daf23ef168d76e54644967868aa546ed Mon Sep 17 00:00:00 2001 From: Chanwoo Lee Date: Wed, 27 May 2026 18:21:34 +0900 Subject: [PATCH 1109/2758] scsi: ufs: Fix wrong value printed in unexpected UPIU response case [ Upstream commit 2483ae0a56231a915c706411421c6c002a2bf83e ] In ufshcd_transfer_rsp_status(), the default case of the inner switch statement prints the UPIU response code when an unexpected response is received. However, the code was printing 'result' variable which is always 0 at that point, making the error message useless for debugging. Fix this by printing the actual UPIU response code returned by ufshcd_get_req_rsp(). Fixes: 08108d31129a ("scsi: ufs: Improve type safety") Signed-off-by: Chanwoo Lee Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20260527092134.275887-1-cw9316.lee@samsung.com Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/ufs/core/ufshcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index c3f08957d179a..385e1a8eec162 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -5634,7 +5634,7 @@ static inline int ufshcd_transfer_rsp_status(struct ufs_hba *hba, default: dev_err(hba->dev, "Unexpected request response code = %x\n", - result); + ufshcd_get_req_rsp(lrbp->ucd_rsp_ptr)); result = DID_ERROR << 16; break; } From 0497ff765746d9b2d17445c8f7cc737b36c0152a Mon Sep 17 00:00:00 2001 From: Deepanshu Kartikey Date: Tue, 2 Jun 2026 08:22:49 +0530 Subject: [PATCH 1110/2758] bpf: fix UAF by restoring RCU-delayed inode freeing in bpffs [ Upstream commit b93c55b4932dd7e32dca8cf34a3443cc87a02906 ] commit 4f375ade6aa9 ("bpf: Avoid RCU context warning when unpinning htab with internal structs") moved inode cleanup from ->free_inode() into ->destroy_inode() to avoid sleeping in RCU context when calling bpf_any_put(). However this removed the RCU delay on freeing the inode itself and the cached symlink body (i_link), both of which can be accessed by RCU pathwalk (pick_link, may_lookup etc.). This causes a use-after-free when a concurrent unlinkat() drops the last inode reference and destroy_inode() frees the inode immediately, while another task is still walking the path in RCU mode and reads inode->i_opflags (offset +2) inside current_time() -> is_mgtime(). KASAN reports: BUG: KASAN: slab-use-after-free in is_mgtime include/linux/fs.h:2313 Read of size 2 at addr ffff8880407e4282 (offset +2 = i_opflags) The rules (per Al Viro): ->destroy_inode() called immediately, can sleep, use for blocking cleanup e.g. bpf_any_put() ->free_inode() called after RCU grace period, use for freeing inode and anything RCU-accessible e.g. i_link Fix: split the two concerns properly: - keep bpf_any_put() in bpf_destroy_inode() since it is blocking and needs to run promptly - introduce bpf_free_inode() to handle kfree(i_link) and free_inode_nonrcu() with proper RCU delay, preventing the UAF Fixes: 4f375ade6aa9 ("bpf: Avoid RCU context warning when unpinning htab with internal structs") Reported-by: syzbot+36e50496c8ac4bcde3f9@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=36e50496c8ac4bcde3f9 Suggested-by: Al Viro Link: https://lore.kernel.org/all/20260423043906.GN3518998@ZenIV/ Link: https://lore.kernel.org/all/20260602002607.110866-1-kartikey406@gmail.com/T/ [v1] Signed-off-by: Deepanshu Kartikey Acked-by: Al Viro Link: https://lore.kernel.org/r/20260602025249.113828-1-kartikey406@gmail.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- kernel/bpf/inode.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c index 25c06a0118258..188c774a469ca 100644 --- a/kernel/bpf/inode.c +++ b/kernel/bpf/inode.c @@ -766,10 +766,18 @@ static void bpf_destroy_inode(struct inode *inode) { enum bpf_type type; - if (S_ISLNK(inode->i_mode)) - kfree(inode->i_link); if (!bpf_inode_type(inode, &type)) bpf_any_put(inode->i_private, type); +} + +/* + * Called after RCU grace period - safe to free inode and anything + * that might be accessed by RCU pathwalk (inode fields, i_link). + */ +static void bpf_free_inode(struct inode *inode) +{ + if (S_ISLNK(inode->i_mode)) + kfree(inode->i_link); free_inode_nonrcu(inode); } @@ -778,6 +786,7 @@ const struct super_operations bpf_super_ops = { .drop_inode = inode_just_drop, .show_options = bpf_show_options, .destroy_inode = bpf_destroy_inode, + .free_inode = bpf_free_inode, }; enum { From 96ec62a55b78c313fc51222486125bce8615e345 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Fri, 17 Apr 2026 22:58:05 +0900 Subject: [PATCH 1111/2758] mm/fake-numa: fix under-allocation detection in uniform split [ Upstream commit 3a3fc1dfd6a958615ebaab8fb251e89fc2b3f2f2 ] When splitting NUMA node uniformly, split_nodes_size_interleave_uniform() returns the next absolute node ID, not the number of nodes created. The existing under-allocation detection logic compares next absolute node ID (ret) and request count (n), which only works when nid starts at 0. For example, on a system with 2 physical NUMA nodes (node 0: 2GB, node 1: 128MB) and numa=fake=8U, 8 fake nodes are successfully created from node 0 and split_nodes_size_interleave_uniform() returns 8. For node 1, fake node nid starts at 8, but only 4 fake nodes are created due to current FAKE_NODE_MIN_SIZE being 32MB, and split_nodes_size_interleave_uniform() returns 12. By existing under-allocation detection logic, "ret < n" (12 < 8) is false, so the under-allocation will not be detected. Fix under-allocation detection logic to compare the number of actually created nodes (ret - nid) against the request count (n). Also skip under-allocation detection logic for memoryless physical nodes where no fake nodes are created. Also, fix the outdated comment describing split_nodes_size_interleave_uniform() to match the actual return value. Signed-off-by: Sang-Heon Jeon Reported-by: Donghyeon Lee Reported-by: Munhui Chae Fixes: cc9aec03e58f ("x86/numa_emulation: Introduce uniform split capability") # 4.19 Link: https://patch.msgid.link/20260417135805.1758378-1-ekffu200098@gmail.com Signed-off-by: Mike Rapoport (Microsoft) Signed-off-by: Sasha Levin --- mm/numa_emulation.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/mm/numa_emulation.c b/mm/numa_emulation.c index 703c8fa050480..55f26b22bb0be 100644 --- a/mm/numa_emulation.c +++ b/mm/numa_emulation.c @@ -214,7 +214,7 @@ static u64 uniform_size(u64 max_addr, u64 base, u64 hole, int nr_nodes) * Sets up fake nodes of `size' interleaved over physical nodes ranging from * `addr' to `max_addr'. * - * Returns zero on success or negative on error. + * Returns node ID of the next node on success or negative error code. */ static int __init split_nodes_size_interleave_uniform(struct numa_meminfo *ei, struct numa_meminfo *pi, @@ -398,7 +398,7 @@ void __init numa_emulation(struct numa_meminfo *numa_meminfo, int numa_dist_cnt) */ if (strchr(emu_cmdline, 'U')) { unsigned long n; - int nid = 0; + int nid = 0, nr_created; n = simple_strtoul(emu_cmdline, &emu_cmdline, 0); ret = -1; @@ -416,9 +416,18 @@ void __init numa_emulation(struct numa_meminfo *numa_meminfo, int numa_dist_cnt) n, &pi.blk[0], nid); if (ret < 0) break; - if (ret < n) { + + /* + * If no memory was found for this physical node, + * skip the under-allocation check. + */ + if (ret == nid) + continue; + + nr_created = ret - nid; + if (nr_created < n) { pr_info("%s: phys: %d only got %d of %ld nodes, failing\n", - __func__, i, ret, n); + __func__, i, nr_created, n); ret = -1; break; } From b6bc07e49a5fbb335f56eb90cd63dec6584c77d3 Mon Sep 17 00:00:00 2001 From: Danila Chernetsov Date: Sat, 30 May 2026 12:23:11 +0000 Subject: [PATCH 1112/2758] ext2: fix ignored return value of generic_write_sync() [ Upstream commit a4659be0bc7cb1856ffb15b67f903229ae8891ec ] Fix ext2_dio_write_iter() to propagate the error returned by generic_write_sync() instead of silently discarding it, which could cause write(2) to return success to userspace on O_SYNC/O_DSYNC files even when the sync failed. The correct pattern, already used in ext2_dax_write_iter() in the same file and in ext4, xfs, f2fs among others, is: if (ret > 0) ret = generic_write_sync(iocb, ret); Found by Linux Verification Center (linuxtesting.org) with SVACE. [JK: Reflect also filemap_write_and_wait() return value] Fixes: fb5de4358e1a ("ext2: Move direct-io to use iomap") Signed-off-by: Danila Chernetsov Link: https://patch.msgid.link/20260530122311.136803-1-listdansp@mail.ru Signed-off-by: Jan Kara Signed-off-by: Sasha Levin --- fs/ext2/file.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/fs/ext2/file.c b/fs/ext2/file.c index d9b1eb34694a0..781d227aff155 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -267,12 +267,15 @@ static ssize_t ext2_dio_write_iter(struct kiocb *iocb, struct iov_iter *from) endbyte = pos + status - 1; ret2 = filemap_write_and_wait_range(inode->i_mapping, pos, endbyte); - if (!ret2) + if (!ret2) { invalidate_mapping_pages(inode->i_mapping, pos >> PAGE_SHIFT, endbyte >> PAGE_SHIFT); - if (ret > 0) - generic_write_sync(iocb, ret); + if (ret > 0) + ret = generic_write_sync(iocb, ret); + } else { + ret = ret2; + } } out_unlock: From 4e3ed2dabf6fb7c5deb8b6278add473fdd2de9af Mon Sep 17 00:00:00 2001 From: "Guanyou.Chen" Date: Fri, 22 May 2026 21:09:59 +0800 Subject: [PATCH 1113/2758] sched: restore timer_slack_ns when resetting RT policy on fork [ Upstream commit 63c1a12bc0e09af7dee919c4fb4a300a719d5125 ] Commit ed4fb6d7ef68 ("hrtimer: Use and report correct timerslack values for realtime tasks") sets timer_slack_ns to 0 for RT tasks in __setscheduler_params(). However, when an RT task with SCHED_RESET_ON_FORK creates child threads, the children inherit timer_slack_ns=0 from the parent. sched_fork() resets the child's policy to SCHED_NORMAL but does not restore timer_slack_ns, leaving the child permanently running with zero slack. Fix this by restoring timer_slack_ns from default_timer_slack_ns in sched_fork() when resetting from RT/DL to NORMAL policy, matching the existing behavior in __setscheduler_params(). Note: this fix alone requires a correct default_timer_slack_ns to be effective. See the following patch for that fix. Fixes: ed4fb6d7ef68 ("hrtimer: Use and report correct timerslack values for realtime tasks") Reported-by: Qiaoting.Lin Signed-off-by: Guanyou.Chen Signed-off-by: Chunhui.Li Signed-off-by: Peter Zijlstra (Intel) Link: https://patch.msgid.link/20260522131000.1664983-2-chenguanyou@xiaomi.com Signed-off-by: Sasha Levin --- kernel/sched/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 091ee8d2b17a8..69c2aa8c62466 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -4710,6 +4710,7 @@ int sched_fork(u64 clone_flags, struct task_struct *p) p->policy = SCHED_NORMAL; p->static_prio = NICE_TO_PRIO(0); p->rt_priority = 0; + p->timer_slack_ns = p->default_timer_slack_ns; } else if (PRIO_TO_NICE(p->static_prio) < 0) p->static_prio = NICE_TO_PRIO(0); From 5d0e7d2af884b91329235abb16652ae4eead8079 Mon Sep 17 00:00:00 2001 From: liuxixin Date: Thu, 28 May 2026 18:00:01 +0800 Subject: [PATCH 1114/2758] nvme: fix FDP fdpcidx bounds check [ Upstream commit 0967074f6830718fd2597404ef119bddd0dbfd00 ] The fdpcidx bounds check sets n = NUMFDPC + 1 but used > instead of >=, incorrectly accepting fdp_idx when it equals n (i.e. NUMFDPC + 1). Fixes: 30b5f20bb2dd ("nvme: register fdp parameters with the block layer") Reviewed-by: Nitesh Shetty Reviewed-by: Christoph Hellwig Signed-off-by: liuxixin Signed-off-by: Keith Busch Signed-off-by: Sasha Levin --- drivers/nvme/host/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 96809227a0e22..a231da6865853 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -2263,7 +2263,7 @@ static int nvme_query_fdp_granularity(struct nvme_ctrl *ctrl, } n = le16_to_cpu(h->numfdpc) + 1; - if (fdp_idx > n) { + if (fdp_idx >= n) { dev_warn(ctrl->device, "FDP index:%d out of range:%d\n", fdp_idx, n); /* Proceed without registering FDP streams */ From b174edd29a337ff30cd6c95f28d0809681866740 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Mon, 1 Jun 2026 20:56:31 -0700 Subject: [PATCH 1115/2758] driver core: Use system_percpu_wq instead of system_wq [ Upstream commit fda8355f13ea3c0f9499acdeff3024995b474948 ] Commit 1137838865bf ("driver core: Use mod_delayed_work to prevent lost deferred probe work") added a use of system_wq, which is deprecated in favor of system_percpu_wq added by commit 128ea9f6ccfb ("workqueue: Add system_percpu_wq and system_dfl_wq"). An upcoming warning in the workqueue tree flags this with: workqueue: work func deferred_probe_timeout_work_func enqueued on deprecated workqueue. Use system_{percpu|dfl}_wq instead. Switch to system_percpu_wq to clear up the warning. Fixes: 1137838865bf ("driver core: Use mod_delayed_work to prevent lost deferred probe work") Signed-off-by: Nathan Chancellor Reviewed-by: Rafael J. Wysocki (Intel) Link: https://patch.msgid.link/20260601-driver-core-fix-system_wq-warning-v1-1-f9001a70ee25@kernel.org Signed-off-by: Danilo Krummrich Signed-off-by: Sasha Levin --- drivers/base/dd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index f64175afefc91..51c7132e98a07 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -324,7 +324,7 @@ void deferred_probe_extend_timeout(void) * start a new one. */ if (delayed_work_pending(&deferred_probe_timeout_work) && - mod_delayed_work(system_wq, &deferred_probe_timeout_work, + mod_delayed_work(system_percpu_wq, &deferred_probe_timeout_work, secs_to_jiffies(driver_deferred_probe_timeout))) pr_debug("Extended deferred probe timeout by %d secs\n", driver_deferred_probe_timeout); From c3da741d5b2119c61c4498bc936f0fc1dbc3c79b Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Tue, 2 Jun 2026 15:30:49 +0200 Subject: [PATCH 1116/2758] bpf: Reject exclusive maps for bpf_map_elem iterators [ Upstream commit 3c56ee343f9412d81918635c3e25e22a5dd6d87e ] Exclusive maps (aka excl_prog_hash) are meant to be reachable only from the single program whose hash matches. This is enforced by check_map_prog_compatibility() when the map is referenced from a program such as signed BPF loaders. A bpf_map_elem iterator, however, binds its target map at attach time in bpf_iter_attach_map() instead of referencing it from the program, so the exclusivity check is never reached. On top of that, the iterator exposes the map value as a writable buffer. Fixes: baefdbdf6812 ("bpf: Implement exclusive map creation") Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/r/20260602133052.423725-2-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- kernel/bpf/map_iter.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/bpf/map_iter.c b/kernel/bpf/map_iter.c index 261a03ea73d34..ae0741a09c6de 100644 --- a/kernel/bpf/map_iter.c +++ b/kernel/bpf/map_iter.c @@ -112,6 +112,10 @@ static int bpf_iter_attach_map(struct bpf_prog *prog, map = bpf_map_get_with_uref(linfo->map.map_fd); if (IS_ERR(map)) return PTR_ERR(map); + if (map->excl_prog_sha) { + err = -EPERM; + goto put_map; + } if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH || map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH || From 05e6e3ea362bf21cad16f68699bb1cf0fe21052d Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Fri, 8 May 2026 15:16:33 +0200 Subject: [PATCH 1117/2758] tick/sched: Fix TOCTOU in nohz idle time fetch [ Upstream commit 86db4084b4b5d1a074bcc66c108a4c9d266812d4 ] When the nohz idle time is fetched, the current clock timestamp is taken outside the seqcount, which can result in a race as reported by Sashiko: get_cpu_sleep_time_us() tick_nohz_start_idle() ----------------------- --------------------- now = ktime_get() write_seqcount_begin(idle_sleeptime_seq); idle_entrytime = ktime_get() tick_sched_flag_set(ts, TS_FLAG_IDLE_ACTIVE); write_seqcount_end(&ts->idle_sleeptime_seq); read_seqcount_begin(idle_sleeptime_seq) delta = now - idle_entrytime); //!! But now < idle_entrytime idle = *sleeptime + delta; read_seqcount_retry(&ts->idle_sleeptime_seq, seq) Here the read side fetches the timestamp before the write side and its update. As a result the time delta computed on the read side is negative (ktime_t is signed) and breaks the cputime monotonicity guarantee. This could possibly be fixed with reading the current clock timestamp inside the seqcount but the reader overhead might then increase. Also simply checking that the current timestamp is above the idle entry time is enough to prevent any issue of the like. Fixes: 620a30fa0bd1 ("timers/nohz: Protect idle/iowait sleep time under seqcount") Reported-by: Sashiko Signed-off-by: Frederic Weisbecker Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260508131647.43868-2-frederic@kernel.org Signed-off-by: Sasha Levin --- kernel/time/tick-sched.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index cbbb87a0c6e7c..171393367b5c1 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -797,15 +797,16 @@ static u64 get_cpu_sleep_time_us(struct tick_sched *ts, ktime_t *sleeptime, *last_update_time = ktime_to_us(now); do { + ktime_t delta = 0; + seq = read_seqcount_begin(&ts->idle_sleeptime_seq); if (tick_sched_flag_test(ts, TS_FLAG_IDLE_ACTIVE) && compute_delta) { - ktime_t delta = ktime_sub(now, ts->idle_entrytime); - - idle = ktime_add(*sleeptime, delta); - } else { - idle = *sleeptime; + if (now > ts->idle_entrytime) + delta = ktime_sub(now, ts->idle_entrytime); } + + idle = ktime_add(*sleeptime, delta); } while (read_seqcount_retry(&ts->idle_sleeptime_seq, seq)); return ktime_to_us(idle); From 61a8ae8eb5222ea5b1d1662d778637599dd31177 Mon Sep 17 00:00:00 2001 From: Alexander Potapenko Date: Mon, 4 May 2026 12:06:37 +0200 Subject: [PATCH 1118/2758] lib/test_meminit: use && for bools [ Upstream commit 8e0c2085c978ed6d9764d79fc785920360096f21 ] As pointed out by Dan Carpenter, test_kmemcache() was using a bitwise AND on two bools instead of a boolean AND. Fix this for the sake of code cleanliness. Link: https://lore.kernel.org/20260504100637.1535762-1-glider@google.com Fixes: 5015a300a522 ("lib: introduce test_meminit module") Signed-off-by: Alexander Potapenko Reported-by: Dan Carpenter Closes: https://lore.kernel.org/kernel-janitors/afOcIan1ap9kD26M@stanley.mountain/ Reviewed-by: Andrew Morton Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- lib/test_meminit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/test_meminit.c b/lib/test_meminit.c index 6298f66c964bb..d028a6552cd61 100644 --- a/lib/test_meminit.c +++ b/lib/test_meminit.c @@ -387,7 +387,7 @@ static int __init test_kmemcache(int *total_failures) ctor = flags & 1; rcu = flags & 2; zero = flags & 4; - if (ctor & zero) + if (ctor && zero) continue; num_tests += do_kmem_cache_size(size, ctor, rcu, zero, &failures); From 9c1099b5c68d6ec9d7e4ffbb7847ecec8196184f Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Sun, 26 Apr 2026 09:34:48 +0800 Subject: [PATCH 1119/2758] riscv: dts: sophgo: sg2044: use hex for CPU unit address [ Upstream commit 207cbc477406a72952e27ace2eadbae55164f129 ] Previous the CPU unit address cpu of sg2044 use decimal, it is not following the general convention for unit addresses of the OF. Convent the unit address to hex to resolve this problem. The introduces a small change for the CPU node name, but it should nothing since there is no direct full-path reference to these CPU nodes. Fixes: 967a94a92aaa ("riscv: dts: add initial Sophgo SG2042 SoC device tree") Reviewed-by: Chen Wang Reviewed-by: Guo Ren Link: https://patch.msgid.link/20260426013449.694435-2-inochiama@gmail.com Signed-off-by: Inochi Amaoto Signed-off-by: Chen Wang Signed-off-by: Sasha Levin --- arch/riscv/boot/dts/sophgo/sg2044-cpus.dtsi | 236 ++++++++++---------- 1 file changed, 118 insertions(+), 118 deletions(-) diff --git a/arch/riscv/boot/dts/sophgo/sg2044-cpus.dtsi b/arch/riscv/boot/dts/sophgo/sg2044-cpus.dtsi index 3135409c21492..f66a382c95bd9 100644 --- a/arch/riscv/boot/dts/sophgo/sg2044-cpus.dtsi +++ b/arch/riscv/boot/dts/sophgo/sg2044-cpus.dtsi @@ -14,7 +14,7 @@ cpu0: cpu@0 { compatible = "thead,c920", "riscv"; - reg = <0>; + reg = <0x0>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -50,7 +50,7 @@ cpu1: cpu@1 { compatible = "thead,c920", "riscv"; - reg = <1>; + reg = <0x1>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -86,7 +86,7 @@ cpu2: cpu@2 { compatible = "thead,c920", "riscv"; - reg = <2>; + reg = <0x2>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -122,7 +122,7 @@ cpu3: cpu@3 { compatible = "thead,c920", "riscv"; - reg = <3>; + reg = <0x3>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -158,7 +158,7 @@ cpu4: cpu@4 { compatible = "thead,c920", "riscv"; - reg = <4>; + reg = <0x4>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -194,7 +194,7 @@ cpu5: cpu@5 { compatible = "thead,c920", "riscv"; - reg = <5>; + reg = <0x5>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -230,7 +230,7 @@ cpu6: cpu@6 { compatible = "thead,c920", "riscv"; - reg = <6>; + reg = <0x6>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -266,7 +266,7 @@ cpu7: cpu@7 { compatible = "thead,c920", "riscv"; - reg = <7>; + reg = <0x7>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -302,7 +302,7 @@ cpu8: cpu@8 { compatible = "thead,c920", "riscv"; - reg = <8>; + reg = <0x8>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -338,7 +338,7 @@ cpu9: cpu@9 { compatible = "thead,c920", "riscv"; - reg = <9>; + reg = <0x9>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -372,9 +372,9 @@ }; }; - cpu10: cpu@10 { + cpu10: cpu@a { compatible = "thead,c920", "riscv"; - reg = <10>; + reg = <0xa>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -408,9 +408,9 @@ }; }; - cpu11: cpu@11 { + cpu11: cpu@b { compatible = "thead,c920", "riscv"; - reg = <11>; + reg = <0xb>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -444,9 +444,9 @@ }; }; - cpu12: cpu@12 { + cpu12: cpu@c { compatible = "thead,c920", "riscv"; - reg = <12>; + reg = <0xc>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -480,9 +480,9 @@ }; }; - cpu13: cpu@13 { + cpu13: cpu@d { compatible = "thead,c920", "riscv"; - reg = <13>; + reg = <0xd>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -516,9 +516,9 @@ }; }; - cpu14: cpu@14 { + cpu14: cpu@e { compatible = "thead,c920", "riscv"; - reg = <14>; + reg = <0xe>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -552,9 +552,9 @@ }; }; - cpu15: cpu@15 { + cpu15: cpu@f { compatible = "thead,c920", "riscv"; - reg = <15>; + reg = <0xf>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -588,9 +588,9 @@ }; }; - cpu16: cpu@16 { + cpu16: cpu@10 { compatible = "thead,c920", "riscv"; - reg = <16>; + reg = <0x10>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -624,9 +624,9 @@ }; }; - cpu17: cpu@17 { + cpu17: cpu@11 { compatible = "thead,c920", "riscv"; - reg = <17>; + reg = <0x11>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -660,9 +660,9 @@ }; }; - cpu18: cpu@18 { + cpu18: cpu@12 { compatible = "thead,c920", "riscv"; - reg = <18>; + reg = <0x12>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -696,9 +696,9 @@ }; }; - cpu19: cpu@19 { + cpu19: cpu@13 { compatible = "thead,c920", "riscv"; - reg = <19>; + reg = <0x13>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -732,9 +732,9 @@ }; }; - cpu20: cpu@20 { + cpu20: cpu@14 { compatible = "thead,c920", "riscv"; - reg = <20>; + reg = <0x14>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -768,9 +768,9 @@ }; }; - cpu21: cpu@21 { + cpu21: cpu@15 { compatible = "thead,c920", "riscv"; - reg = <21>; + reg = <0x15>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -804,9 +804,9 @@ }; }; - cpu22: cpu@22 { + cpu22: cpu@16 { compatible = "thead,c920", "riscv"; - reg = <22>; + reg = <0x16>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -840,9 +840,9 @@ }; }; - cpu23: cpu@23 { + cpu23: cpu@17 { compatible = "thead,c920", "riscv"; - reg = <23>; + reg = <0x17>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -876,9 +876,9 @@ }; }; - cpu24: cpu@24 { + cpu24: cpu@18 { compatible = "thead,c920", "riscv"; - reg = <24>; + reg = <0x18>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -912,9 +912,9 @@ }; }; - cpu25: cpu@25 { + cpu25: cpu@19 { compatible = "thead,c920", "riscv"; - reg = <25>; + reg = <0x19>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -948,9 +948,9 @@ }; }; - cpu26: cpu@26 { + cpu26: cpu@1a { compatible = "thead,c920", "riscv"; - reg = <26>; + reg = <0x1a>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -984,9 +984,9 @@ }; }; - cpu27: cpu@27 { + cpu27: cpu@1b { compatible = "thead,c920", "riscv"; - reg = <27>; + reg = <0x1b>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1020,9 +1020,9 @@ }; }; - cpu28: cpu@28 { + cpu28: cpu@1c { compatible = "thead,c920", "riscv"; - reg = <28>; + reg = <0x1c>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1056,9 +1056,9 @@ }; }; - cpu29: cpu@29 { + cpu29: cpu@1d { compatible = "thead,c920", "riscv"; - reg = <29>; + reg = <0x1d>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1092,9 +1092,9 @@ }; }; - cpu30: cpu@30 { + cpu30: cpu@1e { compatible = "thead,c920", "riscv"; - reg = <30>; + reg = <0x1e>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1128,9 +1128,9 @@ }; }; - cpu31: cpu@31 { + cpu31: cpu@1f { compatible = "thead,c920", "riscv"; - reg = <31>; + reg = <0x1f>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1164,9 +1164,9 @@ }; }; - cpu32: cpu@32 { + cpu32: cpu@20 { compatible = "thead,c920", "riscv"; - reg = <32>; + reg = <0x20>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1200,9 +1200,9 @@ }; }; - cpu33: cpu@33 { + cpu33: cpu@21 { compatible = "thead,c920", "riscv"; - reg = <33>; + reg = <0x21>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1236,9 +1236,9 @@ }; }; - cpu34: cpu@34 { + cpu34: cpu@22 { compatible = "thead,c920", "riscv"; - reg = <34>; + reg = <0x22>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1272,9 +1272,9 @@ }; }; - cpu35: cpu@35 { + cpu35: cpu@23 { compatible = "thead,c920", "riscv"; - reg = <35>; + reg = <0x23>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1308,9 +1308,9 @@ }; }; - cpu36: cpu@36 { + cpu36: cpu@24 { compatible = "thead,c920", "riscv"; - reg = <36>; + reg = <0x24>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1344,9 +1344,9 @@ }; }; - cpu37: cpu@37 { + cpu37: cpu@25 { compatible = "thead,c920", "riscv"; - reg = <37>; + reg = <0x25>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1380,9 +1380,9 @@ }; }; - cpu38: cpu@38 { + cpu38: cpu@26 { compatible = "thead,c920", "riscv"; - reg = <38>; + reg = <0x26>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1416,9 +1416,9 @@ }; }; - cpu39: cpu@39 { + cpu39: cpu@27 { compatible = "thead,c920", "riscv"; - reg = <39>; + reg = <0x27>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1452,9 +1452,9 @@ }; }; - cpu40: cpu@40 { + cpu40: cpu@28 { compatible = "thead,c920", "riscv"; - reg = <40>; + reg = <0x28>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1488,9 +1488,9 @@ }; }; - cpu41: cpu@41 { + cpu41: cpu@29 { compatible = "thead,c920", "riscv"; - reg = <41>; + reg = <0x29>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1524,9 +1524,9 @@ }; }; - cpu42: cpu@42 { + cpu42: cpu@2a { compatible = "thead,c920", "riscv"; - reg = <42>; + reg = <0x2a>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1560,9 +1560,9 @@ }; }; - cpu43: cpu@43 { + cpu43: cpu@2b { compatible = "thead,c920", "riscv"; - reg = <43>; + reg = <0x2b>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1596,9 +1596,9 @@ }; }; - cpu44: cpu@44 { + cpu44: cpu@2c { compatible = "thead,c920", "riscv"; - reg = <44>; + reg = <0x2c>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1632,9 +1632,9 @@ }; }; - cpu45: cpu@45 { + cpu45: cpu@2d { compatible = "thead,c920", "riscv"; - reg = <45>; + reg = <0x2d>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1668,9 +1668,9 @@ }; }; - cpu46: cpu@46 { + cpu46: cpu@2e { compatible = "thead,c920", "riscv"; - reg = <46>; + reg = <0x2e>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1704,9 +1704,9 @@ }; }; - cpu47: cpu@47 { + cpu47: cpu@2f { compatible = "thead,c920", "riscv"; - reg = <47>; + reg = <0x2f>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1740,9 +1740,9 @@ }; }; - cpu48: cpu@48 { + cpu48: cpu@30 { compatible = "thead,c920", "riscv"; - reg = <48>; + reg = <0x30>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1776,9 +1776,9 @@ }; }; - cpu49: cpu@49 { + cpu49: cpu@31 { compatible = "thead,c920", "riscv"; - reg = <49>; + reg = <0x31>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1812,9 +1812,9 @@ }; }; - cpu50: cpu@50 { + cpu50: cpu@32 { compatible = "thead,c920", "riscv"; - reg = <50>; + reg = <0x32>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1848,9 +1848,9 @@ }; }; - cpu51: cpu@51 { + cpu51: cpu@33 { compatible = "thead,c920", "riscv"; - reg = <51>; + reg = <0x33>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1884,9 +1884,9 @@ }; }; - cpu52: cpu@52 { + cpu52: cpu@34 { compatible = "thead,c920", "riscv"; - reg = <52>; + reg = <0x34>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1920,9 +1920,9 @@ }; }; - cpu53: cpu@53 { + cpu53: cpu@35 { compatible = "thead,c920", "riscv"; - reg = <53>; + reg = <0x35>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1956,9 +1956,9 @@ }; }; - cpu54: cpu@54 { + cpu54: cpu@36 { compatible = "thead,c920", "riscv"; - reg = <54>; + reg = <0x36>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1992,9 +1992,9 @@ }; }; - cpu55: cpu@55 { + cpu55: cpu@37 { compatible = "thead,c920", "riscv"; - reg = <55>; + reg = <0x37>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -2028,9 +2028,9 @@ }; }; - cpu56: cpu@56 { + cpu56: cpu@38 { compatible = "thead,c920", "riscv"; - reg = <56>; + reg = <0x38>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -2064,9 +2064,9 @@ }; }; - cpu57: cpu@57 { + cpu57: cpu@39 { compatible = "thead,c920", "riscv"; - reg = <57>; + reg = <0x39>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -2100,9 +2100,9 @@ }; }; - cpu58: cpu@58 { + cpu58: cpu@3a { compatible = "thead,c920", "riscv"; - reg = <58>; + reg = <0x3a>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -2136,9 +2136,9 @@ }; }; - cpu59: cpu@59 { + cpu59: cpu@3b { compatible = "thead,c920", "riscv"; - reg = <59>; + reg = <0x3b>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -2172,9 +2172,9 @@ }; }; - cpu60: cpu@60 { + cpu60: cpu@3c { compatible = "thead,c920", "riscv"; - reg = <60>; + reg = <0x3c>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -2208,9 +2208,9 @@ }; }; - cpu61: cpu@61 { + cpu61: cpu@3d { compatible = "thead,c920", "riscv"; - reg = <61>; + reg = <0x3d>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -2244,9 +2244,9 @@ }; }; - cpu62: cpu@62 { + cpu62: cpu@3e { compatible = "thead,c920", "riscv"; - reg = <62>; + reg = <0x3e>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -2280,9 +2280,9 @@ }; }; - cpu63: cpu@63 { + cpu63: cpu@3f { compatible = "thead,c920", "riscv"; - reg = <63>; + reg = <0x3f>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; From 24084f7d239b5416c1241ee901a06c0df88073f8 Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Sun, 26 Apr 2026 09:34:49 +0800 Subject: [PATCH 1120/2758] riscv: dts: sophgo: sg2042: use hex for CPU unit address [ Upstream commit a7e658907686528fe06a11828b04a3e42df9ef18 ] Previous the CPU unit address cpu of sg2042 use decimal, it is not following the general convention for unit addresses of the OF. Convent the unit address to hex to resolve this problem. The introduces a small change for the CPU node name, but it should affect nothing since there is no direct full-path reference to these CPU nodes. Fixes: ae5bac370ed4 ("riscv: dts: sophgo: Add initial device tree of Sophgo SRD3-10") Tested-by: Chen Wang # Pioneerbox. Reviewed-by: Guo Ren Reviewed-by: Chen Wang Acked-by: Conor Dooley Tested-by: Chen Wang on Pioneerbox. Link: https://patch.msgid.link/20260426013449.694435-3-inochiama@gmail.com Signed-off-by: Inochi Amaoto Signed-off-by: Chen Wang Signed-off-by: Sasha Levin --- arch/riscv/boot/dts/sophgo/sg2042-cpus.dtsi | 236 ++++++++++---------- 1 file changed, 118 insertions(+), 118 deletions(-) diff --git a/arch/riscv/boot/dts/sophgo/sg2042-cpus.dtsi b/arch/riscv/boot/dts/sophgo/sg2042-cpus.dtsi index 509488eee4321..fd8906b313d2e 100644 --- a/arch/riscv/boot/dts/sophgo/sg2042-cpus.dtsi +++ b/arch/riscv/boot/dts/sophgo/sg2042-cpus.dtsi @@ -263,7 +263,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <0>; + reg = <0x0>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -291,7 +291,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <1>; + reg = <0x1>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -319,7 +319,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <2>; + reg = <0x2>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -347,7 +347,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <3>; + reg = <0x3>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -375,7 +375,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <4>; + reg = <0x4>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -403,7 +403,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <5>; + reg = <0x5>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -431,7 +431,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <6>; + reg = <0x6>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -459,7 +459,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <7>; + reg = <0x7>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -487,7 +487,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <8>; + reg = <0x8>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -515,7 +515,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <9>; + reg = <0x9>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -533,7 +533,7 @@ }; }; - cpu10: cpu@10 { + cpu10: cpu@a { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -543,7 +543,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <10>; + reg = <0xa>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -561,7 +561,7 @@ }; }; - cpu11: cpu@11 { + cpu11: cpu@b { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -571,7 +571,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <11>; + reg = <0xb>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -589,7 +589,7 @@ }; }; - cpu12: cpu@12 { + cpu12: cpu@c { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -599,7 +599,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <12>; + reg = <0xc>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -617,7 +617,7 @@ }; }; - cpu13: cpu@13 { + cpu13: cpu@d { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -627,7 +627,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <13>; + reg = <0xd>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -645,7 +645,7 @@ }; }; - cpu14: cpu@14 { + cpu14: cpu@e { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -655,7 +655,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <14>; + reg = <0xe>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -673,7 +673,7 @@ }; }; - cpu15: cpu@15 { + cpu15: cpu@f { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -683,7 +683,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <15>; + reg = <0xf>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -701,7 +701,7 @@ }; }; - cpu16: cpu@16 { + cpu16: cpu@10 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -711,7 +711,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <16>; + reg = <0x10>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -729,7 +729,7 @@ }; }; - cpu17: cpu@17 { + cpu17: cpu@11 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -739,7 +739,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <17>; + reg = <0x11>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -757,7 +757,7 @@ }; }; - cpu18: cpu@18 { + cpu18: cpu@12 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -767,7 +767,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <18>; + reg = <0x12>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -785,7 +785,7 @@ }; }; - cpu19: cpu@19 { + cpu19: cpu@13 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -795,7 +795,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <19>; + reg = <0x13>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -813,7 +813,7 @@ }; }; - cpu20: cpu@20 { + cpu20: cpu@14 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -823,7 +823,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <20>; + reg = <0x14>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -841,7 +841,7 @@ }; }; - cpu21: cpu@21 { + cpu21: cpu@15 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -851,7 +851,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <21>; + reg = <0x15>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -869,7 +869,7 @@ }; }; - cpu22: cpu@22 { + cpu22: cpu@16 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -879,7 +879,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <22>; + reg = <0x16>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -897,7 +897,7 @@ }; }; - cpu23: cpu@23 { + cpu23: cpu@17 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -907,7 +907,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <23>; + reg = <0x17>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -925,7 +925,7 @@ }; }; - cpu24: cpu@24 { + cpu24: cpu@18 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -935,7 +935,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <24>; + reg = <0x18>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -953,7 +953,7 @@ }; }; - cpu25: cpu@25 { + cpu25: cpu@19 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -963,7 +963,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <25>; + reg = <0x19>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -981,7 +981,7 @@ }; }; - cpu26: cpu@26 { + cpu26: cpu@1a { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -991,7 +991,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <26>; + reg = <0x1a>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1009,7 +1009,7 @@ }; }; - cpu27: cpu@27 { + cpu27: cpu@1b { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1019,7 +1019,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <27>; + reg = <0x1b>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1037,7 +1037,7 @@ }; }; - cpu28: cpu@28 { + cpu28: cpu@1c { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1047,7 +1047,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <28>; + reg = <0x1c>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1065,7 +1065,7 @@ }; }; - cpu29: cpu@29 { + cpu29: cpu@1d { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1075,7 +1075,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <29>; + reg = <0x1d>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1093,7 +1093,7 @@ }; }; - cpu30: cpu@30 { + cpu30: cpu@1e { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1103,7 +1103,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <30>; + reg = <0x1e>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1121,7 +1121,7 @@ }; }; - cpu31: cpu@31 { + cpu31: cpu@1f { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1131,7 +1131,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <31>; + reg = <0x1f>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1149,7 +1149,7 @@ }; }; - cpu32: cpu@32 { + cpu32: cpu@20 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1159,7 +1159,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <32>; + reg = <0x20>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1177,7 +1177,7 @@ }; }; - cpu33: cpu@33 { + cpu33: cpu@21 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1187,7 +1187,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <33>; + reg = <0x21>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1205,7 +1205,7 @@ }; }; - cpu34: cpu@34 { + cpu34: cpu@22 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1215,7 +1215,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <34>; + reg = <0x22>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1233,7 +1233,7 @@ }; }; - cpu35: cpu@35 { + cpu35: cpu@23 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1243,7 +1243,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <35>; + reg = <0x23>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1261,7 +1261,7 @@ }; }; - cpu36: cpu@36 { + cpu36: cpu@24 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1271,7 +1271,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <36>; + reg = <0x24>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1289,7 +1289,7 @@ }; }; - cpu37: cpu@37 { + cpu37: cpu@25 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1299,7 +1299,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <37>; + reg = <0x25>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1317,7 +1317,7 @@ }; }; - cpu38: cpu@38 { + cpu38: cpu@26 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1327,7 +1327,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <38>; + reg = <0x26>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1345,7 +1345,7 @@ }; }; - cpu39: cpu@39 { + cpu39: cpu@27 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1355,7 +1355,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <39>; + reg = <0x27>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1373,7 +1373,7 @@ }; }; - cpu40: cpu@40 { + cpu40: cpu@28 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1383,7 +1383,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <40>; + reg = <0x28>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1401,7 +1401,7 @@ }; }; - cpu41: cpu@41 { + cpu41: cpu@29 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1411,7 +1411,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <41>; + reg = <0x29>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1429,7 +1429,7 @@ }; }; - cpu42: cpu@42 { + cpu42: cpu@2a { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1439,7 +1439,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <42>; + reg = <0x2a>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1457,7 +1457,7 @@ }; }; - cpu43: cpu@43 { + cpu43: cpu@2b { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1467,7 +1467,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <43>; + reg = <0x2b>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1485,7 +1485,7 @@ }; }; - cpu44: cpu@44 { + cpu44: cpu@2c { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1495,7 +1495,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <44>; + reg = <0x2c>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1513,7 +1513,7 @@ }; }; - cpu45: cpu@45 { + cpu45: cpu@2d { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1523,7 +1523,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <45>; + reg = <0x2d>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1541,7 +1541,7 @@ }; }; - cpu46: cpu@46 { + cpu46: cpu@2e { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1551,7 +1551,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <46>; + reg = <0x2e>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1569,7 +1569,7 @@ }; }; - cpu47: cpu@47 { + cpu47: cpu@2f { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1579,7 +1579,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <47>; + reg = <0x2f>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1597,7 +1597,7 @@ }; }; - cpu48: cpu@48 { + cpu48: cpu@30 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1607,7 +1607,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <48>; + reg = <0x30>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1625,7 +1625,7 @@ }; }; - cpu49: cpu@49 { + cpu49: cpu@31 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1635,7 +1635,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <49>; + reg = <0x31>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1653,7 +1653,7 @@ }; }; - cpu50: cpu@50 { + cpu50: cpu@32 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1663,7 +1663,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <50>; + reg = <0x32>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1681,7 +1681,7 @@ }; }; - cpu51: cpu@51 { + cpu51: cpu@33 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1691,7 +1691,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <51>; + reg = <0x33>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1709,7 +1709,7 @@ }; }; - cpu52: cpu@52 { + cpu52: cpu@34 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1719,7 +1719,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <52>; + reg = <0x34>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1737,7 +1737,7 @@ }; }; - cpu53: cpu@53 { + cpu53: cpu@35 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1747,7 +1747,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <53>; + reg = <0x35>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1765,7 +1765,7 @@ }; }; - cpu54: cpu@54 { + cpu54: cpu@36 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1775,7 +1775,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <54>; + reg = <0x36>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1793,7 +1793,7 @@ }; }; - cpu55: cpu@55 { + cpu55: cpu@37 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1803,7 +1803,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <55>; + reg = <0x37>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1821,7 +1821,7 @@ }; }; - cpu56: cpu@56 { + cpu56: cpu@38 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1831,7 +1831,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <56>; + reg = <0x38>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1849,7 +1849,7 @@ }; }; - cpu57: cpu@57 { + cpu57: cpu@39 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1859,7 +1859,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <57>; + reg = <0x39>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1877,7 +1877,7 @@ }; }; - cpu58: cpu@58 { + cpu58: cpu@3a { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1887,7 +1887,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <58>; + reg = <0x3a>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1905,7 +1905,7 @@ }; }; - cpu59: cpu@59 { + cpu59: cpu@3b { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1915,7 +1915,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <59>; + reg = <0x3b>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1933,7 +1933,7 @@ }; }; - cpu60: cpu@60 { + cpu60: cpu@3c { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1943,7 +1943,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <60>; + reg = <0x3c>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1961,7 +1961,7 @@ }; }; - cpu61: cpu@61 { + cpu61: cpu@3d { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1971,7 +1971,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <61>; + reg = <0x3d>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1989,7 +1989,7 @@ }; }; - cpu62: cpu@62 { + cpu62: cpu@3e { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1999,7 +1999,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <62>; + reg = <0x3e>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -2017,7 +2017,7 @@ }; }; - cpu63: cpu@63 { + cpu63: cpu@3f { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -2027,7 +2027,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <63>; + reg = <0x3f>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; From 57088b06109f3222963c639d8d743f42c2899b13 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 12 May 2026 00:29:37 -0400 Subject: [PATCH 1121/2758] configfs_lookup(): don't leave ->s_dentry dangling on failure [ Upstream commit 10da12d352b7b2bb330a8609fdda9a58bf0e9856 ] Normally ->s_dentry is cleared when dentry it's pointing to becomes negative (on eviction, realistically). However, that only happens if dentry gets to be positive in the first place; in case of inode allocation failure dentry never becomes positive, so ->d_iput() is not called at all. We do part of what normally would've been done by configfs_d_iput() (dropping the reference to configfs_dirent) manually, but we do not clear ->s_dentry there. Sloppy as it is, it does not matter in case of configfs_create_{dir,link}() - there configfs_dirent does not survive dropping the sole reference to it. However, for configfs_lookup() it *does* survive, with a dangling pointer to soon to be freed dentry sitting it its ->s_dentry. Subsequent getdents(2) in that directory will end up dereferencing that pointer in order to pick the inode number. Use after free... This is the minimal fix; the right approach is to set the linkage between dentry and configfs_dirent only after we know that we have an inode, but that takes more surgery and the bug had been there since 2006, so... Fixes: 3d0f89bb1694 ("configfs: Add permission and ownership to configfs objects") # 2.6.16-rc3 Reviewed-by: Jan Kara Reviewed-by: Breno Leitao Signed-off-by: Al Viro Signed-off-by: Sasha Levin --- fs/configfs/dir.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 362b6ff9b908f..e84483a0836d8 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -486,6 +486,9 @@ static struct dentry * configfs_lookup(struct inode *dir, inode = configfs_create(dentry, mode); if (IS_ERR(inode)) { + spin_lock(&configfs_dirent_lock); + sd->s_dentry = NULL; + spin_unlock(&configfs_dirent_lock); configfs_put(sd); return ERR_CAST(inode); } From e87d8a7d86ce9a337a6ce5035cb5be141f0685ad Mon Sep 17 00:00:00 2001 From: Karl Mehltretter Date: Sat, 23 May 2026 20:51:22 +0200 Subject: [PATCH 1122/2758] lockdep/selftests: Restore migrate_disable() state on PREEMPT_RT [ Upstream commit d8c897b20bf4d4cbb1e935a8ceb666bcc0f82580 ] The lockdep selftests deliberately run unbalanced locking patterns. dotest() restores the task state they leave behind before running the next testcase. On PREEMPT_RT, spin_lock() uses migrate_disable() instead of disabling preemption. dotest() cleans up the resulting migration-disabled state, but that cleanup is still guarded by CONFIG_SMP. That used to match the scheduler data model, where migration_disabled was also CONFIG_SMP-only. The commit referenced below made SMP scheduler state unconditional, so CONFIG_SMP=n PREEMPT_RT kernels with CONFIG_DEBUG_LOCKING_API_SELFTESTS=y report success from the selftests and then trip over stale current->migration_disabled state: releasing a pinned lock bad: scheduling from the idle thread! Kernel panic - not syncing: Fatal exception Save and restore current->migration_disabled for every PREEMPT_RT build. Fixes: cac5cefbade9 ("sched/smp: Make SMP unconditional") Assisted-by: Codex:gpt-5 Signed-off-by: Karl Mehltretter Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Sebastian Andrzej Siewior Link: https://patch.msgid.link/20260523185123.17482-2-kmehltretter@gmail.com Signed-off-by: Sasha Levin --- lib/locking-selftest.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/locking-selftest.c b/lib/locking-selftest.c index d939403331b5a..cb806b628352b 100644 --- a/lib/locking-selftest.c +++ b/lib/locking-selftest.c @@ -1431,9 +1431,7 @@ static void dotest(void (*testcase_fn)(void), int expected, int lockclass_mask) { int saved_preempt_count = preempt_count(); #ifdef CONFIG_PREEMPT_RT -#ifdef CONFIG_SMP int saved_mgd_count = current->migration_disabled; -#endif int saved_rcu_count = current->rcu_read_lock_nesting; #endif @@ -1471,10 +1469,8 @@ static void dotest(void (*testcase_fn)(void), int expected, int lockclass_mask) preempt_count_set(saved_preempt_count); #ifdef CONFIG_PREEMPT_RT -#ifdef CONFIG_SMP while (current->migration_disabled > saved_mgd_count) migrate_enable(); -#endif while (current->rcu_read_lock_nesting > saved_rcu_count) rcu_read_unlock(); From 0cfc9b30ebe6a650a1b98d1bc9c633115ed63dac Mon Sep 17 00:00:00 2001 From: Karl Mehltretter Date: Sat, 23 May 2026 20:51:23 +0200 Subject: [PATCH 1123/2758] lockdep/selftests: Restore sched_rt_mutex state on PREEMPT_RT [ Upstream commit 06961d60a0e410bf8df69ccff7eb1bd824912b8f ] The WW-mutex selftests deliberately exercise failing lock paths. On PREEMPT_RT, some of those paths enter the RT-mutex scheduler helpers. The change referenced by the Fixes tag made those helpers track RT-mutex scheduling state in current->sched_rt_mutex. The bit is normally cleared by the matching post-schedule helper, but some WW-mutex selftests disable the runtime debug_locks flag before that happens. With debug_locks cleared, lockdep_assert() does not evaluate the expression that clears the bit, leaving stale state for the next testcase. With CONFIG_PREEMPT_RT=y and CONFIG_DEBUG_LOCKING_API_SELFTESTS=y, that stale state produces warnings such as: WARNING: kernel/sched/core.c:7557 at rt_mutex_pre_schedule+0x26/0x2d RIP: 0010:rt_mutex_pre_schedule+0x26/0x2d Save and restore current->sched_rt_mutex around each testcase, matching the existing PREEMPT_RT cleanup for task-local migration and RCU state. Fixes: d14f9e930b90 ("locking/rtmutex: Use rt_mutex specific scheduler helpers") Assisted-by: Codex:gpt-5 Signed-off-by: Karl Mehltretter Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Sebastian Andrzej Siewior Link: https://patch.msgid.link/20260523185123.17482-3-kmehltretter@gmail.com Signed-off-by: Sasha Levin --- lib/locking-selftest.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/locking-selftest.c b/lib/locking-selftest.c index cb806b628352b..bfafe1204c7b6 100644 --- a/lib/locking-selftest.c +++ b/lib/locking-selftest.c @@ -1433,6 +1433,7 @@ static void dotest(void (*testcase_fn)(void), int expected, int lockclass_mask) #ifdef CONFIG_PREEMPT_RT int saved_mgd_count = current->migration_disabled; int saved_rcu_count = current->rcu_read_lock_nesting; + int saved_sched_rt_mutex = current->sched_rt_mutex; #endif WARN_ON(irqs_disabled()); @@ -1469,6 +1470,8 @@ static void dotest(void (*testcase_fn)(void), int expected, int lockclass_mask) preempt_count_set(saved_preempt_count); #ifdef CONFIG_PREEMPT_RT + current->sched_rt_mutex = saved_sched_rt_mutex; + while (current->migration_disabled > saved_mgd_count) migrate_enable(); From d06df6e447a8510f86db44b2522d5375362a9499 Mon Sep 17 00:00:00 2001 From: Li Chen Date: Wed, 13 May 2026 16:58:17 +0800 Subject: [PATCH 1124/2758] ext4: fix fast commit wait/wake bit mapping on 64-bit [ Upstream commit 8b3bc93fee6771775243665a0cf31857d6659775 ] On 64-bit, ext4 dynamic inode states live in the upper half of i_flags, and ext4_test_inode_state() applies the corresponding +32 offset. The fast-commit wait and wake paths open-coded the wait key with the raw EXT4_STATE_* value. Add small helpers for the state wait word and bit, and use them for the FC_COMMITTING and FC_FLUSHING_DATA waits so the wait key follows the same mapping as the state helpers. Fixes: 857d32f26181 ("ext4: rework fast commit commit path") Reported-by: Sashiko AI review Signed-off-by: Li Chen Reviewed-by: Baokun Li Reviewed-by: Zhang Yi Reviewed-by: Jan Kara Link: https://patch.msgid.link/20260513085818.552432-1-me@linux.beauty Signed-off-by: Theodore Ts'o Signed-off-by: Sasha Levin --- fs/ext4/ext4.h | 20 +++++++++++++++++ fs/ext4/fast_commit.c | 50 ++++++++++++++++--------------------------- 2 files changed, 38 insertions(+), 32 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 94283a991e5c4..6569d1d575a01 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -2000,6 +2000,8 @@ EXT4_INODE_BIT_FNS(flag, flags, 0) static inline int ext4_test_inode_state(struct inode *inode, int bit); static inline void ext4_set_inode_state(struct inode *inode, int bit); static inline void ext4_clear_inode_state(struct inode *inode, int bit); +static inline unsigned long *ext4_inode_state_wait_word(struct inode *inode); +static inline int ext4_inode_state_wait_bit(int bit); #if (BITS_PER_LONG < 64) EXT4_INODE_BIT_FNS(state, state_flags, 0) @@ -2015,6 +2017,24 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei) /* We depend on the fact that callers will set i_flags */ } #endif + +static inline unsigned long *ext4_inode_state_wait_word(struct inode *inode) +{ +#if (BITS_PER_LONG < 64) + return &EXT4_I(inode)->i_state_flags; +#else + return &EXT4_I(inode)->i_flags; +#endif +} + +static inline int ext4_inode_state_wait_bit(int bit) +{ +#if (BITS_PER_LONG < 64) + return bit; +#else + return bit + 32; +#endif +} #else /* Assume that user mode programs are passing in an ext4fs superblock, not * a kernel struct super_block. This will allow us to call the feature-test diff --git a/fs/ext4/fast_commit.c b/fs/ext4/fast_commit.c index b3c22636251dd..1775bce9649a9 100644 --- a/fs/ext4/fast_commit.c +++ b/fs/ext4/fast_commit.c @@ -239,6 +239,8 @@ void ext4_fc_del(struct inode *inode) struct ext4_inode_info *ei = EXT4_I(inode); struct ext4_fc_dentry_update *fc_dentry; wait_queue_head_t *wq; + unsigned long *wait_word = ext4_inode_state_wait_word(inode); + int wait_bit = ext4_inode_state_wait_bit(EXT4_STATE_FC_FLUSHING_DATA); int alloc_ctx; if (ext4_fc_disabled(inode->i_sb)) @@ -268,17 +270,9 @@ void ext4_fc_del(struct inode *inode) WARN_ON(ext4_test_inode_state(inode, EXT4_STATE_FC_COMMITTING) && !ext4_test_mount_flag(inode->i_sb, EXT4_MF_FC_INELIGIBLE)); while (ext4_test_inode_state(inode, EXT4_STATE_FC_FLUSHING_DATA)) { -#if (BITS_PER_LONG < 64) - DEFINE_WAIT_BIT(wait, &ei->i_state_flags, - EXT4_STATE_FC_FLUSHING_DATA); - wq = bit_waitqueue(&ei->i_state_flags, - EXT4_STATE_FC_FLUSHING_DATA); -#else - DEFINE_WAIT_BIT(wait, &ei->i_flags, - EXT4_STATE_FC_FLUSHING_DATA); - wq = bit_waitqueue(&ei->i_flags, - EXT4_STATE_FC_FLUSHING_DATA); -#endif + DEFINE_WAIT_BIT(wait, wait_word, wait_bit); + + wq = bit_waitqueue(wait_word, wait_bit); prepare_to_wait(wq, &wait.wq_entry, TASK_UNINTERRUPTIBLE); if (ext4_test_inode_state(inode, EXT4_STATE_FC_FLUSHING_DATA)) { ext4_fc_unlock(inode->i_sb, alloc_ctx); @@ -542,6 +536,8 @@ void ext4_fc_track_inode(handle_t *handle, struct inode *inode) { struct ext4_inode_info *ei = EXT4_I(inode); wait_queue_head_t *wq; + unsigned long *wait_word = ext4_inode_state_wait_word(inode); + int wait_bit = ext4_inode_state_wait_bit(EXT4_STATE_FC_COMMITTING); int ret; if (S_ISDIR(inode->i_mode)) @@ -564,17 +560,9 @@ void ext4_fc_track_inode(handle_t *handle, struct inode *inode) lockdep_assert_not_held(&ei->i_data_sem); while (ext4_test_inode_state(inode, EXT4_STATE_FC_COMMITTING)) { -#if (BITS_PER_LONG < 64) - DEFINE_WAIT_BIT(wait, &ei->i_state_flags, - EXT4_STATE_FC_COMMITTING); - wq = bit_waitqueue(&ei->i_state_flags, - EXT4_STATE_FC_COMMITTING); -#else - DEFINE_WAIT_BIT(wait, &ei->i_flags, - EXT4_STATE_FC_COMMITTING); - wq = bit_waitqueue(&ei->i_flags, - EXT4_STATE_FC_COMMITTING); -#endif + DEFINE_WAIT_BIT(wait, wait_word, wait_bit); + + wq = bit_waitqueue(wait_word, wait_bit); prepare_to_wait(wq, &wait.wq_entry, TASK_UNINTERRUPTIBLE); if (ext4_test_inode_state(inode, EXT4_STATE_FC_COMMITTING)) schedule(); @@ -1034,6 +1022,8 @@ static int ext4_fc_perform_commit(journal_t *journal) int ret = 0; u32 crc = 0; int alloc_ctx; + int flushing_wait_bit = + ext4_inode_state_wait_bit(EXT4_STATE_FC_FLUSHING_DATA); /* * Step 1: Mark all inodes on s_fc_q[MAIN] with @@ -1059,11 +1049,8 @@ static int ext4_fc_perform_commit(journal_t *journal) list_for_each_entry(iter, &sbi->s_fc_q[FC_Q_MAIN], i_fc_list) { ext4_clear_inode_state(&iter->vfs_inode, EXT4_STATE_FC_FLUSHING_DATA); -#if (BITS_PER_LONG < 64) - wake_up_bit(&iter->i_state_flags, EXT4_STATE_FC_FLUSHING_DATA); -#else - wake_up_bit(&iter->i_flags, EXT4_STATE_FC_FLUSHING_DATA); -#endif + wake_up_bit(ext4_inode_state_wait_word(&iter->vfs_inode), + flushing_wait_bit); } /* @@ -1279,6 +1266,8 @@ static void ext4_fc_cleanup(journal_t *journal, int full, tid_t tid) struct ext4_inode_info *ei; struct ext4_fc_dentry_update *fc_dentry; int alloc_ctx; + int committing_wait_bit = + ext4_inode_state_wait_bit(EXT4_STATE_FC_COMMITTING); if (full && sbi->s_fc_bh) sbi->s_fc_bh = NULL; @@ -1315,11 +1304,8 @@ static void ext4_fc_cleanup(journal_t *journal, int full, tid_t tid) * barrier in prepare_to_wait() in ext4_fc_track_inode(). */ smp_mb(); -#if (BITS_PER_LONG < 64) - wake_up_bit(&ei->i_state_flags, EXT4_STATE_FC_COMMITTING); -#else - wake_up_bit(&ei->i_flags, EXT4_STATE_FC_COMMITTING); -#endif + wake_up_bit(ext4_inode_state_wait_word(&ei->vfs_inode), + committing_wait_bit); } while (!list_empty(&sbi->s_fc_dentry_q[FC_Q_MAIN])) { From e4ee9d5b3226906179217be8625717b8a5c9541e Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 22 May 2026 08:10:12 +0200 Subject: [PATCH 1125/2758] irqchip/exynos-combiner: Remove useless spinlock [ Upstream commit 6fe450074626eaab3def4b3e8c1819d46d2d682c ] irq_controller_lock doesn't protect anything, it is a leftover from early development or copy/paste. Remove it completely. Fixes: 96031b31a4b3 ("irqchip/exynos-combiner: Switch to raw_spinlock") Suggested-by: Thomas Gleixner Suggested-by: Sebastian Andrzej Siewior Signed-off-by: Marek Szyprowski Signed-off-by: Thomas Gleixner Reviewed-by: Sebastian Andrzej Siewior Reviewed-by: Peter Griffin Link: https://lore.kernel.org/all/20260521090453.bbUZ00tS@linutronix.de Link: https://patch.msgid.link/20260522061012.2687122-1-m.szyprowski@samsung.com/ Signed-off-by: Sasha Levin --- drivers/irqchip/exynos-combiner.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c index 03cafcc5c8355..d9d408cb4711e 100644 --- a/drivers/irqchip/exynos-combiner.c +++ b/drivers/irqchip/exynos-combiner.c @@ -24,8 +24,6 @@ #define IRQ_IN_COMBINER 8 -static DEFINE_RAW_SPINLOCK(irq_controller_lock); - struct combiner_chip_data { unsigned int hwirq_offset; unsigned int irq_mask; @@ -72,9 +70,7 @@ static void combiner_handle_cascade_irq(struct irq_desc *desc) chained_irq_enter(chip, desc); - raw_spin_lock(&irq_controller_lock); status = readl_relaxed(chip_data->base + COMBINER_INT_STATUS); - raw_spin_unlock(&irq_controller_lock); status &= chip_data->irq_mask; if (status == 0) From efb1dadaeb897b22b9e118d398d0f41e70e9ccca Mon Sep 17 00:00:00 2001 From: Vitaly Prosyak Date: Thu, 14 May 2026 18:55:42 -0400 Subject: [PATCH 1126/2758] drm/amdgpu: fix KASAN slab-out-of-bounds in amdgpu_coredump ring dump MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 08ac3a7879d300302a1927ce2038629539a37f8b ] The ring content dump in amdgpu_coredump() uses two separate loops over adev->rings[]: the first counts rings with unsignalled fences to size the allocation, and the second copies ring data into the allocated buffers. Both loops use the same condition to skip rings: atomic_read(&ring->fence_drv.last_seq) == ring->fence_drv.sync_seq Because last_seq is an atomic that is updated concurrently by the fence signalling path, additional rings may appear unsignalled in the second loop that were signalled during the first. When this happens, idx exceeds the allocated ring_count and the store to coredump->rings[idx] writes past the end of the kcalloc-ed buffer. This was found during IGT stressful test amd_queue_reset which triggers random GPU resets. The OVERSIZE subtest (CMD_STREAM_EXEC_INVALID_PACKET_LENGTH_OVERSIZE on GFX ring) provokes a ring timeout and subsequent coredump, which hits the race between the counting and copying loops. The failure is non-deterministic and depends on fence signalling timing during the reset. KASAN log: BUG: KASAN: slab-out-of-bounds in amdgpu_coredump+0x1274/0x12f0 [amdgpu] Write of size 4 at addr ffff888106154258 by task kworker/u128:5/23625 CPU: 16 UID: 0 PID: 23625 Comm: kworker/u128:5 Not tainted 6.19.0+ #35 Workqueue: amdgpu-reset-dev drm_sched_job_timedout [gpu_sched] Call Trace: dump_stack_lvl+0xa5/0x110 print_report+0xd1/0x660 kasan_report+0xf3/0x130 __asan_report_store4_noabort+0x17/0x30 amdgpu_coredump+0x1274/0x12f0 [amdgpu] amdgpu_job_timedout+0xef0/0x16c0 [amdgpu] drm_sched_job_timedout+0x194/0x5c0 [gpu_sched] process_one_work+0x84b/0x1990 worker_thread+0x6b8/0x11b0 Allocated by task 23625: kasan_save_stack+0x39/0x70 __kasan_kmalloc+0xc3/0xd0 __kmalloc_noprof+0x2ec/0x910 amdgpu_coredump+0x5c5/0x12f0 [amdgpu] amdgpu_job_timedout+0xef0/0x16c0 [amdgpu] The buggy address belongs to the object at ffff888106154200 which belongs to the cache kmalloc-rnd-09-96 of size 96 The buggy address is located 16 bytes to the right of allocated 72-byte region [ffff888106154200, ffff888106154248) 72 bytes = 3 * sizeof(struct amdgpu_coredump_ring), so ring_count was 3 but idx reached 3+, writing ring_index (at struct offset 16) 16 bytes past the allocation. Fix by adding an idx < ring_count guard to the copy loop so it cannot exceed the allocated count even when the fence state changes between the two passes. Fixes: eea85914d15b (drm/amdgpu: save ring content before resetting the device) Cc: Pierre-Eric Pelloux-Prayer Cc: Christian König Cc: Alex Deucher Signed-off-by: Vitaly Prosyak Reviewed-by: Pierre-Eric Pelloux-Prayer Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c index d386bc775d03c..3d5a2abf27c63 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c @@ -553,7 +553,7 @@ void amdgpu_coredump(struct amdgpu_device *adev, bool skip_vram_check, coredump->rings_dw = kzalloc(total_ring_size, GFP_NOWAIT); coredump->rings = kcalloc(ring_count, sizeof(struct amdgpu_coredump_ring), GFP_NOWAIT); if (coredump->rings && coredump->rings_dw) { - for (i = 0, off = 0, idx = 0; i < adev->num_rings; i++) { + for (i = 0, off = 0, idx = 0; i < adev->num_rings && idx < ring_count; i++) { ring = adev->rings[i]; if (atomic_read(&ring->fence_drv.last_seq) == ring->fence_drv.sync_seq && From f85750e0f87730bca937fd22bbf4905c1b4d2975 Mon Sep 17 00:00:00 2001 From: Vitaly Prosyak Date: Thu, 23 Apr 2026 19:44:33 -0400 Subject: [PATCH 1127/2758] drm/amd/pm: Add empty string validation to sysfs store functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit c872fc05380c4eb2761ab289ce6a215c9bfa9576 ] Discovery: Fuzzing for secure supply chain requirements Tool: amd_fuzzing_sysfs (IGT test) The AMDGPU power management sysfs store functions accept whitespace-only strings when they should reject them with -EINVAL. This was discovered via systematic fuzzing of sysfs interfaces crossing the user/kernel trust boundary. Affected functions: - amdgpu_set_power_dpm_force_performance_level (power_dpm_force_performance_level) - amdgpu_set_power_dpm_state (power_dpm_state) - amdgpu_set_pp_power_profile_mode (pp_power_profile_mode) - amdgpu_read_mask (used by pp_dpm_sclk/mclk/fclk/socclk/pcie) - amdgpu_set_pp_features (pp_features) Impact: - Whitespace-only writes (e.g., "\n", " ") can cause unexpected behavior - Better input validation at user/kernel trust boundary - Defense-in-depth improvement Root Cause: The sysfs_streq() function matches whitespace-only strings against empty string, allowing invalid input to be processed. Fix: Add explicit validation at the start of each affected store function: if (count == 0 || sysfs_streq(buf, "")) return -EINVAL; This rejects whitespace-only inputs before they are processed. Note that write() calls with count=0 (truly empty strings) are handled by the VFS layer before reaching the sysfs .store() callback - the VFS returns 0 (success) without calling the kernel function. This is POSIX-compliant behavior and cannot be changed at the kernel driver level. What This Patch Fixes: - Whitespace-only strings: "\n", " ", " ", etc. are now rejected - Defense-in-depth: Explicit validation at trust boundary - Code clarity: Intent to reject invalid input is explicit What This Patch Cannot Fix: - write(fd, "", 0) returning success - this is VFS layer behavior - Fuzzer tests for empty strings (count=0) will still report "accepted" because the VFS handles this before the kernel callback Test Results After Fix: - Whitespace strings ("\n", " ") now properly rejected - Empty string tests (count=0) still show as "accepted" due to VFS behavior - Overall improvement in input validation robustness - No impact on valid inputs This is a defense-in-depth improvement that hardens input validation even though VFS layer behavior prevents catching all edge cases. Tested: amd_fuzzing_sysfs IGT test Cc: Christian König Cc: Alex Deucher Cc: Jesse Zhang Signed-off-by: Vitaly Prosyak Acked-by: Alex Deucher Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/pm/amdgpu_pm.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c index 024bfdb7c1571..952391aecf2d3 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c @@ -244,6 +244,10 @@ static ssize_t amdgpu_set_power_dpm_state(struct device *dev, enum amd_pm_state_type state; int ret; + /* Reject empty/whitespace strings - fuzzing found this is not validated */ + if (count == 0 || sysfs_streq(buf, "")) + return -EINVAL; + if (sysfs_streq(buf, "battery")) state = POWER_STATE_TYPE_BATTERY; else if (sysfs_streq(buf, "balanced")) @@ -364,6 +368,10 @@ static ssize_t amdgpu_set_power_dpm_force_performance_level(struct device *dev, enum amd_dpm_forced_level level; int ret = 0; + /* Reject empty/whitespace strings - fuzzing found this is not validated */ + if (count == 0 || sysfs_streq(buf, "")) + return -EINVAL; + if (sysfs_streq(buf, "low")) level = AMD_DPM_FORCED_LEVEL_LOW; else if (sysfs_streq(buf, "high")) @@ -902,6 +910,10 @@ static ssize_t amdgpu_set_pp_features(struct device *dev, uint64_t featuremask; int ret; + /* Reject empty/whitespace strings - fuzzing found kstrtou64 accepts "" as 0 */ + if (count == 0 || sysfs_streq(buf, "")) + return -EINVAL; + ret = kstrtou64(buf, 0, &featuremask); if (ret) return -EINVAL; @@ -1027,6 +1039,10 @@ static ssize_t amdgpu_read_mask(const char *buf, size_t count, uint32_t *mask) *mask = 0; + /* Reject empty/whitespace strings - fuzzing found this is not validated */ + if (count == 0 || sysfs_streq(buf, "")) + return -EINVAL; + bytes = min(count, sizeof(buf_cpy) - 1); memcpy(buf_cpy, buf, bytes); buf_cpy[bytes] = '\0'; @@ -1378,6 +1394,10 @@ static ssize_t amdgpu_set_pp_power_profile_mode(struct device *dev, long int profile_mode = 0; const char delimiter[3] = {' ', '\n', '\0'}; + /* Reject empty/whitespace strings - fuzzing found this is not validated */ + if (count == 0 || sysfs_streq(buf, "")) + return -EINVAL; + tmp[0] = *(buf); tmp[1] = '\0'; ret = kstrtol(tmp, 0, &profile_mode); From be576a522cda35b2dd70dd0f1909193fb060b851 Mon Sep 17 00:00:00 2001 From: Yunxiang Li Date: Mon, 1 Jun 2026 15:15:06 -0400 Subject: [PATCH 1128/2758] drm/amdgpu: set sub_block_index for mca ras sub-blocks [ Upstream commit baa286df5fb366e4aee5b747443dfc7194fdaa59 ] The mca ras sub-blocks (mp0, mp1, mpio) all share the AMDGPU_RAS_BLOCK__MCA block id and are distinguished only by sub_block_index. The ras manager object for an mca block is selected with: con->objs[AMDGPU_RAS_BLOCK__LAST + head->sub_block_index] Since the rework in commit 7f544c5488cf ("drm/amdgpu: Rework mca ras sw_init") moved the ras_comm setup into amdgpu_mca_mp*_ras_sw_init() but left sub_block_index unset, mp0/mp1/mpio all default to index 0 and collide on the same object slot. mp0 grabs the slot and creates its sysfs node first; mp1 (and mpio) then find the slot already in use, so amdgpu_ras_block_late_init() -> amdgpu_ras_sysfs_create() returns -EINVAL: amdgpu: mca.mp1 failed to execute ras_block_late_init_default! ret:-22 amdgpu: amdgpu_ras_late_init failed -22 amdgpu: amdgpu_device_ip_late_init failed amdgpu: Fatal error during GPU init The error is currently masked because amdgpu_ras_late_init() does not check the return value of amdgpu_ras_block_late_init_default(), but it already leaves mp1/mpio without their sysfs nodes and becomes a fatal init failure as soon as that return value is honored. Restore the per-sub-block sub_block_index assignment so each mca sub-block maps to its own object slot. Fixes: 7f544c5488cf ("drm/amdgpu: Rework mca ras sw_init") Signed-off-by: Yunxiang Li Reviewed-by: Hawking Zhang Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdgpu/amdgpu_mca.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mca.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_mca.c index 823ba17e32af4..cc6d1a4e4c3af 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mca.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mca.c @@ -99,6 +99,7 @@ int amdgpu_mca_mp0_ras_sw_init(struct amdgpu_device *adev) strcpy(ras->ras_block.ras_comm.name, "mca.mp0"); ras->ras_block.ras_comm.block = AMDGPU_RAS_BLOCK__MCA; + ras->ras_block.ras_comm.sub_block_index = AMDGPU_RAS_MCA_BLOCK__MP0; ras->ras_block.ras_comm.type = AMDGPU_RAS_ERROR__MULTI_UNCORRECTABLE; adev->mca.mp0.ras_if = &ras->ras_block.ras_comm; @@ -123,6 +124,7 @@ int amdgpu_mca_mp1_ras_sw_init(struct amdgpu_device *adev) strcpy(ras->ras_block.ras_comm.name, "mca.mp1"); ras->ras_block.ras_comm.block = AMDGPU_RAS_BLOCK__MCA; + ras->ras_block.ras_comm.sub_block_index = AMDGPU_RAS_MCA_BLOCK__MP1; ras->ras_block.ras_comm.type = AMDGPU_RAS_ERROR__MULTI_UNCORRECTABLE; adev->mca.mp1.ras_if = &ras->ras_block.ras_comm; @@ -147,6 +149,7 @@ int amdgpu_mca_mpio_ras_sw_init(struct amdgpu_device *adev) strcpy(ras->ras_block.ras_comm.name, "mca.mpio"); ras->ras_block.ras_comm.block = AMDGPU_RAS_BLOCK__MCA; + ras->ras_block.ras_comm.sub_block_index = AMDGPU_RAS_MCA_BLOCK__MPIO; ras->ras_block.ras_comm.type = AMDGPU_RAS_ERROR__MULTI_UNCORRECTABLE; adev->mca.mpio.ras_if = &ras->ras_block.ras_comm; From b6bbac4856fe34d0bf6fc139e21db88109f77841 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Fri, 29 May 2026 09:21:22 -0700 Subject: [PATCH 1129/2758] accel/amdxdna: Return errors for failed debug BO commands [ Upstream commit 62c1671f6454ceaa80e9ceff63f821aa36f35154 ] The config and sync debug BO commands currently may report success even when the operation fails. Capture the firmware return status and propagate the corresponding error to userspace. Fixes: 7ea046838021 ("accel/amdxdna: Support firmware debug buffer") Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260529162122.1976376-1-lizhi.hou@amd.com Signed-off-by: Sasha Levin --- drivers/accel/amdxdna/aie2_ctx.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c index d719000c45325..93535806e72a1 100644 --- a/drivers/accel/amdxdna/aie2_ctx.c +++ b/drivers/accel/amdxdna/aie2_ctx.c @@ -270,17 +270,13 @@ aie2_sched_drvcmd_resp_handler(void *handle, void __iomem *data, size_t size) struct amdxdna_sched_job *job = handle; int ret = 0; - if (unlikely(!data)) - goto out; - - if (unlikely(size != sizeof(u32))) { + if (unlikely(!data || size != sizeof(u32))) { + job->drv_cmd->result = U32_MAX; ret = -EINVAL; - goto out; + } else { + job->drv_cmd->result = readl(data); } - job->drv_cmd->result = readl(data); - -out: aie2_sched_notify(job); return ret; } @@ -894,6 +890,7 @@ static int aie2_hwctx_cfg_debug_bo(struct amdxdna_hwctx *hwctx, u32 bo_hdl, aie2_cmd_wait(hwctx, seq); if (cmd.result) { XDNA_ERR(xdna, "Response failure 0x%x", cmd.result); + ret = -EINVAL; goto put_obj; } From cfec9afb8a80a37c975cc96fb7b4cf5dd3f670c7 Mon Sep 17 00:00:00 2001 From: Woojin Ji Date: Wed, 3 Jun 2026 09:33:39 +0900 Subject: [PATCH 1130/2758] bpftool: Use libbpf error code for flow dissector query [ Upstream commit 8a7f2bff2165e53595d1e91c160b340f978c0ab7 ] bpf_prog_query() returns a negative errno on failure. query_flow_dissector() currently closes the namespace fd and then reads errno to decide whether -EINVAL means that the running kernel does not support flow dissector queries. That errno check controls behavior, not just diagnostics: -EINVAL is handled as a non-fatal old-kernel case, while any other error makes bpftool net fail. The namespace fd is opened read-only, so close() is not expected to commonly fail in normal use. Still, the BPF_PROG_QUERY error is already available in err, and reading errno after an intervening close() is fragile. If close() does change errno, the compatibility branch may be based on close()'s error instead of the BPF_PROG_QUERY result. This was reproduced with an LD_PRELOAD fault injector that forced BPF_PROG_QUERY for BPF_FLOW_DISSECTOR to fail with EINVAL and then forced close() on the netns fd to fail with EIO. The unpatched bpftool reported "can't query prog: Input/output error". With this change, the same injected failure is handled as the intended non-fatal EINVAL compatibility case. Use the libbpf-returned error code instead. Keep the existing errno reset in the non-fatal path to preserve batch mode behavior. The success path is unchanged. Fixes: 7f0c57fec80f ("bpftool: show flow_dissector attachment status") Signed-off-by: Woojin Ji Signed-off-by: Andrii Nakryiko Acked-by: Leon Hwang Acked-by: Yonghong Song Acked-by: Quentin Monnet Link: https://lore.kernel.org/bpf/20260603003339.33791-1-random6.xyz@gmail.com Assisted-by: ChatGPT:gpt-5.5 Signed-off-by: Sasha Levin --- tools/bpf/bpftool/net.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/bpf/bpftool/net.c b/tools/bpf/bpftool/net.c index 974189da8a916..dba28755d2849 100644 --- a/tools/bpf/bpftool/net.c +++ b/tools/bpf/bpftool/net.c @@ -603,14 +603,14 @@ static int query_flow_dissector(struct bpf_attach_info *attach_info) &attach_flags, prog_ids, &prog_cnt); close(fd); if (err) { - if (errno == EINVAL) { + if (err == -EINVAL) { /* Older kernel's don't support querying * flow dissector programs. */ errno = 0; return 0; } - p_err("can't query prog: %s", strerror(errno)); + p_err("can't query prog: %s", strerror(-err)); return -1; } From a9326b652bc7acd748d7a1143573845c7924d847 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 2 Mar 2026 03:51:49 -0500 Subject: [PATCH 1131/2758] vhost: fix vhost_get_avail_idx for a non empty ring [ Upstream commit 09861858a68342f851f71c669ac0f69865c32151 ] vhost_get_avail_idx is supposed to report whether it has updated vq->avail_idx. Instead, it returns whether all entries have been consumed, which is usually the same. But not always - in drivers/vhost/net.c and when mergeable buffers have been enabled, the driver checks whether the combined entries are big enough to store an incoming packet. If not, the driver re-enables notifications with available entries still in the ring. The incorrect return value from vhost_get_avail_idx propagates through vhost_enable_notify and causes the host to livelock if the guest is not making progress, as vhost will immediately disable notifications and retry using the available entries. This goes back to commit d3bb267bbdcb ("vhost: cache avail index in vhost_enable_notify()") which changed vhost_enable_notify() to compare the freshly read avail index against vq->last_avail_idx instead of the previously cached vq->avail_idx. Commit 7ad472397667 ("vhost: move smp_rmb() into vhost_get_avail_idx()") then carried over the same comparison when refactoring vhost_enable_notify() to call the unified vhost_get_avail_idx(). The obvious fix is to make vhost_get_avail_idx do what the comment says it does and report whether new entries have been added. Reported-by: ShuangYu Fixes: d3bb267bbdcb ("vhost: cache avail index in vhost_enable_notify()") Cc: Stefan Hajnoczi Acked-by: Jason Wang Reviewed-by: Stefano Garzarella Signed-off-by: Michael S. Tsirkin Message-Id: <559b04ae6ce52973c535dc47e461638b7f4c3d63.1772441455.git.mst@redhat.com> Signed-off-by: Sasha Levin --- drivers/vhost/vhost.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c index 2f2c45d208832..db329a6f61458 100644 --- a/drivers/vhost/vhost.c +++ b/drivers/vhost/vhost.c @@ -1522,6 +1522,7 @@ static void vhost_dev_unlock_vqs(struct vhost_dev *d) static inline int vhost_get_avail_idx(struct vhost_virtqueue *vq) { __virtio16 idx; + u16 avail_idx; int r; r = vhost_get_avail(vq, idx, &vq->avail->idx); @@ -1532,17 +1533,19 @@ static inline int vhost_get_avail_idx(struct vhost_virtqueue *vq) } /* Check it isn't doing very strange thing with available indexes */ - vq->avail_idx = vhost16_to_cpu(vq, idx); - if (unlikely((u16)(vq->avail_idx - vq->last_avail_idx) > vq->num)) { + avail_idx = vhost16_to_cpu(vq, idx); + if (unlikely((u16)(avail_idx - vq->last_avail_idx) > vq->num)) { vq_err(vq, "Invalid available index change from %u to %u", - vq->last_avail_idx, vq->avail_idx); + vq->last_avail_idx, avail_idx); return -EINVAL; } /* We're done if there is nothing new */ - if (vq->avail_idx == vq->last_avail_idx) + if (avail_idx == vq->avail_idx) return 0; + vq->avail_idx = avail_idx; + /* * We updated vq->avail_idx so we need a memory barrier between * the index read above and the caller reading avail ring entries. From d16923a45d4d08367650fdc3451c89299ab6ac5a Mon Sep 17 00:00:00 2001 From: Pranjal Shrivastava Date: Thu, 4 Jun 2026 14:03:10 +0800 Subject: [PATCH 1132/2758] iommu/vt-d: Fix RB-tree corruption in probe error path [ Upstream commit 43bd9e6d5513cb1edbafdeef146a1edc3aaced56 ] The info->node RB-tree member is zero-initialized via kzalloc. If a device does not support ATS, the device_rbtree_insert() call is skipped. If a subsequent probe step fails, the error path jumps to device_rbtree_remove(), which misinterprets the zeroed node as a tree root and corrupts the device RB-tree. Fix this by explicitly initializing the RB-node as empty using RB_CLEAR_NODE() during initialization and guarding the removal with RB_EMPTY_NODE(). Fixes: 4f1492efb495 ("iommu/vt-d: Revert ATS timing change to fix boot failure") Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/all/20260525205628.CD4431F000E9@smtp.kernel.org/ Suggested-by: Baolu Lu Signed-off-by: Pranjal Shrivastava Link: https://lore.kernel.org/r/20260531170254.60493-2-praan@google.com Signed-off-by: Lu Baolu Signed-off-by: Joerg Roedel Signed-off-by: Sasha Levin --- drivers/iommu/intel/iommu.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c index 4d0e65bc131d7..849d06dfe1aec 100644 --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -157,7 +157,10 @@ static void device_rbtree_remove(struct device_domain_info *info) unsigned long flags; spin_lock_irqsave(&iommu->device_rbtree_lock, flags); - rb_erase(&info->node, &iommu->device_rbtree); + if (!RB_EMPTY_NODE(&info->node)) { + rb_erase(&info->node, &iommu->device_rbtree); + RB_CLEAR_NODE(&info->node); + } spin_unlock_irqrestore(&iommu->device_rbtree_lock, flags); } @@ -3254,6 +3257,7 @@ static struct iommu_device *intel_iommu_probe_device(struct device *dev) info->dev = dev; info->iommu = iommu; + RB_CLEAR_NODE(&info->node); if (dev_is_pci(dev)) { if (ecap_dev_iotlb_support(iommu->ecap) && pci_ats_supported(pdev) && From 8e3788d9605ecc3b8290ce822644b9c7647052ee Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 11 May 2026 07:58:27 -0400 Subject: [PATCH 1133/2758] mm: preserve PG_dropbehind flag during folio split [ Upstream commit 0ad186cf167fdbeb6b29a8005c71973781036bd3 ] __split_folio_to_order() copies page flags from the original folio to newly created sub-folios using an explicit allowlist, but PG_dropbehind is not included. When a large folio with PG_dropbehind set is split, only the head sub-folio retains the flag; all tail sub-folios silently lose it and will not be reclaimed eagerly after writeback completes. Add PG_dropbehind to the flag copy mask so that the drop-behind hint is preserved across folio splits. Fixes: a323281cdfec ("mm: add PG_dropbehind folio flag") Assisted-by: Claude:claude-opus-4-6 Signed-off-by: Jeff Layton Link: https://patch.msgid.link/20260511-dontcache-v7-1-2848ddce8090@kernel.org Acked-by: David Hildenbrand (Arm) Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- mm/huge_memory.c | 1 + 1 file changed, 1 insertion(+) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index b118bcd392cb3..d29e85495091d 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -3644,6 +3644,7 @@ static void __split_folio_to_order(struct folio *folio, int old_order, (1L << PG_arch_3) | #endif (1L << PG_dirty) | + (1L << PG_dropbehind) | LRU_GEN_MASK | LRU_REFS_MASK)); if (handle_hwpoison && From 0fcf81fc5f01a4fb88cab0e4ded79cee09ceecad Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 24 Apr 2026 15:46:39 +0200 Subject: [PATCH 1134/2758] eventpoll: rename attach_epitem() to ep_attach_file() [ Upstream commit 6a3f1a494bc91d7976cf0d2b200bb3f1a22eef64 ] ep_remove_file() tears down the f_ep linkage that attach_epitem() establishes, so the pair should look like one. Rename to ep_attach_file() for the "ep_*" + subject symmetry and to match the naming used elsewhere in the file (ep_insert, ep_modify, ep_remove, ep_remove_file, ep_remove_epi, ep_unregister_pollwait). Pure rename; no functional change. Signed-off-by: Christian Brauner (Amutable) Link: https://patch.msgid.link/20260424-work-epoll-rework-v1-8-249ed00a20f3@kernel.org Signed-off-by: Christian Brauner Stable-dep-of: 0c4aefe3c2d0 ("eventpoll: Fix epoll_wait() report false negative") Signed-off-by: Sasha Levin --- fs/eventpoll.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index a3090b446af10..556049d0da3fb 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -1526,7 +1526,7 @@ static noinline void ep_destroy_wakeup_source(struct epitem *epi) wakeup_source_unregister(ws); } -static int attach_epitem(struct file *file, struct epitem *epi) +static int ep_attach_file(struct file *file, struct epitem *epi) { struct epitems_head *to_free = NULL; struct hlist_head *head = NULL; @@ -1597,7 +1597,7 @@ static int ep_insert(struct eventpoll *ep, const struct epoll_event *event, if (tep) mutex_lock_nested(&tep->mtx, 1); /* Add the current item to the list of active epoll hook for this file */ - if (unlikely(attach_epitem(tfile, epi) < 0)) { + if (unlikely(ep_attach_file(tfile, epi) < 0)) { if (tep) mutex_unlock(&tep->mtx); kmem_cache_free(epi_cache, epi); From e30ab826f7c981b4e4b35c507e5fde77ece3efd8 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 24 Apr 2026 15:46:41 +0200 Subject: [PATCH 1135/2758] eventpoll: split ep_insert() into alloc + register stages [ Upstream commit e0e35f4cb983a55a36e79e9b2a20ca0e0688fae6 ] ep_insert() was 130 lines and mixed four concerns in one body: user quota charge and epitem allocation, attach-into-file-hlist plus rbtree insert plus target-ep locking, reverse-path + EPOLLWAKEUP + poll-queue install with rollback, and ready-list publication. Factor the first two concerns into named helpers so the body reduces to orchestration. ep_alloc_epitem() charges the user's epoll_watches quota, allocates a fresh epitem, and initializes its fields. On failure it returns ERR_PTR(-ENOSPC) or ERR_PTR(-ENOMEM); on success the epi is not yet linked into anything. ep_register_epitem() installs @epi into @tfile's f_ep hlist and @ep's rbtree, optionally chains @tfile onto tfile_check_list for the path check, takes the tep->mtx nested lock for the epoll-watches- epoll case, and finally takes the ep_get() reference that pairs with ep_remove()'s ep_put() in ep_insert()'s error paths. On failure it frees the epi and decrements epoll_watches to match ep_alloc_epitem(). ep_insert()'s remaining body is the rollback-via-ep_remove() chain (reverse_path_check, EPOLLWAKEUP source creation, ep_ptable_queue_proc allocation) and the ready-list / wake publication. Remove a few stale comments that duplicated function-level documentation or described obvious code. No functional change; rollback boundaries unchanged -- every error path after ep_register_epitem() still calls ep_remove(), preserving the ep->refcount invariant that keeps ep_remove()'s WARN_ON_ONCE safe. Signed-off-by: Christian Brauner (Amutable) Link: https://patch.msgid.link/20260424-work-epoll-rework-v1-10-249ed00a20f3@kernel.org Signed-off-by: Christian Brauner Stable-dep-of: 0c4aefe3c2d0 ("eventpoll: Fix epoll_wait() report false negative") Signed-off-by: Sasha Levin --- fs/eventpoll.c | 111 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 74 insertions(+), 37 deletions(-) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 556049d0da3fb..1b340efc1166e 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -1561,68 +1561,112 @@ static int ep_attach_file(struct file *file, struct epitem *epi) } /* - * Must be called with "mtx" held. + * Charge the user's epoll_watches quota, allocate a fresh epitem for + * @tfile/@fd, and initialize its fields. The returned item is not yet + * linked into any data structure; the caller must install it via + * ep_register_epitem() (which takes over on success) or kmem_cache_free() + * it and decrement epoll_watches on its own. + * + * Returns ERR_PTR(-ENOSPC) if the quota is exceeded, ERR_PTR(-ENOMEM) + * if the slab allocation fails. */ -static int ep_insert(struct eventpoll *ep, const struct epoll_event *event, - struct file *tfile, int fd, int full_check) +static struct epitem *ep_alloc_epitem(struct eventpoll *ep, + const struct epoll_event *event, + struct file *tfile, int fd) { - int error, pwake = 0; - __poll_t revents; struct epitem *epi; - struct ep_pqueue epq; - struct eventpoll *tep = NULL; - - if (is_file_epoll(tfile)) - tep = tfile->private_data; - - lockdep_assert_irqs_enabled(); if (unlikely(percpu_counter_compare(&ep->user->epoll_watches, max_user_watches) >= 0)) - return -ENOSPC; + return ERR_PTR(-ENOSPC); percpu_counter_inc(&ep->user->epoll_watches); - if (!(epi = kmem_cache_zalloc(epi_cache, GFP_KERNEL))) { + epi = kmem_cache_zalloc(epi_cache, GFP_KERNEL); + if (unlikely(!epi)) { percpu_counter_dec(&ep->user->epoll_watches); - return -ENOMEM; + return ERR_PTR(-ENOMEM); } - /* Item initialization follow here ... */ INIT_LIST_HEAD(&epi->rdllink); epi->ep = ep; ep_set_ffd(&epi->ffd, tfile, fd); epi->event = *event; epi->next = EP_UNACTIVE_PTR; + return epi; +} + +/* + * Install @epi into its target file's f_ep hlist and into @ep's rbtree, + * taking one additional reference on @ep for the lifetime of the item. + * + * If @tep is non-NULL, the target file is itself an eventpoll; we hold + * tep->mtx at subclass 1 across the attach + rbtree insert to serialize + * with the target side. RB tree ops are protected by @ep->mtx, which + * the caller already holds. + * + * On failure the epi is freed and the epoll_watches counter decremented, + * matching ep_alloc_epitem()'s allocation. After this returns + * successfully, ep_insert()'s later error paths use ep_remove() for + * unwind; that cannot drop @ep's refcount to zero because the ep file + * itself still holds the original reference. + */ +static int ep_register_epitem(struct eventpoll *ep, struct epitem *epi, + struct eventpoll *tep, int full_check) +{ + struct file *tfile = epi->ffd.file; + int error; + if (tep) mutex_lock_nested(&tep->mtx, 1); - /* Add the current item to the list of active epoll hook for this file */ - if (unlikely(ep_attach_file(tfile, epi) < 0)) { + + error = ep_attach_file(tfile, epi); + if (unlikely(error)) { if (tep) mutex_unlock(&tep->mtx); kmem_cache_free(epi_cache, epi); percpu_counter_dec(&ep->user->epoll_watches); - return -ENOMEM; + return error; } if (full_check && !tep) list_file(tfile); - /* - * Add the current item to the RB tree. All RB tree operations are - * protected by "mtx", and ep_insert() is called with "mtx" held. - */ ep_rbtree_insert(ep, epi); + if (tep) mutex_unlock(&tep->mtx); - /* - * ep_remove() calls in the later error paths can't lead to - * ep_free() as the ep file itself still holds an ep reference. - */ ep_get(ep); + return 0; +} + +/* + * Must be called with "mtx" held. + */ +static int ep_insert(struct eventpoll *ep, const struct epoll_event *event, + struct file *tfile, int fd, int full_check) +{ + int error, pwake = 0; + __poll_t revents; + struct epitem *epi; + struct ep_pqueue epq; + struct eventpoll *tep = NULL; + + if (is_file_epoll(tfile)) + tep = tfile->private_data; + + lockdep_assert_irqs_enabled(); + + epi = ep_alloc_epitem(ep, event, tfile, fd); + if (IS_ERR(epi)) + return PTR_ERR(epi); - /* now check if we've created too many backpaths */ + error = ep_register_epitem(ep, epi, tep, full_check); + if (error) + return error; + + /* Reject the insert if the new link would create too many back-paths. */ if (unlikely(full_check && reverse_path_check())) { ep_remove(ep, epi); return -EINVAL; @@ -1649,28 +1693,21 @@ static int ep_insert(struct eventpoll *ep, const struct epoll_event *event, */ revents = ep_item_poll(epi, &epq.pt, 1); - /* - * We have to check if something went wrong during the poll wait queue - * install process. Namely an allocation for a wait queue failed due - * high memory pressure. - */ + /* ep_ptable_queue_proc() signals allocation failure by clearing epq.epi. */ if (unlikely(!epq.epi)) { ep_remove(ep, epi); return -ENOMEM; } - /* We have to drop the new item inside our item list to keep track of it */ + /* Drop the new item onto the ready list if it is already ready. */ spin_lock_irq(&ep->lock); - /* record NAPI ID of new item if present */ ep_set_busy_poll_napi_id(epi); - /* If the file is already "ready" we drop it inside the ready list */ if (revents && !ep_is_linked(epi)) { list_add_tail(&epi->rdllink, &ep->rdllist); ep_pm_stay_awake(epi); - /* Notify waiting tasks that events are available */ if (waitqueue_active(&ep->wq)) wake_up(&ep->wq); if (waitqueue_active(&ep->poll_wait)) From 85b4e4824de72b6ba6b7cca6ee81a7169a0185c0 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 24 Apr 2026 15:46:43 +0200 Subject: [PATCH 1136/2758] eventpoll: extract ep_deliver_event() from ep_send_events() [ Upstream commit 499a5e7f4a57fa08a297c627d007a55069acac9a ] ep_send_events()'s body covered two concerns: per-item work (PM wakeup-source bookkeeping, re-poll, copy_to_user, level-trigger re-queue, EPOLLONESHOT mask clear) and the scan-level accumulator (maxevents cap, EFAULT preservation, txlist/rdllist splice). Extract the per-item work as ep_deliver_event(), which returns a tri-state int: 1 one event was delivered; caller advances the counter, 0 re-poll produced no caller-requested events (item drops out of the ready list; a future callback will re-queue), -EFAULT copy_to_user() faulted; item is already re-inserted at the head of the txlist so ep_done_scan() splices it back to rdllist. The per-item comments (PM ordering, the "sole writer to rdllist" invariant for the LT re-queue, the EFAULT semantics) move into ep_deliver_event(). ep_send_events() reduces to the fatal-signal short-circuit, scan bracket, and a short txlist walk that accumulates the deliveries and preserves the "first error wins" EFAULT contract (res = delivered only if no event was previously delivered; otherwise the success count is returned and -EFAULT is reported on the next call). No functional change. Signed-off-by: Christian Brauner (Amutable) Link: https://patch.msgid.link/20260424-work-epoll-rework-v1-12-249ed00a20f3@kernel.org Signed-off-by: Christian Brauner Stable-dep-of: 0c4aefe3c2d0 ("eventpoll: Fix epoll_wait() report false negative") Signed-off-by: Sasha Levin --- fs/eventpoll.c | 138 ++++++++++++++++++++++++++++++------------------- 1 file changed, 84 insertions(+), 54 deletions(-) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 1b340efc1166e..94667f0f21546 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -1799,6 +1799,82 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi, return 0; } +/* + * Attempt to deliver one event for @epi into @*uevents. + * + * Returns 1 if an event was delivered (with *uevents advanced to the + * next slot), 0 if the re-poll reported no caller-requested events + * (@epi drops out of the ready list; a future callback will re-add + * it), or -EFAULT if copy_to_user() faulted (in which case @epi is + * re-inserted at the head of @txlist so ep_done_scan() merges it + * back to rdllist for the next attempt). + * + * PM bookkeeping and level-triggered re-queue are handled here. + * Caller holds ep->mtx and the scan is active. + */ +static int ep_deliver_event(struct eventpoll *ep, struct epitem *epi, + poll_table *pt, + struct epoll_event __user **uevents, + struct list_head *txlist) +{ + struct epoll_event __user *next; + struct wakeup_source *ws; + __poll_t revents; + + /* + * Activate ep->ws before deactivating epi->ws to prevent + * triggering auto-suspend here (in case we reactivate epi->ws + * below). Rearranging to delay the deactivation would let + * epi->ws drift out of sync with ep_is_linked(). + */ + ws = ep_wakeup_source(epi); + if (ws) { + if (ws->active) + __pm_stay_awake(ep->ws); + __pm_relax(ws); + } + + list_del_init(&epi->rdllink); + + /* + * Re-poll under ep->mtx so userspace cannot change the item + * out from under us. If no caller-requested events remain, + * @epi stays off the ready list; the poll callback will + * re-queue it when events next appear. + */ + revents = ep_item_poll(epi, pt, 1); + if (!revents) + return 0; + + next = epoll_put_uevent(revents, epi->event.data, *uevents); + if (!next) { + /* + * copy_to_user() faulted: put the item back so + * ep_done_scan() splices it onto rdllist for the next + * attempt. + */ + list_add(&epi->rdllink, txlist); + ep_pm_stay_awake(epi); + return -EFAULT; + } + *uevents = next; + + if (epi->event.events & EPOLLONESHOT) { + epi->event.events &= EP_PRIVATE_BITS; + } else if (!(epi->event.events & EPOLLET)) { + /* + * Level-triggered: re-queue so the next epoll_wait() + * rechecks availability. We are the sole writer to + * rdllist here -- epoll_ctl() callers are locked out + * by ep->mtx, and the poll callback queues to ovflist + * during scans. + */ + list_add_tail(&epi->rdllink, &ep->rdllist); + ep_pm_stay_awake(epi); + } + return 1; +} + static int ep_send_events(struct eventpoll *ep, struct epoll_event __user *events, int maxevents) { @@ -1821,70 +1897,24 @@ static int ep_send_events(struct eventpoll *ep, ep_start_scan(ep, &txlist); /* - * We can loop without lock because we are passed a task private list. - * Items cannot vanish during the loop we are holding ep->mtx. + * We can loop without lock because we are passed a task-private + * txlist; items cannot vanish while we hold ep->mtx. */ list_for_each_entry_safe(epi, tmp, &txlist, rdllink) { - struct wakeup_source *ws; - __poll_t revents; + int delivered; if (res >= maxevents) break; - /* - * Activate ep->ws before deactivating epi->ws to prevent - * triggering auto-suspend here (in case we reactive epi->ws - * below). - * - * This could be rearranged to delay the deactivation of epi->ws - * instead, but then epi->ws would temporarily be out of sync - * with ep_is_linked(). - */ - ws = ep_wakeup_source(epi); - if (ws) { - if (ws->active) - __pm_stay_awake(ep->ws); - __pm_relax(ws); - } - - list_del_init(&epi->rdllink); - - /* - * If the event mask intersect the caller-requested one, - * deliver the event to userspace. Again, we are holding ep->mtx, - * so no operations coming from userspace can change the item. - */ - revents = ep_item_poll(epi, &pt, 1); - if (!revents) - continue; - - events = epoll_put_uevent(revents, epi->event.data, events); - if (!events) { - list_add(&epi->rdllink, &txlist); - ep_pm_stay_awake(epi); + delivered = ep_deliver_event(ep, epi, &pt, &events, &txlist); + if (delivered < 0) { if (!res) - res = -EFAULT; + res = delivered; break; } - res++; - if (epi->event.events & EPOLLONESHOT) - epi->event.events &= EP_PRIVATE_BITS; - else if (!(epi->event.events & EPOLLET)) { - /* - * If this file has been added with Level - * Trigger mode, we need to insert back inside - * the ready list, so that the next call to - * epoll_wait() will check again the events - * availability. At this point, no one can insert - * into ep->rdllist besides us. The epoll_ctl() - * callers are locked out by - * ep_send_events() holding "mtx" and the - * poll callback will queue them in ep->ovflist. - */ - list_add_tail(&epi->rdllink, &ep->rdllist); - ep_pm_stay_awake(epi); - } + res += delivered; } + ep_done_scan(ep, &txlist); mutex_unlock(&ep->mtx); From f10a7a4266a4332bd86d8642edba18e8c97694ce Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 24 Apr 2026 15:46:45 +0200 Subject: [PATCH 1137/2758] eventpoll: wrap EP_UNACTIVE_PTR in typed sentinel helpers [ Upstream commit 0dcb726466a556f273beaadfb76f12d1b0087dd7 ] ep->ovflist and epi->next both use EP_UNACTIVE_PTR (a cast to (void *)-1) as a sentinel, with distinct meanings at each site: ep->ovflist == EP_UNACTIVE_PTR no scan in progress epi->next == EP_UNACTIVE_PTR epi not on ovflist Call sites had to know the sentinel's value and, by convention, what it meant in each context. Hide both behind inline helpers: ep_is_scanning(ep) predicate for "scan in progress" ep_enter_scan(ep) WRITE_ONCE flip to NULL (scan start) ep_exit_scan(ep) WRITE_ONCE flip to sentinel (scan end) epi_on_ovflist(epi) predicate for "epi is on ovflist" epi_clear_ovflist(epi) clear epi's ovflist link slot Convert ep_events_available(), ep_start_scan(), ep_done_scan(), ep_poll_callback(), and ep_alloc_epitem() to use the wrappers. The ovflist state-machine transitions are now named, not encoded in sentinel comparisons, and the top-of-file "Ready-list state machine" section is the single place that spells out the sentinel's meaning. ep_alloc() keeps the raw "ep->ovflist = EP_UNACTIVE_PTR" init (no concurrent access at that point) with an inline "not scanning" comment, and the tfile_check_list sentinel is left alone -- it will disappear entirely when the loop-check globals move into a stack-allocated ep_ctl_ctx in a later commit. Also rework ep_done_scan()'s for-loop: the combined initializer + update clause that advanced nepi AND cleared epi->next in one step was clever but hard to read; splitting the update into two statements inside the body makes the epi_clear_ovflist() call visible. No functional change. Signed-off-by: Christian Brauner (Amutable) Link: https://patch.msgid.link/20260424-work-epoll-rework-v1-14-249ed00a20f3@kernel.org Signed-off-by: Christian Brauner Stable-dep-of: 0c4aefe3c2d0 ("eventpoll: Fix epoll_wait() report false negative") Signed-off-by: Sasha Levin --- fs/eventpoll.c | 73 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 21 deletions(-) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 94667f0f21546..dc8dc529684bf 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -372,6 +372,43 @@ static inline struct epitem *ep_item_from_wait(wait_queue_entry_t *p) return container_of(p, struct eppoll_entry, wait)->base; } +/* + * Ready-list / ovflist state (see "Ready-list state machine" in the + * top-of-file banner for the full state machine). EP_UNACTIVE_PTR is + * the sentinel; these wrappers name each transition and each test so + * call sites do not need to know the sentinel's value. + */ + +/* True iff @ep is between ep_enter_scan() and ep_exit_scan(). */ +static inline bool ep_is_scanning(struct eventpoll *ep) +{ + return READ_ONCE(ep->ovflist) != EP_UNACTIVE_PTR; +} + +/* Called by ep_start_scan(): divert ep_poll_callback() to ovflist. */ +static inline void ep_enter_scan(struct eventpoll *ep) +{ + WRITE_ONCE(ep->ovflist, NULL); +} + +/* Called by ep_done_scan(): redirect ep_poll_callback() back to rdllist. */ +static inline void ep_exit_scan(struct eventpoll *ep) +{ + WRITE_ONCE(ep->ovflist, EP_UNACTIVE_PTR); +} + +/* True iff @epi is currently linked on its ep's ovflist. */ +static inline bool epi_on_ovflist(const struct epitem *epi) +{ + return epi->next != EP_UNACTIVE_PTR; +} + +/* Mark @epi as not on any ovflist (init and post-drain). */ +static inline void epi_clear_ovflist(struct epitem *epi) +{ + epi->next = EP_UNACTIVE_PTR; +} + /** * ep_events_available - Checks if ready events might be available. * @@ -382,8 +419,7 @@ static inline struct epitem *ep_item_from_wait(wait_queue_entry_t *p) */ static inline int ep_events_available(struct eventpoll *ep) { - return !list_empty_careful(&ep->rdllist) || - READ_ONCE(ep->ovflist) != EP_UNACTIVE_PTR; + return !list_empty_careful(&ep->rdllist) || ep_is_scanning(ep); } #ifdef CONFIG_NET_RX_BUSY_POLL @@ -736,7 +772,7 @@ static void ep_start_scan(struct eventpoll *ep, struct list_head *txlist) lockdep_assert_irqs_enabled(); spin_lock_irq(&ep->lock); list_splice_init(&ep->rdllist, txlist); - WRITE_ONCE(ep->ovflist, NULL); + ep_enter_scan(ep); spin_unlock_irq(&ep->lock); } @@ -751,29 +787,24 @@ static void ep_done_scan(struct eventpoll *ep, * other events might have been queued by the poll callback. * We re-insert them inside the main ready-list here. */ - for (nepi = READ_ONCE(ep->ovflist); (epi = nepi) != NULL; - nepi = epi->next, epi->next = EP_UNACTIVE_PTR) { + for (nepi = READ_ONCE(ep->ovflist); (epi = nepi) != NULL; ) { + nepi = epi->next; + epi_clear_ovflist(epi); /* - * We need to check if the item is already in the list. - * During the "sproc" callback execution time, items are - * queued into ->ovflist but the "txlist" might already - * contain them, and the list_splice() below takes care of them. + * Skip items that the caller already returned via @txlist + * -- the list_splice() below takes care of those. */ if (!ep_is_linked(epi)) { /* - * ->ovflist is LIFO, so we have to reverse it in order - * to keep in FIFO. + * ovflist is LIFO; list_add() head-insert here + * reverses the iteration order into FIFO. */ list_add(&epi->rdllink, &ep->rdllist); ep_pm_stay_awake(epi); } } - /* - * We need to set back ep->ovflist to EP_UNACTIVE_PTR, so that after - * releasing the lock, events will be queued in the normal way inside - * ep->rdllist. - */ - WRITE_ONCE(ep->ovflist, EP_UNACTIVE_PTR); + /* Back out of scan mode; callbacks target ep->rdllist again. */ + ep_exit_scan(ep); /* * Quickly re-inject items left on "txlist". @@ -1159,7 +1190,7 @@ static int ep_alloc(struct eventpoll **pep) init_waitqueue_head(&ep->poll_wait); INIT_LIST_HEAD(&ep->rdllist); ep->rbr = RB_ROOT_CACHED; - ep->ovflist = EP_UNACTIVE_PTR; + ep->ovflist = EP_UNACTIVE_PTR; /* not scanning */ ep->user = get_current_user(); refcount_set(&ep->refcount, 1); @@ -1283,8 +1314,8 @@ static int ep_poll_callback(wait_queue_entry_t *wait, unsigned mode, int sync, v * semantics). All the events that happen during that period of time are * chained in ep->ovflist and requeued later on. */ - if (READ_ONCE(ep->ovflist) != EP_UNACTIVE_PTR) { - if (epi->next == EP_UNACTIVE_PTR) { + if (ep_is_scanning(ep)) { + if (!epi_on_ovflist(epi)) { epi->next = READ_ONCE(ep->ovflist); WRITE_ONCE(ep->ovflist, epi); ep_pm_stay_awake_rcu(epi); @@ -1591,7 +1622,7 @@ static struct epitem *ep_alloc_epitem(struct eventpoll *ep, epi->ep = ep; ep_set_ffd(&epi->ffd, tfile, fd); epi->event = *event; - epi->next = EP_UNACTIVE_PTR; + epi_clear_ovflist(epi); return epi; } From 7bab94272fa4374459aaa219de5b8b4beecab2cb Mon Sep 17 00:00:00 2001 From: Sandipan Das Date: Mon, 1 Jun 2026 20:28:46 +0530 Subject: [PATCH 1138/2758] perf/x86/amd/core: Always use the NMI latency mitigation [ Upstream commit 73a4c02f94a98d94480c3e5c81450215a4da05ba ] Commit df4d29732fda ("perf/x86/amd: Change/fix NMI latency mitigation to use a timestamp") fixed handling of late-arriving NMIs but limited the mitigation to processors having X86_FEATURE_PERFCTR_CORE. However, it is unclear if processors without this feature are also affected. When Mediated vPMU is enabled on affected hardware, it is also possible to bypass the fix inside KVM guests if X86_FEATURE_PERFCTR_CORE is removed from the guest CPUID (e.g. using "-cpu host,-perfctr-core" with QEMU). Hence, use the mitigation at all times. Fixes: df4d29732fda ("perf/x86/amd: Change/fix NMI latency mitigation to use a timestamp") Signed-off-by: Sandipan Das Signed-off-by: Peter Zijlstra (Intel) Link: https://patch.msgid.link/29a3c970da289ab8f24282933bdb36545c0403e8.1780325517.git.sandipan.das@amd.com Signed-off-by: Sasha Levin --- arch/x86/events/amd/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/x86/events/amd/core.c b/arch/x86/events/amd/core.c index 0c92ed5f464b1..abc84a92cd590 100644 --- a/arch/x86/events/amd/core.c +++ b/arch/x86/events/amd/core.c @@ -1412,12 +1412,12 @@ static int __init amd_core_pmu_init(void) u64 even_ctr_mask = 0ULL; int i; - if (!boot_cpu_has(X86_FEATURE_PERFCTR_CORE)) - return 0; - /* Avoid calculating the value each time in the NMI handler */ perf_nmi_window = msecs_to_jiffies(100); + if (!boot_cpu_has(X86_FEATURE_PERFCTR_CORE)) + return 0; + /* * If core performance counter extensions exists, we must use * MSR_F15H_PERF_CTL/MSR_F15H_PERF_CTR msrs. See also From a2bc4e61b011df2c9e0c8f1fa5277f3584ec3f60 Mon Sep 17 00:00:00 2001 From: Zide Chen Date: Tue, 2 Jun 2026 07:49:02 -0700 Subject: [PATCH 1139/2758] perf/x86/intel/uncore: Fix discovery unit lookup for multi-die systems [ Upstream commit 63f48abd55d0417996bca86022925c853a2b436b ] In uncore_find_add_unit(), PMON units with the same unit ID may be added to the uncore discovery RB-tree for different dies. These units are distinguished by node->die. However, intel_generic_uncore_box_ctl() uses a fixed die ID of -1 when looking up the discovery unit, which may retrieve the wrong node on multi-die systems. Use box->dieid instead so the correct discovery unit is selected. No functional issue has been observed so far because currently supported platforms happen to use the same unit control register for such units. Remove WARN_ON_ONCE() because with the above change a NULL unit can be expected, e.g. when a CPU die is offline during uncore enumeration and the unit is not added to the RB-tree. In this case, intel_uncore_find_discovery_unit() returns NULL once the die becomes online, and it is expected that the PMU box is not functional for that die. Fixes: b1d9ea2e1ca4 ("perf/x86/uncore: Apply the unit control RB tree to MSR uncore units") Signed-off-by: Zide Chen Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Dapeng Mi Link: https://patch.msgid.link/20260602144908.263680-2-zide.chen@intel.com Signed-off-by: Sasha Levin --- arch/x86/events/intel/uncore_discovery.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/events/intel/uncore_discovery.c b/arch/x86/events/intel/uncore_discovery.c index 583cbd06b9b88..60e1200c4691d 100644 --- a/arch/x86/events/intel/uncore_discovery.c +++ b/arch/x86/events/intel/uncore_discovery.c @@ -481,8 +481,8 @@ static u64 intel_generic_uncore_box_ctl(struct intel_uncore_box *box) struct intel_uncore_discovery_unit *unit; unit = intel_uncore_find_discovery_unit(box->pmu->type->boxes, - -1, box->pmu->pmu_idx); - if (WARN_ON_ONCE(!unit)) + box->dieid, box->pmu->pmu_idx); + if (!unit) return 0; return unit->addr; From 734a4b17e655d1705eb818da6df180a8fdf29db2 Mon Sep 17 00:00:00 2001 From: Zide Chen Date: Tue, 2 Jun 2026 07:49:04 -0700 Subject: [PATCH 1140/2758] perf/x86/intel/uncore: Fix PCI device refcount leak in UPI discovery [ Upstream commit 81ec618f59415bdcf3e8989e2691c1f240be27fb ] pci_get_domain_bus_and_slot() increments the reference count of the returned PCI device and therefore requires a matching pci_dev_put(). In skx_upi_topology_cb() and discover_upi_topology(), the lookup is performed inside a loop, but pci_dev_put() is only called once after the loop. As a result, references from all previous iterations are leaked. Move pci_dev_put(dev) into the if (dev) block immediately after upi_fill_topology() returns. Opportunistically, fix uninitialized variable in skx_upi_topology_cb(). Fixes: 4cfce57fa42d ("perf/x86/intel/uncore: Enable UPI topology discovery for Skylake Server") Fixes: f680b6e6062e ("perf/x86/intel/uncore: Enable UPI topology discovery for Icelake Server") Signed-off-by: Zide Chen Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Dapeng Mi Link: https://patch.msgid.link/20260602144908.263680-4-zide.chen@intel.com Signed-off-by: Sasha Levin --- arch/x86/events/intel/uncore_snbep.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/x86/events/intel/uncore_snbep.c b/arch/x86/events/intel/uncore_snbep.c index 215d33e260ede..c9ce206fcbb6e 100644 --- a/arch/x86/events/intel/uncore_snbep.c +++ b/arch/x86/events/intel/uncore_snbep.c @@ -4261,7 +4261,7 @@ static int upi_fill_topology(struct pci_dev *dev, struct intel_uncore_topology * static int skx_upi_topology_cb(struct intel_uncore_type *type, int segment, int die, u64 cpu_bus_msr) { - int idx, ret; + int idx, ret = 0; struct intel_uncore_topology *upi; unsigned int devfn; struct pci_dev *dev = NULL; @@ -4274,12 +4274,12 @@ static int skx_upi_topology_cb(struct intel_uncore_type *type, int segment, dev = pci_get_domain_bus_and_slot(segment, bus, devfn); if (dev) { ret = upi_fill_topology(dev, upi, idx); + pci_dev_put(dev); if (ret) break; } } - pci_dev_put(dev); return ret; } @@ -5499,6 +5499,7 @@ static int discover_upi_topology(struct intel_uncore_type *type, int ubox_did, i devfn); if (dev) { ret = upi_fill_topology(dev, upi, idx); + pci_dev_put(dev); if (ret) goto err; } @@ -5506,7 +5507,6 @@ static int discover_upi_topology(struct intel_uncore_type *type, int ubox_did, i } err: pci_dev_put(ubox); - pci_dev_put(dev); return ret; } From c18ad83ae58e6422e3927b2405d47d1fe65b24bb Mon Sep 17 00:00:00 2001 From: Sandipan Das Date: Mon, 1 Jun 2026 17:43:05 +0530 Subject: [PATCH 1141/2758] perf/x86/amd/uncore: Use Node ID to identify DF and UMC domains [ Upstream commit 67d27727854def4a7e2b386429941f5c4741ccc4 ] For DF and UMC PMUs, a single context is shared across all CPUs that are connected to the same Data Fabric (DF) instance. Currently, the Package ID, which also happens to be the Socket ID, is used to identify DF instances. This approach works for configurations having a single IO Die (IOD) but fails in the following cases. * Older Zen 1 processors, where each chiplet has its own DF instance. * Any configurations with multiple DF instances or multiple IODs in the same package. The correct way to identify DF instances is through the Node ID (not to be confused with NUMA Node ID). This is available in ECX[7:0] of CPUID leaf 0x8000001e and returned via topology_amd_node_id(). Hence, replace usage of topology_logical_package_id() with topology_amd_node_id(). Fixes: 07888daa056e ("perf/x86/amd/uncore: Move discovery and registration") Signed-off-by: Sandipan Das Signed-off-by: Peter Zijlstra (Intel) Link: https://patch.msgid.link/e7a71a727c6a7b118c23d3e469929c538c4665aa.1780315832.git.sandipan.das@amd.com Signed-off-by: Sasha Levin --- arch/x86/events/amd/uncore.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/x86/events/amd/uncore.c b/arch/x86/events/amd/uncore.c index dd956cfcadefa..a0364ca2f9173 100644 --- a/arch/x86/events/amd/uncore.c +++ b/arch/x86/events/amd/uncore.c @@ -700,7 +700,7 @@ void amd_uncore_df_ctx_scan(struct amd_uncore *uncore, unsigned int cpu) info.split.aux_data = 0; info.split.num_pmcs = NUM_COUNTERS_NB; info.split.gid = 0; - info.split.cid = topology_logical_package_id(cpu); + info.split.cid = topology_amd_node_id(cpu); if (pmu_version >= 2) { ebx.full = cpuid_ebx(EXT_PERFMON_DEBUG_FEATURES); @@ -999,8 +999,8 @@ void amd_uncore_umc_ctx_scan(struct amd_uncore *uncore, unsigned int cpu) cpuid(EXT_PERFMON_DEBUG_FEATURES, &eax, &ebx.full, &ecx, &edx); info.split.aux_data = ecx; /* stash active mask */ info.split.num_pmcs = ebx.split.num_umc_pmc; - info.split.gid = topology_logical_package_id(cpu); - info.split.cid = topology_logical_package_id(cpu); + info.split.gid = topology_amd_node_id(cpu); + info.split.cid = topology_amd_node_id(cpu); *per_cpu_ptr(uncore->info, cpu) = info; } From 28c812b62b405e9bb78ae410a3692eb352b90b7f Mon Sep 17 00:00:00 2001 From: Antony Antony Date: Tue, 26 May 2026 21:07:01 +0200 Subject: [PATCH 1142/2758] xfrm: fix NAT-related field inheritance in SA migration [ Upstream commit 364e165e0b63e8142e76de83e96ae8e36c3b955a ] During SA migration via xfrm_state_clone_and_setup(), nat_keepalive_interval was silently dropped and never copied to the new SA. mapping_maxage was unconditionally copied even when migrating to a non-encapsulated SA. Both fields are only meaningful when UDP encapsulation (NAT-T) is in use. Move mapping_maxage and add nat_keepalive_interval inside the existing if (encap) block, so both are inherited when migrating with encapsulation and correctly absent when migrating without it. Fixes: f531d13bdfe3 ("xfrm: support sending NAT keepalives in ESP in UDP states") Reviewed-by: Sabrina Dubroca Signed-off-by: Antony Antony Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin --- net/xfrm/xfrm_state.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 589c3b6e46791..4c7eed689cd44 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -2020,6 +2020,8 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig, if (!x->encap) goto error; + x->mapping_maxage = orig->mapping_maxage; + x->nat_keepalive_interval = orig->nat_keepalive_interval; } if (orig->security) @@ -2054,7 +2056,6 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig, x->km.seq = orig->km.seq; x->replay = orig->replay; x->preplay = orig->preplay; - x->mapping_maxage = orig->mapping_maxage; x->lastused = orig->lastused; x->new_mapping = 0; x->new_mapping_sport = 0; From ff9748200698e9641f41ba922ffef728167807c2 Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Thu, 4 Jun 2026 12:33:16 +0200 Subject: [PATCH 1143/2758] wifi: wlcore: enable the right set of ciphers [ Upstream commit 7495adaa0e45e180f4b6b7436675c6266edff1ff ] The firmware version number check for IGTK introduced in commit c34dbc5900b0 ("wifi: wlcore: Add support for IGTK key") lets the amount of ciphers decrease on every boot of a too old firmware and that is practically happening. It also does not take into account other chips than the wl18xx. On some wl128x, the following can be observed when connecting via nm to a common ap: [ 484.113311] wlcore: WARNING could not set keys [ 484.117828] wlcore: ERROR Could not add or replace key [ 484.123016] wlan0: failed to set key (5, ff:ff:ff:ff:ff:ff) to hardware (-5) [ 484.123046] wlcore: Hardware recovery in progress. FW ver: Rev 7.3.10.0.142 [ 484.139923] wlcore: pc: 0x0, hint_sts: 0x00000048 count: 1 [ 484.145721] wlcore: down [ 484.148986] ieee80211 phy0: Hardware restart was requested [ 484.610473] wlcore: firmware booted (Rev 7.3.10.0.142) [ 484.633758] wlcore: Association completed. [ 484.690490] wlcore: ERROR command execute failure 14 [ 484.690490] ------------[ cut here ]------------ [ 484.700195] WARNING: drivers/net/wireless/ti/wlcore/main.c:872 at wl12xx_queue_recovery_work+0x64/0x74 [wlcore], CPU#0: kworker/0:0/892 This repeats endlessly. Always disable IGTK on wl12xx and fix the decrementing mess. Fixes: c34dbc5900b0 ("wifi: wlcore: Add support for IGTK key") Signed-off-by: Andreas Kemnade Link: https://patch.msgid.link/20260604103316.377251-1-andreas@kemnade.info Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- drivers/net/wireless/ti/wlcore/main.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 1c340a4a09308..be583ae331c03 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -32,6 +32,15 @@ #define WL1271_BOOT_RETRIES 3 #define WL1271_WAKEUP_TIMEOUT 500 +static const u32 cipher_suites[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, + WL1271_CIPHER_SUITE_GEM, + WLAN_CIPHER_SUITE_AES_CMAC, +}; + static char *fwlog_param; static int fwlog_mem_blocks = -1; static int bug_on_recovery = -1; @@ -2367,6 +2376,7 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif) static int wl12xx_init_fw(struct wl1271 *wl) { + struct wlcore_platdev_data *pdev_data = dev_get_platdata(&wl->pdev->dev); int retries = WL1271_BOOT_RETRIES; bool booted = false; struct wiphy *wiphy = wl->hw->wiphy; @@ -2421,8 +2431,9 @@ static int wl12xx_init_fw(struct wl1271 *wl) /* WLAN_CIPHER_SUITE_AES_CMAC must be last in cipher_suites; support only with firmware 8.9.1 and newer */ - if (wl->chip.fw_ver[FW_VER_MAJOR] < 1) - wl->hw->wiphy->n_cipher_suites--; + if (wl->chip.fw_ver[FW_VER_MAJOR] < 1 || + (!strncmp(pdev_data->family->name, "wl12", 4))) + wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites) - 1; /* * Now we know if 11a is supported (info from the NVS), so disable @@ -6198,14 +6209,6 @@ static void wl1271_unregister_hw(struct wl1271 *wl) static int wl1271_init_ieee80211(struct wl1271 *wl) { int i; - static const u32 cipher_suites[] = { - WLAN_CIPHER_SUITE_WEP40, - WLAN_CIPHER_SUITE_WEP104, - WLAN_CIPHER_SUITE_TKIP, - WLAN_CIPHER_SUITE_CCMP, - WL1271_CIPHER_SUITE_GEM, - WLAN_CIPHER_SUITE_AES_CMAC, - }; /* The tx descriptor buffer */ wl->hw->extra_tx_headroom = sizeof(struct wl1271_tx_hw_descr); From 18c67ecc5dafe14055edcea53f36f4e31df1816b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 19 May 2026 15:12:03 -0700 Subject: [PATCH 1144/2758] cxl/fwctl: Fix __fortify_panic [ Upstream commit 6c9d2e87df40d606f1c85143e9acb1ecff463d5e ] Fix a runtime assertion in cxlctl_get_supported_features(). Fortify complains that it is potentially overflowing the entries array per __counted_by_le(num_entries). Quiet the false positive by initializing @num_entries earlier. memcpy: detected buffer overflow: 48 byte write of buffer size 0 WARNING: lib/string_helpers.c:1036 at __fortify_report+0x4d/0xa0, CPU#7: fwctl/1398 RIP: 0010:__fortify_report+0x50/0xa0 Call Trace: __fortify_panic+0xd/0xf cxlctl_get_supported_features.cold+0x23/0x35 [cxl_core] Fixes: 4d1c09cef2c2 ("cxl: Add support for fwctl RPC command to enable CXL feature commands") Signed-off-by: Dan Williams Reviewed-by: Alison Schofield Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260519221204.1517773-2-djbw@kernel.org Signed-off-by: Dave Jiang Signed-off-by: Sasha Levin --- drivers/cxl/core/features.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cxl/core/features.c b/drivers/cxl/core/features.c index 3435db9ea6b11..85185af46b72d 100644 --- a/drivers/cxl/core/features.c +++ b/drivers/cxl/core/features.c @@ -423,6 +423,7 @@ static void *cxlctl_get_supported_features(struct cxl_features_state *cxlfs, rpc_out->size = struct_size(feat_out, ents, requested); feat_out = &rpc_out->get_sup_feats_out; + feat_out->num_entries = cpu_to_le16(requested); for (i = start, pos = &feat_out->ents[0]; i < cxlfs->entries->num_features; i++, pos++) { @@ -444,7 +445,6 @@ static void *cxlctl_get_supported_features(struct cxl_features_state *cxlfs, } } - feat_out->num_entries = cpu_to_le16(requested); feat_out->supported_feats = cpu_to_le16(cxlfs->entries->num_features); rpc_out->retval = CXL_MBOX_CMD_RC_SUCCESS; *out_len = out_size; From 8c4f27a7f364eab514d2fa55c11f39d505122f1b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 19 May 2026 15:12:04 -0700 Subject: [PATCH 1145/2758] cxl/test: Fix __fortify_panic [ Upstream commit 08326b92c7a414a73b5b308d1daf0e91e0134dfc ] Fix a runtime assertion in setup_xor_mapping(). Fortify complains that it is potentially overflowing the xormaps array per __counted_by(nr_maps). Quiet the false positive by initializing @nr_maps earlier. memcpy: detected buffer overflow: 32 byte write of buffer size 0 WARNING: lib/string_helpers.c:1036 at __fortify_report+0x4d/0xa0, CPU#8: modprobe/2728 Call Trace: __fortify_panic+0xd/0xf setup_xor_mapping+0x6c/0xa0 [cxl_translate] [ dj: Fixed up @nr_entries to @nr_maps in commit log. ] Fixes: 06377c54a133 ("cxl/test: Add cxl_translate module for address translation testing") Signed-off-by: Dan Williams Reviewed-by: Alison Schofield Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260519221204.1517773-3-djbw@kernel.org Signed-off-by: Dave Jiang Signed-off-by: Sasha Levin --- tools/testing/cxl/test/cxl_translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/cxl/test/cxl_translate.c b/tools/testing/cxl/test/cxl_translate.c index 16328b2112b23..25a27e01ac21b 100644 --- a/tools/testing/cxl/test/cxl_translate.c +++ b/tools/testing/cxl/test/cxl_translate.c @@ -236,8 +236,8 @@ static int setup_xor_mapping(void) if (!cximsd) return -ENOMEM; - memcpy(cximsd->xormaps, xormaps, nr_maps * sizeof(*cximsd->xormaps)); cximsd->nr_maps = nr_maps; + memcpy(cximsd->xormaps, xormaps, nr_maps * sizeof(*cximsd->xormaps)); return 0; } From 36b1d997866f6083d33934983aa2ce0a184ed642 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Thu, 28 May 2026 15:20:14 -0700 Subject: [PATCH 1146/2758] bpf: Take mmap_lock in zap_pages() [ Upstream commit 80b89d0226a05e8b67969de99c31b51fcd54f76a ] zap_vma_range() requires the owning mm's mmap_lock to be held. Taking mmap_read_lock under arena->lock would AB-BA against arena_vm_close() and arena_map_mmap(), both of which run with mmap_write_lock held and then acquire arena->lock. Instead drop arena->lock, mmget_not_zero() the vma's mm, take mmap_read_lock, and re-resolve the vma via find_vma() since it may have been unmapped or replaced while waiting. Track processed vmls with a per-call generation in vml->zap_gen and serialize zap_pages() callers with a new arena->zap_mutex so concurrent callers on different uaddr ranges do not mark each other's vmls processed before the zap is done. Reported-by: David Hildenbrand Fixes: 317460317a02 ("bpf: Introduce bpf_arena.") Signed-off-by: Alexei Starovoitov Reviewed-by: Emil Tsalapatis Link: https://lore.kernel.org/r/20260528222014.38980-1-alexei.starovoitov@gmail.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- kernel/bpf/arena.c | 61 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/kernel/bpf/arena.c b/kernel/bpf/arena.c index 49a8f7b1beef5..40c42e28a6168 100644 --- a/kernel/bpf/arena.c +++ b/kernel/bpf/arena.c @@ -59,6 +59,8 @@ struct bpf_arena { struct list_head vma_list; /* protects vma_list */ struct mutex lock; + u64 zap_gen; + struct mutex zap_mutex; struct irq_work free_irq; struct work_struct free_work; struct llist_head free_spans; @@ -228,6 +230,7 @@ static struct bpf_map *arena_map_alloc(union bpf_attr *attr) goto err; } mutex_init(&arena->lock); + mutex_init(&arena->zap_mutex); raw_res_spin_lock_init(&arena->spinlock); err = populate_pgtable_except_pte(arena); if (err) { @@ -318,6 +321,7 @@ struct vma_list { struct vm_area_struct *vma; struct list_head head; refcount_t mmap_count; + u64 zap_gen; }; static int remember_vma(struct bpf_arena *arena, struct vm_area_struct *vma) @@ -330,6 +334,7 @@ static int remember_vma(struct bpf_arena *arena, struct vm_area_struct *vma) refcount_set(&vml->mmap_count, 1); vma->vm_private_data = vml; vml->vma = vma; + vml->zap_gen = 0; list_add(&vml->head, &arena->vma_list); return 0; } @@ -668,12 +673,60 @@ static long arena_alloc_pages(struct bpf_arena *arena, long uaddr, long page_cnt */ static void zap_pages(struct bpf_arena *arena, long uaddr, long page_cnt) { + unsigned long size = (unsigned long)page_cnt << PAGE_SHIFT; + struct vm_area_struct *vma; + struct mm_struct *mm; struct vma_list *vml; + unsigned long vm_start; + u64 my_gen; - guard(mutex)(&arena->lock); - /* iterate link list under lock */ - list_for_each_entry(vml, &arena->vma_list, head) - zap_vma_range(vml->vma, uaddr, PAGE_SIZE * page_cnt); + /* + * Taking mmap_read_lock() under arena->lock would deadlock against + * arena_vm_close(), which runs with mmap_write_lock held and then + * acquires arena->lock. Drop arena->lock for mmap_read_lock(). + * + * Use per-call my_gen, recorded in vml->zap_gen, to remember which + * vmls this invocation has already processed across the lock drop. + * Hold zap_mutex around the whole walk so concurrent zap_pages() + * callers cannot overwrite each other's marks on shared vmls -- + * otherwise call B's mark would make call A skip a vml that A has + * not yet zapped for A's uaddr range. + */ + mutex_lock(&arena->zap_mutex); + mutex_lock(&arena->lock); + my_gen = ++arena->zap_gen; + for (;;) { + mm = NULL; + list_for_each_entry(vml, &arena->vma_list, head) { + if (vml->zap_gen >= my_gen) + continue; + vml->zap_gen = my_gen; + if (!mmget_not_zero(vml->vma->vm_mm)) + continue; + mm = vml->vma->vm_mm; + vm_start = vml->vma->vm_start; + break; + } + if (!mm) + break; + mutex_unlock(&arena->lock); + + mmap_read_lock(mm); + /* + * Re-resolve: while we waited the VMA could have been unmapped + * and a different mapping installed at the same address. + */ + vma = find_vma(mm, vm_start); + if (vma && vma->vm_start == vm_start && + vma->vm_file && vma->vm_file->private_data == &arena->map) + zap_vma_range(vma, uaddr, size); + mmap_read_unlock(mm); + mmput(mm); + + mutex_lock(&arena->lock); + } + mutex_unlock(&arena->lock); + mutex_unlock(&arena->zap_mutex); } static void arena_free_pages(struct bpf_arena *arena, long uaddr, long page_cnt, bool sleepable) From b5fc19c898e08deb40b671329d99d85d2d575f0b Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 6 May 2026 16:50:42 -0400 Subject: [PATCH 1147/2758] drm/amdkfd: always resume_all after suspend_all [ Upstream commit 56ae73c92e200e630c2bdf1e98c88b86c8483b37 ] Need to restore any good queues even if the suspend_all failed for some. Always run remove_queue as that will schedule a GPU reset is removing the queue fails. v2: move resume_all after remove Fixes: eb067d65c33e ("drm/amdkfd: Update BadOpcode Interrupt handling with MES") Reviewed-by: Amber Lin Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- .../drm/amd/amdkfd/kfd_device_queue_manager.c | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c index 31187ddbb79ea..ee413117b0ff6 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -3095,32 +3095,24 @@ int kfd_dqm_suspend_bad_queue_mes(struct kfd_node *knode, u32 pasid, u32 doorbel list_for_each_entry(q, &qpd->queues_list, list) { if (q->doorbell_id == doorbell_id && q->properties.is_active) { - ret = suspend_all_queues_mes(dqm); - if (ret) { - dev_err(dev, "Suspending all queues failed"); - goto out; - } + /* suspend all queues will save any good queues and mark the rest as bad */ + suspend_all_queues_mes(dqm); q->properties.is_evicted = true; q->properties.is_active = false; decrement_queue_count(dqm, qpd, q); + /* this will remove the bad queue and sched a GPU reset if needed */ ret = remove_queue_mes(dqm, q, qpd); - if (ret) { - dev_err(dev, "Removing bad queue failed"); - goto out; - } - - ret = resume_all_queues_mes(dqm); if (ret) - dev_err(dev, "Resuming all queues failed"); - + dev_err(dev, "Removing bad queue failed"); + /* resume the good queues */ + resume_all_queues_mes(dqm); break; } } } -out: dqm_unlock(dqm); kfd_unref_process(p); return ret; From cbd3102fe27bc87da244bf4c3ba670ea4698b0a8 Mon Sep 17 00:00:00 2001 From: Wandun Chen Date: Thu, 4 Jun 2026 09:53:32 +0800 Subject: [PATCH 1148/2758] of: reserved_mem: avoid post-init UAF when alloc_reserved_mem_array() fails [ Upstream commit e1686ca81dbf3edbde589b7daf312b45cbf76e03 ] The global pointer 'reserved_mem' continues to reference the reserved_mem_array which lives in __initdata if alloc_reserved_mem_array() fails. of_reserved_mem_lookup() is exported for post-init use, that would dereference freed memory and trigger a use-after-free. So reset reserved_mem_count to 0 when alloc_reserved_mem_array() fails. Fixes: 00c9a452a235 ("of: reserved_mem: Add code to dynamically allocate reserved_mem array") Signed-off-by: Wandun Chen Link: https://patch.msgid.link/20260604015332.3669384-1-chenwandun1@gmail.com Signed-off-by: Rob Herring (Arm) Signed-off-by: Sasha Levin --- drivers/of/of_reserved_mem.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index 8d5777cb5d1b3..deaea58c74f2a 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -69,29 +69,32 @@ static int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size, * the initial static array is copied over to this new array and * the new array is used from this point on. */ -static void __init alloc_reserved_mem_array(void) +static int __init alloc_reserved_mem_array(void) { struct reserved_mem *new_array; size_t alloc_size, copy_size, memset_size; + int ret; + + if (!total_reserved_mem_cnt) + return 0; alloc_size = array_size(total_reserved_mem_cnt, sizeof(*new_array)); if (alloc_size == SIZE_MAX) { - pr_err("Failed to allocate memory for reserved_mem array with err: %d", -EOVERFLOW); - return; + ret = -EOVERFLOW; + goto fail; } new_array = memblock_alloc(alloc_size, SMP_CACHE_BYTES); if (!new_array) { - pr_err("Failed to allocate memory for reserved_mem array with err: %d", -ENOMEM); - return; + ret = -ENOMEM; + goto fail; } copy_size = array_size(reserved_mem_count, sizeof(*new_array)); if (copy_size == SIZE_MAX) { memblock_free(new_array, alloc_size); - total_reserved_mem_cnt = MAX_RESERVED_REGIONS; - pr_err("Failed to allocate memory for reserved_mem array with err: %d", -EOVERFLOW); - return; + ret = -EOVERFLOW; + goto fail; } memset_size = alloc_size - copy_size; @@ -100,6 +103,12 @@ static void __init alloc_reserved_mem_array(void) memset(new_array + reserved_mem_count, 0, memset_size); reserved_mem = new_array; + return 0; + +fail: + pr_err("Failed to allocate memory for reserved_mem array with err: %d", ret); + reserved_mem_count = 0; + return ret; } static void fdt_init_reserved_mem_node(unsigned long node, const char *uname, @@ -266,7 +275,8 @@ void __init fdt_scan_reserved_mem_late(void) } /* Attempt dynamic allocation of a new reserved_mem array */ - alloc_reserved_mem_array(); + if (alloc_reserved_mem_array()) + return; if (__reserved_mem_check_root(node)) { pr_err("Reserved memory: unsupported node format, ignoring\n"); From 610a0d2a35496738e1472fb0f318d5008a1c634f Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Mon, 25 May 2026 12:17:26 +0800 Subject: [PATCH 1149/2758] ocfs2: rebase copied fsdlm LVB pointers in locking_state [ Upstream commit 93612d48fa42b3d1a637eb9279e15281c611c000 ] The locking_state debugfs iterator snapshots struct ocfs2_lock_res by value under ocfs2_dlm_tracking_lock and later formats that copy in ocfs2_dlm_seq_show(). That is fine for the inline fields, but the userspace fsdlm stack stores the LVB through lksb_fsdlm.sb_lvbptr. Once the iterator drops the tracking lock, a copied non-NULL sb_lvbptr still points into the original lockres owner, so teardown can free that container before the debugfs dump walks the raw LVB bytes. Rebase the copied sb_lvbptr to the copied l_lksb before dumping the raw LVB. The seq snapshot already carries the inline LVB storage reserved in struct ocfs2_dlm_lksb, so the debugfs reader can dump the copied bytes without borrowing the original lockres lifetime. The buggy scenario involves two paths, with each column showing the order within that path: locking_state reader: lockres teardown: 1. ocfs2_dlm_seq_start()/next() 1. file release or another owner copies struct ocfs2_lock_res teardown reaches 2. ocfs2_dlm_seq_show() formats ocfs2_lock_res_free() the copied row 2. the lockres is removed from the 3. ocfs2_dlm_lvb() follows the tracking list copied sb_lvbptr 3. the owner frees the original lockres container Validation reproduced this kernel report: KASAN slab-use-after-free in ocfs2_dlm_seq_show+0x1bd/0x430 RIP: 0033:0x7f8ec4b1e29d The buggy address belongs to the object at ffff88810a1e0800 which belongs to the cache kmalloc-1k of size 1024 The buggy address is located 368 bytes inside of freed 1024-byte region [ffff88810a1e0800, ffff88810a1e0c00) Read of size 1 Call trace: dump_stack_lvl+0x66/0xa0 print_report+0xce/0x630 ocfs2_dlm_seq_show+0x1bd/0x430 (fs/ocfs2/dlmglue.c:3137) srso_alias_return_thunk+0x5/0xfbef5 __virt_addr_valid+0x19f/0x330 kasan_report+0xe0/0x110 seq_read_iter+0x29d/0x790 seq_read+0x20a/0x280 find_held_lock+0x2b/0x80 rcu_read_unlock+0x18/0x70 full_proxy_read+0x9e/0xd0 vfs_read+0x12c/0x590 ksys_read+0xd2/0x170 do_user_addr_fault+0x65a/0x890 do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f Allocated by task stack: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0xaa/0xb0 ocfs2_file_open+0x13e/0x300 do_dentry_open+0x233/0x7f0 vfs_open+0x5a/0x1b0 path_openat+0x66d/0x1540 do_file_open+0x186/0x2b0 do_sys_openat2+0xce/0x150 __x64_sys_openat+0xd0/0x140 do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task stack: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 kfree+0x313/0x590 ocfs2_file_release+0x138/0x260 __fput+0x1df/0x4b0 fput_close_sync+0xd2/0x170 __x64_sys_close+0x55/0x90 do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f Link: https://lore.kernel.org/20260525041726.4112882-1-rollkingzzc@gmail.com Fixes: cf4d8d75d8ab ("ocfs2: add fsdlm to stackglue") Assisted-by: Codex:gpt-5.5 Signed-off-by: Zhang Cen Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- fs/ocfs2/dlmglue.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c index 7283bb2c5a31b..a23dd8f86c895 100644 --- a/fs/ocfs2/dlmglue.c +++ b/fs/ocfs2/dlmglue.c @@ -3134,6 +3134,22 @@ static void *ocfs2_dlm_seq_next(struct seq_file *m, void *v, loff_t *pos) * - Add last pr/ex unlock times and first lock wait time in usecs */ #define OCFS2_DLM_DEBUG_STR_VERSION 4 + +/* + * The debug iterator snapshots lockres by value, so a userspace-stack LVB + * pointer copied from the original lockres must be rebased to the copied + * lksb before the dump walks the raw bytes. + */ +static void ocfs2_dlm_seq_rebase_lvb(struct ocfs2_lock_res *lockres) +{ + if (!ocfs2_stack_supports_plocks()) + return; + + if (lockres->l_lksb.lksb_fsdlm.sb_lvbptr) + lockres->l_lksb.lksb_fsdlm.sb_lvbptr = + (char *)&lockres->l_lksb + sizeof(struct dlm_lksb); +} + static int ocfs2_dlm_seq_show(struct seq_file *m, void *v) { int i; @@ -3191,6 +3207,7 @@ static int ocfs2_dlm_seq_show(struct seq_file *m, void *v) lockres->l_blocking); /* Dump the raw LVB */ + ocfs2_dlm_seq_rebase_lvb(lockres); lvb = ocfs2_dlm_lvb(&lockres->l_lksb); for(i = 0; i < DLM_LVB_LEN; i++) seq_printf(m, "0x%x\t", lvb[i]); From 8a3188f3e39bd3cd59deb4d765000705e6e8701a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Tue, 26 May 2026 18:43:40 +0200 Subject: [PATCH 1150/2758] lib: kunit_iov_iter: repeatedly call alloc_pages_bulk() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 9ac9a08e4ac4bc063e56e1aff266c5e8aa5c6c03 ] alloc_pages_bulk() is not guaranteed to return all requested pages in a single call. Call it repeatedly until all pages have been allocated or no more progress is being made. Link: https://lore.kernel.org/20260526-kunit_iov_iter-alloc_bulk-v2-1-24fbcd995c61@weissschuh.net Fixes: 2d71340ff1d4 ("iov_iter: Kunit tests for copying to/from an iterator") Signed-off-by: Thomas Weißschuh Cc: "Christian A. Ehrhardt" Reviewed-by: Andrew Morton Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- lib/tests/kunit_iov_iter.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/tests/kunit_iov_iter.c b/lib/tests/kunit_iov_iter.c index f02f7b7aa7960..1e6fce9cb255f 100644 --- a/lib/tests/kunit_iov_iter.c +++ b/lib/tests/kunit_iov_iter.c @@ -53,7 +53,7 @@ static void *__init iov_kunit_create_buffer(struct kunit *test, size_t npages) { struct page **pages; - unsigned long got; + unsigned long got, last; void *buffer; unsigned int i; @@ -61,7 +61,15 @@ static void *__init iov_kunit_create_buffer(struct kunit *test, KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pages); *ppages = pages; - got = alloc_pages_bulk(GFP_KERNEL, npages, pages); + got = 0; + while (true) { + last = got; + got = alloc_pages_bulk(GFP_KERNEL, npages, pages); + + if (last == got || got == npages) + break; + } + if (got != npages) { release_pages(pages, got); kvfree(pages); From 9e7a057934cdd58e4cc94350bcfe5367bbee0f8e Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Fri, 29 May 2026 12:41:28 +0300 Subject: [PATCH 1151/2758] ocfs2: fix buffer head management in ocfs2_read_blocks() [ Upstream commit 6371a07148ee979af22a9d6f4c277462953a9a4a ] In ocfs2_read_blocks(), caller should't assume that buffer head returned by 'sb_getblk()' is exclusively owned and so 'put_bh()' always drops b_count from 1 to 0. If it is not so, buffer head remains on hold and likely to be returned by the next call to 'sb_getblk()' unchanged - that is, with BH_Uptodate bit set even if it has failed validation previously, thus allowing to insert that buffer head into OCFS2 metadata cache and submit it to upper layers. To avoid such a scenario, BH_Uptodate should be cleared immediately after 'validate()' callback has detected some data inconsistency. Link: https://lore.kernel.org/20260529094128.494293-1-dmantipov@yandex.ru Fixes: cf76c78595ca ("ocfs2: don't put and assigning null to bh allocated outside") Signed-off-by: Dmitry Antipov Reported-by: syzbot+caacd220635a9cc3bac9@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=caacd220635a9cc3bac9 Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- fs/ocfs2/buffer_head_io.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/ocfs2/buffer_head_io.c b/fs/ocfs2/buffer_head_io.c index 701d27d908d4b..6114299b121e5 100644 --- a/fs/ocfs2/buffer_head_io.c +++ b/fs/ocfs2/buffer_head_io.c @@ -350,8 +350,6 @@ int ocfs2_read_blocks(struct ocfs2_caching_info *ci, u64 block, int nr, wait_on_buffer(bh); put_bh(bh); bhs[i] = NULL; - } else if (bh && buffer_uptodate(bh)) { - clear_buffer_uptodate(bh); } continue; } @@ -380,8 +378,11 @@ int ocfs2_read_blocks(struct ocfs2_caching_info *ci, u64 block, int nr, BUG_ON(buffer_jbd(bh)); clear_buffer_needs_validate(bh); status = validate(sb, bh); - if (status) + if (status) { + if (buffer_uptodate(bh)) + clear_buffer_uptodate(bh); goto read_failure; + } } } From f9e2cb692b77a679b1f4cc2b7b277fa908586533 Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Thu, 28 May 2026 23:12:30 +0800 Subject: [PATCH 1152/2758] ocfs2: validate fast symlink target during inode read [ Upstream commit e234973f286ed2e8961a24561ec91594ec3e3ff8 ] ocfs2_validate_inode_block() already rejects several inconsistent self-contained dinodes before they are exposed to the rest of the filesystem. Fast symlinks need the same treatment. A zero-cluster symlink is treated as a fast symlink and later read through page_get_link() and ocfs2_fast_symlink_read_folio(). That path uses strnlen() on the inline payload and then copies len + 1 bytes into the folio. If a corrupt dinode stores an i_size that does not fit the inline area or omits the terminating NUL at i_size, that copy reads past the end of the inode block buffer. Reject zero-cluster symlink dinodes whose i_size exceeds the inline fast-symlink capacity or whose inline payload is not NUL-terminated exactly at i_size when the inode block is validated. This keeps malformed fast symlinks from reaching the read path. Validation reproduced this kernel report: KASAN use-after-free in ocfs2_fast_symlink_read_folio+0x12c/0x1f0 RIP: 0033:0x7f5c6d859aa7 Read of size 3905 Call trace: dump_stack_lvl+0x66/0xa0 (?:?) print_report+0xce/0x630 (?:?) ocfs2_fast_symlink_read_folio+0x12c/0x1f0 (fs/ocfs2/inode.c:?) srso_alias_return_thunk+0x5/0xfbef5 (?:?) __virt_addr_valid+0x19f/0x330 (?:?) kasan_report+0xe0/0x110 (?:?) kasan_check_range+0x105/0x1b0 (?:?) __asan_memcpy+0x23/0x60 (?:?) filemap_read_folio+0x27/0xe0 (?:?) filemap_read_folio+0x35/0xe0 (?:?) do_read_cache_folio+0x138/0x230 (?:?) __page_get_link+0x26/0x110 (?:?) page_get_link+0x2e/0x70 (?:?) vfs_readlink+0x15e/0x250 (?:?) touch_atime+0x4d/0x370 (?:?) do_readlinkat+0x186/0x200 (?:?) do_user_addr_fault+0x65a/0x890 (?:?) __x64_sys_readlink+0x46/0x60 (?:?) do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f (?:?) Link: https://lore.kernel.org/20260528151230.361127-1-rollkingzzc@gmail.com Fixes: ea022dfb3c2a ("ocfs: simplify symlink handling") Assisted-by: Codex:gpt-5.5 Signed-off-by: Zhang Cen Reviewed-by: Joseph Qi Cc: Gui-Dong Han <2045gemini@gmail.com> Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- fs/ocfs2/inode.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index a510a0eb1adcc..994d6e46472f5 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -1525,6 +1525,29 @@ int ocfs2_validate_inode_block(struct super_block *sb, } } + if (S_ISLNK(le16_to_cpu(di->i_mode)) && + !le32_to_cpu(di->i_clusters)) { + int max_inline = ocfs2_fast_symlink_chars(sb); + u64 i_size = le64_to_cpu(di->i_size); + + if (i_size >= max_inline) { + rc = ocfs2_error(sb, + "Invalid dinode #%llu: fast symlink i_size %llu exceeds max %d\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)i_size, + max_inline - 1); + goto bail; + } + + if (strnlen((char *)di->id2.i_symlink, i_size + 1) != i_size) { + rc = ocfs2_error(sb, + "Invalid dinode #%llu: fast symlink is not NUL-terminated at i_size %llu\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)i_size); + goto bail; + } + } + if (le32_to_cpu(di->i_flags) & OCFS2_CHAIN_FL) { struct ocfs2_chain_list *cl = &di->id2.i_chain; u16 bpc = 1 << (OCFS2_SB(sb)->s_clustersize_bits - From 2c13e02592b918be7725ab5965e01ef4e46c4b57 Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Thu, 28 May 2026 23:12:47 +0800 Subject: [PATCH 1153/2758] ocfs2: reject FITRIM ranges shorter than a cluster [ Upstream commit ca1afd88f5eaaff9168e1466e5401385edf59543 ] ocfs2_trim_mainbm() trims the global bitmap in cluster units, but its too-short range validation only checks sb->s_blocksize. On filesystems with a cluster size larger than the block size, a FITRIM range that is at least one block but shorter than one cluster is accepted and shifted down to len == 0. The later start + len - 1 and len -= ... arithmetic then underflows and can drive trimming past the requested range. Reject ranges shorter than s_clustersize instead. That preserves the existing -EINVAL behavior for requests that cannot discard even one allocation unit and keeps zero-cluster trims out of the group walk. Link: https://lore.kernel.org/20260528151247.361854-1-rollkingzzc@gmail.com Fixes: aa89762c5480 ("ocfs2: return EINVAL if the given range to discard is less than block size") Assisted-by: Codex:gpt-5.5 Signed-off-by: Zhang Cen Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- fs/ocfs2/alloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c index 6e5fd3f12a84c..be09e766ac1fc 100644 --- a/fs/ocfs2/alloc.c +++ b/fs/ocfs2/alloc.c @@ -7576,7 +7576,7 @@ int ocfs2_trim_mainbm(struct super_block *sb, struct fstrim_range *range) len = range->len >> osb->s_clustersize_bits; minlen = range->minlen >> osb->s_clustersize_bits; - if (minlen >= osb->bitmap_cpg || range->len < sb->s_blocksize) + if (minlen >= osb->bitmap_cpg || range->len < osb->s_clustersize) return -EINVAL; trace_ocfs2_trim_mainbm(start, len, minlen); From a09831214912a1f270663d935613910fafd8d883 Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Sun, 31 May 2026 12:47:14 +0800 Subject: [PATCH 1154/2758] ocfs2/dlm: require a ref for locking_state debugfs open [ Upstream commit 03ad858ce8064861ea580021976dc19b7aabb549 ] debug_lockres_open() copies inode->i_private into struct debug_lockres and debug_lockres_release() later drops that pointer with dlm_put(). That only works if open successfully pins the struct dlm_ctxt. Today open calls dlm_grab(dlm) but ignores its return value. Once the last domain unregister has removed the context from dlm_domains, dlm_grab() returns NULL, yet open still stores the raw pointer and returns success. The later release path is outside the debugfs removal barrier, so it can call dlm_put() after dlm_free_ctxt_mem() has freed the context. KASAN reports this as a slab-use-after-free in dlm_put() called from debug_lockres_release(). Fail the open when dlm_grab() cannot acquire the reference and unwind the seq_file private state before returning. That keeps locking_state from handing out a file descriptor whose release path does not own the dlm_ctxt. The buggy scenario involves two paths, with each column showing the order within that path: locking_state debugfs open: last domain unregister: 1. debug_lockres_open() reads 1. dlm_unregister_domain() calls inode->i_private. dlm_complete_dlm_shutdown(). 2. debug_lockres_open() calls 2. shutdown removes the dlm_ctxt from dlm_grab(dlm) and gets NULL. dlm_domains. 3. open still stores the raw dlm 3. final teardown reaches pointer in dl->dl_ctxt and dlm_free_ctxt_mem() and frees it. returns success. 4. debug_lockres_release() later calls dlm_put(dl->dl_ctxt). Validation reproduced this kernel report: KASAN slab-use-after-free in dlm_put+0x82/0x200 RIP: 0033:0x7f4d349bc9e0 The buggy address belongs to the object at ffff888103a3c000 which belongs to the cache kmalloc-2k of size 2048 The buggy address is located 816 bytes inside of freed 2048-byte region [ffff888103a3c000, ffff888103a3c800) Write of size 4 Call trace: dump_stack_lvl+0x66/0xa0 (?:?) print_report+0xd0/0x630 (?:?) dlm_put+0x82/0x200 (?:?) srso_alias_return_thunk+0x5/0xfbef5 (?:?) __virt_addr_valid+0x188/0x2f0 (?:?) kasan_report+0xe4/0x120 (?:?) kasan_check_range+0x105/0x1b0 (?:?) debug_lockres_release+0x53/0x80 (fs/ocfs2/dlm/dlmdebug.c:587) dlm_put+0x9/0x200 (?:?) debug_lockres_release+0x5c/0x80 (fs/ocfs2/dlm/dlmdebug.c:587) full_proxy_release+0x67/0x90 (?:?) __fput+0x1df/0x4b0 (?:?) do_raw_spin_lock+0x10f/0x1b0 (?:?) fput_close_sync+0xd2/0x170 (?:?) __x64_sys_close+0x55/0x90 (?:?) do_syscall_64+0x10c/0x640 (arch/x86/entry/syscall_64.c:87) irqentry_exit+0xac/0x6e0 (?:?) entry_SYSCALL_64_after_hwframe+0x77/0x7f (?:?) Freed by task stack: kasan_save_stack+0x33/0x60 (?:?) kasan_save_track+0x14/0x30 (?:?) kasan_save_free_info+0x3b/0x60 (?:?) __kasan_slab_free+0x5f/0x80 (?:?) kfree+0x30f/0x580 (?:?) dlm_put+0x1ce/0x200 (?:?) dlm_unregister_domain+0xf6/0xb30 (?:?) o2cb_cluster_disconnect+0x6b/0x90 (?:?) ocfs2_cluster_disconnect+0x41/0x70 (?:?) ocfs2_dlm_shutdown+0x1c4/0x220 (?:?) ocfs2_dismount_volume+0x38a/0x550 (?:?) generic_shutdown_super+0xc3/0x220 (?:?) kill_block_super+0x29/0x60 (?:?) deactivate_locked_super+0x66/0xe0 (?:?) cleanup_mnt+0x13d/0x210 (?:?) task_work_run+0xfa/0x170 (?:?) exit_to_user_mode_loop+0xd6/0x430 (?:?) do_syscall_64+0x3cb/0x640 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f (?:?) Link: https://lore.kernel.org/20260531044714.1640172-1-rollkingzzc@gmail.com Fixes: 4e3d24ed1a12 ("ocfs2/dlm: Dumps the lockres' into a debugfs file") Assisted-by: Codex:gpt-5.5 Signed-off-by: Zhang Cen Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- fs/ocfs2/dlm/dlmdebug.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/fs/ocfs2/dlm/dlmdebug.c b/fs/ocfs2/dlm/dlmdebug.c index fe4fdd09bae39..5645673586201 100644 --- a/fs/ocfs2/dlm/dlmdebug.c +++ b/fs/ocfs2/dlm/dlmdebug.c @@ -560,6 +560,7 @@ static int debug_lockres_open(struct inode *inode, struct file *file) struct dlm_ctxt *dlm = inode->i_private; struct debug_lockres *dl; void *buf; + int status = -ENOMEM; buf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!buf) @@ -572,16 +573,23 @@ static int debug_lockres_open(struct inode *inode, struct file *file) dl->dl_len = PAGE_SIZE; dl->dl_buf = buf; - dlm_grab(dlm); - dl->dl_ctxt = dlm; + /* ->release uses dl_ctxt after open, so it needs a real pin. */ + dl->dl_ctxt = dlm_grab(dlm); + if (!dl->dl_ctxt) { + status = -ENOENT; + goto bailseq; + } return 0; +bailseq: + seq_release_private(inode, file); bailfree: kfree(buf); bail: - mlog_errno(-ENOMEM); - return -ENOMEM; + if (status != -ENOENT) + mlog_errno(status); + return status; } static int debug_lockres_release(struct inode *inode, struct file *file) From a165709d386af36baedba7827c76098ee4009f1e Mon Sep 17 00:00:00 2001 From: Joseph Qi Date: Mon, 1 Jun 2026 20:16:18 +0800 Subject: [PATCH 1155/2758] ocfs2: fix race between ocfs2_control_install_private() and ocfs2_control_release() [ Upstream commit 57dcfd9049d497c31151787a0696d59f0a98f8e6 ] Move atomic_inc(&ocfs2_control_opened) and the handshake state update inside ocfs2_control_lock to close a race window where ocfs2_control_release() can observe ocfs2_control_opened dropping to zero (resetting ocfs2_control_this_node and running_proto) while ocfs2_control_install_private() is about to bump the counter and mark the connection valid. Link: https://lore.kernel.org/20260601121618.1263346-1-joseph.qi@linux.alibaba.com Fixes: 3cfd4ab6b6b4 ("ocfs2: Add the local node id to the handshake.") Signed-off-by: Joseph Qi Reported-by: Ginger Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- fs/ocfs2/stack_user.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/fs/ocfs2/stack_user.c b/fs/ocfs2/stack_user.c index 5803f1dee6790..91e19d33847c1 100644 --- a/fs/ocfs2/stack_user.c +++ b/fs/ocfs2/stack_user.c @@ -327,18 +327,14 @@ static int ocfs2_control_install_private(struct file *file) ocfs2_control_this_node = p->op_this_node; running_proto.pv_major = p->op_proto.pv_major; running_proto.pv_minor = p->op_proto.pv_minor; - } - -out_unlock: - mutex_unlock(&ocfs2_control_lock); - - if (!rc && set_p) { - /* We set the global values successfully */ atomic_inc(&ocfs2_control_opened); ocfs2_control_set_handshake_state(file, OCFS2_CONTROL_HANDSHAKE_VALID); } +out_unlock: + mutex_unlock(&ocfs2_control_lock); + return rc; } From f5255516ec7add3a6d5d37853ad5a9a9ddb14305 Mon Sep 17 00:00:00 2001 From: Ian Bridges Date: Mon, 1 Jun 2026 13:44:33 -0500 Subject: [PATCH 1156/2758] ocfs2: fix out-of-bounds write in ocfs2_remove_refcount_extent [ Upstream commit 1ec3cca2d8b6b9ff6584ca626d4c8918bbf48d44 ] [BUG] Unlinking a refcounted file whose refcount tree has leaf blocks triggers a fortify panic due to an out-of-bounds write. [CAUSE] When the last leaf block is removed from a refcount tree, ocfs2_remove_refcount_extent() converts the root back to leaf mode with a bulk memset on &rb->rf_records. rf_records sits in an anonymous union with rf_list. rf_list.l_tree_depth aliases rf_records.rl_count, and is 0 for a single-level tree. With rl_count equal to 0, the memset writes past the 16-byte declared size of rf_records, which the fortify checker catches. [FIX] Replace the bulk memset on &rb->rf_records with a correctly-bounded memset on rl_recs[] alone, after setting rl_count to the correct value. Link: https://lore.kernel.org/ah3TESOsEO9j_JLU@dev Fixes: 2f26f58df041 ("ocfs2: annotate flexible array members with __counted_by_le()") Signed-off-by: Ian Bridges Reported-by: syzbot+3ef989aae096b30f1663@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=3ef989aae096b30f1663 Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- fs/ocfs2/refcounttree.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c index 8eee5be4d1ed4..7323bde70caaf 100644 --- a/fs/ocfs2/refcounttree.c +++ b/fs/ocfs2/refcounttree.c @@ -2131,10 +2131,15 @@ static int ocfs2_remove_refcount_extent(handle_t *handle, rb->rf_flags = 0; rb->rf_parent = 0; rb->rf_cpos = 0; - memset(&rb->rf_records, 0, sb->s_blocksize - - offsetof(struct ocfs2_refcount_block, rf_records)); + rb->rf_records.rl_used = 0; + rb->rf_records.rl_reserved2 = 0; + rb->rf_records.rl_reserved1 = 0; + /* rl_count determines the memset size and fortify object size. */ rb->rf_records.rl_count = cpu_to_le16(ocfs2_refcount_recs_per_rb(sb)); + memset(rb->rf_records.rl_recs, 0, + le16_to_cpu(rb->rf_records.rl_count) * + sizeof(*rb->rf_records.rl_recs)); } ocfs2_journal_dirty(handle, ref_root_bh); From 32e0bf80662c981401de9d2d97fe26541cdb3a15 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Thu, 4 Jun 2026 11:01:53 -0700 Subject: [PATCH 1157/2758] cxl/pci: Fix the incorrect check of pci_read_config_word() return [ Upstream commit 66782cfa0085369e2d8c861042f7c6d43431bdb3 ] pci_read_config_word() returns PCIBIOS_* status on error which are positive values. The check should be for non-zero values to indicate error. Fix cxl_set_mem_enable() to check for non-zero return value instead of negative value. While fixing this, also convert the error to negative errno value when returning on error path. Fixes: 34e37b4c432c ("cxl/port: Enable HDM Capability after validating DVSEC Ranges") Reviewed-by: Richard Cheng Reviewed-by: Jonathan Cameron Reviewed-by: Alison Schofield Assisted-by: Claude:claude-opus-4-8 Link: https://patch.msgid.link/20260604180154.1925149-2-dave.jiang@intel.com Signed-off-by: Dave Jiang Signed-off-by: Sasha Levin --- drivers/cxl/core/pci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c index d1f487b3d809a..43885c59a7f24 100644 --- a/drivers/cxl/core/pci.c +++ b/drivers/cxl/core/pci.c @@ -187,8 +187,8 @@ static int cxl_set_mem_enable(struct cxl_dev_state *cxlds, u16 val) int rc; rc = pci_read_config_word(pdev, d + PCI_DVSEC_CXL_CTRL, &ctrl); - if (rc < 0) - return rc; + if (rc) + return pcibios_err_to_errno(rc); if ((ctrl & PCI_DVSEC_CXL_MEM_ENABLE) == val) return 1; @@ -196,8 +196,8 @@ static int cxl_set_mem_enable(struct cxl_dev_state *cxlds, u16 val) ctrl |= val; rc = pci_write_config_word(pdev, d + PCI_DVSEC_CXL_CTRL, ctrl); - if (rc < 0) - return rc; + if (rc) + return pcibios_err_to_errno(rc); return 0; } From d59b76143ca2221ef2f2c2510889fc84c9bda33d Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Thu, 4 Jun 2026 11:01:54 -0700 Subject: [PATCH 1158/2758] cxl/pci: Convert PCIBIOS errors to errno on DVSEC config accesses [ Upstream commit 26aa60e0276272ae61b843a05a91748dcb1130f9 ] PCI config space accessors return positive PCIBIOS_* status codes on failure that are positive integers. Several DVSEC accesses in the CXL core propagated these raw values to callers that test for failure against less than 0. Thus silently misinterpret the return value as success. Convert the positive error values to negative errno values so the checks are correct on error paths. While the chances of a config access failure are low, fix for correctness and to avoid confusion in the future when more DVSEC accesses are added. Fixes: 14d788740774 ("cxl/mem: Consolidate CXL DVSEC Range enumeration in the core") Fixes: ce17ad0d5498 ("cxl: Wait Memory_Info_Valid before access memory related info") Reviewed-by: Richard Cheng Reviewed-by: Jonathan Cameron Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260604180154.1925149-3-dave.jiang@intel.com Signed-off-by: Dave Jiang Signed-off-by: Sasha Levin --- drivers/cxl/core/pci.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c index 43885c59a7f24..e4338fd7e01b4 100644 --- a/drivers/cxl/core/pci.c +++ b/drivers/cxl/core/pci.c @@ -89,7 +89,7 @@ static int cxl_dvsec_mem_range_valid(struct cxl_dev_state *cxlds, int id) d + PCI_DVSEC_CXL_RANGE_SIZE_LOW(id), &temp); if (rc) - return rc; + return pcibios_err_to_errno(rc); valid = FIELD_GET(PCI_DVSEC_CXL_MEM_INFO_VALID, temp); if (valid) @@ -123,7 +123,7 @@ static int cxl_dvsec_mem_range_active(struct cxl_dev_state *cxlds, int id) rc = pci_read_config_dword( pdev, d + PCI_DVSEC_CXL_RANGE_SIZE_LOW(id), &temp); if (rc) - return rc; + return pcibios_err_to_errno(rc); active = FIELD_GET(PCI_DVSEC_CXL_MEM_ACTIVE, temp); if (active) @@ -156,7 +156,7 @@ int cxl_await_media_ready(struct cxl_dev_state *cxlds) rc = pci_read_config_word(pdev, d + PCI_DVSEC_CXL_CAP, &cap); if (rc) - return rc; + return pcibios_err_to_errno(rc); hdm_count = FIELD_GET(PCI_DVSEC_CXL_HDM_COUNT, cap); for (i = 0; i < hdm_count; i++) { @@ -275,7 +275,7 @@ int cxl_dvsec_rr_decode(struct cxl_dev_state *cxlds, rc = pci_read_config_word(pdev, d + PCI_DVSEC_CXL_CAP, &cap); if (rc) - return rc; + return pcibios_err_to_errno(rc); if (!(cap & PCI_DVSEC_CXL_MEM_CAPABLE)) { dev_dbg(dev, "Not MEM Capable\n"); @@ -299,7 +299,7 @@ int cxl_dvsec_rr_decode(struct cxl_dev_state *cxlds, */ rc = pci_read_config_word(pdev, d + PCI_DVSEC_CXL_CTRL, &ctrl); if (rc) - return rc; + return pcibios_err_to_errno(rc); info->mem_enabled = FIELD_GET(PCI_DVSEC_CXL_MEM_ENABLE, ctrl); if (!info->mem_enabled) @@ -316,14 +316,14 @@ int cxl_dvsec_rr_decode(struct cxl_dev_state *cxlds, rc = pci_read_config_dword( pdev, d + PCI_DVSEC_CXL_RANGE_SIZE_HIGH(i), &temp); if (rc) - return rc; + return pcibios_err_to_errno(rc); size = (u64)temp << 32; rc = pci_read_config_dword( pdev, d + PCI_DVSEC_CXL_RANGE_SIZE_LOW(i), &temp); if (rc) - return rc; + return pcibios_err_to_errno(rc); size |= temp & PCI_DVSEC_CXL_MEM_SIZE_LOW; if (!size) { @@ -333,14 +333,14 @@ int cxl_dvsec_rr_decode(struct cxl_dev_state *cxlds, rc = pci_read_config_dword( pdev, d + PCI_DVSEC_CXL_RANGE_BASE_HIGH(i), &temp); if (rc) - return rc; + return pcibios_err_to_errno(rc); base = (u64)temp << 32; rc = pci_read_config_dword( pdev, d + PCI_DVSEC_CXL_RANGE_BASE_LOW(i), &temp); if (rc) - return rc; + return pcibios_err_to_errno(rc); base |= temp & PCI_DVSEC_CXL_MEM_BASE_LOW; From d77ddcb7f75f8f250a8f345ee1857af8f848cce9 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Mon, 25 May 2026 17:35:54 +0200 Subject: [PATCH 1159/2758] netfilter: nfnetlink_osf: fix mss parsing on big-endian architectures [ Upstream commit a625c94144c9b66d32e1f374f909f38db46161c1 ] The MSS calculation in nf_osf_match_one() manually shifts bytes to construct a 16-bit value before passing it to ntohs(). This works on little-endian hosts but it does not work on big-endian as the bytes are being always shifted and set in the same way for all architectures. Use get_unaligned_be16() to fix this on big-endian systems. It also simplifies the code. Fixes: 11eeef41d5f6 ("netfilter: passive OS fingerprint xtables match") Signed-off-by: Fernando Fernandez Mancera Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nfnetlink_osf.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/net/netfilter/nfnetlink_osf.c b/net/netfilter/nfnetlink_osf.c index acb753ec5697a..92002079f8eab 100644 --- a/net/netfilter/nfnetlink_osf.c +++ b/net/netfilter/nfnetlink_osf.c @@ -95,11 +95,7 @@ static bool nf_osf_match_one(const struct sk_buff *skb, switch (*optp) { case OSFOPT_MSS: - mss = optp[3]; - mss <<= 8; - mss |= optp[2]; - - mss = ntohs((__force __be16)mss); + mss = get_unaligned_be16(&optp[2]); break; case OSFOPT_TS: break; From b7b486f006738e2847f9424359fad70229b3b6de Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 26 May 2026 18:40:44 +0200 Subject: [PATCH 1160/2758] netfilter: nfnetlink_cthelper: use {READ,WRITE}_ONCE for accessing helper flags [ Upstream commit f8bf5edf7157984bb8e288c8b04fdb041223b80c ] Conntrack helper flags are accessed from packet and netlink dump path. Concurrent update of userspace helper flags is not possible, because the nfnl_mutex in held on updates. These flags are only used by userspace helpers. Use {READ,WRITE}_ONCE() to access this flags from lockless paths. Fixes: 12f7a505331e ("netfilter: add user-space connection tracking helper infrastructure") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_conntrack_core.c | 4 +++- net/netfilter/nfnetlink_cthelper.c | 20 +++++++++++++------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index b521b5ebd6644..c072a14a306af 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -2213,6 +2213,7 @@ static int nf_confirm_cthelper(struct sk_buff *skb, struct nf_conn *ct, { const struct nf_conntrack_helper *helper; const struct nf_conn_help *help; + unsigned int helper_flags; int protoff; help = nfct_help(ct); @@ -2223,7 +2224,8 @@ static int nf_confirm_cthelper(struct sk_buff *skb, struct nf_conn *ct, if (!helper) return NF_ACCEPT; - if (!(helper->flags & NF_CT_HELPER_F_USERSPACE)) + helper_flags = READ_ONCE(helper->flags); + if (!(helper_flags & NF_CT_HELPER_F_USERSPACE)) return NF_ACCEPT; switch (nf_ct_l3num(ct)) { diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index 0d16ad82d70c0..61a2407b53bd3 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -41,8 +41,9 @@ static int nfnl_userspace_cthelper(struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, enum ip_conntrack_info ctinfo) { - const struct nf_conn_help *help; struct nf_conntrack_helper *helper; + const struct nf_conn_help *help; + unsigned int helper_flags; help = nfct_help(ct); if (help == NULL) @@ -53,8 +54,10 @@ nfnl_userspace_cthelper(struct sk_buff *skb, unsigned int protoff, if (helper == NULL) return NF_DROP; + helper_flags = READ_ONCE(helper->flags); + /* This is a user-space helper not yet configured, skip. */ - if ((helper->flags & + if ((helper_flags & (NF_CT_HELPER_F_USERSPACE | NF_CT_HELPER_F_CONFIGURED)) == NF_CT_HELPER_F_USERSPACE) return NF_ACCEPT; @@ -404,10 +407,10 @@ nfnl_cthelper_update(const struct nlattr * const tb[], switch(status) { case NFCT_HELPER_STATUS_ENABLED: - helper->flags |= NF_CT_HELPER_F_CONFIGURED; + WRITE_ONCE(helper->flags, helper->flags | NF_CT_HELPER_F_CONFIGURED); break; case NFCT_HELPER_STATUS_DISABLED: - helper->flags &= ~NF_CT_HELPER_F_CONFIGURED; + WRITE_ONCE(helper->flags, helper->flags & ~NF_CT_HELPER_F_CONFIGURED); break; } } @@ -529,8 +532,8 @@ static int nfnl_cthelper_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, int event, struct nf_conntrack_helper *helper) { - struct nlmsghdr *nlh; unsigned int flags = portid ? NLM_F_MULTI : 0; + struct nlmsghdr *nlh; int status; event = nfnl_msg_type(NFNL_SUBSYS_CTHELPER, event); @@ -554,7 +557,7 @@ nfnl_cthelper_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, if (nla_put_be32(skb, NFCTH_PRIV_DATA_LEN, htonl(helper->data_len))) goto nla_put_failure; - if (helper->flags & NF_CT_HELPER_F_CONFIGURED) + if (READ_ONCE(helper->flags) & NF_CT_HELPER_F_CONFIGURED) status = NFCT_HELPER_STATUS_ENABLED; else status = NFCT_HELPER_STATUS_DISABLED; @@ -575,6 +578,7 @@ static int nfnl_cthelper_dump_table(struct sk_buff *skb, struct netlink_callback *cb) { struct nf_conntrack_helper *cur, *last; + unsigned int helper_flags; rcu_read_lock(); last = (struct nf_conntrack_helper *)cb->args[1]; @@ -583,8 +587,10 @@ nfnl_cthelper_dump_table(struct sk_buff *skb, struct netlink_callback *cb) hlist_for_each_entry_rcu(cur, &nf_ct_helper_hash[cb->args[0]], hnode) { + helper_flags = READ_ONCE(cur->flags); + /* skip non-userspace conntrack helpers. */ - if (!(cur->flags & NF_CT_HELPER_F_USERSPACE)) + if (!(helper_flags & NF_CT_HELPER_F_USERSPACE)) continue; if (cb->args[1]) { From 819fb408d9c930ec383e86a6e6d670ccacea576b Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Tue, 26 May 2026 23:58:27 +0200 Subject: [PATCH 1161/2758] netfilter: synproxy: drop packets if timestamp adjustment fails [ Upstream commit 63d29ee95c4ab5976d660182d2e4733bb4a091d8 ] If a packet was malformed or if skb_ensure_writable() failed, the synproxy_tstamp_adjust() function returned 0 indicating an error but it was ignored on the callers. Make the function return a boolean instead to clarify the result and drop the packet if synproxy_tstamp_adjust() failed due to ENOMEM from skb_ensure_writable(). In addition, if there are malformed options, skip the tstamp update but do not drop the packet as that should be done by the policy directly. Fixes: 48b1de4c110a ("netfilter: add SYNPROXY core/target") Signed-off-by: Fernando Fernandez Mancera Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_synproxy_core.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/net/netfilter/nf_synproxy_core.c b/net/netfilter/nf_synproxy_core.c index ed00114f65f39..f99c22f57b7e5 100644 --- a/net/netfilter/nf_synproxy_core.c +++ b/net/netfilter/nf_synproxy_core.c @@ -184,7 +184,7 @@ synproxy_check_timestamp_cookie(struct synproxy_options *opts) opts->options |= opts->tsecr & (1 << 5) ? NF_SYNPROXY_OPT_ECN : 0; } -static unsigned int +static bool synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff, struct tcphdr *th, struct nf_conn *ct, enum ip_conntrack_info ctinfo, @@ -194,13 +194,13 @@ synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff, __be32 *ptr, old; if (synproxy->tsoff == 0) - return 1; + return true; optoff = protoff + sizeof(struct tcphdr); optend = protoff + th->doff * 4; if (skb_ensure_writable(skb, optend)) - return 0; + return false; th = (struct tcphdr *)(skb->data + protoff); @@ -209,7 +209,7 @@ synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff, switch (op[0]) { case TCPOPT_EOL: - return 1; + return true; case TCPOPT_NOP: optoff++; continue; @@ -217,7 +217,7 @@ synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff, if (optoff + 1 == optend || optoff + op[1] > optend || op[1] < 2) - return 0; + return true; if (op[0] == TCPOPT_TIMESTAMP && op[1] == TCPOLEN_TIMESTAMP) { if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) { @@ -233,12 +233,12 @@ synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff, } inet_proto_csum_replace4(&th->check, skb, old, *ptr, false); - return 1; + return true; } optoff += op[1]; } } - return 1; + return true; } #ifdef CONFIG_PROC_FS @@ -749,7 +749,9 @@ ipv4_synproxy_hook(void *priv, struct sk_buff *skb, break; } - synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy); + if (!synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy)) + return NF_DROP_REASON(skb, SKB_DROP_REASON_NETFILTER_DROP, ENOMEM); + return NF_ACCEPT; } EXPORT_SYMBOL_GPL(ipv4_synproxy_hook); @@ -1177,7 +1179,9 @@ ipv6_synproxy_hook(void *priv, struct sk_buff *skb, break; } - synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy); + if (!synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy)) + return NF_DROP_REASON(skb, SKB_DROP_REASON_NETFILTER_DROP, ENOMEM); + return NF_ACCEPT; } EXPORT_SYMBOL_GPL(ipv6_synproxy_hook); From a4ff6e76c56b7b97bffec1d296a7a2b363197fa2 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Tue, 26 May 2026 23:58:28 +0200 Subject: [PATCH 1162/2758] netfilter: synproxy: adjust duplicate timestamp options [ Upstream commit 22bb132cfb9b94847d52d73614284b8c5ea8d36e ] RFC 9293 does not mention anything about duplicated options and each networking stack handles it in their own way. Currently, Linux kernel is processing options sequentially and in case of duplicated timestamp options, the value from the latest one overrides the others. As SYNPROXY is modifying only the first timestamp option found, a packet can reach the backend server and it might parse the wrong timestamp value. Let's just continue parsing the following options and in case a duplicated timestamp is found, adjust it too. Fixes: 48b1de4c110a ("netfilter: add SYNPROXY core/target") Signed-off-by: Fernando Fernandez Mancera Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_synproxy_core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/netfilter/nf_synproxy_core.c b/net/netfilter/nf_synproxy_core.c index f99c22f57b7e5..a0bcf188810d1 100644 --- a/net/netfilter/nf_synproxy_core.c +++ b/net/netfilter/nf_synproxy_core.c @@ -233,7 +233,6 @@ synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff, } inet_proto_csum_replace4(&th->check, skb, old, *ptr, false); - return true; } optoff += op[1]; } From ea3d2caa5bfacf5db5c1cad521ca5d9144edd9a7 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Tue, 26 May 2026 23:58:29 +0200 Subject: [PATCH 1163/2758] netfilter: synproxy: fix unaligned memory access in timestamp adjustment [ Upstream commit 992c20bc8a4aba220c8b95b467d049289778dad6 ] Use get_unaligned_be32() and put_unaligned_be32() to safely read and write the timestamp fields. This prevents performance degradation due to unaligned memory access or even a crash on strict alignment architectures. This follows the implementation of timestamp parsing in the networking stack at tcp_parse_options() and synproxy_parse_options(). Fixes: 48b1de4c110a ("netfilter: add SYNPROXY core/target") Signed-off-by: Fernando Fernandez Mancera Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_synproxy_core.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/net/netfilter/nf_synproxy_core.c b/net/netfilter/nf_synproxy_core.c index a0bcf188810d1..acd360515972f 100644 --- a/net/netfilter/nf_synproxy_core.c +++ b/net/netfilter/nf_synproxy_core.c @@ -191,7 +191,7 @@ synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff, const struct nf_conn_synproxy *synproxy) { unsigned int optoff, optend; - __be32 *ptr, old; + u32 new, old; if (synproxy->tsoff == 0) return true; @@ -221,18 +221,17 @@ synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff, if (op[0] == TCPOPT_TIMESTAMP && op[1] == TCPOLEN_TIMESTAMP) { if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) { - ptr = (__be32 *)&op[2]; - old = *ptr; - *ptr = htonl(ntohl(*ptr) - - synproxy->tsoff); + old = get_unaligned_be32(&op[2]); + new = old - synproxy->tsoff; + put_unaligned_be32(new, &op[2]); } else { - ptr = (__be32 *)&op[6]; - old = *ptr; - *ptr = htonl(ntohl(*ptr) + - synproxy->tsoff); + old = get_unaligned_be32(&op[6]); + new = old + synproxy->tsoff; + put_unaligned_be32(new, &op[6]); } inet_proto_csum_replace4(&th->check, skb, - old, *ptr, false); + cpu_to_be32(old), + cpu_to_be32(new), false); } optoff += op[1]; } From 087adcfef920e07d4116339b84c3daf57ec5b751 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Tue, 26 May 2026 23:58:30 +0200 Subject: [PATCH 1164/2758] netfilter: synproxy: protect nf_ct_seqadj_init() with conntrack lock [ Upstream commit 9e37388b8070afe73d4ab2d973b28593ed65f3ad ] nf_ct_seqadj_init() is called without holding the ct lock. This can race with nf_ct_seq_adjust() when a connection is in CLOSE state due to an RST or connection reopening. In addition for SYN_RECV state, concurrent processing of packets can trigger nf_ct_seq_adjust() too. These situations create a read/write data race. As synproxy is the only user of nf_ct_seqadj_init() at the moment, fix this by holding ct->lock inside nf_ct_seqadj_init() until all is done. Fixes: 48b1de4c110a ("netfilter: add SYNPROXY core/target") Signed-off-by: Fernando Fernandez Mancera Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_conntrack_seqadj.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/netfilter/nf_conntrack_seqadj.c b/net/netfilter/nf_conntrack_seqadj.c index 7ab2b25b57bcc..b7e99f34dfce8 100644 --- a/net/netfilter/nf_conntrack_seqadj.c +++ b/net/netfilter/nf_conntrack_seqadj.c @@ -17,12 +17,14 @@ int nf_ct_seqadj_init(struct nf_conn *ct, enum ip_conntrack_info ctinfo, if (off == 0) return 0; + spin_lock_bh(&ct->lock); set_bit(IPS_SEQ_ADJUST_BIT, &ct->status); seqadj = nfct_seqadj(ct); this_way = &seqadj->seq[dir]; this_way->offset_before = off; this_way->offset_after = off; + spin_unlock_bh(&ct->lock); return 0; } EXPORT_SYMBOL_GPL(nf_ct_seqadj_init); From 9aeb0dcfeb460d33d61d434148b51103ab1d2013 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 4 Jun 2026 08:21:08 +0200 Subject: [PATCH 1165/2758] netfilter: cttimeout: detach dataplane timeout policy and repurpose refcount [ Upstream commit 7d6a9cdb8d3a51d9cfe546a09a518ab3d2671549 ] Add a refcount for struct nf_ct_timeout which is used by ct extension to set the custom ct timeout policy, this tells us that the ct timeout is being used by a conntrack entry. When the last conntrack entry drops the refcount on the ct timeout, the ct timeout is released. Remove the refcount for control plane which controls if the ruleset refers to the timeout policy. After this update, it is possible to remove the ct timeout policy from nfnetlink_cttimeout immediately. This is for simplicity not to handle two refcounts on a single object. Remove nf_queue_nf_hook_drop(): a packet sitting in nfqueue will just hold a reference to the nf_ct_timeout object until packet is reinjected, since this is part of the ct extension, this will be released by the time the conntrack is freed. nf_ct_untimeout() is still called to clean up in a best effort basis: the ct timeout on existing entries gets removed when the ct timeout goes away, but as long as the iptables ruleset still refers to the ct timeout through a template, new conntracks may keep attaching it and extend its lifetime until the rule is removed. nf_ct_untimeout() is not called anymore from module removal path, this is unlikely to find timeouts give module refcount is bumped, and the new refcount already tracks the ct timeout policy use so it is released when unused. Fixes: 50978462300f ("netfilter: add cttimeout infrastructure for fine timeout tuning") Fixes: 7e0b2b57f01d ("netfilter: nft_ct: add ct timeout support") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- include/net/netfilter/nf_conntrack_timeout.h | 27 ++++- net/netfilter/nf_conntrack_core.c | 6 +- net/netfilter/nf_conntrack_timeout.c | 27 ++++- net/netfilter/nfnetlink_cttimeout.c | 112 +++++++++---------- net/netfilter/nft_ct.c | 7 +- net/netfilter/xt_CT.c | 2 +- 6 files changed, 107 insertions(+), 74 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_timeout.h b/include/net/netfilter/nf_conntrack_timeout.h index 3a66d4abb6d68..d60aa86be0194 100644 --- a/include/net/netfilter/nf_conntrack_timeout.h +++ b/include/net/netfilter/nf_conntrack_timeout.h @@ -12,6 +12,7 @@ #define CTNL_TIMEOUT_NAME_MAX 32 struct nf_ct_timeout { + refcount_t refcnt; __u16 l3num; const struct nf_conntrack_l4proto *l4proto; struct rcu_head rcu; @@ -22,6 +23,22 @@ struct nf_conn_timeout { struct nf_ct_timeout __rcu *timeout; }; +static inline void nf_ct_timeout_put(const struct nf_conn *ct) +{ +#ifdef CONFIG_NF_CONNTRACK_TIMEOUT + struct nf_conn_timeout *timeout_ext; + struct nf_ct_timeout *timeout; + + timeout_ext = nf_ct_ext_find(ct, NF_CT_EXT_TIMEOUT); + if (!timeout_ext) + return; + + timeout = rcu_dereference(timeout_ext->timeout); + if (timeout && refcount_dec_and_test(&timeout->refcnt)) + kfree_rcu(timeout, rcu); +#endif +} + static inline unsigned int * nf_ct_timeout_data(const struct nf_conn_timeout *t) { @@ -56,8 +73,14 @@ struct nf_conn_timeout *nf_ct_timeout_ext_add(struct nf_conn *ct, #ifdef CONFIG_NF_CONNTRACK_TIMEOUT struct nf_conn_timeout *timeout_ext; + if (!timeout) + return NULL; + timeout_ext = nf_ct_ext_add(ct, NF_CT_EXT_TIMEOUT, gfp); - if (timeout_ext == NULL) + if (!timeout_ext || timeout_ext->timeout) + return NULL; + + if (!refcount_inc_not_zero(&timeout->refcnt)) return NULL; rcu_assign_pointer(timeout_ext->timeout, timeout); @@ -75,7 +98,7 @@ static inline unsigned int *nf_ct_timeout_lookup(const struct nf_conn *ct) struct nf_conn_timeout *timeout_ext; timeout_ext = nf_ct_timeout_find(ct); - if (timeout_ext) + if (timeout_ext && rcu_access_pointer(timeout_ext->timeout)) timeouts = nf_ct_timeout_data(timeout_ext); #endif return timeouts; diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index c072a14a306af..a45b732393693 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1737,16 +1737,18 @@ void nf_conntrack_free(struct nf_conn *ct) */ WARN_ON(refcount_read(&ct->ct_general.use) != 0); + rcu_read_lock(); if (ct->status & IPS_SRC_NAT_DONE) { const struct nf_nat_hook *nat_hook; - rcu_read_lock(); nat_hook = rcu_dereference(nf_nat_hook); if (nat_hook) nat_hook->remove_nat_bysrc(ct); - rcu_read_unlock(); } + nf_ct_timeout_put(ct); + rcu_read_unlock(); + kfree(ct->ext); kmem_cache_free(nf_conntrack_cachep, ct); cnet = nf_ct_pernet(net); diff --git a/net/netfilter/nf_conntrack_timeout.c b/net/netfilter/nf_conntrack_timeout.c index 0cc584d3dbb1d..c81becde2afa9 100644 --- a/net/netfilter/nf_conntrack_timeout.c +++ b/net/netfilter/nf_conntrack_timeout.c @@ -25,17 +25,32 @@ const struct nf_ct_timeout_hooks __rcu *nf_ct_timeout_hook __read_mostly; EXPORT_SYMBOL_GPL(nf_ct_timeout_hook); +/* nf_ct_iterate_cleanup() holds the conntrack lock. */ static int untimeout(struct nf_conn *ct, void *timeout) { struct nf_conn_timeout *timeout_ext = nf_ct_timeout_find(ct); if (timeout_ext) { - const struct nf_ct_timeout *t; + struct nf_ct_timeout *t; - t = rcu_access_pointer(timeout_ext->timeout); + rcu_read_lock(); + t = rcu_dereference(timeout_ext->timeout); + if (!t) { + rcu_read_unlock(); + return 0; + } - if (!timeout || t == timeout) + if (!timeout || t == timeout) { RCU_INIT_POINTER(timeout_ext->timeout, NULL); + + /* No race with nf_conntrack_free() which is called + * only after the conntrack has been removed from + * the hashes. + */ + if (refcount_dec_and_test(&t->refcnt)) + kfree_rcu(t, rcu); + } + rcu_read_unlock(); } /* We are not intended to delete this conntrack. */ @@ -70,6 +85,8 @@ int nf_ct_set_timeout(struct net *net, struct nf_conn *ct, const char *errmsg = NULL; int ret = 0; + WARN_ON_ONCE(!nf_ct_is_template(ct)); + rcu_read_lock(); h = rcu_dereference(nf_ct_timeout_hook); if (!h) { @@ -127,6 +144,8 @@ void nf_ct_destroy_timeout(struct nf_conn *ct) struct nf_conn_timeout *timeout_ext; const struct nf_ct_timeout_hooks *h; + WARN_ON_ONCE(!nf_ct_is_template(ct)); + rcu_read_lock(); h = rcu_dereference(nf_ct_timeout_hook); @@ -139,6 +158,8 @@ void nf_ct_destroy_timeout(struct nf_conn *ct) if (t) h->timeout_put(t); RCU_INIT_POINTER(timeout_ext->timeout, NULL); + if (t && refcount_dec_and_test(&t->refcnt)) + kfree_rcu(t, rcu); } } rcu_read_unlock(); diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c index dca6826af7de3..170d3db860c56 100644 --- a/net/netfilter/nfnetlink_cttimeout.c +++ b/net/netfilter/nfnetlink_cttimeout.c @@ -37,11 +37,8 @@ struct ctnl_timeout { struct list_head head; struct list_head free_head; struct rcu_head rcu_head; - refcount_t refcnt; char name[CTNL_TIMEOUT_NAME_MAX]; - - /* must be at the end */ - struct nf_ct_timeout timeout; + struct nf_ct_timeout *timeout; }; struct nfct_timeout_pernet { @@ -132,12 +129,12 @@ static int cttimeout_new_timeout(struct sk_buff *skb, /* You cannot replace one timeout policy by another of * different kind, sorry. */ - if (matching->timeout.l3num != l3num || - matching->timeout.l4proto->l4proto != l4num) + if (matching->timeout->l3num != l3num || + matching->timeout->l4proto->l4proto != l4num) return -EINVAL; - return ctnl_timeout_parse_policy(&matching->timeout.data, - matching->timeout.l4proto, + return ctnl_timeout_parse_policy(&matching->timeout->data, + matching->timeout->l4proto, info->net, cda[CTA_TIMEOUT_DATA]); } @@ -153,26 +150,35 @@ static int cttimeout_new_timeout(struct sk_buff *skb, goto err_proto_put; } - timeout = kzalloc(sizeof(struct ctnl_timeout) + - l4proto->ctnl_timeout.obj_size, GFP_KERNEL); + timeout = kzalloc(sizeof(*timeout), GFP_KERNEL); if (timeout == NULL) { ret = -ENOMEM; goto err_proto_put; } - ret = ctnl_timeout_parse_policy(&timeout->timeout.data, l4proto, + timeout->timeout = kzalloc(sizeof(*timeout->timeout) + + l4proto->ctnl_timeout.obj_size, GFP_KERNEL); + if (!timeout->timeout) { + ret = -ENOMEM; + goto err; + } + + ret = ctnl_timeout_parse_policy(&timeout->timeout->data, l4proto, info->net, cda[CTA_TIMEOUT_DATA]); if (ret < 0) - goto err; + goto err_free_timeout_policy; strcpy(timeout->name, nla_data(cda[CTA_TIMEOUT_NAME])); - timeout->timeout.l3num = l3num; - timeout->timeout.l4proto = l4proto; - refcount_set(&timeout->refcnt, 1); + timeout->timeout->l3num = l3num; + timeout->timeout->l4proto = l4proto; + refcount_set(&timeout->timeout->refcnt, 1); __module_get(THIS_MODULE); list_add_tail_rcu(&timeout->head, &pernet->nfct_timeout_list); return 0; + +err_free_timeout_policy: + kfree(timeout->timeout); err: kfree(timeout); err_proto_put: @@ -185,7 +191,7 @@ ctnl_timeout_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, { struct nlmsghdr *nlh; unsigned int flags = portid ? NLM_F_MULTI : 0; - const struct nf_conntrack_l4proto *l4proto = timeout->timeout.l4proto; + const struct nf_conntrack_l4proto *l4proto = timeout->timeout->l4proto; struct nlattr *nest_parms; int ret; @@ -197,17 +203,17 @@ ctnl_timeout_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, if (nla_put_string(skb, CTA_TIMEOUT_NAME, timeout->name) || nla_put_be16(skb, CTA_TIMEOUT_L3PROTO, - htons(timeout->timeout.l3num)) || + htons(timeout->timeout->l3num)) || nla_put_u8(skb, CTA_TIMEOUT_L4PROTO, l4proto->l4proto) || nla_put_be32(skb, CTA_TIMEOUT_USE, - htonl(refcount_read(&timeout->refcnt)))) + htonl(refcount_read(&timeout->timeout->refcnt)))) goto nla_put_failure; nest_parms = nla_nest_start(skb, CTA_TIMEOUT_DATA); if (!nest_parms) goto nla_put_failure; - ret = l4proto->ctnl_timeout.obj_to_nlattr(skb, &timeout->timeout.data); + ret = l4proto->ctnl_timeout.obj_to_nlattr(skb, &timeout->timeout->data); if (ret < 0) goto nla_put_failure; @@ -307,23 +313,17 @@ static int cttimeout_get_timeout(struct sk_buff *skb, return ret; } -/* try to delete object, fail if it is still in use. */ -static int ctnl_timeout_try_del(struct net *net, struct ctnl_timeout *timeout) +static void ctnl_timeout_del(struct net *net, struct ctnl_timeout *timeout) { - int ret = 0; + /* We are protected by nfnl mutex. */ + list_del_rcu(&timeout->head); + nf_ct_untimeout(net, timeout->timeout); - /* We want to avoid races with ctnl_timeout_put. So only when the - * current refcnt is 1, we decrease it to 0. - */ - if (refcount_dec_if_one(&timeout->refcnt)) { - /* We are protected by nfnl mutex. */ - list_del_rcu(&timeout->head); - nf_ct_untimeout(net, &timeout->timeout); - kfree_rcu(timeout, rcu_head); - } else { - ret = -EBUSY; - } - return ret; + if (refcount_dec_and_test(&timeout->timeout->refcnt)) + kfree_rcu(timeout->timeout, rcu); + + kfree_rcu(timeout, rcu_head); + module_put(THIS_MODULE); } static int cttimeout_del_timeout(struct sk_buff *skb, @@ -338,7 +338,7 @@ static int cttimeout_del_timeout(struct sk_buff *skb, if (!cda[CTA_TIMEOUT_NAME]) { list_for_each_entry_safe(cur, tmp, &pernet->nfct_timeout_list, head) - ctnl_timeout_try_del(info->net, cur); + ctnl_timeout_del(info->net, cur); return 0; } @@ -348,10 +348,8 @@ static int cttimeout_del_timeout(struct sk_buff *skb, if (strncmp(cur->name, name, CTNL_TIMEOUT_NAME_MAX) != 0) continue; - ret = ctnl_timeout_try_del(info->net, cur); - if (ret < 0) - return ret; - + ctnl_timeout_del(info->net, cur); + ret = 0; break; } return ret; @@ -511,24 +509,22 @@ static struct nf_ct_timeout *ctnl_timeout_find_get(struct net *net, if (strncmp(timeout->name, name, CTNL_TIMEOUT_NAME_MAX) != 0) continue; - if (!refcount_inc_not_zero(&timeout->refcnt)) + if (!refcount_inc_not_zero(&timeout->timeout->refcnt)) goto err; matching = timeout; + __module_get(THIS_MODULE); break; } err: - return matching ? &matching->timeout : NULL; + return matching ? matching->timeout : NULL; } -static void ctnl_timeout_put(struct nf_ct_timeout *t) +static void ctnl_timeout_put(struct nf_ct_timeout *timeout) { - struct ctnl_timeout *timeout = - container_of(t, struct ctnl_timeout, timeout); + if (refcount_dec_and_test(&timeout->refcnt)) + kfree_rcu(timeout, rcu); - if (refcount_dec_and_test(&timeout->refcnt)) { - kfree_rcu(timeout, rcu_head); - module_put(THIS_MODULE); - } + module_put(THIS_MODULE); } static const struct nfnl_callback cttimeout_cb[IPCTNL_MSG_TIMEOUT_MAX] = { @@ -609,8 +605,11 @@ static void __net_exit cttimeout_net_exit(struct net *net) list_for_each_entry_safe(cur, tmp, &pernet->nfct_timeout_freelist, free_head) { list_del(&cur->free_head); - if (refcount_dec_and_test(&cur->refcnt)) - kfree_rcu(cur, rcu_head); + if (refcount_dec_and_test(&cur->timeout->refcnt)) + kfree_rcu(cur->timeout, rcu); + + kfree_rcu(cur, rcu_head); + module_put(THIS_MODULE); } } @@ -649,24 +648,13 @@ static int __init cttimeout_init(void) return ret; } -static int untimeout(struct nf_conn *ct, void *timeout) -{ - struct nf_conn_timeout *timeout_ext = nf_ct_timeout_find(ct); - - if (timeout_ext) - RCU_INIT_POINTER(timeout_ext->timeout, NULL); - - return 0; -} - static void __exit cttimeout_exit(void) { nfnetlink_subsys_unregister(&cttimeout_subsys); unregister_pernet_subsys(&cttimeout_ops); RCU_INIT_POINTER(nf_ct_timeout_hook, NULL); - - nf_ct_iterate_destroy(untimeout, NULL); + synchronize_net(); } module_init(cttimeout_init); diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index 357513c6dcea0..801c01c6af95f 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c @@ -897,8 +897,6 @@ static void nft_ct_timeout_obj_eval(struct nft_object *obj, } } - rcu_assign_pointer(timeout->timeout, priv->timeout); - /* adjust the timeout as per 'new' state. ct is unconfirmed, * so the current timestamp must not be added. */ @@ -949,6 +947,7 @@ static int nft_ct_timeout_obj_init(const struct nft_ctx *ctx, timeout->l3num = l3num; timeout->l4proto = l4proto; + refcount_set(&timeout->refcnt, 1); ret = nf_ct_netns_get(ctx->net, ctx->family); if (ret < 0) @@ -969,10 +968,10 @@ static void nft_ct_timeout_obj_destroy(const struct nft_ctx *ctx, struct nft_ct_timeout_obj *priv = nft_obj_data(obj); struct nf_ct_timeout *timeout = priv->timeout; - nf_queue_nf_hook_drop(ctx->net); nf_ct_untimeout(ctx->net, timeout); nf_ct_netns_put(ctx->net, ctx->family); - kfree_rcu(priv->timeout, rcu); + if (refcount_dec_and_test(&timeout->refcnt)) + kfree_rcu(priv->timeout, rcu); } static int nft_ct_timeout_obj_dump(struct sk_buff *skb, diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c index d2aeacf94230f..b94f004d5f5c2 100644 --- a/net/netfilter/xt_CT.c +++ b/net/netfilter/xt_CT.c @@ -284,7 +284,7 @@ static void xt_ct_tg_destroy(const struct xt_tgdtor_param *par, struct nf_conn_help *help; if (ct) { - if (info->helper[0] || info->timeout[0]) + if (info->helper[0]) nf_queue_nf_hook_drop(par->net); help = nfct_help(ct); From 58a4ad9bf636c7f6d747a0f77c00ddcd9ea909b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Fri, 5 Jun 2026 01:14:40 -0300 Subject: [PATCH 1166/2758] ALSA: usb-audio: qcom: Initialize offload control return value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 2b5632d72fca0841bea283da2e3a478d24118508 ] snd_usb_offload_create_ctl() returns ret after walking the USB PCM list, but ret is only assigned after a playback stream passes the endpoint and PCM-index filters. If all playback streams are skipped, for example because there is no playback endpoint or because all PCM indexes exceed the 0xff control range, the function returns an uninitialized stack value. Initialize ret to 0 so the no-control-created path returns deterministic success, while preserving the existing negative error return when snd_ctl_add() fails. Fixes: a67656f011d1 ("ALSA: usb-audio: qcom: Add USB offload route kcontrol") Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260605-alsa-usb-qcom-offload-ret-init-v1-1-dc72fcc4bd3b@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/usb/qcom/mixer_usb_offload.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/usb/qcom/mixer_usb_offload.c b/sound/usb/qcom/mixer_usb_offload.c index 2adeb64f4d33f..005138714f724 100644 --- a/sound/usb/qcom/mixer_usb_offload.c +++ b/sound/usb/qcom/mixer_usb_offload.c @@ -113,7 +113,7 @@ int snd_usb_offload_create_ctl(struct snd_usb_audio *chip, struct device *bedev) struct snd_usb_substream *subs; struct snd_usb_stream *as; char ctl_name[48]; - int ret; + int ret = 0; list_for_each_entry(as, &chip->pcm_list, list) { subs = &as->substream[SNDRV_PCM_STREAM_PLAYBACK]; From 6e01f23cb087b777442b33931c642d98336dfea0 Mon Sep 17 00:00:00 2001 From: Shengming Hu Date: Thu, 4 Jun 2026 20:27:32 +0800 Subject: [PATCH 1167/2758] mm/slub: preserve original size in _kmalloc_nolock_noprof retry path [ Upstream commit 19b206b9534a85266efa78febeb4ae185e75bccd ] _kmalloc_nolock_noprof() retries from the next kmalloc bucket when the initial allocation fails. The retry currently reuses `size` as the bucket selector and overwrites it with s->object_size + 1. That value is later passed as the original allocation size to __slab_alloc_node(), slab_post_alloc_hook() and kasan_kmalloc(). On a successful retry this makes KASAN/slub-debug observe the retry bucket selector rather than the caller requested size, potentially widening the valid kmalloc range and hiding overflows. Keep the caller requested size separately as orig_size and pass it to the allocation/debug/KASAN paths. Continue using `size` as the retry cache selector. Fixes: af92793e52c3 ("slab: Introduce kmalloc_nolock() and kfree_nolock()") Signed-off-by: Shengming Hu Reviewed-by: Harry Yoo (Oracle) Reviewed-by: Hao Li Link: https://patch.msgid.link/202606042027323804pk3MRY42Jy7y42OHAhQZ@zte.com.cn Signed-off-by: Vlastimil Babka (SUSE) Signed-off-by: Sasha Levin --- mm/slub.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index 9365501d0df14..eed3251eb7d0c 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -5325,6 +5325,7 @@ EXPORT_SYMBOL(__kmalloc_noprof); void *kmalloc_nolock_noprof(size_t size, gfp_t gfp_flags, int node) { gfp_t alloc_gfp = __GFP_NOWARN | __GFP_NOMEMALLOC | gfp_flags; + size_t orig_size = size; struct kmem_cache *s; bool can_retry = true; void *ret; @@ -5373,7 +5374,7 @@ void *kmalloc_nolock_noprof(size_t size, gfp_t gfp_flags, int node) * kfence_alloc. Hence call __slab_alloc_node() (at most twice) * and slab_post_alloc_hook() directly. */ - ret = __slab_alloc_node(s, alloc_gfp, node, _RET_IP_, size); + ret = __slab_alloc_node(s, alloc_gfp, node, _RET_IP_, orig_size); /* * It's possible we failed due to trylock as we preempted someone with @@ -5397,9 +5398,9 @@ void *kmalloc_nolock_noprof(size_t size, gfp_t gfp_flags, int node) success: maybe_wipe_obj_freeptr(s, ret); slab_post_alloc_hook(s, NULL, alloc_gfp, 1, &ret, - slab_want_init_on_alloc(alloc_gfp, s), size); + slab_want_init_on_alloc(alloc_gfp, s), orig_size); - ret = kasan_kmalloc(s, ret, size, alloc_gfp); + ret = kasan_kmalloc(s, ret, orig_size, alloc_gfp); return ret; } EXPORT_SYMBOL_GPL(kmalloc_nolock_noprof); From 8974e226ae04a78be8c565bb42c36ba7289efdb6 Mon Sep 17 00:00:00 2001 From: Junxiao Chang Date: Sat, 6 Jun 2026 10:15:14 +0800 Subject: [PATCH 1168/2758] x86/cpu: Remove obsolete aperfmperf_get_khz() declaration [ Upstream commit a5f28da54f36f1a8b289d9bdd3e780b2ede0da6f ] aperfmperf_get_khz() was replaced by arch_freq_get_on_cpu(). The remaining declaration in the header file is no longer used and should be removed. Fixes: f3eca381bd49 ("x86/aperfmperf: Replace arch_freq_get_on_cpu()") Signed-off-by: Junxiao Chang Signed-off-by: Ingo Molnar Reviewed-by: Nikolay Borisov Link: https://patch.msgid.link/20260606021514.1433619-1-junxiao.chang@intel.com Signed-off-by: Sasha Levin --- arch/x86/kernel/cpu/cpu.h | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/x86/kernel/cpu/cpu.h b/arch/x86/kernel/cpu/cpu.h index 5c7a3a71191a1..dca2d5845e427 100644 --- a/arch/x86/kernel/cpu/cpu.h +++ b/arch/x86/kernel/cpu/cpu.h @@ -75,7 +75,6 @@ static inline struct amd_northbridge *amd_init_l3_cache(int index) } #endif -unsigned int aperfmperf_get_khz(int cpu); void cpu_select_mitigations(void); extern void x86_spec_ctrl_setup_ap(void); From a052a94bcc629acaecc2ce42a4af77f8fa399757 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 4 Jun 2026 08:21:12 +0200 Subject: [PATCH 1169/2758] netfilter: conntrack: revert ct extension genid infrastructure [ Upstream commit 35e21a4dccc5c255ba59ccfbfeb4629ed21da972 ] This infrastructure is not used anymore after moving ct timeout and helper to use datapath refcount to track object use. Revert commit c56716c69ce1 ("netfilter: extensions: introduce extension genid count") this patch disables all ct extensions (leading to NULL) for unconfirmed conntracks, when this is only targeted at ct helper and ct timeout. There is also codebase that dereferences the ct extension without checking for NULL which could lead to crash. Fixes: c56716c69ce1 ("netfilter: extensions: introduce extension genid count") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- include/net/netfilter/nf_conntrack_extend.h | 12 ---- net/netfilter/nf_conntrack_core.c | 61 +-------------------- net/netfilter/nf_conntrack_extend.c | 32 +---------- 3 files changed, 2 insertions(+), 103 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h index 0b247248b0323..fd5c4dbf72caf 100644 --- a/include/net/netfilter/nf_conntrack_extend.h +++ b/include/net/netfilter/nf_conntrack_extend.h @@ -38,7 +38,6 @@ enum nf_ct_ext_id { struct nf_ct_ext { u8 offset[NF_CT_EXT_NUM]; u8 len; - unsigned int gen_id; char data[] __aligned(8); }; @@ -52,8 +51,6 @@ static inline bool nf_ct_ext_exist(const struct nf_conn *ct, u8 id) return (ct->ext && __nf_ct_ext_exist(ct->ext, id)); } -void *__nf_ct_ext_find(const struct nf_ct_ext *ext, u8 id); - static inline void *nf_ct_ext_find(const struct nf_conn *ct, u8 id) { struct nf_ct_ext *ext = ct->ext; @@ -61,19 +58,10 @@ static inline void *nf_ct_ext_find(const struct nf_conn *ct, u8 id) if (!ext || !__nf_ct_ext_exist(ext, id)) return NULL; - if (unlikely(ext->gen_id)) - return __nf_ct_ext_find(ext, id); - return (void *)ct->ext + ct->ext->offset[id]; } /* Add this type, returns pointer to data or NULL. */ void *nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp); -/* ext genid. if ext->id != ext_genid, extensions cannot be used - * anymore unless conntrack has CONFIRMED bit set. - */ -extern atomic_t nf_conntrack_ext_genid; -void nf_ct_ext_bump_genid(void); - #endif /* _NF_CONNTRACK_EXTEND_H */ diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index a45b732393693..d46faf2d7b9ae 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -840,33 +840,6 @@ static void __nf_conntrack_hash_insert(struct nf_conn *ct, &nf_conntrack_hash[reply_hash]); } -static bool nf_ct_ext_valid_pre(const struct nf_ct_ext *ext) -{ - /* if ext->gen_id is not equal to nf_conntrack_ext_genid, some extensions - * may contain stale pointers to e.g. helper that has been removed. - * - * The helper can't clear this because the nf_conn object isn't in - * any hash and synchronize_rcu() isn't enough because associated skb - * might sit in a queue. - */ - return !ext || ext->gen_id == atomic_read(&nf_conntrack_ext_genid); -} - -static bool nf_ct_ext_valid_post(struct nf_ct_ext *ext) -{ - if (!ext) - return true; - - if (ext->gen_id != atomic_read(&nf_conntrack_ext_genid)) - return false; - - /* inserted into conntrack table, nf_ct_iterate_cleanup() - * will find it. Disable nf_ct_ext_find() id check. - */ - WRITE_ONCE(ext->gen_id, 0); - return true; -} - int nf_conntrack_hash_check_insert(struct nf_conn *ct) { @@ -882,9 +855,6 @@ nf_conntrack_hash_check_insert(struct nf_conn *ct) zone = nf_ct_zone(ct); - if (!nf_ct_ext_valid_pre(ct->ext)) - return -EAGAIN; - local_bh_disable(); do { sequence = read_seqcount_begin(&nf_conntrack_generation); @@ -918,18 +888,6 @@ nf_conntrack_hash_check_insert(struct nf_conn *ct) goto chaintoolong; } - /* If genid has changed, we can't insert anymore because ct - * extensions could have stale pointers and nf_ct_iterate_destroy - * might have completed its table scan already. - * - * Increment of the ext genid right after this check is fine: - * nf_ct_iterate_destroy blocks until locks are released. - */ - if (!nf_ct_ext_valid_post(ct->ext)) { - err = -EAGAIN; - goto out; - } - smp_wmb(); /* The caller holds a reference to this object */ refcount_set(&ct->ct_general.use, 2); @@ -1257,11 +1215,6 @@ __nf_conntrack_confirm(struct sk_buff *skb) return NF_DROP; } - if (!nf_ct_ext_valid_pre(ct->ext)) { - NF_CT_STAT_INC(net, insert_failed); - goto dying; - } - /* We have to check the DYING flag after unlink to prevent * a race against nf_ct_get_next_corpse() possibly called from * user context, else we insert an already 'dead' hash, blocking @@ -1324,16 +1277,6 @@ __nf_conntrack_confirm(struct sk_buff *skb) nf_conntrack_double_unlock(hash, reply_hash); local_bh_enable(); - /* ext area is still valid (rcu read lock is held, - * but will go out of scope soon, we need to remove - * this conntrack again. - */ - if (!nf_ct_ext_valid_post(ct->ext)) { - nf_ct_kill(ct); - NF_CT_STAT_INC_ATOMIC(net, drop); - return NF_DROP; - } - help = nfct_help(ct); if (help && help->helper) nf_conntrack_event_cache(IPCT_HELPER, ct); @@ -2440,13 +2383,11 @@ nf_ct_iterate_destroy(int (*iter)(struct nf_conn *i, void *data), void *data) */ synchronize_net(); - nf_ct_ext_bump_genid(); iter_data.data = data; nf_ct_iterate_cleanup(iter, &iter_data); /* Another cpu might be in a rcu read section with - * rcu protected pointer cleared in iter callback - * or hidden via nf_ct_ext_bump_genid() above. + * rcu protected pointer cleared in iter callback. * * Wait until those are done. */ diff --git a/net/netfilter/nf_conntrack_extend.c b/net/netfilter/nf_conntrack_extend.c index dd62cc12e7750..0da105e1ded93 100644 --- a/net/netfilter/nf_conntrack_extend.c +++ b/net/netfilter/nf_conntrack_extend.c @@ -27,8 +27,6 @@ #define NF_CT_EXT_PREALLOC 128u /* conntrack events are on by default */ -atomic_t nf_conntrack_ext_genid __read_mostly = ATOMIC_INIT(1); - static const u8 nf_ct_ext_type_len[NF_CT_EXT_NUM] = { [NF_CT_EXT_HELPER] = sizeof(struct nf_conn_help), #if IS_ENABLED(CONFIG_NF_NAT) @@ -118,10 +116,8 @@ void *nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp) if (!new) return NULL; - if (!ct->ext) { + if (!ct->ext) memset(new->offset, 0, sizeof(new->offset)); - new->gen_id = atomic_read(&nf_conntrack_ext_genid); - } new->offset[id] = newoff; new->len = newlen; @@ -131,29 +127,3 @@ void *nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp) return (void *)new + newoff; } EXPORT_SYMBOL(nf_ct_ext_add); - -/* Use nf_ct_ext_find wrapper. This is only useful for unconfirmed entries. */ -void *__nf_ct_ext_find(const struct nf_ct_ext *ext, u8 id) -{ - unsigned int gen_id = atomic_read(&nf_conntrack_ext_genid); - unsigned int this_id = READ_ONCE(ext->gen_id); - - if (!__nf_ct_ext_exist(ext, id)) - return NULL; - - if (this_id == 0 || ext->gen_id == gen_id) - return (void *)ext + ext->offset[id]; - - return NULL; -} -EXPORT_SYMBOL(__nf_ct_ext_find); - -void nf_ct_ext_bump_genid(void) -{ - unsigned int value = atomic_inc_return(&nf_conntrack_ext_genid); - - if (value == UINT_MAX) - atomic_set(&nf_conntrack_ext_genid, 1); - - msleep(HZ); -} From f0384658eed1d2bc762b3e68b97990ef7d8f8592 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 4 Jun 2026 08:21:13 +0200 Subject: [PATCH 1170/2758] netfilter: conntrack: call nf_ct_gre_keymap_destroy() if master helper is pptp [ Upstream commit b0f02608fbcd607b5131cceb91fc0a035264e61c ] For GRE flows, validate that the ct master helper (if any) is pptp before calling nf_ct_gre_keymap_destroy(), so the helper data area can be accessed safely. Note that only the pptp helper provides a .destroy callback. Fixes: e56894356f60 ("netfilter: conntrack: remove l4proto destroy hook") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_conntrack_core.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index d46faf2d7b9ae..75c91636bf619 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -562,9 +562,23 @@ static void destroy_gre_conntrack(struct nf_conn *ct) { #ifdef CONFIG_NF_CT_PROTO_GRE struct nf_conn *master = ct->master; + struct nf_conn_help *help; + + if (!master) + return; + + help = nfct_help(master); + if (help) { + struct nf_conntrack_helper *helper; - if (master) - nf_ct_gre_keymap_destroy(master); + rcu_read_lock(); + helper = rcu_dereference(help->helper); + /* Only pptp helper has a destroy callback. */ + if (helper && helper->destroy) + nf_ct_gre_keymap_destroy(master); + + rcu_read_unlock(); + } #endif } From ebd4575be3878954ade9948db1fe7fd2ef93bc95 Mon Sep 17 00:00:00 2001 From: DaeMyung Kang Date: Sun, 24 May 2026 14:42:37 +0900 Subject: [PATCH 1171/2758] ntfs: free link name from ntfs_name_cache [ Upstream commit 8488c4d066e6a52937fa5d82ab131c7554ddc9d8 ] ntfs_link() converts the new link name with ntfs_nlstoucs() using NTFS_MAX_NAME_LEN. In this case ntfs_nlstoucs() allocates the result from ntfs_name_cache, and its contract requires callers to release the buffer with kmem_cache_free(ntfs_name_cache, ...). All other ntfs_nlstoucs() callers in namei.c do that, but ntfs_link() uses kfree(), which mismatches the allocator for successfully converted names. The conversion failure path reaches the common out label with uname == NULL. That was harmless for kfree(), but kmem_cache_free() does not provide the same NULL contract. Return directly on conversion failure and free successful conversions with ntfs_name_cache. Fixes: af0db57d4293 ("ntfs: update inode operations") Signed-off-by: DaeMyung Kang Signed-off-by: Namjae Jeon Signed-off-by: Sasha Levin --- fs/ntfs/namei.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c index c4f82846c58c3..9c1c36acfad24 100644 --- a/fs/ntfs/namei.c +++ b/fs/ntfs/namei.c @@ -1532,8 +1532,7 @@ static int ntfs_link(struct dentry *old_dentry, struct inode *dir, if (uname_len < 0) { if (uname_len != -ENAMETOOLONG) ntfs_error(sb, "Failed to convert name to unicode."); - err = -ENOMEM; - goto out; + return -ENOMEM; } if (!(vol->vol_flags & VOLUME_IS_DIRTY)) @@ -1563,7 +1562,7 @@ static int ntfs_link(struct dentry *old_dentry, struct inode *dir, mutex_unlock(&ni->mrec_lock); out: - kfree(uname); + kmem_cache_free(ntfs_name_cache, uname); return err; } From e5ca5e93586ec865aedccd47bd660a1172bb3058 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 2 Jun 2026 16:04:34 +0200 Subject: [PATCH 1172/2758] RDMA/hfi1: Open-code rvt_set_ibdev_name() [ Upstream commit 0ee8ac903e5aa100a70bef8d0afc19a336ffa775 ] clang warns about a function missing a printf attribute: include/rdma/rdma_vt.h:457:47: error: diagnostic behavior may be improved by adding the 'format(printf, 2, 3)' attribute to the declaration of 'rvt_set_ibdev_name' [-Werror,-Wmissing-format-attribute] 447 | static inline void rvt_set_ibdev_name(struct rvt_dev_info *rdi, | __attribute__((format(printf, 2, 3))) 448 | const char *fmt, const char *name, 449 | const int unit) The helper was originally added as an abstraction for the hfi1 and qib drivers needing the same thing, but now qib is gone, and hfi1 is the only remaining user of rdma_vt. Avoid the warning and allow the compiler to check the format string by open-coding the helper and directly assigning the device name. Fixes: 5084c8ff21f2 ("IB/{rdmavt, hfi1, qib}: Self determine driver name") Link: https://patch.msgid.link/r/20260602140453.3542427-1-arnd@kernel.org Signed-off-by: Arnd Bergmann Reviewed-by: Kees Cook Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/hfi1/init.c | 13 ++++++++++++- include/rdma/rdma_vt.h | 20 -------------------- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c index 8b5a5b32b0fa2..b7fd8b1fbbbde 100644 --- a/drivers/infiniband/hw/hfi1/init.c +++ b/drivers/infiniband/hw/hfi1/init.c @@ -1206,6 +1206,7 @@ static struct hfi1_devdata *hfi1_alloc_devdata(struct pci_dev *pdev, size_t extra) { struct hfi1_devdata *dd; + struct ib_device *ibdev; int ret, nports; /* extra is * number of ports */ @@ -1227,7 +1228,17 @@ static struct hfi1_devdata *hfi1_alloc_devdata(struct pci_dev *pdev, "Could not allocate unit ID: error %d\n", -ret); goto bail; } - rvt_set_ibdev_name(&dd->verbs_dev.rdi, "%s_%d", class_name(), dd->unit); + + /* + * FIXME: rvt and its users want to touch the ibdev before + * registration and have things like the name work. We don't have the + * infrastructure in the core to support this directly today, hack it + * to work by setting the name manually here. + */ + ibdev = &dd->verbs_dev.rdi.ibdev; + dev_set_name(&ibdev->dev, "%s_%d", class_name(), dd->unit); + strscpy(ibdev->name, dev_name(&ibdev->dev), IB_DEVICE_NAME_MAX); + /* * If the BIOS does not have the NUMA node information set, select * NUMA 0 so we get consistent performance. diff --git a/include/rdma/rdma_vt.h b/include/rdma/rdma_vt.h index 7d8de561f71b5..7ffc83262a016 100644 --- a/include/rdma/rdma_vt.h +++ b/include/rdma/rdma_vt.h @@ -438,26 +438,6 @@ struct rvt_dev_info { struct rvt_wss *wss; }; -/** - * rvt_set_ibdev_name - Craft an IB device name from client info - * @rdi: pointer to the client rvt_dev_info structure - * @name: client specific name - * @unit: client specific unit number. - */ -static inline void rvt_set_ibdev_name(struct rvt_dev_info *rdi, - const char *fmt, const char *name, - const int unit) -{ - /* - * FIXME: rvt and its users want to touch the ibdev before - * registration and have things like the name work. We don't have the - * infrastructure in the core to support this directly today, hack it - * to work by setting the name manually here. - */ - dev_set_name(&rdi->ibdev.dev, fmt, name, unit); - strscpy(rdi->ibdev.name, dev_name(&rdi->ibdev.dev), IB_DEVICE_NAME_MAX); -} - /** * rvt_get_ibdev_name - return the IB name * @rdi: rdmavt device From 8c1a8248466d34e35ff4c008c36411615230fdd5 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Tue, 2 Jun 2026 16:37:28 -0300 Subject: [PATCH 1173/2758] IB/cm: Fix av cm device leak on an error path in cm_init_av_by_path() [ Upstream commit 9b2207bc5cdb955bdae34b3eec80f04979e17081 ] Codex pointed out that cm_init_av_by_path() can call cm_set_av_port() which takes a reference on the cm device, but then can immediately return error if ib_init_ah_attr_from_path() fails. Since callers like ib_send_cm_req() put the av on the stack this leaks that cm device reference. Re-order cm_init_av_by_path() so it doesn't touch the av until it has done all its failable work, and then update the av in one shot so it is either left alone or fully init'd. Sashiko also pointed out that the cm_destroy_av() prior to cm_init_av_by_path() is harmful as it leaves the AV broken in the error case and thus the REJ won't send. Since cm_init_av_by_path() is now atomic it is safe to delete the cm_destroy_av(). On succees the av from cm_init_av_for_response() is cleaned up by cm_init_av_by_path(), on failure the 'goto rejected' guarentees the av is destroyed during ib_destroy_cm_id(). Fixes: 76039ac9095f ("IB/cm: Protect cm_dev, cm_ports and mad_agent with kref and lock") Link: https://patch.msgid.link/r/0-v1-38292501f539+14f-ib_cm_av_leak_jgg@nvidia.com Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/core/cm.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c index 6ab9a0aee1ec6..1a2c2775b14d5 100644 --- a/drivers/infiniband/core/cm.c +++ b/drivers/infiniband/core/cm.c @@ -530,6 +530,7 @@ static int cm_init_av_by_path(struct sa_path_rec *path, struct rdma_ah_attr new_ah_attr; struct cm_device *cm_dev; struct cm_port *port; + u16 pkey_index; int ret; port = get_cm_port_from_path(path, sgid_attr); @@ -538,12 +539,10 @@ static int cm_init_av_by_path(struct sa_path_rec *path, cm_dev = port->cm_dev; ret = ib_find_cached_pkey(cm_dev->ib_device, port->port_num, - be16_to_cpu(path->pkey), &av->pkey_index); + be16_to_cpu(path->pkey), &pkey_index); if (ret) return ret; - cm_set_av_port(av, port); - /* * av->ah_attr might be initialized based on wc or during * request processing time which might have reference to sgid_attr. @@ -558,6 +557,8 @@ static int cm_init_av_by_path(struct sa_path_rec *path, if (ret) return ret; + av->pkey_index = pkey_index; + cm_set_av_port(av, port); av->timeout = path->packet_life_time + 1; rdma_move_ah_attr(&av->ah_attr, &new_ah_attr); return 0; @@ -2184,8 +2185,10 @@ static int cm_req_handler(struct cm_work *work) cm_id_priv->av.ah_attr.roce.dmac); work->path[0].hop_limit = grh->hop_limit; - /* This destroy call is needed to pair with cm_init_av_for_response */ - cm_destroy_av(&cm_id_priv->av); + /* + * cm_init_av_by_path() will internally pair with the above + * cm_init_av_for_response() if it succeeds. + */ ret = cm_init_av_by_path(&work->path[0], gid_attr, &cm_id_priv->av); if (ret) { int err; From 9bd249b7564622838dae684b4f17d207aff39d2e Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Fri, 29 May 2026 17:23:59 +0200 Subject: [PATCH 1174/2758] ALSA: hda: fix Kconfig dependency of HD Audio PCI [ Upstream commit 1516134cb65526aba5319bb446c296fc8a192f84 ] With commit 2d9223d2d64c ("ALSA: hda: Move controller drivers into sound/hda/controllers directory") the HD Audio drivers have been moved from linux/sound/pci/hda to linux/sound/hda. But the Kconfig dependency for SND_HDA_INTEL stayed on SND_PCI instead of depending on PCI directly. To make the "HD Audio PCI" configuration entry visible it is currently needed to enable "PCI sound devices" although no PCI device in the submenu needs to be selected. Make SND_HDA_INTEL directly depending on hardware/architecture like the other entries in this Kconfig. Fixes: 2d9223d2d64c ("ALSA: hda: Move controller drivers into sound/hda/controllers directory") Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260529-hda-kconfig-v1-1-4a2c6a0efd56@hartkopp.net Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/hda/controllers/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/hda/controllers/Kconfig b/sound/hda/controllers/Kconfig index 72855f2df4514..5d6a77e68588e 100644 --- a/sound/hda/controllers/Kconfig +++ b/sound/hda/controllers/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config SND_HDA_INTEL tristate "HD Audio PCI" - depends on SND_PCI + depends on PCI select SND_HDA select SND_INTEL_DSP_CONFIG help From d5aa82da8f65562da996d184686db9d0ea718b91 Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Tue, 2 Jun 2026 21:44:23 +0000 Subject: [PATCH 1175/2758] RDMA/irdma: Fix OOB read during CQ MR registration [ Upstream commit 4385ddd654d90245eeb83b3cb539670ab5c85ba4 ] Sashiko pointed out an unrelated bug during a previous patch: https://sashiko.dev/#/patchset/20260512183852.614045-1-jmoroni%40google.com This change fixes the bug by eliminating the cqmr->split field which was not being set properly and instead just checks the CQ resize feature flag directly. The cqmr->split field essentially tracks whether IRDMA_FEATURE_CQ_RESIZE is set, but it was not being set until CQ creation time, which is _after_ CQ memory registration (the only other place where it is referenced). As a result, it would always be false during MR registration and would therefore cause irdma_handle_q_mem to populate cqmr->shadow even for GEN_2 HW and beyond: cqmr->shadow = (dma_addr_t)arr[req->cq_pages]; The issue is that for GEN_2 and beyond, req->cq_pages may be exactly equal to iwmr->page_cnt and therefore equal to the size of arr, which would cause an OOB read by one. Fixes: b48c24c2d710 ("RDMA/irdma: Implement device supported verb APIs") Link: https://patch.msgid.link/r/20260602214423.1315105-2-jmoroni@google.com Signed-off-by: Jacob Moroni Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/irdma/verbs.c | 4 ++-- drivers/infiniband/hw/irdma/verbs.h | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index 661f2e0299ef7..d79e130d98ee2 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -2572,7 +2572,6 @@ static int irdma_create_cq(struct ib_cq *ibcq, } cqmr_shadow = &iwpbl_shadow->cq_mr; info.shadow_area_pa = cqmr_shadow->cq_pbl.addr; - cqmr->split = true; } else { info.shadow_area_pa = cqmr->shadow; } @@ -2980,7 +2979,8 @@ static int irdma_handle_q_mem(struct irdma_device *iwdev, case IRDMA_MEMREG_TYPE_CQ: hmc_p = &cqmr->cq_pbl; - if (!cqmr->split) + if (!(iwdev->rf->sc_dev.hw_attrs.uk_attrs.feature_flags & + IRDMA_FEATURE_CQ_RESIZE)) cqmr->shadow = (dma_addr_t)arr[req->cq_pages]; if (lvl) diff --git a/drivers/infiniband/hw/irdma/verbs.h b/drivers/infiniband/hw/irdma/verbs.h index aabbb3442098b..289ebc9b23ca7 100644 --- a/drivers/infiniband/hw/irdma/verbs.h +++ b/drivers/infiniband/hw/irdma/verbs.h @@ -65,7 +65,6 @@ struct irdma_hmc_pble { struct irdma_cq_mr { struct irdma_hmc_pble cq_pbl; dma_addr_t shadow; - bool split; }; struct irdma_srq_mr { From 98066a34a00a461e8f275d48d53b5341e2ecd94d Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Thu, 4 Jun 2026 15:41:04 +0000 Subject: [PATCH 1176/2758] RDMA/irdma: Initialize iwmr->access during MR registration [ Upstream commit 1521d560b7a1a39e437d37fffd9b55435d329ad1 ] Initialize iwmr->access during initial user mem registration so that it contains a valid value during a subsequent rereg_mr. Otherwise, a rereg_mr that doesn't set IB_MR_REREG_ACCESS (for example, one that only changes the PD) ends up clearing the access flags in HW since iwmr->access is zero-initialized, which is not intended. Fixes: 5ac388db27c4 ("RDMA/irdma: Add support to re-register a memory region") Link: https://patch.msgid.link/r/20260604154104.4035581-1-jmoroni@google.com Signed-off-by: Jacob Moroni Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/irdma/verbs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index d79e130d98ee2..7da7a7e8b30ca 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -3325,6 +3325,7 @@ static int irdma_reg_user_mr_type_mem(struct irdma_mr *iwmr, int access, int err; lvl = iwmr->page_cnt != 1 ? PBLE_LEVEL_1 | PBLE_LEVEL_2 : PBLE_LEVEL_0; + iwmr->access = access; err = irdma_setup_pbles(iwdev->rf, iwmr, lvl); if (err) From 531b3ef6ba5bfa6e1e095f0680a76eb8386e9bfe Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Wed, 13 May 2026 15:25:31 +0200 Subject: [PATCH 1177/2758] arm64: dts: imx8mp-kontron: Reduce EERAM SPI clock frequency [ Upstream commit 95f5bc1632ab8f243e517357b5da0dd28d1c6c92 ] There is an onboard level shifter for the SPI signals that causes additional propagation delay and renders the SPI transmission unreliable at 20 MHz. Reduce the clock frequency to a safe value. Fixes: 946ab10e3f40 ("arm64: dts: Add support for Kontron OSM-S i.MX8MP SoM and BL carrier board") Signed-off-by: Frieder Schrempf Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/freescale/imx8mp-kontron-bl-osm-s.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mp-kontron-bl-osm-s.dts b/arch/arm64/boot/dts/freescale/imx8mp-kontron-bl-osm-s.dts index 0924ac50fd2db..75ae466427821 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-kontron-bl-osm-s.dts +++ b/arch/arm64/boot/dts/freescale/imx8mp-kontron-bl-osm-s.dts @@ -63,7 +63,7 @@ eeram@0 { compatible = "microchip,48l640"; reg = <0>; - spi-max-frequency = <20000000>; + spi-max-frequency = <16000000>; }; }; From a339b9b8fef59a56ef6e69c5f97262f0851f62e1 Mon Sep 17 00:00:00 2001 From: Alice Guo Date: Tue, 19 May 2026 18:55:15 +0800 Subject: [PATCH 1178/2758] arm64: dts: imx94: fix DDR PMU interrupt number [ Upstream commit 2521addd5df7e8ab805622d4a0eff9735a45fa11 ] The DDR Performance Monitor node was added with incorrect interrupt number 91, which actually belongs to the wdog4 watchdog. Fix it to the correct interrupt number 374. Fixes: e918e5f847b3 ("arm64: dts: imx94: add DDR Perf Monitor node") Signed-off-by: Alice Guo Reviewed-by: Xu Yang Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/freescale/imx94.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/freescale/imx94.dtsi b/arch/arm64/boot/dts/freescale/imx94.dtsi index c460ece6070f8..379429b3072ac 100644 --- a/arch/arm64/boot/dts/freescale/imx94.dtsi +++ b/arch/arm64/boot/dts/freescale/imx94.dtsi @@ -1481,7 +1481,7 @@ ddr-pmu@4e090dc0 { compatible = "fsl,imx94-ddr-pmu", "fsl,imx93-ddr-pmu"; reg = <0x0 0x4e090dc0 0x0 0x200>; - interrupts = ; + interrupts = ; }; }; }; From 6a42df755d4880e473bfc97521dcb8fe9f5b4ef4 Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Wed, 20 May 2026 15:22:28 +0800 Subject: [PATCH 1179/2758] arm64: dts: imx95: Correct PCIe outbound address space configuration [ Upstream commit dbecf38e60d4ef08c59836b7bc16e8efca01fc47 ] Fix the PCIe outbound memory ranges for both pcie0 and pcie1 controllers on i.MX95. The memory window size was incorrectly set to 256MB during initial bring-up, but the hardware supports up to 4GB of outbound address space per controller. Additionally, the ECAM region cannot be mapped as I/O space. Use a memory-mapped region for I/O space instead, and relocate the 1MB I/O region to immediately follow the memory region at offset 0xf0000000 within each window. Update the outbound address space layout per controller as follows: - 3.5GB 64-bit prefetchable memory - 256MB 32-bit non-prefetchable memory - 1MB I/O Fixes: 3b1d5deb29ff ("arm64: dts: imx95: add pcie[0,1] and pcie-ep[0,1] support") Signed-off-by: Richard Zhu Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/freescale/imx95.dtsi | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx95.dtsi b/arch/arm64/boot/dts/freescale/imx95.dtsi index 71394871d8dd0..4330296ed8e43 100644 --- a/arch/arm64/boot/dts/freescale/imx95.dtsi +++ b/arch/arm64/boot/dts/freescale/imx95.dtsi @@ -1852,8 +1852,9 @@ <0 0x4c360000 0 0x10000>, <0 0x4c340000 0 0x4000>; reg-names = "dbi", "config", "atu", "app"; - ranges = <0x81000000 0x0 0x00000000 0x0 0x6ff00000 0 0x00100000>, - <0x82000000 0x0 0x10000000 0x9 0x10000000 0 0x10000000>; + ranges = <0x43000000 0x9 0x00000000 0x9 0x00000000 0x0 0xe0000000>, + <0x82000000 0x0 0xe0000000 0x9 0xe0000000 0x0 0x10000000>, + <0x81000000 0x0 0x00000000 0x9 0xf0000000 0x0 0x00100000>; #address-cells = <3>; #size-cells = <2>; device_type = "pci"; @@ -1927,8 +1928,9 @@ <0 0x4c3e0000 0 0x10000>, <0 0x4c3c0000 0 0x4000>; reg-names = "dbi", "config", "atu", "app"; - ranges = <0x81000000 0 0x00000000 0x8 0x8ff00000 0 0x00100000>, - <0x82000000 0 0x10000000 0xa 0x10000000 0 0x10000000>; + ranges = <0x43000000 0xa 0x00000000 0xa 0x00000000 0x0 0xe0000000>, + <0x82000000 0x0 0xe0000000 0xa 0xe0000000 0x0 0x10000000>, + <0x81000000 0x0 0x00000000 0xa 0xf0000000 0x0 0x00100000>; #address-cells = <3>; #size-cells = <2>; device_type = "pci"; From 68f788d842ab8e6bc02b7ed88a22ef665040278e Mon Sep 17 00:00:00 2001 From: Josua Mayer Date: Sun, 24 May 2026 16:54:41 +0200 Subject: [PATCH 1180/2758] arm64: dts: lx2162a-clearfog: use rev2 SoC dtsi [ Upstream commit 13a37b30e464503515625ba891018aec0264c7d2 ] LX2160A and LX2162A are different packages of the same silicon. While LX2160A had two revisions, LX2162A was released later based on LX2160A revision 2. Commit a8fe6c8dfc40 ("arm64: dts: fsl-lx2160a: add rev2 support") has added a new soc dtsi for revision 2. Update LX2162A Clearfog description to use revision 2 dtsi. Fixes: 5093b190f9ce ("arm64: dts: freescale: Add support for LX2162 SoM & Clearfog Board") # no-stable Signed-off-by: Josua Mayer Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/freescale/fsl-lx2162a-clearfog.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/freescale/fsl-lx2162a-clearfog.dts b/arch/arm64/boot/dts/freescale/fsl-lx2162a-clearfog.dts index 9d50d3e2761da..f95e9c19bfc75 100644 --- a/arch/arm64/boot/dts/freescale/fsl-lx2162a-clearfog.dts +++ b/arch/arm64/boot/dts/freescale/fsl-lx2162a-clearfog.dts @@ -6,7 +6,7 @@ /dts-v1/; -#include "fsl-lx2160a.dtsi" +#include "fsl-lx2160a-rev2.dtsi" #include "fsl-lx2162a-sr-som.dtsi" / { From bd75d5a3a2f9eb6b60777df9763a77879db4c861 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Tue, 2 Jun 2026 11:33:30 +0200 Subject: [PATCH 1181/2758] arm64: dts: tqma8mpql-mba8mpxl: configure sai clock in audio codec as well [ Upstream commit 748835fcb2eb2120234aef9556d282272501b96b ] With deferrable card binding the sound card driver tries to get the mclk configuration before it is setup in sai3 node. Fix this by setting the sai clock config for the audio codec as well. Fixes: d8f9d8126582 ("arm64: dts: imx8mp: Add analog audio output on i.MX8MP TQMa8MPxL/MBa8MPxL") Signed-off-by: Alexander Stein Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl.dts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl.dts b/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl.dts index 890d1e525a489..6b0f944095e7c 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl.dts +++ b/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl.dts @@ -609,6 +609,9 @@ reset-gpios = <&gpio4 29 GPIO_ACTIVE_LOW>; iov-supply = <®_vcc_1v8>; ldoin-supply = <®_vcc_3v3>; + assigned-clocks = <&clk IMX8MP_CLK_SAI3>; + assigned-clock-parents = <&clk IMX8MP_AUDIO_PLL1_OUT>; + assigned-clock-rates = <12288000>; }; se97_1c: temperature-sensor@1c { From 4e7a828f32d491f294ef09d6c66c184f3e214b20 Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Thu, 28 May 2026 12:15:56 +0200 Subject: [PATCH 1182/2758] arm64: dts: imx8mp-kontron: Fix GPIO for display power switch [ Upstream commit 5b19ca527471903920d713bc48518a036a95bf6b ] The GPIO that controls the power supply for the LVDS display connector has changed between early prototypes and the current production design of the hardware. Reflect this change in the devicetree to properly switch on the panel supply. This was working before even with the wrong GPIO due to the bidirectional level shifter used on the board which drives the EN signal high even when the input has a (weak) pull down configured as reset condition of the SoC pad. As a result the display was working but the supply was always on. Tested on BL i.MX8MP to show the correct voltage level on the level shifter input. Fixes: 946ab10e3f40 ("arm64: dts: Add support for Kontron OSM-S i.MX8MP SoM and BL carrier board") Signed-off-by: Frieder Schrempf Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- .../boot/dts/freescale/imx8mp-kontron-bl-osm-s.dts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mp-kontron-bl-osm-s.dts b/arch/arm64/boot/dts/freescale/imx8mp-kontron-bl-osm-s.dts index 75ae466427821..29ce863403b88 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-kontron-bl-osm-s.dts +++ b/arch/arm64/boot/dts/freescale/imx8mp-kontron-bl-osm-s.dts @@ -49,7 +49,9 @@ reg_vcc_panel: regulator-vcc-panel { compatible = "regulator-fixed"; - gpio = <&gpio4 3 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_reg_vcc_panel>; + gpio = <&gpio5 3 GPIO_ACTIVE_HIGH>; enable-active-high; regulator-max-microvolt = <3300000>; regulator-min-microvolt = <3300000>; @@ -172,7 +174,7 @@ &gpio5 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_gpio5>; - gpio-line-names = "I2S_BITCLK", "I2S_A_DATA_OUT", "I2S_MCLK", "PWM_2", + gpio-line-names = "I2S_BITCLK", "I2S_A_DATA_OUT", "I2S_MCLK", "VCC_PANEL_EN", "PWM_1", "PWM_0", "SPI_A_SCK", "CAN_ADDR1", "CAN_ADDR0", "SPI_A_CS0", "SPI_B_SCK", "SPI_B_SDO", "SPI_B_SDI", "SPI_B_CS0", "I2C_A_SCL", "I2C_A_SDA", @@ -329,4 +331,10 @@ MX8MP_IOMUXC_ECSPI1_MISO__GPIO5_IO08 0x46 /* CAN_ADR1 */ >; }; + + pinctrl_reg_vcc_panel: regvccpanelgrp { + fsl,pins = < + MX8MP_IOMUXC_SPDIF_TX__GPIO5_IO03 0x46 + >; + }; }; From 482d6ab0c3ef34f5fe66816e00c6f5ea860da229 Mon Sep 17 00:00:00 2001 From: Nora Schiffer Date: Tue, 24 Feb 2026 16:25:23 +0100 Subject: [PATCH 1183/2758] arm64: dts: freescale: fsl-ls1028a-tqmls1028a-mbls1028a: switch mmc aliases [ Upstream commit 08886fa69c0f56f886b63d7835da0632ea4082f7 ] All modern TQ-Systems boards follow the convention that mmc0 is the eMMC and mmc1 is the SD-card when both interfaces exist, reducing differences between boards for both documentation and U-Boot code (which uses the same Device Trees). Adjust the recently added MBLS1028A Device Tree accordingly. Fixes: 0538ca1f102d ("arm64: dts: ls1028a: Add mbls1028a and mbls1028a-ind devicetrees") Signed-off-by: Nora Schiffer Reviewed-by: Alexander Stein Reviewed-by: Frank Li Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- .../boot/dts/freescale/fsl-ls1028a-tqmls1028a-mbls1028a.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1028a-tqmls1028a-mbls1028a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1028a-tqmls1028a-mbls1028a.dtsi index cf338b2e80064..426a81e1743f1 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls1028a-tqmls1028a-mbls1028a.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-ls1028a-tqmls1028a-mbls1028a.dtsi @@ -17,8 +17,8 @@ gpio0 = &gpio1; gpio1 = &gpio2; gpio2 = &gpio3; - mmc0 = &esdhc; /* SD-Card */ - mmc1 = &esdhc1; /* eMMC */ + mmc0 = &esdhc1; /* eMMC */ + mmc1 = &esdhc; /* SD-Card */ serial0 = &duart0; serial1 = &duart1; }; From b6cf763eee0a932792bef64ceaca568d324192fc Mon Sep 17 00:00:00 2001 From: Bernard Metzler Date: Thu, 4 Jun 2026 18:08:08 +0200 Subject: [PATCH 1184/2758] RDMA/siw: Fix endpoint/socket association handling [ Upstream commit ea4f6f6c53577fb3f05dbd78b15e586772d49831 ] Disassociating a socket from an endpoint via siw_socket_disassoc() may release the last reference on that endpoint and free it. Therefore, don't clear the endpoints socket pointer after calling that function, but within. This fixes a: BUG: KASAN: slab-use-after-free in siw_cm_work_handler (drivers/infiniband/sw/siw/siw_cm.c:1053 drivers/infiniband/sw/siw/siw_cm.c:1075) which occurred after processing a malformed MPA request during connection establishment, causing the new endpoint to be closed. Fixes: 6c52fdc244b5c ("rdma/siw: connection management") Link: https://patch.msgid.link/r/20260604160808.30948-1-bernard.metzler@linux.dev Reported-by: Shuangpeng Bai Signed-off-by: Bernard Metzler Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/sw/siw/siw_cm.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/infiniband/sw/siw/siw_cm.c b/drivers/infiniband/sw/siw/siw_cm.c index f7ac81c0f267a..87c79527ac090 100644 --- a/drivers/infiniband/sw/siw/siw_cm.c +++ b/drivers/infiniband/sw/siw/siw_cm.c @@ -138,6 +138,7 @@ static void siw_socket_disassoc(struct socket *s) cep = sk_to_cep(sk); if (cep) { siw_sk_restore_upcalls(sk, cep); + cep->sock = NULL; siw_cep_put(cep); } else { pr_warn("siw: cannot restore sk callbacks: no ep\n"); @@ -418,10 +419,11 @@ static void siw_free_cm_id(struct siw_cep *cep) static void siw_destroy_cep_sock(struct siw_cep *cep) { - if (cep->sock) { - siw_socket_disassoc(cep->sock); - sock_release(cep->sock); - cep->sock = NULL; + struct socket *s = cep->sock; + + if (s) { + siw_socket_disassoc(s); + sock_release(s); } } @@ -1050,7 +1052,6 @@ static void siw_accept_newconn(struct siw_cep *cep) if (new_s) { siw_socket_disassoc(new_s); sock_release(new_s); - new_cep->sock = NULL; } siw_dbg_cep(cep, "error %d\n", rv); } @@ -1202,6 +1203,8 @@ static void siw_cm_work_handler(struct work_struct *w) WARN(1, "Undefined CM work type: %d\n", work->type); } if (release_cep) { + struct socket *s = cep->sock; + siw_dbg_cep(cep, "release: timer=%s, QP[%u]\n", cep->mpa_timer ? "y" : "n", @@ -1227,10 +1230,9 @@ static void siw_cm_work_handler(struct work_struct *w) cep->qp = NULL; siw_qp_put(qp); } - if (cep->sock) { - siw_socket_disassoc(cep->sock); - sock_release(cep->sock); - cep->sock = NULL; + if (s) { + siw_socket_disassoc(s); + sock_release(s); } if (cep->cm_id) { siw_free_cm_id(cep); @@ -1561,7 +1563,6 @@ int siw_connect(struct iw_cm_id *id, struct iw_cm_conn_param *params) if (cep) { siw_socket_disassoc(s); sock_release(s); - cep->sock = NULL; cep->qp = NULL; @@ -1937,7 +1938,6 @@ int siw_create_listen(struct iw_cm_id *id, int backlog) siw_cep_set_inuse(cep); siw_free_cm_id(cep); - cep->sock = NULL; siw_socket_disassoc(s); cep->state = SIW_EPSTATE_CLOSED; @@ -1959,6 +1959,7 @@ static void siw_drop_listeners(struct iw_cm_id *id) */ list_for_each_safe(p, tmp, (struct list_head *)id->provider_data) { struct siw_cep *cep = list_entry(p, struct siw_cep, listenq); + struct socket *s = cep->sock; list_del(p); @@ -1967,10 +1968,9 @@ static void siw_drop_listeners(struct iw_cm_id *id) siw_cep_set_inuse(cep); siw_free_cm_id(cep); - if (cep->sock) { - siw_socket_disassoc(cep->sock); - sock_release(cep->sock); - cep->sock = NULL; + if (s) { + siw_socket_disassoc(s); + sock_release(s); } cep->state = SIW_EPSTATE_CLOSED; siw_cep_set_free_and_put(cep); From 865d18a9697792476e83474f7de82084a1db82ce Mon Sep 17 00:00:00 2001 From: Mykyta Yatsenko Date: Wed, 3 Jun 2026 07:39:15 -0700 Subject: [PATCH 1185/2758] selftests/bpf: Fix flaky file_reader test [ Upstream commit aa22d619ba22177f430693cf5e9495052d996644 ] file_reader/on_open_expect_fault test expects page fault when reading pages from the test harness executable. It is not guaranteed that those are paged out, even after madvise(MADV_PAGEOUT). Relax the condition in the test to succeed with both 0 and -EFAULT returned. Fixes: 784cdf931543 ("selftests/bpf: add file dynptr tests") Reported-by: Shung-Hsi Yu Closes: https://lore.kernel.org/all/ah6g7JSYOWGp2oAG@u94a/ Signed-off-by: Mykyta Yatsenko Tested-by: Ihor Solodrai Link: https://lore.kernel.org/r/20260603-file_reader_flake-v1-1-7f3f52d1e388@meta.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- tools/testing/selftests/bpf/progs/file_reader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/progs/file_reader.c b/tools/testing/selftests/bpf/progs/file_reader.c index 462712ff3b8a0..aa2c05cce2b30 100644 --- a/tools/testing/selftests/bpf/progs/file_reader.c +++ b/tools/testing/selftests/bpf/progs/file_reader.c @@ -50,7 +50,7 @@ int on_open_expect_fault(void *c) goto out; local_err = bpf_dynptr_read(tmp_buf, user_buf_sz, &dynptr, user_buf_sz, 0); - if (local_err == -EFAULT) { /* Expect page fault */ + if (local_err == -EFAULT || local_err == 0) { /* Expect page fault or success */ local_err = 0; run_success = 1; } From 574612793bed416f6c05fe7c9b50e9eb0441997e Mon Sep 17 00:00:00 2001 From: Kaitao Cheng Date: Fri, 5 Jun 2026 17:41:43 +0800 Subject: [PATCH 1186/2758] bpf: Clear rb node linkage when freeing bpf_rb_root [ Upstream commit 4a7910ee060d8ce55612f5b3cc267f3a265a3cec ] bpf_rb_root_free() detaches the root by copying the current rb_root_cached and then replacing the live root with RB_ROOT_CACHED. It then walks the copied root and drops each object contained in the tree. This leaves the rb node state intact while dropping the object. If the object is refcounted and survives the drop, its bpf_rb_node_kern still contains an owner pointer to the freed root and stale rb tree linkage. If a later bpf_rb_root allocation reuses the same address, bpf_rbtree_remove() can incorrectly pass the owner check and call rb_erase_cached() on a node whose rb pointers belong to the old tree. Mirror the list draining behavior by marking nodes as busy while the root is being detached, then clear the rb node and release the owner before dropping the containing object. This makes surviving nodes unowned and safe to reject from remove or accept for a later add. Fixes: 9c395c1b99bd ("bpf: Add basic bpf_rb_{root,node} support") Signed-off-by: Kaitao Cheng Acked-by: Yonghong Song Link: https://lore.kernel.org/r/20260605094143.5509-1-kaitao.cheng@linux.dev Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- kernel/bpf/helpers.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index b5314c9fed3cf..5c1e5c8812b12 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -2295,6 +2295,7 @@ void bpf_rb_root_free(const struct btf_field *field, void *rb_root, struct bpf_spin_lock *spin_lock) { struct rb_root_cached orig_root, *root = rb_root; + struct bpf_rb_node_kern *node; struct rb_node *pos, *n; void *obj; @@ -2303,14 +2304,20 @@ void bpf_rb_root_free(const struct btf_field *field, void *rb_root, __bpf_spin_lock_irqsave(spin_lock); orig_root = *root; + bpf_rbtree_postorder_for_each_entry_safe(pos, n, &orig_root.rb_root) { + node = rb_entry(pos, struct bpf_rb_node_kern, rb_node); + WRITE_ONCE(node->owner, BPF_PTR_POISON); + } *root = RB_ROOT_CACHED; __bpf_spin_unlock_irqrestore(spin_lock); bpf_rbtree_postorder_for_each_entry_safe(pos, n, &orig_root.rb_root) { obj = pos; obj -= field->graph_root.node_offset; - - + node = rb_entry(pos, struct bpf_rb_node_kern, rb_node); + RB_CLEAR_NODE(pos); + /* Ensure __bpf_rbtree_add() sees the node as unlinked. */ + smp_store_release(&node->owner, NULL); __bpf_obj_drop_impl(obj, field->graph_root.value_rec, false); } } From 41d82064c43e3db0756ae3a9d4c7315b36a34568 Mon Sep 17 00:00:00 2001 From: Leon Hwang Date: Fri, 5 Jun 2026 23:52:47 +0800 Subject: [PATCH 1187/2758] bpf: Check tail zero of bpf_map_info [ Upstream commit e2a49fdb1beed150125b4104c90eb2a96ec7f63a ] Since there're 4 bytes padding at the end of struct bpf_map_info, they won't be checked by bpf_check_uarg_tail_zero(). pahole -C bpf_map_info ./vmlinux struct bpf_map_info { ... __u64 hash __attribute__((__aligned__(8))); /* 88 8 */ __u32 hash_size; /* 96 4 */ /* size: 104, cachelines: 2, members: 18 */ /* padding: 4 */ /* forced alignments: 1 */ /* last cacheline: 40 bytes */ } __attribute__((__aligned__(8))); If a future kernel extension adds a new 4-byte field, older userspace programs allocating this structure on the stack might inadvertently pass uninitialized stack garbage into the new field, permanently breaking backward compatibility. -- sashiko [1] Fix it by changing sizeof(info) to offsetofend(struct bpf_map_info, hash_size). And, add "__u32 :32" to the tail of struct bpf_map_info. [1] https://lore.kernel.org/bpf/20260513224823.6494FC19425@smtp.kernel.org/ Fixes: ea2e6467ac36 ("bpf: Return hashes of maps in BPF_OBJ_GET_INFO_BY_FD") Acked-by: Mykyta Yatsenko Signed-off-by: Leon Hwang Link: https://lore.kernel.org/r/20260605155249.20772-2-leon.hwang@linux.dev Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- include/uapi/linux/bpf.h | 1 + kernel/bpf/syscall.c | 5 +++-- tools/include/uapi/linux/bpf.h | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 552bc5d9afbd1..93778d60c760e 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -6719,6 +6719,7 @@ struct bpf_map_info { __u64 map_extra; __aligned_u64 hash; __u32 hash_size; + __u32 :32; } __attribute__((aligned(8))); struct bpf_btf_info { diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 2edda1844ec7f..0be81c6437c24 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -5337,10 +5337,11 @@ static int bpf_map_get_info_by_fd(struct file *file, { struct bpf_map_info __user *uinfo = u64_to_user_ptr(attr->info.info); struct bpf_map_info info; - u32 info_len = attr->info.info_len; + u32 info_len = attr->info.info_len, len; int err; - err = bpf_check_uarg_tail_zero(USER_BPFPTR(uinfo), sizeof(info), info_len); + len = offsetofend(struct bpf_map_info, hash_size); + err = bpf_check_uarg_tail_zero(USER_BPFPTR(uinfo), len, info_len); if (err) return err; info_len = min_t(u32, sizeof(info), info_len); diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 677be9a473476..4b3fecb01852e 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -6719,6 +6719,7 @@ struct bpf_map_info { __u64 map_extra; __aligned_u64 hash; __u32 hash_size; + __u32 :32; } __attribute__((aligned(8))); struct bpf_btf_info { From 068532141d1740ec5e9da518fb8dc34b44a44bb1 Mon Sep 17 00:00:00 2001 From: Leon Hwang Date: Fri, 5 Jun 2026 23:52:48 +0800 Subject: [PATCH 1188/2758] bpf: Check tail zero of bpf_prog_info [ Upstream commit 786be2b05980a5828e67fc564ad7517e2adbe9bd ] Since there're 4 bytes padding at the end of struct bpf_prog_info, they won't be checked by bpf_check_uarg_tail_zero(). pahole -C bpf_prog_info ./vmlinux struct bpf_prog_info { ... __u32 attach_btf_obj_id; /* 220 4 */ __u32 attach_btf_id; /* 224 4 */ /* size: 232, cachelines: 4, members: 38 */ /* sum members: 224 */ /* sum bitfield members: 1 bits, bit holes: 1, sum bit holes: 31 bits */ /* padding: 4 */ /* forced alignments: 9 */ /* last cacheline: 40 bytes */ } __attribute__((__aligned__(8))); If a future kernel extension adds a new 4-byte field, older userspace programs allocating this structure on the stack might inadvertently pass uninitialized stack garbage into the new field, permanently breaking backward compatibility. -- sashiko [1] Fix it by changing sizeof(info) to offsetofend(struct bpf_prog_info, attach_btf_id). And, add "__u32 :32" to the tail of struct bpf_prog_info. [1] https://lore.kernel.org/bpf/20260513224823.6494FC19425@smtp.kernel.org/ Fixes: aba64c7da983 ("bpf: Add verified_insns to bpf_prog_info and fdinfo") Acked-by: Mykyta Yatsenko Signed-off-by: Leon Hwang Link: https://lore.kernel.org/r/20260605155249.20772-3-leon.hwang@linux.dev Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- include/uapi/linux/bpf.h | 1 + kernel/bpf/syscall.c | 5 +++-- tools/include/uapi/linux/bpf.h | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 93778d60c760e..daf7a1d3c1857 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -6698,6 +6698,7 @@ struct bpf_prog_info { __u32 verified_insns; __u32 attach_btf_obj_id; __u32 attach_btf_id; + __u32 :32; } __attribute__((aligned(8))); struct bpf_map_info { diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 0be81c6437c24..bd1250a6c1e1f 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -5052,10 +5052,11 @@ static int bpf_prog_get_info_by_fd(struct file *file, u32 info_len = attr->info.info_len; struct bpf_prog_kstats stats; char __user *uinsns; - u32 ulen; + u32 ulen, len; int err; - err = bpf_check_uarg_tail_zero(USER_BPFPTR(uinfo), sizeof(info), info_len); + len = offsetofend(struct bpf_prog_info, attach_btf_id); + err = bpf_check_uarg_tail_zero(USER_BPFPTR(uinfo), len, info_len); if (err) return err; info_len = min_t(u32, sizeof(info), info_len); diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 4b3fecb01852e..9bbf35d5f141f 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -6698,6 +6698,7 @@ struct bpf_prog_info { __u32 verified_insns; __u32 attach_btf_obj_id; __u32 attach_btf_id; + __u32 :32; } __attribute__((aligned(8))); struct bpf_map_info { From d9bcfadd0a3b71fa21f14b413d00c1840788203a Mon Sep 17 00:00:00 2001 From: Leon Hwang Date: Tue, 2 Jun 2026 23:09:30 +0800 Subject: [PATCH 1189/2758] bpf: Update transport_header when encapsulating UDP tunnel in lwt [ Upstream commit 82d7d0adbc678064543e9d254864f6b4ea4a388c ] Currently, bpf_lwt_push_ip_encap() does not update skb->transport_header. When a driver, e.g. ice, reuses the stale skb->transport_header to offload checksum computation to NIC hardware, VxLAN packets encapsulated by bpf_lwt_push_encap() helper may be dropped due to incorrect checksum. Update skb->transport_header in bpf_lwt_push_ip_encap() whenever the encapsulated packet uses UDP, so checksum offload works correctly. Fixes: 52f278774e79 ("bpf: implement BPF_LWT_ENCAP_IP mode in bpf_lwt_push_encap") Cc: Leon Hwang Signed-off-by: Leon Hwang Link: https://lore.kernel.org/r/20260602150931.49629-2-leon.hwang@linux.dev Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- net/core/lwt_bpf.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/net/core/lwt_bpf.c b/net/core/lwt_bpf.c index f71ef82a5f3d3..bf588f508b79e 100644 --- a/net/core/lwt_bpf.c +++ b/net/core/lwt_bpf.c @@ -599,6 +599,7 @@ static int handle_gso_encap(struct sk_buff *skb, bool ipv4, int encap_len) int bpf_lwt_push_ip_encap(struct sk_buff *skb, void *hdr, u32 len, bool ingress) { + bool is_udp_tunnel; struct iphdr *iph; bool ipv4; int err; @@ -612,10 +613,16 @@ int bpf_lwt_push_ip_encap(struct sk_buff *skb, void *hdr, u32 len, bool ingress) ipv4 = true; if (unlikely(len < iph->ihl * 4)) return -EINVAL; + is_udp_tunnel = iph->protocol == IPPROTO_UDP; + if (unlikely(is_udp_tunnel && len < iph->ihl * 4 + sizeof(struct udphdr))) + return -EINVAL; } else if (iph->version == 6) { ipv4 = false; if (unlikely(len < sizeof(struct ipv6hdr))) return -EINVAL; + is_udp_tunnel = ((struct ipv6hdr *)iph)->nexthdr == NEXTHDR_UDP; + if (unlikely(is_udp_tunnel && len < sizeof(struct ipv6hdr) + sizeof(struct udphdr))) + return -EINVAL; } else { return -EINVAL; } @@ -637,6 +644,11 @@ int bpf_lwt_push_ip_encap(struct sk_buff *skb, void *hdr, u32 len, bool ingress) if (ingress) skb_postpush_rcsum(skb, iph, len); skb_reset_network_header(skb); + if (is_udp_tunnel) { + size_t iph_sz = ipv4 ? iph->ihl * 4 : sizeof(struct ipv6hdr); + + skb_set_transport_header(skb, skb_network_offset(skb) + iph_sz); + } memcpy(skb_network_header(skb), hdr, len); bpf_compute_data_pointers(skb); skb_clear_hash(skb); From bf53db51359939755084d08156684ed649489592 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 5 Jun 2026 15:53:16 +0200 Subject: [PATCH 1190/2758] kernfs: fix xattr race condition with multiple superblocks [ Upstream commit 6a07814ff643b5c8e1353d8c6229f52fde205cde ] Multiple superblocks with different namespaces can share the same kernfs_node when kernfs_test_super() finds a matching root but different namespace. This means multiple inodes from different superblocks can reference the same kernfs_node->iattr->xattrs structure. The VFS layer only holds per-inode locks during xattr operations, which is insufficient to serialize concurrent xattr modifications on the shared kernfs_node. This can lead to race conditions in simple_xattr_set() where the lookup->replace/remove sequence is not atomic with respect to operations from other superblocks. Fix this by protecting xattr operations with the existing hashed kernfs_locks->open_file_mutex[] array, which is already used to protect per-node open file data. The hashed mutex array provides scalable per-node serialization (scaled by CPU count, up to 1024 locks on 32+ CPU systems) with zero memory overhead. Changes: - Rename open_file_mutex[] to node_mutex[] to reflect dual purpose - Add kernfs_node_lock_ptr() and kernfs_node_lock() helpers - Protect simple_xattr_set() calls in kernfs_xattr_set() and kernfs_vfs_user_xattr_set() with the hashed mutex - Update file.c to use new helpers via compatibility wrappers - Update documentation to explain the extended lock usage Fixes: b32c4a213698 ("xattr: add rhashtable-based simple_xattr infrastructure") Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260601162454.2116375-1-mszeredi%40redhat.com Assisted-by: Claude:claude-sonnet-4-5 Signed-off-by: Miklos Szeredi Link: https://patch.msgid.link/20260605135322.2632068-2-mszeredi@redhat.com Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/kernfs/file.c | 13 +++---------- fs/kernfs/inode.c | 12 ++++++++++++ fs/kernfs/kernfs-internal.h | 20 ++++++++++++++++++++ fs/kernfs/mount.c | 2 +- include/linux/kernfs.h | 11 ++++++++--- 5 files changed, 44 insertions(+), 14 deletions(-) diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index 1163aa7697384..8e0e90c933720 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -40,22 +40,15 @@ struct kernfs_open_node { static DEFINE_SPINLOCK(kernfs_notify_lock); static struct kernfs_node *kernfs_notify_list = KERNFS_NOTIFY_EOL; +/* Compatibility wrappers - use the common hashed node lock */ static inline struct mutex *kernfs_open_file_mutex_ptr(struct kernfs_node *kn) { - int idx = hash_ptr(kn, NR_KERNFS_LOCK_BITS); - - return &kernfs_locks->open_file_mutex[idx]; + return kernfs_node_lock_ptr(kn); } static inline struct mutex *kernfs_open_file_mutex_lock(struct kernfs_node *kn) { - struct mutex *lock; - - lock = kernfs_open_file_mutex_ptr(kn); - - mutex_lock(lock); - - return lock; + return kernfs_node_lock(kn); } /** diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c index 38b28aa7cd023..e676737d9531a 100644 --- a/fs/kernfs/inode.c +++ b/fs/kernfs/inode.c @@ -320,6 +320,15 @@ int kernfs_xattr_set(struct kernfs_node *kn, const char *name, if (!attrs) return -ENOMEM; + /* + * Protect xattr modifications with the hashed per-node mutex. + * Multiple superblocks (with different namespaces) can share the same + * kernfs_node, so inode locking alone is insufficient. The hashed mutex + * ensures serialization of concurrent xattr operations on the same node, + * including the lazy allocation of the xattrs structure itself. + */ + CLASS(kernfs_node_lock, lock)(kn); + xattrs = simple_xattrs_lazy_alloc(&attrs->xattrs, value, flags); if (IS_ERR_OR_NULL(xattrs)) return PTR_ERR(xattrs); @@ -372,6 +381,9 @@ static int kernfs_vfs_user_xattr_set(const struct xattr_handler *handler, if (!attrs) return -ENOMEM; + /* See comment in kernfs_xattr_set() about locking. */ + CLASS(kernfs_node_lock, lock)(kn); + xattrs = simple_xattrs_lazy_alloc(&attrs->xattrs, value, flags); if (IS_ERR_OR_NULL(xattrs)) return PTR_ERR(xattrs); diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index 8d8912f50b054..1dc6663553d1a 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -211,4 +211,24 @@ extern const struct inode_operations kernfs_symlink_iops; * kernfs locks */ extern struct kernfs_global_locks *kernfs_locks; + +/* Hashed mutex helpers - protect per-node data structures */ +static inline struct mutex *kernfs_node_lock_ptr(struct kernfs_node *kn) +{ + int idx = hash_ptr(kn, NR_KERNFS_LOCK_BITS); + + return &kernfs_locks->node_mutex[idx]; +} + +static inline struct mutex *kernfs_node_lock(struct kernfs_node *kn) +{ + struct mutex *lock = kernfs_node_lock_ptr(kn); + + mutex_lock(lock); + return lock; +} + +DEFINE_CLASS(kernfs_node_lock, struct mutex *, + mutex_unlock(_T), kernfs_node_lock(kn), struct kernfs_node *kn) + #endif /* __KERNFS_INTERNAL_H */ diff --git a/fs/kernfs/mount.c b/fs/kernfs/mount.c index 6e3217b6e4811..f183a96778b9a 100644 --- a/fs/kernfs/mount.c +++ b/fs/kernfs/mount.c @@ -446,7 +446,7 @@ static void __init kernfs_mutex_init(void) int count; for (count = 0; count < NR_KERNFS_LOCKS; count++) - mutex_init(&kernfs_locks->open_file_mutex[count]); + mutex_init(&kernfs_locks->node_mutex[count]); } static void __init kernfs_lock_init(void) diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index e21b2f7f4159f..351a5101c8628 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -76,20 +76,25 @@ struct kernfs_iattrs; * kernfs_open_file. * * kernfs_open_files are chained at kernfs_open_node->files, which is - * protected by kernfs_global_locks.open_file_mutex[i]. + * protected by kernfs_global_locks.node_mutex[i]. * * To reduce possible contention in sysfs access, arising due to single - * locks, use an array of locks (e.g. open_file_mutex) and use kernfs_node + * locks, use an array of locks (e.g. node_mutex) and use kernfs_node * object address as hash keys to get the index of these locks. * * Hashed mutexes are safe to use here because operations using these don't * rely on global exclusion. * + * The hashed mutex array protects per-node data: the kernfs_open_node for + * open file management, and kernfs_node xattr operations (necessary because + * multiple superblocks with different namespaces can share the same + * kernfs_node, making per-inode locking insufficient). + * * In future we intend to replace other global locks with hashed ones as well. * kernfs_global_locks acts as a holder for all such hash tables. */ struct kernfs_global_locks { - struct mutex open_file_mutex[NR_KERNFS_LOCKS]; + struct mutex node_mutex[NR_KERNFS_LOCKS]; }; enum kernfs_node_type { From 23f9cb9762127033456c09edd02eeaa008d2be26 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 5 Jun 2026 15:53:17 +0200 Subject: [PATCH 1191/2758] tmpfs: simplify constructing "security.foo" xattr names [ Upstream commit 832f4de4c8ba4f19e5df1d016a09917204b17834 ] Use kasprintf() instead of doing it with kmalloc() + 2 x memcpy(). Signed-off-by: Miklos Szeredi Link: https://patch.msgid.link/20260605135322.2632068-3-mszeredi@redhat.com Tested-by: Calum Mackay Signed-off-by: Christian Brauner (Amutable) Stable-dep-of: 1e7cd8a53b72 ("simpe_xattr: use per-sb cache") Signed-off-by: Sasha Levin --- mm/shmem.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/mm/shmem.c b/mm/shmem.c index 3b5dc21b323c2..c7897570fc9f9 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -4232,7 +4232,6 @@ static int shmem_initxattrs(struct inode *inode, struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb); const struct xattr *xattr; size_t ispace = 0; - size_t len; CLASS(simple_xattrs, xattrs)(); if (IS_ERR(xattrs)) @@ -4260,17 +4259,11 @@ static int shmem_initxattrs(struct inode *inode, if (IS_ERR(new_xattr)) break; - len = strlen(xattr->name) + 1; - new_xattr->name = kmalloc(XATTR_SECURITY_PREFIX_LEN + len, - GFP_KERNEL_ACCOUNT); + new_xattr->name = kasprintf(GFP_KERNEL_ACCOUNT, + XATTR_SECURITY_PREFIX "%s", xattr->name); if (!new_xattr->name) break; - memcpy(new_xattr->name, XATTR_SECURITY_PREFIX, - XATTR_SECURITY_PREFIX_LEN); - memcpy(new_xattr->name + XATTR_SECURITY_PREFIX_LEN, - xattr->name, len); - if (simple_xattr_add(xattrs, new_xattr)) break; retain_and_null_ptr(new_xattr); From deb90fdafd52dfd96161175faba46c3e49d20807 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 5 Jun 2026 15:53:18 +0200 Subject: [PATCH 1192/2758] simple_xattr: change interface to pass struct simple_xattrs ** [ Upstream commit 076e5cef28e27febfc09b5f72544d2b857c75201 ] Change the simple_xattr API to accept pointer-to-pointer (struct simple_xattrs **) instead of pointer. This allows the functions to handle lazy allocation internally without requiring callers to use simple_xattrs_lazy_alloc(). The simple_xattr_set(), simple_xattr_set_limited() and simple_xattr_add() functions now handle allocation when xattrs is NULL. simple_xattrs_free() now also frees the xattrs structure itself and sets the pointer to NULL. This simplifies callers and removes the need for most callers to explicitly manage xattrs allocation and lifetime. In shmem_initxattrs(), the total required space for all initial xattrs (ispace) is pre-calculated and deducted from sbinfo->free_ispace. Since this patch modifies the function to add new xattrs directly to the inode's &info->xattrs list rather than using a local temporary variable, a failure means that the partially populated info->xattrs list remains attached to the inode. When the VFS caller handles the -ENOMEM error, it drops the newly created inode via iput(), shmem_free_inode() adds freed to sbinfo->free_ispace a second time, permanently inflating the tmpfs free space quota. Fix by substracting already added xattrs from ispace. Signed-off-by: Miklos Szeredi Link: https://patch.msgid.link/20260605135322.2632068-4-mszeredi@redhat.com Signed-off-by: Christian Brauner (Amutable) Stable-dep-of: 1e7cd8a53b72 ("simpe_xattr: use per-sb cache") Signed-off-by: Sasha Levin --- fs/kernfs/dir.c | 12 +++-------- fs/kernfs/inode.c | 24 ++++------------------ fs/pidfs.c | 37 ++++++---------------------------- fs/xattr.c | 46 ++++++++++++++++++++++++++++++++++--------- include/linux/xattr.h | 21 ++++++-------------- mm/shmem.c | 34 ++++++++++---------------------- net/socket.c | 24 +++++----------------- 7 files changed, 71 insertions(+), 127 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index e88b71607f1e5..cf6fc2bfa5802 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -608,11 +608,8 @@ void kernfs_put(struct kernfs_node *kn) if (kernfs_type(kn) == KERNFS_LINK) kernfs_put(kn->symlink.target_kn); - if (kn->iattr && kn->iattr->xattrs) { - simple_xattrs_free(kn->iattr->xattrs, NULL); - kfree(kn->iattr->xattrs); - kn->iattr->xattrs = NULL; - } + if (kn->iattr) + simple_xattrs_free(&kn->iattr->xattrs, NULL); spin_lock(&root->kernfs_idr_lock); idr_remove(&root->ino_idr, (u32)kernfs_ino(kn)); @@ -712,10 +709,7 @@ static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root, err_out4: if (kn->iattr) { - if (kn->iattr->xattrs) { - simple_xattrs_free(kn->iattr->xattrs, NULL); - kfree(kn->iattr->xattrs); - } + simple_xattrs_free(&kn->iattr->xattrs, NULL); kmem_cache_free(kernfs_iattrs_cache, kn->iattr); } err_out3: diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c index e676737d9531a..f2298de6bc6f8 100644 --- a/fs/kernfs/inode.c +++ b/fs/kernfs/inode.c @@ -144,8 +144,7 @@ ssize_t kernfs_iop_listxattr(struct dentry *dentry, char *buf, size_t size) if (!attrs) return -ENOMEM; - return simple_xattr_list(d_inode(dentry), READ_ONCE(attrs->xattrs), - buf, size); + return simple_xattr_list(d_inode(dentry), &attrs->xattrs, buf, size); } static inline void set_default_inode_attr(struct inode *inode, umode_t mode) @@ -297,23 +296,17 @@ int kernfs_xattr_get(struct kernfs_node *kn, const char *name, void *value, size_t size) { struct kernfs_iattrs *attrs = kernfs_iattrs_noalloc(kn); - struct simple_xattrs *xattrs; if (!attrs) return -ENODATA; - xattrs = READ_ONCE(attrs->xattrs); - if (!xattrs) - return -ENODATA; - - return simple_xattr_get(xattrs, name, value, size); + return simple_xattr_get(&attrs->xattrs, name, value, size); } int kernfs_xattr_set(struct kernfs_node *kn, const char *name, const void *value, size_t size, int flags) { struct simple_xattr *old_xattr; - struct simple_xattrs *xattrs; struct kernfs_iattrs *attrs; attrs = kernfs_iattrs(kn); @@ -329,11 +322,7 @@ int kernfs_xattr_set(struct kernfs_node *kn, const char *name, */ CLASS(kernfs_node_lock, lock)(kn); - xattrs = simple_xattrs_lazy_alloc(&attrs->xattrs, value, flags); - if (IS_ERR_OR_NULL(xattrs)) - return PTR_ERR(xattrs); - - old_xattr = simple_xattr_set(xattrs, name, value, size, flags); + old_xattr = simple_xattr_set(&attrs->xattrs, name, value, size, flags); if (IS_ERR(old_xattr)) return PTR_ERR(old_xattr); @@ -371,7 +360,6 @@ static int kernfs_vfs_user_xattr_set(const struct xattr_handler *handler, { const char *full_name = xattr_full_name(handler, suffix); struct kernfs_node *kn = inode->i_private; - struct simple_xattrs *xattrs; struct kernfs_iattrs *attrs; if (!(kernfs_root(kn)->flags & KERNFS_ROOT_SUPPORT_USER_XATTR)) @@ -384,11 +372,7 @@ static int kernfs_vfs_user_xattr_set(const struct xattr_handler *handler, /* See comment in kernfs_xattr_set() about locking. */ CLASS(kernfs_node_lock, lock)(kn); - xattrs = simple_xattrs_lazy_alloc(&attrs->xattrs, value, flags); - if (IS_ERR_OR_NULL(xattrs)) - return PTR_ERR(xattrs); - - return simple_xattr_set_limited(xattrs, &attrs->xattr_limits, + return simple_xattr_set_limited(&attrs->xattrs, &attrs->xattr_limits, full_name, value, size, flags); } diff --git a/fs/pidfs.c b/fs/pidfs.c index 1cce4f34a0512..eb5105bddecaf 100644 --- a/fs/pidfs.c +++ b/fs/pidfs.c @@ -196,12 +196,7 @@ static void pidfs_free_attr_work(struct work_struct *work) head = llist_del_all(&pidfs_free_list); llist_for_each_entry_safe(attr, next, head, pidfs_llist) { - struct simple_xattrs *xattrs = attr->xattrs; - - if (xattrs) { - simple_xattrs_free(xattrs, NULL); - kfree(xattrs); - } + simple_xattrs_free(&attr->xattrs, NULL); kfree(attr); } } @@ -815,14 +810,8 @@ static ssize_t pidfs_listxattr(struct dentry *dentry, char *buf, size_t size) { struct inode *inode = d_inode(dentry); struct pid *pid = inode->i_private; - struct pidfs_attr *attr = pid->attr; - struct simple_xattrs *xattrs; - - xattrs = READ_ONCE(attr->xattrs); - if (!xattrs) - return 0; - return simple_xattr_list(inode, xattrs, buf, size); + return simple_xattr_list(inode, &pid->attr->xattrs, buf, size); } static const struct inode_operations pidfs_inode_operations = { @@ -1057,16 +1046,9 @@ static int pidfs_xattr_get(const struct xattr_handler *handler, const char *suffix, void *value, size_t size) { struct pid *pid = inode->i_private; - struct pidfs_attr *attr = pid->attr; - const char *name; - struct simple_xattrs *xattrs; - - xattrs = READ_ONCE(attr->xattrs); - if (!xattrs) - return -ENODATA; + const char *name = xattr_full_name(handler, suffix); - name = xattr_full_name(handler, suffix); - return simple_xattr_get(xattrs, name, value, size); + return simple_xattr_get(&pid->attr->xattrs, name, value, size); } static int pidfs_xattr_set(const struct xattr_handler *handler, @@ -1075,20 +1057,13 @@ static int pidfs_xattr_set(const struct xattr_handler *handler, const void *value, size_t size, int flags) { struct pid *pid = inode->i_private; - struct pidfs_attr *attr = pid->attr; - const char *name; - struct simple_xattrs *xattrs; + const char *name = xattr_full_name(handler, suffix); struct simple_xattr *old_xattr; /* Ensure we're the only one to set @attr->xattrs. */ WARN_ON_ONCE(!inode_is_locked(inode)); - xattrs = simple_xattrs_lazy_alloc(&attr->xattrs, value, flags); - if (IS_ERR_OR_NULL(xattrs)) - return PTR_ERR(xattrs); - - name = xattr_full_name(handler, suffix); - old_xattr = simple_xattr_set(xattrs, name, value, size, flags); + old_xattr = simple_xattr_set(&pid->attr->xattrs, name, value, size, flags); if (IS_ERR(old_xattr)) return PTR_ERR(old_xattr); diff --git a/fs/xattr.c b/fs/xattr.c index 09ecbaaa16608..9ef7ad8a8f321 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -1311,12 +1311,17 @@ static const struct rhashtable_params simple_xattr_params = { * Return: On success the length of the xattr value is returned. On error a * negative error code is returned. */ -int simple_xattr_get(struct simple_xattrs *xattrs, const char *name, +int simple_xattr_get(struct simple_xattrs **xattrsp, const char *name, void *buffer, size_t size) { + struct simple_xattrs *xattrs; struct simple_xattr *xattr; int ret = -ENODATA; + xattrs = READ_ONCE(*xattrsp); + if (!xattrs) + return -ENODATA; + guard(rcu)(); xattr = rhashtable_lookup(&xattrs->ht, name, simple_xattr_params); if (xattr) { @@ -1331,6 +1336,9 @@ int simple_xattr_get(struct simple_xattrs *xattrs, const char *name, return ret; } +static struct simple_xattrs *simple_xattrs_lazy_alloc(struct simple_xattrs **xattrsp, + const void *value, int flags); + /** * simple_xattr_set - set an xattr object * @xattrs: the header of the xattr object @@ -1362,13 +1370,18 @@ int simple_xattr_get(struct simple_xattrs *xattrs, const char *name, * Return: On success, the removed or replaced xattr is returned, to be freed * by the caller; or NULL if none. On failure a negative error code is returned. */ -struct simple_xattr *simple_xattr_set(struct simple_xattrs *xattrs, +struct simple_xattr *simple_xattr_set(struct simple_xattrs **xattrsp, const char *name, const void *value, size_t size, int flags) { + struct simple_xattrs *xattrs; struct simple_xattr *old_xattr = NULL; int err; + xattrs = simple_xattrs_lazy_alloc(xattrsp, value, flags); + if (IS_ERR_OR_NULL(xattrs)) + return ERR_CAST(xattrs); + CLASS(simple_xattr, new_xattr)(value, size); if (IS_ERR(new_xattr)) return new_xattr; @@ -1467,7 +1480,7 @@ static inline int simple_xattr_limits_inc(struct simple_xattr_limits *limits, * Return: On success zero is returned. On failure a negative error code is * returned. */ -int simple_xattr_set_limited(struct simple_xattrs *xattrs, +int simple_xattr_set_limited(struct simple_xattrs **xattrs, struct simple_xattr_limits *limits, const char *name, const void *value, size_t size, int flags) @@ -1527,10 +1540,11 @@ static bool xattr_is_maclabel(const char *name) * Return: On success the required size or the size of the copied xattrs is * returned. On error a negative error code is returned. */ -ssize_t simple_xattr_list(struct inode *inode, struct simple_xattrs *xattrs, +ssize_t simple_xattr_list(struct inode *inode, struct simple_xattrs **xattrsp, char *buffer, size_t size) { bool trusted = ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN); + struct simple_xattrs *xattrs; struct rhashtable_iter iter; struct simple_xattr *xattr; ssize_t remaining_size = size; @@ -1552,6 +1566,7 @@ ssize_t simple_xattr_list(struct inode *inode, struct simple_xattrs *xattrs, remaining_size -= err; err = 0; + xattrs = READ_ONCE(*xattrsp); if (!xattrs) return size - remaining_size; @@ -1597,9 +1612,15 @@ ssize_t simple_xattr_list(struct inode *inode, struct simple_xattrs *xattrs, * Return: On success zero is returned. On failure a negative error code is * returned. */ -int simple_xattr_add(struct simple_xattrs *xattrs, +int simple_xattr_add(struct simple_xattrs **xattrsp, struct simple_xattr *new_xattr) { + struct simple_xattrs *xattrs; + + xattrs = simple_xattrs_lazy_alloc(xattrsp, new_xattr->value, 0); + if (IS_ERR(xattrs)) + return PTR_ERR(xattrs); + return rhashtable_insert_fast(&xattrs->ht, &new_xattr->hash_node, simple_xattr_params); } @@ -1629,7 +1650,7 @@ int simple_xattrs_init(struct simple_xattrs *xattrs) * Return: On success a new simple_xattrs is returned. On failure an * ERR_PTR is returned. */ -struct simple_xattrs *simple_xattrs_alloc(void) +static struct simple_xattrs *simple_xattrs_alloc(void) { struct simple_xattrs *xattrs __free(kfree) = NULL; int ret; @@ -1661,8 +1682,8 @@ struct simple_xattrs *simple_xattrs_alloc(void) * check with IS_ERR_OR_NULL() and propagate with PTR_ERR() which * correctly returns 0 for the NULL no-op case. */ -struct simple_xattrs *simple_xattrs_lazy_alloc(struct simple_xattrs **xattrsp, - const void *value, int flags) +static struct simple_xattrs *simple_xattrs_lazy_alloc(struct simple_xattrs **xattrsp, + const void *value, int flags) { struct simple_xattrs *xattrs; @@ -1697,12 +1718,19 @@ static void simple_xattr_ht_free(void *ptr, void *arg) * Destroy all xattrs in @xattr. When this is called no one can hold a * reference to any of the xattrs anymore. */ -void simple_xattrs_free(struct simple_xattrs *xattrs, size_t *freed_space) +void simple_xattrs_free(struct simple_xattrs **xattrsp, size_t *freed_space) { + struct simple_xattrs *xattrs = *xattrsp; + might_sleep(); + if (!xattrs) + return; + if (freed_space) *freed_space = 0; rhashtable_free_and_destroy(&xattrs->ht, simple_xattr_ht_free, freed_space); + kfree(xattrs); + *xattrsp = NULL; } diff --git a/include/linux/xattr.h b/include/linux/xattr.h index 8b6601367eae8..ded446c1ef81f 100644 --- a/include/linux/xattr.h +++ b/include/linux/xattr.h @@ -133,26 +133,23 @@ static inline void simple_xattr_limits_init(struct simple_xattr_limits *limits) } int simple_xattrs_init(struct simple_xattrs *xattrs); -struct simple_xattrs *simple_xattrs_alloc(void); -struct simple_xattrs *simple_xattrs_lazy_alloc(struct simple_xattrs **xattrsp, - const void *value, int flags); -void simple_xattrs_free(struct simple_xattrs *xattrs, size_t *freed_space); +void simple_xattrs_free(struct simple_xattrs **xattrs, size_t *freed_space); size_t simple_xattr_space(const char *name, size_t size); struct simple_xattr *simple_xattr_alloc(const void *value, size_t size); void simple_xattr_free(struct simple_xattr *xattr); void simple_xattr_free_rcu(struct simple_xattr *xattr); -int simple_xattr_get(struct simple_xattrs *xattrs, const char *name, +int simple_xattr_get(struct simple_xattrs **xattrs, const char *name, void *buffer, size_t size); -struct simple_xattr *simple_xattr_set(struct simple_xattrs *xattrs, +struct simple_xattr *simple_xattr_set(struct simple_xattrs **xattrs, const char *name, const void *value, size_t size, int flags); -int simple_xattr_set_limited(struct simple_xattrs *xattrs, +int simple_xattr_set_limited(struct simple_xattrs **xattrs, struct simple_xattr_limits *limits, const char *name, const void *value, size_t size, int flags); -ssize_t simple_xattr_list(struct inode *inode, struct simple_xattrs *xattrs, +ssize_t simple_xattr_list(struct inode *inode, struct simple_xattrs **xattrs, char *buffer, size_t size); -int simple_xattr_add(struct simple_xattrs *xattrs, +int simple_xattr_add(struct simple_xattrs **xattrs, struct simple_xattr *new_xattr); int xattr_list_one(char **buffer, ssize_t *remaining_size, const char *name); @@ -162,10 +159,4 @@ DEFINE_CLASS(simple_xattr, simple_xattr_alloc(value, size), const void *value, size_t size) -DEFINE_CLASS(simple_xattrs, - struct simple_xattrs *, - if (!IS_ERR_OR_NULL(_T)) { simple_xattrs_free(_T, NULL); kfree(_T); }, - simple_xattrs_alloc(), - void) - #endif /* _LINUX_XATTR_H */ diff --git a/mm/shmem.c b/mm/shmem.c index c7897570fc9f9..cf4ee9f411911 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1425,10 +1425,8 @@ static void shmem_evict_inode(struct inode *inode) } } - if (info->xattrs) { - simple_xattrs_free(info->xattrs, sbinfo->max_inodes ? &freed : NULL); - kfree(info->xattrs); - } + simple_xattrs_free(&info->xattrs, sbinfo->max_inodes ? &freed : NULL); + shmem_free_inode(inode->i_sb, freed); WARN_ON(inode->i_blocks); clear_inode(inode); @@ -4233,10 +4231,6 @@ static int shmem_initxattrs(struct inode *inode, const struct xattr *xattr; size_t ispace = 0; - CLASS(simple_xattrs, xattrs)(); - if (IS_ERR(xattrs)) - return PTR_ERR(xattrs); - if (sbinfo->max_inodes) { for (xattr = xattr_array; xattr->name != NULL; xattr++) { ispace += simple_xattr_space(xattr->name, @@ -4264,8 +4258,11 @@ static int shmem_initxattrs(struct inode *inode, if (!new_xattr->name) break; - if (simple_xattr_add(xattrs, new_xattr)) + if (simple_xattr_add(&info->xattrs, new_xattr)) break; + + if (sbinfo->max_inodes) + ispace -= simple_xattr_space(new_xattr->name, new_xattr->size); retain_and_null_ptr(new_xattr); } @@ -4277,8 +4274,8 @@ static int shmem_initxattrs(struct inode *inode, } return -ENOMEM; } + WARN_ON(ispace); - smp_store_release(&info->xattrs, no_free_ptr(xattrs)); return 0; } @@ -4287,14 +4284,9 @@ static int shmem_xattr_handler_get(const struct xattr_handler *handler, const char *name, void *buffer, size_t size) { struct shmem_inode_info *info = SHMEM_I(inode); - struct simple_xattrs *xattrs; - - xattrs = READ_ONCE(info->xattrs); - if (!xattrs) - return -ENODATA; name = xattr_full_name(handler, name); - return simple_xattr_get(xattrs, name, buffer, size); + return simple_xattr_get(&info->xattrs, name, buffer, size); } static int shmem_xattr_handler_set(const struct xattr_handler *handler, @@ -4305,16 +4297,11 @@ static int shmem_xattr_handler_set(const struct xattr_handler *handler, { struct shmem_inode_info *info = SHMEM_I(inode); struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb); - struct simple_xattrs *xattrs; struct simple_xattr *old_xattr; size_t ispace = 0; name = xattr_full_name(handler, name); - xattrs = simple_xattrs_lazy_alloc(&info->xattrs, value, flags); - if (IS_ERR_OR_NULL(xattrs)) - return PTR_ERR(xattrs); - if (value && sbinfo->max_inodes) { ispace = simple_xattr_space(name, size); raw_spin_lock(&sbinfo->stat_lock); @@ -4327,7 +4314,7 @@ static int shmem_xattr_handler_set(const struct xattr_handler *handler, return -ENOSPC; } - old_xattr = simple_xattr_set(xattrs, name, value, size, flags); + old_xattr = simple_xattr_set(&info->xattrs, name, value, size, flags); if (!IS_ERR(old_xattr)) { ispace = 0; if (old_xattr && sbinfo->max_inodes) @@ -4375,8 +4362,7 @@ static ssize_t shmem_listxattr(struct dentry *dentry, char *buffer, size_t size) { struct shmem_inode_info *info = SHMEM_I(d_inode(dentry)); - return simple_xattr_list(d_inode(dentry), READ_ONCE(info->xattrs), - buffer, size); + return simple_xattr_list(d_inode(dentry), &info->xattrs, buffer, size); } #endif /* CONFIG_TMPFS_XATTR */ diff --git a/net/socket.c b/net/socket.c index c2698a1441a7d..c59aa002d3e35 100644 --- a/net/socket.c +++ b/net/socket.c @@ -347,12 +347,8 @@ static struct inode *sock_alloc_inode(struct super_block *sb) static void sock_evict_inode(struct inode *inode) { struct sockfs_inode *si = SOCKFS_I(inode); - struct simple_xattrs *xattrs = si->xattrs; - if (xattrs) { - simple_xattrs_free(xattrs, NULL); - kfree(xattrs); - } + simple_xattrs_free(&si->xattrs, NULL); clear_inode(inode); } @@ -443,13 +439,9 @@ static int sockfs_user_xattr_get(const struct xattr_handler *handler, const char *suffix, void *value, size_t size) { const char *name = xattr_full_name(handler, suffix); - struct simple_xattrs *xattrs; - - xattrs = READ_ONCE(SOCKFS_I(inode)->xattrs); - if (!xattrs) - return -ENODATA; + struct sockfs_inode *si = SOCKFS_I(inode); - return simple_xattr_get(xattrs, name, value, size); + return simple_xattr_get(&si->xattrs, name, value, size); } static int sockfs_user_xattr_set(const struct xattr_handler *handler, @@ -460,13 +452,8 @@ static int sockfs_user_xattr_set(const struct xattr_handler *handler, { const char *name = xattr_full_name(handler, suffix); struct sockfs_inode *si = SOCKFS_I(inode); - struct simple_xattrs *xattrs; - - xattrs = simple_xattrs_lazy_alloc(&si->xattrs, value, flags); - if (IS_ERR_OR_NULL(xattrs)) - return PTR_ERR(xattrs); - return simple_xattr_set_limited(xattrs, &si->xattr_limits, + return simple_xattr_set_limited(&si->xattrs, &si->xattr_limits, name, value, size, flags); } @@ -635,8 +622,7 @@ static ssize_t sockfs_listxattr(struct dentry *dentry, char *buffer, struct sockfs_inode *si = SOCKFS_I(d_inode(dentry)); ssize_t len, used; - len = simple_xattr_list(d_inode(dentry), READ_ONCE(si->xattrs), - buffer, size); + len = simple_xattr_list(d_inode(dentry), &si->xattrs, buffer, size); if (len < 0) return len; From 86c65ca2d69b8501542991f6aca1417b2abc6261 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 5 Jun 2026 15:53:19 +0200 Subject: [PATCH 1193/2758] simpe_xattr: use per-sb cache [ Upstream commit 1e7cd8a53b72a58a44c4d282aed95f6ce0e76db0 ] Move the hash table to the super block to remove excessive overhead in case of small number of xattrs per inode. Add linked list to the inode, used for listxattr and eviction. Listxattr uses rcu protection to iterate the list of xattrs. Before being made per-sb, lazy allocation was protected by inode lock. Now inode lock no longer provides sufficient exclusion, so use cmpxchg() to ensure atomicity. Though I haven't found a description of this pattern, after some research it seems that cmpxchg_release() and READ_ONCE() should provide the necessary memory barriers. Use simple_xattr_free_rcu() in simple_xattrs_free(). This is needed because the hash table is now shared between inodes and lookup on a different inode might be running the compare function on the just freed element within the RCU grace period. Following stats are based on slabinfo diff, after creating 100k empty files, then adding a "user.test=foo" xattr to each: v7.0 (no rhashtable): File creation: 993.40 bytes/file Xattr addition: 79.99 bytes/file v7.1-rc2 (per-inode rhashtable): File creation: 939.73 bytes/file Xattr addition: 1296.08 bytes/file v7.1-rc2 + this patch (per-sb rhashtable) File creation: 946.84 bytes/file Xattr addition: 111.86 bytes/file The overhead of a single xattr is reduced to nearly v7.0 levels. The per xattr overhead is slightly larger due to the addition of three pointers to struct simple_xattr. Fixes: b32c4a213698 ("xattr: add rhashtable-based simple_xattr infrastructure") Signed-off-by: Miklos Szeredi Link: https://patch.msgid.link/20260605135322.2632068-5-mszeredi@redhat.com Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/kernfs/dir.c | 5 +- fs/kernfs/inode.c | 10 +- fs/kernfs/kernfs-internal.h | 4 +- fs/pidfs.c | 14 +- fs/xattr.c | 283 +++++++++++++++++------------------- include/linux/shmem_fs.h | 3 +- include/linux/xattr.h | 26 ++-- mm/shmem.c | 13 +- net/socket.c | 12 +- 9 files changed, 192 insertions(+), 178 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index cf6fc2bfa5802..78812cf1256b9 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -609,7 +609,7 @@ void kernfs_put(struct kernfs_node *kn) kernfs_put(kn->symlink.target_kn); if (kn->iattr) - simple_xattrs_free(&kn->iattr->xattrs, NULL); + simple_xattrs_free(&root->xa_cache, &kn->iattr->xattrs, NULL); spin_lock(&root->kernfs_idr_lock); idr_remove(&root->ino_idr, (u32)kernfs_ino(kn)); @@ -624,6 +624,7 @@ void kernfs_put(struct kernfs_node *kn) } else { /* just released the root kn, free @root too */ idr_destroy(&root->ino_idr); + simple_xattr_cache_cleanup(&root->xa_cache); kfree_rcu(root, rcu); } } @@ -709,7 +710,7 @@ static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root, err_out4: if (kn->iattr) { - simple_xattrs_free(&kn->iattr->xattrs, NULL); + simple_xattrs_free(&root->xa_cache, &kn->iattr->xattrs, NULL); kmem_cache_free(kernfs_iattrs_cache, kn->iattr); } err_out3: diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c index f2298de6bc6f8..2cb20294aaf58 100644 --- a/fs/kernfs/inode.c +++ b/fs/kernfs/inode.c @@ -37,6 +37,7 @@ static struct kernfs_iattrs *__kernfs_iattrs(struct kernfs_node *kn, bool alloc) if (!ret) return NULL; + INIT_LIST_HEAD_RCU(&ret->xattrs); /* assign default attributes */ ret->ia_uid = GLOBAL_ROOT_UID; ret->ia_gid = GLOBAL_ROOT_GID; @@ -296,11 +297,12 @@ int kernfs_xattr_get(struct kernfs_node *kn, const char *name, void *value, size_t size) { struct kernfs_iattrs *attrs = kernfs_iattrs_noalloc(kn); + struct simple_xattr_cache *cache = &kernfs_root(kn)->xa_cache; if (!attrs) return -ENODATA; - return simple_xattr_get(&attrs->xattrs, name, value, size); + return simple_xattr_get(cache, &attrs->xattrs, name, value, size); } int kernfs_xattr_set(struct kernfs_node *kn, const char *name, @@ -308,6 +310,7 @@ int kernfs_xattr_set(struct kernfs_node *kn, const char *name, { struct simple_xattr *old_xattr; struct kernfs_iattrs *attrs; + struct simple_xattr_cache *cache = &kernfs_root(kn)->xa_cache; attrs = kernfs_iattrs(kn); if (!attrs) @@ -322,7 +325,7 @@ int kernfs_xattr_set(struct kernfs_node *kn, const char *name, */ CLASS(kernfs_node_lock, lock)(kn); - old_xattr = simple_xattr_set(&attrs->xattrs, name, value, size, flags); + old_xattr = simple_xattr_set(cache, &attrs->xattrs, name, value, size, flags); if (IS_ERR(old_xattr)) return PTR_ERR(old_xattr); @@ -372,7 +375,8 @@ static int kernfs_vfs_user_xattr_set(const struct xattr_handler *handler, /* See comment in kernfs_xattr_set() about locking. */ CLASS(kernfs_node_lock, lock)(kn); - return simple_xattr_set_limited(&attrs->xattrs, &attrs->xattr_limits, + return simple_xattr_set_limited(&kernfs_root(kn)->xa_cache, + &attrs->xattrs, &attrs->xattr_limits, full_name, value, size, flags); } diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index 1dc6663553d1a..aa784b540b361 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -26,7 +26,7 @@ struct kernfs_iattrs { struct timespec64 ia_mtime; struct timespec64 ia_ctime; - struct simple_xattrs *xattrs; + struct list_head xattrs; struct simple_xattr_limits xattr_limits; }; @@ -54,6 +54,8 @@ struct kernfs_root { rwlock_t kernfs_rename_lock; struct rcu_head rcu; + + struct simple_xattr_cache xa_cache; }; /* +1 to avoid triggering overflow warning when negating it */ diff --git a/fs/pidfs.c b/fs/pidfs.c index eb5105bddecaf..143d0aec16af1 100644 --- a/fs/pidfs.c +++ b/fs/pidfs.c @@ -37,6 +37,8 @@ static struct kmem_cache *pidfs_attr_cachep __ro_after_init; static struct path pidfs_root_path = {}; +static struct simple_xattr_cache pidfs_xa_cache; + void pidfs_get_root(struct path *path) { *path = pidfs_root_path; @@ -96,7 +98,7 @@ static const struct rhashtable_params pidfs_ino_ht_params = { * use file handles. */ struct pidfs_attr { - struct simple_xattrs *xattrs; + struct list_head xattrs; union { struct pidfs_anon_attr; struct llist_node pidfs_llist; @@ -196,7 +198,7 @@ static void pidfs_free_attr_work(struct work_struct *work) head = llist_del_all(&pidfs_free_list); llist_for_each_entry_safe(attr, next, head, pidfs_llist) { - simple_xattrs_free(&attr->xattrs, NULL); + simple_xattrs_free(&pidfs_xa_cache, &attr->xattrs, NULL); kfree(attr); } } @@ -224,7 +226,7 @@ void pidfs_free_pid(struct pid *pid) if (IS_ERR(attr)) return; - if (likely(!attr->xattrs)) + if (likely(list_empty(&attr->xattrs))) kfree(attr); else if (llist_add(&attr->pidfs_llist, &pidfs_free_list)) schedule_work(&pidfs_free_work); @@ -1007,6 +1009,8 @@ int pidfs_register_pid(struct pid *pid) if (!new_attr) return -ENOMEM; + INIT_LIST_HEAD_RCU(&new_attr->xattrs); + /* Synchronize with pidfs_exit(). */ guard(spinlock_irq)(&pid->wait_pidfd.lock); @@ -1048,7 +1052,7 @@ static int pidfs_xattr_get(const struct xattr_handler *handler, struct pid *pid = inode->i_private; const char *name = xattr_full_name(handler, suffix); - return simple_xattr_get(&pid->attr->xattrs, name, value, size); + return simple_xattr_get(&pidfs_xa_cache, &pid->attr->xattrs, name, value, size); } static int pidfs_xattr_set(const struct xattr_handler *handler, @@ -1063,7 +1067,7 @@ static int pidfs_xattr_set(const struct xattr_handler *handler, /* Ensure we're the only one to set @attr->xattrs. */ WARN_ON_ONCE(!inode_is_locked(inode)); - old_xattr = simple_xattr_set(&pid->attr->xattrs, name, value, size, flags); + old_xattr = simple_xattr_set(&pidfs_xa_cache, &pid->attr->xattrs, name, value, size, flags); if (IS_ERR(old_xattr)) return PTR_ERR(old_xattr); diff --git a/fs/xattr.c b/fs/xattr.c index 9ef7ad8a8f321..89374cd9029a7 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -28,6 +28,11 @@ #include "internal.h" +struct sx_key { + const struct list_head *parent; + const char *name; +}; + static const char * strcmp_prefix(const char *a, const char *a_prefix) { @@ -1269,23 +1274,32 @@ struct simple_xattr *simple_xattr_alloc(const void *value, size_t size) return new_xattr; } +static u32 sx_hashfn(const char *name, const struct list_head *parent, u32 seed) +{ + return jhash(name, strlen(name), jhash(&parent, sizeof(parent), seed)); +} + static u32 simple_xattr_hashfn(const void *data, u32 len, u32 seed) { - const char *name = data; - return jhash(name, strlen(name), seed); + const struct sx_key *key = data; + + return sx_hashfn(key->name, key->parent, seed); } static u32 simple_xattr_obj_hashfn(const void *obj, u32 len, u32 seed) { const struct simple_xattr *xattr = obj; - return jhash(xattr->name, strlen(xattr->name), seed); + + return sx_hashfn(xattr->name, xattr->parent, seed); } static int simple_xattr_obj_cmpfn(struct rhashtable_compare_arg *arg, const void *obj) { const struct simple_xattr *xattr = obj; - return strcmp(xattr->name, arg->key); + const struct sx_key *key = arg->key; + + return xattr->parent != key->parent || strcmp(xattr->name, key->name); } static const struct rhashtable_params simple_xattr_params = { @@ -1298,6 +1312,7 @@ static const struct rhashtable_params simple_xattr_params = { /** * simple_xattr_get - get an xattr object + * @cache: anchor for the hash table * @xattrs: the header of the xattr object * @name: the name of the xattr to retrieve * @buffer: the buffer to store the value into @@ -1311,19 +1326,19 @@ static const struct rhashtable_params simple_xattr_params = { * Return: On success the length of the xattr value is returned. On error a * negative error code is returned. */ -int simple_xattr_get(struct simple_xattrs **xattrsp, const char *name, - void *buffer, size_t size) +int simple_xattr_get(struct simple_xattr_cache *cache, struct list_head *xattrs, + const char *name, void *buffer, size_t size) { - struct simple_xattrs *xattrs; struct simple_xattr *xattr; + struct sx_key key = { .parent = xattrs, .name = name }; + struct rhashtable *ht = READ_ONCE(cache->ht); int ret = -ENODATA; - xattrs = READ_ONCE(*xattrsp); - if (!xattrs) - return -ENODATA; + if (!ht) + return ret; guard(rcu)(); - xattr = rhashtable_lookup(&xattrs->ht, name, simple_xattr_params); + xattr = rhashtable_lookup(ht, &key, simple_xattr_params); if (xattr) { ret = xattr->size; if (buffer) { @@ -1336,11 +1351,45 @@ int simple_xattr_get(struct simple_xattrs **xattrsp, const char *name, return ret; } -static struct simple_xattrs *simple_xattrs_lazy_alloc(struct simple_xattrs **xattrsp, - const void *value, int flags); +static struct rhashtable *simple_xattrs_lazy_alloc(struct simple_xattr_cache *cache, + const void *value, int flags) +{ + struct rhashtable *oldht, *ht = READ_ONCE(cache->ht); + int err; + + if (unlikely(!ht)) { + if (!value) + return (flags & XATTR_REPLACE) ? ERR_PTR(-ENODATA) : NULL; + + ht = kzalloc_obj(*ht); + if (!ht) + return ERR_PTR(-ENOMEM); + + err = rhashtable_init(ht, &simple_xattr_params); + if (err) { + kfree(ht); + return ERR_PTR(err); + } + + /* + * Provides release semantics on success, so that use of a + * non-NULL READ_ONCE(cache->ht) will be ordered relative to the + * above initialization, due to implicit address dependency. + */ + oldht = cmpxchg_release(&cache->ht, NULL, ht); + if (oldht) { + /* Race lost */ + rhashtable_destroy(ht); + kfree(ht); + ht = oldht; + } + } + return ht; +} /** * simple_xattr_set - set an xattr object + * @cache: anchor for the hash table * @xattrs: the header of the xattr object * @name: the name of the xattr to retrieve * @value: the value to store along the xattr @@ -1370,50 +1419,58 @@ static struct simple_xattrs *simple_xattrs_lazy_alloc(struct simple_xattrs **xat * Return: On success, the removed or replaced xattr is returned, to be freed * by the caller; or NULL if none. On failure a negative error code is returned. */ -struct simple_xattr *simple_xattr_set(struct simple_xattrs **xattrsp, +struct simple_xattr *simple_xattr_set(struct simple_xattr_cache *cache, struct list_head *xattrs, const char *name, const void *value, size_t size, int flags) { - struct simple_xattrs *xattrs; + struct sx_key key = { .parent = xattrs, .name = name }; struct simple_xattr *old_xattr = NULL; + struct rhashtable *ht; int err; - xattrs = simple_xattrs_lazy_alloc(xattrsp, value, flags); - if (IS_ERR_OR_NULL(xattrs)) - return ERR_CAST(xattrs); + ht = simple_xattrs_lazy_alloc(cache, value, flags); + if (IS_ERR_OR_NULL(ht)) + return ERR_CAST(ht); CLASS(simple_xattr, new_xattr)(value, size); if (IS_ERR(new_xattr)) return new_xattr; if (new_xattr) { + new_xattr->parent = xattrs; new_xattr->name = kstrdup(name, GFP_KERNEL_ACCOUNT); if (!new_xattr->name) return ERR_PTR(-ENOMEM); } - /* Lookup is safe without RCU here since writes are serialized. */ - old_xattr = rhashtable_lookup_fast(&xattrs->ht, name, - simple_xattr_params); - + /* + * Hash table lookup/replace/remove will grab RCU read lock themselves. + * This makes sure that hash table lookup is safe against concurrent + * modification on another inode. + */ + old_xattr = rhashtable_lookup_fast(ht, &key, simple_xattr_params); if (old_xattr) { /* Fail if XATTR_CREATE is requested and the xattr exists. */ if (flags & XATTR_CREATE) return ERR_PTR(-EEXIST); if (new_xattr) { - err = rhashtable_replace_fast(&xattrs->ht, + err = rhashtable_replace_fast(ht, &old_xattr->hash_node, &new_xattr->hash_node, simple_xattr_params); if (err) return ERR_PTR(err); + + list_replace_rcu(&old_xattr->node, &new_xattr->node); } else { - err = rhashtable_remove_fast(&xattrs->ht, + err = rhashtable_remove_fast(ht, &old_xattr->hash_node, simple_xattr_params); if (err) return ERR_PTR(err); + + list_del_rcu(&old_xattr->node); } } else { /* Fail if XATTR_REPLACE is requested but no xattr is found. */ @@ -1425,11 +1482,13 @@ struct simple_xattr *simple_xattr_set(struct simple_xattrs **xattrsp, * new value simply insert it. */ if (new_xattr) { - err = rhashtable_insert_fast(&xattrs->ht, + err = rhashtable_insert_fast(ht, &new_xattr->hash_node, simple_xattr_params); if (err) return ERR_PTR(err); + + list_add_tail_rcu(&new_xattr->node, xattrs); } /* @@ -1466,6 +1525,7 @@ static inline int simple_xattr_limits_inc(struct simple_xattr_limits *limits, /** * simple_xattr_set_limited - set an xattr with per-inode user.* limits + * @cache: anchor for the hash table * @xattrs: the header of the xattr object * @limits: per-inode limit counters for user.* xattrs * @name: the name of the xattr to set or remove @@ -1480,7 +1540,7 @@ static inline int simple_xattr_limits_inc(struct simple_xattr_limits *limits, * Return: On success zero is returned. On failure a negative error code is * returned. */ -int simple_xattr_set_limited(struct simple_xattrs **xattrs, +int simple_xattr_set_limited(struct simple_xattr_cache *cache, struct list_head *xattrs, struct simple_xattr_limits *limits, const char *name, const void *value, size_t size, int flags) @@ -1494,7 +1554,7 @@ int simple_xattr_set_limited(struct simple_xattrs **xattrs, return ret; } - old_xattr = simple_xattr_set(xattrs, name, value, size, flags); + old_xattr = simple_xattr_set(cache, xattrs, name, value, size, flags); if (IS_ERR(old_xattr)) { if (value) simple_xattr_limits_dec(limits, size); @@ -1540,12 +1600,10 @@ static bool xattr_is_maclabel(const char *name) * Return: On success the required size or the size of the copied xattrs is * returned. On error a negative error code is returned. */ -ssize_t simple_xattr_list(struct inode *inode, struct simple_xattrs **xattrsp, +ssize_t simple_xattr_list(struct inode *inode, struct list_head *xattrs, char *buffer, size_t size) { bool trusted = ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN); - struct simple_xattrs *xattrs; - struct rhashtable_iter iter; struct simple_xattr *xattr; ssize_t remaining_size = size; int err = 0; @@ -1566,21 +1624,11 @@ ssize_t simple_xattr_list(struct inode *inode, struct simple_xattrs **xattrsp, remaining_size -= err; err = 0; - xattrs = READ_ONCE(*xattrsp); if (!xattrs) return size - remaining_size; - rhashtable_walk_enter(&xattrs->ht, &iter); - rhashtable_walk_start(&iter); - - while ((xattr = rhashtable_walk_next(&iter)) != NULL) { - if (IS_ERR(xattr)) { - if (PTR_ERR(xattr) == -EAGAIN) - continue; - err = PTR_ERR(xattr); - break; - } - + rcu_read_lock(); + list_for_each_entry_rcu(xattr, xattrs, node) { /* skip "trusted." attributes for unprivileged callers */ if (!trusted && xattr_is_trusted(xattr->name)) continue; @@ -1593,15 +1641,14 @@ ssize_t simple_xattr_list(struct inode *inode, struct simple_xattrs **xattrsp, if (err) break; } - - rhashtable_walk_stop(&iter); - rhashtable_walk_exit(&iter); + rcu_read_unlock(); return err ? err : size - remaining_size; } /** * simple_xattr_add - add xattr objects + * @cache: anchor for the hash table * @xattrs: the header of the xattr object * @new_xattr: the xattr object to add * @@ -1612,125 +1659,67 @@ ssize_t simple_xattr_list(struct inode *inode, struct simple_xattrs **xattrsp, * Return: On success zero is returned. On failure a negative error code is * returned. */ -int simple_xattr_add(struct simple_xattrs **xattrsp, +int simple_xattr_add(struct simple_xattr_cache *cache, struct list_head *xattrs, struct simple_xattr *new_xattr) { - struct simple_xattrs *xattrs; - - xattrs = simple_xattrs_lazy_alloc(xattrsp, new_xattr->value, 0); - if (IS_ERR(xattrs)) - return PTR_ERR(xattrs); - - return rhashtable_insert_fast(&xattrs->ht, &new_xattr->hash_node, - simple_xattr_params); -} - -/** - * simple_xattrs_init - initialize new xattr header - * @xattrs: header to initialize - * - * Initialize the rhashtable used to store xattr objects. - * - * Return: On success zero is returned. On failure a negative error code is - * returned. - */ -int simple_xattrs_init(struct simple_xattrs *xattrs) -{ - return rhashtable_init(&xattrs->ht, &simple_xattr_params); -} - -/** - * simple_xattrs_alloc - allocate and initialize a new xattr header - * - * Dynamically allocate a simple_xattrs header and initialize the - * underlying rhashtable. This is intended for consumers that want - * to lazily allocate xattr storage only when the first xattr is set, - * avoiding the per-inode rhashtable overhead when no xattrs are used. - * - * Return: On success a new simple_xattrs is returned. On failure an - * ERR_PTR is returned. - */ -static struct simple_xattrs *simple_xattrs_alloc(void) -{ - struct simple_xattrs *xattrs __free(kfree) = NULL; - int ret; + struct rhashtable *ht; + int err; - xattrs = kzalloc(sizeof(*xattrs), GFP_KERNEL); - if (!xattrs) - return ERR_PTR(-ENOMEM); + ht = simple_xattrs_lazy_alloc(cache, new_xattr->value, 0); + if (IS_ERR(ht)) + return PTR_ERR(ht); - ret = simple_xattrs_init(xattrs); - if (ret) - return ERR_PTR(ret); + new_xattr->parent = xattrs; + err = rhashtable_insert_fast(ht, &new_xattr->hash_node, simple_xattr_params); + if (err) + return err; - return no_free_ptr(xattrs); + list_add_tail_rcu(&new_xattr->node, xattrs); + return 0; } /** - * simple_xattrs_lazy_alloc - get or allocate xattrs for a set operation - * @xattrsp: pointer to the xattrs pointer (may point to NULL) - * @value: value being set (NULL means remove) - * @flags: xattr set flags - * - * For lazily-allocated xattrs on the write path. If no xattrs exist yet - * and this is a remove operation, returns the appropriate result without - * allocating. Otherwise ensures xattrs is allocated and published with - * store-release semantics. + * simple_xattrs_free - free xattrs + * @cache: anchor for the hash table + * @xattrs: xattr header whose xattrs to destroy + * @freed_space: approximate number of bytes of memory freed from @xattrs * - * Return: On success a valid pointer to the xattrs is returned. On - * failure or early-exit an ERR_PTR or NULL is returned. Callers should - * check with IS_ERR_OR_NULL() and propagate with PTR_ERR() which - * correctly returns 0 for the NULL no-op case. + * Destroy all xattrs in @xattrs. When this is called no one can hold a + * reference to any of the xattrs anymore. */ -static struct simple_xattrs *simple_xattrs_lazy_alloc(struct simple_xattrs **xattrsp, - const void *value, int flags) +void simple_xattrs_free(struct simple_xattr_cache *cache, struct list_head *xattrs, + size_t *freed_space) { - struct simple_xattrs *xattrs; - - xattrs = READ_ONCE(*xattrsp); - if (xattrs) - return xattrs; - - if (!value) - return (flags & XATTR_REPLACE) ? ERR_PTR(-ENODATA) : NULL; - - xattrs = simple_xattrs_alloc(); - if (!IS_ERR(xattrs)) - smp_store_release(xattrsp, xattrs); - return xattrs; -} + if (freed_space) + *freed_space = 0; -static void simple_xattr_ht_free(void *ptr, void *arg) -{ - struct simple_xattr *xattr = ptr; - size_t *freed_space = arg; + while (!list_empty(xattrs)) { + struct simple_xattr *xattr = list_first_entry(xattrs, typeof(*xattr), node); - if (freed_space) - *freed_space += simple_xattr_space(xattr->name, xattr->size); - simple_xattr_free(xattr); + rhashtable_remove_fast(cache->ht, &xattr->hash_node, simple_xattr_params); + list_del(&xattr->node); + if (freed_space) + *freed_space += simple_xattr_space(xattr->name, xattr->size); + /* + * Free with RCU, since the xattr might still get accessed by + * the hash compare function + */ + simple_xattr_free_rcu(xattr); + } } /** - * simple_xattrs_free - free xattrs - * @xattrs: xattr header whose xattrs to destroy - * @freed_space: approximate number of bytes of memory freed from @xattrs + * simple_xattr_cache_cleanup - free the cache + * @cache: anchor for the hash table * - * Destroy all xattrs in @xattr. When this is called no one can hold a - * reference to any of the xattrs anymore. + * Destroy the cache table, which was lazily allocated on adding the first xattr. */ -void simple_xattrs_free(struct simple_xattrs **xattrsp, size_t *freed_space) +void simple_xattr_cache_cleanup(struct simple_xattr_cache *cache) { - struct simple_xattrs *xattrs = *xattrsp; - - might_sleep(); - - if (!xattrs) - return; - - if (freed_space) - *freed_space = 0; - rhashtable_free_and_destroy(&xattrs->ht, simple_xattr_ht_free, - freed_space); - kfree(xattrs); - *xattrsp = NULL; + if (cache->ht) { + WARN_ON(atomic_read(&cache->ht->nelems)); + rhashtable_destroy(cache->ht); + kfree(cache->ht); + cache->ht = NULL; + } } diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h index 93a0ba872ebe0..69b0177da1564 100644 --- a/include/linux/shmem_fs.h +++ b/include/linux/shmem_fs.h @@ -48,7 +48,7 @@ struct shmem_inode_info { }; struct timespec64 i_crtime; /* file creation time */ struct shared_policy policy; /* NUMA memory alloc policy */ - struct simple_xattrs *xattrs; /* list of xattrs */ + struct list_head xattrs; /* list of xattrs */ pgoff_t fallocend; /* highest fallocate endindex */ unsigned int fsflags; /* for FS_IOC_[SG]ETFLAGS */ atomic_t stop_eviction; /* hold when working on inode */ @@ -89,6 +89,7 @@ struct shmem_sb_info { struct list_head shrinklist; /* List of shinkable inodes */ unsigned long shrinklist_len; /* Length of shrinklist */ struct shmem_quota_limits qlimits; /* Default quota limits */ + struct simple_xattr_cache xa_cache; }; static inline struct shmem_inode_info *SHMEM_I(struct inode *inode) diff --git a/include/linux/xattr.h b/include/linux/xattr.h index ded446c1ef81f..7aaaf4f8aff5b 100644 --- a/include/linux/xattr.h +++ b/include/linux/xattr.h @@ -106,12 +106,14 @@ static inline const char *xattr_prefix(const struct xattr_handler *handler) return handler->prefix ?: handler->name; } -struct simple_xattrs { - struct rhashtable ht; +struct simple_xattr_cache { + struct rhashtable *ht; }; struct simple_xattr { struct rhash_head hash_node; + struct list_head *parent; + struct list_head node; struct rcu_head rcu; char *name; size_t size; @@ -132,27 +134,31 @@ static inline void simple_xattr_limits_init(struct simple_xattr_limits *limits) atomic_set(&limits->xattr_size, 0); } -int simple_xattrs_init(struct simple_xattrs *xattrs); -void simple_xattrs_free(struct simple_xattrs **xattrs, size_t *freed_space); +void simple_xattrs_free(struct simple_xattr_cache *cache, struct list_head *xattrs, + size_t *freed_space); size_t simple_xattr_space(const char *name, size_t size); struct simple_xattr *simple_xattr_alloc(const void *value, size_t size); void simple_xattr_free(struct simple_xattr *xattr); void simple_xattr_free_rcu(struct simple_xattr *xattr); -int simple_xattr_get(struct simple_xattrs **xattrs, const char *name, - void *buffer, size_t size); -struct simple_xattr *simple_xattr_set(struct simple_xattrs **xattrs, +int simple_xattr_get(struct simple_xattr_cache *cache, struct list_head *xattrs, + const char *name, void *buffer, size_t size); +struct simple_xattr *simple_xattr_set(struct simple_xattr_cache *cache, + struct list_head *xattrs, const char *name, const void *value, size_t size, int flags); -int simple_xattr_set_limited(struct simple_xattrs **xattrs, +int simple_xattr_set_limited(struct simple_xattr_cache *cache, + struct list_head *xattrs, struct simple_xattr_limits *limits, const char *name, const void *value, size_t size, int flags); -ssize_t simple_xattr_list(struct inode *inode, struct simple_xattrs **xattrs, +ssize_t simple_xattr_list(struct inode *inode, struct list_head *xattrs, char *buffer, size_t size); -int simple_xattr_add(struct simple_xattrs **xattrs, +int simple_xattr_add(struct simple_xattr_cache *cache, struct list_head *xattrs, struct simple_xattr *new_xattr); int xattr_list_one(char **buffer, ssize_t *remaining_size, const char *name); +void simple_xattr_cache_cleanup(struct simple_xattr_cache *cache); + DEFINE_CLASS(simple_xattr, struct simple_xattr *, if (!IS_ERR_OR_NULL(_T)) simple_xattr_free(_T), diff --git a/mm/shmem.c b/mm/shmem.c index cf4ee9f411911..7b1ea9fb598f4 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1425,7 +1425,7 @@ static void shmem_evict_inode(struct inode *inode) } } - simple_xattrs_free(&info->xattrs, sbinfo->max_inodes ? &freed : NULL); + simple_xattrs_free(&sbinfo->xa_cache, &info->xattrs, sbinfo->max_inodes ? &freed : NULL); shmem_free_inode(inode->i_sb, freed); WARN_ON(inode->i_blocks); @@ -3084,6 +3084,7 @@ static struct inode *__shmem_get_inode(struct mnt_idmap *idmap, inode->i_generation = get_random_u32(); info = SHMEM_I(inode); memset(info, 0, (char *)inode - (char *)info); + INIT_LIST_HEAD_RCU(&info->xattrs); spin_lock_init(&info->lock); atomic_set(&info->stop_eviction, 0); info->seals = F_SEAL_SEAL; @@ -4258,7 +4259,7 @@ static int shmem_initxattrs(struct inode *inode, if (!new_xattr->name) break; - if (simple_xattr_add(&info->xattrs, new_xattr)) + if (simple_xattr_add(&sbinfo->xa_cache, &info->xattrs, new_xattr)) break; if (sbinfo->max_inodes) @@ -4283,10 +4284,11 @@ static int shmem_xattr_handler_get(const struct xattr_handler *handler, struct dentry *unused, struct inode *inode, const char *name, void *buffer, size_t size) { + struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb); struct shmem_inode_info *info = SHMEM_I(inode); name = xattr_full_name(handler, name); - return simple_xattr_get(&info->xattrs, name, buffer, size); + return simple_xattr_get(&sbinfo->xa_cache, &info->xattrs, name, buffer, size); } static int shmem_xattr_handler_set(const struct xattr_handler *handler, @@ -4314,7 +4316,7 @@ static int shmem_xattr_handler_set(const struct xattr_handler *handler, return -ENOSPC; } - old_xattr = simple_xattr_set(&info->xattrs, name, value, size, flags); + old_xattr = simple_xattr_set(&sbinfo->xa_cache, &info->xattrs, name, value, size, flags); if (!IS_ERR(old_xattr)) { ispace = 0; if (old_xattr && sbinfo->max_inodes) @@ -4963,6 +4965,9 @@ static void shmem_put_super(struct super_block *sb) free_percpu(sbinfo->ino_batch); percpu_counter_destroy(&sbinfo->used_blocks); mpol_put(sbinfo->mpol); +#ifdef CONFIG_TMPFS_XATTR + simple_xattr_cache_cleanup(&sbinfo->xa_cache); +#endif kfree(sbinfo); sb->s_fs_info = NULL; } diff --git a/net/socket.c b/net/socket.c index c59aa002d3e35..f51bdcbaa43f7 100644 --- a/net/socket.c +++ b/net/socket.c @@ -310,8 +310,10 @@ static int move_addr_to_user(struct sockaddr_storage *kaddr, int klen, static struct kmem_cache *sock_inode_cachep __ro_after_init; +static struct simple_xattr_cache sockfs_xa_cache; + struct sockfs_inode { - struct simple_xattrs *xattrs; + struct list_head xattrs; struct simple_xattr_limits xattr_limits; struct socket_alloc; }; @@ -328,7 +330,7 @@ static struct inode *sock_alloc_inode(struct super_block *sb) si = alloc_inode_sb(sb, sock_inode_cachep, GFP_KERNEL); if (!si) return NULL; - si->xattrs = NULL; + INIT_LIST_HEAD_RCU(&si->xattrs); simple_xattr_limits_init(&si->xattr_limits); init_waitqueue_head(&si->socket.wq.wait); @@ -348,7 +350,7 @@ static void sock_evict_inode(struct inode *inode) { struct sockfs_inode *si = SOCKFS_I(inode); - simple_xattrs_free(&si->xattrs, NULL); + simple_xattrs_free(&sockfs_xa_cache, &si->xattrs, NULL); clear_inode(inode); } @@ -441,7 +443,7 @@ static int sockfs_user_xattr_get(const struct xattr_handler *handler, const char *name = xattr_full_name(handler, suffix); struct sockfs_inode *si = SOCKFS_I(inode); - return simple_xattr_get(&si->xattrs, name, value, size); + return simple_xattr_get(&sockfs_xa_cache, &si->xattrs, name, value, size); } static int sockfs_user_xattr_set(const struct xattr_handler *handler, @@ -453,7 +455,7 @@ static int sockfs_user_xattr_set(const struct xattr_handler *handler, const char *name = xattr_full_name(handler, suffix); struct sockfs_inode *si = SOCKFS_I(inode); - return simple_xattr_set_limited(&si->xattrs, &si->xattr_limits, + return simple_xattr_set_limited(&sockfs_xa_cache, &si->xattrs, &si->xattr_limits, name, value, size, flags); } From 319b82e8b46edaf557436ad858e734e583f78ec9 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 26 May 2026 10:57:06 +0200 Subject: [PATCH 1194/2758] kernfs: link kn to its parent before the LSM init hook [ Upstream commit 6cccc49b027c7551ffc1d2532f2ef1922661f3da ] After commit 12e9e3cd03b5 ("simpe_xattr: use per-sb cache"), kernfs_xattr_set() and kernfs_xattr_get() compute the cache via kernfs_root(kn) before any other check. kernfs_root(kn) walks kn->__parent first and falls back to kn->dir.root, both of which are NULL on a freshly kmem_cache_zalloc()'d kn. kn->__parent was being set in kernfs_new_node() after __kernfs_new_node() returned, and kn->dir.root is set even later by kernfs_create_dir_ns() / kernfs_create_empty_dir(). The LSM kernfs_init_security hook is invoked from inside __kernfs_new_node(), before either field has been initialized. selinux_kernfs_init_security() ends with kernfs_xattr_set(kn, XATTR_NAME_SELINUX, ...). kernfs_root(kn) then returns NULL, and &((struct kernfs_root *)NULL)->xa_cache evaluates to offsetof(struct kernfs_root, xa_cache) which faults: BUG: kernel NULL pointer dereference, address: 00000000000000e0 RIP: 0010:simple_xattr_set+0x27/0x8b0 Call Trace: kernfs_xattr_set+0x63/0xb0 selinux_kernfs_init_security+0x13b/0x270 security_kernfs_init_security+0x36/0xc0 __kernfs_new_node+0x182/0x290 kernfs_new_node+0x80/0xc0 kernfs_create_dir_ns+0x2b/0xa0 cgroup_create+0x116/0x380 cgroup_mkdir+0x7c/0x1a0 Reproduces deterministically at PID 1 (systemd) on an SELinux-enabled distro. The first cgroup mkdir under /sys/fs/cgroup with a labelled parent panics the kernel. The LSM hook's contract is that the kn_dir argument is the parent of the new kn, so kn->__parent should already point at kn_dir when the hook runs. Move kernfs_get(parent) and rcu_assign_pointer of kn->__parent from kernfs_new_node() into __kernfs_new_node() right before the security hook, and unwind the parent reference on the err_out4 path. kernfs_root(kn) then takes its parent branch during the hook and returns parent->dir.root, which is the correct root. This also closes the same-shape latent bug in kernfs_xattr_get() (which today is hidden only by kernfs_iattrs_noalloc() returning NULL on a fresh kn). Fixes: 12e9e3cd03b5 ("simpe_xattr: use per-sb cache") Reported-by: Calum Mackay Closes: https://lore.kernel.org/all/5386153f-9112-4971-98fc-de90d7aae2c6@oracle.com/ Link: https://patch.msgid.link/20260526-ablief-demut-wehen-aef8446ef5c9@brauner Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/kernfs/dir.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 78812cf1256b9..97d9d227b66d9 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -701,6 +701,9 @@ static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root, } if (parent) { + kernfs_get(parent); + rcu_assign_pointer(kn->__parent, parent); + ret = security_kernfs_init_security(parent, kn); if (ret) goto err_out4; @@ -709,6 +712,8 @@ static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root, return kn; err_out4: + RCU_INIT_POINTER(kn->__parent, NULL); + kernfs_put(parent); if (kn->iattr) { simple_xattrs_free(&root->xa_cache, &kn->iattr->xattrs, NULL); kmem_cache_free(kernfs_iattrs_cache, kn->iattr); @@ -745,10 +750,6 @@ struct kernfs_node *kernfs_new_node(struct kernfs_node *parent, kn = __kernfs_new_node(kernfs_root(parent), parent, name, mode, uid, gid, flags); - if (kn) { - kernfs_get(parent); - rcu_assign_pointer(kn->__parent, parent); - } return kn; } From 18813b90032bfaafb225906a4d2b51be4dfc02c3 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Tue, 21 Apr 2026 13:50:16 +0000 Subject: [PATCH 1195/2758] wifi: wcn36xx: fix heap overflow from oversized firmware HAL response [ Upstream commit 88a240d86d3d64521f9194abe185ac71cc74d0bd ] The firmware response dispatcher copies all synchronous HAL responses into the 4096-byte hal_buf without validating the response length. A response exceeding WCN36XX_HAL_BUF_SIZE causes a heap buffer overflow with firmware-controlled content. Add a bounds check on the response length. Fixes: 8e84c2582169 ("wcn36xx: mac80211 driver for Qualcomm WCN3660/WCN3680 hardware") Signed-off-by: Tristan Madani Reviewed-by: Loic Poulain Link: https://patch.msgid.link/20260421135018.352774-2-tristmd@gmail.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/wcn36xx/smd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c index 813553edcb789..f65328329f4f0 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.c +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -3293,6 +3293,10 @@ int wcn36xx_smd_rsp_process(struct rpmsg_device *rpdev, case WCN36XX_HAL_EXIT_IMPS_RSP: case WCN36XX_HAL_UPDATE_CHANNEL_LIST_RSP: case WCN36XX_HAL_ADD_BCN_FILTER_RSP: + if (len > WCN36XX_HAL_BUF_SIZE) { + wcn36xx_warn("HAL response too large: %d\n", len); + break; + } memcpy(wcn->hal_buf, buf, len); wcn->hal_rsp_len = len; complete(&wcn->hal_rsp_compl); From f987efff29a5fe45320ea5991c9d5cb98b676953 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Tue, 21 Apr 2026 13:50:17 +0000 Subject: [PATCH 1196/2758] wifi: wcn36xx: fix OOB read from firmware count in PRINT_REG_INFO indication [ Upstream commit df2187acfca6c6cca372c5d35f42394d9c270b09 ] The firmware-controlled rsp->count field is used as the loop bound for indexing into the flexible rsp->regs[] array without validation against the message length. A count exceeding the actual data causes out-of- bounds reads from the heap-allocated message buffer. Add a check that count fits within the received message. Fixes: 43efa3c0f241 ("wcn36xx: Implement print_reg indication") Signed-off-by: Tristan Madani Reviewed-by: Loic Poulain Link: https://patch.msgid.link/20260421135018.352774-3-tristmd@gmail.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/wcn36xx/smd.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c index f65328329f4f0..2a0c946d81095 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.c +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -2805,6 +2805,12 @@ static int wcn36xx_smd_print_reg_info_ind(struct wcn36xx *wcn, return -EIO; } + if (rsp->count > (len - sizeof(*rsp)) / sizeof(rsp->regs[0])) { + wcn36xx_warn("Truncated print reg info indication: count %u, len %zu\n", + rsp->count, len); + return -EIO; + } + wcn36xx_dbg(WCN36XX_DBG_HAL, "reginfo indication, scenario: 0x%x reason: 0x%x\n", rsp->scenario, rsp->reason); From d0cafe6ed8d1f6d0097eda31d85f5760d4f359c2 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Tue, 21 Apr 2026 13:50:18 +0000 Subject: [PATCH 1197/2758] wifi: wcn36xx: fix OOB read from short trigger BA firmware response [ Upstream commit b5e6f21923ca89d90256e7346301056f6502691e ] The firmware response length is only checked against sizeof(*rsp) (20 bytes), but when candidate_cnt >= 1, a 22-byte candidate struct is read at buf + 20 without verifying the response contains it. This causes an out-of-bounds read of stale heap data, corrupting the BA session state. Add validation that the response includes the candidate data. Fixes: 16be1ac55944 ("wcn36xx: Parse trigger_ba response properly") Signed-off-by: Tristan Madani Reviewed-by: Loic Poulain Link: https://patch.msgid.link/20260421135018.352774-4-tristmd@gmail.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/wcn36xx/smd.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c index 2a0c946d81095..c0b477345832b 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.c +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -2599,6 +2599,9 @@ static int wcn36xx_smd_trigger_ba_rsp(void *buf, int len, struct add_ba_info *ba if (rsp->candidate_cnt < 1) return rsp->status ? rsp->status : -EINVAL; + if (len < sizeof(*rsp) + sizeof(*candidate)) + return -EINVAL; + candidate = (struct wcn36xx_hal_trigger_ba_rsp_candidate *)(buf + sizeof(*rsp)); for (i = 0; i < STACFG_MAX_TC; i++) { From 890dddd48ecd698d28eeb6abe49709428dd78d98 Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Sat, 6 Jun 2026 13:09:13 +0900 Subject: [PATCH 1198/2758] ALSA: seq: Fix partial userptr event expansion [ Upstream commit 2b7bd6f548292aec92a386deebe62324d21d62a9 ] snd_seq_expand_var_event_at() clamps the number of bytes to copy to the remaining variable-event length, but passes the original buffer size to expand_var_event(). For SNDRV_SEQ_EXT_USRPTR events, expand_var_event() copies exactly the size argument from userspace. On the final chunk, when the remaining event data is shorter than the caller's buffer, this can read past the declared event data and can spuriously fail with -EFAULT if the extra bytes cross an unmapped page. Pass the clamped length instead. The chained and kernel-backed paths already reclamp in dump_var_event(), but the user-pointer path handles the size directly. Fixes: ea46f79709b6 ("ALSA: seq: Add snd_seq_expand_var_event_at() helper") Signed-off-by: HyeongJun An Link: https://patch.msgid.link/20260606040913.230213-1-sammiee5311@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/core/seq/seq_memory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c index 670f6599e002d..209b08c2a9408 100644 --- a/sound/core/seq/seq_memory.c +++ b/sound/core/seq/seq_memory.c @@ -211,7 +211,7 @@ int snd_seq_expand_var_event_at(const struct snd_seq_event *event, int count, len -= offset; if (len > count) len = count; - err = expand_var_event(event, offset, count, buf, true); + err = expand_var_event(event, offset, len, buf, true); if (err < 0) return err; return len; From b02c5430ef2c0858fa182429181f801b5fe11424 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Fri, 5 Jun 2026 12:48:27 -0300 Subject: [PATCH 1199/2758] ALSA: pcm: Fix unlocked runtime state reads in xfer ioctls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 98fe3988a2efe89a1a1ded213a0561e6543e94e2 ] The recent runtime state locking cleanup converted several PCM ioctl state checks to snd_pcm_get_state(), including snd_pcm_pre_prepare(), snd_pcm_drain() and snd_pcm_kernel_ioctl(). The native and compat xfer ioctl paths still sample runtime->state directly before dispatching to the PCM transfer helpers, and snd_pcm_common_ioctl() still samples the DISCONNECTED state directly in its common precheck. Use snd_pcm_get_state() for those ioctl-side prechecks as well. This keeps the externally visible ioctl entry checks consistent with the stream-locked state access used by the recent PCM state-read cleanup. Fixes: 032322b44c02 ("ALSA: pcm: oss: use proper stream lock for runtime->state access") Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260605-alsa-pcm-xfer-state-helper-v1-1-eba97cecf820@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/core/pcm_compat.c | 4 ++-- sound/core/pcm_native.c | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index 5313f50f17da5..55ecf87586c4d 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c @@ -293,7 +293,7 @@ static int snd_pcm_ioctl_xferi_compat(struct snd_pcm_substream *substream, return -ENOTTY; if (substream->stream != dir) return -EINVAL; - if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) + if (snd_pcm_get_state(substream) == SNDRV_PCM_STATE_OPEN) return -EBADFD; if (get_user(buf, &data32->buf) || @@ -338,7 +338,7 @@ static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream, return -ENOTTY; if (substream->stream != dir) return -EINVAL; - if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) + if (snd_pcm_get_state(substream) == SNDRV_PCM_STATE_OPEN) return -EBADFD; ch = substream->runtime->channels; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 302643c1c1921..aa33441696855 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3303,10 +3303,9 @@ static int snd_pcm_xferi_frames_ioctl(struct snd_pcm_substream *substream, struct snd_xferi __user *_xferi) { struct snd_xferi xferi; - struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_sframes_t result; - if (runtime->state == SNDRV_PCM_STATE_OPEN) + if (snd_pcm_get_state(substream) == SNDRV_PCM_STATE_OPEN) return -EBADFD; if (put_user(0, &_xferi->result)) return -EFAULT; @@ -3329,7 +3328,7 @@ static int snd_pcm_xfern_frames_ioctl(struct snd_pcm_substream *substream, void *bufs __free(kfree) = NULL; snd_pcm_sframes_t result; - if (runtime->state == SNDRV_PCM_STATE_OPEN) + if (snd_pcm_get_state(substream) == SNDRV_PCM_STATE_OPEN) return -EBADFD; if (runtime->channels > 128) return -EINVAL; @@ -3392,7 +3391,7 @@ static int snd_pcm_common_ioctl(struct file *file, if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; - if (substream->runtime->state == SNDRV_PCM_STATE_DISCONNECTED) + if (snd_pcm_get_state(substream) == SNDRV_PCM_STATE_DISCONNECTED) return -EBADFD; res = snd_power_wait(substream->pcm->card); From 79f51eb2feec75c4c2bcea9433ca9ab5fd3519d0 Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Sat, 6 Jun 2026 20:17:54 -0600 Subject: [PATCH 1200/2758] riscv: cpu_ops: Change return value type of cpu_is_stopped() to bool [ Upstream commit 2b2b207e1162e577cd6208e184d3d3a0fcfa9cca ] In the original sbi_cpu_is_stopped(), if rc doesn't equal to the SBI_HSM_STATE_STOPPED, it will return rc to the caller directly. But there is a hidden problem, the rc could be SBI_HSM_STATE_STARTED, if so, this function will report cpu stopped while the cpu isn't really stopped. Furthermore, from the name of cpu_is_stopped(), it gives a sense the return value is a bool type, true means the cpu is stopped, conversely false means the cpu is not stopped. Here change the return value type to bool and change the callers accordingly. This could fix the above two issues. Fixes: f1e58583b9c7c ("RISC-V: Support cpu hotplug") Signed-off-by: Hui Wang Link: https://patch.msgid.link/20260413123515.48423-1-hui.wang@canonical.com [pjw@kernel.org: cleaned up some of the pr_warn() messages] Signed-off-by: Paul Walmsley Signed-off-by: Sasha Levin --- arch/riscv/include/asm/cpu_ops.h | 2 +- arch/riscv/kernel/cpu-hotplug.c | 4 ++-- arch/riscv/kernel/cpu_ops_sbi.c | 11 +++++++---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/arch/riscv/include/asm/cpu_ops.h b/arch/riscv/include/asm/cpu_ops.h index 176b570ef9827..065811fca594d 100644 --- a/arch/riscv/include/asm/cpu_ops.h +++ b/arch/riscv/include/asm/cpu_ops.h @@ -24,7 +24,7 @@ struct cpu_operations { struct task_struct *tidle); #ifdef CONFIG_HOTPLUG_CPU void (*cpu_stop)(void); - int (*cpu_is_stopped)(unsigned int cpu); + bool (*cpu_is_stopped)(unsigned int cpu); #endif }; diff --git a/arch/riscv/kernel/cpu-hotplug.c b/arch/riscv/kernel/cpu-hotplug.c index a0ee426f6d938..0bc56d8381b6b 100644 --- a/arch/riscv/kernel/cpu-hotplug.c +++ b/arch/riscv/kernel/cpu-hotplug.c @@ -57,8 +57,8 @@ void arch_cpuhp_cleanup_dead_cpu(unsigned int cpu) /* Verify from the firmware if the cpu is really stopped*/ if (cpu_ops->cpu_is_stopped) ret = cpu_ops->cpu_is_stopped(cpu); - if (ret) - pr_warn("CPU%u may not have stopped: %d\n", cpu, ret); + if (!ret) + pr_warn("CPU%u may not have stopped\n", cpu); } /* diff --git a/arch/riscv/kernel/cpu_ops_sbi.c b/arch/riscv/kernel/cpu_ops_sbi.c index 00aff669f5f2f..146ceab1011fe 100644 --- a/arch/riscv/kernel/cpu_ops_sbi.c +++ b/arch/riscv/kernel/cpu_ops_sbi.c @@ -88,16 +88,19 @@ static void sbi_cpu_stop(void) pr_crit("Unable to stop the cpu %d (%d)\n", smp_processor_id(), ret); } -static int sbi_cpu_is_stopped(unsigned int cpuid) +static bool sbi_cpu_is_stopped(unsigned int cpuid) { int rc; unsigned long hartid = cpuid_to_hartid_map(cpuid); rc = sbi_hsm_hart_get_status(hartid); - if (rc == SBI_HSM_STATE_STOPPED) - return 0; - return rc; + if (rc != SBI_HSM_STATE_STOPPED) { + pr_warn("HART%lu isn't stopped; status %d\n", hartid, rc); + return false; + } + + return true; } #endif From ec54e525cb6b5bfa1164ab519901fe2e71beb4c8 Mon Sep 17 00:00:00 2001 From: Rui Qi Date: Sat, 6 Jun 2026 20:17:59 -0600 Subject: [PATCH 1201/2758] riscv: stacktrace: Remove bogus -0x4 offset in non-FP walk_stackframe [ Upstream commit 8ac35bac70e7e581d673b76878f7691cdadc33b8 ] In the non-frame-pointer version of walk_stackframe, each value read from the stack is treated as a potential return address and has 0x4 subtracted before being used as the program counter. This was intended to convert the return address (the instruction after a call) back to the call site, but it is incorrect: 1. RISC-V has variable-length instructions due to the RVC (compressed instruction) extension. A call instruction can be either 4 bytes (regular) or 2 bytes (compressed, e.g. c.jal). Subtracting a fixed 0x4 assumes all call instructions are 4 bytes, which is wrong for compressed instructions. 2. Stack traces conventionally report return addresses, not call sites. Other architectures (ARM64, x86, ARM) do not subtract instruction size from return addresses in their stack unwinding code. 3. The frame-pointer version of walk_stackframe already dropped the -0x4 offset. Commit b785ec129bd9 ("riscv/ftrace: Add HAVE_FUNCTION_GRAPH_RET_ADDR_PTR support") replaced "pc = frame->ra - 0x4" with ftrace_graph_ret_addr(), and the commit message explicitly noted that "the original calculation, pc = frame->ra - 4, is buggy when the instruction at the return address happened to be a compressed inst." The non-FP version was simply overlooked. Remove the bogus -0x4 offset to match the FP version and the conventions used by other architectures. Fixes: 5d8544e2d007 ("RISC-V: Generic library routines and assembly") Signed-off-by: Rui Qi Link: https://patch.msgid.link/20260603115329.791603-2-qirui.001@bytedance.com Signed-off-by: Paul Walmsley Signed-off-by: Sasha Levin --- arch/riscv/kernel/stacktrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/kernel/stacktrace.c b/arch/riscv/kernel/stacktrace.c index b41b6255751cb..31fd3abb57d32 100644 --- a/arch/riscv/kernel/stacktrace.c +++ b/arch/riscv/kernel/stacktrace.c @@ -129,7 +129,7 @@ void notrace walk_stackframe(struct task_struct *task, while (!kstack_end(ksp)) { if (__kernel_text_address(pc) && unlikely(!fn(arg, pc))) break; - pc = READ_ONCE_NOCHECK(*ksp++) - 0x4; + pc = READ_ONCE_NOCHECK(*ksp++); } } From c2ac9287e89916da684c2a548798351e63eb59ee Mon Sep 17 00:00:00 2001 From: Kyle Zeng Date: Sat, 6 Jun 2026 17:41:29 -0700 Subject: [PATCH 1202/2758] ALSA: seq: Clear variable event pointer on read [ Upstream commit 705dd6dcbc0ea87351c660c1a6443f85f1001c76 ] snd_seq_read() copies a queued variable-length event header to userspace before expanding the payload. Queued variable-length events use SNDRV_SEQ_EXT_CHAINED internally, and data.ext.ptr points at the first extension cell. The read side strips SNDRV_SEQ_EXT_* bits from data.ext.len before the copy, but it leaves data.ext.ptr untouched. A userspace sequencer client can therefore write a direct variable event to itself and read back the extension-cell kernel address from the returned header. Clear the temporary header pointer before copy_to_user(). The original queued event remains unchanged and is still passed to snd_seq_expand_var_event(), so payload expansion keeps using the internal chain. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Kyle Zeng Link: https://patch.msgid.link/20260607004129.61345-1-kylebot@openai.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/core/seq/seq_clientmgr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 5719637575a91..ce0dd53399bfa 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -441,6 +441,7 @@ static ssize_t snd_seq_read(struct file *file, char __user *buf, size_t count, memcpy(&tmpev, &cell->event, aligned_size); tmpev.data.ext.len &= ~SNDRV_SEQ_EXT_MASK; + tmpev.data.ext.ptr = NULL; if (copy_to_user(buf, &tmpev, aligned_size)) { err = -EFAULT; break; From dd381ed4528a737a12f9c46013a56ac22423f527 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 4 May 2026 08:30:51 +0200 Subject: [PATCH 1203/2758] riscv: alternative: Use IS_ENABLED() over ifdeffery for apply_vdso_alternatives() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 4fd6505f189d447abbed1f1f6fe6b82649f27a46 ] IS_ENABLED() allows better compilation coverage while still optimizing away all the dead code. Also it will make some upcoming changes easier. Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260504-riscv-cfi-vdso-alternative-v1-2-bcdf3d37f62e@linutronix.de Signed-off-by: Paul Walmsley Stable-dep-of: d3e0634787a2 ("riscv: alternative: Also patch the CFI vDSO") Signed-off-by: Sasha Levin --- arch/riscv/kernel/alternative.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/arch/riscv/kernel/alternative.c b/arch/riscv/kernel/alternative.c index 7642704c7f184..59991922a5dca 100644 --- a/arch/riscv/kernel/alternative.c +++ b/arch/riscv/kernel/alternative.c @@ -173,7 +173,6 @@ static void __init_or_module _apply_alternatives(struct alt_entry *begin, stage); } -#ifdef CONFIG_MMU static void __init apply_vdso_alternatives(void) { const Elf_Ehdr *hdr; @@ -194,9 +193,6 @@ static void __init apply_vdso_alternatives(void) (struct alt_entry *)end, RISCV_ALTERNATIVES_BOOT); } -#else -static void __init apply_vdso_alternatives(void) { } -#endif void __init apply_boot_alternatives(void) { @@ -207,7 +203,8 @@ void __init apply_boot_alternatives(void) (struct alt_entry *)__alt_end, RISCV_ALTERNATIVES_BOOT); - apply_vdso_alternatives(); + if (IS_ENABLED(CONFIG_MMU)) + apply_vdso_alternatives(); } /* From a24b3d9c8d70b05776b2260b4e60de43614da61e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 4 May 2026 08:30:52 +0200 Subject: [PATCH 1204/2758] riscv: alternative: Pass vDSO start as parameter to apply_vdso_alternatives() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 6386161abb02880ace2cc965e73f7857b351706c ] The dedicated vDSO with CFI should also be patched in the same way. To prepare for that move the currently hardcoded vDSO start symbol into a parameter. Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260504-riscv-cfi-vdso-alternative-v1-3-bcdf3d37f62e@linutronix.de Signed-off-by: Paul Walmsley Stable-dep-of: d3e0634787a2 ("riscv: alternative: Also patch the CFI vDSO") Signed-off-by: Sasha Levin --- arch/riscv/kernel/alternative.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/riscv/kernel/alternative.c b/arch/riscv/kernel/alternative.c index 59991922a5dca..89c283a5cec7f 100644 --- a/arch/riscv/kernel/alternative.c +++ b/arch/riscv/kernel/alternative.c @@ -173,14 +173,14 @@ static void __init_or_module _apply_alternatives(struct alt_entry *begin, stage); } -static void __init apply_vdso_alternatives(void) +static void __init apply_vdso_alternatives(void *start) { const Elf_Ehdr *hdr; const Elf_Shdr *shdr; const Elf_Shdr *alt; struct alt_entry *begin, *end; - hdr = (Elf_Ehdr *)vdso_start; + hdr = (Elf_Ehdr *)start; shdr = (void *)hdr + hdr->e_shoff; alt = find_section(hdr, shdr, ".alternative"); if (!alt) @@ -204,7 +204,7 @@ void __init apply_boot_alternatives(void) RISCV_ALTERNATIVES_BOOT); if (IS_ENABLED(CONFIG_MMU)) - apply_vdso_alternatives(); + apply_vdso_alternatives(vdso_start); } /* From a88a476986908b32d1f9d212b2d21fbbb573bcbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 4 May 2026 08:30:53 +0200 Subject: [PATCH 1205/2758] riscv: alternative: Also patch the CFI vDSO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit d3e0634787a234b40a740b9c398fd320a68db81c ] The dedicated vDSO for CFI-enabled userspace can also contain alternative entries. Patch those, too. Fixes: ccad8c1336b6 ("arch/riscv: add dual vdso creation logic and select vdso based on hw") Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260504-riscv-cfi-vdso-alternative-v1-4-bcdf3d37f62e@linutronix.de Signed-off-by: Paul Walmsley Signed-off-by: Sasha Levin --- arch/riscv/kernel/alternative.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/riscv/kernel/alternative.c b/arch/riscv/kernel/alternative.c index 89c283a5cec7f..104dc0862c5c2 100644 --- a/arch/riscv/kernel/alternative.c +++ b/arch/riscv/kernel/alternative.c @@ -205,6 +205,9 @@ void __init apply_boot_alternatives(void) if (IS_ENABLED(CONFIG_MMU)) apply_vdso_alternatives(vdso_start); + + if (IS_ENABLED(CONFIG_RISCV_USER_CFI)) + apply_vdso_alternatives(vdso_cfi_start); } /* From 23b933c6223f873ce7923704365b6efcab24d8b8 Mon Sep 17 00:00:00 2001 From: Mykyta Yatsenko Date: Wed, 22 Apr 2026 12:41:09 -0700 Subject: [PATCH 1206/2758] bpf: Verifier support for sleepable tracepoint programs [ Upstream commit 8cfb77d3092052b52582e804e644202e2b10167a ] Allow BPF_PROG_TYPE_RAW_TRACEPOINT, BPF_PROG_TYPE_TRACEPOINT, and BPF_TRACE_RAW_TP (tp_btf) programs to be sleepable by adding them to can_be_sleepable(). For BTF-based raw tracepoints (tp_btf), add a load-time check in bpf_check_attach_target() that rejects sleepable programs attaching to non-faultable tracepoints with a descriptive error message. For raw tracepoints (raw_tp), add an attach-time check in bpf_raw_tp_link_attach() that rejects sleepable programs on non-faultable tracepoints. The attach-time check is needed because the tracepoint name is not known at load time for raw_tp. The attach-time check for classic tracepoints (tp) in __perf_event_set_bpf_prog() was added in the previous patch. Replace the verbose error message that enumerates allowed program types with a generic "Program of this type cannot be sleepable" message, since the list of sleepable-capable types keeps growing. Signed-off-by: Mykyta Yatsenko Acked-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/bpf/20260422-sleepable_tracepoints-v13-4-99005dff21ef@meta.com Signed-off-by: Kumar Kartikeya Dwivedi Stable-dep-of: 5b038319be44 ("bpf: Reject sleepable BPF_LSM_CGROUP programs at load time") Signed-off-by: Sasha Levin --- kernel/bpf/syscall.c | 5 +++++ kernel/bpf/verifier.c | 13 +++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index bd1250a6c1e1f..d366797088558 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -4288,6 +4288,11 @@ static int bpf_raw_tp_link_attach(struct bpf_prog *prog, if (!btp) return -ENOENT; + if (prog->sleepable && !tracepoint_is_faultable(btp->tp)) { + bpf_put_raw_tracepoint(btp); + return -EINVAL; + } + link = kzalloc_obj(*link, GFP_USER); if (!link) { err = -ENOMEM; diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index ff9b1f68ceca4..16da9f76804aa 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -19317,6 +19317,12 @@ int bpf_check_attach_target(struct bpf_verifier_log *log, btp = bpf_get_raw_tracepoint(tname); if (!btp) return -EINVAL; + if (prog->sleepable && !tracepoint_is_faultable(btp->tp)) { + bpf_log(log, "Sleepable program cannot attach to non-faultable tracepoint %s\n", + tname); + bpf_put_raw_tracepoint(btp); + return -EINVAL; + } fname = kallsyms_lookup((unsigned long)btp->bpf_func, NULL, NULL, NULL, trace_symbol); bpf_put_raw_tracepoint(btp); @@ -19533,6 +19539,7 @@ static bool can_be_sleepable(struct bpf_prog *prog) case BPF_MODIFY_RETURN: case BPF_TRACE_ITER: case BPF_TRACE_FSESSION: + case BPF_TRACE_RAW_TP: return true; default: return false; @@ -19540,7 +19547,9 @@ static bool can_be_sleepable(struct bpf_prog *prog) } return prog->type == BPF_PROG_TYPE_LSM || prog->type == BPF_PROG_TYPE_KPROBE /* only for uprobes */ || - prog->type == BPF_PROG_TYPE_STRUCT_OPS; + prog->type == BPF_PROG_TYPE_STRUCT_OPS || + prog->type == BPF_PROG_TYPE_RAW_TRACEPOINT || + prog->type == BPF_PROG_TYPE_TRACEPOINT; } static int check_attach_btf_id(struct bpf_verifier_env *env) @@ -19562,7 +19571,7 @@ static int check_attach_btf_id(struct bpf_verifier_env *env) } if (prog->sleepable && !can_be_sleepable(prog)) { - verbose(env, "Only fentry/fexit/fsession/fmod_ret, lsm, iter, uprobe, and struct_ops programs can be sleepable\n"); + verbose(env, "Program of this type cannot be sleepable\n"); return -EINVAL; } From be9eaf2bb5db4ad3de61ef739fd268fd7f135737 Mon Sep 17 00:00:00 2001 From: David Windsor Date: Fri, 5 Jun 2026 10:57:07 -0400 Subject: [PATCH 1207/2758] bpf: Reject sleepable BPF_LSM_CGROUP programs at load time [ Upstream commit 5b038319be442c620f774e6fc9e9283deeca1c75 ] The cgroup shim runs under rcu_read_lock_dont_migrate(), so we should not attach any sleepable BPF programs there. Add support to the verifier to explicitly reject attempts to load sleepable BPF programs destined for LSM cgroup attachment. Without this, we get the following splat from a BPF_LSM_CGROUP program marked BPF_F_SLEEPABLE attached to file_open when it calls bpf_get_dentry_xattr(): BUG: sleeping function called from invalid context at kernel/locking/rwsem.c:1567 in_atomic(): 0, irqs_disabled(): 0, non_block: 0, pid: 34317, name: load preempt_count: 0, expected: 0 RCU nest depth: 2, expected: 0 Call Trace: down_read+0x76/0x480 ext4_xattr_get+0x11f/0x700 __vfs_getxattr+0xf0/0x150 bpf_get_dentry_xattr+0xbb/0xf0 bpf_prog_e76a298dac9218c6_test_open+0x6a/0x85 __cgroup_bpf_run_lsm_current+0x326/0x840 bpf_trampoline_6442534646+0x62/0x14d security_file_open+0x34/0x60 do_dentry_open+0x340/0x1260 vfs_open+0x7a/0x440 path_openat+0x1bac/0x30a0 libbpf provides a .s named section variant for every sleepable program type except lsm_cgroup, reflecting that per-cgroup LSM programs are intended to only run in a non-sleepable context. The above splat was obtained by bypassing libbpf by using bpf(2) directly. Fixes: 69fd337a975c ("bpf: per-cgroup lsm flavor") Signed-off-by: David Windsor Acked-by: Yonghong Song Acked-by: Song Liu Link: https://lore.kernel.org/bpf/20260605145707.608579-1-dwindsor@gmail.com Signed-off-by: Kumar Kartikeya Dwivedi Signed-off-by: Sasha Levin --- kernel/bpf/verifier.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 16da9f76804aa..c34bf4169d5c9 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -19545,8 +19545,10 @@ static bool can_be_sleepable(struct bpf_prog *prog) return false; } } - return prog->type == BPF_PROG_TYPE_LSM || - prog->type == BPF_PROG_TYPE_KPROBE /* only for uprobes */ || + if (prog->type == BPF_PROG_TYPE_LSM) + return prog->expected_attach_type != BPF_LSM_CGROUP; + + return prog->type == BPF_PROG_TYPE_KPROBE /* only for uprobes */ || prog->type == BPF_PROG_TYPE_STRUCT_OPS || prog->type == BPF_PROG_TYPE_RAW_TRACEPOINT || prog->type == BPF_PROG_TYPE_TRACEPOINT; From 2f55fa28011c97d6495d5787808db10a8c2d690d Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 23 May 2026 16:26:21 +0100 Subject: [PATCH 1208/2758] netfilter: flowtable: avoid num_encaps underflow on bridge VLAN untag [ Upstream commit e052f920773b73be49eb4d8702a9f85de7464363 ] The DEV_PATH_BR_VLAN_UNTAG case post-decrements info->num_encaps inside WARN_ON_ONCE(). num_encaps is u8, so if it's already 0 the decrement still happens and wraps it to 255. The break only leaves the inner switch -- a later path entry can set info->indev back to a real device, and we end up returning with num_encaps == 255. nft_dev_forward_path() then walks info.encap[] (size 2) up to num_encaps, which means an OOB stack read and a bogus count copied into the route descriptor. Should only happen on a malformed bridge path stack, hence the WARN, but worth handling sanely. Move the decrement out of the WARN. [ While at this, remove the WARN_ON_ONCE since this can only happen with a buggy bridge path stack --pablo ]. Fixes: e990cef6516d ("netfilter: flowtable: add bridge vlan filtering support") Signed-off-by: David Carlier Reviewed-by: Fernando Fernandez Mancera Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_flow_table_path.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/netfilter/nf_flow_table_path.c b/net/netfilter/nf_flow_table_path.c index 9e88ea6a2eef7..a3e6b82f2f8e9 100644 --- a/net/netfilter/nf_flow_table_path.c +++ b/net/netfilter/nf_flow_table_path.c @@ -163,10 +163,11 @@ static void nft_dev_path_info(const struct net_device_path_stack *stack, info->num_encaps++; break; case DEV_PATH_BR_VLAN_UNTAG: - if (WARN_ON_ONCE(info->num_encaps-- == 0)) { + if (info->num_encaps == 0) { info->indev = NULL; break; } + info->num_encaps--; break; case DEV_PATH_BR_VLAN_KEEP: break; From 716566c6c82c6a4340cbee820557ba9ef00ac4dc Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Sun, 7 Jun 2026 11:40:28 +0200 Subject: [PATCH 1209/2758] filelock: fix break_lease() stub signature for CONFIG_FILE_LOCKING=n [ Upstream commit 4bbcff264b678859cc404669bd145bcd6819804b ] The CONFIG_FILE_LOCKING=n stub for break_lease() takes a 'bool wait' argument, whereas the CONFIG_FILE_LOCKING=y version and every caller pass an openmode as an 'unsigned int mode'. The mismatch was introduced when __break_lease() was reworked to use flags: only the stub was switched to 'bool wait', a stray leftover from the neighbouring break_layout() helper. The real prototype kept 'unsigned int mode'. This was harmless until O_WRONLY changed from the octal literal 00000001 to (1 << 0). clang's -Wtautological-constant-compare then fires on the implicit shift-to-bool conversion at the first FILE_LOCKING=n caller: fs/open.c:112:29: warning: converting the result of '<<' to a boolean always evaluates to true [-Wtautological-constant-compare] 112 | error = break_lease(inode, O_WRONLY); Restore the stub's parameter to 'unsigned int mode' so it matches the real prototype and every caller. The stub still just returns 0, so there is no functional change; it removes the type inconsistency and silences the warning. Root cause diagnosed by Nathan Chancellor. Fixes: 4be9f3cc582a ("filelock: rework the __break_lease API to use flags") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202606071029.DKCs8WOs-lkp@intel.com/ Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- include/linux/filelock.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/filelock.h b/include/linux/filelock.h index 5f0a2fb314506..77e1cc4afbaa8 100644 --- a/include/linux/filelock.h +++ b/include/linux/filelock.h @@ -564,7 +564,7 @@ static inline bool is_delegated(struct delegated_inode *di) return false; } -static inline int break_lease(struct inode *inode, bool wait) +static inline int break_lease(struct inode *inode, unsigned int mode) { return 0; } From 440a2fdbb40608d55a7b11f2be53592a3785131d Mon Sep 17 00:00:00 2001 From: Mykyta Yatsenko Date: Sun, 7 Jun 2026 13:30:41 -0700 Subject: [PATCH 1210/2758] bpf: Fix NMI/tracepoint re-entry deadlock on lru locks [ Upstream commit 89edbdfc5d0308cef57b71359331de5c4ddbf763 ] NMI and tracepoint BPF programs can re-enter the per-CPU or global LRU lock that bpf_lru_pop_free()/push_free() already hold on the same CPU, AA-deadlocking. Lockdep reports "inconsistent {INITIAL USE} -> {IN-NMI}" on &l->lock (syzbot c69a0a2c816716f1e0d5) and "possible recursive locking detected" on &loc_l->lock (syzbot 18b26edb69b2e19f3b33). Prior trylock and rqspinlock based fixes (see links) were nacked because compromised on reliability. This patch converts every LRU lock site to rqspinlock_t and adds a recovery path for some failure windows to avoid node leaks. Failure recovery: - *_pop_free top-level: return NULL; prealloc_lru_pop() already treats that as no-free-element (-ENOMEM). - Cross-CPU steal: skip the victim's locked loc_l, try next CPU. - Post-steal local lock fail: publish stolen node to lockless per-CPU free_llist; next pop on this CPU picks it up. - push_free fail: mark node pending_free=1. __local_list_flush(), __local_list_pop_pending() reclaim the node from pending_list. __bpf_lru_list_shrink_inactive() reclaims the node from inactive list. Nodes from active list are reclaimed by __bpf_lru_list_shrink() or after __bpf_lru_list_rotate_active() demotes it to the inactive. Fixes: 3a08c2fd7634 ("bpf: LRU List") Reported-by: syzbot+c69a0a2c816716f1e0d5@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=c69a0a2c816716f1e0d5 Reported-by: syzbot+18b26edb69b2e19f3b33@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=18b26edb69b2e19f3b33 Link: https://lore.kernel.org/bpf/CAPPBnEYO4R+m+SpVc2gNj_x31R6fo1uJvj2bK2YS1P09GWT6kQ@mail.gmail.com/ Link: https://lore.kernel.org/bpf/CAPPBnEZmFA3ab8Uc=PEm0bdojZy=7T_F5_+eyZSHyZR3MBG4Vw@mail.gmail.com/ Link: https://lore.kernel.org/bpf/20251030030010.95352-1-dongml2@chinatelecom.cn/ Link: https://lore.kernel.org/bpf/20260119142120.28170-1-leon.hwang@linux.dev/ Signed-off-by: Mykyta Yatsenko Link: https://lore.kernel.org/r/20260607-lru_map_spin-v3-1-bcd9332e911b@meta.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- kernel/bpf/bpf_lru_list.c | 165 +++++++++++++++++++++++--------------- kernel/bpf/bpf_lru_list.h | 25 ++++-- 2 files changed, 119 insertions(+), 71 deletions(-) diff --git a/kernel/bpf/bpf_lru_list.c b/kernel/bpf/bpf_lru_list.c index e7a2fc60523f6..5ed7cb4b98c00 100644 --- a/kernel/bpf/bpf_lru_list.c +++ b/kernel/bpf/bpf_lru_list.c @@ -13,23 +13,8 @@ #define PERCPU_FREE_TARGET (4) #define PERCPU_NR_SCANS PERCPU_FREE_TARGET -/* Helpers to get the local list index */ -#define LOCAL_LIST_IDX(t) ((t) - BPF_LOCAL_LIST_T_OFFSET) -#define LOCAL_FREE_LIST_IDX LOCAL_LIST_IDX(BPF_LRU_LOCAL_LIST_T_FREE) -#define LOCAL_PENDING_LIST_IDX LOCAL_LIST_IDX(BPF_LRU_LOCAL_LIST_T_PENDING) #define IS_LOCAL_LIST_TYPE(t) ((t) >= BPF_LOCAL_LIST_T_OFFSET) -/* Local list helpers */ -static struct list_head *local_free_list(struct bpf_lru_locallist *loc_l) -{ - return &loc_l->lists[LOCAL_FREE_LIST_IDX]; -} - -static struct list_head *local_pending_list(struct bpf_lru_locallist *loc_l) -{ - return &loc_l->lists[LOCAL_PENDING_LIST_IDX]; -} - /* bpf_lru_node helpers */ static bool bpf_lru_node_is_ref(const struct bpf_lru_node *node) { @@ -72,6 +57,7 @@ static void __bpf_lru_node_move_to_free(struct bpf_lru_list *l, bpf_lru_list_count_dec(l, node->type); node->type = tgt_free_type; + WRITE_ONCE(node->pending_free, 0); list_move(&node->list, free_list); } @@ -87,6 +73,9 @@ static void __bpf_lru_node_move_in(struct bpf_lru_list *l, bpf_lru_list_count_inc(l, tgt_type); node->type = tgt_type; bpf_lru_node_clear_ref(node); + /* Reset pending_free only when moving to the free list */ + if (tgt_type == BPF_LRU_LIST_T_FREE) + WRITE_ONCE(node->pending_free, 0); list_move(&node->list, &l->lists[tgt_type]); } @@ -212,9 +201,11 @@ __bpf_lru_list_shrink_inactive(struct bpf_lru *lru, unsigned int i = 0; list_for_each_entry_safe_reverse(node, tmp_node, inactive, list) { - if (bpf_lru_node_is_ref(node)) { + if (bpf_lru_node_is_ref(node) && + !READ_ONCE(node->pending_free)) { __bpf_lru_node_move(l, node, BPF_LRU_LIST_T_ACTIVE); - } else if (lru->del_from_htab(lru->del_arg, node)) { + } else if (READ_ONCE(node->pending_free) || + lru->del_from_htab(lru->del_arg, node)) { __bpf_lru_node_move_to_free(l, node, free_list, tgt_free_type); if (++nshrinked == tgt_nshrink) @@ -273,7 +264,8 @@ static unsigned int __bpf_lru_list_shrink(struct bpf_lru *lru, list_for_each_entry_safe_reverse(node, tmp_node, force_shrink_list, list) { - if (lru->del_from_htab(lru->del_arg, node)) { + if (READ_ONCE(node->pending_free) || + lru->del_from_htab(lru->del_arg, node)) { __bpf_lru_node_move_to_free(l, node, free_list, tgt_free_type); return 1; @@ -290,8 +282,10 @@ static void __local_list_flush(struct bpf_lru_list *l, struct bpf_lru_node *node, *tmp_node; list_for_each_entry_safe_reverse(node, tmp_node, - local_pending_list(loc_l), list) { - if (bpf_lru_node_is_ref(node)) + &loc_l->pending_list, list) { + if (READ_ONCE(node->pending_free)) + __bpf_lru_node_move_in(l, node, BPF_LRU_LIST_T_FREE); + else if (bpf_lru_node_is_ref(node)) __bpf_lru_node_move_in(l, node, BPF_LRU_LIST_T_ACTIVE); else __bpf_lru_node_move_in(l, node, @@ -307,9 +301,12 @@ static void bpf_lru_list_push_free(struct bpf_lru_list *l, if (WARN_ON_ONCE(IS_LOCAL_LIST_TYPE(node->type))) return; - raw_spin_lock_irqsave(&l->lock, flags); + if (raw_res_spin_lock_irqsave(&l->lock, flags)) { + WRITE_ONCE(node->pending_free, 1); + return; + } __bpf_lru_node_move(l, node, BPF_LRU_LIST_T_FREE); - raw_spin_unlock_irqrestore(&l->lock, flags); + raw_res_spin_unlock_irqrestore(&l->lock, flags); } static void bpf_lru_list_pop_free_to_local(struct bpf_lru *lru, @@ -318,8 +315,10 @@ static void bpf_lru_list_pop_free_to_local(struct bpf_lru *lru, struct bpf_lru_list *l = &lru->common_lru.lru_list; struct bpf_lru_node *node, *tmp_node; unsigned int nfree = 0; + LIST_HEAD(tmp_free); - raw_spin_lock(&l->lock); + if (raw_res_spin_lock(&l->lock)) + return; __local_list_flush(l, loc_l); @@ -327,7 +326,7 @@ static void bpf_lru_list_pop_free_to_local(struct bpf_lru *lru, list_for_each_entry_safe(node, tmp_node, &l->lists[BPF_LRU_LIST_T_FREE], list) { - __bpf_lru_node_move_to_free(l, node, local_free_list(loc_l), + __bpf_lru_node_move_to_free(l, node, &tmp_free, BPF_LRU_LOCAL_LIST_T_FREE); if (++nfree == lru->target_free) break; @@ -335,10 +334,19 @@ static void bpf_lru_list_pop_free_to_local(struct bpf_lru *lru, if (nfree < lru->target_free) __bpf_lru_list_shrink(lru, l, lru->target_free - nfree, - local_free_list(loc_l), + &tmp_free, BPF_LRU_LOCAL_LIST_T_FREE); - raw_spin_unlock(&l->lock); + raw_res_spin_unlock(&l->lock); + + /* + * Transfer the harvested nodes from the temporary list_head into + * the lockless per-CPU free llist. + */ + list_for_each_entry_safe(node, tmp_node, &tmp_free, list) { + list_del(&node->list); + llist_add(&node->llist, &loc_l->free_llist); + } } static void __local_list_add_pending(struct bpf_lru *lru, @@ -350,22 +358,21 @@ static void __local_list_add_pending(struct bpf_lru *lru, *(u32 *)((void *)node + lru->hash_offset) = hash; node->cpu = cpu; node->type = BPF_LRU_LOCAL_LIST_T_PENDING; + WRITE_ONCE(node->pending_free, 0); bpf_lru_node_clear_ref(node); - list_add(&node->list, local_pending_list(loc_l)); + list_add(&node->list, &loc_l->pending_list); } static struct bpf_lru_node * __local_list_pop_free(struct bpf_lru_locallist *loc_l) { - struct bpf_lru_node *node; + struct llist_node *llnode; - node = list_first_entry_or_null(local_free_list(loc_l), - struct bpf_lru_node, - list); - if (node) - list_del(&node->list); + llnode = llist_del_first(&loc_l->free_llist); + if (!llnode) + return NULL; - return node; + return container_of(llnode, struct bpf_lru_node, llist); } static struct bpf_lru_node * @@ -376,10 +383,10 @@ __local_list_pop_pending(struct bpf_lru *lru, struct bpf_lru_locallist *loc_l) ignore_ref: /* Get from the tail (i.e. older element) of the pending list. */ - list_for_each_entry_reverse(node, local_pending_list(loc_l), - list) { + list_for_each_entry_reverse(node, &loc_l->pending_list, list) { if ((!bpf_lru_node_is_ref(node) || force) && - lru->del_from_htab(lru->del_arg, node)) { + (READ_ONCE(node->pending_free) || + lru->del_from_htab(lru->del_arg, node))) { list_del(&node->list); return node; } @@ -404,7 +411,8 @@ static struct bpf_lru_node *bpf_percpu_lru_pop_free(struct bpf_lru *lru, l = per_cpu_ptr(lru->percpu_lru, cpu); - raw_spin_lock_irqsave(&l->lock, flags); + if (raw_res_spin_lock_irqsave(&l->lock, flags)) + return NULL; __bpf_lru_list_rotate(lru, l); @@ -420,7 +428,7 @@ static struct bpf_lru_node *bpf_percpu_lru_pop_free(struct bpf_lru *lru, __bpf_lru_node_move(l, node, BPF_LRU_LIST_T_INACTIVE); } - raw_spin_unlock_irqrestore(&l->lock, flags); + raw_res_spin_unlock_irqrestore(&l->lock, flags); return node; } @@ -437,7 +445,8 @@ static struct bpf_lru_node *bpf_common_lru_pop_free(struct bpf_lru *lru, loc_l = per_cpu_ptr(clru->local_list, cpu); - raw_spin_lock_irqsave(&loc_l->lock, flags); + if (raw_res_spin_lock_irqsave(&loc_l->lock, flags)) + return NULL; node = __local_list_pop_free(loc_l); if (!node) { @@ -448,17 +457,22 @@ static struct bpf_lru_node *bpf_common_lru_pop_free(struct bpf_lru *lru, if (node) __local_list_add_pending(lru, loc_l, cpu, node, hash); - raw_spin_unlock_irqrestore(&loc_l->lock, flags); + raw_res_spin_unlock_irqrestore(&loc_l->lock, flags); if (node) return node; - /* No free nodes found from the local free list and + /* + * No free nodes found from the local free list and * the global LRU list. * * Steal from the local free/pending list of the * current CPU and remote CPU in RR. It starts * with the loc_l->next_steal CPU. + * + * Acquire the victim's lock before touching either list. On + * acquisition failure (rqspinlock AA or timeout) skip the victim + * and try the next CPU. */ first_steal = loc_l->next_steal; @@ -466,24 +480,36 @@ static struct bpf_lru_node *bpf_common_lru_pop_free(struct bpf_lru *lru, do { steal_loc_l = per_cpu_ptr(clru->local_list, steal); - raw_spin_lock_irqsave(&steal_loc_l->lock, flags); - - node = __local_list_pop_free(steal_loc_l); - if (!node) - node = __local_list_pop_pending(lru, steal_loc_l); - - raw_spin_unlock_irqrestore(&steal_loc_l->lock, flags); + if (!raw_res_spin_lock_irqsave(&steal_loc_l->lock, flags)) { + node = __local_list_pop_free(steal_loc_l); + if (!node) + node = __local_list_pop_pending(lru, steal_loc_l); + raw_res_spin_unlock_irqrestore(&steal_loc_l->lock, flags); + } steal = cpumask_next_wrap(steal, cpu_possible_mask); } while (!node && steal != first_steal); loc_l->next_steal = steal; - if (node) { - raw_spin_lock_irqsave(&loc_l->lock, flags); - __local_list_add_pending(lru, loc_l, cpu, node, hash); - raw_spin_unlock_irqrestore(&loc_l->lock, flags); + if (!node) + return NULL; + + if (raw_res_spin_lock_irqsave(&loc_l->lock, flags)) { + /* + * The local pending lock can't be acquired (rqspinlock AA + * or timeout). Return the stolen node to the per-CPU + * free_llist instead of orphaning it; the next pop_free on + * this CPU will pick it up. + */ + node->type = BPF_LRU_LOCAL_LIST_T_FREE; + bpf_lru_node_clear_ref(node); + WRITE_ONCE(node->pending_free, 0); + llist_add(&node->llist, &loc_l->free_llist); + return NULL; } + __local_list_add_pending(lru, loc_l, cpu, node, hash); + raw_res_spin_unlock_irqrestore(&loc_l->lock, flags); return node; } @@ -511,18 +537,24 @@ static void bpf_common_lru_push_free(struct bpf_lru *lru, loc_l = per_cpu_ptr(lru->common_lru.local_list, node->cpu); - raw_spin_lock_irqsave(&loc_l->lock, flags); + if (raw_res_spin_lock_irqsave(&loc_l->lock, flags)) { + WRITE_ONCE(node->pending_free, 1); + return; + } if (unlikely(node->type != BPF_LRU_LOCAL_LIST_T_PENDING)) { - raw_spin_unlock_irqrestore(&loc_l->lock, flags); + raw_res_spin_unlock_irqrestore(&loc_l->lock, + flags); goto check_lru_list; } node->type = BPF_LRU_LOCAL_LIST_T_FREE; bpf_lru_node_clear_ref(node); - list_move(&node->list, local_free_list(loc_l)); + list_del(&node->list); + + raw_res_spin_unlock_irqrestore(&loc_l->lock, flags); - raw_spin_unlock_irqrestore(&loc_l->lock, flags); + llist_add(&node->llist, &loc_l->free_llist); return; } @@ -538,11 +570,14 @@ static void bpf_percpu_lru_push_free(struct bpf_lru *lru, l = per_cpu_ptr(lru->percpu_lru, node->cpu); - raw_spin_lock_irqsave(&l->lock, flags); + if (raw_res_spin_lock_irqsave(&l->lock, flags)) { + WRITE_ONCE(node->pending_free, 1); + return; + } __bpf_lru_node_move(l, node, BPF_LRU_LIST_T_FREE); - raw_spin_unlock_irqrestore(&l->lock, flags); + raw_res_spin_unlock_irqrestore(&l->lock, flags); } void bpf_lru_push_free(struct bpf_lru *lru, struct bpf_lru_node *node) @@ -565,6 +600,7 @@ static void bpf_common_lru_populate(struct bpf_lru *lru, void *buf, node = (struct bpf_lru_node *)(buf + node_offset); node->type = BPF_LRU_LIST_T_FREE; + node->pending_free = 0; bpf_lru_node_clear_ref(node); list_add(&node->list, &l->lists[BPF_LRU_LIST_T_FREE]); buf += elem_size; @@ -594,6 +630,7 @@ static void bpf_percpu_lru_populate(struct bpf_lru *lru, void *buf, node = (struct bpf_lru_node *)(buf + node_offset); node->cpu = cpu; node->type = BPF_LRU_LIST_T_FREE; + node->pending_free = 0; bpf_lru_node_clear_ref(node); list_add(&node->list, &l->lists[BPF_LRU_LIST_T_FREE]); i++; @@ -618,14 +655,12 @@ void bpf_lru_populate(struct bpf_lru *lru, void *buf, u32 node_offset, static void bpf_lru_locallist_init(struct bpf_lru_locallist *loc_l, int cpu) { - int i; - - for (i = 0; i < NR_BPF_LRU_LOCAL_LIST_T; i++) - INIT_LIST_HEAD(&loc_l->lists[i]); + INIT_LIST_HEAD(&loc_l->pending_list); + init_llist_head(&loc_l->free_llist); loc_l->next_steal = cpu; - raw_spin_lock_init(&loc_l->lock); + raw_res_spin_lock_init(&loc_l->lock); } static void bpf_lru_list_init(struct bpf_lru_list *l) @@ -640,7 +675,7 @@ static void bpf_lru_list_init(struct bpf_lru_list *l) l->next_inactive_rotation = &l->lists[BPF_LRU_LIST_T_INACTIVE]; - raw_spin_lock_init(&l->lock); + raw_res_spin_lock_init(&l->lock); } int bpf_lru_init(struct bpf_lru *lru, bool percpu, u32 hash_offset, diff --git a/kernel/bpf/bpf_lru_list.h b/kernel/bpf/bpf_lru_list.h index fe2661a58ea94..8d0ee61622af0 100644 --- a/kernel/bpf/bpf_lru_list.h +++ b/kernel/bpf/bpf_lru_list.h @@ -6,11 +6,11 @@ #include #include -#include +#include +#include #define NR_BPF_LRU_LIST_T (3) #define NR_BPF_LRU_LIST_COUNT (2) -#define NR_BPF_LRU_LOCAL_LIST_T (2) #define BPF_LOCAL_LIST_T_OFFSET NR_BPF_LRU_LIST_T enum bpf_lru_list_type { @@ -22,10 +22,22 @@ enum bpf_lru_list_type { }; struct bpf_lru_node { - struct list_head list; + /* + * A node is in at most one list at a time. The free path on the + * per-CPU locallist uses an llist, so share storage via a union. + */ + union { + struct list_head list; + struct llist_node llist; + }; u16 cpu; u8 type; u8 ref; + /* + * Marks nodes whose *_push_free() lock acquire failed; reclaimed + * by flush/shrink which honor the flag instead of del_from_htab(). + */ + u8 pending_free; }; struct bpf_lru_list { @@ -34,13 +46,14 @@ struct bpf_lru_list { /* The next inactive list rotation starts from here */ struct list_head *next_inactive_rotation; - raw_spinlock_t lock ____cacheline_aligned_in_smp; + rqspinlock_t lock ____cacheline_aligned_in_smp; }; struct bpf_lru_locallist { - struct list_head lists[NR_BPF_LRU_LOCAL_LIST_T]; + struct list_head pending_list; + struct llist_head free_llist; u16 next_steal; - raw_spinlock_t lock; + rqspinlock_t lock; }; struct bpf_common_lru { From 32aefd60ae40f1d814dd9739da8769ab30ea5458 Mon Sep 17 00:00:00 2001 From: David Gow Date: Sat, 6 Jun 2026 10:03:15 +0800 Subject: [PATCH 1211/2758] kunit:tool: Don't write to stdout when it should be disabled [ Upstream commit 29afed142d64e181749214072315c976f8510bd7 ] The kunit_parser module accepts a 'printer' object which is used as a destination for all output. This is typically set to stdout, so that the parsed results are visible, but can be set to a special 'null_printer' to implement options where not all results are always printed. However, there are a few places where use of stdout is hardcoded, notably in handling crashed tests and in outputting the colour escape sequences. Properly use the specified printer for all output. This is okay for the colour handling (as this is already gated behind isatty() anyway), and also for the crash handling, as cases where printer != stdout are separately printed afterwards. Link: https://lore.kernel.org/r/20260606020317.264178-1-david@davidgow.net Fixes: 062a9dd9bad7 ("kunit: tool: Only print the summary") Signed-off-by: David Gow Signed-off-by: Shuah Khan Signed-off-by: Sasha Levin --- tools/testing/kunit/kunit_parser.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/testing/kunit/kunit_parser.py b/tools/testing/kunit/kunit_parser.py index 1c61a0ed740d4..266c9dd953512 100644 --- a/tools/testing/kunit/kunit_parser.py +++ b/tools/testing/kunit/kunit_parser.py @@ -17,7 +17,7 @@ from enum import Enum, auto from typing import Iterable, Iterator, List, Optional, Tuple -from kunit_printer import Printer, stdout +from kunit_printer import Printer class Test: """ @@ -57,7 +57,7 @@ def __repr__(self) -> str: def add_error(self, printer: Printer, error_message: str) -> None: """Records an error that occurred while parsing this test.""" self.counts.errors += 1 - printer.print_with_timestamp(stdout.red('[ERROR]') + f' Test: {self.name}: {error_message}') + printer.print_with_timestamp(printer.red('[ERROR]') + f' Test: {self.name}: {error_message}') def ok_status(self) -> bool: """Returns true if the status was ok, i.e. passed or skipped.""" @@ -544,7 +544,7 @@ def format_test_result(test: Test, printer: Printer) -> str: return printer.yellow('[NO TESTS RUN] ') + test.name if test.status == TestStatus.TEST_CRASHED: print_log(test.log, printer) - return stdout.red('[CRASHED] ') + test.name + return printer.red('[CRASHED] ') + test.name print_log(test.log, printer) return printer.red('[FAILED] ') + test.name @@ -651,11 +651,11 @@ def print_summary_line(test: Test, printer: Printer) -> None: printer - Printer object to output results """ if test.status == TestStatus.SUCCESS: - color = stdout.green + color = printer.green elif test.status in (TestStatus.SKIPPED, TestStatus.NO_TESTS): - color = stdout.yellow + color = printer.yellow else: - color = stdout.red + color = printer.red printer.print_with_timestamp(color(f'Testing complete. {test.counts}')) # Summarize failures that might have gone off-screen since we had a lot From 3abc13ec3ac28c343ad7e7b7496e807b924f0edd Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sat, 6 Jun 2026 07:43:45 +0000 Subject: [PATCH 1212/2758] wifi: mac80211: bound S1G TIM PVB walk to the TIM element [ Upstream commit b224d18b1e5d1cddfc67f63f41d80023b2ec8889 ] ieee80211_s1g_check_tim() parses the S1G Partial Virtual Bitmap (PVB) of a received TIM element. The TIM is handed in as the element payload: ieee802_11_parse_elems_full() stores elems->tim = elem->data and elems->tim_len = elem->datalen (net/mac80211/parse.c), so the valid bytes are [tim, tim + tim_len). When walking the encoded blocks the function passes the walker an end sentinel of (const u8 *)tim + tim_len + 2, i.e. two bytes past the end of the element. ieee80211_s1g_find_target_block() loops while (ptr + 1 <= end) and dereferences ptr (and the per-mode ieee80211_s1g_len_*() helpers read *ptr), so it can read up to two bytes beyond the TIM element -- an out-of-bounds read of adjacent skb/heap data when the TIM is the last element in the frame. The +2 appears to account for the element id/len header, but tim already points past that header at the element payload, so the addend is wrong. Pass the correct element end, (const u8 *)tim + tim_len. Fixes: e0c47c6229c2 ("wifi: mac80211: support parsing S1G TIM PVB") Signed-off-by: Bryam Vargas Link: https://patch.msgid.link/20260606074341.49135-1-hexlabsecurity@proton.me Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- include/linux/ieee80211-s1g.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/ieee80211-s1g.h b/include/linux/ieee80211-s1g.h index 22dde4cbc1b05..3f9626ad3d97c 100644 --- a/include/linux/ieee80211-s1g.h +++ b/include/linux/ieee80211-s1g.h @@ -556,7 +556,7 @@ static inline bool ieee80211_s1g_check_tim(const struct ieee80211_tim_ie *tim, */ err = ieee80211_s1g_find_target_block(&enc_blk, &target_aid, tim->virtual_map, - (const u8 *)tim + tim_len + 2); + (const u8 *)tim + tim_len); if (err) return false; From f97ecf17813acec204eb0575e0ead6718e07d970 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 3 Jun 2026 10:26:26 +0200 Subject: [PATCH 1213/2758] powerpc/8xx: implement get_direction() in cpm1 [ Upstream commit 052f67a1ae6ffa6e43c82193bf2e6c0dfd2e6d8f ] The lack of get_direction() callbacks in this driver causes GPIOLIB to emit a warning. Implement them for 16- and 32-bit variants. Reported-by: Christophe Leroy Closes: https://lore.kernel.org/all/63487206f6e5a93eaf9f41784317fe99d394312f.1780399750.git.chleroy@kernel.org/ Signed-off-by: Bartosz Golaszewski Fixes: e623c4303ed1 ("gpiolib: sanitize the return value of gpio_chip::get_direction()") Tested-by: Christophe Leroy (CS GROUP) Reviewed-by: Linus Walleij Reviewed-by: Christophe Leroy (CS GROUP) [Maddy: Fixed the Fixes tag] Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260603-powerpc-8xx-cpm1-get-dir-v1-1-2ae1c9a5b992@oss.qualcomm.com Signed-off-by: Sasha Levin --- arch/powerpc/platforms/8xx/cpm1.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/arch/powerpc/platforms/8xx/cpm1.c b/arch/powerpc/platforms/8xx/cpm1.c index f00734f0590cf..b31376bf67788 100644 --- a/arch/powerpc/platforms/8xx/cpm1.c +++ b/arch/powerpc/platforms/8xx/cpm1.c @@ -472,6 +472,18 @@ static int cpm1_gpio16_dir_in(struct gpio_chip *gc, unsigned int gpio) return 0; } +static int cpm1_gpio16_get_direction(struct gpio_chip *gc, unsigned int gpio) +{ + struct cpm1_gpio16_chip *cpm1_gc = gpiochip_get_data(gc); + struct cpm_ioport16 __iomem *iop = cpm1_gc->regs; + u16 pin_mask = 1 << (15 - gpio); + + if (in_be16(&iop->dir) & pin_mask) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; +} + int cpm1_gpiochip_add16(struct device *dev) { struct device_node *np = dev->of_node; @@ -498,6 +510,7 @@ int cpm1_gpiochip_add16(struct device *dev) gc->ngpio = 16; gc->direction_input = cpm1_gpio16_dir_in; gc->direction_output = cpm1_gpio16_dir_out; + gc->get_direction = cpm1_gpio16_get_direction; gc->get = cpm1_gpio16_get; gc->set = cpm1_gpio16_set; gc->to_irq = cpm1_gpio16_to_irq; @@ -604,6 +617,18 @@ static int cpm1_gpio32_dir_in(struct gpio_chip *gc, unsigned int gpio) return 0; } +static int cpm1_gpio32_get_direction(struct gpio_chip *gc, unsigned int gpio) +{ + struct cpm1_gpio32_chip *cpm1_gc = gpiochip_get_data(gc); + struct cpm_ioport32b __iomem *iop = cpm1_gc->regs; + u32 pin_mask = 1 << (31 - gpio); + + if (in_be32(&iop->dir) & pin_mask) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; +} + int cpm1_gpiochip_add32(struct device *dev) { struct device_node *np = dev->of_node; @@ -621,6 +646,7 @@ int cpm1_gpiochip_add32(struct device *dev) gc->ngpio = 32; gc->direction_input = cpm1_gpio32_dir_in; gc->direction_output = cpm1_gpio32_dir_out; + gc->get_direction = cpm1_gpio32_get_direction; gc->get = cpm1_gpio32_get; gc->set = cpm1_gpio32_set; gc->parent = dev; From a4c95b6221cb2972a94ed72663c09bd5b0b6dea4 Mon Sep 17 00:00:00 2001 From: Sechang Lim Date: Mon, 8 Jun 2026 05:00:00 +0000 Subject: [PATCH 1214/2758] bpf: Fix NULL pointer dereference in bpf_task_from_vpid() [ Upstream commit 50dff00615522f3ec03449680ca23beb4cfc549c ] bpf_task_from_vpid() looks up a task in the pid namespace of the current task, via find_task_by_vpid(): find_task_by_vpid(vpid) find_task_by_pid_ns(vpid, task_active_pid_ns(current)) find_pid_ns(nr, ns) -> idr_find(&ns->idr, nr) cgroup_skb programs run in softirq, which may interrupt a task that is itself in do_exit(). Once that task has passed exit_notify() -> release_task() -> __unhash_process(), its thread_pid is cleared, so task_active_pid_ns(current) returns NULL and find_pid_ns() dereferences &NULL->idr: BUG: kernel NULL pointer dereference, address: 0000000000000050 RIP: 0010:idr_find+0x11/0x30 lib/idr.c:176 Call Trace: find_pid_ns kernel/pid.c:370 [inline] find_task_by_pid_ns+0x3b/0xe0 kernel/pid.c:485 bpf_task_from_vpid+0x5b/0x200 kernel/bpf/helpers.c:2916 bpf_prog_run_array_cg+0x17e/0x530 kernel/bpf/cgroup.c:81 __cgroup_bpf_run_filter_skb+0x12b/0x250 kernel/bpf/cgroup.c:1612 sk_filter_trim_cap+0x1dc/0x4c0 net/core/filter.c:148 tcp_v4_rcv+0x18d1/0x2200 net/ipv4/tcp_ipv4.c:2223 do_exit+0xa63/0x1270 kernel/exit.c:1010 get_signal+0x141c/0x1530 kernel/signal.c:3037 Bail out when current has no pid namespace. Fixes: 675c3596ff32 ("bpf: Add bpf_task_from_vpid() kfunc") Signed-off-by: Sechang Lim Acked-by: Leon Hwang Link: https://lore.kernel.org/bpf/20260608050001.2545245-1-rhkrqnwk98@gmail.com Signed-off-by: Kumar Kartikeya Dwivedi Signed-off-by: Sasha Levin --- kernel/bpf/helpers.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 5c1e5c8812b12..c2032af98e083 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -2919,11 +2919,13 @@ __bpf_kfunc struct task_struct *bpf_task_from_vpid(s32 vpid) { struct task_struct *p; - rcu_read_lock(); + guard(rcu)(); + if (!task_active_pid_ns(current)) + return NULL; + p = find_task_by_vpid(vpid); if (p) p = bpf_task_acquire(p); - rcu_read_unlock(); return p; } From bb9c7a9c5e8bd35ffb07747bcce29951883b4f63 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Wed, 3 Jun 2026 12:31:08 -0400 Subject: [PATCH 1215/2758] ACPI: IPMI: Fix message kref handling on dead device [ Upstream commit 63320db6a5d84ec3fed8b3d36ba5244d07ddd108 ] acpi_ipmi_space_handler() takes an extra reference on tx_msg before checking whether the selected IPMI device is dead. The reference belongs to the tx_msg_list entry and is normally dropped by ipmi_cancel_tx_msg() or ipmi_flush_tx_msg() after the message is removed from the list. On the dead-device path, the message has not been queued yet, but the error path still calls ipmi_msg_release() directly. That bypasses kref_put() and frees tx_msg while the queued-message reference is still recorded in the kref count. Take the queued-message reference only after the dead-device check succeeds, immediately before adding tx_msg to the list. Fixes: 7b9844772237 ("ACPI / IPMI: Add reference counting for ACPI IPMI transfers") Signed-off-by: Yuho Choi Link: https://patch.msgid.link/20260603163108.2149359-1-dbgh9129@gmail.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/acpi/acpi_ipmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/acpi_ipmi.c b/drivers/acpi/acpi_ipmi.c index 8f1aeae8b72ed..79ce6e72bf295 100644 --- a/drivers/acpi/acpi_ipmi.c +++ b/drivers/acpi/acpi_ipmi.c @@ -550,7 +550,6 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address, return AE_TYPE; } - acpi_ipmi_msg_get(tx_msg); mutex_lock(&driver_data.ipmi_lock); /* Do not add a tx_msg that can not be flushed. */ if (ipmi_device->dead) { @@ -558,6 +557,7 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address, ipmi_msg_release(tx_msg); return AE_NOT_EXIST; } + acpi_ipmi_msg_get(tx_msg); spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags); list_add_tail(&tx_msg->head, &ipmi_device->tx_msg_list); spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags); From 666495333fc27e9ec46eafda8d77a1cecc528f92 Mon Sep 17 00:00:00 2001 From: Tony W Wang-oc Date: Tue, 9 Jun 2026 03:03:59 +0800 Subject: [PATCH 1216/2758] ACPI: processor: Add cpuidle driver check in acpi_processor_register_idle_driver() [ Upstream commit 66c62e6773c54ce5233eb21c6d48999c3747bd13 ] Commit 7a8c994cbb2d ("ACPI: processor: idle: Optimize ACPI idle driver registration") moved the ACPI idle driver registration to acpi_processor_driver_init(), but it didn't check whether a cpuidle driver was already registered. For example, on Intel platforms, if the intel_idle driver is already loaded, the code would still evaluate the _CST object in the ACPI table and attempt to register the acpi_idle driver. This registration would fail with -EBUSY due to the existing check in cpuidle_register_driver. Add a check at the beginning of acpi_processor_register_idle_driver() to avoid unnecessary _CST evaluate and potential registration failures. Fixes: 7a8c994cbb2d ("ACPI: processor: idle: Optimize ACPI idle driver registration") Signed-off-by: Tony W Wang-oc Link: https://patch.msgid.link/20260608190359.3254-1-TonyWWang-oc@zhaoxin.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/acpi/processor_idle.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index ee5facccbe10c..390ab5f1d3137 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -1355,6 +1355,15 @@ void acpi_processor_register_idle_driver(void) int ret = -ENODEV; int cpu; + /* + * If a cpuidle driver is already registered, there is no need to + * evaluate _CST or attempt to register the ACPI idle driver. + */ + if (cpuidle_get_driver()) { + pr_debug("cpuidle driver %pS already registered.\n", cpuidle_get_driver()); + return; + } + acpi_processor_update_max_cstate(); /* From af57a748c4210a00d69692caefab1d3cff7fff72 Mon Sep 17 00:00:00 2001 From: Pengjie Zhang Date: Wed, 3 Jun 2026 13:56:35 +0800 Subject: [PATCH 1217/2758] cpufreq: Documentation: fix conservative governor freq_step description [ Upstream commit 03120e1425262c7d11f93362a88e579a1720390b ] The conservative governor documentation incorrectly states that setting freq_step to 0 will use the default 5% frequency step. In reality, since at least commit 8e677ce83bf4 ("[CPUFREQ] conservative: fixup governor to function more like ondemand logic"), freq_step=0 has always caused the governor to skip frequency updates entirely. Correct the documentation to reflect the actual behavior: freq_step=0 disables frequency changes by the governor entirely. Fixes: 2a0e49279850 ("cpufreq: User/admin documentation update and consolidation") Signed-off-by: Pengjie Zhang Reviewed-by: Zhongqiu Han [ rjw: Subject adjustment ] Link: https://patch.msgid.link/20260603055635.1549943-1-zhangpengjie2@huawei.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- Documentation/admin-guide/pm/cpufreq.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/admin-guide/pm/cpufreq.rst b/Documentation/admin-guide/pm/cpufreq.rst index fdca59c955dcc..8831cface5855 100644 --- a/Documentation/admin-guide/pm/cpufreq.rst +++ b/Documentation/admin-guide/pm/cpufreq.rst @@ -586,8 +586,8 @@ This governor exposes the following tunables: 100 (5 by default). This is how much the frequency is allowed to change in one go. Setting - it to 0 will cause the default frequency step (5 percent) to be used - and setting it to 100 effectively causes the governor to periodically + it to 0 disables frequency changes by the governor entirely and setting + it to 100 effectively causes the governor to periodically switch the frequency between the ``scaling_min_freq`` and ``scaling_max_freq`` policy limits. From 44f37dc3b3f9fcde52934ccfa6fc014c63762086 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Fri, 5 Jun 2026 18:52:06 +0000 Subject: [PATCH 1218/2758] thermal: testing: reject missing command arguments [ Upstream commit ef3e98b0aa4b348f44065d7130251273c83bd204 ] The thermal testing debugfs command parser splits commands at ':' and passes the right-hand side to the command implementation. Commands such as deltz, tzaddtrip, tzreg, and tzunreg require a zone id, but writing one of those command names without ':' leaves the argument pointer NULL. The command implementations parse the id with sscanf(arg, "%d", ...), so the missing-argument form dereferences a NULL pointer from the debugfs write path. Reject missing arguments in tt_command_exec() before calling handlers that require an id. Fixes: f6a034f2df42 ("thermal: Introduce a debugfs-based testing facility") Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius Link: https://patch.msgid.link/20260605185212.2491144-1-sam.moelius@trailofbits.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/thermal/testing/command.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/thermal/testing/command.c b/drivers/thermal/testing/command.c index 1159ecea57e7c..fbf7ab9729b5a 100644 --- a/drivers/thermal/testing/command.c +++ b/drivers/thermal/testing/command.c @@ -116,18 +116,30 @@ static int tt_command_exec(int index, const char *arg) break; case TT_CMD_DELTZ: + if (!arg || !*arg) + return -EINVAL; + ret = tt_del_tz(arg); break; case TT_CMD_TZADDTRIP: + if (!arg || !*arg) + return -EINVAL; + ret = tt_zone_add_trip(arg); break; case TT_CMD_TZREG: + if (!arg || !*arg) + return -EINVAL; + ret = tt_zone_reg(arg); break; case TT_CMD_TZUNREG: + if (!arg || !*arg) + return -EINVAL; + ret = tt_zone_unreg(arg); break; From 4010a227f4a5aeaae1f328b79687a9c6383da6eb Mon Sep 17 00:00:00 2001 From: Mark Harmstone Date: Wed, 22 Apr 2026 15:03:35 +0100 Subject: [PATCH 1219/2758] btrfs: don't force DIO writes to be serialized [ Upstream commit 14f9161e872072075284b8afa4c3f929e199a0f6 ] Before btrfs switched to the new mount API in 2023, we were setting SB_NOSEC in btrfs_mount_root(). This flag tells the VFS that the filesystem may have files which don't have security xattrs, enabling it to do some optimizations. Unfortunately this was missed in the transition, meaning that IS_NOSEC will always return false for a btrfs inode. This means that btrfs_direct_write() calls will always get the inode lock exclusively, meaning that DIO writes to the same file will be serialized. On my machine, this one-line change results in a ~59% improvement in DIO throughput: Before patch: test: (g=0): rw=randwrite, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=io_uring, iodepth=64 ... fio-3.39 Starting 32 processes test: Laying out IO file (1 file / 1024MiB) Jobs: 32 (f=32): [w(32)][100.0%][w=764MiB/s][w=195k IOPS][eta 00m:00s] test: (groupid=0, jobs=32): err= 0: pid=586: Wed Apr 22 13:03:04 2026 write: IOPS=202k, BW=787MiB/s (826MB/s)(46.1GiB/60012msec); 0 zone resets bw ( KiB/s): min=498714, max=1199892, per=100.00%, avg=806659.03, stdev=4229.94, samples=3808 iops : min=124677, max=299971, avg=201661.82, stdev=1057.49, samples=3808 cpu : usr=0.32%, sys=1.27%, ctx=8329204, majf=0, minf=1163 IO depths : 1=0.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=100.0% submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.1%, >=64=0.0% issued rwts: total=0,12094328,0,0 short=0,0,0,0 dropped=0,0,0,0 latency : target=0, window=0, percentile=100.00%, depth=64 Run status group 0 (all jobs): WRITE: bw=787MiB/s (826MB/s), 787MiB/s-787MiB/s (826MB/s-826MB/s), io=46.1GiB (49.5GB), run=60012-60012msec After patch: test: (g=0): rw=randwrite, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=io_uring, iodepth=64 ... fio-3.39 Starting 32 processes test: Laying out IO file (1 file / 1024MiB) Jobs: 32 (f=32): [w(32)][100.0%][w=1255MiB/s][w=321k IOPS][eta 00m:00s] test: (groupid=0, jobs=32): err= 0: pid=572: Wed Apr 22 13:13:46 2026 write: IOPS=320k, BW=1250MiB/s (1311MB/s)(73.3GiB/60003msec); 0 zone resets bw ( MiB/s): min= 619, max= 2289, per=100.00%, avg=1251.28, stdev= 9.64, samples=3808 iops : min=158538, max=586025, avg=320320.80, stdev=2468.97, samples=3808 cpu : usr=0.35%, sys=11.50%, ctx=1584847, majf=0, minf=1160 IO depths : 1=0.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=100.0% submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.1%, >=64=0.0% issued rwts: total=0,19203309,0,0 short=0,0,0,0 dropped=0,0,0,0 latency : target=0, window=0, percentile=100.00%, depth=64 Run status group 0 (all jobs): WRITE: bw=1250MiB/s (1311MB/s), 1250MiB/s-1250MiB/s (1311MB/s-1311MB/s), io=73.3GiB (78.7GB), run=60003-60003msec The script to reproduce that: #!/bin/bash mkfs.btrfs -f /dev/nvme0n1 mount /dev/nvme0n1 /mnt/test mkdir /mnt/test/nocow chattr +C /mnt/test/nocow fio /root/test.fio # cat /root/test.fio [global] rw=randwrite ioengine=io_uring iodepth=64 size=1g direct=1 startdelay=20 force_async=4 ramp_time=5 runtime=60 group_reporting=1 numjobs=32 time_based disk_util=0 clat_percentiles=0 disable_lat=1 disable_clat=1 disable_slat=1 filename=/mnt/test/nocow/fiofile [test] name=test bs=4k stonewall This was on a VM with 8 cores and 8GB of RAM, with a real NVMe exposed through PCI passthrough. The figures for XFS and ext4 in comparison are both about ~3GB/s. Fixes: ad21f15b0f79 ("btrfs: switch to the new mount API") Signed-off-by: Mark Harmstone Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/super.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index b26aa9169e838..64514d600eec7 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -1873,6 +1873,7 @@ static int btrfs_get_tree_super(struct fs_context *fc) fs_info->fs_devices = fs_devices; mutex_unlock(&uuid_mutex); + fc->sb_flags |= SB_NOSEC; sb = sget_fc(fc, btrfs_fc_test_super, set_anon_super_fc); if (IS_ERR(sb)) { From 55e97648f7753c6097cb682d24d1abcfe878e812 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sun, 7 Jun 2026 20:30:00 +0000 Subject: [PATCH 1220/2758] ntfs: validate resident attribute lists and harden the validator [ Upstream commit 7d19e1ffee084c4f7d321a360c14ba43404f7cc8 ] A base inode's $ATTRIBUTE_LIST is sanity-checked by load_attribute_list() only on the non-resident path; ntfs_read_locked_inode() copies a *resident* attribute list into ni->attr_list with a plain memcpy() and no validation at all. Every subsequent walk of ni->attr_list -- ntfs_external_attr_find(), ntfs_inode_attach_all_extents() and ntfs_attrlist_need() -- then trusts the entries are well-formed and reads attr_list_entry fixed-header fields (lowest_vcn at offset 8, mft_reference at offset 16, and the name) with bounds that assume validation already happened. A crafted resident attribute list therefore reaches those walks unvalidated and can drive out-of-bounds reads of the attribute-list buffer. load_attribute_list() itself reads ale->name_offset (offset 7), ale->mft_reference (offset 16) and the name length under only an "al < al_start + size" bound, so its own validation loop can over-read the fixed header of a truncated trailing entry by a few bytes. Factor the per-entry validation into ntfs_attr_list_entry_is_valid(), which requires each entry's fixed header (offsetof(struct attr_list_entry, name)) to be in range before any field is dereferenced, that ale->length is a multiple of 8 covering the fixed header plus the name, and that the entry is in use and carries a live MFT reference. ntfs_attr_list_is_valid() walks the buffer with it and checks the entries tile it exactly. Use the list validator in load_attribute_list() (replacing the open-coded loop, closing its own over-read) and on the resident path in ntfs_read_locked_inode() (which previously skipped validation entirely); patches 2/3 reuse the per-entry helper at the other two attribute-list walks. Fixes: 1e9ea7e04472 ("Revert "fs: Remove NTFS classic"") Signed-off-by: Bryam Vargas Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Sasha Levin --- fs/ntfs/attrib.c | 78 ++++++++++++++++++++++++++++++++++++++---------- fs/ntfs/attrib.h | 4 +++ fs/ntfs/inode.c | 6 ++++ 3 files changed, 73 insertions(+), 15 deletions(-) diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index 421c6cdcbb530..abc0add6f0c44 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -843,11 +843,71 @@ char *ntfs_attr_name_get(const struct ntfs_volume *vol, const __le16 *uname, return NULL; } +/* + * ntfs_attr_list_entry_is_valid - sanity check one $ATTRIBUTE_LIST entry + * @ale: the attribute-list entry to check + * @al_end: end of the attribute-list buffer @ale lives in + * + * Verify that @ale is a well-formed attr_list_entry wholly contained in + * [.., @al_end): its fixed header must lie in range before any field is + * dereferenced, its length must be a multiple of 8 that covers the fixed + * header plus the name, the name must lie within the buffer, the entry must + * be in use and carry a live MFT reference. Return true if valid. + */ +bool ntfs_attr_list_entry_is_valid(const struct attr_list_entry *ale, + const u8 *al_end) +{ + const u8 *al = (const u8 *)ale; + u16 ale_len; + + /* The fixed header must be in bounds before it is parsed. */ + if (al + offsetof(struct attr_list_entry, name) > al_end) + return false; + ale_len = le16_to_cpu(ale->length); + /* On-disk entries are 8-byte aligned (see struct attr_list_entry). */ + if (ale_len & 7) + return false; + if (ale->name_offset != sizeof(struct attr_list_entry)) + return false; + if ((u32)ale->name_offset + + (u32)ale->name_length * sizeof(__le16) > ale_len || + al + ale_len > al_end) + return false; + if (ale->type == AT_UNUSED) + return false; + if (MSEQNO_LE(ale->mft_reference) == 0) + return false; + return true; +} + +/* + * ntfs_attr_list_is_valid - sanity check an in-memory $ATTRIBUTE_LIST + * @al_start: start of the attribute list buffer + * @size: length of the attribute list in bytes + * + * Verify that [@al_start, @al_start + @size) is a sequence of valid + * attr_list_entry records (see ntfs_attr_list_entry_is_valid()) that tile the + * buffer exactly. Return true if valid, false otherwise. + */ +bool ntfs_attr_list_is_valid(const u8 *al_start, s64 size) +{ + const u8 *al = al_start; + const u8 *al_end = al_start + size; + + while (al < al_end) { + const struct attr_list_entry *ale = + (const struct attr_list_entry *)al; + + if (!ntfs_attr_list_entry_is_valid(ale, al_end)) + return false; + al += le16_to_cpu(ale->length); + } + return al == al_end; +} + int load_attribute_list(struct ntfs_inode *base_ni, u8 *al_start, const s64 size) { struct inode *attr_vi = NULL; - u8 *al; - struct attr_list_entry *ale; if (!al_start || size <= 0) return -EINVAL; @@ -869,19 +929,7 @@ int load_attribute_list(struct ntfs_inode *base_ni, u8 *al_start, const s64 size } iput(attr_vi); - for (al = al_start; al < al_start + size; al += le16_to_cpu(ale->length)) { - ale = (struct attr_list_entry *)al; - if (ale->name_offset != sizeof(struct attr_list_entry)) - break; - if (le16_to_cpu(ale->length) <= ale->name_offset + ale->name_length || - al + le16_to_cpu(ale->length) > al_start + size) - break; - if (ale->type == AT_UNUSED) - break; - if (MSEQNO_LE(ale->mft_reference) == 0) - break; - } - if (al != al_start + size) { + if (!ntfs_attr_list_is_valid(al_start, size)) { ntfs_error(base_ni->vol->sb, "Corrupt attribute list, mft = %llu", base_ni->mft_no); return -EIO; diff --git a/fs/ntfs/attrib.h b/fs/ntfs/attrib.h index f7acc7986b090..e2224fbfaabe9 100644 --- a/fs/ntfs/attrib.h +++ b/fs/ntfs/attrib.h @@ -71,6 +71,10 @@ int ntfs_attr_lookup(const __le32 type, const __le16 *name, const u32 name_len, const u32 ic, const s64 lowest_vcn, const u8 *val, const u32 val_len, struct ntfs_attr_search_ctx *ctx); +bool ntfs_attr_list_entry_is_valid(const struct attr_list_entry *ale, + const u8 *al_end); +bool ntfs_attr_list_is_valid(const u8 *al_start, s64 size); + int load_attribute_list(struct ntfs_inode *base_ni, u8 *al_start, const s64 size); diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index 360bebd1ee3fe..a5f7400fd19dc 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -848,6 +848,12 @@ static int ntfs_read_locked_inode(struct inode *vi) a->data.resident.value_offset), le32_to_cpu( a->data.resident.value_length)); + /* A resident list is not validated on load; check it now. */ + if (!ntfs_attr_list_is_valid(ni->attr_list, + ni->attr_list_size)) { + ntfs_error(vi->i_sb, "Corrupt attribute list."); + goto unm_err_out; + } } } skip_attr_list_load: From 44885c9b45eb4082fb7f558590d84bb254a08e94 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sun, 7 Jun 2026 20:30:00 +0000 Subject: [PATCH 1221/2758] ntfs: bound the look-ahead attribute-list entry in ntfs_external_attr_find() [ Upstream commit 344b18f389f9934d59c7b0cf3d20541ea2e0da58 ] When resolving an attribute lookup with a non-zero @lowest_vcn, ntfs_external_attr_find() peeks at the next $ATTRIBUTE_LIST entry to decide whether to keep searching, but bounds that not-yet-validated entry only with "(u8 *)next_al_entry + 6 < al_end" (which proves just bytes 0..6 are in range) and "(u8 *)next_al_entry + length <= al_end" with an attacker-controlled, non-8-aligned length. It then reads next_al_entry->lowest_vcn (an __le64 at offset 8) and the name at next_al_entry->name_offset, both of which can lie past al_end -- the exact end of the kvmalloc'd attribute-list buffer (allocated at the on-disk attr_list_size, no rounding). A crafted on-disk $ATTRIBUTE_LIST whose last entry sits a few bytes before al_end therefore yields a slab out-of-bounds read when the inode is read. Validate the look-ahead entry with ntfs_attr_list_entry_is_valid() (added in patch 1/3) before dereferencing lowest_vcn and the name, so the same fixed-header, length and name bounds the main attribute-list walk uses now guard this read too. Fixes: 1e9ea7e04472 ("Revert "fs: Remove NTFS classic"") Signed-off-by: Bryam Vargas Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Sasha Levin --- fs/ntfs/attrib.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index abc0add6f0c44..e425c8d074c58 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -1185,9 +1185,8 @@ static int ntfs_external_attr_find(const __le32 type, * we have reached the right one or the search has failed. */ if (lowest_vcn && (u8 *)next_al_entry >= al_start && - (u8 *)next_al_entry + 6 < al_end && - (u8 *)next_al_entry + le16_to_cpu( - next_al_entry->length) <= al_end && + ntfs_attr_list_entry_is_valid(next_al_entry, + al_end) && le64_to_cpu(next_al_entry->lowest_vcn) <= lowest_vcn && next_al_entry->type == al_entry->type && From c7a7e48e71ce915ec4cbd6e9f355590c1ecbc29a Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sun, 7 Jun 2026 20:30:00 +0000 Subject: [PATCH 1222/2758] ntfs: bound the attribute-list entry in ntfs_read_inode_mount() [ Upstream commit 98634df5b1cb56c26299b7409227025ddb0167d8 ] The $MFT attribute-list walk in ntfs_read_inode_mount() validates each entry only with "(u8 *)al_entry + 6 > al_end" and "(u8 *)al_entry + le16_to_cpu(al_entry->length) > al_end", but then reads al_entry->lowest_vcn (an __le64 at offset 8) and al_entry->mft_reference (offset 16) -- fields beyond the 6 bytes proven in range. al_entry->length is attacker-controlled and only required non-zero, so a short entry (e.g. length 8) placed at the tail passes both checks while the lowest_vcn / mft_reference reads fall past al_end. al_end is ni->attr_list + attr_list_size (the on-disk size); the buffer is kvzalloc(round_up(attr_list_size, SECTOR_SIZE)), so the sector rounding usually absorbs the over-read -- but when attr_list_size is a multiple of SECTOR_SIZE there is no slack and a crafted $MFT attribute list produces an out-of-bounds read at mount time. Validate the entry with ntfs_attr_list_entry_is_valid() (added in patch 1/3) before dereferencing it, matching the bound the other attribute-list walks now use. The validator already requires the length to cover the fixed header, which makes the separate "!al_entry->length" check redundant, so drop it too. Fixes: 1e9ea7e04472 ("Revert "fs: Remove NTFS classic"") Signed-off-by: Bryam Vargas Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Sasha Levin --- fs/ntfs/inode.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index a5f7400fd19dc..c4fa6aa11c9e8 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -2000,10 +2000,7 @@ int ntfs_read_inode_mount(struct inode *vi) /* Catch the end of the attribute list. */ if ((u8 *)al_entry == al_end) goto em_put_err_out; - if (!al_entry->length) - goto em_put_err_out; - if ((u8 *)al_entry + 6 > al_end || - (u8 *)al_entry + le16_to_cpu(al_entry->length) > al_end) + if (!ntfs_attr_list_entry_is_valid(al_entry, al_end)) goto em_put_err_out; next_al_entry = (struct attr_list_entry *)((u8 *)al_entry + le16_to_cpu(al_entry->length)); From 07a4751ef3ccc8bfd17bfbb16e5003c03161790d Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Mon, 8 Jun 2026 23:00:45 +0900 Subject: [PATCH 1223/2758] ntfs: fix u16 truncation of restart-area length check [ Upstream commit 390936fb15053d8d8991ca3a22776e251a5a7f2f ] ntfs_check_restart_area() validates that the $LogFile restart area and its trailing log client record array fit within the system page size: u16 ra_ofs, ra_len, ca_ofs; ... ra_len = ca_ofs + le16_to_cpu(ra->log_clients) * sizeof(struct log_client_record); if (ra_ofs + ra_len > le32_to_cpu(rp->system_page_size) || ...) return false; ra_len is u16, but the right-hand side is computed in size_t (sizeof(struct log_client_record) == 160). Both ca_ofs and log_clients come straight from the on-disk restart area. With an on-disk log_clients of 410 the product 410 * 160 = 65600; adding ca_ofs and storing into the u16 ra_len truncates modulo 65536 (e.g. ca_ofs 64 gives ra_len 128), so the "fits in the page" check passes even though the client array described by log_clients extends far beyond the page. ntfs_check_log_client_array() then walks the array bounded only by the on-disk log_clients count: cr = ca + idx; if (cr->prev_client != LOGFILE_NO_CLIENT) ... For log_clients 410 it dereferences records up to ca + 409 * 160, ~64 KiB past the kvzalloc(system_page_size) restart-page buffer -- an out-of-bounds read of attacker-controlled extent, reachable when a crafted NTFS image is mounted (load_and_check_logfile() at mount time). This is the in-kernel analogue of CVE-2022-30789, fixed in the ntfs-3g userspace driver but never in this revived classic driver. Compute the restart-area length in a u32 so the existing bounds check rejects an over-large client array instead of being defeated by the truncation. Widen ra_ofs and ca_ofs to u32 as well: both are loaded from __le16 on-disk fields and every comparison already promotes to int/size_t, so this changes no result and keeps the declaration uniform. Fixes: 1e9ea7e04472 ("Revert "fs: Remove NTFS classic"") Signed-off-by: Bryam Vargas Signed-off-by: Namjae Jeon Signed-off-by: Sasha Levin --- fs/ntfs/logfile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ntfs/logfile.c b/fs/ntfs/logfile.c index d3f25d8e29f9d..9bc34572908ef 100644 --- a/fs/ntfs/logfile.c +++ b/fs/ntfs/logfile.c @@ -132,7 +132,7 @@ static bool ntfs_check_restart_area(struct inode *vi, struct restart_page_header { u64 file_size; struct restart_area *ra; - u16 ra_ofs, ra_len, ca_ofs; + u32 ra_ofs, ra_len, ca_ofs; u8 fs_bits; ntfs_debug("Entering."); From bd68d4f768f60fbf36b66f4c037eb0c3e0eb50b4 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 3 Jun 2026 22:27:40 -0300 Subject: [PATCH 1224/2758] IB/mlx5: Don't take the rereg_mr fallback without a new translation [ Upstream commit 55d339d200c908de83a408d023cfb7be779b0dd7 ] Jumping to mlx5_ib_reg_user_mr() without IB_MR_REREG_TRANS set will use garbage values for start, length, and iova. Recovering the original mr parameters for ODP and DMABUF to properly recreate it is too hard in this flow, so just fail it. Fixes: ef3642c4f54d ("RDMA/mlx5: Fix error unwinds for rereg_mr") Link: https://patch.msgid.link/r/1-v1-29ebd2c229b5+fd5-ib_mr_pd_jgg@nvidia.com Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/mr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index fb40b44496f47..d70d3e3dbd1a9 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -1202,7 +1202,7 @@ struct ib_mr *mlx5_ib_rereg_user_mr(struct ib_mr *ib_mr, int flags, u64 start, } /* DM or ODP MR's don't have a normal umem so we can't re-use it */ if (!mr->umem || is_odp_mr(mr) || is_dmabuf_mr(mr)) - goto recreate; + return ERR_PTR(-EOPNOTSUPP); /* * Only one active MR can refer to a umem at one time, revoke From 5d02b9a2efd11d28d2a8feac7769686b1b871d9c Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 3 Jun 2026 22:27:42 -0300 Subject: [PATCH 1225/2758] IB/mlx5: Properly support implicit ODP rereg_mr [ Upstream commit ee7a8335069150c3f1893a697ab30bbeca00d796 ] Due to all the child mkeys in the implicit ODP configuration we cannot change anything in place for the parent mkey. Instead the whole thing needs to be rebuilt if any change is requested. If the user does not specify a translation then force the implicit values which will then fall through the logic into mlx5_ib_reg_user_mr() to allocate a completely new MR. Since implicit children were also touching the mr->pd, this removes another case where the access was racy. Fixes: ef3642c4f54d ("RDMA/mlx5: Fix error unwinds for rereg_mr") Link: https://sashiko.dev/#/patchset/20260427-security-bug-fixes-v3-0-4621fa52de0e%40nvidia.com?part=4 Link: https://patch.msgid.link/r/3-v1-29ebd2c229b5+fd5-ib_mr_pd_jgg@nvidia.com Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/mr.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index d70d3e3dbd1a9..f3884faf1a117 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -1188,6 +1188,21 @@ struct ib_mr *mlx5_ib_rereg_user_mr(struct ib_mr *ib_mr, int flags, u64 start, if (!(flags & IB_MR_REREG_PD)) new_pd = ib_mr->pd; + if (mr->is_odp_implicit && !(flags & IB_MR_REREG_TRANS)) { + if (!(new_access_flags & IB_ACCESS_ON_DEMAND)) + return ERR_PTR(-EOPNOTSUPP); + + /* + * Due to all the child mkeys we cannot actually change an + * implicit MR in place. If the user did not specify a new + * translation then force the fixed implicit MR values. + */ + start = 0; + iova = 0; + length = U64_MAX; + flags |= IB_MR_REREG_TRANS; + } + if (!(flags & IB_MR_REREG_TRANS)) { struct ib_umem *umem; From 1a132ee4e655288d9a0937ea5109a0d038431ae9 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 3 Jun 2026 22:27:43 -0300 Subject: [PATCH 1226/2758] RDMA/nldev: Fix locking when accessing mr->pd [ Upstream commit 50d5c02ab8e62325548bd3a6e6b758a9dcd6e7c3 ] Sashiko points out that, due to rereg_mr, the PD is actually variable and all the touches in nldev are racy. Use mr->device instead of mr->pd->device. Getting the PD restrack ID is more tricky. To avoid disturbing all the happy paths, add an rdma_restrack_sync() operation which is sort of like flush_workqueue() or synchronize_irq(): after it returns, all the old nldev touches to the mr are gone and everything sees the new PD. This makes it safe to reach into the PD pointer. Fixes: da5c85078215 ("RDMA/nldev: add driver-specific resource tracking") Link: https://patch.msgid.link/r/4-v1-29ebd2c229b5+fd5-ib_mr_pd_jgg@nvidia.com Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/core/nldev.c | 15 +++++---- drivers/infiniband/core/restrack.c | 49 ++++++++++++++++++++++++++++ drivers/infiniband/core/restrack.h | 1 + drivers/infiniband/core/uverbs_cmd.c | 10 ++++-- include/rdma/ib_verbs.h | 5 +++ 5 files changed, 72 insertions(+), 8 deletions(-) diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c index 5aaba2b9746ba..02a0a9c0a4a6a 100644 --- a/drivers/infiniband/core/nldev.c +++ b/drivers/infiniband/core/nldev.c @@ -695,7 +695,7 @@ static int fill_res_mr_entry(struct sk_buff *msg, bool has_cap_net_admin, struct rdma_restrack_entry *res, uint32_t port) { struct ib_mr *mr = container_of(res, struct ib_mr, res); - struct ib_device *dev = mr->pd->device; + struct ib_device *dev = mr->device; if (has_cap_net_admin) { if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_RKEY, mr->rkey)) @@ -711,9 +711,12 @@ static int fill_res_mr_entry(struct sk_buff *msg, bool has_cap_net_admin, if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_MRN, res->id)) return -EMSGSIZE; - if (!rdma_is_kernel_res(res) && - nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_PDN, mr->pd->res.id)) - return -EMSGSIZE; + if (!rdma_is_kernel_res(res)) { + struct ib_pd *pd = READ_ONCE(mr->pd); + + if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_PDN, pd->res.id)) + return -EMSGSIZE; + } if (fill_res_name_pid(msg, res)) return -EMSGSIZE; @@ -727,7 +730,7 @@ static int fill_res_mr_raw_entry(struct sk_buff *msg, bool has_cap_net_admin, struct rdma_restrack_entry *res, uint32_t port) { struct ib_mr *mr = container_of(res, struct ib_mr, res); - struct ib_device *dev = mr->pd->device; + struct ib_device *dev = mr->device; if (!dev->ops.fill_res_mr_entry_raw) return -EINVAL; @@ -1017,7 +1020,7 @@ static int fill_stat_mr_entry(struct sk_buff *msg, bool has_cap_net_admin, struct rdma_restrack_entry *res, uint32_t port) { struct ib_mr *mr = container_of(res, struct ib_mr, res); - struct ib_device *dev = mr->pd->device; + struct ib_device *dev = mr->device; if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_MRN, res->id)) goto err; diff --git a/drivers/infiniband/core/restrack.c b/drivers/infiniband/core/restrack.c index ac3688952cabb..cfee2071586c1 100644 --- a/drivers/infiniband/core/restrack.c +++ b/drivers/infiniband/core/restrack.c @@ -71,6 +71,8 @@ int rdma_restrack_count(struct ib_device *dev, enum rdma_restrack_type type, xa_lock(&rt->xa); xas_for_each(&xas, e, U32_MAX) { + if (xa_is_zero(e)) + continue; if (xa_get_mark(&rt->xa, e->id, RESTRACK_DD) && !show_details) continue; cnt++; @@ -276,6 +278,53 @@ int rdma_restrack_put(struct rdma_restrack_entry *res) } EXPORT_SYMBOL(rdma_restrack_put); +/** + * rdma_restrack_sync() - Fence concurrent netlink dumps on an entry + * @res: resource entry + * + * After this returns any concurrent netlink dump threads will see the current + * value of the object. This is useful if the object has to be changed and there + * is not locking to protect the nl side. Eg for mr->pd. This effectively + * destroys the object from a kref/xarray perspective and then immediately + * restores it. The kref is acting like a lock to barrier concurrent nl threads. + * Callers must ensure rdma_restrack_del() is not concurrently called. + */ +void rdma_restrack_sync(struct rdma_restrack_entry *res) +{ + struct rdma_restrack_entry *old; + struct rdma_restrack_root *rt; + struct task_struct *task; + struct ib_device *dev; + + if (!res->valid || res->no_track) + return; + + dev = res_to_dev(res); + if (WARN_ON(!dev)) + return; + + rt = &dev->res[res->type]; + if (WARN_ON(xa_get_mark(&rt->xa, res->id, RESTRACK_DD))) + return; + + old = xa_cmpxchg(&rt->xa, res->id, res, XA_ZERO_ENTRY, GFP_KERNEL); + if (WARN_ON(old != res)) + return; + + task = res->task; + if (task) + get_task_struct(task); + rdma_restrack_put(res); + wait_for_completion(&res->comp); + reinit_completion(&res->comp); + if (task) + res->task = task; + kref_init(&res->kref); + + xa_cmpxchg(&rt->xa, res->id, XA_ZERO_ENTRY, res, GFP_KERNEL); +} +EXPORT_SYMBOL(rdma_restrack_sync); + /** * rdma_restrack_del() - delete object from the resource tracking database * @res: resource entry diff --git a/drivers/infiniband/core/restrack.h b/drivers/infiniband/core/restrack.h index 6a04fc41f7380..75b8d1005a984 100644 --- a/drivers/infiniband/core/restrack.h +++ b/drivers/infiniband/core/restrack.h @@ -27,6 +27,7 @@ int rdma_restrack_init(struct ib_device *dev); void rdma_restrack_clean(struct ib_device *dev); void rdma_restrack_add(struct rdma_restrack_entry *res); void rdma_restrack_del(struct rdma_restrack_entry *res); +void rdma_restrack_sync(struct rdma_restrack_entry *res); void rdma_restrack_new(struct rdma_restrack_entry *res, enum rdma_restrack_type type); void rdma_restrack_set_name(struct rdma_restrack_entry *res, diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index 91a62d2ade4dd..22793e4b1895e 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c @@ -47,6 +47,7 @@ #include "uverbs.h" #include "core_priv.h" +#include "restrack.h" /* * Copy a response to userspace. If the provided 'resp' is larger than the @@ -819,6 +820,10 @@ static int ib_uverbs_rereg_mr(struct uverbs_attr_bundle *attrs) ret = PTR_ERR(new_pd); goto put_uobjs; } + if (new_pd == orig_pd) { + uobj_put_obj_read(new_pd); + cmd.flags &= ~IB_MR_REREG_PD; + } } else { new_pd = mr->pd; } @@ -866,9 +871,10 @@ static int ib_uverbs_rereg_mr(struct uverbs_attr_bundle *attrs) mr = new_mr; } else { if (cmd.flags & IB_MR_REREG_PD) { - atomic_dec(&orig_pd->usecnt); - mr->pd = new_pd; atomic_inc(&new_pd->usecnt); + WRITE_ONCE(mr->pd, new_pd); + rdma_restrack_sync(&mr->res); + atomic_dec(&orig_pd->usecnt); } if (cmd.flags & IB_MR_REREG_TRANS) { mr->iova = cmd.hca_va; diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 9dd76f489a0ba..46568a5221f40 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -1977,6 +1977,11 @@ struct ib_dmah { struct ib_mr { struct ib_device *device; + /* + * Due to IB_MR_REREG_PD pd is not a fixed pointer and can change. For a + * user MR, this value should only be read from a system call that holds + * the uobject lock, or the driver should disable in-place REREG_PD. + */ struct ib_pd *pd; u32 lkey; u32 rkey; From 3d11543cd14a30be969702ba4d0564f49eaed461 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 3 Jun 2026 22:27:44 -0300 Subject: [PATCH 1227/2758] IB/mlx5: Remove unused mkc bits in mlx5r_umr_update_mr_page_shift() [ Upstream commit 4910483bd7ee2b40d0cc8ebd537fa33c95562029 ] The HW only processes mkc fields selected by mkey_mask. pd, qpn and mkey_7_0 are never selected so they can be left as zero. This removes a racy read of mr->pd. Fixes: e73242aa14d2 ("RDMA/mlx5: Optimize DMABUF mkey page size") Link: https://patch.msgid.link/r/5-v1-29ebd2c229b5+fd5-ib_mr_pd_jgg@nvidia.com Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/umr.c | 16 +++------------- drivers/infiniband/hw/mlx5/umr.h | 3 +-- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/umr.c b/drivers/infiniband/hw/mlx5/umr.c index 688d5246f284e..f0ba8d91f81d1 100644 --- a/drivers/infiniband/hw/mlx5/umr.c +++ b/drivers/infiniband/hw/mlx5/umr.c @@ -937,8 +937,7 @@ int mlx5r_umr_update_xlt(struct mlx5_ib_mr *mr, u64 idx, int npages, * pinned and the HW can switch from 4K to huge-page alignment). */ int mlx5r_umr_update_mr_page_shift(struct mlx5_ib_mr *mr, - unsigned int page_shift, - bool dd) + unsigned int page_shift) { struct mlx5_ib_dev *dev = mr_to_mdev(mr); struct mlx5r_umr_wqe wqe = {}; @@ -953,16 +952,8 @@ int mlx5r_umr_update_mr_page_shift(struct mlx5_ib_mr *mr, /* Fill mkey segment with the new page size, keep the rest unchanged */ MLX5_SET(mkc, &wqe.mkey_seg, log_page_size, page_shift); - if (dd) - MLX5_SET(mkc, &wqe.mkey_seg, pd, dev->ddr.pdn); - else - MLX5_SET(mkc, &wqe.mkey_seg, pd, to_mpd(mr->ibmr.pd)->pdn); - MLX5_SET64(mkc, &wqe.mkey_seg, start_addr, mr->ibmr.iova); MLX5_SET64(mkc, &wqe.mkey_seg, len, mr->ibmr.length); - MLX5_SET(mkc, &wqe.mkey_seg, qpn, 0xffffff); - MLX5_SET(mkc, &wqe.mkey_seg, mkey_7_0, - mlx5_mkey_variant(mr->mmkey.key)); err = mlx5r_umr_post_send_wait(dev, mr->mmkey.key, &wqe, false); if (!err) @@ -1049,7 +1040,7 @@ static int _mlx5r_umr_zap_mkey(struct mlx5_ib_mr *mr, * non-present. */ if (*nblocks) { - err = mlx5r_umr_update_mr_page_shift(mr, max_page_shift, dd); + err = mlx5r_umr_update_mr_page_shift(mr, max_page_shift); if (err) { mr->page_shift = old_page_shift; return err; @@ -1114,8 +1105,7 @@ int mlx5r_umr_dmabuf_update_pgsz(struct mlx5_ib_mr *mr, u32 xlt_flags, goto err; } - err = mlx5r_umr_update_mr_page_shift(mr, mr->page_shift, - mr->data_direct); + err = mlx5r_umr_update_mr_page_shift(mr, mr->page_shift); if (err) goto err; err = _mlx5r_dmabuf_umr_update_pas(mr, xlt_flags, 0, zapped_blocks, diff --git a/drivers/infiniband/hw/mlx5/umr.h b/drivers/infiniband/hw/mlx5/umr.h index 7eeaf6a94c974..59809d4d7d729 100644 --- a/drivers/infiniband/hw/mlx5/umr.h +++ b/drivers/infiniband/hw/mlx5/umr.h @@ -106,8 +106,7 @@ int mlx5r_umr_update_mr_pas(struct mlx5_ib_mr *mr, unsigned int flags); int mlx5r_umr_update_xlt(struct mlx5_ib_mr *mr, u64 idx, int npages, int page_shift, int flags); int mlx5r_umr_update_mr_page_shift(struct mlx5_ib_mr *mr, - unsigned int page_shift, - bool dd); + unsigned int page_shift); int mlx5r_umr_dmabuf_update_pgsz(struct mlx5_ib_mr *mr, u32 xlt_flags, unsigned int page_shift); From 9f32805f907dca875d2d6e7d5446df0f10a24264 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 3 Jun 2026 22:27:45 -0300 Subject: [PATCH 1228/2758] IB/mlx5: Pull the pdn out of the depths of the umr machinery [ Upstream commit f387a9f06ea4f05e607a91b161963c0b19436652 ] Instead of getting the pdn deep inside the umr code, pass it in from the top. to_mpd(mr->ibmr.pd)->pdn is not safe due to the rereg races, so all the call sites need some revision to obtain the pdn in a safe way. Mark them with mlx5_mr_pdn(); following patches will go through and remove these. Cases where the XLT flags are known and do not require the PDN can pass 0, such as for mlx5_ib_dmabuf_invalidate_cb(). Also extract the DMABUF data_direct special case from inside the UMR code and into the only place that needs it, pagefault_dmabuf_mr(). The actual mr was created directly without using the UMR flow. Ultimately this will be moved into mlx5_ib_init_dmabuf_mr(). Link: https://patch.msgid.link/r/6-v1-29ebd2c229b5+fd5-ib_mr_pd_jgg@nvidia.com Assisted-by: Codex:gpt-5-5 Signed-off-by: Jason Gunthorpe Stable-dep-of: 8e0a02a989c1 ("IB/mlx5: Don't mangle the mr->pd inside the rereg callback") Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/mlx5_ib.h | 9 ++++ drivers/infiniband/hw/mlx5/mr.c | 8 +-- drivers/infiniband/hw/mlx5/odp.c | 12 +++-- drivers/infiniband/hw/mlx5/umr.c | 75 ++++++++++++++-------------- drivers/infiniband/hw/mlx5/umr.h | 6 +-- 5 files changed, 64 insertions(+), 46 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index e156dc4d75299..0a2b8ede0d818 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -331,6 +331,10 @@ struct mlx5_ib_flow_db { #define MLX5_IB_QPT_DCT IB_QPT_RESERVED4 #define MLX5_IB_WR_UMR IB_WR_RESERVED1 +/* + * A valid pdn is required when flags include MLX5_IB_UPD_XLT_ENABLE, + * MLX5_IB_UPD_XLT_PD or MLX5_IB_UPD_XLT_ACCESS. + */ #define MLX5_IB_UPD_XLT_ZAP BIT(0) #define MLX5_IB_UPD_XLT_ENABLE BIT(1) #define MLX5_IB_UPD_XLT_ATOMIC BIT(2) @@ -1209,6 +1213,11 @@ static inline struct mlx5_ib_pd *to_mpd(struct ib_pd *ibpd) return container_of(ibpd, struct mlx5_ib_pd, ibpd); } +static inline u32 mlx5_mr_pdn(struct mlx5_ib_mr *mr) +{ + return to_mpd(mr->ibmr.pd)->pdn; +} + static inline struct mlx5_ib_srq *to_msrq(struct ib_srq *ibsrq) { return container_of(ibsrq, struct mlx5_ib_srq, ibsrq); diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index f3884faf1a117..e29f5e628312b 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -781,7 +781,8 @@ static struct ib_mr *create_real_mr(struct ib_pd *pd, struct ib_umem *umem, * configured properly but left disabled. It is safe to go ahead * and configure it again via UMR while enabling it. */ - err = mlx5r_umr_update_mr_pas(mr, MLX5_IB_UPD_XLT_ENABLE); + err = mlx5r_umr_update_mr_pas(mr, MLX5_IB_UPD_XLT_ENABLE, + to_mpd(pd)->pdn); if (err) { mlx5_ib_dereg_mr(&mr->ibmr, NULL); return ERR_PTR(err); @@ -890,7 +891,8 @@ static void mlx5_ib_dmabuf_invalidate_cb(struct dma_buf_attachment *attach) if (!umem_dmabuf->sgt || !mr) return; - mlx5r_umr_update_mr_pas(mr, MLX5_IB_UPD_XLT_ZAP); + /* MLX5_IB_UPD_XLT_ZAP does not change the pdn */ + mlx5r_umr_update_mr_pas(mr, MLX5_IB_UPD_XLT_ZAP, 0); ib_umem_dmabuf_unmap_pages(umem_dmabuf); } @@ -1141,7 +1143,7 @@ static int umr_rereg_pas(struct mlx5_ib_mr *mr, struct ib_pd *pd, mr->ibmr.length = new_umem->length; mr->page_shift = order_base_2(page_size); mr->umem = new_umem; - err = mlx5r_umr_update_mr_pas(mr, upd_flags); + err = mlx5r_umr_update_mr_pas(mr, upd_flags, mlx5_mr_pdn(mr)); if (err) { /* * The MR is revoked at this point so there is no issue to free diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c index 1119ce163ea78..42235b9dd6ab1 100644 --- a/drivers/infiniband/hw/mlx5/odp.c +++ b/drivers/infiniband/hw/mlx5/odp.c @@ -833,12 +833,14 @@ static int pagefault_dmabuf_mr(struct mlx5_ib_mr *mr, size_t bcnt, u32 *bytes_mapped, u32 flags) { struct ib_umem_dmabuf *umem_dmabuf = to_ib_umem_dmabuf(mr->umem); + struct mlx5_ib_dev *dev = mr_to_mdev(mr); int access_mode = mr->data_direct ? MLX5_MKC_ACCESS_MODE_KSM : MLX5_MKC_ACCESS_MODE_MTT; unsigned int old_page_shift = mr->page_shift; unsigned int page_shift; unsigned long page_size; u32 xlt_flags = 0; + u32 pdn = 0; int err; if (flags & MLX5_PF_FLAGS_ENABLE) @@ -857,8 +859,12 @@ static int pagefault_dmabuf_mr(struct mlx5_ib_mr *mr, size_t bcnt, err = -EINVAL; } else { page_shift = order_base_2(page_size); + if (mr->data_direct) + pdn = dev->ddr.pdn; + else + pdn = mlx5_mr_pdn(mr); if (page_shift != mr->page_shift && mr->dmabuf_faulted) { - err = mlx5r_umr_dmabuf_update_pgsz(mr, xlt_flags, + err = mlx5r_umr_dmabuf_update_pgsz(mr, xlt_flags, pdn, page_shift); } else { mr->page_shift = page_shift; @@ -866,8 +872,8 @@ static int pagefault_dmabuf_mr(struct mlx5_ib_mr *mr, size_t bcnt, err = mlx5r_umr_update_data_direct_ksm_pas( mr, xlt_flags); else - err = mlx5r_umr_update_mr_pas(mr, - xlt_flags); + err = mlx5r_umr_update_mr_pas(mr, xlt_flags, + pdn); } } dma_resv_unlock(umem_dmabuf->attach->dmabuf->resv); diff --git a/drivers/infiniband/hw/mlx5/umr.c b/drivers/infiniband/hw/mlx5/umr.c index f0ba8d91f81d1..f3f428f5e1b66 100644 --- a/drivers/infiniband/hw/mlx5/umr.c +++ b/drivers/infiniband/hw/mlx5/umr.c @@ -603,11 +603,11 @@ mlx5r_umr_set_update_xlt_ctrl_seg(struct mlx5_wqe_umr_ctrl_seg *ctrl_seg, static void mlx5r_umr_set_update_xlt_mkey_seg(struct mlx5_ib_dev *dev, struct mlx5_mkey_seg *mkey_seg, - struct mlx5_ib_mr *mr, + struct mlx5_ib_mr *mr, u32 pdn, unsigned int page_shift) { mlx5r_umr_set_access_flags(dev, mkey_seg, mr->access_flags); - MLX5_SET(mkc, mkey_seg, pd, to_mpd(mr->ibmr.pd)->pdn); + MLX5_SET(mkc, mkey_seg, pd, pdn); MLX5_SET64(mkc, mkey_seg, start_addr, mr->ibmr.iova); MLX5_SET64(mkc, mkey_seg, len, mr->ibmr.length); MLX5_SET(mkc, mkey_seg, log_page_size, page_shift); @@ -670,23 +670,22 @@ static void mlx5r_umr_final_update_xlt(struct mlx5_ib_dev *dev, wqe->data_seg.byte_count = cpu_to_be32(sg->length); } -static void -_mlx5r_umr_init_wqe(struct mlx5_ib_mr *mr, struct mlx5r_umr_wqe *wqe, - struct ib_sge *sg, unsigned int flags, - unsigned int page_shift, bool dd) +static void _mlx5r_umr_init_wqe(struct mlx5_ib_mr *mr, + struct mlx5r_umr_wqe *wqe, struct ib_sge *sg, + unsigned int flags, u32 pdn, + unsigned int page_shift) { struct mlx5_ib_dev *dev = mr_to_mdev(mr); mlx5r_umr_set_update_xlt_ctrl_seg(&wqe->ctrl_seg, flags, sg); - mlx5r_umr_set_update_xlt_mkey_seg(dev, &wqe->mkey_seg, mr, page_shift); - if (dd) /* Use the data direct internal kernel PD */ - MLX5_SET(mkc, &wqe->mkey_seg, pd, dev->ddr.pdn); + mlx5r_umr_set_update_xlt_mkey_seg(dev, &wqe->mkey_seg, mr, pdn, + page_shift); mlx5r_umr_set_update_xlt_data_seg(&wqe->data_seg, sg); } -static int -_mlx5r_umr_update_mr_pas(struct mlx5_ib_mr *mr, unsigned int flags, bool dd, - size_t start_block, size_t nblocks) +static int _mlx5r_umr_update_mr_pas(struct mlx5_ib_mr *mr, unsigned int flags, + u32 pdn, bool dd, size_t start_block, + size_t nblocks) { size_t ent_size = dd ? sizeof(struct mlx5_ksm) : sizeof(struct mlx5_mtt); struct mlx5_ib_dev *dev = mr_to_mdev(mr); @@ -720,7 +719,7 @@ _mlx5r_umr_update_mr_pas(struct mlx5_ib_mr *mr, unsigned int flags, bool dd, orig_sg_length = sg.length; - _mlx5r_umr_init_wqe(mr, &wqe, &sg, flags, mr->page_shift, dd); + _mlx5r_umr_init_wqe(mr, &wqe, &sg, flags, pdn, mr->page_shift); /* Set initial translation offset to start_block */ offset = (u64)start_block * ent_size; @@ -811,7 +810,8 @@ int mlx5r_umr_update_data_direct_ksm_pas_range(struct mlx5_ib_mr *mr, !(flags & MLX5_IB_UPD_XLT_KEEP_PGSZ))) return -EINVAL; - return _mlx5r_umr_update_mr_pas(mr, flags, true, start_block, nblocks); + return _mlx5r_umr_update_mr_pas(mr, flags, mr_to_mdev(mr)->ddr.pdn, + true, start_block, nblocks); } int mlx5r_umr_update_data_direct_ksm_pas(struct mlx5_ib_mr *mr, @@ -821,12 +821,13 @@ int mlx5r_umr_update_data_direct_ksm_pas(struct mlx5_ib_mr *mr, } int mlx5r_umr_update_mr_pas_range(struct mlx5_ib_mr *mr, unsigned int flags, - size_t start_block, size_t nblocks) + u32 pdn, size_t start_block, size_t nblocks) { if (WARN_ON(mr->umem->is_odp)) return -EINVAL; - return _mlx5r_umr_update_mr_pas(mr, flags, false, start_block, nblocks); + return _mlx5r_umr_update_mr_pas(mr, flags, pdn, false, start_block, + nblocks); } /* @@ -834,9 +835,9 @@ int mlx5r_umr_update_mr_pas_range(struct mlx5_ib_mr *mr, unsigned int flags, * Dmabuf MR is handled in a similar way, except that the MLX5_IB_UPD_XLT_ZAP * flag may be used. */ -int mlx5r_umr_update_mr_pas(struct mlx5_ib_mr *mr, unsigned int flags) +int mlx5r_umr_update_mr_pas(struct mlx5_ib_mr *mr, unsigned int flags, u32 pdn) { - return mlx5r_umr_update_mr_pas_range(mr, flags, 0, 0); + return mlx5r_umr_update_mr_pas_range(mr, flags, pdn, 0, 0); } static bool umr_can_use_indirect_mkey(struct mlx5_ib_dev *dev) @@ -861,6 +862,7 @@ int mlx5r_umr_update_xlt(struct mlx5_ib_mr *mr, u64 idx, int npages, size_t orig_sg_length; size_t pages_iter; struct ib_sge sg; + u32 pdn = mlx5_mr_pdn(mr); int err = 0; void *xlt; @@ -895,7 +897,8 @@ int mlx5r_umr_update_xlt(struct mlx5_ib_mr *mr, u64 idx, int npages, } mlx5r_umr_set_update_xlt_ctrl_seg(&wqe.ctrl_seg, flags, &sg); - mlx5r_umr_set_update_xlt_mkey_seg(dev, &wqe.mkey_seg, mr, page_shift); + mlx5r_umr_set_update_xlt_mkey_seg(dev, &wqe.mkey_seg, mr, pdn, + page_shift); mlx5r_umr_set_update_xlt_data_seg(&wqe.data_seg, &sg); for (pages_mapped = 0; @@ -962,17 +965,18 @@ int mlx5r_umr_update_mr_page_shift(struct mlx5_ib_mr *mr, return err; } -static inline int -_mlx5r_dmabuf_umr_update_pas(struct mlx5_ib_mr *mr, unsigned int flags, - size_t start_block, size_t nblocks, bool dd) +static inline int _mlx5r_dmabuf_umr_update_pas(struct mlx5_ib_mr *mr, + unsigned int flags, u32 pdn, + size_t start_block, + size_t nblocks, bool dd) { if (dd) return mlx5r_umr_update_data_direct_ksm_pas_range(mr, flags, start_block, nblocks); else - return mlx5r_umr_update_mr_pas_range(mr, flags, start_block, - nblocks); + return mlx5r_umr_update_mr_pas_range(mr, flags, pdn, + start_block, nblocks); } /** @@ -986,11 +990,9 @@ _mlx5r_dmabuf_umr_update_pas(struct mlx5_ib_mr *mr, unsigned int flags, * Return: On success, returns the number of entries that were zapped. * On error, returns a negative error code. */ -static int _mlx5r_umr_zap_mkey(struct mlx5_ib_mr *mr, - unsigned int flags, - unsigned int page_shift, - size_t *nblocks, - bool dd) +static int _mlx5r_umr_zap_mkey(struct mlx5_ib_mr *mr, unsigned int flags, + unsigned int page_shift, size_t *nblocks, + u32 pdn, bool dd) { unsigned int old_page_shift = mr->page_shift; struct mlx5_ib_dev *dev = mr_to_mdev(mr); @@ -1030,7 +1032,7 @@ static int _mlx5r_umr_zap_mkey(struct mlx5_ib_mr *mr, */ if (*nblocks) mr->page_shift = max_page_shift; - err = _mlx5r_dmabuf_umr_update_pas(mr, flags, 0, *nblocks, dd); + err = _mlx5r_dmabuf_umr_update_pas(mr, flags, pdn, 0, *nblocks, dd); if (err) { mr->page_shift = old_page_shift; return err; @@ -1055,6 +1057,7 @@ static int _mlx5r_umr_zap_mkey(struct mlx5_ib_mr *mr, * entries accordingly * @mr: The memory region to update * @xlt_flags: Translation table update flags + * @pdn: Protection domain number * @page_shift: The new (optimized) page shift to use * * This function updates the page size and mkey translation entries for a DMABUF @@ -1074,7 +1077,7 @@ static int _mlx5r_umr_zap_mkey(struct mlx5_ib_mr *mr, * * Returns 0 on success or a negative error code on failure. */ -int mlx5r_umr_dmabuf_update_pgsz(struct mlx5_ib_mr *mr, u32 xlt_flags, +int mlx5r_umr_dmabuf_update_pgsz(struct mlx5_ib_mr *mr, u32 xlt_flags, u32 pdn, unsigned int page_shift) { unsigned int old_page_shift = mr->page_shift; @@ -1083,7 +1086,7 @@ int mlx5r_umr_dmabuf_update_pgsz(struct mlx5_ib_mr *mr, u32 xlt_flags, int err; err = _mlx5r_umr_zap_mkey(mr, xlt_flags, page_shift, &zapped_blocks, - mr->data_direct); + pdn, mr->data_direct); if (err) return err; @@ -1096,10 +1099,8 @@ int mlx5r_umr_dmabuf_update_pgsz(struct mlx5_ib_mr *mr, u32 xlt_flags, * the page size in the mkey yet. */ err = _mlx5r_dmabuf_umr_update_pas( - mr, - xlt_flags | MLX5_IB_UPD_XLT_KEEP_PGSZ, - zapped_blocks, - total_blocks - zapped_blocks, + mr, xlt_flags | MLX5_IB_UPD_XLT_KEEP_PGSZ, pdn, + zapped_blocks, total_blocks - zapped_blocks, mr->data_direct); if (err) goto err; @@ -1108,7 +1109,7 @@ int mlx5r_umr_dmabuf_update_pgsz(struct mlx5_ib_mr *mr, u32 xlt_flags, err = mlx5r_umr_update_mr_page_shift(mr, mr->page_shift); if (err) goto err; - err = _mlx5r_dmabuf_umr_update_pas(mr, xlt_flags, 0, zapped_blocks, + err = _mlx5r_dmabuf_umr_update_pas(mr, xlt_flags, pdn, 0, zapped_blocks, mr->data_direct); if (err) goto err; diff --git a/drivers/infiniband/hw/mlx5/umr.h b/drivers/infiniband/hw/mlx5/umr.h index 59809d4d7d729..99192ec67957c 100644 --- a/drivers/infiniband/hw/mlx5/umr.h +++ b/drivers/infiniband/hw/mlx5/umr.h @@ -101,13 +101,13 @@ int mlx5r_umr_update_data_direct_ksm_pas_range(struct mlx5_ib_mr *mr, size_t nblocks); int mlx5r_umr_update_data_direct_ksm_pas(struct mlx5_ib_mr *mr, unsigned int flags); int mlx5r_umr_update_mr_pas_range(struct mlx5_ib_mr *mr, unsigned int flags, - size_t start_block, size_t nblocks); -int mlx5r_umr_update_mr_pas(struct mlx5_ib_mr *mr, unsigned int flags); + u32 pdn, size_t start_block, size_t nblocks); +int mlx5r_umr_update_mr_pas(struct mlx5_ib_mr *mr, unsigned int flags, u32 pdn); int mlx5r_umr_update_xlt(struct mlx5_ib_mr *mr, u64 idx, int npages, int page_shift, int flags); int mlx5r_umr_update_mr_page_shift(struct mlx5_ib_mr *mr, unsigned int page_shift); -int mlx5r_umr_dmabuf_update_pgsz(struct mlx5_ib_mr *mr, u32 xlt_flags, +int mlx5r_umr_dmabuf_update_pgsz(struct mlx5_ib_mr *mr, u32 xlt_flags, u32 pdn, unsigned int page_shift); #endif /* _MLX5_IB_UMR_H */ From e89c8578d219507a32ecb437d1c45d1689db29c7 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 3 Jun 2026 22:27:46 -0300 Subject: [PATCH 1229/2758] IB/mlx5: Don't mangle the mr->pd inside the rereg callback [ Upstream commit 8e0a02a989c156ce17f06a645d5f075277e06b95 ] The rereg protocol expects the core code to change mr->pd and synchronize that change with the atomics and syncs. The driver should not touch it. mlx5 needed to update it in umr_rereg_pas() because mlx5r_umr_update_mr_pas() required the updated mr->pd to build the UMR. Simply switch mlx5r_umr_update_mr_pas() to use the pdn directly from the new pd and remove the mr->pd update. Fixes: 56e11d628c5d ("IB/mlx5: Added support for re-registration of MRs") Link: https://patch.msgid.link/r/7-v1-29ebd2c229b5+fd5-ib_mr_pd_jgg@nvidia.com Assisted-by: Codex:gpt-5-5 Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/mr.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index e29f5e628312b..41ca3b1a6db07 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -1130,10 +1130,8 @@ static int umr_rereg_pas(struct mlx5_ib_mr *mr, struct ib_pd *pd, if (err) return err; - if (flags & IB_MR_REREG_PD) { - mr->ibmr.pd = pd; + if (flags & IB_MR_REREG_PD) upd_flags |= MLX5_IB_UPD_XLT_PD; - } if (flags & IB_MR_REREG_ACCESS) { mr->access_flags = access_flags; upd_flags |= MLX5_IB_UPD_XLT_ACCESS; @@ -1143,7 +1141,7 @@ static int umr_rereg_pas(struct mlx5_ib_mr *mr, struct ib_pd *pd, mr->ibmr.length = new_umem->length; mr->page_shift = order_base_2(page_size); mr->umem = new_umem; - err = mlx5r_umr_update_mr_pas(mr, upd_flags, mlx5_mr_pdn(mr)); + err = mlx5r_umr_update_mr_pas(mr, upd_flags, to_mpd(pd)->pdn); if (err) { /* * The MR is revoked at this point so there is no issue to free From 6193f7a46229b413da3ae041f2cb95de19c1a9da Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Fri, 29 May 2026 23:31:06 +0800 Subject: [PATCH 1230/2758] spi: ep93xx: fix double-free of zeropage on DMA setup failure [ Upstream commit 7886054b06f762f62054957a8f6de0f14e6b7541 ] If DMA setup fails after allocating the zeropage, the error path frees the page but leaves espi->zeropage dangling. A subsequent call to ep93xx_spi_release_dma() sees the non-NULL pointer and frees the page again. Clear the pointer after freeing in the error path of ep93xx_spi_setup_dma(). Fixes: 626a96db1169 ("spi/ep93xx: add DMA support") Signed-off-by: Felix Gu Link: https://patch.msgid.link/20260529-ep93xx-v1-1-9185070ca1fc@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-ep93xx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c index f716c9607be4a..4cab0d97a68af 100644 --- a/drivers/spi/spi-ep93xx.c +++ b/drivers/spi/spi-ep93xx.c @@ -600,6 +600,7 @@ static int ep93xx_spi_setup_dma(struct device *dev, struct ep93xx_spi *espi) espi->dma_rx = NULL; fail_free_page: free_page((unsigned long)espi->zeropage); + espi->zeropage = NULL; return ret; } From 0230471eef5902207516851dcd47c559571dded2 Mon Sep 17 00:00:00 2001 From: Aaron Ma Date: Thu, 28 May 2026 16:21:09 +0800 Subject: [PATCH 1231/2758] ASoC: amd: acp-sdw-legacy: Bound DAI link iteration [ Upstream commit d49ecdf327cc91062d2f80996a163cf65fda1e60 ] create_sdw_dailinks() walks soc_dais until it finds an entry with initialised cleared, but soc_dais is allocated with exactly num_ends entries. If all entries are initialised, the loop reads past the end of the array. This was reported by KASAN: BUG: KASAN: slab-out-of-bounds in mc_probe+0x26b3/0x2774 [snd_acp_sdw_legacy_mach] Read of size 1 Pass the allocated entry count to create_sdw_dailinks() and stop before reading past the array. Fixes: 2981d9b0789c ("ASoC: amd: acp: add soundwire machine driver for legacy stack") Signed-off-by: Aaron Ma Link: https://patch.msgid.link/20260528082110.915549-1-aaron.ma@canonical.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/amd/acp/acp-sdw-legacy-mach.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c index 09b475c83c496..e8b6819cc4b45 100644 --- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c +++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c @@ -303,13 +303,14 @@ static int create_sdw_dailink(struct snd_soc_card *card, static int create_sdw_dailinks(struct snd_soc_card *card, struct snd_soc_dai_link **dai_links, int *be_id, - struct asoc_sdw_dailink *soc_dais, + struct asoc_sdw_dailink *soc_dais, int num_dais, struct snd_soc_codec_conf **codec_conf) { struct device *dev = card->dev; struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private; struct snd_soc_dai_link_component *sdw_platform_component; + int i; int ret; sdw_platform_component = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), @@ -329,7 +330,7 @@ static int create_sdw_dailinks(struct snd_soc_card *card, } /* generate DAI links by each sdw link */ - while (soc_dais->initialised) { + for (i = 0; i < num_dais && soc_dais->initialised; i++) { int current_be_id = 0; ret = create_sdw_dailink(card, soc_dais, dai_links, @@ -463,7 +464,7 @@ static int soc_card_dai_links_create(struct snd_soc_card *card) /* SDW */ if (sdw_be_num) { ret = create_sdw_dailinks(card, &dai_links, &be_id, - soc_dais, &codec_conf); + soc_dais, num_ends, &codec_conf); if (ret) return ret; } From 86a64c049873fe4d4a6a378e27a333ab5631c4b2 Mon Sep 17 00:00:00 2001 From: Aaron Ma Date: Thu, 28 May 2026 16:21:10 +0800 Subject: [PATCH 1232/2758] ASoC: amd: acp-sdw-sof: Bound DAI link iteration [ Upstream commit 4d992e63f52d58f52b724606c60ae7b37a1c582f ] create_sdw_dailinks() walks sof_dais until it finds an entry with initialised cleared, but sof_dais is allocated with exactly num_ends entries. If all entries are initialised, the loop reads past the end of the array. Pass the allocated entry count to create_sdw_dailinks() and stop before reading past the array. Fixes: 6d8348ddc56e ("ASoC: amd: acp: refactor SoundWire machine driver code") Signed-off-by: Aaron Ma Link: https://patch.msgid.link/20260528082110.915549-2-aaron.ma@canonical.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/amd/acp/acp-sdw-sof-mach.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c index a0fd8a6f99708..a423853f3a97d 100644 --- a/sound/soc/amd/acp/acp-sdw-sof-mach.c +++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c @@ -220,13 +220,14 @@ static int create_sdw_dailink(struct snd_soc_card *card, static int create_sdw_dailinks(struct snd_soc_card *card, struct snd_soc_dai_link **dai_links, int *be_id, - struct asoc_sdw_dailink *sof_dais, + struct asoc_sdw_dailink *sof_dais, int num_dais, struct snd_soc_codec_conf **codec_conf) { + int i; int ret; /* generate DAI links by each sdw link */ - while (sof_dais->initialised) { + for (i = 0; i < num_dais && sof_dais->initialised; i++) { int current_be_id = 0; ret = create_sdw_dailink(card, sof_dais, dai_links, @@ -334,7 +335,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) /* SDW */ if (sdw_be_num) { ret = create_sdw_dailinks(card, &dai_links, &be_id, - sof_dais, &codec_conf); + sof_dais, num_ends, &codec_conf); if (ret) return ret; } From c0f2dedd41fe14dbe076c1672214802fb42cd8c8 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Fri, 29 May 2026 15:09:06 +0000 Subject: [PATCH 1233/2758] firmware_loader: Fix recursive lock in device_cache_fw_images() [ Upstream commit d3ec78f8f8d48a04a9fac38d47275c34645e5103 ] A recursive locking deadlock can occur in the firmware loader's power management notification handler. During system suspend or hibernation preparation, fw_pm_notify() calls device_cache_fw_images(). This function acquires fw_lock to set the firmware cache state to FW_LOADER_START_CACHE and then iterates over all devices using dpm_for_each_dev() while still holding the lock. For each device, dev_cache_fw_image() schedules asynchronous work to cache the firmware. If memory allocation for the async work entry fails (e.g., in out-of-memory conditions), async_schedule_node_domain() falls back to executing the work function synchronously in the current thread. The synchronous execution path (__async_dev_cache_fw_image() -> cache_firmware() -> request_firmware() -> assign_fw()) attempts to acquire fw_lock again. Since the current thread already holds fw_lock, this results in a recursive locking deadlock. Fix this by releasing fw_lock immediately after updating the cache state and before calling dpm_for_each_dev(). The lock is only needed to protect the state update. Concurrent firmware requests will correctly see the FW_LOADER_START_CACHE state and use the piggyback mechanism, which is independently protected by its own fwc->name_lock. Fixes: ac39b3ea73aa ("firmware loader: let caching firmware piggyback on loading firmware") Assisted-by: Gemini:gemini-3.1-pro-preview Gemini:gemini-3-flash-preview syzbot Reported-by: syzbot+e70e4c6f6eee43357ba7@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=e70e4c6f6eee43357ba7 Link: https://syzkaller.appspot.com/ai_job?id=8b4af9fd-24af-423f-8acb-1159fd34c1a5 Signed-off-by: Dmitry Vyukov Link: https://patch.msgid.link/48b092a5-f49d-48a4-95f4-f65bebfc6bc3@mail.kernel.org Signed-off-by: Danilo Krummrich Signed-off-by: Sasha Levin --- drivers/base/firmware_loader/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c index a11b30dda23be..c96312ac2be7b 100644 --- a/drivers/base/firmware_loader/main.c +++ b/drivers/base/firmware_loader/main.c @@ -1503,9 +1503,10 @@ static void device_cache_fw_images(void) mutex_lock(&fw_lock); fwc->state = FW_LOADER_START_CACHE; - dpm_for_each_dev(NULL, dev_cache_fw_image); mutex_unlock(&fw_lock); + dpm_for_each_dev(NULL, dev_cache_fw_image); + /* wait for completion of caching firmware for all devices */ async_synchronize_full_domain(&fw_cache_domain); From e6d93108e0a27d7e6f95c7e45017d14ba2900d32 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 30 May 2026 03:48:34 -0400 Subject: [PATCH 1234/2758] configfs: fix lockless traversals of ->s_children [ Upstream commit 9b9e8bb81c41fd27e7b57a1c936fde140548535f ] Having the parent directory locked protects entries from removal by another thread, but it does *not* protect cursors from being moved around by lseek() - or freed, for that matter. Fixes: 6f6107640625 ("configfs: Introduce configfs_dirent_lock") Reviewed-by: Jan Kara Signed-off-by: Al Viro Signed-off-by: Sasha Levin --- fs/configfs/dir.c | 54 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index e84483a0836d8..eb991b2a9c341 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -235,15 +235,16 @@ static int configfs_dirent_exists(struct dentry *dentry) const unsigned char *new = dentry->d_name.name; struct configfs_dirent *sd; + spin_lock(&configfs_dirent_lock); list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { if (sd->s_element) { - const unsigned char *existing = configfs_get_name(sd); - if (strcmp(existing, new)) - continue; - else + if (strcmp(configfs_get_name(sd), new) == 0) { + spin_unlock(&configfs_dirent_lock); return -EEXIST; + } } } + spin_unlock(&configfs_dirent_lock); return 0; } @@ -575,11 +576,28 @@ static void configfs_detach_rollback(struct dentry *dentry) configfs_detach_rollback(sd->s_dentry); } +/* + * Find the next non-cursor. configfs_dirent_lock held by caller. + */ +static struct configfs_dirent *next_dirent(struct configfs_dirent *parent, + struct configfs_dirent *last) +{ + struct configfs_dirent *s; + + s = list_prepare_entry(last, &parent->s_children, s_sibling); + + list_for_each_entry_continue(s, &parent->s_children, s_sibling) { + if (s->s_element) + return s; + } + return NULL; +} + static void detach_attrs(struct config_item * item) { struct dentry * dentry = dget(item->ci_dentry); - struct configfs_dirent * parent_sd; - struct configfs_dirent * sd, * tmp; + struct configfs_dirent *parent_sd; + struct configfs_dirent *sd, *next; if (!dentry) return; @@ -588,15 +606,19 @@ static void detach_attrs(struct config_item * item) dentry->d_name.name); parent_sd = dentry->d_fsdata; - list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) { - if (!sd->s_element || !(sd->s_type & CONFIGFS_NOT_PINNED)) + + spin_lock(&configfs_dirent_lock); + for (sd = next_dirent(parent_sd, NULL); sd; sd = next) { + next = next_dirent(parent_sd, sd); + if (!(sd->s_type & CONFIGFS_NOT_PINNED)) continue; - spin_lock(&configfs_dirent_lock); list_del_init(&sd->s_sibling); spin_unlock(&configfs_dirent_lock); configfs_drop_dentry(sd, dentry); configfs_put(sd); + spin_lock(&configfs_dirent_lock); } + spin_unlock(&configfs_dirent_lock); /** * Drop reference from dget() on entrance. @@ -655,18 +677,20 @@ static void detach_groups(struct config_group *group) struct dentry * dentry = dget(group->cg_item.ci_dentry); struct dentry *child; struct configfs_dirent *parent_sd; - struct configfs_dirent *sd, *tmp; + struct configfs_dirent *sd, *next; if (!dentry) return; parent_sd = dentry->d_fsdata; - list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) { - if (!sd->s_element || - !(sd->s_type & CONFIGFS_USET_DEFAULT)) + spin_lock(&configfs_dirent_lock); + for (sd = next_dirent(parent_sd, NULL); sd; sd = next) { + next = next_dirent(parent_sd, sd); + if (!(sd->s_type & CONFIGFS_USET_DEFAULT)) continue; child = sd->s_dentry; + spin_unlock(&configfs_dirent_lock); inode_lock(d_inode(child)); @@ -678,7 +702,9 @@ static void detach_groups(struct config_group *group) d_delete(child); dput(child); + spin_lock(&configfs_dirent_lock); } + spin_unlock(&configfs_dirent_lock); /** * Drop reference from dget() on entrance. @@ -1130,6 +1156,7 @@ configfs_find_subsys_dentry(struct configfs_dirent *root_sd, struct configfs_dirent *p; struct configfs_dirent *ret = NULL; + spin_lock(&configfs_dirent_lock); list_for_each_entry(p, &root_sd->s_children, s_sibling) { if (p->s_type & CONFIGFS_DIR && p->s_element == subsys_item) { @@ -1137,6 +1164,7 @@ configfs_find_subsys_dentry(struct configfs_dirent *root_sd, break; } } + spin_unlock(&configfs_dirent_lock); return ret; } From c70b5bb6f04f77ef90e047d7edc2f476e206909f Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Mon, 1 Jun 2026 15:20:05 -0400 Subject: [PATCH 1235/2758] watchdog: unregister PM notifier on watchdog unregister [ Upstream commit a298c7302ee9584a7a1ac1e8acbede8d98ab51a4 ] watchdog_register_device() registers wdd->pm_nb when WDOG_NO_PING_ON_SUSPEND is set, but watchdog_unregister_device() does not remove it. This leaves an embedded notifier block on the PM notifier chain after the watchdog device has been unregistered. A later suspend/resume notification can then call watchdog_pm_notifier() with a stale watchdog_device pointer, or at minimum after wdd->wd_data has been cleared by watchdog_dev_unregister(). Unregister the PM notifier before tearing down the watchdog device. Fixes: 60bcd91aafd2 ("watchdog: introduce watchdog_dev_suspend/resume") Signed-off-by: Yuho Choi Link: https://lore.kernel.org/r/20260601192005.1970805-1-dbgh9129@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/watchdog/watchdog_core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index 8300520688d07..6c087b4ef5dd5 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c @@ -391,6 +391,9 @@ static void __watchdog_unregister_device(struct watchdog_device *wdd) if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) unregister_reboot_notifier(&wdd->reboot_nb); + if (test_bit(WDOG_NO_PING_ON_SUSPEND, &wdd->status)) + unregister_pm_notifier(&wdd->pm_nb); + watchdog_dev_unregister(wdd); ida_free(&watchdog_ida, wdd->id); } From e992db85f18784dd8a8a091fb4abc23b0109c26c Mon Sep 17 00:00:00 2001 From: Sneh Mankad Date: Fri, 29 May 2026 18:25:45 +0530 Subject: [PATCH 1236/2758] pinctrl: qcom: Fix resolving register base address from device node [ Upstream commit 4e31ab47997540d0ff4c9de801741bed03c1ab3f ] Commit 56ffb63749f4 ("pinctrl: qcom: add multi TLMM region option parameter") added reg-names property based register reading. However multiple platforms are not using the reg-names as they have only single TLMM register region. Commit tried to handle this using the default_region module parameter, however this condition is unreachable as the error return precedes it by just checking if reg-names property exists or not, making it impossible to use tlmm-test for the SoCs (x1e80100) which don't have reg-names property in TLMM device. Fix this by moving the default_region check at the start of the tlmm_reg_base(). Fixes: 56ffb63749f4 ("pinctrl: qcom: add multi TLMM region option parameter") Signed-off-by: Sneh Mankad Reviewed-by: Dmitry Baryshkov Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/qcom/tlmm-test.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/pinctrl/qcom/tlmm-test.c b/drivers/pinctrl/qcom/tlmm-test.c index 7d7fff538755a..4ac96538a417f 100644 --- a/drivers/pinctrl/qcom/tlmm-test.c +++ b/drivers/pinctrl/qcom/tlmm-test.c @@ -581,6 +581,9 @@ static int tlmm_reg_base(struct device_node *tlmm, struct resource *res) int ret; int i; + if (!strcmp(tlmm_reg_name, "default_region")) + return of_address_to_resource(tlmm, 0, res); + count = of_property_count_strings(tlmm, "reg-names"); if (count <= 0) { pr_err("failed to find tlmm reg name\n"); @@ -597,18 +600,14 @@ static int tlmm_reg_base(struct device_node *tlmm, struct resource *res) return -EINVAL; } - if (!strcmp(tlmm_reg_name, "default_region")) { - ret = of_address_to_resource(tlmm, 0, res); - } else { - for (i = 0; i < count; i++) { - if (!strcmp(reg_names[i], tlmm_reg_name)) { - ret = of_address_to_resource(tlmm, i, res); - break; - } + for (i = 0; i < count; i++) { + if (!strcmp(reg_names[i], tlmm_reg_name)) { + ret = of_address_to_resource(tlmm, i, res); + break; } - if (i == count) - ret = -EINVAL; } + if (i == count) + ret = -EINVAL; kfree(reg_names); From e30b595bfbb025181561c6b4c7ed59455a9813eb Mon Sep 17 00:00:00 2001 From: David Disseldorp Date: Fri, 5 Jun 2026 22:16:47 +1000 Subject: [PATCH 1237/2758] scsi: target: Fix hexadecimal CHAP_I handling [ Upstream commit 7e161211f1dd5288b4ea802b30e70ef919ebc3da ] A mutual CHAP handshake requires target processing of an initiator-sent CHAP_I identifier. The RFC 3720 specification states: 11.1.4. Challenge Handshake Authentication Protocol (CHAP) ... CHAP_A= CHAP_I= CHAP_C= ... Where N, (A,A1,A2), I, C, and R are (correspondingly) the Name, Algorithm, Identifier, Challenge, and Response as defined in [RFC1994], N is a text string, A,A1,A2, and I are numbers CHAP_I parsing currently calls extract_param(), which returns the @identifier string (stripped of any 0b/0B or 0x/0X prefix) and a @type which indicates DECIMAL, HEX, or BASE64 encoding (based on any stripped prefix). Any HEX encoded CHAP_I string is further processed via: ret = kstrtoul(&identifier[2], 0, &id); This is incorrect for two reasons: * The @identifier string has already been stripped of the 0x/0X prefix, so skipping the first two bytes omits part of the number. * The kstrtoul() call specifies a base of 0, which will see &identifier[2] parsed as a decimal, unless a '0x' or (octal) '0' is erroneously present at that offset. Fix this by passing the (zero-offset) identifier string to kstrtoul() along with a base=16 parameter. Also add an explicit error handler for BASE64 encoding. Hex-encoded CHAP_I handling can be testing using the libiscsi EncodedI test linked below. Reported-by: Sashiko (gemini/gemini-3.1-pro-preview) Link: https://sashiko.dev/#/patchset/20260521151121.808477-1-hossu.alexandru%40gmail.com Link: https://github.com/sahlberg/libiscsi/pull/473 Fixes: 85db7391310b ("scsi: target: iscsi: Validate CHAP_R length before base64 decode") Signed-off-by: David Disseldorp Reviewed-by: Lee Duncan Reviewed-by: John Garry Link: https://patch.msgid.link/20260605122019.24146-2-ddiss@suse.de Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/target/iscsi/iscsi_target_auth.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target_auth.c b/drivers/target/iscsi/iscsi_target_auth.c index a3ad2d244dbee..5858cc3089796 100644 --- a/drivers/target/iscsi/iscsi_target_auth.c +++ b/drivers/target/iscsi/iscsi_target_auth.c @@ -438,9 +438,11 @@ static int chap_server_compute_hash( } if (type == HEX) - ret = kstrtoul(&identifier[2], 0, &id); + ret = kstrtoul(identifier, 16, &id); + else if (type == DECIMAL) + ret = kstrtoul(identifier, 10, &id); else - ret = kstrtoul(identifier, 0, &id); + ret = -EINVAL; if (ret < 0) { pr_err("kstrtoul() failed for CHAP identifier: %d\n", ret); From 36f3faeffefbb871afd745e3365b9ad8a8d3d65f Mon Sep 17 00:00:00 2001 From: Hongjie Fang Date: Fri, 5 Jun 2026 19:20:34 +0800 Subject: [PATCH 1238/2758] scsi: ufs: core: Handle PM commands timeout before SCSI EH [ Upstream commit 01d5e237b33931b970dd190dd6a19c5ef32c105d ] A PM START STOP sent from the UFS well-known LU resume path can race with SCSI EH: The "wl resume" task flow is: __ufshcd_wl_resume() ufshcd_set_dev_pwr_mode(UFS_ACTIVE_PWR_MODE) ufshcd_execute_start_stop() scsi_execute_cmd() blk_execute_rq <-- wait scsi_check_passthrough() <-- may retry START STOP If the first START STOP time out, SCSI EH may already recover the link and reset the device before scsi_execute_cmd() returns: scsi_timeout() scsi_eh_scmd_add() scsi_error_handler() scsi_unjam_host() scsi_eh_ready_devs() scsi_eh_host_reset() ufshcd_eh_host_reset_handler() if (hba->pm_op_in_progress) ufshcd_link_recovery() ufshcd_device_reset() ufshcd_host_reset_and_restore() ... scsi_eh_flush_done_q() <-- wakeup "wl resume" task ... <-- host still in SHOST_RECOVERY scsi_restart_operations() A later passthrough retry can then run while the host is still in SHOST_RECOVERY and hit the SCMD_FAIL_IF_RECOVERING path: scsi_queue_rq() if (scsi_host_in_recovery(shost) && cmd->flags & SCMD_FAIL_IF_RECOVERING) return BLK_STS_OFFLINE That retry completes with DID_ERROR or DID_NO_CONNECT even though EH may already have restored the device to an operational ACTIVE state. Handle these PM timeouts directly from ufshcd_eh_timed_out() instead. After ufshcd_link_recovery(), complete the timed-out command immediately if it has not been completed already. For regular SCSI commands, complete them with DID_REQUEUE to match the existing MCQ force-completion semantics and allow scsi_execute_cmd() to retry if needed. For reserved internal device-management commands, finish the request with DID_TIME_OUT without calling ufshcd_release_scsi_cmd() since those commands use different resource lifetime rules. The system_suspending flag is no longer needed because PM command timeout handling now uses pm_op_in_progress. Fixes: b8c3a7bac9b6 ("scsi: ufs: Have midlayer retry start stop errors") Signed-off-by: Hongjie Fang Reviewed-by: Bart Van Assche Reviewed-by: Peter Wang Link: https://patch.msgid.link/20260605112034.3802540-1-hongjiefang@asrmicro.com Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/ufs/core/ufshcd.c | 34 +++++++++++++++++++++++++++------- include/ufs/ufshcd.h | 3 --- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index 385e1a8eec162..84e32957c332d 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -9466,22 +9466,44 @@ static enum scsi_timeout_action ufshcd_eh_timed_out(struct scsi_cmnd *scmd) { struct ufs_hba *hba = shost_priv(scmd->device->host); - if (!hba->system_suspending) { + if (!hba->pm_op_in_progress) { /* Activate the error handler in the SCSI core. */ return SCSI_EH_NOT_HANDLED; } /* - * If we get here we know that no TMFs are outstanding and also that - * the only pending command is a START STOP UNIT command. Handle the - * timeout of that command directly to prevent a deadlock between + * Handle the timeout directly to prevent a deadlock between * ufshcd_set_dev_pwr_mode() and ufshcd_err_handler(). */ ufshcd_link_recovery(hba); dev_info(hba->dev, "%s() finished; outstanding_tasks = %#lx.\n", __func__, hba->outstanding_tasks); - return scsi_host_busy(hba->host) ? SCSI_EH_RESET_TIMER : SCSI_EH_DONE; + /* + * ufshcd_link_recovery() may already have completed @scmd, e.g. via + * the existing MCQ force-completion path. + */ + if (!test_bit(SCMD_STATE_COMPLETE, &scmd->state)) { + if (!hba->mcq_enabled) { + unsigned long flags; + struct request *rq = scsi_cmd_to_rq(scmd); + + spin_lock_irqsave(&hba->outstanding_lock, flags); + __clear_bit(rq->tag, &hba->outstanding_reqs); + spin_unlock_irqrestore(&hba->outstanding_lock, flags); + } + + if (ufshcd_is_scsi_cmd(scmd)) { + set_host_byte(scmd, DID_REQUEUE); + ufshcd_release_scsi_cmd(hba, scmd); + } else { + set_host_byte(scmd, DID_TIME_OUT); + } + + scsi_done(scmd); + } + + return SCSI_EH_DONE; } static const struct attribute_group *ufshcd_driver_groups[] = { @@ -10518,7 +10540,6 @@ static int ufshcd_wl_suspend(struct device *dev) hba = shost_priv(sdev->host); down(&hba->host_sem); - hba->system_suspending = true; if (pm_runtime_suspended(dev)) goto out; @@ -10560,7 +10581,6 @@ static int ufshcd_wl_resume(struct device *dev) hba->curr_dev_pwr_mode, hba->uic_link_state); if (!ret) hba->is_sys_suspended = false; - hba->system_suspending = false; up(&hba->host_sem); return ret; } diff --git a/include/ufs/ufshcd.h b/include/ufs/ufshcd.h index cfbc75d8df836..8280a95c00c74 100644 --- a/include/ufs/ufshcd.h +++ b/include/ufs/ufshcd.h @@ -1020,8 +1020,6 @@ enum ufshcd_mcq_opr { * @caps: bitmask with information about UFS controller capabilities * @devfreq: frequency scaling information owned by the devfreq core * @clk_scaling: frequency scaling information owned by the UFS driver - * @system_suspending: system suspend has been started and system resume has - * not yet finished. * @is_sys_suspended: UFS device has been suspended because of system suspend * @urgent_bkops_lvl: keeps track of urgent bkops level for device * @is_urgent_bkops_lvl_checked: keeps track if the urgent bkops level for @@ -1197,7 +1195,6 @@ struct ufs_hba { struct devfreq *devfreq; struct ufs_clk_scaling clk_scaling; - bool system_suspending; bool is_sys_suspended; enum bkops_status urgent_bkops_lvl; From fe034958db5fb9c92fafaa1bb242aaa47cce04cc Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Sat, 30 May 2026 00:23:47 -0500 Subject: [PATCH 1239/2758] scsi: target: Remove tcm_loop target reset handling [ Upstream commit 7c08d430835a90414cd962e3a9602e5b002dee3b ] tcm_loop_target_reset is supposed to handle all the LUNs on a target but it's only doing a TMR_LUN_RESET so only that one LUN is handled. This will cause us to return early while IOs to other LUNs are still hung in lower layers. This just removes the target reset handler for the driver because LIO doesn't support target resets and for the common case where this is run from the scsi-ml error hamdler we have already tried an abort and lun reset so waiting again is most likely useless. Fixes: 1333eee56cdf ("scsi: target: tcm_loop: Drain commands in target_reset handler") Signed-off-by: Mike Christie Reviewed-by: Hannes Reinecke Link: https://patch.msgid.link/20260530052349.5134-1-michael.christie@oracle.com Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/target/loopback/tcm_loop.c | 64 ------------------------------ 1 file changed, 64 deletions(-) diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c index 1102973457513..d29830b951f75 100644 --- a/drivers/target/loopback/tcm_loop.c +++ b/drivers/target/loopback/tcm_loop.c @@ -270,69 +270,6 @@ static int tcm_loop_device_reset(struct scsi_cmnd *sc) return (ret == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED; } -static bool tcm_loop_flush_work_iter(struct request *rq, void *data) -{ - struct scsi_cmnd *sc = blk_mq_rq_to_pdu(rq); - struct tcm_loop_cmd *tl_cmd = scsi_cmd_priv(sc); - struct se_cmd *se_cmd = &tl_cmd->tl_se_cmd; - - flush_work(&se_cmd->work); - return true; -} - -static int tcm_loop_target_reset(struct scsi_cmnd *sc) -{ - struct tcm_loop_hba *tl_hba; - struct tcm_loop_tpg *tl_tpg; - struct Scsi_Host *sh = sc->device->host; - int ret; - - /* - * Locate the tcm_loop_hba_t pointer - */ - tl_hba = *(struct tcm_loop_hba **)shost_priv(sh); - if (!tl_hba) { - pr_err("Unable to perform device reset without active I_T Nexus\n"); - return FAILED; - } - /* - * Locate the tl_tpg pointer from TargetID in sc->device->id - */ - tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; - if (!tl_tpg) - return FAILED; - - /* - * Issue a LUN_RESET to drain all commands that the target core - * knows about. This handles commands not yet marked CMD_T_COMPLETE. - */ - ret = tcm_loop_issue_tmr(tl_tpg, sc->device->lun, 0, TMR_LUN_RESET); - if (ret != TMR_FUNCTION_COMPLETE) - return FAILED; - - /* - * Flush any deferred target core completion work that may still be - * queued. Commands that already had CMD_T_COMPLETE set before the TMR - * are skipped by the TMR drain, but their async completion work - * (transport_lun_remove_cmd → percpu_ref_put, release_cmd → scsi_done) - * may still be pending in target_completion_wq. - * - * The SCSI EH will reuse in-flight scsi_cmnd structures for recovery - * commands (e.g. TUR) immediately after this handler returns SUCCESS — - * if deferred work is still pending, the memset in queuecommand would - * zero the se_cmd while the work accesses it, leaking the LUN - * percpu_ref and hanging configfs unlink forever. - * - * Use blk_mq_tagset_busy_iter() to find all started requests and - * flush_work() on each — the same pattern used by mpi3mr, scsi_debug, - * and other SCSI drivers to drain outstanding commands during reset. - */ - blk_mq_tagset_busy_iter(&sh->tag_set, tcm_loop_flush_work_iter, NULL); - - tl_tpg->tl_transport_status = TCM_TRANSPORT_ONLINE; - return SUCCESS; -} - static const struct scsi_host_template tcm_loop_driver_template = { .show_info = tcm_loop_show_info, .proc_name = "tcm_loopback", @@ -341,7 +278,6 @@ static const struct scsi_host_template tcm_loop_driver_template = { .change_queue_depth = scsi_change_queue_depth, .eh_abort_handler = tcm_loop_abort_task, .eh_device_reset_handler = tcm_loop_device_reset, - .eh_target_reset_handler = tcm_loop_target_reset, .this_id = -1, .sg_tablesize = 256, .max_sectors = 0xFFFF, From e8a62a475e8926e46137d367abf0b8cca17ec63c Mon Sep 17 00:00:00 2001 From: Luca Leonardo Scorcia Date: Sun, 31 May 2026 17:22:30 +0100 Subject: [PATCH 1240/2758] pinctrl: mediatek: mt8516: Fix Schmitt trigger register offset of pins 34-39 [ Upstream commit 1c3044cab23a056ea28da47da1cdd667a39df0b8 ] The correct Schmitt trigger register offset for pins 34-39 is 0xA00. Signed-off-by: Luca Leonardo Scorcia Fixes: 264667112ef0 ("pinctrl: mediatek: Add MT8516 Pinctrl driver") Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/mediatek/pinctrl-mt8516.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/mediatek/pinctrl-mt8516.c b/drivers/pinctrl/mediatek/pinctrl-mt8516.c index abda75d4354e2..68d6638e7f4b1 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mt8516.c +++ b/drivers/pinctrl/mediatek/pinctrl-mt8516.c @@ -244,7 +244,7 @@ static const struct mtk_pin_ies_smt_set mt8516_smt_set[] = { MTK_PIN_IES_SMT_SPEC(24, 25, 0xA00, 12), MTK_PIN_IES_SMT_SPEC(26, 30, 0xA00, 0), MTK_PIN_IES_SMT_SPEC(31, 33, 0xA00, 1), - MTK_PIN_IES_SMT_SPEC(34, 39, 0xA900, 2), + MTK_PIN_IES_SMT_SPEC(34, 39, 0xA00, 2), MTK_PIN_IES_SMT_SPEC(40, 40, 0xA10, 11), MTK_PIN_IES_SMT_SPEC(41, 43, 0xA00, 10), MTK_PIN_IES_SMT_SPEC(44, 47, 0xA00, 11), From fae2a81f7e97640b420d310b54dc16441485d41b Mon Sep 17 00:00:00 2001 From: Luca Leonardo Scorcia Date: Sun, 31 May 2026 17:23:32 +0100 Subject: [PATCH 1241/2758] pinctrl: mediatek: mt8167: Fix Schmitt trigger register offset of pins 34-39 [ Upstream commit 439bc91d20188901dac698bed4921caac76d9074 ] The correct Schmitt trigger register offset for pins 34-39 is 0xA00. Value was verified with SoC data sheet. Signed-off-by: Luca Leonardo Scorcia Fixes: 82d70627e94a ("pinctrl: mediatek: Add MT8167 Pinctrl driver") Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/mediatek/pinctrl-mt8167.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/mediatek/pinctrl-mt8167.c b/drivers/pinctrl/mediatek/pinctrl-mt8167.c index 143c266222725..c812d614e9d45 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mt8167.c +++ b/drivers/pinctrl/mediatek/pinctrl-mt8167.c @@ -244,7 +244,7 @@ static const struct mtk_pin_ies_smt_set mt8167_smt_set[] = { MTK_PIN_IES_SMT_SPEC(24, 25, 0xA00, 12), MTK_PIN_IES_SMT_SPEC(26, 30, 0xA00, 0), MTK_PIN_IES_SMT_SPEC(31, 33, 0xA00, 1), - MTK_PIN_IES_SMT_SPEC(34, 39, 0xA900, 2), + MTK_PIN_IES_SMT_SPEC(34, 39, 0xA00, 2), MTK_PIN_IES_SMT_SPEC(40, 40, 0xA10, 11), MTK_PIN_IES_SMT_SPEC(41, 43, 0xA00, 10), MTK_PIN_IES_SMT_SPEC(44, 47, 0xA00, 11), From 2518efbf2c9438a2272e3e507804ecad471c8ab2 Mon Sep 17 00:00:00 2001 From: Yu-Chun Lin Date: Mon, 1 Jun 2026 15:52:29 +0800 Subject: [PATCH 1242/2758] dt-bindings: pinctrl: realtek,rtd1625: Fix input voltage property name [ Upstream commit 388d2fe16283d68e4be8c5317809a4a218ebeace ] The property 'input-voltage-microvolt' is a typo. Rename it to 'input-threshold-voltage-microvolt' to align with the standard pin configuration defined in pincfg-node.yaml and parsed by pinconf-generic.c. Fixes: f6ea7004e926 ("dt-bindings: pinctrl: realtek: Add RTD1625 pinctrl binding") Signed-off-by: Yu-Chun Lin Acked-by: Conor Dooley Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- .../devicetree/bindings/pinctrl/realtek,rtd1625-pinctrl.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/pinctrl/realtek,rtd1625-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/realtek,rtd1625-pinctrl.yaml index 9562a043707ee..adc5955a2047c 100644 --- a/Documentation/devicetree/bindings/pinctrl/realtek,rtd1625-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/realtek,rtd1625-pinctrl.yaml @@ -110,7 +110,7 @@ patternProperties: input-schmitt-disable: true - input-voltage-microvolt: + input-threshold-voltage-microvolt: description: | Select the input receiver voltage domain for the pin. Valid arguments are: From c3e84e2968abe30c516ca084e009e3b2771e9186 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Tue, 2 Jun 2026 16:11:16 -0500 Subject: [PATCH 1243/2758] pinctrl: PINCTRL_STMFX should depend on CONFIG_OF [ Upstream commit fd9490bdd4536a8e0911578f62164176b9000552 ] Commit e785c990adcc ("pinctrl: Kconfig: drop unneeded dependencies on OF_GPIO") removed a redundant dependecy on CONFIG_OF_GPIO for several pinctrl drivers, but this change also removed a dependency on CONFIG_OF for some of those drivers. Normally, this wouldn't be a problem, but PINCTRL_STMFX also selected MFD_STMFX, which does depend on CONFIG_OF. This conflict allows MFD_STMFX to be enabled even if CONFIG_OF is disabled. Fix this by also having PINCTRL_STMFX depend on CONFIG_OF. This is okay because the pinctrl-stmfx driver actually does depend on CONFIG_OF functions. Fixes: e785c990adcc ("pinctrl: Kconfig: drop unneeded dependencies on OF_GPIO") Signed-off-by: Timur Tabi Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 03f2e3ee065f4..75131b6e6eeac 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -548,6 +548,7 @@ config PINCTRL_ST config PINCTRL_STMFX tristate "STMicroelectronics STMFX GPIO expander pinctrl driver" + depends on OF depends on I2C depends on HAS_IOMEM select GENERIC_PINCONF From cc69d0332421fd2943b66dbe5e597bf4fd5af126 Mon Sep 17 00:00:00 2001 From: Ankit Soni Date: Tue, 26 May 2026 11:10:34 +0000 Subject: [PATCH 1244/2758] iommufd: Take dma_resv lock before dma_buf_unpin() in release path [ Upstream commit e745cd2c749e557c14a15ac931761c3f58c24489 ] dma_buf_unpin() requires the caller to hold the exporter's dma_resv lock: void dma_buf_unpin(struct dma_buf_attachment *attach) { ... dma_resv_assert_held(dmabuf->resv); ... } iopt_release_pages() calls dma_buf_unpin() without taking that lock, so every iommufd_ioas_destroy()/iommufd_ioas_unmap() that releases the last reference on a DMABUF-backed iopt_pages triggers a WARN. This was hit while running tools/testing/selftests/iommu/iommufd: WARNING: drivers/dma-buf/dma-buf.c:1137 at dma_buf_unpin+0x62/0x70 RIP: 0010:dma_buf_unpin+0x62/0x70 Call Trace: dma_buf_unpin+0x62/0x70 iopt_release_pages+0xe4/0x190 iopt_unmap_iova_range+0x1c7/0x290 iopt_unmap_all+0x1a/0x30 iommufd_ioas_destroy+0x1d/0x50 iommufd_fops_release+0x93/0x150 __fput+0xfc/0x2c0 __x64_sys_close+0x3d/0x80 do_syscall_64+0x65/0x180 Take the dma_resv lock around dma_buf_unpin() in iopt_release_pages(), matching the iopt_map_dmabuf() convention. dma_buf_detach() acquires the reservation lock internally, so it must remain outside the locked region. Fixes: 8c5f9645c389 ("iommufd: Add dma_buf_pin()") Link: https://patch.msgid.link/r/20260526111034.4079-1-Ankit.Soni@amd.com Reported-by: Ankit Soni Signed-off-by: Ankit Soni Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/iommu/iommufd/pages.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iommu/iommufd/pages.c b/drivers/iommu/iommufd/pages.c index 9bdb2945afe1e..7b64002e54b9a 100644 --- a/drivers/iommu/iommufd/pages.c +++ b/drivers/iommu/iommufd/pages.c @@ -1663,7 +1663,9 @@ void iopt_release_pages(struct kref *kref) if (iopt_is_dmabuf(pages) && pages->dmabuf.attach) { struct dma_buf *dmabuf = pages->dmabuf.attach->dmabuf; + dma_resv_lock(dmabuf->resv, NULL); dma_buf_unpin(pages->dmabuf.attach); + dma_resv_unlock(dmabuf->resv); dma_buf_detach(dmabuf, pages->dmabuf.attach); dma_buf_put(dmabuf); WARN_ON(!list_empty(&pages->dmabuf.tracker)); From 0507fcedbdcc87281ef8639c045fc9980363bbb6 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Mon, 8 Jun 2026 21:20:25 -0300 Subject: [PATCH 1245/2758] iommufd: Destroy the pages content after detaching from dmabuf [ Upstream commit f2d70dbd3dcefa8e3c380beff9c31f5f033a4221 ] Sashiko points out this has gotten out of order, the mutex could still be in use through the dmabuf invalidation callbacks. Don't destroy any of the pages content until the dmabuf is fully detached. Fixes: 71db84a092c3 ("iommufd: Add DMABUF to iopt_pages") Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/iommu/iommufd/pages.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iommu/iommufd/pages.c b/drivers/iommu/iommufd/pages.c index 7b64002e54b9a..03c8379bbc347 100644 --- a/drivers/iommu/iommufd/pages.c +++ b/drivers/iommu/iommufd/pages.c @@ -1656,10 +1656,6 @@ void iopt_release_pages(struct kref *kref) WARN_ON(!RB_EMPTY_ROOT(&pages->domains_itree.rb_root)); WARN_ON(pages->npinned); WARN_ON(!xa_empty(&pages->pinned_pfns)); - mmdrop(pages->source_mm); - mutex_destroy(&pages->mutex); - put_task_struct(pages->source_task); - free_uid(pages->source_user); if (iopt_is_dmabuf(pages) && pages->dmabuf.attach) { struct dma_buf *dmabuf = pages->dmabuf.attach->dmabuf; @@ -1672,6 +1668,10 @@ void iopt_release_pages(struct kref *kref) } else if (pages->type == IOPT_ADDRESS_FILE) { fput(pages->file); } + mmdrop(pages->source_mm); + mutex_destroy(&pages->mutex); + put_task_struct(pages->source_task); + free_uid(pages->source_user); kfree(pages); } From 8346a7db76402108e280737ffb9e4e6891db4da7 Mon Sep 17 00:00:00 2001 From: Hao Ge Date: Thu, 28 May 2026 09:13:36 +0800 Subject: [PATCH 1246/2758] lib/test_hmm: fix memory leak in dmirror_migrate_to_system() [ Upstream commit 8f7275c174bc5bcc8fc1bec8024e2b3e6fe17f46 ] Move the kvcalloc() calls after the early return checks to avoid leaking src_pfns and dst_pfns when end < start or mmget_not_zero() fails. Link: https://lore.kernel.org/20260528011336.20797-1-hao.ge@linux.dev Fixes: 775465fd26a3 ("lib/test_hmm: add zone device private THP test infrastructure") Signed-off-by: Hao Ge Reviewed-by: Alistair Popple Reported-by: Sashiko Reviewed-by: Balbir Singh Cc: Jason Gunthorpe Cc: Leon Romanovsky Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- lib/test_hmm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/test_hmm.c b/lib/test_hmm.c index 38996c4baa408..1e2b76e79c74e 100644 --- a/lib/test_hmm.c +++ b/lib/test_hmm.c @@ -1111,9 +1111,6 @@ static int dmirror_migrate_to_system(struct dmirror *dmirror, unsigned long *src_pfns; unsigned long *dst_pfns; - src_pfns = kvcalloc(PTRS_PER_PTE, sizeof(*src_pfns), GFP_KERNEL | __GFP_NOFAIL); - dst_pfns = kvcalloc(PTRS_PER_PTE, sizeof(*dst_pfns), GFP_KERNEL | __GFP_NOFAIL); - start = cmd->addr; end = start + size; if (end < start) @@ -1123,6 +1120,9 @@ static int dmirror_migrate_to_system(struct dmirror *dmirror, if (!mmget_not_zero(mm)) return -EINVAL; + src_pfns = kvcalloc(PTRS_PER_PTE, sizeof(*src_pfns), GFP_KERNEL | __GFP_NOFAIL); + dst_pfns = kvcalloc(PTRS_PER_PTE, sizeof(*dst_pfns), GFP_KERNEL | __GFP_NOFAIL); + cmd->cpages = 0; mmap_read_lock(mm); for (addr = start; addr < end; addr = next) { From 55a44f5259e6a5f1d5b11cb0e19e943c9cc62280 Mon Sep 17 00:00:00 2001 From: Hui Zhu Date: Fri, 29 May 2026 09:41:30 +0800 Subject: [PATCH 1247/2758] vmalloc: fix NULL pointer dereference in is_vm_area_hugepages() [ Upstream commit c55dd3b46c1208d6d2ea737a8aefef4aa4c70cb8 ] find_vm_area() can return NULL if the given address is not a valid vmalloc area. Check the return value before dereferencing it to avoid a kernel crash. Link: https://lore.kernel.org/20260529014130.671291-1-hui.zhu@linux.dev Fixes: 121e6f3258fe ("mm/vmalloc: hugepage vmalloc mappings") Signed-off-by: Hui Zhu Reviewed-by: Dev Jain Reviewed-by: Uladzislau Rezki (Sony) Cc: Nicholas Piggin Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- include/linux/vmalloc.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h index 3b02c0c6b3718..d87dc7f77f4e8 100644 --- a/include/linux/vmalloc.h +++ b/include/linux/vmalloc.h @@ -265,7 +265,9 @@ static inline bool is_vm_area_hugepages(const void *addr) * allocated in the vmalloc layer. */ #ifdef CONFIG_HAVE_ARCH_HUGE_VMALLOC - return find_vm_area(addr)->page_order > 0; + struct vm_struct *area = find_vm_area(addr); + + return area && area->page_order > 0; #else return false; #endif From 3c853f7bd67db3df98ee3685180eab78f92b873f Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Mon, 8 Jun 2026 16:14:21 +0200 Subject: [PATCH 1248/2758] rust: kbuild: show the right `quiet_cmd_rustc_procmacrolibrary` [ Upstream commit 70a8d5ada9fabf4a34732b718f9c992195eed2ea ] When Clippy is skipped, `RUSTC` should be shown in `quiet` instead of `CLIPPY` to be accurate and to avoid confusion. Thus do so, matching what we do in `quiet_cmd_rustc_library`. Fixes: 7dbe46c0b11d ("rust: kbuild: add proc macro library support") Reviewed-by: Nicolas Schier Link: https://patch.msgid.link/20260608141439.182634-3-ojeda@kernel.org Signed-off-by: Miguel Ojeda Signed-off-by: Sasha Levin --- rust/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/Makefile b/rust/Makefile index b9e9f512cec31..bec9726f256ce 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -517,7 +517,7 @@ $(obj)/exports_bindings_generated.h: $(obj)/bindings.o FORCE $(obj)/exports_kernel_generated.h: $(obj)/kernel.o FORCE $(call if_changed,exports) -quiet_cmd_rustc_procmacrolibrary = $(RUSTC_OR_CLIPPY_QUIET) PL $@ +quiet_cmd_rustc_procmacrolibrary = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) PL $@ cmd_rustc_procmacrolibrary = \ $(if $(skip_clippy),$(RUSTC),$(RUSTC_OR_CLIPPY)) \ $(filter-out $(skip_flags),$(rust_common_flags) $(rustc_target_flags)) \ From fdd7c6490ec39ff59ffed60b4eff3644ccfff53c Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Mon, 8 Dec 2025 16:33:14 -0600 Subject: [PATCH 1249/2758] remoteproc: qcom_q6v5_wcss: drop redundant wcss_q6_bcr_reset [ Upstream commit 6ad61d0acd41044a949e84f96a5f8e02284d350f ] The wcss_q6_bcr_reset used on QCS404, and wcss_q6_reset used on IPQ are the same. "BCR reset" is redundant, and likely a mistake. Use the documented "wcss_q6_reset" instead. Drop ".wcss_q6_reset_required" from the descriptor, since all targets now need it. This changes the bindings expectations, however, it actually fixes the driver to consume the intended ones (qcom,q6v5.txt), which lists "wcss_q6_reset" and *not* "wcss_q6_bcr_reset" Fixes: 0af65b9b915e ("remoteproc: qcom: wcss: Add non pas wcss Q6 support for QCS404") Reviewed-by: Konrad Dybcio Signed-off-by: Alexandru Gagniuc Link: https://lore.kernel.org/r/20251208223315.3540680-1-mr.nuke.me@gmail.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- drivers/remoteproc/qcom_q6v5_wcss.c | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/drivers/remoteproc/qcom_q6v5_wcss.c b/drivers/remoteproc/qcom_q6v5_wcss.c index c27200159a88a..b391724cfd08e 100644 --- a/drivers/remoteproc/qcom_q6v5_wcss.c +++ b/drivers/remoteproc/qcom_q6v5_wcss.c @@ -96,7 +96,6 @@ struct wcss_data { unsigned int crash_reason_smem; u32 version; bool aon_reset_required; - bool wcss_q6_reset_required; const char *ssr_name; const char *sysmon_name; int ssctl_id; @@ -134,7 +133,6 @@ struct q6v5_wcss { struct reset_control *wcss_aon_reset; struct reset_control *wcss_reset; struct reset_control *wcss_q6_reset; - struct reset_control *wcss_q6_bcr_reset; struct qcom_q6v5 q6v5; @@ -309,7 +307,7 @@ static int q6v5_wcss_qcs404_power_on(struct q6v5_wcss *wcss) return ret; /* Remove reset to the WCNSS QDSP6SS */ - reset_control_deassert(wcss->wcss_q6_bcr_reset); + reset_control_deassert(wcss->wcss_q6_reset); /* Enable Q6SSTOP_AHBFABRIC_CBCR clock */ ret = clk_prepare_enable(wcss->ahbfabric_cbcr_clk); @@ -803,19 +801,10 @@ static int q6v5_wcss_init_reset(struct q6v5_wcss *wcss, return PTR_ERR(wcss->wcss_reset); } - if (desc->wcss_q6_reset_required) { - wcss->wcss_q6_reset = devm_reset_control_get_exclusive(dev, "wcss_q6_reset"); - if (IS_ERR(wcss->wcss_q6_reset)) { - dev_err(wcss->dev, "unable to acquire wcss_q6_reset\n"); - return PTR_ERR(wcss->wcss_q6_reset); - } - } - - wcss->wcss_q6_bcr_reset = devm_reset_control_get_optional_exclusive(dev, - "wcss_q6_bcr_reset"); - if (IS_ERR(wcss->wcss_q6_bcr_reset)) { - dev_err(wcss->dev, "unable to acquire wcss_q6_bcr_reset\n"); - return PTR_ERR(wcss->wcss_q6_bcr_reset); + wcss->wcss_q6_reset = devm_reset_control_get_exclusive(dev, "wcss_q6_reset"); + if (IS_ERR(wcss->wcss_q6_reset)) { + dev_err(wcss->dev, "unable to acquire wcss_q6_reset\n"); + return PTR_ERR(wcss->wcss_q6_reset); } return 0; @@ -1062,7 +1051,6 @@ static const struct wcss_data wcss_ipq8074_res_init = { .firmware_name = "IPQ8074/q6_fw.mdt", .crash_reason_smem = WCSS_CRASH_REASON, .aon_reset_required = true, - .wcss_q6_reset_required = true, .ops = &q6v5_wcss_ipq8074_ops, .requires_force_stop = true, }; @@ -1072,7 +1060,6 @@ static const struct wcss_data wcss_qcs404_res_init = { .firmware_name = "wcnss.mdt", .version = WCSS_QCS404, .aon_reset_required = false, - .wcss_q6_reset_required = false, .ssr_name = "mpss", .sysmon_name = "wcnss", .ssctl_id = 0x12, From 274cd52fde330b74ae2a53e8864db89f9cb18509 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 12 May 2026 11:13:03 +0200 Subject: [PATCH 1250/2758] hwspinlock: qcom: avoid uninitialized struct members [ Upstream commit 8752c396ce3b2136b3d4c906fe103f6efb6782d9 ] The reg_field is allocated on stack, so using the REG_FIELD macro will ensure that unused members do not have uninitialized values. Fixes: 19a0f61224d2 ("hwspinlock: qcom: Add support for Qualcomm HW Mutex block") Link: https://sashiko.dev/#/patchset/20260319105947.6237-1-wsa%2Brenesas%40sang-engineering.com Signed-off-by: Wolfram Sang Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260512091339.31085-2-wsa+renesas@sang-engineering.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- drivers/hwspinlock/qcom_hwspinlock.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/hwspinlock/qcom_hwspinlock.c b/drivers/hwspinlock/qcom_hwspinlock.c index 0390979fd765d..712003a4640cc 100644 --- a/drivers/hwspinlock/qcom_hwspinlock.c +++ b/drivers/hwspinlock/qcom_hwspinlock.c @@ -202,7 +202,6 @@ static struct regmap *qcom_hwspinlock_probe_mmio(struct platform_device *pdev, static int qcom_hwspinlock_probe(struct platform_device *pdev) { struct hwspinlock_device *bank; - struct reg_field field; struct regmap *regmap; size_t array_size; u32 stride; @@ -224,9 +223,7 @@ static int qcom_hwspinlock_probe(struct platform_device *pdev) platform_set_drvdata(pdev, bank); for (i = 0; i < QCOM_MUTEX_NUM_LOCKS; i++) { - field.reg = base + i * stride; - field.lsb = 0; - field.msb = 31; + struct reg_field field = REG_FIELD(base + i * stride, 0, 31); bank->lock[i].priv = devm_regmap_field_alloc(&pdev->dev, regmap, field); From 94911533a4b7b371df4d06f1a5c393042b9ba7bb Mon Sep 17 00:00:00 2001 From: Hongyan Xia Date: Fri, 5 Jun 2026 09:43:39 +0000 Subject: [PATCH 1251/2758] sched/fair: Fix cpu_util runnable_avg arithmetic [ Upstream commit 29922fdfc2a4008d66418bedd0ebf5038fc54efa ] If we take runnable_avg in max(runnable_avg, util_avg) in cpu_util(), we should then add or subtract task runnable_avg, but the arithmetic below is still with task util_avg. This mixes runnable_avg with util_avg which is incorrect. Fix by always doing arithmetic with runnable_avg and only take max(runnable_avg, util_avg) at the last step. Fixes: 7d0583cf9ec7 ("sched/fair, cpufreq: Introduce 'runnable boosting'") Signed-off-by: Hongyan Xia Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Vincent Guittot Link: https://patch.msgid.link/20260605094318.37931-1-hongyan.xia@transsion.com Signed-off-by: Sasha Levin --- kernel/sched/fair.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index bcc984e462272..abb76775aa728 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -8256,25 +8256,32 @@ static int select_idle_sibling(struct task_struct *p, int prev, int target) static unsigned long cpu_util(int cpu, struct task_struct *p, int dst_cpu, int boost) { + bool add_task = p && task_cpu(p) != cpu && dst_cpu == cpu; + bool sub_task = p && task_cpu(p) == cpu && dst_cpu != cpu; struct cfs_rq *cfs_rq = &cpu_rq(cpu)->cfs; unsigned long util = READ_ONCE(cfs_rq->avg.util_avg); unsigned long runnable; - if (boost) { - runnable = READ_ONCE(cfs_rq->avg.runnable_avg); - util = max(util, runnable); - } - /* * If @dst_cpu is -1 or @p migrates from @cpu to @dst_cpu remove its * contribution. If @p migrates from another CPU to @cpu add its * contribution. In all the other cases @cpu is not impacted by the * migration so its util_avg is already correct. */ - if (p && task_cpu(p) == cpu && dst_cpu != cpu) - lsub_positive(&util, task_util(p)); - else if (p && task_cpu(p) != cpu && dst_cpu == cpu) + if (add_task) util += task_util(p); + else if (sub_task) + lsub_positive(&util, task_util(p)); + + if (boost) { + runnable = READ_ONCE(cfs_rq->avg.runnable_avg); + if (add_task) + runnable += READ_ONCE(p->se.avg.runnable_avg); + else if (sub_task) + lsub_positive(&runnable, + READ_ONCE(p->se.avg.runnable_avg)); + util = max(util, runnable); + } if (sched_feat(UTIL_EST)) { unsigned long util_est; From b577efff47f7c18717546b777f5e10cd2092f2f9 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 3 Jun 2026 09:27:27 +0200 Subject: [PATCH 1252/2758] ARM: configs: Drop duplicated CONFIG_EXT4_FS [ Upstream commit ae371a58117d30a496e3be27cce8d9d13acdd740 ] Remove redundant, duplicated CONFIG_EXT4_FS to fix warnings like: axm55xx_defconfig:198:warning: override: reassigning to symbol EXT4_FS Fixes: c065b6046b34 ("Use CONFIG_EXT4_FS instead of CONFIG_EXT3_FS in all of the defconfigs") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Richard Cheng Link: https://lore.kernel.org/r/20260603072726.19404-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Arnd Bergmann Signed-off-by: Sasha Levin --- arch/arm/configs/axm55xx_defconfig | 1 - arch/arm/configs/dove_defconfig | 1 - arch/arm/configs/ep93xx_defconfig | 1 - arch/arm/configs/mmp2_defconfig | 1 - arch/arm/configs/mv78xx0_defconfig | 1 - 5 files changed, 5 deletions(-) diff --git a/arch/arm/configs/axm55xx_defconfig b/arch/arm/configs/axm55xx_defconfig index 22b189090e15f..0952e5e94c5e7 100644 --- a/arch/arm/configs/axm55xx_defconfig +++ b/arch/arm/configs/axm55xx_defconfig @@ -195,7 +195,6 @@ CONFIG_PL320_MBOX=y # CONFIG_IOMMU_SUPPORT is not set CONFIG_EXT2_FS=y CONFIG_EXT4_FS=y -CONFIG_EXT4_FS=y CONFIG_AUTOFS_FS=y CONFIG_FUSE_FS=y CONFIG_CUSE=y diff --git a/arch/arm/configs/dove_defconfig b/arch/arm/configs/dove_defconfig index e98c35df675e6..3d978ce34b54f 100644 --- a/arch/arm/configs/dove_defconfig +++ b/arch/arm/configs/dove_defconfig @@ -97,7 +97,6 @@ CONFIG_MV_XOR=y CONFIG_EXT2_FS=y CONFIG_EXT4_FS=y # CONFIG_EXT4_FS_XATTR is not set -CONFIG_EXT4_FS=y CONFIG_ISO9660_FS=y CONFIG_JOLIET=y CONFIG_UDF_FS=m diff --git a/arch/arm/configs/ep93xx_defconfig b/arch/arm/configs/ep93xx_defconfig index 9f3c7324d1cf4..ce41dc8c435cb 100644 --- a/arch/arm/configs/ep93xx_defconfig +++ b/arch/arm/configs/ep93xx_defconfig @@ -105,7 +105,6 @@ CONFIG_EP93XX_DMA=y CONFIG_EXT2_FS=y CONFIG_EXT4_FS=y # CONFIG_EXT4_FS_XATTR is not set -CONFIG_EXT4_FS=y CONFIG_VFAT_FS=y CONFIG_TMPFS=y CONFIG_JFFS2_FS=y diff --git a/arch/arm/configs/mmp2_defconfig b/arch/arm/configs/mmp2_defconfig index a9a212abfd69a..0ea608c75f226 100644 --- a/arch/arm/configs/mmp2_defconfig +++ b/arch/arm/configs/mmp2_defconfig @@ -54,7 +54,6 @@ CONFIG_RTC_DRV_MAX8925=y # CONFIG_RESET_CONTROLLER is not set CONFIG_EXT2_FS=y CONFIG_EXT4_FS=y -CONFIG_EXT4_FS=y # CONFIG_DNOTIFY is not set CONFIG_MSDOS_FS=y CONFIG_FAT_DEFAULT_CODEPAGE=437 diff --git a/arch/arm/configs/mv78xx0_defconfig b/arch/arm/configs/mv78xx0_defconfig index d3a26efe766c4..c6b46141cb697 100644 --- a/arch/arm/configs/mv78xx0_defconfig +++ b/arch/arm/configs/mv78xx0_defconfig @@ -93,7 +93,6 @@ CONFIG_RTC_DRV_M41T80=y CONFIG_EXT2_FS=y CONFIG_EXT4_FS=y # CONFIG_EXT4_FS_XATTR is not set -CONFIG_EXT4_FS=m CONFIG_ISO9660_FS=m CONFIG_JOLIET=y CONFIG_UDF_FS=m From fd61b79406a83ed22e63ac36db2feae718c527f6 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Sun, 26 Apr 2026 23:33:36 +0900 Subject: [PATCH 1253/2758] wifi: mt76: mt7925: clean up DMA on probe failure [ Upstream commit 9629f31f505d74e76ac0d7a9492fd06c0316fc5d ] mt7925_pci_probe() initializes DMA before registering the device. If mt7925_register_device() fails, probe returns through err_free_irq without tearing down DMA state. That leaves the TX NAPI instance enabled and skips the DMA queue cleanup that the normal remove path performs through mt7925e_unregister_device(). Add a dedicated unwind label for failures after mt7925_dma_init() succeeds. Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 chips") Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Link: https://patch.msgid.link/20260426143728.41534-1-pakmyeonghun@bagmyeonghun-ui-MacBookPro.local Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c index c4161754c01df..48837723024d7 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c @@ -415,10 +415,12 @@ static int mt7925_pci_probe(struct pci_dev *pdev, ret = mt7925_register_device(dev); if (ret) - goto err_free_irq; + goto err_free_dma; return 0; +err_free_dma: + mt792x_dma_cleanup(dev); err_free_irq: devm_free_irq(&pdev->dev, pdev->irq, dev); err_free_dev: From 79a307b2730e4b4e060cf3e19a6c21b87116ee78 Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Tue, 12 May 2026 14:52:45 +0800 Subject: [PATCH 1254/2758] wifi: mt76: mt7921: fix resource leak in probe error path [ Upstream commit 346dac35b1384af9338b34b6835e82e634ea4d2c ] When pcim_iomap_region() or devm_kmemdup() fail, the code returns directly without cleaning up previously allocated resources: - mt76_device allocated by mt76_alloc_device() - pci irq vectors allocated by pci_alloc_irq_vectors() Fix this by jumping to the existing error cleanup path instead of returning directly. Fixes: ee5bb35d2b83 ("wifi: mt76: mt7921: Replace deprecated PCI function") Fixes: 222606f43b58 ("wifi: mt76: mt7921: handle MT7902 irq_map quirk with mutable copy") Signed-off-by: Hongling Zeng Link: https://patch.msgid.link/20260512065245.46496-1-zenghongling@kylinos.cn Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7921/pci.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c index 7a790ddf43bb6..49a37185f0564 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c @@ -343,11 +343,14 @@ static int mt7921_pci_probe(struct pci_dev *pdev, pci_set_drvdata(pdev, mdev); + dev = container_of(mdev, struct mt792x_dev, mt76); + regs = pcim_iomap_region(pdev, 0, pci_name(pdev)); - if (IS_ERR(regs)) - return PTR_ERR(regs); + if (IS_ERR(regs)) { + ret = PTR_ERR(regs); + goto err_free_dev; + } - dev = container_of(mdev, struct mt792x_dev, mt76); dev->fw_features = features; dev->hif_ops = &mt7921_pcie_ops; dev->irq_map = &irq_map; @@ -359,8 +362,10 @@ static int mt7921_pci_probe(struct pci_dev *pdev, /* MT7902 needs a mutable copy because wm2_complete_mask differs */ map = devm_kmemdup(&pdev->dev, &irq_map, sizeof(irq_map), GFP_KERNEL); - if (!map) - return -ENOMEM; + if (!map) { + ret = -ENOMEM; + goto err_free_dev; + } map->rx.wm2_complete_mask = 0; dev->irq_map = map; From 50e700ac0edce72dee5c3a9755865d9423696ac7 Mon Sep 17 00:00:00 2001 From: Rajat Gupta Date: Wed, 6 May 2026 21:35:31 -0700 Subject: [PATCH 1255/2758] wifi: mt76: use kfree_rcu for offchannel link in mt76_put_vif_phy_link [ Upstream commit 7fae097aa9a56c30febf539d72ef3773165d3aa3 ] mt76_put_vif_phy_link() frees the offchannel mlink with plain kfree() after rcu_assign_pointer(NULL). However, rcu_assign_pointer only prevents future RCU readers from obtaining the pointer -- it does not wait for existing readers that already hold it via rcu_dereference. The TX datapath (e.g. mt7996_mac_write_txwi) dereferences mlink->wcid and mlink->idx under rcu_read_lock. If a TX softirq obtained the pointer via rcu_dereference just before the NULL assignment, it will dereference freed memory after the kfree. struct mt76_vif_link already contains an rcu_head field that is unused at this free site -- a developer oversight, since the adjacent kfree_rcu_mightsleep call for rx_sc in the same function shows the pattern was understood. Replace kfree(mlink) with kfree_rcu(mlink, rcu_head). Fixes: a8f424c1287c ("wifi: mt76: add multi-radio remain_on_channel functions") Signed-off-by: Rajat Gupta Link: https://patch.msgid.link/20260507043531.492-1-rajat.gupta@oss.qualcomm.com Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/channel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/channel.c b/drivers/net/wireless/mediatek/mt76/channel.c index 05eee64706ea8..6edcb3b8f2798 100644 --- a/drivers/net/wireless/mediatek/mt76/channel.c +++ b/drivers/net/wireless/mediatek/mt76/channel.c @@ -307,7 +307,7 @@ void mt76_put_vif_phy_link(struct mt76_phy *phy, struct ieee80211_vif *vif, rcu_assign_pointer(mvif->offchannel_link, NULL); dev->drv->vif_link_remove(phy, vif, &vif->bss_conf, mlink); - kfree(mlink); + kfree_rcu(mlink, rcu_head); } void mt76_roc_complete(struct mt76_phy *phy) From 82fe6fc1e6ad8358a680a4a98ec35d1625a69ef4 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 24 Mar 2026 15:49:04 +0000 Subject: [PATCH 1256/2758] wifi: mt76: mt7996: add missing max_remain_on_channel_duration [ Upstream commit ac41612e0044fa29cf9bc45b6808dda6d87ac2da ] Having this unset breaks remain-on-channel and mgmt TX. Move setting it to mt76 core to keep it in one place. Fixes: 69d54ce7491d0 ("wifi: mt76: mt7996: switch to single multi-radio wiphy") Link: https://patch.msgid.link/20260324154904.2555603-2-nbd@nbd.name Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mac80211.c | 2 ++ drivers/net/wireless/mediatek/mt76/mt7615/init.c | 1 - drivers/net/wireless/mediatek/mt76/mt792x_core.c | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index b78b4cd206e06..dd09ef1bd0c25 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -449,6 +449,8 @@ mt76_phy_init(struct mt76_phy *phy, struct ieee80211_hw *hw) wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_AIRTIME_FAIRNESS); wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_AQL); + if (!wiphy->max_remain_on_channel_duration) + wiphy->max_remain_on_channel_duration = 5000; if (!wiphy->available_antennas_tx) wiphy->available_antennas_tx = phy->antenna_mask; if (!wiphy->available_antennas_rx) diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/init.c b/drivers/net/wireless/mediatek/mt76/mt7615/init.c index 42e11ba1206ee..e437e088b2e91 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/init.c @@ -195,7 +195,6 @@ mt7615_check_offload_capability(struct mt7615_dev *dev) ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); wiphy->flags &= ~WIPHY_FLAG_4ADDR_STATION; - wiphy->max_remain_on_channel_duration = 5000; wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR | NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c index 152cfcca2f908..5a5d7534830bb 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c @@ -657,7 +657,6 @@ int mt792x_init_wiphy(struct ieee80211_hw *hw) BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_DEVICE); - wiphy->max_remain_on_channel_duration = 5000; wiphy->max_scan_ie_len = MT76_CONNAC_SCAN_IE_LEN; wiphy->max_scan_ssids = 4; wiphy->max_sched_scan_plan_interval = From 93f3f9a9e955569ab7c3f02f0486d3aaf2314a56 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 18 Apr 2026 20:04:16 +0200 Subject: [PATCH 1257/2758] wifi: mt76: mt7996: Fix NULL pointer dereference in mt7996_init_tx_queues() [ Upstream commit 6794edb55b5f5ef834e03b0b241b1a8b725f82c0 ] When MT76_NPU and CONFIG_NET_MEDIATEK_SOC_WED are enabled and mt76 detects properly the Airoha NPU SoC, mt7996_init_tx_queues() will dereference a NULL WED pointer. Fix the issue by always passing the WED pointer from mt7996_dma_init(). Fixes: cd7951f242a7 ("wifi: mt76: mt7996: Integrate MT7990 dma configuration for NPU") Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260418-mt7996-dma-init-npu-fix-v1-1-6b8dcffbcb57@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7996/dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/dma.c b/drivers/net/wireless/mediatek/mt76/mt7996/dma.c index 8f5d297dafce2..3d9353811a02e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/dma.c @@ -683,7 +683,7 @@ int mt7996_dma_init(struct mt7996_dev *dev) ret = mt7996_init_tx_queues(&dev->phy, MT_TXQ_ID(0), MT7996_NPU_TX_RING_SIZE, MT_TXQ_RING_BASE(0) + hif1_ofs, - NULL); + wed); else ret = mt7996_init_tx_queues(&dev->phy, MT_TXQ_ID(dev->mphy.band_idx), From 8703e294044f808425fca0b7c65220a4884c5ed7 Mon Sep 17 00:00:00 2001 From: Javier Tia Date: Sat, 25 Apr 2026 14:49:51 -0500 Subject: [PATCH 1258/2758] wifi: mt76: mt7925: fix stale pointer comparisons in change_vif_links MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 9f1accf3069a0cd42c14ca6c7d44d5b17cc48a80 ] In the error path of mt7925_change_vif_links(), the free: label iterates over link_ids to clean up, but compares against `mconf` and `mlink` which hold stale values from the last loop iteration rather than the current link_id being freed. Use array-indexed access (mconfs[link_id] / mlinks[link_id]) to compare against the correct per-link pointers. Fixes: 69acd6d910b0 ("wifi: mt76: mt7925: add mt7925_change_vif_links") Tested-by: Marcin FM Tested-by: Cristian-Florin Radoi Tested-by: George Salukvadze Tested-by: Evgeny Kapusta <3193631@gmail.com> Tested-by: Samu Toljamo Tested-by: Ariel Rosenfeld Tested-by: Chapuis Dario Tested-by: Thibaut François Tested-by: 张旭涵 Reviewed-by: Sean Wang Signed-off-by: Javier Tia Link: https://patch.msgid.link/20260425195011.790265-2-sean.wang@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7925/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 9dc5ee51eb9f9..8765121b916a2 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -2182,9 +2182,9 @@ mt7925_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif, rcu_assign_pointer(mvif->link_conf[link_id], NULL); rcu_assign_pointer(mvif->sta.link[link_id], NULL); - if (mconf != &mvif->bss_conf) + if (mconfs[link_id] != &mvif->bss_conf) devm_kfree(dev->mt76.dev, mconfs[link_id]); - if (mlink != &mvif->sta.deflink) + if (mlinks[link_id] != &mvif->sta.deflink) devm_kfree(dev->mt76.dev, mlinks[link_id]); } From 4992ec3019ead1047f87a91bf5ed00ae3a47debe Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 25 Apr 2026 10:47:19 -0500 Subject: [PATCH 1259/2758] wifi: mt76: mt7925: keep TX BA state in the primary WCID [ Upstream commit d3c854068bad22a25db6515f12784f64c663fed2 ] For MLO, the same TID can run over different links. Keeping TX BA state in a link WCID makes the state depend on which link starts aggregation first. Store it in the primary WCID instead, so the BA state stays stable across links. Fixes: 44eb173bdd4f ("wifi: mt76: mt7925: add link handling in mt7925_txwi_free") Tested-by: Yao Ting Hsieh Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260425154721.738101-1-sean.wang@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c index c47bd812b66b9..f025c0efeda21 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c @@ -841,7 +841,6 @@ static void mt7925_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb, { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_link_sta *link_sta; - struct mt792x_link_sta *mlink; struct mt792x_sta *msta; bool is_8023; u16 fc, tid; @@ -880,14 +879,14 @@ static void mt7925_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb, msta = (struct mt792x_sta *)sta->drv_priv; - if (sta->mlo && msta->deflink_id != IEEE80211_LINK_UNSPECIFIED) - mlink = rcu_dereference(msta->link[msta->deflink_id]); - else - mlink = &msta->deflink; - - if (!test_and_set_bit(tid, &mlink->wcid.ampdu_state)) { + /* Packets belonging to the same TID can be transmitted over multiple + * links. Keep the TX BA session state in the primary link so all links + * share the same AMPDU bookkeeping. + */ + if (!test_and_set_bit(tid, &msta->deflink.wcid.ampdu_state)) { if (ieee80211_start_tx_ba_session(sta, tid, 0)) - clear_bit(tid, &mlink->wcid.ampdu_state); + clear_bit(tid, &msta->deflink.wcid.ampdu_state); + } } From 3a42b17b655eb1ddd435c0bd84dba0748e69ee6f Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 25 Apr 2026 09:46:48 -0500 Subject: [PATCH 1260/2758] wifi: mt76: mt792x: skip MLD header rewrite for 802.3 encap TX [ Upstream commit a1152244702bb31b64650e5ca8308142286c0e4a ] mt792x_tx() rewrites addr1/addr2/addr3 by treating skb->data as an 802.11 header for MLD traffic. That is only valid for native 802.11 frames. Direct 802.3 TX can also reach this path with IEEE80211_TX_CTL_HW_80211_ENCAP set, where skb->data is not an 802.11 header. Skip the MLD header rewrite for HW-encap packets to avoid corrupting 802.3 frame contents. Fixes: ebb1406813c6 ("wifi: mt76: mt7925: add link handling to txwi") Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260425144648.734030-1-sean.wang@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt792x_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c index 5a5d7534830bb..dc63400c1b996 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c @@ -105,7 +105,8 @@ void mt792x_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, wcid = &mvif->sta.deflink.wcid; } - if (vif && control->sta && ieee80211_vif_is_mld(vif)) { + if (vif && control->sta && ieee80211_vif_is_mld(vif) && + !(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_link_sta *link_sta; struct ieee80211_bss_conf *conf; From e8b214b6d6e1902025452db0a0af73dc9693e4ba Mon Sep 17 00:00:00 2001 From: Aviel Zohar Date: Mon, 13 Apr 2026 06:31:34 +0300 Subject: [PATCH 1261/2758] wifi: mt76: mt7925: validate skb length in testmode query [ Upstream commit c7369a00860a0704461d440e7c3bf9b49bfdbaee ] In mt7925_tm_query(), the response skb from mt76_mcu_send_and_get_msg() is used in a memcpy without validating its length: memcpy(evt_resp, skb->data + 8, MT7925_EVT_RSP_LEN); where MT7925_EVT_RSP_LEN is 512. If the firmware returns a response shorter than 520 bytes (8 + 512), this reads beyond the skb data buffer. The over-read data is then returned to userspace via nla_put() in mt7925_testmode_dump(). Add a length check before the memcpy to ensure the skb contains sufficient data. Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 chips") Signed-off-by: Aviel Zohar Link: https://patch.msgid.link/20260413033136.5417-2-avielzohar123@gmail.com Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7925/testmode.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c index 3d40aacfc011f..22a8f1ddc321d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c @@ -105,6 +105,11 @@ mt7925_tm_query(struct mt792x_dev *dev, struct mt7925_tm_cmd *req, if (ret) goto out; + if (skb->len < MT7925_EVT_RSP_LEN + 8) { + ret = -EINVAL; + goto out; + } + memcpy((char *)evt_resp, (char *)skb->data + 8, MT7925_EVT_RSP_LEN); out: From fa0e9aa92a7bc97fc42014c5efaaf1278fa92723 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sun, 31 May 2026 11:10:59 +0200 Subject: [PATCH 1262/2758] wifi: mt76: mt7996: Fix possible token leak in mt7996_tx_prepare_skb() [ Upstream commit 831074096d0450308357271fc0ffd3f600a2487e ] If link_conf or link_sta lookup fails in mt7996_tx_prepare_skb routine, mt7996 driver leaks an already allocated tx token. Fix the issue releasing the token in case of error. Fixes: 7ef0c7ad735b0 ("wifi: mt76: mt7996: Implement MLD address translation for EAPOL") Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260531-mt7996_tx_prepare_skb-token-leack-v1-1-2b9c9f59ceb1@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 8 ++++++-- drivers/net/wireless/mediatek/mt76/tx.c | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index e2a83da3a09c0..09df505ee5a77 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -1067,11 +1067,11 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, link_conf = rcu_dereference(vif->link_conf[wcid->link_id]); if (!link_conf) - return -EINVAL; + goto error_release_token; link_sta = rcu_dereference(sta->link[wcid->link_id]); if (!link_sta) - return -EINVAL; + goto error_release_token; dma_sync_single_for_cpu(mdev->dma_dev, tx_info->buf[1].addr, tx_info->buf[1].len, DMA_TO_DEVICE); @@ -1176,6 +1176,10 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, tx_info->nbuf = MT_CT_DMA_BUF_NUM; return 0; + +error_release_token: + mt76_token_release(mdev, id, NULL); + return -EINVAL; } u32 mt7996_wed_init_buf(void *ptr, dma_addr_t phys, int token_id) diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c index 22f9690634c94..f96d9c4718535 100644 --- a/drivers/net/wireless/mediatek/mt76/tx.c +++ b/drivers/net/wireless/mediatek/mt76/tx.c @@ -933,7 +933,7 @@ mt76_token_release(struct mt76_dev *dev, int token, bool *wake) #endif } - if (dev->token_count < dev->token_size - MT76_TOKEN_FREE_THR && + if (wake && dev->token_count < dev->token_size - MT76_TOKEN_FREE_THR && dev->phy.q_tx[0]->blocked) *wake = true; From 63e6151b9791b31877a88ff62806e7c407683955 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sun, 31 May 2026 11:38:57 +0200 Subject: [PATCH 1263/2758] wifi: mt76: mt7996: Fix possible NULL pointer dereference in mt7996_mac_write_txwi_80211() [ Upstream commit 61370e6674b5253de5686813ceeceebc35a7d3e5 ] For injected frames (e.g. via radiotap), mac80211 can pass info->control.vif = NULL, as explicitly noted in struct ieee80211_tx_info. Check vif pointer before executing ieee80211_vif_is_mld() in mt7996_mac_write_txwi_80211 routine in order to avoid a possible NULL pointer dereference. Fixes: f0b0b239b8f36 ("wifi: mt76: mt7996: rework mt7996_mac_write_txwi() for MLO support") Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260531-mt7996_mac_write_txwi_80211-null-ptr-deref-v1-1-6dd38e1d3422@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 09df505ee5a77..7533146b772b3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -757,6 +757,7 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, bool multicast = is_multicast_ether_addr(hdr->addr1); u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK; __le16 fc = hdr->frame_control, sc = hdr->seq_ctrl; + struct ieee80211_vif *vif = info->control.vif; u16 seqno = le16_to_cpu(sc); bool hw_bigtk = false; u8 fc_type, fc_stype; @@ -819,7 +820,7 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, txwi[3] |= cpu_to_le32(MT_TXD3_REM_TX_COUNT); } - if (multicast && ieee80211_vif_is_mld(info->control.vif)) { + if (multicast && vif && ieee80211_vif_is_mld(vif)) { val = MT_TXD3_SN_VALID | FIELD_PREP(MT_TXD3_SEQ, IEEE80211_SEQ_TO_SN(seqno)); txwi[3] |= cpu_to_le32(val); @@ -839,12 +840,12 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, txwi[3] &= ~cpu_to_le32(MT_TXD3_HW_AMSDU); } - if (ieee80211_vif_is_mld(info->control.vif) && + if (vif && ieee80211_vif_is_mld(vif) && (multicast || unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE)))) txwi[5] |= cpu_to_le32(MT_TXD5_FL); if (ieee80211_is_nullfunc(fc) && ieee80211_has_a4(fc) && - ieee80211_vif_is_mld(info->control.vif)) { + vif && ieee80211_vif_is_mld(vif)) { txwi[5] |= cpu_to_le32(MT_TXD5_FL); txwi[6] |= cpu_to_le32(MT_TXD6_DIS_MAT); } From e6abc3325ff25c5637e078b61fa7e55cabd2c83b Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sun, 31 May 2026 10:55:04 +0200 Subject: [PATCH 1264/2758] wifi: mt76: mt7996: fix reading zeroed info->control.flags after mt76_tx_status_skb_add() [ Upstream commit 729c83a3330c0a56662cd0d8e40db96d41c00a54 ] mt76_tx_status_skb_add() zeroes the mt76_tx_cb struct stored at info->status.status_driver_data via memset(). Since info->control and info->status are members of the same union in ieee80211_tx_info, this overwrites info->control.flags. In mt7996_tx_prepare_skb(), mt76_tx_status_skb_add() is called before mt7996_mac_write_txwi(), which re-reads info->control.flags to extract IEEE80211_TX_CTRL_MLO_LINK. Because the field has been zeroed, the link_id always resolves to 0 for frames using global_wcid, leading to incorrect TXWI configuration. Fix this by passing link_id as an explicit parameter to mt7996_mac_write_txwi(). In mt7996_tx_prepare_skb(), the link_id is already extracted from info->control.flags before the destructive mt76_tx_status_skb_add() call. For the beacon and inband discovery callers in mcu.c, use link_conf->link_id directly. Fixes: f0b0b239b8f36 ("wifi: mt76: mt7996: rework mt7996_mac_write_txwi() for MLO support") Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260531-mt76_tx_status_skb_add-overwrite-fix-v2-1-b73c4b4a9798@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 14 ++++---------- drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 5 +++-- drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h | 3 ++- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 7533146b772b3..ffa15f32c250c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -857,7 +857,8 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, struct sk_buff *skb, struct mt76_wcid *wcid, struct ieee80211_key_conf *key, int pid, - enum mt76_txq_id qid, u32 changed) + enum mt76_txq_id qid, u32 changed, + unsigned int link_id) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -867,7 +868,6 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP; struct mt76_vif_link *mlink = NULL; struct mt7996_vif *mvif; - unsigned int link_id; u16 tx_count = 15; u32 val; bool inband_disc = !!(changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP | @@ -875,17 +875,11 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, bool beacon = !!(changed & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED)) && (!inband_disc); - if (wcid != &dev->mt76.global_wcid) - link_id = wcid->link_id; - else - link_id = u32_get_bits(info->control.flags, - IEEE80211_TX_CTRL_MLO_LINK); - mvif = vif ? (struct mt7996_vif *)vif->drv_priv : NULL; if (mvif) { if (wcid->offchannel) mlink = rcu_dereference(mvif->mt76.offchannel_link); - if (!mlink) + if (!mlink && link_id != IEEE80211_LINK_UNSPECIFIED) mlink = rcu_dereference(mvif->mt76.link[link_id]); } @@ -1097,7 +1091,7 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, /* Transmit non qos data by 802.11 header and need to fill txd by host*/ if (!is_8023 || pid >= MT_PACKET_ID_FIRST) mt7996_mac_write_txwi(dev, txwi_ptr, tx_info->skb, wcid, key, - pid, qid, 0); + pid, qid, 0, link_id); /* MT7996 and MT7992 require driver to provide the MAC TXP for AddBA * req diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 16420375112d1..2748bfeb47974 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -3098,7 +3098,7 @@ mt7996_mcu_beacon_cont(struct mt7996_dev *dev, buf = (u8 *)bcn + sizeof(*bcn); mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, NULL, 0, 0, - BSS_CHANGED_BEACON); + BSS_CHANGED_BEACON, link_conf->link_id); memcpy(buf + MT_TXD_SIZE, skb->data, skb->len); } @@ -3244,7 +3244,8 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev, buf = (u8 *)tlv + sizeof(*discov); - mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, NULL, 0, 0, changed); + mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, NULL, 0, 0, + changed, link_conf->link_id); memcpy(buf + MT_TXD_SIZE, skb->data, skb->len); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index bdcf724579549..5f39a583bd372 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -855,7 +855,8 @@ void mt7996_mac_enable_nf(struct mt7996_dev *dev, u8 band); void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, struct sk_buff *skb, struct mt76_wcid *wcid, struct ieee80211_key_conf *key, int pid, - enum mt76_txq_id qid, u32 changed); + enum mt76_txq_id qid, u32 changed, + unsigned int link_id); void mt7996_mac_update_beacons(struct mt7996_phy *phy); void mt7996_mac_set_coverage_class(struct mt7996_phy *phy); void mt7996_mac_work(struct work_struct *work); From 0df68463abdaf655d8ea493d68aea785a6093d2a Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Fri, 22 May 2026 09:24:52 +0200 Subject: [PATCH 1265/2758] wifi: mt76: mt7996: remove redundant pdev->bus check in probe [ Upstream commit 6b294950eaac246e6b0f42d74aa643ff36384c6e ] Drop the unnecessary pdev->bus NULL check in mt7996_pci_probe() since the pointer is already dereferenced earlier in mt76_pci_disable_aspm(), making the check dead code. Silences the related Smatch warning. Fixes: 377aa17d2aed ("wifi: mt76: mt7996: Add NPU offload support to MT7996 driver") Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260522-mt7996-pdev-bus-fix-v1-1-c91716484365@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7996/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/pci.c b/drivers/net/wireless/mediatek/mt76/mt7996/pci.c index 12523ddba6304..b7d9193e042f4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/pci.c @@ -141,7 +141,7 @@ static int mt7996_pci_probe(struct pci_dev *pdev, dev->hif2 = hif2; mt76_npu_init(mdev, pci_resource_start(pdev, 0), - pdev->bus && pci_domain_nr(pdev->bus) ? 3 : 2); + pci_domain_nr(pdev->bus) ? 3 : 2); ret = mt7996_mmio_wed_init(dev, pdev, false, &irq); if (ret < 0) From 66d36e9bb4367fac6661b4804c765460cbf0e767 Mon Sep 17 00:00:00 2001 From: Dylan Eskew Date: Wed, 8 Apr 2026 07:50:58 -0700 Subject: [PATCH 1266/2758] wifi: mt76: mt7996: limit work in set_bitrate_mask [ Upstream commit 5fd3385505600934f5faa9635e5b30fa38e548b9 ] Calls to mt7996_set_bitrate_mask() would propagate work for all stations on the ieee80211_hw regardless of the vif specified in the call. To prevent unnecessary work in FW, limit setting the sta_rate to only the specified vif in mt7996_sta_rate_ctrl_update(). Fixes: afff4325548f0 ("wifi: mt76: mt7996: Use proper link_id in link_sta_rc_update callback") Signed-off-by: Dylan Eskew Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260408145057.2356878-2-dylan.eskew@candelatech.com Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7996/main.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index a8a6552d49f69..26b8c91db0a81 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -1959,7 +1959,11 @@ static void mt7996_sta_rate_ctrl_update(void *data, struct ieee80211_sta *sta) { struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; struct mt7996_sta_link *msta_link; - u32 *changed = data; + struct mt7996_vif *mvif = data; + u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED; + + if (msta->vif != mvif) + return; msta_link = rcu_dereference(msta->link[msta->deflink_id]); if (msta_link) @@ -1972,7 +1976,6 @@ mt7996_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif, { struct mt7996_dev *dev = mt7996_hw_dev(hw); struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED; mvif->deflink.bitrate_mask = *mask; @@ -1985,7 +1988,7 @@ mt7996_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif, * then multiple MCS setting (MCS 4,5,6) is not supported. */ ieee80211_iterate_stations_atomic(hw, mt7996_sta_rate_ctrl_update, - &changed); + mvif); ieee80211_queue_work(hw, &dev->rc_work); return 0; From 2a2830ff8febe2332001e901558cca12226a4e71 Mon Sep 17 00:00:00 2001 From: "Bjoern A. Zeeb" Date: Tue, 31 Mar 2026 22:05:47 +0000 Subject: [PATCH 1267/2758] wifi: mt76: fix argument to ieee80211_is_first_frag() [ Upstream commit 5832743279da8c6ae72f715bad2f7141eca6f4b8 ] ieee80211_is_first_frag() operates on the seq_ctrl not the frame_control header field. Pass the correct one in; otherwise the results may vary. Sponsored by: The FreeBSD Foundation Fixes: 30ce7f4456ae4 ("mt76: validate rx CCMP PN") Link: https://cgit.freebsd.org/src/commit/sys/contrib/dev/mediatek/mt76/mac80211.c?id=c67fd35e58c6ee1e19877a7fe5998885683abedc Signed-off-by: Bjoern A. Zeeb Link: https://patch.msgid.link/83s4psnr-popo-8789-757o-npr2n9n7rs2o@SerrOFQ.bet Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mac80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index dd09ef1bd0c25..13c4e8abe2819 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -1323,7 +1323,7 @@ mt76_check_ccmp_pn(struct sk_buff *skb) * All further fragments will be validated by mac80211 only. */ if (ieee80211_is_frag(hdr) && - !ieee80211_is_first_frag(hdr->frame_control)) + !ieee80211_is_first_frag(hdr->seq_ctrl)) return; } From c990141f11dd255cabed5da2c5a4a249e09d4bc3 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Fri, 5 Jun 2026 04:33:03 -0700 Subject: [PATCH 1268/2758] wifi: mt76: mt7915: fix potential tx_retries underflow [ Upstream commit 05e72b6167970043348bfbe8f72a3b67a38a9f1c ] When FIELD_GET returns 0 for the retry count, subtracting 1 causes an unsigned integer underflow, resulting in tx_retries becoming a very large value (0xFFFFFFFF for u32). Fix by checking if count is non-zero before subtracting 1. Fixes: 943e4fb96e6f ("wifi: mt76: mt7915: report tx retries/failed counts for non-WED path") Signed-off-by: Ryder Lee Link: https://patch.msgid.link/20260605113306.3485554-1-ryder.lee@mediatek.com Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7915/mac.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index cec2c4208255f..334c19ab2b22c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -912,16 +912,16 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, void *data, int len) } if (!mtk_wed_device_active(&mdev->mmio.wed) && wcid) { - u32 tx_retries = 0, tx_failed = 0; + u32 tx_retries = 0, tx_failed = 0, count; if (v3 && (info & MT_TX_FREE_MPDU_HEADER_V3)) { - tx_retries = - FIELD_GET(MT_TX_FREE_COUNT_V3, info) - 1; + count = FIELD_GET(MT_TX_FREE_COUNT_V3, info); + tx_retries = count ? count - 1 : 0; tx_failed = tx_retries + !!FIELD_GET(MT_TX_FREE_STAT_V3, info); } else if (!v3 && (info & MT_TX_FREE_MPDU_HEADER)) { - tx_retries = - FIELD_GET(MT_TX_FREE_COUNT, info) - 1; + count = FIELD_GET(MT_TX_FREE_COUNT, info); + tx_retries = count ? count - 1 : 0; tx_failed = tx_retries + !!FIELD_GET(MT_TX_FREE_STAT, info); } From e13959152ad85b656e336af9be695c283cce3462 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Fri, 5 Jun 2026 04:33:04 -0700 Subject: [PATCH 1269/2758] wifi: mt76: mt7921: fix potential tx_retries underflow [ Upstream commit 3c5671ed81b1fff97fa868dae771690599db94f7 ] When FIELD_GET returns 0 for the retry count, subtracting 1 causes an unsigned integer underflow, resulting in tx_retries becoming a very large value (0xFFFFFFFF for u32). Fix by checking if count is non-zero before subtracting 1. Fixes: 9aecfa754c7f ("wifi: mt76: mt7921e: report tx retries/failed counts in tx free event") Signed-off-by: Ryder Lee Link: https://patch.msgid.link/20260605113306.3485554-2-ryder.lee@mediatek.com Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7921/mac.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c index 03b4960db73f0..668bfa1953807 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c @@ -530,8 +530,9 @@ static void mt7921_mac_tx_free(struct mt792x_dev *dev, void *data, int len) stat = FIELD_GET(MT_TX_FREE_STATUS, info); if (wcid) { - wcid->stats.tx_retries += - FIELD_GET(MT_TX_FREE_COUNT, info) - 1; + u32 count = FIELD_GET(MT_TX_FREE_COUNT, info); + + wcid->stats.tx_retries += count ? count - 1 : 0; wcid->stats.tx_failed += !!stat; } From e17c4412754eac348ff1205fa337ec7ffc21f43d Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Fri, 5 Jun 2026 04:33:05 -0700 Subject: [PATCH 1270/2758] wifi: mt76: mt7925: fix potential tx_retries underflow [ Upstream commit 1e1fd84571e62a2961cea44c053340ec5c99b2cb ] When FIELD_GET returns 0 for the retry count, subtracting 1 causes an unsigned integer underflow, resulting in tx_retries becoming a very large value (0xFFFFFFFF for u32). Fix by checking if count is non-zero before subtracting 1. Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 chips") Signed-off-by: Ryder Lee Link: https://patch.msgid.link/20260605113306.3485554-3-ryder.lee@mediatek.com Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c index f025c0efeda21..50034d7c04f0c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c @@ -1140,8 +1140,9 @@ mt7925_mac_tx_free(struct mt792x_dev *dev, void *data, int len) if (info & MT_TXFREE_INFO_HEADER) { if (wcid) { - wcid->stats.tx_retries += - FIELD_GET(MT_TXFREE_INFO_COUNT, info) - 1; + u32 count = FIELD_GET(MT_TXFREE_INFO_COUNT, info); + + wcid->stats.tx_retries += count ? count - 1 : 0; wcid->stats.tx_failed += !!FIELD_GET(MT_TXFREE_INFO_STAT, info); } From b13342aa835685cdec5f216fe7c4df2f59a48016 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Fri, 5 Jun 2026 04:33:06 -0700 Subject: [PATCH 1271/2758] wifi: mt76: mt7996: fix potential tx_retries underflow [ Upstream commit 4d8bba99d645bcb46a442b18eb42402610cba03a ] When FIELD_GET returns 0 for the retry count, subtracting 1 causes an unsigned integer underflow, resulting in tx_retries becoming a very large value (0xFFFFFFFF for u32). Fix by checking if count is non-zero before subtracting 1. Fixes: 2461599f835e ("wifi: mt76: mt7996: get tx_retries and tx_failed from txfree") Signed-off-by: Ryder Lee Link: https://patch.msgid.link/20260605113306.3485554-4-ryder.lee@mediatek.com Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index ffa15f32c250c..2a0511b2e4d29 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -1359,13 +1359,13 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len) cur_info++; continue; } else if (info & MT_TXFREE_INFO_HEADER) { - u32 tx_retries = 0, tx_failed = 0; + u32 tx_retries = 0, tx_failed = 0, count; if (!wcid) continue; - tx_retries = - FIELD_GET(MT_TXFREE_INFO_COUNT, info) - 1; + count = FIELD_GET(MT_TXFREE_INFO_COUNT, info); + tx_retries = count ? count - 1 : 0; tx_failed = tx_retries + !!FIELD_GET(MT_TXFREE_INFO_STAT, info); From 9faa6b69ad73f03c7bde53e07d75a28822dc9a1a Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Thu, 21 May 2026 15:19:37 +0100 Subject: [PATCH 1272/2758] btrfs: fix invalid pointer dereference in __btrfs_run_delayed_refs() [ Upstream commit 486f8298b6188ff11ef1f4be7f1d5d2e4d1b1fae ] In the beginning of the loop, we try to obtain a locked delayed ref head, if 'locked_ref' is currently NULL, by calling btrfs_select_ref_head(), which can return an error pointer. If the error pointer is -EAGAIN we do a continue and go back to the beginning of the loop, which will not try again to call btrfs_select_ref_head() since 'locked_ref' is no longer NULL but it's ERR_PTR(-EAGAIN), and then we do: spin_lock(&locked_ref->lock); against a ERR_PTR(-EAGAIN) value, generating an invalid pointer dereference. Fix this by ensuring that 'locked_ref' is set to NULL when btrfs_select_ref_head() returns ERR_PTR(-EAGAIN) and incrementing 'count' as well, to prevent infinite looping. We do this by doing a goto to the bottom of the loop that already sets 'locked_ref' to NULL and does a cond_resched(), with an increment to 'count' right before the goto. These measures were in place before the refactoring in commit 0110a4c43451 ("btrfs: refactor __btrfs_run_delayed_refs loop") but were unintentionally lost afterwards. Reported-by: Dan Carpenter Link: https://lore.kernel.org/linux-btrfs/ag8ARRwykv8bpJ87@stanley.mountain/ Fixes: 0110a4c43451 ("btrfs: refactor __btrfs_run_delayed_refs loop") Reviewed-by: Boris Burkov Reviewed-by: Qu Wenruo Signed-off-by: Filipe Manana Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/extent-tree.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index a8c77f31ff784..70d1f7ead160f 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -2108,7 +2108,8 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans, locked_ref = btrfs_select_ref_head(fs_info, delayed_refs); if (IS_ERR_OR_NULL(locked_ref)) { if (PTR_ERR(locked_ref) == -EAGAIN) { - continue; + count++; + goto again; } else { break; } @@ -2156,7 +2157,7 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans, * Either success case or btrfs_run_delayed_refs_for_head * returned -EAGAIN, meaning we need to select another head */ - +again: locked_ref = NULL; cond_resched(); } while ((min_bytes != U64_MAX && bytes_processed < min_bytes) || From 79a1d86b83cf895d73c0cdebbe8273ece79428af Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 9 Jun 2026 09:49:04 +0200 Subject: [PATCH 1273/2758] ALSA: aloop: Drop superfluous break [ Upstream commit 123fd13f35ccaf7d2b98f5a8cc6c8a3de378568d ] At converting the spinlock to guard(), a break statement was put in the scoped_guard block in loopback_jiffies_timer_function(), but it's obviously superfluous (although it's harmless). Better to drop it for avoiding confusion. Fixes: 1ef2cb6b29c2 ("ALSA: aloop: Use guard() for spin locks") Link: https://patch.msgid.link/20260609074907.726593-1-tiwai@suse.de Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/drivers/aloop.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index a37a1695f51c7..3f8488716a082 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -728,7 +728,6 @@ static void loopback_jiffies_timer_function(struct timer_list *t) if (dpcm->period_update_pending) { dpcm->period_update_pending = 0; period_elapsed = true; - break; } } } From 9e5c672ab9c91357550f6f5fd0d458cc28110fe2 Mon Sep 17 00:00:00 2001 From: Sergio Paracuellos Date: Tue, 9 Jun 2026 05:11:18 +0200 Subject: [PATCH 1274/2758] gpio: mt7621: fix interrupt banks mapping on gpio chips [ Upstream commit a46f2e5720f5670feda145709d1f0d20be5c7263 ] The GPIO controller's registers are organized as sets of eight 32-bit registers with each set controlling a bank of up to 32 pins. A single interrupt is shared for all of the banks handled by the controller. The driver implements this using three gpio chip instances every one with its own irq chip. Every single pin can generate interrupts having a total of 96 possible interrupts here. It looks like there is a problem with interrupts being properly mapped to the gpio bank using this solution. This problem report is in the following lore's link [0]. Device tree is using two cells for this, so only the interrupt pin and the interrupt type are described there. Changing to have three cells to setup also the bank and implement 'of_node_instance_match()' would also work but this would be an ABI breakage and also a bit incoherent since gpios itself are also using two cells and properly mapped in desired bank using through its pin number on 'of_xlate()'. That said, register a linear IRQ domain of the total of 96 interrupts shared with the three gpio chip instances so the bank and the interrupt is properly decoded and devices using gpio IRQs properly work. [0]: https://lore.kernel.org/linux-gpio/CAAMcf8C_A9dJ_v4QRKtb9eGNOpJ7BZNOGsFP4i2WFOZxOVBPnQ@mail.gmail.com/T/#u Fixes: 4ba9c3afda41 ("gpio: mt7621: Add a driver for MT7621") Co-developed-by: Vicente Bergas Signed-off-by: Vicente Bergas Tested-by: Vicente Bergas Signed-off-by: Sergio Paracuellos Link: https://patch.msgid.link/20260609031118.2275735-1-sergio.paracuellos@gmail.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpio-mt7621.c | 282 ++++++++++++++++++++++++++++--------- 1 file changed, 216 insertions(+), 66 deletions(-) diff --git a/drivers/gpio/gpio-mt7621.c b/drivers/gpio/gpio-mt7621.c index 91230be515879..a814885ccd5d1 100644 --- a/drivers/gpio/gpio-mt7621.c +++ b/drivers/gpio/gpio-mt7621.c @@ -29,8 +29,8 @@ #define GPIO_REG_EDGE 0xA0 struct mtk_gc { - struct irq_chip irq_chip; struct gpio_generic_chip chip; + struct mtk *parent_priv; int bank; u32 rising; u32 falling; @@ -41,20 +41,32 @@ struct mtk_gc { /** * struct mtk - state container for * data of the platform driver. It is 3 - * separate gpio-chip each one with its - * own irq_chip. - * @dev: device instance + * separate gpio-chip having an IRQ + * linear domain shared for all of them + * @pdev: platform device instance * @base: memory base address + * @irq_domain: IRQ linear domain shared across the three gpio chips * @gpio_irq: irq number from the device tree + * @num_gpios: total number of gpio pins on the three gpio chips * @gc_map: array of the gpio chips */ struct mtk { - struct device *dev; + struct platform_device *pdev; void __iomem *base; + struct irq_domain *irq_domain; int gpio_irq; + int num_gpios; struct mtk_gc gc_map[MTK_BANK_CNT]; }; +static inline struct mtk * +mt7621_gpio_gc_to_priv(struct gpio_chip *gc) +{ + struct mtk_gc *bank = gpiochip_get_data(gc); + + return bank->parent_priv; +} + static inline struct mtk_gc * to_mediatek_gpio(struct gpio_chip *chip) { @@ -67,7 +79,7 @@ static inline void mtk_gpio_w32(struct mtk_gc *rg, u32 offset, u32 val) { struct gpio_chip *gc = &rg->chip.gc; - struct mtk *mtk = gpiochip_get_data(gc); + struct mtk *mtk = mt7621_gpio_gc_to_priv(gc); offset = (rg->bank * GPIO_BANK_STRIDE) + offset; gpio_generic_write_reg(&rg->chip, mtk->base + offset, val); @@ -77,41 +89,62 @@ static inline u32 mtk_gpio_r32(struct mtk_gc *rg, u32 offset) { struct gpio_chip *gc = &rg->chip.gc; - struct mtk *mtk = gpiochip_get_data(gc); + struct mtk *mtk = mt7621_gpio_gc_to_priv(gc); offset = (rg->bank * GPIO_BANK_STRIDE) + offset; return gpio_generic_read_reg(&rg->chip, mtk->base + offset); } -static irqreturn_t -mediatek_gpio_irq_handler(int irq, void *data) +static void +mt7621_gpio_irq_bank_handler(struct mtk_gc *bank) { - struct gpio_chip *gc = data; - struct mtk_gc *rg = to_mediatek_gpio(gc); - irqreturn_t ret = IRQ_NONE; + struct mtk *priv = bank->parent_priv; + struct irq_domain *domain = priv->irq_domain; + int hwbase = bank->chip.gc.offset; unsigned long pending; - int bit; + unsigned int offset; + + pending = mtk_gpio_r32(bank, GPIO_REG_STAT); + if (!pending) + return; + + mtk_gpio_w32(bank, GPIO_REG_STAT, pending); + + for_each_set_bit(offset, &pending, MTK_BANK_WIDTH) + generic_handle_domain_irq(domain, hwbase + offset); +} + +static void +mt7621_gpio_irq_handler(struct irq_desc *desc) +{ + struct mtk *priv = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + int i; - pending = mtk_gpio_r32(rg, GPIO_REG_STAT); + chained_irq_enter(chip, desc); + for (i = 0; i < MTK_BANK_CNT; i++) { + struct mtk_gc *bank = &priv->gc_map[i]; - for_each_set_bit(bit, &pending, MTK_BANK_WIDTH) { - generic_handle_domain_irq(gc->irq.domain, bit); - mtk_gpio_w32(rg, GPIO_REG_STAT, BIT(bit)); - ret |= IRQ_HANDLED; + mt7621_gpio_irq_bank_handler(bank); } + chained_irq_exit(chip, desc); +} - return ret; +static int +mt7621_gpio_hwirq_to_offset(irq_hw_number_t hwirq, struct mtk_gc *bank) +{ + return hwirq - bank->chip.gc.offset; } static void mediatek_gpio_irq_unmask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct mtk_gc *rg = to_mediatek_gpio(gc); - int pin = d->hwirq; + struct mtk_gc *rg = gpiochip_get_data(gc); + u32 mask = mt7621_gpio_hwirq_to_offset(d->hwirq, rg); u32 rise, fall, high, low; - gpiochip_enable_irq(gc, d->hwirq); + gpiochip_enable_irq(gc, mask); guard(gpio_generic_lock_irqsave)(&rg->chip); @@ -119,18 +152,18 @@ mediatek_gpio_irq_unmask(struct irq_data *d) fall = mtk_gpio_r32(rg, GPIO_REG_FEDGE); high = mtk_gpio_r32(rg, GPIO_REG_HLVL); low = mtk_gpio_r32(rg, GPIO_REG_LLVL); - mtk_gpio_w32(rg, GPIO_REG_REDGE, rise | (BIT(pin) & rg->rising)); - mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall | (BIT(pin) & rg->falling)); - mtk_gpio_w32(rg, GPIO_REG_HLVL, high | (BIT(pin) & rg->hlevel)); - mtk_gpio_w32(rg, GPIO_REG_LLVL, low | (BIT(pin) & rg->llevel)); + mtk_gpio_w32(rg, GPIO_REG_REDGE, rise | (BIT(mask) & rg->rising)); + mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall | (BIT(mask) & rg->falling)); + mtk_gpio_w32(rg, GPIO_REG_HLVL, high | (BIT(mask) & rg->hlevel)); + mtk_gpio_w32(rg, GPIO_REG_LLVL, low | (BIT(mask) & rg->llevel)); } static void mediatek_gpio_irq_mask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct mtk_gc *rg = to_mediatek_gpio(gc); - int pin = d->hwirq; + struct mtk_gc *rg = gpiochip_get_data(gc); + u32 mask = mt7621_gpio_hwirq_to_offset(d->hwirq, rg); u32 rise, fall, high, low; scoped_guard(gpio_generic_lock_irqsave, &rg->chip) { @@ -138,22 +171,21 @@ mediatek_gpio_irq_mask(struct irq_data *d) fall = mtk_gpio_r32(rg, GPIO_REG_FEDGE); high = mtk_gpio_r32(rg, GPIO_REG_HLVL); low = mtk_gpio_r32(rg, GPIO_REG_LLVL); - mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall & ~BIT(pin)); - mtk_gpio_w32(rg, GPIO_REG_REDGE, rise & ~BIT(pin)); - mtk_gpio_w32(rg, GPIO_REG_HLVL, high & ~BIT(pin)); - mtk_gpio_w32(rg, GPIO_REG_LLVL, low & ~BIT(pin)); + mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall & ~BIT(mask)); + mtk_gpio_w32(rg, GPIO_REG_REDGE, rise & ~BIT(mask)); + mtk_gpio_w32(rg, GPIO_REG_HLVL, high & ~BIT(mask)); + mtk_gpio_w32(rg, GPIO_REG_LLVL, low & ~BIT(mask)); } - gpiochip_disable_irq(gc, d->hwirq); + gpiochip_disable_irq(gc, mask); } static int mediatek_gpio_irq_type(struct irq_data *d, unsigned int type) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct mtk_gc *rg = to_mediatek_gpio(gc); - int pin = d->hwirq; - u32 mask = BIT(pin); + struct mtk_gc *rg = gpiochip_get_data(gc); + u32 mask = BIT(mt7621_gpio_hwirq_to_offset(d->hwirq, rg)); if (type == IRQ_TYPE_PROBE) { if ((rg->rising | rg->falling | @@ -190,6 +222,26 @@ mediatek_gpio_irq_type(struct irq_data *d, unsigned int type) return 0; } +static int +mt7621_gpio_irq_reqres(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct mtk_gc *rg = gpiochip_get_data(gc); + unsigned int irq = mt7621_gpio_hwirq_to_offset(d->hwirq, rg); + + return gpiochip_reqres_irq(gc, irq); +} + +static void +mt7621_gpio_irq_relres(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct mtk_gc *rg = gpiochip_get_data(gc); + unsigned int irq = mt7621_gpio_hwirq_to_offset(d->hwirq, rg); + + gpiochip_relres_irq(gc, irq); +} + static int mediatek_gpio_xlate(struct gpio_chip *chip, const struct of_phandle_args *spec, u32 *flags) @@ -208,14 +260,123 @@ mediatek_gpio_xlate(struct gpio_chip *chip, static const struct irq_chip mt7621_irq_chip = { .name = "mt7621-gpio", + .irq_request_resources = mt7621_gpio_irq_reqres, + .irq_release_resources = mt7621_gpio_irq_relres, .irq_mask_ack = mediatek_gpio_irq_mask, .irq_mask = mediatek_gpio_irq_mask, .irq_unmask = mediatek_gpio_irq_unmask, .irq_set_type = mediatek_gpio_irq_type, .flags = IRQCHIP_IMMUTABLE, - GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + +static void +mt7621_gpio_remove(struct platform_device *pdev) +{ + struct mtk *priv = platform_get_drvdata(pdev); + int offset, virq; + + if (priv->gpio_irq > 0) + irq_set_chained_handler_and_data(priv->gpio_irq, NULL, NULL); + + /* Remove all IRQ mappings and delete the domain */ + if (priv->irq_domain) { + for (offset = 0; offset < priv->num_gpios; offset++) { + virq = irq_find_mapping(priv->irq_domain, offset); + irq_dispose_mapping(virq); + } + irq_domain_remove(priv->irq_domain); + } +} + +static struct mtk_gc * +mt7621_gpio_hwirq_to_bank(struct mtk *priv, irq_hw_number_t hwirq) +{ + int i; + + for (i = 0; i < MTK_BANK_CNT; i++) { + struct mtk_gc *bank = &priv->gc_map[i]; + + if (hwirq >= bank->chip.gc.offset && + hwirq < (bank->chip.gc.offset + bank->chip.gc.ngpio)) + return bank; + } + + return NULL; +} + +static int +mt7621_gpio_irq_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + struct mtk *priv = d->host_data; + struct mtk_gc *bank = mt7621_gpio_hwirq_to_bank(priv, hwirq); + struct platform_device *pdev = priv->pdev; + int ret; + + if (!bank) + return -EINVAL; + + dev_dbg(&pdev->dev, "Mapping irq %d for gpio line %d (bank %d)\n", + irq, (int)hwirq, bank->bank); + + ret = irq_set_chip_data(irq, &bank->chip.gc); + if (ret < 0) + return ret; + + irq_set_chip_and_handler(irq, &mt7621_irq_chip, handle_simple_irq); + irq_set_noprobe(irq); + + return 0; +} + +static void +mt7621_gpio_irq_unmap(struct irq_domain *d, unsigned int irq) +{ + irq_set_chip_and_handler(irq, NULL, NULL); + irq_set_chip_data(irq, NULL); +} + +static const struct irq_domain_ops mt7621_gpio_irq_domain_ops = { + .map = mt7621_gpio_irq_map, + .unmap = mt7621_gpio_irq_unmap, + .xlate = irq_domain_xlate_twocell, }; +static int +mt7621_gpio_irq_setup(struct platform_device *pdev, + struct mtk *priv) +{ + struct device *dev = &pdev->dev; + + priv->irq_domain = irq_domain_create_linear(dev_fwnode(dev), + priv->num_gpios, + &mt7621_gpio_irq_domain_ops, + priv); + if (!priv->irq_domain) { + dev_err(dev, "Couldn't allocate IRQ domain\n"); + return -ENXIO; + } + + irq_set_chained_handler_and_data(priv->gpio_irq, + mt7621_gpio_irq_handler, priv); + irq_set_status_flags(priv->gpio_irq, IRQ_DISABLE_UNLAZY); + + return 0; +} + +static int +mt7621_gpio_to_irq(struct gpio_chip *gc, unsigned int offset) +{ + struct mtk *priv = mt7621_gpio_gc_to_priv(gc); + /* gc_offset is relative to this gpio_chip; want real offset */ + int hwirq = offset + gc->offset; + + if (hwirq >= priv->num_gpios) + return -ENXIO; + + return irq_create_mapping(priv->irq_domain, hwirq); +} + static int mediatek_gpio_bank_probe(struct device *dev, int bank) { @@ -228,6 +389,7 @@ mediatek_gpio_bank_probe(struct device *dev, int bank) rg = &mtk->gc_map[bank]; memset(rg, 0, sizeof(*rg)); + rg->parent_priv = mtk; rg->bank = bank; dat = mtk->base + GPIO_REG_DATA + (rg->bank * GPIO_BANK_STRIDE); @@ -253,41 +415,17 @@ mediatek_gpio_bank_probe(struct device *dev, int bank) rg->chip.gc.of_gpio_n_cells = 2; rg->chip.gc.of_xlate = mediatek_gpio_xlate; + rg->chip.gc.ngpio = MTK_BANK_WIDTH; rg->chip.gc.label = devm_kasprintf(dev, GFP_KERNEL, "%s-bank%d", dev_name(dev), bank); if (!rg->chip.gc.label) return -ENOMEM; rg->chip.gc.offset = bank * MTK_BANK_WIDTH; + if (mtk->gpio_irq > 0) + rg->chip.gc.to_irq = mt7621_gpio_to_irq; - if (mtk->gpio_irq) { - struct gpio_irq_chip *girq; - - /* - * Directly request the irq here instead of passing - * a flow-handler because the irq is shared. - */ - ret = devm_request_irq(dev, mtk->gpio_irq, - mediatek_gpio_irq_handler, IRQF_SHARED, - rg->chip.gc.label, &rg->chip.gc); - - if (ret) { - dev_err(dev, "Error requesting IRQ %d: %d\n", - mtk->gpio_irq, ret); - return ret; - } - - girq = &rg->chip.gc.irq; - gpio_irq_chip_set_chip(girq, &mt7621_irq_chip); - /* This will let us handle the parent IRQ in the driver */ - girq->parent_handler = NULL; - girq->num_parents = 0; - girq->parents = NULL; - girq->default_type = IRQ_TYPE_NONE; - girq->handler = handle_simple_irq; - } - - ret = devm_gpiochip_add_data(dev, &rg->chip.gc, mtk); + ret = devm_gpiochip_add_data(dev, &rg->chip.gc, rg); if (ret < 0) { dev_err(dev, "Could not register gpio %d, ret=%d\n", rg->chip.gc.ngpio, ret); @@ -322,7 +460,8 @@ mediatek_gpio_probe(struct platform_device *pdev) if (mtk->gpio_irq < 0) return mtk->gpio_irq; - mtk->dev = dev; + mtk->pdev = pdev; + mtk->num_gpios = MTK_BANK_WIDTH * MTK_BANK_CNT; platform_set_drvdata(pdev, mtk); for (i = 0; i < MTK_BANK_CNT; i++) { @@ -331,7 +470,17 @@ mediatek_gpio_probe(struct platform_device *pdev) return ret; } + if (mtk->gpio_irq > 0) { + ret = mt7621_gpio_irq_setup(pdev, mtk); + if (ret) + goto fail; + } + return 0; + +fail: + mt7621_gpio_remove(pdev); + return ret; } static const struct of_device_id mediatek_gpio_match[] = { @@ -342,6 +491,7 @@ MODULE_DEVICE_TABLE(of, mediatek_gpio_match); static struct platform_driver mediatek_gpio_driver = { .probe = mediatek_gpio_probe, + .remove = mt7621_gpio_remove, .driver = { .name = "mt7621_gpio", .of_match_table = mediatek_gpio_match, From 1280e828e3c3d7de5aadc816d5fc6a8408ebfcce Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Tue, 9 Jun 2026 10:10:47 +0800 Subject: [PATCH 1275/2758] wifi: ath12k: fix EAPOL TX failure caused by stale tcl_metadata bits [ Upstream commit fdea4d44e4b9c3f7021c85f8cd766e84e224472d ] On WCN7850, after the following sequence: 1. load ath12k and connect to a non-MLO AP 2. disconnect and connect to an MLO AP 3. disconnect and reconnect to the non-MLO AP the third connection always fails with a 4-Way handshake timeout. The supplicant transmits message 2 of 4 four times in response to AP retries of message 1, but the AP never sees any of them. ath12k_dp_vdev_tx_attach() composes dp_link_vif->tcl_metadata using |=, but dp_link_vif is embedded in struct ath12k_dp_vif and its slots are reused across vif/peer teardown and setup. Since tcl_metadata is never cleared on detach, vdev_id bits from a previous attach remain set when the same link slot is reused with a different vdev_id. In this specific issue, the same link slot is used for vdev_id 0, then vdev_id 1, then vdev_id 0 again, the OR yields tcl_metadata == 0x9, which encodes vdev_id 1 in the HTT_TCL_META_DATA_VDEV_ID field even though ti.vdev_id is 0. Firmware then routes the EAPOL frame to the wrong vdev and the AP never receives message 2. Use plain assignment instead of |= so the field is fully recomputed from the current arvif on every attach. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3 Fixes: af66c7640cf9 ("wifi: ath12k: Refactor ath12k_vif structure") Signed-off-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260609-ath12k-fix-eapol-tcl-metadata-v1-1-d47e6f90d4ee@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath12k/dp.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp.c b/drivers/net/wireless/ath/ath12k/dp.c index 90802ed1aa59f..af5f11fc1d84a 100644 --- a/drivers/net/wireless/ath/ath12k/dp.c +++ b/drivers/net/wireless/ath/ath12k/dp.c @@ -943,11 +943,11 @@ void ath12k_dp_vdev_tx_attach(struct ath12k *ar, struct ath12k_link_vif *arvif) dp_link_vif = ath12k_dp_vif_to_dp_link_vif(&ahvif->dp_vif, link_id); - dp_link_vif->tcl_metadata |= u32_encode_bits(1, HTT_TCL_META_DATA_TYPE) | - u32_encode_bits(arvif->vdev_id, - HTT_TCL_META_DATA_VDEV_ID) | - u32_encode_bits(ar->pdev->pdev_id, - HTT_TCL_META_DATA_PDEV_ID); + dp_link_vif->tcl_metadata = u32_encode_bits(1, HTT_TCL_META_DATA_TYPE) | + u32_encode_bits(arvif->vdev_id, + HTT_TCL_META_DATA_VDEV_ID) | + u32_encode_bits(ar->pdev->pdev_id, + HTT_TCL_META_DATA_PDEV_ID); /* set HTT extension valid bit to 0 by default */ dp_link_vif->tcl_metadata &= ~HTT_TCL_META_DATA_VALID_HTT; From 4abb91b6de01332548655b060078b5d2160d2afe Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Thu, 4 Jun 2026 15:28:31 +0530 Subject: [PATCH 1276/2758] wifi: ath12k: enable IEEE80211_VHT_EXT_NSS_BW_CAPABLE when NSS ratio is reported [ Upstream commit 63abe299b12b317dfee5bcd09037da4668a4431a ] When firmware reports NSS ratio support, SUPPORTS_VHT_EXT_NSS_BW is enabled in ath12k. However, IEEE80211_VHT_EXT_NSS_BW_CAPABLE must also be set to make the advertisement valid. According to IEEE Std 802.11-2024, Subclause 9.4.2.156.3 (Supported VHT-MCS and NSS Set subfields), the VHT Extended NSS BW Capable bit indicates whether a STA is capable of interpreting the Extended NSS BW Support subfield of the VHT capabilities information field. Advertising extended NSS BW support without setting this capability bit is therefore invalid. Without this change, mac80211 detects the inconsistency and logs: ieee80211 phy0: copying sband (band 1) due to VHT EXT NSS BW flag This indicates that mac80211 implicitly aligns IEEE80211_VHT_EXT_NSS_BW_CAPABLE during ieee80211_register_hw(). Explicitly setting the bit in ath12k avoids this fixup and ensures capabilities are advertised correctly by the driver. This change follows the same approach as the existing ath11k fix. https://lore.kernel.org/all/20211013073704.15888-1-wgong@codeaurora.org/ Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.5-01651-QCAHKSWPL_SILICONZ-1 Fixes: 18ab9d038fad ("wifi: ath12k: add support for 160 MHz bandwidth") Signed-off-by: Wen Gong Signed-off-by: Maharaja Kennadyrajan Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260604095831.2674298-1-maharaja.kennadyrajan@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath12k/mac.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 74af291350241..7190aafd3ae69 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -8399,6 +8399,10 @@ ath12k_create_vht_cap(struct ath12k *ar, u32 rate_cap_tx_chainmask, vht_cap.vht_supported = 1; vht_cap.cap = ar->pdev->cap.vht_cap; + if (ar->pdev->cap.nss_ratio_enabled) + vht_cap.vht_mcs.tx_highest |= + cpu_to_le16(IEEE80211_VHT_EXT_NSS_BW_CAPABLE); + ath12k_set_vht_txbf_cap(ar, &vht_cap.cap); /* 80P80 is not supported */ From 85c36b01a2efb5dd256c41eeddc1941ac0bddf6c Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Tue, 9 Jun 2026 09:22:09 +0200 Subject: [PATCH 1277/2758] fbdev/arm: Export acorndata_8x8 font symbol for bootloader [ Upstream commit 470ea955a18c76eeb10ca11ffcb2fe923bfc5515 ] The text display code used in the Risc PC kernel image decompression code uses arch/arm/boot/compressed/font.c, which includes lib/fonts/font_acorn_8x8.c, which further includes . Since commit 97df8960240a ("lib/fonts: Provide helpers for calculating glyph pitch and size") contains inline functions that require __do_div64, which is not linked into the ARM kernel decompressor. This makes Risc PC zImages fail to build. Resolve this issue by defining the BOOTLOADER symbol and use it to avoid a static declaration of the acorndata_8x8 symbol. That way it can be referenced by the arm bootloader, and other static math functions and symbols (like __do_div64) stay static and don't get unneccesary included in the ARM kernel bootloader decompressor object file. Fixes: 97df8960240a ("lib/fonts: Provide helpers for calculating glyph pitch and size") Reported-by: Ethan Nelson-Moore Reviewed-by: Thomas Zimmermann Cc: linux-arm-kernel@lists.infradead.org Cc: Russell King Signed-off-by: Helge Deller Signed-off-by: Sasha Levin --- arch/arm/boot/compressed/Makefile | 2 +- lib/fonts/font_acorn_8x8.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile index a159120d1e42e..e3f550d628578 100644 --- a/arch/arm/boot/compressed/Makefile +++ b/arch/arm/boot/compressed/Makefile @@ -157,4 +157,4 @@ $(obj)/piggy_data: $(obj)/../Image FORCE $(obj)/piggy.o: $(obj)/piggy_data -CFLAGS_font.o := -Dstatic= +CFLAGS_font.o := -DBOOTLOADER diff --git a/lib/fonts/font_acorn_8x8.c b/lib/fonts/font_acorn_8x8.c index 36c51016769d4..4ff52c79f8c4d 100644 --- a/lib/fonts/font_acorn_8x8.c +++ b/lib/fonts/font_acorn_8x8.c @@ -5,7 +5,12 @@ #define FONTDATAMAX 2048 +#ifdef BOOTLOADER +/* The acorndata_8x8 symbol is needed by the ARM bootloader too. */ +const struct font_data acorndata_8x8 = { +#else static const struct font_data acorndata_8x8 = { +#endif { 0, 0, FONTDATAMAX, 0 }, { /* 00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ^@ */ /* 01 */ 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e, /* ^A */ From 48cf0f4c6581ec90104cec9c5bf794a8bf347e30 Mon Sep 17 00:00:00 2001 From: David Laight Date: Mon, 8 Jun 2026 13:42:42 +0100 Subject: [PATCH 1278/2758] fbdev: sm501fb: Fix buffer errors in OF binding code [ Upstream commit d8421e09382cfe0bd2a044c8b0a822f64855dd4e ] The code that gets the frame buffer mode from OF has 'use after free', 'buffer overrun' and memory leaks. info->edid_data isn't free if the probe functions fail or if pd->def_mode is set. If both the CRT and PANEL are enabled info->edid_data is used after being freed and is freed twice. The string returned by of_get_property(np, "mode", &len) is just written over either the static "640x480-16@60" or the module parameter string without any regard for the length (which is most likely longer). Use kstrump() for the OF mode and free everything before freeing 'info. Fixes: 4295f9bf74a88 ("video, sm501: add OF binding to support SM501") Signed-off-by: David Laight Signed-off-by: Helge Deller Signed-off-by: Sasha Levin --- drivers/video/fbdev/sm501fb.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/video/fbdev/sm501fb.c b/drivers/video/fbdev/sm501fb.c index fee4b9f84592f..ea5375ed4ea6f 100644 --- a/drivers/video/fbdev/sm501fb.c +++ b/drivers/video/fbdev/sm501fb.c @@ -96,6 +96,7 @@ struct sm501fb_info { void __iomem *fbmem; /* remapped framebuffer */ size_t fbmem_len; /* length of remapped region */ u8 *edid_data; + char *fb_mode; }; /* per-framebuffer private data */ @@ -1793,12 +1794,11 @@ static int sm501fb_init_fb(struct fb_info *fb, enum sm501_controller head, fb->var.yres_virtual = fb->var.yres; } else { if (info->edid_data) { - ret = fb_find_mode(&fb->var, fb, fb_mode, + ret = fb_find_mode(&fb->var, fb, + info->fb_mode ?: fb_mode, fb->monspecs.modedb, fb->monspecs.modedb_len, &sm501_default_mode, default_bpp); - /* edid_data is no longer needed, free it */ - kfree(info->edid_data); } else { ret = fb_find_mode(&fb->var, fb, NULL, NULL, 0, NULL, 8); @@ -1974,7 +1974,7 @@ static int sm501fb_probe(struct platform_device *pdev) /* Get EDID */ cp = of_get_property(np, "mode", &len); if (cp) - strcpy(fb_mode, cp); + info->fb_mode = kstrdup(cp, GFP_KERNEL); prop = of_get_property(np, "edid", &len); if (prop && len == EDID_LENGTH) { info->edid_data = kmemdup(prop, EDID_LENGTH, @@ -2031,6 +2031,12 @@ static int sm501fb_probe(struct platform_device *pdev) goto err_started_crt; } + /* These aren't needed any more */ + kfree(info->edid_data); + kfree(info->fb_mode); + info->edid_data = NULL; + info->fb_mode = NULL; + /* we registered, return ok */ return 0; @@ -2048,6 +2054,8 @@ static int sm501fb_probe(struct platform_device *pdev) framebuffer_release(info->fb[HEAD_CRT]); err_alloc: + kfree(info->edid_data); + kfree(info->fb_mode); kfree(info); return ret; From 4d47898f150fd73cec3d92f0f8cb739fe5111fe8 Mon Sep 17 00:00:00 2001 From: Sumit Gupta Date: Wed, 27 May 2026 19:31:25 +0530 Subject: [PATCH 1279/2758] memory: tegra186-emc: stop borrowing MC aggregate hook for EMC [ Upstream commit 2e05f3d6005d9aa3e2e423d2471f290d9ccbe3d2 ] tegra186_emc_interconnect_init() copies the MC's ICC aggregate hook into the EMC provider. That hook (tegra234_mc_icc_aggregate / tegra264_mc_icc_aggregate) uses container_of() to recover 'mc', which is only valid when the icc_provider is embedded in struct tegra_mc. For an EMC node the provider is embedded in struct tegra186_emc, so 'mc' points into unrelated memory. This stayed harmless until commit faafd6ca7e6e ("memory: tegra: make icc_set_bw return zero if BWMGR not supported") added an unconditional read of mc->bwmgr_mrq_supported at the top of the hook. UBSAN catches the stray load on every EMC aggregation: UBSAN: invalid-load in drivers/memory/tegra/tegra234.c:1104:9 load of value 112 is not a valid value for type '_Bool' No functional impact in practice, since the hook's only other mc dereference (mc->num_channels) sits inside a TEGRA_ICC_MC_CPU_CLUSTER* branch that EMC nodes never enter. Fix this by setting the EMC provider's aggregate hook to icc_std_aggregate, instead of borrowing the MC's hook. The MC providers continue using their own aggregate hooks, where container_of() correctly resolves to struct tegra_mc. Reported-by: Jon Hunter Fixes: 9a38cb27668e ("memory: tegra: Add interconnect support for DRAM scaling in Tegra234") Signed-off-by: Sumit Gupta Reviewed-by: Jon Hunter Tested-by: Jon Hunter Link: https://patch.msgid.link/20260527140127.49172-2-sumitg@nvidia.com Signed-off-by: Krzysztof Kozlowski Signed-off-by: Sasha Levin --- drivers/memory/tegra/tegra186-emc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/memory/tegra/tegra186-emc.c b/drivers/memory/tegra/tegra186-emc.c index 03ebab6fbe68f..f71265b303b97 100644 --- a/drivers/memory/tegra/tegra186-emc.c +++ b/drivers/memory/tegra/tegra186-emc.c @@ -258,15 +258,13 @@ static int tegra186_emc_icc_get_init_bw(struct icc_node *node, u32 *avg, u32 *pe static int tegra186_emc_interconnect_init(struct tegra186_emc *emc) { - struct tegra_mc *mc = dev_get_drvdata(emc->dev->parent); - const struct tegra_mc_soc *soc = mc->soc; struct icc_node *node; int err; emc->provider.dev = emc->dev; emc->provider.set = tegra186_emc_icc_set_bw; emc->provider.data = &emc->provider; - emc->provider.aggregate = soc->icc_ops->aggregate; + emc->provider.aggregate = icc_std_aggregate; emc->provider.xlate = tegra186_emc_of_icc_xlate; emc->provider.get_bw = tegra186_emc_icc_get_init_bw; From 458c679c228b15476c5856af303301dfea2b954b Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 29 Jan 2026 16:47:43 -0500 Subject: [PATCH 1280/2758] vfs: add FS_USERNS_DELEGATABLE flag and set it for NFS [ Upstream commit c5d6cac28646b0d5d81ef632be748ae93c1f36c7 ] Commit e1c5ae59c0f2 ("fs: don't allow non-init s_user_ns for filesystems without FS_USERNS_MOUNT") prevents the mount of any filesystem inside a container that doesn't have FS_USERNS_MOUNT set. This broke NFS mounts in our containerized environment. We have a daemon somewhat like systemd-mountfsd running in the init_ns. A process does a fsopen() inside the container and passes it to the daemon via unix socket. The daemon then vets that the request is for an allowed NFS server and performs the mount. This now fails because the fc->user_ns is set to the value in the container and NFS doesn't set FS_USERNS_MOUNT. We don't want to add FS_USERNS_MOUNT to NFS since that would allow the container to mount any NFS server (even malicious ones). Add a new FS_USERNS_DELEGATABLE flag, and enable it on NFS. Fixes: e1c5ae59c0f2 ("fs: don't allow non-init s_user_ns for filesystems without FS_USERNS_MOUNT") Signed-off-by: Jeff Layton Link: https://patch.msgid.link/20260129-twmount-v1-1-4874ed2a15c4@kernel.org Acked-by: Anna Schumaker Reviewed-by: Alexander Mikhalitsyn Reviewed-by: Jeff Layton Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/nfs/fs_context.c | 8 ++++++-- fs/super.c | 11 ++++++----- include/linux/fs.h | 1 + 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/fs/nfs/fs_context.c b/fs/nfs/fs_context.c index c105882edd16e..1967de7d1dff9 100644 --- a/fs/nfs/fs_context.c +++ b/fs/nfs/fs_context.c @@ -1769,7 +1769,9 @@ struct file_system_type nfs_fs_type = { .init_fs_context = nfs_init_fs_context, .parameters = nfs_fs_parameters, .kill_sb = nfs_kill_super, - .fs_flags = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA, + .fs_flags = FS_RENAME_DOES_D_MOVE | + FS_BINARY_MOUNTDATA | + FS_USERNS_DELEGATABLE, }; MODULE_ALIAS_FS("nfs"); EXPORT_SYMBOL_GPL(nfs_fs_type); @@ -1781,7 +1783,9 @@ struct file_system_type nfs4_fs_type = { .init_fs_context = nfs_init_fs_context, .parameters = nfs_fs_parameters, .kill_sb = nfs_kill_super, - .fs_flags = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA, + .fs_flags = FS_RENAME_DOES_D_MOVE | + FS_BINARY_MOUNTDATA | + FS_USERNS_DELEGATABLE, }; MODULE_ALIAS_FS("nfs4"); MODULE_ALIAS("nfs4"); diff --git a/fs/super.c b/fs/super.c index 378e81efe643b..97df9e574d8bf 100644 --- a/fs/super.c +++ b/fs/super.c @@ -741,12 +741,13 @@ struct super_block *sget_fc(struct fs_context *fc, int err; /* - * Never allow s_user_ns != &init_user_ns when FS_USERNS_MOUNT is - * not set, as the filesystem is likely unprepared to handle it. - * This can happen when fsconfig() is called from init_user_ns with - * an fs_fd opened in another user namespace. + * Never allow s_user_ns != &init_user_ns when FS_USERNS_MOUNT or + * FS_USERNS_DELEGATABLE is not set, as the filesystem is likely + * unprepared to handle it. This can happen when fsconfig() is called + * from init_user_ns with an fs_fd opened in another user namespace. */ - if (user_ns != &init_user_ns && !(fc->fs_type->fs_flags & FS_USERNS_MOUNT)) { + if (user_ns != &init_user_ns && + !(fc->fs_type->fs_flags & (FS_USERNS_MOUNT | FS_USERNS_DELEGATABLE))) { errorfc(fc, "VFS: Mounting from non-initial user namespace is not allowed"); return ERR_PTR(-EPERM); } diff --git a/include/linux/fs.h b/include/linux/fs.h index f93b216149cc9..0f5f022e6e7bc 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2281,6 +2281,7 @@ struct file_system_type { #define FS_MGTIME 64 /* FS uses multigrain timestamps */ #define FS_LBS 128 /* FS supports LBS */ #define FS_POWER_FREEZE 256 /* Always freeze on suspend/hibernate */ +#define FS_USERNS_DELEGATABLE 1024 /* Can be mounted inside userns from outside */ #define FS_RENAME_DOES_D_MOVE 32768 /* FS will handle d_move() during rename() internally. */ int (*init_fs_context)(struct fs_context *); const struct fs_parameter_spec *parameters; From 5287b34dfbec273ed5a895108d1ef2221fde1491 Mon Sep 17 00:00:00 2001 From: Nikita Zhandarovich Date: Fri, 29 May 2026 17:18:36 +0300 Subject: [PATCH 1281/2758] hwmon: (it87) Clamp negative values to zero in set_fan() [ Upstream commit 7f8581c70a3bd50a932d3d2d253e99c5ec3eda74 ] set_fan() parses user input with kstrtol() and passes the resulting value to FAN16_TO_REG() on chips with 16-bit fan support. Negative fan speeds are not meaningful and should be rejected before conversion. Worst scenario, one may be able to abuse undefined behaviour of signed overflow to possibly induce rpm * 2 == 0 in FAN16_TO_REG(), thus causing a division by zero. Instead, clamp val < 0 to zero and keep the conversion in its valid input domain, avoiding unsafe arithmetic in the register conversion path. Found by Linux Verification Center (linuxtesting.org) with static analysis tool SVACE. Fixes: 17d648bf5786 ("it87: Add support for the IT8716F") Signed-off-by: Nikita Zhandarovich Link: https://lore.kernel.org/r/20260529141839.1639287-1-n.zhandarovich@fintech.ru Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/it87.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 5fd310662ee43..87edb1b6048bb 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -1412,6 +1412,9 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr, if (kstrtol(buf, 10, &val) < 0) return -EINVAL; + if (val < 0) + val = 0; + err = it87_lock(data); if (err) return err; From e19456097758a0744624bdc8363d38ea3b75a2f3 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Mon, 8 Jun 2026 13:07:48 -0400 Subject: [PATCH 1282/2758] PM: QoS: Fix misc device registration unwind [ Upstream commit 51a7c560cffdd3653ac2b930d01410569b23b23e ] cpu_latency_qos_init() registers cpu_dma_latency first and, when CONFIG_PM_QOS_CPU_SYSTEM_WAKEUP is enabled, registers cpu_wakeup_latency afterwards. The second registration overwrites the first return value. As a result, a failure to register cpu_dma_latency can be masked if the second registration succeeds. Conversely, if cpu_dma_latency succeeds and cpu_wakeup_latency fails, the function returns an error while leaving the first misc device registered. Return immediately on the first registration failure and deregister cpu_dma_latency if the second registration fails. Fixes: a4e6512a79d8 ("PM: QoS: Introduce a CPU system wakeup QoS limit") Signed-off-by: Yuho Choi Link: https://patch.msgid.link/20260608170748.82273-1-dbgh9129@gmail.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- kernel/power/qos.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/kernel/power/qos.c b/kernel/power/qos.c index 398b994b73aab..1944dbeb0d4c9 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -519,18 +519,23 @@ static int __init cpu_latency_qos_init(void) int ret; ret = misc_register(&cpu_latency_qos_miscdev); - if (ret < 0) + if (ret < 0) { pr_err("%s: %s setup failed\n", __func__, cpu_latency_qos_miscdev.name); + return ret; + } #ifdef CONFIG_PM_QOS_CPU_SYSTEM_WAKEUP ret = misc_register(&cpu_wakeup_latency_qos_miscdev); - if (ret < 0) + if (ret < 0) { pr_err("%s: %s setup failed\n", __func__, cpu_wakeup_latency_qos_miscdev.name); + misc_deregister(&cpu_latency_qos_miscdev); + return ret; + } #endif - return ret; + return 0; } late_initcall(cpu_latency_qos_init); #endif /* CONFIG_CPU_IDLE */ From f3aa5ceea27ac613a56715159a1c19e429063ec0 Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Fri, 22 May 2026 11:02:46 +0200 Subject: [PATCH 1283/2758] btrfs: zoned: don't account data relocation space-info in statfs free space [ Upstream commit 52416a27aabc43eab3792fd0ca9f5dabeab58f31 ] Don't account the free space in a data relocation space-info sub-group as usable free space in statfs. This is misleading as no user allocations can be made in this space-info sub-group. It is only a target for relocation. Fixes: f92ee31e031c ("btrfs: introduce btrfs_space_info sub-group") Reviewed-by: Boris Burkov Reviewed-by: Naohiro Aota Signed-off-by: Johannes Thumshirn Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/super.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 64514d600eec7..ba70d727622e8 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -1740,7 +1740,8 @@ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf) int mixed = 0; list_for_each_entry(found, &fs_info->space_info, list) { - if (found->flags & BTRFS_BLOCK_GROUP_DATA) { + if (found->flags & BTRFS_BLOCK_GROUP_DATA && + found->subgroup_id != BTRFS_SUB_GROUP_DATA_RELOC) { int i; total_free_data += found->disk_total - found->disk_used; From f84dbbaa988e1344634a20ddf6c7d51a99f12477 Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Fri, 22 May 2026 11:02:47 +0200 Subject: [PATCH 1284/2758] btrfs: zoned: fix deadlock waiting for ticket during data relocation [ Upstream commit 814c3b4ea357297c507158bceb07bcdc5fbe9808 ] When performing data relocation on a zoned filesystem, BTRFS can deadlock in handle_reserve_tickets(). The relocation process is waiting on a space reservation ticket that can never be fulfilled, because the relocation itself is the operation responsible for freeing up that space. Fix this by introducing a new flush state, BTRFS_RESERVE_FLUSH_ZONED_RELOCATION, specifically for data chunk allocation during zoned relocation. Like BTRFS_RESERVE_FLUSH_FREE_SPACE_INODE, this state uses priority_reclaim_data_space() instead of the normal flushing path, which avoids re-entering the relocation code and breaking the deadlock cycle. In btrfs_alloc_data_chunk_ondemand(), select this new flush state when the inode belongs to a data relocation root on a zoned filesystem. Fixes: e2a7fd22378f ("btrfs: zoned: add zone reclaim flush state for DATA space_info") Reviewed-by: Boris Burkov Reviewed-by: Naohiro Aota Signed-off-by: Johannes Thumshirn Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/delalloc-space.c | 2 ++ fs/btrfs/space-info.c | 2 ++ fs/btrfs/space-info.h | 11 +++++++++++ 3 files changed, 15 insertions(+) diff --git a/fs/btrfs/delalloc-space.c b/fs/btrfs/delalloc-space.c index 0970799d0aa44..4293a63834337 100644 --- a/fs/btrfs/delalloc-space.c +++ b/fs/btrfs/delalloc-space.c @@ -134,6 +134,8 @@ int btrfs_alloc_data_chunk_ondemand(const struct btrfs_inode *inode, u64 bytes) if (btrfs_is_free_space_inode(inode)) flush = BTRFS_RESERVE_FLUSH_FREE_SPACE_INODE; + else if (btrfs_is_zoned(fs_info) && btrfs_is_data_reloc_root(root)) + flush = BTRFS_RESERVE_FLUSH_ZONED_RELOCATION; return btrfs_reserve_data_bytes(data_sinfo_for_inode(inode), bytes, flush); } diff --git a/fs/btrfs/space-info.c b/fs/btrfs/space-info.c index f0436eea15445..e7a5cf50caa4b 100644 --- a/fs/btrfs/space-info.c +++ b/fs/btrfs/space-info.c @@ -1698,6 +1698,7 @@ static int handle_reserve_ticket(struct btrfs_space_info *space_info, ARRAY_SIZE(evict_flush_states)); break; case BTRFS_RESERVE_FLUSH_FREE_SPACE_INODE: + case BTRFS_RESERVE_FLUSH_ZONED_RELOCATION: priority_reclaim_data_space(space_info, ticket); break; default: @@ -1961,6 +1962,7 @@ int btrfs_reserve_data_bytes(struct btrfs_space_info *space_info, u64 bytes, ASSERT(flush == BTRFS_RESERVE_FLUSH_DATA || flush == BTRFS_RESERVE_FLUSH_FREE_SPACE_INODE || + flush == BTRFS_RESERVE_FLUSH_ZONED_RELOCATION || flush == BTRFS_RESERVE_NO_FLUSH, "flush=%d", flush); ASSERT(!current->journal_info || flush != BTRFS_RESERVE_FLUSH_DATA, "current->journal_info=0x%lx flush=%d", diff --git a/fs/btrfs/space-info.h b/fs/btrfs/space-info.h index 24f45072ca4b1..aa836e8a9d4a6 100644 --- a/fs/btrfs/space-info.h +++ b/fs/btrfs/space-info.h @@ -77,6 +77,17 @@ enum btrfs_reserve_flush_enum { */ BTRFS_RESERVE_FLUSH_ALL_STEAL, + /* + * This is for relocation on zoned filesystems only. We need to use + * priority flushing for this, because otherwise we can deadlock on + * waiting for a ticket, that cannot be granted, because we cannot do + * any allocations. + * + * Apart from being specific to zoned relocation, it is equal to + * BTRFS_FLUSH_FREE_SPACE_INODE. + */ + BTRFS_RESERVE_FLUSH_ZONED_RELOCATION, + /* * This is for btrfs_use_block_rsv only. We have exhausted our block * rsv and our global block rsv. This can happen for things like From d0f74832ba65985ee3235995d22fc249ff3710bb Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Fri, 22 May 2026 19:14:07 +0100 Subject: [PATCH 1285/2758] Revert "btrfs: fix the file offset calculation inside btrfs_decompress_buf2page()" [ Upstream commit 0279bed34c22dd5ebff12e5af8ef940de93c5523 ] It seems that af566bdaff54 was tested against a tree which did not contain commit 12851bd921d4 ("fs: Turn page_offset() into a wrapper around folio_pos()). Unfortunately it has a bug of its own; on 32-bit systems, shifting by PAGE_SHIFT will overflow on files larger than 4GiB. Since page_offset() is now fixed, just revert af566bdaff54. Fixes: af566bdaff54 (btrfs: fix the file offset calculation inside btrfs_decompress_buf2page()) Signed-off-by: Matthew Wilcox (Oracle) Reviewed-by: Qu Wenruo Reviewed-by: Boris Burkov Tested-by: Boris Burkov Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/compression.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index a02b62e0a8f33..2ceb5661e0718 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c @@ -1191,22 +1191,6 @@ void __cold btrfs_exit_compress(void) bioset_exit(&btrfs_compressed_bioset); } -/* - * The bvec is a single page bvec from a bio that contains folios from a filemap. - * - * Since the folio may be a large one, and if the bv_page is not a head page of - * a large folio, then page->index is unreliable. - * - * Thus we need this helper to grab the proper file offset. - */ -static u64 file_offset_from_bvec(const struct bio_vec *bvec) -{ - const struct page *page = bvec->bv_page; - const struct folio *folio = page_folio(page); - - return (page_pgoff(folio, page) << PAGE_SHIFT) + bvec->bv_offset; -} - /* * Copy decompressed data from working buffer to pages. * @@ -1259,7 +1243,7 @@ int btrfs_decompress_buf2page(const char *buf, u32 buf_len, * cb->start may underflow, but subtracting that value can still * give us correct offset inside the full decompressed extent. */ - bvec_offset = file_offset_from_bvec(&bvec) - cb->start; + bvec_offset = page_offset(bvec.bv_page) + bvec.bv_offset - cb->start; /* Haven't reached the bvec range, exit */ if (decompressed + buf_len <= bvec_offset) From 68747085ea2a7745efcab089862da2fb76462bc8 Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Fri, 22 May 2026 11:22:12 +0200 Subject: [PATCH 1286/2758] btrfs: zoned: always set max_active_zones for zoned devices [ Upstream commit 21a3533b99b8a53a026cc9f5041b10795c1f3ae8 ] When a block device does not report a maximum number of open or active zones, currently assign BTRFS_DEFAULT_MAX_ACTIVE_ZONES (128) to the internal limit, if the device has more than BTRFS_DEFAULT_MAX_ACTIVE_ZONES zones. But if the device has less than BTRFS_DEFAULT_MAX_ACTIVE_ZONES the internal max_active_zones limit will stay at 0, even if the device has zone resource limits. Furthermore, if the device has a total number of zones that is less than BTRFS_DEFAULT_MAX_ACTIVE_ZONE, max_active_zones should be set to at most the number of zones. Also move the max_active_zone calculation and setting into a dedicated helper, to shrink btrfs_get_dev_zone_info(). Fixes: 04147d8394e8 ("btrfs: zoned: limit active zones to max_open_zones") Reviewed-by: Damien Le Moal Signed-off-by: Johannes Thumshirn Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/zoned.c | 64 +++++++++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c index 16dd87aa06f20..0d590e81f3259 100644 --- a/fs/btrfs/zoned.c +++ b/fs/btrfs/zoned.c @@ -354,12 +354,33 @@ int btrfs_get_dev_zone_info_all_devices(struct btrfs_fs_info *fs_info) return ret; } +static int btrfs_get_max_active_zones(struct btrfs_device *device, + struct btrfs_zoned_device_info *zone_info) +{ + struct block_device *bdev = device->bdev; + int max_active_zones; + + if (unlikely(zone_info->nr_zones < BTRFS_MIN_ACTIVE_ZONES)) { + btrfs_err(device->fs_info, "zoned: not enough zones to mount filesystem: %u < %d", + zone_info->nr_zones, BTRFS_MIN_ACTIVE_ZONES); + return -EINVAL; + } + + max_active_zones = min_not_zero(bdev_max_active_zones(bdev), + bdev_max_open_zones(bdev)); + if (max_active_zones == 0) + max_active_zones = min(zone_info->nr_zones / 4, + BTRFS_DEFAULT_MAX_ACTIVE_ZONES); + + zone_info->max_active_zones = max(max_active_zones, BTRFS_MIN_ACTIVE_ZONES); + return 0; +} + int btrfs_get_dev_zone_info(struct btrfs_device *device, bool populate_cache) { struct btrfs_fs_info *fs_info = device->fs_info; struct btrfs_zoned_device_info *zone_info = NULL; struct block_device *bdev = device->bdev; - unsigned int max_active_zones; unsigned int nactive; sector_t nr_sectors; sector_t sector = 0; @@ -424,19 +445,9 @@ int btrfs_get_dev_zone_info(struct btrfs_device *device, bool populate_cache) if (!IS_ALIGNED(nr_sectors, zone_sectors)) zone_info->nr_zones++; - max_active_zones = min_not_zero(bdev_max_active_zones(bdev), - bdev_max_open_zones(bdev)); - if (!max_active_zones && zone_info->nr_zones > BTRFS_DEFAULT_MAX_ACTIVE_ZONES) - max_active_zones = BTRFS_DEFAULT_MAX_ACTIVE_ZONES; - if (max_active_zones && max_active_zones < BTRFS_MIN_ACTIVE_ZONES) { - btrfs_err(fs_info, -"zoned: %s: max active zones %u is too small, need at least %u active zones", - rcu_dereference(device->name), max_active_zones, - BTRFS_MIN_ACTIVE_ZONES); - ret = -EINVAL; + ret = btrfs_get_max_active_zones(device, zone_info); + if (ret) goto out; - } - zone_info->max_active_zones = max_active_zones; zone_info->seq_zones = bitmap_zalloc(zone_info->nr_zones, GFP_KERNEL); if (!zone_info->seq_zones) { @@ -517,26 +528,29 @@ int btrfs_get_dev_zone_info(struct btrfs_device *device, bool populate_cache) goto out; } - if (max_active_zones) { - if (unlikely(nactive > max_active_zones)) { - if (bdev_max_active_zones(bdev) == 0) { - max_active_zones = 0; - zone_info->max_active_zones = 0; - goto validate; - } + if (unlikely(nactive > zone_info->max_active_zones)) { + if (bdev_max_active_zones(bdev) > 0) { btrfs_err(device->fs_info, - "zoned: %u active zones on %s exceeds max_active_zones %u", - nactive, rcu_dereference(device->name), - max_active_zones); + "zoned: %u active zones on %s exceeds max_active_zones %u", + nactive, rcu_dereference(device->name), + zone_info->max_active_zones); ret = -EIO; goto out; } + + /* + * This is for backward compatibility with old filesystems that + * have a lot of active zones because the device doesn't report + * a maximum number of zones and we previously didn't care for + * the limit. + */ + zone_info->max_active_zones = 0; + } else { atomic_set(&zone_info->active_zones_left, - max_active_zones - nactive); + zone_info->max_active_zones - nactive); set_bit(BTRFS_FS_ACTIVE_ZONE_TRACKING, &fs_info->flags); } -validate: /* Validate superblock log */ nr_zones = BTRFS_NR_SB_LOG_ZONES; for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) { From 70780885a617a6675e4ce26d6636389b60fe185e Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Wed, 1 Apr 2026 10:21:53 +0800 Subject: [PATCH 1287/2758] btrfs: annotate lockless read of defrag_bytes in should_nocow() [ Upstream commit 89c0dc3de7a73e8aba5e9bfef543eee047a3d0d2 ] should_nocow() reads inode->defrag_bytes without holding inode->lock, while btrfs_set_delalloc_extent() and btrfs_clear_delalloc_extent() update it under that spinlock. This is a data race. The read is a quick check used to decide whether to fall back to COW for a NOCOW inode: if defrag_bytes is non-zero and the range is tagged EXTENT_DEFRAG, we force COW so that defragmentation can rewrite the extent. Reading a stale value is harmless because: - A missed increment may skip COW once, but the defrag pass will redo the extent later. - A stale non-zero may force an unnecessary COW, which is a minor efficiency loss, not a correctness issue. On 64-bit platforms an aligned u64 load is naturally atomic so tearing cannot happen. On 32-bit platforms u64 may tear, but we only test for zero vs non-zero, so the heuristic stays correct regardless. Use data_race() annotation. Fixes: 47059d930f0e ("Btrfs: make defragment work with nodatacow option") Signed-off-by: Cen Zhang [ Use data_race() instead of READ_ONCXE() ] Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index f1f7ac8684735..dc5148f176e77 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -2317,7 +2317,7 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode, static bool should_nocow(struct btrfs_inode *inode, u64 start, u64 end) { if (inode->flags & (BTRFS_INODE_NODATACOW | BTRFS_INODE_PREALLOC)) { - if (inode->defrag_bytes && + if (data_race(inode->defrag_bytes) && btrfs_test_range_bit_exists(&inode->io_tree, start, end, EXTENT_DEFRAG)) return false; return true; From ea3452726ccb6bcaa732f43cc57bb928eca3dd59 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Tue, 26 May 2026 14:44:30 +0100 Subject: [PATCH 1288/2758] btrfs: fix deadlock cloning inline extent when using flushoncommit [ Upstream commit 532085d00eb54c074bdeae648b194765239f4d11 ] In commit b48c980b6a7e ("btrfs: fix deadlock between reflink and transaction commit when using flushoncommit") a deadlock was fixed between reflinks and transaction commits when the fs is mounted with the flushoncommit option. This happened when we had to copy an inline extent's data to the destination file. However the issue was fixed only for the case where the destination offset is 0, it missed the case when the offset is greater than zero. Fix this by ensuring we get i_size update whenever we copied an inline extent's data into the destination file. Syzbot reported this with the following trace: INFO: task kworker/u8:3:57 blocked for more than 143 seconds. Not tainted syzkaller #0 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. task:kworker/u8:3 state:D stack:21600 pid:57 tgid:57 ppid:2 task_flags:0x4208160 flags:0x00080000 Workqueue: writeback wb_workfn (flush-btrfs-129) Call Trace: context_switch kernel/sched/core.c:5402 [inline] __schedule+0x16f9/0x5500 kernel/sched/core.c:7204 __schedule_loop kernel/sched/core.c:7283 [inline] schedule+0x164/0x360 kernel/sched/core.c:7298 wait_extent_bit fs/btrfs/extent-io-tree.c:905 [inline] btrfs_lock_extent_bits+0x59c/0x700 fs/btrfs/extent-io-tree.c:2008 btrfs_lock_extent fs/btrfs/extent-io-tree.h:152 [inline] btrfs_invalidate_folio+0x440/0xc00 fs/btrfs/inode.c:7718 extent_writepage fs/btrfs/extent_io.c:1848 [inline] extent_write_cache_pages fs/btrfs/extent_io.c:2552 [inline] btrfs_writepages+0x12f3/0x2410 fs/btrfs/extent_io.c:2684 do_writepages+0x32e/0x550 mm/page-writeback.c:2571 __writeback_single_inode+0x133/0x10e0 fs/fs-writeback.c:1764 writeback_sb_inodes+0x97f/0x1980 fs/fs-writeback.c:2056 wb_writeback+0x445/0xb00 fs/fs-writeback.c:2241 wb_do_writeback fs/fs-writeback.c:2388 [inline] wb_workfn+0x3fd/0xf20 fs/fs-writeback.c:2428 process_one_work+0x98b/0x1630 kernel/workqueue.c:3318 process_scheduled_works kernel/workqueue.c:3401 [inline] worker_thread+0xb49/0x1140 kernel/workqueue.c:3482 kthread+0x388/0x470 kernel/kthread.c:436 ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 INFO: task syz.0.145:8523 blocked for more than 143 seconds. Not tainted syzkaller #0 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. task:syz.0.145 state:D stack:22752 pid:8523 tgid:8522 ppid:5850 task_flags:0x400140 flags:0x00080002 Call Trace: context_switch kernel/sched/core.c:5402 [inline] __schedule+0x16f9/0x5500 kernel/sched/core.c:7204 __schedule_loop kernel/sched/core.c:7283 [inline] schedule+0x164/0x360 kernel/sched/core.c:7298 wb_wait_for_completion+0x3e8/0x790 fs/fs-writeback.c:227 __writeback_inodes_sb_nr+0x24c/0x2d0 fs/fs-writeback.c:2847 try_to_writeback_inodes_sb+0x9a/0xc0 fs/fs-writeback.c:2895 btrfs_start_delalloc_flush fs/btrfs/transaction.c:2182 [inline] btrfs_commit_transaction+0x813/0x2fc0 fs/btrfs/transaction.c:2371 btrfs_sync_file+0xdf4/0x1230 fs/btrfs/file.c:1822 generic_write_sync include/linux/fs.h:2663 [inline] btrfs_do_write_iter+0x6a9/0x840 fs/btrfs/file.c:1473 new_sync_write fs/read_write.c:595 [inline] vfs_write+0x629/0xba0 fs/read_write.c:688 ksys_write+0x156/0x270 fs/read_write.c:740 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x15f/0x560 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7f5a0bdece59 RSP: 002b:00007f5a0b446028 EFLAGS: 00000246 ORIG_RAX: 0000000000000001 RAX: ffffffffffffffda RBX: 00007f5a0c065fa0 RCX: 00007f5a0bdece59 RDX: 000000000000029f RSI: 0000200000000200 RDI: 0000000000000004 RBP: 00007f5a0be82d6f R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 R13: 00007f5a0c066038 R14: 00007f5a0c065fa0 R15: 00007ffe149206b8 INFO: task syz.0.145:8539 blocked for more than 143 seconds. Not tainted syzkaller #0 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. task:syz.0.145 state:D stack:23704 pid:8539 tgid:8522 ppid:5850 task_flags:0x400140 flags:0x00080002 Call Trace: context_switch kernel/sched/core.c:5402 [inline] __schedule+0x16f9/0x5500 kernel/sched/core.c:7204 __schedule_loop kernel/sched/core.c:7283 [inline] schedule+0x164/0x360 kernel/sched/core.c:7298 wait_current_trans+0x39f/0x590 fs/btrfs/transaction.c:536 start_transaction+0xbd8/0x1820 fs/btrfs/transaction.c:716 clone_copy_inline_extent fs/btrfs/reflink.c:299 [inline] btrfs_clone+0x1316/0x2540 fs/btrfs/reflink.c:574 btrfs_clone_files+0x271/0x3f0 fs/btrfs/reflink.c:795 btrfs_remap_file_range+0x76b/0x1320 fs/btrfs/reflink.c:948 vfs_clone_file_range+0x435/0x7b0 fs/remap_range.c:403 ioctl_file_clone fs/ioctl.c:239 [inline] ioctl_file_clone_range fs/ioctl.c:257 [inline] do_vfs_ioctl+0xe15/0x1540 fs/ioctl.c:544 __do_sys_ioctl fs/ioctl.c:595 [inline] __se_sys_ioctl+0x82/0x170 fs/ioctl.c:583 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x15f/0x560 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7f5a0bdece59 RSP: 002b:00007f5a0b425028 EFLAGS: 00000246 ORIG_RAX: 0000000000000010 RAX: ffffffffffffffda RBX: 00007f5a0c066090 RCX: 00007f5a0bdece59 RDX: 00002000000000c0 RSI: 000000004020940d RDI: 0000000000000004 RBP: 00007f5a0be82d6f R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 R13: 00007f5a0c066128 R14: 00007f5a0c066090 R15: 00007ffe149206b8 Reported-by: syzbot+c7443384724bb0f9e913@syzkaller.appspotmail.com Link: https://lore.kernel.org/linux-btrfs/6a150a09.820a0220.e7972.0006.GAE@google.com/ Fixes: 05a5a7621ce6 ("Btrfs: implement full reflink support for inline extents") Reviewed-by: Boris Burkov Signed-off-by: Filipe Manana Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/reflink.c | 101 +++++++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 44 deletions(-) diff --git a/fs/btrfs/reflink.c b/fs/btrfs/reflink.c index 76a7b56f6cdee..951824b033b78 100644 --- a/fs/btrfs/reflink.c +++ b/fs/btrfs/reflink.c @@ -179,10 +179,12 @@ static int clone_copy_inline_extent(struct btrfs_inode *inode, struct btrfs_drop_extents_args drop_args = { 0 }; int ret; struct btrfs_key key; + bool copied_inline_to_page = false; if (new_key->offset > 0) { ret = copy_inline_to_page(inode, new_key->offset, inline_data, size, datal, comp_type); + copied_inline_to_page = (ret == 0); goto out; } @@ -288,6 +290,60 @@ static int clone_copy_inline_extent(struct btrfs_inode *inode, btrfs_abort_transaction(trans, ret); out: if (!ret && !trans) { + if (copied_inline_to_page && + new_key->offset + datal > i_size_read(&inode->vfs_inode)) { + /* + * If we copied the inline extent data to a page/folio + * beyond the i_size of the destination inode, then we + * need to increase the i_size before we start a + * transaction to update the inode item. This is to + * prevent a deadlock when the flushoncommit mount + * option is used, which happens like this: + * + * 1) Task A clones an inline extent from inode X to an + * offset of inode Y that is beyond Y's current + * i_size. This means we copied the inline extent's + * data to a folio of inode Y that is beyond its EOF, + * using the call above to copy_inline_to_page(); + * + * 2) Task B starts a transaction commit and calls + * btrfs_start_delalloc_flush() to flush delalloc; + * + * 3) The delalloc flushing sees the new dirty folio of + * inode Y and when it attempts to flush it, it ends + * up at extent_writepage() and sees that the offset + * of the folio is beyond the i_size of inode Y, so + * it attempts to invalidate the folio by calling + * folio_invalidate(), which ends up at btrfs' folio + * invalidate callback - btrfs_invalidate_folio(). + * There it tries to lock the folio's range in inode + * Y's extent io tree, but it blocks since it's + * currently locked by task A - during reflink we + * lock the inodes and the source and destination + * ranges after flushing all delalloc and waiting for + * ordered extent completion - after that we don't + * expect to have dirty folios in the ranges, the + * exception is if we have to copy an inline extent's + * data (because the destination offset is not zero); + * + * 4) Task A then does the 'goto out' below and attempts + * to start a transaction to update the inode item, + * and then it's blocked since the current + * transaction is in the TRANS_STATE_COMMIT_START + * state. Therefore task A has to wait for the + * current transaction to become unblocked (its + * state >= TRANS_STATE_UNBLOCKED). + * + * This leads to a deadlock - the task committing the + * transaction waiting for the delalloc flushing which + * is blocked during folio invalidation on the inode's + * extent lock and the reflink task waiting for the + * current transaction to be unblocked so that it can + * start a new one to update the inode item (while + * holding the extent lock). + */ + i_size_write(&inode->vfs_inode, new_key->offset + datal); + } /* * No transaction here means we copied the inline extent into a * page of the destination inode. @@ -320,50 +376,7 @@ static int clone_copy_inline_extent(struct btrfs_inode *inode, ret = copy_inline_to_page(inode, new_key->offset, inline_data, size, datal, comp_type); - - /* - * If we copied the inline extent data to a page/folio beyond the i_size - * of the destination inode, then we need to increase the i_size before - * we start a transaction to update the inode item. This is to prevent a - * deadlock when the flushoncommit mount option is used, which happens - * like this: - * - * 1) Task A clones an inline extent from inode X to an offset of inode - * Y that is beyond Y's current i_size. This means we copied the - * inline extent's data to a folio of inode Y that is beyond its EOF, - * using the call above to copy_inline_to_page(); - * - * 2) Task B starts a transaction commit and calls - * btrfs_start_delalloc_flush() to flush delalloc; - * - * 3) The delalloc flushing sees the new dirty folio of inode Y and when - * it attempts to flush it, it ends up at extent_writepage() and sees - * that the offset of the folio is beyond the i_size of inode Y, so - * it attempts to invalidate the folio by calling folio_invalidate(), - * which ends up at btrfs' folio invalidate callback - - * btrfs_invalidate_folio(). There it tries to lock the folio's range - * in inode Y's extent io tree, but it blocks since it's currently - * locked by task A - during reflink we lock the inodes and the - * source and destination ranges after flushing all delalloc and - * waiting for ordered extent completion - after that we don't expect - * to have dirty folios in the ranges, the exception is if we have to - * copy an inline extent's data (because the destination offset is - * not zero); - * - * 4) Task A then does the 'goto out' below and attempts to start a - * transaction to update the inode item, and then it's blocked since - * the current transaction is in the TRANS_STATE_COMMIT_START state. - * Therefore task A has to wait for the current transaction to become - * unblocked (its state >= TRANS_STATE_UNBLOCKED). - * - * This leads to a deadlock - the task committing the transaction - * waiting for the delalloc flushing which is blocked during folio - * invalidation on the inode's extent lock and the reflink task waiting - * for the current transaction to be unblocked so that it can start a - * a new one to update the inode item (while holding the extent lock). - */ - if (ret == 0 && new_key->offset + datal > i_size_read(&inode->vfs_inode)) - i_size_write(&inode->vfs_inode, new_key->offset + datal); + copied_inline_to_page = (ret == 0); goto out; } From 1641d058adfbd50cf95d54581ed5d142ee82c07f Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Sat, 6 Jun 2026 22:25:13 -0700 Subject: [PATCH 1289/2758] btrfs: lzo: reject compressed segment that overflows the compressed input [ Upstream commit b0d27d43791b7a3057c3c4aedf9b4aa033d37c46 ] lzo_decompress_bio() validates each on-disk segment length seg_len only against the workspace cbuf size, not against the compressed input size (compressed_len, the total folio bytes of the bio). A crafted extent can carry a segment whose seg_len passes the cbuf check but runs past the end of the bio, so copy_compressed_segment() walks off the last folio: get_current_folio() then returns the NULL folio from bio_next_folio(), and with CONFIG_BTRFS_ASSERT disabled (default) folio_size(NULL) faults. BUG: KASAN: null-ptr-deref in lzo_decompress_bio (fs/btrfs/lzo.c:383) Read of size 8 at addr 0000000000000000 by task kworker/u8:1/29 Workqueue: btrfs-endio simple_end_io_work kasan_report (mm/kasan/report.c:590) lzo_decompress_bio (fs/btrfs/lzo.c:383) end_bbio_compressed_read (fs/btrfs/compression.c:1065) btrfs_bio_end_io (fs/btrfs/bio.c:135) btrfs_check_read_bio (fs/btrfs/bio.c:180 fs/btrfs/bio.c:285) simple_end_io_work process_one_work worker_thread Reject any segment whose payload would extend beyond compressed_len before copying it, treating it as corruption like the other on-disk validation failures in this function. Reported-by: Xiang Mei Fixes: a6e66e6f8c1b ("btrfs: rework lzo_decompress_bio() to make it subpage compatible") Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Qu Wenruo Signed-off-by: Weiming Shi Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/lzo.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/fs/btrfs/lzo.c b/fs/btrfs/lzo.c index 2de18c7b563af..6e4aa22853aba 100644 --- a/fs/btrfs/lzo.c +++ b/fs/btrfs/lzo.c @@ -491,6 +491,17 @@ int lzo_decompress_bio(struct list_head *ws, struct compressed_bio *cb) return -EIO; } + /* The segment must not extend beyond the compressed input. */ + if (unlikely(cur_in + seg_len > compressed_len)) { + struct btrfs_inode *inode = cb->bbio.inode; + + btrfs_err(fs_info, + "lzo segment overflows compressed input, root %llu inode %llu offset %llu cur_in %u len %u compressed len %u", + btrfs_root_id(inode->root), btrfs_ino(inode), + cb->start, cur_in, seg_len, compressed_len); + return -EUCLEAN; + } + /* Copy the compressed segment payload into workspace */ copy_compressed_segment(cb, &fi, &cur_folio_index, workspace->cbuf, seg_len, &cur_in); From a2a224f5e344ccb1ec3693a0735822db9214e2ca Mon Sep 17 00:00:00 2001 From: Larysa Zaremba Date: Mon, 18 May 2026 13:15:04 +0200 Subject: [PATCH 1290/2758] ixgbe: do not configure xps for XDP queues [ Upstream commit 7bd4355272de34c2e90e34b72c5613736d03c32b ] netif_set_xps_queue() should not be called for an XDP Tx queue, since such queues are not netdev-exposed. On systems with number of CPUs >=64, on E610 adapter, netdev is configured with maximum number queue pairs being 63 (due to MSI-X assignment), but configuring XDP results in 64 XDP queues. So, during XDP program load, when netif_set_xps_queue() is called for the last XDP queue, we get a WARNING with a call trace and KASAN report afterwards (if enabled). [ 2012.699800] WARNING: net/core/dev.c:2854 at __netif_set_xps_queue+0x116a/0x1e40, CPU#36: xdpsock/103668 [...] [ 2012.700029] RIP: 0010:__netif_set_xps_queue+0x116a/0x1e40 [ 2012.700035] Code: b6 34 06 48 89 f8 83 e0 07 83 c0 01 40 38 f0 7c 09 40 84 f6 0f 85 03 0a 00 00 0f b7 44 24 40 66 43 89 44 6a 18 e9 01 fb ff ff <0f> 0b e9 f2 ee ff ff 44 8b 44 24 44 45 85 c0 74 50 4d 85 e4 0f 84 [ 2012.700040] RSP: 0018:ffff8882369aeb28 EFLAGS: 00010246 [ 2012.700046] RAX: 0000000000000000 RBX: 000000000000003f RCX: 0000000000000000 [ 2012.700050] RDX: 1ffff1111da3d891 RSI: ffff888120e34250 RDI: ffff8888ed1ec488 [ 2012.700054] RBP: ffff888913281560 R08: 0000000000000000 R09: ffff8888ed1ec000 [ 2012.700058] R10: ffff8888a2e83180 R11: 0000000000000000 R12: 0000000000007fa8 [ 2012.700061] R13: 000000000000003f R14: ffff888120e34854 R15: ffff8889132817c8 [ 2012.700065] FS: 00007fc8ea9ff740(0000) GS:ffff88884cefe000(0000) knlGS:0000000000000000 [ 2012.700069] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 2012.700073] CR2: 00007f81c8000020 CR3: 00000002299f8006 CR4: 00000000007726f0 [ 2012.700077] PKRU: 55555554 [ 2012.700080] Call Trace: [ 2012.700084] [ 2012.700087] ? ktime_get+0x61/0x150 [ 2012.700097] ? usleep_range_state+0x133/0x1b0 [ 2012.700108] ? __pfx_usleep_range_state+0x10/0x10 [ 2012.700114] netif_set_xps_queue+0x31/0x50 [ 2012.700119] ixgbe_configure_tx_ring+0x472/0x920 [ixgbe] [...] [ 2012.700486] ixgbe_xdp+0x38f/0x750 [ixgbe] [...] [ 2012.701094] BUG: KASAN: slab-out-of-bounds in __netif_set_xps_queue+0x1ac5/0x1e40 [ 2012.701100] Write of size 4 at addr ffff88888d43cff8 by task xdpsock/103668 Skip XPS configuration for XDP Tx queues. Fixes: 33fdc82f0883 ("ixgbe: add support for XDP_TX action") Reviewed-by: Alexander Lobakin Reviewed-by: Aleksandr Loktionov Signed-off-by: Larysa Zaremba Reviewed-by: Simon Horman Tested-by: Patryk Holda Signed-off-by: Tony Nguyen Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 2646ee6f295f0..9ec250c262842 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -3958,7 +3958,8 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, } /* initialize XPS */ - if (!test_and_set_bit(__IXGBE_TX_XPS_INIT_DONE, ring->state)) { + if (!ring_is_xdp(ring) && + !test_and_set_bit(__IXGBE_TX_XPS_INIT_DONE, ring->state)) { struct ixgbe_q_vector *q_vector = ring->q_vector; if (q_vector) From 28781aa2351faa4c5a426ea713eda6a0838c5159 Mon Sep 17 00:00:00 2001 From: KhaiWenTan Date: Fri, 24 Apr 2026 15:59:07 +0800 Subject: [PATCH 1291/2758] igc: skip RX timestamp header for frame preemption verification [ Upstream commit 38b7a274cf84af9b1f4b602b8e2741565b81947b ] When RX hardware timestamping is enabled, a 16-byte inline timestamp header is added to the start of the packet buffer, causing FPE handshake verification to fail. Because an incorrect packet buffer is passed to igc_fpe_handle_mpacket(), the mem_is_zero() check inspects the timestamp metadata instead of the actual mPacket payload. As a result, valid Verify/Response mPackets can be missed when inline RX timestamps are present. Pass pktbuf + pkt_offset to igc_fpe_handle_mpacket() so it inspects the actual mPacket payload instead of the timestamp header. Fixes: 5422570c0010 ("igc: add support for frame preemption verification") Co-developed-by: Faizal Rahim Signed-off-by: Faizal Rahim Signed-off-by: KhaiWenTan Reviewed-by: Aleksandr Loktionov Signed-off-by: Tony Nguyen Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/igc/igc_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 8ac16808023cc..c470d2354ce8f 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -2649,7 +2649,7 @@ static int igc_clean_rx_irq(struct igc_q_vector *q_vector, const int budget) } if (igc_fpe_is_pmac_enabled(adapter) && - igc_fpe_handle_mpacket(adapter, rx_desc, size, pktbuf)) { + igc_fpe_handle_mpacket(adapter, rx_desc, size, pktbuf + pkt_offset)) { /* Advance the ring next-to-clean */ igc_is_non_eop(rx_ring, rx_desc); cleaned_count++; From e9414bbe3974f511a18bc728e813f0883838ab4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Tue, 9 Jun 2026 09:03:56 -0300 Subject: [PATCH 1292/2758] ASoC: sma1307: Fix uevent string leaks in fault worker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit a750ca72af72dde9744468fdca6eda0b698a1cfc ] sma1307_check_fault_worker() stores dynamically allocated uevent strings in envp[0]. Several fault conditions are checked in sequence, so a later fault can overwrite envp[0] before the final kfree() and leak the previous allocation. The same flow can leave an OT1 volume entry in envp[1] while envp[0] has been overwritten by a later non-OT1 fault, causing an inconsistent uevent payload. Use static STATUS strings and a stack buffer for the optional VOLUME entry. This removes the allocations from the worker and keeps VOLUME tied only to the OT1 events that produce it. Fixes: 576c57e6b4c1 ("ASoC: sma1307: Add driver for Iron Device SMA1307") Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260609-asoc-sma1307-uevent-leak-v1-1-cd7f5b062ab7@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/sma1307.c | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/sound/soc/codecs/sma1307.c b/sound/soc/codecs/sma1307.c index 5850bf6e71cad..5cc0834873f00 100644 --- a/sound/soc/codecs/sma1307.c +++ b/sound/soc/codecs/sma1307.c @@ -1592,6 +1592,7 @@ static void sma1307_check_fault_worker(struct work_struct *work) struct sma1307_priv *sma1307 = container_of(work, struct sma1307_priv, check_fault_work.work); unsigned int status1_val, status2_val; + char volume[sizeof("VOLUME=0x12345678")]; char *envp[3] = { NULL, NULL, NULL }; if (sma1307->tsdw_cnt) @@ -1607,7 +1608,7 @@ static void sma1307_check_fault_worker(struct work_struct *work) if (~status1_val & SMA1307_OT1_OK_STATUS) { dev_crit(sma1307->dev, "%s: OT1(Over Temperature Level 1)\n", __func__); - envp[0] = kasprintf(GFP_KERNEL, "STATUS=OT1"); + envp[0] = "STATUS=OT1"; if (sma1307->sw_ot1_prot) { /* Volume control (Current Volume -3dB) */ if ((sma1307->cur_vol + 6) <= 0xFA) { @@ -1615,8 +1616,9 @@ static void sma1307_check_fault_worker(struct work_struct *work) regmap_write(sma1307->regmap, SMA1307_0A_SPK_VOL, sma1307->cur_vol); - envp[1] = kasprintf(GFP_KERNEL, - "VOLUME=0x%02X", sma1307->cur_vol); + snprintf(volume, sizeof(volume), + "VOLUME=0x%02X", sma1307->cur_vol); + envp[1] = volume; } } sma1307->tsdw_cnt++; @@ -1625,48 +1627,53 @@ static void sma1307_check_fault_worker(struct work_struct *work) SMA1307_0A_SPK_VOL, sma1307->init_vol); sma1307->tsdw_cnt = 0; sma1307->cur_vol = sma1307->init_vol; - envp[0] = kasprintf(GFP_KERNEL, "STATUS=OT1_CLEAR"); - envp[1] = kasprintf(GFP_KERNEL, - "VOLUME=0x%02X", sma1307->cur_vol); + envp[0] = "STATUS=OT1_CLEAR"; + snprintf(volume, sizeof(volume), "VOLUME=0x%02X", + sma1307->cur_vol); + envp[1] = volume; } if (~status1_val & SMA1307_OT2_OK_STATUS) { dev_crit(sma1307->dev, "%s: OT2(Over Temperature Level 2)\n", __func__); - envp[0] = kasprintf(GFP_KERNEL, "STATUS=OT2"); + envp[0] = "STATUS=OT2"; + envp[1] = NULL; } if (status1_val & SMA1307_UVLO_STATUS) { dev_crit(sma1307->dev, "%s: UVLO(Under Voltage Lock Out)\n", __func__); - envp[0] = kasprintf(GFP_KERNEL, "STATUS=UVLO"); + envp[0] = "STATUS=UVLO"; + envp[1] = NULL; } if (status1_val & SMA1307_OVP_BST_STATUS) { dev_crit(sma1307->dev, "%s: OVP_BST(Over Voltage Protection)\n", __func__); - envp[0] = kasprintf(GFP_KERNEL, "STATUS=OVP_BST"); + envp[0] = "STATUS=OVP_BST"; + envp[1] = NULL; } if (status2_val & SMA1307_OCP_SPK_STATUS) { dev_crit(sma1307->dev, "%s: OCP_SPK(Over Current Protect SPK)\n", __func__); - envp[0] = kasprintf(GFP_KERNEL, "STATUS=OCP_SPK"); + envp[0] = "STATUS=OCP_SPK"; + envp[1] = NULL; } if (status2_val & SMA1307_OCP_BST_STATUS) { dev_crit(sma1307->dev, "%s: OCP_BST(Over Current Protect Boost)\n", __func__); - envp[0] = kasprintf(GFP_KERNEL, "STATUS=OCP_BST"); + envp[0] = "STATUS=OCP_BST"; + envp[1] = NULL; } if (status2_val & SMA1307_CLK_MON_STATUS) { dev_crit(sma1307->dev, "%s: CLK_FAULT(No clock input)\n", __func__); - envp[0] = kasprintf(GFP_KERNEL, "STATUS=CLK_FAULT"); + envp[0] = "STATUS=CLK_FAULT"; + envp[1] = NULL; } if (envp[0] != NULL) { if (kobject_uevent_env(sma1307->kobj, KOBJ_CHANGE, envp)) dev_err(sma1307->dev, "%s: Error sending uevent\n", __func__); - kfree(envp[0]); - kfree(envp[1]); } if (sma1307->check_fault_status) { From 29a7ec9a53ffd6e17595fc8a41d19bbfd4d616e1 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Fri, 5 Jun 2026 08:53:35 -0300 Subject: [PATCH 1293/2758] IB/mlx4: Fill in the access_flags if IB_MR_REREG_ACCESS is not specified [ Upstream commit bade9a3150d44ed20b8c6484c4c8a943b7289abb ] Sashiko noticed mlx4 was using whatever random access flags were provided when IB_MR_REREG_ACCESS is not used. Since IB_MR_REREG_TRANS needs access_flags it used the random ones which means it doesn't work sensibly if userspace provides only IB_MR_REREG_TRANS. Keep track of the current access_flag of the MR and use it if the user does not specify one. Also fixup a little confusion around mmr.access, it is the HW access flags so the convert_access() was missing. But nothing reads this by the time rereg_mr can happen. Fixes: 9376932d0c26 ("IB/mlx4_ib: Add support for user MR re-registration") Link: https://patch.msgid.link/r/0-v1-29ca7a402625+ddd6-mlx4_rereg_flags_jgg@nvidia.com Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx4/mlx4_ib.h | 1 + drivers/infiniband/hw/mlx4/mr.c | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h index 5a799d6df93eb..898c8363422ab 100644 --- a/drivers/infiniband/hw/mlx4/mlx4_ib.h +++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h @@ -135,6 +135,7 @@ struct mlx4_ib_mr { dma_addr_t page_map; u32 npages; u32 max_pages; + int access_flags; struct mlx4_mr mmr; struct ib_umem *umem; size_t page_map_size; diff --git a/drivers/infiniband/hw/mlx4/mr.c b/drivers/infiniband/hw/mlx4/mr.c index 6747bca306777..67b0e8954b5e5 100644 --- a/drivers/infiniband/hw/mlx4/mr.c +++ b/drivers/infiniband/hw/mlx4/mr.c @@ -181,6 +181,7 @@ struct ib_mr *mlx4_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, if (err) goto err_mr; + mr->access_flags = access_flags; mr->ibmr.rkey = mr->ibmr.lkey = mr->mmr.key; mr->ibmr.page_size = 1U << shift; @@ -241,6 +242,8 @@ struct ib_mr *mlx4_ib_rereg_user_mr(struct ib_mr *mr, int flags, u64 start, if (err) goto release_mpt_entry; + } else { + mr_access_flags = mmr->access_flags; } if (flags & IB_MR_REREG_TRANS) { @@ -282,8 +285,10 @@ struct ib_mr *mlx4_ib_rereg_user_mr(struct ib_mr *mr, int flags, u64 start, * return a failure. But dereg_mr will free the resources. */ err = mlx4_mr_hw_write_mpt(dev->dev, &mmr->mmr, pmpt_entry); - if (!err && flags & IB_MR_REREG_ACCESS) - mmr->mmr.access = mr_access_flags; + if (!err && flags & IB_MR_REREG_ACCESS) { + mmr->access_flags = mr_access_flags; + mmr->mmr.access = convert_access(mr_access_flags); + } release_mpt_entry: mlx4_mr_hw_put_mpt(dev->dev, pmpt_entry); From 8024028ef91616cf91cc669f2446a0406bc0ba19 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Sun, 19 Apr 2026 14:53:00 -0400 Subject: [PATCH 1294/2758] NFSD: Handle layout stid in nfsd4_drop_revoked_stid() [ Upstream commit 86b9898920a6d02b4149f4fef9efd77b8aa3b9ca ] nfsd4_drop_revoked_stid() has no SC_TYPE_LAYOUT case, so when a client sends FREE_STATEID for an admin-revoked layout stid, the default branch releases cl_lock and returns without unhashing or releasing the stid. The stid remains in the IDR and on the per-client list until the client is destroyed. Remove the layout stid from the per-client list and call nfs4_put_stid() to drop the creation reference. When the refcount reaches zero, nfsd4_free_layout_stateid() handles the remaining cleanup: cancelling the fence worker, removing from the per-file list, and freeing the slab object. Fixes: 1e33e1414bec ("nfsd: allow layout state to be admin-revoked.") Reviewed-by: Jeff Layton Tested-by: Dai Ngo Signed-off-by: Chuck Lever Signed-off-by: Sasha Levin --- fs/nfsd/nfs4state.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 2696a9fef0291..523db702464c1 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -5056,6 +5056,7 @@ static void nfsd4_drop_revoked_stid(struct nfs4_stid *s) { struct nfs4_client *cl = s->sc_client; LIST_HEAD(reaplist); + struct nfs4_layout_stateid *ls; struct nfs4_ol_stateid *stp; struct nfs4_delegation *dp; bool unhashed; @@ -5081,6 +5082,12 @@ static void nfsd4_drop_revoked_stid(struct nfs4_stid *s) spin_unlock(&cl->cl_lock); nfs4_put_stid(s); break; + case SC_TYPE_LAYOUT: + ls = layoutstateid(s); + list_del_init(&ls->ls_perclnt); + spin_unlock(&cl->cl_lock); + nfs4_put_stid(s); + break; default: spin_unlock(&cl->cl_lock); } From 710582532196094a921247f06242e20ca2a5302c Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 12 May 2026 14:13:36 -0400 Subject: [PATCH 1295/2758] lockd: Stop warning on nlm__int__drop_reply in !V4 cast_status [ Upstream commit 5cca6056f2bae9be14566e0f7f6e351103a6aef3 ] cast_status folds internal lock-daemon sentinels into NLMv1/v3 wire status codes. The !CONFIG_LOCKD_V4 variant warns when an unrecognized status falls into the internal-sentinel range, gated by be32_to_cpu(status) >= 30000. nlm__int__drop_reply is defined as cpu_to_be32(30000), so it sits at the lower edge of that range and trips pr_warn_once ("lockd: unhandled internal status %u"). The status is returned unchanged so the reply is still dropped, but every dropped reply on a !CONFIG_LOCKD_V4 build emits a spurious warning. Compare against nlm__int__drop_reply directly so the warning still catches the genuinely unexpected sentinels deadlock, stale_fh, and failed (30001 through 30003) but excludes the legitimate dropped-reply marker. Fixes: d343fce148a4 ("[PATCH] knfsd: Allow lockd to drop replies as appropriate") Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Sasha Levin --- fs/lockd/svcproc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index c0a3487719e28..110e186802b6c 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -49,7 +49,7 @@ static inline __be32 cast_status(__be32 status) status = nlm_lck_denied_nolocks; break; default: - if (be32_to_cpu(status) >= 30000) + if (be32_to_cpu(status) > be32_to_cpu(nlm__int__drop_reply)) pr_warn_once("lockd: unhandled internal status %u\n", be32_to_cpu(status)); break; From 6ed9b32130d6571329564491ec64b81fefa58e91 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 12 May 2026 14:13:39 -0400 Subject: [PATCH 1296/2758] lockd: Translate nlm__int__deadlock in __nlm4svc_proc_lock_msg() [ Upstream commit 2175ca75882e346c9d8741a2196693d70f635340 ] When nlmsvc_lock() detects a deadlock it returns the internal sentinel nlm__int__deadlock (30001), which version-specific handlers must translate to a wire-valid status before the reply is encoded. The xdrgen LOCK_MSG handler stores the sentinel unmodified in resp->status; the LOCK_RES callback then places 30001 on the v4 wire, where the client rejects the reply. Commit 9e0d0c619407 ("lockd: Introduce nlm__int__deadlock") established the translation boundary and updated the synchronous v4 path nlm4svc_do_lock(), but the xdrgen LOCK_MSG handler added later in commit b2be4e28c23a ("lockd: Use xdrgen XDR functions for the NLMv4 LOCK_MSG procedure") missed the corresponding remap. Apply the same translation in __nlm4svc_proc_lock_msg() so deadlock results are reported as nlm4_deadlock on LOCK_RES. Fixes: b2be4e28c23a ("lockd: Use xdrgen XDR functions for the NLMv4 LOCK_MSG procedure") Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Sasha Levin --- fs/lockd/svc4proc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index 41cab858de578..74070e33cb3e2 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -669,6 +669,8 @@ __nlm4svc_proc_lock_msg(struct svc_rqst *rqstp, struct nlm_res *resp) resp->status = nlmsvc_lock(rqstp, file, host, &argp->lock, argp->xdrgen.block, &resp->cookie, argp->xdrgen.reclaim); + if (resp->status == nlm__int__deadlock) + resp->status = nlm4_deadlock; nlmsvc_release_lockowner(&argp->lock); out: From ae3178e73497693923f33d5d56cc5c4e37c51956 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 12 May 2026 14:13:40 -0400 Subject: [PATCH 1297/2758] lockd: Do not monitor when looking up the LOCK_MSG callback host [ Upstream commit b3d200166a35305ac77941845dcab99bb6badd76 ] A LOCK_MSG handler that fails to obtain a host returns rpc_system_err, which causes the dispatcher to send an RPC-level error rather than an NLM LOCK_RES denial. Before the xdrgen conversion, the outer host lookup was unmonitored, so an NSM upcall failure was reported back to the client through LOCK_RES with status nlm_lck_denied_nolocks generated by the inner helper. The xdrgen conversion replaced the unmonitored lookup with nlm4svc_lookup_host(..., true). When nsm_monitor() fails, the outer lookup now returns NULL, so the procedure short-circuits to rpc_system_err and __nlm4svc_proc_lock_msg() never runs. The client therefore receives no LOCK_RES, regressing the legacy behavior. The inner helper still performs a monitored lookup while building the LOCK_RES, so the outer call only needs an unmonitored host reference for the callback path. Pass false here to restore the previous semantics. Fixes: b2be4e28c23a ("lockd: Use xdrgen XDR functions for the NLMv4 LOCK_MSG procedure") Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Sasha Levin --- fs/lockd/svc4proc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index 74070e33cb3e2..ca1d165e0d860 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -699,7 +699,7 @@ static __be32 nlm4svc_proc_lock_msg(struct svc_rqst *rqstp) struct nlm4_lockargs_wrapper *argp = rqstp->rq_argp; struct nlm_host *host; - host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, true); + host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); if (!host) return rpc_system_err; From 686c2434f9f16b87aeed18d76cc562df9f2695ab Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 14 May 2026 16:56:07 -0400 Subject: [PATCH 1298/2758] lockd: Avoid hashing uninitialized bytes in nlm4svc_lookup_file() [ Upstream commit 6e4c62caecf792e8a15ad9bc7f371e57c17e3302 ] file_hash() digests the first LOCKD_FH_HASH_SIZE bytes of nfs_fh.data when bucketing nlm_files[], independent of fh.size. Commit 3de744ee4e45 ("lockd: Use xdrgen XDR functions for the NLMv4 TEST procedure") set .pc_argzero to zero for the converted procedures and moved file-handle population into nlm4svc_lookup_file(), which copies only xdr_lock->fh.len bytes into lock->fh.data. When an NLMv4 client presents a file handle shorter than LOCKD_FH_HASH_SIZE, bytes fh.len..31 retain whatever the argument buffer held from an earlier request. The same wire handle then hashes to different buckets across calls; nlm_lookup_file() misses the existing nlm_file entry, and lock-state lookups fail. Zero only the tail bytes that file_hash() would otherwise consume. Handles of LOCKD_FH_HASH_SIZE or larger already populate every byte that file_hash() reads. Reported-by: Jeff Layton Closes: https://lore.kernel.org/r/5229a9746d723a3f830120c0b966510f75badfc2.camel@kernel.org Fixes: 3de744ee4e45 ("lockd: Use xdrgen XDR functions for the NLMv4 TEST procedure") Signed-off-by: Chuck Lever Signed-off-by: Sasha Levin --- fs/lockd/lockd.h | 8 ++++++++ fs/lockd/svc4proc.c | 3 +++ fs/lockd/svcsubs.c | 3 +-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/fs/lockd/lockd.h b/fs/lockd/lockd.h index 1db6cb3525425..9aa6acb43f9a4 100644 --- a/fs/lockd/lockd.h +++ b/fs/lockd/lockd.h @@ -52,6 +52,14 @@ */ #define LOCKD_DFLT_TIMEO 10 +/* + * Number of leading bytes of nfs_fh.data that file_hash() + * digests when bucketing nlm_files[]. Sized for historical + * NFSv2 handles; nfs_fh.data must be initialized at least + * this far before lookup, regardless of fh.size. + */ +#define LOCKD_FH_HASH_SIZE 32 + /* error codes new to NLMv4 */ #define nlm4_deadlock cpu_to_be32(NLM_DEADLCK) #define nlm4_rofs cpu_to_be32(NLM_ROFS) diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index ca1d165e0d860..3bf6cccae5685 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -157,6 +157,9 @@ nlm4svc_lookup_file(struct svc_rqst *rqstp, struct nlm_host *host, return nlm_lck_denied_nolocks; lock->fh.size = xdr_lock->fh.len; memcpy(lock->fh.data, xdr_lock->fh.data, xdr_lock->fh.len); + if (xdr_lock->fh.len < LOCKD_FH_HASH_SIZE) + memset(lock->fh.data + xdr_lock->fh.len, 0, + LOCKD_FH_HASH_SIZE - xdr_lock->fh.len); lock->oh.len = xdr_lock->oh.len; lock->oh.data = xdr_lock->oh.data; diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index 9da9d6e0b42e2..95a499940daf2 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -17,7 +17,6 @@ #include #include #include -#include #include "lockd.h" #include "share.h" @@ -67,7 +66,7 @@ static inline unsigned int file_hash(struct nfs_fh *f) { unsigned int tmp=0; int i; - for (i=0; idata[i]; return tmp & (FILE_NRHASH - 1); } From ad88aa6cac75ef1c84c2e453e1bb787d1ff9384a Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Tue, 9 Jun 2026 13:26:47 +0800 Subject: [PATCH 1299/2758] spi: meson-spifc: fix runtime PM leak on remove [ Upstream commit 606c0826bd90384a54571c0c5475ca41f50164ea ] pm_runtime_get_sync() increments the runtime PM usage counter even when it returns an error. meson_spifc_remove() uses it to resume the controller before disabling runtime PM, but never drops the usage counter again. Balance the get with pm_runtime_put_noidle() after disabling runtime PM, matching the teardown pattern used by other SPI controller drivers. Found by static analysis. I do not have hardware to test this. Fixes: c3e4bc5434d2 ("spi: meson: Add support for Amlogic Meson SPIFC") Signed-off-by: Ruoyu Wang Link: https://patch.msgid.link/20260609052647.5-1-ruoyuw560@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-meson-spifc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-meson-spifc.c b/drivers/spi/spi-meson-spifc.c index b818950a8cb7d..e2d19c3873f71 100644 --- a/drivers/spi/spi-meson-spifc.c +++ b/drivers/spi/spi-meson-spifc.c @@ -351,6 +351,7 @@ static void meson_spifc_remove(struct platform_device *pdev) { pm_runtime_get_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); } #ifdef CONFIG_PM_SLEEP From 6b86dc584bf3182504e7a12fce73b3b3184d9364 Mon Sep 17 00:00:00 2001 From: Val Packett Date: Fri, 29 May 2026 17:05:13 -0300 Subject: [PATCH 1300/2758] ASoC: codecs: aw88261: fix incorrect masks for boost regs [ Upstream commit 79c053a1ff9d3ab31cefbc791e8d7816ba830491 ] The boost-related register fields used in aw88261_reg_force_set use the exact same definitions as the rest of the fields, where the mask must be inverted when passing it to regmap_update_bits, but they weren't inverted here. Fixes: 028a2ae25691 ("ASoC: codecs: Add aw88261 amplifier driver") Signed-off-by: Val Packett Tested-by: Luca Weiss Link: https://patch.msgid.link/20260529200550.529719-7-val@packett.cool Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/aw88261.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c index a6805d5405cd1..a6545f5a8ed0d 100644 --- a/sound/soc/codecs/aw88261.c +++ b/sound/soc/codecs/aw88261.c @@ -284,22 +284,22 @@ static void aw88261_reg_force_set(struct aw88261 *aw88261) if (aw88261->frcset_en == AW88261_FRCSET_ENABLE) { /* set FORCE_PWM */ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL3_REG, - AW88261_FORCE_PWM_MASK, AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE); + ~AW88261_FORCE_PWM_MASK, AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE); /* set BOOST_OS_WIDTH */ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL5_REG, - AW88261_BST_OS_WIDTH_MASK, AW88261_BST_OS_WIDTH_50NS_VALUE); + ~AW88261_BST_OS_WIDTH_MASK, AW88261_BST_OS_WIDTH_50NS_VALUE); /* set BURST_LOOPR */ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL6_REG, - AW88261_BST_LOOPR_MASK, AW88261_BST_LOOPR_340K_VALUE); + ~AW88261_BST_LOOPR_MASK, AW88261_BST_LOOPR_340K_VALUE); /* set RSQN_DLY */ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL7_REG, - AW88261_RSQN_DLY_MASK, AW88261_RSQN_DLY_35NS_VALUE); + ~AW88261_RSQN_DLY_MASK, AW88261_RSQN_DLY_35NS_VALUE); /* set BURST_SSMODE */ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL8_REG, - AW88261_BURST_SSMODE_MASK, AW88261_BURST_SSMODE_FAST_VALUE); + ~AW88261_BURST_SSMODE_MASK, AW88261_BURST_SSMODE_FAST_VALUE); /* set BST_BURST */ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL9_REG, - AW88261_BST_BURST_MASK, AW88261_BST_BURST_30MA_VALUE); + ~AW88261_BST_BURST_MASK, AW88261_BST_BURST_30MA_VALUE); } else { dev_dbg(aw88261->aw_pa->dev, "needn't set reg value"); } From 9ea734e2cc0143d7429ab7dc0b20c85e5836183c Mon Sep 17 00:00:00 2001 From: Justin Suess Date: Tue, 9 Jun 2026 22:25:44 +0200 Subject: [PATCH 1301/2758] bpf: Cancel special fields on map value recycle [ Upstream commit a3a81d247651218e47153f2d2afd7aee236726fd ] Map update and delete paths currently call bpf_obj_free_fields() when a value is being replaced or recycled. That makes field destruction depend on the context of the update/delete operation. For tracing programs this can include NMI context, where referenced kptr destructors, uptr unpinning, and graph root destruction are not generally safe. Introduce bpf_obj_cancel_fields() for the reusable-value path. It only performs NMI-safe cleanup for timer, workqueue, and task_work fields. Fields that need full destruction are left attached to the recycled value and are destroyed by the final cleanup path instead. Switch array and hashtab update/delete/recycle paths to this cancel helper. Keep bpf_obj_free_fields() for final map destruction and for bpf_mem_alloc destructors. Preallocated hashtabs do not have allocator destructors, so teardown continues to walk the normal and extra elements and fully destroy their fields. This deliberately relaxes the eager-free semantics of map update/delete for special fields. Programs that relied on a recycled map slot becoming empty immediately after update/delete were relying on behavior that cannot be implemented safely from every BPF execution context without offloading arbitrary destructors. There is a chance this change breaks programs making assumptions regarding the eager freeing of fields. If so, we can relax semantics to cancellation only when irqs_disabled() is true in the future. However, theoretically, map values that get reused eagerly already have weaker guarantees as parallel users can recreate freed fields before the new element becomes visible again. Fixes: 14a324f6a67e ("bpf: Wire up freeing of referenced kptr") Signed-off-by: Justin Suess Co-developed-by: Kumar Kartikeya Dwivedi Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20260609202548.3571690-3-memxor@gmail.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- include/linux/bpf.h | 1 + kernel/bpf/arraymap.c | 8 +-- kernel/bpf/hashtab.c | 32 +++++---- kernel/bpf/syscall.c | 5 ++ .../selftests/bpf/prog_tests/htab_update.c | 4 +- .../selftests/bpf/prog_tests/linked_list.c | 33 ++++----- .../selftests/bpf/prog_tests/map_kptr.c | 10 +-- .../bpf/prog_tests/refcounted_kptr.c | 8 ++- .../testing/selftests/bpf/progs/htab_update.c | 4 +- .../testing/selftests/bpf/progs/linked_list.c | 71 +++++++++++++++++++ .../selftests/bpf/progs/refcounted_kptr.c | 20 +++++- 11 files changed, 146 insertions(+), 50 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 487f4653d8a66..a2cf24d168802 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -2596,6 +2596,7 @@ bool btf_record_equal(const struct btf_record *rec_a, const struct btf_record *r void bpf_obj_free_timer(const struct btf_record *rec, void *obj); void bpf_obj_free_workqueue(const struct btf_record *rec, void *obj); void bpf_obj_free_task_work(const struct btf_record *rec, void *obj); +void bpf_obj_cancel_fields(struct bpf_map *map, void *obj); void bpf_obj_free_fields(const struct btf_record *rec, void *obj); void __bpf_obj_drop_impl(void *p, const struct btf_record *rec, bool percpu); diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c index dfb2110ab7335..4b68d7a2b90ea 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c @@ -386,7 +386,7 @@ static long array_map_update_elem(struct bpf_map *map, void *key, void *value, if (array->map.map_type == BPF_MAP_TYPE_PERCPU_ARRAY) { val = this_cpu_ptr(array->pptrs[index & array->index_mask]); copy_map_value(map, val, value); - bpf_obj_free_fields(array->map.record, val); + bpf_obj_cancel_fields(map, val); } else { val = array->value + (u64)array->elem_size * (index & array->index_mask); @@ -394,7 +394,7 @@ static long array_map_update_elem(struct bpf_map *map, void *key, void *value, copy_map_value_locked(map, val, value, false); else copy_map_value(map, val, value); - bpf_obj_free_fields(array->map.record, val); + bpf_obj_cancel_fields(map, val); } return 0; } @@ -434,14 +434,14 @@ int bpf_percpu_array_update(struct bpf_map *map, void *key, void *value, cpu = map_flags >> 32; ptr = per_cpu_ptr(pptr, cpu); copy_map_value(map, ptr, value); - bpf_obj_free_fields(array->map.record, ptr); + bpf_obj_cancel_fields(map, ptr); goto unlock; } for_each_possible_cpu(cpu) { ptr = per_cpu_ptr(pptr, cpu); val = (map_flags & BPF_F_ALL_CPUS) ? value : value + size * cpu; copy_map_value(map, ptr, val); - bpf_obj_free_fields(array->map.record, ptr); + bpf_obj_cancel_fields(map, ptr); } unlock: rcu_read_unlock(); diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c index 3dd9b4924ae4f..74c5ced74032b 100644 --- a/kernel/bpf/hashtab.c +++ b/kernel/bpf/hashtab.c @@ -242,6 +242,10 @@ static void htab_free_prealloced_fields(struct bpf_htab *htab) if (IS_ERR_OR_NULL(htab->map.record)) return; + /* + * Preallocated maps do not have a bpf_mem_alloc destructor, so fully + * destroy every element, including the extra elements. + */ if (htab_has_extra_elems(htab)) num_entries += num_possible_cpus(); for (i = 0; i < num_entries; i++) { @@ -834,8 +838,8 @@ static int htab_lru_map_gen_lookup(struct bpf_map *map, return insn - insn_buf; } -static void check_and_free_fields(struct bpf_htab *htab, - struct htab_elem *elem) +static void check_and_cancel_fields(struct bpf_htab *htab, + struct htab_elem *elem) { if (IS_ERR_OR_NULL(htab->map.record)) return; @@ -845,11 +849,11 @@ static void check_and_free_fields(struct bpf_htab *htab, int cpu; for_each_possible_cpu(cpu) - bpf_obj_free_fields(htab->map.record, per_cpu_ptr(pptr, cpu)); + bpf_obj_cancel_fields(&htab->map, per_cpu_ptr(pptr, cpu)); } else { void *map_value = htab_elem_value(elem, htab->map.key_size); - bpf_obj_free_fields(htab->map.record, map_value); + bpf_obj_cancel_fields(&htab->map, map_value); } } @@ -884,7 +888,7 @@ static bool htab_lru_map_delete_node(void *arg, struct bpf_lru_node *node) htab_unlock_bucket(b, flags); if (l == tgt_l) - check_and_free_fields(htab, l); + check_and_cancel_fields(htab, l); return l == tgt_l; } @@ -949,7 +953,7 @@ static int htab_map_get_next_key(struct bpf_map *map, void *key, void *next_key) static void htab_elem_free(struct bpf_htab *htab, struct htab_elem *l) { - check_and_free_fields(htab, l); + check_and_cancel_fields(htab, l); if (htab->map.map_type == BPF_MAP_TYPE_PERCPU_HASH) bpf_mem_cache_free(&htab->pcpu_ma, l->ptr_to_pptr); @@ -1002,7 +1006,7 @@ static void free_htab_elem(struct bpf_htab *htab, struct htab_elem *l) if (htab_is_prealloc(htab)) { bpf_map_dec_elem_count(&htab->map); - check_and_free_fields(htab, l); + check_and_cancel_fields(htab, l); pcpu_freelist_push(&htab->freelist, &l->fnode); } else { dec_elem_count(htab); @@ -1019,7 +1023,7 @@ static void pcpu_copy_value(struct bpf_htab *htab, void __percpu *pptr, /* copy true value_size bytes */ ptr = this_cpu_ptr(pptr); copy_map_value(&htab->map, ptr, value); - bpf_obj_free_fields(htab->map.record, ptr); + bpf_obj_cancel_fields(&htab->map, ptr); } else { u32 size = round_up(htab->map.value_size, 8); void *val; @@ -1029,7 +1033,7 @@ static void pcpu_copy_value(struct bpf_htab *htab, void __percpu *pptr, cpu = map_flags >> 32; ptr = per_cpu_ptr(pptr, cpu); copy_map_value(&htab->map, ptr, value); - bpf_obj_free_fields(htab->map.record, ptr); + bpf_obj_cancel_fields(&htab->map, ptr); return; } @@ -1037,7 +1041,7 @@ static void pcpu_copy_value(struct bpf_htab *htab, void __percpu *pptr, ptr = per_cpu_ptr(pptr, cpu); val = (map_flags & BPF_F_ALL_CPUS) ? value : value + size * cpu; copy_map_value(&htab->map, ptr, val); - bpf_obj_free_fields(htab->map.record, ptr); + bpf_obj_cancel_fields(&htab->map, ptr); } } } @@ -1253,11 +1257,11 @@ static long htab_map_update_elem(struct bpf_map *map, void *key, void *value, if (l_old) { hlist_nulls_del_rcu(&l_old->hash_node); - /* l_old has already been stashed in htab->extra_elems, free - * its special fields before it is available for reuse. + /* l_old has already been stashed in htab->extra_elems, cancel + * its reusable special fields before it is available for reuse. */ if (htab_is_prealloc(htab)) - check_and_free_fields(htab, l_old); + check_and_cancel_fields(htab, l_old); } htab_unlock_bucket(b, flags); if (l_old && !htab_is_prealloc(htab)) @@ -1270,7 +1274,7 @@ static long htab_map_update_elem(struct bpf_map *map, void *key, void *value, static void htab_lru_push_free(struct bpf_htab *htab, struct htab_elem *elem) { - check_and_free_fields(htab, elem); + check_and_cancel_fields(htab, elem); bpf_map_dec_elem_count(&htab->map); bpf_lru_push_free(&htab->lru, &elem->lru_node); } diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index d366797088558..ddf0571f476bf 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -807,6 +807,11 @@ void bpf_obj_free_task_work(const struct btf_record *rec, void *obj) bpf_task_work_cancel_and_free(obj + rec->task_work_off); } +void bpf_obj_cancel_fields(struct bpf_map *map, void *obj) +{ + bpf_map_free_internal_structs(map, obj); +} + void bpf_obj_free_fields(const struct btf_record *rec, void *obj) { const struct btf_field *fields; diff --git a/tools/testing/selftests/bpf/prog_tests/htab_update.c b/tools/testing/selftests/bpf/prog_tests/htab_update.c index ea1a6766fbe98..0a28d43469240 100644 --- a/tools/testing/selftests/bpf/prog_tests/htab_update.c +++ b/tools/testing/selftests/bpf/prog_tests/htab_update.c @@ -23,7 +23,7 @@ static void test_reenter_update(void) if (!ASSERT_OK_PTR(skel, "htab_update__open")) return; - bpf_program__set_autoload(skel->progs.bpf_obj_free_fields, true); + bpf_program__set_autoload(skel->progs.bpf_obj_cancel_fields, true); err = htab_update__load(skel); if (!ASSERT_TRUE(!err, "htab_update__load") || err) goto out; @@ -50,7 +50,7 @@ static void test_reenter_update(void) /* * Second update: replace existing element with same key and trigger * the reentrancy of bpf_map_update_elem(). - * check_and_free_fields() calls bpf_obj_free_fields() on the old + * check_and_cancel_fields() calls bpf_obj_cancel_fields() on the old * value, which is where fentry program runs and performs a nested * bpf_map_update_elem(), triggering -EDEADLK. */ diff --git a/tools/testing/selftests/bpf/prog_tests/linked_list.c b/tools/testing/selftests/bpf/prog_tests/linked_list.c index 6f25b5f39a79c..62d974a1987ff 100644 --- a/tools/testing/selftests/bpf/prog_tests/linked_list.c +++ b/tools/testing/selftests/bpf/prog_tests/linked_list.c @@ -131,13 +131,14 @@ static void test_linked_list_fail_prog(const char *prog_name, const char *err_ms linked_list_fail__destroy(skel); } -static void clear_fields(struct bpf_map *map) +static void clear_fields(struct bpf_program *prog) { - char buf[24]; - int key = 0; + LIBBPF_OPTS(bpf_test_run_opts, opts); + int ret; - memset(buf, 0xff, sizeof(buf)); - ASSERT_OK(bpf_map__update_elem(map, &key, sizeof(key), buf, sizeof(buf), 0), "check_and_free_fields"); + ret = bpf_prog_test_run_opts(bpf_program__fd(prog), &opts); + ASSERT_OK(ret, "clear_fields"); + ASSERT_OK(opts.retval, "clear_fields retval"); } enum { @@ -170,31 +171,31 @@ static void test_linked_list_success(int mode, bool leave_in_map) ASSERT_OK(ret, "map_list_push_pop"); ASSERT_OK(opts.retval, "map_list_push_pop retval"); if (!leave_in_map) - clear_fields(skel->maps.array_map); + clear_fields(skel->progs.clear_map_list); ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.inner_map_list_push_pop), &opts); ASSERT_OK(ret, "inner_map_list_push_pop"); ASSERT_OK(opts.retval, "inner_map_list_push_pop retval"); if (!leave_in_map) - clear_fields(skel->maps.inner_map); + clear_fields(skel->progs.clear_inner_map_list); ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.global_list_push_pop), &opts); ASSERT_OK(ret, "global_list_push_pop"); ASSERT_OK(opts.retval, "global_list_push_pop retval"); if (!leave_in_map) - clear_fields(skel->maps.bss_A); + clear_fields(skel->progs.clear_global_list); ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.global_list_push_pop_nested), &opts); ASSERT_OK(ret, "global_list_push_pop_nested"); ASSERT_OK(opts.retval, "global_list_push_pop_nested retval"); if (!leave_in_map) - clear_fields(skel->maps.bss_A); + clear_fields(skel->progs.clear_global_nested_list); ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.global_list_array_push_pop), &opts); ASSERT_OK(ret, "global_list_array_push_pop"); ASSERT_OK(opts.retval, "global_list_array_push_pop retval"); if (!leave_in_map) - clear_fields(skel->maps.bss_A); + clear_fields(skel->progs.clear_global_array_list); if (mode == PUSH_POP) goto end; @@ -204,19 +205,19 @@ static void test_linked_list_success(int mode, bool leave_in_map) ASSERT_OK(ret, "map_list_push_pop_multiple"); ASSERT_OK(opts.retval, "map_list_push_pop_multiple retval"); if (!leave_in_map) - clear_fields(skel->maps.array_map); + clear_fields(skel->progs.clear_map_list); ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.inner_map_list_push_pop_multiple), &opts); ASSERT_OK(ret, "inner_map_list_push_pop_multiple"); ASSERT_OK(opts.retval, "inner_map_list_push_pop_multiple retval"); if (!leave_in_map) - clear_fields(skel->maps.inner_map); + clear_fields(skel->progs.clear_inner_map_list); ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.global_list_push_pop_multiple), &opts); ASSERT_OK(ret, "global_list_push_pop_multiple"); ASSERT_OK(opts.retval, "global_list_push_pop_multiple retval"); if (!leave_in_map) - clear_fields(skel->maps.bss_A); + clear_fields(skel->progs.clear_global_list); if (mode == PUSH_POP_MULT) goto end; @@ -226,19 +227,19 @@ static void test_linked_list_success(int mode, bool leave_in_map) ASSERT_OK(ret, "map_list_in_list"); ASSERT_OK(opts.retval, "map_list_in_list retval"); if (!leave_in_map) - clear_fields(skel->maps.array_map); + clear_fields(skel->progs.clear_map_list); ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.inner_map_list_in_list), &opts); ASSERT_OK(ret, "inner_map_list_in_list"); ASSERT_OK(opts.retval, "inner_map_list_in_list retval"); if (!leave_in_map) - clear_fields(skel->maps.inner_map); + clear_fields(skel->progs.clear_inner_map_list); ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.global_list_in_list), &opts); ASSERT_OK(ret, "global_list_in_list"); ASSERT_OK(opts.retval, "global_list_in_list retval"); if (!leave_in_map) - clear_fields(skel->maps.bss_A); + clear_fields(skel->progs.clear_global_list); end: linked_list__destroy(skel); } diff --git a/tools/testing/selftests/bpf/prog_tests/map_kptr.c b/tools/testing/selftests/bpf/prog_tests/map_kptr.c index 03b46f17cf537..ec6f2f2e83089 100644 --- a/tools/testing/selftests/bpf/prog_tests/map_kptr.c +++ b/tools/testing/selftests/bpf/prog_tests/map_kptr.c @@ -51,7 +51,6 @@ static void test_map_kptr_success(bool test_run) ret = bpf_map__update_elem(skel->maps.array_map, &key, sizeof(key), buf, sizeof(buf), 0); ASSERT_OK(ret, "array_map update"); - skel->data->ref--; ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_map_kptr_ref3), &opts); ASSERT_OK(ret, "test_map_kptr_ref3 refcount"); ASSERT_OK(opts.retval, "test_map_kptr_ref3 retval"); @@ -59,49 +58,42 @@ static void test_map_kptr_success(bool test_run) ret = bpf_map__update_elem(skel->maps.pcpu_array_map, &key, sizeof(key), pbuf, cpu * sizeof(buf), 0); ASSERT_OK(ret, "pcpu_array_map update"); - skel->data->ref--; ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_map_kptr_ref3), &opts); ASSERT_OK(ret, "test_map_kptr_ref3 refcount"); ASSERT_OK(opts.retval, "test_map_kptr_ref3 retval"); ret = bpf_map__delete_elem(skel->maps.hash_map, &key, sizeof(key), 0); ASSERT_OK(ret, "hash_map delete"); - skel->data->ref--; ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_map_kptr_ref3), &opts); ASSERT_OK(ret, "test_map_kptr_ref3 refcount"); ASSERT_OK(opts.retval, "test_map_kptr_ref3 retval"); ret = bpf_map__delete_elem(skel->maps.pcpu_hash_map, &key, sizeof(key), 0); ASSERT_OK(ret, "pcpu_hash_map delete"); - skel->data->ref--; ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_map_kptr_ref3), &opts); ASSERT_OK(ret, "test_map_kptr_ref3 refcount"); ASSERT_OK(opts.retval, "test_map_kptr_ref3 retval"); ret = bpf_map__delete_elem(skel->maps.hash_malloc_map, &key, sizeof(key), 0); ASSERT_OK(ret, "hash_malloc_map delete"); - skel->data->ref--; ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_map_kptr_ref3), &opts); ASSERT_OK(ret, "test_map_kptr_ref3 refcount"); ASSERT_OK(opts.retval, "test_map_kptr_ref3 retval"); ret = bpf_map__delete_elem(skel->maps.pcpu_hash_malloc_map, &key, sizeof(key), 0); ASSERT_OK(ret, "pcpu_hash_malloc_map delete"); - skel->data->ref--; ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_map_kptr_ref3), &opts); ASSERT_OK(ret, "test_map_kptr_ref3 refcount"); ASSERT_OK(opts.retval, "test_map_kptr_ref3 retval"); ret = bpf_map__delete_elem(skel->maps.lru_hash_map, &key, sizeof(key), 0); ASSERT_OK(ret, "lru_hash_map delete"); - skel->data->ref--; ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_map_kptr_ref3), &opts); ASSERT_OK(ret, "test_map_kptr_ref3 refcount"); ASSERT_OK(opts.retval, "test_map_kptr_ref3 retval"); ret = bpf_map__delete_elem(skel->maps.lru_pcpu_hash_map, &key, sizeof(key), 0); ASSERT_OK(ret, "lru_pcpu_hash_map delete"); - skel->data->ref--; ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_map_kptr_ref3), &opts); ASSERT_OK(ret, "test_map_kptr_ref3 refcount"); ASSERT_OK(opts.retval, "test_map_kptr_ref3 retval"); @@ -175,7 +167,7 @@ void serial_test_map_kptr(void) ASSERT_OK(kern_sync_rcu(), "sync rcu"); wait_for_map_release(); - /* Observe refcount dropping to 1 on synchronous delete elem */ + /* Observe refcount dropping to 1 on map release. */ test_map_kptr_success(true); } diff --git a/tools/testing/selftests/bpf/prog_tests/refcounted_kptr.c b/tools/testing/selftests/bpf/prog_tests/refcounted_kptr.c index d2c0542716a84..1737eba343234 100644 --- a/tools/testing/selftests/bpf/prog_tests/refcounted_kptr.c +++ b/tools/testing/selftests/bpf/prog_tests/refcounted_kptr.c @@ -57,6 +57,7 @@ void test_percpu_hash_refcounted_kptr_refcount_leak(void) .data_size_in = sizeof(pkt_v4), .repeat = 1, ); + LIBBPF_OPTS(bpf_test_run_opts, syscall_opts); cpu_nr = libbpf_num_possible_cpus(); if (!ASSERT_GT(cpu_nr, 0, "libbpf_num_possible_cpus")) @@ -87,8 +88,11 @@ void test_percpu_hash_refcounted_kptr_refcount_leak(void) if (!ASSERT_EQ(opts.retval, 2, "opts.retval")) goto out; - err = bpf_map__update_elem(map, &key, sizeof(key), values, values_sz, 0); - if (!ASSERT_OK(err, "bpf_map__update_elem")) + fd = bpf_program__fd(skel->progs.clear_percpu_hash_kptr); + err = bpf_prog_test_run_opts(fd, &syscall_opts); + if (!ASSERT_OK(err, "bpf_prog_test_run_opts")) + goto out; + if (!ASSERT_EQ(syscall_opts.retval, 1, "syscall_opts.retval")) goto out; fd = bpf_program__fd(skel->progs.check_percpu_hash_refcount); diff --git a/tools/testing/selftests/bpf/progs/htab_update.c b/tools/testing/selftests/bpf/progs/htab_update.c index 195d3b2fba00c..62c1b1325ec27 100644 --- a/tools/testing/selftests/bpf/progs/htab_update.c +++ b/tools/testing/selftests/bpf/progs/htab_update.c @@ -22,8 +22,8 @@ struct { int pid = 0; int update_err = 0; -SEC("?fentry/bpf_obj_free_fields") -int bpf_obj_free_fields(void *ctx) +SEC("?fentry/bpf_obj_cancel_fields") +int bpf_obj_cancel_fields(void *ctx) { __u32 key = 0; struct val value = { .payload = 1 }; diff --git a/tools/testing/selftests/bpf/progs/linked_list.c b/tools/testing/selftests/bpf/progs/linked_list.c index 421f40835acd7..fa97faa5358bc 100644 --- a/tools/testing/selftests/bpf/progs/linked_list.c +++ b/tools/testing/selftests/bpf/progs/linked_list.c @@ -290,6 +290,77 @@ int test_list_in_list(struct bpf_spin_lock *lock, struct bpf_list_head *head) return list_in_list(lock, head, true); } +#define MAX_LIST_CLEAR_NODES 256 + +static __always_inline +int clear_list(struct bpf_spin_lock *lock, struct bpf_list_head *head) +{ + struct bpf_list_node *n; + int i; + + for (i = 0; i < MAX_LIST_CLEAR_NODES; i++) { + bpf_spin_lock(lock); + n = bpf_list_pop_front(head); + bpf_spin_unlock(lock); + if (!n) + return 0; + bpf_obj_drop(container_of(n, struct foo, node2)); + } + return 1; +} + +SEC("syscall") +int clear_map_list(void *ctx) +{ + struct map_value *v; + + v = bpf_map_lookup_elem(&array_map, &(int){0}); + if (!v) + return 1; + return clear_list(&v->lock, &v->head); +} + +SEC("syscall") +int clear_inner_map_list(void *ctx) +{ + struct map_value *v; + void *map; + + map = bpf_map_lookup_elem(&map_of_maps, &(int){0}); + if (!map) + return 1; + v = bpf_map_lookup_elem(map, &(int){0}); + if (!v) + return 1; + return clear_list(&v->lock, &v->head); +} + +SEC("syscall") +int clear_global_list(void *ctx) +{ + return clear_list(&glock, &ghead); +} + +SEC("syscall") +int clear_global_nested_list(void *ctx) +{ + return clear_list(&ghead_nested.inner.lock, &ghead_nested.inner.head); +} + +SEC("syscall") +int clear_global_array_list(void *ctx) +{ + int ret; + + ret = clear_list(&glock_c, &ghead_array[0]); + if (ret) + return ret; + ret = clear_list(&glock_c, &ghead_array[1]); + if (ret) + return ret; + return clear_list(&glock_c, &ghead_array_one[0]); +} + SEC("tc") int map_list_push_pop(void *ctx) { diff --git a/tools/testing/selftests/bpf/progs/refcounted_kptr.c b/tools/testing/selftests/bpf/progs/refcounted_kptr.c index c847398837ccd..9837dccfc141c 100644 --- a/tools/testing/selftests/bpf/progs/refcounted_kptr.c +++ b/tools/testing/selftests/bpf/progs/refcounted_kptr.c @@ -615,13 +615,31 @@ int percpu_hash_refcount_leak(void *ctx) struct map_value *v; int key = 0; - v = bpf_map_lookup_elem(&percpu_hash, &key); + v = bpf_map_lookup_percpu_elem(&percpu_hash, &key, 0); if (!v) return 0; return __insert_in_list(&head, &lock, &v->node); } +SEC("syscall") +int clear_percpu_hash_kptr(void *ctx) +{ + struct node_data *n; + struct map_value *v; + int key = 0; + + v = bpf_map_lookup_percpu_elem(&percpu_hash, &key, 0); + if (!v) + return 0; + + n = bpf_kptr_xchg(&v->node, NULL); + if (!n) + return 0; + bpf_obj_drop(n); + return probe_read_refcount(); +} + SEC("tc") int check_percpu_hash_refcount(void *ctx) { From 72202c72c3fca6d91fb189b650838f0dad7fc5a6 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Thu, 14 May 2026 13:14:17 +0200 Subject: [PATCH 1302/2758] clocksource: move NXP timer selection to drivers/clocksource [ Upstream commit a9ac745bc320cbdc2ed3c851eb78f91f22ff975b ] The Kconfig logic for selecting the scheduler clocksource on NXP Vybrid (VF610) uses a `choice` block restricted to 32-bit ARM. This prevents 64-bit architectures, such as the NXP S32 family, from enabling the NXP Periodic Interrupt Timer (PIT) driver (CONFIG_NXP_PIT_TIMER). Relocate the NXP clocksource selection from arch/arm/mach-imx/Kconfig to drivers/clocksource/Kconfig. This allows the configuration to be shared across different architectures. Update the selection to include support for ARCH_S32 and add a "None" option restricted to ARCH_S32, since Vybrid lacks the ARM Architected Timer. The Vybrid Global Timer option is restricted to ARCH_MULTI_V7 SOC_VF610 platforms to prevent it from being visible on Cortex-M4 builds, which lack the ARM Global Timer hardware. Fixes: bee33f22d7c3 ("clocksource/drivers/nxp-pit: Add NXP Automotive s32g2 / s32g3 support") Signed-off-by: Enric Balletbo i Serra Signed-off-by: Daniel Lezcano Reviewed-by: Frank Li Link: https://patch.msgid.link/20260514-fix-nxp-timer-v3-1-a3e68fdb505e@redhat.com Signed-off-by: Sasha Levin --- arch/arm/mach-imx/Kconfig | 21 --------------------- drivers/clocksource/Kconfig | 31 +++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index 6ea1bd55acf8d..a361840d7a047 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -227,27 +227,6 @@ config SOC_VF610 help This enables support for Freescale Vybrid VF610 processor. -choice - prompt "Clocksource for scheduler clock" - depends on SOC_VF610 - default VF_USE_ARM_GLOBAL_TIMER - - config VF_USE_ARM_GLOBAL_TIMER - bool "Use ARM Global Timer" - depends on ARCH_MULTI_V7 - select ARM_GLOBAL_TIMER - select CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK - help - Use the ARM Global Timer as clocksource - - config VF_USE_PIT_TIMER - bool "Use PIT timer" - select NXP_PIT_TIMER - help - Use SoC Periodic Interrupt Timer (PIT) as clocksource - -endchoice - endif endif diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index d1a33a231a44c..d9c76dd443f8a 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -793,4 +793,35 @@ config RTK_SYSTIMER this option only when building for a Realtek platform or for compilation testing. +choice + prompt "NXP clocksource for scheduler clock" + depends on SOC_VF610 || ARCH_S32 + # Default to Global Timer for Vybrid (32-bit) + default VF_USE_ARM_GLOBAL_TIMER if SOC_VF610 + # Default to None for S32 (64-bit) + default VF_TIMER_NONE if ARCH_S32 + + config VF_USE_ARM_GLOBAL_TIMER + bool "Use NXP Vybrid Global Timer" + depends on ARCH_MULTI_V7 && SOC_VF610 + select ARM_GLOBAL_TIMER + select CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK + help + Use the NXP Vybrid Global Timer as clocksource. + + config VF_USE_PIT_TIMER + bool "Use NXP PIT timer" + select NXP_PIT_TIMER + help + Use NXP Periodic Interrupt Timer (PIT) as clocksource. + + config VF_TIMER_NONE + bool "None (Use standard Arch Timer)" + depends on ARCH_S32 + help + Do not use any specific NXP timer driver. Use the standard + ARM Architected Timer instead. + +endchoice + endmenu From 79e12c891940b0c4c75881b7fd82a8cbb8ac97be Mon Sep 17 00:00:00 2001 From: Qihang Tang Date: Fri, 8 May 2026 17:46:59 +0800 Subject: [PATCH 1303/2758] vduse: hold vduse_lock across IDR lookup in open path [ Upstream commit e440e077748939839d9f76e24383b76b785f80ce ] vduse_dev_open() looks up struct vduse_dev through the IDR and then acquires dev->lock only after vduse_lock has been dropped. This leaves a window where a concurrent VDUSE_DESTROY_DEV can remove the same object from the IDR and free it before the open path locks the device, leading to a use-after-free. Close this race by keeping vduse_lock held until dev->lock has been acquired in the open path, matching the lock ordering already used by the destroy path. Fixes: c8a6153b6c59 ("vduse: Introduce VDUSE - vDPA Device in Userspace") Signed-off-by: Qihang Tang Signed-off-by: Michael S. Tsirkin Message-ID: <20260508094659.94647-1-q.h.hack.winter@gmail.com> Signed-off-by: Sasha Levin --- drivers/vdpa/vdpa_user/vduse_dev.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/drivers/vdpa/vdpa_user/vduse_dev.c b/drivers/vdpa/vdpa_user/vduse_dev.c index 6202f6902fcdf..d5c34260ed68d 100644 --- a/drivers/vdpa/vdpa_user/vduse_dev.c +++ b/drivers/vdpa/vdpa_user/vduse_dev.c @@ -1637,26 +1637,18 @@ static int vduse_dev_release(struct inode *inode, struct file *file) return 0; } -static struct vduse_dev *vduse_dev_get_from_minor(int minor) +static int vduse_dev_open(struct inode *inode, struct file *file) { + int ret = -EBUSY; struct vduse_dev *dev; mutex_lock(&vduse_lock); - dev = idr_find(&vduse_idr, minor); - mutex_unlock(&vduse_lock); - - return dev; -} - -static int vduse_dev_open(struct inode *inode, struct file *file) -{ - int ret; - struct vduse_dev *dev = vduse_dev_get_from_minor(iminor(inode)); - - if (!dev) + dev = idr_find(&vduse_idr, iminor(inode)); + if (!dev) { + mutex_unlock(&vduse_lock); return -ENODEV; + } - ret = -EBUSY; mutex_lock(&dev->lock); if (dev->connected) goto unlock; @@ -1666,6 +1658,7 @@ static int vduse_dev_open(struct inode *inode, struct file *file) file->private_data = dev; unlock: mutex_unlock(&dev->lock); + mutex_unlock(&vduse_lock); return ret; } From 32ac9097aa2463fcfc12f61cc4a9ebc3579cba7d Mon Sep 17 00:00:00 2001 From: Qihang Tang Date: Fri, 8 May 2026 15:58:21 +0800 Subject: [PATCH 1304/2758] vhost/vdpa: validate virtqueue index in mmap and fault paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 929e4f044621c8cc30b612fb74e1410bef09e41b ] vhost_vdpa_mmap() and vhost_vdpa_fault() use vma->vm_pgoff as a virtqueue index for get_vq_notification(), but they do not validate that the index is smaller than v->nvqs. The ioctl path already performs both a bounds check and array_index_nospec(), but the mmap/fault path only checks that the index fits in u16. This allows an out-of-range queue index to reach driver-specific get_vq_notification() callbacks. Fix this by extracting a unified vhost_vdpa_get_vq_notification() helper that validates the queue index against v->nvqs and applies array_index_nospec() before calling the driver callback. Both the mmap and fault paths use this helper, and the bounds checking is consolidated into a single location. From source inspection, the most defensible impact is out-of-bounds access in the callback path, potentially leading to invalid PFN remaps and crash/DoS. Fixes: ddd89d0a059d ("vhost_vdpa: support doorbell mapping via mmap") Acked-by: Eugenio Pérez Acked-by: Michael S. Tsirkin Signed-off-by: Qihang Tang Signed-off-by: Michael S. Tsirkin Message-ID: <20260508075821.92656-1-q.h.hack.winter@gmail.com> Signed-off-by: Sasha Levin --- drivers/vhost/vdpa.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c index 692564b1bcbbe..ac55275fa0d0a 100644 --- a/drivers/vhost/vdpa.c +++ b/drivers/vhost/vdpa.c @@ -1482,16 +1482,32 @@ static int vhost_vdpa_release(struct inode *inode, struct file *filep) } #ifdef CONFIG_MMU -static vm_fault_t vhost_vdpa_fault(struct vm_fault *vmf) +static int +vhost_vdpa_get_vq_notification(struct vhost_vdpa *v, unsigned long index, + struct vdpa_notification_area *notify) { - struct vhost_vdpa *v = vmf->vma->vm_file->private_data; struct vdpa_device *vdpa = v->vdpa; const struct vdpa_config_ops *ops = vdpa->config; + + if (index > 65535 || index >= v->nvqs) + return -EINVAL; + + index = array_index_nospec(index, v->nvqs); + + *notify = ops->get_vq_notification(vdpa, index); + + return 0; +} + +static vm_fault_t vhost_vdpa_fault(struct vm_fault *vmf) +{ + struct vhost_vdpa *v = vmf->vma->vm_file->private_data; struct vdpa_notification_area notify; struct vm_area_struct *vma = vmf->vma; - u16 index = vma->vm_pgoff; + unsigned long index = vma->vm_pgoff; - notify = ops->get_vq_notification(vdpa, index); + if (vhost_vdpa_get_vq_notification(v, index, ¬ify)) + return VM_FAULT_SIGBUS; return vmf_insert_pfn(vma, vmf->address & PAGE_MASK, PFN_DOWN(notify.addr)); } @@ -1514,8 +1530,6 @@ static int vhost_vdpa_mmap(struct file *file, struct vm_area_struct *vma) return -EINVAL; if (vma->vm_flags & VM_READ) return -EINVAL; - if (index > 65535) - return -EINVAL; if (!ops->get_vq_notification) return -ENOTSUPP; @@ -1523,7 +1537,8 @@ static int vhost_vdpa_mmap(struct file *file, struct vm_area_struct *vma) * support the doorbell which sits on the page boundary and * does not share the page with other registers. */ - notify = ops->get_vq_notification(vdpa, index); + if (vhost_vdpa_get_vq_notification(v, index, ¬ify)) + return -EINVAL; if (notify.addr & (PAGE_SIZE - 1)) return -EINVAL; if (vma->vm_end - vma->vm_start != notify.size) From aebebd1e9d70b650fc9e877082e0134edcf511da Mon Sep 17 00:00:00 2001 From: Jia Jia Date: Thu, 7 May 2026 20:08:01 +0800 Subject: [PATCH 1305/2758] virtio: rtc: tear down old virtqueues before restore [ Upstream commit 548d2208455f14e6121404c6e30e997bfe0cd264 ] virtio_device_restore() resets the device and restores the negotiated features before calling ->restore(). viortc_freeze() intentionally leaves the existing virtqueues in place so the alarm queue can still wake the system, but viortc_restore() immediately calls viortc_init_vqs() without first deleting those old queues. If virtqueue reinitialization fails on virtio-pci, the transport error path can run vp_del_vqs() against a newly allocated vp_dev->vqs array while vdev->vqs still contains the old virtqueues. vp_del_vqs() then looks up queue state through the new array and can dereference a NULL info pointer in vp_del_vq(), crashing the guest kernel during restore. This can also happen during a non-faulty reinitialization, when one of the vp_find_vqs_msix() attempts is unsuccessful before a later attempt would succeed. Delete the stale virtqueues before rebuilding them. If restore fails before virtio_device_ready(), reuse the remove path to stop the device. Once the device is ready, return errors directly instead of deleting the virtqueues again. Fixes: 0623c7592768 ("virtio_rtc: Add module and driver core") Signed-off-by: Jia Jia Reviewed-by: Peter Hilber Signed-off-by: Michael S. Tsirkin Message-ID: <20260507120801.3677552-1-physicalmtea@gmail.com> Signed-off-by: Sasha Levin --- drivers/virtio/virtio_rtc_driver.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/drivers/virtio/virtio_rtc_driver.c b/drivers/virtio/virtio_rtc_driver.c index a57d5e06e19d2..4419735b0f0dc 100644 --- a/drivers/virtio/virtio_rtc_driver.c +++ b/drivers/virtio/virtio_rtc_driver.c @@ -1257,6 +1257,15 @@ static int viortc_init_vqs(struct viortc_dev *viortc) return 0; } +static void __viortc_remove(struct viortc_dev *viortc) +{ + struct virtio_device *vdev = viortc->vdev; + + viortc_clocks_deinit(viortc); + virtio_reset_device(vdev); + vdev->config->del_vqs(vdev); +} + /** * viortc_probe() - probe a virtio_rtc virtio device * @vdev: virtio device @@ -1282,7 +1291,7 @@ static int viortc_probe(struct virtio_device *vdev) ret = viortc_init_vqs(viortc); if (ret) - return ret; + goto err_reset_vdev; virtio_device_ready(vdev); @@ -1329,10 +1338,7 @@ static void viortc_remove(struct virtio_device *vdev) { struct viortc_dev *viortc = vdev->priv; - viortc_clocks_deinit(viortc); - - virtio_reset_device(vdev); - vdev->config->del_vqs(vdev); + __viortc_remove(viortc); } static int viortc_freeze(struct virtio_device *dev) @@ -1353,9 +1359,11 @@ static int viortc_restore(struct virtio_device *dev) bool notify = false; int ret; + dev->config->del_vqs(dev); + ret = viortc_init_vqs(viortc); if (ret) - return ret; + goto err_remove; alarm_viortc_vq = &viortc->vqs[VIORTC_ALARMQ]; alarm_vq = alarm_viortc_vq->vq; @@ -1364,7 +1372,7 @@ static int viortc_restore(struct virtio_device *dev) ret = viortc_populate_vq(viortc, alarm_viortc_vq, VIORTC_ALARMQ_BUF_CAP, false); if (ret) - return ret; + goto err_remove; notify = virtqueue_kick_prepare(alarm_vq); } @@ -1372,8 +1380,12 @@ static int viortc_restore(struct virtio_device *dev) virtio_device_ready(dev); if (notify && !virtqueue_notify(alarm_vq)) - ret = -EIO; + return -EIO; + + return 0; +err_remove: + __viortc_remove(viortc); return ret; } From d308261f2fe0b462cf7427bcb4521ca3fe9a92f6 Mon Sep 17 00:00:00 2001 From: Filip Hejsek Date: Mon, 23 Feb 2026 18:37:02 +0100 Subject: [PATCH 1306/2758] virtio_console: read size from config space during device init [ Upstream commit b3592a32b34f37874dc94aa1a0d15c4334ed86ca ] Previously, the size was only read upon receiving the config interrupt. This interrupt is sent when the size changes. However, we also need to read the initial size. Also make sure to only read the size from config if F_SIZE is enabled. Fixes: 9778829cffd4 ("virtio: console: Store each console's size in the console structure") Signed-off-by: Filip Hejsek Signed-off-by: Michael S. Tsirkin Message-ID: <20260223-virtio-console-fix-v1-1-0cf08303b428@gmail.com> Signed-off-by: Sasha Levin --- drivers/char/virtio_console.c | 52 +++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 9a33217c68d9d..198b973141680 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1771,32 +1771,40 @@ static void config_intr(struct virtio_device *vdev) schedule_work(&portdev->config_work); } -static void config_work_handler(struct work_struct *work) +static void update_size_from_config(struct ports_device *portdev) { - struct ports_device *portdev; + struct virtio_device *vdev; + struct port *port; + u16 rows, cols; - portdev = container_of(work, struct ports_device, config_work); - if (!use_multiport(portdev)) { - struct virtio_device *vdev; - struct port *port; - u16 rows, cols; + vdev = portdev->vdev; - vdev = portdev->vdev; - virtio_cread(vdev, struct virtio_console_config, cols, &cols); - virtio_cread(vdev, struct virtio_console_config, rows, &rows); + /* + * We'll use this way of resizing only for legacy support. + * For multiport devices, use control messages to indicate + * console size changes so that it can be done per-port. + * + * Don't test F_SIZE at all if we're rproc: not a valid feature. + */ + if (is_rproc_serial(vdev) || + use_multiport(portdev) || + !virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE)) + return; - port = find_port_by_id(portdev, 0); - set_console_size(port, rows, cols); + virtio_cread(vdev, struct virtio_console_config, cols, &cols); + virtio_cread(vdev, struct virtio_console_config, rows, &rows); - /* - * We'll use this way of resizing only for legacy - * support. For newer userspace - * (VIRTIO_CONSOLE_F_MULTPORT+), use control messages - * to indicate console size changes so that it can be - * done per-port. - */ - resize_console(port); - } + port = find_port_by_id(portdev, 0); + set_console_size(port, rows, cols); + resize_console(port); +} + +static void config_work_handler(struct work_struct *work) +{ + struct ports_device *portdev; + + portdev = container_of(work, struct ports_device, config_work); + update_size_from_config(portdev); } static int init_vqs(struct ports_device *portdev) @@ -2052,6 +2060,8 @@ static int virtcons_probe(struct virtio_device *vdev) __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID, VIRTIO_CONSOLE_DEVICE_READY, 1); + update_size_from_config(portdev); + return 0; free_chrdev: From 911fc274a65185ac994718ce2cc90edaec1e479a Mon Sep 17 00:00:00 2001 From: Zhang Tianci Date: Thu, 26 Feb 2026 19:55:49 +0800 Subject: [PATCH 1307/2758] vduse: Requeue failed read to send_list head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 373ec43ded742b2f3aecf14731ffe1a57f438f38 ] When copy_to_iter() fails in vduse_dev_read_iter(), put the message back at the head of send_list to preserve FIFO ordering and retry the oldest pending request first. Fixes: c8a6153b6c59 ("vduse: Introduce VDUSE - vDPA Device in Userspace") Reported-by: Michael S. Tsirkin Suggested-by: Xie Yongji Signed-off-by: Zhang Tianci Reviewed-by: Xie Yongji Acked-by: Jason Wang Acked-by: Eugenio Pérez Acked-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Message-ID: <20260226115550.1814-2-zhangtianci.1997@bytedance.com> Signed-off-by: Sasha Levin --- drivers/vdpa/vdpa_user/vduse_dev.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/vdpa/vdpa_user/vduse_dev.c b/drivers/vdpa/vdpa_user/vduse_dev.c index d5c34260ed68d..a479fef535ac6 100644 --- a/drivers/vdpa/vdpa_user/vduse_dev.c +++ b/drivers/vdpa/vdpa_user/vduse_dev.c @@ -221,6 +221,12 @@ static void vduse_enqueue_msg(struct list_head *head, list_add_tail(&msg->list, head); } +static void vduse_enqueue_msg_head(struct list_head *head, + struct vduse_dev_msg *msg) +{ + list_add(&msg->list, head); +} + static void vduse_dev_broken(struct vduse_dev *dev) { struct vduse_dev_msg *msg, *tmp; @@ -387,7 +393,7 @@ static ssize_t vduse_dev_read_iter(struct kiocb *iocb, struct iov_iter *to) spin_lock(&dev->msg_lock); if (ret != size) { ret = -EFAULT; - vduse_enqueue_msg(&dev->send_list, msg); + vduse_enqueue_msg_head(&dev->send_list, msg); goto unlock; } vduse_enqueue_msg(&dev->recv_list, msg); From c069437924663539a93a1e5afe90838d9ccee284 Mon Sep 17 00:00:00 2001 From: Qing Ming Date: Mon, 1 Jun 2026 18:43:00 +0800 Subject: [PATCH 1308/2758] vhost/net: complete zerocopy ubufs only once [ Upstream commit 8f6898fe80794f2d7c3d38c1158c806e4074a1c4 ] vhost-net initializes one ubuf_info per outstanding zerocopy TX descriptor and hands it to the backend socket. The networking stack may then clone a zerocopy skb before all skb references are released. For example, batman-adv fragmentation reaches skb_split(), which calls skb_zerocopy_clone() and increments the same ubuf_info refcount. vhost_zerocopy_complete() currently treats every ubuf callback as a completed vhost descriptor. It dereferences ubuf->ctx, writes the descriptor completion state, and drops the vhost_net_ubuf_ref even when the callback only releases a cloned skb reference. A backend reset can therefore wait for and free the vhost_net_ubuf_ref while another cloned skb still carries the same ubuf_info. A later completion then dereferences the freed ubufs pointer. KASAN reports the stale completion as: BUG: KASAN: slab-use-after-free in vhost_zerocopy_complete+0x1d7/0x1f0 BUG: KASAN: slab-use-after-free in vhost_zerocopy_complete+0x101/0x1f0 vhost_zerocopy_complete skb_copy_ubufs __dev_forward_skb2 veth_xmit The freed object was allocated from vhost_net_ioctl() while setting the backend and freed through kfree_rcu()/kvfree_rcu_bulk after backend removal, while delayed skb completion still reached vhost_zerocopy_complete(). Honor the generic ubuf_info refcount before touching vhost state, and run the vhost descriptor completion only for the final ubuf reference. This matches the msg_zerocopy_complete() ownership rule for cloned zerocopy skbs. Fixes: bab632d69ee4 ("vhost: vhost TX zero-copy support") Signed-off-by: Qing Ming Signed-off-by: Michael S. Tsirkin Message-ID: <20260601104300.197210-1-a0yami@mailbox.org> Signed-off-by: Sasha Levin --- drivers/vhost/net.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index c6536cad9c4f9..b9af63fb63060 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -390,13 +390,20 @@ static void vhost_zerocopy_signal_used(struct vhost_net *net, static void vhost_zerocopy_complete(struct sk_buff *skb, struct ubuf_info *ubuf_base, bool success) { - struct ubuf_info_msgzc *ubuf = uarg_to_msgzc(ubuf_base); - struct vhost_net_ubuf_ref *ubufs = ubuf->ctx; - struct vhost_virtqueue *vq = ubufs->vq; + struct ubuf_info_msgzc *ubuf; + struct vhost_net_ubuf_ref *ubufs; + struct vhost_virtqueue *vq; int cnt; - rcu_read_lock_bh(); + /* Only the final cloned skb reference completes the vhost descriptor. */ + if (!refcount_dec_and_test(&ubuf_base->refcnt)) + return; + + ubuf = uarg_to_msgzc(ubuf_base); + ubufs = ubuf->ctx; + vq = ubufs->vq; + rcu_read_lock_bh(); /* set len to mark this desc buffers done DMA */ vq->heads[ubuf->desc].len = success ? VHOST_DMA_DONE_LEN : VHOST_DMA_FAILED_LEN; From 068bdabc39a35d367af4a5755eff883bde7b5ed6 Mon Sep 17 00:00:00 2001 From: longlong yan Date: Fri, 5 Jun 2026 10:14:45 +0800 Subject: [PATCH 1309/2758] tools/virtio: check mmap return value in vringh_test [ Upstream commit ec6177dfe98b9be1c3ede6c0dfe4394ea2a76959 ] In parallel_test(), the return values of mmap() for both host_map and guest_map are not checked against MAP_FAILED. If mmap() fails, the subsequent code will dereference the invalid pointer, leading to a segmentation fault. Add MAP_FAILED checks after both mmap() calls, using err() to report the error and exit, consistent with the existing error handling style in this file (e.g., the open() call on line 149). Fixes: 1515c5ce26ae ("tools/virtio: add vring_test.") Signed-off-by: longlong yan Signed-off-by: Michael S. Tsirkin Message-ID: <20260605021446.1611-1-yanlonglong@kylinos.cn> Signed-off-by: Sasha Levin --- tools/virtio/vringh_test.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/virtio/vringh_test.c b/tools/virtio/vringh_test.c index b9591223437a6..5ea6d29bc992d 100644 --- a/tools/virtio/vringh_test.c +++ b/tools/virtio/vringh_test.c @@ -159,7 +159,12 @@ static int parallel_test(u64 features, /* Parent and child use separate addresses, to check our mapping logic! */ host_map = mmap(NULL, mapsize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (host_map == MAP_FAILED) + err(1, "mmap host_map"); + guest_map = mmap(NULL, mapsize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (guest_map == MAP_FAILED) + err(1, "mmap guest_map"); pipe_ret = pipe(to_guest); assert(!pipe_ret); From 7dd28904c42ee3b0728c0692dd3332c2c83e8f33 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 13 Feb 2026 16:40:46 +0100 Subject: [PATCH 1310/2758] vduse: fix compat handling for VDUSE_IOTLB_GET_FD/VDUSE_VQ_GET_INFO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 455a2a1af92651764e9eb42cec0d95ac142afc28 ] These two ioctls are incompatible on 32-bit x86 userspace, because the data structures are shorter than they are on 64-bit. Add a proper .compat_ioctl handler for x86 that reads the structures with the smaller padding before calling the internal handlers. On all other architectures, CONFIG_COMPAT_FOR_U64_ALIGNMENT is disabled and no special handling is required. Fixes: ad146355bfad ("vduse: Support querying information of IOVA regions") Fixes: c8a6153b6c59 ("vduse: Introduce VDUSE - vDPA Device in Userspace") Acked-by: Eugenio Pérez Signed-off-by: Arnd Bergmann Signed-off-by: Michael S. Tsirkin Message-ID: <20260213154051.4172275-1-arnd@kernel.org> Signed-off-by: Sasha Levin --- drivers/vdpa/vdpa_user/vduse_dev.c | 123 ++++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 1 deletion(-) diff --git a/drivers/vdpa/vdpa_user/vduse_dev.c b/drivers/vdpa/vdpa_user/vduse_dev.c index a479fef535ac6..1367d14e5105e 100644 --- a/drivers/vdpa/vdpa_user/vduse_dev.c +++ b/drivers/vdpa/vdpa_user/vduse_dev.c @@ -1624,6 +1624,127 @@ static long vduse_dev_ioctl(struct file *file, unsigned int cmd, return ret; } +#ifdef CONFIG_COMPAT_FOR_U64_ALIGNMENT +/* + * i386 has different alignment constraints than x86_64, + * so there are only 3 bytes of padding instead of 7. + */ +struct compat_vduse_iotlb_entry { + compat_u64 offset; + compat_u64 start; + compat_u64 last; + __u8 perm; + __u8 padding[3]; +}; +#define COMPAT_VDUSE_IOTLB_GET_FD _IOWR(VDUSE_BASE, 0x10, struct compat_vduse_iotlb_entry) + +struct compat_vduse_vq_info { + __u32 index; + __u32 num; + compat_u64 desc_addr; + compat_u64 driver_addr; + compat_u64 device_addr; + union { + struct vduse_vq_state_split split; + struct vduse_vq_state_packed packed; + }; + __u8 ready; + __u8 padding[3]; +}; +#define COMPAT_VDUSE_VQ_GET_INFO _IOWR(VDUSE_BASE, 0x15, struct compat_vduse_vq_info) + +static long vduse_dev_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct vduse_dev *dev = file->private_data; + void __user *argp = (void __user *)arg; + int ret; + + if (unlikely(dev->broken)) + return -EPERM; + + switch (cmd) { + case COMPAT_VDUSE_IOTLB_GET_FD: { + struct vduse_iotlb_entry_v2 entry = {0}; + struct file *f = NULL; + + ret = -EFAULT; + if (copy_from_user(&entry, argp, _IOC_SIZE(cmd))) + break; + + ret = vduse_dev_iotlb_entry(dev, &entry, &f, NULL); + if (ret) + break; + + ret = -EINVAL; + if (!f) + break; + + ret = copy_to_user(argp, &entry, _IOC_SIZE(cmd)); + if (ret) { + ret = -EFAULT; + fput(f); + break; + } + ret = receive_fd(f, NULL, perm_to_file_flags(entry.perm)); + fput(f); + break; + } + case COMPAT_VDUSE_VQ_GET_INFO: { + struct vduse_vq_info vq_info = {}; + struct vduse_virtqueue *vq; + u32 index; + + ret = -EFAULT; + if (copy_from_user(&vq_info, argp, + sizeof(struct compat_vduse_vq_info))) + break; + + ret = -EINVAL; + if (vq_info.index >= dev->vq_num) + break; + + index = array_index_nospec(vq_info.index, dev->vq_num); + vq = dev->vqs[index]; + vq_info.desc_addr = vq->desc_addr; + vq_info.driver_addr = vq->driver_addr; + vq_info.device_addr = vq->device_addr; + vq_info.num = vq->num; + + if (dev->driver_features & BIT_ULL(VIRTIO_F_RING_PACKED)) { + vq_info.packed.last_avail_counter = + vq->state.packed.last_avail_counter; + vq_info.packed.last_avail_idx = + vq->state.packed.last_avail_idx; + vq_info.packed.last_used_counter = + vq->state.packed.last_used_counter; + vq_info.packed.last_used_idx = + vq->state.packed.last_used_idx; + } else + vq_info.split.avail_index = + vq->state.split.avail_index; + + vq_info.ready = vq->ready; + + ret = -EFAULT; + if (copy_to_user(argp, &vq_info, + sizeof(struct compat_vduse_vq_info))) + break; + + ret = 0; + break; + } + default: + ret = -ENOIOCTLCMD; + break; + } + + return vduse_dev_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); +} +#else +#define vduse_dev_compat_ioctl compat_ptr_ioctl +#endif + static int vduse_dev_release(struct inode *inode, struct file *file) { struct vduse_dev *dev = file->private_data; @@ -1677,7 +1798,7 @@ static const struct file_operations vduse_dev_fops = { .write_iter = vduse_dev_write_iter, .poll = vduse_dev_poll, .unlocked_ioctl = vduse_dev_ioctl, - .compat_ioctl = compat_ptr_ioctl, + .compat_ioctl = vduse_dev_compat_ioctl, .llseek = noop_llseek, }; From beae8aa57c8e61919f88d50134ea8c83e4dc35c2 Mon Sep 17 00:00:00 2001 From: Srujana Challa Date: Tue, 24 Feb 2026 15:22:23 +0530 Subject: [PATCH 1311/2758] vdpa/octeon_ep: Fix PF->VF mailbox data address calculation [ Upstream commit 74dc530f4c505d61f0f3620e59fe56c325ae3437 ] The mailbox address was computed assuming 1 ring per VF. Read the actual rings-per-VF from OCTEP_EPF_RINFO and use it when calculating OCTEP_PF_MBOX_DATA offsets, fixing VF initialization when rings per VF > 1. Fixes: 8b6c724cdab8 ("virtio: vdpa: vDPA driver for Marvell OCTEON DPU devices") Signed-off-by: Srujana Challa Signed-off-by: Michael S. Tsirkin Message-ID: <20260224095226.1001151-2-schalla@marvell.com> Signed-off-by: Sasha Levin --- drivers/vdpa/octeon_ep/octep_vdpa_main.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/vdpa/octeon_ep/octep_vdpa_main.c b/drivers/vdpa/octeon_ep/octep_vdpa_main.c index 31a02e7fd7f27..9946480ee7046 100644 --- a/drivers/vdpa/octeon_ep/octep_vdpa_main.c +++ b/drivers/vdpa/octeon_ep/octep_vdpa_main.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright (C) 2024 Marvell. */ +#include #include #include #include @@ -722,6 +723,8 @@ static int octep_sriov_enable(struct pci_dev *pdev, int num_vfs) bool done = false; int index = 0; int ret, i; + u8 rpvf; + u64 val; ret = pci_enable_sriov(pdev, num_vfs); if (ret) @@ -741,9 +744,11 @@ static int octep_sriov_enable(struct pci_dev *pdev, int num_vfs) } } + val = readq(addr + OCTEP_EPF_RINFO(0)); + rpvf = FIELD_GET(GENMASK_ULL(35, 32), val); if (done) { for (i = 0; i < pf->enabled_vfs; i++) - writeq(OCTEP_DEV_READY_SIGNATURE, addr + OCTEP_PF_MBOX_DATA(i)); + writeq(OCTEP_DEV_READY_SIGNATURE, addr + OCTEP_PF_MBOX_DATA(i * rpvf)); } return num_vfs; From c6c7eae5de798442987619434171ac886035d57c Mon Sep 17 00:00:00 2001 From: Srujana Challa Date: Tue, 24 Feb 2026 15:22:26 +0530 Subject: [PATCH 1312/2758] vdpa/octeon_ep: fix IRQ-to-ring mapping in interrupt handler [ Upstream commit 0d21a1d6375a05274291e32c1ab7cd57dbb69513 ] Look up the IRQ index in oct_hw->irqs instead of assuming irq - irqs[0]. This supports non-contiguous IRQ numbers and avoids incorrect ring indexing when irqs[0] is not the base. Fixes: 26f8ce06af64 ("vdpa/octeon_ep: enable support for multiple interrupts per device") Signed-off-by: Srujana Challa Signed-off-by: Michael S. Tsirkin Message-ID: <20260224095226.1001151-5-schalla@marvell.com> Signed-off-by: Sasha Levin --- drivers/vdpa/octeon_ep/octep_vdpa_main.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/vdpa/octeon_ep/octep_vdpa_main.c b/drivers/vdpa/octeon_ep/octep_vdpa_main.c index 9946480ee7046..df8af6c1454cc 100644 --- a/drivers/vdpa/octeon_ep/octep_vdpa_main.c +++ b/drivers/vdpa/octeon_ep/octep_vdpa_main.c @@ -48,7 +48,7 @@ static struct octep_hw *vdpa_to_octep_hw(struct vdpa_device *vdpa_dev) static irqreturn_t octep_vdpa_intr_handler(int irq, void *data) { struct octep_hw *oct_hw = data; - int i; + int i, start_ring_idx = -1; /* Each device has multiple interrupts (nb_irqs) shared among rings * (nr_vring). Device interrupts are mapped to the rings in a @@ -61,7 +61,16 @@ static irqreturn_t octep_vdpa_intr_handler(int irq, void *data) * 7 -> 7, 15, 23, 31, 39, 47, 55, 63; */ - for (i = irq - oct_hw->irqs[0]; i < oct_hw->nr_vring; i += oct_hw->nb_irqs) { + for (i = 0; i < oct_hw->nb_irqs; i++) { + if (oct_hw->irqs[i] == irq) { + start_ring_idx = i; + break; + } + } + if (start_ring_idx == -1) + return IRQ_NONE; + + for (i = start_ring_idx; i < oct_hw->nr_vring; i += oct_hw->nb_irqs) { if (ioread8(oct_hw->vqs[i].cb_notify_addr)) { /* Acknowledge the per ring notification to the device */ iowrite8(0, oct_hw->vqs[i].cb_notify_addr); From 8bdd1032038e1730b748bf68c1fd5d38151959b5 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 10 Jun 2026 07:06:42 +0200 Subject: [PATCH 1313/2758] iomap: pass the correct len to fserror_report_io in __iomap_write_begin [ Upstream commit de654d66ff30e75d9308fd4d4f1627addef7923e ] len is size of the (larger) write request, plen is the range for which the read failed here. Fixes: a9d573ee88af ("iomap: report file I/O errors to the VFS") Signed-off-by: Christoph Hellwig Link: https://patch.msgid.link/20260610050642.1906695-1-hch@lst.de Reviewed-by: "Darrick J. Wong" Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/iomap/buffered-io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c index d55b936e69862..5fa9a2c7e30eb 100644 --- a/fs/iomap/buffered-io.c +++ b/fs/iomap/buffered-io.c @@ -850,7 +850,7 @@ static int __iomap_write_begin(const struct iomap_iter *iter, if (status < 0) fserror_report_io(iter->inode, FSERR_BUFFERED_READ, pos, - len, status, GFP_NOFS); + plen, status, GFP_NOFS); if (status) return status; } From c0c2c02507f24aebd250f4fdd373cdab04feeec0 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 10 Jun 2026 10:34:30 +0100 Subject: [PATCH 1314/2758] ASoC: cs35l56: Fix missing calls to wm_adsp2_remove() [ Upstream commit 85f7bf03632bfcdd6cedfb3945b7e387d9487d73 ] Call wm_adsp2_remove() in cs35l56_remove() and the error path of cs35l56_common_probe(). Depends on commit 7d3fb78b5503 ("ASoC: wm_adsp: Fix NULL dereference when removing firmware controls"). The call to wm_halo_init() during driver probe should be paired with a call to wm_adsp2_remove() but this was missing. The consequence would be a memory leak of the control lists in the cs_dsp driver. Fixes: e49611252900 ("ASoC: cs35l56: Add driver for Cirrus Logic CS35L56") Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260610093432.557375-2-rf@opensource.cirrus.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/cs35l56.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 4fbbdcc871516..3ab5395f15bb2 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -1964,11 +1964,14 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56) cs35l56_dai, ARRAY_SIZE(cs35l56_dai)); if (ret < 0) { dev_err_probe(cs35l56->base.dev, ret, "Register codec failed\n"); - goto err; + goto err_remove_wm_adsp; } return 0; +err_remove_wm_adsp: + wm_adsp2_remove(&cs35l56->dsp); + err: gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies); @@ -2076,6 +2079,8 @@ void cs35l56_remove(struct cs35l56_private *cs35l56) destroy_workqueue(cs35l56->dsp_wq); + wm_adsp2_remove(&cs35l56->dsp); + pm_runtime_dont_use_autosuspend(cs35l56->base.dev); pm_runtime_suspend(cs35l56->base.dev); pm_runtime_disable(cs35l56->base.dev); From 691edbf080289298f14b510117d4c4d5d508b0ff Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 10 Jun 2026 10:34:31 +0100 Subject: [PATCH 1315/2758] ASoC: cs35l56: Prevent double-free of debugfs [ Upstream commit 344a12ca7ba6e10f9779476780afe9d977d47322 ] Invalidate the debugfs pointer after debugfs_remove_recursive() in cs35l56_remove_cal_debugfs(). This prevents a double-free situation when a future commit adds proper failure cleanup in cs35l56_component_probe(). As described by Sashiko (including the future cs35l56_component_probe() cleanup commit): During a normal component unbind, cs35l56_component_remove() calls cs35l56_remove_cal_debugfs() which removes the directory but leaves a dangling pointer. If the component is later bound again, but _cs35l56_component_probe() fails early (for example, if the init_completion times out), this new error path will call cs35l56_component_remove(). This causes cs35l56_remove_cal_debugfs() to be called again with the dangling cs35l56_base->debugfs pointer from the previous lifecycle, resulting in a use-after-free in debugfs_remove_recursive(). Fixes: f7097161e94c ("ASoC: cs35l56: Add common code for factory calibration") Reported-by: sashiko Link: https://sashiko.dev/#/patchset/20260609120738.284770-1-rf%40opensource.cirrus.com Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260610093432.557375-3-rf@opensource.cirrus.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/cs35l56-shared.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index 795e2764d67ec..e04b114a32b7f 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -1293,6 +1293,7 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_create_cal_debugfs, "SND_SOC_CS35L56_SHARED"); void cs35l56_remove_cal_debugfs(struct cs35l56_base *cs35l56_base) { debugfs_remove_recursive(cs35l56_base->debugfs); + cs35l56_base->debugfs = ERR_PTR(-ENOENT); } EXPORT_SYMBOL_NS_GPL(cs35l56_remove_cal_debugfs, "SND_SOC_CS35L56_SHARED"); From c78c8371f71968a826d9dca990795ac272b187b1 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 10 Jun 2026 10:34:32 +0100 Subject: [PATCH 1316/2758] ASoC: cs35l56: Cleanup if component_probe fails [ Upstream commit a0df7522dfb098d56b42560247082d6f5a8581dd ] If cs35l56_component_probe() fails, call cs35l56_component_remove() to clean up. All the cleanup in cs35l56_component_remove() is the same cleanup that would need to be done (at least partially) if cs35l56_component_probe() fails. So calling cs35l56_component_remove() avoids convoluted cleanup gotos and duplicated code in cs35l56_component_probe(). The only action in cs35l56_component_remove() that is nominally dependent on having completed the component_probe() action is the call to wm_adsp2_component_remove(). Though it is currently safe to call that even if wm_adsp2_component_probe() was not called. However, wm_adsp2_component_probe() has been trivially updated to check itself whether it needs to cleanup. Fixes: e49611252900 ("ASoC: cs35l56: Add driver for Cirrus Logic CS35L56") Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260610093432.557375-4-rf@opensource.cirrus.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/cs35l56.c | 13 ++++++++++++- sound/soc/codecs/wm_adsp.c | 7 +++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 3ab5395f15bb2..3641ac1378c7c 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -1326,7 +1326,7 @@ VISIBLE_IF_KUNIT int cs35l56_set_fw_name(struct snd_soc_component *component) } EXPORT_SYMBOL_IF_KUNIT(cs35l56_set_fw_name); -static int cs35l56_component_probe(struct snd_soc_component *component) +static int _cs35l56_component_probe(struct snd_soc_component *component) { struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); @@ -1426,6 +1426,17 @@ static void cs35l56_component_remove(struct snd_soc_component *component) cs35l56->component = NULL; } +static int cs35l56_component_probe(struct snd_soc_component *component) +{ + int ret; + + ret = _cs35l56_component_probe(component); + if (ret < 0) + cs35l56_component_remove(component); + + return ret; +} + static int cs35l56_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level) { diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index ca630c9948e49..baa75e7ff53ba 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -1170,7 +1170,14 @@ EXPORT_SYMBOL_GPL(wm_adsp2_component_probe); int wm_adsp2_component_remove(struct wm_adsp *dsp, struct snd_soc_component *component) { + if (!dsp) + return 0; + + if (!dsp->component) + return 0; + cs_dsp_cleanup_debugfs(&dsp->cs_dsp); + dsp->component = NULL; return 0; } From eb8ff0201437449879357cb89bfc010c6055b1dc Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 10 Jun 2026 11:55:56 +0100 Subject: [PATCH 1317/2758] ASoC: cs35l56: Don't leave parent IRQ disabled if system_suspend fails [ Upstream commit 53cebeb017164254cde5e31c94d8deef9e4fff97 ] In cs35l56_system_suspend() re-enable the parent IRQ if the call to pm_runtime_force_suspend() returns an error. Fixes: f9dc6b875ec0 ("ASoC: cs35l56: Add basic system suspend handling") Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260610105556.612830-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/cs35l56.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 3641ac1378c7c..033028a4734b5 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -1491,6 +1491,7 @@ static int __maybe_unused cs35l56_runtime_resume_i2c_spi(struct device *dev) int cs35l56_system_suspend(struct device *dev) { struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + int ret; dev_dbg(dev, "system_suspend\n"); @@ -1506,7 +1507,11 @@ int cs35l56_system_suspend(struct device *dev) if (cs35l56->base.irq) disable_irq(cs35l56->base.irq); - return pm_runtime_force_suspend(dev); + ret = pm_runtime_force_suspend(dev); + if ((ret < 0) && cs35l56->base.irq) + enable_irq(cs35l56->base.irq); + + return ret; } EXPORT_SYMBOL_GPL(cs35l56_system_suspend); From 8fc88157a53ccbc0c0a560ebf22cd9049ed40797 Mon Sep 17 00:00:00 2001 From: Pei Xiao Date: Wed, 10 Jun 2026 09:49:09 +0800 Subject: [PATCH 1318/2758] hwmon: (gpd-fan): drop global driver data and use per-device allocation [ Upstream commit 03b4addf8282fa2b63f2d7448d1a9bce9be3f556 ] replace the global state gpd_driver_priv with per-device private data (struct gpd_fan_data) allocated in probe. This allows the driver to support multiple instances in the future and aligns with kernel best practices. No functional change intended. Signed-off-by: Pei Xiao Link: https://lore.kernel.org/r/1cd3e13033fdd3d0f9b59322f7c86e350d113b92.1781055639.git.xiaopei01@kylinos.cn Signed-off-by: Guenter Roeck Stable-dep-of: 1fb4397509bd ("hwmon: (gpd-fan): fix race condition between device removal and sysfs access") Signed-off-by: Sasha Levin --- drivers/hwmon/gpd-fan.c | 209 ++++++++++++++++++++++------------------ 1 file changed, 115 insertions(+), 94 deletions(-) diff --git a/drivers/hwmon/gpd-fan.c b/drivers/hwmon/gpd-fan.c index 80de5f20781eb..7284babd4f5c9 100644 --- a/drivers/hwmon/gpd-fan.c +++ b/drivers/hwmon/gpd-fan.c @@ -40,12 +40,11 @@ enum FAN_PWM_ENABLE { AUTOMATIC = 2, }; -static struct { +struct gpd_fan_data { enum FAN_PWM_ENABLE pwm_enable; u8 pwm_value; - const struct gpd_fan_drvdata *drvdata; -} gpd_driver_priv; +}; struct gpd_fan_drvdata { const char *board_name; // Board name for module param comparison @@ -249,10 +248,10 @@ static const struct gpd_fan_drvdata *gpd_module_drvdata[] = { }; // Helper functions to handle EC read/write -static void gpd_ecram_read(u16 offset, u8 *val) +static void gpd_ecram_read(const struct gpd_fan_drvdata *drvdata, u16 offset, u8 *val) { - u16 addr_port = gpd_driver_priv.drvdata->addr_port; - u16 data_port = gpd_driver_priv.drvdata->data_port; + u16 addr_port = drvdata->addr_port; + u16 data_port = drvdata->data_port; outb(0x2E, addr_port); outb(0x11, data_port); @@ -270,10 +269,10 @@ static void gpd_ecram_read(u16 offset, u8 *val) *val = inb(data_port); } -static void gpd_ecram_write(u16 offset, u8 value) +static void gpd_ecram_write(const struct gpd_fan_drvdata *drvdata, u16 offset, u8 value) { - u16 addr_port = gpd_driver_priv.drvdata->addr_port; - u16 data_port = gpd_driver_priv.drvdata->data_port; + u16 addr_port = drvdata->addr_port; + u16 data_port = drvdata->data_port; outb(0x2E, addr_port); outb(0x11, data_port); @@ -291,198 +290,198 @@ static void gpd_ecram_write(u16 offset, u8 value) outb(value, data_port); } -static int gpd_generic_read_rpm(void) +static int gpd_generic_read_rpm(struct gpd_fan_data *data) { - const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; + const struct gpd_fan_drvdata *drvdata = data->drvdata; u8 high, low; - gpd_ecram_read(drvdata->rpm_read, &high); - gpd_ecram_read(drvdata->rpm_read + 1, &low); + gpd_ecram_read(drvdata, drvdata->rpm_read, &high); + gpd_ecram_read(drvdata, drvdata->rpm_read + 1, &low); return (u16)high << 8 | low; } -static int gpd_wm2_read_rpm(void) +static int gpd_wm2_read_rpm(struct gpd_fan_data *data) { + const struct gpd_fan_drvdata *drvdata = data->drvdata; + for (u16 pwm_ctr_offset = GPD_PWM_CTR_OFFSET; pwm_ctr_offset <= GPD_PWM_CTR_OFFSET + 2; pwm_ctr_offset++) { u8 PWMCTR; - gpd_ecram_read(pwm_ctr_offset, &PWMCTR); + gpd_ecram_read(drvdata, pwm_ctr_offset, &PWMCTR); if (PWMCTR != 0xB8) - gpd_ecram_write(pwm_ctr_offset, 0xB8); + gpd_ecram_write(drvdata, pwm_ctr_offset, 0xB8); } - return gpd_generic_read_rpm(); + return gpd_generic_read_rpm(data); } // Read value for fan1_input -static int gpd_read_rpm(void) +static int gpd_read_rpm(struct gpd_fan_data *data) { - switch (gpd_driver_priv.drvdata->board) { + switch (data->drvdata->board) { case win4_6800u: case win_mini: case duo: case mpc2: - return gpd_generic_read_rpm(); + return gpd_generic_read_rpm(data); case win_max_2: - return gpd_wm2_read_rpm(); + return gpd_wm2_read_rpm(data); } return 0; } -static int gpd_wm2_read_pwm(void) +static int gpd_wm2_read_pwm(struct gpd_fan_data *data) { - const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; + const struct gpd_fan_drvdata *drvdata = data->drvdata; u8 var; - gpd_ecram_read(drvdata->pwm_write, &var); + gpd_ecram_read(drvdata, drvdata->pwm_write, &var); // Match gpd_generic_write_pwm(u8) below return DIV_ROUND_CLOSEST((var - 1) * 255, (drvdata->pwm_max - 1)); } // Read value for pwm1 -static int gpd_read_pwm(void) +static int gpd_read_pwm(struct gpd_fan_data *data) { - switch (gpd_driver_priv.drvdata->board) { + switch (data->drvdata->board) { case win_mini: case duo: case win4_6800u: case mpc2: - switch (gpd_driver_priv.pwm_enable) { + switch (data->pwm_enable) { case DISABLE: return 255; case MANUAL: - return gpd_driver_priv.pwm_value; + return data->pwm_value; case AUTOMATIC: return -EOPNOTSUPP; } break; case win_max_2: - return gpd_wm2_read_pwm(); + return gpd_wm2_read_pwm(data); } return 0; } // PWM value's range in EC is 1 - pwm_max, cast 0 - 255 to it. -static inline u8 gpd_cast_pwm_range(u8 val) +static inline u8 gpd_cast_pwm_range(const struct gpd_fan_drvdata *drvdata, u8 val) { - const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; - return DIV_ROUND_CLOSEST(val * (drvdata->pwm_max - 1), 255) + 1; } -static void gpd_generic_write_pwm(u8 val) +static void gpd_generic_write_pwm(struct gpd_fan_data *data, u8 val) { - const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; + const struct gpd_fan_drvdata *drvdata = data->drvdata; u8 pwm_reg; - pwm_reg = gpd_cast_pwm_range(val); - gpd_ecram_write(drvdata->pwm_write, pwm_reg); + pwm_reg = gpd_cast_pwm_range(drvdata, val); + gpd_ecram_write(drvdata, drvdata->pwm_write, pwm_reg); } -static void gpd_duo_write_pwm(u8 val) +static void gpd_duo_write_pwm(struct gpd_fan_data *data, u8 val) { - const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; + const struct gpd_fan_drvdata *drvdata = data->drvdata; u8 pwm_reg; - pwm_reg = gpd_cast_pwm_range(val); - gpd_ecram_write(drvdata->pwm_write, pwm_reg); - gpd_ecram_write(drvdata->pwm_write + 1, pwm_reg); + pwm_reg = gpd_cast_pwm_range(drvdata, val); + gpd_ecram_write(drvdata, drvdata->pwm_write, pwm_reg); + gpd_ecram_write(drvdata, drvdata->pwm_write + 1, pwm_reg); } // Write value for pwm1 -static int gpd_write_pwm(u8 val) +static int gpd_write_pwm(struct gpd_fan_data *data, u8 val) { - if (gpd_driver_priv.pwm_enable != MANUAL) + if (data->pwm_enable != MANUAL) return -EPERM; - switch (gpd_driver_priv.drvdata->board) { + switch (data->drvdata->board) { case duo: - gpd_duo_write_pwm(val); + gpd_duo_write_pwm(data, val); break; case win_mini: case win4_6800u: case win_max_2: case mpc2: - gpd_generic_write_pwm(val); + gpd_generic_write_pwm(data, val); break; } return 0; } -static void gpd_win_mini_set_pwm_enable(enum FAN_PWM_ENABLE pwm_enable) +static void gpd_win_mini_set_pwm_enable(struct gpd_fan_data *data, enum FAN_PWM_ENABLE pwm_enable) { switch (pwm_enable) { case DISABLE: - gpd_generic_write_pwm(255); + gpd_generic_write_pwm(data, 255); break; case MANUAL: - gpd_generic_write_pwm(gpd_driver_priv.pwm_value); + gpd_generic_write_pwm(data, data->pwm_value); break; case AUTOMATIC: - gpd_ecram_write(gpd_driver_priv.drvdata->pwm_write, 0); + gpd_ecram_write(data->drvdata, data->drvdata->pwm_write, 0); break; } } -static void gpd_duo_set_pwm_enable(enum FAN_PWM_ENABLE pwm_enable) +static void gpd_duo_set_pwm_enable(struct gpd_fan_data *data, enum FAN_PWM_ENABLE pwm_enable) { switch (pwm_enable) { case DISABLE: - gpd_duo_write_pwm(255); + gpd_duo_write_pwm(data, 255); break; case MANUAL: - gpd_duo_write_pwm(gpd_driver_priv.pwm_value); + gpd_duo_write_pwm(data, data->pwm_value); break; case AUTOMATIC: - gpd_ecram_write(gpd_driver_priv.drvdata->pwm_write, 0); + gpd_ecram_write(data->drvdata, data->drvdata->pwm_write, 0); break; } } -static void gpd_wm2_set_pwm_enable(enum FAN_PWM_ENABLE enable) +static void gpd_wm2_set_pwm_enable(struct gpd_fan_data *data, enum FAN_PWM_ENABLE enable) { - const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; + const struct gpd_fan_drvdata *drvdata = data->drvdata; switch (enable) { case DISABLE: - gpd_generic_write_pwm(255); - gpd_ecram_write(drvdata->manual_control_enable, 1); + gpd_generic_write_pwm(data, 255); + gpd_ecram_write(drvdata, drvdata->manual_control_enable, 1); break; case MANUAL: - gpd_generic_write_pwm(gpd_driver_priv.pwm_value); - gpd_ecram_write(drvdata->manual_control_enable, 1); + gpd_generic_write_pwm(data, data->pwm_value); + gpd_ecram_write(drvdata, drvdata->manual_control_enable, 1); break; case AUTOMATIC: - gpd_ecram_write(drvdata->manual_control_enable, 0); + gpd_ecram_write(drvdata, drvdata->manual_control_enable, 0); break; } } // Write value for pwm1_enable -static void gpd_set_pwm_enable(enum FAN_PWM_ENABLE enable) +static void gpd_set_pwm_enable(struct gpd_fan_data *data, enum FAN_PWM_ENABLE enable) { if (enable == MANUAL) // Set pwm_value to max firstly when switching to manual mode, in // consideration of device safety. - gpd_driver_priv.pwm_value = 255; + data->pwm_value = 255; - switch (gpd_driver_priv.drvdata->board) { + switch (data->drvdata->board) { case win_mini: case win4_6800u: case mpc2: - gpd_win_mini_set_pwm_enable(enable); + gpd_win_mini_set_pwm_enable(data, enable); break; case duo: - gpd_duo_set_pwm_enable(enable); + gpd_duo_set_pwm_enable(data, enable); break; case win_max_2: - gpd_wm2_set_pwm_enable(enable); + gpd_wm2_set_pwm_enable(data, enable); break; } } @@ -505,15 +504,16 @@ static umode_t gpd_fan_hwmon_is_visible(__always_unused const void *drvdata, return 0; } -static int gpd_fan_hwmon_read(__always_unused struct device *dev, +static int gpd_fan_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, __always_unused int channel, long *val) { + struct gpd_fan_data *data = dev_get_drvdata(dev); int ret; if (type == hwmon_fan) { if (attr == hwmon_fan_input) { - ret = gpd_read_rpm(); + ret = gpd_read_rpm(data); if (ret < 0) return ret; @@ -524,10 +524,10 @@ static int gpd_fan_hwmon_read(__always_unused struct device *dev, } else if (type == hwmon_pwm) { switch (attr) { case hwmon_pwm_enable: - *val = gpd_driver_priv.pwm_enable; + *val = data->pwm_enable; return 0; case hwmon_pwm_input: - ret = gpd_read_pwm(); + ret = gpd_read_pwm(data); if (ret < 0) return ret; @@ -540,27 +540,29 @@ static int gpd_fan_hwmon_read(__always_unused struct device *dev, return -EOPNOTSUPP; } -static int gpd_fan_hwmon_write(__always_unused struct device *dev, +static int gpd_fan_hwmon_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, __always_unused int channel, long val) { + struct gpd_fan_data *data = dev_get_drvdata(dev); + if (type == hwmon_pwm) { switch (attr) { case hwmon_pwm_enable: if (!in_range(val, 0, 3)) return -EINVAL; - gpd_driver_priv.pwm_enable = val; + data->pwm_enable = val; - gpd_set_pwm_enable(gpd_driver_priv.pwm_enable); + gpd_set_pwm_enable(data, data->pwm_enable); return 0; case hwmon_pwm_input: if (!in_range(val, 0, 256)) return -EINVAL; - gpd_driver_priv.pwm_value = val; + data->pwm_value = val; - return gpd_write_pwm(val); + return gpd_write_pwm(data, val); } } @@ -584,26 +586,27 @@ static struct hwmon_chip_info gpd_fan_chip_info = { .info = gpd_fan_hwmon_channel_info }; -static void gpd_win4_init_ec(void) +static void gpd_win4_init_ec(struct gpd_fan_data *data) { + const struct gpd_fan_drvdata *drvdata = data->drvdata; u8 chip_id, chip_ver; - gpd_ecram_read(0x2000, &chip_id); + gpd_ecram_read(drvdata, 0x2000, &chip_id); if (chip_id == 0x55) { - gpd_ecram_read(0x1060, &chip_ver); - gpd_ecram_write(0x1060, chip_ver | 0x80); + gpd_ecram_read(drvdata, 0x1060, &chip_ver); + gpd_ecram_write(drvdata, 0x1060, chip_ver | 0x80); } } -static void gpd_init_ec(void) +static void gpd_init_ec(struct gpd_fan_data *data) { // The buggy firmware won't initialize EC properly on boot. // Before its initialization, reading RPM will always return 0, // and writing PWM will have no effect. // Initialize it manually on driver load. - if (gpd_driver_priv.drvdata->board == win4_6800u) - gpd_win4_init_ec(); + if (data->drvdata->board == win4_6800u) + gpd_win4_init_ec(data); } static int gpd_fan_probe(struct platform_device *pdev) @@ -611,7 +614,9 @@ static int gpd_fan_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; const struct resource *region; const struct resource *res; - const struct device *hwdev; + struct device *hwdev; + struct gpd_fan_data *data; + const struct gpd_fan_drvdata *match; res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!res) @@ -624,24 +629,42 @@ static int gpd_fan_probe(struct platform_device *pdev) return dev_err_probe(dev, -EBUSY, "Failed to request region\n"); + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + match = dev_get_platdata(dev); + if (!match) + return -EINVAL; + + data->drvdata = match; + data->pwm_enable = AUTOMATIC; + data->pwm_value = 255; + + dev_set_drvdata(dev, data); + hwdev = devm_hwmon_device_register_with_info(dev, DRIVER_NAME, - NULL, + data, &gpd_fan_chip_info, NULL); if (IS_ERR(hwdev)) return dev_err_probe(dev, PTR_ERR(hwdev), "Failed to register hwmon device\n"); - gpd_init_ec(); + gpd_init_ec(data); return 0; } -static void gpd_fan_remove(__always_unused struct platform_device *pdev) +static void gpd_fan_remove(struct platform_device *pdev) { - gpd_driver_priv.pwm_enable = AUTOMATIC; - gpd_set_pwm_enable(AUTOMATIC); + struct gpd_fan_data *data = dev_get_drvdata(&pdev->dev); + + if (data) { + data->pwm_enable = AUTOMATIC; + gpd_set_pwm_enable(data, AUTOMATIC); + } } static struct platform_driver gpd_fan_driver = { @@ -668,6 +691,7 @@ static int __init gpd_fan_init(void) if (!match) { const struct dmi_system_id *dmi_match = dmi_first_match(dmi_table); + if (dmi_match) match = dmi_match->driver_data; } @@ -675,10 +699,6 @@ static int __init gpd_fan_init(void) if (!match) return -ENODEV; - gpd_driver_priv.pwm_enable = AUTOMATIC; - gpd_driver_priv.pwm_value = 255; - gpd_driver_priv.drvdata = match; - struct resource gpd_fan_resources[] = { { .start = match->addr_port, @@ -690,7 +710,8 @@ static int __init gpd_fan_init(void) gpd_fan_platform_device = platform_create_bundle(&gpd_fan_driver, gpd_fan_probe, gpd_fan_resources, - 1, NULL, 0); + 1, + match, sizeof(*match)); if (IS_ERR(gpd_fan_platform_device)) { pr_warn("Failed to create platform device\n"); From a8a4e60b137405824d07fff77d476f6543a0f247 Mon Sep 17 00:00:00 2001 From: Pei Xiao Date: Wed, 10 Jun 2026 09:49:10 +0800 Subject: [PATCH 1319/2758] hwmon: (gpd-fan): Initialize EC before registering hwmon device [ Upstream commit a8a444917fe5d30a9787f41cc179f55fc5f559d3 ] Move the gpd_init_ec() call to before devm_hwmon_device_register_with_info in the probe function. With the previous ordering the hwmon device was registered and exposed to userspace before the EC initialization completes, creating a window where sysfs reads could return invalid values. Some buggy firmware won't initialize EC properly on boot. Before its initialization, reading RPM will always return 0, and writing PWM will have no effect. So move gpd_init_ec to before hwmon device register. Fixes: 0ab88e239439 ("hwmon: add GPD devices sensor driver") Signed-off-by: Pei Xiao Link: https://lore.kernel.org/r/4be3734b135c8013157979ab5e80c7ee51243ddd.1781055639.git.xiaopei01@kylinos.cn Signed-off-by: Guenter Roeck Stable-dep-of: 1fb4397509bd ("hwmon: (gpd-fan): fix race condition between device removal and sysfs access") Signed-off-by: Sasha Levin --- drivers/hwmon/gpd-fan.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/hwmon/gpd-fan.c b/drivers/hwmon/gpd-fan.c index 7284babd4f5c9..745b3fb9e3a49 100644 --- a/drivers/hwmon/gpd-fan.c +++ b/drivers/hwmon/gpd-fan.c @@ -643,6 +643,7 @@ static int gpd_fan_probe(struct platform_device *pdev) dev_set_drvdata(dev, data); + gpd_init_ec(data); hwdev = devm_hwmon_device_register_with_info(dev, DRIVER_NAME, data, @@ -651,9 +652,6 @@ static int gpd_fan_probe(struct platform_device *pdev) if (IS_ERR(hwdev)) return dev_err_probe(dev, PTR_ERR(hwdev), "Failed to register hwmon device\n"); - - gpd_init_ec(data); - return 0; } From 881c149497c99f0d65307a482d6a0c1045514c4b Mon Sep 17 00:00:00 2001 From: Pei Xiao Date: Wed, 10 Jun 2026 09:49:12 +0800 Subject: [PATCH 1320/2758] hwmon: (gpd-fan): fix race condition between device removal and sysfs access [ Upstream commit 1fb4397509bd8701d323e81ac9a97c2e24ed49eb ] Replace the manual gpd_fan_remove() callback with a devres-managed action using devm_add_action_or_reset(). The original remove hook resets the fan to AUTOMATIC mode, but the hwmon sysfs interface (registered with devm_hwmon_device_register_with_info()) remains active until after the remove callback completes. This creates a race window where a concurrent userspace sysfs access can interleave with the EC I/O sequence, potentially corrupting EC registers. Using devm_add_action_or_reset() registers the reset function as a devres action. Due to the LIFO release order of devres, the hwmon device is unregistered (sysfs removed) before the reset action executes, eliminating the race condition. Fixes: 0ab88e239439 ("hwmon: add GPD devices sensor driver") Signed-off-by: Pei Xiao Link: https://lore.kernel.org/r/4400828422cf3a88adad4db224d9efccdb1049d2.1781055639.git.xiaopei01@kylinos.cn Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/gpd-fan.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/hwmon/gpd-fan.c b/drivers/hwmon/gpd-fan.c index 745b3fb9e3a49..4615f98771ddf 100644 --- a/drivers/hwmon/gpd-fan.c +++ b/drivers/hwmon/gpd-fan.c @@ -609,6 +609,16 @@ static void gpd_init_ec(struct gpd_fan_data *data) gpd_win4_init_ec(data); } +static void gpd_fan_reset_hardware(void *pdata) +{ + struct gpd_fan_data *data = pdata; + + if (data) { + data->pwm_enable = AUTOMATIC; + gpd_set_pwm_enable(data, AUTOMATIC); + } +} + static int gpd_fan_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -617,6 +627,7 @@ static int gpd_fan_probe(struct platform_device *pdev) struct device *hwdev; struct gpd_fan_data *data; const struct gpd_fan_drvdata *match; + int ret; res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!res) @@ -644,6 +655,11 @@ static int gpd_fan_probe(struct platform_device *pdev) dev_set_drvdata(dev, data); gpd_init_ec(data); + + ret = devm_add_action_or_reset(dev, gpd_fan_reset_hardware, data); + if (ret) + return ret; + hwdev = devm_hwmon_device_register_with_info(dev, DRIVER_NAME, data, @@ -655,19 +671,8 @@ static int gpd_fan_probe(struct platform_device *pdev) return 0; } -static void gpd_fan_remove(struct platform_device *pdev) -{ - struct gpd_fan_data *data = dev_get_drvdata(&pdev->dev); - - if (data) { - data->pwm_enable = AUTOMATIC; - gpd_set_pwm_enable(data, AUTOMATIC); - } -} - static struct platform_driver gpd_fan_driver = { .probe = gpd_fan_probe, - .remove = gpd_fan_remove, .driver = { .name = KBUILD_MODNAME, }, From 1b7831675b4040a22f8bc9133e781cc0ebaa92a8 Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Thu, 4 Jun 2026 15:36:47 +0800 Subject: [PATCH 1321/2758] ext4: fix ERR_PTR(0) in ext4_mkdir() [ Upstream commit 8e1c43af7cf5091d99db38b7c8129e394d7f45b5 ] When mkdir succeeds, ext4_mkdir() returns ERR_PTR(0) which is incorrect. It should return NULL instead for success and ERR_PTR() only with negative error codes for failure. Fixes: 88d5baf69082 ("Change inode_operations.mkdir to return struct dentry *") Signed-off-by: Hongling Zeng Reviewed-by: Jan Kara Reviewed-by: Baokun Li Link: https://patch.msgid.link/20260604073647.211279-1-zenghongling@kylinos.cn Signed-off-by: Theodore Ts'o Signed-off-by: Sasha Levin --- fs/ext4/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 4a47fbd8dd30c..8cadaeb15b2bd 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -3054,7 +3054,7 @@ static struct dentry *ext4_mkdir(struct mnt_idmap *idmap, struct inode *dir, out_retry: if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries)) goto retry; - return ERR_PTR(err); + return err ? ERR_PTR(err) : NULL; } /* From b10839d1d0aff96a0f60a4af8c674a2704560d68 Mon Sep 17 00:00:00 2001 From: Louis Scalbert Date: Wed, 3 Jun 2026 17:03:26 +0200 Subject: [PATCH 1322/2758] tools: missed broadcast_neigh if_link uapi header [ Upstream commit 0134432215f0e0d4526544ac63dabe20e8a6951e ] Add missing IFLA_BOND_BROADCAST_NEIGH in if_link uapi header. Signed-off-by: Louis Scalbert Acked-by: Jay Vosburgh Link: https://patch.msgid.link/20260603150331.1919611-2-louis.scalbert@6wind.com Signed-off-by: Jakub Kicinski Stable-dep-of: 32b0b8953343 ("bonding: 3ad: add lacp_strict configuration knob") Signed-off-by: Sasha Levin --- tools/include/uapi/linux/if_link.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/include/uapi/linux/if_link.h b/tools/include/uapi/linux/if_link.h index 7e46ca4cd31bb..97a2d4411534a 100644 --- a/tools/include/uapi/linux/if_link.h +++ b/tools/include/uapi/linux/if_link.h @@ -1526,6 +1526,7 @@ enum { IFLA_BOND_MISSED_MAX, IFLA_BOND_NS_IP6_TARGET, IFLA_BOND_COUPLED_CONTROL, + IFLA_BOND_BROADCAST_NEIGH, __IFLA_BOND_MAX, }; From 7caa5f615e0bac22daac7c1f1dce293e2a56ddd9 Mon Sep 17 00:00:00 2001 From: Louis Scalbert Date: Wed, 3 Jun 2026 17:03:27 +0200 Subject: [PATCH 1323/2758] netlink: specs: rt-link: missed broadcast-neigh [ Upstream commit 363037983cc503eb0b5a5c0ab80bf1434cfd168a ] Add missed broadcast-neigh. Signed-off-by: Louis Scalbert Acked-by: Jay Vosburgh Link: https://patch.msgid.link/20260603150331.1919611-3-louis.scalbert@6wind.com Signed-off-by: Jakub Kicinski Stable-dep-of: 32b0b8953343 ("bonding: 3ad: add lacp_strict configuration knob") Signed-off-by: Sasha Levin --- Documentation/netlink/specs/rt-link.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/netlink/specs/rt-link.yaml b/Documentation/netlink/specs/rt-link.yaml index f23aa5f229c50..484e0a97b653a 100644 --- a/Documentation/netlink/specs/rt-link.yaml +++ b/Documentation/netlink/specs/rt-link.yaml @@ -1351,6 +1351,9 @@ attribute-sets: - name: coupled-control type: u8 + - + name: broadcast-neigh + type: u8 - name: bond-ad-info-attrs name-prefix: ifla-bond-ad-info- From 4edbd91f4f97dfa9df875f429b08c0dc4fe209b8 Mon Sep 17 00:00:00 2001 From: Louis Scalbert Date: Wed, 3 Jun 2026 17:03:28 +0200 Subject: [PATCH 1324/2758] bonding: 3ad: add lacp_strict configuration knob [ Upstream commit 32b0b8953343eaceaa816a9ead3b6bb66355c64e ] When an 802.3ad (LACP) bonding interface has no slaves in the collecting/distributing state, the bonding master still reports carrier as up as long as at least 'min_links' slaves have carrier. In this situation, only one slave is effectively used for TX/RX, while traffic received on other slaves is dropped. Upper-layer daemons therefore consider the interface operational, even though traffic may be blackholed if the lack of LACP negotiation means the partner is not ready to deal with traffic. Introduce a configuration knob to control this behavior. It allows the bonding master to assert carrier only when at least 'min_links' slaves are in Collecting_Distributing state. The default mode preserves the existing behavior. This patch only introduces the knob; its behavior is implemented in the subsequent commit. Fixes: 655f8919d549 ("bonding: add min links parameter to 802.3ad") Signed-off-by: Louis Scalbert Acked-by: Jay Vosburgh Link: https://patch.msgid.link/20260603150331.1919611-4-louis.scalbert@6wind.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- Documentation/netlink/specs/rt-link.yaml | 3 +++ Documentation/networking/bonding.rst | 23 +++++++++++++++++++++ drivers/net/bonding/bond_main.c | 1 + drivers/net/bonding/bond_netlink.c | 16 +++++++++++++++ drivers/net/bonding/bond_options.c | 26 ++++++++++++++++++++++++ include/net/bond_options.h | 1 + include/net/bonding.h | 1 + include/uapi/linux/if_link.h | 1 + tools/include/uapi/linux/if_link.h | 1 + 9 files changed, 73 insertions(+) diff --git a/Documentation/netlink/specs/rt-link.yaml b/Documentation/netlink/specs/rt-link.yaml index 484e0a97b653a..644a8bd7b93c3 100644 --- a/Documentation/netlink/specs/rt-link.yaml +++ b/Documentation/netlink/specs/rt-link.yaml @@ -1354,6 +1354,9 @@ attribute-sets: - name: broadcast-neigh type: u8 + - + name: lacp-strict + type: u8 - name: bond-ad-info-attrs name-prefix: ifla-bond-ad-info- diff --git a/Documentation/networking/bonding.rst b/Documentation/networking/bonding.rst index e700bf1d095c3..33ca5afafdf6d 100644 --- a/Documentation/networking/bonding.rst +++ b/Documentation/networking/bonding.rst @@ -619,6 +619,29 @@ min_links aggregator cannot be active without at least one available link, setting this option to 0 or to 1 has the exact same effect. +lacp_strict + + Specifies the fallback behavior of a bonding when LACP negotiation + fails on all slave links, i.e. when no slave is in the + Collecting_Distributing state, while at least `min_links` link still + reports carrier up. + + This option is only applicable to 802.3ad mode (mode 4). + + Valid values are: + + off or 0 + One interface of the bond is selected to be active, in order to + facilitate communication with peer devices that do not implement + LACP. + + on or 1 + Interfaces are only permitted to be made active if they have an + active LACP partner and have successfully reached + Collecting_Distributing state. + + The default value is 0 (off). + mode Specifies one of the bonding policies. The default is diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 8e75453ce0efd..cd9b0a6d65217 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -6446,6 +6446,7 @@ static int __init bond_check_params(struct bond_params *params) params->ad_user_port_key = ad_user_port_key; params->coupled_control = 1; params->broadcast_neighbor = 0; + params->lacp_strict = 0; if (packets_per_slave > 0) { params->reciprocal_packets_per_slave = reciprocal_value(packets_per_slave); diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c index 90365d3f7ebff..4a11572f663d3 100644 --- a/drivers/net/bonding/bond_netlink.c +++ b/drivers/net/bonding/bond_netlink.c @@ -143,6 +143,7 @@ static const struct nla_policy bond_policy[IFLA_BOND_MAX + 1] = { [IFLA_BOND_NS_IP6_TARGET] = { .type = NLA_NESTED }, [IFLA_BOND_COUPLED_CONTROL] = { .type = NLA_U8 }, [IFLA_BOND_BROADCAST_NEIGH] = { .type = NLA_U8 }, + [IFLA_BOND_LACP_STRICT] = { .type = NLA_U8 }, }; static const struct nla_policy bond_slave_policy[IFLA_BOND_SLAVE_MAX + 1] = { @@ -599,6 +600,16 @@ static int bond_changelink(struct net_device *bond_dev, struct nlattr *tb[], return err; } + if (data[IFLA_BOND_LACP_STRICT]) { + int fallback_mode = nla_get_u8(data[IFLA_BOND_LACP_STRICT]); + + bond_opt_initval(&newval, fallback_mode); + err = __bond_opt_set(bond, BOND_OPT_LACP_STRICT, &newval, + data[IFLA_BOND_LACP_STRICT], extack); + if (err) + return err; + } + return 0; } @@ -671,6 +682,7 @@ static size_t bond_get_size(const struct net_device *bond_dev) nla_total_size(sizeof(struct in6_addr)) * BOND_MAX_NS_TARGETS + nla_total_size(sizeof(u8)) + /* IFLA_BOND_COUPLED_CONTROL */ nla_total_size(sizeof(u8)) + /* IFLA_BOND_BROADCAST_NEIGH */ + nla_total_size(sizeof(u8)) + /* IFLA_BOND_LACP_STRICT */ 0; } @@ -838,6 +850,10 @@ static int bond_fill_info(struct sk_buff *skb, bond->params.broadcast_neighbor)) goto nla_put_failure; + if (nla_put_u8(skb, IFLA_BOND_LACP_STRICT, + bond->params.lacp_strict)) + goto nla_put_failure; + if (BOND_MODE(bond) == BOND_MODE_8023AD) { struct ad_info info; diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c index 7380cc4ee75a9..d358b831df773 100644 --- a/drivers/net/bonding/bond_options.c +++ b/drivers/net/bonding/bond_options.c @@ -68,6 +68,8 @@ static int bond_option_lacp_active_set(struct bonding *bond, const struct bond_opt_value *newval); static int bond_option_lacp_rate_set(struct bonding *bond, const struct bond_opt_value *newval); +static int bond_option_lacp_strict_set(struct bonding *bond, + const struct bond_opt_value *newval); static int bond_option_ad_select_set(struct bonding *bond, const struct bond_opt_value *newval); static int bond_option_queue_id_set(struct bonding *bond, @@ -162,6 +164,12 @@ static const struct bond_opt_value bond_lacp_rate_tbl[] = { { NULL, -1, 0}, }; +static const struct bond_opt_value bond_lacp_strict_tbl[] = { + { "off", 0, BOND_VALFLAG_DEFAULT}, + { "on", 1, 0}, + { NULL, -1, 0 } +}; + static const struct bond_opt_value bond_ad_select_tbl[] = { { "stable", BOND_AD_STABLE, BOND_VALFLAG_DEFAULT}, { "bandwidth", BOND_AD_BANDWIDTH, 0}, @@ -363,6 +371,14 @@ static const struct bond_option bond_opts[BOND_OPT_LAST] = { .values = bond_lacp_rate_tbl, .set = bond_option_lacp_rate_set }, + [BOND_OPT_LACP_STRICT] = { + .id = BOND_OPT_LACP_STRICT, + .name = "lacp_strict", + .desc = "Define the LACP fallback mode when no slaves have negotiated", + .unsuppmodes = BOND_MODE_ALL_EX(BIT(BOND_MODE_8023AD)), + .values = bond_lacp_strict_tbl, + .set = bond_option_lacp_strict_set + }, [BOND_OPT_MINLINKS] = { .id = BOND_OPT_MINLINKS, .name = "min_links", @@ -1684,6 +1700,16 @@ static int bond_option_lacp_rate_set(struct bonding *bond, return 0; } +static int bond_option_lacp_strict_set(struct bonding *bond, + const struct bond_opt_value *newval) +{ + netdev_dbg(bond->dev, "Setting LACP fallback to %s (%llu)\n", + newval->string, newval->value); + bond->params.lacp_strict = newval->value; + + return 0; +} + static int bond_option_ad_select_set(struct bonding *bond, const struct bond_opt_value *newval) { diff --git a/include/net/bond_options.h b/include/net/bond_options.h index e6eedf23aea1a..52b966e927938 100644 --- a/include/net/bond_options.h +++ b/include/net/bond_options.h @@ -79,6 +79,7 @@ enum { BOND_OPT_COUPLED_CONTROL, BOND_OPT_BROADCAST_NEIGH, BOND_OPT_ACTOR_PORT_PRIO, + BOND_OPT_LACP_STRICT, BOND_OPT_LAST }; diff --git a/include/net/bonding.h b/include/net/bonding.h index edd1942dcd736..2c54a36a8477b 100644 --- a/include/net/bonding.h +++ b/include/net/bonding.h @@ -129,6 +129,7 @@ struct bond_params { int peer_notif_delay; int lacp_active; int lacp_fast; + int lacp_strict; unsigned int min_links; int ad_select; char primary[IFNAMSIZ]; diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 79ce4bc24cba6..9ef5784e78e83 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -1584,6 +1584,7 @@ enum { IFLA_BOND_NS_IP6_TARGET, IFLA_BOND_COUPLED_CONTROL, IFLA_BOND_BROADCAST_NEIGH, + IFLA_BOND_LACP_STRICT, __IFLA_BOND_MAX, }; diff --git a/tools/include/uapi/linux/if_link.h b/tools/include/uapi/linux/if_link.h index 97a2d4411534a..757ce5e9426e9 100644 --- a/tools/include/uapi/linux/if_link.h +++ b/tools/include/uapi/linux/if_link.h @@ -1527,6 +1527,7 @@ enum { IFLA_BOND_NS_IP6_TARGET, IFLA_BOND_COUPLED_CONTROL, IFLA_BOND_BROADCAST_NEIGH, + IFLA_BOND_LACP_STRICT, __IFLA_BOND_MAX, }; From fd35901594aea87072eef8dea0c3cd0a43babca8 Mon Sep 17 00:00:00 2001 From: Louis Scalbert Date: Wed, 3 Jun 2026 17:03:29 +0200 Subject: [PATCH 1325/2758] bonding: 3ad: fix carrier when no usable slaves [ Upstream commit 0bd695db23c6262e9cb980017a6273925172ec5b ] Apply the "lacp_strict" configuration from the previous commit. "lacp_strict" mode "on" asserts that the bonding master carrier is up only when at least 'min_links' slaves are in the Collecting_Distributing state. Fixes: 655f8919d549 ("bonding: add min links parameter to 802.3ad") Signed-off-by: Louis Scalbert Acked-by: Jay Vosburgh Link: https://patch.msgid.link/20260603150331.1919611-5-louis.scalbert@6wind.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/bonding/bond_3ad.c | 26 +++++++++++++++++++++++--- drivers/net/bonding/bond_options.c | 1 + 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index 985ef66dc3331..51d86e3d34db8 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -745,6 +745,21 @@ static void __set_agg_ports_ready(struct aggregator *aggregator, int val) } } +static int __agg_usable_ports(struct aggregator *agg) +{ + struct port *port; + int valid = 0; + + for (port = agg->lag_ports; port; + port = port->next_port_in_aggregator) { + if (port->actor_oper_port_state & LACP_STATE_COLLECTING && + port->actor_oper_port_state & LACP_STATE_DISTRIBUTING) + valid++; + } + + return valid; +} + static int __agg_active_ports(struct aggregator *agg) { struct port *port; @@ -1179,10 +1194,10 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) switch (port->sm_mux_state) { case AD_MUX_DETACHED: port->actor_oper_port_state &= ~LACP_STATE_SYNCHRONIZATION; - ad_disable_collecting_distributing(port, - update_slave_arr); port->actor_oper_port_state &= ~LACP_STATE_COLLECTING; port->actor_oper_port_state &= ~LACP_STATE_DISTRIBUTING; + ad_disable_collecting_distributing(port, + update_slave_arr); port->ntt = true; break; case AD_MUX_WAITING: @@ -2107,6 +2122,7 @@ static void ad_disable_distributing(struct port *port, bool *update_slave_arr) port->actor_port_number, aggregator->aggregator_identifier); __disable_distributing_port(port); + bond_3ad_set_carrier(port->slave->bond); /* Slave array needs an update */ *update_slave_arr = true; } @@ -2130,6 +2146,7 @@ static void ad_enable_collecting_distributing(struct port *port, port->actor_port_number, aggregator->aggregator_identifier); __enable_port(port); + bond_3ad_set_carrier(port->slave->bond); /* Slave array needs update */ *update_slave_arr = true; /* Should notify peers if possible */ @@ -2153,6 +2170,7 @@ static void ad_disable_collecting_distributing(struct port *port, port->actor_port_number, aggregator->aggregator_identifier); __disable_port(port); + bond_3ad_set_carrier(port->slave->bond); /* Slave array needs an update */ *update_slave_arr = true; } @@ -2832,7 +2850,9 @@ int bond_3ad_set_carrier(struct bonding *bond) active = __get_active_agg(&(SLAVE_AD_INFO(first_slave)->aggregator)); if (active) { /* are enough slaves available to consider link up? */ - if (__agg_active_ports(active) < bond->params.min_links) { + if ((bond->params.lacp_strict ? __agg_usable_ports(active) + : __agg_active_ports(active)) < + bond->params.min_links) { if (netif_carrier_ok(bond->dev)) { netif_carrier_off(bond->dev); goto out; diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c index d358b831df773..94b7b0851f16a 100644 --- a/drivers/net/bonding/bond_options.c +++ b/drivers/net/bonding/bond_options.c @@ -1706,6 +1706,7 @@ static int bond_option_lacp_strict_set(struct bonding *bond, netdev_dbg(bond->dev, "Setting LACP fallback to %s (%llu)\n", newval->string, newval->value); bond->params.lacp_strict = newval->value; + bond_3ad_set_carrier(bond); return 0; } From fa466bc24aa64c3b9210904db61fef16be2f0cf9 Mon Sep 17 00:00:00 2001 From: Louis Scalbert Date: Wed, 3 Jun 2026 17:03:30 +0200 Subject: [PATCH 1326/2758] bonding: 3ad: fix mux port state on oper down [ Upstream commit 807afc7544b865d4d09068a415fd5b71bf5665cc ] When the bonding interface has carrier down due to the absence of usable slaves and a slave transitions from down to up, the bonding interface briefly goes carrier up, then down again, and finally up once LACP negotiates collecting and distributing on the port. When lacp_strict mode is on, the interface should not transition to carrier up until LACP negotiation is complete. This happens because the actor and partner port states remain in Collecting_Distributing when the port goes down. When the port comes back up, it temporarily remains in this state until LACP renegotiation occurs. Previously this was mostly cosmetic, but since the bonding carrier state may depend on the LACP negotiation state, it causes the interface to flap. According to IEEE 802.3ad-2000 and IEEE 802.1ax-2014, Collecting and Distributing should be reset when a port goes down: - In the Receive state machine, port_enabled == FALSE causes a transition to the PORT_DISABLED state, which is expected to clear Partner_Oper_Port_State.Synchronization. - In the Mux state machine, Partner_Oper_Port_State.Synchronization == FALSE causes a transition to the ATTACHED state, which disables Collecting and Distributing. However, Partner_Oper_Port_State.Synchronization is not cleared in the PORT_DISABLED state. Clear Partner_Oper_Port_State.Synchronization in the Receive PORT_DISABLED state. Fixes: 655f8919d549 ("bonding: add min links parameter to 802.3ad") Signed-off-by: Louis Scalbert Acked-by: Jay Vosburgh Link: https://patch.msgid.link/20260603150331.1919611-6-louis.scalbert@6wind.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/bonding/bond_3ad.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index 51d86e3d34db8..acbba08dbdfad 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -1337,6 +1337,7 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port) fallthrough; case AD_RX_PORT_DISABLED: port->sm_vars &= ~AD_PORT_MATCHED; + port->partner_oper.port_state &= ~LACP_STATE_SYNCHRONIZATION; break; case AD_RX_LACP_DISABLED: port->sm_vars &= ~AD_PORT_SELECTED; From 0ae42b51607240990614e0843f0d3529aaff62cc Mon Sep 17 00:00:00 2001 From: Aditya Prakash Srivastava Date: Mon, 8 Jun 2026 06:52:27 +0000 Subject: [PATCH 1327/2758] ext4: fix kernel BUG in ext4_write_inline_data_end [ Upstream commit ad09aa45965d3fafaf9963bc78109b73c0f9ac8d ] When the data=journal mount option is used, the ext4_journalled_write_end() function incorrectly calls ext4_write_inline_data_end() without checking if the EXT4_STATE_MAY_INLINE_DATA flag is still set on the inode. If a previous attempt to convert the inline data to an extent failed (e.g. due to ENOSPC), the EXT4_STATE_MAY_INLINE_DATA flag is cleared, but the EXT4_INODE_INLINE_DATA flag remains set. In this scenario, the next call to ext4_write_begin() will not prepare the inline data xattr for writing, but ext4_journalled_write_end() will incorrectly attempt to write to it, triggering a BUG_ON(pos + len > EXT4_I(inode)->i_inline_size) in ext4_write_inline_data() since i_inline_size was not expanded. Fix this by ensuring that ext4_journalled_write_end() only calls ext4_write_inline_data_end() if the EXT4_STATE_MAY_INLINE_DATA flag is set, mirroring the behavior of ext4_write_end() and ext4_da_write_end(). Reported-by: syzbot+0c89d865531d053abb2d@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=0c89d865531d053abb2d Fixes: 3fdcfb668fd7 ("ext4: add journalled write support for inline data") Signed-off-by: Aditya Prakash Srivastava Reviewed-by: Jan Kara Link: https://patch.msgid.link/20260608065227.3018-1-aditya.ansh182@gmail.com Signed-off-by: Theodore Ts'o Signed-off-by: Sasha Levin --- fs/ext4/inode.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index c2c2d6ac7f3d1..4fce9ec176f88 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1560,7 +1560,8 @@ static int ext4_journalled_write_end(const struct kiocb *iocb, BUG_ON(!ext4_handle_valid(handle)); - if (ext4_has_inline_data(inode)) + if (ext4_has_inline_data(inode) && + ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA)) return ext4_write_inline_data_end(inode, pos, len, copied, folio); From fb52013cad9e9b7d3a6a14ea1bcd841e41da7c6c Mon Sep 17 00:00:00 2001 From: Yun Zhou Date: Mon, 8 Jun 2026 23:25:21 +0800 Subject: [PATCH 1328/2758] ext4: validate donor file superblock early in EXT4_IOC_MOVE_EXT [ Upstream commit c143957520c6c9b5cd72e0de8b52b814f0c576fe ] Reject the EXT4_IOC_MOVE_EXT ioctl early if the donor file does not belong to the same superblock as the original file. Currently, this validation is performed inside ext4_move_extents() by mext_check_validity(), but only after lock_two_nondirectories() has already acquired the inode locks. When the donor fd refers to a file on a different filesystem (e.g., overlayfs), this late validation creates a circular lock dependency: CPU0 (overlayfs write) CPU1 (ext4 ioctl) ---- ---- inode_lock(ovl_inode) mnt_want_write_file(filp) sb_start_write(ext4_sb) [sb_writers] backing_file_write_iter() vfs_iter_write(real_file) file_start_write(real_file) sb_start_write(ext4_sb) [blocked by freeze] lock_two_nondirectories() inode_lock(ovl_inode) [blocked] With a concurrent freeze operation holding sb_writers write side, this forms a deadlock cycle: CPU0 waits for freeze to complete, freeze waits for CPU1's sb_writers reader to exit, CPU1 waits for CPU0's inode lock. Since EXT4_IOC_MOVE_EXT exchanges physical extents between two files, it fundamentally requires both files to reside on the same ext4 filesystem. Moving the superblock check before any lock acquisition is both semantically correct and eliminates the circular dependency by ensuring that cross-filesystem donor fds are rejected before sb_writers or inode locks are taken. Fixes: fcf6b1b729bc ("ext4: refactor ext4_move_extents code base") Reported-by: syzbot+ad6118a7584b607c67f2@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=ad6118a7584b607c67f2 Signed-off-by: Yun Zhou Reviewed-by: Jan Kara Reviewed-by: Andreas Dilger Link: https://patch.msgid.link/20260608152521.1292656-1-yun.zhou@windriver.com Signed-off-by: Theodore Ts'o Signed-off-by: Sasha Levin --- fs/ext4/ioctl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 110e3fb194ec6..c8387e6a2c6e9 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -1656,6 +1656,9 @@ static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (!(fd_file(donor)->f_mode & FMODE_WRITE)) return -EBADF; + if (file_inode(filp)->i_sb != file_inode(fd_file(donor))->i_sb) + return -EXDEV; + err = mnt_want_write_file(filp); if (err) return err; From fbbd7604fdf105a421ba6503d43259b4301e3283 Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Tue, 9 Jun 2026 22:18:31 -0700 Subject: [PATCH 1329/2758] selftests/bpf: Fix bpf_iter/task_vma test [ Upstream commit 2e8ad1ff712d2a397e407c9fde60901f68d077dc ] For selftest bpf_iter/task_vma, I got a failure like below on my qemu run: test_task_vma_common:FAIL:compare_output unexpected compare_output: actual '561593546000-561593585000r--p0000000000:241256579534/root/devshare/bpf-next/tools/testing/selftests/bpf/test_progs' != expected '561593546000-561593585000r--p0000000000:245551546830/root/devshare/bpf-next/tools/testing/selftests/bpf/test_progs' Further debugging found out file->f_inode->i_ino value may exceed 32bit, e.g., i_ino = 0x14c2eae35, but the format string is '%u'. This caused inode mismatch between bpf iter and proc result. Fix the issue by using format string '%llu' to accommodate 64bit i_ino. Fixes: e8168840e16c ("selftests/bpf: Add test for bpf_iter_task_vma") Signed-off-by: Yonghong Song Acked-by: Leon Hwang Link: https://lore.kernel.org/r/20260610051831.1346659-1-yonghong.song@linux.dev Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- tools/testing/selftests/bpf/progs/bpf_iter_task_vmas.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/progs/bpf_iter_task_vmas.c b/tools/testing/selftests/bpf/progs/bpf_iter_task_vmas.c index d64ba7ddaed54..d7fb561ed4fb9 100644 --- a/tools/testing/selftests/bpf/progs/bpf_iter_task_vmas.c +++ b/tools/testing/selftests/bpf/progs/bpf_iter_task_vmas.c @@ -52,7 +52,7 @@ SEC("iter/task_vma") int proc_maps(struct bpf_iter__task_vma *ctx) bpf_d_path(&file->f_path, d_path_buf, D_PATH_BUF_SIZE); BPF_SEQ_PRINTF(seq, "%08llx ", vma->vm_pgoff << 12); - BPF_SEQ_PRINTF(seq, "%02x:%02x %u", MAJOR(dev), MINOR(dev), + BPF_SEQ_PRINTF(seq, "%02x:%02x %llu", MAJOR(dev), MINOR(dev), file->f_inode->i_ino); BPF_SEQ_PRINTF(seq, "\t%s\n", d_path_buf); } else { From 87f734a444cffa3ea12499d682386469c0e47710 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Fri, 5 Jun 2026 11:15:08 -0700 Subject: [PATCH 1330/2758] cxl/test: Verify cmd->size_in before accessing payload [ Upstream commit 71a1def165267bc0947d4236f7336f490739c379 ] Several mock mailbox handlers access input payload fields before verifying that cmd->size_in is large enough for the corresponding structure. To ensure invalid commands are rejected before any payload data is consumed, add missing size checks and move existing checks ahead of the first payload field access. [dj: Updated commit log per Alison's comments. ] Fixes: 7d3eb23c4ccf ("tools/testing/cxl: Introduce a mock memory device + driver") Fixes: d1dca858f058 ("cxl/test: Add generic mock events") Fixes: f6448cb5f2f3 ("tools/testing/cxl: add firmware update emulation to CXL memdevs") Fixes: e77e9c107978 ("cxl/test: Add Get Feature support to cxl_test") Link: https://lore.kernel.org/linux-cxl/20260605143748.235271F00893@smtp.kernel.org/ Suggested-by: sashiko-bot Tested-by: Alison Schofield Reviewed-by: Alison Schofield Signed-off-by: Dave Jiang Signed-off-by: Sasha Levin --- tools/testing/cxl/test/mem.c | 39 +++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c index 271c7ad8cc32c..2e9a5f151e983 100644 --- a/tools/testing/cxl/test/mem.c +++ b/tools/testing/cxl/test/mem.c @@ -312,12 +312,17 @@ static int mock_get_event(struct device *dev, struct cxl_mbox_cmd *cmd) static int mock_clear_event(struct device *dev, struct cxl_mbox_cmd *cmd) { - struct cxl_mbox_clear_event_payload *pl = cmd->payload_in; + struct cxl_mbox_clear_event_payload *pl; struct mock_event_log *log; - u8 log_type = pl->event_log; + u8 log_type; u16 handle; int nr; + if (cmd->size_in < sizeof(*pl)) + return -EINVAL; + + pl = cmd->payload_in; + log_type = pl->event_log; if (log_type >= CXL_EVENT_TYPE_MAX) return -EINVAL; @@ -574,14 +579,19 @@ static int mock_gsl(struct cxl_mbox_cmd *cmd) static int mock_get_log(struct cxl_memdev_state *mds, struct cxl_mbox_cmd *cmd) { struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox; - struct cxl_mbox_get_log *gl = cmd->payload_in; - u32 offset = le32_to_cpu(gl->offset); - u32 length = le32_to_cpu(gl->length); uuid_t uuid = DEFINE_CXL_CEL_UUID; + struct cxl_mbox_get_log *gl; void *data = &mock_cel; + u32 offset; + u32 length; if (cmd->size_in < sizeof(*gl)) return -EINVAL; + + gl = cmd->payload_in; + offset = le32_to_cpu(gl->offset); + length = le32_to_cpu(gl->length); + if (length > cxl_mbox->payload_size) return -EINVAL; if (offset + length > sizeof(mock_cel)) @@ -1336,10 +1346,14 @@ static int mock_fw_info(struct cxl_mockmem_data *mdata, static int mock_transfer_fw(struct cxl_mockmem_data *mdata, struct cxl_mbox_cmd *cmd) { - struct cxl_mbox_transfer_fw *transfer = cmd->payload_in; + struct cxl_mbox_transfer_fw *transfer; void *fw = mdata->fw; size_t offset, length; + if (cmd->size_in < sizeof(*transfer)) + return -EINVAL; + + transfer = cmd->payload_in; offset = le32_to_cpu(transfer->offset) * CXL_FW_TRANSFER_ALIGNMENT; length = cmd->size_in - sizeof(*transfer); if (offset + length > FW_SIZE) @@ -1415,11 +1429,18 @@ static int mock_get_test_feature(struct cxl_mockmem_data *mdata, struct cxl_mbox_cmd *cmd) { struct vendor_test_feat *output = cmd->payload_out; - struct cxl_mbox_get_feat_in *input = cmd->payload_in; - u16 offset = le16_to_cpu(input->offset); - u16 count = le16_to_cpu(input->count); + struct cxl_mbox_get_feat_in *input; + u16 offset; + u16 count; u8 *ptr; + if (cmd->size_in < sizeof(*input)) + return -EINVAL; + + input = cmd->payload_in; + offset = le16_to_cpu(input->offset); + count = le16_to_cpu(input->count); + if (offset > sizeof(*output)) { cmd->return_code = CXL_MBOX_CMD_RC_INPUT; return -EINVAL; From 206895bd3e88a236efabf63e92902e19ae4c5a94 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Fri, 5 Jun 2026 10:12:38 -0700 Subject: [PATCH 1331/2758] cxl/test: Fix integer overflow in mock LSA bounds checks [ Upstream commit 81eafcada109b653977c4dfbd2b6a72470025a01 ] Pre-existing issue discovered by sashiko-bot. mock_get_lsa() and mock_set_lsa() validate the requested LSA range with "offset + length > LSA_SIZE". Both offset and length are u32 and, in mock_get_lsa(), both are taken directly from the user-supplied payload. The addition is evaluated modulo 2^32, so a large offset combined with a small length wraps around and passes the check. Rewrite the checks to first bound offset, then compare length against the remaining LSA size. Suggested-by: sashiko-bot Fixes: 7d3eb23c4ccf ("tools/testing/cxl: Introduce a mock memory device + driver") Link: https://lore.kernel.org/linux-cxl/20260605143748.235271F00893@smtp.kernel.org/ Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Alison Schofield Signed-off-by: Dave Jiang Signed-off-by: Sasha Levin --- tools/testing/cxl/test/mem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c index 2e9a5f151e983..9a7cd3f46a1ee 100644 --- a/tools/testing/cxl/test/mem.c +++ b/tools/testing/cxl/test/mem.c @@ -1063,7 +1063,7 @@ static int mock_get_lsa(struct cxl_mockmem_data *mdata, return -EINVAL; offset = le32_to_cpu(get_lsa->offset); length = le32_to_cpu(get_lsa->length); - if (offset + length > LSA_SIZE) + if (offset > LSA_SIZE || length > LSA_SIZE - offset) return -EINVAL; if (length > cmd->size_out) return -EINVAL; @@ -1083,7 +1083,7 @@ static int mock_set_lsa(struct cxl_mockmem_data *mdata, return -EINVAL; offset = le32_to_cpu(set_lsa->offset); length = cmd->size_in - sizeof(*set_lsa); - if (offset + length > LSA_SIZE) + if (offset > LSA_SIZE || length > LSA_SIZE - offset) return -EINVAL; memcpy(lsa + offset, &set_lsa->data[0], length); From d881369a8553f9e75bfaf32f291839e34bc3e804 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Fri, 5 Jun 2026 11:44:26 -0700 Subject: [PATCH 1332/2758] cxl/test: Zero out LSA backing memory to avoid leaking to user [ Upstream commit 60f065dbaf46e65830da62a0041761f0c039e086 ] Memory through vmalloc() is not zeroed out. When this memory is copied into output payload, it leaks memory content to user. Use vzalloc() instead to zero out the memory. Suggested-by: sashiko-bot Link: https://lore.kernel.org/linux-cxl/20260605173146.2B9A31F00893@smtp.kernel.org/ Fixes: 7d3eb23c4ccf ("tools/testing/cxl: Introduce a mock memory device + driver") Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260605184426.4070913-1-dave.jiang@intel.com Signed-off-by: Dave Jiang Signed-off-by: Sasha Levin --- tools/testing/cxl/test/mem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c index 9a7cd3f46a1ee..739343cd58022 100644 --- a/tools/testing/cxl/test/mem.c +++ b/tools/testing/cxl/test/mem.c @@ -1724,7 +1724,7 @@ static int cxl_mock_mem_probe(struct platform_device *pdev) return -ENOMEM; dev_set_drvdata(dev, mdata); - mdata->lsa = vmalloc(LSA_SIZE); + mdata->lsa = vzalloc(LSA_SIZE); if (!mdata->lsa) return -ENOMEM; mdata->fw = vmalloc(FW_SIZE); From bfc8eddca6ec274df6517cb73ba8a137e502c13b Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Wed, 29 Apr 2026 23:14:39 +0300 Subject: [PATCH 1333/2758] of: cpu: add check in __of_find_n_match_cpu_property() [ Upstream commit 5901eda2ed99ba0d3661da6eb265970559323bb3 ] In __of_find_n_match_cpu_property(), checking the variable ac for 0 won't prevent a possible overflow when multiplying it by sizeof(*cell). Besides, of_read_number() (called in the *for* loop) can't return correct result if that variable (which equals the #address-cells prop's value) exceeds 2, so additionally checking for that seems logical... Found by Linux Verification Center (linuxtesting.org) with the Svace static analysis tool. Fixes: f3cea45a77c8 ("of: Fix iteration bug over CPU reg properties") Signed-off-by: Sergey Shtylyov Link: https://patch.msgid.link/0c7bf7e9-887c-42d5-bcfb-0ba7fe1e70b6@auroraos.dev Signed-off-by: Rob Herring (Arm) Signed-off-by: Sasha Levin --- drivers/of/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/of/cpu.c b/drivers/of/cpu.c index 5214dc3d05ae1..bd0e918d6f290 100644 --- a/drivers/of/cpu.c +++ b/drivers/of/cpu.c @@ -60,7 +60,7 @@ static bool __of_find_n_match_cpu_property(struct device_node *cpun, cell = of_get_property(cpun, prop_name, &prop_len); if (!cell && !ac && arch_match_cpu_phys_id(cpu, 0)) return true; - if (!cell || !ac) + if (!cell || !ac || ac > 2) return false; prop_len /= sizeof(*cell) * ac; for (tid = 0; tid < prop_len; tid++) { From b6fd7a40a66485c8aa8156d8fcf50b95cb8ba281 Mon Sep 17 00:00:00 2001 From: Giovanni Cabiddu Date: Mon, 8 Jun 2026 16:12:57 +0100 Subject: [PATCH 1334/2758] vfio/qat: fix f_pos race in qat_vf_resume_write() [ Upstream commit 4ec5e932e636896e97e4c6a8205b0ac76d52421a ] qat_vf_resume_write() checks filp->f_pos before taking migf->lock, but copies into the migration-state buffer after taking the lock and re-reading the shared file position. Two concurrent writers could therefore pass the bounds check with the old offset, then have the second writer copy after the first advanced f_pos, writing past the end of the migration-state buffer. Take migf->lock before doing the boundary checks. Fixes: bb208810b1ab ("vfio/qat: Add vfio_pci driver for Intel QAT SR-IOV VF devices") Reviewed-by: Ahsan Atta Signed-off-by: Giovanni Cabiddu Link: https://lore.kernel.org/r/20260608151317.136613-1-giovanni.cabiddu@intel.com Signed-off-by: Alex Williamson Signed-off-by: Sasha Levin --- drivers/vfio/pci/qat/main.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/vfio/pci/qat/main.c b/drivers/vfio/pci/qat/main.c index ac9652539d66a..60ff907b6a67a 100644 --- a/drivers/vfio/pci/qat/main.c +++ b/drivers/vfio/pci/qat/main.c @@ -298,14 +298,18 @@ static ssize_t qat_vf_resume_write(struct file *filp, const char __user *buf, return -ESPIPE; offs = &filp->f_pos; - if (*offs < 0 || - check_add_overflow(len, *offs, &end)) - return -EOVERFLOW; + mutex_lock(&migf->lock); - if (end > mig_dev->state_size) - return -ENOMEM; + if (*offs < 0 || check_add_overflow(len, *offs, &end)) { + done = -EOVERFLOW; + goto out_unlock; + } + + if (end > mig_dev->state_size) { + done = -ENOMEM; + goto out_unlock; + } - mutex_lock(&migf->lock); if (migf->disabled) { done = -ENODEV; goto out_unlock; From cb22dc79528eb26a46d346c3118dbd6b14c70609 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 10 Jun 2026 12:55:38 +0200 Subject: [PATCH 1335/2758] bpf: Tighten cgroup storage cookie checks for prog arrays [ Upstream commit 10627ddc0167aab5c1c390a10ef461e9937aba08 ] The fix in commit abad3d0bad72 ("bpf: Fix oob access in cgroup local storage") is still incomplete. The prog-array compatibility check treats a program with no cgroup storage as compatible with any stored storage cookie. This allows a storage-less program to bridge a tail call chain between an entry program and a storage-using callee even though cgroup local storage at runtime still follows the caller's context, that is, A -> B(no storage) -> C(storage) path. Requiring exact cookie equality would break the legitimate case of a storage-less leaf program being tail called from a storage-using one. Instead, only accept a zero storage cookie if the program cannot perform tail calls itself. This keeps A -> B(no storage) working while rejecting the A -> B(no storage) -> C(storage) bridge. Fixes: abad3d0bad72 ("bpf: Fix oob access in cgroup local storage") Reported-by: Lin Ma Signed-off-by: Daniel Borkmann Acked-by: Yonghong Song Link: https://lore.kernel.org/r/20260610105539.705887-1-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- kernel/bpf/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index de61e1894452e..f5d9c27e654d4 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -2527,7 +2527,7 @@ static bool __bpf_prog_map_compatible(struct bpf_map *map, cookie = aux->cgroup_storage[i] ? aux->cgroup_storage[i]->cookie : 0; ret = map->owner->storage_cookie[i] == cookie || - !cookie; + (!cookie && !aux->tail_call_reachable); } if (ret && map->owner->attach_func_proto != aux->attach_func_proto) { From f7751e4e9d347406322c2d7d9db904958ef9502b Mon Sep 17 00:00:00 2001 From: Angelo Dureghello Date: Wed, 10 Jun 2026 22:35:06 +0200 Subject: [PATCH 1336/2758] m68k: mcf5441x: fix clocks numbering [ Upstream commit 919afb86694f5a8a9b50bff56cd8199f065b0dbb ] Fix clocks numbering, set correct values for eport and DAC, as per RM Rev 5, 05/2018, table 9.5. Fixes: bea8bcb12da09 ("m68knommu: Add support for the Coldfire m5441x.") Fixes: 007f84ede6e3e ("m68k: coldfire: remove private clk_get/clk_put") Signed-off-by: Angelo Dureghello Signed-off-by: Greg Ungerer Signed-off-by: Sasha Levin --- arch/m68k/coldfire/m5441x.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/arch/m68k/coldfire/m5441x.c b/arch/m68k/coldfire/m5441x.c index 7a25cfc7ac075..ac9c17d0ee647 100644 --- a/arch/m68k/coldfire/m5441x.c +++ b/arch/m68k/coldfire/m5441x.c @@ -41,9 +41,9 @@ DEFINE_CLK(0, "mcfpit.0", 32, MCF_BUSCLK); DEFINE_CLK(0, "mcfpit.1", 33, MCF_BUSCLK); DEFINE_CLK(0, "mcfpit.2", 34, MCF_BUSCLK); DEFINE_CLK(0, "mcfpit.3", 35, MCF_BUSCLK); -DEFINE_CLK(0, "mcfeport.0", 37, MCF_CLK); -DEFINE_CLK(0, "mcfadc.0", 38, MCF_CLK); -DEFINE_CLK(0, "mcfdac.0", 39, MCF_CLK); +DEFINE_CLK(0, "mcfeport.0", 36, MCF_CLK); +DEFINE_CLK(0, "mcfadc.0", 37, MCF_CLK); +DEFINE_CLK(0, "mcfdac.0", 38, MCF_CLK); DEFINE_CLK(0, "mcfrtc.0", 42, MCF_CLK); DEFINE_CLK(0, "mcfsim.0", 43, MCF_CLK); DEFINE_CLK(0, "mcfusb-otg.0", 44, MCF_CLK); @@ -103,9 +103,9 @@ static struct clk_lookup m5411x_clk_lookup[] = { CLKDEV_INIT("mcfpit.1", NULL, &__clk_0_33), CLKDEV_INIT("mcfpit.2", NULL, &__clk_0_34), CLKDEV_INIT("mcfpit.3", NULL, &__clk_0_35), - CLKDEV_INIT("mcfeport.0", NULL, &__clk_0_37), - CLKDEV_INIT("mcfadc.0", NULL, &__clk_0_38), - CLKDEV_INIT("mcfdac.0", NULL, &__clk_0_39), + CLKDEV_INIT("mcfeport.0", NULL, &__clk_0_36), + CLKDEV_INIT("mcfadc.0", NULL, &__clk_0_37), + CLKDEV_INIT("mcfdac.0", NULL, &__clk_0_38), CLKDEV_INIT("mcfrtc.0", NULL, &__clk_0_42), CLKDEV_INIT("mcfsim.0", NULL, &__clk_0_43), CLKDEV_INIT("mcfusb-otg.0", NULL, &__clk_0_44), @@ -156,7 +156,7 @@ static struct clk * const enable_clks[] __initconst = { &__clk_0_27, /* uart3 */ &__clk_0_33, /* pit.1 */ - &__clk_0_37, /* eport */ + &__clk_0_36, /* eport */ &__clk_0_48, /* pll */ &__clk_0_51, /* esdhc */ @@ -174,8 +174,8 @@ static struct clk * const disable_clks[] __initconst = { &__clk_0_32, /* pit.0 */ &__clk_0_34, /* pit.2 */ &__clk_0_35, /* pit.3 */ - &__clk_0_38, /* adc */ - &__clk_0_39, /* dac */ + &__clk_0_37, /* adc */ + &__clk_0_38, /* dac.0 */ &__clk_0_44, /* usb otg */ &__clk_0_45, /* usb host */ &__clk_0_47, /* ssi.0 */ From bcd974652a914e639cf662b501be2c05684c73fc Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Fri, 27 Mar 2026 11:30:04 +0000 Subject: [PATCH 1337/2758] pinctrl: sunxi: a523: Remove unneeded IRQ remuxing flag [ Upstream commit eaf84ff673409fa3dfc390c6afb53b641ee5acba ] The Allwinner A10 and H3 SoCs cannot read the state of a GPIO line when that line is muxed for IRQ triggering (muxval 6), but only if it's explicitly muxed for GPIO input (muxval 0). Other SoCs do not show this behaviour, so we added a optional workaround, triggered by a quirk bit, which triggers remuxing the pin when it's configured for IRQ, while we need to read its value. For some reasons this quirk flag was copied over to newer SoCs, even though they don't show this behaviour, and the GPIO data register reflects the true GPIO state even with a pin muxed to IRQ trigger. Remove the unneeded quirk from the A523 family, where it's definitely not needed (confirmed by experiments), and where it actually breaks, because the workaround is not compatible with the newer generation pinctrl IP used in that chip. Together with a DT change this fixes GPIO IRQ operation on the A523 family of SoCs, as for instance used for the SD card detection. Signed-off-by: Andre Przywara Fixes: b8a51e95b376 ("pinctrl: sunxi: Add support for the secondary A523 GPIO ports") Reviewed-by: Jernej Skrabec Acked-by: Chen-Yu Tsai Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/sunxi/pinctrl-sun55i-a523-r.c | 1 - drivers/pinctrl/sunxi/pinctrl-sun55i-a523.c | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/pinctrl/sunxi/pinctrl-sun55i-a523-r.c b/drivers/pinctrl/sunxi/pinctrl-sun55i-a523-r.c index 69cd2b4ebd7d7..462aa1c4a5fa6 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun55i-a523-r.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun55i-a523-r.c @@ -26,7 +26,6 @@ static const u8 a523_r_irq_bank_muxes[SUNXI_PINCTRL_MAX_BANKS] = static struct sunxi_pinctrl_desc a523_r_pinctrl_data = { .irq_banks = ARRAY_SIZE(a523_r_irq_bank_map), .irq_bank_map = a523_r_irq_bank_map, - .irq_read_needs_mux = true, .io_bias_cfg_variant = BIAS_VOLTAGE_PIO_POW_MODE_SEL, .pin_base = PL_BASE, }; diff --git a/drivers/pinctrl/sunxi/pinctrl-sun55i-a523.c b/drivers/pinctrl/sunxi/pinctrl-sun55i-a523.c index 7d2308c37d29e..b6f78f1f30ac4 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun55i-a523.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun55i-a523.c @@ -26,7 +26,6 @@ static const u8 a523_irq_bank_muxes[SUNXI_PINCTRL_MAX_BANKS] = static struct sunxi_pinctrl_desc a523_pinctrl_data = { .irq_banks = ARRAY_SIZE(a523_irq_bank_map), .irq_bank_map = a523_irq_bank_map, - .irq_read_needs_mux = true, .io_bias_cfg_variant = BIAS_VOLTAGE_PIO_POW_MODE_SEL, }; From 8de35c96965a70af5be8ec5e1bbb9116ca6f96f2 Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Sat, 6 Jun 2026 05:03:32 +0300 Subject: [PATCH 1338/2758] pinctrl: airoha: an7581: add missed gpio32 pin group [ Upstream commit bdc95d7e8de3eefa9fc062302625259c0b79136d ] gpio32 pin group is missed for an7581 SoC. This patch add it. Fixes: 1c8ace2d0725 ("pinctrl: airoha: Add support for EN7581 SoC") Signed-off-by: Mikhail Kshevetskiy Reviewed-by: Bartosz Golaszewski Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/mediatek/pinctrl-airoha.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pinctrl/mediatek/pinctrl-airoha.c b/drivers/pinctrl/mediatek/pinctrl-airoha.c index 995ba6175c950..8051662232287 100644 --- a/drivers/pinctrl/mediatek/pinctrl-airoha.c +++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c @@ -539,6 +539,7 @@ static const int en7581_gpio28_pins[] = { 41 }; static const int en7581_gpio29_pins[] = { 42 }; static const int en7581_gpio30_pins[] = { 43 }; static const int en7581_gpio31_pins[] = { 44 }; +static const int en7581_gpio32_pins[] = { 45 }; static const int en7581_gpio33_pins[] = { 46 }; static const int en7581_gpio34_pins[] = { 47 }; static const int en7581_gpio35_pins[] = { 48 }; @@ -623,6 +624,7 @@ static const struct pingroup en7581_pinctrl_groups[] = { PINCTRL_PIN_GROUP("gpio29", en7581_gpio29), PINCTRL_PIN_GROUP("gpio30", en7581_gpio30), PINCTRL_PIN_GROUP("gpio31", en7581_gpio31), + PINCTRL_PIN_GROUP("gpio32", en7581_gpio32), PINCTRL_PIN_GROUP("gpio33", en7581_gpio33), PINCTRL_PIN_GROUP("gpio34", en7581_gpio34), PINCTRL_PIN_GROUP("gpio35", en7581_gpio35), From d59971decbd3bcfa12ae7695d08fbb0c68f719d0 Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Sat, 6 Jun 2026 05:03:33 +0300 Subject: [PATCH 1339/2758] pinctrl: airoha: an7583: add missed gpio32 pin group [ Upstream commit 81cc2285cea84e3ed8688d353e1250cf8899c80a ] gpio32 pin group is missed for an7583 SoC. This patch add it. Fixes: 3ffeb17a9a27 ("pinctrl: airoha: add support for Airoha AN7583 PINs") Signed-off-by: Mikhail Kshevetskiy Reviewed-by: Bartosz Golaszewski Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/mediatek/pinctrl-airoha.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pinctrl/mediatek/pinctrl-airoha.c b/drivers/pinctrl/mediatek/pinctrl-airoha.c index 8051662232287..c0aed1b607920 100644 --- a/drivers/pinctrl/mediatek/pinctrl-airoha.c +++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c @@ -758,6 +758,7 @@ static const int an7583_gpio28_pins[] = { 30 }; static const int an7583_gpio29_pins[] = { 31 }; static const int an7583_gpio30_pins[] = { 32 }; static const int an7583_gpio31_pins[] = { 33 }; +static const int an7583_gpio32_pins[] = { 34 }; static const int an7583_gpio33_pins[] = { 35 }; static const int an7583_gpio34_pins[] = { 36 }; static const int an7583_gpio35_pins[] = { 37 }; @@ -836,6 +837,7 @@ static const struct pingroup an7583_pinctrl_groups[] = { PINCTRL_PIN_GROUP("gpio29", an7583_gpio29), PINCTRL_PIN_GROUP("gpio30", an7583_gpio30), PINCTRL_PIN_GROUP("gpio31", an7583_gpio31), + PINCTRL_PIN_GROUP("gpio32", an7583_gpio32), PINCTRL_PIN_GROUP("gpio33", an7583_gpio33), PINCTRL_PIN_GROUP("gpio34", an7583_gpio34), PINCTRL_PIN_GROUP("gpio35", an7583_gpio35), From f880faa7c6dac1cb74379bf886fabc77aec1ece1 Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Sat, 6 Jun 2026 05:03:34 +0300 Subject: [PATCH 1340/2758] pinctrl: airoha: an7581: fix misprint in gpio19 pinconf [ Upstream commit 08a39a0617ff32a7c3962bbc38a9eee41b14659a ] Pin 32 (gpio19) duplicate pinconf settings of pin 31. Fix it using a proper bit number in the configuration register. Fixes: 1c8ace2d0725 ("pinctrl: airoha: Add support for EN7581 SoC") Signed-off-by: Mikhail Kshevetskiy Reviewed-by: Bartosz Golaszewski Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/mediatek/pinctrl-airoha.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pinctrl/mediatek/pinctrl-airoha.c b/drivers/pinctrl/mediatek/pinctrl-airoha.c index c0aed1b607920..14b2357277360 100644 --- a/drivers/pinctrl/mediatek/pinctrl-airoha.c +++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c @@ -1798,7 +1798,7 @@ static const struct airoha_pinctrl_conf en7581_pinctrl_pullup_conf[] = { PINCTRL_CONF_DESC(29, REG_GPIO_L_PU, BIT(16)), PINCTRL_CONF_DESC(30, REG_GPIO_L_PU, BIT(17)), PINCTRL_CONF_DESC(31, REG_GPIO_L_PU, BIT(18)), - PINCTRL_CONF_DESC(32, REG_GPIO_L_PU, BIT(18)), + PINCTRL_CONF_DESC(32, REG_GPIO_L_PU, BIT(19)), PINCTRL_CONF_DESC(33, REG_GPIO_L_PU, BIT(20)), PINCTRL_CONF_DESC(34, REG_GPIO_L_PU, BIT(21)), PINCTRL_CONF_DESC(35, REG_GPIO_L_PU, BIT(22)), @@ -1915,7 +1915,7 @@ static const struct airoha_pinctrl_conf en7581_pinctrl_pulldown_conf[] = { PINCTRL_CONF_DESC(29, REG_GPIO_L_PD, BIT(16)), PINCTRL_CONF_DESC(30, REG_GPIO_L_PD, BIT(17)), PINCTRL_CONF_DESC(31, REG_GPIO_L_PD, BIT(18)), - PINCTRL_CONF_DESC(32, REG_GPIO_L_PD, BIT(18)), + PINCTRL_CONF_DESC(32, REG_GPIO_L_PD, BIT(19)), PINCTRL_CONF_DESC(33, REG_GPIO_L_PD, BIT(20)), PINCTRL_CONF_DESC(34, REG_GPIO_L_PD, BIT(21)), PINCTRL_CONF_DESC(35, REG_GPIO_L_PD, BIT(22)), @@ -2032,7 +2032,7 @@ static const struct airoha_pinctrl_conf en7581_pinctrl_drive_e2_conf[] = { PINCTRL_CONF_DESC(29, REG_GPIO_L_E2, BIT(16)), PINCTRL_CONF_DESC(30, REG_GPIO_L_E2, BIT(17)), PINCTRL_CONF_DESC(31, REG_GPIO_L_E2, BIT(18)), - PINCTRL_CONF_DESC(32, REG_GPIO_L_E2, BIT(18)), + PINCTRL_CONF_DESC(32, REG_GPIO_L_E2, BIT(19)), PINCTRL_CONF_DESC(33, REG_GPIO_L_E2, BIT(20)), PINCTRL_CONF_DESC(34, REG_GPIO_L_E2, BIT(21)), PINCTRL_CONF_DESC(35, REG_GPIO_L_E2, BIT(22)), @@ -2149,7 +2149,7 @@ static const struct airoha_pinctrl_conf en7581_pinctrl_drive_e4_conf[] = { PINCTRL_CONF_DESC(29, REG_GPIO_L_E4, BIT(16)), PINCTRL_CONF_DESC(30, REG_GPIO_L_E4, BIT(17)), PINCTRL_CONF_DESC(31, REG_GPIO_L_E4, BIT(18)), - PINCTRL_CONF_DESC(32, REG_GPIO_L_E4, BIT(18)), + PINCTRL_CONF_DESC(32, REG_GPIO_L_E4, BIT(19)), PINCTRL_CONF_DESC(33, REG_GPIO_L_E4, BIT(20)), PINCTRL_CONF_DESC(34, REG_GPIO_L_E4, BIT(21)), PINCTRL_CONF_DESC(35, REG_GPIO_L_E4, BIT(22)), From 38208826f4bc3e698f413ca7ee391397d502c56d Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Sat, 6 Jun 2026 05:03:35 +0300 Subject: [PATCH 1341/2758] pinctrl: airoha: an7583: fix misprint in gpio19 pinconf [ Upstream commit a7f3e2b7730fc1d6c7431af49e0dc1ee97589795 ] Pin 21 (gpio19) duplicate pinconf settings of pin 20. Fix it using a proper bit number in the configuration register. Fixes: 3ffeb17a9a27 ("pinctrl: airoha: add support for Airoha AN7583 PINs") Signed-off-by: Mikhail Kshevetskiy Reviewed-by: Bartosz Golaszewski Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/mediatek/pinctrl-airoha.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pinctrl/mediatek/pinctrl-airoha.c b/drivers/pinctrl/mediatek/pinctrl-airoha.c index 14b2357277360..34eef79d058f3 100644 --- a/drivers/pinctrl/mediatek/pinctrl-airoha.c +++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c @@ -1851,7 +1851,7 @@ static const struct airoha_pinctrl_conf an7583_pinctrl_pullup_conf[] = { PINCTRL_CONF_DESC(18, REG_GPIO_L_PU, BIT(16)), PINCTRL_CONF_DESC(19, REG_GPIO_L_PU, BIT(17)), PINCTRL_CONF_DESC(20, REG_GPIO_L_PU, BIT(18)), - PINCTRL_CONF_DESC(21, REG_GPIO_L_PU, BIT(18)), + PINCTRL_CONF_DESC(21, REG_GPIO_L_PU, BIT(19)), PINCTRL_CONF_DESC(22, REG_GPIO_L_PU, BIT(20)), PINCTRL_CONF_DESC(23, REG_GPIO_L_PU, BIT(21)), PINCTRL_CONF_DESC(24, REG_GPIO_L_PU, BIT(22)), @@ -1968,7 +1968,7 @@ static const struct airoha_pinctrl_conf an7583_pinctrl_pulldown_conf[] = { PINCTRL_CONF_DESC(18, REG_GPIO_L_PD, BIT(16)), PINCTRL_CONF_DESC(19, REG_GPIO_L_PD, BIT(17)), PINCTRL_CONF_DESC(20, REG_GPIO_L_PD, BIT(18)), - PINCTRL_CONF_DESC(21, REG_GPIO_L_PD, BIT(18)), + PINCTRL_CONF_DESC(21, REG_GPIO_L_PD, BIT(19)), PINCTRL_CONF_DESC(22, REG_GPIO_L_PD, BIT(20)), PINCTRL_CONF_DESC(23, REG_GPIO_L_PD, BIT(21)), PINCTRL_CONF_DESC(24, REG_GPIO_L_PD, BIT(22)), @@ -2085,7 +2085,7 @@ static const struct airoha_pinctrl_conf an7583_pinctrl_drive_e2_conf[] = { PINCTRL_CONF_DESC(18, REG_GPIO_L_E2, BIT(16)), PINCTRL_CONF_DESC(19, REG_GPIO_L_E2, BIT(17)), PINCTRL_CONF_DESC(20, REG_GPIO_L_E2, BIT(18)), - PINCTRL_CONF_DESC(21, REG_GPIO_L_E2, BIT(18)), + PINCTRL_CONF_DESC(21, REG_GPIO_L_E2, BIT(19)), PINCTRL_CONF_DESC(22, REG_GPIO_L_E2, BIT(20)), PINCTRL_CONF_DESC(23, REG_GPIO_L_E2, BIT(21)), PINCTRL_CONF_DESC(24, REG_GPIO_L_E2, BIT(22)), @@ -2202,7 +2202,7 @@ static const struct airoha_pinctrl_conf an7583_pinctrl_drive_e4_conf[] = { PINCTRL_CONF_DESC(18, REG_GPIO_L_E4, BIT(16)), PINCTRL_CONF_DESC(19, REG_GPIO_L_E4, BIT(17)), PINCTRL_CONF_DESC(20, REG_GPIO_L_E4, BIT(18)), - PINCTRL_CONF_DESC(21, REG_GPIO_L_E4, BIT(18)), + PINCTRL_CONF_DESC(21, REG_GPIO_L_E4, BIT(19)), PINCTRL_CONF_DESC(22, REG_GPIO_L_E4, BIT(20)), PINCTRL_CONF_DESC(23, REG_GPIO_L_E4, BIT(21)), PINCTRL_CONF_DESC(24, REG_GPIO_L_E4, BIT(22)), From 98977e9a78d712d572c9606467f2a075457e57a9 Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Sat, 6 Jun 2026 05:03:36 +0300 Subject: [PATCH 1342/2758] pinctrl: airoha: an7581: fix incorrect led mapping in phy4_led1 pin function [ Upstream commit e20c85c79cc2f45b87eb3dab38d4c641bbf83ed6 ] phy4_led1 pin function maps led incorrectly. It uses the same map as phy3_led1. PHY{X} should map to LAN{N}_PHY_LED_MAP(X-1). Fixes: 579839c9548c ("pinctrl: airoha: convert PHY LED GPIO to macro") Signed-off-by: Mikhail Kshevetskiy Reviewed-by: Bartosz Golaszewski Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/mediatek/pinctrl-airoha.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pinctrl/mediatek/pinctrl-airoha.c b/drivers/pinctrl/mediatek/pinctrl-airoha.c index 34eef79d058f3..9497f5110f61b 100644 --- a/drivers/pinctrl/mediatek/pinctrl-airoha.c +++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c @@ -1622,13 +1622,13 @@ static const struct airoha_pinctrl_func_group phy3_led1_func_group[] = { static const struct airoha_pinctrl_func_group phy4_led1_func_group[] = { AIROHA_PINCTRL_PHY_LED1("gpio43", GPIO_LAN0_LED1_MODE_MASK, - LAN0_LED_MAPPING_MASK, LAN0_PHY_LED_MAP(2)), + LAN0_LED_MAPPING_MASK, LAN0_PHY_LED_MAP(3)), AIROHA_PINCTRL_PHY_LED1("gpio44", GPIO_LAN1_LED1_MODE_MASK, - LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(2)), + LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(3)), AIROHA_PINCTRL_PHY_LED1("gpio45", GPIO_LAN2_LED1_MODE_MASK, - LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(2)), + LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(3)), AIROHA_PINCTRL_PHY_LED1("gpio46", GPIO_LAN3_LED1_MODE_MASK, - LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(2)), + LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(3)), }; static const struct airoha_pinctrl_func_group an7583_phy1_led0_func_group[] = { From 1d494be03832c0bdce90256845043761d23ab398 Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Sat, 6 Jun 2026 05:03:37 +0300 Subject: [PATCH 1343/2758] pinctrl: airoha: an7583: fix incorrect led mapping in phy4_led1 pin function [ Upstream commit a3602577fdfc49c6dc08d67304426d5ef6d7dec6 ] phy4_led1 pin function maps led incorrectly. It uses the same map as phy3_led1. PHY{X} should map to LAN{N}_PHY_LED_MAP(X-1). Fixes: 3ffeb17a9a27 ("pinctrl: airoha: add support for Airoha AN7583 PINs") Signed-off-by: Mikhail Kshevetskiy Reviewed-by: Bartosz Golaszewski Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/mediatek/pinctrl-airoha.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pinctrl/mediatek/pinctrl-airoha.c b/drivers/pinctrl/mediatek/pinctrl-airoha.c index 9497f5110f61b..9be759f08b184 100644 --- a/drivers/pinctrl/mediatek/pinctrl-airoha.c +++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c @@ -1710,13 +1710,13 @@ static const struct airoha_pinctrl_func_group an7583_phy3_led1_func_group[] = { static const struct airoha_pinctrl_func_group an7583_phy4_led1_func_group[] = { AIROHA_PINCTRL_PHY_LED1("gpio8", GPIO_LAN0_LED1_MODE_MASK, - LAN0_LED_MAPPING_MASK, LAN0_PHY_LED_MAP(2)), + LAN0_LED_MAPPING_MASK, LAN0_PHY_LED_MAP(3)), AIROHA_PINCTRL_PHY_LED1("gpio9", GPIO_LAN1_LED1_MODE_MASK, - LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(2)), + LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(3)), AIROHA_PINCTRL_PHY_LED1("gpio10", GPIO_LAN2_LED1_MODE_MASK, - LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(2)), + LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(3)), AIROHA_PINCTRL_PHY_LED1("gpio11", GPIO_LAN3_LED1_MODE_MASK, - LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(2)), + LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(3)), }; static const struct airoha_pinctrl_func en7581_pinctrl_funcs[] = { From 5ec8f823ec2a0df83bbbb4dc8f4052d3ab3e496d Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Sat, 6 Jun 2026 05:03:38 +0300 Subject: [PATCH 1344/2758] pinctrl: airoha: fix pwm pin function for an7581 and an7583 [ Upstream commit 08a5af468e613b6d8cd9725d284c9e6be288d364 ] AN7581 have 47 valid GPIOs only (gpio0-gpio46), so gpio47 is a fiction. AN7583 have 49 valid GPIOs (gpio0-gpio48), so gpio48 is missed To fix an issue * create AN7583 specific pwm pin function, * remove gpio47 from AN7581 pwm pin function. Fixes: 3ffeb17a9a27 ("pinctrl: airoha: add support for Airoha AN7583 PINs") Signed-off-by: Mikhail Kshevetskiy Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/mediatek/pinctrl-airoha.c | 74 ++++++++++++++++++++++- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/drivers/pinctrl/mediatek/pinctrl-airoha.c b/drivers/pinctrl/mediatek/pinctrl-airoha.c index 9be759f08b184..15a5417243494 100644 --- a/drivers/pinctrl/mediatek/pinctrl-airoha.c +++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c @@ -906,7 +906,30 @@ static const char *const pwm_groups[] = { "gpio0", "gpio1", "gpio40", "gpio41", "gpio42", "gpio43", "gpio44", "gpio45", - "gpio46", "gpio47" }; + "gpio46" }; +static const char *const an7583_pwm_groups[] = { "gpio0", "gpio1", + "gpio2", "gpio3", + "gpio4", "gpio5", + "gpio6", "gpio7", + "gpio8", "gpio9", + "gpio10", "gpio11", + "gpio12", "gpio13", + "gpio14", "gpio15", + "gpio16", "gpio17", + "gpio18", "gpio19", + "gpio20", "gpio21", + "gpio22", "gpio23", + "gpio24", "gpio25", + "gpio26", "gpio27", + "gpio28", "gpio29", + "gpio30", "gpio31", + "gpio36", "gpio37", + "gpio38", "gpio39", + "gpio40", "gpio41", + "gpio42", "gpio43", + "gpio44", "gpio45", + "gpio46", "gpio47", + "gpio48" }; static const char *const phy1_led0_groups[] = { "gpio33", "gpio34", "gpio35", "gpio42" }; static const char *const phy2_led0_groups[] = { "gpio33", "gpio34", @@ -1504,7 +1527,54 @@ static const struct airoha_pinctrl_func_group pwm_func_group[] = { AIROHA_PINCTRL_PWM_EXT("gpio44", GPIO44_FLASH_MODE_CFG), AIROHA_PINCTRL_PWM_EXT("gpio45", GPIO45_FLASH_MODE_CFG), AIROHA_PINCTRL_PWM_EXT("gpio46", GPIO46_FLASH_MODE_CFG), +}; + +static const struct airoha_pinctrl_func_group an7583_pwm_func_group[] = { + AIROHA_PINCTRL_PWM("gpio0", GPIO0_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio1", GPIO1_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio2", GPIO2_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio3", GPIO3_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio4", GPIO4_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio5", GPIO5_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio6", GPIO6_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio7", GPIO7_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio8", GPIO8_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio9", GPIO9_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio10", GPIO10_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio11", GPIO11_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio12", GPIO12_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio13", GPIO13_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio14", GPIO14_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio15", GPIO15_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio16", GPIO16_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio17", GPIO17_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio18", GPIO18_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio19", GPIO19_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio20", GPIO20_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio21", GPIO21_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio22", GPIO22_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio23", GPIO23_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio24", GPIO24_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio25", GPIO25_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio26", GPIO26_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio27", GPIO27_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio28", GPIO28_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio29", GPIO29_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio30", GPIO30_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio31", GPIO31_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio36", GPIO36_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio37", GPIO37_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio38", GPIO38_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio39", GPIO39_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio40", GPIO40_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio41", GPIO41_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio42", GPIO42_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio43", GPIO43_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio44", GPIO44_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio45", GPIO45_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio46", GPIO46_FLASH_MODE_CFG), AIROHA_PINCTRL_PWM_EXT("gpio47", GPIO47_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio48", GPIO48_FLASH_MODE_CFG), }; #define AIROHA_PINCTRL_PHY_LED0(gpio, mux_val, map_mask, map_val) \ @@ -1759,7 +1829,7 @@ static const struct airoha_pinctrl_func an7583_pinctrl_funcs[] = { PINCTRL_FUNC_DESC("emmc", emmc), PINCTRL_FUNC_DESC("pnand", pnand), PINCTRL_FUNC_DESC("pcie_reset", an7583_pcie_reset), - PINCTRL_FUNC_DESC("pwm", pwm), + PINCTRL_FUNC_DESC("pwm", an7583_pwm), PINCTRL_FUNC_DESC("phy1_led0", an7583_phy1_led0), PINCTRL_FUNC_DESC("phy2_led0", an7583_phy2_led0), PINCTRL_FUNC_DESC("phy3_led0", an7583_phy3_led0), From 0b30b5305fd76281d16f696a88de4b251af10a58 Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Sat, 6 Jun 2026 05:03:39 +0300 Subject: [PATCH 1345/2758] pinctrl: airoha: an7583: fix gpio21 pin group [ Upstream commit abf92c45cc82e9a01aa581f9fbc790e78250a4d4 ] gpio21 pin group refers to gpio22 pin, this is wrong. Fixes: 3ffeb17a9a27 ("pinctrl: airoha: add support for Airoha AN7583 PINs") Signed-off-by: Mikhail Kshevetskiy Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/mediatek/pinctrl-airoha.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/mediatek/pinctrl-airoha.c b/drivers/pinctrl/mediatek/pinctrl-airoha.c index 15a5417243494..9dce3ed6de174 100644 --- a/drivers/pinctrl/mediatek/pinctrl-airoha.c +++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c @@ -748,7 +748,7 @@ static const int an7583_gpio17_pins[] = { 19 }; static const int an7583_gpio18_pins[] = { 20 }; static const int an7583_gpio19_pins[] = { 21 }; static const int an7583_gpio20_pins[] = { 22 }; -static const int an7583_gpio21_pins[] = { 24 }; +static const int an7583_gpio21_pins[] = { 23 }; static const int an7583_gpio23_pins[] = { 25 }; static const int an7583_gpio24_pins[] = { 26 }; static const int an7583_gpio25_pins[] = { 27 }; From 0b3e0c425ea9358ff180f415a941fc9207d2b796 Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Sat, 6 Jun 2026 05:03:40 +0300 Subject: [PATCH 1346/2758] pinctrl: airoha: an7583: add missed gpio22 pin group [ Upstream commit 9ef86358855d5fd89db019ace33c097d2d752b9d ] gpio22 pin group is missed, fix it. Fixes: 3ffeb17a9a27 ("pinctrl: airoha: add support for Airoha AN7583 PINs") Signed-off-by: Mikhail Kshevetskiy Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/mediatek/pinctrl-airoha.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pinctrl/mediatek/pinctrl-airoha.c b/drivers/pinctrl/mediatek/pinctrl-airoha.c index 9dce3ed6de174..e66b608c4803a 100644 --- a/drivers/pinctrl/mediatek/pinctrl-airoha.c +++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c @@ -749,6 +749,7 @@ static const int an7583_gpio18_pins[] = { 20 }; static const int an7583_gpio19_pins[] = { 21 }; static const int an7583_gpio20_pins[] = { 22 }; static const int an7583_gpio21_pins[] = { 23 }; +static const int an7583_gpio22_pins[] = { 24 }; static const int an7583_gpio23_pins[] = { 25 }; static const int an7583_gpio24_pins[] = { 26 }; static const int an7583_gpio25_pins[] = { 27 }; @@ -828,6 +829,7 @@ static const struct pingroup an7583_pinctrl_groups[] = { PINCTRL_PIN_GROUP("gpio19", an7583_gpio19), PINCTRL_PIN_GROUP("gpio20", an7583_gpio20), PINCTRL_PIN_GROUP("gpio21", an7583_gpio21), + PINCTRL_PIN_GROUP("gpio22", an7583_gpio22), PINCTRL_PIN_GROUP("gpio23", an7583_gpio23), PINCTRL_PIN_GROUP("gpio24", an7583_gpio24), PINCTRL_PIN_GROUP("gpio25", an7583_gpio25), From 82cc075fc3d9dea538eecc6afa02724ac71b4547 Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Sat, 6 Jun 2026 05:03:41 +0300 Subject: [PATCH 1347/2758] pinctrl: airoha: an7583: fix phy1_led1 pin function [ Upstream commit dbe28a2a22a3455d1adbf9fd61d3537603ac3072 ] phy1_led1 pin function wrongly refers to gpio1 instead of gpio11. Fix it. Fixes: 3ffeb17a9a27 ("pinctrl: airoha: add support for Airoha AN7583 PINs") Signed-off-by: Mikhail Kshevetskiy Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/mediatek/pinctrl-airoha.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/mediatek/pinctrl-airoha.c b/drivers/pinctrl/mediatek/pinctrl-airoha.c index e66b608c4803a..b73ab60d00659 100644 --- a/drivers/pinctrl/mediatek/pinctrl-airoha.c +++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c @@ -1754,7 +1754,7 @@ static const struct airoha_pinctrl_func_group an7583_phy1_led1_func_group[] = { LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(0)), AIROHA_PINCTRL_PHY_LED1("gpio10", GPIO_LAN2_LED1_MODE_MASK, LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(0)), - AIROHA_PINCTRL_PHY_LED1("gpio1", GPIO_LAN3_LED1_MODE_MASK, + AIROHA_PINCTRL_PHY_LED1("gpio11", GPIO_LAN3_LED1_MODE_MASK, LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(0)), }; From 30d6552f7d8c4fd8fd4f198eea7a5f7035e9c615 Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Sat, 6 Jun 2026 05:03:42 +0300 Subject: [PATCH 1348/2758] pinctrl: airoha: an7583: remove undefined groups from pcm_spi pin function [ Upstream commit 7b87a686a5ab138b3f8ca3d7e3489d8371c02695 ] pcm_spi_int, pcm_spi_cs2, pcm_spi_cs3, pcm_spi_cs4 pin groups are not defined, so pcm_spi function can't be applied to these groups. Fixes: 3ffeb17a9a27 ("pinctrl: airoha: add support for Airoha AN7583 PINs") Signed-off-by: Mikhail Kshevetskiy Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/mediatek/pinctrl-airoha.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/pinctrl/mediatek/pinctrl-airoha.c b/drivers/pinctrl/mediatek/pinctrl-airoha.c index b73ab60d00659..bf5ebb31e6350 100644 --- a/drivers/pinctrl/mediatek/pinctrl-airoha.c +++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c @@ -877,10 +877,8 @@ static const char *const pcm_spi_groups[] = { "pcm_spi", "pcm_spi_int", "pcm_spi_cs2_p156", "pcm_spi_cs2_p128", "pcm_spi_cs3", "pcm_spi_cs4" }; -static const char *const an7583_pcm_spi_groups[] = { "pcm_spi", "pcm_spi_int", - "pcm_spi_rst", "pcm_spi_cs1", - "pcm_spi_cs2", "pcm_spi_cs3", - "pcm_spi_cs4" }; +static const char *const an7583_pcm_spi_groups[] = { "pcm_spi", + "pcm_spi_rst", "pcm_spi_cs1" }; static const char *const i2s_groups[] = { "i2s" }; static const char *const emmc_groups[] = { "emmc" }; static const char *const pnand_groups[] = { "pnand" }; From dc450743c42e600dd2feb9661c2c3358de427fcd Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Fri, 27 Mar 2026 11:30:06 +0000 Subject: [PATCH 1349/2758] arm64: dts: allwinner: a523: Add missing GPIO interrupt [ Upstream commit 6b81aa0c8a4f038712fa549e4d44d8279eeb0440 ] Even though the Allwinner A523 SoC implements 10 GPIO banks, it has actually registers for 11 IRQ banks, and even an interrupt assigned to the first, non-implemented IRQ bank. Add that first interrupt to the list of GPIO interrupts, to correct the association between IRQs and GPIO banks. This fixes GPIO IRQ operation on boards with A523 SoCs, as seen by broken SD card detect functionality, for instance. Signed-off-by: Andre Przywara Fixes: 35ac96f79664 ("arm64: dts: allwinner: Add Allwinner A523 .dtsi file") Reviewed-by: Chen-Yu Tsai Reviewed-by: Jernej Skrabec Link: https://patch.msgid.link/20260327113006.3135663-4-andre.przywara@arm.com Signed-off-by: Chen-Yu Tsai Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi b/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi index 5afa8d92acbfb..b71dc1d78987a 100644 --- a/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi @@ -128,7 +128,8 @@ pio: pinctrl@2000000 { compatible = "allwinner,sun55i-a523-pinctrl"; reg = <0x2000000 0x800>; - interrupts = , + interrupts = , + , , , , From d657d18d1c699b0b8cf0b5b8bddf71ec70a7b194 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Thu, 11 Jun 2026 14:22:21 +0100 Subject: [PATCH 1350/2758] ASoC: cs35l56: Fix possible uninitialized value in cs35l56_spi_system_reset() [ Upstream commit 007699d278a655871b07d45a1268761260d03124 ] In cs35l56_spi_system_reset() initialize val to zero before using it in the read_poll_timeout(). This prevents testing an uninitialized value if the regmap_read_bypassed() returns an error. Read errors are intentionally ignored during this loop because the device is resetting (though SPI can't really detect that so shouldn't fail because of that, it's safer to ignore errors and keep polling). Because of this, val must be initialized to something in case the first read fails. The polling loop is looking for a non-zero value, so initializing val to 0 will ensure that the loop continues until a valid state is read from the device or it times out. Fixes: 769c1b79295c ("ASoC: cs35l56: Prevent races when soft-resetting using SPI control") Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260611132221.1100497-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/cs35l56-shared.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index e04b114a32b7f..5405f70b7119d 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -534,6 +534,7 @@ static void cs35l56_spi_system_reset(struct cs35l56_base *cs35l56_base) * The regmap must remain in cache-only until the chip has * booted, so use a bypassed read. */ + val = 0; ret = read_poll_timeout(regmap_read_bypassed, read_ret, (val > 0) && (val < 0xffffffff), CS35L56_HALO_STATE_POLL_US, From 27798cc01c28acbdaf2151185ed742b9d86dd41c Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 8 Jun 2026 16:19:08 +0200 Subject: [PATCH 1351/2758] s390/process: Fix kernel thread function pointer type [ Upstream commit d0478f5d3cba1095bfdeb43a9b063c10cdebef14 ] In case of a kernel thread __ret_from_fork() calls the specified function indirectly. Fix the kernel thread function pointer, since kernel threads return an int instead of void. Fixes: 56e62a737028 ("s390: convert to generic entry") Reviewed-by: Alexander Gordeev Signed-off-by: Heiko Carstens Signed-off-by: Alexander Gordeev Signed-off-by: Sasha Levin --- arch/s390/kernel/process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index 0df95dcb21012..e4bd273e31f4e 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c @@ -50,7 +50,7 @@ void ret_from_fork(void) asm("ret_from_fork"); void __ret_from_fork(struct task_struct *prev, struct pt_regs *regs) { - void (*func)(void *arg); + int (*func)(void *arg); schedule_tail(prev); From 0704c04769ccc1a0939682db35d816dfa5fe75dc Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Mon, 1 Jun 2026 04:30:55 -0700 Subject: [PATCH 1352/2758] Bluetooth: hci_qca: fix NULL pointer dereference in qca_setup() for non-serdev device [ Upstream commit 3ec629fee178d429f01ae843e4ea888de93012bf ] hu->serdev is NULL for hci_uart attached via non-serdev paths, but qca_setup() unconditionally calls serdev_device_get_drvdata(hu->serdev) and dereferences the result, causing a NULL pointer dereference. Fix by guarding the dereference with a NULL check, consistent with the rest of qca_setup(). Fixes: 22d893eec0d5 ("Bluetooth: hci_qca: Refactor HFP hardware offload capability handling") Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- drivers/bluetooth/hci_qca.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 34500137df2c1..cc7b34a61fa78 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -1916,9 +1916,12 @@ static int qca_setup(struct hci_uart *hu) const char *rampatch_name = qca_get_rampatch_name(hu); int ret; struct qca_btsoc_version ver; - struct qca_serdev *qcadev = serdev_device_get_drvdata(hu->serdev); + struct qca_serdev *qcadev = NULL; const char *soc_name; + if (hu->serdev) + qcadev = serdev_device_get_drvdata(hu->serdev); + ret = qca_check_speeds(hu); if (ret) return ret; @@ -1980,7 +1983,7 @@ static int qca_setup(struct hci_uart *hu) case QCA_WCN6750: case QCA_WCN6855: case QCA_WCN7850: - if (qcadev->bdaddr_property_broken) + if (qcadev && qcadev->bdaddr_property_broken) hci_set_quirk(hdev, HCI_QUIRK_BDADDR_PROPERTY_BROKEN); hci_set_aosp_capable(hdev); @@ -2073,7 +2076,7 @@ static int qca_setup(struct hci_uart *hu) else hu->hdev->set_bdaddr = qca_set_bdaddr; - if (qcadev->support_hfp_hw_offload) + if (qcadev && qcadev->support_hfp_hw_offload) qca_configure_hfp_offload(hdev); qca->fw_version = le16_to_cpu(ver.patch_ver); From d54a5fb7d821b88c7c5569d450af59dafccaf414 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Mon, 1 Jun 2026 04:30:56 -0700 Subject: [PATCH 1353/2758] Bluetooth: hci_qca: fix NULL pointer dereference in qca_dmp_hdr() for non-serdev device [ Upstream commit 6b8cbcf08de0db62254d1981f83db0f94681ccd9 ] hu->serdev is NULL for hci_uart attached via non-serdev paths, but qca_dmp_hdr() unconditionally dereferences hu->serdev->dev.driver->name, causing a NULL pointer dereference. Fix by guarding the dereference with a NULL check and falling back to "hci_ldisc_qca" for the non-serdev case. Fixes: 06d3fdfcdf5c ("Bluetooth: hci_qca: Add qcom devcoredump support") Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- drivers/bluetooth/hci_qca.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index cc7b34a61fa78..2444471956197 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -1028,7 +1028,7 @@ static void qca_dmp_hdr(struct hci_dev *hdev, struct sk_buff *skb) skb_put_data(skb, buf, strlen(buf)); snprintf(buf, sizeof(buf), "Driver: %s\n", - hu->serdev->dev.driver->name); + hu->serdev ? hu->serdev->dev.driver->name : "hci_ldisc_qca"); skb_put_data(skb, buf, strlen(buf)); } From 57077eeb586c42f124bc09e018449362223067b3 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Wed, 3 Jun 2026 01:06:21 +0800 Subject: [PATCH 1354/2758] Bluetooth: eir: Fix stack OOB write when prepending the Flags AD [ Upstream commit 6f5fb689fdf80bdd143f22a502f9eb1f3c85e286 ] eir_create_adv_data() builds the advertising data into a fixed-size buffer ("size", 31 for the legacy path). It may prepend a 3-byte "Flags" AD structure (LE_AD_NO_BREDR on an LE-only controller) and then copies the per-instance data without checking that it still fits: memcpy(ptr, adv->adv_data, adv->adv_data_len); tlv_data_max_len() only reserves those 3 bytes when the user-supplied flags carry a managed-flags bit, so an instance added with flags == 0 is accepted with adv_data_len up to the full buffer. At advertise time the flags are still prepended, and the memcpy() writes 3 + adv_data_len bytes into the size-byte buffer: BUG: KASAN: stack-out-of-bounds in eir_create_adv_data (net/bluetooth/eir.c:301) Write of size 31 at addr ffff88800a547bdc by task kworker/u9:0/65 Workqueue: hci0 hci_cmd_sync_work __asan_memcpy (mm/kasan/shadow.c:106) eir_create_adv_data (net/bluetooth/eir.c:301) hci_update_adv_data_sync (net/bluetooth/hci_sync.c:1310) hci_schedule_adv_instance_sync (net/bluetooth/hci_sync.c:1817) hci_cmd_sync_work (net/bluetooth/hci_sync.c:332) This frame has 1 object: [32, 64) 'cp' The "Flags" structure is added by the kernel, not requested by userspace, so only prepend it when it fits together with the instance advertising data; when there is no room for both, drop the flags rather than the user-provided data. Reachable by a local user with CAP_NET_ADMIN owning an LE-only controller on the legacy advertising path. Fixes: b44133ff03be ("Bluetooth: Support the "discoverable" adv flag") Reported-by: Xiang Mei Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Weiming Shi Reported-by: Xiang Mei Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/eir.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/net/bluetooth/eir.c b/net/bluetooth/eir.c index 3f72111ba651f..1de5f9df6eec0 100644 --- a/net/bluetooth/eir.c +++ b/net/bluetooth/eir.c @@ -283,10 +283,12 @@ u8 eir_create_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr, u8 size) if (!flags) flags |= mgmt_get_adv_discov_flags(hdev); - /* If flags would still be empty, then there is no need to - * include the "Flags" AD field". + /* Only add the "Flags" if it fits together with the instance + * advertising data; drop it rather than overflow the buffer. */ - if (flags && (ad_len + eir_precalc_len(1) <= size)) { + if (flags && + (ad_len + eir_precalc_len(1) + + (adv ? adv->adv_data_len : 0) <= size)) { ptr[0] = 0x02; ptr[1] = EIR_FLAGS; ptr[2] = flags; From e4053143e7dc0c86958351e81d618ffc42302828 Mon Sep 17 00:00:00 2001 From: Jiajia Liu Date: Tue, 2 Jun 2026 15:00:32 +0800 Subject: [PATCH 1355/2758] Bluetooth: hci_event: fix simultaneous discovery stuck in FINDING [ Upstream commit 96d006ae6445679436b945593950fd465eba7e76 ] When hci_inquiry_complete_evt is called between le_scan_disable and le_set_scan_enable_complete and no remote name needs to be resolved, the interleaved discovery with SIMULTANEOUS quirk gets stuck in DISCOVERY_FINDING. le_set_scan_enable_complete does not check inquiry state. No one sets DISCOVERY_STOPPED in this process. Add state check in le_set_scan_enable_complete and change state if the state is DISCOVERY_FINDING. Tested with AX201 (8087:0026) in Dell Vostro 13. Discovering disabled MGMT Event below is reported when running into the above condition. @ MGMT Command: Start Discovery (0x0023) {0x0001} [hci0] 10885.970873 Address type: 0x07 BR/EDR LE Public LE Random ... < HCI Command: LE Set Extended Scan Enable #38205 [hci0] 10886.131438 Extended scan: Enabled (0x01) Filter duplicates: Enabled (0x01) Duration: 0 msec (0x0000) Period: 0.00 sec (0x0000) > HCI Event: Command Complete (0x0e) plen 4 #38206 [hci0] 10886.133295 LE Set Extended Scan Enable (0x08|0x0042) ncmd 2 Status: Success (0x00) @ MGMT Event: Discovering (0x0013) plen 2 {0x0001} [hci0] 10886.133414 Address type: 0x07 BR/EDR LE Public LE Random Discovery: Enabled (0x01) < HCI Command: Inquiry (0x01|0x0001) plen 5 #38207 [hci0] 10886.133528 Access code: 0x9e8b33 (General Inquiry) Length: 10.24s (0x08) Num responses: 0 > HCI Event: Command Status (0x0f) plen 4 #38208 [hci0] 10886.141333 Inquiry (0x01|0x0001) ncmd 2 Status: Success (0x00) ... < HCI Command: LE Set Extended Scan Enable #38242 [hci0] 10896.381802 Extended scan: Disabled (0x00) Filter duplicates: Disabled (0x00) Duration: 0 msec (0x0000) Period: 0.00 sec (0x0000) > HCI Event: Inquiry Complete (0x01) plen 1 #38243 [hci0] 10896.383419 Status: Success (0x00) > HCI Event: Command Complete (0x0e) plen 4 #38244 [hci0] 10896.394378 LE Set Extended Scan Enable (0x08|0x0042) ncmd 2 Status: Success (0x00) @ MGMT Event: Device Found (0x0012) plen 22 {0x0001} [hci0] 10896.394497 LE Address: 88:12:AC:92:43:69 RSSI: -101 dBm (0x9b) Flags: 0x00000004 Not Connectable Data length: 8 Company: Xiaomi Inc. (911) Data[0]: 16-bit Service UUIDs (complete): 1 entry Xiaomi Inc. (0xfdaa) @ MGMT Event: Discovering (0x0013) plen 2 {0x0001} [hci0] 10896.394506 Address type: 0x07 BR/EDR LE Public LE Random Discovery: Disabled (0x00) Fixes: 8ffde2a73f2c ("Bluetooth: Convert le_scan_disable timeout to hci_sync") Signed-off-by: Jiajia Liu Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/hci_event.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index eea2f810aafab..1cd5f97daafe3 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1769,6 +1769,13 @@ static void le_set_scan_enable_complete(struct hci_dev *hdev, u8 enable) hci_dev_clear_flag(hdev, HCI_LE_SCAN); + if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED && + hci_test_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY) && + !test_bit(HCI_INQUIRY, &hdev->flags) && + hdev->discovery.state == DISCOVERY_FINDING) { + hci_discovery_set_state(hdev, DISCOVERY_STOPPED); + } + /* The HCI_LE_SCAN_INTERRUPTED flag indicates that we * interrupted scanning due to a connect request. Mark * therefore discovery as stopped. From 672d52d9412252e61b8de8d773ccdf5a277cf540 Mon Sep 17 00:00:00 2001 From: Jordan Walters Date: Wed, 3 Jun 2026 04:50:47 -0400 Subject: [PATCH 1356/2758] Bluetooth: hci_core: Fix UAF in hci_unregister_dev() [ Upstream commit 5edcc018fa6e80b2c478454a4a8229c23d67c181 ] hci_unregister_dev() does not disable cmd_timer and ncmd_timer before the hci_dev structure is freed. If a timeout fires during device teardown, the callback dereferences freed memory (including the hdev->reset function pointer), leading to a use-after-free. Add disable_delayed_work_sync() calls alongside the existing disable_work_sync() calls to ensure both timers are fully quiesced before teardown proceeds. Fixes: 0d151a103775 ("Bluetooth: hci_core: cancel all works upon hci_unregister_dev()") Signed-off-by: Jordan Walters Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/hci_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 28d7929dc5937..1cbc666527c57 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2671,6 +2671,8 @@ void hci_unregister_dev(struct hci_dev *hdev) disable_work_sync(&hdev->tx_work); disable_work_sync(&hdev->power_on); disable_work_sync(&hdev->error_reset); + disable_delayed_work_sync(&hdev->cmd_timer); + disable_delayed_work_sync(&hdev->ncmd_timer); hci_cmd_sync_clear(hdev); From 4e354991da5890d5ce7bfea3f66fb897ae301756 Mon Sep 17 00:00:00 2001 From: Zhao Dongdong Date: Thu, 4 Jun 2026 19:46:40 +0800 Subject: [PATCH 1357/2758] Bluetooth: btmtk: fix URB leak in alloc_mtk_intr_urb error path [ Upstream commit f396f4005180928cd9e15e352a6512865d3bc908 ] When btmtk_isopkt_pad() fails, the previously allocated URB is not freed, leaking the urb structure. Add usb_free_urb() before returning the error. Fixes: ceac1cb0259d ("Bluetooth: btusb: mediatek: add ISO data transmission functions") Signed-off-by: Zhao Dongdong Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- drivers/bluetooth/btmtk.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c index 8ff66b276af03..c29e1841b2b3f 100644 --- a/drivers/bluetooth/btmtk.c +++ b/drivers/bluetooth/btmtk.c @@ -1074,8 +1074,10 @@ struct urb *alloc_mtk_intr_urb(struct hci_dev *hdev, struct sk_buff *skb, if (!urb) return ERR_PTR(-ENOMEM); - if (btmtk_isopkt_pad(hdev, skb)) + if (btmtk_isopkt_pad(hdev, skb)) { + usb_free_urb(urb); return ERR_PTR(-EINVAL); + } pipe = usb_sndintpipe(btmtk_data->udev, btmtk_data->isopkt_tx_ep->bEndpointAddress); From 5cdc6763279b0eb704a4fa88d66d65fadf577f10 Mon Sep 17 00:00:00 2001 From: Sai Teja Aluvala Date: Sun, 7 Jun 2026 11:51:17 +0530 Subject: [PATCH 1358/2758] Bluetooth: btintel_pcie: Load IOSF debug regs by controller variant [ Upstream commit e43b33bf8d671c50a45fe5f487819927595bbd50 ] Load the IOSF DBGC base address based on the controller hardware variant when reading DRAM buffers during a trace dump. Scorpius Peak family controllers (SCP/SCP2/SCP2F) use a different DBGC base address (0xf0d5d500) than Blazar family controllers (BZRI/BZRIW, 0xf3800300). Fixes: 07e6bddb54b4 ("Bluetooth: btintel_pcie: Add support for device coredump") Signed-off-by: Sai Teja Aluvala Signed-off-by: Kiran K Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- drivers/bluetooth/btintel.h | 1 + drivers/bluetooth/btintel_pcie.c | 30 ++++++++++++++++++++++++------ drivers/bluetooth/btintel_pcie.h | 4 ++++ 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h index 0e9ca99aaaaea..035ed1ea6ce96 100644 --- a/drivers/bluetooth/btintel.h +++ b/drivers/bluetooth/btintel.h @@ -77,6 +77,7 @@ struct intel_tlv { #define BTINTEL_HWID_BZRU 0x1d /* BlazarU - Meteor Lake */ #define BTINTEL_HWID_SCP 0x1f /* Scorpius Peak - Panther Lake */ #define BTINTEL_HWID_SCP2 0x20 /* Scorpius Peak2 - Nova Lake */ +#define BTINTEL_HWID_SCP2F 0x21 /* Scorpius Peak2-F - Nova Lake */ #define BTINTEL_HWID_BZRIW 0x22 /* BlazarIW - Wildcat Lake */ struct intel_version_tlv { diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c index 37e0507636330..d4426ac767fd8 100644 --- a/drivers/bluetooth/btintel_pcie.c +++ b/drivers/bluetooth/btintel_pcie.c @@ -626,9 +626,10 @@ static void *btintel_pcie_copy_tlv(void *dest, enum btintel_pcie_tlv_type type, static int btintel_pcie_read_dram_buffers(struct btintel_pcie_data *data) { u32 offset, prev_size, wr_ptr_status, dump_size, data_len; + u32 status_reg, wrap_reg; struct btintel_pcie_dbgc *dbgc = &data->dbgc; struct hci_dev *hdev = data->hdev; - u8 *pdata, *p, buf_idx; + u8 *pdata, *p, buf_idx, hw_variant; struct intel_tlv *tlv; struct timespec64 now; struct tm tm_now; @@ -641,7 +642,28 @@ static int btintel_pcie_read_dram_buffers(struct btintel_pcie_data *data) return -EOPNOTSUPP; - wr_ptr_status = btintel_pcie_rd_dev_mem(data, BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS); + hw_variant = INTEL_HW_VARIANT(data->cnvi); + switch (hw_variant) { + case BTINTEL_HWID_BZRI: + case BTINTEL_HWID_BZRIW: + status_reg = BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS; + wrap_reg = BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND; + break; + case BTINTEL_HWID_SCP: + case BTINTEL_HWID_SCP2: + case BTINTEL_HWID_SCP2F: + status_reg = BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS_SCP; + wrap_reg = BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND_SCP; + break; + default: + bt_dev_err(hdev, "Unsupported Intel hardware variant (0x%2.2x)", + hw_variant); + return -EINVAL; + } + + wr_ptr_status = btintel_pcie_rd_dev_mem(data, status_reg); + data->dmp_hdr.wrap_ctr = btintel_pcie_rd_dev_mem(data, wrap_reg); + offset = wr_ptr_status & BTINTEL_PCIE_DBG_OFFSET_BIT_MASK; buf_idx = BTINTEL_PCIE_DBGC_DBG_BUF_IDX(wr_ptr_status); @@ -718,10 +740,6 @@ static int btintel_pcie_read_dram_buffers(struct btintel_pcie_data *data) sizeof(data->dmp_hdr.write_ptr)); p = btintel_pcie_copy_tlv(p, BTINTEL_WRAP_CTR, &data->dmp_hdr.wrap_ctr, sizeof(data->dmp_hdr.wrap_ctr)); - - data->dmp_hdr.wrap_ctr = btintel_pcie_rd_dev_mem(data, - BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND); - p = btintel_pcie_copy_tlv(p, BTINTEL_TRIGGER_REASON, &data->dmp_hdr.trigger_reason, sizeof(data->dmp_hdr.trigger_reason)); p = btintel_pcie_copy_tlv(p, BTINTEL_FW_SHA, &data->dmp_hdr.fw_git_sha1, diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h index 13efef499e4e8..c7db89af49056 100644 --- a/drivers/bluetooth/btintel_pcie.h +++ b/drivers/bluetooth/btintel_pcie.h @@ -68,6 +68,10 @@ #define BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS (BTINTEL_PCIE_DBGC_BASE_ADDR + 0x1C) #define BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND (BTINTEL_PCIE_DBGC_BASE_ADDR + 0x2C) +#define BTINTEL_PCIE_DBGC_BASE_ADDR_SCP (0xf0d5d500) +#define BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS_SCP (BTINTEL_PCIE_DBGC_BASE_ADDR_SCP + 0x1C) +#define BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND_SCP (BTINTEL_PCIE_DBGC_BASE_ADDR_SCP + 0x2C) + #define BTINTEL_PCIE_DBG_IDX_BIT_MASK 0x0F #define BTINTEL_PCIE_DBGC_DBG_BUF_IDX(data) (((data) >> 24) & BTINTEL_PCIE_DBG_IDX_BIT_MASK) #define BTINTEL_PCIE_DBG_OFFSET_BIT_MASK 0xFFFFFF From 4bc16db0f11918e07edf9fdcda4a30cf4c9df45c Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Mon, 8 Jun 2026 23:56:28 +0000 Subject: [PATCH 1359/2758] Bluetooth: hci: validate codec capability element length [ Upstream commit c38fbcdc407925c7088f7e5f11c1fff73d2d35a2 ] Read Local Codec Capabilities returns a sequence of capability elements. Each element starts with a one-byte length followed by that many payload bytes. hci_read_codec_capabilities() checks that the skb contains the length byte, but then validates only caps->len against the remaining skb length. A malformed controller response with one remaining byte and caps->len set to one passes that check even though the element needs two bytes. The parser then records a two-byte capability and copies one byte beyond the advertised response payload into the codec list. Validate the full element size, including the length byte, before adding it to the accumulated capability length. This preserves all well-formed capability elements and drops only truncated controller responses. Fixes: 8961987f3f5f ("Bluetooth: Enumerate local supported codec and cache details") Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/hci_codec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/hci_codec.c b/net/bluetooth/hci_codec.c index 3cc135bb1d30c..5bc5003c387c6 100644 --- a/net/bluetooth/hci_codec.c +++ b/net/bluetooth/hci_codec.c @@ -100,7 +100,7 @@ static void hci_read_codec_capabilities(struct hci_dev *hdev, __u8 transport, caps = (void *)skb->data; if (skb->len < sizeof(*caps)) goto error; - if (skb->len < caps->len) + if (skb->len < sizeof(caps->len) + caps->len) goto error; len += sizeof(caps->len) + caps->len; skb_pull(skb, sizeof(caps->len) + caps->len); From a64e19774f213c5ccd9114a83db84dceac9dffd4 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Mon, 8 Jun 2026 23:58:23 +0000 Subject: [PATCH 1360/2758] Bluetooth: vhci: validate devcoredump state before side effects [ Upstream commit 88c2404a3c59c3126453919388dbd5ed98ed01bd ] The VHCI force_devcoredump debugfs hook accepts a small test record from userspace. It validates the requested terminal state only after registering, initializing and appending a Bluetooth devcoredump. As a result, an invalid state returns -EINVAL but still leaves queued devcoredump work behind. With a non-zero timeout field, the rejected write can still emit a devcoredump after the timeout expires. Reject unsupported states before allocating the skb or changing the HCI devcoredump state machine. Fixes: ab4e4380d4e1 ("Bluetooth: Add vhci devcoredump support") Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- drivers/bluetooth/hci_vhci.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index 2762eacf7f20e..eddb32b30539f 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -337,7 +337,17 @@ static ssize_t force_devcd_write(struct file *file, const char __user *user_buf, if (copy_from_user(&dump_data, user_buf, count)) return -EFAULT; + switch (dump_data.state) { + case HCI_DEVCOREDUMP_DONE: + case HCI_DEVCOREDUMP_ABORT: + case HCI_DEVCOREDUMP_TIMEOUT: + break; + default: + return -EINVAL; + } + data_size = count - offsetof(struct devcoredump_test_data, data); + skb = alloc_skb(data_size, GFP_ATOMIC); if (!skb) return -ENOMEM; From 8386cf70210a1506d767be3158c593b4d00e29f2 Mon Sep 17 00:00:00 2001 From: Michael Guralnik Date: Wed, 10 Jun 2026 03:01:37 +0300 Subject: [PATCH 1361/2758] RDMA/mlx5: Fix mkey creation error flow rollback [ Upstream commit c70fcfa9881207659ad193ae10a3bd56b2ae3f8a ] Fix the indices of mkeys destroyed in case of an error in batch mkey creation. Fixes: 36680ef7bceb ("RDMA/mlx5: Switch from MR cache to FRMR pools") Link: https://patch.msgid.link/r/20260610000145.820592-2-michaelgur@nvidia.com Signed-off-by: Michael Guralnik Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/mr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index 41ca3b1a6db07..253545ae39fd5 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -294,7 +294,7 @@ static int mlx5r_create_mkeys(struct ib_device *device, struct ib_frmr_key *key, free_in: kfree(in); if (err) - for (; i > 0; i--) + for (i--; i >= 0; i--) mlx5_core_destroy_mkey(dev->mdev, handles[i]); return err; } From 220a32cd43c4814f680257b026d70f8f78210f16 Mon Sep 17 00:00:00 2001 From: Michael Guralnik Date: Wed, 10 Jun 2026 03:01:38 +0300 Subject: [PATCH 1362/2758] RDMA/mlx5: Fix TPH extraction in FRMR pool key [ Upstream commit fe683274fee497834d2e6b54b7342642e1f21892 ] Fix reading the PH value from the FRMR pool key by shifting the pool key to the relevant bits. Fixes: 36680ef7bceb ("RDMA/mlx5: Switch from MR cache to FRMR pools") Link: https://patch.msgid.link/r/20260610000145.820592-3-michaelgur@nvidia.com Signed-off-by: Michael Guralnik Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/mr.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index 253545ae39fd5..7fbb01371f1f7 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -31,6 +31,7 @@ * SOFTWARE. */ +#include #include #include #include @@ -163,9 +164,8 @@ static int get_unchangeable_access_flags(struct mlx5_ib_dev *dev, #define MLX5_FRMR_POOLS_KEY_VENDOR_KEY_SUPPORTED \ MLX5_FRMR_POOLS_KEY_ACCESS_MODE_KSM_MASK -#define MLX5_FRMR_POOLS_KERNEL_KEY_PH_SHIFT 16 -#define MLX5_FRMR_POOLS_KERNEL_KEY_PH_MASK 0xFF0000 -#define MLX5_FRMR_POOLS_KERNEL_KEY_ST_INDEX_MASK 0xFFFF +#define MLX5_FRMR_POOLS_KERNEL_KEY_PH_MASK GENMASK_ULL(23, 16) +#define MLX5_FRMR_POOLS_KERNEL_KEY_ST_INDEX_MASK GENMASK_ULL(15, 0) static struct mlx5_ib_mr * _mlx5_frmr_pool_alloc(struct mlx5_ib_dev *dev, struct ib_umem *umem, @@ -194,7 +194,8 @@ _mlx5_frmr_pool_alloc(struct mlx5_ib_dev *dev, struct ib_umem *umem, ph ^= MLX5_IB_NO_PH; mr->ibmr.frmr.key.kernel_vendor_key = - st_index | (ph << MLX5_FRMR_POOLS_KERNEL_KEY_PH_SHIFT); + FIELD_PREP(MLX5_FRMR_POOLS_KERNEL_KEY_ST_INDEX_MASK, st_index) | + FIELD_PREP(MLX5_FRMR_POOLS_KERNEL_KEY_PH_MASK, ph); err = ib_frmr_pool_pop(&dev->ib_dev, &mr->ibmr); if (err) { kfree(mr); @@ -271,9 +272,10 @@ static int mlx5r_create_mkeys(struct ib_device *device, struct ib_frmr_key *key, get_mkc_octo_size(access_mode, key->num_dma_blocks)); MLX5_SET(mkc, mkc, log_page_size, PAGE_SHIFT); - st_index = key->kernel_vendor_key & - MLX5_FRMR_POOLS_KERNEL_KEY_ST_INDEX_MASK; - ph = key->kernel_vendor_key & MLX5_FRMR_POOLS_KERNEL_KEY_PH_MASK; + st_index = FIELD_GET(MLX5_FRMR_POOLS_KERNEL_KEY_ST_INDEX_MASK, + key->kernel_vendor_key); + ph = FIELD_GET(MLX5_FRMR_POOLS_KERNEL_KEY_PH_MASK, + key->kernel_vendor_key); if (ph) { /* Normalize ph: swap MLX5_IB_NO_PH for 0 */ if (ph == MLX5_IB_NO_PH) From 97fa194ba9a66fc464897d7752c212ebcc7ff6cb Mon Sep 17 00:00:00 2001 From: Michael Guralnik Date: Wed, 10 Jun 2026 03:01:39 +0300 Subject: [PATCH 1363/2758] RDMA/core: Fix skipped usage for driver built FRMR key [ Upstream commit 3d7fd88aeff73f25ee740b3a65a3b4dd38ad7783 ] When creating FRMR handles following a netlink command to pin handles, use the key after driver callback instead of using the key passed directly from user. Fixes: 020d189d16a6 ("RDMA/core: Add pinned handles to FRMR pools") Link: https://patch.msgid.link/r/20260610000145.820592-4-michaelgur@nvidia.com Signed-off-by: Michael Guralnik Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/core/frmr_pools.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/core/frmr_pools.c b/drivers/infiniband/core/frmr_pools.c index 5e992ff3d7cff..6170466ea9581 100644 --- a/drivers/infiniband/core/frmr_pools.c +++ b/drivers/infiniband/core/frmr_pools.c @@ -426,7 +426,7 @@ int ib_frmr_pools_set_pinned(struct ib_device *device, struct ib_frmr_key *key, if (!handles) return -ENOMEM; - ret = pools->pool_ops->create_frmrs(device, key, handles, + ret = pools->pool_ops->create_frmrs(device, &driver_key, handles, needed_handles); if (ret) { kfree(handles); From edf133d28fc43f7f8b0da43a8d5b93fdf8073d35 Mon Sep 17 00:00:00 2001 From: Michael Guralnik Date: Wed, 10 Jun 2026 03:01:40 +0300 Subject: [PATCH 1364/2758] RDMA/core: Fix FRMR aging push to queue error flow [ Upstream commit c6936506ed556ce3ccad36ab999baf2764dd7d25 ] Aging pools with pinned handles requires moving handles from the active queue to a non-empty inactive queue that might fail on new page allocation, we are currently not handling the fault and leaking any mkey that fails the push. Fix by Introducing push_queue_to_queue_locked() that fills the destination's partial tail page from the source and then splices the remaining source pages onto the destination, performing no allocation. Replace the per-handle move loop in age_pinned_pool() and the open-coded splice in pool_aging_work() with calls to the helper. As the helper cannot fail under memory pressure, removing a class of GFP_ATOMIC allocations under the pool lock and simplifying the error flow. Fixes: 020d189d16a6 ("RDMA/core: Add pinned handles to FRMR pools") Link: https://patch.msgid.link/r/20260610000145.820592-5-michaelgur@nvidia.com Signed-off-by: Michael Guralnik Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/core/frmr_pools.c | 53 ++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/drivers/infiniband/core/frmr_pools.c b/drivers/infiniband/core/frmr_pools.c index 6170466ea9581..927642c06f3a4 100644 --- a/drivers/infiniband/core/frmr_pools.c +++ b/drivers/infiniband/core/frmr_pools.c @@ -97,13 +97,44 @@ static void destroy_all_handles_in_queue(struct ib_device *device, } } +/* + * Bulk-move all handles from @src into @dst without allocating new pages. + * If @dst has a partial tail page, fill it handle-by-handle from @src first + * to preserve the invariant that only the tail page is partial, then splice + * the remaining @src pages onto @dst. On return @src is empty. + * + * Caller must hold the lock protecting both queues. + */ +static void splice_frmr_queue_locked(struct frmr_queue *dst, + struct frmr_queue *src) +{ + u32 free_in_tail = dst->ci % NUM_HANDLES_PER_PAGE; + u32 handle; + + if (free_in_tail) { + free_in_tail = NUM_HANDLES_PER_PAGE - free_in_tail; + while (free_in_tail && src->ci) { + handle = pop_handle_from_queue_locked(src); + push_handle_to_queue_locked(dst, handle); + free_in_tail--; + } + } + + if (src->ci > 0) { + list_splice_tail_init(&src->pages_list, &dst->pages_list); + dst->num_pages += src->num_pages; + dst->ci += src->ci; + src->num_pages = 0; + src->ci = 0; + } +} + static bool age_pinned_pool(struct ib_device *device, struct ib_frmr_pool *pool) { struct ib_frmr_pools *pools = device->frmr_pools; u32 total, to_destroy, destroyed = 0; bool has_work = false; u32 *handles; - u32 handle; spin_lock(&pool->lock); total = pool->queue.ci + pool->inactive_queue.ci + pool->in_use; @@ -112,7 +143,7 @@ static bool age_pinned_pool(struct ib_device *device, struct ib_frmr_pool *pool) return false; } - to_destroy = total - pool->pinned_handles; + to_destroy = min(total - pool->pinned_handles, pool->inactive_queue.ci); handles = kcalloc(to_destroy, sizeof(*handles), GFP_ATOMIC); if (!handles) { @@ -121,15 +152,13 @@ static bool age_pinned_pool(struct ib_device *device, struct ib_frmr_pool *pool) } /* Destroy all excess handles in the inactive queue */ - while (pool->inactive_queue.ci && destroyed < to_destroy) { - handles[destroyed++] = pop_handle_from_queue_locked( + for (; destroyed < to_destroy; destroyed++) + handles[destroyed] = pop_handle_from_queue_locked( &pool->inactive_queue); - } /* Move all handles from regular queue to inactive queue */ - while (pool->queue.ci) { - handle = pop_handle_from_queue_locked(&pool->queue); - push_handle_to_queue_locked(&pool->inactive_queue, handle); + if (pool->queue.ci > 0) { + splice_frmr_queue_locked(&pool->inactive_queue, &pool->queue); has_work = true; } @@ -158,13 +187,7 @@ static void pool_aging_work(struct work_struct *work) /* Move all pages from regular queue to inactive queue */ spin_lock(&pool->lock); if (pool->queue.ci > 0) { - list_splice_tail_init(&pool->queue.pages_list, - &pool->inactive_queue.pages_list); - pool->inactive_queue.num_pages = pool->queue.num_pages; - pool->inactive_queue.ci = pool->queue.ci; - - pool->queue.num_pages = 0; - pool->queue.ci = 0; + splice_frmr_queue_locked(&pool->inactive_queue, &pool->queue); has_work = true; } spin_unlock(&pool->lock); From a487c14dda02a649d84c8303772430d32bd76eb9 Mon Sep 17 00:00:00 2001 From: Michael Guralnik Date: Wed, 10 Jun 2026 03:01:41 +0300 Subject: [PATCH 1365/2758] RDMA/core: Fix FRMR set pinned push error path [ Upstream commit 41a707d0275cdec9ac125e826dd6836fa9623cbc ] Add destruction of FRMR handles in case the push to the pool fails. This prevents resources leak in case pool page allocation fails. Fixes: 020d189d16a6 ("RDMA/core: Add pinned handles to FRMR pools") Link: https://patch.msgid.link/r/20260610000145.820592-6-michaelgur@nvidia.com Signed-off-by: Michael Guralnik Reviewed-by: Tao Cui Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/core/frmr_pools.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/core/frmr_pools.c b/drivers/infiniband/core/frmr_pools.c index 927642c06f3a4..1cfdddc3fcdaf 100644 --- a/drivers/infiniband/core/frmr_pools.c +++ b/drivers/infiniband/core/frmr_pools.c @@ -461,11 +461,16 @@ int ib_frmr_pools_set_pinned(struct ib_device *device, struct ib_frmr_key *key, ret = push_handle_to_queue_locked(&pool->queue, handles[i]); if (ret) - goto end; + break; } - -end: spin_unlock(&pool->lock); + + if (ret) { + /* Destroy handles created but never pushed to the pool. */ + pools->pool_ops->destroy_frmrs(device, &handles[i], + needed_handles - i); + } + kfree(handles); schedule_aging: From eadccdba848c6ab9d5a020662c8e102d664a27b4 Mon Sep 17 00:00:00 2001 From: Michael Guralnik Date: Wed, 10 Jun 2026 03:01:42 +0300 Subject: [PATCH 1366/2758] RDMA/core: Avoid NULL dereference on FRMR bad usage [ Upstream commit 3937243095b5cfed6556bd1ea170790223f3eeb0 ] In case a driver calls FRMR pop operation without a successful init, return after triggering a warning to avoid the NULL dereference. Fixes: ce5df0b891ed ("IB/core: Introduce FRMR pools") Link: https://patch.msgid.link/r/20260610000145.820592-7-michaelgur@nvidia.com Signed-off-by: Michael Guralnik Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/core/frmr_pools.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/core/frmr_pools.c b/drivers/infiniband/core/frmr_pools.c index 1cfdddc3fcdaf..892aedfe03bed 100644 --- a/drivers/infiniband/core/frmr_pools.c +++ b/drivers/infiniband/core/frmr_pools.c @@ -529,7 +529,9 @@ int ib_frmr_pool_pop(struct ib_device *device, struct ib_mr *mr) struct ib_frmr_pools *pools = device->frmr_pools; struct ib_frmr_pool *pool; - WARN_ON_ONCE(!device->frmr_pools); + if (WARN_ON_ONCE(!pools)) + return -EINVAL; + pool = ib_frmr_pool_find(pools, &mr->frmr.key); if (!pool) { pool = create_frmr_pool(device, &mr->frmr.key); From d9e9d10e56ab613fa74c47bd33f45ceed6988cc6 Mon Sep 17 00:00:00 2001 From: Michael Guralnik Date: Wed, 10 Jun 2026 03:01:43 +0300 Subject: [PATCH 1367/2758] RDMA/core: Fix FRMR handle leak on push failure [ Upstream commit 8c76126b866649d8e8acc09a06f2b03b6ff88900 ] Failure to push a handle to the pool, caused by ENOMEM on queue page allocation, will trigger missing in_use counter update, skewing pool state indefinitely. Fix that by moving the handling of handle destruction in such case into the FRMR code, ensuring the handle is either pushed to the pool or destroyed inside the same function. Adjust mlx5_ib call site accordingly. Fixes: ce5df0b891ed ("IB/core: Introduce FRMR pools") Link: https://patch.msgid.link/r/20260610000145.820592-8-michaelgur@nvidia.com Signed-off-by: Michael Guralnik Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/core/frmr_pools.c | 19 +++++++++++-------- drivers/infiniband/hw/mlx5/mr.c | 5 +++-- include/rdma/frmr_pools.h | 2 +- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/infiniband/core/frmr_pools.c b/drivers/infiniband/core/frmr_pools.c index 892aedfe03bed..e214a8273df84 100644 --- a/drivers/infiniband/core/frmr_pools.c +++ b/drivers/infiniband/core/frmr_pools.c @@ -549,9 +549,8 @@ EXPORT_SYMBOL(ib_frmr_pool_pop); * @device: The device to push the FRMR handle to. * @mr: The MR containing the FRMR handle to push back to the pool. * - * Returns 0 on success, negative error code on failure. */ -int ib_frmr_pool_push(struct ib_device *device, struct ib_mr *mr) +void ib_frmr_pool_push(struct ib_device *device, struct ib_mr *mr) { struct ib_frmr_pool *pool = mr->frmr.pool; struct ib_frmr_pools *pools = device->frmr_pools; @@ -559,19 +558,23 @@ int ib_frmr_pool_push(struct ib_device *device, struct ib_mr *mr) int ret; spin_lock(&pool->lock); + pool->in_use--; + ret = push_handle_to_queue_locked(&pool->queue, mr->frmr.handle); + /* Schedule aging every time an empty pool becomes non-empty */ - if (pool->queue.ci == 0) + if (!ret && pool->queue.ci == 1) schedule_aging = true; - ret = push_handle_to_queue_locked(&pool->queue, mr->frmr.handle); - if (ret == 0) - pool->in_use--; spin_unlock(&pool->lock); - if (ret == 0 && schedule_aging) + if (ret) { + pools->pool_ops->destroy_frmrs(device, &mr->frmr.handle, 1); + return; + } + + if (schedule_aging) queue_delayed_work(pools->aging_wq, &pool->aging_work, secs_to_jiffies(READ_ONCE(pools->aging_period_sec))); - return ret; } EXPORT_SYMBOL(ib_frmr_pool_push); diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index 7fbb01371f1f7..723594efa094e 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -1398,9 +1398,10 @@ static int mlx5r_handle_mkey_cleanup(struct mlx5_ib_mr *mr) bool is_odp = is_odp_mr(mr); int ret; - if (mr->ibmr.frmr.pool && !mlx5_umr_revoke_mr_with_lock(mr) && - !ib_frmr_pool_push(mr->ibmr.device, &mr->ibmr)) + if (mr->ibmr.frmr.pool && !mlx5_umr_revoke_mr_with_lock(mr)) { + ib_frmr_pool_push(mr->ibmr.device, &mr->ibmr); return 0; + } if (is_odp) mutex_lock(&to_ib_umem_odp(mr->umem)->umem_mutex); diff --git a/include/rdma/frmr_pools.h b/include/rdma/frmr_pools.h index af1b88801fa46..5b57bafa36366 100644 --- a/include/rdma/frmr_pools.h +++ b/include/rdma/frmr_pools.h @@ -34,6 +34,6 @@ int ib_frmr_pools_init(struct ib_device *device, const struct ib_frmr_pool_ops *pool_ops); void ib_frmr_pools_cleanup(struct ib_device *device); int ib_frmr_pool_pop(struct ib_device *device, struct ib_mr *mr); -int ib_frmr_pool_push(struct ib_device *device, struct ib_mr *mr); +void ib_frmr_pool_push(struct ib_device *device, struct ib_mr *mr); #endif /* FRMR_POOLS_H */ From 7800de33e1cb86aa28fdc6329edd93b4924d4ac3 Mon Sep 17 00:00:00 2001 From: Michael Guralnik Date: Wed, 10 Jun 2026 03:01:44 +0300 Subject: [PATCH 1368/2758] RDMA/core: Add ib_frmr_pool_drop for unrecoverable handles [ Upstream commit ddbc251be18fb82884ee6e9af634cc9f1171a4d6 ] A driver that has popped a handle from an FRMR pool can hit failures that leave the handle in a state where it can't safely be returned for reuse. The driver destroys the handle itself, but the pool has no way to learn about it, so the in_use counter drifts upward. Add ib_frmr_pool_drop to balance the pool's accounting in this case. Every pop is now balanced by exactly one push or drop. Fixes: 36680ef7bceb ("RDMA/mlx5: Switch from MR cache to FRMR pools") Link: https://patch.msgid.link/r/20260610000145.820592-9-michaelgur@nvidia.com Signed-off-by: Michael Guralnik Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/core/frmr_pools.c | 15 +++++++++++++++ include/rdma/frmr_pools.h | 1 + 2 files changed, 16 insertions(+) diff --git a/drivers/infiniband/core/frmr_pools.c b/drivers/infiniband/core/frmr_pools.c index e214a8273df84..ce8ae4305b9c8 100644 --- a/drivers/infiniband/core/frmr_pools.c +++ b/drivers/infiniband/core/frmr_pools.c @@ -578,3 +578,18 @@ void ib_frmr_pool_push(struct ib_device *device, struct ib_mr *mr) } EXPORT_SYMBOL(ib_frmr_pool_push); + +/* + * Drop a handle previously popped from the pool without returning it for + * reuse. The caller is responsible for destroying the underlying hardware + * resource. + */ +void ib_frmr_pool_drop(struct ib_mr *mr) +{ + struct ib_frmr_pool *pool = mr->frmr.pool; + + spin_lock(&pool->lock); + pool->in_use--; + spin_unlock(&pool->lock); +} +EXPORT_SYMBOL(ib_frmr_pool_drop); diff --git a/include/rdma/frmr_pools.h b/include/rdma/frmr_pools.h index 5b57bafa36366..aed4d69d3841c 100644 --- a/include/rdma/frmr_pools.h +++ b/include/rdma/frmr_pools.h @@ -35,5 +35,6 @@ int ib_frmr_pools_init(struct ib_device *device, void ib_frmr_pools_cleanup(struct ib_device *device); int ib_frmr_pool_pop(struct ib_device *device, struct ib_mr *mr); void ib_frmr_pool_push(struct ib_device *device, struct ib_mr *mr); +void ib_frmr_pool_drop(struct ib_mr *mr); #endif /* FRMR_POOLS_H */ From ac4b710e58ab9195a6b688f5d7f4dd80b41a94b0 Mon Sep 17 00:00:00 2001 From: Michael Guralnik Date: Wed, 10 Jun 2026 03:01:45 +0300 Subject: [PATCH 1369/2758] RDMA/mlx5: Drop FRMR pool handle on UMR revoke failure [ Upstream commit c37d79dd967d450ea02e0ee2b6438b8534bbd044 ] When UMR revoke fails during MR cleanup, the handle is left in an unknown state and cannot be returned to the pool. The driver already destroys the mkey via the fallback path, but the pool's in_use counter is never decremented, drifting upward over time. Call ib_frmr_pool_drop on the revoke-failure path so the pool's accounting stays consistent with the handles it has handed out. Fixes: 36680ef7bceb ("RDMA/mlx5: Switch from MR cache to FRMR pools") Link: https://patch.msgid.link/r/20260610000145.820592-10-michaelgur@nvidia.com Signed-off-by: Michael Guralnik Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/mr.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index 723594efa094e..4118cd2d15fd5 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -1398,9 +1398,11 @@ static int mlx5r_handle_mkey_cleanup(struct mlx5_ib_mr *mr) bool is_odp = is_odp_mr(mr); int ret; - if (mr->ibmr.frmr.pool && !mlx5_umr_revoke_mr_with_lock(mr)) { - ib_frmr_pool_push(mr->ibmr.device, &mr->ibmr); - return 0; + if (mr->ibmr.frmr.pool) { + if (!mlx5_umr_revoke_mr_with_lock(mr)) { + ib_frmr_pool_push(mr->ibmr.device, &mr->ibmr); + return 0; + } } if (is_odp) @@ -1422,6 +1424,10 @@ static int mlx5r_handle_mkey_cleanup(struct mlx5_ib_mr *mr) dma_resv_unlock( to_ib_umem_dmabuf(mr->umem)->attach->dmabuf->resv); } + + if (mr->ibmr.frmr.pool && !ret) + ib_frmr_pool_drop(&mr->ibmr); + return ret; } From ccb5dbc07de2e1fba55ff48780c7ebe3de046c07 Mon Sep 17 00:00:00 2001 From: Maxwell Doose Date: Thu, 4 Jun 2026 22:52:51 -0500 Subject: [PATCH 1370/2758] fs: efs: remove unneeded debug prints [ Upstream commit 89009392c80da5da00876c8334ff20028e6e3eb6 ] The current code uses debug prints conditionally compiled with #ifdef DEBUG. However, that code, when compiled, causes compiler errors due to incompatible formatters and undefined variables, notably: fs/efs/file.c: In function `efs_get_block': fs/efs/file.c:26:35: error: `block' undeclared (first use in this function); did you mean `iblock'? 26 | __func__, block, inode->i_blocks, inode->i_size); | ^~~~~ and: fs/efs/file.c: In function `efs_bmap': ./include/linux/kern_levels.h:5:25: error: format `%ld' expects argument of type `long int', but argument 4 has type `blkcnt_t' {aka `long long unsigned int'} [-Werror=format=] 5 | #define KERN_SOH "\001" /* ASCII Start Of Header */ | ^~~~~~ which also extends to the other formatters. As this part of the code has been dead for just about 14 years now, it has not been modernized to stay compatible with the most recent gcc compilers. Fix these issues by removing the debug prints. Link: https://lore.kernel.org/20260605035251.89305-2-m32285159@gmail.com Fixes: f403d1dbac6d ("fs/efs: add pr_fmt / use __func__") Signed-off-by: Maxwell Doose Suggested-by: Andrew Morton Cc: Fabian Frederick Cc: Christian Brauner Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- fs/efs/file.c | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/fs/efs/file.c b/fs/efs/file.c index 9e641da6fab27..9153dfe79bbcc 100644 --- a/fs/efs/file.c +++ b/fs/efs/file.c @@ -18,16 +18,9 @@ int efs_get_block(struct inode *inode, sector_t iblock, if (create) return error; - if (iblock >= inode->i_blocks) { -#ifdef DEBUG - /* - * i have no idea why this happens as often as it does - */ - pr_warn("%s(): block %d >= %ld (filesize %ld)\n", - __func__, block, inode->i_blocks, inode->i_size); -#endif + if (iblock >= inode->i_blocks) return 0; - } + phys = efs_map_block(inode, iblock); if (phys) map_bh(bh_result, inode->i_sb, phys); @@ -42,16 +35,8 @@ int efs_bmap(struct inode *inode, efs_block_t block) { } /* are we about to read past the end of a file ? */ - if (!(block < inode->i_blocks)) { -#ifdef DEBUG - /* - * i have no idea why this happens as often as it does - */ - pr_warn("%s(): block %d >= %ld (filesize %ld)\n", - __func__, block, inode->i_blocks, inode->i_size); -#endif + if (!(block < inode->i_blocks)) return 0; - } return efs_map_block(inode, block); } From 91088093cb00abd5ad3681ed95fd73949f56a689 Mon Sep 17 00:00:00 2001 From: Patrisious Haddad Date: Sun, 7 Jun 2026 21:18:08 +0300 Subject: [PATCH 1371/2758] RDMA/mlx5: Remove DCT restrack tracking [ Upstream commit b136a7af41f796a48665afc6a55907488a3d5500 ] DCT restrack tracking wasn't working to begin with as it was only tracking the first DCT which was added, since at creation the DCT number isn't yet initialized because the DCT FW object is only created during modify. The following DCT additions were failing silently. Since the fix isn't trivial and there were no users that required or complained about this issue we are dropping this for now instead of fixing. Fixes: fd3af5e21866 ("RDMA/mlx5: Track DCT, DCI and REG_UMR QPs as diver_detail resources.") Link: https://patch.msgid.link/r/20260607-restrack-uaf-fix-v1-1-d72e45eb76c2@nvidia.com Signed-off-by: Patrisious Haddad Reviewed-by: Michael Guralnik Signed-off-by: Edward Srouji Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/qp.c | 1 + drivers/infiniband/hw/mlx5/restrack.c | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index 8fd05532c09cc..7f8cd69905a42 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -3115,6 +3115,7 @@ static int create_qp(struct mlx5_ib_dev *dev, struct ib_pd *pd, switch (qp->type) { case MLX5_IB_QPT_DCT: + rdma_restrack_no_track(&qp->ibqp.res); err = create_dct(dev, pd, qp, params); break; case MLX5_IB_QPT_DCI: diff --git a/drivers/infiniband/hw/mlx5/restrack.c b/drivers/infiniband/hw/mlx5/restrack.c index 67841922c7b87..00a9bcb2603f0 100644 --- a/drivers/infiniband/hw/mlx5/restrack.c +++ b/drivers/infiniband/hw/mlx5/restrack.c @@ -178,9 +178,6 @@ static int fill_res_qp_entry(struct sk_buff *msg, struct ib_qp *ibqp) ret = nla_put_string(msg, RDMA_NLDEV_ATTR_RES_SUBTYPE, "REG_UMR"); break; - case MLX5_IB_QPT_DCT: - ret = nla_put_string(msg, RDMA_NLDEV_ATTR_RES_SUBTYPE, "DCT"); - break; case MLX5_IB_QPT_DCI: ret = nla_put_string(msg, RDMA_NLDEV_ATTR_RES_SUBTYPE, "DCI"); break; From 90488804bcf44644763e4ebed39096b5c021e95c Mon Sep 17 00:00:00 2001 From: Patrisious Haddad Date: Sun, 7 Jun 2026 21:18:09 +0300 Subject: [PATCH 1372/2758] RDMA/mlx5: Remove raw RSS QP restrack tracking [ Upstream commit 666031fed8f0fdfc29b20d125a628c1b0a04cdaf ] Raw RSS QP restrack tracking wasn't working to begin with as it was only tracking the first raw RSS QP which was added, since at creation the raw RSS QP number is reserved so the QP number for this qp type was always zero. The following raw RSS QP additions were always failing silently. Since the fix isn't trivial and there were no users that required or complained about this issue we are dropping this for now instead of fixing. Fixes: 968f0b6f9c01 ("RDMA/mlx5: Consolidate into special function all create QP calls") Link: https://patch.msgid.link/r/20260607-restrack-uaf-fix-v1-2-d72e45eb76c2@nvidia.com Signed-off-by: Patrisious Haddad Reviewed-by: Michael Guralnik Signed-off-by: Edward Srouji Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/qp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index 7f8cd69905a42..767444bcbce5d 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -3109,6 +3109,7 @@ static int create_qp(struct mlx5_ib_dev *dev, struct ib_pd *pd, int err; if (params->is_rss_raw) { + rdma_restrack_no_track(&qp->ibqp.res); err = create_rss_raw_qp_tir(dev, pd, qp, params); goto out; } From b732db02a2b04cde393638df19de6251ce62a74e Mon Sep 17 00:00:00 2001 From: Maher Sanalla Date: Thu, 11 Jun 2026 15:50:42 +0300 Subject: [PATCH 1373/2758] RDMA/mlx5: Fix undefined shift of user RQ WQE size [ Upstream commit d881d60223aac8fdc12b227d89c76e131e92a9cd ] set_rq_size() computes the RQ WQE size as "1 << rq_wqe_shift" based on the user-provided rq_wqe_shift, which is only checked to be greater than 32, so shifts of 32 are still accepted. A shift of 31 also overflows a signed integer, leading to undefined behavior. Use check_shl_overflow() to compute the RQ WQE size and reject any invalid values. Fixes: e126ba97dba9 ("mlx5: Add driver for Mellanox Connect-IB adapters") Link: https://patch.msgid.link/r/20260611-maher-sec-fixes-v1-1-cd8eb2542869@nvidia.com Signed-off-by: Maher Sanalla Signed-off-by: Edward Srouji Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/qp.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index 767444bcbce5d..3dc0c85587fff 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -451,16 +451,13 @@ static int set_rq_size(struct mlx5_ib_dev *dev, struct ib_qp_cap *cap, if (ucmd) { qp->rq.wqe_cnt = ucmd->rq_wqe_count; - if (ucmd->rq_wqe_shift > BITS_PER_BYTE * sizeof(ucmd->rq_wqe_shift)) - return -EINVAL; qp->rq.wqe_shift = ucmd->rq_wqe_shift; - if ((1 << qp->rq.wqe_shift) / - sizeof(struct mlx5_wqe_data_seg) < - wq_sig) + if (check_shl_overflow(1, qp->rq.wqe_shift, &wqe_size)) + return -EINVAL; + if (wqe_size / sizeof(struct mlx5_wqe_data_seg) < wq_sig) return -EINVAL; qp->rq.max_gs = - (1 << qp->rq.wqe_shift) / - sizeof(struct mlx5_wqe_data_seg) - + wqe_size / sizeof(struct mlx5_wqe_data_seg) - wq_sig; qp->rq.max_post = qp->rq.wqe_cnt; } else { From ef369446f62903ea079e8a7954b5bf8bb8300fe3 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Thu, 11 Jun 2026 13:20:15 +0300 Subject: [PATCH 1374/2758] =?UTF-8?q?RDMA/mlx5:=20Release=20the=20HW?= =?UTF-8?q?=E2=80=91provided=20UAR=20index=20rather=20than=20the=20SW=20on?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 449ae7927152e46acbe5f19f97eafdae6d3a96b1 ] Free the UAR index returned by the hardware. Fixes: 4ed131d0bb15 ("IB/mlx5: Expose dynamic mmap allocation") Link: https://patch.msgid.link/r/20260611-fix-uar-release-v1-1-f5464d845dbf@nvidia.com Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index aa2eb64ecf15e..1144bfee58048 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -2662,7 +2662,7 @@ static int uar_mmap(struct mlx5_ib_dev *dev, enum mlx5_ib_mmap_cmd cmd, if (!dyn_uar) return err; - mlx5_cmd_uar_dealloc(dev->mdev, idx, context->devx_uid); + mlx5_cmd_uar_dealloc(dev->mdev, uar_index, context->devx_uid); free_bfreg: mlx5_ib_free_bfreg(dev, bfregi, bfreg_dyn_idx); From c180cb20e0065ff0d2dd0df42d0df33569db977f Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Thu, 11 Jun 2026 16:12:34 +0100 Subject: [PATCH 1375/2758] ASoC: cs35l56: Fix wrong error test on simple_write_to_buffer() [ Upstream commit 3073eb1f1143deabbda6a043238ab9d99672e7c8 ] In cs35l56_cal_data_debugfs_write() fix the if statement that checks for error return to only check for negative values. Reported by Sashiko: simple_write_to_buffer() returns the positive number of bytes copied on success. Since the condition returns immediately on any non-zero value, is it possible that the written calibration data is discarded and cs35l56_stash_calibration() is never called? Fixes: f7097161e94c ("ASoC: cs35l56: Add common code for factory calibration") Reported-by: Sashiko Link: https://sashiko.dev/#/patchset/20260610093432.557375-1-rf%40opensource.cirrus.com Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260611151234.1111153-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/cs35l56-shared.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index 5405f70b7119d..9928608296d88 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -1260,7 +1260,7 @@ ssize_t cs35l56_cal_data_debugfs_write(struct cs35l56_base *cs35l56_base, return -EMSGSIZE; ret = simple_write_to_buffer(&cal_data, sizeof(cal_data), ppos, from, count); - if (ret) + if (ret < 0) return ret; ret = cs35l56_stash_calibration(cs35l56_base, &cal_data); From ace130dae7023eb31b4ef3da4f00699a7c433b13 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 11 Jun 2026 15:23:05 +0200 Subject: [PATCH 1376/2758] ASoC: SOF: Intel: select SND_SOC_SDW_UTILS=y from SND_SOC_SOF_HDA_GENERIC=y [ Upstream commit 67805f57e5b1a8589f89bd48936c65cbbaeec300 ] When SND_SOC_SOF_HDA_GENERIC=y but SND_SOC_SOF_INTEL_SOUNDWIRE=m, the SND_SOC_SDW_UTILS is also set to =m even though there is a direct link dependency from the hda.c: aarch64-linux-ld: sound/soc/sof/intel/hda.o: in function `hda_machine_select': hda.c:(.text+0x21ac): undefined reference to `codec_info_list' hda.c:(.text+0x241c): undefined reference to `asoc_sdw_get_dai_type' hda.c:(.text+0x25b4): undefined reference to `asoc_sdw_get_codec_info_list_count' hda.c:(.text+0x25d8): undefined reference to `asoc_sdw_get_codec_info_list_count' Change this the same way as the other related 'select' statements to allow linking against it. Fixes: 2b4d53eb5cf3 ("ASoC: SOF: Intel: select SND_SOC_SDW_UTILS in SND_SOC_SOF_HDA_GENERIC") Signed-off-by: Arnd Bergmann Tested-by: Julian Braha Link: https://patch.msgid.link/20260611132310.137688-1-arnd@kernel.org Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/sof/intel/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index e31f4c4061d80..9ddf14e7946aa 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -329,7 +329,7 @@ config SND_SOC_SOF_HDA_GENERIC select SND_INTEL_DSP_CONFIG select SND_SOC_SOF_HDA_LINK_BASELINE select SND_SOC_SOF_HDA_PROBES - select SND_SOC_SDW_UTILS if SND_SOC_SOF_INTEL_SOUNDWIRE + select SND_SOC_SDW_UTILS if SND_SOC_SOF_INTEL_SOUNDWIRE !=n select SND_SOC_SOF_HDA_MLINK if SND_SOC_SOF_HDA_LINK help This option is not user-selectable but automagically handled by From 544ee81c23da1ff1fb7f711446a409be8adbc313 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 11 Jun 2026 15:23:06 +0200 Subject: [PATCH 1377/2758] ASoC: SOF: Intel: hda-sdw-bpt: select SND_SOF_SOF_HDA_SDW_BPT properly [ Upstream commit 999ec4c29d73ac85b639a31bbc85ad2ec00eb9d7 ] When SND_SOC_SOF_INTEL_LNL is set, SND_SOF_SOF_HDA_SDW_BPT must also be enabled, in order to let the soundwire support call into it. However, there are configurations with SND_SOF_SOF_HDA_SDW_BPT=m and SND_SOF_SOF_HDA_SDW_BPT=m but SOUNDWIRE_INTEL=y, which still lead to a link failure: aarch64-linux-ld: drivers/soundwire/intel_ace2x.o: in function `intel_ace2x_bpt_wait': intel_ace2x.c:(.text+0xfc8): undefined reference to `hda_sdw_bpt_wait' aarch64-linux-ld: drivers/soundwire/intel_ace2x.o: in function `intel_ace2x_bpt_send_async': intel_ace2x.c:(.text+0x1ff8): undefined reference to `hda_sdw_bpt_get_buf_size_alignment' Address this by moving the 'select SND_SOF_SOF_HDA_SDW_BPT' into SND_SOC_SOF_HDA_GENERIC. Fixes: 614d416dd8ae ("ASoC: SOF: Intel: hda-sdw-bpt: fix SND_SOF_SOF_HDA_SDW_BPT dependencies") Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20260611132310.137688-2-arnd@kernel.org Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/sof/intel/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index 9ddf14e7946aa..915abbef398d5 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -266,10 +266,8 @@ config SND_SOC_SOF_METEORLAKE config SND_SOC_SOF_INTEL_LNL tristate - select SOUNDWIRE_INTEL if SND_SOC_SOF_INTEL_SOUNDWIRE != n select SND_SOC_SOF_HDA_GENERIC select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE - select SND_SOF_SOF_HDA_SDW_BPT if SND_SOC_SOF_INTEL_SOUNDWIRE != n select SND_SOC_SOF_IPC4 select SND_SOC_SOF_INTEL_MTL @@ -331,6 +329,8 @@ config SND_SOC_SOF_HDA_GENERIC select SND_SOC_SOF_HDA_PROBES select SND_SOC_SDW_UTILS if SND_SOC_SOF_INTEL_SOUNDWIRE !=n select SND_SOC_SOF_HDA_MLINK if SND_SOC_SOF_HDA_LINK + select SND_SOF_SOF_HDA_SDW_BPT if SND_SOC_SOF_INTEL_LNL != n && \ + SND_SOC_SOF_INTEL_SOUNDWIRE !=n help This option is not user-selectable but automagically handled by 'select' statements at a higher level. From d8961b5c7889b6ecc00f1409d36826df1665df27 Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Tue, 9 Jun 2026 21:43:13 +0900 Subject: [PATCH 1378/2758] ASoC: codecs: hdac_hdmi: Validate written enum value [ Upstream commit 0b08baeccdcf52fad328ad645f5b4fbee04eea34 ] hdac_hdmi_set_pin_port_mux() uses the written enum value to index the texts array before calling snd_soc_dapm_put_enum_double(), which validates that the value is within the enum item range. An out-of-range value can therefore make the driver read past the texts array before the helper rejects the write. Move the lookup after the helper has accepted the value. Fixes: 4a3478debf36 ("ASoC: hdac_hdmi: Add jack reporting") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: HyeongJun An Link: https://patch.msgid.link/20260609124317.38046-2-sammiee5311@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/hdac_hdmi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 2652fcf2a3a34..3220f9226e0b2 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -911,12 +911,14 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol, struct hdac_device *hdev = dev_to_hdac_dev(dev); struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pcm *pcm; - const char *cvt_name = e->texts[ucontrol->value.enumerated.item[0]]; + const char *cvt_name; ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol); if (ret < 0) return ret; + cvt_name = e->texts[ucontrol->value.enumerated.item[0]]; + if (port == NULL) return -EINVAL; From 0965892cc486ca554d72eeb62d85ccf8d0137a5a Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Tue, 9 Jun 2026 21:43:14 +0900 Subject: [PATCH 1379/2758] ASoC: meson: aiu: Validate written enum values [ Upstream commit d65adf85477247be04ac86886f8edfaa047b5d4a ] The AIU HDMI and internal codec mux put callbacks use the written enum value with snd_soc_enum_item_to_val() before checking whether the value is valid for the enumeration. Reject out-of-range values before converting the enum item, matching the validation already done by the G12A HDMI and internal codec mux controls. Fixes: b82b734c0e9a ("ASoC: meson: aiu: add hdmi codec control support") Fixes: 65816025d461 ("ASoC: meson: aiu: add internal dac codec control support") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: HyeongJun An Link: https://patch.msgid.link/20260609124317.38046-3-sammiee5311@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/meson/aiu-acodec-ctrl.c | 3 +++ sound/soc/meson/aiu-codec-ctrl.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/sound/soc/meson/aiu-acodec-ctrl.c b/sound/soc/meson/aiu-acodec-ctrl.c index 483772ba69cd7..94c5d65335233 100644 --- a/sound/soc/meson/aiu-acodec-ctrl.c +++ b/sound/soc/meson/aiu-acodec-ctrl.c @@ -36,6 +36,9 @@ static int aiu_acodec_ctrl_mux_put_enum(struct snd_kcontrol *kcontrol, struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int mux, changed; + if (ucontrol->value.enumerated.item[0] >= e->items) + return -EINVAL; + mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]); changed = snd_soc_component_test_bits(component, e->reg, CTRL_DIN_LRCLK_SRC, diff --git a/sound/soc/meson/aiu-codec-ctrl.c b/sound/soc/meson/aiu-codec-ctrl.c index 396f815077e29..60bb4cdfee520 100644 --- a/sound/soc/meson/aiu-codec-ctrl.c +++ b/sound/soc/meson/aiu-codec-ctrl.c @@ -28,6 +28,9 @@ static int aiu_codec_ctrl_mux_put_enum(struct snd_kcontrol *kcontrol, struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int mux, changed; + if (ucontrol->value.enumerated.item[0] >= e->items) + return -EINVAL; + mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]); changed = snd_soc_component_test_bits(component, e->reg, CTRL_DATA_SEL, From 5b7a23c1ed04e794ef3b31e452ef5c93e1e34b4a Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Tue, 9 Jun 2026 21:43:15 +0900 Subject: [PATCH 1380/2758] ASoC: fsl: fsl_audmix: Validate written enum values [ Upstream commit 3cd17e4e2871114d5579fa7bc8da66faf7fc1930 ] fsl_audmix_put_mix_clk_src() and fsl_audmix_put_out_src() convert the user-provided enum item with snd_soc_enum_item_to_val() before checking whether the item is within the enum's item count. The generic snd_soc_put_enum_double() helper performs that validation, but these callbacks use the converted value first: the clock-source path tests it with BIT(), and the output-source path indexes the prms transition table with it. Reject out-of-range enum items before converting them. Fixes: be1df61cf06e ("ASoC: fsl: Add Audio Mixer CPU DAI driver") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: HyeongJun An Link: https://patch.msgid.link/20260609124317.38046-4-sammiee5311@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/fsl/fsl_audmix.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c index 40a3b74321744..f819f33ec46b8 100644 --- a/sound/soc/fsl/fsl_audmix.c +++ b/sound/soc/fsl/fsl_audmix.c @@ -117,6 +117,9 @@ static int fsl_audmix_put_mix_clk_src(struct snd_kcontrol *kcontrol, unsigned int *item = ucontrol->value.enumerated.item; unsigned int reg_val, val, mix_clk; + if (item[0] >= e->items) + return -EINVAL; + /* Get current state */ reg_val = snd_soc_component_read(comp, FSL_AUDMIX_CTR); mix_clk = ((reg_val & FSL_AUDMIX_CTR_MIXCLK_MASK) @@ -157,6 +160,9 @@ static int fsl_audmix_put_out_src(struct snd_kcontrol *kcontrol, unsigned int reg_val, val, mask = 0, ctr = 0; int ret; + if (item[0] >= e->items) + return -EINVAL; + /* Get current state */ reg_val = snd_soc_component_read(comp, FSL_AUDMIX_CTR); From 226d93b65f4f99b35fb7a03bdb24acb577364ebc Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Tue, 9 Jun 2026 21:43:16 +0900 Subject: [PATCH 1381/2758] ASoC: tegra: tegra210_ahub: Validate written enum value [ Upstream commit 1d8aabb413b5638670dfd1162169edc0ba276a2e ] tegra_ahub_put_value_enum() reads e->values[item[0]] before checking whether item[0] is within the enum item range. The existing check therefore happens too late to prevent an out-of-range read of the values array. Move the check before the array access. Fixes: 16e1bcc2caf4 ("ASoC: tegra: Add Tegra210 based AHUB driver") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: HyeongJun An Link: https://patch.msgid.link/20260609124317.38046-5-sammiee5311@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/tegra/tegra210_ahub.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/tegra/tegra210_ahub.c b/sound/soc/tegra/tegra210_ahub.c index ece33b7ff190a..efc8f33886682 100644 --- a/sound/soc/tegra/tegra210_ahub.c +++ b/sound/soc/tegra/tegra210_ahub.c @@ -62,13 +62,15 @@ static int tegra_ahub_put_value_enum(struct snd_kcontrol *kctl, struct snd_soc_dapm_update update[TEGRA_XBAR_UPDATE_MAX_REG] = { }; int val_bytes = snd_soc_component_regmap_val_bytes(cmpnt); unsigned int *item = uctl->value.enumerated.item; - unsigned int value = e->values[item[0]]; + unsigned int value; unsigned int i, bit_pos, reg_idx = 0, reg_val = 0; int change = 0; if (item[0] >= e->items) return -EINVAL; + value = e->values[item[0]]; + if (value) { /* Get the register index and value to set */ reg_idx = (value - 1) / (8 * val_bytes); From ba37b62ed0a443b8e23f53a7477e7f2537fd34c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Thu, 4 Jun 2026 00:10:58 -0300 Subject: [PATCH 1382/2758] ASoC: topology: Check PCM and DAI name strings before use MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit b7e44d1986d6671342c19b82192189ca5db5dab7 ] Topology objects store several PCM and DAI names in fixed-size UAPI arrays. Other topology parser paths validate these fields with bounded strnlen() checks before using them as C strings, but the PCM and DAI paths still pass some fixed-size arrays directly to strlen(), devm_kstrdup(), DAI lookup, and diagnostic prints. A malformed topology blob with a non-NUL-terminated PCM, DAI, or stream capability name can therefore make the parser read past the end of the fixed-size field. Reject unterminated PCM and DAI name fields before consuming them as C strings. Fixes: 64527e8a3529 ("ASoC: topology: Add FE DAIs dynamically") Fixes: acfc7d46cddc ("ASoC: topology: Add FE DAI links dynamically") Fixes: 0038be9a84dc ("ASoC: topology: Add support for configuring existing BE DAIs") Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260604-asoc-topology-check-pcm-dai-names-v1-1-e1b0f6f7c2ce@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/soc-topology.c | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 85679c8e02299..35cbe29d22758 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1326,9 +1326,24 @@ static int soc_tplg_dapm_complete(struct soc_tplg *tplg) return ret; } +static int soc_tplg_check_name(const char *name) +{ + if (strnlen(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == + SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + return -EINVAL; + + return 0; +} + static int set_stream_info(struct soc_tplg *tplg, struct snd_soc_pcm_stream *stream, struct snd_soc_tplg_stream_caps *caps) { + int ret; + + ret = soc_tplg_check_name(caps->name); + if (ret) + return ret; + stream->stream_name = devm_kstrdup(tplg->dev, caps->name, GFP_KERNEL); if (!stream->stream_name) return -ENOMEM; @@ -1380,7 +1395,11 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg, if (dai_drv == NULL) return -ENOMEM; - if (strlen(pcm->dai_name)) { + ret = soc_tplg_check_name(pcm->dai_name); + if (ret) + goto err; + + if (pcm->dai_name[0]) { dai_drv->name = devm_kstrdup(tplg->dev, pcm->dai_name, GFP_KERNEL); if (!dai_drv->name) { ret = -ENOMEM; @@ -1486,7 +1505,11 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg, if (tplg->ops) link->dobj.unload = tplg->ops->link_unload; - if (strlen(pcm->pcm_name)) { + ret = soc_tplg_check_name(pcm->pcm_name); + if (ret) + goto err; + + if (pcm->pcm_name[0]) { link->name = devm_kstrdup(tplg->dev, pcm->pcm_name, GFP_KERNEL); link->stream_name = devm_kstrdup(tplg->dev, pcm->pcm_name, GFP_KERNEL); if (!link->name || !link->stream_name) { @@ -1496,7 +1519,11 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg, } link->id = le32_to_cpu(pcm->pcm_id); - if (strlen(pcm->dai_name)) { + ret = soc_tplg_check_name(pcm->dai_name); + if (ret) + goto err; + + if (pcm->dai_name[0]) { link->cpus->dai_name = devm_kstrdup(tplg->dev, pcm->dai_name, GFP_KERNEL); if (!link->cpus->dai_name) { ret = -ENOMEM; @@ -1848,6 +1875,10 @@ static int soc_tplg_dai_config(struct soc_tplg *tplg, memset(&dai_component, 0, sizeof(dai_component)); + ret = soc_tplg_check_name(d->dai_name); + if (ret) + return ret; + dai_component.dai_name = d->dai_name; dai = snd_soc_find_dai(&dai_component); if (!dai) { From 5560427efb1b3fd0a3d45cf1dc477e3b9e96f702 Mon Sep 17 00:00:00 2001 From: George Moussalem Date: Mon, 8 Jun 2026 11:22:08 +0400 Subject: [PATCH 1383/2758] net: dsa: qca8k: fix led devicename when using external mdio bus [ Upstream commit 0b7b378ce6cafbb948786cb6f17f406d94016c8c ] The qca8k dsa switch can use either an external or internal mdio bus. This depends on whether the mdio node is defined under the switch node itself. Upon registering the internal mdio bus, the internal_mdio_bus of the dsa switch is assigned to this bus. When an external mdio bus is used, the driver still uses the internal_mdio_bus id which is used to create the device names of the leds. This leads to the leds being prefixed with '(efault)' as the internal_mii_bus is null. So let's fix this by adding a null check and use the devicename of the external bus instead when an external bus is configured. Fixes: 1e264f9d2918 ("net: dsa: qca8k: add LEDs basic support") Signed-off-by: George Moussalem Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20260608-qca8k-leds-fix-v3-1-a915bb2f37ae@outlook.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/dsa/qca/qca8k-leds.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/dsa/qca/qca8k-leds.c b/drivers/net/dsa/qca/qca8k-leds.c index 43ac68052baf9..ef496e345a4e7 100644 --- a/drivers/net/dsa/qca/qca8k-leds.c +++ b/drivers/net/dsa/qca/qca8k-leds.c @@ -429,7 +429,8 @@ qca8k_parse_port_leds(struct qca8k_priv *priv, struct fwnode_handle *port, int p init_data.fwnode = led; init_data.devname_mandatory = true; init_data.devicename = kasprintf(GFP_KERNEL, "%s:0%d", - priv->internal_mdio_bus->id, + priv->internal_mdio_bus ? + priv->internal_mdio_bus->id : priv->bus->id, port_num); if (!init_data.devicename) { fwnode_handle_put(led); From 6151159618198675e01c391676e579738286c135 Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Wed, 10 Jun 2026 06:18:39 -0400 Subject: [PATCH 1384/2758] net/sched: cls_flow: Dont expose folded kernel pointers [ Upstream commit f294fc71c4a0fa4964f6428a1b4e7929c1d83125 ] The flow classifier falls back to addr_fold() for fields that are missing from packet headers. In map mode, userspace controls mask, xor, rshift, addend and divisor, and can observe the resulting classid through class statistics. This allows a tc classifier in a user/network namespace to recover the 32-bit folded value of skb->sk, skb_dst() or skb_nfct(). Align with standard kernel practices for pointer hashing and replace the XOR folding with a keyed siphash (which is cryptographically secure) Fixes: e5dfb815181f ("[NET_SCHED]: Add flow classifier") Reported-by: Kyle Zeng Tested-by: Kyle Zeng Tested-by: Victor Nogueira Signed-off-by: Jamal Hadi Salim Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260610101839.14135-1-jhs@mojatatu.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/cls_flow.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index ab364e4e46862..356c68ebc3895 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -57,11 +58,15 @@ struct flow_filter { struct rcu_work rwork; }; +static siphash_aligned_key_t flow_keys_secret __read_mostly; + static inline u32 addr_fold(void *addr) { - unsigned long a = (unsigned long)addr; - - return (a & 0xFFFFFFFF) ^ (BITS_PER_LONG > 32 ? a >> 32 : 0); +#ifdef CONFIG_64BIT + return (u32)siphash_1u64((u64)addr, &flow_keys_secret); +#else + return (u32)siphash_1u32((u32)addr, &flow_keys_secret); +#endif } static u32 flow_get_src(const struct sk_buff *skb, const struct flow_keys *flow) @@ -596,6 +601,7 @@ static int flow_init(struct tcf_proto *tp) return -ENOBUFS; INIT_LIST_HEAD(&head->filters); rcu_assign_pointer(tp->root, head); + net_get_random_once(&flow_keys_secret, sizeof(flow_keys_secret)); return 0; } From 676482da8d938ea72c26da0fc86af2d2ec238ab2 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Wed, 10 Jun 2026 06:17:18 +0000 Subject: [PATCH 1385/2758] ipv4: fib: Don't dump dying fib_info in fib_leaf_notify(). [ Upstream commit 06b693d2eb6651a63ad85bad8673de3b7d4edd6d ] syzbot reported use-after-free in nsim_fib4_prepare_event(). [0] The problem is that the following functions call fib_info_hold() / refcount_inc() while dumping fib_info under RCU, which is unsafe. * mlxsw_sp_router_fib4_event() * rocker_router_fib_event() * nsim_fib4_prepare_event() refcount_inc_not_zero() must be used, but it would be too late there. Let's guarantee the lifetime of fib_info in fib_leaf_notify(). Note that IPv6 does not need the corresponding change since fib6_table_dump() holds fib6_table.tb6_lock. [0]: refcount_t: addition on 0; use-after-free. WARNING: lib/refcount.c:25 at refcount_warn_saturate+0x9f/0x110 lib/refcount.c:25, CPU#0: kworker/u8:15/3420 Modules linked in: CPU: 0 UID: 0 PID: 3420 Comm: kworker/u8:15 Not tainted syzkaller #0 PREEMPT_{RT,(full)} Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/18/2026 Workqueue: netns cleanup_net RIP: 0010:refcount_warn_saturate+0x9f/0x110 lib/refcount.c:25 Code: eb 66 85 db 74 3e 83 fb 01 75 4c e8 1b f1 22 fd 48 8d 3d 84 cb f1 0a 67 48 0f b9 3a eb 4a e8 08 f1 22 fd 48 8d 3d 81 cb f1 0a <67> 48 0f b9 3a eb 37 e8 f5 f0 22 fd 48 8d 3d 7e cb f1 0a 67 48 0f RSP: 0018:ffffc9000f2c7270 EFLAGS: 00010293 RAX: ffffffff84a18858 RBX: 0000000000000002 RCX: ffff888032ff9ec0 RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffffffff8f9353e0 RBP: 0000000000000000 R08: ffff888032ff9ec0 R09: 0000000000000005 R10: 0000000000000100 R11: 0000000000000004 R12: ffff8880570cc000 R13: dffffc0000000000 R14: ffff88802b40563c R15: ffff8880570cc000 FS: 0000000000000000(0000) GS:ffff888126173000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007fb1f4d5d000 CR3: 000000006072a000 CR4: 00000000003526f0 Call Trace: __refcount_add include/linux/refcount.h:-1 [inline] __refcount_inc include/linux/refcount.h:366 [inline] refcount_inc include/linux/refcount.h:383 [inline] fib_info_hold include/net/ip_fib.h:629 [inline] nsim_fib4_prepare_event drivers/net/netdevsim/fib.c:930 [inline] nsim_fib_event_schedule_work drivers/net/netdevsim/fib.c:1000 [inline] nsim_fib_event_nb+0x1055/0x1240 drivers/net/netdevsim/fib.c:1043 call_fib_notifier+0x45/0x80 net/core/fib_notifier.c:25 call_fib_entry_notifier net/ipv4/fib_trie.c:90 [inline] fib_leaf_notify net/ipv4/fib_trie.c:2176 [inline] fib_table_notify net/ipv4/fib_trie.c:2194 [inline] fib_notify+0x36b/0x5e0 net/ipv4/fib_trie.c:2217 fib_net_dump net/core/fib_notifier.c:70 [inline] register_fib_notifier+0x184/0x360 net/core/fib_notifier.c:108 nsim_fib_create+0x85d/0x9f0 drivers/net/netdevsim/fib.c:1596 nsim_dev_reload_create drivers/net/netdevsim/dev.c:1604 [inline] nsim_dev_reload_up+0x374/0x7c0 drivers/net/netdevsim/dev.c:1058 devlink_reload+0x501/0x8d0 net/devlink/dev.c:475 devlink_pernet_pre_exit+0x1ff/0x420 net/devlink/core.c:558 ops_pre_exit_list net/core/net_namespace.c:161 [inline] ops_undo_list+0x187/0x940 net/core/net_namespace.c:234 cleanup_net+0x56e/0x800 net/core/net_namespace.c:702 process_one_work kernel/workqueue.c:3314 [inline] process_scheduled_works+0xb5d/0x1860 kernel/workqueue.c:3397 worker_thread+0xa53/0xfc0 kernel/workqueue.c:3478 kthread+0x388/0x470 kernel/kthread.c:436 ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 Fixes: 0ae3eb7b4611 ("netdevsim: fib: Perform the route programming in a non-atomic context") Fixes: c3852ef7f2f8 ("ipv4: fib: Replay events when registering FIB notifier") Reported-by: syzbot+cb2aa2390ac024e25f5c@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6a290011.39669fcc.33b062.00b1.GAE@google.com/ Signed-off-by: Kuniyuki Iwashima Reviewed-by: Ido Schimmel Reviewed-by: David Ahern Link: https://patch.msgid.link/20260610061744.2030996-2-kuniyu@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- include/net/ip_fib.h | 5 +++++ net/ipv4/fib_trie.c | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 318593743b6e1..541da2dde6266 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -629,6 +629,11 @@ static inline void fib_info_hold(struct fib_info *fi) refcount_inc(&fi->fib_clntref); } +static inline bool fib_info_hold_safe(struct fib_info *fi) +{ + return refcount_inc_not_zero(&fi->fib_clntref); +} + static inline void fib_info_put(struct fib_info *fi) { if (refcount_dec_and_test(&fi->fib_clntref)) diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 1308213791f19..dac543c1d6867 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -2172,10 +2172,14 @@ static int fib_leaf_notify(struct key_vector *l, struct fib_table *tb, if (fa->fa_slen == last_slen) continue; + if (!fib_info_hold_safe(fa->fa_info)) + continue; + last_slen = fa->fa_slen; err = call_fib_entry_notifier(nb, FIB_EVENT_ENTRY_REPLACE, l->key, KEYLENGTH - fa->fa_slen, fa, extack); + fib_info_put(fa->fa_info); if (err) return err; } From 3af0bc1bd9039e2e50abf3e2d7fee411f38bce4e Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Wed, 10 Jun 2026 06:17:19 +0000 Subject: [PATCH 1386/2758] net: fib_rules: Don't dump dying fib_rule in fib_rules_dump(). [ Upstream commit 2821e85c058f81c9948a2fb1a634f7b47457d51c ] rocker_router_fib_event() calls fib_rule_get() during RCU dump. If the fib_rule is dying, refcount_inc() will complain about it. Let's call refcount_inc_not_zero() in fib_rules_dump(). Fixes: 5d7bfd141924 ("ipv4: fib_rules: Dump FIB rules when registering FIB notifier") Signed-off-by: Kuniyuki Iwashima Reviewed-by: Ido Schimmel Reviewed-by: David Ahern Link: https://patch.msgid.link/20260610061744.2030996-3-kuniyu@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- include/net/fib_rules.h | 5 +++++ net/core/fib_rules.c | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index 6e68e359ad18c..7dee0ae616e32 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -111,6 +111,11 @@ static inline void fib_rule_get(struct fib_rule *rule) refcount_inc(&rule->refcnt); } +static inline bool fib_rule_get_safe(struct fib_rule *rule) +{ + return refcount_inc_not_zero(&rule->refcnt); +} + static inline void fib_rule_put(struct fib_rule *rule) { if (refcount_dec_and_test(&rule->refcnt)) diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index 8ca634964e363..cf374c2087325 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -349,7 +349,7 @@ int fib_rules_lookup(struct fib_rules_ops *ops, struct flowi *fl, if (err != -EAGAIN) { if ((arg->flags & FIB_LOOKUP_NOREF) || - likely(refcount_inc_not_zero(&rule->refcnt))) { + likely(fib_rule_get_safe(rule))) { arg->rule = rule; goto out; } @@ -410,8 +410,12 @@ int fib_rules_dump(struct net *net, struct notifier_block *nb, int family, if (!ops) return -EAFNOSUPPORT; list_for_each_entry_rcu(rule, &ops->rules_list, list) { + if (!fib_rule_get_safe(rule)) + continue; + err = call_fib_rule_notifier(nb, FIB_EVENT_RULE_ADD, rule, family, extack); + fib_rule_put(rule); if (err) break; } From 865643640b5b5c4579b32d7a55ac9ad648362eaa Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Mon, 8 Jun 2026 23:51:16 -0700 Subject: [PATCH 1387/2758] bridge: cfm: reject invalid CCM interval at configuration time [ Upstream commit f3e02edd8322b31b8e6517faa6ba053bf29d1e26 ] ccm_tx_work_expired() re-arms itself via queue_delayed_work() using the configured exp_interval converted by interval_to_us(). When exp_interval is BR_CFM_CCM_INTERVAL_NONE or out of range, interval_to_us() returns 0, causing the worker to fire immediately in a tight loop that allocates skbs until OOM. Fix this by validating exp_interval at configuration time: - Constrain IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL to the valid range [BR_CFM_CCM_INTERVAL_3_3_MS, BR_CFM_CCM_INTERVAL_10_MIN] in the netlink policy so userspace cannot set an invalid value. - Reject starting CCM TX in br_cfm_cc_ccm_tx() when exp_interval has not yet been configured (defaults to 0 from kzalloc). Fixes: 2be665c3940d ("bridge: cfm: Netlink SET configuration Interface.") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Acked-by: Nikolay Aleksandrov Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260609065116.2818837-1-xmei5@asu.edu Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/bridge/br_cfm.c | 6 ++++++ net/bridge/br_cfm_netlink.c | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/net/bridge/br_cfm.c b/net/bridge/br_cfm.c index 118c7ea48c351..dea56fffa1c19 100644 --- a/net/bridge/br_cfm.c +++ b/net/bridge/br_cfm.c @@ -805,6 +805,12 @@ int br_cfm_cc_ccm_tx(struct net_bridge *br, const u32 instance, goto save; } + if (!interval_to_us(mep->cc_config.exp_interval)) { + NL_SET_ERR_MSG_MOD(extack, + "Invalid CCM interval"); + return -EINVAL; + } + /* Start delayed work to transmit CCM frames. It is done with zero delay * to send first frame immediately */ diff --git a/net/bridge/br_cfm_netlink.c b/net/bridge/br_cfm_netlink.c index 2faab44652e7c..91b9922dc3f25 100644 --- a/net/bridge/br_cfm_netlink.c +++ b/net/bridge/br_cfm_netlink.c @@ -34,7 +34,9 @@ br_cfm_cc_config_policy[IFLA_BRIDGE_CFM_CC_CONFIG_MAX + 1] = { [IFLA_BRIDGE_CFM_CC_CONFIG_UNSPEC] = { .type = NLA_REJECT }, [IFLA_BRIDGE_CFM_CC_CONFIG_INSTANCE] = { .type = NLA_U32 }, [IFLA_BRIDGE_CFM_CC_CONFIG_ENABLE] = { .type = NLA_U32 }, - [IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL] = { .type = NLA_U32 }, + [IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL] = + NLA_POLICY_RANGE(NLA_U32, BR_CFM_CCM_INTERVAL_3_3_MS, + BR_CFM_CCM_INTERVAL_10_MIN), [IFLA_BRIDGE_CFM_CC_CONFIG_EXP_MAID] = { .type = NLA_BINARY, .len = CFM_MAID_LENGTH }, }; From 0c674b20c9cdb54f8a46c88b4d00cadf82b8f0c0 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Tue, 9 Jun 2026 18:14:28 -0400 Subject: [PATCH 1388/2758] sctp: validate embedded address parameter length [ Upstream commit e9361d0ca55c4af12aac09e2572852fa91046229 ] sctp_verify_asconf() and sctp_verify_param() only validate ADD_IP, DEL_IP, and SET_PRIMARY parameters against a fixed minimum size of sizeof(struct sctp_addip_param) + sizeof(struct sctp_paramhdr). This ensures the outer parameter is large enough to contain an embedded address parameter header, but does not verify that the embedded address parameter's declared length fits within the bounds of the outer parameter. Later, sctp_process_param() and sctp_process_asconf_param() extract the embedded address parameter and pass it to af->from_addr_param(), which uses the address parameter length to parse the variable-length address payload. A malformed peer can therefore advertise an embedded address parameter length that exceeds the remaining bytes in the enclosing parameter. Validate that addr_param->p.length does not exceed the space available after the sctp_addip_param header before processing the embedded address parameter. Reject malformed parameters when the embedded address length extends beyond the enclosing parameter bounds. This prevents out-of-bounds reads when parsing malformed parameters carried in INIT or ASCONF processing paths. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: sashiko Signed-off-by: Xin Long Link: https://patch.msgid.link/7838b86b69f52add28808fb59034c8f992e97b2d.1781043268.git.lucien.xin@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sctp/sm_make_chunk.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 1741a9f33d8c7..41958b8e59fd5 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -2641,6 +2641,9 @@ static int sctp_process_param(struct sctp_association *asoc, goto fall_through; addr_param = param.v + sizeof(struct sctp_addip_param); + if (ntohs(addr_param->p.length) > + ntohs(param.p->length) - sizeof(struct sctp_addip_param)) + break; af = sctp_get_af_specific(param_type2af(addr_param->p.type)); if (!af) @@ -3039,13 +3042,16 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc, union sctp_addr addr; struct sctp_af *af; - addr_param = (void *)asconf_param + sizeof(*asconf_param); - if (asconf_param->param_hdr.type != SCTP_PARAM_ADD_IP && asconf_param->param_hdr.type != SCTP_PARAM_DEL_IP && asconf_param->param_hdr.type != SCTP_PARAM_SET_PRIMARY) return SCTP_ERROR_UNKNOWN_PARAM; + addr_param = (void *)asconf_param + sizeof(*asconf_param); + if (ntohs(addr_param->p.length) > + ntohs(asconf_param->param_hdr.length) - sizeof(*asconf_param)) + return SCTP_ERROR_PROTO_VIOLATION; + switch (addr_param->p.type) { case SCTP_PARAM_IPV6_ADDRESS: if (!asoc->peer.ipv6_address) From 67e4b283de36d9eebf95acdea5d6674b6ef4b2f4 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Tue, 9 Jun 2026 23:22:45 +0000 Subject: [PATCH 1389/2758] net: pfcp: allocate per-cpu tstats for PFCP netdevs [ Upstream commit 24041543da8cd84eb5d8ae738c534372fff54820 ] PFCP uses dev_get_tstats64() as its ndo_get_stats64 callback, but pfcp_link_setup() does not request NETDEV_PCPU_STAT_TSTATS. The net core therefore leaves dev->tstats NULL for PFCP devices. Creating a PFCP rtnetlink device can immediately ask the new netdev for stats while building the RTM_NEWLINK notification. That reaches dev_get_tstats64() and dereferences the NULL dev->tstats pointer. Set pcpu_stat_type to NETDEV_PCPU_STAT_TSTATS during PFCP link setup so the net core allocates the storage expected by dev_get_tstats64(). Fixes: 76c8764ef36a ("pfcp: add PFCP module") Signed-off-by: Samuel Moelius Reviewed-by: Alexander Lobakin Link: https://patch.msgid.link/20260609232244.1602027.c569f6c530f6.pfcp-missing-tstats-link-create-oops@trailofbits.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/pfcp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/pfcp.c b/drivers/net/pfcp.c index 28e6bc4a1f14c..a6aa30ae0af76 100644 --- a/drivers/net/pfcp.c +++ b/drivers/net/pfcp.c @@ -148,6 +148,7 @@ static void pfcp_link_setup(struct net_device *dev) dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; dev->priv_flags |= IFF_NO_QUEUE; + dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS; netif_keep_dst(dev); } From 03f4c6ed3cffbfce0b85a3a0193d08a46692a9f8 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Tue, 9 Jun 2026 23:22:40 +0200 Subject: [PATCH 1390/2758] net: Stop leased rxq before uninstalling its memory provider [ Upstream commit 37314c9dbe95b4d924c7b61aaf563cec4f4e4133 ] netif_rxq_cleanup_unlease() tears down the memory provider that was installed on a physical RX queue through a netkit queue lease. It currently revokes the provider's DMA mappings before stopping the physical queue: __netif_mp_uninstall_rxq(virt_rxq, p); /* DMA unmap */ __netif_mp_close_rxq(phys_rxq->dev, rxq_idx, p); /* queue stop */ This inverts the ordering used by the regular teardown paths (normal device unregister and the io_uring zcrx close path), which stop the queue before revoking the provider's mappings. With the physical queue still live, its NAPI can keep consuming net_iov entries from the page_pool alloc cache after the __netif_mp_uninstall_rxq() has already cleared their dma_addr, opening a window for the device to DMA to a stale or zero address. Fix it by swapping the two calls so the queue is stopped (and its NAPI quiesced) before the provider is uninstalled. No functional regression was observed across repeated runs of the nk_qlease.py HW selftest, which exercises the lease teardown path; this was tested against fbnic QEMU emulation. Fixes: 5602ad61ebee ("net: Proxy netif_mp_{open,close}_rxq for leased queues") Reported-by: Ahmed Abdelmoemen Signed-off-by: Daniel Borkmann Cc: David Wei Reviewed-by: Bobby Eshleman Reviewed-by: Nikolay Aleksandrov Link: https://patch.msgid.link/20260609212240.677889-1-daniel@iogearbox.net Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/core/netdev_rx_queue.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/core/netdev_rx_queue.c b/net/core/netdev_rx_queue.c index de4dac4c88b36..00a7011eb4d57 100644 --- a/net/core/netdev_rx_queue.c +++ b/net/core/netdev_rx_queue.c @@ -338,12 +338,12 @@ void __netif_mp_uninstall_rxq(struct netdev_rx_queue *rxq, void netif_rxq_cleanup_unlease(struct netdev_rx_queue *phys_rxq, struct netdev_rx_queue *virt_rxq) { - struct pp_memory_provider_params *p = &phys_rxq->mp_params; unsigned int rxq_idx = get_netdev_rx_queue_index(phys_rxq); + struct pp_memory_provider_params p = phys_rxq->mp_params; - if (!p->mp_ops) + if (!p.mp_ops) return; - __netif_mp_uninstall_rxq(virt_rxq, p); - __netif_mp_close_rxq(phys_rxq->dev, rxq_idx, p); + __netif_mp_close_rxq(phys_rxq->dev, rxq_idx, &p); + __netif_mp_uninstall_rxq(virt_rxq, &p); } From 3a49bbae676fef1ffe548971e6229ae2adeb9d10 Mon Sep 17 00:00:00 2001 From: Victor Nogueira Date: Wed, 10 Jun 2026 10:28:24 -0300 Subject: [PATCH 1391/2758] net/sched: sch_hfsc: Don't make class passive twice [ Upstream commit 90b662ea25f5e83bb3b8ccec5b93ced810b92fb8 ] update_vf() is called from two places for the same class during a single dequeue when the class's child qdisc (e.g. codel/fq_codel) drops its last packets while dequeuing: 1. The child calls qdisc_tree_reduce_backlog(), which, now that the child is empty, invokes hfsc_qlen_notify() -> update_vf(cl, 0, 0) and turns the class passive (cl_nactive is decremented up the hierarchy). 2. hfsc_dequeue() then calls update_vf(cl, qdisc_pkt_len(skb), cur_time) to charge the dequeued bytes. On the second call the class is already passive, but its child qdisc is still empty, so update_vf() arms go_passive again: if (cl->qdisc->q.qlen == 0 && cl->cl_flags & HFSC_FSC) go_passive = 1; The leaf is then skipped by the cl_nactive == 0 check inside the loop, which does not clear go_passive, so the stale go_passive propagates to the parent and decrements its cl_nactive a second time. A parent that still has other active children is driven to cl_nactive == 0 and removed from the vttree, even though those siblings are still backlogged. They are never dequeued again and the qdisc stalls. Fix this by only arming go_passive when the class is actually active, so an already-passive class no longer triggers a second passive transition. The byte accounting (cl->cl_total += len) still runs for every ancestor, so dequeued bytes continue to be counted exactly once. Fixes: 51eb3b65544c ("sch_hfsc: make hfsc_qlen_notify() idempotent") Reported-by: Anirudh Gupta Closes: https://lore.kernel.org/netdev/CAN2cbVe79oj0O9==m4+4x3v+O+qzRagA=2=wkrp9i9=CqYvyZA@mail.gmail.com/ Tested-by: Anirudh Gupta Acked-by: Jamal Hadi Salim Signed-off-by: Victor Nogueira Link: https://patch.msgid.link/20260610132824.3027549-1-victor@mojatatu.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/sch_hfsc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index e3dd6de8f1b6e..50112c8a71e03 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -753,7 +753,7 @@ update_vf(struct hfsc_class *cl, unsigned int len, u64 cur_time) u64 f; /* , myf_bound, delta; */ int go_passive = 0; - if (cl->qdisc->q.qlen == 0 && cl->cl_flags & HFSC_FSC) + if (cl->qdisc->q.qlen == 0 && cl->cl_flags & HFSC_FSC && cl->cl_nactive) go_passive = 1; for (; cl->cl_parent != NULL; cl = cl->cl_parent) { From 56f0a2e0a1d004e025cd031c3a801ab73959269f Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 10 Jun 2026 08:40:01 -0400 Subject: [PATCH 1392/2758] tipc: require net admin for TIPCv2 netlink mutators [ Upstream commit 86b0c540e2ea397cde021eecd24145f7c16a3d4e ] TIPCv2 registers mutating generic-netlink operations without admin permission flags. Generic netlink only checks CAP_NET_ADMIN when an operation sets GENL_ADMIN_PERM or GENL_UNS_ADMIN_PERM, so a local unprivileged process can currently change TIPC state through commands such as TIPC_NL_NET_SET, TIPC_NL_KEY_SET, TIPC_NL_KEY_FLUSH, and bearer enable/disable. The legacy TIPC netlink API already checks netlink_net_capable(..., CAP_NET_ADMIN) for administrative commands. Give the TIPCv2 mutators the equivalent generic-netlink gate. Use GENL_UNS_ADMIN_PERM, which maps to the same namespace-aware CAP_NET_ADMIN check that netlink_net_capable() performs, so the behaviour matches the legacy path and keeps working for CAP_NET_ADMIN holders in a non-initial user namespace (containers). A QEMU/KASAN repro run as uid/gid 65534 with zero effective capabilities previously succeeded in changing the network id and node identity, setting and flushing key material, and enabling/disabling a UDP bearer. With this patch applied the same operations fail with -EPERM. Fixes: 0655f6a8635b ("tipc: add bearer disable/enable to new netlink api") Link: https://lore.kernel.org/all/20260604163102.2658553-1-dominik.czarnota@trailofbits.com/ Signed-off-by: Michael Bommarito Reviewed-by: Tung Nguyen Link: https://patch.msgid.link/20260610124003.3831170-2-michael.bommarito@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/tipc/netlink.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c index 1a9a5bdaccf4f..8336a9664703f 100644 --- a/net/tipc/netlink.c +++ b/net/tipc/netlink.c @@ -152,11 +152,13 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_BEARER_DISABLE, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_bearer_disable, }, { .cmd = TIPC_NL_BEARER_ENABLE, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_bearer_enable, }, { @@ -168,11 +170,13 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_BEARER_ADD, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_bearer_add, }, { .cmd = TIPC_NL_BEARER_SET, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_bearer_set, }, { @@ -197,11 +201,13 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_LINK_SET, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_node_set_link, }, { .cmd = TIPC_NL_LINK_RESET_STATS, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_node_reset_link_stats, }, { @@ -213,6 +219,7 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_MEDIA_SET, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_media_set, }, { @@ -228,6 +235,7 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_NET_SET, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_net_set, }, { @@ -238,6 +246,7 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_MON_SET, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_node_set_monitor, }, { @@ -255,6 +264,7 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_PEER_REMOVE, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_peer_rm, }, #ifdef CONFIG_TIPC_MEDIA_UDP @@ -269,11 +279,13 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_KEY_SET, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_node_set_key, }, { .cmd = TIPC_NL_KEY_FLUSH, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_node_flush_key, }, #endif From 1e2c956745777e6ffdee7f30da5935741c03ee1e Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 10 Jun 2026 08:40:02 -0400 Subject: [PATCH 1393/2758] tipc: prevent snt_unacked underflow on CONN_ACK [ Upstream commit ab3e10b44ba5411779aac7afd2477917dd77750f ] tipc_sk_conn_proto_rcv() subtracts the peer-supplied connection ack count from the unsigned 16-bit send counter snt_unacked without checking that it does not exceed the number of messages actually outstanding: tsk->snt_unacked -= msg_conn_ack(hdr); msg_conn_ack() is read straight from a received CONN_MANAGER/CONN_ACK message. If the ack count is larger than snt_unacked, the subtraction wraps to a near-maximum value, leaving tsk_conn_cong() permanently true and starving the connection of further transmits. Validate the ACK count at the start of the CONN_ACK block and drop the message if it acknowledges more messages than are outstanding. A peer (or, for a local connection, the connected peer socket) can otherwise wedge a TIPC connection's send side by sending an oversized connection ack. Fixes: 10724cc7bb78 ("tipc: redesign connection-level flow control") Signed-off-by: Michael Bommarito Reviewed-by: Tung Nguyen Link: https://patch.msgid.link/20260610124003.3831170-3-michael.bommarito@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/tipc/socket.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 9329919fb07f0..f64f7a35b5c91 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -1362,6 +1362,9 @@ static void tipc_sk_conn_proto_rcv(struct tipc_sock *tsk, struct sk_buff *skb, __skb_queue_tail(xmitq, skb); return; } else if (mtyp == CONN_ACK) { + if (tsk->snt_unacked < msg_conn_ack(hdr)) + goto exit; + was_cong = tsk_conn_cong(tsk); tipc_sk_push_backlog(tsk, msg_nagle_ack(hdr)); tsk->snt_unacked -= msg_conn_ack(hdr); From 973bf0ed896b9898668dbadb82ed849d7d573010 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 10 Jun 2026 08:40:03 -0400 Subject: [PATCH 1394/2758] tipc: reject inverted service ranges from peer bindings [ Upstream commit 2afb648f7b99216c687db1f89739c995e1144153 ] tipc_update_nametbl() inserts a binding advertised by a peer node using the lower and upper service-range bounds taken directly from the wire, without checking that lower <= upper. The local bind path validates the ordering (tipc_uaddr_valid()), but the name-distribution path does not. A binding with lower > upper is inserted at the far end of the service-range rbtree (keyed on lower) where no lookup or withdrawal can ever match it (service_range_foreach_match() requires sr->lower <= end). The publication, its service_range node and the augmented rbtree entry are then leaked for the lifetime of the namespace, and there is no per-peer cap equivalent to TIPC_MAX_PUBL on locally created bindings. Reject inverted ranges in the network path as well. A peer node can otherwise leak unbounded binding-table memory by sending PUBLICATION items with lower > upper. Fixes: 37922ea4a310 ("tipc: permit overlapping service ranges in name table") Signed-off-by: Michael Bommarito Reviewed-by: Tung Nguyen Link: https://patch.msgid.link/20260610124003.3831170-4-michael.bommarito@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/tipc/name_distr.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c index 190b49c5cbc3e..ba4f4906e13b7 100644 --- a/net/tipc/name_distr.c +++ b/net/tipc/name_distr.c @@ -280,12 +280,21 @@ static bool tipc_update_nametbl(struct net *net, struct distr_item *i, u32 node, u32 dtype) { struct publication *p = NULL; + u32 lower = ntohl(i->lower); + u32 upper = ntohl(i->upper); struct tipc_socket_addr sk; - struct tipc_uaddr ua; u32 key = ntohl(i->key); + struct tipc_uaddr ua; + + /* A peer-advertised binding with lower > upper can never be matched + * or withdrawn and would leak the publication; the local bind path + * rejects such ranges, so reject ranges learned from the network too. + */ + if (lower > upper) + return false; tipc_uaddr(&ua, TIPC_SERVICE_RANGE, TIPC_CLUSTER_SCOPE, - ntohl(i->type), ntohl(i->lower), ntohl(i->upper)); + ntohl(i->type), lower, upper); sk.ref = ntohl(i->port); sk.node = node; From a79b884de8fb7fd6019d2c3d8cd23695a17d898a Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Thu, 11 Jun 2026 16:03:55 -0700 Subject: [PATCH 1395/2758] cxl/test: Unregister cxl_acpi in cxl_test_init() error path [ Upstream commit 50cc34be04a0ea7522b739c9c7a71367cfbc489c ] In cxl_test_init(), Once cxl_mock_platform_device_add() succeeds, all error paths after needs to call platform_device_unregister() instead of platform_device_put() to clean up. Fixes: 67dcdd4d3b83 ("tools/testing/cxl: Introduce a mocked-up CXL port hierarchy") Reported-by: sashiko-bot Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260611230355.198912-1-dave.jiang@intel.com Signed-off-by: Dave Jiang Signed-off-by: Sasha Levin --- tools/testing/cxl/test/cxl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c index 296516eecfd6a..fd6ff00541dce 100644 --- a/tools/testing/cxl/test/cxl.c +++ b/tools/testing/cxl/test/cxl.c @@ -1951,7 +1951,7 @@ static __init int cxl_test_init(void) err_mem: cxl_mem_exit(); err_root: - platform_device_put(cxl_acpi); + platform_device_unregister(cxl_acpi); err_rch: cxl_rch_topo_exit(); err_single: From d37344fc7729aa24e4576245db81803c8c190fa9 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Thu, 11 Jun 2026 16:03:05 -0700 Subject: [PATCH 1396/2758] cxl/test: Add check after kzalloc() memory in alloc_mock_res() [ Upstream commit dfe28c8592538152e9611341dae6f7be1735b3f1 ] alloc_mock_res() calls kzalloc() without checking the return value. Add scope based resource management to deal with the allocated memory cleanly. Reported-by: sashiko-bot Fixes: 67dcdd4d3b83 ("tools/testing/cxl: Introduce a mocked-up CXL port hierarchy") Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260611230305.197390-1-dave.jiang@intel.com Signed-off-by: Dave Jiang Signed-off-by: Sasha Levin --- tools/testing/cxl/test/cxl.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c index fd6ff00541dce..5733a526f7dcb 100644 --- a/tools/testing/cxl/test/cxl.c +++ b/tools/testing/cxl/test/cxl.c @@ -433,12 +433,16 @@ static void depopulate_all_mock_resources(void) static struct cxl_mock_res *alloc_mock_res(resource_size_t size, int align) { - struct cxl_mock_res *res = kzalloc(sizeof(*res), GFP_KERNEL); struct genpool_data_align data = { .align = align, }; unsigned long phys; + struct cxl_mock_res *res __free(kfree) = kzalloc(sizeof(*res), + GFP_KERNEL); + if (!res) + return NULL; + INIT_LIST_HEAD(&res->list); phys = gen_pool_alloc_algo(cxl_mock_pool, size, gen_pool_first_fit_align, &data); @@ -453,7 +457,7 @@ static struct cxl_mock_res *alloc_mock_res(resource_size_t size, int align) list_add(&res->list, &mock_res); mutex_unlock(&mock_res_lock); - return res; + return no_free_ptr(res); } /* Only update CFMWS0 as this is used by the auto region. */ From acff30cfc0d72465b51b0bfdf019f1cb54e15314 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Tue, 2 Jun 2026 22:38:26 +0800 Subject: [PATCH 1397/2758] crypto: marvell/octeontx - fix DMA cleanup using wrong loop index [ Upstream commit 7891c64c0520519782470ba29bac8a5761e295d8 ] The sg_cleanup path used list[i] instead of list[j] when unmapping DMA buffers, leaking successfully mapped entries and repeatedly unmapping the failed one. Fixes: 10b4f09491bf ("crypto: marvell - add the Virtual Function driver for CPT") Signed-off-by: Felix Gu Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/marvell/octeontx/otx_cptvf_reqmgr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/crypto/marvell/octeontx/otx_cptvf_reqmgr.c b/drivers/crypto/marvell/octeontx/otx_cptvf_reqmgr.c index c80baf1ad90b2..89030e2711ce4 100644 --- a/drivers/crypto/marvell/octeontx/otx_cptvf_reqmgr.c +++ b/drivers/crypto/marvell/octeontx/otx_cptvf_reqmgr.c @@ -157,8 +157,8 @@ static inline int setup_sgio_components(struct pci_dev *pdev, sg_cleanup: for (j = 0; j < i; j++) { if (list[j].dma_addr) { - dma_unmap_single(&pdev->dev, list[i].dma_addr, - list[i].size, DMA_BIDIRECTIONAL); + dma_unmap_single(&pdev->dev, list[j].dma_addr, + list[j].size, DMA_BIDIRECTIONAL); } list[j].dma_addr = 0; From d319b83b97b3585550d9ae592dfa78c755b6129e Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Tue, 2 Jun 2026 22:55:35 +0800 Subject: [PATCH 1398/2758] crypto: cavium/cpt - fix DMA cleanup using wrong loop index [ Upstream commit 9dbf173bd32d5f81b005008b682bfb50aa093455 ] The sg_cleanup error path used list[i] instead of list[j] when unmapping DMA buffers, leaking successfully mapped entries and repeatedly unmapping the failed one. Fixes: c694b233295b ("crypto: cavium - Add the Virtual Function driver for CPT") Signed-off-by: Felix Gu Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/cavium/cpt/cptvf_reqmanager.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/crypto/cavium/cpt/cptvf_reqmanager.c b/drivers/crypto/cavium/cpt/cptvf_reqmanager.c index e183b60277ff1..de305cbeccbe0 100644 --- a/drivers/crypto/cavium/cpt/cptvf_reqmanager.c +++ b/drivers/crypto/cavium/cpt/cptvf_reqmanager.c @@ -108,8 +108,8 @@ static int setup_sgio_components(struct cpt_vf *cptvf, struct buf_ptr *list, sg_cleanup: for (j = 0; j < i; j++) { if (list[j].dma_addr) { - dma_unmap_single(&pdev->dev, list[i].dma_addr, - list[i].size, DMA_BIDIRECTIONAL); + dma_unmap_single(&pdev->dev, list[j].dma_addr, + list[j].size, DMA_BIDIRECTIONAL); } list[j].dma_addr = 0; From f92b8ba180658cfd1351b98d5e5ae4d144a2daac Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Thu, 4 Jun 2026 12:30:54 +0800 Subject: [PATCH 1399/2758] crypto: rng - Free default RNG on module exit [ Upstream commit 606ba888b98e0d26a2c4e5c8dc0542e3ad8f0f3a ] When the rng module is removed the default RNG will be leaked. Call crypto_del_default_rng to free it if possible. Fixes: 7cecadb7cca8 ("crypto: rng - Do not free default RNG when it becomes unused") Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- crypto/rng.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/crypto/rng.c b/crypto/rng.c index 1d4b9177bad4d..a07569ed1e5e0 100644 --- a/crypto/rng.c +++ b/crypto/rng.c @@ -232,5 +232,16 @@ void crypto_unregister_rngs(struct rng_alg *algs, int count) } EXPORT_SYMBOL_GPL(crypto_unregister_rngs); +static void __exit rng_exit(void) +{ + int err; + + err = crypto_del_default_rng(); + if (err) + pr_err("Failed delete default RNG: %d\n", err); +} + +module_exit(rng_exit); + MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Random Number Generator"); From 89345f1cf007a6ea626cca1488c4d60c49631322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Thu, 11 Jun 2026 10:57:22 -0300 Subject: [PATCH 1400/2758] ALSA: usb-audio: qcom: Guard sideband endpoint removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 2773023abb381e36ce02d364022d901f6f7a416d ] qmi_stop_session() conditionally looks up the cached data and sync endpoints, but removes each endpoint unconditionally. The data endpoint is always present for an active offload stream, while the sync endpoint is optional. When no sync endpoint exists, ep still refers to the data endpoint and the code attempts to remove that endpoint a second time. The current sideband implementation rejects the duplicate removal, but the teardown path should not pass an unrelated endpoint for an absent sync endpoint. Only look up and remove an endpoint when its cached pipe exists, check the lookup result, and clear the cached pipe after handling it. This matches the normal stream-disable path. Fixes: 326bbc348298 ("ALSA: usb-audio: qcom: Introduce QC USB SND offloading support") Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260611-alsa-usb-qcom-guard-sideband-endpoint-removal-v1-1-00e73787c156@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/usb/qcom/qc_audio_offload.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c index a0009503b2c59..649228421e2f6 100644 --- a/sound/usb/qcom/qc_audio_offload.c +++ b/sound/usb/qcom/qc_audio_offload.c @@ -794,15 +794,23 @@ static void qmi_stop_session(void) continue; } /* Release XHCI endpoints */ - if (info->data_ep_pipe) + if (info->data_ep_pipe) { ep = usb_pipe_endpoint(uadev[pcm_card_num].udev, info->data_ep_pipe); - xhci_sideband_remove_endpoint(uadev[pcm_card_num].sb, ep); + if (ep) + xhci_sideband_remove_endpoint(uadev[pcm_card_num].sb, + ep); + info->data_ep_pipe = 0; + } - if (info->sync_ep_pipe) + if (info->sync_ep_pipe) { ep = usb_pipe_endpoint(uadev[pcm_card_num].udev, info->sync_ep_pipe); - xhci_sideband_remove_endpoint(uadev[pcm_card_num].sb, ep); + if (ep) + xhci_sideband_remove_endpoint(uadev[pcm_card_num].sb, + ep); + info->sync_ep_pipe = 0; + } disable_audio_stream(subs); } From 0527a56cb327021cb73167cfddf0e49efa043500 Mon Sep 17 00:00:00 2001 From: HanQuan Date: Fri, 12 Jun 2026 10:32:22 +0000 Subject: [PATCH 1401/2758] ALSA: seq: Fix kernel heap address leak in bounce_error_event() [ Upstream commit efc86691e4d8083d9e380ea95042c2cf679f65fd ] The comment above bounce_error_event() documents that user clients should receive SNDRV_SEQ_EVENT_BOUNCE with the original event embedded as variable-length data, while kernel clients should receive SNDRV_SEQ_EVENT_KERNEL_ERROR with a quoted kernel pointer. However, the implementation unconditionally uses SNDRV_SEQ_EVENT_KERNEL_ERROR with data.quote.event set to the raw struct snd_seq_event pointer for all clients. When a bounce error event is delivered to a USER_CLIENT via snd_seq_read(), the kernel heap address in data.quote.event is exposed to userspace through copy_to_user() in the fixed-length branch. This is a distinct leak path from the one addressed by commit 705dd6dcbc0e ("ALSA: seq: Clear variable event pointer on read"), which sanitizes data.ext.ptr in the variable-length branch of snd_seq_read(). The bounce_error_event() leak uses fixed-length events that take the else branch where no sanitization occurs. Differentiate the bounce event by client type. For USER_CLIENT, send SNDRV_SEQ_EVENT_BOUNCE with SNDRV_SEQ_EVENT_LENGTH_VARIABLE and data.ext pointing to the original event. The variable-length path in snd_seq_event_dup() copies the event data into chained cells, and snd_seq_expand_var_event() copies only the content -- never the pointer -- to userspace. For KERNEL_CLIENT, keep the existing SNDRV_SEQ_EVENT_KERNEL_ERROR behavior with the quoted pointer. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: HanQuan Link: https://patch.msgid.link/20260612103222.2528305-1-eilaimemedsnaimel@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/core/seq/seq_clientmgr.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index ce0dd53399bfa..9481fa0ca0f12 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -538,16 +538,38 @@ static int bounce_error_event(struct snd_seq_client *client, /* set up quoted error */ memset(&bounce_ev, 0, sizeof(bounce_ev)); - bounce_ev.type = SNDRV_SEQ_EVENT_KERNEL_ERROR; - bounce_ev.flags = SNDRV_SEQ_EVENT_LENGTH_FIXED; + + if (client->type == USER_CLIENT) { + /* + * For user clients, send SNDRV_SEQ_EVENT_BOUNCE with the + * original event embedded as variable-length data. This + * avoids exposing data.quote.event (a kernel pointer) to + * userspace. The variable-length path in snd_seq_event_dup() + * copies the event data from data.ext.ptr into chained cells, + * and snd_seq_expand_var_event() copies only the data content + * -- never the pointer -- to userspace. + */ + bounce_ev.type = SNDRV_SEQ_EVENT_BOUNCE; + bounce_ev.flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE; + bounce_ev.data.ext.len = sizeof(struct snd_seq_event); + bounce_ev.data.ext.ptr = (char *)event; + } else { + /* + * For kernel clients, quote the event pointer directly. + * Kernel consumers can safely dereference the pointer. + */ + bounce_ev.type = SNDRV_SEQ_EVENT_KERNEL_ERROR; + bounce_ev.flags = SNDRV_SEQ_EVENT_LENGTH_FIXED; + bounce_ev.data.quote.origin = event->dest; + bounce_ev.data.quote.event = event; + bounce_ev.data.quote.value = -err; /* use positive value */ + } + bounce_ev.queue = SNDRV_SEQ_QUEUE_DIRECT; bounce_ev.source.client = SNDRV_SEQ_CLIENT_SYSTEM; bounce_ev.source.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE; bounce_ev.dest.client = client->number; bounce_ev.dest.port = event->source.port; - bounce_ev.data.quote.origin = event->dest; - bounce_ev.data.quote.event = event; - bounce_ev.data.quote.value = -err; /* use positive value */ result = snd_seq_deliver_single_event(NULL, &bounce_ev, atomic, hop + 1); if (result < 0) { client->event_lost++; From 8646f00ce021e49f4f05bc1d4060a0c27b25d0e1 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Sat, 30 May 2026 07:28:52 -0400 Subject: [PATCH 1402/2758] iommu/dma-iommu: Fix wrong scatterlist length assignment in P2PDMA path [ Upstream commit db50fb87015b955a5a0c155293b2dd40d63a3b9e ] In iommu_dma_map_sg(), when handling PCI P2PDMA cases, the DMA length of the current scatterlist segment `s` is incorrectly assigned from the head entry `sg->length` instead of the current entry `s->length`. This typo causes all P2PDMA segments in the scatterlist to inherit the length of the first segment, leading to corrupted DMA lengths for multi- segment scatterlists. Fix this by using `s->length` instead of `sg->length`. Fixes: a25e7962db ("PCI/P2PDMA: Refactor the p2pdma mapping helpers") Signed-off-by: Li RongQing Reviewed-by: Logan Gunthorpe Signed-off-by: Joerg Roedel Signed-off-by: Sasha Levin --- drivers/iommu/dma-iommu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index 381b60d9e7cea..3ca2c5da2843b 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -1465,7 +1465,7 @@ int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, */ s->dma_address = pci_p2pdma_bus_addr_map( p2pdma_state.mem, sg_phys(s)); - sg_dma_len(s) = sg->length; + sg_dma_len(s) = s->length; sg_dma_mark_bus_address(s); continue; default: From 3f6340faca9efb68f4df4f4da4bd7a30615bab0a Mon Sep 17 00:00:00 2001 From: Alex Mastro Date: Wed, 10 Jun 2026 13:44:41 -0700 Subject: [PATCH 1403/2758] iommufd: Clarify IOAS_MAP_FILE dma-buf support [ Upstream commit 298ab7e6f1637cec44163f52e84e2030ec16ed9d ] IOMMU_IOAS_MAP_FILE is documented as mapping a memfd, but the implementation first tries to resolve the fd as a dma-buf and has a special path for supported dma-buf exporters. In particular, VFIO PCI dma-bufs exported through VFIO_DEVICE_FEATURE_DMA_BUF can be mapped when they describe a single DMA range. Update the UAPI comment so userspace understands that certain kinds of dma-buf are supported in addition to memfd. Fixes: 44ebaa1744fd ("iommufd: Accept a DMABUF through IOMMU_IOAS_MAP_FILE") Link: https://patch.msgid.link/r/20260610-tmp-v1-1-b8ccbf557391@fb.com Signed-off-by: Alex Mastro Assisted-by: Codex:gpt-5.5-high Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- include/uapi/linux/iommufd.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/include/uapi/linux/iommufd.h b/include/uapi/linux/iommufd.h index e998dfbd69603..0425d452d41ed 100644 --- a/include/uapi/linux/iommufd.h +++ b/include/uapi/linux/iommufd.h @@ -224,13 +224,17 @@ struct iommu_ioas_map { * @size: sizeof(struct iommu_ioas_map_file) * @flags: same as for iommu_ioas_map * @ioas_id: same as for iommu_ioas_map - * @fd: the memfd to map - * @start: byte offset from start of file to map from + * @fd: the memfd or supported dma-buf file to map + * @start: byte offset from start of the file to map from * @length: same as for iommu_ioas_map * @iova: same as for iommu_ioas_map * - * Set an IOVA mapping from a memfd file. All other arguments and semantics - * match those of IOMMU_IOAS_MAP. + * Set an IOVA mapping from a memfd file. On kernels with dma-buf support, + * supported dma-buf files may also be accepted. This is not a generic + * dma-buf import path; currently supported dma-bufs include single-range + * VFIO PCI dma-bufs exported through VFIO_DEVICE_FEATURE_DMA_BUF, and + * other dma-bufs may be rejected. All other arguments and semantics match + * those of IOMMU_IOAS_MAP. */ struct iommu_ioas_map_file { __u32 size; From d1944b71c18e7494756bff1a6f80c25be99eaecb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20P=C3=B6schel?= Date: Fri, 12 Jun 2026 12:52:44 +0200 Subject: [PATCH 1404/2758] spi: xilinx: use FIFO occupancy register to determine buffer size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 47f3b5365536e8c38f264824ab15fdb74454e066 ] The method the driver uses to determine the size of the FIFO has a problem. What it currently does is this: It stops the SPI hardware and writes to the TX FIFO register until TX FIFO FULL asserts in the status register. But the hardware does not only have the FIFO, it also has a shift register which can hold a byte. This can be seen, when writing a byte to the FIFO (while the SPI hardware is stopped,) the TX FIFO EMPTY is still empty. So, if we have a FIFO size of 16 for example, the current method returns a 17. This is a problem, at least when using the driver in irq mode. The same size determined for the TX FIFO is also assumed for the RX FIFO. When a SPI transaction wants to write the amount of the FIFO size or more bytes, the following happens, for example with 16 bytes FIFO size: The driver stops the SPI hardware and writes 17 bytes to the TX FIFO and starts the SPI hardware and goes sleep. The hardware then shifts out 17 bytes (FIFO + shift register) and simultaneously reads bytes into the RX FIFO, but it only has 16 places, so it looses one byte. Then TX FIFO empty asserts, wakes the driver again, which has a fast path and reads 16 bytes from the RX FIFO, but before reading the last 17th byte (which is lost) it does this: sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET); if (!(sr & XSPI_SR_RX_EMPTY_MASK)) { xilinx_spi_rx(xspi); rx_words--; } It reads the status register and checks if the RX FIFO is not empty. But it is empty in our case. So this check spins in a while loop forever locking the driver. This patch fixes the logic to determine the FIFO size. Fixes: 4c9a761402d7 ("spi/xilinx: Simplify spi_fill_tx_fifo") Signed-off-by: Lars Pöschel Reviewed-by: Michal Simek Link: https://patch.msgid.link/20260612105244.9076-1-lars.poeschel.linux@edag.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-xilinx.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c index 9f065d4e27d19..b95485710e2f7 100644 --- a/drivers/spi/spi-xilinx.c +++ b/drivers/spi/spi-xilinx.c @@ -371,11 +371,18 @@ static int xilinx_spi_find_buffer_size(struct xilinx_spi *xspi) xspi->regs + XIPIF_V123B_RESETR_OFFSET); /* Fill the Tx FIFO with as many words as possible */ - do { + while (1) { xspi->write_fn(0, xspi->regs + XSPI_TXD_OFFSET); sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET); + if (sr & XSPI_SR_TX_FULL_MASK) + break; + n_words++; - } while (!(sr & XSPI_SR_TX_FULL_MASK)); + } + + /* Handle the NO FIFO case separately */ + if (!n_words) + return 1; return n_words; } From 119f943c67a7e9bf1187089f349ec547cd9713ed Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Wed, 3 Jun 2026 14:26:55 -0700 Subject: [PATCH 1405/2758] iommu: Avoid copying the user array twice in the full-array copy helper [ Upstream commit e28bee5b445178390d63f7a93a5a219063c6434e ] iommu_copy_struct_from_full_user_array() copies a whole user array into a kernel buffer. In the common case, where user entry_len equals destination entry size, it takes a fast path and copies the whole array with a single copy_from_user(). That fast path does not return, so it falls through into the item-by-item copy_struct_from_user() loop and copies every entry a second time. For an equal entry_len that loop is just a copy_from_user() of the same bytes, so the whole array is copied twice for no benefit. Return right after the bulk copy. The per-item loop then runs only on the slow path, where entry_len differs and each entry needs size adaption. Fixes: 4f2e59ccb698 ("iommu: Add iommu_copy_struct_from_full_user_array helper") Link: https://patch.msgid.link/r/6c9eca4ff584cb977661e97799ac6fe934e7f51c.1780521606.git.nicolinc@nvidia.com Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Nicolin Chen Reviewed-by: Lu Baolu Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- include/linux/iommu.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/iommu.h b/include/linux/iommu.h index e587d4ac4d331..6957144263793 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -547,6 +547,7 @@ iommu_copy_struct_from_full_user_array(void *kdst, size_t kdst_entry_size, user_array->entry_num * user_array->entry_len)) return -EFAULT; + return 0; } /* Copy item by item */ From d1a993c8ce3f2c978aeea16180e977cf54ca5588 Mon Sep 17 00:00:00 2001 From: "Chia-Lin Kao (AceLan)" Date: Wed, 10 Jun 2026 12:17:53 +0800 Subject: [PATCH 1406/2758] ASoC: sdw_utils: fix missing component_name for cs42l43 part_id 0x2A3B MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit c429fbea6174a7dd40c4215288589a50e8a33ff6 ] commit 87a3f5c8ac20 ("ASoC: sdw_utils: cs42l43: allow spk component names to be combined") moved spk:cs42l43-spk generation from rtd_init() into the asoc_sdw_rtd_init() generic path by adding component_name to codec_info_list entries. However, only the 0x4243 cs42l43 entry was updated; the 0x2A3B entry (vendor_id 0x01fa, Cirrus Logic cs42l43 with sidecar bridge) was missed. Without component_name on the 0x2A3B dp6 DAI, asoc_sdw_rtd_init() never accumulates spk_components and never appends 'spk:cs42l43-spk' (or its sidecar alias 'spk:cs35l56-bridge') to card->components. The sof-soundwire UCM regex ' spk:([a-z0-9]+...)' then fails to match, causing WirePlumber to mark all HiFi profiles as unavailable=no and fall back to the Off profile — resulting in Dummy Output in GNOME. The existing sidecar redirect in asoc_sdw_rtd_init() already handles the SOC_SDW_SIDECAR_AMPS case: when component_name is 'cs42l43-spk' and sidecar amps are active, it substitutes 'cs35l56-bridge' into card->components, which matches the existing cs35l56-bridge.conf UCM file. Fixes: 87a3f5c8ac20 ("ASoC: sdw_utils: cs42l43: allow spk component names to be combined") Signed-off-by: Chia-Lin Kao (AceLan) Link: https://patch.msgid.link/20260610041753.1151088-1-acelan.kao@canonical.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/sdw_utils/soc_sdw_utils.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index e440c23271001..4e8646d231c4f 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -818,6 +818,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { { .direction = {true, false}, .codec_name = "cs42l43-codec", + .component_name = "cs42l43-spk", .dai_name = "cs42l43-dp6", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, From 44b2397eb67b7f728640989a22d062e41f94ab64 Mon Sep 17 00:00:00 2001 From: Li Ming Date: Sat, 6 Jun 2026 15:51:00 +0800 Subject: [PATCH 1407/2758] cxl/region: Fix out-of-bounds access in cxl_cancel_auto_attach() [ Upstream commit cbda6a2c2bec2a5fb30a2ce85baeab15b5fc7db3 ] In cxl_cancel_auto_attach(), it assumes cxled->pos is a valid index for accessing p->targets[]. However, cxled->pos can be set to negative errno in cxl_region_sort_targets() if cxl_calc_interleave_pos() fails. This causes the driver to use a negative index to access p->targets[], resulting in out-of-bounds access. Fix it by walking p->targets[] instead of using cxled->pos directly. Fixes: 87805c32e6ad ("cxl/region: Fix use-after-free from auto assembly failure") Signed-off-by: Li Ming Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260606-fix_two_issues_introduced_by_cxl_cancel_auto_attach-v1-1-5d94ca06c4e4@zohomail.com Signed-off-by: Dave Jiang Signed-off-by: Sasha Levin --- drivers/cxl/core/region.c | 40 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index e50dc716d4e82..8956b049c4620 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -2011,8 +2011,9 @@ static int cxl_region_sort_targets(struct cxl_region *cxlr) cxled->pos = cxl_calc_interleave_pos(cxled, &cxlr->hpa_range); /* * Record that sorting failed, but still continue to calc - * cxled->pos so that follow-on code paths can reliably - * do p->targets[cxled->pos] to self-reference their entry. + * cxled->pos so that cxl_calc_interleave_pos() emits its + * dev_dbg() for every member. which is useful for auto + * discovery debug. */ if (cxled->pos < 0) rc = -ENXIO; @@ -2202,18 +2203,30 @@ static int cxl_region_attach(struct cxl_region *cxlr, return 0; } -static int cxl_region_by_target(struct device *dev, const void *data) +static int cxl_region_remove_target(struct device *dev, void *data) { - const struct cxl_endpoint_decoder *cxled = data; + struct cxl_endpoint_decoder *cxled = data; struct cxl_region_params *p; struct cxl_region *cxlr; + int i; if (!is_cxl_region(dev)) return 0; cxlr = to_cxl_region(dev); p = &cxlr->params; - return p->targets[cxled->pos] == cxled; + for (i = 0; i < p->interleave_ways; i++) { + if (p->targets[i] == cxled) { + p->nr_targets--; + cxled->state = CXL_DECODER_STATE_AUTO; + cxled->pos = -1; + p->targets[i] = NULL; + + return 1; + } + } + + return 0; } /* @@ -2222,25 +2235,10 @@ static int cxl_region_by_target(struct device *dev, const void *data) */ static void cxl_cancel_auto_attach(struct cxl_endpoint_decoder *cxled) { - struct cxl_region_params *p; - struct cxl_region *cxlr; - int pos = cxled->pos; - if (cxled->state != CXL_DECODER_STATE_AUTO_STAGED) return; - struct device *dev __free(put_device) = - bus_find_device(&cxl_bus_type, NULL, cxled, cxl_region_by_target); - if (!dev) - return; - - cxlr = to_cxl_region(dev); - p = &cxlr->params; - - p->nr_targets--; - cxled->state = CXL_DECODER_STATE_AUTO; - cxled->pos = -1; - p->targets[pos] = NULL; + bus_for_each_dev(&cxl_bus_type, NULL, cxled, cxl_region_remove_target); } static struct cxl_region * From 6fde3fcdfec438ba0df26e25a8ac7c3707211799 Mon Sep 17 00:00:00 2001 From: Li Ming Date: Sat, 6 Jun 2026 15:51:01 +0800 Subject: [PATCH 1408/2758] cxl/region: Fill first free targets[] slot during auto-discovery [ Upstream commit aa8a76711c15041ec1e42c3a74c15c2df0bd31f6 ] Any invalid endpoint decoder pointer in the target array of an active region is not allowed by cxl driver. This means cxl driver always assumes the first p->nr_targets entries of the target array in an auto-assembly region are valid. However, there are scenarios that could leave NULL endpoint decoder pointer holes in the target array. 1. When cxl_cancel_auto_attach() removes an endpoint decoder from a target array, the target slot is set to NULL. If the removed endpoint decoder is not the last element in the target array, the target array will contain a NULL hole. 2. When a auto-assembly region removes an assigned endpoint decoder, if the removed endpoint decoder is not the last element in the target array, always remains a NULL hole in the target array. When a NULL pointer hole exists in a region's target array, it introduces two potential problems: 1. Access an endpoint decoder via a NULL pointer. it always trigger calltrace like that. Oops: general protection fault, probably for non-canonical address 0xdffffc0000000008: 0000 [#1] SMP KASAN PTI RIP: 0010:cxl_calc_interleave_pos+0x26/0x810 [cxl_core] Call Trace: cxl_region_attach+0xc50/0x2140 [cxl_core] cxl_add_to_region+0x321/0x2330 [cxl_core] discover_region+0x92/0x150 [cxl_port] device_for_each_child+0xf3/0x170 cxl_port_probe+0x150/0x200 [cxl_port] cxl_bus_probe+0x4f/0xa0 [cxl_core] really_probe+0x1c8/0x960 __driver_probe_device+0x323/0x450 driver_probe_device+0x45/0x120 __device_attach_driver+0x15d/0x280 bus_for_each_drv+0x10f/0x190 2. Not having enough valid endpoint decoders attached to an auto-assembly region. if an auto-assembly region is created with lock flag or assigned endpoint decoder with lock flag, which means assigned endpoint decoder will not be reset during detaching, they could re-attach to the auto-assembly region again. But cxl region driver relies on p->nr_targets to verify whether the required number of endpoint decoders has been attached, and NULL endpoint decoder pointers are still counted in that case. To fix above issues, adjust cxl_region_attach_auto() logic to find the first free target slot for endpoint decoder attachment, this ensures NULL holes in the target array are filled, rather than adding new endpoint decoders at the tail of the target array. Fixes: 87805c32e6ad ("cxl/region: Fix use-after-free from auto assembly failure") Fixes: 2230c4bdc412 ("cxl: Add handling of locked CXL decoder") Suggested-by: Alison Schofield Signed-off-by: Li Ming Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260606-fix_two_issues_introduced_by_cxl_cancel_auto_attach-v1-2-5d94ca06c4e4@zohomail.com Signed-off-by: Dave Jiang Signed-off-by: Sasha Levin --- drivers/cxl/core/region.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index 8956b049c4620..174876e0475f1 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -1848,8 +1848,21 @@ static int cxl_region_attach_auto(struct cxl_region *cxlr, * this means that userspace can view devices in the wrong position * before the region activates, and must be careful to understand when * it might be racing region autodiscovery. + * + * The endpoint decoder will be recorded into the first free slot of + * the target array. */ - pos = p->nr_targets; + for (pos = 0; pos < p->interleave_ways; pos++) { + if (!p->targets[pos]) + break; + } + + if (pos == p->interleave_ways) { + dev_err(&cxlr->dev, "%s: unable to find a free target slot\n", + dev_name(&cxled->cxld.dev)); + return -ENXIO; + } + p->targets[pos] = cxled; cxled->pos = pos; cxled->state = CXL_DECODER_STATE_AUTO_STAGED; From 93299544227948cab8b2700aa7198075278f677b Mon Sep 17 00:00:00 2001 From: David Matlack Date: Thu, 11 Jun 2026 21:39:45 +0000 Subject: [PATCH 1409/2758] vfio: selftests: Ensure libvfio output dirs are always created [ Upstream commit 785562e31dbcd85ca583cf58c446e63aa8a5af08 ] Add an explicit dependency between the output object files and the output directories that need to be created to hold those files. This ensures that the output directories are always created. Creating the output directories at parse time (current behavior) doesn't support the scenario where someone does "make clean all". The directories will be created during parsing, deleted during "clean" and then not available for the "all" target. Use an order-only prerequisite for the output directories, rather than a normal prerequisite, to avoid unnecessary recompilations. Fixes: 19faf6fd969c ("vfio: selftests: Add a helper library for VFIO selftests") Reported-by: Sashiko Closes: https://lore.kernel.org/kvm/20260610010314.DB8861F00893@smtp.kernel.org/ Signed-off-by: David Matlack Link: https://lore.kernel.org/r/20260611213945.3714421-1-dmatlack@google.com Signed-off-by: Alex Williamson Signed-off-by: Sasha Levin --- tools/testing/selftests/vfio/lib/libvfio.mk | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/vfio/lib/libvfio.mk b/tools/testing/selftests/vfio/lib/libvfio.mk index 9f47bceed16f4..f90235a1043fe 100644 --- a/tools/testing/selftests/vfio/lib/libvfio.mk +++ b/tools/testing/selftests/vfio/lib/libvfio.mk @@ -19,11 +19,13 @@ LIBVFIO_OUTPUT := $(OUTPUT)/libvfio LIBVFIO_O := $(patsubst %.c, $(LIBVFIO_OUTPUT)/%.o, $(LIBVFIO_C)) LIBVFIO_O_DIRS := $(shell dirname $(LIBVFIO_O) | uniq) -$(shell mkdir -p $(LIBVFIO_O_DIRS)) + +$(LIBVFIO_O_DIRS): + mkdir -p $@ CFLAGS += -I$(LIBVFIO_SRCDIR)/include -$(LIBVFIO_O): $(LIBVFIO_OUTPUT)/%.o : $(LIBVFIO_SRCDIR)/%.c +$(LIBVFIO_O): $(LIBVFIO_OUTPUT)/%.o : $(LIBVFIO_SRCDIR)/%.c | $(LIBVFIO_O_DIRS) $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@ EXTRA_CLEAN += $(LIBVFIO_OUTPUT) From 2e8e11cd32243a2cefe13cd8be59c7cda995b37a Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Thu, 4 Jun 2026 20:55:20 +0800 Subject: [PATCH 1410/2758] ASoC: adau1372: Clear PLL_EN on failed PLL lock without reset GPIO [ Upstream commit 69b4141b428bcf2cf7a863950c0d6e5c5ae89ac1 ] The PLL lock failure path in adau1372_set_power() unwinds by putting the regmap back in cache-only mode, asserting the optional power-down GPIO and disabling mclk. adau1372_enable_pll() enables CLK_CTRL.PLL_EN before polling the PLL lock bit. If the lock fails on a board without a power-down GPIO, the error path disables mclk and returns an error, but leaves PLL_EN set in the hardware register. The normal power-off path already handles the no-GPIO case by explicitly clearing PLL_EN. Mirror that cleanup in the PLL lock failure path and clear PLL_EN while the regmap is still live, before switching it back to cache-only mode. Fixes: bfe6a264effc ("ASoC: adau1372: Fix clock leak on PLL lock failure") Signed-off-by: Guangshuo Li Link: https://patch.msgid.link/20260604125520.1428905-1-lgs201920130244@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/adau1372.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/codecs/adau1372.c b/sound/soc/codecs/adau1372.c index d7363f9d53bb3..879afeb81c422 100644 --- a/sound/soc/codecs/adau1372.c +++ b/sound/soc/codecs/adau1372.c @@ -813,6 +813,11 @@ static int adau1372_set_power(struct adau1372 *adau1372, bool enable) if (adau1372->use_pll) { ret = adau1372_enable_pll(adau1372); if (ret) { + if (!adau1372->pd_gpio) + regmap_update_bits(adau1372->regmap, + ADAU1372_REG_CLK_CTRL, + ADAU1372_CLK_CTRL_PLL_EN, + 0); regcache_cache_only(adau1372->regmap, true); if (adau1372->pd_gpio) gpiod_set_value(adau1372->pd_gpio, 1); From b0b6a9c65cb72c901fdcc6ae83d7afd70cdca1b8 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 19 May 2026 14:01:54 -0700 Subject: [PATCH 1411/2758] cxl/region: Block region delete during region creation [ Upstream commit d91feb88692e81b00cd22f0125cfcd04970b4a0b ] Expand the range lock, rename it "regions_lock", to disable region deletion in the critical period between construct_region() and attach_target(), as well as the period between device_add() and registering the remove actions. Otherwise, userspace can confuse the kernel. It can violate the assumption the region stays registered through the completion of cxl_add_to_region(). It can violate the assumption that devm_add_action_or_reset() is working with a live 'struct cxl_region'. It is ok for the region to disappear outside of those windows as that mirrors device hotplug flows where the proper locks are held. Fixes: a32320b71f08 ("cxl/region: Add region autodiscovery") Signed-off-by: Dan Williams Reviewed-by: Alejandro Lucero Tested-by: ALejandro Lucero Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260519210158.1499795-2-djbw@kernel.org Signed-off-by: Dave Jiang Signed-off-by: Sasha Levin --- drivers/cxl/core/port.c | 2 +- drivers/cxl/core/region.c | 12 ++++++++++-- drivers/cxl/cxl.h | 4 ++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c index c5aacd7054f1d..6e7a70d51cfe4 100644 --- a/drivers/cxl/core/port.c +++ b/drivers/cxl/core/port.c @@ -2016,7 +2016,7 @@ struct cxl_root_decoder *cxl_root_decoder_alloc(struct cxl_port *port, return ERR_PTR(rc); } - mutex_init(&cxlrd->range_lock); + mutex_init(&cxlrd->regions_lock); cxld = &cxlsd->cxld; cxld->dev.type = &cxl_decoder_root_type; diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index 174876e0475f1..3abf7703f7f25 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -2790,6 +2790,10 @@ static ssize_t create_region_store(struct device *dev, const char *buf, if (rc != 1) return -EINVAL; + ACQUIRE(mutex_intr, regions_lock)(&cxlrd->regions_lock); + if ((rc = ACQUIRE_ERR(mutex_intr, ®ions_lock))) + return rc; + cxlr = __create_region(cxlrd, mode, id, CXL_DECODER_HOSTONLYMEM); if (IS_ERR(cxlr)) return PTR_ERR(cxlr); @@ -2849,6 +2853,11 @@ static ssize_t delete_region_store(struct device *dev, struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev); struct cxl_port *port = to_cxl_port(dev->parent); struct cxl_region *cxlr; + int rc; + + ACQUIRE(mutex_intr, regions_lock)(&cxlrd->regions_lock); + if ((rc = ACQUIRE_ERR(mutex_intr, ®ions_lock))) + return rc; cxlr = cxl_find_region_by_name(cxlrd, buf); if (IS_ERR(cxlr)) @@ -3787,12 +3796,11 @@ int cxl_add_to_region(struct cxl_endpoint_decoder *cxled) * for the HPA range, one does the construction and the others * add to that. */ - mutex_lock(&cxlrd->range_lock); + guard(mutex)(&cxlrd->regions_lock); struct cxl_region *cxlr __free(put_cxl_region) = cxl_find_region_by_range(cxlrd, &ctx.hpa_range); if (!cxlr) cxlr = construct_region(cxlrd, &ctx); - mutex_unlock(&cxlrd->range_lock); rc = PTR_ERR_OR_ZERO(cxlr); if (rc) diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h index 1297594beaec3..3900a0778571d 100644 --- a/drivers/cxl/cxl.h +++ b/drivers/cxl/cxl.h @@ -359,7 +359,7 @@ struct cxl_rd_ops { * @cache_size: extended linear cache size if exists, otherwise zero. * @region_id: region id for next region provisioning event * @platform_data: platform specific configuration data - * @range_lock: sync region autodiscovery by address range + * @regions_lock: sync region discovery, construction, and deletion * @qos_class: QoS performance class cookie * @ops: CXL root decoder operations * @cxlsd: base cxl switch decoder @@ -369,7 +369,7 @@ struct cxl_root_decoder { resource_size_t cache_size; atomic_t region_id; void *platform_data; - struct mutex range_lock; + struct mutex regions_lock; int qos_class; struct cxl_rd_ops ops; struct cxl_switch_decoder cxlsd; From be44c1c04f85d7b7ac1c597a30b443bbd346acbd Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 19 May 2026 14:01:55 -0700 Subject: [PATCH 1412/2758] cxl/region: Resolve region deletion races [ Upstream commit 4dd86ca99ffcc413cbf79063fd9956ef54e0ca91 ] Sungwoo noticed that the sysfs trigger to delete a region may try to delete a region multiple times. It also has no exclusion relative to the kernel releasing the region via CXL root device teardown. Instead of installing new cxl root devres actions per region, use the existing root decoder unregistration event to remove all remaining regions. An xarray of regions replaces a devres list of regions. This handles 3 separate issues with the old approach: 1/ sysfs users racing to delete the same region: no longer possible now that the regions_lock is held over the lookup and deletion. 2/ multiple actions triggering deletion of the same region: solved by erasing regions while holding @regions_lock, and only proceeding on successful erasure. 3/ userspace racing devres_release_all() to trigger the devres not found warning: solved by sysfs unregistration not requiring a release action Fixes: 779dd20cfb56 ("cxl/region: Add region creation support") Reported-by: Sungwoo Kim Closes: http://lore.kernel.org/20260427032010.916681-2-iam@sung-woo.kim Signed-off-by: Dan Williams Reviewed-by: Alejandro Lucero Tested-by: ALejandro Lucero Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260519210158.1499795-3-djbw@kernel.org Signed-off-by: Dave Jiang Signed-off-by: Sasha Levin --- drivers/cxl/core/core.h | 2 ++ drivers/cxl/core/port.c | 5 ++++ drivers/cxl/core/region.c | 60 +++++++++++++++++++++------------------ drivers/cxl/cxl.h | 4 +++ 4 files changed, 44 insertions(+), 27 deletions(-) diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h index 82ca3a4767080..07555ae638594 100644 --- a/drivers/cxl/core/core.h +++ b/drivers/cxl/core/core.h @@ -52,6 +52,7 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd, u64 dpa); int devm_cxl_add_dax_region(struct cxl_region *cxlr); int devm_cxl_add_pmem_region(struct cxl_region *cxlr); +void kill_regions(struct cxl_root_decoder *cxlrd); #else static inline u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, @@ -81,6 +82,7 @@ static inline int cxl_region_init(void) static inline void cxl_region_exit(void) { } +static inline void kill_regions(struct cxl_root_decoder *cxlrd) { }; #define CXL_REGION_ATTR(x) NULL #define CXL_REGION_TYPE(x) NULL #define SET_CXL_REGION_ATTR(x) diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c index 6e7a70d51cfe4..1215ee4f40351 100644 --- a/drivers/cxl/core/port.c +++ b/drivers/cxl/core/port.c @@ -458,6 +458,8 @@ static void cxl_root_decoder_release(struct device *dev) if (atomic_read(&cxlrd->region_id) >= 0) memregion_free(atomic_read(&cxlrd->region_id)); + mutex_destroy(&cxlrd->regions_lock); + xa_destroy(&cxlrd->regions); __cxl_decoder_release(&cxlrd->cxlsd.cxld); kfree(cxlrd); } @@ -2017,6 +2019,7 @@ struct cxl_root_decoder *cxl_root_decoder_alloc(struct cxl_port *port, } mutex_init(&cxlrd->regions_lock); + xa_init(&cxlrd->regions); cxld = &cxlsd->cxld; cxld->dev.type = &cxl_decoder_root_type; @@ -2192,6 +2195,8 @@ static void cxld_unregister(void *dev) if (is_endpoint_decoder(dev)) cxl_decoder_detach(NULL, to_cxl_endpoint_decoder(dev), -1, DETACH_INVALIDATE); + if (is_root_decoder(dev)) + kill_regions(to_cxl_root_decoder(dev)); device_unregister(dev); } diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index 3abf7703f7f25..fda9e97a6ddcd 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -2548,12 +2548,13 @@ static struct cxl_region *to_cxl_region(struct device *dev) return container_of(dev, struct cxl_region, dev); } -static void unregister_region(void *_cxlr) +static void unregister_region(struct cxl_region *cxlr) { - struct cxl_region *cxlr = _cxlr; + struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent); struct cxl_region_params *p = &cxlr->params; int i; + xa_erase(&cxlrd->regions, cxlr->id); device_del(&cxlr->dev); /* @@ -2684,6 +2685,19 @@ static int cxl_region_calculate_adistance(struct notifier_block *nb, return NOTIFY_STOP; } +/* unwind all remaining regions */ +void kill_regions(struct cxl_root_decoder *cxlrd) +{ + unsigned long index; + struct cxl_region *cxlr; + + guard(mutex)(&cxlrd->regions_lock); + /* no more region creation */ + cxlrd->dead = true; + xa_for_each(&cxlrd->regions, index, cxlr) + unregister_region(cxlr); +} + /** * devm_cxl_add_region - Adds a region to a decoder * @cxlrd: root decoder @@ -2722,14 +2736,15 @@ static struct cxl_region *devm_cxl_add_region(struct cxl_root_decoder *cxlrd, if (rc) goto err; - rc = devm_add_action_or_reset(port->uport_dev, unregister_region, cxlr); - if (rc) + rc = xa_insert(&cxlrd->regions, cxlr->id, cxlr, GFP_KERNEL); + if (rc) { + unregister_region(cxlr); return ERR_PTR(rc); + } dev_dbg(port->uport_dev, "%s: created %s\n", dev_name(&cxlrd->cxlsd.cxld.dev), dev_name(dev)); return cxlr; - err: put_device(dev); return ERR_PTR(rc); @@ -2758,6 +2773,9 @@ static struct cxl_region *__create_region(struct cxl_root_decoder *cxlrd, { int rc; + if (cxlrd->dead) + return ERR_PTR(-ENXIO); + switch (mode) { case CXL_PARTMODE_RAM: case CXL_PARTMODE_PMEM: @@ -2833,38 +2851,27 @@ static ssize_t region_show(struct device *dev, struct device_attribute *attr, } DEVICE_ATTR_RO(region); -static struct cxl_region * -cxl_find_region_by_name(struct cxl_root_decoder *cxlrd, const char *name) -{ - struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld; - struct device *region_dev; - - region_dev = device_find_child_by_name(&cxld->dev, name); - if (!region_dev) - return ERR_PTR(-ENODEV); - - return to_cxl_region(region_dev); -} - static ssize_t delete_region_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev); - struct cxl_port *port = to_cxl_port(dev->parent); struct cxl_region *cxlr; - int rc; + int rc, id; ACQUIRE(mutex_intr, regions_lock)(&cxlrd->regions_lock); if ((rc = ACQUIRE_ERR(mutex_intr, ®ions_lock))) return rc; - cxlr = cxl_find_region_by_name(cxlrd, buf); - if (IS_ERR(cxlr)) - return PTR_ERR(cxlr); + rc = sscanf(buf, "region%d\n", &id); + if (rc != 1) + return -EINVAL; - devm_release_action(port->uport_dev, unregister_region, cxlr); - put_device(&cxlr->dev); + cxlr = xa_load(&cxlrd->regions, id); + if (!cxlr || !sysfs_streq(buf, dev_name(&cxlr->dev))) + return -ENODEV; + + unregister_region(cxlr); return len; } @@ -3729,7 +3736,6 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd, { struct cxl_endpoint_decoder *cxled = ctx->cxled; struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); - struct cxl_port *port = cxlrd_to_port(cxlrd); struct cxl_dev_state *cxlds = cxlmd->cxlds; int rc, part = READ_ONCE(cxled->part); struct cxl_region *cxlr; @@ -3750,7 +3756,7 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd, rc = __construct_region(cxlr, ctx); if (rc) { - devm_release_action(port->uport_dev, unregister_region, cxlr); + unregister_region(cxlr); return ERR_PTR(rc); } diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h index 3900a0778571d..f43abd1903ce9 100644 --- a/drivers/cxl/cxl.h +++ b/drivers/cxl/cxl.h @@ -360,6 +360,8 @@ struct cxl_rd_ops { * @region_id: region id for next region provisioning event * @platform_data: platform specific configuration data * @regions_lock: sync region discovery, construction, and deletion + * @regions: regions to remove at root decoder destruct time + * @dead: root decoder dead to region creation * @qos_class: QoS performance class cookie * @ops: CXL root decoder operations * @cxlsd: base cxl switch decoder @@ -370,6 +372,8 @@ struct cxl_root_decoder { atomic_t region_id; void *platform_data; struct mutex regions_lock; + struct xarray regions; + bool dead; int qos_class; struct cxl_rd_ops ops; struct cxl_switch_decoder cxlsd; From c4c7aeb3e5f4243519b474f8c73d4fcffdb2d3a6 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 19 May 2026 14:01:56 -0700 Subject: [PATCH 1413/2758] cxl/memdev: Pin parents for entire memdev lifetime [ Upstream commit bd3a6ff4b84e0fc3fca8556d338270603df13f2e ] In order to be able to manage the driver that uses a memdev attach mechanism the parent needs to stick around for the device_release_driver(cxlmd->dev.parent) event. Fixes: 29317f8dc6ed ("cxl/mem: Introduce cxl_memdev_attach for CXL-dependent operation") Signed-off-by: Dan Williams Reviewed-by: Alejandro Lucero Tested-by: ALejandro Lucero Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260519210158.1499795-4-djbw@kernel.org Signed-off-by: Dave Jiang Signed-off-by: Sasha Levin --- drivers/cxl/core/memdev.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c index 80e65690eb773..91c99eeea92ce 100644 --- a/drivers/cxl/core/memdev.c +++ b/drivers/cxl/core/memdev.c @@ -25,9 +25,11 @@ static DEFINE_IDA(cxl_memdev_ida); static void cxl_memdev_release(struct device *dev) { struct cxl_memdev *cxlmd = to_cxl_memdev(dev); + struct device *parent = dev->parent; ida_free(&cxl_memdev_ida, cxlmd->id); kfree(cxlmd); + put_device(parent); } static char *cxl_memdev_devnode(const struct device *dev, umode_t *mode, kuid_t *uid, @@ -707,7 +709,7 @@ static struct cxl_memdev *cxl_memdev_alloc(struct cxl_dev_state *cxlds, dev = &cxlmd->dev; device_initialize(dev); lockdep_set_class(&dev->mutex, &cxl_memdev_key); - dev->parent = cxlds->dev; + dev->parent = get_device(cxlds->dev); dev->bus = &cxl_bus_type; dev->devt = MKDEV(cxl_mem_major, cxlmd->id); dev->type = &cxl_memdev_type; From 14357ba006e1586e4b4e809c074b21baf0aa4c4b Mon Sep 17 00:00:00 2001 From: Lucas Tsai Date: Tue, 9 Jun 2026 19:44:03 +0800 Subject: [PATCH 1414/2758] power: supply: core: fix supplied_from allocations [ Upstream commit ba61aed9a34671222d1149acfc2f0179a9ce7e80 ] If dts property power-supplies has multiple values, then accessing to psy->supplied_from[i-1] in __power_supply_populate_supplied_from will overrun supplied_from array. Fixes: f6e0b081fb30 ("power_supply: Populate supplied_from hierarchy from the device tree") Signed-off-by: Lucas Tsai Link: https://patch.msgid.link/20260609114403.3896073-1-lucas_tsai@richtek.com Signed-off-by: Sebastian Reichel Signed-off-by: Sasha Levin --- drivers/power/supply/power_supply_core.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index a446d3d086fcd..2532e221b2e19 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -292,18 +292,13 @@ static int power_supply_check_supplies(struct power_supply *psy) if (cnt == 1) return 0; - /* All supplies found, allocate char ** array for filling */ - psy->supplied_from = devm_kzalloc(&psy->dev, sizeof(*psy->supplied_from), + /* All supplies found, allocate char * array for filling */ + psy->supplied_from = devm_kcalloc(&psy->dev, + cnt - 1, sizeof(*psy->supplied_from), GFP_KERNEL); if (!psy->supplied_from) return -ENOMEM; - *psy->supplied_from = devm_kcalloc(&psy->dev, - cnt - 1, sizeof(**psy->supplied_from), - GFP_KERNEL); - if (!*psy->supplied_from) - return -ENOMEM; - return power_supply_populate_supplied_from(psy); } #else From 4dafc411948469277b276724c3b2b4408c02c04c Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 9 Jun 2026 10:18:31 -0400 Subject: [PATCH 1415/2758] handshake: Require admin permission for DONE command [ Upstream commit 81246a65303d9635266b1334490142caaf86a11f ] ACCEPT and DONE are the two downcalls of the handshake genl family, both intended for use by the trusted handshake agent (tlshd). ACCEPT already requires GENL_ADMIN_PERM; DONE has no privilege check at all. The fd-lookup in handshake_nl_done_doit() only confirms that some pending handshake request exists for the supplied sockfd; it does not authenticate the sender. An unprivileged process that guesses or observes a valid sockfd can therefore submit a DONE with HANDSHAKE_A_DONE_STATUS == 0, leaving the kernel consumer to proceed as if the handshake succeeded. A non-zero status on a forged DONE tears down a legitimate in-flight handshake before tlshd can report its real result. Fixes: 3b3009ea8abb ("net/handshake: Create a NETLINK service for handling handshake requests") Reviewed-by: Jeff Layton Reviewed-by: Hannes Reinecke Signed-off-by: Chuck Lever Link: https://patch.msgid.link/20260609141831.90694-1-cel@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- Documentation/netlink/specs/handshake.yaml | 1 + net/handshake/genl.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/netlink/specs/handshake.yaml b/Documentation/netlink/specs/handshake.yaml index 1024297b38513..ffec12b467597 100644 --- a/Documentation/netlink/specs/handshake.yaml +++ b/Documentation/netlink/specs/handshake.yaml @@ -125,6 +125,7 @@ operations: name: done doc: Handler reports handshake completion attribute-set: done + flags: [admin-perm] do: request: attributes: diff --git a/net/handshake/genl.c b/net/handshake/genl.c index 4b20cd9cdd0e0..feac1ad063ee7 100644 --- a/net/handshake/genl.c +++ b/net/handshake/genl.c @@ -38,7 +38,7 @@ static const struct genl_split_ops handshake_nl_ops[] = { .doit = handshake_nl_done_doit, .policy = handshake_done_nl_policy, .maxattr = HANDSHAKE_A_DONE_REMOTE_AUTH, - .flags = GENL_CMD_CAP_DO, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, }, }; From bb72b1c6755631c74b7e0878ee55bb81c06776c0 Mon Sep 17 00:00:00 2001 From: Joe Damato Date: Tue, 9 Jun 2026 13:44:58 -0700 Subject: [PATCH 1416/2758] bnxt: fix head underflow on XDP head-grow [ Upstream commit e26657fe3b85c068b01f42bb0c602f242d643ba9 ] The xdp.py test test_xdp_native_adjst_head_grow_data crashes when run on a bnxt machine (and also crashes in NIPA). It seems that the bug is an underflow in bnxt_rx_multi_page_skb, which builds the skb head: napi_build_skb(data_ptr - bp->rx_offset, rxr->rx_page_size); The problem with this expression is that in page mode, rx_offset is: bp->rx_offset = NET_IP_ALIGN + XDP_PACKET_HEADROOM; Which evaluates (at least on x86_64) to 258. The test test_xdp_native_adjst_head_grow_data tests a case where the head is adjusted by -256. When this test runs, data_ptr is shifted to frag_start + 2 (where frag_start = page_address(page) + offset). Then, bnxt_rx_multi_page_skb is invoked and the napi_build_skb expression subtracts 258, landing at an address before frag_start. This could be either the previous fragment or the previous physical page when the offset is < 256 (e.g. if the fragment started at offset 0). When the skb is freed, the page pool fragment reference is dropped on either the wrong page or the wrong frag of the right page. In either case, the corrupted reference count can lead to the page being prematurely recycled while still in use. Once (incorrectly) recycled, it can be handed out again and on driver teardown this would result in a double free. The commit under fixes updated this code to handle the case where the native page size is >= 64k, but it unintentionally broke the head grow case. To fix this, add an offset field to struct bnxt_sw_rx_bd, mirroring the existing offset field in struct bnxt_sw_rx_agg_bd. Populate it on allocation and preserve it on reuse. In bnxt_rx_multi_page_skb, use the newly added offset field to compute the fragment start and pass that to napi_build_skb. Adjust the layout with skb_reserve. There are two cases, the non-adjustment case and the adjustment case. In both cases, the skb is built at page_address(page) + offset to account for the case where the native page size >= 64K and skb_reserve is called with data_ptr - (page_address(page) + offset). That difference equals bp->rx_offset when data_ptr was not moved, or bp->rx_offset + xdp_adjust when XDP adjusted the head. Re-running the failing test with this commit applied causes the test to run successfully to completion. The other rx_skb_func implementations don't have this issue. Fixes: f6974b4c2d8e ("bnxt_en: Fix page pool logic for page size >= 64K") Signed-off-by: Joe Damato Reviewed-by: Michael Chan Link: https://patch.msgid.link/20260609204458.2237787-2-joe@dama.to Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 10 ++++++++-- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index c999f9733326a..1125130449887 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -1011,6 +1011,7 @@ int bnxt_alloc_rx_data(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, mapping += bp->rx_dma_offset; rx_buf->data = page; rx_buf->data_ptr = page_address(page) + offset + bp->rx_offset; + rx_buf->offset = offset; } else { u8 *data = __bnxt_alloc_rx_frag(bp, &mapping, rxr, gfp); @@ -1019,6 +1020,7 @@ int bnxt_alloc_rx_data(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, rx_buf->data = data; rx_buf->data_ptr = data + bp->rx_offset; + rx_buf->offset = 0; } rx_buf->mapping = mapping; @@ -1040,6 +1042,7 @@ void bnxt_reuse_rx_data(struct bnxt_rx_ring_info *rxr, u16 cons, void *data) prod_rx_buf->data_ptr = cons_rx_buf->data_ptr; prod_rx_buf->mapping = cons_rx_buf->mapping; + prod_rx_buf->offset = cons_rx_buf->offset; prod_bd = &rxr->rx_desc_ring[RX_RING(bp, prod)][RX_IDX(prod)]; cons_bd = &rxr->rx_desc_ring[RX_RING(bp, cons)][RX_IDX(cons)]; @@ -1175,8 +1178,11 @@ static struct sk_buff *bnxt_rx_multi_page_skb(struct bnxt *bp, struct page *page = data; u16 prod = rxr->rx_prod; struct sk_buff *skb; + void *frag_start; int err; + frag_start = page_address(page) + rxr->rx_buf_ring[cons].offset; + err = bnxt_alloc_rx_data(bp, rxr, prod, GFP_ATOMIC); if (unlikely(err)) { bnxt_reuse_rx_data(rxr, cons, data); @@ -1185,13 +1191,13 @@ static struct sk_buff *bnxt_rx_multi_page_skb(struct bnxt *bp, dma_addr -= bp->rx_dma_offset; dma_sync_single_for_cpu(&bp->pdev->dev, dma_addr, rxr->rx_page_size, bp->rx_dir); - skb = napi_build_skb(data_ptr - bp->rx_offset, rxr->rx_page_size); + skb = napi_build_skb(frag_start, rxr->rx_page_size); if (!skb) { page_pool_recycle_direct(rxr->page_pool, page); return NULL; } skb_mark_for_recycle(skb); - skb_reserve(skb, bp->rx_offset); + skb_reserve(skb, data_ptr - (u8 *)frag_start); __skb_put(skb, len); return skb; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 61c847b36b9ff..a2e75cfb65ab0 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -911,6 +911,7 @@ struct bnxt_sw_rx_bd { void *data; u8 *data_ptr; dma_addr_t mapping; + unsigned int offset; }; struct bnxt_sw_rx_agg_bd { From 8874dafc9099bc49c2e5ebba030f85d276421f92 Mon Sep 17 00:00:00 2001 From: Sechang Lim Date: Thu, 11 Jun 2026 09:29:18 +0000 Subject: [PATCH 1417/2758] tcp: clear sock_ops cb flags before force-closing a child socket [ Upstream commit 990348e5bb457697c2f1f7f7b65154a3334d9d2b ] A child socket inherits the listener's bpf_sock_ops_cb_flags via sk_clone_lock(). If its setup fails in tcp_v4_syn_recv_sock() / tcp_v6_syn_recv_sock(), the child is freed through put_and_exit, where inet_csk_prepare_forced_close() drops the socket lock and tcp_done() runs without it. If BPF_SOCK_OPS_STATE_CB_FLAG was inherited, tcp_done() -> tcp_set_state() calls tcp_call_bpf(), which expects the lock and trips sock_owned_by_me(): WARNING: include/net/sock.h:1799 at tcp_set_state+0x433/0x550 RIP: 0010:tcp_set_state+0x433/0x550 include/net/sock.h:1799 Call Trace: tcp_done+0xba/0x250 net/ipv4/tcp.c:5095 tcp_v4_syn_recv_sock+0x850/0xa50 net/ipv4/tcp_ipv4.c:1787 tcp_check_req+0xf30/0x1360 net/ipv4/tcp_minisocks.c:926 tcp_v4_rcv+0x1047/0x1b50 net/ipv4/tcp_ipv4.c:2164 The child is freed before it is ever established, so it should run no sock_ops callback. Clear its cb flags in inet_csk_prepare_for_destroy_sock(), the common point for the IPv4, IPv6 and chtls forced-close paths and for the MPTCP ->syn_recv_sock() failure path (dispose_child), which reaches tcp_done() on a child that was never established too. Suggested-by: Jiayuan Chen Fixes: d44874910a26 ("bpf: Add BPF_SOCK_OPS_STATE_CB") Signed-off-by: Sechang Lim Reviewed-by: Jiayuan Chen Reviewed-by: Kuniyuki Iwashima Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260611092923.1895982-1-rhkrqnwk98@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- include/net/tcp.h | 9 +++++++++ net/ipv4/inet_connection_sock.c | 1 + 2 files changed, 10 insertions(+) diff --git a/include/net/tcp.h b/include/net/tcp.h index 98848db62894a..607298501e123 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2942,6 +2942,11 @@ static inline int tcp_call_bpf_3arg(struct sock *sk, int op, u32 arg1, u32 arg2, return tcp_call_bpf(sk, op, 3, args); } +static inline void tcp_clear_sock_ops_cb_flags(struct sock *sk) +{ + tcp_sk(sk)->bpf_sock_ops_cb_flags = 0; +} + #else static inline int tcp_call_bpf(struct sock *sk, int op, u32 nargs, u32 *args) { @@ -2959,6 +2964,10 @@ static inline int tcp_call_bpf_3arg(struct sock *sk, int op, u32 arg1, u32 arg2, return -EPERM; } +static inline void tcp_clear_sock_ops_cb_flags(struct sock *sk) +{ +} + #endif static inline u32 tcp_timeout_init(struct sock *sk) diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 5b934ce8d98a8..690f7fb3f029e 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -1285,6 +1285,7 @@ EXPORT_SYMBOL(inet_csk_destroy_sock); void inet_csk_prepare_for_destroy_sock(struct sock *sk) { /* The below has to be done to allow calling inet_csk_destroy_sock */ + tcp_clear_sock_ops_cb_flags(sk); sock_set_flag(sk, SOCK_DEAD); tcp_orphan_count_inc(); } From 796dc63764d24bf01b68c02301ca9f9fafa68966 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Thu, 11 Jun 2026 18:36:48 +0200 Subject: [PATCH 1418/2758] virtio_net: do not allow tunnel csum offload for non GSO packets [ Upstream commit 86c51f0f23136ea5ef5541f607287e07150cd23f ] Fiona reports broken connectivity for virtio net setup using UDP tunnel inside the guest and NIC with not UDP tunnel TSO support in the host. Currently the virtio_net driver exposes csum offload for UDP-tunneled, TCP non GSO packets. Such packet reach the host as CSUM_PARTIAL ones with the 'encapsulation' flag cleared, as the virtio specification do not support this specific kind of offload. HW NICs with UDP tunnel TSO support - and those drivers directly accessing skb->csum_start/csum_offset - are still capable of computing the needed csum correctly, but otherwise the packets reach the wire with bad csum on both the inner and outer transport header. Address the issue explicitly disabling csum offload for UDP tunneled, non GSO packets via the ndo_features_check op. Fixes: 56a06bd40fab ("virtio_net: enable gso over UDP tunnel support.") Reported-by: Fiona Ebner Closes: https://bugzilla.proxmox.com/show_bug.cgi?id=7627 Tested-by: Fiona Ebner Tested-by: Gabriel Goller Acked-by: Michael S. Tsirkin Signed-off-by: Paolo Abeni Reviewed-by: Gabriel Goller Tested-by: Gabriel Goller Link: https://patch.msgid.link/6c3b6c47fb05c100f384630dc48f3975cf37b67a.1781195144.git.pabeni@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/virtio_net.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index f4adcfee7a800..7d2eeb9b12266 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -6222,6 +6222,19 @@ static void virtnet_free_irq_moder(struct virtnet_info *vi) rtnl_unlock(); } +static netdev_features_t virtnet_features_check(struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features) +{ + /* Inner csum offload is only available for GSO packets. */ + if (skb->encapsulation && + (!skb_is_gso(skb) || netif_needs_gso(skb, features))) + return features & ~NETIF_F_CSUM_MASK; + + /* Passthru. */ + return features; +} + static const struct net_device_ops virtnet_netdev = { .ndo_open = virtnet_open, .ndo_stop = virtnet_close, @@ -6235,7 +6248,7 @@ static const struct net_device_ops virtnet_netdev = { .ndo_bpf = virtnet_xdp, .ndo_xdp_xmit = virtnet_xdp_xmit, .ndo_xsk_wakeup = virtnet_xsk_wakeup, - .ndo_features_check = passthru_features_check, + .ndo_features_check = virtnet_features_check, .ndo_get_phys_port_name = virtnet_get_phys_port_name, .ndo_set_features = virtnet_set_features, .ndo_tx_timeout = virtnet_tx_timeout, From af54df2f44d9605614bc7ed96640302a240a9a62 Mon Sep 17 00:00:00 2001 From: Victor Nogueira Date: Wed, 10 Jun 2026 16:28:52 -0300 Subject: [PATCH 1419/2758] net/sched: sch_fq_codel: Do not call qdisc_tree_reduce_backlog during peek before restoring qlen [ Upstream commit 097f6fc7b1ae362dd7a9444b2572162fda73b284 ] Whenever fq_codel drops packets during peek, it calls qdisc_tree_reduce_backlog. An issue arises because it calls qdisc_tree_reduce_backlog before it reincrements the qlen. If qlen drops to zero, but peek returns an skb, the parent's qlen_notify callback will be executed even though fq_codel still has 1 packet on the queue and, thus, will mistakenly deactivate the parent's class causing issues like a recent report [1] and a wild memory access in qfq: [ 29.371146][ T360] Oops: general protection fault, probably for non-canonical address 0xfbd59c0000000024: 0000 [#1] SMP KASAN NOPTI [ 29.371666][ T360] KASAN: maybe wild-memory-access in range [0xdead000000000120-0xdead000000000127] [ 29.371987][ T360] CPU: 6 UID: 0 PID: 360 Comm: tc Not tainted 7.1.0-rc5-00285-gc530e5b2dbc6-dirty #82 PREEMPT(full) [ 29.372384][ T360] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 [ 29.372620][ T360] RIP: 0010:qfq_deactivate_agg (include/linux/list.h:1029 (discriminator 2) include/linux/list.h:1043 (discriminator 2) net/sched/sch_qfq.c:1369 (discriminator 2) net/sched/sch_qfq.c:1395 (discriminator 2)) sch_qfq [ 29.373544][ T360] RSP: 0018:ffff888102417370 EFLAGS: 00010216 [ 29.373800][ T360] RAX: 0000000000000000 RBX: ffff88811224d568 RCX: dffffc0000000000 [ 29.374079][ T360] RDX: 1ffff11021fe1543 RSI: ffff88810ff0aa00 RDI: dffffc0000000000 [ 29.374368][ T360] RBP: ffff88811224c280 R08: dead000000000122 R09: 1bd5a00000000024 [ 29.374649][ T360] R10: fffffbfff7940329 R11: fffffbfff7940329 R12: 0000000000000000 [ 29.374926][ T360] R13: dead000000000100 R14: ffff88811224d580 R15: ffff88811224d578 [ 29.375207][ T360] FS: 00007f5b794e5780(0000) GS:ffff88815d1e9000(0000) knlGS:0000000000000000 [ 29.375545][ T360] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 29.375823][ T360] CR2: 000055ffb091f000 CR3: 000000010a305000 CR4: 0000000000750ef0 [ 29.376103][ T360] PKRU: 55555554 [ 29.376258][ T360] Call Trace: [ 29.376401][ T360] ... [ 29.376885][ T360] qfq_reset_qdisc (net/sched/sch_qfq.c:357 net/sched/sch_qfq.c:1487) sch_qfq [ 29.377074][ T360] qdisc_reset (net/sched/sch_generic.c:1057) [ 29.377414][ T360] __qdisc_destroy (net/sched/sch_generic.c:1096) [ 29.377600][ T360] qdisc_graft (net/sched/sch_api.c:1062 net/sched/sch_api.c:1053 net/sched/sch_api.c:1159) [ 29.378593][ T360] tc_get_qdisc (net/sched/sch_api.c:1528 net/sched/sch_api.c:1556) Fix this by only calling qdisc_tree_reduce_backlog in peek after the qlen is restored. [1] http://lore.kernel.org/netdev/CAN2cbVe79oj0O9==m4+4x3v+O+qzRagA=2=wkrp9i9=CqYvyZA@mail.gmail.com/ Fixes: 342debc12183 ("codel: remove sch->q.qlen check before qdisc_tree_reduce_backlog()") Reported-by: Anirudh Gupta Closes: https://lore.kernel.org/netdev/CAN2cbVe79oj0O9==m4+4x3v+O+qzRagA=2=wkrp9i9=CqYvyZA@mail.gmail.com/ Tested-by: Anirudh Gupta Acked-by: Jamal Hadi Salim Signed-off-by: Victor Nogueira Link: https://patch.msgid.link/20260610192855.3121513-2-victor@mojatatu.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/sch_fq_codel.c | 41 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c index 5302d50f4bef6..09ab84580160a 100644 --- a/net/sched/sch_fq_codel.c +++ b/net/sched/sch_fq_codel.c @@ -281,7 +281,7 @@ static void drop_func(struct sk_buff *skb, void *ctx) qdisc_qstats_drop(sch); } -static struct sk_buff *fq_codel_dequeue(struct Qdisc *sch) +static struct sk_buff *__fq_codel_dequeue(struct Qdisc *sch) { struct fq_codel_sched_data *q = qdisc_priv(sch); struct sk_buff *skb; @@ -318,12 +318,49 @@ static struct sk_buff *fq_codel_dequeue(struct Qdisc *sch) qdisc_bstats_update(sch, skb); WRITE_ONCE(flow->deficit, flow->deficit - qdisc_pkt_len(skb)); + return skb; +} + +static void fq_codel_dequeue_drop(struct Qdisc *sch) +{ + struct fq_codel_sched_data *q = qdisc_priv(sch); + if (q->cstats.drop_count) { qdisc_tree_reduce_backlog(sch, q->cstats.drop_count, q->cstats.drop_len); q->cstats.drop_count = 0; q->cstats.drop_len = 0; } +} + +static struct sk_buff *fq_codel_dequeue(struct Qdisc *sch) +{ + struct sk_buff *skb; + + skb = __fq_codel_dequeue(sch); + + fq_codel_dequeue_drop(sch); + + return skb; +} + +static struct sk_buff *fq_codel_peek(struct Qdisc *sch) +{ + struct sk_buff *skb = skb_peek(&sch->gso_skb); + + if (!skb) { + skb = __fq_codel_dequeue(sch); + + if (skb) { + __skb_queue_head(&sch->gso_skb, skb); + /* it's still part of the queue */ + qdisc_qstats_backlog_inc(sch, skb); + sch->q.qlen++; + } + + fq_codel_dequeue_drop(sch); + } + return skb; } @@ -726,7 +763,7 @@ static struct Qdisc_ops fq_codel_qdisc_ops __read_mostly = { .priv_size = sizeof(struct fq_codel_sched_data), .enqueue = fq_codel_enqueue, .dequeue = fq_codel_dequeue, - .peek = qdisc_peek_dequeued, + .peek = fq_codel_peek, .init = fq_codel_init, .reset = fq_codel_reset, .destroy = fq_codel_destroy, From 91e0a793a72374c20ab31a40ccec21373e82e973 Mon Sep 17 00:00:00 2001 From: Victor Nogueira Date: Wed, 10 Jun 2026 16:28:53 -0300 Subject: [PATCH 1420/2758] net/sched: sch_codel: Do not call qdisc_tree_reduce_backlog during peek before restoring qlen [ Upstream commit 52f1da34c9f4d5bdc1e8b44242da5c7ba8db85f3 ] Whenever codel drops packets during peek, it calls qdisc_tree_reduce_backlog. An issue arises because it calls qdisc_tree_reduce_backlog before it reincrements the qlen. If qlen drops to zero, but peek returns an skb, the parent's qlen_notify callback will be executed even though codel still has 1 packet on the queue and, thus, will mistakenly deactivate the parent's class causing issues like a wild memory access when qfq has codel as a child: [ 36.339843][ T370] Oops: general protection fault, probably for non-canonical address 0xfbd59c0000000024: 0000 [#1] SMP KASAN NOPTI [ 36.340408][ T370] KASAN: maybe wild-memory-access in range [0xdead000000000120-0xdead000000000127] [ 36.340737][ T370] CPU: 2 UID: 0 PID: 370 Comm: tc Not tainted 7.1.0-rc5-00287-g66e13b626592 #87 PREEMPT(full) [ 36.341113][ T370] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 [ 36.341357][ T370] RIP: 0010:qfq_deactivate_agg (include/linux/list.h:1029 (discriminator 2) include/linux/list.h:1043 (discriminator 2) net/sched/sch_qfq.c:1369 (discriminator 2) net/sched/sch_qfq.c:1395 (discriminator 2)) sch_qfq [ 36.342221][ T370] RSP: 0018:ffff8881100ef370 EFLAGS: 00010216 [ 36.342422][ T370] RAX: 0000000000000000 RBX: ffff8881058a9568 RCX: dffffc0000000000 [ 36.342664][ T370] RDX: 1ffff11021064dc3 RSI: ffff888108326e00 RDI: dffffc0000000000 [ 36.342905][ T370] RBP: ffff8881058a8280 R08: dead000000000122 R09: 1bd5a00000000024 [ 36.343140][ T370] R10: fffffbfff2940329 R11: fffffbfff2940329 R12: 0000000000000000 [ 36.343383][ T370] R13: dead000000000100 R14: ffff8881058a9580 R15: ffff8881058a9578 [ 36.343631][ T370] FS: 00007fc04b0ca780(0000) GS:ffff888184fef000(0000) knlGS:0000000000000000 [ 36.343911][ T370] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 36.344116][ T370] CR2: 0000557c02c02000 CR3: 000000010e0ba000 CR4: 0000000000750ef0 [ 36.344359][ T370] PKRU: 55555554 [ 36.344481][ T370] Call Trace: ... [ 36.345054][ T370] qfq_reset_qdisc (net/sched/sch_qfq.c:357 net/sched/sch_qfq.c:1487) sch_qfq [ 36.345222][ T370] qdisc_reset (net/sched/sch_generic.c:1057) [ 36.345503][ T370] __qdisc_destroy (net/sched/sch_generic.c:1096) [ 36.345677][ T370] qdisc_graft (net/sched/sch_api.c:1062 net/sched/sch_api.c:1053 net/sched/sch_api.c:1159) [ 36.346335][ T370] tc_get_qdisc (net/sched/sch_api.c:1528 net/sched/sch_api.c:1556) Fix this by only calling qdisc_tree_reduce_backlog in peek after the qlen is restored. Fixes: 342debc12183 ("codel: remove sch->q.qlen check before qdisc_tree_reduce_backlog()") Acked-by: Jamal Hadi Salim Signed-off-by: Victor Nogueira Link: https://patch.msgid.link/20260610192855.3121513-3-victor@mojatatu.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/sch_codel.c | 48 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/net/sched/sch_codel.c b/net/sched/sch_codel.c index 317aae0ec7bd6..7d5076196aff6 100644 --- a/net/sched/sch_codel.c +++ b/net/sched/sch_codel.c @@ -56,7 +56,7 @@ static void drop_func(struct sk_buff *skb, void *ctx) qdisc_qstats_drop(sch); } -static struct sk_buff *codel_qdisc_dequeue(struct Qdisc *sch) +static struct sk_buff *__codel_qdisc_dequeue(struct Qdisc *sch) { struct codel_sched_data *q = qdisc_priv(sch); struct sk_buff *skb; @@ -65,13 +65,51 @@ static struct sk_buff *codel_qdisc_dequeue(struct Qdisc *sch) &q->stats, qdisc_pkt_len, codel_get_enqueue_time, drop_func, dequeue_func); + if (skb) + qdisc_bstats_update(sch, skb); + return skb; +} + +static void codel_dequeue_drop(struct Qdisc *sch) +{ + struct codel_sched_data *q = qdisc_priv(sch); + if (q->stats.drop_count) { - qdisc_tree_reduce_backlog(sch, q->stats.drop_count, q->stats.drop_len); + qdisc_tree_reduce_backlog(sch, q->stats.drop_count, + q->stats.drop_len); q->stats.drop_count = 0; q->stats.drop_len = 0; } - if (skb) - qdisc_bstats_update(sch, skb); +} + +static struct sk_buff *codel_qdisc_dequeue(struct Qdisc *sch) +{ + struct sk_buff *skb; + + skb = __codel_qdisc_dequeue(sch); + + codel_dequeue_drop(sch); + + return skb; +} + +static struct sk_buff *codel_peek(struct Qdisc *sch) +{ + struct sk_buff *skb = skb_peek(&sch->gso_skb); + + if (!skb) { + skb = __codel_qdisc_dequeue(sch); + + if (skb) { + __skb_queue_head(&sch->gso_skb, skb); + /* it's still part of the queue */ + qdisc_qstats_backlog_inc(sch, skb); + sch->q.qlen++; + } + + codel_dequeue_drop(sch); + } + return skb; } @@ -257,7 +295,7 @@ static struct Qdisc_ops codel_qdisc_ops __read_mostly = { .enqueue = codel_qdisc_enqueue, .dequeue = codel_qdisc_dequeue, - .peek = qdisc_peek_dequeued, + .peek = codel_peek, .init = codel_init, .reset = codel_reset, .change = codel_change, From 466f29477cae2bd1982a066d518a9b20c5a37924 Mon Sep 17 00:00:00 2001 From: Victor Nogueira Date: Wed, 10 Jun 2026 16:28:54 -0300 Subject: [PATCH 1421/2758] net/sched: sch_dualpi2: Do not call qdisc_tree_reduce_backlog during peek before restoring qlen [ Upstream commit 15cd0c93bf4f892d66bc7a93667e2357b5673365 ] Whenever dualpi2 drops packets during peek, it calls qdisc_tree_reduce_backlog. An issue arises because it calls qdisc_tree_reduce_backlog before it reincrements the qlen. If qlen drops to zero, but peek returns an skb, the parent's qlen_notify callback will be executed even though dualpi2 still has 1 packet on the queue and, thus, mistakenly deactivates the parent's class which leads to a null-ptr-deref: [ 101.427314][ T599] Oops: general protection fault, probably for non-canonical address 0xdffffc0000000009: 0000 [#1] SMP KASAN NOPTI [ 101.427755][ T599] KASAN: null-ptr-deref in range [0x0000000000000048-0x000000000000004f] [ 101.428048][ T599] CPU: 2 UID: 0 PID: 599 Comm: ping Not tainted 7.1.0-rc5-00284-gbce53c430ed7 #102 PREEMPT(full) [ 101.428400][ T599] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 [ 101.428608][ T599] RIP: 0010:qfq_dequeue (net/sched/sch_qfq.c:1150) sch_qfq [ 101.428821][ T599] Code: 00 fc ff df 80 3c 02 00 0f 85 46 0c 00 00 4c 8d 73 48 48 89 9d b8 02 00 00 48 b8 00 00 00 00 00 fc ff df 4c 89 f2 48 c1 ea 03 <80> 3c 02 00 0f 85 2d 0c 00 00 48 b8 00 00 00 00 00 fc ff df 4c 8b All code [ 101.429348][ T599] RSP: 0018:ffff8881110df4f0 EFLAGS: 00010216 [ 101.429541][ T599] RAX: dffffc0000000000 RBX: 0000000000000000 RCX: dffffc0000000000 [ 101.429763][ T599] RDX: 0000000000000009 RSI: 00000024c0000000 RDI: ffff88811436c2b0 [ 101.429985][ T599] RBP: ffff88811436c000 R08: ffff88811436c280 R09: 1ffff11021277523 [ 101.430206][ T599] R10: 1ffff11021277526 R11: 1ffff11021277527 R12: 00000024c0000000 [ 101.430423][ T599] R13: ffff88811436c2b8 R14: 0000000000000048 R15: 0000000020000000 [ 101.430642][ T599] FS: 00007f61813e1c40(0000) GS:ffff8881691ef000(0000) knlGS:0000000000000000 [ 101.430913][ T599] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 101.431100][ T599] CR2: 00005651650850a8 CR3: 000000010ca0b000 CR4: 0000000000750ef0 [ 101.431320][ T599] PKRU: 55555554 [ 101.431433][ T599] Call Trace: [ 101.431544][ T599] [ 101.431628][ T599] __qdisc_run (net/sched/sch_generic.c:322 net/sched/sch_generic.c:427 net/sched/sch_generic.c:445) [ 101.431792][ T599] ? dev_qdisc_enqueue (./include/trace/events/qdisc.h:49 (discriminator 22) net/core/dev.c:4176 (discriminator 22)) [ 101.431941][ T599] __dev_queue_xmit (./include/net/pkt_sched.h:120 ./include/net/pkt_sched.h:117 net/core/dev.c:4292 net/core/dev.c:4831) Fix this by only calling qdisc_tree_reduce_backlog in peek after the qlen is restored. Fixes: 8f9516daedd6 ("sched: Add enqueue/dequeue of dualpi2 qdisc") Acked-by: Jamal Hadi Salim Signed-off-by: Victor Nogueira Link: https://patch.msgid.link/20260610192855.3121513-4-victor@mojatatu.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/sch_dualpi2.c | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/net/sched/sch_dualpi2.c b/net/sched/sch_dualpi2.c index b81a922f5e468..592c58938d3e8 100644 --- a/net/sched/sch_dualpi2.c +++ b/net/sched/sch_dualpi2.c @@ -577,7 +577,7 @@ static void drop_and_retry(struct dualpi2_sched_data *q, struct sk_buff *skb, qdisc_qstats_drop(sch); } -static struct sk_buff *dualpi2_qdisc_dequeue(struct Qdisc *sch) +static struct sk_buff *__dualpi2_qdisc_dequeue(struct Qdisc *sch) { struct dualpi2_sched_data *q = qdisc_priv(sch); struct sk_buff *skb; @@ -604,12 +604,49 @@ static struct sk_buff *dualpi2_qdisc_dequeue(struct Qdisc *sch) break; } + return skb; +} + +static void dualpi2_dequeue_drop(struct Qdisc *sch) +{ + struct dualpi2_sched_data *q = qdisc_priv(sch); + if (q->deferred_drops_cnt) { qdisc_tree_reduce_backlog(sch, q->deferred_drops_cnt, q->deferred_drops_len); q->deferred_drops_cnt = 0; q->deferred_drops_len = 0; } +} + +static struct sk_buff *dualpi2_qdisc_dequeue(struct Qdisc *sch) +{ + struct sk_buff *skb; + + skb = __dualpi2_qdisc_dequeue(sch); + + dualpi2_dequeue_drop(sch); + + return skb; +} + +static struct sk_buff *dualpi2_peek(struct Qdisc *sch) +{ + struct sk_buff *skb = skb_peek(&sch->gso_skb); + + if (!skb) { + skb = __dualpi2_qdisc_dequeue(sch); + + if (skb) { + __skb_queue_head(&sch->gso_skb, skb); + /* it's still part of the queue */ + qdisc_qstats_backlog_inc(sch, skb); + sch->q.qlen++; + } + + dualpi2_dequeue_drop(sch); + } + return skb; } @@ -1164,7 +1201,7 @@ static struct Qdisc_ops dualpi2_qdisc_ops __read_mostly = { .priv_size = sizeof(struct dualpi2_sched_data), .enqueue = dualpi2_qdisc_enqueue, .dequeue = dualpi2_qdisc_dequeue, - .peek = qdisc_peek_dequeued, + .peek = dualpi2_peek, .init = dualpi2_init, .destroy = dualpi2_destroy, .reset = dualpi2_reset, From 150590d9ab0dbeb0087193058363819d4c82a00f Mon Sep 17 00:00:00 2001 From: Aditya Garg Date: Mon, 8 Jun 2026 03:13:40 -0700 Subject: [PATCH 1422/2758] net: mana: initialize gdma queue id to INVALID_QUEUE_ID [ Upstream commit 5985474e1cb4034680fac2145497a94b0860be50 ] mana_gd_create_mana_wq_cq() leaves queue->id as 0 (from kzalloc_obj()) until mana_create_wq_obj() assigns the firmware-returned id. If creation fails before that, cleanup calls mana_gd_destroy_cq() with id 0, NULLing gc->cq_table[0] and silently breaking whichever real CQ owns that slot. Initialize queue->id to INVALID_QUEUE_ID right after allocation, matching mana_gd_create_eq(). The existing (id >= max_num_cqs) guard then short-circuits cleanly. Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)") Signed-off-by: Aditya Garg Reviewed-by: Dipayaan Roy Reviewed-by: Haiyang Zhang Link: https://patch.msgid.link/20260608101345.2267320-2-gargaditya@linux.microsoft.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/microsoft/mana/gdma_main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c index d8e816882f02c..ac71ca8450bf4 100644 --- a/drivers/net/ethernet/microsoft/mana/gdma_main.c +++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c @@ -1192,6 +1192,8 @@ int mana_gd_create_mana_wq_cq(struct gdma_dev *gd, if (!queue) return -ENOMEM; + queue->id = INVALID_QUEUE_ID; + gmi = &queue->mem_info; err = mana_gd_alloc_memory(gc, spec->queue_size, gmi); if (err) { From d2d27bd4d4c7acc4312b854d67e22d958c689e72 Mon Sep 17 00:00:00 2001 From: Aditya Garg Date: Mon, 8 Jun 2026 03:13:41 -0700 Subject: [PATCH 1423/2758] net: mana: guard TX wq object destroy with INVALID_MANA_HANDLE check [ Upstream commit f8fd56977eeea3d6939b1a9cd8bd36f1779b3ad0 ] mana_create_txq() has several error paths (after mana_alloc_queues() or mana_create_wq_obj() failure) where tx_qp[i].tx_object stays as the INVALID_MANA_HANDLE sentinel set at allocation. mana_destroy_txq() then unconditionally calls mana_destroy_wq_obj() with (u64)-1, which firmware rejects and logs an error. Mirror the RX-side pattern in mana_destroy_rxq() and skip the destroy when the handle is still INVALID_MANA_HANDLE. Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)") Signed-off-by: Aditya Garg Reviewed-by: Dipayaan Roy Reviewed-by: Haiyang Zhang Link: https://patch.msgid.link/20260608101345.2267320-3-gargaditya@linux.microsoft.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/microsoft/mana/mana_en.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index c9b1df1ed1098..d7de4c4d25bbe 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -2334,7 +2334,8 @@ static void mana_destroy_txq(struct mana_port_context *apc) netif_napi_del_locked(napi); apc->tx_qp[i].txq.napi_initialized = false; } - mana_destroy_wq_obj(apc, GDMA_SQ, apc->tx_qp[i].tx_object); + if (apc->tx_qp[i].tx_object != INVALID_MANA_HANDLE) + mana_destroy_wq_obj(apc, GDMA_SQ, apc->tx_qp[i].tx_object); mana_deinit_cq(apc, &apc->tx_qp[i].tx_cq); From 7ce2b00ff058ec4cafc9b447e1f0a6d6f49275d9 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 11 Jun 2026 15:27:37 +0000 Subject: [PATCH 1424/2758] net: watchdog: fix refcount tracking races [ Upstream commit 8eed5519e496b7a07f441a0f579cb228a33189f7 ] Blamed commit converted the untracked dev_hold()/dev_put() calls in the watchdog code to use the tracked dev_hold_track()/dev_put_track() (which were later renamed/interfaced to netdev_hold() and netdev_put()). By introducing dev->watchdog_dev_tracker to store the reference tracking information without adding synchronization between netdev_watchdog_up() and dev_watchdog(), it enabled the race condition where this pointer could be overwritten or freed concurrently, leading to the list corruption crash syzbot reported: list_del corruption, ffff888114a18c00->next is NULL kernel BUG at lib/list_debug.c:52 ! Oops: invalid opcode: 0000 [#1] SMP KASAN PTI CPU: 1 UID: 0 PID: 91 Comm: kworker/u8:5 Not tainted syzkaller #0 PREEMPT(lazy) Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 05/09/2026 Workqueue: events_unbound linkwatch_event RIP: 0010:__list_del_entry_valid_or_report.cold+0x22/0x2a lib/list_debug.c:52 Call Trace: __list_del_entry_valid include/linux/list.h:132 [inline] __list_del_entry include/linux/list.h:246 [inline] list_move_tail include/linux/list.h:341 [inline] ref_tracker_free+0x1a7/0x6c0 lib/ref_tracker.c:329 netdev_tracker_free include/linux/netdevice.h:4491 [inline] netdev_put include/linux/netdevice.h:4508 [inline] netdev_put include/linux/netdevice.h:4504 [inline] netdev_watchdog_down net/sched/sch_generic.c:600 [inline] dev_deactivate_many+0x28c/0xfe0 net/sched/sch_generic.c:1363 dev_deactivate+0x109/0x1d0 net/sched/sch_generic.c:1397 linkwatch_do_dev net/core/link_watch.c:184 [inline] linkwatch_do_dev+0xd3/0x120 net/core/link_watch.c:166 __linkwatch_run_queue+0x3a5/0x810 net/core/link_watch.c:240 linkwatch_event+0x8f/0xc0 net/core/link_watch.c:314 process_one_work+0xa0e/0x1980 kernel/workqueue.c:3314 process_scheduled_works kernel/workqueue.c:3397 [inline] worker_thread+0x5ef/0xe50 kernel/workqueue.c:3478 kthread+0x370/0x450 kernel/kthread.c:436 ret_from_fork+0x69a/0xc80 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 This patch has three coordinated parts: 1) Add dev->watchdog_lock and dev->watchdog_ref_held to serialize watchdog operations. 2) Remove netdev_watchdog_up() call from netif_carrier_on(): This ensures netdev_watchdog_up() is only called from process/BH context (via linkwatch workqueue dev_activate()), allowing us to use spin_lock_bh() for synchronization. 3) Synchronize watchdog up and watchdog timer: Protect netdev_watchdog_up() with tx_global_lock and watchdog_lock. Only allocate a new tracker in netdev_watchdog_up() if one is not already present. In dev_watchdog(), ensure we don't release the tracker if the timer was rescheduled either by dev_watchdog() itself or concurrently by netdev_watchdog_up(). Fixes: f12bf6f3f942 ("net: watchdog: add net device refcount tracker") Reported-by: syzbot+381d82bbf0253710b35d@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6a26b751.c25708ab.1b19ef.0013.GAE@google.com/T/#u Tested-by: syzbot+3479efbc2821cb2a79f2@syzkaller.appspotmail.com Signed-off-by: Eric Dumazet Link: https://patch.msgid.link/20260611152737.2580480-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- include/linux/netdevice.h | 4 ++++ net/core/dev.c | 3 ++- net/sched/sch_generic.c | 44 +++++++++++++++++++++++++++++---------- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 0e1e581efc5ac..4a0e83709f29e 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1980,6 +1980,8 @@ enum netdev_reg_state { * @qdisc_hash: qdisc hash table * @watchdog_timeo: Represents the timeout that is used by * the watchdog (see dev_watchdog()) + * @watchdog_lock: protect watchdog_ref_held + * @watchdog_ref_held: True if the watchdog device ref is taken. * @watchdog_timer: List of timers * * @proto_down_reason: reason a netdev interface is held down @@ -2392,6 +2394,8 @@ struct net_device { /* These may be needed for future network-power-down code. */ struct timer_list watchdog_timer; int watchdog_timeo; + spinlock_t watchdog_lock; + bool watchdog_ref_held; u32 proto_down_reason; diff --git a/net/core/dev.c b/net/core/dev.c index 0c6c270d9f7d1..731e661d7be65 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -11217,7 +11217,8 @@ static int netif_alloc_netdev_queues(struct net_device *dev) netdev_for_each_tx_queue(dev, netdev_init_one_queue, NULL); spin_lock_init(&dev->tx_global_lock); - + spin_lock_init(&dev->watchdog_lock); + dev->watchdog_ref_held = false; return 0; } diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index e35d9c58850fa..3a0f3bb6a1ff4 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -568,16 +568,24 @@ static void dev_watchdog(struct timer_list *t) dev->netdev_ops->ndo_tx_timeout(dev, i); netif_unfreeze_queues(dev); } - if (!mod_timer(&dev->watchdog_timer, - round_jiffies(oldest_start + - dev->watchdog_timeo))) - release = false; + spin_lock(&dev->watchdog_lock); + mod_timer(&dev->watchdog_timer, + round_jiffies(oldest_start + + dev->watchdog_timeo)); + release = false; + spin_unlock(&dev->watchdog_lock); } } spin_unlock(&dev->tx_global_lock); - if (release) + spin_lock(&dev->watchdog_lock); + if (timer_pending(&dev->watchdog_timer)) + release = false; + if (release && dev->watchdog_ref_held) { netdev_put(dev, &dev->watchdog_dev_tracker); + dev->watchdog_ref_held = false; + } + spin_unlock(&dev->watchdog_lock); } void netdev_watchdog_up(struct net_device *dev) @@ -586,18 +594,34 @@ void netdev_watchdog_up(struct net_device *dev) return; if (dev->watchdog_timeo <= 0) dev->watchdog_timeo = 5*HZ; + spin_lock_bh(&dev->tx_global_lock); + + spin_lock(&dev->watchdog_lock); if (!mod_timer(&dev->watchdog_timer, - round_jiffies(jiffies + dev->watchdog_timeo))) - netdev_hold(dev, &dev->watchdog_dev_tracker, - GFP_ATOMIC); + round_jiffies(jiffies + dev->watchdog_timeo))) { + if (!dev->watchdog_ref_held) { + netdev_hold(dev, &dev->watchdog_dev_tracker, + GFP_ATOMIC); + dev->watchdog_ref_held = true; + } + } + spin_unlock(&dev->watchdog_lock); + + spin_unlock_bh(&dev->tx_global_lock); } EXPORT_SYMBOL_GPL(netdev_watchdog_up); static void netdev_watchdog_down(struct net_device *dev) { netif_tx_lock_bh(dev); - if (timer_delete(&dev->watchdog_timer)) + + spin_lock(&dev->watchdog_lock); + if (timer_delete(&dev->watchdog_timer)) { netdev_put(dev, &dev->watchdog_dev_tracker); + dev->watchdog_ref_held = false; + } + spin_unlock(&dev->watchdog_lock); + netif_tx_unlock_bh(dev); } @@ -614,8 +638,6 @@ void netif_carrier_on(struct net_device *dev) return; atomic_inc(&dev->carrier_up_count); linkwatch_fire_event(dev); - if (netif_running(dev)) - netdev_watchdog_up(dev); } } EXPORT_SYMBOL(netif_carrier_on); From 87a0daf4ddf993d9636ca7fd4163728ca60e1c25 Mon Sep 17 00:00:00 2001 From: Zhi-Jun You Date: Thu, 11 Jun 2026 23:00:51 +0800 Subject: [PATCH 1425/2758] net: ethernet: mtk_wed: fix loading WO firmware for MT7986 [ Upstream commit 9192a18f6de2f5e3eb3813ecd2895ac0f5c008a9 ] MT7986 requires a different mask for second WO firmware. Without this, WO would timeout after loading FW. The correct mask was removed when adding WED for MT7988. Add it back and add a WED version check to fix it. This can be reproduced with a MT7986 + MT7916 board. Fixes: e2f64db13aa1 ("net: ethernet: mtk_wed: introduce WED support for MT7988") Signed-off-by: Zhi-Jun You Link: https://patch.msgid.link/20260611150051.586-1-hujy652@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/mediatek/mtk_wed_mcu.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c index fa6b216034169..0d38183c6ba70 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c +++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c @@ -367,8 +367,12 @@ mtk_wed_mcu_load_firmware(struct mtk_wed_wo *wo) /* wo firmware reset */ wo_w32(MTK_WO_MCU_CFG_LS_WF_MCCR_CLR_ADDR, 0xc00); - val = wo_r32(MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR) | - MTK_WO_MCU_CFG_LS_WF_WM_WA_WM_CPU_RSTB_MASK; + val = wo_r32(MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR); + + if (!mtk_wed_is_v3_or_greater(wo->hw) && wo->hw->index) + val |= MTK_WO_MCU_CFG_LS_WF_WM_WA_WA_CPU_RSTB_MASK; + else + val |= MTK_WO_MCU_CFG_LS_WF_WM_WA_WM_CPU_RSTB_MASK; wo_w32(MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR, val); out: release_firmware(fw); From dd3c3ed8f180a1536ef03743d7b9ffffd64175a4 Mon Sep 17 00:00:00 2001 From: Victor Nogueira Date: Thu, 11 Jun 2026 17:58:49 -0300 Subject: [PATCH 1426/2758] net/sched: sch_dualpi2: Add missing module alias [ Upstream commit ee1ba0add3fbd5a28fa5423be373acd147f1e344 ] When a qdisc is added by name, the kernel tries to autoload its module via request_qdisc_module(), which calls: request_module(NET_SCH_ALIAS_PREFIX "%s", name); i.e. it asks modprobe to resolve the "net-sch-" alias (e.g. "net-sch-dualpi2") rather than the module's file name. Since dualpi2 was shipped without this alias, the autoload fails: tc qdisc add dev lo root handle 1: dualpi2 Error: Specified qdisc kind is unknown. Fix this by adding the missing alias so the qdisc is autoloaded on demand like the others. Fixes: 320d031ad6e4 ("sched: Struct definition and parsing of dualpi2 qdisc") Signed-off-by: Victor Nogueira Reviewed-by: Pedro Tammela Link: https://patch.msgid.link/20260611205849.3287640-1-victor@mojatatu.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/sch_dualpi2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/sched/sch_dualpi2.c b/net/sched/sch_dualpi2.c index 592c58938d3e8..5434df6ca8efe 100644 --- a/net/sched/sch_dualpi2.c +++ b/net/sched/sch_dualpi2.c @@ -1210,6 +1210,7 @@ static struct Qdisc_ops dualpi2_qdisc_ops __read_mostly = { .dump_stats = dualpi2_dump_stats, .owner = THIS_MODULE, }; +MODULE_ALIAS_NET_SCH("dualpi2"); static int __init dualpi2_module_init(void) { From 6b20679c00459a04f5cba5564926334e0cb83b09 Mon Sep 17 00:00:00 2001 From: Sun Jian Date: Fri, 12 Jun 2026 19:40:31 +0800 Subject: [PATCH 1427/2758] bpf: Run generic devmap egress prog on private skb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 6001896f00984d317fb75160ba05c4a885fbe2a0 ] Generic XDP devmap multi redirect uses skb_clone() for intermediate destinations and sends the last destination with the original skb. This can leave multiple destinations sharing the same packet data. This becomes visible after generic devmap egress-program support was added: a devmap egress program may mutate packet data, and another destination sharing the same data can observe that mutation. Native XDP broadcast redirect does not have this issue because xdpf_clone() copies the frame data for each destination. Generic XDP should provide the same per-destination isolation before running a devmap egress program. Fix this by making cloned skbs private before running the generic devmap egress program. Use skb_copy() instead of skb_unshare() so allocation failure does not consume the skb and the existing caller error paths keep their ownership semantics. Fixes: 2ea5eabaf04a ("bpf: devmap: Implement devmap prog execution for generic XDP") Suggested-by: Jiayuan Chen Suggested-by: Jakub Kicinski Reviewed-by: Toke Høiland-Jørgensen Signed-off-by: Sun Jian Link: https://lore.kernel.org/r/20260612114032.244616-2-sun.jian.kdev@gmail.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- kernel/bpf/devmap.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/kernel/bpf/devmap.c b/kernel/bpf/devmap.c index 5b9eac5342a90..dc7b859e8bbfd 100644 --- a/kernel/bpf/devmap.c +++ b/kernel/bpf/devmap.c @@ -710,6 +710,18 @@ int dev_map_generic_redirect(struct bpf_dtab_netdev *dst, struct sk_buff *skb, if (unlikely(err)) return err; + if (dst->xdp_prog && skb_cloned(skb)) { + struct sk_buff *nskb; + + nskb = skb_copy(skb, GFP_ATOMIC); + if (!nskb) + return -ENOMEM; + + nskb->mac_len = skb->mac_len; + consume_skb(skb); + skb = nskb; + } + /* Redirect has already succeeded semantically at this point, so we just * return 0 even if packet is dropped. Helper below takes care of * freeing skb. From fe1b3f8d47ffc98fdebf38cacab522bf0a9227eb Mon Sep 17 00:00:00 2001 From: Dragos Tatulea Date: Thu, 11 Jun 2026 16:52:30 +0300 Subject: [PATCH 1428/2758] net/mlx5: Check max_macs devlink param value against max capability [ Upstream commit d7b0413b35715d7b32cb12d4d424613eff85ed2b ] The max_macs devlink param is checked against the FW max value only at param register time (driver load) and inside the validate callback (devlink param set). The stored DRIVERINIT value persists across FW resets and devlink reloads without any further checks against the max. If the FW link type changes from Ethernet to IB and a FW reset happens, the MAX cap for log_max_current_uc_list will become zero, but the previously stored max_macs value remains and is unconditionally programmed into the HCA caps in handle_hca_cap(). FW will then return a syndrome during SET_HCA_CAP: mlx5_cmd_out_err:839:(pid 3831): SET_HCA_CAP(0x109) op_mod(0x0) failed, status bad parameter(0x3), syndrome (0x537801), err(-22) set_hca_cap:907:(pid 3831): handle_hca_cap failed This results in a failure to register the RDMA device. This patch skips programming log_max_current_uc_list when the MAX capability is 0 (in case of IB). Fixes: 8680a60fc1fc ("net/mlx5: Let user configure max_macs generic param") Signed-off-by: Dragos Tatulea Reviewed-by: Yael Chemla Reviewed-by: Carolina Jubran Signed-off-by: Tariq Toukan Reviewed-by: Alexander Lobakin Link: https://patch.msgid.link/20260611135230.534513-1-tariqt@nvidia.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/mellanox/mlx5/core/main.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 74827e8ca1255..37af619e5e045 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -527,7 +527,6 @@ static int handle_hca_cap(struct mlx5_core_dev *dev, void *set_ctx) { struct mlx5_profile *prof = &dev->profile; void *set_hca_cap; - int max_uc_list; int err; err = mlx5_core_get_caps(dev, MLX5_CAP_GENERAL); @@ -610,10 +609,13 @@ static int handle_hca_cap(struct mlx5_core_dev *dev, void *set_ctx) MLX5_SET(cmd_hca_cap, set_hca_cap, roce, mlx5_is_roce_on(dev)); - max_uc_list = max_uc_list_get_devlink_param(dev); - if (max_uc_list > 0) - MLX5_SET(cmd_hca_cap, set_hca_cap, log_max_current_uc_list, - ilog2(max_uc_list)); + if (MLX5_CAP_GEN_MAX(dev, log_max_current_uc_list)) { + int max_uc_list = max_uc_list_get_devlink_param(dev); + + if (max_uc_list > 0) + MLX5_SET(cmd_hca_cap, set_hca_cap, + log_max_current_uc_list, ilog2(max_uc_list)); + } /* enable absolute native port num */ if (MLX5_CAP_GEN_MAX(dev, abs_native_port_num)) From 442d232dbcd33578d3d57146c7ecc0d389a99ded Mon Sep 17 00:00:00 2001 From: Xu Kuohai Date: Wed, 10 Jun 2026 20:17:23 +0000 Subject: [PATCH 1429/2758] bpf: Fix setting retval to -EPERM for cgroup hooks not returning errno [ Upstream commit 4c71303c837449158815c521fcee4ec3b8721dbd ] When a cgroup BPF program exits with 0, bpf_prog_run_array_cg() sets the hook return value to -EPERM if it is not a valid errno. This is correct for errno-based hooks, which return 0 on success and negative errno on failure, but wrong for boolean and void LSM hooks. Boolean LSM hooks should only return true or false, and void LSM hooks have no return value at all. Fix it by skipping setting -EPERM for hooks not returning errno. Fixes: 69fd337a975c ("bpf: per-cgroup lsm flavor") Signed-off-by: Xu Kuohai Link: https://lore.kernel.org/r/20260610201724.733943-2-xukuohai@huaweicloud.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- include/linux/bpf_lsm.h | 6 ++++++ kernel/bpf/bpf_lsm.c | 20 ++++++++++++++++++ kernel/bpf/cgroup.c | 47 +++++++++++++++++++++++++++++------------ 3 files changed, 60 insertions(+), 13 deletions(-) diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h index e4593b4a12382..dda272d78f013 100644 --- a/include/linux/bpf_lsm.h +++ b/include/linux/bpf_lsm.h @@ -54,6 +54,7 @@ int bpf_set_dentry_xattr_locked(struct dentry *dentry, const char *name__str, const struct bpf_dynptr *value_p, int flags); int bpf_remove_dentry_xattr_locked(struct dentry *dentry, const char *name__str); bool bpf_lsm_has_d_inode_locked(const struct bpf_prog *prog); +bool bpf_lsm_hook_returns_errno(u32 btf_id); #else /* !CONFIG_BPF_LSM */ @@ -108,6 +109,11 @@ static inline bool bpf_lsm_has_d_inode_locked(const struct bpf_prog *prog) { return false; } + +static inline bool bpf_lsm_hook_returns_errno(u32 btf_id) +{ + return true; +} #endif /* CONFIG_BPF_LSM */ #endif /* _LINUX_BPF_LSM_H */ diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c index c5c925f002021..564071a92d7d2 100644 --- a/kernel/bpf/bpf_lsm.c +++ b/kernel/bpf/bpf_lsm.c @@ -427,6 +427,26 @@ BTF_ID(func, bpf_lsm_audit_rule_known) BTF_ID(func, bpf_lsm_inode_xattr_skipcap) BTF_SET_END(bool_lsm_hooks) +/* hooks returning void */ +#define LSM_HOOK_void(DEFAULT, NAME, ...) BTF_ID(func, bpf_lsm_##NAME) +#define LSM_HOOK_int(DEFAULT, NAME, ...) /* nothing */ +#define LSM_HOOK(RET, DEFAULT, NAME, ...) LSM_HOOK_##RET(DEFAULT, NAME, __VA_ARGS__) +BTF_SET_START(void_lsm_hooks) +#include +#undef LSM_HOOK +#undef LSM_HOOK_void +#undef LSM_HOOK_int +BTF_SET_END(void_lsm_hooks) + +bool bpf_lsm_hook_returns_errno(u32 btf_id) +{ + if (btf_id_set_contains(&bool_lsm_hooks, btf_id)) + return false; + if (btf_id_set_contains(&void_lsm_hooks, btf_id)) + return false; + return true; +} + int bpf_lsm_get_retval_range(const struct bpf_prog *prog, struct bpf_retval_range *retval_range) { diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index 89ea605457906..90921459e2c43 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -55,6 +55,28 @@ void __init cgroup_bpf_lifetime_notifier_init(void) &cgroup_bpf_lifetime_nb)); } +#ifdef CONFIG_BPF_LSM +struct cgroup_lsm_atype { + u32 attach_btf_id; + int refcnt; + bool returns_errno; +}; + +static struct cgroup_lsm_atype cgroup_lsm_atype[CGROUP_LSM_NUM]; + +static bool cgroup_bpf_hook_returns_errno(enum cgroup_bpf_attach_type atype) +{ + if (atype >= CGROUP_LSM_START && atype <= CGROUP_LSM_END) + return READ_ONCE(cgroup_lsm_atype[atype - CGROUP_LSM_START].returns_errno); + return true; +} +#else +static bool cgroup_bpf_hook_returns_errno(enum cgroup_bpf_attach_type atype) +{ + return true; +} +#endif + /* __always_inline is necessary to prevent indirect call through run_prog * function pointer. */ @@ -83,7 +105,8 @@ bpf_prog_run_array_cg(const struct cgroup_bpf *cgrp, *(ret_flags) |= (func_ret >> 1); func_ret &= 1; } - if (!func_ret && !IS_ERR_VALUE((long)run_ctx.retval)) + if (!func_ret && cgroup_bpf_hook_returns_errno(atype) && + !IS_ERR_VALUE((long)run_ctx.retval)) run_ctx.retval = -EPERM; item++; } @@ -156,13 +179,6 @@ unsigned int __cgroup_bpf_run_lsm_current(const void *ctx, } #ifdef CONFIG_BPF_LSM -struct cgroup_lsm_atype { - u32 attach_btf_id; - int refcnt; -}; - -static struct cgroup_lsm_atype cgroup_lsm_atype[CGROUP_LSM_NUM]; - static enum cgroup_bpf_attach_type bpf_cgroup_atype_find(enum bpf_attach_type attach_type, u32 attach_btf_id) { @@ -191,10 +207,13 @@ void bpf_cgroup_atype_get(u32 attach_btf_id, int cgroup_atype) lockdep_assert_held(&cgroup_mutex); - WARN_ON_ONCE(cgroup_lsm_atype[i].attach_btf_id && - cgroup_lsm_atype[i].attach_btf_id != attach_btf_id); - - cgroup_lsm_atype[i].attach_btf_id = attach_btf_id; + if (!cgroup_lsm_atype[i].attach_btf_id) { + cgroup_lsm_atype[i].attach_btf_id = attach_btf_id; + WRITE_ONCE(cgroup_lsm_atype[i].returns_errno, + bpf_lsm_hook_returns_errno(attach_btf_id)); + } else { + WARN_ON_ONCE(cgroup_lsm_atype[i].attach_btf_id != attach_btf_id); + } cgroup_lsm_atype[i].refcnt++; } @@ -203,8 +222,10 @@ void bpf_cgroup_atype_put(int cgroup_atype) int i = cgroup_atype - CGROUP_LSM_START; cgroup_lock(); - if (--cgroup_lsm_atype[i].refcnt <= 0) + if (--cgroup_lsm_atype[i].refcnt <= 0) { + WRITE_ONCE(cgroup_lsm_atype[i].returns_errno, true); cgroup_lsm_atype[i].attach_btf_id = 0; + } WARN_ON_ONCE(cgroup_lsm_atype[i].refcnt < 0); cgroup_unlock(); } From 2477c523c6e69733d51101ae9bd2dfc0a054b13d Mon Sep 17 00:00:00 2001 From: Ratheesh Kannoth Date: Wed, 10 Jun 2026 07:53:44 +0530 Subject: [PATCH 1430/2758] octeontx2-af: npc: Fix size of entry2cntr_map [ Upstream commit f9cd6fabe0e7c7f6fc30c6c192c7ed72aba37232 ] KASAN prints below splat. This is caused by allocating counter for reserved mcam entry for cpt 2nd pass entry. But mcam->entry2cntr_map is not allocated for reserved entries. BUG: KASAN: slab-out-of-bounds in npc_map_mcam_entry_and_cntr+0xb0/0x1a0 Write of size 2 at addr ffff0001033e7ffe by task kworker/0:1/14 CPU: 0 PID: 14 Comm: kworker/0:1 Not tainted 6.1.67 #1 Hardware name: Marvell CN106XX board (DT) Workqueue: events work_for_cpu_fn Call trace: dump_backtrace.part.0+0xe4/0xf0 show_stack+0x18/0x30 dump_stack_lvl+0x88/0xb4 print_report+0x154/0x458 kasan_report+0xb8/0x194 __asan_store2+0x7c/0xa0 npc_map_mcam_entry_and_cntr+0xb0/0x1a0 rvu_mbox_handler_npc_mcam_write_entry+0x268/0x280 npc_install_flow+0x840/0xfe0 rvu_npc_install_cpt_pass2_entry+0x138/0x190 rvu_nix_init+0x148c/0x2880 rvu_probe+0x1800/0x30b0 local_pci_probe+0x78/0xe0 work_for_cpu_fn+0x30/0x50 process_one_work+0x4cc/0x97c worker_thread+0x360/0x630 kthread+0x1a0/0x1b0 ret_from_fork+0x10/0x20 Fixes: 55307fcb9258 ("octeontx2-af: Add mbox messages to install and delete MCAM rules") Cc: Subbaraya Sundeep Signed-off-by: Ratheesh Kannoth Link: https://patch.msgid.link/20260610022344.969774-1-rkannoth@marvell.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- .../ethernet/marvell/octeontx2/af/rvu_npc.c | 40 +++++++------------ 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c index d301a3f0f87a8..4994385a822b7 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c @@ -2181,7 +2181,7 @@ int npc_mcam_rsrcs_init(struct rvu *rvu, int blkaddr) /* Alloc memory for MCAM entry to counter mapping and for tracking * counter's reference count. */ - mcam->entry2cntr_map = kcalloc(mcam->bmap_entries, sizeof(u16), + mcam->entry2cntr_map = kcalloc(mcam->total_entries, sizeof(u16), GFP_KERNEL); if (!mcam->entry2cntr_map) goto free_cntr_map; @@ -2197,10 +2197,11 @@ int npc_mcam_rsrcs_init(struct rvu *rvu, int blkaddr) if (!mcam->entry2target_pffunc) goto free_cntr_refcnt; - for (index = 0; index < mcam->bmap_entries; index++) { + for (index = 0; index < mcam->bmap_entries; index++) mcam->entry2pfvf_map[index] = NPC_MCAM_INVALID_MAP; + + for (index = 0; index < mcam->total_entries; index++) mcam->entry2cntr_map[index] = NPC_MCAM_INVALID_MAP; - } for (cntr = 0; cntr < mcam->counters.max; cntr++) mcam->cntr2pfvf_map[cntr] = NPC_MCAM_INVALID_MAP; @@ -3531,7 +3532,7 @@ static int __npc_mcam_free_counter(struct rvu *rvu, struct msg_rsp *rsp) { struct npc_mcam *mcam = &rvu->hw->mcam; - u16 index, entry = 0; + u16 index; int blkaddr, err; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); @@ -3547,20 +3548,16 @@ static int __npc_mcam_free_counter(struct rvu *rvu, mcam->cntr2pfvf_map[req->cntr] = NPC_MCAM_INVALID_MAP; rvu_free_rsrc(&mcam->counters, req->cntr); - /* Disable all MCAM entry's stats which are using this counter */ - while (entry < mcam->bmap_entries) { + /* Disable all MCAM entry's stats which are using this counter. + * Scan the full MCAM index range: AF-reserved rules (e.g. CPT pass-2) + */ + for (index = 0; index < mcam->total_entries; index++) { if (!mcam->cntr_refcnt[req->cntr]) break; - - index = find_next_bit(mcam->bmap, mcam->bmap_entries, entry); - if (index >= mcam->bmap_entries) - break; - entry = index + 1; if (mcam->entry2cntr_map[index] != req->cntr) continue; - - npc_unmap_mcam_entry_and_cntr(rvu, mcam, blkaddr, - index, req->cntr); + npc_unmap_mcam_entry_and_cntr(rvu, mcam, blkaddr, index, + req->cntr); } return 0; @@ -3631,7 +3628,7 @@ int rvu_mbox_handler_npc_mcam_unmap_counter(struct rvu *rvu, struct npc_mcam_unmap_counter_req *req, struct msg_rsp *rsp) { struct npc_mcam *mcam = &rvu->hw->mcam; - u16 index, entry = 0; + u16 index; int blkaddr, rc; /* Counter is not supported for CN20K */ @@ -3658,20 +3655,13 @@ int rvu_mbox_handler_npc_mcam_unmap_counter(struct rvu *rvu, } /* Disable all MCAM entry's stats which are using this counter */ - while (entry < mcam->bmap_entries) { + for (index = 0; index < mcam->total_entries; index++) { if (!mcam->cntr_refcnt[req->cntr]) break; - - index = find_next_bit(mcam->bmap, mcam->bmap_entries, entry); - if (index >= mcam->bmap_entries) - break; - entry = index + 1; - if (mcam->entry2cntr_map[index] != req->cntr) continue; - - npc_unmap_mcam_entry_and_cntr(rvu, mcam, blkaddr, - index, req->cntr); + npc_unmap_mcam_entry_and_cntr(rvu, mcam, blkaddr, index, + req->cntr); } exit: mutex_unlock(&mcam->lock); From 10b83897a90c3518db832288e87efc4fd5502429 Mon Sep 17 00:00:00 2001 From: "Wayen.Yan" Date: Fri, 12 Jun 2026 17:37:00 +0800 Subject: [PATCH 1431/2758] net: airoha: Fix error handling in airoha_ppe_flush_sram_entries() [ Upstream commit d7d81b00301398fcd38cf5b5869f0fdb674472ef ] In airoha_ppe_flush_sram_entries(), the outer "err" variable was never updated when the inner loop variable shadowed it, causing the function to always return 0 even when airoha_ppe_foe_commit_sram_entry() fails. Drop the outer "err" variable and return directly on error, propagating the error code from airoha_ppe_foe_commit_sram_entry() correctly. Fixes: 620d7b91aadb ("net: airoha: ppe: Flush PPE SRAM table during PPE setup") Link: https://lore.kernel.org/netdev/6a2b40e4.4dd82583.3a5c46.e52f@mx.google.com/ Signed-off-by: Wayen.Yan Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/6a2bd37a.4034e349.1b41bb.1caf@mx.google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/airoha/airoha_ppe.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c index 5c9dff6bccd1e..a124f15345a91 100644 --- a/drivers/net/ethernet/airoha/airoha_ppe.c +++ b/drivers/net/ethernet/airoha/airoha_ppe.c @@ -1330,7 +1330,7 @@ static int airoha_ppe_flush_sram_entries(struct airoha_ppe *ppe) { u32 sram_num_entries = airoha_ppe_get_total_sram_num_entries(ppe); struct airoha_foe_entry *hwe = ppe->foe; - int i, err = 0; + int i; for (i = 0; i < sram_num_entries; i++) { int err; @@ -1338,10 +1338,10 @@ static int airoha_ppe_flush_sram_entries(struct airoha_ppe *ppe) memset(&hwe[i], 0, sizeof(*hwe)); err = airoha_ppe_foe_commit_sram_entry(ppe, i); if (err) - break; + return err; } - return err; + return 0; } static struct airoha_npu *airoha_ppe_npu_get(struct airoha_eth *eth) From dd791968b8418404cb8268bb9fdf13b56299c63e Mon Sep 17 00:00:00 2001 From: Wentao Guan Date: Fri, 12 Jun 2026 14:45:01 +0800 Subject: [PATCH 1432/2758] net: ethernet: mtk_wed: debugfs: correct index in wed_amsdu_show() [ Upstream commit 14a8bc41ce9edae42d56466063a7f2c84a16c45c ] WED_MON_AMSDU_ENG_CNT point to different entry by 'base+n*offset' mode, correct the wed amsdu entry number in wed_amsdu_show(). Fixes: 3f3de094e8342 ("net: ethernet: mtk_wed: debugfs: add WED 3.0 debugfs entries") Signed-off-by: Wentao Guan Link: https://patch.msgid.link/20260612064501.203058-1-guanwentao@uniontech.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/mediatek/mtk_wed_debugfs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c index 781c691473e14..519c364e87d19 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c +++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c @@ -310,9 +310,9 @@ wed_amsdu_show(struct seq_file *s, void *data) WED_AMSDU_ENG_MAX_QGPP_CNT), DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(1), WED_AMSDU_ENG_CUR_ENTRY), - DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2), + DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(1), WED_AMSDU_ENG_MAX_BUF_MERGED), - DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2), + DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(1), WED_AMSDU_ENG_MAX_MSDU_MERGED), DUMP_STR("WED AMDSU ENG2 INFO"), @@ -414,7 +414,7 @@ wed_amsdu_show(struct seq_file *s, void *data) WED_AMSDU_ENG_CUR_ENTRY), DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(7), WED_AMSDU_ENG_MAX_BUF_MERGED), - DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(4), + DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(7), WED_AMSDU_ENG_MAX_MSDU_MERGED), DUMP_STR("WED AMDSU ENG8 INFO"), From 4c1b25d85f4c9a0f85f3f8c39988ad266a3f3095 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Fri, 12 Jun 2026 11:56:13 +0800 Subject: [PATCH 1433/2758] net: wwan: t7xx: check skb_clone in control TX [ Upstream commit 05f789fa90d95d5771230e78453cedff2486039d ] t7xx_port_ctrl_tx() clones each skb fragment before passing it to the port transmit path. The clone is used immediately to set cloned->len, so an skb_clone() failure results in a NULL pointer dereference. Check the clone before using it. If previous fragments were already queued, preserve the driver's existing partial-write behavior by returning the number of bytes submitted so far. Fixes: 36bd28c1cb0d ("wwan: core: Support slicing in port TX flow of WWAN subsystem") Signed-off-by: Ruoyu Wang Reviewed-by: Loic Poulain Link: https://patch.msgid.link/20260612035613.1192486-1-ruoyuw560@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/wwan/t7xx/t7xx_port_wwan.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wwan/t7xx/t7xx_port_wwan.c b/drivers/net/wwan/t7xx/t7xx_port_wwan.c index 7fc569565ff99..d2529df7592a7 100644 --- a/drivers/net/wwan/t7xx/t7xx_port_wwan.c +++ b/drivers/net/wwan/t7xx/t7xx_port_wwan.c @@ -106,6 +106,8 @@ static int t7xx_port_ctrl_tx(struct t7xx_port *port, struct sk_buff *skb) while (cur) { cloned = skb_clone(cur, GFP_KERNEL); + if (!cloned) + return cnt ? cnt : -ENOMEM; cloned->len = skb_headlen(cur); ret = t7xx_port_send_skb(port, cloned, 0, 0); if (ret) { From 75a52d107203645341af365c10e7bf18df8d78d7 Mon Sep 17 00:00:00 2001 From: Grzegorz Nitka Date: Sun, 7 Jun 2026 20:30:35 +0200 Subject: [PATCH 1434/2758] dpll: fix stale iteration in dpll_pin_on_pin_unregister() [ Upstream commit 32239d600236a986c8e6d16aa814d3d91066b244 ] Neither parent->dpll_refs nor pin->dpll_refs on its own is a correct iteration target at unregister time: - pin->dpll_refs includes DPLLs the child was registered against via a different parent or directly; blind unregister WARNs on the cookie miss in dpll_xa_ref_pin_del(). - parent->dpll_refs reflects the parent's current attachments, not those at child-register time. Another driver may have (un)reg'd the parent against additional DPLLs in the meantime, so we miss registrations that exist and visit DPLLs that have none. Walk pin->dpll_refs and use dpll_pin_registration_find() to filter to entries whose cookie is this parent. Symmetric with dpll_pin_on_pin_register(), correct under any subsequent change to parent->dpll_refs. Fixes: 9431063ad323 ("dpll: core: Add DPLL framework base functions") Signed-off-by: Grzegorz Nitka Link: https://patch.msgid.link/20260607183045.1213735-4-grzegorz.nitka@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/dpll/dpll_core.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c index cbb635db43210..1f1b9a6696231 100644 --- a/drivers/dpll/dpll_core.c +++ b/drivers/dpll/dpll_core.c @@ -1018,14 +1018,19 @@ EXPORT_SYMBOL_GPL(dpll_pin_on_pin_register); void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin, const struct dpll_pin_ops *ops, void *priv) { + struct dpll_pin_registration *reg; struct dpll_pin_ref *ref; unsigned long i; mutex_lock(&dpll_lock); dpll_pin_delete_ntf(pin); dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv, pin); - xa_for_each(&pin->dpll_refs, i, ref) + xa_for_each(&pin->dpll_refs, i, ref) { + reg = dpll_pin_registration_find(ref, ops, priv, parent); + if (!reg) + continue; __dpll_pin_unregister(ref->dpll, pin, ops, priv, parent); + } mutex_unlock(&dpll_lock); } EXPORT_SYMBOL_GPL(dpll_pin_on_pin_unregister); From 0ea6703cb3dc1309d399998cc1fdf5fe5048a401 Mon Sep 17 00:00:00 2001 From: Grzegorz Nitka Date: Sun, 7 Jun 2026 20:30:36 +0200 Subject: [PATCH 1435/2758] dpll: send delete notification before unregister in on-pin rollback [ Upstream commit e83b403eb142be18d223fc599c0ac45519053671 ] The rollback path in dpll_pin_on_pin_register() called __dpll_pin_unregister() before dpll_pin_delete_ntf(). When the unregister dropped the pin's last DPLL reference it cleared the DPLL_REGISTERED mark in dpll_pin_xa, so the subsequent dpll_pin_event_send() failed dpll_pin_available() and aborted with -ENODEV. As a result userspace was never notified of the rollback deletion and remained out of sync with the kernel. Send the delete notification first, matching the order used by dpll_pin_unregister() and dpll_pin_on_pin_unregister(). Fixes: 9d71b54b65b1 ("dpll: netlink: Add DPLL framework base functions") Signed-off-by: Grzegorz Nitka Link: https://patch.msgid.link/20260607183045.1213735-5-grzegorz.nitka@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/dpll/dpll_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c index 1f1b9a6696231..a58977c87de19 100644 --- a/drivers/dpll/dpll_core.c +++ b/drivers/dpll/dpll_core.c @@ -994,9 +994,9 @@ int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin, dpll_unregister: xa_for_each(&parent->dpll_refs, i, ref) if (i < stop) { + dpll_pin_delete_ntf(pin); __dpll_pin_unregister(ref->dpll, pin, ops, priv, parent); - dpll_pin_delete_ntf(pin); } dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv, pin); unlock: From dc37a9a9495400aa9ce4f58aabeba676dfeef0d3 Mon Sep 17 00:00:00 2001 From: Grzegorz Nitka Date: Sun, 7 Jun 2026 20:30:37 +0200 Subject: [PATCH 1436/2758] dpll: emit per-dpll delete notifications in dpll_pin_on_pin_unregister() [ Upstream commit df0ba51ccf873e533669578104981109217d8201 ] dpll_pin_on_pin_register() emits a creation notification for every parent->dpll_refs entry, but dpll_pin_on_pin_unregister() emitted only one deletion notification outside the loop. When a pin is registered against multiple parent dplls, userspace sees N creates but a single delete and leaks per-dpll state. Move dpll_pin_delete_ntf() into the loop and call it before __dpll_pin_unregister() so the DPLL_REGISTERED mark is still set when dpll_pin_available() is consulted. Fixes: 9d71b54b65b1 ("dpll: netlink: Add DPLL framework base functions") Signed-off-by: Grzegorz Nitka Reviewed-by: Arkadiusz Kubalewski Link: https://patch.msgid.link/20260607183045.1213735-6-grzegorz.nitka@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/dpll/dpll_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c index a58977c87de19..ac9654b42b77c 100644 --- a/drivers/dpll/dpll_core.c +++ b/drivers/dpll/dpll_core.c @@ -1023,14 +1023,14 @@ void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin, unsigned long i; mutex_lock(&dpll_lock); - dpll_pin_delete_ntf(pin); - dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv, pin); xa_for_each(&pin->dpll_refs, i, ref) { reg = dpll_pin_registration_find(ref, ops, priv, parent); if (!reg) continue; + dpll_pin_delete_ntf(pin); __dpll_pin_unregister(ref->dpll, pin, ops, priv, parent); } + dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv, pin); mutex_unlock(&dpll_lock); } EXPORT_SYMBOL_GPL(dpll_pin_on_pin_unregister); From 8c48e6581c433cdff280e0cc481e6c9480dc0a4b Mon Sep 17 00:00:00 2001 From: Grzegorz Nitka Date: Sun, 7 Jun 2026 20:30:38 +0200 Subject: [PATCH 1437/2758] dpll: guard sync-pair removal on full pin unregister [ Upstream commit 0a5c720a7d57d2287d5566c4ad93ee26b7c06845 ] __dpll_pin_unregister() wiped the global sync-pair state on every (dpll, ops, priv, cookie) tuple removed from a pin. When a pin is registered multiple times and only one registration is being torn down, this dropped sync-pair pairings still in use by the surviving registrations. Move dpll_pin_ref_sync_pair_del() inside the xa_empty(&pin->dpll_refs) branch so it only runs when the last registration is gone, alongside clearing the DPLL_REGISTERED mark. Fixes: 58256a26bfb3 ("dpll: add reference sync get/set") Signed-off-by: Grzegorz Nitka Reviewed-by: Arkadiusz Kubalewski Link: https://patch.msgid.link/20260607183045.1213735-7-grzegorz.nitka@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/dpll/dpll_core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c index ac9654b42b77c..e3d7aeed43088 100644 --- a/drivers/dpll/dpll_core.c +++ b/drivers/dpll/dpll_core.c @@ -913,11 +913,12 @@ __dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin, const struct dpll_pin_ops *ops, void *priv, void *cookie) { ASSERT_DPLL_PIN_REGISTERED(pin); - dpll_pin_ref_sync_pair_del(pin->id); dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv, cookie); dpll_xa_ref_dpll_del(&pin->dpll_refs, dpll, ops, priv, cookie); - if (xa_empty(&pin->dpll_refs)) + if (xa_empty(&pin->dpll_refs)) { + dpll_pin_ref_sync_pair_del(pin->id); xa_clear_mark(&dpll_pin_xa, pin->id, DPLL_REGISTERED); + } } /** From 467ae77921ada611a92c4654aee0c8293817bcc9 Mon Sep 17 00:00:00 2001 From: Grzegorz Nitka Date: Sun, 7 Jun 2026 20:30:39 +0200 Subject: [PATCH 1438/2758] dpll: balance create/delete notifications in __dpll_pin_(un)register [ Upstream commit 1a2292101c0dc422466c673031de03d2e871adbe ] __dpll_pin_register() emits dpll_pin_create_ntf() internally, but __dpll_pin_unregister() left the matching delete to its callers. The counts then diverge on dpll_pin_on_pin_register() rollback and on dpll_pin_on_pin_unregister(), leaking stale notifications. Emit dpll_pin_delete_ntf() inside __dpll_pin_unregister() and drop the now-redundant call in dpll_pin_unregister(). Fixes: 9431063ad323 ("dpll: core: Add DPLL framework base functions") Signed-off-by: Grzegorz Nitka Reviewed-by: Arkadiusz Kubalewski Link: https://patch.msgid.link/20260607183045.1213735-8-grzegorz.nitka@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/dpll/dpll_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c index e3d7aeed43088..8a8f2f4351235 100644 --- a/drivers/dpll/dpll_core.c +++ b/drivers/dpll/dpll_core.c @@ -913,6 +913,7 @@ __dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin, const struct dpll_pin_ops *ops, void *priv, void *cookie) { ASSERT_DPLL_PIN_REGISTERED(pin); + dpll_pin_delete_ntf(pin); dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv, cookie); dpll_xa_ref_dpll_del(&pin->dpll_refs, dpll, ops, priv, cookie); if (xa_empty(&pin->dpll_refs)) { @@ -940,7 +941,6 @@ void dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin, return; mutex_lock(&dpll_lock); - dpll_pin_delete_ntf(pin); __dpll_pin_unregister(dpll, pin, ops, priv, NULL); mutex_unlock(&dpll_lock); } From d0de5037dce5774ff574b449cb7c623913906ebf Mon Sep 17 00:00:00 2001 From: Matthieu Buffet Date: Tue, 9 Jun 2026 23:15:10 +0200 Subject: [PATCH 1439/2758] landlock: Fix unmarked concurrent access to socket family MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 0ce4243509d1580349dd0d50624036d6b097e958 ] Socket family is read (twice) in a context where the socket is not locked, so another thread can setsockopt(IPV6_ADDRFORM) to write it concurrently. Add needed READ_ONCE() annotation. Use the proper macro to access __sk_common.skc_family like everywhere else. Fixes: fff69fb03dde ("landlock: Support network rules with TCP bind and connect") Signed-off-by: Matthieu Buffet Link: https://patch.msgid.link/20260609211511.85630-1-matthieu@buffet.re Link: https://patch.msgid.link/20260609211511.85630-2-matthieu@buffet.re [mic: Squash two patches, move variable to ease backport, fix comment formatting] Signed-off-by: Mickaël Salaün Signed-off-by: Sasha Levin --- security/landlock/net.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/security/landlock/net.c b/security/landlock/net.c index a38bdfcffc22a..4ee4002a8f567 100644 --- a/security/landlock/net.c +++ b/security/landlock/net.c @@ -46,6 +46,7 @@ static int current_check_access_socket(struct socket *const sock, const int addrlen, access_mask_t access_request) { + unsigned short sock_family; __be16 port; struct layer_access_masks layer_masks = {}; const struct landlock_rule *rule; @@ -66,6 +67,12 @@ static int current_check_access_socket(struct socket *const sock, if (addrlen < offsetofend(typeof(*address), sa_family)) return -EINVAL; + /* + * The socket is not locked, so sk_family can change concurrently due to + * e.g. setsockopt(IPV6_ADDRFORM). + */ + sock_family = READ_ONCE(sock->sk->sk_family); + switch (address->sa_family) { case AF_UNSPEC: if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP) { @@ -102,7 +109,7 @@ static int current_check_access_socket(struct socket *const sock, * these checks, but it is safer to return a proper * error and test consistency thanks to kselftest. */ - if (sock->sk->__sk_common.skc_family == AF_INET) { + if (sock_family == AF_INET) { const struct sockaddr_in *const sockaddr = (struct sockaddr_in *)address; @@ -180,7 +187,7 @@ static int current_check_access_socket(struct socket *const sock, * check, but it is safer to return a proper error and test * consistency thanks to kselftest. */ - if (address->sa_family != sock->sk->__sk_common.skc_family && + if (address->sa_family != sock_family && address->sa_family != AF_UNSPEC) return -EINVAL; From 6be4da4f5a16fa1335b2625232883955f116db41 Mon Sep 17 00:00:00 2001 From: Ovidiu Panait Date: Wed, 10 Jun 2026 08:52:38 +0000 Subject: [PATCH 1440/2758] net: bcmgenet: Use weighted round-robin TX DMA arbitration [ Upstream commit fd615abd53110f0f815984e99e7cc51ca6b7d979 ] Under heavy network traffic, we observed sporadic TX queue timeouts on the Raspberry Pi 4. The timeouts can be reproduced by stress testing the TX path with multiple concurrent iperf UDP streams: iperf3 -c -u -b0 -P16 -t60 NETDEV WATCHDOG: CPU: 0: transmit queue 0 timed out 2044 ms NETDEV WATCHDOG: CPU: 3: transmit queue 0 timed out 2004 ms Investigation showed that the timeouts are caused by the priority-based arbiter. Under heavy load the highest priority queue starves the lower priority ones, causing timeouts. The TX strict priority arbiter is not suitable for the default use case where all the traffic gets spread across all the TX queues. Therefore, to fix this, switch the TX DMA arbiter to Weighted Round-Robin, which services all queues, so they do not stall. The weights were chosen to follow the existing priority scheme: q0 gets the smallest weight, while q1-4 get the bulk of the TX bandwidth. Fixes: 1c1008c793fa ("net: bcmgenet: add main driver file") Signed-off-by: Ovidiu Panait Link: https://patch.msgid.link/20260610085238.56300-1-ovidiu.panait.rb@renesas.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- .../net/ethernet/broadcom/genet/bcmgenet.c | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 7c11cf9167620..ad08c67269be5 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -40,9 +40,8 @@ #include "bcmgenet.h" -/* Default highest priority queue for multi queue support */ -#define GENET_Q1_PRIORITY 0 -#define GENET_Q0_PRIORITY 1 +#define GENET_Q0_WEIGHT 1 +#define GENET_Q1_WEIGHT 4 #define GENET_Q0_RX_BD_CNT \ (TOTAL_DESC - priv->hw_params->rx_queues * priv->hw_params->rx_bds_per_q) @@ -2129,13 +2128,6 @@ static netdev_tx_t bcmgenet_xmit(struct sk_buff *skb, struct net_device *dev) int i; index = skb_get_queue_mapping(skb); - /* Mapping strategy: - * queue_mapping = 0, unclassified, packet xmited through ring 0 - * queue_mapping = 1, goes to ring 1. (highest priority queue) - * queue_mapping = 2, goes to ring 2. - * queue_mapping = 3, goes to ring 3. - * queue_mapping = 4, goes to ring 4. - */ ring = &priv->tx_rings[index]; txq = netdev_get_tx_queue(dev, index); @@ -2881,8 +2873,9 @@ static int bcmgenet_rdma_disable(struct bcmgenet_priv *priv) /* Initialize Tx queues * - * Queues 1-4 are priority-based, each one has 32 descriptors, - * with queue 1 being the highest priority queue. + * Queues 1-4 are the priority queues, each one has 32 descriptors. + * The weighted round-robin arbiter gives them a larger share of TX + * bandwidth than the default queue 0. * * Queue 0 is the default Tx queue with * GENET_Q0_TX_BD_CNT = 256 - 4 * 32 = 128 descriptors. @@ -2900,8 +2893,8 @@ static void bcmgenet_init_tx_queues(struct net_device *dev) unsigned int start = 0, end = GENET_Q0_TX_BD_CNT; u32 i, ring_mask, dma_priority[3] = {0, 0, 0}; - /* Enable strict priority arbiter mode */ - bcmgenet_tdma_writel(priv, DMA_ARBITER_SP, DMA_ARB_CTRL); + /* Enable Weighted Round-Robin arbiter mode */ + bcmgenet_tdma_writel(priv, DMA_ARBITER_WRR, DMA_ARB_CTRL); /* Initialize Tx priority queues */ for (i = 0; i <= priv->hw_params->tx_queues; i++) { @@ -2909,7 +2902,7 @@ static void bcmgenet_init_tx_queues(struct net_device *dev) start = end; end += priv->hw_params->tx_bds_per_q; dma_priority[DMA_PRIO_REG_INDEX(i)] |= - (i ? GENET_Q1_PRIORITY : GENET_Q0_PRIORITY) + (i ? GENET_Q1_WEIGHT : GENET_Q0_WEIGHT) << DMA_PRIO_REG_SHIFT(i); } From be55f99a0b088cb79549ae8c49fdbc63ecd7ef38 Mon Sep 17 00:00:00 2001 From: Ratheesh Kannoth Date: Thu, 11 Jun 2026 14:03:30 +0530 Subject: [PATCH 1441/2758] octeontx2-af: fix NPC mailbox codes in mbox.h [ Upstream commit 925551c944a1de3058dc3fb55a517eea7749835d ] Several NPC mailbox command IDs in the 0x601x range were assigned out of order. Renumber and reorder the M() definitions so each opcode matches the stable contract expected by userspace tools and applications. Fixes: 4e527f1e5c15 ("octeontx2-af: npc: cn20k: Add new mailboxes for CN20K silicon") Cc: Suman Ghosh Signed-off-by: Ratheesh Kannoth Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260611083330.1652181-1-rkannoth@marvell.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- .../net/ethernet/marvell/octeontx2/af/mbox.h | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h index dc42c81c09425..44fdd6ba7307c 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h @@ -283,31 +283,30 @@ M(NPC_GET_FIELD_HASH_INFO, 0x6013, npc_get_field_hash_info, M(NPC_GET_FIELD_STATUS, 0x6014, npc_get_field_status, \ npc_get_field_status_req, \ npc_get_field_status_rsp) \ -M(NPC_CN20K_MCAM_GET_FREE_COUNT, 0x6015, npc_cn20k_get_fcnt, \ - msg_req, npc_cn20k_get_fcnt_rsp) \ -M(NPC_CN20K_GET_KEX_CFG, 0x6016, npc_cn20k_get_kex_cfg, \ +M(NPC_MCAM_DEFRAG, 0x6016, npc_defrag, \ + msg_req, \ + msg_rsp) \ +M(NPC_CN20K_GET_KEX_CFG, 0x6017, npc_cn20k_get_kex_cfg, \ msg_req, npc_cn20k_get_kex_cfg_rsp) \ -M(NPC_CN20K_MCAM_WRITE_ENTRY, 0x6017, npc_cn20k_mcam_write_entry, \ - npc_cn20k_mcam_write_entry_req, msg_rsp) \ -M(NPC_CN20K_MCAM_ALLOC_AND_WRITE_ENTRY, 0x6018, \ -npc_cn20k_mcam_alloc_and_write_entry, \ +M(NPC_CN20K_MCAM_GET_FREE_COUNT, 0x6018, npc_cn20k_get_fcnt, \ + msg_req, npc_cn20k_get_fcnt_rsp) \ +M(NPC_CN20K_MCAM_WRITE_ENTRY, 0x6019, npc_cn20k_mcam_write_entry, \ + npc_cn20k_mcam_write_entry_req, msg_rsp) \ +M(NPC_CN20K_MCAM_ALLOC_AND_WRITE_ENTRY, 0x601a, npc_cn20k_mcam_alloc_and_write_entry, \ npc_cn20k_mcam_alloc_and_write_entry_req, \ npc_mcam_alloc_and_write_entry_rsp) \ -M(NPC_CN20K_MCAM_READ_ENTRY, 0x6019, npc_cn20k_mcam_read_entry, \ +M(NPC_CN20K_MCAM_READ_ENTRY, 0x601b, npc_cn20k_mcam_read_entry, \ npc_mcam_read_entry_req, \ npc_cn20k_mcam_read_entry_rsp) \ -M(NPC_CN20K_MCAM_READ_BASE_RULE, 0x601a, npc_cn20k_read_base_steer_rule, \ - msg_req, npc_cn20k_mcam_read_base_rule_rsp) \ -M(NPC_MCAM_DEFRAG, 0x601b, npc_defrag, \ - msg_req, \ - msg_rsp) \ -M(NPC_MCAM_GET_NUM_KWS, 0x601c, npc_get_num_kws, \ +M(NPC_CN20K_MCAM_READ_BASE_RULE, 0x601c, npc_cn20k_read_base_steer_rule, \ + msg_req, npc_cn20k_mcam_read_base_rule_rsp) \ +M(NPC_MCAM_GET_NUM_KWS, 0x601d, npc_get_num_kws, \ npc_get_num_kws_req, \ npc_get_num_kws_rsp) \ -M(NPC_MCAM_GET_DFT_RL_IDXS, 0x601d, npc_get_dft_rl_idxs, \ +M(NPC_MCAM_GET_DFT_RL_IDXS, 0x601e, npc_get_dft_rl_idxs, \ msg_req, \ npc_get_dft_rl_idxs_rsp)\ -M(NPC_MCAM_GET_NPC_PFL_INFO, 0x601e, npc_get_pfl_info, \ +M(NPC_MCAM_GET_NPC_PFL_INFO, 0x601f, npc_get_pfl_info, \ msg_req, \ npc_get_pfl_info_rsp) \ /* NIX mbox IDs (range 0x8000 - 0xFFFF) */ \ From 2ac37fca3052e9d73ac22ff857f4cccdb0f882c5 Mon Sep 17 00:00:00 2001 From: "Wayen.Yan" Date: Fri, 12 Jun 2026 07:09:13 +0800 Subject: [PATCH 1442/2758] net: airoha: Fix register index for Tx-fwd counter configuration [ Upstream commit 1402ecccf5630a0b7fa4749d7d2e72abc3f3d73d ] In airoha_qdma_init_qos_stats(), the Tx-fwd counter configuration register uses the same index (i << 1) as the Tx-cpu counter, which overwrites the Tx-cpu configuration. The Tx-fwd counter value register correctly uses (i << 1) + 1, so the configuration register should use the same index. Fix the REG_CNTR_CFG index from (i << 1) to ((i << 1) + 1) so that the Tx-fwd counter is properly configured instead of clobbering the Tx-cpu counter config. Fixes: 20bf7d07c956 ("net: airoha: Add sched ETS offload support") Signed-off-by: Wayen.Yan Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/6a2b40e7.4dd82583.3a5c46.e566@mx.google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/airoha/airoha_eth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index 31cdb11cd78da..329988a8400c1 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -1256,7 +1256,7 @@ static void airoha_qdma_init_qos_stats(struct airoha_qdma *qdma) FIELD_PREP(CNTR_CHAN_MASK, i)); /* Tx-fwd transferred count */ airoha_qdma_wr(qdma, REG_CNTR_VAL((i << 1) + 1), 0); - airoha_qdma_wr(qdma, REG_CNTR_CFG(i << 1), + airoha_qdma_wr(qdma, REG_CNTR_CFG((i << 1) + 1), CNTR_EN_MASK | CNTR_ALL_QUEUE_EN_MASK | CNTR_ALL_DSCP_RING_EN_MASK | FIELD_PREP(CNTR_SRC_MASK, 1) | From 6b638db5ec068de99ef5e3c190b80327f6e919ce Mon Sep 17 00:00:00 2001 From: "Wayen.Yan" Date: Fri, 12 Jun 2026 07:09:56 +0800 Subject: [PATCH 1443/2758] net: airoha: Fix debugfs new-tuple display for IPv4 ROUTE entries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 1c3a77471afbb3981af28f7f7c8b2487558e4b00 ] In airoha_ppe_debugfs_foe_show(), the second switch statement falls through from PPE_PKT_TYPE_IPV4_HNAPT/DSLITE to PPE_PKT_TYPE_IPV4_ROUTE, accessing hwe->ipv4.new_tuple for all three types. However, IPv4 ROUTE (3-tuple) entries do not contain a valid new_tuple — this field is only meaningful for NATted flows (HNAPT/DSLITE). For ROUTE entries, the memory at the new_tuple offset holds routing information, not NAT data, so displaying "new=" produces garbage output. Display new_tuple only for HNAPT and DSLITE, and let IPV4_ROUTE fall through to the default case. Fixes: 3fe15c640f38 ("net: airoha: Introduce PPE debugfs support") Link: https://lore.kernel.org/6a2b40ea.4dd82583.3a5c46.e5a2@mx.google.com Signed-off-by: Wayen.Yan Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/6a2be54b.ef98c1b2.3c3224.2ed8@mx.google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/airoha/airoha_ppe_debugfs.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/ethernet/airoha/airoha_ppe_debugfs.c b/drivers/net/ethernet/airoha/airoha_ppe_debugfs.c index 0112c41150bb0..e46a98514486f 100644 --- a/drivers/net/ethernet/airoha/airoha_ppe_debugfs.c +++ b/drivers/net/ethernet/airoha/airoha_ppe_debugfs.c @@ -121,8 +121,6 @@ static int airoha_ppe_debugfs_foe_show(struct seq_file *m, void *private, case PPE_PKT_TYPE_IPV4_DSLITE: src_port = &hwe->ipv4.new_tuple.src_port; dest_port = &hwe->ipv4.new_tuple.dest_port; - fallthrough; - case PPE_PKT_TYPE_IPV4_ROUTE: src_addr = &hwe->ipv4.new_tuple.src_ip; dest_addr = &hwe->ipv4.new_tuple.dest_ip; seq_puts(m, " new="); From 9684fff87124b201e11dea01ded9173025359a0f Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Thu, 11 Jun 2026 13:35:43 +0800 Subject: [PATCH 1444/2758] kcm: use WRITE_ONCE() when changing lower socket callbacks [ Upstream commit 47186409c092cd7dd70350999186c700233e854d ] kcm_attach() replaces a live lower TCP socket's sk_data_ready and sk_write_space callbacks with KCM handlers, and kcm_unattach() restores them later. Those callback-pointer updates are still plain stores even though the same fields can be read and invoked concurrently on other CPUs. If another CPU observes an older callback snapshot after the live field has already been restored, callback execution can run with a mismatched target and sk_user_data state, leading to stale or misdirected wakeups. Use WRITE_ONCE() for the callback replacement and restore operations so these shared callback fields follow the same visibility contract already established by the earlier 4022 fixes. Fixes: ab7ac4eb9832 ("kcm: Kernel Connection Multiplexor module") Signed-off-by: Runyu Xiao Link: https://patch.msgid.link/20260611053543.2429462-1-runyu.xiao@seu.edu.cn Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/kcm/kcmsock.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/kcm/kcmsock.c b/net/kcm/kcmsock.c index 3912e75079f5e..a998336840c33 100644 --- a/net/kcm/kcmsock.c +++ b/net/kcm/kcmsock.c @@ -1304,8 +1304,8 @@ static int kcm_attach(struct socket *sock, struct socket *csock, psock->save_write_space = csk->sk_write_space; psock->save_state_change = csk->sk_state_change; csk->sk_user_data = psock; - csk->sk_data_ready = psock_data_ready; - csk->sk_write_space = psock_write_space; + WRITE_ONCE(csk->sk_data_ready, psock_data_ready); + WRITE_ONCE(csk->sk_write_space, psock_write_space); csk->sk_state_change = psock_state_change; write_unlock_bh(&csk->sk_callback_lock); @@ -1381,8 +1381,8 @@ static void kcm_unattach(struct kcm_psock *psock) */ write_lock_bh(&csk->sk_callback_lock); csk->sk_user_data = NULL; - csk->sk_data_ready = psock->save_data_ready; - csk->sk_write_space = psock->save_write_space; + WRITE_ONCE(csk->sk_data_ready, psock->save_data_ready); + WRITE_ONCE(csk->sk_write_space, psock->save_write_space); csk->sk_state_change = psock->save_state_change; strp_stop(&psock->strp); From 43e10709b1ba288bcbabb9b9cb6e518b2a5d8506 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sun, 14 Jun 2026 08:48:00 +0800 Subject: [PATCH 1445/2758] ALSA: seq: oss: Serialize readq reset state with q->lock [ Upstream commit 49ce92d207820f588b0406add82f053decfbe5d9 ] snd_seq_oss_readq_clear() resets qlen, head, and tail without q->lock even though the normal reader and producer paths serialize the same ring state under that spinlock. A reset can therefore race snd_seq_oss_readq_free() or snd_seq_oss_readq_put_event() and leave stale records in the queue, drop freshly queued ones, or report the wrong readiness after wakeup. KCSAN reports a data race between snd_seq_oss_readq_clear() and snd_seq_oss_readq_free(). Take q->lock while clearing the ring and resetting input_time. Factor the enqueue logic into a caller-locked helper so snd_seq_oss_readq_put_timestamp() updates its suppression state under the same lock instead of racing the reset path. The buggy scenario involves two paths, with each column showing the order within that path: reset path: locked readq updater: 1. snd_seq_oss_reset() or 1. A reader or callback producer release reaches takes q->lock on the same queue. snd_seq_oss_readq_clear(). 2. snd_seq_oss_readq_clear() 2. The updater tests or modifies resets qlen, head, tail, qlen, head, and tail. and input_time. 3. snd_seq_oss_readq_clear() 3. The updater completes its wakes sleepers on read-modify-write sequence. q->midi_sleep. 4. Without q->lock, the reset 4. The resulting ring state drives can overlap the locked later reads and readiness. update. KCSAN reports: BUG: KCSAN: data-race in snd_seq_oss_readq_clear / snd_seq_oss_readq_free write to 0xffff8881069fe608 of 4 bytes by task 120516 on cpu 0: snd_seq_oss_readq_free+0x6c/0x80 snd_seq_oss_read+0xcb/0x250 odev_read+0x38/0x60 vfs_read+0xff/0x600 ksys_read+0xb4/0x140 __x64_sys_read+0x46/0x60 do_syscall_64+0xbb/0x2f0 entry_SYSCALL_64_after_hwframe+0x77/0x7f read to 0xffff8881069fe608 of 4 bytes by task 120517 on cpu 1: snd_seq_oss_readq_clear+0x1f/0x90 snd_seq_oss_reset+0xa7/0xf0 snd_seq_oss_ioctl+0x6f6/0x7e0 odev_ioctl+0x56/0xc0 __x64_sys_ioctl+0xd1/0x120 do_syscall_64+0xbb/0x2f0 entry_SYSCALL_64_after_hwframe+0x77/0x7f value changed: 0x00000001 -> 0x00000000 Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Cen Zhang Link: https://patch.msgid.link/20260614004801.3507773-1-zzzccc427@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/core/seq/oss/seq_oss_readq.c | 77 ++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 25 deletions(-) diff --git a/sound/core/seq/oss/seq_oss_readq.c b/sound/core/seq/oss/seq_oss_readq.c index c880d47711698..06e12707db213 100644 --- a/sound/core/seq/oss/seq_oss_readq.c +++ b/sound/core/seq/oss/seq_oss_readq.c @@ -73,13 +73,17 @@ snd_seq_oss_readq_delete(struct seq_oss_readq *q) void snd_seq_oss_readq_clear(struct seq_oss_readq *q) { - if (q->qlen) { - q->qlen = 0; - q->head = q->tail = 0; + scoped_guard(spinlock_irqsave, &q->lock) { + if (q->qlen) { + q->qlen = 0; + q->head = 0; + q->tail = 0; + } + q->input_time = (unsigned long)-1; } + /* if someone sleeping, wake'em up */ wake_up(&q->midi_sleep); - q->input_time = (unsigned long)-1; } /* @@ -136,11 +140,11 @@ int snd_seq_oss_readq_sysex(struct seq_oss_readq *q, int dev, /* * copy an event to input queue: * return zero if enqueued + * caller must hold lock */ -int -snd_seq_oss_readq_put_event(struct seq_oss_readq *q, union evrec *ev) +static int snd_seq_oss_readq_put_event_locked(struct seq_oss_readq *q, + union evrec *ev) { - guard(spinlock_irqsave)(&q->lock); if (q->qlen >= q->maxlen - 1) return -ENOMEM; @@ -148,12 +152,27 @@ snd_seq_oss_readq_put_event(struct seq_oss_readq *q, union evrec *ev) q->tail = (q->tail + 1) % q->maxlen; q->qlen++; - /* wake up sleeper */ - wake_up(&q->midi_sleep); - return 0; } +/* + * copy an event to input queue: + * return zero if enqueued + */ +int +snd_seq_oss_readq_put_event(struct seq_oss_readq *q, union evrec *ev) +{ + int rc; + + scoped_guard(spinlock_irqsave, &q->lock) { + rc = snd_seq_oss_readq_put_event_locked(q, ev); + if (!rc) + wake_up(&q->midi_sleep); + } + + return rc; +} + /* * pop queue @@ -209,23 +228,31 @@ snd_seq_oss_readq_poll(struct seq_oss_readq *q, struct file *file, poll_table *w int snd_seq_oss_readq_put_timestamp(struct seq_oss_readq *q, unsigned long curt, int seq_mode) { - if (curt != q->input_time) { - union evrec rec; - memset(&rec, 0, sizeof(rec)); - switch (seq_mode) { - case SNDRV_SEQ_OSS_MODE_SYNTH: - rec.echo = (curt << 8) | SEQ_WAIT; - snd_seq_oss_readq_put_event(q, &rec); - break; - case SNDRV_SEQ_OSS_MODE_MUSIC: - rec.t.code = EV_TIMING; - rec.t.cmd = TMR_WAIT_ABS; - rec.t.time = curt; - snd_seq_oss_readq_put_event(q, &rec); - break; + int queued = 0; + + scoped_guard(spinlock_irqsave, &q->lock) { + if (curt != q->input_time) { + union evrec rec; + + memset(&rec, 0, sizeof(rec)); + switch (seq_mode) { + case SNDRV_SEQ_OSS_MODE_SYNTH: + rec.echo = (curt << 8) | SEQ_WAIT; + queued = !snd_seq_oss_readq_put_event_locked(q, &rec); + break; + case SNDRV_SEQ_OSS_MODE_MUSIC: + rec.t.code = EV_TIMING; + rec.t.cmd = TMR_WAIT_ABS; + rec.t.time = curt; + queued = !snd_seq_oss_readq_put_event_locked(q, &rec); + break; + } + q->input_time = curt; } - q->input_time = curt; } + if (queued) + wake_up(&q->midi_sleep); + return 0; } From 1c4c35fb68d520241f7d9f1c356b5e2370fd8364 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sun, 14 Jun 2026 08:48:01 +0800 Subject: [PATCH 1446/2758] ALSA: seq: avoid stale FIFO cells during resize [ Upstream commit e546128291f8d688dcb931827e2efd2aa6c0734d ] snd_seq_fifo_resize() still needs to publish the replacement pool before it waits for FIFO users. A blocking snd_seq_read() holds f->use_lock while it sleeps, so concurrent senders must be able to queue to the new pool and wake that reader instead of failing against a closing old pool. However, snd_seq_fifo_event_in() duplicates an event before it takes f->lock, and snd_seq_read() can dequeue a cell and later call snd_seq_fifo_cell_putback() if copy_to_user() or snd_seq_expand_var_event() fails. If resize swaps f->pool and detaches oldhead in between, either path can relink an old-pool cell after the snapshot. That stale cell sits outside the drained oldhead list, keeps oldpool->counter elevated, and can leave snd_seq_pool_delete() waiting for the retired pool to drain. Keep the existing swap-before-wait ordering in snd_seq_fifo_resize(), but reject stale cells before any FIFO relink. Revalidate event-in cells under f->lock and retry them against the published replacement pool, and free stale putback cells instead of linking them back into the FIFO. The buggy scenario involves two paths, with each column showing the order within that path: resize path: relink path: 1. Allocate newpool. 1. Take f->use_lock. 2. Swap f->pool to newpool and 2. Duplicate or dequeue an old-pool detach oldhead. cell before oldpool closes. 3. Mark oldpool closing and 3. Reach a later relink point after wait for FIFO users. resize published newpool. 4. Free oldhead and delete 4. Relink the old-pool cell after oldpool. resize detached oldhead. 5. Drop f->use_lock. The reproducer reports a resize ioctl blocked in the expected pool teardown path: signal: resize iteration=98 target_pool=4 exceeded 250ms (elapsed=251ms) diagnostic: resize_tid=651 wchan=snd_seq_pool_done diagnostic: resize_tid=651 stack= snd_seq_pool_done+0x5b/0x140 snd_seq_pool_delete+0x7a/0x90 snd_seq_fifo_resize+0x193/0x1e0 snd_seq_ioctl_set_client_pool+0x214/0x260 snd_seq_ioctl+0x119/0x540 __x64_sys_ioctl+0xd1/0x120 do_syscall_64+0xbb/0x2f0 entry_SYSCALL_64_after_hwframe+0x77/0x7f A second run with larger pools hit the same target path: signal: resize iteration=32 target_pool=64 exceeded 250ms (elapsed=251ms) diagnostic: resize_tid=663 wchan=snd_seq_pool_done diagnostic: resize_tid=663 stack= snd_seq_pool_done+0x5b/0x140 snd_seq_pool_delete+0x7a/0x90 snd_seq_fifo_resize+0x193/0x1e0 snd_seq_ioctl_set_client_pool+0x214/0x260 snd_seq_ioctl+0x119/0x540 __x64_sys_ioctl+0xd1/0x120 do_syscall_64+0xbb/0x2f0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Fixes: 2d7d54002e39 ("ALSA: seq: Fix race during FIFO resize") Signed-off-by: Cen Zhang Link: https://patch.msgid.link/20260614004801.3507773-2-zzzccc427@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/core/seq/seq_fifo.c | 52 ++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c index ebe1394c18a96..cffa8d4303747 100644 --- a/sound/core/seq/seq_fifo.c +++ b/sound/core/seq/seq_fifo.c @@ -101,13 +101,17 @@ int snd_seq_fifo_event_in(struct snd_seq_fifo *f, struct snd_seq_event *event) { struct snd_seq_event_cell *cell; + struct snd_seq_pool *pool; + bool linked; int err; if (snd_BUG_ON(!f)) return -EINVAL; guard(snd_seq_fifo)(f); - err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL, NULL); /* always non-blocking */ +retry: + pool = READ_ONCE(f->pool); + err = snd_seq_event_dup(pool, event, &cell, 1, NULL, NULL); /* always non-blocking */ if (err < 0) { if ((err == -ENOMEM) || (err == -EAGAIN)) atomic_inc(&f->overflow); @@ -115,14 +119,24 @@ int snd_seq_fifo_event_in(struct snd_seq_fifo *f, } /* append new cells to fifo */ + linked = false; scoped_guard(spinlock_irqsave, &f->lock) { - if (f->tail != NULL) - f->tail->next = cell; - f->tail = cell; - if (f->head == NULL) - f->head = cell; - cell->next = NULL; - f->cells++; + if (cell->pool == f->pool) { + if (f->tail) + f->tail->next = cell; + f->tail = cell; + if (!f->head) + f->head = cell; + cell->next = NULL; + f->cells++; + linked = true; + } + } + + if (!linked) { + /* Retry against the replacement pool after resize publishes it. */ + snd_seq_cell_free(cell); + goto retry; } /* wakeup client */ @@ -194,13 +208,21 @@ int snd_seq_fifo_cell_out(struct snd_seq_fifo *f, void snd_seq_fifo_cell_putback(struct snd_seq_fifo *f, struct snd_seq_event_cell *cell) { + bool linked = false; + if (cell) { - guard(spinlock_irqsave)(&f->lock); - cell->next = f->head; - f->head = cell; - if (!f->tail) - f->tail = cell; - f->cells++; + scoped_guard(spinlock_irqsave, &f->lock) { + if (cell->pool == f->pool) { + cell->next = f->head; + f->head = cell; + if (!f->tail) + f->tail = cell; + f->cells++; + linked = true; + } + } + if (!linked) + snd_seq_cell_free(cell); } } @@ -237,7 +259,7 @@ int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize) oldpool = f->pool; oldhead = f->head; /* exchange pools */ - f->pool = newpool; + WRITE_ONCE(f->pool, newpool); f->head = NULL; f->tail = NULL; f->cells = 0; From db50e2d289b668c96ea8334f68bcebdd7a5932ef Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Fri, 5 Jun 2026 15:11:19 +0200 Subject: [PATCH 1447/2758] netfilter: nf_conncount: callers must hold rcu read lock [ Upstream commit 64d7d5abe2160bba369b4a8f06bdf5630573bab0 ] rcu_derefence_raw() should not have been used here, it concealed this bug. Its used because struct rb_node lacks __rcu annotated pointers, so plain rcu_derefence causes sparse warnings. The major tradeoff is that rcu_derefence_raw() doesn't warn when the caller isn't in a rcu read section. Extend the rcu read lock scope accordingly and cause sparse warnings, those warnings are the lesser evil. Fixes: 11efd5cb04a1 ("openvswitch: Support conntrack zone limit") Closes: https://sashiko.dev/#/patchset/20260603230610.7900-1-fw%40strlen.de Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_conncount.c | 6 +++--- net/openvswitch/conntrack.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/net/netfilter/nf_conncount.c b/net/netfilter/nf_conncount.c index 00eed5b4d1b12..dac29e933553d 100644 --- a/net/netfilter/nf_conncount.c +++ b/net/netfilter/nf_conncount.c @@ -499,7 +499,7 @@ count_tree(struct net *net, hash = jhash2(key, data->keylen, conncount_rnd) % CONNCOUNT_SLOTS; root = &data->root[hash]; - parent = rcu_dereference_raw(root->rb_node); + parent = rcu_dereference(root->rb_node); while (parent) { int diff; @@ -507,9 +507,9 @@ count_tree(struct net *net, diff = key_diff(key, rbconn->key, data->keylen); if (diff < 0) { - parent = rcu_dereference_raw(parent->rb_left); + parent = rcu_dereference(parent->rb_left); } else if (diff > 0) { - parent = rcu_dereference_raw(parent->rb_right); + parent = rcu_dereference(parent->rb_right); } else { int ret; diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c index 7c9256572284f..c6fd9c424e8fe 100644 --- a/net/openvswitch/conntrack.c +++ b/net/openvswitch/conntrack.c @@ -1797,10 +1797,10 @@ static int ovs_ct_limit_get_zone_limit(struct net *net, } else { rcu_read_lock(); limit = ct_limit_get(info, zone); - rcu_read_unlock(); err = __ovs_ct_limit_get_zone_limit( net, info->data, zone, limit, reply); + rcu_read_unlock(); if (err) return err; } From edf234f71fb327792196f813048ab8f5bd3bb712 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 10 Jun 2026 00:03:19 +0200 Subject: [PATCH 1448/2758] netfilter: nf_dup_netdev: add nf_dev_xmit_recursion*() helpers and use them [ Upstream commit 2354e975932dabb06fad239f07a3b68fd1809737 ] Update nft_dup and nft_fwd to use the nf_dev_xmit_recursion() helpers. This patch also disables BH when transmitting the skb to address a possible migration to different CPU leading to imbalanced decrementation of the recursion counters. This is modeled after Florian Westphal's dev_xmit_recursion*() API available since commit 97cdcf37b57e ("net: place xmit recursion in softnet data") according to its current state in the tree. Fixes: 1d47b55b36d2 ("netfilter: nft_fwd_netdev: use recursion counter in neigh egress path") Fixes: f37ad9127039 ("netfilter: nf_dup_netdev: Move the recursion counter struct netdev_xmit") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- include/net/netfilter/nf_dup_netdev.h | 34 +++++++++++++++++++++++---- net/netfilter/nf_dup_netdev.c | 15 ++++++------ net/netfilter/nft_fwd_netdev.c | 17 ++++++++------ 3 files changed, 47 insertions(+), 19 deletions(-) diff --git a/include/net/netfilter/nf_dup_netdev.h b/include/net/netfilter/nf_dup_netdev.h index 609bcf422a9b3..f6b05bd80c3fe 100644 --- a/include/net/netfilter/nf_dup_netdev.h +++ b/include/net/netfilter/nf_dup_netdev.h @@ -11,15 +11,39 @@ void nf_fwd_netdev_egress(const struct nft_pktinfo *pkt, int oif); #define NF_RECURSION_LIMIT 2 -static inline u8 *nf_get_nf_dup_skb_recursion(void) -{ #ifndef CONFIG_PREEMPT_RT - return this_cpu_ptr(&softnet_data.xmit.nf_dup_skb_recursion); +static inline bool nf_dev_xmit_recursion(void) +{ + return unlikely(__this_cpu_read(softnet_data.xmit.nf_dup_skb_recursion) > + NF_RECURSION_LIMIT); +} + +static inline void nf_dev_xmit_recursion_inc(void) +{ + __this_cpu_inc(softnet_data.xmit.nf_dup_skb_recursion); +} + +static inline void nf_dev_xmit_recursion_dec(void) +{ + __this_cpu_dec(softnet_data.xmit.nf_dup_skb_recursion); +} #else - return ¤t->net_xmit.nf_dup_skb_recursion; -#endif +static inline bool nf_dev_xmit_recursion(void) +{ + return unlikely(current->net_xmit.nf_dup_skb_recursion > NF_RECURSION_LIMIT); +} + +static inline void nf_dev_xmit_recursion_inc(void) +{ + current->net_xmit.nf_dup_skb_recursion++; } +static inline void nf_dev_xmit_recursion_dec(void) +{ + current->net_xmit.nf_dup_skb_recursion--; +} +#endif + struct nft_offload_ctx; struct nft_flow_rule; diff --git a/net/netfilter/nf_dup_netdev.c b/net/netfilter/nf_dup_netdev.c index 3d88ef927f31c..c6bd5c29bed66 100644 --- a/net/netfilter/nf_dup_netdev.c +++ b/net/netfilter/nf_dup_netdev.c @@ -16,11 +16,6 @@ static void nf_do_netdev_egress(struct sk_buff *skb, struct net_device *dev, enum nf_dev_hooks hook) { - u8 *nf_dup_skb_recursion = nf_get_nf_dup_skb_recursion(); - - if (*nf_dup_skb_recursion > NF_RECURSION_LIMIT) - goto err; - if (hook == NF_NETDEV_INGRESS && skb_mac_header_was_set(skb)) { if (skb_cow_head(skb, skb->mac_len)) goto err; @@ -30,9 +25,15 @@ static void nf_do_netdev_egress(struct sk_buff *skb, struct net_device *dev, skb->dev = dev; skb_clear_tstamp(skb); - (*nf_dup_skb_recursion)++; + local_bh_disable(); + if (nf_dev_xmit_recursion()) { + local_bh_enable(); + goto err; + } + nf_dev_xmit_recursion_inc(); dev_queue_xmit(skb); - (*nf_dup_skb_recursion)--; + nf_dev_xmit_recursion_dec(); + local_bh_enable(); return; err: kfree_skb(skb); diff --git a/net/netfilter/nft_fwd_netdev.c b/net/netfilter/nft_fwd_netdev.c index b9e88d7cf3081..a48c2f765bbaa 100644 --- a/net/netfilter/nft_fwd_netdev.c +++ b/net/netfilter/nft_fwd_netdev.c @@ -95,7 +95,6 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) { - u8 *nf_dup_skb_recursion = nf_get_nf_dup_skb_recursion(); struct nft_fwd_neigh *priv = nft_expr_priv(expr); void *addr = ®s->data[priv->sreg_addr]; int oif = regs->data[priv->sreg_dev]; @@ -154,13 +153,15 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr, goto out; } - if (*nf_dup_skb_recursion > NF_RECURSION_LIMIT) { + dev = dev_get_by_index_rcu(nft_net(pkt), oif); + if (!dev) { verdict = NF_DROP; goto out; } - dev = dev_get_by_index_rcu(nft_net(pkt), oif); - if (dev == NULL) { + local_bh_disable(); + if (nf_dev_xmit_recursion()) { + local_bh_enable(); verdict = NF_DROP; goto out; } @@ -169,16 +170,18 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr, if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) { skb = skb_expand_head(skb, hh_len); if (!skb) { - verdict = NF_STOLEN; + local_bh_enable(); goto out; } } skb->dev = dev; skb_clear_tstamp(skb); - (*nf_dup_skb_recursion)++; + + nf_dev_xmit_recursion_inc(); neigh_xmit(neigh_table, dev, addr, skb); - (*nf_dup_skb_recursion)--; + nf_dev_xmit_recursion_dec(); + local_bh_enable(); out: regs->verdict.code = verdict; } From 1e8ff78520d96b235021b0910b4fa33b3e82af11 Mon Sep 17 00:00:00 2001 From: Alice Mikityanska Date: Thu, 11 Jun 2026 21:29:46 +0200 Subject: [PATCH 1449/2758] geneve: Fix off-by-one comparing with GRO_LEGACY_MAX_SIZE [ Upstream commit 2319688890d97c63da423a3c57c23b4ab5952dfc ] GRO_LEGACY_MAX_SIZE = 65536; total_len being 65536 is too big to fit into a u16. As can be seen in skb_gro_receive, packets bigger or equal to gro_max_size (or GRO_LEGACY_MAX_SIZE) are dropped with -E2BIG. Apply the same boundary to geneve_post_decap_hint to avoid writing 65536 to a 16-bit iph->tot_len field with an overflow. Fixes: fd0dd796576e ("geneve: use GRO hint option in the RX path") Signed-off-by: Alice Mikityanska Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260611192955.604661-3-alice.kernel@fastmail.im Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/geneve.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 715180c3a1b34..b2e74e815e577 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -603,7 +603,7 @@ static int geneve_post_decap_hint(const struct sock *sk, struct sk_buff *skb, ipv6h = (void *)skb->data + gro_hint->nested_nh_offset; iph = (struct iphdr *)ipv6h; total_len = skb->len - gro_hint->nested_nh_offset; - if (total_len > GRO_LEGACY_MAX_SIZE) + if (total_len >= GRO_LEGACY_MAX_SIZE) return -E2BIG; /* From b8ca5fcc3182a701b48c6d9ddace4da223efd358 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 14 Jun 2026 11:05:05 +0200 Subject: [PATCH 1450/2758] ALSA: core: Fix unintuitive behavior of snd_power_ref_and_wait() [ Upstream commit b113a891252c3fa4fab11ec8c2894a22ecaf278c ] snd_power_ref_and_wait() takes the power refcount and doesn't leave it no matter whether it returns an error or not. However, the majority of callers don't expect but just returns without unreferencing in the caller side upon errors. For addressing the potential refcount unbalance, rather correct the behavior of snd_power_ref_wait() to unreference upon returning an error. Note that the problem above is likely negligible; the function returns an error only when the sound card is being shutdown, hence it doesn't matter about the power refcount any longer at such a state. Fixes: e94fdbd7b25d ("ALSA: control: Track in-flight control read/write/tlv accesses") Reported-by: WenTao Liang Closes: https://lore.kernel.org/20260612022121.14329-1-vulab@iscas.ac.cn Link: https://patch.msgid.link/20260614090507.772540-1-tiwai@suse.de Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/core/init.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/sound/core/init.c b/sound/core/init.c index 0c316189e9476..9bff101af6478 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -1139,7 +1139,7 @@ EXPORT_SYMBOL(snd_card_file_remove); * typically around calling control ops. * * The caller needs to pull down the refcount via snd_power_unref() later - * no matter whether the error is returned from this function or not. + * when this function returns 0. * * Return: Zero if successful, or a negative error code. */ @@ -1152,7 +1152,11 @@ int snd_power_ref_and_wait(struct snd_card *card) card->shutdown || snd_power_get_state(card) == SNDRV_CTL_POWER_D0, snd_power_unref(card), snd_power_ref(card)); - return card->shutdown ? -ENODEV : 0; + if (card->shutdown) { + snd_power_unref(card); + return -ENODEV; + } + return 0; } EXPORT_SYMBOL_GPL(snd_power_ref_and_wait); @@ -1169,7 +1173,8 @@ int snd_power_wait(struct snd_card *card) int ret; ret = snd_power_ref_and_wait(card); - snd_power_unref(card); + if (!ret) + snd_power_unref(card); return ret; } EXPORT_SYMBOL(snd_power_wait); From 018b3c8248f5d5f335d28f9cbc04abd01b99ecab Mon Sep 17 00:00:00 2001 From: Henrique Carvalho Date: Wed, 27 May 2026 09:26:42 -0300 Subject: [PATCH 1451/2758] smb: client: fix conflicting option validation for new mount API [ Upstream commit 10ce03879f935f756bc8a386b3fa3a1c7264d950 ] Apply conflicting option validation consistently across all the new mount API paths, for both mount and remount. Some checks were only applied during initial mount validation, while others were handled during option parsing, causing mount and remount/reconfigure to behave differently. Move the conflicting option checks into smb3_handle_conflicting_options() and call it from the common validation paths, including for multichannel/max_channels handling. Fixes: 24e0a1eff9e2 ("cifs: switch to new mount api") Signed-off-by: Henrique Carvalho Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/client/fs_context.c | 102 +++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 49 deletions(-) diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c index 2f86158f85d7b..fd4b13cd654d9 100644 --- a/fs/smb/client/fs_context.c +++ b/fs/smb/client/fs_context.c @@ -693,6 +693,41 @@ static int smb3_handle_conflicting_options(struct fs_context *fc) { struct smb3_fs_context *ctx = smb3_fc2context(fc); + if (ctx->rdma && ctx->vals->protocol_id < SMB30_PROT_ID) { + cifs_errorf(fc, "SMB Direct requires Version >=3.0\n"); + return -EOPNOTSUPP; + } + + if (ctx->multiuser && !IS_ENABLED(CONFIG_KEYS)) { + cifs_errorf(fc, "Multiuser mounts require kernels with CONFIG_KEYS enabled\n"); + return -EOPNOTSUPP; + } + + if (ctx->multiuser && ctx->upcall_target == UPTARGET_MOUNT) { + cifs_errorf(fc, "multiuser mount option not supported with upcalltarget set as 'mount'\n"); + return -EINVAL; + } + + if (ctx->uid_specified && !ctx->forceuid_specified) { + ctx->override_uid = 1; + pr_notice("enabling forceuid mount option implicitly because uid= option is specified\n"); + } + + if (ctx->gid_specified && !ctx->forcegid_specified) { + ctx->override_gid = 1; + pr_notice("enabling forcegid mount option implicitly because gid= option is specified\n"); + } + + if (ctx->override_uid && !ctx->uid_specified) { + ctx->override_uid = 0; + pr_notice("ignoring forceuid mount option specified with no uid= option\n"); + } + + if (ctx->override_gid && !ctx->gid_specified) { + ctx->override_gid = 0; + pr_notice("ignoring forcegid mount option specified with no gid= option\n"); + } + if (ctx->multichannel_specified) { if (ctx->multichannel) { if (!ctx->max_channels_specified) { @@ -711,19 +746,14 @@ static int smb3_handle_conflicting_options(struct fs_context *fc) return -EINVAL; } } - } else { - if (ctx->max_channels_specified) { - if (ctx->max_channels > 1) - ctx->multichannel = true; - else - ctx->multichannel = false; - } else { + } else if (ctx->max_channels_specified) { + if (ctx->max_channels > 1) + ctx->multichannel = true; + else ctx->multichannel = false; - ctx->max_channels = 1; - } } - //resetting default values as remount doesn't initialize fs_context again + /* clear parse-time latches so they don't persist across remounts */ ctx->multichannel_specified = false; ctx->max_channels_specified = false; @@ -804,28 +834,23 @@ static int smb3_fs_context_parse_monolithic(struct fs_context *fc, if (ret < 0) break; } - return ret ?: smb3_handle_conflicting_options(fc); + return ret; } /* - * Validate the preparsed information in the config. + * smb3_fs_context_validate - check initial-mount-only constraints: + * UNC presence, address resolution, dialect warnings + * + * @fc: generic mount context */ static int smb3_fs_context_validate(struct fs_context *fc) { struct smb3_fs_context *ctx = smb3_fc2context(fc); + int rc; - if (ctx->rdma && ctx->vals->protocol_id < SMB30_PROT_ID) { - cifs_errorf(fc, "SMB Direct requires Version >=3.0\n"); - return -EOPNOTSUPP; - } - -#ifndef CONFIG_KEYS - /* Muliuser mounts require CONFIG_KEYS support */ - if (ctx->multiuser) { - cifs_errorf(fc, "Multiuser mounts require kernels with CONFIG_KEYS enabled\n"); - return -1; - } -#endif + rc = smb3_handle_conflicting_options(fc); + if (rc) + return rc; if (ctx->got_version == false) pr_warn_once("No dialect specified on mount. Default has changed to a more secure dialect, SMB2.1 or later (e.g. SMB3.1.1), from CIFS (SMB1). To use the less secure SMB1 dialect to access old servers which do not support SMB3.1.1 (or even SMB3 or SMB2.1) specify vers=1.0 on mount.\n"); @@ -860,26 +885,6 @@ static int smb3_fs_context_validate(struct fs_context *fc) /* set the port that we got earlier */ cifs_set_port((struct sockaddr *)&ctx->dstaddr, ctx->port); - if (ctx->uid_specified && !ctx->forceuid_specified) { - ctx->override_uid = 1; - pr_notice("enabling forceuid mount option implicitly because uid= option is specified\n"); - } - - if (ctx->gid_specified && !ctx->forcegid_specified) { - ctx->override_gid = 1; - pr_notice("enabling forcegid mount option implicitly because gid= option is specified\n"); - } - - if (ctx->override_uid && !ctx->uid_specified) { - ctx->override_uid = 0; - pr_notice("ignoring forceuid mount option specified with no uid= option\n"); - } - - if (ctx->override_gid && !ctx->gid_specified) { - ctx->override_gid = 0; - pr_notice("ignoring forcegid mount option specified with no gid= option\n"); - } - return 0; } @@ -1078,6 +1083,10 @@ static int smb3_reconfigure(struct fs_context *fc) if (rc) return rc; + rc = smb3_handle_conflicting_options(fc); + if (rc) + return rc; + old_ctx = kzalloc_obj(*old_ctx); if (!old_ctx) return -ENOMEM; @@ -1933,11 +1942,6 @@ static int smb3_fs_context_parse_param(struct fs_context *fc, } /* case Opt_ignore: - is ignored as expected ... */ - if (ctx->multiuser && ctx->upcall_target == UPTARGET_MOUNT) { - cifs_errorf(fc, "multiuser mount option not supported with upcalltarget set as 'mount'\n"); - goto cifs_parse_mount_err; - } - return 0; cifs_parse_mount_err: From 7839f1817a0cb6c4ed5cfe25d04845c43380a129 Mon Sep 17 00:00:00 2001 From: Jian Zhang Date: Tue, 19 May 2026 18:44:22 +0800 Subject: [PATCH 1452/2758] cifs: remove all cifs files before kill super [ Upstream commit 6d9a4aaaa8b2612b5ef9d581e2f286a458b71ee1 ] Cifs files may be put into fileinfo_put_wq during umounting cifs. After umount done, cifsFileInfo_put_final is called, which cause following BUG: BUG: kernel NULL pointer dereference, address: 0000000000000000 ... [ 134.222152] list_lru_add+0x64/0x1a0 [ 134.222399] ? cifs_put_tcon+0x171/0x340 [cifs] [ 134.222772] d_lru_add+0x44/0x60 [ 134.222997] dput+0x1fc/0x210 [ 134.223213] cifsFileInfo_put_final+0x11a/0x140 [cifs] [ 134.223576] process_one_work+0x17c/0x320 [ 134.223843] worker_thread+0x188/0x280 [ 134.224084] ? __pfx_worker_thread+0x10/0x10 [ 134.224366] kthread+0xcc/0x100 [ 134.224576] ? __pfx_kthread+0x10/0x10 [ 134.224827] ret_from_fork+0x30/0x50 [ 134.225063] ? __pfx_kthread+0x10/0x10 [ 134.225328] ret_from_fork_asm+0x1b/0x30 This can be reproduce by following: unshare -n bash -c " mkdir -p ${CIFS_MNT} ip netns attach root 1 ip link add eth0 type veth peer veth0 netns root ip link set eth0 up ip -n root link set veth0 up ip addr add 192.168.0.2/24 dev eth0 ip -n root addr add 192.168.0.1/24 dev veth0 ip route add default via 192.168.0.1 dev eth0 ip netns exec root sysctl net.ipv4.ip_forward=1 ip netns exec root iptables -t nat -A POSTROUTING -s 192.168.0.2 -o ${DEV} -j MASQUERADE mount -t cifs ${CIFS_PATH} ${CIFS_MNT} -o vers=3.0,sec=ntlmssp,credentials=${CIFS_CRED},rsize=65536,wsize=65536,cache=none,echo_interval=1 touch ${CIFS_MNT}/a.txt ip netns exec root iptables -t nat -D POSTROUTING -s 192.168.0.2 -o ${DEV} -j MASQUERADE " umount ${CIFS_MNT} Fixes: 340cea84f691 ("cifs: open files should not hold ref on superblock") Signed-off-by: Jian Zhang Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/client/connect.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c index dcde25da468df..cbeb5637eeb92 100644 --- a/fs/smb/client/connect.c +++ b/fs/smb/client/connect.c @@ -3996,6 +3996,9 @@ cifs_umount(struct cifs_sb_info *cifs_sb) } spin_unlock(&cifs_sb->tlink_tree_lock); + flush_workqueue(serverclose_wq); + flush_workqueue(fileinfo_put_wq); + kfree(cifs_sb->prepath); call_rcu(&cifs_sb->rcu, delayed_free); } From 2bc610c9db5d0df660a726d068594d2db8977792 Mon Sep 17 00:00:00 2001 From: Huiwen He Date: Mon, 8 Jun 2026 23:57:29 +0800 Subject: [PATCH 1453/2758] smb/client: always return a value for FS_IOC_GETFLAGS [ Upstream commit 7acbaa16b99edaf8ef432229d4b7a6f3b666767d ] Currently, repeated lsattr calls on a regular CIFS file without the compressed attribute may show random flags: $ touch test.bin $ lsattr test.bin s-S-ia-A-EjI---------m test.bin $ lsattr test.bin ------d-cEjI---------m test.bin The lsattr reproducer depends on the previous contents of its userspace buffer, so it may not reproduce on every setup. A deterministic reproducer is to initialize the ioctl argument before FS_IOC_GETFLAGS on a file without the compressed attribute: int flags = 0x7fffffff; ioctl(fd, FS_IOC_GETFLAGS, &flags); On an affected kernel, flags remains 0x7fffffff. With the fix, it is set to 0. This happens because when the cached inode does not have the compressed bit set, the CIFS fallback path in FS_IOC_GETFLAGS returns success without calling put_user() to write the zero flags value into the user buffer. As a result, the caller observes stale contents from its own buffer. Fix this by always writing the visible flags value back to the user buffer before returning success, even when the value is zero. Fixes: 64a5cfa6db94 ("Allow setting per-file compression via SMB2/3") Signed-off-by: Huiwen He Reviewed-by: ChenXiaoSong Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/client/ioctl.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/fs/smb/client/ioctl.c b/fs/smb/client/ioctl.c index 17408bb8ab65b..746d70091f3d1 100644 --- a/fs/smb/client/ioctl.c +++ b/fs/smb/client/ioctl.c @@ -392,13 +392,11 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) } #endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ #endif /* CONFIG_CIFS_POSIX */ - rc = 0; - if (CIFS_I(inode)->cifsAttrs & ATTR_COMPRESSED) { - /* add in the compressed bit */ - ExtAttrBits = FS_COMPR_FL; - rc = put_user(ExtAttrBits & FS_FL_USER_VISIBLE, - (int __user *)arg); - } + if (CIFS_I(inode)->cifsAttrs & FILE_ATTRIBUTE_COMPRESSED) + ExtAttrBits |= FS_COMPR_FL; + + rc = put_user(ExtAttrBits & FS_FL_USER_VISIBLE, + (int __user *)arg); break; case FS_IOC_SETFLAGS: if (pSMBFile == NULL) From 590d696f846a60ee0c2ff2618fcbbe0e6e7b72b8 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Mon, 1 Jun 2026 19:46:26 -0700 Subject: [PATCH 1454/2758] btrfs: Drop WQ_PERCPU from ordered_flags in btrfs_init_workqueues() [ Upstream commit c7703f05d85f71153f5e241184397bc34da305e3 ] After commit 21c05ca88a54 ("workqueue: Add warnings and ensure one among WQ_PERCPU or WQ_UNBOUND is present"), there is a warning from the btrfs-qgroup-rescan workqueue at run time: workqueue: btrfs-qgroup-rescan uses both WQ_PERCPU and WQ_UNBOUND. Dropped WQ_PERCPU, keeping WQ_UNBOUND. WQ_PERCPU is included in ordered_flags after commit 69635d7f4b34 ("fs: WQ_PERCPU added to alloc_workqueue users") and WQ_UNBOUND is set in alloc_ordered_workqueue(), which btrfs_alloc_ordered_workqueue() calls. Drop WQ_PERCPU from ordered_flags, as alloc_ordered_workqueue() notes that only WQ_FREEZABLE and WQ_MEM_RECLAIM are meaningful. Fixes: 69635d7f4b34 ("fs: WQ_PERCPU added to alloc_workqueue users") Fixes: 21c05ca88a54 ("workqueue: Add warnings and ensure one among WQ_PERCPU or WQ_UNBOUND is present") Signed-off-by: Nathan Chancellor Reviewed-by: Breno Leitao Acked-by: Marco Crivellari Acked-by: David Sterba Signed-off-by: Tejun Heo Signed-off-by: Sasha Levin --- fs/btrfs/disk-io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index c0a30bb213d7a..ab92b35fa3cc1 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1928,7 +1928,7 @@ static int btrfs_init_workqueues(struct btrfs_fs_info *fs_info) { u32 max_active = fs_info->thread_pool_size; unsigned int flags = WQ_MEM_RECLAIM | WQ_FREEZABLE | WQ_UNBOUND; - unsigned int ordered_flags = WQ_MEM_RECLAIM | WQ_FREEZABLE | WQ_PERCPU; + unsigned int ordered_flags = WQ_MEM_RECLAIM | WQ_FREEZABLE; fs_info->workers = btrfs_alloc_workqueue(fs_info, "worker", flags, max_active, 16); From c6d51ad36490013ee9df94a372d3bf794bd304d1 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 11 Jun 2026 13:42:24 +0200 Subject: [PATCH 1455/2758] bpf: Guard __get_user acesss with access_ok for uprobe_multi data [ Upstream commit 4d87a251d45b4a95eb4c0abcfab809c9f231258a ] As reported by sashiko [1] we need to use access_ok to check the user space data bounds before we use __get-user to get it. [1] https://lore.kernel.org/bpf/20260610145235.CB1441F00893@smtp.kernel.org/ Fixes: 0b779b61f651 ("bpf: Add cookies support for uprobe_multi link") Fixes: 89ae89f53d20 ("bpf: Add multi uprobe link") Signed-off-by: Jiri Olsa Link: https://lore.kernel.org/r/20260611114230.950379-2-jolsa@kernel.org Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- kernel/trace/bpf_trace.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index a02bd258677ee..a4de502950c4b 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -3180,6 +3180,7 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr unsigned long __user *uoffsets; u64 __user *ucookies; void __user *upath; + unsigned long size; u32 flags, cnt, i; struct path path; char *name; @@ -3217,6 +3218,16 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr uref_ctr_offsets = u64_to_user_ptr(attr->link_create.uprobe_multi.ref_ctr_offsets); ucookies = u64_to_user_ptr(attr->link_create.uprobe_multi.cookies); + /* + * All uoffsets/uref_ctr_offsets/ucookies arrays have the same value + * size, we need to check their address range is safe for __get_user + * calls. + */ + size = sizeof(*uoffsets) * cnt; + if (!access_ok(uoffsets, size) || !access_ok(uref_ctr_offsets, size) || + !access_ok(ucookies, size)) + return -EFAULT; + name = strndup_user(upath, PATH_MAX); if (IS_ERR(name)) { err = PTR_ERR(name); From 85100de4f473173e96645d5a6c00516fab61e379 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 11 Jun 2026 13:42:30 +0200 Subject: [PATCH 1456/2758] selftests/bpf: Fix typo in verify_umulti_link_info [ Upstream commit df29003c55115737a8fb4f8a60c6c2bba4c4a484 ] We verify info.uprobe_multi.flags against wrong kprobe-multi flag (BPF_F_KPROBE_MULTI_RETURN). It's the same value as the correct flag (BPF_F_UPROBE_MULTI_RETURN), so there's not functional change. Fixes: 147c69307bcf ("selftests/bpf: Add link_info test for uprobe_multi link") Signed-off-by: Jiri Olsa Link: https://lore.kernel.org/r/20260611114230.950379-8-jolsa@kernel.org Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- tools/testing/selftests/bpf/prog_tests/fill_link_info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/prog_tests/fill_link_info.c b/tools/testing/selftests/bpf/prog_tests/fill_link_info.c index e401146207510..f589eefbf9fbd 100644 --- a/tools/testing/selftests/bpf/prog_tests/fill_link_info.c +++ b/tools/testing/selftests/bpf/prog_tests/fill_link_info.c @@ -469,7 +469,7 @@ verify_umulti_link_info(int fd, bool retprobe, __u64 *offsets, ASSERT_EQ(info.uprobe_multi.pid, getpid(), "info.uprobe_multi.pid"); ASSERT_EQ(info.uprobe_multi.count, 3, "info.uprobe_multi.count"); - ASSERT_EQ(info.uprobe_multi.flags & BPF_F_KPROBE_MULTI_RETURN, + ASSERT_EQ(info.uprobe_multi.flags & BPF_F_UPROBE_MULTI_RETURN, retprobe, "info.uprobe_multi.flags.retprobe"); ASSERT_EQ(info.uprobe_multi.path_size, strlen(path) + 1, "info.uprobe_multi.path_size"); ASSERT_STREQ(path_buf, path, "info.uprobe_multi.path"); From e68343ee3c131cd84ffec5ae29c5d91127b8bb67 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Tue, 2 Jun 2026 15:47:15 +0100 Subject: [PATCH 1457/2758] selftests/bpf: Initialize operation name before use [ Upstream commit 55ffbe8a15b1254f44d56952fb425a10e3f15c31 ] ASAN reports stack-buffer-overflow due to the uninitialized op_name. Initialize it to fix the issue. Fixes: 054b6c7866c7 ("selftests/bpf: Add verifier log tests for BPF_BTF_LOAD command") Signed-off-by: Leo Yan Acked-by: Ihor Solodrai Link: https://lore.kernel.org/r/20260602-tools_build_fix_zero_init_bpf_only-v2-6-c76e5250ea1c@arm.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- tools/testing/selftests/bpf/prog_tests/verifier_log.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/bpf/prog_tests/verifier_log.c b/tools/testing/selftests/bpf/prog_tests/verifier_log.c index c01c0114af1b7..4542bb586d723 100644 --- a/tools/testing/selftests/bpf/prog_tests/verifier_log.c +++ b/tools/testing/selftests/bpf/prog_tests/verifier_log.c @@ -317,6 +317,7 @@ static void verif_btf_log_subtest(bool bad_btf) res = load_btf(&opts, true); ASSERT_EQ(res, -ENOSPC, "half_log_fd"); ASSERT_EQ(strlen(logs.buf), 24, "log_fixed_25"); + strscpy(op_name, "log_fixed", sizeof(op_name)); ASSERT_STRNEQ(logs.buf, logs.reference, 24, op_name); /* validate rolling verifier log logic: try all variations of log buf From c12e3c9e5224a51d14914467d846315f5da0b241 Mon Sep 17 00:00:00 2001 From: Leon Hwang Date: Sun, 14 Jun 2026 00:24:42 +0800 Subject: [PATCH 1458/2758] bpf: Fix bpf_get/setsockopt to tos for ipv4-mapped ipv6 socket [ Upstream commit ca0f587c029afa66227f7b932450b1c417403394 ] When TCP over IPv4 via INET6 API, bpf_get/setsockopt with ipv4 will fail, because sk->sk_family is AF_INET6. With ipv6 will success, not take effect, because inet_csk(sk)->icsk_af_ops is ipv6_mapped and use ip_queue_xmit, inet_sk(sk)->tos. To relax this restriction, allow getting/setting tos for those possible ipv4-mapped ipv6 sockets. Fixes: ee7f1e1302f5 ("bpf: Change bpf_setsockopt(SOL_IP) to reuse do_ip_setsockopt()") Signed-off-by: Feng Zhou Signed-off-by: Leon Hwang Link: https://lore.kernel.org/r/20260613162443.60515-2-leon.hwang@linux.dev Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- net/core/filter.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/net/core/filter.c b/net/core/filter.c index 40037413dd4ec..83fcc64afa206 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -5571,11 +5571,24 @@ static int sol_tcp_sockopt(struct sock *sk, int optname, KERNEL_SOCKPTR(optval), *optlen); } +static bool sk_allows_sol_ip_sockopt(struct sock *sk) +{ + switch (sk->sk_family) { + case AF_INET: + return true; + case AF_INET6: + /* Allow getting/setting sockopt for possible ipv4-mapped ipv6 socket. */ + return sk->sk_type != SOCK_RAW && !ipv6_only_sock(sk); + default: + return false; + } +} + static int sol_ip_sockopt(struct sock *sk, int optname, char *optval, int *optlen, bool getopt) { - if (sk->sk_family != AF_INET) + if (!sk_allows_sol_ip_sockopt(sk)) return -EINVAL; switch (optname) { From 478c7f68ef254a59de812a12a47b16d853685abf Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 11 Feb 2026 15:11:28 -0500 Subject: [PATCH 1459/2758] udf: fix nls leak on udf_fill_super() failure [ Upstream commit 462bdd08fbdf41db223c6117d907c8fd68d666ea ] On all failure exits that go to error_out there we have already moved the nls reference from uopt->nls_map to sbi->s_nls_map, leaving NULL behind. Fixes: c4e89cc674ac ("udf: convert to new mount API") Acked-by: Jan Kara Signed-off-by: Al Viro Signed-off-by: Sasha Levin --- fs/udf/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/udf/super.c b/fs/udf/super.c index 1b5282790de6f..f5328c0084dca 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -2330,7 +2330,7 @@ static int udf_fill_super(struct super_block *sb, struct fs_context *fc) error_out: iput(sbi->s_vat_inode); - unload_nls(uopt->nls_map); + unload_nls(sbi->s_nls_map); if (lvid_open) udf_close_lvid(sb); brelse(sbi->s_lvid_bh); From bd004716ba75fed6d185795c85cdc92540ebeaab Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Mon, 15 Jun 2026 10:19:54 +0800 Subject: [PATCH 1460/2758] bpf, sockmap: reject overflowing copy + len in bpf_msg_push_data() [ Upstream commit 0c0a8ed85349dae298712d79cb276acfeb794d82 ] When the scatterlist ring is full or nearly full, bpf_msg_push_data() enters a copy fallback path and computes copy + len for the page allocation size. Since len comes from BPF with arg3_type = ARG_ANYTHING and both are u32, a crafted len can wrap the sum to a small value, causing an undersized allocation followed by an out-of-bounds memcpy. BUG: unable to handle page fault for address: ffffed104089a402 Oops: Oops: 0000 [#1] SMP KASAN NOPTI Call Trace: __asan_memcpy (mm/kasan/shadow.c:105) bpf_msg_push_data (net/core/filter.c:2852 net/core/filter.c:2788) bpf_prog_9ed8b5711920a7d7+0x2e/0x36 sk_psock_msg_verdict (net/core/skmsg.c:934) tcp_bpf_sendmsg (net/ipv4/tcp_bpf.c:421 net/ipv4/tcp_bpf.c:584) __sys_sendto (net/socket.c:2206) do_syscall_64 (arch/x86/entry/syscall_64.c:94) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:130) Add an overflow check before the allocation. Link: https://lore.kernel.org/all/20260424155913.A19FDC19425@smtp.kernel.org Fixes: 6fff607e2f14 ("bpf: sk_msg program helper bpf_msg_push_data") Tested-by: Xiang Mei Tested-by: Xinyu Ma Reviewed-by: Jiayuan Chen Reviewed-by: Emil Tsalapatis Reviewed-by: Kuniyuki Iwashima Signed-off-by: Weiming Shi Signed-off-by: Jiayuan Chen Link: https://lore.kernel.org/r/20260615021959.140010-2-jiayuan.chen@linux.dev Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- net/core/filter.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/core/filter.c b/net/core/filter.c index 83fcc64afa206..f159a21e92588 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2834,6 +2834,9 @@ BPF_CALL_4(bpf_msg_push_data, struct sk_msg *, msg, u32, start, if (!space || (space == 1 && start != offset)) copy = msg->sg.data[i].length; + if (unlikely(copy + len < copy)) + return -EINVAL; + page = alloc_pages(__GFP_NOWARN | GFP_ATOMIC | __GFP_COMP, get_order(copy + len)); if (unlikely(!page)) From 39d44ed6904bfa9a1c6d0538672dd50c7b85520e Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Mon, 15 Jun 2026 10:19:57 +0800 Subject: [PATCH 1461/2758] sockmap: Fix use-after-free in udp_bpf_recvmsg() [ Upstream commit c010995b29c8939c6aa69e3cb26f8dbee163d156 ] syzbot reported use-after-free of struct sk_msg in sk_msg_recvmsg(). [0] sk_msg_recvmsg() peeks sk_msg from psock->ingress_msg under a lock, but its processing is lockless. Thus, sk_msg_recvmsg() must be serialised by callers, otherwise multiple threads could touch the same sk_msg. For example, TCP uses lock_sock(), and AF_UNIX uses unix_sk(sk)->iolock. Initially, udp_bpf_recvmsg() had used lock_sock(), but the cited commit removed it. Let's serialise sk_msg_recvmsg() with lock_sock() in udp_bpf_recvmsg(). Note that holding spin_lock_bh(&sk->sk_receive_queue.lock) is not an option due to copy_page_to_iter() in sk_msg_recvmsg(). [0]: BUG: KASAN: slab-use-after-free in sk_msg_recvmsg+0xb54/0xc30 net/core/skmsg.c:428 Read of size 4 at addr ffff88814cdcf000 by task syz.0.24/6020 CPU: 1 UID: 0 PID: 6020 Comm: syz.0.24 Not tainted syzkaller #0 PREEMPT(full) Hardware name: Google Compute Engine/Google Compute Engine, BIOS Google 01/13/2026 Call Trace: dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:378 [inline] print_report+0xba/0x230 mm/kasan/report.c:482 kasan_report+0x117/0x150 mm/kasan/report.c:595 sk_msg_recvmsg+0xb54/0xc30 net/core/skmsg.c:428 udp_bpf_recvmsg+0x4bd/0xe00 net/ipv4/udp_bpf.c:84 inet_recvmsg+0x260/0x270 net/ipv4/af_inet.c:891 sock_recvmsg_nosec net/socket.c:1078 [inline] sock_recvmsg+0x1a8/0x270 net/socket.c:1100 ____sys_recvmsg+0x1e6/0x4a0 net/socket.c:2812 ___sys_recvmsg+0x215/0x590 net/socket.c:2854 do_recvmmsg+0x334/0x800 net/socket.c:2949 __sys_recvmmsg net/socket.c:3023 [inline] __do_sys_recvmmsg net/socket.c:3046 [inline] __se_sys_recvmmsg net/socket.c:3039 [inline] __x64_sys_recvmmsg+0x198/0x250 net/socket.c:3039 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0xe2/0xf80 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7fb319f9aeb9 Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 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 e8 ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007fb31ad97028 EFLAGS: 00000246 ORIG_RAX: 000000000000012b RAX: ffffffffffffffda RBX: 00007fb31a216090 RCX: 00007fb319f9aeb9 RDX: 0000000000000001 RSI: 0000200000000400 RDI: 0000000000000004 RBP: 00007fb31a008c1f R08: 0000000000000000 R09: 0000000000000000 R10: 0000000040000021 R11: 0000000000000246 R12: 0000000000000000 R13: 00007fb31a216128 R14: 00007fb31a216090 R15: 00007ffe21dd0a98 Allocated by task 6019: kasan_save_stack mm/kasan/common.c:57 [inline] kasan_save_track+0x3e/0x80 mm/kasan/common.c:78 poison_kmalloc_redzone mm/kasan/common.c:398 [inline] __kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415 kasan_kmalloc include/linux/kasan.h:263 [inline] __kmalloc_cache_noprof+0x3d1/0x6e0 mm/slub.c:5780 kmalloc_noprof include/linux/slab.h:957 [inline] kzalloc_noprof include/linux/slab.h:1094 [inline] alloc_sk_msg net/core/skmsg.c:510 [inline] sk_psock_skb_ingress_self+0x60/0x350 net/core/skmsg.c:612 sk_psock_verdict_apply net/core/skmsg.c:1038 [inline] sk_psock_verdict_recv+0x7d9/0x8d0 net/core/skmsg.c:1236 udp_read_skb+0x73e/0x7e0 net/ipv4/udp.c:2045 sk_psock_verdict_data_ready+0x12d/0x550 net/core/skmsg.c:1257 __udp_enqueue_schedule_skb+0xc54/0x10b0 net/ipv4/udp.c:1789 __udp_queue_rcv_skb net/ipv4/udp.c:2346 [inline] udp_queue_rcv_one_skb+0xac5/0x19c0 net/ipv4/udp.c:2475 __udp4_lib_mcast_deliver+0xc06/0xcf0 net/ipv4/udp.c:2585 __udp4_lib_rcv+0x10f6/0x2620 net/ipv4/udp.c:2724 ip_protocol_deliver_rcu+0x282/0x440 net/ipv4/ip_input.c:207 ip_local_deliver_finish+0x3bb/0x6f0 net/ipv4/ip_input.c:241 NF_HOOK+0x336/0x3c0 include/linux/netfilter.h:318 dst_input include/net/dst.h:474 [inline] ip_sublist_rcv_finish+0x221/0x2a0 net/ipv4/ip_input.c:584 ip_list_rcv_finish net/ipv4/ip_input.c:628 [inline] ip_sublist_rcv+0x5c6/0xa70 net/ipv4/ip_input.c:644 ip_list_rcv+0x3f1/0x450 net/ipv4/ip_input.c:678 __netif_receive_skb_list_ptype net/core/dev.c:6195 [inline] __netif_receive_skb_list_core+0x7e5/0x810 net/core/dev.c:6242 __netif_receive_skb_list net/core/dev.c:6294 [inline] netif_receive_skb_list_internal+0x995/0xcf0 net/core/dev.c:6385 netif_receive_skb_list+0x54/0x410 net/core/dev.c:6437 xdp_recv_frames net/bpf/test_run.c:269 [inline] xdp_test_run_batch net/bpf/test_run.c:350 [inline] bpf_test_run_xdp_live+0x1946/0x1cf0 net/bpf/test_run.c:379 bpf_prog_test_run_xdp+0x81c/0x1160 net/bpf/test_run.c:1396 bpf_prog_test_run+0x2c7/0x340 kernel/bpf/syscall.c:4703 __sys_bpf+0x5cb/0x920 kernel/bpf/syscall.c:6182 __do_sys_bpf kernel/bpf/syscall.c:6274 [inline] __se_sys_bpf kernel/bpf/syscall.c:6272 [inline] __x64_sys_bpf+0x7c/0x90 kernel/bpf/syscall.c:6272 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0xe2/0xf80 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 6021: kasan_save_stack mm/kasan/common.c:57 [inline] kasan_save_track+0x3e/0x80 mm/kasan/common.c:78 kasan_save_free_info+0x46/0x50 mm/kasan/generic.c:584 poison_slab_object mm/kasan/common.c:253 [inline] __kasan_slab_free+0x5c/0x80 mm/kasan/common.c:285 kasan_slab_free include/linux/kasan.h:235 [inline] slab_free_hook mm/slub.c:2540 [inline] slab_free mm/slub.c:6674 [inline] kfree+0x1be/0x650 mm/slub.c:6882 kfree_sk_msg include/linux/skmsg.h:385 [inline] sk_msg_recvmsg+0xaa8/0xc30 net/core/skmsg.c:483 udp_bpf_recvmsg+0x4bd/0xe00 net/ipv4/udp_bpf.c:84 inet_recvmsg+0x260/0x270 net/ipv4/af_inet.c:891 sock_recvmsg_nosec net/socket.c:1078 [inline] sock_recvmsg+0x1a8/0x270 net/socket.c:1100 ____sys_recvmsg+0x1e6/0x4a0 net/socket.c:2812 ___sys_recvmsg+0x215/0x590 net/socket.c:2854 do_recvmmsg+0x334/0x800 net/socket.c:2949 __sys_recvmmsg net/socket.c:3023 [inline] __do_sys_recvmmsg net/socket.c:3046 [inline] __se_sys_recvmmsg net/socket.c:3039 [inline] __x64_sys_recvmmsg+0x198/0x250 net/socket.c:3039 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0xe2/0xf80 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f Fixes: 9f2470fbc4cb ("skmsg: Improve udp_bpf_recvmsg() accuracy") Reported-by: syzbot+9307c991a6d07ce6e6d8@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/69922ac9.a70a0220.2c38d7.00e0.GAE@google.com/ Reviewed-by: Jiayuan Chen Reviewed-by: Jakub Sitnicki Reviewed-by: Emil Tsalapatis Signed-off-by: Kuniyuki Iwashima Signed-off-by: Jiayuan Chen Link: https://lore.kernel.org/r/20260615021959.140010-5-jiayuan.chen@linux.dev Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- net/ipv4/udp_bpf.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/net/ipv4/udp_bpf.c b/net/ipv4/udp_bpf.c index 9f33b07b14813..ad57c4c9eaab6 100644 --- a/net/ipv4/udp_bpf.c +++ b/net/ipv4/udp_bpf.c @@ -50,7 +50,9 @@ static int udp_msg_wait_data(struct sock *sk, struct sk_psock *psock, sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk); ret = udp_msg_has_data(sk, psock); if (!ret) { + release_sock(sk); wait_woken(&wait, TASK_INTERRUPTIBLE, timeo); + lock_sock(sk); ret = udp_msg_has_data(sk, psock); } sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk); @@ -79,6 +81,7 @@ static int udp_bpf_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, goto out; } + lock_sock(sk); msg_bytes_ready: copied = sk_msg_recvmsg(sk, psock, msg, len, flags); if (!copied) { @@ -90,11 +93,17 @@ static int udp_bpf_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, if (data) { if (psock_has_data(psock)) goto msg_bytes_ready; + + release_sock(sk); + ret = sk_udp_recvmsg(sk, msg, len, flags); goto out; } copied = -EAGAIN; } + + release_sock(sk); + ret = copied; out: sk_psock_put(sk, psock); From e09f7bd7273928b4089e6b71f8e992f2b356ca1b Mon Sep 17 00:00:00 2001 From: Sechang Lim Date: Mon, 15 Jun 2026 10:19:58 +0800 Subject: [PATCH 1462/2758] bpf, sockmap: fix integer overflow in bpf_msg_pop_data() bounds check [ Upstream commit a48802fb2cd2d1e23651989f8ff4d15e9d5dad54 ] start and len are u32, so u64 last = start + len; evaluates start + len in 32-bit and wraps before storing it in last. The bounds check if (start >= offset + l || last > msg->sg.size) return -EINVAL; can then be passed with an out-of-range start/len, after which the pop loop runs off the end of the scatterlist and sk_msg_shift_left() calls put_page() on the empty msg->sg.end slot: Oops: general protection fault, probably for non-canonical address 0xdffffc0000000001: 0000 [#1] SMP KASAN PTI KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f] RIP: 0010:sk_msg_shift_left net/core/filter.c:2957 [inline] RIP: 0010:____bpf_msg_pop_data net/core/filter.c:3103 [inline] RIP: 0010:bpf_msg_pop_data+0x753/0x1a10 net/core/filter.c:2984 Call Trace: bpf_prog_4cc92c278f4d5d56+0x1b1/0x1e8 bpf_prog_run_pin_on_cpu+0x107/0x320 include/linux/filter.h:746 sk_psock_msg_verdict+0x357/0x7f0 net/core/skmsg.c:934 tcp_bpf_send_verdict net/ipv4/tcp_bpf.c:420 [inline] tcp_bpf_sendmsg+0x766/0x1ae0 net/ipv4/tcp_bpf.c:583 __sock_sendmsg+0x153/0x1c0 net/socket.c:802 __sys_sendto+0x326/0x430 net/socket.c:2265 __x64_sys_sendto+0xe3/0x100 net/socket.c:2268 do_syscall_64+0x14c/0x480 entry_SYSCALL_64_after_hwframe+0x77/0x7f Widen the addition with a (u64) cast so the bound is evaluated in 64-bit and a len near U32_MAX no longer wraps below msg->sg.size. While here, change pop from int to u32. It counts bytes against the unsigned scatterlist lengths and can never be negative, so the signed type only invites sign-confusion in the pop loop. Fixes: 7246d8ed4dcc ("bpf: helper to pop data from messages") Reviewed-by: Jiayuan Chen Reviewed-by: Emil Tsalapatis Reviewed-by: Kuniyuki Iwashima Signed-off-by: Sechang Lim Signed-off-by: Jiayuan Chen Link: https://lore.kernel.org/r/20260615021959.140010-6-jiayuan.chen@linux.dev Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- net/core/filter.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/core/filter.c b/net/core/filter.c index f159a21e92588..c140e2ed5d291 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -3000,8 +3000,8 @@ BPF_CALL_4(bpf_msg_pop_data, struct sk_msg *, msg, u32, start, u32, len, u64, flags) { u32 i = 0, l = 0, space, offset = 0; - u64 last = start + len; - int pop; + u64 last = (u64)start + len; + u32 pop; if (unlikely(flags)) return -EINVAL; From 038f068cced8e9c49e8c419f3112d909ad366a3a Mon Sep 17 00:00:00 2001 From: Yadan Fan Date: Mon, 25 May 2026 12:04:36 +0800 Subject: [PATCH 1463/2758] MIPS: mm: Fix out-of-bounds write in maar_res_walk() [ Upstream commit 1b001b16bc88f3f7817e228acfd91ee01bdcfcce ] maar_res_walk() uses wi->num_cfg as the index into the fixed-size wi->cfg array, but checks whether the array is full only after it has filled the selected entry. If walk_system_ram_range() reports more than 16 memory ranges, the overflow call writes one struct maar_config past the end of the array before WARN_ON() prevents num_cfg from advancing. Move the full-array check before taking the array slot and return non-zero when the scratch array is full, so walk_system_ram_range() terminates the walk instead of invoking the callback for further ranges. Fixes: a5718fe8f70f ("MIPS: mm: Drop boot_mem_map") Signed-off-by: Yadan Fan Signed-off-by: Thomas Bogendoerfer Signed-off-by: Sasha Levin --- arch/mips/mm/init.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c index 55b25e85122a3..1c07ca84ee214 100644 --- a/arch/mips/mm/init.c +++ b/arch/mips/mm/init.c @@ -272,9 +272,15 @@ static int maar_res_walk(unsigned long start_pfn, unsigned long nr_pages, void *data) { struct maar_walk_info *wi = data; - struct maar_config *cfg = &wi->cfg[wi->num_cfg]; + struct maar_config *cfg; unsigned int maar_align; + /* Ensure we don't overflow the cfg array */ + if (WARN_ON(wi->num_cfg >= ARRAY_SIZE(wi->cfg))) + return -1; + + cfg = &wi->cfg[wi->num_cfg]; + /* MAAR registers hold physical addresses right shifted by 4 bits */ maar_align = BIT(MIPS_MAAR_ADDR_SHIFT + 4); @@ -283,9 +289,7 @@ static int maar_res_walk(unsigned long start_pfn, unsigned long nr_pages, cfg->upper = ALIGN_DOWN(PFN_PHYS(start_pfn + nr_pages), maar_align) - 1; cfg->attrs = MIPS_MAAR_S; - /* Ensure we don't overflow the cfg array */ - if (!WARN_ON(wi->num_cfg >= ARRAY_SIZE(wi->cfg))) - wi->num_cfg++; + wi->num_cfg++; return 0; } From b504fd953664c3f4d176ae8cb594adb3fda9d577 Mon Sep 17 00:00:00 2001 From: Aboorva Devarajan Date: Fri, 5 Jun 2026 13:59:10 +0530 Subject: [PATCH 1464/2758] powerpc/perf: fix preempt count underflow in fsl_emb_pmu_del [ Upstream commit 81e3a86030462824a67d697739cf3f387f4ba350 ] fsl_emb_pmu_del() unconditionally calls put_cpu_var(cpu_hw_events) at the 'out:' label, but only calls the matching get_cpu_var() after the 'i < 0' early-return check. When event->hw.idx is negative the function jumps to 'out:' without having taken get_cpu_var(), and the trailing put_cpu_var() then issues an unmatched preempt_enable(), underflowing preempt_count. On a CONFIG_PREEMPT=y kernel preempt_count would underflow and eventually present as a 'scheduling while atomic' BUG. Move put_cpu_var() to pair with get_cpu_var() so the percpu access is correctly bracketed and the 'out:' label only handles perf_pmu_enable. Fixes: a11106544f33 ("powerpc/perf: e500 support") Reviewed-by: Shrikanth Hegde Signed-off-by: Aboorva Devarajan Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260605082912.305100-2-aboorvad@linux.ibm.com Signed-off-by: Sasha Levin --- arch/powerpc/perf/core-fsl-emb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/perf/core-fsl-emb.c b/arch/powerpc/perf/core-fsl-emb.c index 7120ab20cbfec..02b5dd74c187a 100644 --- a/arch/powerpc/perf/core-fsl-emb.c +++ b/arch/powerpc/perf/core-fsl-emb.c @@ -366,9 +366,10 @@ static void fsl_emb_pmu_del(struct perf_event *event, int flags) cpuhw->n_events--; + put_cpu_var(cpu_hw_events); + out: perf_pmu_enable(event->pmu); - put_cpu_var(cpu_hw_events); } static void fsl_emb_pmu_start(struct perf_event *event, int ef_flags) From e4e69cee0b01e6aa1a8794154136698b8ed5bb28 Mon Sep 17 00:00:00 2001 From: Aboorva Devarajan Date: Fri, 5 Jun 2026 13:59:11 +0530 Subject: [PATCH 1465/2758] powerpc/powernv: fix preempt count leak in pnv_kexec_wait_secondaries_down [ Upstream commit 0ecd26e93e698c8327521910fc6296f5b84a4b92 ] pnv_kexec_wait_secondaries_down() calls get_cpu() to obtain the current CPU id but never calls the matching put_cpu(), leaking one preempt_disable() nesting level on every invocation. In practice the imbalance does not trigger a visible splat because the kexec teardown path is a one-way trip: IRQs are already disabled, no schedule() occurs after the leak, and default_machine_kexec() overwrites preempt_count with HARDIRQ_OFFSET before jumping into kexec_sequence() which never returns. However the bookkeeping is still wrong. The function only needs the current CPU id, and this path runs with interrupts disabled and the CPU pinned, so the preempt_disable() side-effect of get_cpu() is unnecessary. Replace it with raw_smp_processor_id(). Fixes: 298b34d7d578 ("powerpc/powernv: Fix kexec races going back to OPAL") Signed-off-by: Aboorva Devarajan Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260605082912.305100-3-aboorvad@linux.ibm.com Signed-off-by: Sasha Levin --- arch/powerpc/platforms/powernv/setup.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c index 4dbb47ddbdcc4..06ed5e2aa2658 100644 --- a/arch/powerpc/platforms/powernv/setup.c +++ b/arch/powerpc/platforms/powernv/setup.c @@ -396,7 +396,8 @@ static void pnv_kexec_wait_secondaries_down(void) { int my_cpu, i, notified = -1; - my_cpu = get_cpu(); + /* Called with interrupts disabled, so the CPU is pinned. */ + my_cpu = raw_smp_processor_id(); for_each_online_cpu(i) { uint8_t status; From c893bfb0d696d9f9f726d1b8f16d50cc78bfc9e5 Mon Sep 17 00:00:00 2001 From: Aboorva Devarajan Date: Fri, 5 Jun 2026 13:59:12 +0530 Subject: [PATCH 1466/2758] powerpc/kexec: fix double get_cpu() imbalance in kexec_prepare_cpus [ Upstream commit 5c86f1c1f972761a04bf22f4c0618d1aa714185b ] kexec_prepare_cpus_wait() calls get_cpu() internally to obtain the current CPU id. kexec_prepare_cpus() calls kexec_prepare_cpus_wait() twice -- once for KEXEC_STATE_IRQS_OFF and once for KEXEC_STATE_REAL_MODE -- but only issues a single put_cpu() at the end, leaving preempt_count elevated by one extra nesting level. In practice the imbalance does not trigger a 'scheduling while atomic' splat because the kexec path is a one-way trip: IRQs are already disabled, no schedule() occurs after the leak, and default_machine_kexec() overwrites preempt_count with HARDIRQ_OFFSET before jumping into kexec_sequence() which never returns. However the bookkeeping is still wrong. kexec_prepare_cpus() calls local_irq_disable()/hard_irq_disable() before invoking kexec_prepare_cpus_wait(), so the CPU is already pinned and the get_cpu()/put_cpu() preempt_disable() bracketing is unnecessary. Only the current CPU id is needed, so replace get_cpu() with raw_smp_processor_id() and drop the now-unneeded put_cpu(). Fixes: 1fc711f7ffb0 ("powerpc/kexec: Fix race in kexec shutdown") Signed-off-by: Aboorva Devarajan Reviewed-by: Shrikanth Hegde Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260605082912.305100-4-aboorvad@linux.ibm.com Signed-off-by: Sasha Levin --- arch/powerpc/kexec/core_64.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/powerpc/kexec/core_64.c b/arch/powerpc/kexec/core_64.c index 825ab8a88f18e..58c13a59b93b5 100644 --- a/arch/powerpc/kexec/core_64.c +++ b/arch/powerpc/kexec/core_64.c @@ -169,7 +169,7 @@ static void kexec_prepare_cpus_wait(int wait_state) int my_cpu, i, notified=-1; hw_breakpoint_disable(); - my_cpu = get_cpu(); + my_cpu = raw_smp_processor_id(); /* Make sure each CPU has at least made it to the state we need. * * FIXME: There is a (slim) chance of a problem if not all of the CPUs @@ -267,8 +267,6 @@ static void kexec_prepare_cpus(void) /* after we tell the others to go down */ if (ppc_md.kexec_cpu_down) ppc_md.kexec_cpu_down(0, 0); - - put_cpu(); } #else /* ! SMP */ From d2fb2ef76008e1aa16326521d43ef65504d6f042 Mon Sep 17 00:00:00 2001 From: Gui-Dong Han Date: Fri, 29 May 2026 11:34:06 +0800 Subject: [PATCH 1467/2758] KEYS: Use acquire when reading state in keyring search [ Upstream commit c1201b37f666f6466ab1fd3a381c2b7a4b7e9fee ] The negative-key race fix added release/acquire ordering for key use. Publish payload before state; read state before payload. keyring_search_iterator() still uses READ_ONCE() before match callbacks. An asymmetric match callback calls asymmetric_key_ids(), which reads key->payload.data[asym_key_ids]. Use key_read_state() there to complete that ordering. Fixes: 363b02dab09b ("KEYS: Fix race between updating and finding a negative key") Signed-off-by: Gui-Dong Han Reviewed-by: Jarkko Sakkinen Link: https://lore.kernel.org/r/20260529033406.20673-1-hanguidong02@gmail.com Signed-off-by: Jarkko Sakkinen Signed-off-by: Sasha Levin --- security/keys/keyring.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 5a9887d6b7be3..7a2ee0ded7c93 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -576,7 +576,7 @@ static int keyring_search_iterator(const void *object, void *iterator_data) struct keyring_search_context *ctx = iterator_data; const struct key *key = keyring_ptr_to_key(object); unsigned long kflags = READ_ONCE(key->flags); - short state = READ_ONCE(key->state); + short state = key_read_state(key); kenter("{%d}", key->serial); From 35e0297a93c3c34a3924eeef816c03504e3ab5c5 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 12 Jun 2026 13:59:49 +0000 Subject: [PATCH 1468/2758] tipc: fix UAF in tipc_l2_send_msg() [ Upstream commit f4c3d89fc986b0da196ddfc6cfe0ea5d5d08bec6 ] Syzbot reported a slab-use-after-free in ipvlan_hard_header() when called from tipc_l2_send_msg(). The root cause is that tipc_disable_l2_media() calls synchronize_net() while b->media_ptr is still valid. This allows concurrent RCU readers to obtain the device pointer after synchronize_net() has finished. The pointer is cleared later in bearer_disable(), but without any subsequent synchronization, allowing the device to be freed while still in use by readers. Fix this by clearing b->media_ptr in tipc_disable_l2_media() before calling synchronize_net(). This is safe to do now because the call order in bearer_disable() was reversed in 0d051bf93c06 ("tipc: make bearer packet filtering generic") to call tipc_node_delete_links() (which needs the pointer) before disable_media(). Fixes: 282b3a056225 ("tipc: send out RESET immediately when link goes down") https: //lore.kernel.org/netdev/6a2c1007.428ffe26.258b27.015d.GAE@google.com/T/#u Reported-by: syzbot+64ec81389cbad56a8c35@syzkaller.appspotmail.com Signed-off-by: Eric Dumazet Cc: Jon Maloy Reviewed-by: Tung Nguyen Link: https://patch.msgid.link/20260612135949.4010482-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/tipc/bearer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index a3bd1ef17558a..05dcd2f9e887a 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -482,6 +482,7 @@ void tipc_disable_l2_media(struct tipc_bearer *b) dev = (struct net_device *)rtnl_dereference(b->media_ptr); dev_remove_pack(&b->pt); RCU_INIT_POINTER(dev->tipc_ptr, NULL); + RCU_INIT_POINTER(b->media_ptr, NULL); synchronize_net(); dev_put(dev); } From a210791f33345aa87187f7d7a9f3b9b7f4a28e6d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 12 Jun 2026 16:25:17 +0000 Subject: [PATCH 1469/2758] tcp: ipv6: clamp default adverting MSS to avoid GSO_BY_FRAGS (0xFFFF) [ Upstream commit 2bf43d0e2e6a27d52a7d624e2d6b9116972e8a22 ] When MTU is large, ip6_default_advmss() can return IPV6_MAXPLEN (65535). This is interpreted by TCP as mss_clamp, allowing the MSS to reach 65535. However, 0xFFFF is also used as a magic value GSO_BY_FRAGS in the kernel. If a TCP packet with gso_size=0xFFFF is passed to skb_segment(), it will be mistakenly treated as GSO_BY_FRAGS, leading to a NULL pointer dereference because local TCP packets do not use frag_list. Fix this by returning min(IPV6_MAXPLEN, GSO_BY_FRAGS - 1) (65534) from ip6_default_advmss() when MTU is large. Also update the stale comment in ip6_default_advmss() which suggested that IPV6_MAXPLEN is returned to mean "any MSS". Fixes: 3953c46c3ac7 ("sk_buff: allow segmenting based on frag sizes") Reported-by: syzbot+ebdb22d461c904fc3cb2@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6a2c3193.8812e0fc.3c3fa4.0001.GAE@google.com/T/#u Signed-off-by: Eric Dumazet Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260612162517.83394-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/route.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 636f0120d7e38..3c97ba01297aa 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -3275,11 +3275,11 @@ static unsigned int ip6_default_advmss(const struct dst_entry *dst) /* * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and * corresponding MSS is IPV6_MAXPLEN - tcp_header_size. - * IPV6_MAXPLEN is also valid and means: "any MSS, - * rely only on pmtu discovery" + * Limit the default MSS to GSO_BY_FRAGS - 1 to avoid + * collision with the GSO_BY_FRAGS magic value (0xFFFF). */ if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr)) - mtu = IPV6_MAXPLEN; + mtu = min_t(unsigned int, IPV6_MAXPLEN, GSO_BY_FRAGS - 1); return mtu; } From ad262d2b96beeae434493a2041eab7e0cc155008 Mon Sep 17 00:00:00 2001 From: "Wayen.Yan" Date: Sat, 13 Jun 2026 08:23:12 +0800 Subject: [PATCH 1470/2758] net: airoha: Fix always-true condition in PPE1 queue reservation loop [ Upstream commit c66f8511a8109fa50767941b26d3623e316fde02 ] In airoha_fe_pse_ports_init(), the inner condition for PPE1 queue reservation is identical to the for-loop bound, making it always true and the else branch dead code: for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_PPE1]; q++) { if (q < pse_port_num_queues[FE_PSE_PORT_PPE1]) /* always true */ set RSV_PAGES; else set 0; /* unreachable */ } The intended behavior is to reserve pages only for the first half of the queues, matching the PPE2 implementation on line 334 which correctly uses the /2 divisor. Fix the PPE1 condition accordingly. Fixes: 23020f049327 ("net: airoha: Introduce ethernet support for EN7581 SoC") Signed-off-by: Wayen.Yan Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/6a2ca3de.ad59c0a6.147df9.2ac1@mx.google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/airoha/airoha_eth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index 329988a8400c1..7b8d0c5e262ab 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -311,7 +311,7 @@ static void airoha_fe_pse_ports_init(struct airoha_eth *eth) PSE_QUEUE_RSV_PAGES); /* PPE1 */ for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_PPE1]; q++) { - if (q < pse_port_num_queues[FE_PSE_PORT_PPE1]) + if (q < pse_port_num_queues[FE_PSE_PORT_PPE1] / 2) airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE1, q, PSE_QUEUE_RSV_PAGES); else From b478a6ffda4efd8b14f6a947d474aed60e817624 Mon Sep 17 00:00:00 2001 From: Meghana Malladi Date: Fri, 12 Jun 2026 00:27:41 +0530 Subject: [PATCH 1471/2758] net: ti: icssg-prueth: Fix AF_XDP fill ring alloc and wakeup condition [ Upstream commit dfb787f7d157f97ba91344b584d33481f572530e ] emac_rx_packet_zc() calls prueth_rx_alloc_zc() with count (frames received in the current NAPI poll) as the allocation budget. Two problems arise from this: 1. When the CPPI5 descriptor pool is exhausted (avail_desc == 0, FDQ already holds the maximum number of descriptors), count > 0 still triggers allocation attempts that all fail, spamming the kernel log with "rx push: failed to allocate descriptor" at high packet rates. 2. The XSK wakeup condition "ret < count" is wrong when avail_desc is zero: ret == 0 and count can be up to 64, so the condition is always true. This causes ~200 spurious ndo_xsk_wakeup() calls per second even when the FDQ is already full, wasting CPU cycles in repeated NAPI invocations that process zero frames. Fix both by introducing alloc_budget = min(budget, avail_desc): - When avail_desc == 0 no allocation is attempted, avoiding pool exhaustion errors. The wakeup condition "ret < alloc_budget" evaluates to 0 < 0 == false, correctly clearing the wakeup flag so the hardware IRQ re-arms NAPI without spurious kicks. - In steady state avail_desc == count <= budget, so alloc_budget == count and behaviour is unchanged. - After a dry-ring stall (count == 0, avail_desc > 0), alloc_budget > 0 causes new descriptors to be posted to the FDQ so the hardware can resume receiving immediately. Fixes: 7a64bb388df3 ("net: ti: icssg-prueth: Add AF_XDP zero copy for RX") Signed-off-by: Meghana Malladi Link: https://patch.msgid.link/20260611185744.2498070-2-m-malladi@ti.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/ti/icssg/icssg_common.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/ti/icssg/icssg_common.c b/drivers/net/ethernet/ti/icssg/icssg_common.c index a28a608f9bf4b..55a696912811f 100644 --- a/drivers/net/ethernet/ti/icssg/icssg_common.c +++ b/drivers/net/ethernet/ti/icssg/icssg_common.c @@ -927,6 +927,7 @@ static int emac_rx_packet_zc(struct prueth_emac *emac, u32 flow_id, struct cppi5_host_desc_t *desc_rx; struct prueth_swdata *swdata; dma_addr_t desc_dma, buf_dma; + int avail_desc, alloc_budget; struct xdp_buff *xdp; int xdp_status = 0; int count = 0; @@ -993,16 +994,13 @@ static int emac_rx_packet_zc(struct prueth_emac *emac, u32 flow_id, if (xdp_status & ICSSG_XDP_REDIR) xdp_do_flush(); - /* Allocate xsk buffers from the pool for the "count" number of - * packets processed in order to be able to receive more packets. - */ - ret = prueth_rx_alloc_zc(emac, count); + avail_desc = k3_cppi_desc_pool_avail(rx_chn->desc_pool); + alloc_budget = min_t(int, budget, avail_desc); + + ret = prueth_rx_alloc_zc(emac, alloc_budget); if (xsk_uses_need_wakeup(rx_chn->xsk_pool)) { - /* If the user space doesn't provide enough buffers then it must - * explicitly wake up the kernel when new buffers are available - */ - if (ret < count) + if (ret < alloc_budget) xsk_set_rx_need_wakeup(rx_chn->xsk_pool); else xsk_clear_rx_need_wakeup(rx_chn->xsk_pool); From 40a91dcc62607019e36488c718218cd6f4eb9d3b Mon Sep 17 00:00:00 2001 From: Meghana Malladi Date: Fri, 12 Jun 2026 00:27:42 +0530 Subject: [PATCH 1472/2758] net: ti: icssg: Use undirected TX tag for native XDP in HSR offload mode [ Upstream commit bcbf73d98195577a89e788179843f6d0c66244a5 ] emac_xmit_xdp_frame() always sets the CPPI5 descriptor destination tag to emac->port_id, which directs the PRU firmware to transmit the frame on that specific slave port only. In HSR offload mode this bypasses the firmware's HSR duplication logic: the frame goes out on one ring leg and never appears on the other, breaking HSR redundancy for XDP_TX paths. icssg_ndo_start_xmit() already handles this correctly: when HSR offload mode is active and NETIF_F_HW_HSR_DUP is set it substitutes PRUETH_UNDIRECTED_PKT_DST_TAG (port 0) so the PRU duplicates the frame to both slave ports. It also sets PRUETH_UNDIRECTED_PKT_TAG_INS in epib[1] when NETIF_F_HW_HSR_TAG_INS is set so the PRU inserts the HSR sequence tag, which XDP_TX frames lack (the tag is stripped by the PRU on RX before the frame reaches the XDP program). Apply the same logic in emac_xmit_xdp_frame() so XDP_TX frames in HSR mode are treated identically to skb TX via hsr0. Fixes: 62aa3246f462 ("net: ti: icssg-prueth: Add XDP support") Signed-off-by: Meghana Malladi Link: https://patch.msgid.link/20260611185744.2498070-3-m-malladi@ti.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/ti/icssg/icssg_common.c | 21 ++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/ti/icssg/icssg_common.c b/drivers/net/ethernet/ti/icssg/icssg_common.c index 55a696912811f..ede32f266729e 100644 --- a/drivers/net/ethernet/ti/icssg/icssg_common.c +++ b/drivers/net/ethernet/ti/icssg/icssg_common.c @@ -696,6 +696,7 @@ u32 emac_xmit_xdp_frame(struct prueth_emac *emac, dma_addr_t desc_dma, buf_dma; struct prueth_swdata *swdata; struct page *page; + u32 dst_tag_id; u32 *epib; int ret; @@ -737,9 +738,25 @@ u32 emac_xmit_xdp_frame(struct prueth_emac *emac, /* set dst tag to indicate internal qid at the firmware which is at * bit8..bit15. bit0..bit7 indicates port num for directed - * packets in case of switch mode operation + * packets in case of switch mode operation and port num 0 + * for undirected packets in case of HSR offload mode. + * + * XDP_TX frames arrive on a slave port with the HSR tag already + * stripped by the PRU firmware. Like skb TX via hsr0, they must + * be sent as undirected so the PRU duplicates them to both ports + * and re-inserts the HSR sequence tag. */ - cppi5_desc_set_tags_ids(&first_desc->hdr, 0, (emac->port_id | (q_idx << 8))); + dst_tag_id = emac->port_id | (q_idx << 8); + + if (emac->prueth->is_hsr_offload_mode && + (ndev->features & NETIF_F_HW_HSR_DUP)) + dst_tag_id = PRUETH_UNDIRECTED_PKT_DST_TAG; + + if (emac->prueth->is_hsr_offload_mode && + (ndev->features & NETIF_F_HW_HSR_TAG_INS)) + epib[1] |= PRUETH_UNDIRECTED_PKT_TAG_INS; + + cppi5_desc_set_tags_ids(&first_desc->hdr, 0, dst_tag_id); k3_udma_glue_tx_dma_to_cppi5_addr(tx_chn->tx_chn, &buf_dma); cppi5_hdesc_attach_buf(first_desc, buf_dma, xdpf->len, buf_dma, xdpf->len); swdata = cppi5_hdesc_get_swdata(first_desc); From 785e3765bf9a5dd9af871988569403159e57782e Mon Sep 17 00:00:00 2001 From: Meghana Malladi Date: Fri, 12 Jun 2026 00:27:43 +0530 Subject: [PATCH 1473/2758] net: ti: icssg: Use undirected TX tag for XDP zero copy in HSR offload mode [ Upstream commit f9691288413ceb4fc72f3ccc4d8e42adf66eb28d ] emac_xsk_xmit_zc() has the same issue as the fixed emac_xmit_xdp_frame(): it always sets the CPPI5 descriptor destination tag to emac->port_id, which directs the PRU firmware to transmit on only one slave port in HSR mode, breaking redundancy. Apply the same fix: in HSR offload mode when NETIF_F_HW_HSR_DUP is set, use PRUETH_UNDIRECTED_PKT_DST_TAG (port 0) so the PRU duplicates frames to both ports. Also set PRUETH_UNDIRECTED_PKT_TAG_INS when NETIF_F_HW_HSR_TAG_INS is set so the PRU re-inserts the HSR sequence tag that was stripped by the PRU on RX before the XDP program saw the frame. This ensures XSK XDP_TX frames in HSR mode are treated identically to skb TX via hsr0. Fixes: 8756ef2eb078 ("net: ti: icssg-prueth: Add AF_XDP zero copy for TX") Signed-off-by: Meghana Malladi Link: https://patch.msgid.link/20260611185744.2498070-4-m-malladi@ti.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/ti/icssg/icssg_common.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/ti/icssg/icssg_common.c b/drivers/net/ethernet/ti/icssg/icssg_common.c index ede32f266729e..82ddef9c17d54 100644 --- a/drivers/net/ethernet/ti/icssg/icssg_common.c +++ b/drivers/net/ethernet/ti/icssg/icssg_common.c @@ -105,6 +105,7 @@ static int emac_xsk_xmit_zc(struct prueth_emac *emac, struct xdp_desc xdp_desc; int num_tx = 0, pkt_len; int descs_avail, ret; + u32 dst_tag_id; u32 *epib; int i; @@ -137,9 +138,17 @@ static int emac_xsk_xmit_zc(struct prueth_emac *emac, epib[0] = 0; epib[1] = 0; cppi5_hdesc_set_pktlen(host_desc, pkt_len); - cppi5_desc_set_tags_ids(&host_desc->hdr, 0, - (emac->port_id | (q_idx << 8))); + dst_tag_id = emac->port_id | (q_idx << 8); + + if (emac->prueth->is_hsr_offload_mode && + (ndev->features & NETIF_F_HW_HSR_DUP)) + dst_tag_id = PRUETH_UNDIRECTED_PKT_DST_TAG; + + if (emac->prueth->is_hsr_offload_mode && + (ndev->features & NETIF_F_HW_HSR_TAG_INS)) + epib[1] |= PRUETH_UNDIRECTED_PKT_TAG_INS; + cppi5_desc_set_tags_ids(&host_desc->hdr, 0, dst_tag_id); k3_udma_glue_tx_dma_to_cppi5_addr(tx_chn->tx_chn, &dma_buf); cppi5_hdesc_attach_buf(host_desc, dma_buf, pkt_len, dma_buf, pkt_len); From d01d4cfc806a1c56b6ba694fc4ca0948f7b4d7ed Mon Sep 17 00:00:00 2001 From: Selvamani Rajagopal Date: Thu, 11 Jun 2026 14:55:40 -0700 Subject: [PATCH 1474/2758] net: ethernet: oa_tc6: Remove FCS size in RX frame [ Upstream commit a5a1d11dd3729146abe7420b874bab15870a42b4 ] OA TC6 MAC-PHY appends FCS to the incoming frame. It must be removed from the frame before being passed to the stack. With FCS in the frame, many applications, like ping or any application that uses IP layer may work as they may carry the packet size information in the protocol. Application like ptp4l, particularly if it uses layer 2 for its communication, it will fail with "bad message" due to the extra 4 bytes added by the presence of FCS. Fixes: d70a0d8f2f2d ("net: ethernet: oa_tc6: implement receive path to receive rx ethernet frames") Signed-off-by: Selvamani Rajagopal Link: https://patch.msgid.link/20260611-level-trigger-v5-3-4533a9e85ce2@onsemi.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/oa_tc6.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/net/ethernet/oa_tc6.c b/drivers/net/ethernet/oa_tc6.c index 91a906a7918a2..cfe1f6e3cd12c 100644 --- a/drivers/net/ethernet/oa_tc6.c +++ b/drivers/net/ethernet/oa_tc6.c @@ -752,6 +752,17 @@ static int oa_tc6_process_rx_chunk_footer(struct oa_tc6 *tc6, u32 footer) static void oa_tc6_submit_rx_skb(struct oa_tc6 *tc6) { + /* MAC-PHY delivers each frame with its Ethernet FCS attached. + * Strip it before handing over to the stack, unless the user + * has asked to keep it via NETIF_F_RXFCS. Keeping the FCS + * in the frame is harmless for IP traffic, but is parsed as + * a (malformed) suffix TLV by PTP, which makes ptp4l reject + * every message with "bad message" error. + */ + if (!(tc6->netdev->features & NETIF_F_RXFCS) && + tc6->rx_skb->len > ETH_FCS_LEN) + skb_trim(tc6->rx_skb, tc6->rx_skb->len - ETH_FCS_LEN); + tc6->rx_skb->protocol = eth_type_trans(tc6->rx_skb, tc6->netdev); tc6->netdev->stats.rx_packets++; tc6->netdev->stats.rx_bytes += tc6->rx_skb->len; From 5126f099295c72a3a62bd4bf98c47add909cb6fe Mon Sep 17 00:00:00 2001 From: Brett Creeley Date: Sun, 14 Jun 2026 13:52:59 -0700 Subject: [PATCH 1475/2758] ionic: Fix check in ionic_get_link_ext_stats [ Upstream commit 7678e69079c10b2fb10977f28f44ddb22971ea5b ] The current check will fail if SR-IOV is not initialized for the physical function; this is because is_physfn is 0 if sriov_init() isn't run or fails. Change the check that prevents getting the link down count to use is_virtfn instead so that VFs don't get this functionality, which was the original intent. Fixes: 132b4ebfa090 ("ionic: add support for ethtool extended stat link_down_count") Signed-off-by: Brett Creeley Signed-off-by: Eric Joyner Link: https://patch.msgid.link/20260614205303.48088-2-eric.joyner@amd.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/pensando/ionic/ionic_ethtool.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c index 78a802eb159f0..6069fa4609131 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c @@ -116,8 +116,15 @@ static void ionic_get_link_ext_stats(struct net_device *netdev, { struct ionic_lif *lif = netdev_priv(netdev); - if (lif->ionic->pdev->is_physfn) - stats->link_down_events = lif->link_down_count; + if (lif->ionic->pdev->is_virtfn) + return; + + if (!lif->ionic->idev.port_info) { + netdev_err_once(netdev, "port_info not initialized\n"); + return; + } + + stats->link_down_events = lif->link_down_count; } static int ionic_get_link_ksettings(struct net_device *netdev, From 3d00b375853fbbf4157ad092884b4ed9a49429c1 Mon Sep 17 00:00:00 2001 From: Selvin Xavier Date: Mon, 15 Jun 2026 15:47:37 -0700 Subject: [PATCH 1476/2758] RDMA/bnxt_re: Initialize dpi variable to zero [ Upstream commit 978b27d6ce538bb832ccd69e45802824e4301c4b ] dpi is initialized only for BNXT_RE_ALLOC_WC_PAGE, but copied for all the cases. So initialize the dpi to 0. Fixes: eee6268421a2 ("RDMA/bnxt_re: Move the UAPI methods to a dedicated file") Fixes: 360da60d6c6e ("RDMA/bnxt_re: Enable low latency push") Link: https://patch.msgid.link/r/20260615224751.232802-2-selvin.xavier@broadcom.com Reviewed-by: Anantha Prabhu Signed-off-by: Kalesh AP Signed-off-by: Selvin Xavier Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/bnxt_re/uapi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/bnxt_re/uapi.c b/drivers/infiniband/hw/bnxt_re/uapi.c index 3eaee7101615b..61ddfa94c1aa1 100644 --- a/drivers/infiniband/hw/bnxt_re/uapi.c +++ b/drivers/infiniband/hw/bnxt_re/uapi.c @@ -76,8 +76,8 @@ static int UVERBS_HANDLER(BNXT_RE_METHOD_ALLOC_PAGE)(struct uverbs_attr_bundle * struct ib_ucontext *ib_uctx; struct bnxt_re_dev *rdev; u64 mmap_offset; + u32 dpi = 0; u32 length; - u32 dpi; u64 addr; int err; From 0adcd67f3d6f84835be682da0153f57f5c2f8036 Mon Sep 17 00:00:00 2001 From: Selvin Xavier Date: Mon, 15 Jun 2026 15:47:38 -0700 Subject: [PATCH 1477/2758] RDMA/bnxt_re: Free SRQ toggle page after firmware teardown [ Upstream commit 131e2918b9b0529687e67e2e58047304027f095a ] Free the toggle page only after firmware teardown completes so that an NQ interrupt arriving during bnxt_qplib_destroy_srq() won't write the toggle values to an already-freed page. Move free_page() after bnxt_qplib_destroy_srq(). Fixes: 181028a0d84c ("RDMA/bnxt_re: Share a page to expose per SRQ info with userspace") Link: https://patch.msgid.link/r/20260615224751.232802-3-selvin.xavier@broadcom.com Signed-off-by: Selvin Xavier Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/bnxt_re/ib_verbs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index 365ec2767d253..822484f3b9597 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -2029,11 +2029,11 @@ int bnxt_re_destroy_srq(struct ib_srq *ib_srq, struct ib_udata *udata) if (ret) return ret; - if (rdev->chip_ctx->modes.toggle_bits & BNXT_QPLIB_SRQ_TOGGLE_BIT) { - free_page((unsigned long)srq->uctx_srq_page); + if (rdev->chip_ctx->modes.toggle_bits & BNXT_QPLIB_SRQ_TOGGLE_BIT) hash_del(&srq->hash_entry); - } bnxt_qplib_destroy_srq(&rdev->qplib_res, qplib_srq); + if (rdev->chip_ctx->modes.toggle_bits & BNXT_QPLIB_SRQ_TOGGLE_BIT) + free_page((unsigned long)srq->uctx_srq_page); ib_umem_release(srq->umem); atomic_dec(&rdev->stats.res.srq_count); return ib_respond_empty_udata(udata); From b193854675ecad43b4d69c304c1b6a90b206cc99 Mon Sep 17 00:00:00 2001 From: Selvin Xavier Date: Mon, 15 Jun 2026 15:47:39 -0700 Subject: [PATCH 1478/2758] RDMA/bnxt_re: Free CQ toggle page after firmware teardown [ Upstream commit bb45e06f9914ca64ac95341a80a0c20bb8dd46a9 ] Free the toggle page only after firmware teardown completes so that an NQ interrupt arriving during bnxt_qplib_destroy_cq() won't write the toggle value to an already-freed page. Move free_page() after bnxt_qplib_destroy_cq. Fixes: e275919d9669 ("RDMA/bnxt_re: Share a page to expose per CQ info with userspace") Link: https://patch.msgid.link/r/20260615224751.232802-4-selvin.xavier@broadcom.com Signed-off-by: Selvin Xavier Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/bnxt_re/ib_verbs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index 822484f3b9597..7bea3bee434b8 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -3332,11 +3332,11 @@ int bnxt_re_destroy_cq(struct ib_cq *ib_cq, struct ib_udata *udata) if (ret) return ret; - if (cctx->modes.toggle_bits & BNXT_QPLIB_CQ_TOGGLE_BIT) { - free_page((unsigned long)cq->uctx_cq_page); + if (cctx->modes.toggle_bits & BNXT_QPLIB_CQ_TOGGLE_BIT) hash_del(&cq->hash_entry); - } bnxt_qplib_destroy_cq(&rdev->qplib_res, &cq->qplib_cq); + if (cctx->modes.toggle_bits & BNXT_QPLIB_CQ_TOGGLE_BIT) + free_page((unsigned long)cq->uctx_cq_page); bnxt_re_put_nq(rdev, nq); From 0c403e0786768d88cabe0ccf4e45425da2fd8841 Mon Sep 17 00:00:00 2001 From: Selvin Xavier Date: Mon, 15 Jun 2026 15:47:44 -0700 Subject: [PATCH 1479/2758] RDMA/bnxt_re: Avoid displaying the kernel pointer [ Upstream commit 7d70c704a06f620d5d421ab76bac5e225bfb4308 ] While dumping the info on MR using the rdma tool, we dump the mr_hwq which is a kernel pointer. There is no need to expose this value for end user. So avoid it. Fixes: 7363eb76b7f3 ("RDMA/bnxt_re: Support driver specific data collection using rdma tool") Link: https://patch.msgid.link/r/20260615224751.232802-9-selvin.xavier@broadcom.com Reviewed-by: Kalesh AP Signed-off-by: Selvin Xavier Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/bnxt_re/main.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c index a892f11729179..d25fdc458120e 100644 --- a/drivers/infiniband/hw/bnxt_re/main.c +++ b/drivers/infiniband/hw/bnxt_re/main.c @@ -1093,8 +1093,6 @@ static int bnxt_re_fill_res_mr_entry(struct sk_buff *msg, struct ib_mr *ib_mr) goto err; if (rdma_nl_put_driver_u32(msg, "element_size", mr_hwq->element_size)) goto err; - if (rdma_nl_put_driver_u64_hex(msg, "hwq", (unsigned long)mr_hwq)) - goto err; if (rdma_nl_put_driver_u64_hex(msg, "va", mr->qplib_mr.va)) goto err; From 7605fd8bbf4d3d608a96f06e1d047775d2ec8574 Mon Sep 17 00:00:00 2001 From: Sriharsha Basavapatna Date: Tue, 19 May 2026 20:30:33 +0530 Subject: [PATCH 1480/2758] RDMA/bnxt_re: Refactor bnxt_re_init_user_qp() [ Upstream commit 9a79dcbedcc7a0330a227f23fbd220515c61d54c ] The umem changes for CQ added a helper - bnxt_re_setup_sginfo(). Use the same helper for QP creation since we support only 4K pages for QP ring memory too. Add a new helper function bnxt_re_get_psn_bytes() to improve readability as this code will be updated in subsequent patches. Link: https://patch.msgid.link/r/20260519150041.7251-2-sriharsha.basavapatna@broadcom.com Signed-off-by: Sriharsha Basavapatna Reviewed-by: Selvin Xavier Signed-off-by: Jason Gunthorpe Stable-dep-of: dc95931b7e13 ("RDMA/bnxt_re: Add a max slot check for SQ") Signed-off-by: Sasha Levin --- drivers/infiniband/hw/bnxt_re/ib_verbs.c | 127 +++++++++++++---------- 1 file changed, 73 insertions(+), 54 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index 7bea3bee434b8..dac50fe6725ae 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -1136,34 +1136,64 @@ static int bnxt_re_setup_swqe_size(struct bnxt_re_qp *qp, return 0; } +static int bnxt_re_setup_sginfo(struct bnxt_re_dev *rdev, + struct ib_umem *umem, + struct bnxt_qplib_sg_info *sginfo) +{ + unsigned long page_size; + + if (!umem) + return -EINVAL; + + page_size = ib_umem_find_best_pgsz(umem, SZ_4K, 0); + if (!page_size || page_size != SZ_4K) + return -EINVAL; + + sginfo->umem = umem; + sginfo->npages = ib_umem_num_dma_blocks(umem, page_size); + sginfo->pgsize = page_size; + sginfo->pgshft = __builtin_ctz(page_size); + return 0; +} + +static int bnxt_re_get_psn_bytes(struct bnxt_re_dev *rdev, + struct bnxt_re_ucontext *cntx, + struct bnxt_qplib_qp *qplib_qp, + struct bnxt_re_qp_req *ureq) +{ + int psn_sz, psn_nume; + + psn_sz = bnxt_qplib_is_chip_gen_p5_p7(rdev->chip_ctx) ? + sizeof(struct sq_psn_search_ext) : + sizeof(struct sq_psn_search); + if (cntx && bnxt_re_is_var_size_supported(rdev, cntx)) { + psn_nume = ureq->sq_slots; + } else { + psn_nume = (qplib_qp->wqe_mode == BNXT_QPLIB_WQE_MODE_STATIC) ? + qplib_qp->sq.max_wqe : ((qplib_qp->sq.max_wqe * qplib_qp->sq.wqe_size) / + sizeof(struct bnxt_qplib_sge)); + } + if (_is_host_msn_table(rdev->qplib_res.dattr->dev_cap_flags2)) + psn_nume = roundup_pow_of_two(psn_nume); + + return psn_nume * psn_sz; +} + static int bnxt_re_init_user_qp(struct bnxt_re_dev *rdev, struct bnxt_re_pd *pd, struct bnxt_re_qp *qp, struct bnxt_re_ucontext *cntx, struct bnxt_re_qp_req *ureq) { struct bnxt_qplib_qp *qplib_qp; - int bytes = 0, psn_sz; struct ib_umem *umem; - int psn_nume; + int bytes; + int rc; qplib_qp = &qp->qplib_qp; bytes = (qplib_qp->sq.max_wqe * qplib_qp->sq.wqe_size); /* Consider mapping PSN search memory only for RC QPs. */ - if (qplib_qp->type == CMDQ_CREATE_QP_TYPE_RC) { - psn_sz = bnxt_qplib_is_chip_gen_p5_p7(rdev->chip_ctx) ? - sizeof(struct sq_psn_search_ext) : - sizeof(struct sq_psn_search); - if (cntx && bnxt_re_is_var_size_supported(rdev, cntx)) { - psn_nume = ureq->sq_slots; - } else { - psn_nume = (qplib_qp->wqe_mode == BNXT_QPLIB_WQE_MODE_STATIC) ? - qplib_qp->sq.max_wqe : ((qplib_qp->sq.max_wqe * qplib_qp->sq.wqe_size) / - sizeof(struct bnxt_qplib_sge)); - } - if (_is_host_msn_table(rdev->qplib_res.dattr->dev_cap_flags2)) - psn_nume = roundup_pow_of_two(psn_nume); - bytes += (psn_nume * psn_sz); - } + if (qplib_qp->type == CMDQ_CREATE_QP_TYPE_RC) + bytes += bnxt_re_get_psn_bytes(rdev, cntx, qplib_qp, ureq); bytes = PAGE_ALIGN(bytes); umem = ib_umem_get(&rdev->ibdev, ureq->qpsva, bytes, @@ -1172,33 +1202,42 @@ static int bnxt_re_init_user_qp(struct bnxt_re_dev *rdev, struct bnxt_re_pd *pd, return PTR_ERR(umem); qp->sumem = umem; - qplib_qp->sq.sg_info.umem = umem; - qplib_qp->sq.sg_info.pgsize = PAGE_SIZE; - qplib_qp->sq.sg_info.pgshft = PAGE_SHIFT; - qplib_qp->qp_handle = ureq->qp_handle; + rc = bnxt_re_setup_sginfo(rdev, qp->sumem, &qplib_qp->sq.sg_info); + if (rc) + goto fail; + + if (qp->qplib_qp.srq) + goto done; - if (!qp->qplib_qp.srq) { - bytes = (qplib_qp->rq.max_wqe * qplib_qp->rq.wqe_size); - bytes = PAGE_ALIGN(bytes); - umem = ib_umem_get(&rdev->ibdev, ureq->qprva, bytes, - IB_ACCESS_LOCAL_WRITE); - if (IS_ERR(umem)) - goto rqfail; - qp->rumem = umem; - qplib_qp->rq.sg_info.umem = umem; - qplib_qp->rq.sg_info.pgsize = PAGE_SIZE; - qplib_qp->rq.sg_info.pgshft = PAGE_SHIFT; + bytes = (qplib_qp->rq.max_wqe * qplib_qp->rq.wqe_size); + bytes = PAGE_ALIGN(bytes); + umem = ib_umem_get(&rdev->ibdev, ureq->qprva, bytes, + IB_ACCESS_LOCAL_WRITE); + if (IS_ERR(umem)) { + rc = PTR_ERR(umem); + goto fail; } + qp->rumem = umem; + rc = bnxt_re_setup_sginfo(rdev, qp->rumem, &qplib_qp->rq.sg_info); + if (rc) + goto rqfail; + +done: + qplib_qp->qp_handle = ureq->qp_handle; qplib_qp->dpi = &cntx->dpi; qplib_qp->is_user = true; return 0; + rqfail: + ib_umem_release(qp->rumem); + qp->rumem = NULL; + memset(&qplib_qp->rq.sg_info, 0, sizeof(qplib_qp->rq.sg_info)); +fail: ib_umem_release(qp->sumem); qp->sumem = NULL; memset(&qplib_qp->sq.sg_info, 0, sizeof(qplib_qp->sq.sg_info)); - - return PTR_ERR(umem); + return rc; } static struct bnxt_re_ah *bnxt_re_create_shadow_qp_ah @@ -3345,26 +3384,6 @@ int bnxt_re_destroy_cq(struct ib_cq *ib_cq, struct ib_udata *udata) return ib_respond_empty_udata(udata); } -static int bnxt_re_setup_sginfo(struct bnxt_re_dev *rdev, - struct ib_umem *umem, - struct bnxt_qplib_sg_info *sginfo) -{ - unsigned long page_size; - - if (!umem) - return -EINVAL; - - page_size = ib_umem_find_best_pgsz(umem, SZ_4K, 0); - if (!page_size || page_size != SZ_4K) - return -EINVAL; - - sginfo->umem = umem; - sginfo->npages = ib_umem_num_dma_blocks(umem, page_size); - sginfo->pgsize = page_size; - sginfo->pgshft = __builtin_ctz(page_size); - return 0; -} - int bnxt_re_create_user_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, struct uverbs_attr_bundle *attrs) { From 3169824fd8f44a4b6fcba45bd6397541ff0aa4a9 Mon Sep 17 00:00:00 2001 From: Sriharsha Basavapatna Date: Tue, 19 May 2026 20:30:36 +0530 Subject: [PATCH 1481/2758] RDMA/bnxt_re: Update msn table size for app allocated QPs [ Upstream commit 9ce2a8c81c3099f68d371d040d77aab8cd1a9ed5 ] For app allocated QPs, the driver shouldn't use slots/round-up logic to compute the msn table size. The application handles this logic and computes 'sq_npsn' and passes it to the driver using a new uapi parameter. Link: https://patch.msgid.link/r/20260519150041.7251-5-sriharsha.basavapatna@broadcom.com Signed-off-by: Sriharsha Basavapatna Reviewed-by: Selvin Xavier Signed-off-by: Jason Gunthorpe Stable-dep-of: dc95931b7e13 ("RDMA/bnxt_re: Add a max slot check for SQ") Signed-off-by: Sasha Levin --- drivers/infiniband/hw/bnxt_re/ib_verbs.c | 61 +++++++++++++++--------- include/uapi/rdma/bnxt_re-abi.h | 1 + 2 files changed, 40 insertions(+), 22 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index dac50fe6725ae..2aaf9093b612d 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -1159,29 +1159,39 @@ static int bnxt_re_setup_sginfo(struct bnxt_re_dev *rdev, static int bnxt_re_get_psn_bytes(struct bnxt_re_dev *rdev, struct bnxt_re_ucontext *cntx, struct bnxt_qplib_qp *qplib_qp, - struct bnxt_re_qp_req *ureq) + struct bnxt_re_qp_req *ureq, + bool fixed_que_attr) { int psn_sz, psn_nume; - psn_sz = bnxt_qplib_is_chip_gen_p5_p7(rdev->chip_ctx) ? - sizeof(struct sq_psn_search_ext) : - sizeof(struct sq_psn_search); - if (cntx && bnxt_re_is_var_size_supported(rdev, cntx)) { - psn_nume = ureq->sq_slots; + if (rdev->dev_attr && + _is_host_msn_table(rdev->dev_attr->dev_cap_flags2)) + psn_sz = sizeof(struct sq_msn_search); + else + psn_sz = bnxt_qplib_is_chip_gen_p5_p7(rdev->chip_ctx) ? + sizeof(struct sq_psn_search_ext) : + sizeof(struct sq_psn_search); + if (!fixed_que_attr) { + if (cntx && bnxt_re_is_var_size_supported(rdev, cntx)) { + psn_nume = ureq->sq_slots; + } else { + psn_nume = (qplib_qp->wqe_mode == BNXT_QPLIB_WQE_MODE_STATIC) ? + qplib_qp->sq.max_wqe : ((qplib_qp->sq.max_wqe * qplib_qp->sq.wqe_size) / + sizeof(struct bnxt_qplib_sge)); + } + if (_is_host_msn_table(rdev->qplib_res.dattr->dev_cap_flags2)) + psn_nume = roundup_pow_of_two(psn_nume); } else { - psn_nume = (qplib_qp->wqe_mode == BNXT_QPLIB_WQE_MODE_STATIC) ? - qplib_qp->sq.max_wqe : ((qplib_qp->sq.max_wqe * qplib_qp->sq.wqe_size) / - sizeof(struct bnxt_qplib_sge)); + psn_nume = ureq->sq_npsn; } - if (_is_host_msn_table(rdev->qplib_res.dattr->dev_cap_flags2)) - psn_nume = roundup_pow_of_two(psn_nume); return psn_nume * psn_sz; } static int bnxt_re_init_user_qp(struct bnxt_re_dev *rdev, struct bnxt_re_pd *pd, struct bnxt_re_qp *qp, struct bnxt_re_ucontext *cntx, - struct bnxt_re_qp_req *ureq) + struct bnxt_re_qp_req *ureq, + bool fixed_que_attr) { struct bnxt_qplib_qp *qplib_qp; struct ib_umem *umem; @@ -1193,7 +1203,7 @@ static int bnxt_re_init_user_qp(struct bnxt_re_dev *rdev, struct bnxt_re_pd *pd, bytes = (qplib_qp->sq.max_wqe * qplib_qp->sq.wqe_size); /* Consider mapping PSN search memory only for RC QPs. */ if (qplib_qp->type == CMDQ_CREATE_QP_TYPE_RC) - bytes += bnxt_re_get_psn_bytes(rdev, cntx, qplib_qp, ureq); + bytes += bnxt_re_get_psn_bytes(rdev, cntx, qplib_qp, ureq, fixed_que_attr); bytes = PAGE_ALIGN(bytes); umem = ib_umem_get(&rdev->ibdev, ureq->qpsva, bytes, @@ -1636,7 +1646,9 @@ static int bnxt_re_init_qp_type(struct bnxt_re_dev *rdev, return qptype; } -static void bnxt_re_qp_calculate_msn_psn_size(struct bnxt_re_qp *qp) +static void bnxt_re_qp_calculate_msn_psn_size(struct bnxt_re_qp *qp, + bool fixed_que_attr, + struct bnxt_re_qp_req *req) { struct bnxt_qplib_qp *qplib_qp = &qp->qplib_qp; struct bnxt_qplib_q *sq = &qplib_qp->sq; @@ -1659,12 +1671,17 @@ static void bnxt_re_qp_calculate_msn_psn_size(struct bnxt_re_qp *qp) /* Update msn tbl size */ if (qplib_qp->is_host_msn_tbl && qplib_qp->psn_sz) { - if (wqe_mode == BNXT_QPLIB_WQE_MODE_STATIC) - qplib_qp->msn_tbl_sz = - roundup_pow_of_two(bnxt_qplib_set_sq_size(sq, wqe_mode)); - else - qplib_qp->msn_tbl_sz = - roundup_pow_of_two(bnxt_qplib_set_sq_size(sq, wqe_mode)) / 2; + if (!fixed_que_attr) { + if (wqe_mode == BNXT_QPLIB_WQE_MODE_STATIC) + qplib_qp->msn_tbl_sz = + roundup_pow_of_two(bnxt_qplib_set_sq_size(sq, wqe_mode)); + else + qplib_qp->msn_tbl_sz = + roundup_pow_of_two(bnxt_qplib_set_sq_size(sq, wqe_mode)) + / 2; + } else { + qplib_qp->msn_tbl_sz = req->sq_npsn; + } qplib_qp->msn = 0; } } @@ -1738,12 +1755,12 @@ static int bnxt_re_init_qp_attr(struct bnxt_re_qp *qp, struct bnxt_re_pd *pd, bnxt_re_adjust_gsi_sq_attr(qp, init_attr, uctx); if (uctx) { /* This will update DPI and qp_handle */ - rc = bnxt_re_init_user_qp(rdev, pd, qp, uctx, ureq); + rc = bnxt_re_init_user_qp(rdev, pd, qp, uctx, ureq, fixed_que_attr); if (rc) return rc; } - bnxt_re_qp_calculate_msn_psn_size(qp); + bnxt_re_qp_calculate_msn_psn_size(qp, fixed_que_attr, ureq); rc = bnxt_re_setup_qp_hwqs(qp); if (rc) diff --git a/include/uapi/rdma/bnxt_re-abi.h b/include/uapi/rdma/bnxt_re-abi.h index 40955eaba32e6..db8400f2ce3bc 100644 --- a/include/uapi/rdma/bnxt_re-abi.h +++ b/include/uapi/rdma/bnxt_re-abi.h @@ -135,6 +135,7 @@ struct bnxt_re_qp_req { __aligned_u64 qp_handle; __aligned_u64 comp_mask; __u32 sq_slots; + __u32 sq_npsn; }; struct bnxt_re_qp_resp { From 2234acd1d1d29790e2a23f08782a8954be320ae1 Mon Sep 17 00:00:00 2001 From: Sriharsha Basavapatna Date: Tue, 19 May 2026 20:30:38 +0530 Subject: [PATCH 1482/2758] RDMA/bnxt_re: Enhance dbr usecnt logic in doorbell uapis [ Upstream commit 73607410f4f1b0d5c3d0af2d70a79c265482a0e3 ] The current logic in the doorbell cleanup function is not sufficient for a change in a subsequent patch, that fails doorbell remove operation in some conditions. The cleanup should facilitate freeing of the dbr object when the caller may not retry the teardown operation (implicit teardown: process-exit/driver-removal). Extend this counter to use kref mechanism so that the dbr object gets freed (via kref callback) when there are no more references to it, rather than directly freeing it in the cleanup uapi. Link: https://patch.msgid.link/r/20260519150041.7251-7-sriharsha.basavapatna@broadcom.com Signed-off-by: Sriharsha Basavapatna Reviewed-by: Selvin Xavier Signed-off-by: Jason Gunthorpe Stable-dep-of: dc95931b7e13 ("RDMA/bnxt_re: Add a max slot check for SQ") Signed-off-by: Sasha Levin --- drivers/infiniband/hw/bnxt_re/ib_verbs.h | 3 ++- drivers/infiniband/hw/bnxt_re/uapi.c | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.h b/drivers/infiniband/hw/bnxt_re/ib_verbs.h index 08f71a94d55d7..13dac48ed453e 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.h +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.h @@ -167,7 +167,7 @@ struct bnxt_re_dbr_obj { struct bnxt_re_dev *rdev; struct bnxt_qplib_dpi dpi; struct bnxt_re_user_mmap_entry *entry; - atomic_t usecnt; /* QPs using this dbr */ + struct kref usecnt; /* 1 (uobject) + n (QPs using this dbr) */ }; struct bnxt_re_flow { @@ -308,4 +308,5 @@ void bnxt_re_unlock_cqs(struct bnxt_re_qp *qp, unsigned long flags); struct bnxt_re_user_mmap_entry* bnxt_re_mmap_entry_insert(struct bnxt_re_ucontext *uctx, u64 mem_offset, enum bnxt_re_mmap_flag mmap_flag, u64 *offset); +void bnxt_re_dbr_kref_release(struct kref *ref); #endif /* __BNXT_RE_IB_VERBS_H__ */ diff --git a/drivers/infiniband/hw/bnxt_re/uapi.c b/drivers/infiniband/hw/bnxt_re/uapi.c index 61ddfa94c1aa1..058f76f9b679b 100644 --- a/drivers/infiniband/hw/bnxt_re/uapi.c +++ b/drivers/infiniband/hw/bnxt_re/uapi.c @@ -369,6 +369,7 @@ static int UVERBS_HANDLER(BNXT_RE_METHOD_DBR_ALLOC)(struct uverbs_attr_bundle *a } obj->rdev = rdev; + kref_init(&obj->usecnt); uobj->object = obj; uverbs_finalize_uobj_create(attrs, BNXT_RE_ALLOC_DBR_HANDLE); @@ -391,15 +392,24 @@ static int UVERBS_HANDLER(BNXT_RE_METHOD_DBR_ALLOC)(struct uverbs_attr_bundle *a return ret; } +void bnxt_re_dbr_kref_release(struct kref *ref) +{ + struct bnxt_re_dbr_obj *obj = + container_of(ref, struct bnxt_re_dbr_obj, usecnt); + struct bnxt_re_dev *rdev = obj->rdev; + + rdma_user_mmap_entry_remove(&obj->entry->rdma_entry); + bnxt_qplib_free_uc_dpi(&rdev->qplib_res, &obj->dpi); + kfree(obj); +} + static int bnxt_re_dbr_cleanup(struct ib_uobject *uobject, enum rdma_remove_reason why, struct uverbs_attr_bundle *attrs) { struct bnxt_re_dbr_obj *obj = uobject->object; - struct bnxt_re_dev *rdev = obj->rdev; - rdma_user_mmap_entry_remove(&obj->entry->rdma_entry); - bnxt_qplib_free_uc_dpi(&rdev->qplib_res, &obj->dpi); + kref_put(&obj->usecnt, bnxt_re_dbr_kref_release); return 0; } From 6eceb09df972d8188e7e9bf77d2bab2d596dceac Mon Sep 17 00:00:00 2001 From: Sriharsha Basavapatna Date: Tue, 19 May 2026 20:30:40 +0530 Subject: [PATCH 1483/2758] RDMA/bnxt_re: Support doorbells for app allocated QPs [ Upstream commit 7e812673a948395675e9893e75b0fc60d19bbf27 ] App allocated QPs can use a separate doorbell for each QP. This doorbell region can be passed through a new driver specific DBR_HANDLE attribute, during QP creation. When this attribute is set, associate the QP with the given doorbell region. While the QP holds a reference to the dbr, the dbr itself cannot be destroyed and is rejected with EBUSY error. Link: https://patch.msgid.link/r/20260519150041.7251-9-sriharsha.basavapatna@broadcom.com Signed-off-by: Sriharsha Basavapatna Reviewed-by: Selvin Xavier Signed-off-by: Jason Gunthorpe Stable-dep-of: dc95931b7e13 ("RDMA/bnxt_re: Add a max slot check for SQ") Signed-off-by: Sasha Levin --- drivers/infiniband/hw/bnxt_re/ib_verbs.c | 35 ++++++++++++++++++++---- drivers/infiniband/hw/bnxt_re/ib_verbs.h | 1 + drivers/infiniband/hw/bnxt_re/uapi.c | 24 ++++++++++++++++ include/uapi/rdma/bnxt_re-abi.h | 4 +++ 4 files changed, 59 insertions(+), 5 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index 2aaf9093b612d..0f30e55141594 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -1024,6 +1024,9 @@ int bnxt_re_destroy_qp(struct ib_qp *ib_qp, struct ib_udata *udata) if (rc) ibdev_err(&rdev->ibdev, "Failed to destroy HW QP"); + if (qp->dbr_obj) + kref_put(&qp->dbr_obj->usecnt, bnxt_re_dbr_kref_release); + if (rdma_is_kernel_res(&qp->ib_qp.res)) { flags = bnxt_re_lock_cqs(qp); bnxt_qplib_clean_qp(&qp->qplib_qp); @@ -1191,7 +1194,8 @@ static int bnxt_re_get_psn_bytes(struct bnxt_re_dev *rdev, static int bnxt_re_init_user_qp(struct bnxt_re_dev *rdev, struct bnxt_re_pd *pd, struct bnxt_re_qp *qp, struct bnxt_re_ucontext *cntx, struct bnxt_re_qp_req *ureq, - bool fixed_que_attr) + bool fixed_que_attr, + struct bnxt_re_dbr_obj *dbr_obj) { struct bnxt_qplib_qp *qplib_qp; struct ib_umem *umem; @@ -1234,8 +1238,11 @@ static int bnxt_re_init_user_qp(struct bnxt_re_dev *rdev, struct bnxt_re_pd *pd, goto rqfail; done: + if (dbr_obj) + qplib_qp->dpi = &dbr_obj->dpi; + else + qplib_qp->dpi = &cntx->dpi; qplib_qp->qp_handle = ureq->qp_handle; - qplib_qp->dpi = &cntx->dpi; qplib_qp->is_user = true; return 0; @@ -1689,7 +1696,8 @@ static void bnxt_re_qp_calculate_msn_psn_size(struct bnxt_re_qp *qp, static int bnxt_re_init_qp_attr(struct bnxt_re_qp *qp, struct bnxt_re_pd *pd, struct ib_qp_init_attr *init_attr, struct bnxt_re_ucontext *uctx, - struct bnxt_re_qp_req *ureq) + struct bnxt_re_qp_req *ureq, + struct bnxt_re_dbr_obj *dbr_obj) { struct bnxt_qplib_dev_attr *dev_attr; struct bnxt_qplib_qp *qplqp; @@ -1755,7 +1763,8 @@ static int bnxt_re_init_qp_attr(struct bnxt_re_qp *qp, struct bnxt_re_pd *pd, bnxt_re_adjust_gsi_sq_attr(qp, init_attr, uctx); if (uctx) { /* This will update DPI and qp_handle */ - rc = bnxt_re_init_user_qp(rdev, pd, qp, uctx, ureq, fixed_que_attr); + rc = bnxt_re_init_user_qp(rdev, pd, qp, uctx, ureq, fixed_que_attr, + dbr_obj); if (rc) return rc; } @@ -1891,7 +1900,9 @@ static int bnxt_re_add_unique_gid(struct bnxt_re_dev *rdev) int bnxt_re_create_qp(struct ib_qp *ib_qp, struct ib_qp_init_attr *qp_init_attr, struct ib_udata *udata) { + struct bnxt_re_dbr_obj *dbr_obj = NULL; struct bnxt_qplib_dev_attr *dev_attr; + struct uverbs_attr_bundle *attrs; struct bnxt_re_ucontext *uctx; struct bnxt_re_qp_req ureq; struct bnxt_re_dev *rdev; @@ -1912,6 +1923,17 @@ int bnxt_re_create_qp(struct ib_qp *ib_qp, struct ib_qp_init_attr *qp_init_attr, rc = ib_copy_validate_udata_in_cm(udata, ureq, qp_handle, 0); if (rc) return rc; + + attrs = rdma_udata_to_uverbs_attr_bundle(udata); + if (uverbs_attr_is_valid(attrs, + BNXT_RE_CREATE_QP_ATTR_DBR_HANDLE)) { + dbr_obj = uverbs_attr_get_obj(attrs, + BNXT_RE_CREATE_QP_ATTR_DBR_HANDLE); + if (IS_ERR(dbr_obj)) + return PTR_ERR(dbr_obj); + kref_get(&dbr_obj->usecnt); + qp->dbr_obj = dbr_obj; + } } rc = bnxt_re_test_qp_limits(rdev, qp_init_attr, dev_attr); @@ -1921,7 +1943,8 @@ int bnxt_re_create_qp(struct ib_qp *ib_qp, struct ib_qp_init_attr *qp_init_attr, } qp->rdev = rdev; - rc = bnxt_re_init_qp_attr(qp, pd, qp_init_attr, uctx, &ureq); + rc = bnxt_re_init_qp_attr(qp, pd, qp_init_attr, uctx, &ureq, + dbr_obj); if (rc) goto fail; @@ -1991,6 +2014,8 @@ int bnxt_re_create_qp(struct ib_qp *ib_qp, struct ib_qp_init_attr *qp_init_attr, bnxt_qplib_free_qp_res(&rdev->qplib_res, &qp->qplib_qp); bnxt_re_qp_free_umem(qp); fail: + if (dbr_obj) + kref_put(&dbr_obj->usecnt, bnxt_re_dbr_kref_release); return rc; } diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.h b/drivers/infiniband/hw/bnxt_re/ib_verbs.h index 13dac48ed453e..cdc403bf9e5d6 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.h +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.h @@ -96,6 +96,7 @@ struct bnxt_re_qp { struct bnxt_re_cq *scq; struct bnxt_re_cq *rcq; struct dentry *dentry; + struct bnxt_re_dbr_obj *dbr_obj; /* doorbell region */ }; struct bnxt_re_cq { diff --git a/drivers/infiniband/hw/bnxt_re/uapi.c b/drivers/infiniband/hw/bnxt_re/uapi.c index 058f76f9b679b..65539f6bacb6b 100644 --- a/drivers/infiniband/hw/bnxt_re/uapi.c +++ b/drivers/infiniband/hw/bnxt_re/uapi.c @@ -409,6 +409,15 @@ static int bnxt_re_dbr_cleanup(struct ib_uobject *uobject, { struct bnxt_re_dbr_obj *obj = uobject->object; + /* If it is being destroyed explicitly while QPs still hold a + * reference (> 1), reject it with EBUSY. If no QP references + * or implicit teardown (process exit, driver removal), drop + * the uobject reference unconditionally. The object gets freed + * (bnxt_re_dbr_kref_release) when the usecnt goes to zero. + */ + if (why == RDMA_REMOVE_DESTROY && kref_read(&obj->usecnt) > 1) + return -EBUSY; + kref_put(&obj->usecnt, bnxt_re_dbr_kref_release); return 0; } @@ -469,11 +478,26 @@ DECLARE_UVERBS_NAMED_METHOD(BNXT_RE_METHOD_GET_DEFAULT_DBR, DECLARE_UVERBS_GLOBAL_METHODS(BNXT_RE_OBJECT_DEFAULT_DBR, &UVERBS_METHOD(BNXT_RE_METHOD_GET_DEFAULT_DBR)); +ADD_UVERBS_ATTRIBUTES_SIMPLE( + bnxt_re_qp_create, + UVERBS_OBJECT_QP, + UVERBS_METHOD_QP_CREATE, + UVERBS_ATTR_IDR(BNXT_RE_CREATE_QP_ATTR_DBR_HANDLE, + BNXT_RE_OBJECT_DBR, + UVERBS_ACCESS_READ, + UA_OPTIONAL)); + +const struct uapi_definition bnxt_re_create_qp_defs[] = { + UAPI_DEF_CHAIN_OBJ_TREE(UVERBS_OBJECT_QP, &bnxt_re_qp_create), + {}, +}; + const struct uapi_definition bnxt_re_uapi_defs[] = { UAPI_DEF_CHAIN_OBJ_TREE_NAMED(BNXT_RE_OBJECT_ALLOC_PAGE), UAPI_DEF_CHAIN_OBJ_TREE_NAMED(BNXT_RE_OBJECT_NOTIFY_DRV), UAPI_DEF_CHAIN_OBJ_TREE_NAMED(BNXT_RE_OBJECT_GET_TOGGLE_MEM), UAPI_DEF_CHAIN_OBJ_TREE_NAMED(BNXT_RE_OBJECT_DBR), UAPI_DEF_CHAIN_OBJ_TREE_NAMED(BNXT_RE_OBJECT_DEFAULT_DBR), + UAPI_DEF_CHAIN(bnxt_re_create_qp_defs), {} }; diff --git a/include/uapi/rdma/bnxt_re-abi.h b/include/uapi/rdma/bnxt_re-abi.h index db8400f2ce3bc..4da8cda337dcb 100644 --- a/include/uapi/rdma/bnxt_re-abi.h +++ b/include/uapi/rdma/bnxt_re-abi.h @@ -138,6 +138,10 @@ struct bnxt_re_qp_req { __u32 sq_npsn; }; +enum bnxt_re_create_qp_attrs { + BNXT_RE_CREATE_QP_ATTR_DBR_HANDLE = UVERBS_ID_DRIVER_NS_WITH_UHW, +}; + struct bnxt_re_qp_resp { __u32 qpid; __u32 rsvd; From a65b5258b14ca9442f9fbab501bddb9e98d9477a Mon Sep 17 00:00:00 2001 From: Sriharsha Basavapatna Date: Tue, 19 May 2026 20:30:41 +0530 Subject: [PATCH 1484/2758] RDMA/bnxt_re: Enable app allocated QPs [ Upstream commit 47b730054f05a05acd497c138bf3255a5de1a973 ] The driver supports a new comp_mask: REQ_MASK_FIXED_QUE_ATTR. The application sets this comp_mask bit in the CREATE_QP ureq to indicate direct control of the QP. The driver goes through the required processing for app allocated QPs (previous patches). Only variable WQE mode is supported for these QPs. This patch removes an unused comp_mask: BNXT_RE_QP_REQ_MASK_VAR_WQE_SQ_SLOTS Link: https://patch.msgid.link/r/20260519150041.7251-10-sriharsha.basavapatna@broadcom.com Signed-off-by: Sriharsha Basavapatna Reviewed-by: Selvin Xavier Signed-off-by: Jason Gunthorpe Stable-dep-of: dc95931b7e13 ("RDMA/bnxt_re: Add a max slot check for SQ") Signed-off-by: Sasha Levin --- drivers/infiniband/hw/bnxt_re/ib_verbs.c | 18 +++++++++++++++--- include/uapi/rdma/bnxt_re-abi.h | 2 +- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index 0f30e55141594..e97c17727da16 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -1697,7 +1697,8 @@ static int bnxt_re_init_qp_attr(struct bnxt_re_qp *qp, struct bnxt_re_pd *pd, struct ib_qp_init_attr *init_attr, struct bnxt_re_ucontext *uctx, struct bnxt_re_qp_req *ureq, - struct bnxt_re_dbr_obj *dbr_obj) + struct bnxt_re_dbr_obj *dbr_obj, + bool fixed_que_attr) { struct bnxt_qplib_dev_attr *dev_attr; struct bnxt_qplib_qp *qplqp; @@ -1720,6 +1721,13 @@ static int bnxt_re_init_qp_attr(struct bnxt_re_qp *qp, struct bnxt_re_pd *pd, return qptype; qplqp->type = (u8)qptype; qplqp->wqe_mode = bnxt_re_is_var_size_supported(rdev, uctx); + if (fixed_que_attr) { + if (qplqp->wqe_mode != BNXT_QPLIB_WQE_MODE_VARIABLE) + return -EOPNOTSUPP; + if (!ureq->sq_npsn || + ureq->sq_npsn > roundup_pow_of_two(ureq->sq_slots / 2)) + return -EINVAL; + } qplqp->dev_cap_flags = dev_attr->dev_cap_flags; qplqp->cctx = rdev->chip_ctx; if (init_attr->qp_type == IB_QPT_RC) { @@ -1904,6 +1912,7 @@ int bnxt_re_create_qp(struct ib_qp *ib_qp, struct ib_qp_init_attr *qp_init_attr, struct bnxt_qplib_dev_attr *dev_attr; struct uverbs_attr_bundle *attrs; struct bnxt_re_ucontext *uctx; + bool fixed_que_attr = false; struct bnxt_re_qp_req ureq; struct bnxt_re_dev *rdev; struct bnxt_re_pd *pd; @@ -1920,7 +1929,8 @@ int bnxt_re_create_qp(struct ib_qp *ib_qp, struct ib_qp_init_attr *qp_init_attr, uctx = rdma_udata_to_drv_context(udata, struct bnxt_re_ucontext, ib_uctx); if (udata) { - rc = ib_copy_validate_udata_in_cm(udata, ureq, qp_handle, 0); + rc = ib_copy_validate_udata_in_cm(udata, ureq, qp_handle, + BNXT_RE_QP_REQ_MASK_FIXED_QUE_ATTR); if (rc) return rc; @@ -1934,6 +1944,8 @@ int bnxt_re_create_qp(struct ib_qp *ib_qp, struct ib_qp_init_attr *qp_init_attr, kref_get(&dbr_obj->usecnt); qp->dbr_obj = dbr_obj; } + if (ureq.comp_mask & BNXT_RE_QP_REQ_MASK_FIXED_QUE_ATTR) + fixed_que_attr = true; } rc = bnxt_re_test_qp_limits(rdev, qp_init_attr, dev_attr); @@ -1944,7 +1956,7 @@ int bnxt_re_create_qp(struct ib_qp *ib_qp, struct ib_qp_init_attr *qp_init_attr, qp->rdev = rdev; rc = bnxt_re_init_qp_attr(qp, pd, qp_init_attr, uctx, &ureq, - dbr_obj); + dbr_obj, fixed_que_attr); if (rc) goto fail; diff --git a/include/uapi/rdma/bnxt_re-abi.h b/include/uapi/rdma/bnxt_re-abi.h index 4da8cda337dcb..a4599d7b736aa 100644 --- a/include/uapi/rdma/bnxt_re-abi.h +++ b/include/uapi/rdma/bnxt_re-abi.h @@ -126,7 +126,7 @@ struct bnxt_re_resize_cq_req { }; enum bnxt_re_qp_mask { - BNXT_RE_QP_REQ_MASK_VAR_WQE_SQ_SLOTS = 0x1, + BNXT_RE_QP_REQ_MASK_FIXED_QUE_ATTR = 0x1, }; struct bnxt_re_qp_req { From a59d815cbe667929b693b5fa6716a074e6a31c5b Mon Sep 17 00:00:00 2001 From: Selvin Xavier Date: Mon, 15 Jun 2026 15:47:45 -0700 Subject: [PATCH 1485/2758] RDMA/bnxt_re: Add a max slot check for SQ [ Upstream commit dc95931b7e1326dacae547874bf38c092e5960d8 ] The variable WQE mode must be validated against the maximum slots supported by HW. The max supported value is 64K. Adding a max and min check and fail if user supplied value is more than the max supported and zero. Fixes: d8ea645d6984 ("RDMA/bnxt_re: Handle variable WQE support for user applications") Link: https://patch.msgid.link/r/20260615224751.232802-10-selvin.xavier@broadcom.com Reviewed-by: Sriharsha Basavapatna Signed-off-by: Selvin Xavier Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/bnxt_re/ib_verbs.c | 3 +++ drivers/infiniband/hw/bnxt_re/qplib_sp.h | 1 + 2 files changed, 4 insertions(+) diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index e97c17727da16..24270371a0a56 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -1721,6 +1721,9 @@ static int bnxt_re_init_qp_attr(struct bnxt_re_qp *qp, struct bnxt_re_pd *pd, return qptype; qplqp->type = (u8)qptype; qplqp->wqe_mode = bnxt_re_is_var_size_supported(rdev, uctx); + if (uctx && qplqp->wqe_mode == BNXT_QPLIB_WQE_MODE_VARIABLE && + (!ureq->sq_slots || ureq->sq_slots > BNXT_RE_MAX_SQ_SLOTS)) + return -EINVAL; if (fixed_que_attr) { if (qplqp->wqe_mode != BNXT_QPLIB_WQE_MODE_VARIABLE) return -EOPNOTSUPP; diff --git a/drivers/infiniband/hw/bnxt_re/qplib_sp.h b/drivers/infiniband/hw/bnxt_re/qplib_sp.h index 9fadd637cb5b1..c4193ae75b54d 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_sp.h +++ b/drivers/infiniband/hw/bnxt_re/qplib_sp.h @@ -369,6 +369,7 @@ int bnxt_qplib_destroy_flow(struct bnxt_qplib_res *res); #define BNXT_VAR_MAX_SLOT_ALIGN 256 #define BNXT_VAR_MAX_SGE 13 #define BNXT_RE_MAX_RQ_WQES 65536 +#define BNXT_RE_MAX_SQ_SLOTS 65536 #define BNXT_STATIC_MAX_SGE 6 From 303f6fef95df5e5316970746d861cf6daeeca77f Mon Sep 17 00:00:00 2001 From: Selvin Xavier Date: Mon, 15 Jun 2026 15:47:46 -0700 Subject: [PATCH 1486/2758] RDMA/bnxt_re: Proper rollback if the ioremap fails [ Upstream commit 87267803a8c824616eb147c5dad7030a5db6f878 ] bnxt_qplib_alloc_dpi returns success even if ioremap fails. Add the proper rollback when the ioremap fails and return -ENOMEM status. Fixes: 0ac20faf5d83 ("RDMA/bnxt_re: Reorg the bar mapping") Fixes: 360da60d6c6e ("RDMA/bnxt_re: Enable low latency push") Link: https://patch.msgid.link/r/20260615224751.232802-11-selvin.xavier@broadcom.com Reviewed-by: Sriharsha Basavapatna Signed-off-by: Selvin Xavier Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/bnxt_re/qplib_res.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/infiniband/hw/bnxt_re/qplib_res.c b/drivers/infiniband/hw/bnxt_re/qplib_res.c index 95e0489c53c37..756f8b5f042a9 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_res.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_res.c @@ -764,9 +764,13 @@ int bnxt_qplib_alloc_dpi(struct bnxt_qplib_res *res, break; case BNXT_QPLIB_DPI_TYPE_WC: dpi->dbr = ioremap_wc(umaddr, PAGE_SIZE); + if (!dpi->dbr) + goto fail_ioremap; break; default: dpi->dbr = ioremap(umaddr, PAGE_SIZE); + if (!dpi->dbr) + goto fail_ioremap; break; } @@ -774,6 +778,13 @@ int bnxt_qplib_alloc_dpi(struct bnxt_qplib_res *res, mutex_unlock(&res->dpi_tbl_lock); return 0; +fail_ioremap: + /* Roll back the bit we just claimed. */ + set_bit(bit_num, dpit->tbl); + dpit->app_tbl[bit_num] = NULL; + mutex_unlock(&res->dpi_tbl_lock); + return -ENOMEM; + } int bnxt_qplib_dealloc_dpi(struct bnxt_qplib_res *res, From da406b8b49c1dfe661a497483940d7ee781430db Mon Sep 17 00:00:00 2001 From: Selvin Xavier Date: Mon, 15 Jun 2026 15:47:47 -0700 Subject: [PATCH 1487/2758] RDMA/bnxt_re: Avoid repeated requests to allocate WC pages [ Upstream commit 441baa79043431807115fd030d7d0bb14ed441a0 ] Applications can request multiple WC pages for the same ucontext. As of now, only 1 WC page per ucontext is supported. Add a lock to avoid concurrent access and a check to fail repeated requests. Also, if the mmap entry insert fails for the WC, free the Doorbell page index mapped for the WC page. Fixes: eee6268421a2 ("RDMA/bnxt_re: Move the UAPI methods to a dedicated file") Fixes: 360da60d6c6e ("RDMA/bnxt_re: Enable low latency push") Link: https://patch.msgid.link/r/20260615224751.232802-12-selvin.xavier@broadcom.com Reviewed-by: Kalesh AP Signed-off-by: Selvin Xavier Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/bnxt_re/ib_verbs.c | 1 + drivers/infiniband/hw/bnxt_re/ib_verbs.h | 1 + drivers/infiniband/hw/bnxt_re/uapi.c | 33 +++++++++++++++++++----- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index 24270371a0a56..5729063474636 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -4720,6 +4720,7 @@ int bnxt_re_alloc_ucontext(struct ib_ucontext *ctx, struct ib_udata *udata) goto fail; } spin_lock_init(&uctx->sh_lock); + mutex_init(&uctx->wcdpi_lock); resp.comp_mask = BNXT_RE_UCNTX_CMASK_HAVE_CCTX; chip_met_rev_num = rdev->chip_ctx->chip_num; diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.h b/drivers/infiniband/hw/bnxt_re/ib_verbs.h index cdc403bf9e5d6..acb40fc528d24 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.h +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.h @@ -142,6 +142,7 @@ struct bnxt_re_ucontext { struct bnxt_re_dev *rdev; struct bnxt_qplib_dpi dpi; struct bnxt_qplib_dpi wcdpi; + struct mutex wcdpi_lock; /* serialises WC DPI alloc/free */ void *shpg; spinlock_t sh_lock; /* protect shpg */ struct rdma_user_mmap_entry *shpage_mmap; diff --git a/drivers/infiniband/hw/bnxt_re/uapi.c b/drivers/infiniband/hw/bnxt_re/uapi.c index 65539f6bacb6b..5b0215eb4f58f 100644 --- a/drivers/infiniband/hw/bnxt_re/uapi.c +++ b/drivers/infiniband/hw/bnxt_re/uapi.c @@ -98,14 +98,23 @@ static int UVERBS_HANDLER(BNXT_RE_METHOD_ALLOC_PAGE)(struct uverbs_attr_bundle * switch (alloc_type) { case BNXT_RE_ALLOC_WC_PAGE: - if (cctx->modes.db_push) { + if (cctx->modes.db_push) { + mutex_lock(&uctx->wcdpi_lock); + /* already allocated — one WC page per context */ + if (uctx->wcdpi.dbr) { + mutex_unlock(&uctx->wcdpi_lock); + return -EEXIST; + } if (bnxt_qplib_alloc_dpi(&rdev->qplib_res, &uctx->wcdpi, - uctx, BNXT_QPLIB_DPI_TYPE_WC)) + uctx, BNXT_QPLIB_DPI_TYPE_WC)) { + mutex_unlock(&uctx->wcdpi_lock); return -ENOMEM; + } length = PAGE_SIZE; dpi = uctx->wcdpi.dpi; addr = (u64)uctx->wcdpi.umdbr; mmap_flag = BNXT_RE_MMAP_WC_DB; + mutex_unlock(&uctx->wcdpi_lock); } else { return -EINVAL; } @@ -128,8 +137,15 @@ static int UVERBS_HANDLER(BNXT_RE_METHOD_ALLOC_PAGE)(struct uverbs_attr_bundle * } entry = bnxt_re_mmap_entry_insert(uctx, addr, mmap_flag, &mmap_offset); - if (!entry) + if (!entry) { + if (mmap_flag == BNXT_RE_MMAP_WC_DB) { + mutex_lock(&uctx->wcdpi_lock); + bnxt_qplib_dealloc_dpi(&rdev->qplib_res, &uctx->wcdpi); + uctx->wcdpi.dbr = NULL; + mutex_unlock(&uctx->wcdpi_lock); + } return -ENOMEM; + } uobj->object = entry; uverbs_finalize_uobj_create(attrs, BNXT_RE_ALLOC_PAGE_HANDLE); @@ -160,11 +176,16 @@ static int alloc_page_obj_cleanup(struct ib_uobject *uobject, switch (entry->mmap_flag) { case BNXT_RE_MMAP_WC_DB: - if (uctx && uctx->wcdpi.dbr) { + if (uctx) { struct bnxt_re_dev *rdev = uctx->rdev; - bnxt_qplib_dealloc_dpi(&rdev->qplib_res, &uctx->wcdpi); - uctx->wcdpi.dbr = NULL; + mutex_lock(&uctx->wcdpi_lock); + if (uctx->wcdpi.dbr) { + bnxt_qplib_dealloc_dpi(&rdev->qplib_res, + &uctx->wcdpi); + uctx->wcdpi.dbr = NULL; + } + mutex_unlock(&uctx->wcdpi_lock); } break; case BNXT_RE_MMAP_DBR_BAR: From 03c9a2fba68e0e1923b3bca22e61c6913cf0efce Mon Sep 17 00:00:00 2001 From: Selvin Xavier Date: Mon, 15 Jun 2026 15:47:50 -0700 Subject: [PATCH 1488/2758] RDMA/bnxt_re: Fail DBR related page allocation UAPIs if the feature is disabled [ Upstream commit a57592c6392a8e333c9c2731701297a5a279313a ] No need to support the DBR related page allocations if the pacing feature is disabled. Fail the request if pacing is disabled. Fixes: ea2224857882 ("RDMA/bnxt_re: Update alloc_page uapi for pacing") Link: https://patch.msgid.link/r/20260615224751.232802-15-selvin.xavier@broadcom.com Reviewed-by: Sriharsha Basavapatna Signed-off-by: Selvin Xavier Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/bnxt_re/uapi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/infiniband/hw/bnxt_re/uapi.c b/drivers/infiniband/hw/bnxt_re/uapi.c index 5b0215eb4f58f..2891b18b9437f 100644 --- a/drivers/infiniband/hw/bnxt_re/uapi.c +++ b/drivers/infiniband/hw/bnxt_re/uapi.c @@ -121,12 +121,16 @@ static int UVERBS_HANDLER(BNXT_RE_METHOD_ALLOC_PAGE)(struct uverbs_attr_bundle * break; case BNXT_RE_ALLOC_DBR_BAR_PAGE: + if (!rdev->pacing.dbr_pacing) + return -EOPNOTSUPP; length = PAGE_SIZE; addr = (u64)rdev->pacing.dbr_bar_addr; mmap_flag = BNXT_RE_MMAP_DBR_BAR; break; case BNXT_RE_ALLOC_DBR_PAGE: + if (!rdev->pacing.dbr_pacing) + return -EOPNOTSUPP; length = PAGE_SIZE; addr = (u64)rdev->pacing.dbr_page; mmap_flag = BNXT_RE_MMAP_DBR_PAGE; From 967099102562d562c2a02cf5d00b722d3352da4d Mon Sep 17 00:00:00 2001 From: Selvin Xavier Date: Mon, 15 Jun 2026 15:47:51 -0700 Subject: [PATCH 1489/2758] RDMA/bnxt_re: Reject GET_TOGGLE_MEM when toggle page was not allocated [ Upstream commit 33a215f499b0643cd88a32ab5b8de1547be419c6 ] If a user calls BNXT_RE_METHOD_GET_TOGGLE_MEM on a device that does not support the CQ/SRQ toggle feature, uctx_cq_page or uctx_srq_page will be NULL. Add an explicit -EOPNOTSUPP return after capturing the address from uctx_cq_page / uctx_srq_page if the address is zero. Fixes: e275919d9669 ("RDMA/bnxt_re: Share a page to expose per CQ info with userspace") Fixes: 181028a0d84c ("RDMA/bnxt_re: Share a page to expose per SRQ info with userspace") Link: https://patch.msgid.link/r/20260615224751.232802-16-selvin.xavier@broadcom.com Signed-off-by: Selvin Xavier Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/bnxt_re/uapi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/infiniband/hw/bnxt_re/uapi.c b/drivers/infiniband/hw/bnxt_re/uapi.c index 2891b18b9437f..ca6b1630cd83d 100644 --- a/drivers/infiniband/hw/bnxt_re/uapi.c +++ b/drivers/infiniband/hw/bnxt_re/uapi.c @@ -277,6 +277,8 @@ static int UVERBS_HANDLER(BNXT_RE_METHOD_GET_TOGGLE_MEM)(struct uverbs_attr_bund return -EINVAL; addr = (u64)cq->uctx_cq_page; + if (!addr) + return -EOPNOTSUPP; break; case BNXT_RE_SRQ_TOGGLE_MEM: srq = bnxt_re_search_for_srq(rdev, res_id); @@ -284,6 +286,8 @@ static int UVERBS_HANDLER(BNXT_RE_METHOD_GET_TOGGLE_MEM)(struct uverbs_attr_bund return -EINVAL; addr = (u64)srq->uctx_srq_page; + if (!addr) + return -EOPNOTSUPP; break; default: From bc4caea7a82bbcf94a34eff7094e7f9b501680ab Mon Sep 17 00:00:00 2001 From: Junxian Huang Date: Sat, 13 Jun 2026 18:20:45 +0800 Subject: [PATCH 1490/2758] RDMA/hns: Fix memory leak of bonding resources [ Upstream commit c0bd03b850d81a8914168d87ddf7f6ffa58875ef ] In a corner case of concurrent driver removal and driver reset, bonding resource is first released in hns_roce_hw_v2_exit() during driver removal, and then is allocated again in hns_roce_register_device() during driver reset. This leads to memory leak because the release timing has already passed. This may also lead to a kernel panic as below because of the leaked notifier callback: Call trace: 0xffffa20fccc04978 (P) raw_notifier_call_chain+0x20/0x38 call_netdevice_notifiers_info+0x60/0xb8 netdev_lower_state_changed+0x4c/0xb8 As Sashiko suggested, the teardown order of bonding resources should be inverted to make sure the resources are released when the driver is removed. Fixes: b37ad2e290fc ("RDMA/hns: Initialize bonding resources") Link: https://patch.msgid.link/r/20260613102045.811623-1-huangjunxian6@hisilicon.com Signed-off-by: Junxian Huang Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index 8810fa0b65973..60478382e82ff 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -7586,8 +7586,8 @@ static int __init hns_roce_hw_v2_init(void) static void __exit hns_roce_hw_v2_exit(void) { - hns_roce_dealloc_bond_grp(); hnae3_unregister_client(&hns_roce_hw_v2_client); + hns_roce_dealloc_bond_grp(); hns_roce_cleanup_debugfs(); } From bde37aed0724c0139dea177f3aae8d989b6babb1 Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Tue, 16 Jun 2026 15:56:01 +0000 Subject: [PATCH 1491/2758] RDMA/irdma: Replace waitqueue and flag with completion [ Upstream commit d9c8c45e6d2f438a3c8e643ae78b59454fa0fadd ] The driver previously used a waitqueue along with an explicit request_done flag, but without proper barriers around request_done. An earlier patch by Gui-Dong Han attempted to fix this by adding the missing memory barriers. Rather than adding the barriers, this patch replaces the waitqueue+flag with a completion, which is designed for this exact purpose. Fixes: 44d9e52977a1 ("RDMA/irdma: Implement device initialization definitions") Fixes: 915cc7ac0f8e ("RDMA/irdma: Add miscellaneous utility definitions") Link: https://patch.msgid.link/r/20260616155601.1081448-1-jmoroni@google.com Signed-off-by: Jacob Moroni Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/irdma/hw.c | 7 +++---- drivers/infiniband/hw/irdma/main.h | 3 +-- drivers/infiniband/hw/irdma/utils.c | 12 +++++------- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/drivers/infiniband/hw/irdma/hw.c b/drivers/infiniband/hw/irdma/hw.c index f9be467d137f8..c345cc6542566 100644 --- a/drivers/infiniband/hw/irdma/hw.c +++ b/drivers/infiniband/hw/irdma/hw.c @@ -235,8 +235,7 @@ static void irdma_complete_cqp_request(struct irdma_cqp *cqp, struct irdma_cqp_request *cqp_request) { if (cqp_request->waiting) { - WRITE_ONCE(cqp_request->request_done, true); - wake_up(&cqp_request->waitq); + complete_all(&cqp_request->comp); } else if (cqp_request->callback_fcn) { cqp_request->callback_fcn(cqp_request); } @@ -1107,9 +1106,9 @@ static int irdma_create_cqp(struct irdma_pci_f *rf) INIT_LIST_HEAD(&cqp->cqp_avail_reqs); INIT_LIST_HEAD(&cqp->cqp_pending_reqs); - /* init the waitqueue of the cqp_requests and add them to the list */ + /* init the completion of the cqp_requests and add them to the list */ for (i = 0; i < sqsize; i++) { - init_waitqueue_head(&cqp->cqp_requests[i].waitq); + init_completion(&cqp->cqp_requests[i].comp); list_add_tail(&cqp->cqp_requests[i].list, &cqp->cqp_avail_reqs); } init_waitqueue_head(&cqp->remove_wq); diff --git a/drivers/infiniband/hw/irdma/main.h b/drivers/infiniband/hw/irdma/main.h index 3d49bd57bae7c..8c17a201c1fd2 100644 --- a/drivers/infiniband/hw/irdma/main.h +++ b/drivers/infiniband/hw/irdma/main.h @@ -161,13 +161,12 @@ struct irdma_cqp_compl_info { struct irdma_cqp_request { struct cqp_cmds_info info; - wait_queue_head_t waitq; + struct completion comp; struct list_head list; refcount_t refcnt; void (*callback_fcn)(struct irdma_cqp_request *cqp_request); void *param; struct irdma_cqp_compl_info compl_info; - bool request_done; /* READ/WRITE_ONCE macros operate on it */ bool waiting:1; bool dynamic:1; bool pending:1; diff --git a/drivers/infiniband/hw/irdma/utils.c b/drivers/infiniband/hw/irdma/utils.c index 495e5daff4b45..8e9e159f19ffe 100644 --- a/drivers/infiniband/hw/irdma/utils.c +++ b/drivers/infiniband/hw/irdma/utils.c @@ -442,7 +442,7 @@ struct irdma_cqp_request *irdma_alloc_and_get_cqp_request(struct irdma_cqp *cqp, if (cqp_request) { cqp_request->dynamic = true; if (wait) - init_waitqueue_head(&cqp_request->waitq); + init_completion(&cqp_request->comp); } } if (!cqp_request) { @@ -480,7 +480,7 @@ void irdma_free_cqp_request(struct irdma_cqp *cqp, if (cqp_request->dynamic) { kfree(cqp_request); } else { - WRITE_ONCE(cqp_request->request_done, false); + reinit_completion(&cqp_request->comp); cqp_request->callback_fcn = NULL; cqp_request->waiting = false; cqp_request->pending = false; @@ -515,8 +515,7 @@ irdma_free_pending_cqp_request(struct irdma_cqp *cqp, { if (cqp_request->waiting) { cqp_request->compl_info.error = true; - WRITE_ONCE(cqp_request->request_done, true); - wake_up(&cqp_request->waitq); + complete_all(&cqp_request->comp); } wait_event_timeout(cqp->remove_wq, refcount_read(&cqp_request->refcnt) == 1, 1000); @@ -609,9 +608,8 @@ static int irdma_wait_event(struct irdma_pci_f *rf, cqp_timeout.compl_cqp_cmds = atomic64_read(&rf->sc_dev.cqp->completed_ops); do { irdma_cqp_ce_handler(rf, &rf->ccq.sc_cq); - if (wait_event_timeout(cqp_request->waitq, - READ_ONCE(cqp_request->request_done), - msecs_to_jiffies(CQP_COMPL_WAIT_TIME_MS))) + if (wait_for_completion_timeout(&cqp_request->comp, + msecs_to_jiffies(CQP_COMPL_WAIT_TIME_MS))) break; if (cqp_request->pending) From 2fface6e0bbd6314d1d9d071abf2c4d67548511c Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 16 Jun 2026 14:13:17 +0000 Subject: [PATCH 1492/2758] net: serialize netif_running() check in enqueue_to_backlog() [ Upstream commit 46762cefe7f4e5bffc1eb467810a7bbb02e461d7 ] Syzbot reported a KASAN slab-use-after-free in fib_rules_lookup(). The root cause is a race condition where packets can escape the backlog flushing during device unregistration (e.g., during netns exit). Commit e9e4dd3267d0 ("net: do not process device backlog during unregistration") introduced a lockless netif_running() check in enqueue_to_backlog() to prevent queuing packets to an unregistering device. However, this creates a TOCTOU race window. A lockless transmitter (like veth_xmit) can pass the check before dev_close() clears IFF_UP. If the transmitter is then delayed, flush_all_backlogs() can run and finish before the transmitter grabs the backlog lock and queues the packet. The packet then escapes the flush and triggers UAF later when processed. Fix this by moving the netif_running() check inside the backlog lock. This serializes the check with the flush work (which also grabs the lock). We then either queue the packet before the flush runs (so it gets flushed), or check netif_running() after the flush/close completes (so it gets dropped). Fixes: e9e4dd3267d0 ("net: do not process device backlog during unregistration") Reported-by: syzbot+965506b59a2de0b6905c@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6a315824.b0403584.28d0ff.0000.GAE@google.com/T/#u Signed-off-by: Eric Dumazet Cc: Julian Anastasov Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260616141317.407791-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/core/dev.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index 731e661d7be65..f81ce83fb3250 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5381,8 +5381,6 @@ static int enqueue_to_backlog(struct sk_buff *skb, int cpu, u32 tail; reason = SKB_DROP_REASON_DEV_READY; - if (unlikely(!netif_running(skb->dev))) - goto bad_dev; sd = &per_cpu(softnet_data, cpu); @@ -5394,6 +5392,10 @@ static int enqueue_to_backlog(struct sk_buff *skb, int cpu, backlog_lock_irq_save(sd, &flags); qlen = skb_queue_len(&sd->input_pkt_queue); if (likely(qlen <= max_backlog)) { + if (unlikely(!netif_running(skb->dev))) { + backlog_unlock_irq_restore(sd, flags); + goto bad_dev; + } if (!qlen) { /* Schedule NAPI for backlog device. We can use * non atomic operation as we own the queue lock. From 0ff82a9cf9312678d8bc4edeef0b6e82659ac12a Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Fri, 5 Jun 2026 12:30:16 +0800 Subject: [PATCH 1493/2758] ksmbd: fix use-after-free in same_client_has_lease() [ Upstream commit 65b655f65c3ca1ab5d598d3832bb0ff531725858 ] same_client_has_lease() returns an opinfo pointer from ci->m_op_list after dropping ci->m_lock without taking a reference. smb_grant_oplock() then dereferences that pointer in copy_lease() and when checking breaking_cnt. A concurrent close can remove the old lease from ci->m_op_list and drop the last reference before the caller uses the returned pointer, leading to a use-after-free. Take a reference when same_client_has_lease() selects an existing lease, drop any previous match while scanning, and release the returned reference in smb_grant_oplock() after copying the lease state. Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") Signed-off-by: Guangshuo Li Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/server/oplock.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index b193dde4810dc..60e7e821c2455 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -528,7 +528,12 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci, ret = compare_guid_key(opinfo, client_guid, lctx->lease_key); if (ret) { + if (!atomic_inc_not_zero(&opinfo->refcount)) + continue; + if (m_opinfo) + opinfo_put(m_opinfo); m_opinfo = opinfo; + /* skip upgrading lease about breaking lease */ if (atomic_read(&opinfo->breaking_cnt)) continue; @@ -1246,6 +1251,7 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, if (atomic_read(&m_opinfo->breaking_cnt)) opinfo->o_lease->flags = SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE; + opinfo_put(m_opinfo); goto out; } } From e18ffb7541de052d21039a3e6ae0d719c5107369 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Tue, 7 Apr 2026 16:26:48 +0300 Subject: [PATCH 1494/2758] mfd: bd72720: Drop BUCK11 ID [ Upstream commit 6a2cb13761d90ef3fa960db189a2cc1bdf965ae1 ] The BD72720 header reserves an ID for BUCK11. While this does not (at the moment) cause problems I can see, it is misleading as the BD72720 contains only 10 BUCKs. Make the code clearer and drop the BUCK11 ID. Fixes: af25277b1ddc ("mfd: rohm-bd71828: Support ROHM BD72720") Signed-off-by: Matti Vaittinen Link: https://patch.msgid.link/812c3749a18d609d6f4698506bc516ec7183dfdd.1775565298.git.mazziesaccount@gmail.com Signed-off-by: Lee Jones Signed-off-by: Sasha Levin --- include/linux/mfd/rohm-bd72720.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/linux/mfd/rohm-bd72720.h b/include/linux/mfd/rohm-bd72720.h index ae7343bcab064..d8ddbf232bb3e 100644 --- a/include/linux/mfd/rohm-bd72720.h +++ b/include/linux/mfd/rohm-bd72720.h @@ -21,7 +21,6 @@ enum { BD72720_BUCK8, BD72720_BUCK9, BD72720_BUCK10, - BD72720_BUCK11, BD72720_LDO1, BD72720_LDO2, BD72720_LDO3, From afb1a5af6dd949196a4cf289751f3cca09d27469 Mon Sep 17 00:00:00 2001 From: Matthew Bystrin Date: Wed, 29 Apr 2026 10:20:46 +0300 Subject: [PATCH 1495/2758] mfd: rsmu: Fix page register setup [ Upstream commit 6bc38f26ed07197b11a2b588edf1f43bfbc81d76 ] Fix writes to page register in 8A3400x family (Clock Matrix). All calls to rsmu_write_page_register() (both in i2c and spi) have resulted in early return, because all addresses in include/linux/mfd/idt8a340_reg.h are less than RSMU_CM_SCSR_BASE. There were 2 separate patch series which have to be merged in one time: mfd and ptp. The latter have been merged, the former[1] have not. Link: https://lore.kernel.org/netdev/LV3P220MB1202F8E2FCCFBA2519B4966EA0192@LV3P220MB1202.NAMP220.PROD.OUTLOOK.COM/ Fixes: 67d6c76fc815 ("mfd: rsmu: Support 32-bit address space") Signed-off-by: Matthew Bystrin Link: https://patch.msgid.link/20260429072047.1111427-2-dev.mbstr@gmail.com Signed-off-by: Lee Jones Signed-off-by: Sasha Levin --- drivers/mfd/rsmu_i2c.c | 6 +----- drivers/mfd/rsmu_spi.c | 5 +---- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/mfd/rsmu_i2c.c b/drivers/mfd/rsmu_i2c.c index cba64f107a2fd..9e5fc8259eec2 100644 --- a/drivers/mfd/rsmu_i2c.c +++ b/drivers/mfd/rsmu_i2c.c @@ -134,14 +134,10 @@ static int rsmu_i2c_write_device(struct rsmu_ddata *rsmu, u8 reg, u8 *buf, u8 by static int rsmu_write_page_register(struct rsmu_ddata *rsmu, u32 reg, rsmu_rw_device rsmu_write_device) { - u32 page = reg & RSMU_CM_PAGE_MASK; + u32 page = (reg | RSMU_CM_SCSR_BASE) & RSMU_CM_PAGE_MASK; u8 buf[4]; int err; - /* Do not modify offset register for none-scsr registers */ - if (reg < RSMU_CM_SCSR_BASE) - return 0; - /* Simply return if we are on the same page */ if (rsmu->page == page) return 0; diff --git a/drivers/mfd/rsmu_spi.c b/drivers/mfd/rsmu_spi.c index 39d9be1e141fb..c931d8cea0a1a 100644 --- a/drivers/mfd/rsmu_spi.c +++ b/drivers/mfd/rsmu_spi.c @@ -101,11 +101,8 @@ static int rsmu_write_page_register(struct rsmu_ddata *rsmu, u32 reg) switch (rsmu->type) { case RSMU_CM: - /* Do not modify page register for none-scsr registers */ - if (reg < RSMU_CM_SCSR_BASE) - return 0; page_reg = RSMU_CM_PAGE_ADDR; - page = reg & RSMU_PAGE_MASK; + page = (reg | RSMU_CM_SCSR_BASE) & RSMU_PAGE_MASK; buf[0] = (u8)(page & 0xFF); buf[1] = (u8)((page >> 8) & 0xFF); buf[2] = (u8)((page >> 16) & 0xFF); From 17d79248b4f370663f9d351409a130fc1ed9416d Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 8 May 2026 14:48:04 +0100 Subject: [PATCH 1496/2758] mfd: cs42l43: Sanity check firmware size [ Upstream commit b6ef1a74b3ec254f87a6a3c554fe8f8083ebd37c ] Currently the code checks if a firmware was received, however it does not verify that the firmware size is larger than the firmware header. As the firmware pointer is dereferenced as a pointer to the header structure this could lead to an out of bounds memory access. Add the missing check. Fixes: ace6d1448138 ("mfd: cs42l43: Add support for cs42l43 core driver") Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260508134804.1787461-1-ckeepax@opensource.cirrus.com Signed-off-by: Lee Jones Signed-off-by: Sasha Levin --- drivers/mfd/cs42l43.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/cs42l43.c b/drivers/mfd/cs42l43.c index 166881751e698..ed6d93893de04 100644 --- a/drivers/mfd/cs42l43.c +++ b/drivers/mfd/cs42l43.c @@ -722,7 +722,7 @@ static void cs42l43_mcu_load_firmware(const struct firmware *firmware, void *con unsigned int loadaddr, val; int ret; - if (!firmware) { + if (!firmware || firmware->size < sizeof(*hdr)) { dev_err(cs42l43->dev, "Failed to load firmware\n"); cs42l43->firmware_error = -ENODEV; goto err; From f3dd1e534e9de64669415f8239e0094afecfed78 Mon Sep 17 00:00:00 2001 From: Aleksandr Nogikh Date: Fri, 12 Jun 2026 11:50:20 +0000 Subject: [PATCH 1497/2758] ocfs2: fix circular locking dependency in ocfs2_dio_end_io_write [ Upstream commit ff6f26c58421614b02694ac9d219ac61d924bc68 ] A circular locking dependency involves INODE_ALLOC_SYSTEM_INODE, EXTENT_ALLOC_SYSTEM_INODE, and ORPHAN_DIR_SYSTEM_INODE. 1. ocfs2_mknod() acquires INODE_ALLOC then EXTENT_ALLOC. 2. ocfs2_dio_end_io_write() acquires EXTENT_ALLOC for unwritten extents, then ORPHAN_DIR via ocfs2_del_inode_from_orphan() while still holding EXTENT_ALLOC. 3. ocfs2_wipe_inode() acquires ORPHAN_DIR then INODE_ALLOC via ocfs2_remove_inode. Break the cycle in ocfs2_dio_end_io_write() by freeing the allocation contexts (releasing EXTENT_ALLOC) before acquiring ORPHAN_DIR. WARNING: possible circular locking dependency detected ------------------------------------------------------ is trying to acquire lock: ffff8881e78b33a0 (&ocfs2_sysfile_lock_key[INODE_ALLOC_SYSTEM_INODE]){+.+.}-{4:4}, at: ocfs2_evict_inode+0x1539/0x43b0 fs/ocfs2/inode.c:1299 but task is already holding lock: ffff8881e78b4fa0 (&ocfs2_sysfile_lock_key[ORPHAN_DIR_SYSTEM_INODE]){+.+.}-{4:4}, at: ocfs2_evict_inode+0xe97/0x43b0 fs/ocfs2/inode.c:1299 the existing dependency chain (in reverse order) is: -> #2 (&ocfs2_sysfile_lock_key[ORPHAN_DIR_SYSTEM_INODE]){+.+.}-{4:4}: inode_lock include/linux/fs.h:1029 [inline] ocfs2_del_inode_from_orphan+0x12e/0x7a0 fs/ocfs2/namei.c:2728 ocfs2_dio_end_io+0xf9c/0x1370 fs/ocfs2/aops.c:2418 dio_complete+0x25b/0x790 fs/direct-io.c:281 -> #1 (&ocfs2_sysfile_lock_key[EXTENT_ALLOC_SYSTEM_INODE]){+.+.}-{4:4}: inode_lock include/linux/fs.h:1029 [inline] ocfs2_reserve_suballoc_bits+0x16d/0x4840 fs/ocfs2/suballoc.c:882 ocfs2_reserve_new_metadata_blocks+0x415/0x9a0 fs/ocfs2/suballoc.c:1078 ocfs2_mknod+0x10f3/0x2260 fs/ocfs2/namei.c:351 -> #0 (&ocfs2_sysfile_lock_key[INODE_ALLOC_SYSTEM_INODE]){+.+.}-{4:4}: __lock_acquire+0x15a5/0x2cf0 kernel/locking/lockdep.c:5237 lock_acquire+0x106/0x350 kernel/locking/lockdep.c:5868 down_write+0x96/0x200 kernel/locking/rwsem.c:1625 inode_lock include/linux/fs.h:1029 [inline] ocfs2_remove_inode fs/ocfs2/inode.c:733 [inline] ocfs2_wipe_inode fs/ocfs2/inode.c:896 [inline] ocfs2_delete_inode fs/ocfs2/inode.c:1157 [inline] ocfs2_evict_inode+0x1539/0x43b0 fs/ocfs2/inode.c:1299 Chain exists of: &ocfs2_sysfile_lock_key[INODE_ALLOC_SYSTEM_INODE] --> &ocfs2_sysfile_lock_key[EXTENT_ALLOC_SYSTEM_INODE] --> &ocfs2_sysfile_lock_key[ORPHAN_DIR_SYSTEM_INODE] Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(&ocfs2_sysfile_lock_key[ORPHAN_DIR_SYSTEM_INODE]); lock(&ocfs2_sysfile_lock_key[EXTENT_ALLOC_SYSTEM_INODE]); lock(&ocfs2_sysfile_lock_key[ORPHAN_DIR_SYSTEM_INODE]); lock(&ocfs2_sysfile_lock_key[INODE_ALLOC_SYSTEM_INODE]); *** DEADLOCK *** Link: https://lore.kernel.org/97c902a6-3bcf-43ea-9b70-f1f136a6c3f2@mail.kernel.org Fixes: d647c5b2fbf8 ("ocfs2: split transactions in dio completion to avoid credit exhaustion") Assisted-by: Gemini:gemini-3.1-pro-preview Gemini:gemini-3-flash-preview syzbot Reported-by: syzbot+b225d4dfce6219600c42@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=b225d4dfce6219600c42 Link: https://syzkaller.appspot.com/ai_job?id=0b53ce1e-2972-4192-aa85-8097a702762c Signed-off-by: Aleksandr Nogikh Reviewed-by: Heming Zhao Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Joseph Qi Cc: Changwei Ge Cc: Jun Piao Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- fs/ocfs2/aops.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index 6ec198bdab121..4acdbb70882cd 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -2372,6 +2372,15 @@ static int ocfs2_dio_end_io_write(struct inode *inode, unlock: up_write(&oi->ip_alloc_sem); + if (data_ac) { + ocfs2_free_alloc_context(data_ac); + data_ac = NULL; + } + if (meta_ac) { + ocfs2_free_alloc_context(meta_ac); + meta_ac = NULL; + } + /* everything looks good, let's start the cleanup */ if (!ret && dwc->dw_orphaned) { BUG_ON(dwc->dw_writer_pid != task_pid_nr(current)); @@ -2383,10 +2392,6 @@ static int ocfs2_dio_end_io_write(struct inode *inode, ocfs2_inode_unlock(inode, 1); brelse(di_bh); out: - if (data_ac) - ocfs2_free_alloc_context(data_ac); - if (meta_ac) - ocfs2_free_alloc_context(meta_ac); ocfs2_run_deallocs(osb, &dealloc); ocfs2_dio_free_write_ctx(inode, dwc); From fdc9043cfd50262e86dcb4d5d4ac233ddeb6ff3b Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Wed, 20 May 2026 10:26:50 +0800 Subject: [PATCH 1498/2758] 9p: avoid returning ERR_PTR(0) from mkdir operations [ Upstream commit 314b58c01a9047567fd19446ca5fd46c473b89ff ] When mkdir succeeds, v9fs_vfs_mkdir_dotl() and v9fs_vfs_mkdir() return ERR_PTR(0) which is incorrect. They should return NULL instead for success and ERR_PTR() only with negative error codes for failure. Return NULL instead of passing to ERR_PTR while err is zero Fixes smatch warnings: fs/9p/vfs_inode_dotl.c:420 v9fs_vfs_mkdir_dotl() warn: passing zero to 'ERR_PTR' fs/9p/vfs_inode.c:695 v9fs_vfs_mkdir() warn: passing zero to 'ERR_PTR' The v9fs_vfs_mkdir() code was further simplified because v9fs_create() can never return NULL, so we do not need to check for fid being set separately, and the error path can be a simple return immediately after v9fs_create() failure. There is no intended functional change. Fixes: 88d5baf69082 ("Change inode_operations.mkdir to return struct dentry *") Suggested-by: David Laight Acked-by: Christian Schoenebeck Signed-off-by: Hongling Zeng Message-ID: <20260520022650.14217-1-zenghongling@kylinos.cn> Signed-off-by: Dominique Martinet Signed-off-by: Sasha Levin --- fs/9p/vfs_inode.c | 19 ++++++------------- fs/9p/vfs_inode_dotl.c | 4 ++-- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index f468acb8ee7df..cdaa259af16de 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -672,27 +672,20 @@ v9fs_vfs_create(struct mnt_idmap *idmap, struct inode *dir, static struct dentry *v9fs_vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode) { - int err; u32 perm; struct p9_fid *fid; struct v9fs_session_info *v9ses; p9_debug(P9_DEBUG_VFS, "name %pd\n", dentry); - err = 0; v9ses = v9fs_inode2v9ses(dir); perm = unixmode2p9mode(v9ses, mode | S_IFDIR); fid = v9fs_create(v9ses, dir, dentry, NULL, perm, P9_OREAD); - if (IS_ERR(fid)) { - err = PTR_ERR(fid); - fid = NULL; - } else { - inc_nlink(dir); - v9fs_invalidate_inode_attr(dir); - } - - if (fid) - p9_fid_put(fid); - return ERR_PTR(err); + if (IS_ERR(fid)) + return ERR_CAST(fid); + inc_nlink(dir); + v9fs_invalidate_inode_attr(dir); + p9_fid_put(fid); + return NULL; } /** diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c index 141fb54db65d2..e90808808ea57 100644 --- a/fs/9p/vfs_inode_dotl.c +++ b/fs/9p/vfs_inode_dotl.c @@ -349,7 +349,7 @@ static struct dentry *v9fs_vfs_mkdir_dotl(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t omode) { - int err; + int err = 0; struct v9fs_session_info *v9ses; struct p9_fid *fid = NULL, *dfid = NULL; kgid_t gid; @@ -412,7 +412,7 @@ static struct dentry *v9fs_vfs_mkdir_dotl(struct mnt_idmap *idmap, p9_fid_put(fid); v9fs_put_acl(dacl, pacl); p9_fid_put(dfid); - return ERR_PTR(err); + return err ? ERR_PTR(err) : NULL; } static int From ebbcbe5c0db215feecc17def06178da443f4eea6 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Fri, 29 May 2026 15:39:31 +0800 Subject: [PATCH 1499/2758] net/9p: fix race condition on rdma->state in trans_rdma.c [ Upstream commit 7d54894a1ee265a72d70f7cae1da6cc774cccc71 ] The rdma->state field is modified without holding req_lock in both recv_done() and p9_cm_event_handler(), while rdma_request() accesses the same field under the req_lock spinlock. This inconsistent locking creates a race condition: - recv_done() running in softirq completion context sets rdma->state = P9_RDMA_FLUSHING without acquiring req_lock - p9_cm_event_handler() modifies rdma->state at multiple points (ADDR_RESOLVED, ROUTE_RESOLVED, ESTABLISHED, CLOSED) without req_lock - rdma_request() uses spin_lock_irqsave(&rdma->req_lock, flags) to protect the read-modify-write of rdma->state The race can cause lost state transitions: recv_done() or the CM event handler could set state to FLUSHING/CLOSED while rdma_request() is concurrently checking or modifying state under the lock, leading to the FLUSHING transition being silently overwritten by CLOSING. This corrupts the connection state machine and can cause use-after-free on RDMA request objects during teardown. Fix by adding req_lock protection to all rdma->state modifications in recv_done() and p9_cm_event_handler(), matching the pattern already used in rdma_request(). Use spin_lock_irqsave/spin_unlock_irqrestore in the CM event handler since it can race with recv_done() which runs in softirq context. Tested with a kernel module that races two threads (simulating rdma_request and recv_done/CM handler) on rdma->state with proper locking: 5.5M+ FLUSHING writes over 27M iterations with 0 lost transitions. Fixes: 473c7dd1d7b5 ("9p/rdma: remove useless check in cm_event_handler") Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Assisted-by: GLM:GLM-5.1 Signed-off-by: Yizhou Zhao Message-ID: <20260529073933.77315-1-zhaoyz24@mails.tsinghua.edu.cn> Signed-off-by: Dominique Martinet Signed-off-by: Sasha Levin --- net/9p/trans_rdma.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/net/9p/trans_rdma.c b/net/9p/trans_rdma.c index aa5bd74d333f3..b4274f10fa448 100644 --- a/net/9p/trans_rdma.c +++ b/net/9p/trans_rdma.c @@ -128,25 +128,36 @@ p9_cm_event_handler(struct rdma_cm_id *id, struct rdma_cm_event *event) { struct p9_client *c = id->context; struct p9_trans_rdma *rdma = c->trans; + unsigned long flags; + switch (event->event) { case RDMA_CM_EVENT_ADDR_RESOLVED: + spin_lock_irqsave(&rdma->req_lock, flags); BUG_ON(rdma->state != P9_RDMA_INIT); rdma->state = P9_RDMA_ADDR_RESOLVED; + spin_unlock_irqrestore(&rdma->req_lock, flags); break; case RDMA_CM_EVENT_ROUTE_RESOLVED: + spin_lock_irqsave(&rdma->req_lock, flags); BUG_ON(rdma->state != P9_RDMA_ADDR_RESOLVED); rdma->state = P9_RDMA_ROUTE_RESOLVED; + spin_unlock_irqrestore(&rdma->req_lock, flags); break; case RDMA_CM_EVENT_ESTABLISHED: + spin_lock_irqsave(&rdma->req_lock, flags); BUG_ON(rdma->state != P9_RDMA_ROUTE_RESOLVED); rdma->state = P9_RDMA_CONNECTED; + spin_unlock_irqrestore(&rdma->req_lock, flags); break; case RDMA_CM_EVENT_DISCONNECTED: - if (rdma) + if (rdma) { + spin_lock_irqsave(&rdma->req_lock, flags); rdma->state = P9_RDMA_CLOSED; + spin_unlock_irqrestore(&rdma->req_lock, flags); + } c->status = Disconnected; break; @@ -184,6 +195,7 @@ recv_done(struct ib_cq *cq, struct ib_wc *wc) struct p9_req_t *req; int err = 0; int16_t tag; + unsigned long flags; req = NULL; ib_dma_unmap_single(rdma->cm_id->device, c->busa, client->msize, @@ -220,7 +232,10 @@ recv_done(struct ib_cq *cq, struct ib_wc *wc) err_out: p9_debug(P9_DEBUG_ERROR, "req %p err %d status %d\n", req, err, wc->status); - rdma->state = P9_RDMA_FLUSHING; + spin_lock_irqsave(&rdma->req_lock, flags); + if (rdma->state < P9_RDMA_FLUSHING) + rdma->state = P9_RDMA_FLUSHING; + spin_unlock_irqrestore(&rdma->req_lock, flags); client->status = Disconnected; goto out; } From 0c44866f4a23b9c2ceebedaf1b3a842857095e07 Mon Sep 17 00:00:00 2001 From: Gui-Dong Han Date: Fri, 29 May 2026 15:54:41 +0800 Subject: [PATCH 1500/2758] 9p: Add missing read barrier in virtio zero-copy path [ Upstream commit aa88278693cbfaf7a2acf961379973fbb63b165c ] Commit 2b6e72ed747f ("9P: Add memory barriers to protect request fields over cb/rpc threads handoff") added a read barrier after p9_client_rpc() waits for req->status, pairing with the write barrier in p9_client_cb(). The virtio zero-copy wait path was missed. Add the same read barrier after the zero-copy wait before reading the completed request. Fixes: 2b6e72ed747f ("9P: Add memory barriers to protect request fields over cb/rpc threads handoff") Signed-off-by: Gui-Dong Han Message-ID: <20260529075441.233369-1-hanguidong02@gmail.com> Signed-off-by: Dominique Martinet Signed-off-by: Sasha Levin --- net/9p/trans_virtio.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c index 4cdab7094b273..b0d0094ec8e2c 100644 --- a/net/9p/trans_virtio.c +++ b/net/9p/trans_virtio.c @@ -532,6 +532,11 @@ p9_virtio_zc_request(struct p9_client *client, struct p9_req_t *req, p9_debug(P9_DEBUG_TRANS, "virtio request kicked\n"); err = io_wait_event_killable(req->wq, READ_ONCE(req->status) >= REQ_STATUS_RCVD); + /* + * Make sure our req is coherent with regard to updates in other + * threads - echoes to wmb() in the callback + */ + smp_rmb(); // RERROR needs reply (== error string) in static data if (READ_ONCE(req->status) == REQ_STATUS_RCVD && unlikely(req->rc.sdata[4] == P9_RERROR)) From b698ee9abf409e0ddd4c9f5eceb56fd27cc956d8 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 24 Apr 2026 15:46:32 +0200 Subject: [PATCH 1501/2758] eventpoll: expand top-of-file overview / locking doc [ Upstream commit 7c25a0bd4bf7139944c5893ff61211f4b5a3455e ] The existing ~40-line "LOCKING:" banner covered the three-level lock hierarchy (epnested_mutex > ep->mtx > ep->lock) but nothing else. Lifetime rules, the ready-list state machine, the three removal paths, and the POLLFREE contract are implicit in the code. The recent UAF series (a6dc643c6931, 07712db80857, 8c2e52ebbe88, f2e467a48287) rode on invariants that were only implicit. Codify them at the top of the file: the subsystem overview, the lock hierarchy and its mutex_lock_nested() subclass convention (reworded from the old banner), a field-protection table for struct eventpoll and struct epitem that names the two faces of the rbn/rcu union (rbn under ep->mtx while linked into ep->rbr; rcu touched only by kfree_rcu(epi) on the free path), the ovflist sentinel encoding and scan-flip invariants, the three removal paths (A ep_remove, B ep_clear_and_put, C eventpoll_release_file) and the epi_fget() pin that orchestrates A vs C, and the POLLFREE store-release / load-acquire handshake. No functional change. Signed-off-by: Christian Brauner (Amutable) Link: https://patch.msgid.link/20260424-work-epoll-rework-v1-1-249ed00a20f3@kernel.org Signed-off-by: Christian Brauner Stable-dep-of: 0c4aefe3c2d0 ("eventpoll: Fix epoll_wait() report false negative") Signed-off-by: Sasha Levin --- fs/eventpoll.c | 199 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 162 insertions(+), 37 deletions(-) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index dc8dc529684bf..9cd6b3511f065 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -41,45 +41,170 @@ #include /* - * LOCKING: - * There are three level of locking required by epoll : + * fs/eventpoll.c - Efficient event polling ("epoll") kernel implementation. * - * 1) epnested_mutex (mutex) - * 2) ep->mtx (mutex) - * 3) ep->lock (spinlock) * - * The acquire order is the one listed above, from 1 to 3. - * We need a spinlock (ep->lock) because we manipulate objects - * from inside the poll callback, that might be triggered from - * a wake_up() that in turn might be called from IRQ context. - * So we can't sleep inside the poll callback and hence we need - * a spinlock. During the event transfer loop (from kernel to - * user space) we could end up sleeping due a copy_to_user(), so - * we need a lock that will allow us to sleep. This lock is a - * mutex (ep->mtx). It is acquired during the event transfer loop, - * during epoll_ctl(EPOLL_CTL_DEL) and during eventpoll_release_file(). - * The epnested_mutex is acquired when inserting an epoll fd onto another - * epoll fd. We do this so that we walk the epoll tree and ensure that this - * insertion does not create a cycle of epoll file descriptors, which - * could lead to deadlock. We need a global mutex to prevent two - * simultaneous inserts (A into B and B into A) from racing and - * constructing a cycle without either insert observing that it is - * going to. - * It is necessary to acquire multiple "ep->mtx"es at once in the - * case when one epoll fd is added to another. In this case, we - * always acquire the locks in the order of nesting (i.e. after - * epoll_ctl(e1, EPOLL_CTL_ADD, e2), e1->mtx will always be acquired - * before e2->mtx). Since we disallow cycles of epoll file - * descriptors, this ensures that the mutexes are well-ordered. In - * order to communicate this nesting to lockdep, when walking a tree - * of epoll file descriptors, we use the current recursion depth as - * the lockdep subkey. - * It is possible to drop the "ep->mtx" and to use the global - * mutex "epnested_mutex" (together with "ep->lock") to have it working, - * but having "ep->mtx" will make the interface more scalable. - * Events that require holding "epnested_mutex" are very rare, while for - * normal operations the epoll private "ep->mtx" will guarantee - * a better scalability. + * Overview + * -------- + * + * Each epoll_create(2) returns an anonymous [eventpoll] file whose + * ->private_data is a struct eventpoll. Each EPOLL_CTL_ADD installs + * a struct epitem linking one (watched file, fd) pair back to that + * eventpoll via the watched file's f_op->poll() wait queue(s). When + * the watched file signals readiness, ep_poll_callback() fires and + * marks the epitem ready. epoll_wait(2) drains the ready list under + * ep->mtx, re-queueing items in level-triggered mode. + * + * epoll instances can watch other epoll instances up to EP_MAX_NESTS + * deep; cycles are forbidden and detected at EPOLL_CTL_ADD time. + * + * + * Locking + * ------- + * + * Three levels, acquired from outer to inner: + * + * epnested_mutex (global; rare; taken only for EPOLL_CTL_ADD + * loop / path checks) + * > ep->mtx (per-eventpoll; sleepable; serializes most ops) + * > ep->lock (per-eventpoll; IRQ-safe spinlock) + * + * file->f_lock (per-file; NOT IRQ-safe; guards f_ep hlist ops; + * nested inside ep->mtx, outside ep->lock) + * + * Rationale: + * - ep->lock is a spinlock because ep_poll_callback() is called from + * wake_up() which may run in hard-IRQ context. All ep->lock + * critical sections use spin_lock_irqsave(). + * - ep->mtx is a sleepable mutex because the event delivery loop + * calls copy_to_user(), and ep_insert() may sleep in + * kmem_cache_alloc() and f_op->poll(). + * - epnested_mutex is global because cycle detection needs a global + * view of the epoll topology; a per-object scheme would let two + * concurrent inserts (A into B, B into A) construct a cycle + * without either observer seeing it. + * - Per-ep ep->mtx is preferred for scalability elsewhere. Events + * that require epnested_mutex are rare. + * + * When EPOLL_CTL_ADD nests one eventpoll inside another we acquire + * ep->mtx on both: outer first, target second. Since cycles are + * forbidden the set of live ep->mtx holds is always a strict chain, + * communicated to lockdep via mutex_lock_nested() subclasses derived + * from the current recursion depth. + * + * + * Field protection + * ---------------- + * + * struct eventpoll: + * mtx - self + * rbr - ep->mtx + * ovflist, rdllist - ep->lock (IRQ-safe) + * wq - ep->lock for queue mutation + * poll_wait - internal waitqueue spinlock + * refs - file->f_lock for adds; ep->mtx for removes; + * RCU for readers (hlist_del_rcu + kfree_rcu(ep)) + * ws - ep->mtx + * gen, loop_check_depth - epnested_mutex + * file, user - immutable after setup + * refcount - atomic (refcount_t) + * napi_* - READ_ONCE / WRITE_ONCE + * + * struct epitem: + * rbn / rcu union - rbn: ep->mtx (while epi is linked in ep->rbr). + * rcu: written only by kfree_rcu(epi) on the free + * path; otherwise untouched by epoll code. + * rdllink, next - ep->lock + * ffd, ep - immutable after ep_insert() + * pwqlist - ep->mtx for writes; POLLFREE clears pwq->whead + * via smp_store_release(), see below + * fllink - file->f_lock for mutation; hlist_del_rcu + + * kfree_rcu(epi) for safe RCU readers + * ws - RCU (rcu_assign_pointer / + * rcu_dereference_check(mtx)) + * event - ep->mtx for writes; lockless read in + * ep_poll_callback pairs with smp_mb() in + * ep_modify() + * + * + * Ready-list state machine + * ------------------------ + * + * Readiness is tracked in two lists under ep->lock: + * + * rdllist - doubly-linked FIFO; the "current" ready list. + * ovflist - singly-linked LIFO; used during a scan to catch + * events that arrive while rdllist is being iterated + * without ep->lock. + * + * Encoded in ep->ovflist: + * EP_UNACTIVE_PTR - no scan active; callback appends to rdllist. + * NULL - scan active, no spill yet. + * pointer to epi - scan active with spilled items (LIFO). + * + * Encoded in epi->next: + * EP_UNACTIVE_PTR - epi is not on ovflist. + * otherwise - next epi on ovflist (NULL at tail). + * + * ep_start_scan() flips "not scanning" to "scanning" and splices + * rdllist into a caller-local txlist. ep_done_scan() drains ovflist + * back to rdllist (list_add head-insert reverses LIFO to FIFO), + * flips back to "not scanning", and re-splices any items the caller + * left in txlist (e.g., level-triggered re-queues). + * + * + * Removal paths + * ------------- + * + * Three paths dispose of epitems and/or eventpolls: + * + * A. ep_remove() - EPOLL_CTL_DEL and ep_insert() + * rollback. Caller holds ep->mtx. + * B. ep_clear_and_put() - close of the epoll fd itself + * (ep_eventpoll_release). + * C. eventpoll_release_file() - close of a watched file, invoked + * from __fput(). + * + * Coordination: + * A and C exclude each other via the watched file's refcount. + * A pins the file with epi_fget() before touching file->f_ep or + * file->f_lock; if the pin fails, __fput() is in flight and C + * will clean this epi up. See the epi_fget() block comment. + * A and B both hold ep->mtx serially. B walks the rbtree with + * rb_next() captured before ep_remove() erases the current node. + * B and C both take ep->mtx; the loser sees fewer entries or an + * empty file->f_ep. + * + * Within every path the internal order is strict: + * ep_unregister_pollwait() - drain pwqlist; synchronizes with any + * in-flight ep_poll_callback via the + * watched wait-queue head's lock. + * ep_remove_file() - hlist_del_rcu of epi->fllink and, + * if last watcher, clear file->f_ep, + * under file->f_lock. + * ep_remove_epi() - rb_erase, rdllist unlink (ep->lock), + * wakeup_source_unregister, + * kfree_rcu(epi). + * + * kfree_rcu(epi) defers the free past RCU readers in + * reverse_path_check_proc(); kfree_rcu(ep) defers past readers in + * ep_get_upwards_depth_proc(). + * + * + * POLLFREE handshake + * ------------------ + * + * When a subsystem tears down a wait-queue head that an epitem is + * registered on (binder, signalfd, ...), it wakes the callback with + * POLLFREE and must RCU-defer the head's free. The store/load pair: + * + * ep_poll_callback() POLLFREE branch: + * smp_store_release(&pwq->whead, NULL) + * + * ep_remove_wait_queue(): + * smp_load_acquire(&pwq->whead) + * + * See those sites for the full argument. */ /* Epoll private bits inside the event mask */ From 8679e9e068762d1964330dc4bc7717ddcac6da31 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 24 Apr 2026 15:46:46 +0200 Subject: [PATCH 1502/2758] eventpoll: rename epi->next and txlist for clarity [ Upstream commit f38567bb63ae029e3b63fcb99b6a2dcc6f421e69 ] Two list-related names were confusing in isolation: struct epitem::next A singly-linked link slot used only when an epi is queued on ep->ovflist during an ep_start_scan/ep_done_scan window. The bare name "next" suggests a generic list link and doesn't say which list it belongs to. txlist The caller-local list_head used by ep_send_events() and __ep_eventpoll_poll() to hold the batch of items stolen from ep->rdllist for the current scan. "txlist" ("transmission list") is abbreviated and overloaded: it doesn't distinguish itself from ep->rdllist or ep->ovflist at a glance. Rename for what each actually is: struct epitem::next -> struct epitem::ovflist_next local txlist -> scan_batch With these in place: - epi->ovflist_next reads as "this is the ep->ovflist link slot", matching the rdllink pattern above it. - scan_batch reads as "the batch currently being scanned", clearly distinct from rdllist (canonical ready list) and ovflist (scan-window overflow). ep->rdllist and ep->ovflist struct field names are preserved -- they are long-standing interface-facing identifiers, and the new inline helpers (ep_is_scanning, epi_on_ovflist, ...) already hide the sentinel semantics at call sites. No functional change. Signed-off-by: Christian Brauner (Amutable) Link: https://patch.msgid.link/20260424-work-epoll-rework-v1-15-249ed00a20f3@kernel.org Signed-off-by: Christian Brauner Stable-dep-of: 0c4aefe3c2d0 ("eventpoll: Fix epoll_wait() report false negative") Signed-off-by: Sasha Levin --- fs/eventpoll.c | 62 ++++++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 9cd6b3511f065..c2f97187f9db4 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -142,15 +142,15 @@ * NULL - scan active, no spill yet. * pointer to epi - scan active with spilled items (LIFO). * - * Encoded in epi->next: + * Encoded in epi->ovflist_next: * EP_UNACTIVE_PTR - epi is not on ovflist. * otherwise - next epi on ovflist (NULL at tail). * * ep_start_scan() flips "not scanning" to "scanning" and splices - * rdllist into a caller-local txlist. ep_done_scan() drains ovflist + * rdllist into a caller-local scan_batch. ep_done_scan() drains ovflist * back to rdllist (list_add head-insert reverses LIFO to FIFO), * flips back to "not scanning", and re-splices any items the caller - * left in txlist (e.g., level-triggered re-queues). + * left in scan_batch (e.g., level-triggered re-queues). * * * Removal paths @@ -261,14 +261,16 @@ struct epitem { struct rcu_head rcu; }; - /* List header used to link this structure to the eventpoll ready list */ + /* Link on the owning eventpoll's ready list (ep->rdllist). */ struct list_head rdllink; /* - * Works together "struct eventpoll"->ovflist in keeping the - * single linked chain of items. + * Link on the owning eventpoll's scan-overflow list (ep->ovflist), + * EP_UNACTIVE_PTR when not linked. See epi_on_ovflist() / + * epi_clear_ovflist() and the "Ready-list state machine" section + * in the top-of-file banner. */ - struct epitem *next; + struct epitem *ovflist_next; /* The file descriptor information this item refers to */ struct epoll_filefd ffd; @@ -525,13 +527,13 @@ static inline void ep_exit_scan(struct eventpoll *ep) /* True iff @epi is currently linked on its ep's ovflist. */ static inline bool epi_on_ovflist(const struct epitem *epi) { - return epi->next != EP_UNACTIVE_PTR; + return epi->ovflist_next != EP_UNACTIVE_PTR; } /* Mark @epi as not on any ovflist (init and post-drain). */ static inline void epi_clear_ovflist(struct epitem *epi) { - epi->next = EP_UNACTIVE_PTR; + epi->ovflist_next = EP_UNACTIVE_PTR; } /** @@ -884,7 +886,7 @@ static inline void ep_pm_stay_awake_rcu(struct epitem *epi) * ep->mutex needs to be held because we could be hit by * eventpoll_release_file() and epoll_ctl(). */ -static void ep_start_scan(struct eventpoll *ep, struct list_head *txlist) +static void ep_start_scan(struct eventpoll *ep, struct list_head *scan_batch) { /* * Steal the ready list, and re-init the original one to the @@ -896,13 +898,13 @@ static void ep_start_scan(struct eventpoll *ep, struct list_head *txlist) */ lockdep_assert_irqs_enabled(); spin_lock_irq(&ep->lock); - list_splice_init(&ep->rdllist, txlist); + list_splice_init(&ep->rdllist, scan_batch); ep_enter_scan(ep); spin_unlock_irq(&ep->lock); } static void ep_done_scan(struct eventpoll *ep, - struct list_head *txlist) + struct list_head *scan_batch) { struct epitem *epi, *nepi; @@ -913,10 +915,10 @@ static void ep_done_scan(struct eventpoll *ep, * We re-insert them inside the main ready-list here. */ for (nepi = READ_ONCE(ep->ovflist); (epi = nepi) != NULL; ) { - nepi = epi->next; + nepi = epi->ovflist_next; epi_clear_ovflist(epi); /* - * Skip items that the caller already returned via @txlist + * Skip items that the caller already returned via @scan_batch * -- the list_splice() below takes care of those. */ if (!ep_is_linked(epi)) { @@ -932,9 +934,9 @@ static void ep_done_scan(struct eventpoll *ep, ep_exit_scan(ep); /* - * Quickly re-inject items left on "txlist". + * Quickly re-inject items left on "scan_batch". */ - list_splice(txlist, &ep->rdllist); + list_splice(scan_batch, &ep->rdllist); __pm_relax(ep->ws); if (!list_empty(&ep->rdllist)) { @@ -1155,7 +1157,7 @@ static __poll_t ep_item_poll(const struct epitem *epi, poll_table *pt, int depth static __poll_t __ep_eventpoll_poll(struct file *file, poll_table *wait, int depth) { struct eventpoll *ep = file->private_data; - LIST_HEAD(txlist); + LIST_HEAD(scan_batch); struct epitem *epi, *tmp; poll_table pt; __poll_t res = 0; @@ -1170,8 +1172,8 @@ static __poll_t __ep_eventpoll_poll(struct file *file, poll_table *wait, int dep * the ready list. */ mutex_lock_nested(&ep->mtx, depth); - ep_start_scan(ep, &txlist); - list_for_each_entry_safe(epi, tmp, &txlist, rdllink) { + ep_start_scan(ep, &scan_batch); + list_for_each_entry_safe(epi, tmp, &scan_batch, rdllink) { if (ep_item_poll(epi, &pt, depth + 1)) { res = EPOLLIN | EPOLLRDNORM; break; @@ -1185,7 +1187,7 @@ static __poll_t __ep_eventpoll_poll(struct file *file, poll_table *wait, int dep list_del_init(&epi->rdllink); } } - ep_done_scan(ep, &txlist); + ep_done_scan(ep, &scan_batch); mutex_unlock(&ep->mtx); return res; } @@ -1441,7 +1443,7 @@ static int ep_poll_callback(wait_queue_entry_t *wait, unsigned mode, int sync, v */ if (ep_is_scanning(ep)) { if (!epi_on_ovflist(epi)) { - epi->next = READ_ONCE(ep->ovflist); + epi->ovflist_next = READ_ONCE(ep->ovflist); WRITE_ONCE(ep->ovflist, epi); ep_pm_stay_awake_rcu(epi); } @@ -1962,7 +1964,7 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi, * next slot), 0 if the re-poll reported no caller-requested events * (@epi drops out of the ready list; a future callback will re-add * it), or -EFAULT if copy_to_user() faulted (in which case @epi is - * re-inserted at the head of @txlist so ep_done_scan() merges it + * re-inserted at the head of @scan_batch so ep_done_scan() merges it * back to rdllist for the next attempt). * * PM bookkeeping and level-triggered re-queue are handled here. @@ -1971,7 +1973,7 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi, static int ep_deliver_event(struct eventpoll *ep, struct epitem *epi, poll_table *pt, struct epoll_event __user **uevents, - struct list_head *txlist) + struct list_head *scan_batch) { struct epoll_event __user *next; struct wakeup_source *ws; @@ -2009,7 +2011,7 @@ static int ep_deliver_event(struct eventpoll *ep, struct epitem *epi, * ep_done_scan() splices it onto rdllist for the next * attempt. */ - list_add(&epi->rdllink, txlist); + list_add(&epi->rdllink, scan_batch); ep_pm_stay_awake(epi); return -EFAULT; } @@ -2035,7 +2037,7 @@ static int ep_send_events(struct eventpoll *ep, struct epoll_event __user *events, int maxevents) { struct epitem *epi, *tmp; - LIST_HEAD(txlist); + LIST_HEAD(scan_batch); poll_table pt; int res = 0; @@ -2050,19 +2052,19 @@ static int ep_send_events(struct eventpoll *ep, init_poll_funcptr(&pt, NULL); mutex_lock(&ep->mtx); - ep_start_scan(ep, &txlist); + ep_start_scan(ep, &scan_batch); /* * We can loop without lock because we are passed a task-private - * txlist; items cannot vanish while we hold ep->mtx. + * scan_batch; items cannot vanish while we hold ep->mtx. */ - list_for_each_entry_safe(epi, tmp, &txlist, rdllink) { + list_for_each_entry_safe(epi, tmp, &scan_batch, rdllink) { int delivered; if (res >= maxevents) break; - delivered = ep_deliver_event(ep, epi, &pt, &events, &txlist); + delivered = ep_deliver_event(ep, epi, &pt, &events, &scan_batch); if (delivered < 0) { if (!res) res = delivered; @@ -2071,7 +2073,7 @@ static int ep_send_events(struct eventpoll *ep, res += delivered; } - ep_done_scan(ep, &txlist); + ep_done_scan(ep, &scan_batch); mutex_unlock(&ep->mtx); return res; From 4791b91daeb1959865bf73be3bf141f58dbccd67 Mon Sep 17 00:00:00 2001 From: Nam Cao Date: Tue, 2 Jun 2026 19:51:46 +0200 Subject: [PATCH 1503/2758] eventpoll: Fix epoll_wait() report false negative [ Upstream commit 0c4aefe3c2d0f272a2ad73699a12d4446ffdbe7b ] ep_events_available() checks for available events by looking at ep->rdllist and ep_is_scanning(). However, this is done without a lock and can report false negative if ep_start_scan() or ep_done_scan() are executed by another task concurrently. For example: _________________________________________________________________________ |ep_start_scan() | list_splice_init(&ep->rdllist, ...) ep_events_available() | !list_empty_careful(&ep->rdllist)| || ep_is_scanning(ep) | | ep_enter_scan(ep) ___________________________________|_____________________________________ Another example: _________________________________________________________________________ ep_events_available() | |ep_start_scan() | list_splice_init(&ep->rdllist, ...) | ep_enter_scan(ep) !list_empty_careful(&ep->rdllist)| |ep_done_scan() | ep_exit_scan(ep) | list_splice(..., &ep->rdllist) || ep_is_scanning(ep) | ___________________________________|_____________________________________ In the above examples, ep_events_available() sees no event despite events being available. In case epoll_wait() is called with timeout=0, epoll_wait() will wrongly return "no event" to user. Introduce a sequence lock to resolve this issue. Measuring the time consumption of 10 million loop iterations doing epoll_wait(), the following performance drop is observed: timeout #event before after diff 0ms 0 3727ms 3974ms +6.6% 0ms 1 8099ms 9134ms +13% 1ms 1 13525ms 13586ms +0.45% Considering the use case of epoll_wait() (wait for events, do something with the events, repeat), it should only contribute to a small portion of user's CPU consumption. Therefore this performance drop is not alarming. Fixes: c5a282e9635e ("fs/epoll: reduce the scope of wq lock in epoll_wait()") Suggested-by: Mateusz Guzik Signed-off-by: Nam Cao Link: https://patch.msgid.link/4363cd8e34a21d4f0d257be1b33e84dc25030fdf.1780422138.git.namcao@linutronix.de Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/eventpoll.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index c2f97187f9db4..c35580194ad0e 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -38,6 +38,7 @@ #include #include #include +#include #include /* @@ -317,6 +318,9 @@ struct eventpoll { /* Lock which protects rdllist and ovflist */ spinlock_t lock; + /* Protect switching between rdllist and ovflist */ + seqcount_spinlock_t seq; + /* RB tree root used to store monitored fd structs */ struct rb_root_cached rbr; @@ -546,7 +550,10 @@ static inline void epi_clear_ovflist(struct epitem *epi) */ static inline int ep_events_available(struct eventpoll *ep) { - return !list_empty_careful(&ep->rdllist) || ep_is_scanning(ep); + unsigned int seq = read_seqcount_begin(&ep->seq); + + return !list_empty_careful(&ep->rdllist) || ep_is_scanning(ep) || + read_seqcount_retry(&ep->seq, seq); } #ifdef CONFIG_NET_RX_BUSY_POLL @@ -898,8 +905,12 @@ static void ep_start_scan(struct eventpoll *ep, struct list_head *scan_batch) */ lockdep_assert_irqs_enabled(); spin_lock_irq(&ep->lock); + write_seqcount_begin(&ep->seq); + list_splice_init(&ep->rdllist, scan_batch); ep_enter_scan(ep); + + write_seqcount_end(&ep->seq); spin_unlock_irq(&ep->lock); } @@ -930,6 +941,9 @@ static void ep_done_scan(struct eventpoll *ep, ep_pm_stay_awake(epi); } } + + write_seqcount_begin(&ep->seq); + /* Back out of scan mode; callbacks target ep->rdllist again. */ ep_exit_scan(ep); @@ -937,6 +951,9 @@ static void ep_done_scan(struct eventpoll *ep, * Quickly re-inject items left on "scan_batch". */ list_splice(scan_batch, &ep->rdllist); + + write_seqcount_end(&ep->seq); + __pm_relax(ep->ws); if (!list_empty(&ep->rdllist)) { @@ -1313,6 +1330,7 @@ static int ep_alloc(struct eventpoll **pep) mutex_init(&ep->mtx); spin_lock_init(&ep->lock); + seqcount_spinlock_init(&ep->seq, &ep->lock); init_waitqueue_head(&ep->wq); init_waitqueue_head(&ep->poll_wait); INIT_LIST_HEAD(&ep->rdllist); From b0194db10032e08e82fc6a0a359bde4a8bda68ab Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 28 Apr 2026 21:52:39 -0500 Subject: [PATCH 1504/2758] gpiolib: acpi: Only trigger ActiveBoth interrupts on boot [ Upstream commit 3bb62e3f99a557d257e5f5a803200051b7de3afa ] Commit ca876c7483b6 ("gpiolib-acpi: make sure we trigger edge events at least once on boot") introduced logic to trigger edge-based GPIO interrupts during initialization to ensure proper initial state setup when firmware doesn't initialize it. However, according to the Microsoft GPIO documentation, triggering GPIO interrupts during initialization should only happen for interrupts marked as ActiveBoth (both IRQF_TRIGGER_RISING and IRQF_TRIGGER_FALLING) and only when the associated GPIO line is already asserted (logic level low). The current implementation incorrectly triggers: 1. Any edge-triggered interrupt (RISING-only or FALLING-only) 2. RISING interrupts when value is high and FALLING when value is low This causes problems at bootup for single-edge interrupts that don't follow the ActiveBoth pattern. Fix this by: - Only triggering when BOTH rising and falling edges are configured - Only triggering when the GPIO line is asserted (value == 0) Reported-by: Francesco Lauritano Closes: https://lore.kernel.org/all/6iFCwGH2vssb7NRUTWGpkubGMNbgIlBHSz40z8ZsezjxngXpoiiRiJaijviNvhiDAGIr43bfUmdxLmxYoHDjyft4DgwFc3Pnu5hzPguTa0s=@protonmail.com/ Tested-by: Marco Scardovi Fixes: ca876c7483b69 ("gpiolib-acpi: make sure we trigger edge events at least once on boot") Link: https://learn.microsoft.com/en-us/windows-hardware/drivers/bringup/general-purpose-i-o--gpio- Suggested-by: Armin Wolf Signed-off-by: Mario Limonciello Reviewed-by: Mika Westerberg Reviewed-by: Hans de Goede Signed-off-by: Andy Shevchenko Signed-off-by: Sasha Levin --- drivers/gpio/gpiolib-acpi-core.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi-core.c b/drivers/gpio/gpiolib-acpi-core.c index 09f860200a059..eb8a40cfb7a98 100644 --- a/drivers/gpio/gpiolib-acpi-core.c +++ b/drivers/gpio/gpiolib-acpi-core.c @@ -233,12 +233,23 @@ static void acpi_gpiochip_request_irq(struct acpi_gpio_chip *acpi_gpio, event->irq_requested = true; - /* Make sure we trigger the initial state of edge-triggered IRQs */ + /* + * Make sure we trigger the initial state of ActiveBoth IRQs. + * + * According to the Microsoft GPIO documentation, triggering GPIO + * interrupts marked as ActiveBoth during initialization is correct + * as long as the associated GPIO line is already "asserted" + * (logic level low). We should not trigger edge-based GPIO + * interrupts not marked as ActiveBoth. + * + * See: https://learn.microsoft.com/en-us/windows-hardware/drivers/bringup/general-purpose-i-o--gpio- + * Section: "GPIO controllers and ActiveBoth interrupts" + */ if (acpi_gpio_need_run_edge_events_on_boot() && - (event->irqflags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))) { + ((event->irqflags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) == + (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))) { value = gpiod_get_raw_value_cansleep(event->desc); - if (((event->irqflags & IRQF_TRIGGER_RISING) && value == 1) || - ((event->irqflags & IRQF_TRIGGER_FALLING) && value == 0)) + if (value == 0) event->handler(event->irq, event); } } From 697af8745d5c61e05eafa055dd0444d5bc45e6e6 Mon Sep 17 00:00:00 2001 From: Stanley Chu Date: Mon, 13 Apr 2026 08:50:39 +0800 Subject: [PATCH 1505/2758] i3c: master: svc: Fix missed IBI after false SLVSTART on NPCM845 [ Upstream commit fa1d4fa118f4229168e9ca88cea260c5e5a94652 ] The NPCM845 I3C controller may raise a false SLVSTART interrupt. The handler first latches MSTATUS and then clears SLVSTART. If a real IBI request arrives after the handler latches MSTATUS but before it clears the SLVSTART interrupt status, HW sets the SLVREQ state. However, the handler still relies on the stale MSTATUS snapshot, returns early, and misses the real IBI. No further interrupt is generated for this pending IBI. Re-read MSTATUS to obtain the latest state and avoid missing a real IBI due to this race condition. Fixes: 4dd12e944f07 ("i3c: master: svc: Fix npcm845 invalid slvstart event") Signed-off-by: Stanley Chu Reviewed-by: Frank Li Link: https://patch.msgid.link/20260413005040.1211107-2-yschu@nuvoton.com Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/i3c/master/svc-i3c-master.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c index e2d99a3ac07da..63063741fbd12 100644 --- a/drivers/i3c/master/svc-i3c-master.c +++ b/drivers/i3c/master/svc-i3c-master.c @@ -672,10 +672,18 @@ static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id) /* Clear the interrupt status */ writel(SVC_I3C_MINT_SLVSTART, master->regs + SVC_I3C_MSTATUS); - /* Ignore the false event */ - if (svc_has_quirk(master, SVC_I3C_QUIRK_FALSE_SLVSTART) && - !SVC_I3C_MSTATUS_STATE_SLVREQ(active)) - return IRQ_HANDLED; + if (svc_has_quirk(master, SVC_I3C_QUIRK_FALSE_SLVSTART)) { + /* + * Re-read MSTATUS to obtain the latest state and avoid + * missing an IBI that arrives after MSTATUS is latched + * but before SLVSTART is cleared. + */ + active = readl(master->regs + SVC_I3C_MSTATUS); + + /* Ignore the false event */ + if (!SVC_I3C_MSTATUS_STATE_SLVREQ(active)) + return IRQ_HANDLED; + } /* * The SDA line remains low until the request is processed. From fde2296f87b72ccd72460ce13ad56e8652b321d4 Mon Sep 17 00:00:00 2001 From: Maksym Pikhotskyi Date: Fri, 17 Apr 2026 13:54:51 +0400 Subject: [PATCH 1506/2758] staging: rtl8723bs: fix stainfo check in rtw_aes_decrypt [ Upstream commit 9a3f9b3c47d8f071b0eb9e63906ac0448058278d ] The null-pointer-guard was incorrect, returning _FAIL on valid pointer. Invert the guard, so it returns _FAIL on invalid pointer. Fixes: e23ad1570028 ("staging: rtl8723bs: use guard clause for stainfo check") Reported-by: Luka Gejak Closes: https://lore.kernel.org/linux-staging/E4BF62EF-C6F6-431F-8EDC-77C1E613E66B@linux.dev/ Reviewed-by: Dan Carpenter Signed-off-by: Maksym Pikhotskyi Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260417095452.23440-1-mpikhotskyi@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/staging/rtl8723bs/core/rtw_security.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_security.c b/drivers/staging/rtl8723bs/core/rtw_security.c index a00504ff29109..f467cb5b1dca3 100644 --- a/drivers/staging/rtl8723bs/core/rtw_security.c +++ b/drivers/staging/rtl8723bs/core/rtw_security.c @@ -1212,7 +1212,7 @@ u32 rtw_aes_decrypt(struct adapter *padapter, u8 *precvframe) if (prxattrib->encrypt != _AES_) return _SUCCESS; stainfo = rtw_get_stainfo(&padapter->stapriv, &prxattrib->ta[0]); - if (stainfo) + if (!stainfo) return _FAIL; if (is_multicast_ether_addr(prxattrib->ra)) { static unsigned long start; From 5de04caa46b635e180cecbd164e333eca535db94 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Mon, 27 Apr 2026 10:17:12 +0200 Subject: [PATCH 1507/2758] staging: nvec: fix use-after-free in nvec_rx_completed() [ Upstream commit 26813881181deb3a32fbb59eadb2599cbe8423f6 ] In nvec_rx_completed(), when an incomplete RX transfer is detected, nvec_msg_free() is called to return the message back to the pool by clearing its 'used' atomic flag. Immediately after this, the code accesses nvec->rx->data[0] to check the message type. Since nvec_msg_free() marks the pool slot as available via atomic_set(), any concurrent or subsequent call to nvec_msg_alloc() could claim that same slot and overwrite its data[] array. Reading nvec->rx->data[0] after freeing the message is therefore a use-after-free. Fix this by saving the message type byte before calling nvec_msg_free(), then using the saved value for the battery quirk check. Fixes: d6bdcf2e1019 ("staging: nvec: Add battery quirk to ignore incomplete responses") Reviewed-by: Dan Carpenter Signed-off-by: Alexandru Hossu Link: https://patch.msgid.link/20260427081713.3401874-2-hossu.alexandru@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/staging/nvec/nvec.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/nvec/nvec.c b/drivers/staging/nvec/nvec.c index 952c5a849a563..2a3499dd4d634 100644 --- a/drivers/staging/nvec/nvec.c +++ b/drivers/staging/nvec/nvec.c @@ -494,6 +494,8 @@ static void nvec_tx_completed(struct nvec_chip *nvec) static void nvec_rx_completed(struct nvec_chip *nvec) { if (nvec->rx->pos != nvec_msg_size(nvec->rx)) { + unsigned char msg_type = nvec->rx->data[0]; + dev_err(nvec->dev, "RX incomplete: Expected %u bytes, got %u\n", (uint)nvec_msg_size(nvec->rx), (uint)nvec->rx->pos); @@ -502,7 +504,7 @@ static void nvec_rx_completed(struct nvec_chip *nvec) nvec->state = 0; /* Battery quirk - Often incomplete, and likes to crash */ - if (nvec->rx->data[0] == NVEC_BAT) + if (msg_type == NVEC_BAT) complete(&nvec->ec_transfer); return; From 23ec342a8fa649c543fe8cc8a62eff88fe74df3b Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 4 May 2026 01:12:19 -0700 Subject: [PATCH 1508/2758] perf dwarf-aux: Fix libdw segmentation fault in cu_walk_functions_at [ Upstream commit d37a5467709e627370124d7346ef71ce605ababa ] A segmentation fault was observed in `libdw` when running `perf kmem` with `--page stat` on some workloads. The crash occurred deep inside `libdw` (specifically in `dwarf_child` and `dwarf_diename`) when processing DWARF information. The root cause was improper error handling of `dwarf_getfuncs` in `die_find_realfunc` and `die_find_tailfunc`. `dwarf_getfuncs` returns: - `0` on success (when all functions have been processed). - A positive offset if the callback aborts early (e.g., via `DWARF_CB_ABORT` when a match is found). - `-1` on error. The original code used `if (!dwarf_getfuncs(...)) return NULL;`. On error (`-1`), `!-1` evaluates to `0` (false), bypassing the error check. Execution then proceeded as if a match was found, returning uninitialized stack memory (`die_mem`) to the caller (`cu_walk_functions_at`). When `cu_walk_functions_at` passed this uninitialized memory to `libdw` via `dwarf_diename`, it caused a segmentation fault. Fix this by correcting the error check to `if (dwarf_getfuncs(...) <= 0)`. Fixes: e0d153c69040 ("perf-probe: Move dwarf library routines to dwarf-aux.{c, h}") Fixes: d4c537e6bf86 ("perf probe: Ignore tail calls to probed functions") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Peter Zijlstra Cc: Zecheng Li Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/dwarf-aux.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index 92db2fccc788a..109a166a6d19c 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -171,7 +171,6 @@ int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr, } return ret; - } /** @@ -620,7 +619,7 @@ Dwarf_Die *die_find_tailfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, ad.addr = addr; ad.die_mem = die_mem; /* dwarf_getscopes can't find subprogram. */ - if (!dwarf_getfuncs(cu_die, __die_search_func_tail_cb, &ad, 0)) + if (dwarf_getfuncs(cu_die, __die_search_func_tail_cb, &ad, 0) <= 0) return NULL; else return die_mem; @@ -659,7 +658,7 @@ Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, ad.addr = addr; ad.die_mem = die_mem; /* dwarf_getscopes can't find subprogram. */ - if (!dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0)) + if (dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0) <= 0) return NULL; else return die_mem; From eb0062b3e76d8ef6d78eabcdda771424fc9276b4 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 4 May 2026 01:12:20 -0700 Subject: [PATCH 1509/2758] perf dwarf-aux: Fix libdw API contract violations [ Upstream commit 17e9b4243e76f0a0fe951d30ce990d6081a8b426 ] Check return values of `dwarf_decl_line` (where non-optional), `dwarf_getfuncs`, and `dwarf_lineaddr` to prevent using uninitialized stack variables or incorrectly reporting success on failure. For the root DIE in `die_walk_lines()`, `dwarf_decl_line` and `die_get_decl_file` are optional and their failures are handled gracefully to avoid breaking line walking on valid functions. Specifically, remove the strict `!decf` (declared file) check that would prematurely abort line walking on generated or artificial functions lacking this optional attribute. Additionally: - Add NULL pointer protection for `strcmp()` in `die_walk_lines()` when `inf` or `decf` are NULL to prevent crashes on generated code. - Use `dwarf_attr_integrate` in `die_get_data_member_location` to correctly resolve inherited member locations (e.g. via abstract origin or specification). Fixes: 57f95bf5f882 ("perf probe: Show correct statement line number by perf probe -l") Fixes: 3f4460a28fb2 ("perf probe: Filter out redundant inline-instances") Fixes: 75186a9b09e4 ("perf probe: Fix to show lines of sys_ functions correctly") Fixes: e0d153c69040 ("perf-probe: Move dwarf library routines to dwarf-aux.{c, h}") Fixes: 6243b9dc4c99 ("perf probe: Move dwarf specific functions to dwarf-aux.c") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zecheng Li Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/dwarf-aux.c | 34 +++++++++++++++++----------------- tools/perf/util/dwarf-aux.h | 5 +++++ 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index 109a166a6d19c..d7160f87ac7d7 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -125,7 +125,8 @@ int cu_find_lineinfo(Dwarf_Die *cu_die, Dwarf_Addr addr, && die_entrypc(&die_mem, &faddr) == 0 && faddr == addr) { *fname = die_get_decl_file(&die_mem); - dwarf_decl_line(&die_mem, lineno); + if (dwarf_decl_line(&die_mem, lineno) != 0) + return -ENOENT; goto out; } @@ -459,7 +460,7 @@ int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs) size_t nexpr; int ret; - if (dwarf_attr(mb_die, DW_AT_data_member_location, &attr) == NULL) + if (dwarf_attr_integrate(mb_die, DW_AT_data_member_location, &attr) == NULL) return -ENOENT; if (dwarf_formudata(&attr, offs) != 0) { @@ -795,8 +796,7 @@ static int __die_walk_instances_cb(Dwarf_Die *inst, void *data) /* Ignore redundant instances */ if (dwarf_tag(inst) == DW_TAG_inlined_subroutine) { - dwarf_decl_line(origin, &tmp); - if (die_get_call_lineno(inst) == tmp) { + if (dwarf_decl_line(origin, &tmp) == 0 && die_get_call_lineno(inst) == tmp) { tmp = die_get_decl_fileno(origin); if (die_get_call_fileno(inst) == tmp) return DIE_FIND_CB_CONTINUE; @@ -950,11 +950,6 @@ int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data) cu_die = dwarf_diecu(rt_die, &die_mem, NULL, NULL); dwarf_decl_line(rt_die, &decl); decf = die_get_decl_file(rt_die); - if (!decf) { - pr_debug2("Failed to get the declared file name of %s\n", - dwarf_diename(rt_die)); - return -EINVAL; - } } else cu_die = rt_die; if (!cu_die) { @@ -998,11 +993,12 @@ int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data) if (die_find_inlinefunc(rt_die, addr, &die_mem)) { /* Call-site check */ inf = die_get_call_file(&die_mem); - if ((inf && !strcmp(inf, decf)) && + if ((inf == decf || (inf && decf && !strcmp(inf, decf))) && die_get_call_lineno(&die_mem) == lineno) goto found; - dwarf_decl_line(&die_mem, &inl); + if (dwarf_decl_line(&die_mem, &inl) != 0) + inl = 0; if (inl != decl || decf != die_get_decl_file(&die_mem)) continue; @@ -1034,8 +1030,10 @@ int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data) .data = data, .retval = 0, }; - dwarf_getfuncs(cu_die, __die_walk_culines_cb, ¶m, 0); - ret = param.retval; + if (dwarf_getfuncs(cu_die, __die_walk_culines_cb, ¶m, 0) < 0) + ret = -EINVAL; + else + ret = param.retval; } return ret; @@ -1939,10 +1937,12 @@ static bool die_get_postprologue_addr(unsigned long entrypc_idx, break; } - dwarf_lineaddr(line, postprologue_addr); - if (*postprologue_addr >= highpc) - dwarf_lineaddr(dwarf_onesrcline(lines, i - 1), - postprologue_addr); + if (dwarf_lineaddr(line, postprologue_addr) != 0) + return false; + if (*postprologue_addr >= highpc) { + if (dwarf_lineaddr(dwarf_onesrcline(lines, i - 1), postprologue_addr) != 0) + return false; + } return true; } diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h index a79968a2e573b..161f0bf980b6e 100644 --- a/tools/perf/util/dwarf-aux.h +++ b/tools/perf/util/dwarf-aux.h @@ -10,6 +10,11 @@ #include #include +static inline const char *die_name(Dwarf_Die *die) +{ + return dwarf_diename(die) ?: ""; +} + struct strbuf; /* Find the realpath of the target file */ From e542c8800bbc714d0e7bcca0d414ff2b3a745a06 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 4 May 2026 01:12:21 -0700 Subject: [PATCH 1510/2758] perf srcline: Introduce inline_node__clear_frames() commit 674dea094ef6594ea1aa0efb074a4646a81350b2 upstream. Introduce inline_node__clear_frames() to clean up partial allocations. This is a prerequisite for error handling in libdw inline unwinding. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zecheng Li Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Greg Kroah-Hartman --- tools/perf/util/srcline.c | 9 ++++++++- tools/perf/util/srcline.h | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index db164d2581634..62884428fb5a0 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c @@ -429,10 +429,13 @@ struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr, return addr2inlines(dso_name, addr, dso, sym); } -void inline_node__delete(struct inline_node *node) +void inline_node__clear_frames(struct inline_node *node) { struct inline_list *ilist, *tmp; + if (node == NULL) + return; + list_for_each_entry_safe(ilist, tmp, &node->val, list) { list_del_init(&ilist->list); zfree_srcline(&ilist->srcline); @@ -441,7 +444,11 @@ void inline_node__delete(struct inline_node *node) symbol__delete(ilist->symbol); free(ilist); } +} +void inline_node__delete(struct inline_node *node) +{ + inline_node__clear_frames(node); free(node); } diff --git a/tools/perf/util/srcline.h b/tools/perf/util/srcline.h index 7c37b3bf9ce79..1018cbc886d62 100644 --- a/tools/perf/util/srcline.h +++ b/tools/perf/util/srcline.h @@ -47,6 +47,7 @@ struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr, struct symbol *sym); /* free resources associated to the inline node list */ void inline_node__delete(struct inline_node *node); +void inline_node__clear_frames(struct inline_node *node); /* insert the inline node list into the DSO, which will take ownership */ void inlines__tree_insert(struct rb_root_cached *tree, From d739d9f4525b8cd475b8f84b0ac901d8bc24925e Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 4 May 2026 01:12:24 -0700 Subject: [PATCH 1511/2758] perf libdw: Fix libdw API contract violations and memory leaks [ Upstream commit 9c6d535ddb794fb540c3b142ab1d7416d469c1de ] Check return values of `dwfl_report_end` and `dwfl_module_addrdie` to prevent using uninitialized stack variables or reporting success on failure. Additionally: - Ensure `*file` is freed and inline frames are cleared on error in `libdw__addr2line()` to prevent memory leaks and duplicated callchains when falling back to other unwinders. - Use `die_name()` safe wrapper inside the inline function unwinding callback (`libdw_a2l_cb`). - Refactor `libdw_a2l_cb`'s repeated memory error handling/cleanup paths using a cleaner goto control flow. Fixes: b7a2b011e9627ff3 ("perf powerpc: Unify the skip-callchain-idx libdw with that for addr2line") Fixes: 88c51002d06f9a68 ("perf addr2line: Add a libdw implementation") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zecheng Li Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/libdw.c | 49 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/tools/perf/util/libdw.c b/tools/perf/util/libdw.c index 2169778841030..15bf646d104f5 100644 --- a/tools/perf/util/libdw.c +++ b/tools/perf/util/libdw.c @@ -60,7 +60,10 @@ struct Dwfl *dso__libdw_dwfl(struct dso *dso) return NULL; } - dwfl_report_end(dwfl, /*removed=*/NULL, /*arg=*/NULL); + if (dwfl_report_end(dwfl, /*removed=*/NULL, /*arg=*/NULL) != 0) { + dwfl_end(dwfl); + return NULL; + } dso__set_libdw(dso, dwfl); return dwfl; @@ -72,18 +75,19 @@ struct libdw_a2l_cb_args { struct inline_node *node; char *leaf_srcline; bool leaf_srcline_used; + int err; }; static int libdw_a2l_cb(Dwarf_Die *die, void *_args) { struct libdw_a2l_cb_args *args = _args; - struct symbol *inline_sym = new_inline_sym(args->dso, args->sym, dwarf_diename(die)); + struct symbol *inline_sym = new_inline_sym(args->dso, args->sym, die_name(die)); const char *call_fname = die_get_call_file(die); char *call_srcline = srcline__unknown; struct inline_list *ilist; if (!inline_sym) - return -ENOMEM; + goto abort_enomem; /* Assign caller information to the parent. */ if (call_fname) @@ -103,12 +107,27 @@ static int libdw_a2l_cb(Dwarf_Die *die, void *_args) /* Add this symbol to the chain as the leaf. */ if (!args->leaf_srcline_used) { - inline_list__append_tail(inline_sym, args->leaf_srcline, args->node); + if (inline_list__append_tail(inline_sym, args->leaf_srcline, args->node) != 0) + goto abort_delete_sym; args->leaf_srcline_used = true; } else { - inline_list__append_tail(inline_sym, strdup(args->leaf_srcline), args->node); + char *srcline = strdup(args->leaf_srcline); + + if (!srcline) + goto abort_delete_sym; + if (inline_list__append_tail(inline_sym, srcline, args->node) != 0) { + free(srcline); + goto abort_delete_sym; + } } return 0; + +abort_delete_sym: + if (inline_sym->inlined) + symbol__delete(inline_sym); +abort_enomem: + args->err = -ENOMEM; + return DWARF_CB_ABORT; } int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr, @@ -162,11 +181,29 @@ int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr, .leaf_srcline = srcline_from_fileline(src ?: "", lineno), }; + if (!args.leaf_srcline) { + if (file && *file) { + free(*file); + *file = NULL; + } + return 0; + } + /* Walk from the parent down to the leaf. */ - cu_walk_functions_at(cudie, addr, libdw_a2l_cb, &args); + if (cudie) + cu_walk_functions_at(cudie, addr, libdw_a2l_cb, &args); if (!args.leaf_srcline_used) free(args.leaf_srcline); + + if (args.err) { + if (file && *file) { + free(*file); + *file = NULL; + } + inline_node__clear_frames(node); + return 0; + } } return 1; } From 881af00c02c07117fe243580a4e9b393ebd989e0 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 4 May 2026 01:12:25 -0700 Subject: [PATCH 1512/2758] perf probe-finder: Fix libdw API contract violations [ Upstream commit 9a2ef19b5f5218d35c161f272a901f6c070faf79 ] Check return values of `dwarf_formsdata`, `dwarf_entrypc`, `dwarf_highpc`, `dwarf_bytesize`, `dwarf_attr`, `dwarf_decl_line`, `dwarf_getfuncs`, and `dwarf_formref_die`. Validate `dwarf_diename` and `dwarf_diecu` results to prevent potential crashes. Fix C90 mixed declarations. Additionally: - Avoid vfprintf undefined behavior with NULL strings by using the `die_name()` helper for `dwarf_diename()` in `pr_*` calls, including when warning about tail calls. - Prevent NULL pointer dereference in `convert_variable_fields()` when processing array elements for variables in registers. - Fallback to offset 0 in `line_range_search_cb()` instead of skipping functions without `DW_AT_decl_line`. - Relax `dwarf_getfuncs` error checking in `find_probe_point_by_func()` and `find_line_range_by_func()` to prevent premature CU search aborts, ensuring robustness against corrupted CUs. Fixes: 66f69b2197167cb9 ("perf probe: Support DW_AT_const_value constant value") Fixes: 3d918a12a1b3088a ("perf probe: Find fentry mcount fuzzed parameter location") Fixes: bcfc082150c6b1e9 ("perf probe: Remove redundant dwarf functions") Fixes: 221d061182b8ff55 ("perf probe: Fix to search local variables in appropriate scope") Fixes: b55a87ade3839c33 ("perf probe: Remove die() from probe-finder code") Fixes: 4c859351226c920b ("perf probe: Support glob wildcards for function name") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zecheng Li Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/probe-finder.c | 102 +++++++++++++++++++++------------ 1 file changed, 65 insertions(+), 37 deletions(-) diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 64328abeef8b2..f3f9a1573502c 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -79,7 +79,7 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, unsigned int regn; Dwarf_Word offs = 0; bool ref = false; - const char *regs; + const char *regs, *name; int ret, ret2 = 0; if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL) @@ -93,7 +93,8 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, if (!tvar) return 0; - dwarf_formsdata(&attr, &snum); + if (dwarf_formsdata(&attr, &snum) != 0) + return -ENOENT; ret = asprintf(&tvar->value, "\\%ld", (long)snum); return ret < 0 ? -ENOMEM : 0; @@ -103,8 +104,7 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) return -EINVAL; /* Broken DIE ? */ if (dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0) { - ret = dwarf_entrypc(sp_die, &tmp); - if (ret) + if (dwarf_entrypc(sp_die, &tmp) != 0) return -ENOENT; if (probe_conf.show_location_range && @@ -115,8 +115,7 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, return -ENOENT; } - ret = dwarf_highpc(sp_die, &tmp); - if (ret) + if (dwarf_highpc(sp_die, &tmp) != 0) return -ENOENT; /* * This is fuzzed by fentry mcount. We try to find the @@ -138,12 +137,16 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, static_var: if (!tvar) return ret2; + /* Static variables on memory (not stack), make @varname */ - ret = strlen(dwarf_diename(vr_die)); + name = dwarf_diename(vr_die); + if (!name) + return -ENOENT; + ret = strlen(name); tvar->value = zalloc(ret + 2); if (tvar->value == NULL) return -ENOMEM; - snprintf(tvar->value, ret + 2, "@%s", dwarf_diename(vr_die)); + snprintf(tvar->value, ret + 2, "@%s", name); tvar->ref = alloc_trace_arg_ref((long)offs); if (tvar->ref == NULL) return -ENOMEM; @@ -234,13 +237,14 @@ static int convert_variable_type(Dwarf_Die *vr_die, } if (die_get_real_type(vr_die, &type) == NULL) { - pr_warning("Failed to get a type information of %s.\n", - dwarf_diename(vr_die)); + const char *name = dwarf_diename(vr_die); + + pr_warning("Failed to get a type information of %s.\n", name ?: ""); return -ENOENT; } pr_debug("%s type is %s.\n", - dwarf_diename(vr_die), dwarf_diename(&type)); + die_name(vr_die), die_name(&type)); if (cast && (!strcmp(cast, "string") || !strcmp(cast, "ustring"))) { /* String type */ @@ -249,7 +253,7 @@ static int convert_variable_type(Dwarf_Die *vr_die, ret != DW_TAG_array_type) { pr_warning("Failed to cast into string: " "%s(%s) is not a pointer nor array.\n", - dwarf_diename(vr_die), dwarf_diename(&type)); + die_name(vr_die), die_name(&type)); return -EINVAL; } if (die_get_real_type(&type, &type) == NULL) { @@ -272,7 +276,7 @@ static int convert_variable_type(Dwarf_Die *vr_die, !die_compare_name(&type, "unsigned char")) { pr_warning("Failed to cast into string: " "%s is not (unsigned) char *.\n", - dwarf_diename(vr_die)); + die_name(vr_die)); return -EINVAL; } tvar->type = strdup(cast); @@ -299,7 +303,7 @@ static int convert_variable_type(Dwarf_Die *vr_die, /* Check the bitwidth */ if (ret > MAX_BASIC_TYPE_BITS) { pr_info("%s exceeds max-bitwidth. Cut down to %d bits.\n", - dwarf_diename(&type), MAX_BASIC_TYPE_BITS); + die_name(&type), MAX_BASIC_TYPE_BITS); ret = MAX_BASIC_TYPE_BITS; } ret = snprintf(buf, 16, "%c%d", prefix, ret); @@ -333,12 +337,14 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, pr_warning("Failed to get the type of %s.\n", varname); return -ENOENT; } - pr_debug2("Var real type: %s (%x)\n", dwarf_diename(&type), + pr_debug2("Var real type: %s (%x)\n", die_name(&type), (unsigned)dwarf_dieoffset(&type)); tag = dwarf_tag(&type); if (field->name[0] == '[' && (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)) { + int bsize; + /* Save original type for next field or type */ memcpy(die_mem, &type, sizeof(*die_mem)); /* Get the type of this array */ @@ -346,7 +352,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, pr_warning("Failed to get the type of %s.\n", varname); return -ENOENT; } - pr_debug2("Array real type: %s (%x)\n", dwarf_diename(&type), + pr_debug2("Array real type: %s (%x)\n", die_name(&type), (unsigned)dwarf_dieoffset(&type)); if (tag == DW_TAG_pointer_type) { ref = zalloc(sizeof(struct probe_trace_arg_ref)); @@ -357,7 +363,15 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, else *ref_ptr = ref; } - ref->offset += dwarf_bytesize(&type) * field->index; + bsize = dwarf_bytesize(&type); + + if (bsize < 0) + return -EINVAL; + if (!ref) { + pr_warning("Array indexing not supported for variables in registers.\n"); + return -ENOTSUP; + } + ref->offset += bsize * field->index; ref->user_access = user_access; goto next; } else if (tag == DW_TAG_pointer_type) { @@ -414,7 +428,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, if (die_find_member(&type, field->name, die_mem) == NULL) { pr_warning("%s(type:%s) has no member %s.\n", varname, - dwarf_diename(&type), field->name); + die_name(&type), field->name); return -EINVAL; } @@ -461,7 +475,7 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) int ret; pr_debug("Converting variable %s into trace event.\n", - dwarf_diename(vr_die)); + die_name(vr_die)); ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops, &pf->sp_die, pf, pf->tvar); @@ -542,7 +556,7 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwfl_Module *mod, /* Verify the address is correct */ if (!dwarf_haspc(sp_die, paddr)) { pr_warning("Specified offset is out of %s\n", - dwarf_diename(sp_die)); + die_name(sp_die)); return -EINVAL; } @@ -599,7 +613,7 @@ static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf) if (!die_find_realfunc(&pf->cu_die, pf->addr, &pf->sp_die)) { if (die_find_tailfunc(&pf->cu_die, pf->addr, &pf->sp_die)) { pr_warning("Ignoring tail call from %s\n", - dwarf_diename(&pf->sp_die)); + die_name(&pf->sp_die)); return 0; } else { pr_warning("Failed to find probe point in any " @@ -611,10 +625,16 @@ static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf) memcpy(&pf->sp_die, sc_die, sizeof(Dwarf_Die)); /* Get the frame base attribute/ops from subprogram */ - dwarf_attr(&pf->sp_die, DW_AT_frame_base, &fb_attr); - ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); - if (ret <= 0 || nops == 0) { + if (dwarf_attr(&pf->sp_die, DW_AT_frame_base, &fb_attr) == NULL) { pf->fb_ops = NULL; + } else { + ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); + if (ret <= 0 || nops == 0) + pf->fb_ops = NULL; + } + + if (pf->fb_ops == NULL) { + /* Not supported */ } else if (nops == 1 && pf->fb_ops[0].atom == DW_OP_call_frame_cfa && (pf->cfi_eh != NULL || pf->cfi_dbg != NULL)) { if ((dwarf_cfi_addrframe(pf->cfi_eh, pf->addr, &frame) != 0 && @@ -667,8 +687,8 @@ static int find_best_scope_cb(Dwarf_Die *fn_die, void *data) } } else { /* With the line number, find the nearest declared DIE */ - dwarf_decl_line(fn_die, &lno); - if (lno < fsp->line && fsp->diff > fsp->line - lno) { + if (dwarf_decl_line(fn_die, &lno) == 0 && lno < fsp->line && + fsp->diff > fsp->line - lno) { /* Keep a candidate and continue */ fsp->diff = fsp->line - lno; memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die)); @@ -924,12 +944,12 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) /* Get probe address */ if (die_entrypc(in_die, &addr) != 0) { pr_warning("Failed to get entry address of %s.\n", - dwarf_diename(in_die)); + die_name(in_die)); return -ENOENT; } if (addr == 0) { pr_debug("%s has no valid entry address. skipped.\n", - dwarf_diename(in_die)); + die_name(in_die)); return -ENOENT; } pf->addr = addr; @@ -971,12 +991,13 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) if (pp->file && fname && strtailcmp(pp->file, fname)) return DWARF_CB_OK; - pr_debug("Matched function: %s [%lx]\n", dwarf_diename(sp_die), + pr_debug("Matched function: %s [%lx]\n", die_name(sp_die), (unsigned long)dwarf_dieoffset(sp_die)); pf->fname = fname; pf->abstrace_dieoffset = dwarf_dieoffset(sp_die); if (pp->line) { /* Function relative line */ - dwarf_decl_line(sp_die, &pf->lno); + if (dwarf_decl_line(sp_die, &pf->lno) != 0) + return DWARF_CB_OK; pf->lno += pp->line; param->retval = find_probe_point_by_line(pf); } else if (die_is_func_instance(sp_die)) { @@ -985,7 +1006,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) /* But in some case the entry address is 0 */ if (pf->addr == 0) { pr_debug("%s has no entry PC. Skipped\n", - dwarf_diename(sp_die)); + die_name(sp_die)); param->retval = 0; /* Real function */ } else if (pp->lazy_line) @@ -1018,7 +1039,8 @@ static int find_probe_point_by_func(struct probe_finder *pf) { struct dwarf_callback_param _param = {.data = (void *)pf, .retval = 0}; - dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0); + if (dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0) < 0) + pr_debug("Failed to get functions from CU\n"); return _param.retval; } @@ -1207,7 +1229,8 @@ static int copy_variables_cb(Dwarf_Die *die_mem, void *data) * points to correct die. */ if (dwarf_attr(die_mem, DW_AT_abstract_origin, &attr)) { - dwarf_formref_die(&attr, &var_die); + if (dwarf_formref_die(&attr, &var_die) == NULL) + goto out; if (pf->abstrace_dieoffset != dwarf_dieoffset(&var_die)) goto out; } @@ -1293,13 +1316,16 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) if (ret < 0) goto end; - tev->point.realname = strdup(dwarf_diename(sc_die)); + tev->point.realname = strdup(die_name(sc_die)); if (!tev->point.realname) { ret = -ENOMEM; goto end; } - tev->lang = dwarf_srclang(dwarf_diecu(sc_die, &pf->cu_die, NULL, NULL)); + if (dwarf_diecu(sc_die, &pf->cu_die, NULL, NULL) != NULL) + tev->lang = dwarf_srclang(&pf->cu_die); + else + tev->lang = DW_LANG_C; /* Fallback */ pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, tev->point.offset); @@ -1794,7 +1820,8 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) if (die_match_name(sp_die, lr->function) && die_is_func_def(sp_die)) { lf->fname = die_get_decl_file(sp_die); - dwarf_decl_line(sp_die, &lr->offset); + if (dwarf_decl_line(sp_die, &lr->offset) != 0) + lr->offset = 0; /* Fallback if no line info */ pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); lf->lno_s = lr->offset + lr->start; if (lf->lno_s < 0) /* Overflow */ @@ -1818,7 +1845,8 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) static int find_line_range_by_func(struct line_finder *lf) { struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; - dwarf_getfuncs(&lf->cu_die, line_range_search_cb, ¶m, 0); + if (dwarf_getfuncs(&lf->cu_die, line_range_search_cb, ¶m, 0) < 0) + pr_debug("Failed to get functions from CU\n"); return param.retval; } From 2a86103b44af9ad8e7b673ded90dd55fb04d0a66 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 4 May 2026 01:12:26 -0700 Subject: [PATCH 1513/2758] perf annotate-data: Fix libdw API contract violations [ Upstream commit 5aa1941900050a2c80d29bc7ee0dfbddbad8f294 ] Check return values of `dwarf_aggregate_size` and `dwarf_formudata`. Additionally: - Avoid `vfprintf` undefined behavior with `NULL` strings by using the `die_name()` helper for `dwarf_diename()` in `pr_*` calls. - Use `die_get_data_member_location()` (updated to use `dwarf_attr_integrate`) to correctly parse location expressions for inherited member locations in the fallback path when `dwarf_formudata()` fails. Fixes: 2bc3cf575a162a2c ("perf annotate-data: Improve debug message with location info") Fixes: 4a111cadac85362e ("perf annotate-data: Add member field in the data type") Fixes: 8b1042c425f6a5a9 ("perf annotate-data: Set bitfield member offset and size properly") Fixes: fc044c53b99fad03 ("perf annotate-data: Add dso->data_types tree") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zecheng Li Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/annotate-data.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index 1eff0a27237d9..63e3c54fab421 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -74,7 +74,8 @@ void pr_debug_type_name(Dwarf_Die *die, enum type_state_kind kind) break; } - dwarf_aggregate_size(die, &size); + if (dwarf_aggregate_size(die, &size) != 0) + size = 0; strbuf_init(&sb, 32); die_get_typename_from_type(die, &sb); @@ -146,9 +147,9 @@ static void pr_debug_scope(Dwarf_Die *scope_die) tag = dwarf_tag(scope_die); if (tag == DW_TAG_subprogram) - pr_info("[function] %s\n", dwarf_diename(scope_die)); + pr_info("[function] %s\n", die_name(scope_die)); else if (tag == DW_TAG_inlined_subroutine) - pr_info("[inlined] %s\n", dwarf_diename(scope_die)); + pr_info("[inlined] %s\n", die_name(scope_die)); else if (tag == DW_TAG_lexical_block) pr_info("[block]\n"); else @@ -250,9 +251,12 @@ static int __add_member_cb(Dwarf_Die *die, void *arg) if (dwarf_aggregate_size(&die_mem, &size) < 0) size = 0; - if (dwarf_attr_integrate(die, DW_AT_data_member_location, &attr)) - dwarf_formudata(&attr, &loc); - else { + if (dwarf_attr_integrate(die, DW_AT_data_member_location, &attr)) { + if (dwarf_formudata(&attr, &loc) != 0) { + if (die_get_data_member_location(die, &loc) != 0) + loc = 0; + } + } else { /* bitfield member */ if (dwarf_attr_integrate(die, DW_AT_data_bit_offset, &attr) && dwarf_formudata(&attr, &loc) == 0) @@ -273,7 +277,9 @@ static int __add_member_cb(Dwarf_Die *die, void *arg) dwarf_diename(die), (long)bit_size) < 0) member->var_name = NULL; } else { - member->var_name = strdup(dwarf_diename(die)); + const char *name = dwarf_diename(die); + + member->var_name = name ? strdup(name) : NULL; } if (member->var_name == NULL) { @@ -370,7 +376,8 @@ static struct annotated_data_type *dso__findnew_data_type(struct dso *dso, if (dwarf_tag(type_die) == DW_TAG_typedef) die_get_real_type(type_die, type_die); - dwarf_aggregate_size(type_die, &size); + if (dwarf_aggregate_size(type_die, &size) != 0) + size = 0; /* Check existing nodes in dso->data_types tree */ key.self.type_name = type_name; @@ -1569,7 +1576,7 @@ static int find_data_type_die(struct data_loc_info *dloc, Dwarf_Die *type_die) offset = loc->offset; pr_debug_dtp("CU for %s (die:%#lx)\n", - dwarf_diename(&cu_die), (long)dwarf_dieoffset(&cu_die)); + die_name(&cu_die), (long)dwarf_dieoffset(&cu_die)); if (reg == DWARF_REG_PC) { if (get_global_var_type(&cu_die, dloc, dloc->ip, dloc->var_addr, @@ -1636,7 +1643,7 @@ static int find_data_type_die(struct data_loc_info *dloc, Dwarf_Die *type_die) } pr_debug_dtp("found \"%s\" (die: %#lx) in scope=%d/%d (die: %#lx) ", - dwarf_diename(&var_die), (long)dwarf_dieoffset(&var_die), + die_name(&var_die), (long)dwarf_dieoffset(&var_die), i+1, nr_scopes, (long)dwarf_dieoffset(&scopes[i])); if (reg == DWARF_REG_PC) { From ffddd64eae0b39b8074bbcee24b0fc10cef42b2e Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 4 May 2026 01:12:27 -0700 Subject: [PATCH 1514/2758] perf debuginfo: Fix libdw API contract violations [ Upstream commit 31088ccf0312b1a547046f1f69890ede07834a30 ] Check return value of `dwfl_report_end` during offline initialization. Validate `dwfl_module_relocation_info` result before passing to `strcmp` to avoid potential segmentation faults. Additionally: - Fix a file descriptor leak in `debuginfo__init_offline_dwarf()` when `dwfl_report_offline()` or subsequent setup calls fail. Fixes: 6f1b6291cf73cb32 ("perf tools: Add util/debuginfo.[ch] files") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zecheng Li Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/debuginfo.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/debuginfo.c b/tools/perf/util/debuginfo.c index 0e35c13abd041..84a78b30ceac1 100644 --- a/tools/perf/util/debuginfo.c +++ b/tools/perf/util/debuginfo.c @@ -42,6 +42,7 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, { GElf_Addr dummy; int fd; + bool fd_consumed = false; fd = open(path, O_RDONLY); if (fd < 0) @@ -55,6 +56,7 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, dbg->mod = dwfl_report_offline(dbg->dwfl, "", "", fd); if (!dbg->mod) goto error; + fd_consumed = true; dbg->dbg = dwfl_module_getdwarf(dbg->mod, &dbg->bias); if (!dbg->dbg) @@ -62,13 +64,14 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, dwfl_module_build_id(dbg->mod, &dbg->build_id, &dummy); - dwfl_report_end(dbg->dwfl, NULL, NULL); + if (dwfl_report_end(dbg->dwfl, NULL, NULL) != 0) + goto error; return 0; error: if (dbg->dwfl) dwfl_end(dbg->dwfl); - else + if (!fd_consumed) close(fd); memset(dbg, 0, sizeof(*dbg)); @@ -167,7 +170,7 @@ int debuginfo__get_text_offset(struct debuginfo *dbg, Dwarf_Addr *offs, /* Search the relocation related .text section */ for (i = 0; i < n; i++) { p = dwfl_module_relocation_info(dbg->mod, i, &shndx); - if (strcmp(p, ".text") == 0) { + if (p && strcmp(p, ".text") == 0) { /* OK, get the section header */ scn = elf_getscn(elf, shndx); if (!scn) From 2830eedfcc7dc4c387a47046478d8e561d7f89a0 Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Tue, 14 Apr 2026 14:42:41 +0200 Subject: [PATCH 1515/2758] perf callchain: Handle multiple address spaces [ Upstream commit ae15db3e9b639491007cc1e9e99638e4b6091781 ] perf test 'perf inject to convert DWARF callchains to regular ones' fails on s390. It was introduced with commit 92ea788d2af4e65a ("perf inject: Add --convert-callchain option") The failure comes the difference in output. Without the inject script to convert DWARF the callchains is: # perf record -F 999 --call-graph dwarf -- perf test -w noploop # perf report -i perf.data --stdio --no-children -q \ --percent-limit=1 > /tmp/111 # cat /tmp/111 99.30% perf-noploop perf [.] noploop | ---noploop run_workload (inlined) cmd_test run_builtin (inlined) handle_internal_command run_argv (inlined) main __libc_start_call_main __libc_start_main_impl (inlined) _start # With the inject script step the output is: # perf inject -i perf.data --convert-callchain -o /tmp/perf-inject-1.out # perf report -i /tmp/perf-inject-1.out --stdio --no-children -q \ --percent-limit=1 > /tmp/222 # cat /tmp/222 99.40% perf-noploop perf [.] noploop | ---noploop run_workload (inlined) cmd_test run_builtin (inlined) handle_internal_command run_argv (inlined) main _start # diff /tmp/111 /tmp/222 1c1 < 99.30% perf-noploop perf [.] noploop --- > 99.40% perf-noploop perf [.] noploop 10,11d9 < __libc_start_call_main < __libc_start_main_impl (inlined) # The difference are the symbols __libc_start_call_main and __libc_start_main_impl. On x86_64, kernel and user space share a single virtual address space, with the kernel mapped to the upper end of memory. The instruction pointer value alone is sufficient to distinguish between user space and kernel space addresses. This is not true for s390, which uses separate address spaces for user and kernel. The same virtual address can be valid in both address spaces, so the instruction pointer value alone cannot determine whether an address belongs to the kernel or user space. Instead, perf must rely on the cpumode metadata derived from the processor status word (PSW) at sample time. In function perf_event__convert_sample_callchain() the first part copies a kernel callchain and context entries, if any. It then appends additional entries ignoring the address space architecture. Taking that into account, the symbols at addresses 0x3ff970348cb __libc_start_call_main 0x3ff970349c5 __libc_start_main_impl (located after the kernel address space on s390) are now included. Output before: # perf test 83 83: perf inject to convert DWARF callchains to regular ones : FAILED! Output after: # perf test 83 83: perf inject to convert DWARF callchains to regular ones : Ok Question to Namhyung: In function perf_event__convert_sample_callchain() just before the for() loop this patch modifies, the kernel callchain is copied, see this comment and the next 5 lines: /* copy kernel callchain and context entries */ Then why is machine__kernel_ip() needed in the for() loop, when the kernel entries have been copied just before the loop? Note: This patch was tested on x86_64 virtual machine and succeeded. Fixes: 92ea788d2af4e65a ("perf inject: Add --convert-callchain option") Signed-off-by: Thomas Richter Acked-by: Namhyung Kim Cc: Alexander Gordeev Cc: Heiko Carstens Cc: Jan Polensky Cc: linux-s390@vger.kernel.org Cc: Sumanth Korikkar Cc: Vasily Gorbik Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/arch/common.c | 4 +++- tools/perf/builtin-inject.c | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/perf/arch/common.c b/tools/perf/arch/common.c index 21836f70f231e..ad0cab830a4da 100644 --- a/tools/perf/arch/common.c +++ b/tools/perf/arch/common.c @@ -237,5 +237,7 @@ int perf_env__lookup_objdump(struct perf_env *env, char **path) */ bool perf_env__single_address_space(struct perf_env *env) { - return strcmp(perf_env__arch(env), "sparc"); + const char *arch = perf_env__arch(env); + + return strcmp(arch, "s390") && strcmp(arch, "sparc"); } diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index f174bc69cec45..6ab20df358c43 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -438,7 +438,8 @@ static int perf_event__convert_sample_callchain(const struct perf_tool *tool, node = cursor->first; for (k = 0; k < cursor->nr && i < PERF_MAX_STACK_DEPTH; k++) { - if (machine__kernel_ip(machine, node->ip)) + if (machine->single_address_space && + machine__kernel_ip(machine, node->ip)) /* kernel IPs were added already */; else if (node->ms.sym && node->ms.sym->inlined) /* we can't handle inlined callchains */; From fbd5d3168740f57d029fab74aa7dbe6e82b72358 Mon Sep 17 00:00:00 2001 From: Yingchao Deng Date: Sun, 26 Apr 2026 17:59:34 +0800 Subject: [PATCH 1516/2758] coresight: cti: Fix DT filter signals silently ignored [ Upstream commit 551bb2fd5e4ed63d33aa11f07102cce5179b7595 ] In cti_plat_process_filter_sigs(), after allocating a temporary cti_trig_grp struct via kzalloc_obj(), the code never assigns tg->nr_sigs = nr_filter_sigs. Since kzalloc zero-initialises the struct, tg->nr_sigs remains 0. cti_plat_read_trig_group() guards with: if (!tgrp->nr_sigs) return 0; so it returns immediately without reading any signal indices from DT. Fix by assigning tg->nr_sigs before calling cti_plat_read_trig_group(). Fixes: a5614770ab97 ("coresight: cti: Add device tree support for custom CTI") Signed-off-by: Yingchao Deng Reviewed-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260426-nr_sigs-v1-1-3b9df99dab97@oss.qualcomm.com Signed-off-by: Sasha Levin --- drivers/hwtracing/coresight/coresight-cti-platform.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwtracing/coresight/coresight-cti-platform.c b/drivers/hwtracing/coresight/coresight-cti-platform.c index 4eff96f48594e..d6d5388705c3e 100644 --- a/drivers/hwtracing/coresight/coresight-cti-platform.c +++ b/drivers/hwtracing/coresight/coresight-cti-platform.c @@ -329,6 +329,7 @@ static int cti_plat_process_filter_sigs(struct cti_drvdata *drvdata, if (!tg) return -ENOMEM; + tg->nr_sigs = nr_filter_sigs; err = cti_plat_read_trig_group(tg, fwnode, CTI_DT_FILTER_OUT_SIGS); if (!err) drvdata->config.trig_out_filter |= tg->used_mask; From f0481e6bcc5d34cae85b244b1479784ee79df877 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 28 Apr 2026 16:46:12 +0800 Subject: [PATCH 1517/2758] soundwire: don't program SDW_SCP_BUSCLOCK_SCALE on a unattached Peripheral MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit c368dd5cbd61ffab2b6f8a89b0d5775e2e16cde6 ] The SDW_SCP_BUSCLOCK_SCALE register will be programmed when the Peripheral is attached. We can and should skip programming the SDW_SCP_BUSCLOCK_SCALE register when the Peripheral is unattached. Fixes: 645291cfe5e5 ("Soundwire: stream: program BUSCLOCK_SCALE") Signed-off-by: Bard Liao Reviewed-by: Simon Trimmer Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20260428084612.322701-1-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/soundwire/stream.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 4ed8fb7663ad4..0b96268380283 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -697,6 +697,13 @@ static int sdw_program_params(struct sdw_bus *bus, bool prepare) if (scale_index < 0) return scale_index; + /* Skip the unattached Peripherals */ + if (!completion_done(&slave->enumeration_complete)) { + dev_warn(&slave->dev, + "Not enumerated, skip programming BUSCLOCK_SCALE\n"); + continue; + } + ret = sdw_write_no_pm(slave, addr1, scale_index); if (ret < 0) { dev_err(&slave->dev, "SDW_SCP_BUSCLOCK_SCALE register write failed\n"); From e483a406a23a92d5202e8d324f206e127eef48ff Mon Sep 17 00:00:00 2001 From: "Baoli.Zhang" Date: Wed, 6 May 2026 13:50:35 +0800 Subject: [PATCH 1518/2758] soundwire: fix bug in sdw_add_element_group_count found by syzkaller [ Upstream commit f772ff5a0e6758fd412803c09e03ba3bca5f5878 ] The original implementation caused an out-of-bounds memory access in the sdw_add_element_group_count for-loop when i == num. for (i = 0; i <= num; i++) { if (rate == group->rates[i] && lane == group->lanes[i]) ... To fix this error, the function now checks for existing rate/lane entries in the group(a function parameter) using a for-loop before adding them. No functional changes apart from this fix. Fixes: 9026118f20e2 ("soundwire: Add generic bandwidth allocation algorithm") Reviewed-by: Bard Liao Reviewed-by: Andy Shevchenko Signed-off-by: Baoli.Zhang Link: https://patch.msgid.link/20260506055039.3751028-2-baoli.zhang@linux.intel.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- .../soundwire/generic_bandwidth_allocation.c | 47 +++++++++---------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index fb3970e12dac9..f016ad088a1db 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -299,39 +299,36 @@ static int sdw_add_element_group_count(struct sdw_group *group, int num = group->count; int i; - for (i = 0; i <= num; i++) { + for (i = 0; i < num; i++) { if (rate == group->rates[i] && lane == group->lanes[i]) - break; - - if (i != num) - continue; - - if (group->count >= group->max_size) { - unsigned int *rates; - unsigned int *lanes; + return 0; + } - group->max_size += 1; - rates = krealloc(group->rates, - (sizeof(int) * group->max_size), - GFP_KERNEL); - if (!rates) - return -ENOMEM; + if (group->count >= group->max_size) { + unsigned int *rates; + unsigned int *lanes; - group->rates = rates; + group->max_size += 1; + rates = krealloc(group->rates, + (sizeof(int) * group->max_size), + GFP_KERNEL); + if (!rates) + return -ENOMEM; - lanes = krealloc(group->lanes, - (sizeof(int) * group->max_size), - GFP_KERNEL); - if (!lanes) - return -ENOMEM; + group->rates = rates; - group->lanes = lanes; - } + lanes = krealloc(group->lanes, + (sizeof(int) * group->max_size), + GFP_KERNEL); + if (!lanes) + return -ENOMEM; - group->rates[group->count] = rate; - group->lanes[group->count++] = lane; + group->lanes = lanes; } + group->rates[group->count] = rate; + group->lanes[group->count++] = lane; + return 0; } From 9d802907fc2a57adf2cb83d7ec55e4e337922857 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Tue, 17 Feb 2026 13:19:43 +0000 Subject: [PATCH 1519/2758] coresight: tmc: Fix overflow when calculating is bigger than 2GiB [ Upstream commit f195d54deef1bc6dd3326394975baff02c7ae487 ] When specifying a 2GB AUX buffer, the ETR driver ends up allocating only a 1MB buffer instead: # echo 'file coresight-tmc-etr.c +p' > \ /sys/kernel/debug/dynamic_debug/control # perf record -e cs_etm/@tmc_etr0,timestamp=0/u -C 0 -m ,2G -- test coresight tmc_etr0: allocated buffer of size 1024KB in mode 0 The page index is an 'int' type, and shifting it by PAGE_SHIFT overflows when the resulting value exceeds 2GB. This produces a negative value, causing the driver to fall back to the minimum buffer size (1MB). Cast the page index to a wider type to accommodate large buffer sizes. Also fix a similar issue in the buffer offset calculation. Reported-by: Michiel van Tol Fixes: 99443ea19e8b ("coresight: Add generic TMC sg table framework") Fixes: eebe8dbd8630 ("coresight: tmc: Decouple the perf buffer allocation from sysfs mode") Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260217-arm_coresight_fix_big_buffer_size-v1-1-774e893d8e3f@arm.com Signed-off-by: Sasha Levin --- drivers/hwtracing/coresight/coresight-tmc-etr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 4dc1defe27a5f..361a433e6f0c5 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -154,7 +154,7 @@ tmc_pages_get_offset(struct tmc_pages *tmc_pages, dma_addr_t addr) for (i = 0; i < tmc_pages->nr_pages; i++) { page_start = tmc_pages->daddrs[i]; if (addr >= page_start && addr < (page_start + PAGE_SIZE)) - return i * PAGE_SIZE + (addr - page_start); + return (long)i * PAGE_SIZE + (addr - page_start); } return -EINVAL; @@ -1379,7 +1379,7 @@ alloc_etr_buf(struct tmc_drvdata *drvdata, struct perf_event *event, node = (event->cpu == -1) ? NUMA_NO_NODE : cpu_to_node(event->cpu); /* Use the minimum limit if the required size is smaller */ - size = nr_pages << PAGE_SHIFT; + size = (ssize_t)nr_pages << PAGE_SHIFT; size = max_t(ssize_t, size, TMC_ETR_PERF_MIN_BUF_SIZE); /* From 293dacd5b6a9f1275d92ca6c4fd874e54b2c8319 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 5 May 2026 17:51:25 +0100 Subject: [PATCH 1520/2758] coresight: ete: Always save state on power down [ Upstream commit 2ab4645fe4206c142a5f1491e191c906279686cf ] System register ETMs and ETE are unlikely to be preserved on CPU power down. The ETE DT binding also never documented "arm,coresight-loses-context-with-cpu" so nobody would have legitimately been able to use that binding to fix it and ACPI has no such binding at all. Fix it by hard coding the setting for sysreg ETMs (ETE is always sysreg) or ACPI boots. Use a local variable when setting up save_state so that it's immune to concurrent probing when devices have different configurations which is an issue with modifying the global. This fixes the following error when using Coresight with ACPI on the FVP which supports CPU PM: coresight ete0: External agent took claim tag WARNING: drivers/hwtracing/coresight/coresight-core.c:248 at coresight_disclaim_device_unlocked+0xe0/0xe8, CPU#0: perf/117 Fixes: 35e1c9163e02 ("coresight: ete: Add support for ETE tracing") Signed-off-by: James Clark Reviewed-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260505-james-cs-ete-pm_save_enable-v3-1-485d21dd79b8@linaro.org Signed-off-by: Sasha Levin --- .../coresight/coresight-etm4x-core.c | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index d565a73f0042e..591dfe0bc6350 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -56,10 +56,14 @@ MODULE_PARM_DESC(boot_enable, "Enable tracing on boot"); #define PARAM_PM_SAVE_NEVER 1 /* never save any state */ #define PARAM_PM_SAVE_SELF_HOSTED 2 /* save self-hosted state only */ +/* + * Save option for ETM4. ETE, sysreg ETM4s and ACPI boots ignore this option and + * will always save. + */ static int pm_save_enable = PARAM_PM_SAVE_FIRMWARE; module_param(pm_save_enable, int, 0444); MODULE_PARM_DESC(pm_save_enable, - "Save/restore state on power down: 1 = never, 2 = self-hosted"); + "Save/restore state on power down: 1 = never, 2 = self-hosted. MMIO and DT only."); static struct etmv4_drvdata *etmdrvdata[NR_CPUS]; static void etm4_set_default_config(struct etmv4_config *config); @@ -2012,7 +2016,7 @@ static int etm4_cpu_save(struct etmv4_drvdata *drvdata) { int ret = 0; - if (pm_save_enable != PARAM_PM_SAVE_SELF_HOSTED) + if (!drvdata->save_state) return 0; /* @@ -2127,7 +2131,7 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata) static void etm4_cpu_restore(struct etmv4_drvdata *drvdata) { - if (pm_save_enable != PARAM_PM_SAVE_SELF_HOSTED) + if (!drvdata->save_state) return; if (coresight_get_mode(drvdata->csdev)) @@ -2212,6 +2216,17 @@ static void etm4_pm_clear(void) } } +static bool etm4x_always_pm_save(struct device *dev, struct csdev_access *csa) +{ + /* + * Only IO mem ETM devices will benefit from skipping PM save and only + * DT has the option to control it, not ACPI. Otherwise system register + * based ETMs and ETEs will always lose context on CPU power down, so + * always save. + */ + return !csa->io_mem || is_acpi_device_node(dev_fwnode(dev)); +} + static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg) { int ret; @@ -2221,6 +2236,7 @@ static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg) struct coresight_desc desc = { 0 }; u8 major, minor; char *type_name; + bool pm_save; if (!drvdata) return -EINVAL; @@ -2248,6 +2264,21 @@ static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg) etm4_set_default(&drvdata->config); + if (etm4x_always_pm_save(dev, init_arg->csa)) + pm_save = true; + else if (pm_save_enable == PARAM_PM_SAVE_FIRMWARE) + pm_save = coresight_loses_context_with_cpu(dev); + else + pm_save = pm_save_enable != PARAM_PM_SAVE_NEVER; + + if (pm_save) { + drvdata->save_state = devm_kmalloc(dev, + sizeof(struct etmv4_save_state), + GFP_KERNEL); + if (!drvdata->save_state) + return -ENOMEM; + } + pdata = coresight_get_platform_data(dev); if (IS_ERR(pdata)) return PTR_ERR(pdata); @@ -2305,17 +2336,6 @@ static int etm4_probe(struct device *dev) if (ret) return ret; - if (pm_save_enable == PARAM_PM_SAVE_FIRMWARE) - pm_save_enable = coresight_loses_context_with_cpu(dev) ? - PARAM_PM_SAVE_SELF_HOSTED : PARAM_PM_SAVE_NEVER; - - if (pm_save_enable != PARAM_PM_SAVE_NEVER) { - drvdata->save_state = devm_kmalloc(dev, - sizeof(struct etmv4_save_state), GFP_KERNEL); - if (!drvdata->save_state) - return -ENOMEM; - } - raw_spin_lock_init(&drvdata->spinlock); drvdata->cpu = coresight_get_cpu(dev); From 12007c55d9c08f837e5f29daba32e04ee7ca22c3 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Wed, 8 Apr 2026 13:31:43 +0100 Subject: [PATCH 1521/2758] coresight: etm4x: Correct TRCVMIDCCTLR1 save and restore [ Upstream commit 0ec0a8785d21f63db520bd9d2a67c55e855d36a8 ] It is a typo to use trcvmidcctlr0 to save and restore TRCVMIDCCTLR1. Use trcvmidcctlr1 instead. Fixes: f5bd523690d2 ("coresight: etm4x: Convert all register accesses") Signed-off-by: Leo Yan Reviewed-by: James Clark Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260408-arm_cs_fix_trcvmidcctlr1_typo-v1-1-6a5695363b46@arm.com Signed-off-by: Sasha Levin --- drivers/hwtracing/coresight/coresight-etm4x-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index 591dfe0bc6350..a251375db24b1 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -1983,7 +1983,7 @@ static int __etm4_cpu_save(struct etmv4_drvdata *drvdata) state->trcvmidcctlr0 = etm4x_read32(csa, TRCVMIDCCTLR0); if (drvdata->numvmidc > 4) - state->trcvmidcctlr0 = etm4x_read32(csa, TRCVMIDCCTLR1); + state->trcvmidcctlr1 = etm4x_read32(csa, TRCVMIDCCTLR1); state->trcclaimset = etm4x_read32(csa, TRCCLAIMCLR); @@ -2106,7 +2106,7 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata) etm4x_relaxed_write32(csa, state->trcvmidcctlr0, TRCVMIDCCTLR0); if (drvdata->numvmidc > 4) - etm4x_relaxed_write32(csa, state->trcvmidcctlr0, TRCVMIDCCTLR1); + etm4x_relaxed_write32(csa, state->trcvmidcctlr1, TRCVMIDCCTLR1); etm4x_relaxed_write32(csa, state->trcclaimset, TRCCLAIMSET); From d50ba5e4642c05b4d39ce5c6f5b1aa18fd224563 Mon Sep 17 00:00:00 2001 From: Carlos Bilbao Date: Mon, 27 Apr 2026 21:01:04 -0700 Subject: [PATCH 1522/2758] PCI/ASPM: Don't reconfigure ASPM entering low-power state [ Upstream commit c855c9921da72e535c24737c748f603a52d03f7e ] Reconfiguring ASPM when a device transitions to low-power state can enable L1.1/L1.2 substates on the PCIe link at a time when the device is sleeping and may be unable to exit them. ASPM should be reconfigured on D0 entry (resume), not on the way down. pci_set_low_power_state() calls pcie_aspm_pm_state_change() after writing D3hot to PCI_PM_CTRL. pcie_aspm_pm_state_change() resets link->aspm_capable to link->aspm_support and then calls pcie_config_aspm_path(), which can enable ASPM L1.1/L1.2 substates on the PCIe link. If the device cannot recover the link from L1.2 while in D3hot, subsequent config space reads return 0xFFFF ("device inaccessible") and pci_power_up() fails with messages like: vfio-pci 0000:5d:00.0: Unable to change power state from D3hot to D0, device inaccessible This was observed on NVIDIA H100 SXM5 GPUs bound to vfio-pci when Linux runtime PM suspends them to D3hot: the GPU becomes permanently inaccessible and disappears from the PCIe bus. The call to pcie_aspm_pm_state_change() in pci_set_low_power_state() was restored by f93e71aea6c6 ("Revert "PCI/ASPM: Remove pcie_aspm_pm_state_change()""), which reverted 08d0cc5f3426 ("PCI/ASPM: Remove pcie_aspm_pm_state_change()"). The revert was necessary because the removal broke suspend/resume on certain platforms that required ASPM to be reconfigured on D0 entry. However, the revert restored the call in both pci_set_full_power_state() (D0 entry) and pci_set_low_power_state() (low-power entry). Only the D0-entry call is needed to fix the suspend/resume regression. The low-power-entry call is harmful: reconfiguring ASPM immediately after putting a device into D3hot can enable link substates that the device or platform cannot exit while the device is sleeping. Remove the pcie_aspm_pm_state_change() call from pci_set_low_power_state(). ASPM will still be reconfigured correctly when the device returns to D0 via pci_set_full_power_state(). Fixes: f93e71aea6c6 ("Revert "PCI/ASPM: Remove pcie_aspm_pm_state_change()"") Signed-off-by: Carlos Bilbao (Lambda) Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260428040104.78524-1-carlos.bilbao@kernel.org Signed-off-by: Sasha Levin --- drivers/pci/pci.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index d34266651ad09..0ee01d99e3275 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1514,9 +1514,6 @@ static int pci_set_low_power_state(struct pci_dev *dev, pci_power_t state, bool pci_power_name(dev->current_state), pci_power_name(state)); - if (dev->bus->self) - pcie_aspm_pm_state_change(dev->bus->self, locked); - return 0; } From 4e82818ead50b985ccd44e420fbfc313ddc02e5a Mon Sep 17 00:00:00 2001 From: Guixin Liu Date: Fri, 8 May 2026 16:21:27 +0800 Subject: [PATCH 1523/2758] PCI: Introduce named defines for PCI ROM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 113e86bc58a918f85d250723436a4d541a873358 ] Convert the magic numbers associated with PCI ROM into named definitions. Some of these definitions will be used in the second fix patch. Signed-off-by: Guixin Liu Signed-off-by: Bjorn Helgaas Reviewed-by: Andy Shevchenko Reviewed-by: Krzysztof Wilczyński Link: https://patch.msgid.link/20260508082128.3344255-2-kanie@linux.alibaba.com Stable-dep-of: 538796b807fc ("PCI: Check ROM header and data structure addr before accessing") Signed-off-by: Sasha Levin --- drivers/pci/rom.c | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c index e18d3a4383ba6..d4a141bd148b4 100644 --- a/drivers/pci/rom.c +++ b/drivers/pci/rom.c @@ -5,13 +5,28 @@ * (C) Copyright 2004 Jon Smirl * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes */ + +#include #include #include #include +#include #include #include "pci.h" +#define PCI_ROM_HEADER_SIZE 0x1A +#define PCI_ROM_POINTER_TO_DATA_STRUCT 0x18 +#define PCI_ROM_LAST_IMAGE_INDICATOR 0x15 +#define PCI_ROM_LAST_IMAGE_INDICATOR_BIT BIT(7) +#define PCI_ROM_IMAGE_LEN 0x10 +#define PCI_ROM_IMAGE_SECTOR_SIZE SZ_512 +#define PCI_ROM_IMAGE_SIGNATURE 0xAA55 + +/* Data structure signature is "PCIR" in ASCII representation */ +#define PCI_ROM_DATA_STRUCT_SIGNATURE 0x52494350 +#define PCI_ROM_DATA_STRUCT_LEN 0x0A + /** * pci_enable_rom - enable ROM decoding for a PCI device * @pdev: PCI device to enable @@ -91,26 +106,27 @@ static size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, do { void __iomem *pds; /* Standard PCI ROMs start out with these bytes 55 AA */ - if (readw(image) != 0xAA55) { - pci_info(pdev, "Invalid PCI ROM header signature: expecting 0xaa55, got %#06x\n", - readw(image)); + if (readw(image) != PCI_ROM_IMAGE_SIGNATURE) { + pci_info(pdev, "Invalid PCI ROM header signature: expecting %#06x, got %#06x\n", + PCI_ROM_IMAGE_SIGNATURE, readw(image)); break; } - /* get the PCI data structure and check its "PCIR" signature */ - pds = image + readw(image + 24); - if (readl(pds) != 0x52494350) { - pci_info(pdev, "Invalid PCI ROM data signature: expecting 0x52494350, got %#010x\n", - readl(pds)); + /* Get the PCI data structure and check its "PCIR" signature */ + pds = image + readw(image + PCI_ROM_POINTER_TO_DATA_STRUCT); + if (readl(pds) != PCI_ROM_DATA_STRUCT_SIGNATURE) { + pci_info(pdev, "Invalid PCI ROM data signature: expecting %#010x, got %#010x\n", + PCI_ROM_DATA_STRUCT_SIGNATURE, readl(pds)); break; } - last_image = readb(pds + 21) & 0x80; - length = readw(pds + 16); - image += length * 512; + last_image = readb(pds + PCI_ROM_LAST_IMAGE_INDICATOR) & + PCI_ROM_LAST_IMAGE_INDICATOR_BIT; + length = readw(pds + PCI_ROM_IMAGE_LEN); + image += length * PCI_ROM_IMAGE_SECTOR_SIZE; /* Avoid iterating through memory outside the resource window */ if (image >= rom + size) break; if (!last_image) { - if (readw(image) != 0xAA55) { + if (readw(image) != PCI_ROM_IMAGE_SIGNATURE) { pci_info(pdev, "No more image in the PCI ROM\n"); break; } From 721ad5b72448b5065ed309017ab563205f162404 Mon Sep 17 00:00:00 2001 From: Guixin Liu Date: Fri, 8 May 2026 16:21:28 +0800 Subject: [PATCH 1524/2758] PCI: Check ROM header and data structure addr before accessing [ Upstream commit 538796b807fcfb81b2ce40cc97a614fd8588feb5 ] We meet a crash when running stress-ng on x86_64 machine: BUG: unable to handle page fault for address: ffa0000007f40000 RIP: 0010:pci_get_rom_size+0x52/0x220 Call Trace: pci_map_rom+0x80/0x130 pci_read_rom+0x4b/0xe0 kernfs_file_read_iter+0x96/0x180 vfs_read+0x1b1/0x300 Our analysis reveals that the ROM space's start address is 0xffa0000007f30000, and size is 0x10000. Because of broken ROM space, before calling readl(pds), the pds's value is 0xffa0000007f3ffff, which is already pointed to the ROM space end, invoking readl() would read 4 bytes therefore cause an out-of-bounds access and trigger a crash. Fix this by adding image header and data structure checking. We also found another crash on arm64 machine: Unable to handle kernel paging request at virtual address ffff8000dd1393ff Mem abort info: ESR = 0x0000000096000021 EC = 0x25: DABT (current EL), IL = 32 bits SET = 0, FnV = 0 EA = 0, S1PTW = 0 FSC = 0x21: alignment fault The call trace is the same with x86_64, but the crash reason is that the data structure addr is not aligned with 4, and arm64 machine report "alignment fault". Fix this by adding alignment checking. Fixes: 47b975d234ea ("PCI: Avoid iterating through memory outside the resource window") Suggested-by: Guanghui Feng Signed-off-by: Guixin Liu [bhelgaas: shorten function names, wrap comments] Signed-off-by: Bjorn Helgaas Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260508082128.3344255-3-kanie@linux.alibaba.com Signed-off-by: Sasha Levin --- drivers/pci/rom.c | 123 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 105 insertions(+), 18 deletions(-) diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c index d4a141bd148b4..f2105c6ceef52 100644 --- a/drivers/pci/rom.c +++ b/drivers/pci/rom.c @@ -6,9 +6,12 @@ * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes */ +#include #include #include #include +#include +#include #include #include #include @@ -27,6 +30,15 @@ #define PCI_ROM_DATA_STRUCT_SIGNATURE 0x52494350 #define PCI_ROM_DATA_STRUCT_LEN 0x0A +/* + * Per PCI Firmware r3.3, sec 5.1.3, a conformant PCI Data Structure is at + * least 24 bytes (0x18), large enough to cover every fixed field this + * driver reads (up to the Indicator byte at offset 0x15). Reject smaller + * device-claimed lengths so the follow-up readers in pci_get_rom_size() + * cannot escape the mapped ROM window. + */ +#define PCI_ROM_DATA_STRUCT_MIN_LEN 0x18 + /** * pci_enable_rom - enable ROM decoding for a PCI device * @pdev: PCI device to enable @@ -84,6 +96,91 @@ void pci_disable_rom(struct pci_dev *pdev) } EXPORT_SYMBOL_GPL(pci_disable_rom); +static bool pci_rom_header_valid(struct pci_dev *pdev, void __iomem *image, + void __iomem *rom, size_t size, + bool expect_valid) +{ + unsigned long rom_end = (unsigned long)rom + size - 1; + unsigned long header_end; + u16 signature; + + /* + * Per PCI Firmware r3.3, sec 5.1, each image must start on a + * 512-byte boundary and must contain the PCI Expansion ROM header. + * Because @rom is page-aligned (returned by ioremap()), checking + * 512-byte alignment of @image is equivalent to enforcing the + * spec's sector-aligned layout within the ROM. This also + * satisfies the natural-alignment requirement of readw() on archs + * such as arm64 that disallow unaligned IOMEM access. + */ + if (!IS_ALIGNED((unsigned long)image, PCI_ROM_IMAGE_SECTOR_SIZE)) + return false; + + if (check_add_overflow((unsigned long)image, PCI_ROM_HEADER_SIZE - 1, + &header_end)) + return false; + + if (image < rom || header_end > rom_end) + return false; + + /* Standard PCI ROMs start out with these bytes 55 AA */ + signature = readw(image); + if (signature != PCI_ROM_IMAGE_SIGNATURE) { + if (expect_valid) { + pci_info(pdev, "Invalid PCI ROM header signature: expecting %#06x, got %#06x\n", + PCI_ROM_IMAGE_SIGNATURE, signature); + } else { + pci_info(pdev, "No more images in PCI ROM\n"); + } + return false; + } + + return true; +} + +static bool pci_rom_data_struct_valid(struct pci_dev *pdev, void __iomem *pds, + void __iomem *rom, size_t size) +{ + unsigned long rom_end = (unsigned long)rom + size - 1; + unsigned long end; + u32 signature; + u16 data_len; + + /* + * Some CPU architectures require IOMEM access addresses to be + * aligned, for example arm64, so since we're about to call + * readl(), check here for 4-byte alignment. + */ + if (!IS_ALIGNED((unsigned long)pds, 4)) + return false; + + if (check_add_overflow((unsigned long)pds, PCI_ROM_DATA_STRUCT_LEN + 1, + &end)) + return false; + + if (pds < rom || end > rom_end) + return false; + + signature = readl(pds); + if (signature != PCI_ROM_DATA_STRUCT_SIGNATURE) { + pci_info(pdev, "Invalid PCI ROM data signature: expecting %#010x, got %#010x\n", + PCI_ROM_DATA_STRUCT_SIGNATURE, signature); + return false; + } + + data_len = readw(pds + PCI_ROM_DATA_STRUCT_LEN); + if (data_len < PCI_ROM_DATA_STRUCT_MIN_LEN || data_len == U16_MAX) + return false; + + if (check_add_overflow((unsigned long)pds, data_len - 1, &end)) + return false; + + if (end > rom_end) + return false; + + return true; +} + /** * pci_get_rom_size - obtain the actual size of the ROM image * @pdev: target PCI device @@ -99,38 +196,28 @@ static size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, size_t size) { void __iomem *image; - int last_image; unsigned int length; + bool last_image; image = rom; do { void __iomem *pds; - /* Standard PCI ROMs start out with these bytes 55 AA */ - if (readw(image) != PCI_ROM_IMAGE_SIGNATURE) { - pci_info(pdev, "Invalid PCI ROM header signature: expecting %#06x, got %#06x\n", - PCI_ROM_IMAGE_SIGNATURE, readw(image)); + if (!pci_rom_header_valid(pdev, image, rom, size, true)) break; - } + /* Get the PCI data structure and check its "PCIR" signature */ pds = image + readw(image + PCI_ROM_POINTER_TO_DATA_STRUCT); - if (readl(pds) != PCI_ROM_DATA_STRUCT_SIGNATURE) { - pci_info(pdev, "Invalid PCI ROM data signature: expecting %#010x, got %#010x\n", - PCI_ROM_DATA_STRUCT_SIGNATURE, readl(pds)); + if (!pci_rom_data_struct_valid(pdev, pds, rom, size)) break; - } + last_image = readb(pds + PCI_ROM_LAST_IMAGE_INDICATOR) & PCI_ROM_LAST_IMAGE_INDICATOR_BIT; length = readw(pds + PCI_ROM_IMAGE_LEN); image += length * PCI_ROM_IMAGE_SECTOR_SIZE; - /* Avoid iterating through memory outside the resource window */ - if (image >= rom + size) + + if (!last_image && + !pci_rom_header_valid(pdev, image, rom, size, false)) break; - if (!last_image) { - if (readw(image) != PCI_ROM_IMAGE_SIGNATURE) { - pci_info(pdev, "No more image in the PCI ROM\n"); - break; - } - } } while (length && !last_image); /* never return a size larger than the PCI resource window */ From df6d71c9a818eed13aa0f1f5d20d3ed201451269 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 8 May 2026 20:00:27 +0200 Subject: [PATCH 1525/2758] x86/platform/olpc: xo15: Drop wakeup source on driver removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit cc966553e6ff0849978b5754531b768b0ff54985 ] Prevent leaking a wakeup source object after removing the driver by adding appropriate cleanup code to its remove callback function. Fixes: a0f30f592d2d ("x86, olpc: Add XO-1.5 SCI driver") Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2069931.usQuhbGJ8B@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Sasha Levin --- arch/x86/platform/olpc/olpc-xo15-sci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/platform/olpc/olpc-xo15-sci.c b/arch/x86/platform/olpc/olpc-xo15-sci.c index 82c51b6ec5288..276caf756a9c5 100644 --- a/arch/x86/platform/olpc/olpc-xo15-sci.c +++ b/arch/x86/platform/olpc/olpc-xo15-sci.c @@ -186,6 +186,7 @@ static int xo15_sci_add(struct acpi_device *device) static void xo15_sci_remove(struct acpi_device *device) { + device_init_wakeup(&device->dev, false); acpi_disable_gpe(NULL, xo15_sci_gpe); acpi_remove_gpe_handler(NULL, xo15_sci_gpe, xo15_sci_gpe_handler); cancel_work_sync(&sci_work); From f05c3b4c9cc07b31745ead77d1285330d31d6f1d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 8 May 2026 19:40:31 +0200 Subject: [PATCH 1526/2758] platform/x86: xo15-ebook: Fix wakeup source and GPE handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit b2fc2c6ebbd2d49935c8960755d8170faead2159 ] The device_set_wakeup_enable() call in ebook_switch_add() doesn't actually do anything because power.can_wakeup is not set for ACPI device objects. Moreover, had it done anything, it would have registered a wakeup source object that wouldn't have been used going forward and that wakeup source would have been leaked after driver removal because ebook_switch_remove() doesn't clean it up. Accordingly, remove that call from ebook_switch_add(). Also prevent leaking an enabled ACPI GPE after removing the driver by adding appropriate cleanup code to ebook_switch_remove(). Fixes: 89ca11771a4b ("OLPC XO-1.5 ebook switch driver") Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/1966125.tdWV9SEqCh@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Sasha Levin --- drivers/platform/x86/xo15-ebook.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c index 4d1b1b310cc56..1568169b78727 100644 --- a/drivers/platform/x86/xo15-ebook.c +++ b/drivers/platform/x86/xo15-ebook.c @@ -38,6 +38,7 @@ MODULE_DEVICE_TABLE(acpi, ebook_device_ids); struct ebook_switch { struct input_dev *input; char phys[32]; /* for input device */ + bool gpe_enabled; }; static int ebook_send_state(struct acpi_device *device) @@ -128,7 +129,7 @@ static int ebook_switch_add(struct acpi_device *device) /* Button's GPE is run-wake GPE */ acpi_enable_gpe(device->wakeup.gpe_device, device->wakeup.gpe_number); - device_set_wakeup_enable(&device->dev, true); + button->gpe_enabled = true; } return 0; @@ -144,6 +145,10 @@ static void ebook_switch_remove(struct acpi_device *device) { struct ebook_switch *button = acpi_driver_data(device); + if (button->gpe_enabled) + acpi_disable_gpe(device->wakeup.gpe_device, + device->wakeup.gpe_number); + input_unregister_device(button->input); kfree(button); } From 6ed3cea56b778620b213caef0656416d0c5fb57a Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 5 May 2026 17:45:42 -0700 Subject: [PATCH 1527/2758] perf sched: Add missing mmap2 handler in timehist [ Upstream commit 91182741369b261c441e63e6678893032a6d7e4c ] perf_sched__timehist() registers event handlers for options using the sched->tool struct. It registers handlers for MMAP, COMM, EXIT, FORK, etc. but completely omits registering a handler for MMAP2 events. Failing to register both MMAP and MMAP2 handlers causes modern systems (which primarily output MMAP2 records) to silently drop VMA map mappings. This results in uninitialized machine/thread mapping structures, making it impossible to resolve shared library instruction pointers (IPs) to dynamic symbols/DSOs during timehist callchain analysis. Fix this by correctly registering perf_event__process_mmap2 in sched->tool inside perf_sched__timehist(). Fixes: 49394a2a24c78ce0 ("perf sched timehist: Introduce timehist command") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: David Ahern Cc: Gabriel Marin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-sched.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 555247568e7a6..241c2f808f7b0 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -3299,6 +3299,7 @@ static int perf_sched__timehist(struct perf_sched *sched) */ sched->tool.sample = perf_timehist__process_sample; sched->tool.mmap = perf_event__process_mmap; + sched->tool.mmap2 = perf_event__process_mmap2; sched->tool.comm = perf_event__process_comm; sched->tool.exit = perf_event__process_exit; sched->tool.fork = perf_event__process_fork; From fc9d6f81587185844dd220ef23c9e05a7823abde Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 5 May 2026 17:45:43 -0700 Subject: [PATCH 1528/2758] perf tool: Fix missing schedstat delegates and dont_split_sample_group in delegate_tool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 09d355618f7ccc27ffc7fc668b2e232872962079 ] delegate_tool was missing the delegate overrides for schedstat_cpu and schedstat_domain. As a result, when allocated with zalloc, these callbacks defaulted to NULL, causing a segmentation fault crash if any schedstat events were delivered during event processing. Fix this by adding delegate_schedstat_cpu and delegate_schedstat_domain via the CREATE_DELEGATE_OP2 macro, and ensuring delegate_tool__init correctly registers them. Additionally, delegate_tool__init completely omitted copying the dont_split_sample_group property from the delegate. This would cause wrapper tools to default the flag to false, which corrupts piped event processing (e.g., in perf inject) by triggering duplicate event deliveries on split sample values in deliver_sample_group(). Similarly, perf_tool__init() omitted the initialization of this boolean field. On stack-allocated tools that rely on this initializer (like intel-tpebs or __cmd_evlist), this could result in uninitialized stack garbage evaluating to true—silently dropping non-leader event members in deliver_sample_group(). Fix both issues by properly copying the field in delegate_tool__init and initializing it to false in perf_tool__init. Fixes: 6331b266935916bf ("perf tool: Add a delegate_tool that just delegates actions to another tool") Fixes: 79bcd34e0f3da39f ("perf inject: Fix leader sampling inserting additional samples") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Gabriel Marin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/tool.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/perf/util/tool.c b/tools/perf/util/tool.c index 013c7839e2cfd..ff2150517b755 100644 --- a/tools/perf/util/tool.c +++ b/tools/perf/util/tool.c @@ -285,6 +285,7 @@ void perf_tool__init(struct perf_tool *tool, bool ordered_events) tool->no_warn = false; tool->show_feat_hdr = SHOW_FEAT_NO_HEADER; tool->merge_deferred_callchains = true; + tool->dont_split_sample_group = false; tool->sample = process_event_sample_stub; tool->mmap = process_event_stub; @@ -433,6 +434,8 @@ CREATE_DELEGATE_OP2(stat_config); CREATE_DELEGATE_OP2(stat_round); CREATE_DELEGATE_OP2(thread_map); CREATE_DELEGATE_OP2(time_conv); +CREATE_DELEGATE_OP2(schedstat_cpu); +CREATE_DELEGATE_OP2(schedstat_domain); CREATE_DELEGATE_OP2(tracing_data); #define CREATE_DELEGATE_OP3(name) \ @@ -470,6 +473,7 @@ void delegate_tool__init(struct delegate_tool *tool, struct perf_tool *delegate) tool->tool.no_warn = delegate->no_warn; tool->tool.show_feat_hdr = delegate->show_feat_hdr; tool->tool.merge_deferred_callchains = delegate->merge_deferred_callchains; + tool->tool.dont_split_sample_group = delegate->dont_split_sample_group; tool->tool.sample = delegate_sample; tool->tool.read = delegate_read; @@ -516,4 +520,6 @@ void delegate_tool__init(struct delegate_tool *tool, struct perf_tool *delegate) tool->tool.bpf_metadata = delegate_bpf_metadata; tool->tool.compressed = delegate_compressed; tool->tool.auxtrace = delegate_auxtrace; + tool->tool.schedstat_cpu = delegate_schedstat_cpu; + tool->tool.schedstat_domain = delegate_schedstat_domain; } From 28c35ea3515fc43fb1dcf9b05c20c73aa47ea32a Mon Sep 17 00:00:00 2001 From: Florian Eckert Date: Fri, 17 Apr 2026 10:35:47 +0200 Subject: [PATCH 1529/2758] PCI: intel-gw: Move interrupt enable to own function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit abddc0539e5931f4ad2f589a03cbba5a6a64485f ] To improve the readability of the code, move the interrupt enable instructions to a separate function. That is already done for the disable interrupt instruction. In addition, clear and disable all pending interrupts, as is done in intel_pcie_core_irq_disable(). After that, enable all relevant interrupts again. The 'PCIE_APP_IRNEN' definition contains all the relevant interrupts that are of interest. This change is also done in the MaxLinear SDK [1]. As I unfortunately don’t have any documentation for this IP core, I suspect that the intention is to set the IP core for interrupt handling to a specific state. Perhaps the problem is that the IP core did not reinitialize the interrupt register properly after a power cycle. In my view, it can’t do any harm to switch the interrupt off and then on again to set them to a specific state. The reason why the MaxLinear SDK is used as a reference here is, that this PCIe DWC IP is used in the URX851 and URX850 SoC. This SoC was originally developed by Intel when they acquired Lantiq’s home networking division in 2015 [2]. In 2020 the home network division was sold to MaxLinear [3]. Since then, this SoC belongs to MaxLinear. They use their own SDK, which runs on kernel version '5.15.x'. [1] https://github.com/maxlinear/linux/blob/updk_9.1.90/drivers/pci/controller/dwc/pcie-intel-gw.c#L431 [2] https://www.intc.com/news-events/press-releases/detail/364/intel-to-acquire-lantiq-advancing-the-connected-home [3] https://investors.maxlinear.com/press-releases/detail/395/maxlinear-to-acquire-intels-home-gateway-platform Signed-off-by: Florian Eckert Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260417-pcie-intel-gw-v5-3-0a2b933fe04f@dev.tdt.de Stable-dep-of: 1eedabe7c617 ("PCI: intel-gw: Add .start_link() callback") Signed-off-by: Sasha Levin --- drivers/pci/controller/dwc/pcie-intel-gw.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-intel-gw.c b/drivers/pci/controller/dwc/pcie-intel-gw.c index c21906eced618..3a85bd0ef1b7f 100644 --- a/drivers/pci/controller/dwc/pcie-intel-gw.c +++ b/drivers/pci/controller/dwc/pcie-intel-gw.c @@ -196,6 +196,13 @@ static void intel_pcie_device_rst_deassert(struct intel_pcie *pcie) gpiod_set_value_cansleep(pcie->reset_gpio, 0); } +static void intel_pcie_core_irq_enable(struct intel_pcie *pcie) +{ + pcie_app_wr(pcie, PCIE_APP_IRNEN, 0); + pcie_app_wr(pcie, PCIE_APP_IRNCR, PCIE_APP_IRN_INT); + pcie_app_wr(pcie, PCIE_APP_IRNEN, PCIE_APP_IRN_INT); +} + static void intel_pcie_core_irq_disable(struct intel_pcie *pcie) { pcie_app_wr(pcie, PCIE_APP_IRNEN, 0); @@ -317,9 +324,7 @@ static int intel_pcie_host_setup(struct intel_pcie *pcie) if (ret) goto app_init_err; - /* Enable integrated interrupts */ - pcie_app_wr_mask(pcie, PCIE_APP_IRNEN, PCIE_APP_IRN_INT, - PCIE_APP_IRN_INT); + intel_pcie_core_irq_enable(pcie); return 0; From b2dd40f1d15efcf435403307fa60e049c849c7d0 Mon Sep 17 00:00:00 2001 From: Florian Eckert Date: Fri, 17 Apr 2026 10:35:48 +0200 Subject: [PATCH 1530/2758] PCI: intel-gw: Enable clock before PHY init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit febf9ed3c35e5eec7ea384ebbd55a5296e3ca5e9 ] To ensure that the boot sequence is correct, the DWC PCIe core clock must be switched on before PHY init call [1]. This changes are based on patched kernel sources of the MaxLinear SDK. The reason why the MaxLinear SDK is used as a reference here is, that this PCIe DWC IP is used in the URX851 and URX850 SoC. This SoC was originally developed by Intel when they acquired Lantiq’s home networking division in 2015 [2]. In 2020 the home network division was sold to MaxLinear [3]. Since then, this SoC belongs to MaxLinear. They use their own SDK, which runs on kernel version '5.15.x'. [1] https://github.com/maxlinear/linux/blob/updk_9.1.90/drivers/pci/controller/dwc/pcie-intel-gw.c#L544 [2] https://www.intc.com/news-events/press-releases/detail/364/intel-to-acquire-lantiq-advancing-the-connected-home [3] https://investors.maxlinear.com/press-releases/detail/395/maxlinear-to-acquire-intels-home-gateway-platform Signed-off-by: Florian Eckert Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260417-pcie-intel-gw-v5-4-0a2b933fe04f@dev.tdt.de Stable-dep-of: 1eedabe7c617 ("PCI: intel-gw: Add .start_link() callback") Signed-off-by: Sasha Levin --- drivers/pci/controller/dwc/pcie-intel-gw.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-intel-gw.c b/drivers/pci/controller/dwc/pcie-intel-gw.c index 3a85bd0ef1b7f..e84703c1c4a1c 100644 --- a/drivers/pci/controller/dwc/pcie-intel-gw.c +++ b/drivers/pci/controller/dwc/pcie-intel-gw.c @@ -292,13 +292,9 @@ static int intel_pcie_host_setup(struct intel_pcie *pcie) intel_pcie_core_rst_assert(pcie); intel_pcie_device_rst_assert(pcie); - - ret = phy_init(pcie->phy); - if (ret) - return ret; - intel_pcie_core_rst_deassert(pcie); + /* Controller clock must be provided earlier than PHY */ ret = clk_prepare_enable(pcie->core_clk); if (ret) { dev_err(pcie->pci.dev, "Core clock enable failed: %d\n", ret); @@ -307,13 +303,17 @@ static int intel_pcie_host_setup(struct intel_pcie *pcie) pci->atu_base = pci->dbi_base + 0xC0000; + ret = phy_init(pcie->phy); + if (ret) + goto phy_err; + intel_pcie_ltssm_disable(pcie); intel_pcie_link_setup(pcie); intel_pcie_init_n_fts(pci); ret = dw_pcie_setup_rc(&pci->pp); if (ret) - goto app_init_err; + goto err; dw_pcie_upconfig_setup(pci); @@ -322,17 +322,18 @@ static int intel_pcie_host_setup(struct intel_pcie *pcie) ret = dw_pcie_wait_for_link(pci); if (ret) - goto app_init_err; + goto err; intel_pcie_core_irq_enable(pcie); return 0; -app_init_err: +err: + phy_exit(pcie->phy); +phy_err: clk_disable_unprepare(pcie->core_clk); clk_err: intel_pcie_core_rst_assert(pcie); - phy_exit(pcie->phy); return ret; } From f62ffd973b07fffe22fc3689e08b39f9a1adc845 Mon Sep 17 00:00:00 2001 From: Florian Eckert Date: Fri, 17 Apr 2026 10:35:49 +0200 Subject: [PATCH 1531/2758] PCI: intel-gw: Add .start_link() callback [ Upstream commit 1eedabe7c6170b5c73c7d801f427c127be74916e ] The pcie-intel-gw driver had no .start_link() callback. Add one so the driver works again and does not abort with the following error messages during probing: intel-gw-pcie d1000000.pcie: host bridge /soc/pcie@d1000000 ranges: intel-gw-pcie d1000000.pcie: MEM 0x00dc000000..0x00ddffffff -> 0x00dc000000 intel-combo-phy d0c00000.combo-phy: Set combo mode: combophy[1]: mode: PCIe single lane mode intel-gw-pcie d1000000.pcie: No outbound iATU found intel-gw-pcie d1000000.pcie: Cannot initialize host intel-gw-pcie d1000000.pcie: probe with driver intel-gw-pcie failed with error -22 intel-gw-pcie c1100000.pcie: host bridge /soc/pcie@c1100000 ranges: intel-gw-pcie c1100000.pcie: MEM 0x00ce000000..0x00cfffffff -> 0x00ce000000 intel-combo-phy c0c00000.combo-phy: Set combo mode: combophy[3]: mode: PCIe single lane mode intel-gw-pcie c1100000.pcie: No outbound iATU found intel-gw-pcie c1100000.pcie: Cannot initialize host intel-gw-pcie c1100000.pcie: probe with driver intel-gw-pcie failed with error -22 Fixes: c5097b9869a1 ("Revert "PCI: dwc: Wait for link up only if link is started"") Fixes: da56a1bfbab5 ("PCI: dwc: Wait for link up only if link is started") Signed-off-by: Florian Eckert Signed-off-by: Manivannan Sadhasivam [bhelgaas: remove timestamps] Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260417-pcie-intel-gw-v5-5-0a2b933fe04f@dev.tdt.de Signed-off-by: Sasha Levin --- drivers/pci/controller/dwc/pcie-intel-gw.c | 24 ++++++++++------------ 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-intel-gw.c b/drivers/pci/controller/dwc/pcie-intel-gw.c index e84703c1c4a1c..f157c716953c2 100644 --- a/drivers/pci/controller/dwc/pcie-intel-gw.c +++ b/drivers/pci/controller/dwc/pcie-intel-gw.c @@ -285,6 +285,16 @@ static void intel_pcie_turn_off(struct intel_pcie *pcie) pcie_rc_cfg_wr_mask(pcie, PCI_COMMAND, PCI_COMMAND_MEMORY, 0); } +static int intel_pcie_start_link(struct dw_pcie *pci) +{ + struct intel_pcie *pcie = dev_get_drvdata(pci->dev); + + intel_pcie_device_rst_deassert(pcie); + intel_pcie_ltssm_enable(pcie); + + return 0; +} + static int intel_pcie_host_setup(struct intel_pcie *pcie) { int ret; @@ -311,25 +321,12 @@ static int intel_pcie_host_setup(struct intel_pcie *pcie) intel_pcie_link_setup(pcie); intel_pcie_init_n_fts(pci); - ret = dw_pcie_setup_rc(&pci->pp); - if (ret) - goto err; - dw_pcie_upconfig_setup(pci); - intel_pcie_device_rst_deassert(pcie); - intel_pcie_ltssm_enable(pcie); - - ret = dw_pcie_wait_for_link(pci); - if (ret) - goto err; - intel_pcie_core_irq_enable(pcie); return 0; -err: - phy_exit(pcie->phy); phy_err: clk_disable_unprepare(pcie->core_clk); clk_err: @@ -387,6 +384,7 @@ static int intel_pcie_rc_init(struct dw_pcie_rp *pp) } static const struct dw_pcie_ops intel_pcie_ops = { + .start_link = intel_pcie_start_link, }; static const struct dw_pcie_host_ops intel_pcie_dw_ops = { From 1f95260a82374648888b2fdae50a98f1b1978741 Mon Sep 17 00:00:00 2001 From: Rong Zhang Date: Fri, 1 May 2026 02:45:23 +0800 Subject: [PATCH 1532/2758] PCI: loongson: Do not ignore downstream devices on external bridges [ Upstream commit 1389ab9bf9f627d4daed86f492091b00f110aa86 ] Loongson PCI host controllers have a hardware quirk that requires software to ignore downstream devices with device number > 0 on the internal bridges. The current implementation applies the workaround to all non-root buses, which breaks external bridges (e.g., PCIe switches) with multiple downstream devices. Fix it by only applying the workaround to internal bridges. Tested on Loongson-LS3A4000-7A1000-NUC-SE, using AMD Promontory 21 chipset add-in card [1]. $ lspci -tnnnvvv -[0000:00]-+-00.0 Loongson Technology LLC 7A1000 Chipset Hyper Transport Bridge Controller [0014:7a00] +-00.1 Loongson Technology LLC 7A2000 Chipset Hyper Transport Bridge Controller [0014:7a10] +-03.0 Loongson Technology LLC 2K1000/2000 / 7A1000 Chipset Gigabit Ethernet Controller [0014:7a03] +-04.0 Loongson Technology LLC 2K1000 / 7A1000/2000 Chipset USB OHCI Controller [0014:7a24] +-04.1 Loongson Technology LLC 2K1000 / 7A1000/2000 Chipset USB EHCI Controller [0014:7a14] +-05.0 Loongson Technology LLC 2K1000 / 7A1000/2000 Chipset USB OHCI Controller [0014:7a24] +-05.1 Loongson Technology LLC 2K1000 / 7A1000/2000 Chipset USB EHCI Controller [0014:7a14] +-06.0 Loongson Technology LLC 7A1000 Chipset Vivante GC1000 GPU [0014:7a15] +-06.1 Loongson Technology LLC 2K1000 / 7A1000 Chipset Display Controller [0014:7a06] +-07.0 Loongson Technology LLC 2K1000/2000/3000 / 3B6000M / 7A1000/2000 Chipset HD Audio Controller [0014:7a07] +-08.0 Loongson Technology LLC 2K1000 / 7A1000 Chipset 3Gb/s SATA AHCI Controller [0014:7a08] +-08.1 Loongson Technology LLC 2K1000 / 7A1000 Chipset 3Gb/s SATA AHCI Controller [0014:7a08] +-08.2 Loongson Technology LLC 2K1000 / 7A1000 Chipset 3Gb/s SATA AHCI Controller [0014:7a08] +-09.0-[01]----00.0 Qualcomm Technologies, Inc QCNFA765 Wireless Network Adapter [17cb:1103] +-0a.0-[02]----00.0 Etron Technology, Inc. EJ188/EJ198 USB 3.0 Host Controller [1b6f:7052] +-0f.0-[03-08]----00.0-[04-08]--+-00.0-[05]----00.0 Shenzhen Longsys Electronics Co., Ltd. FORESEE XP1000 / Lexar Professional CFexpress Type B Gold series, NM620 PCIe NVME SSD (DRAM-less) [1d97:5216] | +-08.0-[06]----00.0 MAXIO Technology (Hangzhou) Ltd. NVMe SSD Controller MAP1202 (DRAM-less) [1e4b:1202] | +-0c.0-[07]----00.0 Advanced Micro Devices, Inc. [AMD] 600 Series Chipset USB 3.2 Controller [1022:43f7] | \-0d.0-[08]----00.0 Advanced Micro Devices, Inc. [AMD] 600 Series Chipset SATA Controller [1022:43f6] \-16.0 Loongson Technology LLC 7A1000 Chipset SPI Controller [0014:7a0b] Fixes: 2410e3301fcc ("PCI: loongson: Don't access non-existent devices") Co-developed-by: Jiaxun Yang Signed-off-by: Jiaxun Yang Co-developed-by: Lain "Fearyncess" Yang Signed-off-by: Lain "Fearyncess" Yang Signed-off-by: Rong Zhang Signed-off-by: Manivannan Sadhasivam Link: https://oshwhub.com/wesd/b650 [1] Link: https://patch.msgid.link/20260501-ls7a-bridge-fixes-v2-1-69fa93683805@rong.moe Signed-off-by: Sasha Levin --- drivers/pci/controller/pci-loongson.c | 31 ++++++++++++++------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/drivers/pci/controller/pci-loongson.c b/drivers/pci/controller/pci-loongson.c index 9609e6f50b981..d0c643996476f 100644 --- a/drivers/pci/controller/pci-loongson.c +++ b/drivers/pci/controller/pci-loongson.c @@ -80,6 +80,18 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_LPC, system_bus_quirk); +static const struct pci_device_id loongson_internal_bridge_devids[] = { + { PCI_VDEVICE(LOONGSON, DEV_LS2K_PCIE_PORT0) }, + { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT0) }, + { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT1) }, + { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT2) }, + { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT3) }, + { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT4) }, + { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT5) }, + { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT6) }, + { 0, }, +}; + /* * Some Loongson PCIe ports have hardware limitations on their Maximum Read * Request Size. They can't handle anything larger than this. Sane @@ -92,24 +104,13 @@ static void loongson_set_min_mrrs_quirk(struct pci_dev *pdev) { struct pci_bus *bus = pdev->bus; struct pci_dev *bridge; - static const struct pci_device_id bridge_devids[] = { - { PCI_VDEVICE(LOONGSON, DEV_LS2K_PCIE_PORT0) }, - { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT0) }, - { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT1) }, - { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT2) }, - { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT3) }, - { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT4) }, - { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT5) }, - { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT6) }, - { 0, }, - }; /* look for the matching bridge */ while (!pci_is_root_bus(bus)) { bridge = bus->self; bus = bus->parent; - if (pci_match_id(bridge_devids, bridge)) { + if (pci_match_id(loongson_internal_bridge_devids, bridge)) { if (pcie_get_readrq(pdev) > 256) { pci_info(pdev, "limiting MRRS to 256\n"); pcie_set_readrq(pdev, 256); @@ -266,11 +267,11 @@ static void __iomem *pci_loongson_map_bus(struct pci_bus *bus, struct loongson_pci *priv = pci_bus_to_loongson_pci(bus); /* - * Do not read more than one device on the bus other than - * the host bus. + * Do not read more than one device on the internal bridges. */ if ((priv->data->flags & FLAG_DEV_FIX) && bus->self) { - if (!pci_is_root_bus(bus) && (device > 0)) + if (!pci_is_root_bus(bus) && (device > 0) && + pci_match_id(loongson_internal_bridge_devids, bus->self)) return NULL; } From 34b2a1076dd71b979e60056721e0811fcdcd1800 Mon Sep 17 00:00:00 2001 From: Hsiu Che Yu Date: Mon, 27 Apr 2026 22:15:49 +0800 Subject: [PATCH 1533/2758] rust: alloc: fix assert in `Vec::reserve` doc test [ Upstream commit 75619f2df7a5da6ffb61eedc2a73fdf70c65471c ] The assert in the doctest used `>= 10`, which only checks that the capacity can hold `additional` elements, ignoring the existing length of `v`. The correct check should ensure there is room for `additional` *extra* elements on top of what is already in the vector. Fix the assert to use `>= v.len() + 10` so the example accurately reflects the actual semantics of the function. Reported-by: Miguel Ojeda Closes: https://lore.kernel.org/rust-for-linux/CANiq72nkXWhjK9iFRrhGtkMZGsvNE_zVsu4JnxaFRfxWL7RRdg@mail.gmail.com/ Fixes: 2aac4cd7dae3d ("rust: alloc: implement kernel `Vec` type") Signed-off-by: Hsiu Che Yu Reviewed-by: Alice Ryhl Reviewed-by: Alexandre Courbot Link: https://patch.msgid.link/20260427-doctest-kvec-reserve-v1-1-0623abcd9c2e@gmail.com Signed-off-by: Danilo Krummrich Signed-off-by: Sasha Levin --- rust/kernel/alloc/kvec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs index 5c24fcf05bdfe..0f7b43e81030c 100644 --- a/rust/kernel/alloc/kvec.rs +++ b/rust/kernel/alloc/kvec.rs @@ -614,7 +614,7 @@ where /// /// v.reserve(10, GFP_KERNEL)?; /// let cap = v.capacity(); - /// assert!(cap >= 10); + /// assert!(cap >= v.len() + 10); /// /// v.reserve(10, GFP_KERNEL)?; /// let new_cap = v.capacity(); From e30fa2246972ebc6492e282d161349ba7f6e9094 Mon Sep 17 00:00:00 2001 From: Sumit Kumar Date: Tue, 14 Apr 2026 11:59:40 +0530 Subject: [PATCH 1534/2758] bus: mhi: ep: Fix potential deadlock in mhi_ep_reset_worker() [ Upstream commit 9dece4435d396e9877e27483552b910ba8654169 ] There is a potential deadlock scenario in mhi_ep_reset_worker() where the state_lock mutex is acquired twice in the same call chain: mhi_ep_reset_worker() mutex_lock(&mhi_cntrl->state_lock) mhi_ep_power_up() mhi_ep_set_ready_state() mutex_lock(&mhi_cntrl->state_lock) <- Deadlock Fix this by releasing the state_lock before calling mhi_ep_power_up(). The lock is only needed to protect current MHI state read operation. The lock can be safely released before proceeding with the power up sequence. Fixes: 7a97b6b47353 ("bus: mhi: ep: Add support for handling MHI_RESET") Signed-off-by: Sumit Kumar Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260414-reset_worker_deadlock-v2-1-42fd682b45db@oss.qualcomm.com Signed-off-by: Sasha Levin --- drivers/bus/mhi/ep/main.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c index 0277e1ab11988..425525e232f91 100644 --- a/drivers/bus/mhi/ep/main.c +++ b/drivers/bus/mhi/ep/main.c @@ -1087,11 +1087,12 @@ static void mhi_ep_reset_worker(struct work_struct *work) mhi_ep_power_down(mhi_cntrl); - mutex_lock(&mhi_cntrl->state_lock); - /* Reset MMIO to signal host that the MHI_RESET is completed in endpoint */ mhi_ep_mmio_reset(mhi_cntrl); + + mutex_lock(&mhi_cntrl->state_lock); cur_state = mhi_cntrl->mhi_state; + mutex_unlock(&mhi_cntrl->state_lock); /* * Only proceed further if the reset is due to SYS_ERR. The host will @@ -1100,8 +1101,6 @@ static void mhi_ep_reset_worker(struct work_struct *work) */ if (cur_state == MHI_STATE_SYS_ERR) mhi_ep_power_up(mhi_cntrl); - - mutex_unlock(&mhi_cntrl->state_lock); } /* From adec0b0df4e24950924410ef9dcf892d6e3b7973 Mon Sep 17 00:00:00 2001 From: Sumit Kumar Date: Tue, 14 Apr 2026 11:59:41 +0530 Subject: [PATCH 1535/2758] bus: mhi: ep: Add missing state_lock protection for mhi_state access [ Upstream commit ce3e534ee9c8d13a68c8a611c3b7bd0c2152d2ab ] The mhi_cntrl->mhi_state field should be protected by state_lock to ensure atomic state transitions. However, mhi_ep_power_up() access mhi_state without holding this lock, which can race with concurrent state transitions and lead to state corruption. Add proper state_lock protection around mhi_state access. Fixes: fb3a26b7e8af ("bus: mhi: ep: Add support for powering up the MHI endpoint stack") Fixes: f7d0806bdb1b3 ("bus: mhi: ep: Add support for handling SYS_ERR condition") Signed-off-by: Sumit Kumar Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260414-reset_worker_deadlock-v2-2-42fd682b45db@oss.qualcomm.com Signed-off-by: Sasha Levin --- drivers/bus/mhi/ep/main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c index 425525e232f91..5330b03dc6369 100644 --- a/drivers/bus/mhi/ep/main.c +++ b/drivers/bus/mhi/ep/main.c @@ -1147,7 +1147,9 @@ int mhi_ep_power_up(struct mhi_ep_cntrl *mhi_cntrl) for (i = 0; i < mhi_cntrl->event_rings; i++) mhi_ep_ring_init(&mhi_cntrl->mhi_event[i].ring, RING_TYPE_ER, i); + mutex_lock(&mhi_cntrl->state_lock); mhi_cntrl->mhi_state = MHI_STATE_RESET; + mutex_unlock(&mhi_cntrl->state_lock); /* Set AMSS EE before signaling ready state */ mhi_ep_mmio_set_env(mhi_cntrl, MHI_EE_AMSS); From d1da8fcb88024f0bb721a6b4aedb0970928f9164 Mon Sep 17 00:00:00 2001 From: Jie Gan Date: Tue, 12 May 2026 09:56:07 +0800 Subject: [PATCH 1536/2758] coresight: fix missing error code when trace ID is invalid [ Upstream commit f4526ffee6ff9f5845b430957417149eded74bf3 ] When coresight_path_assign_trace_id() cannot assign a valid trace ID, coresight_enable_sysfs() takes the err_path goto with ret still 0, returning success to the caller despite no trace session being started. Change coresight_path_assign_trace_id() to return int, moving the IS_VALID_CS_TRACE_ID() check inside it so it returns -EINVAL on failure and 0 on success. Update both callers to propagate this return value directly instead of inspecting path->trace_id after the call. Fixes: d87d76d823d1 ("Coresight: Allocate trace ID after building the path") Reviewed-by: James Clark Reviewed-by: Richard Cheng Signed-off-by: Jie Gan Reviewed-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260512-fix-trace-id-error-v4-1-eb3de789767a@oss.qualcomm.com Signed-off-by: Sasha Levin --- drivers/hwtracing/coresight/coresight-core.c | 23 +++++++++++-------- .../hwtracing/coresight/coresight-etm-perf.c | 5 ++-- drivers/hwtracing/coresight/coresight-priv.h | 2 +- drivers/hwtracing/coresight/coresight-sysfs.c | 4 ++-- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 46f247f73cf64..2105bb8139407 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -739,8 +739,8 @@ static int coresight_get_trace_id(struct coresight_device *csdev, * Call this after creating the path and before enabling it. This leaves * the trace ID set on the path, or it remains 0 if it couldn't be assigned. */ -void coresight_path_assign_trace_id(struct coresight_path *path, - enum cs_mode mode) +int coresight_path_assign_trace_id(struct coresight_path *path, + enum cs_mode mode) { struct coresight_device *sink = coresight_get_sink(path); struct coresight_node *nd; @@ -750,15 +750,18 @@ void coresight_path_assign_trace_id(struct coresight_path *path, /* Assign a trace ID to the path for the first device that wants to do it */ trace_id = coresight_get_trace_id(nd->csdev, mode, sink); - /* - * 0 in this context is that it didn't want to assign so keep searching. - * Non 0 is either success or fail. - */ - if (trace_id != 0) { - path->trace_id = trace_id; - return; - } + /* 0 means the device has no ID assignment, so keep searching */ + if (trace_id == 0) + continue; + + if (!IS_VALID_CS_TRACE_ID(trace_id)) + return -EINVAL; + + path->trace_id = trace_id; + return 0; } + + return -EINVAL; } /** diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index f85dedf89a3f9..89ba7c9a66137 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -324,6 +324,7 @@ static void *etm_setup_aux(struct perf_event *event, void **pages, struct coresight_device *sink = NULL; struct coresight_device *user_sink = NULL, *last_sink = NULL; struct etm_event_data *event_data = NULL; + int ret; event_data = alloc_event_data(cpu); if (!event_data) @@ -420,8 +421,8 @@ static void *etm_setup_aux(struct perf_event *event, void **pages, } /* ensure we can allocate a trace ID for this CPU */ - coresight_path_assign_trace_id(path, CS_MODE_PERF); - if (!IS_VALID_CS_TRACE_ID(path->trace_id)) { + ret = coresight_path_assign_trace_id(path, CS_MODE_PERF); + if (ret) { cpumask_clear_cpu(cpu, mask); coresight_release_path(path); continue; diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 1ea882dffd703..34c7e792adbd9 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -153,7 +153,7 @@ int coresight_make_links(struct coresight_device *orig, void coresight_remove_links(struct coresight_device *orig, struct coresight_connection *conn); u32 coresight_get_sink_id(struct coresight_device *csdev); -void coresight_path_assign_trace_id(struct coresight_path *path, +int coresight_path_assign_trace_id(struct coresight_path *path, enum cs_mode mode); #if IS_ENABLED(CONFIG_CORESIGHT_SOURCE_ETM3X) diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c index d2a6ed8bcc74d..b6a870399e834 100644 --- a/drivers/hwtracing/coresight/coresight-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-sysfs.c @@ -211,8 +211,8 @@ int coresight_enable_sysfs(struct coresight_device *csdev) goto out; } - coresight_path_assign_trace_id(path, CS_MODE_SYSFS); - if (!IS_VALID_CS_TRACE_ID(path->trace_id)) + ret = coresight_path_assign_trace_id(path, CS_MODE_SYSFS); + if (ret) goto err_path; ret = coresight_enable_path(path, CS_MODE_SYSFS); From 6c7f2108af205d090b18d3cc5a33bb5e71dd6ce2 Mon Sep 17 00:00:00 2001 From: Luo Jie Date: Tue, 6 Jan 2026 21:35:10 -0800 Subject: [PATCH 1537/2758] clk: qcom: cmnpll: Account for reference clock divider [ Upstream commit 88c543fff756450bcd04ec4560c4440be36c9e75 ] The clk_cmn_pll_recalc_rate() function must account for the reference clock divider programmed in CMN_PLL_REFCLK_CONFIG. Without this fix, platforms with a reference divider other than 1 calculate incorrect CMN PLL rates. For example, on IPQ5332 where the reference divider is 2, the computed rate becomes twice the actual output. Read CMN_PLL_REFCLK_DIV and divide the parent rate by this value before applying the 2 * FACTOR scaling. This yields the correct rate calculation: rate = (parent_rate / ref_div) * 2 * factor. Maintain backward compatibility with earlier platforms (e.g. IPQ9574, IPQ5424, IPQ5018) that use ref_div = 1. Fixes: f81715a4c87c ("clk: qcom: Add CMN PLL clock controller driver for IPQ SoC") Signed-off-by: Luo Jie Reviewed-by: Konrad Dybcio Tested-by: George Moussalem Link: https://lore.kernel.org/r/20260106-qcom_ipq5332_cmnpll-v2-1-f9f7e4efbd79@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- drivers/clk/qcom/ipq-cmn-pll.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/clk/qcom/ipq-cmn-pll.c b/drivers/clk/qcom/ipq-cmn-pll.c index 5763e4df59a1a..889c176089c2f 100644 --- a/drivers/clk/qcom/ipq-cmn-pll.c +++ b/drivers/clk/qcom/ipq-cmn-pll.c @@ -199,7 +199,7 @@ static unsigned long clk_cmn_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_cmn_pll *cmn_pll = to_clk_cmn_pll(hw); - u32 val, factor; + u32 val, factor, ref_div; /* * The value of CMN_PLL_DIVIDER_CTRL_FACTOR is automatically adjusted @@ -207,8 +207,15 @@ static unsigned long clk_cmn_pll_recalc_rate(struct clk_hw *hw, */ regmap_read(cmn_pll->regmap, CMN_PLL_DIVIDER_CTRL, &val); factor = FIELD_GET(CMN_PLL_DIVIDER_CTRL_FACTOR, val); + if (WARN_ON(factor == 0)) + factor = 1; - return parent_rate * 2 * factor; + regmap_read(cmn_pll->regmap, CMN_PLL_REFCLK_CONFIG, &val); + ref_div = FIELD_GET(CMN_PLL_REFCLK_DIV, val); + if (WARN_ON(ref_div == 0)) + ref_div = 1; + + return div_u64((u64)parent_rate * 2 * factor, ref_div); } static int clk_cmn_pll_determine_rate(struct clk_hw *hw, From f478709f7be8a5f3200b24f4fd735e95d222b1b4 Mon Sep 17 00:00:00 2001 From: Biswapriyo Nath Date: Mon, 30 Mar 2026 10:13:49 +0000 Subject: [PATCH 1538/2758] dt-bindings: clock: qcom,sm6125-dispcc: reference qcom,gcc.yaml [ Upstream commit dbabf6a32ffb69a604f966ec01a20a060836939d ] Just like most of Qualcomm clock controllers, we can reference common qcom,gcc.yaml schema to unify the common parts of the binding. This also adds the '#reset-cells' property which is permitted for the SM6125 SoC clock controllers, but not listed as a valid property. Fixes: bb4d28e377cf ("arm64: dts: qcom: sm6125: Add missing MDSS core reset") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202603150629.GYoouFwZ-lkp@intel.com/ Signed-off-by: Biswapriyo Nath Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260330-ginkgo-add-usb-ir-vib-v3-2-c4b778b0d7f8@gmail.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- .../bindings/clock/qcom,dispcc-sm6125.yaml | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/Documentation/devicetree/bindings/clock/qcom,dispcc-sm6125.yaml b/Documentation/devicetree/bindings/clock/qcom,dispcc-sm6125.yaml index ef2b1e2044309..a177a1934b19f 100644 --- a/Documentation/devicetree/bindings/clock/qcom,dispcc-sm6125.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,dispcc-sm6125.yaml @@ -42,12 +42,6 @@ properties: - const: cfg_ahb_clk - const: gcc_disp_gpll0_div_clk_src - '#clock-cells': - const: 1 - - '#power-domain-cells': - const: 1 - power-domains: description: A phandle and PM domain specifier for the CX power domain. @@ -58,18 +52,16 @@ properties: A phandle to an OPP node describing the power domain's performance point. maxItems: 1 - reg: - maxItems: 1 - required: - compatible - - reg - clocks - clock-names - - '#clock-cells' - '#power-domain-cells' -additionalProperties: false +allOf: + - $ref: qcom,gcc.yaml# + +unevaluatedProperties: false examples: - | @@ -101,6 +93,7 @@ examples: power-domains = <&rpmpd SM6125_VDDCX>; #clock-cells = <1>; + #reset-cells = <1>; #power-domain-cells = <1>; }; ... From 72a7bfee9fe8f067c73b3a53c6973a5cc8c4ce2c Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Fri, 10 Apr 2026 11:55:07 +0530 Subject: [PATCH 1539/2758] PCI: dwc: Apply ECRC workaround for DesignWare cores prior to 5.10a [ Upstream commit 1e1b554c2b910591aa3fffdb8077a2ca33bf3cb2 ] The ECRC (TLP digest) workaround was originally applied only for DesignWare core version 4.90a. Per discussion in Synopsys case, the dependency of the iATU TD bit on ECRC generation was removed in 5.10a, so apply the workaround for all DWC versions below that release. Replace the misleading comment that referred to raw version constants with readable DesignWare release name to help readability. Fixes: b210b1595606 ("PCI: dwc: Apply ECRC workaround to DesignWare 5.00a as well") Signed-off-by: Manikanta Maddireddy [mani: corrected fixes tag format] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20260410062507.657453-1-mmaddireddy@nvidia.com Signed-off-by: Sasha Levin --- drivers/pci/controller/dwc/pcie-designware.c | 16 ++++++++-------- drivers/pci/controller/dwc/pcie-designware.h | 1 + 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index c11cf61b8319e..76c9a0a10367b 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -487,13 +487,13 @@ static inline void dw_pcie_writel_atu_ob(struct dw_pcie *pci, u32 index, u32 reg static inline u32 dw_pcie_enable_ecrc(u32 val) { /* - * DWC versions 0x3530302a and 0x3536322a have a design issue where - * the 'TD' bit in the Control register-1 of the ATU outbound - * region acts like an override for the ECRC setting, i.e., the - * presence of TLP Digest (ECRC) in the outgoing TLPs is solely - * determined by this bit. This is contrary to the PCIe spec which - * says that the enablement of the ECRC is solely determined by the - * AER registers. + * DesignWare core versions prior to 5.10A have a design issue where the + * 'TD' bit in the Control register-1 of the ATU outbound region acts + * like an override for the ECRC setting, i.e., the presence of TLP + * Digest (ECRC) in the outgoing TLPs is solely determined by this + * bit. This is contrary to the PCIe spec which says that the + * enablement of the ECRC is solely determined by the AER + * registers. * * Because of this, even when the ECRC is enabled through AER * registers, the transactions going through ATU won't have TLP @@ -563,7 +563,7 @@ int dw_pcie_prog_outbound_atu(struct dw_pcie *pci, if (upper_32_bits(limit_addr) > upper_32_bits(parent_bus_addr) && dw_pcie_ver_is_ge(pci, 460A)) val |= PCIE_ATU_INCREASE_REGION_SIZE; - if (dw_pcie_ver_is(pci, 490A) || dw_pcie_ver_is(pci, 500A)) + if (!dw_pcie_ver_is_ge(pci, 510A)) val = dw_pcie_enable_ecrc(val); dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_REGION_CTRL1, val); diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 3e69ef60165b0..a07b7abda41f3 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -35,6 +35,7 @@ #define DW_PCIE_VER_480A 0x3438302a #define DW_PCIE_VER_490A 0x3439302a #define DW_PCIE_VER_500A 0x3530302a +#define DW_PCIE_VER_510A 0x3531302a #define DW_PCIE_VER_520A 0x3532302a #define DW_PCIE_VER_540A 0x3534302a #define DW_PCIE_VER_562A 0x3536322a From 0ba6fd199192e4f564df3e7217c90d5bfc44d2b2 Mon Sep 17 00:00:00 2001 From: Qiang Yu Date: Thu, 16 Apr 2026 21:16:25 -0700 Subject: [PATCH 1540/2758] PCI: qcom: Set max OPP before DBI access during resume [ Upstream commit 5dc31cd4a91a7f006d23efa97a5594a7e3ac7790 ] During resume, qcom_pcie_icc_opp_update() may access DBI registers before the OPP votes are restored, triggering NoC errors. Set the PCIe controller to the maximum OPP first in resume_noirq(), then proceed with link/DBI accesses. The OPP is later updated again based on the actual link bandwidth requirements. Introduce a helper to reuse the max-OPP setup code and share it with probe(). Fixes: 5b6272e0efd5 ("PCI: qcom: Add OPP support to scale performance") Signed-off-by: Qiang Yu [mani: commit log and error log rewording] Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260416-setmaxopp-v1-1-6a74e2d945a0@oss.qualcomm.com Signed-off-by: Sasha Levin --- drivers/pci/controller/dwc/pcie-qcom.c | 42 ++++++++++++++++---------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 7d8eaef436efe..8604aa564dce6 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -1613,6 +1613,22 @@ static void qcom_pcie_icc_opp_update(struct qcom_pcie *pcie) } } +static int qcom_pcie_set_max_opp(struct device *dev) +{ + unsigned long max_freq = ULONG_MAX; + struct dev_pm_opp *opp; + int ret; + + opp = dev_pm_opp_find_freq_floor(dev, &max_freq); + if (IS_ERR(opp)) + return PTR_ERR(opp); + + ret = dev_pm_opp_set_opp(dev, opp); + dev_pm_opp_put(opp); + + return ret; +} + static int qcom_pcie_link_transition_count(struct seq_file *s, void *data) { struct qcom_pcie *pcie = (struct qcom_pcie *)dev_get_drvdata(s->private); @@ -1851,9 +1867,7 @@ static int qcom_pcie_probe(struct platform_device *pdev) struct qcom_pcie_perst *perst, *tmp_perst; struct qcom_pcie_port *port, *tmp_port; const struct qcom_pcie_cfg *pcie_cfg; - unsigned long max_freq = ULONG_MAX; struct device *dev = &pdev->dev; - struct dev_pm_opp *opp; struct qcom_pcie *pcie; struct dw_pcie_rp *pp; struct resource *res; @@ -1957,21 +1971,9 @@ static int qcom_pcie_probe(struct platform_device *pdev) * probe(), OPP will be updated using qcom_pcie_icc_opp_update(). */ if (!ret) { - opp = dev_pm_opp_find_freq_floor(dev, &max_freq); - if (IS_ERR(opp)) { - ret = PTR_ERR(opp); - dev_err_probe(pci->dev, ret, - "Unable to find max freq OPP\n"); - goto err_pm_runtime_put; - } else { - ret = dev_pm_opp_set_opp(dev, opp); - } - - dev_pm_opp_put(opp); + ret = qcom_pcie_set_max_opp(dev); if (ret) { - dev_err_probe(pci->dev, ret, - "Failed to set OPP for freq %lu\n", - max_freq); + dev_err_probe(dev, ret, "Failed to set max OPP\n"); goto err_pm_runtime_put; } @@ -2106,6 +2108,14 @@ static int qcom_pcie_resume_noirq(struct device *dev) return 0; if (pm_suspend_target_state != PM_SUSPEND_MEM) { + if (pcie->use_pm_opp) { + ret = qcom_pcie_set_max_opp(dev); + if (ret) { + dev_err(dev, "Failed to set max OPP: %d\n", ret); + return ret; + } + } + ret = icc_enable(pcie->icc_cpu); if (ret) { dev_err(dev, "Failed to enable CPU-PCIe interconnect path: %d\n", ret); From cb329b1fa702ece1bb0c5638c5eb715c5476d0a8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 May 2026 00:01:26 +0200 Subject: [PATCH 1541/2758] phy: phy-can-transceiver: Check driver match and driver data against NULL [ Upstream commit ebee9004cc0200b2b708ebf7ac625d35c71c049f ] Every platform driver can be forced to match a device that doesn't match its list of device IDs because of device_match_driver_override() so platform drivers that rely on the existence of a device's driver data need to verify its presence. Accordingly, add requisite match and driver data checks against NULL to the driver where they are missing. Fixes: a4a86d273ff1 ("phy: phy-can-transceiver: Add support for generic CAN transceiver driver") Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20260513220336.369628-2-andriy.shevchenko@linux.intel.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/phy/phy-can-transceiver.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c index 2b52e47f247a2..1808f903c0570 100644 --- a/drivers/phy/phy-can-transceiver.c +++ b/drivers/phy/phy-can-transceiver.c @@ -162,6 +162,9 @@ static int can_transceiver_phy_probe(struct platform_device *pdev) int err, i, num_ch = 1; match = of_match_node(can_transceiver_phy_ids, pdev->dev.of_node); + if (!match || !match->data) + return -ENODEV; + drvdata = match->data; if (drvdata->flags & CAN_TRANSCEIVER_DUAL_CH) num_ch = 2; From 9810f833df66fc3f7afcff667155344177b46f7f Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 14 Apr 2026 10:58:55 -0700 Subject: [PATCH 1542/2758] perf pmu-events AMD: Switch l2_itlb_misses to bp_l1_tlb_miss_l2_tlb_miss.all [ Upstream commit 41a543c86d110073275e5294852d692e5faf6b3b ] l2_itlb_misses is a valid legacy cache event name, hence allowing it in all_events in metric.py. l2_itlb_misses was also a json event for AMD zen1, zen2 and zen3. For zen4, zen5 and zen6 the checking that metric events are within the json was skipping l2_itlb_misses as it is a valid legacy event, however, the PMU driver lacks the event mapping causing it to be a bad event when used in the metric. Add bp_l1_tlb_miss_l2_tlb_miss.all as the l2 itlb miss event (bp = branch predictor, the AMD way to say itlb), so that is used in preference to l2_itlb_misses when the event exists. Remove l2_itlb_misses from metric.py as the legacy event isn't used by any metrics and having it is error prone for newer AMD zen models. Fixes: e596f329668ec2b5 ("perf jevents: Add itlb metric group for AMD") Reviewed-by: Sandipan Das Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Ravi Bangoria Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/pmu-events/amd_metrics.py | 2 +- tools/perf/pmu-events/metric.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/perf/pmu-events/amd_metrics.py b/tools/perf/pmu-events/amd_metrics.py index e2defaffde3e6..971f6e7af1f82 100755 --- a/tools/perf/pmu-events/amd_metrics.py +++ b/tools/perf/pmu-events/amd_metrics.py @@ -268,7 +268,7 @@ def AmdDtlb() -> Optional[MetricGroup]: def AmdItlb(): global _zen_model l2h = Event("bp_l1_tlb_miss_l2_tlb_hit", "bp_l1_tlb_miss_l2_hit") - l2m = Event("l2_itlb_misses") + l2m = Event("bp_l1_tlb_miss_l2_tlb_miss.all", "l2_itlb_misses",) l2r = l2h + l2m itlb_l1_mg = None diff --git a/tools/perf/pmu-events/metric.py b/tools/perf/pmu-events/metric.py index 585454828c2fe..ac582db785fc1 100644 --- a/tools/perf/pmu-events/metric.py +++ b/tools/perf/pmu-events/metric.py @@ -25,7 +25,6 @@ def LoadEvents(directory: str) -> None: "cycles", "duration_time", "instructions", - "l2_itlb_misses", } for file in os.listdir(os.fsencode(directory)): filename = os.fsdecode(file) From 03dda04f2f768ad620eee20fec1e04068330d483 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 13 May 2026 16:31:45 -0700 Subject: [PATCH 1543/2758] perf unwind: Refactor get_entries to allow dynamic libdw/libunwind selection [ Upstream commit 4248ae6e799605b3e62855be6085935d89de50d1 ] Currently, both libdw and libunwind define 'unwind__get_entries'. This causes a duplicate symbol build failure when both are compiled into perf. This commit refactors the DWARF unwind post-processing to be configurable at runtime via the .perfconfig file option 'unwind.style', or using the argument '--unwind-style' in the commands 'perf report', 'perf script' and 'perf inject', in a similar manner to the addr2line or the disassembler style. The file 'tools/perf/util/unwind.c' adds the top-level dispatch function 'unwind__get_entries'. The backend implementations are renamed to 'libdw__get_entries' and 'libunwind__get_entries'. Both are attempted as fallbacks if not configured, or if the primary backend fails. Fixes: 2e9191573a69ff96 ("perf build: Remove NO_LIBDW_DWARF_UNWIND option") Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andrew Jones Cc: Athira Rajeev Cc: Dapeng Mi Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Florian Fainelli Cc: Howard Chu Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Leo Yan Cc: libunwind-devel@nongnu.org Cc: Li Guan Cc: Namhyung Kim Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Shimin Guo Cc: Thomas Richter Cc: Tomas Glozar Cc: Will Deacon [ Don't mix declarations and code, move 'entries' variable to the start of scope ] [ Use pr_warning_once() instead of pr_err() in stubs for get_entries(), suggested by a local sashiko instance ] Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-inject.c | 4 + tools/perf/builtin-report.c | 4 + tools/perf/builtin-script.c | 4 + tools/perf/util/Build | 1 + tools/perf/util/config.c | 4 + tools/perf/util/symbol_conf.h | 10 +++ tools/perf/util/unwind-libdw.c | 20 ++++- tools/perf/util/unwind-libunwind-local.c | 27 ++++-- tools/perf/util/unwind-libunwind.c | 2 +- tools/perf/util/unwind.c | 104 +++++++++++++++++++++++ tools/perf/util/unwind.h | 61 ++++++++----- 11 files changed, 208 insertions(+), 33 deletions(-) create mode 100644 tools/perf/util/unwind.c diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 6ab20df358c43..a2493f1097df9 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -26,6 +26,7 @@ #include "util/synthetic-events.h" #include "util/thread.h" #include "util/namespaces.h" +#include "util/unwind.h" #include "util/util.h" #include "util/tsc.h" @@ -2563,6 +2564,9 @@ int cmd_inject(int argc, const char **argv) OPT_STRING(0, "guestmount", &symbol_conf.guestmount, "directory", "guest mount directory under which every guest os" " instance has a subdir"), + OPT_CALLBACK(0, "unwind-style", NULL, "unwind style", + "unwind styles (libdw,libunwind)", + unwind__option), OPT_BOOLEAN(0, "convert-callchain", &inject.convert_callchain, "Generate callchains using DWARF and drop register/stack data"), OPT_END() diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 95c0bdba6b116..0b0966d94128e 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -48,6 +48,7 @@ #include "util/time-utils.h" #include "util/auxtrace.h" #include "util/units.h" +#include "util/unwind.h" #include "util/util.h" // perf_tip() #include "ui/ui.h" #include "ui/progress.h" @@ -1449,6 +1450,9 @@ int cmd_report(int argc, const char **argv) OPT_CALLBACK(0, "addr2line-style", NULL, "addr2line style", "addr2line styles (libdw,llvm,libbfd,addr2line)", report_parse_addr2line_config), + OPT_CALLBACK(0, "unwind-style", NULL, "unwind style", + "unwind styles (libdw,libunwind)", + unwind__option), OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle, "Symbol demangling. Enabled by default, use --no-demangle to disable."), OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel, diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index c8ac9f01a36bc..fd0b4609516b3 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -63,6 +63,7 @@ #include #include "util/dlfilter.h" #include "util/record.h" +#include "util/unwind.h" #include "util/util.h" #include "util/cgroup.h" #include "util/annotate.h" @@ -4159,6 +4160,9 @@ int cmd_script(int argc, const char **argv) "Enable symbol demangling"), OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel, "Enable kernel symbol demangling"), + OPT_CALLBACK(0, "unwind-style", NULL, "unwind style", + "unwind styles (libdw,libunwind)", + unwind__option), OPT_STRING(0, "addr2line", &symbol_conf.addr2line_path, "path", "addr2line binary to use for line numbers"), OPT_STRING(0, "time", &script.time_str, "str", diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 70cc91d00804d..01edfccebb88e 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -216,6 +216,7 @@ ifndef CONFIG_SETNS perf-util-y += setns.o endif +perf-util-y += unwind.o perf-util-$(CONFIG_LIBDW) += probe-finder.o perf-util-$(CONFIG_LIBDW) += dwarf-aux.o perf-util-$(CONFIG_LIBDW) += dwarf-regs.o diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index 087002fb1b9bc..7988149dc7ed8 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c @@ -23,6 +23,7 @@ #include "build-id.h" #include "debug.h" #include "config.h" +#include "unwind.h" #include #include #include @@ -525,6 +526,9 @@ int perf_default_config(const char *var, const char *value, if (strstarts(var, "addr2line.")) return addr2line_configure(var, value, dummy); + if (strstarts(var, "unwind.")) + return unwind__configure(var, value, dummy); + /* Add other config variables here. */ return 0; } diff --git a/tools/perf/util/symbol_conf.h b/tools/perf/util/symbol_conf.h index 6cd454d7c98e6..0dee5aa6a5340 100644 --- a/tools/perf/util/symbol_conf.h +++ b/tools/perf/util/symbol_conf.h @@ -9,6 +9,15 @@ struct strlist; struct intlist; +enum unwind_style { + UNWIND_STYLE_UNKNOWN = 0, + UNWIND_STYLE_LIBDW, + UNWIND_STYLE_LIBUNWIND, +}; + +#define MAX_UNWIND_STYLE (UNWIND_STYLE_LIBUNWIND + 1) + + enum a2l_style { A2L_STYLE_UNKNOWN = 0, A2L_STYLE_LIBDW, @@ -81,6 +90,7 @@ struct symbol_conf { const char *addr2line_path; enum a2l_style addr2line_style[MAX_A2L_STYLE]; int addr2line_timeout_ms; + enum unwind_style unwind_style[MAX_UNWIND_STYLE]; unsigned long time_quantum; struct strlist *dso_list, *comm_list, diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c index 05e8e68bd49c4..7f35042be5677 100644 --- a/tools/perf/util/unwind-libdw.c +++ b/tools/perf/util/unwind-libdw.c @@ -339,7 +339,7 @@ frame_callback(Dwfl_Frame *state, void *arg) DWARF_CB_ABORT : DWARF_CB_OK; } -int unwind__get_entries(unwind_entry_cb_t cb, void *arg, +int libdw__get_entries(unwind_entry_cb_t cb, void *arg, struct thread *thread, struct perf_sample *data, int max_stack, @@ -353,10 +353,10 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, static struct unwind_info *ui; Dwfl *dwfl; Dwarf_Word ip; - int err = -EINVAL, i; + int err = -EINVAL, i, entries; if (!data->user_regs || !data->user_regs->regs) - return -EINVAL; + return 0; ui = zalloc(sizeof(*ui) + sizeof(ui->entries[0]) * max_stack); if (!ui) @@ -430,6 +430,18 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, map_symbol__exit(&ui->entries[i].ms); dwfl_ui_ti->ui = NULL; + entries = (int)ui->idx; free(ui); - return 0; + /* + * Unwinder return contract: + * > 0 : unwinding succeeded (stops fallback). If we found frames but hit an error + * (e.g. truncated stack), report success to preserve existing frames. + * 0 : unwinding failed without yielding frames. Ignore non-fatal errors + * (e.g. missing debug info, DWARF corruption) to allow fallback unwinder or + * kernel callchain resolution to proceed. + * < 0 : fatal error (e.g. -ENOMEM). Aborts unwinding entirely. + */ + if (err) + return (err == -ENOMEM) ? -ENOMEM : (entries > 0 ? 1 : 0); + return entries; } diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c index 87d496e9dfa66..27e2f7b317895 100644 --- a/tools/perf/util/unwind-libunwind-local.c +++ b/tools/perf/util/unwind-libunwind-local.c @@ -744,7 +744,7 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, ret = perf_reg_value(&val, perf_sample__user_regs(ui->sample), perf_arch_reg_ip(e_machine)); if (ret) - return ret; + return 0; ips[i++] = (unw_word_t) val; @@ -757,7 +757,7 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, addr_space = maps__addr_space(thread__maps(ui->thread)); if (addr_space == NULL) - return -1; + return 0; ret = unw_init_remote(&c, addr_space, ui); if (ret && !ui->best_effort) @@ -785,15 +785,30 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, /* * Display what we got based on the order setup. */ + int entries = 0; for (i = 0; i < max_stack && !ret; i++) { int j = i; if (callchain_param.order == ORDER_CALLER) j = max_stack - i - 1; - ret = ips[j] ? entry(ips[j], ui->thread, cb, arg) : 0; + if (ips[j]) { + ret = entry(ips[j], ui->thread, cb, arg); + if (ret) + break; + entries++; + } } - return ret; + /* + * Unwinder return contract: + * > 0 : unwinding succeeded (stops fallback). + * 0 : unwinding failed without yielding frames. Ignore non-fatal errors + * (e.g. stepping failure) to allow fallback unwinder or kernel callchains. + * < 0 : fatal error (e.g. -ENOMEM). Aborts unwinding entirely. + */ + if (ret == -ENOMEM) + return -ENOMEM; + return (entries > 0 || ret == 0) ? entries : 0; } static int _unwind__get_entries(unwind_entry_cb_t cb, void *arg, @@ -809,10 +824,10 @@ static int _unwind__get_entries(unwind_entry_cb_t cb, void *arg, }; if (!data->user_regs || !data->user_regs->regs) - return -EINVAL; + return 0; if (max_stack <= 0) - return -EINVAL; + return 0; return get_entries(&ui, cb, arg, max_stack); } diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c index cb8be6acfb6f5..a0016b897dae2 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c @@ -79,7 +79,7 @@ void unwind__finish_access(struct maps *maps) ops->finish_access(maps); } -int unwind__get_entries(unwind_entry_cb_t cb, void *arg, +int libunwind__get_entries(unwind_entry_cb_t cb, void *arg, struct thread *thread, struct perf_sample *data, int max_stack, bool best_effort) diff --git a/tools/perf/util/unwind.c b/tools/perf/util/unwind.c new file mode 100644 index 0000000000000..4ed4b1d55c69f --- /dev/null +++ b/tools/perf/util/unwind.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "debug.h" +#include "symbol_conf.h" +#include "unwind.h" +#include +#include +#include + +int unwind__get_entries(unwind_entry_cb_t cb __maybe_unused, void *arg __maybe_unused, + struct thread *thread __maybe_unused, + struct perf_sample *data __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused) +{ + int ret = 0; + +#if defined(HAVE_LIBDW_SUPPORT) || defined(HAVE_LIBUNWIND_SUPPORT) + if (symbol_conf.unwind_style[0] == UNWIND_STYLE_UNKNOWN) { + int i = 0; +#ifdef HAVE_LIBDW_SUPPORT + symbol_conf.unwind_style[i++] = UNWIND_STYLE_LIBDW; +#endif +#ifdef HAVE_LIBUNWIND_SUPPORT + symbol_conf.unwind_style[i++] = UNWIND_STYLE_LIBUNWIND; +#endif + } +#endif //defined(HAVE_LIBDW_SUPPORT) || defined(HAVE_LIBUNWIND_SUPPORT) + + for (size_t i = 0; i < ARRAY_SIZE(symbol_conf.unwind_style); i++) { + switch (symbol_conf.unwind_style[i]) { + case UNWIND_STYLE_LIBDW: + ret = libdw__get_entries(cb, arg, thread, data, max_stack, best_effort); + break; + case UNWIND_STYLE_LIBUNWIND: + ret = libunwind__get_entries(cb, arg, thread, data, max_stack, best_effort); + break; + case UNWIND_STYLE_UNKNOWN: + default: +#if !defined(HAVE_LIBDW_SUPPORT) && !defined(HAVE_LIBUNWIND_SUPPORT) + pr_warning_once( + "Error: dwarf unwinding not supported, build perf with libdw or libunwind.\n"); +#endif + ret = 0; + break; + } + if (ret > 0) { + ret = 0; + break; + } + if (ret < 0) + break; + } + return ret; +} + +int unwind__configure(const char *var, const char *value, void *cb __maybe_unused) +{ + static const char * const unwind_style_names[] = { + [UNWIND_STYLE_LIBDW] = "libdw", + [UNWIND_STYLE_LIBUNWIND] = "libunwind", + NULL + }; + char *s, *p, *saveptr; + size_t i = 0; + + if (strcmp(var, "unwind.style")) + return 0; + + if (!value) + return -1; + + s = strdup(value); + if (!s) + return -1; + + memset(symbol_conf.unwind_style, 0, sizeof(symbol_conf.unwind_style)); + + p = strtok_r(s, ",", &saveptr); + while (p && i < ARRAY_SIZE(symbol_conf.unwind_style)) { + bool found = false; + char *q = strim(p); + + for (size_t j = UNWIND_STYLE_LIBDW; j < MAX_UNWIND_STYLE; j++) { + if (!strcasecmp(q, unwind_style_names[j])) { + symbol_conf.unwind_style[i++] = j; + found = true; + break; + } + } + if (!found) + pr_warning("Unknown unwind style: %s\n", q); + p = strtok_r(NULL, ",", &saveptr); + } + + free(s); + return 0; +} + +int unwind__option(const struct option *opt __maybe_unused, + const char *arg, + int unset __maybe_unused) +{ + return unwind__configure("unwind.style", arg, NULL); +} diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h index 9f7164c6d9aa9..69ba08afda792 100644 --- a/tools/perf/util/unwind.h +++ b/tools/perf/util/unwind.h @@ -4,9 +4,10 @@ #include #include -#include "util/map_symbol.h" +#include "map_symbol.h" struct maps; +struct option; struct perf_sample; struct thread; @@ -26,7 +27,9 @@ struct unwind_libunwind_ops { struct perf_sample *data, int max_stack, bool best_effort); }; -#ifdef HAVE_DWARF_UNWIND_SUPPORT +int unwind__configure(const char *var, const char *value, void *cb); +int unwind__option(const struct option *opt, const char *arg, int unset); + /* * When best_effort is set, don't report errors and fail silently. This could * be expanded in the future to be more permissive about things other than @@ -36,8 +39,31 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, struct thread *thread, struct perf_sample *data, int max_stack, bool best_effort); -/* libunwind specific */ + +#ifdef HAVE_LIBDW_SUPPORT +int libdw__get_entries(unwind_entry_cb_t cb, void *arg, + struct thread *thread, + struct perf_sample *data, int max_stack, + bool best_effort); +#else +#include "debug.h" +static inline int libdw__get_entries(unwind_entry_cb_t cb __maybe_unused, void *arg __maybe_unused, + struct thread *thread __maybe_unused, + struct perf_sample *data __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused) +{ + pr_warning_once("Error: libdw dwarf unwinding not built into perf\n"); + return 0; +} +#endif + #ifdef HAVE_LIBUNWIND_SUPPORT +/* libunwind specific */ +int libunwind__get_entries(unwind_entry_cb_t cb, void *arg, + struct thread *thread, + struct perf_sample *data, int max_stack, + bool best_effort); #ifndef LIBUNWIND__ARCH_REG_ID #define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__arch_reg_id(regnum) #endif @@ -47,25 +73,15 @@ int unwind__prepare_access(struct maps *maps, struct map *map, bool *initialized void unwind__flush_access(struct maps *maps); void unwind__finish_access(struct maps *maps); #else -static inline int unwind__prepare_access(struct maps *maps __maybe_unused, - struct map *map __maybe_unused, - bool *initialized __maybe_unused) -{ - return 0; -} - -static inline void unwind__flush_access(struct maps *maps __maybe_unused) {} -static inline void unwind__finish_access(struct maps *maps __maybe_unused) {} -#endif -#else -static inline int -unwind__get_entries(unwind_entry_cb_t cb __maybe_unused, - void *arg __maybe_unused, - struct thread *thread __maybe_unused, - struct perf_sample *data __maybe_unused, - int max_stack __maybe_unused, - bool best_effort __maybe_unused) +#include "debug.h" +static inline int libunwind__get_entries(unwind_entry_cb_t cb __maybe_unused, + void *arg __maybe_unused, + struct thread *thread __maybe_unused, + struct perf_sample *data __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused) { + pr_warning_once("Error: libunwind dwarf unwinding not built into perf\n"); return 0; } @@ -78,5 +94,6 @@ static inline int unwind__prepare_access(struct maps *maps __maybe_unused, static inline void unwind__flush_access(struct maps *maps __maybe_unused) {} static inline void unwind__finish_access(struct maps *maps __maybe_unused) {} -#endif /* HAVE_DWARF_UNWIND_SUPPORT */ +#endif + #endif /* __UNWIND_H */ From 8a7a8ac82791d218600dffa3fabbe7cf37b452ba Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 10 Apr 2026 12:13:44 +0100 Subject: [PATCH 1544/2758] perf pmu: Skip test on Arm64 when #slots is zero [ Upstream commit 2e2ba7d1ea554ee6e9e751a53eebf3e9270b0670 ] Some Arm64 PMUs expose 'caps/slots' as 0 when the slot count is not implemented, tool_pmu__read_event() currently returns false for this, so metrics that reference #slots are reported as syntax error. Since the commit 3a61fd866ef9 ("perf expr: Return -EINVAL for syntax error in expr__find_ids()"), these syntax errors are populated as failures and make the PMU metric test fail: 9.3: Parsing of PMU event table metrics: --- start --- ... Found metric 'backend_bound' metric expr 100 * (stall_slot_backend / (#slots * cpu_cycles)) for backend_bound parsing metric: 100 * (stall_slot_backend / (#slots * cpu_cycles)) Failure to read '#slots' literal: #slots = nan syntax error Fail to parse metric or group `backend_bound' ... ---- end(-1) ---- 9.3: Parsing of PMU event table metrics : FAILED! This commit introduces a new function is_expected_broken_metric() to identify broken metrics, and treats metrics containing "#slots" as expected broken when #slots == 0 on Arm64 platforms. Fixes: 3a61fd866ef9aaa1 ("perf expr: Return -EINVAL for syntax error in expr__find_ids()") Reviewed-by: Ian Rogers Reviewed-by: James Clark Signed-off-by: Leo Yan Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/tests/pmu-events.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c index a997168621688..b1609a7e1d8c9 100644 --- a/tools/perf/tests/pmu-events.c +++ b/tools/perf/tests/pmu-events.c @@ -15,6 +15,7 @@ #include "util/expr.h" #include "util/hashmap.h" #include "util/parse-events.h" +#include "util/tool_pmu.h" #include "metricgroup.h" #include "stat.h" @@ -817,6 +818,26 @@ struct metric { struct metric_ref metric_ref; }; +static bool is_expected_broken_metric(const struct pmu_metric *pm) +{ + if (!strcmp(pm->metric_name, "M1") || !strcmp(pm->metric_name, "M2") || + !strcmp(pm->metric_name, "M3")) + return true; + +#if defined(__aarch64__) + /* + * Arm64 platforms may return "#slots == 0", which is treated as a + * syntax error by the parser. Don't test these metrics when running + * on such platforms. + */ + if (strstr(pm->metric_expr, "#slots") && + !tool_pmu__cpu_slots_per_cycle()) + return true; +#endif + + return false; +} + static int test__parsing_callback(const struct pmu_metric *pm, const struct pmu_metrics_table *table, void *data) @@ -852,8 +873,7 @@ static int test__parsing_callback(const struct pmu_metric *pm, err = metricgroup__parse_groups_test(evlist, table, pm->metric_name); if (err) { - if (!strcmp(pm->metric_name, "M1") || !strcmp(pm->metric_name, "M2") || - !strcmp(pm->metric_name, "M3")) { + if (is_expected_broken_metric(pm)) { (*failures)--; pr_debug("Expected broken metric %s skipping\n", pm->metric_name); err = 0; From 14622b111e4e16c9066597a1d08c3fa55d81c14d Mon Sep 17 00:00:00 2001 From: Mihai Sain Date: Mon, 9 Mar 2026 09:53:28 +0200 Subject: [PATCH 1545/2758] clk: at91: sam9x7: Fix gmac_gclk clock definition [ Upstream commit b6f6ebb0fb57ae6da622fb8fd4ebdc9ba1ae5756 ] According to the datasheet (see link section), table 12.1, instance ID 24 is used for the GMAC generic clock, while instance ID 67 is reserved. Add the correct gmac_gclk entry at ID 24, aligned with the SoC clock layout, and remove the old misplaced entry at ID 67. Link: https://ww1.microchip.com/downloads/aemDocuments/documents/MPU32/ProductDocuments/DataSheets/SAM9X75-SIP-Series-Data-Sheet-DS60001827.pdf Fixes: 33013b43e271 ("clk: at91: sam9x7: add sam9x7 pmc driver") Signed-off-by: Mihai Sain Link: https://lore.kernel.org/r/20260309075329.1528-4-mihai.sain@microchip.com [claudiu.beznea: massaged the patch description] Signed-off-by: Claudiu Beznea Signed-off-by: Sasha Levin --- drivers/clk/at91/sam9x7.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/clk/at91/sam9x7.c b/drivers/clk/at91/sam9x7.c index 89868a0aeaba9..6b330c3e6bca8 100644 --- a/drivers/clk/at91/sam9x7.c +++ b/drivers/clk/at91/sam9x7.c @@ -569,6 +569,15 @@ static const struct { .pp_chg_id = INT_MIN, }, + { + .n = "gmac_gclk", + .id = 24, + .pp = { "audiopll_divpmcck", "plla_div2pmcck", }, + .pp_mux_table = { 6, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, + }, + { .n = "lcd_gclk", .id = 25, @@ -702,15 +711,6 @@ static const struct { .pp_count = 1, .pp_chg_id = INT_MIN, }, - - { - .n = "gmac_gclk", - .id = 67, - .pp = { "audiopll_divpmcck", "plla_div2pmcck", }, - .pp_mux_table = { 6, 8, }, - .pp_count = 2, - .pp_chg_id = INT_MIN, - }, }; static void __init sam9x7_pmc_setup(struct device_node *np) From abdfdb8e6220ab9ec50822ba5f5416f2d674668a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 12 May 2026 18:28:26 +0200 Subject: [PATCH 1546/2758] iio: light: acpi-als: Check ACPI_COMPANION() against NULL [ Upstream commit ff29241030eb6f4505d903d87c29f51c1866a95d ] Every platform driver can be forced to match a device that doesn't match its list of device IDs because of device_match_driver_override(), so platform drivers that rely on the existence of a device's ACPI companion object need to verify its presence. Accordingly, add a requisite ACPI_COMPANION() check against NULL to the acpi-als IIO driver. Fixes: d4243cb08a27 ("iio: light: acpi-als: Convert ACPI driver to a platform one") Signed-off-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron Signed-off-by: Sasha Levin --- drivers/iio/light/acpi-als.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/iio/light/acpi-als.c b/drivers/iio/light/acpi-als.c index ab229318dce94..1983a7f17aa93 100644 --- a/drivers/iio/light/acpi-als.c +++ b/drivers/iio/light/acpi-als.c @@ -179,11 +179,15 @@ static irqreturn_t acpi_als_trigger_handler(int irq, void *p) static int acpi_als_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct acpi_device *device = ACPI_COMPANION(dev); + struct acpi_device *device; struct iio_dev *indio_dev; struct acpi_als *als; int ret; + device = ACPI_COMPANION(dev); + if (!device) + return -ENODEV; + indio_dev = devm_iio_device_alloc(dev, sizeof(*als)); if (!indio_dev) return -ENOMEM; From 81ed540159efd744c9fa4dd99039ecf788b8922f Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 14 May 2026 22:16:25 +0800 Subject: [PATCH 1547/2758] soundwire: intel_ace2x: release bpt_stream when close it [ Upstream commit 8a7fe10eec64bfb7cf4091bca540de4c55d56bfa ] The BPT stream was allocated in intel_ace2x_bpt_open_stream(), we need to free it in intel_ace2x_bpt_close_stream(). Fixes: 4c1ce9f37d8a8 ("soundwire: intel_ace2x: add BPT send_async/wait callbacks") Signed-off-by: Bard Liao Reviewed-by: Simon Trimmer Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260514141625.1834216-1-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/soundwire/intel_ace2x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c index 20422534baf19..6cd3a87323750 100644 --- a/drivers/soundwire/intel_ace2x.c +++ b/drivers/soundwire/intel_ace2x.c @@ -317,6 +317,7 @@ static void intel_ace2x_bpt_close_stream(struct sdw_intel *sdw, struct sdw_slave dev_err(cdns->dev, "%s: remove slave failed: %d\n", __func__, ret); + sdw_release_stream(cdns->bus.bpt_stream); cdns->bus.bpt_stream = NULL; } From c37f87151990dc079560b98ebe3b00a067b4953c Mon Sep 17 00:00:00 2001 From: Jie Gan Date: Fri, 15 May 2026 21:08:08 +0100 Subject: [PATCH 1548/2758] coresight: Fix source not disabled on idr_alloc_u32 failure [ Upstream commit ea2c2b9e2a66e2b4aa0455b2d70058e2f0ea4d23 ] In coresight_enable_sysfs(), for non-CPU sources (SOFTWARE, TPDM, OTHERS), the source device is enabled via coresight_enable_source_sysfs() before idr_alloc_u32() maps the path. If idr_alloc_u32() fails, the original code jumped directly to err_source, which only calls coresight_disable_path() and coresight_release_path(). The source device was left enabled with an incremented refcnt but no path tracked for it, leaving the device in an inconsistent state. Disable the source before jumping to err_source so the enable and path operations are fully unwound. Fixes: 5c0016d7b343 ("coresight: core: Use IDR for non-cpu bound sources' paths.") Signed-off-by: Jie Gan Reviewed-by: Yeoreum Yun Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-1-f88c4a3ecfe9@arm.com Signed-off-by: Sasha Levin --- drivers/hwtracing/coresight/coresight-sysfs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c index b6a870399e834..da6f22b512c92 100644 --- a/drivers/hwtracing/coresight/coresight-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-sysfs.c @@ -244,8 +244,10 @@ int coresight_enable_sysfs(struct coresight_device *csdev) */ hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev))); ret = idr_alloc_u32(&path_idr, path, &hash, hash, GFP_KERNEL); - if (ret) + if (ret) { + coresight_disable_source_sysfs(csdev, NULL); goto err_source; + } break; default: /* We can't be here */ From 8c1dac9c05d489decb7db9b5e90573727a15354a Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 15 May 2026 21:08:09 +0100 Subject: [PATCH 1549/2758] coresight: Handle helper enable failure properly [ Upstream commit 864754d0a084141085f154db044401fb2dce6a34 ] If a helper fails to be enabled, unwind any helpers that were already enabled earlier in the loop. This avoids leaving partially enabled helpers behind. Fixes: 6148652807ba ("coresight: Enable and disable helper devices adjacent to the path") Reviewed-by: Yeoreum Yun Reviewed-by: James Clark Tested-by: James Clark Tested-by: Jie Gan Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-2-f88c4a3ecfe9@arm.com Signed-off-by: Sasha Levin --- drivers/hwtracing/coresight/coresight-core.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 2105bb8139407..256f6a32621b8 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -499,10 +499,19 @@ static int coresight_enable_helpers(struct coresight_device *csdev, ret = coresight_enable_helper(helper, mode, path); if (ret) - return ret; + goto err; } return 0; + +err: + while (i--) { + helper = csdev->pdata->out_conns[i]->dest_dev; + if (helper && coresight_is_helper(helper)) + coresight_disable_helper(helper, path); + } + + return ret; } int coresight_enable_path(struct coresight_path *path, enum cs_mode mode) From 80cf6501acb9766778e2518a921ccae2c7c46924 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 5 May 2026 18:59:16 +0800 Subject: [PATCH 1550/2758] PCI: mediatek-gen3: Do full device power down on removal [ Upstream commit d39d55d7411c18ca6aeb63aafa8035f4ad8b317f ] When power control for downstream devices was introduced in the mediatek-gen3 PCIe controller driver, only the power to the downstream devices was cut when the controller driver is removed. This matched existing behavior, but in hindsight a proper power down sequence should have been followed. Call mtk_pcie_devices_power_down() on driver removal so that in addition to removing power from the downstream devices, PERST# is asserted. Fixes: 1a152e21940a ("PCI: mediatek-gen3: Integrate new pwrctrl API") Signed-off-by: Chen-Yu Tsai Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260505105918.1823170-1-wenst@chromium.org Signed-off-by: Sasha Levin --- drivers/pci/controller/pcie-mediatek-gen3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c index b0accd8285892..e6acca7e79a0f 100644 --- a/drivers/pci/controller/pcie-mediatek-gen3.c +++ b/drivers/pci/controller/pcie-mediatek-gen3.c @@ -1260,7 +1260,7 @@ static void mtk_pcie_remove(struct platform_device *pdev) pci_remove_root_bus(host->bus); pci_unlock_rescan_remove(); - pci_pwrctrl_power_off_devices(pcie->dev); + mtk_pcie_devices_power_down(pcie); mtk_pcie_power_down(pcie); pci_pwrctrl_destroy_devices(pcie->dev); mtk_pcie_irq_teardown(pcie); From 8ceeb0541978dbafdc5f3c98ff8bbe0be713b8c6 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Thu, 14 May 2026 17:39:51 +0100 Subject: [PATCH 1551/2758] mailbox: mpfs: fix check for syscon presence in mpfs_mbox_inbox_isr() [ Upstream commit e30d8b2730a33e5e8789371e947c3529789a6070 ] mpfs_mbox_inbox_isr() writes to the sysreg scb syscon, not the control scb syscon, but checks for the presence of the latter. Ultimately this makes little difference because if one syscon is present, both will be. Fixes: a4123ffab9ece ("mailbox: mpfs: support new, syscon based, devicetree configuration") Signed-off-by: Conor Dooley Signed-off-by: Jassi Brar Signed-off-by: Sasha Levin --- drivers/mailbox/mailbox-mpfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mailbox/mailbox-mpfs.c b/drivers/mailbox/mailbox-mpfs.c index d5d9effece979..ef40fe2be30d6 100644 --- a/drivers/mailbox/mailbox-mpfs.c +++ b/drivers/mailbox/mailbox-mpfs.c @@ -201,7 +201,7 @@ static irqreturn_t mpfs_mbox_inbox_isr(int irq, void *data) struct mbox_chan *chan = data; struct mpfs_mbox *mbox = (struct mpfs_mbox *)chan->con_priv; - if (mbox->control_scb) + if (mbox->sysreg_scb) regmap_write(mbox->sysreg_scb, MESSAGE_INT_OFFSET, 0); else writel_relaxed(0, mbox->int_reg); From 25d6ea6c76e1b1b7c57337b2f8f1b6fc8d5c52bc Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Tue, 28 Apr 2026 11:55:44 +0900 Subject: [PATCH 1552/2758] mailbox: mtk-adsp: fix UAF during device teardown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit b57d1a40bc43258372fa1f4d39305e093947a262 ] When the SOF audio driver fails to initialize (e.g. firmware boot timeout), its devres unwind frees the snd_sof_dev object that the mailbox client (mtk-adsp-ipc) reaches via chan->cl->rx_callback. The mtk-adsp-mailbox shutdown clears the mailbox command registers but leaves the IRQ line unmasked, so a late interrupt can still queue a threaded handler after mbox_free_channel() had cleared chan->cl, and mbox_chan_received_data() would then trigger UAF: BUG: KASAN: slab-use-after-free in sof_ipc3_validate_fw_version sof_ipc3_validate_fw_version sof_ipc3_do_rx_work sof_ipc3_rx_msg mt8196_dsp_handle_request mtk_adsp_ipc_recv mbox_chan_received_data mtk_adsp_mbox_isr irq_thread_fn Freed by task ...: kfree devres_release_all really_probe ... (sof-audio-of-mt8196 probe failure) The crash was observed roughly three seconds after the failed probe. disable_irq() in shutdown and enable_irq() in startup. disable_irq() also waits for any in-flight interrupts, so by the time mbox_free_channel() proceeds to clear chan->cl no rx_callback can run. In addition, request the IRQ with IRQF_NO_AUTOEN so it stays masked between probe and the first client bind — otherwise an early interrupt can crash on chan->cl == NULL in mbox_chan_received_data(). Fixes: af2dfa96c52d ("mailbox: mediatek: add support for adsp mailbox controller") Signed-off-by: Sergey Senozhatsky Reviewed-by: Tzung-Bi Shih Signed-off-by: Jassi Brar Signed-off-by: Sasha Levin --- drivers/mailbox/mtk-adsp-mailbox.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/mailbox/mtk-adsp-mailbox.c b/drivers/mailbox/mtk-adsp-mailbox.c index 91487aa4d7da0..8bcecddee0eb5 100644 --- a/drivers/mailbox/mtk-adsp-mailbox.c +++ b/drivers/mailbox/mtk-adsp-mailbox.c @@ -19,6 +19,7 @@ struct mtk_adsp_mbox_priv { struct mbox_controller mbox; void __iomem *va_mboxreg; const struct mtk_adsp_mbox_cfg *cfg; + int irq; }; struct mtk_adsp_mbox_cfg { @@ -67,6 +68,8 @@ static int mtk_adsp_mbox_startup(struct mbox_chan *chan) writel(0xFFFFFFFF, priv->va_mboxreg + priv->cfg->clr_in); writel(0xFFFFFFFF, priv->va_mboxreg + priv->cfg->clr_out); + enable_irq(priv->irq); + return 0; } @@ -74,6 +77,8 @@ static void mtk_adsp_mbox_shutdown(struct mbox_chan *chan) { struct mtk_adsp_mbox_priv *priv = get_mtk_adsp_mbox_priv(chan->mbox); + disable_irq(priv->irq); + /* Clear ADSP mbox command */ writel(0xFFFFFFFF, priv->va_mboxreg + priv->cfg->clr_in); writel(0xFFFFFFFF, priv->va_mboxreg + priv->cfg->clr_out); @@ -139,8 +144,10 @@ static int mtk_adsp_mbox_probe(struct platform_device *pdev) if (irq < 0) return irq; + priv->irq = irq; ret = devm_request_threaded_irq(dev, irq, mtk_adsp_mbox_irq, - mtk_adsp_mbox_isr, IRQF_TRIGGER_NONE, + mtk_adsp_mbox_isr, + IRQF_TRIGGER_NONE | IRQF_NO_AUTOEN, dev_name(dev), mbox->chans); if (ret < 0) return ret; From ac8a86dcaf59cd30cc37efb64149b4475fb1f9d9 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 6 May 2026 09:09:47 +0200 Subject: [PATCH 1553/2758] mailbox: don't free the channel if the startup callback failed [ Upstream commit 4f176444dcc977d1888fd9220c357a4d32338ee0 ] If the optional startup() callbacks fails, we need to clear some states. Currently, this is done by freeing the channel. This does, however, more than needed which creates problems. Namely, it is calling the shutdown() callback. This is totally not intuitive. No user expects that shutdown() is called when startup() fails, similar to remove() not being called when probe() fails. Currently, quite some mailbox users register the IRQ in startup() and free them in shutdown(). These drivers will get a WARN about freeing an already free IRQ. Other subtle issues could arise from this unexpected behaviour. To solve this problem, introduce a helper which does the minimal cleanup and use it in both, in free_channel() and after startup() failed. Link: https://sashiko.dev/#/patchset/20260402112709.13002-1-wsa%2Brenesas%40sang-engineering.com # second issue Fixes: 2b6d83e2b8b7 ("mailbox: Introduce framework for mailbox") Signed-off-by: Wolfram Sang Signed-off-by: Jassi Brar Signed-off-by: Sasha Levin --- drivers/mailbox/mailbox.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c index bbc9fd75a95f7..006ea5a5c3207 100644 --- a/drivers/mailbox/mailbox.c +++ b/drivers/mailbox/mailbox.c @@ -327,6 +327,19 @@ int mbox_flush(struct mbox_chan *chan, unsigned long timeout) } EXPORT_SYMBOL_GPL(mbox_flush); +static void mbox_clean_and_put_channel(struct mbox_chan *chan) +{ + /* The queued TX requests are simply aborted, no callbacks are made */ + scoped_guard(spinlock_irqsave, &chan->lock) { + chan->cl = NULL; + chan->active_req = MBOX_NO_MSG; + if (chan->txdone_method == MBOX_TXDONE_BY_ACK) + chan->txdone_method = MBOX_TXDONE_BY_POLL; + } + + module_put(chan->mbox->dev->driver->owner); +} + static int __mbox_bind_client(struct mbox_chan *chan, struct mbox_client *cl) { struct device *dev = cl->dev; @@ -350,10 +363,9 @@ static int __mbox_bind_client(struct mbox_chan *chan, struct mbox_client *cl) if (chan->mbox->ops->startup) { ret = chan->mbox->ops->startup(chan); - if (ret) { dev_err(dev, "Unable to startup the chan (%d)\n", ret); - mbox_free_channel(chan); + mbox_clean_and_put_channel(chan); return ret; } } @@ -495,15 +507,7 @@ void mbox_free_channel(struct mbox_chan *chan) if (chan->mbox->ops->shutdown) chan->mbox->ops->shutdown(chan); - /* The queued TX requests are simply aborted, no callbacks are made */ - scoped_guard(spinlock_irqsave, &chan->lock) { - chan->cl = NULL; - chan->active_req = MBOX_NO_MSG; - if (chan->txdone_method == MBOX_TXDONE_BY_ACK) - chan->txdone_method = MBOX_TXDONE_BY_POLL; - } - - module_put(chan->mbox->dev->driver->owner); + mbox_clean_and_put_channel(chan); } EXPORT_SYMBOL_GPL(mbox_free_channel); From f344f6ae8517627efd891a8c0d33d4b6ce193b8d Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 18 May 2026 12:07:00 +0200 Subject: [PATCH 1554/2758] PCI/pwrctrl: Lock device when calling device_is_bound() [ Upstream commit 548f3d287d92bcbc908b02db57fb889f8a8276a0 ] The kerneldoc for device_is_bound() states that it must be called with the device lock taken. Synchronize the two calls in pwrctrl core. Fixes: b35cf3b6aa1e ("PCI/pwrctrl: Add APIs to power on/off pwrctrl devices") Signed-off-by: Bartosz Golaszewski Signed-off-by: Bjorn Helgaas Reviewed-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260518100700.47581-1-bartosz.golaszewski@oss.qualcomm.com Signed-off-by: Sasha Levin --- drivers/pci/pwrctrl/core.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/pci/pwrctrl/core.c b/drivers/pci/pwrctrl/core.c index 97cff5b8ca885..cd08d590483b8 100644 --- a/drivers/pci/pwrctrl/core.c +++ b/drivers/pci/pwrctrl/core.c @@ -161,10 +161,12 @@ static void pci_pwrctrl_power_off_device(struct device_node *np) if (!pdev) return; - if (device_is_bound(&pdev->dev)) { - ret = __pci_pwrctrl_power_off_device(&pdev->dev); - if (ret) - dev_err(&pdev->dev, "Failed to power off device: %d", ret); + scoped_guard(device, &pdev->dev) { + if (device_is_bound(&pdev->dev)) { + ret = __pci_pwrctrl_power_off_device(&pdev->dev); + if (ret) + dev_err(&pdev->dev, "Failed to power off device: %d", ret); + } } platform_device_put(pdev); @@ -205,7 +207,7 @@ static int __pci_pwrctrl_power_on_device(struct device *dev) static int pci_pwrctrl_power_on_device(struct device_node *np) { struct platform_device *pdev; - int ret; + int ret = 0; for_each_available_child_of_node_scoped(np, child) { ret = pci_pwrctrl_power_on_device(child); @@ -217,12 +219,14 @@ static int pci_pwrctrl_power_on_device(struct device_node *np) if (!pdev) return 0; - if (device_is_bound(&pdev->dev)) { - ret = __pci_pwrctrl_power_on_device(&pdev->dev); - } else { - /* FIXME: Use blocking wait instead of probe deferral */ - dev_dbg(&pdev->dev, "driver is not bound\n"); - ret = -EPROBE_DEFER; + scoped_guard(device, &pdev->dev) { + if (device_is_bound(&pdev->dev)) { + ret = __pci_pwrctrl_power_on_device(&pdev->dev); + } else { + /* FIXME: Use blocking wait instead of probe deferral */ + dev_dbg(&pdev->dev, "driver is not bound\n"); + ret = -EPROBE_DEFER; + } } platform_device_put(pdev); From 8ca9adc805884d3bb5038082462577f86c2c4a10 Mon Sep 17 00:00:00 2001 From: Jie Gan Date: Mon, 11 May 2026 12:19:18 +0800 Subject: [PATCH 1555/2758] coresight: platform: defer connection counter increment until alloc succeeds [ Upstream commit 1563ae33dc4f5ebac96b93af2ef72e72aaaa31ae ] coresight_add_out_conn() increments nr_outconns before calling devm_krealloc_array() and again before devm_kmalloc(). If either allocation fails, the counter is already bumped while the corresponding array entry is NULL or uninitialized garbage. coresight_add_in_conn() has the same problem with nr_inconns and devm_krealloc_array(). In both cases the probe returns -ENOMEM, which causes coresight_get_platform_data() to call coresight_release_platform_data() for cleanup. That function iterates up to nr_outconns (or nr_inconns) entries and dereferences each pointer unconditionally, hitting the NULL or garbage entry and panicking instead of failing gracefully. Fix by moving the counter increments to after all allocations succeed, so the struct is always consistent on any error path. Fixes: 3d4ff657e454 ("coresight: Dynamically add connections") Fixes: e3f4e68797a9 ("coresight: Store in-connections as well as out-connections") Signed-off-by: Jie Gan Reviewed-by: James Clark Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260511-fix-ref-count-issue-v1-1-99d647810d3c@oss.qualcomm.com Signed-off-by: Sasha Levin --- drivers/hwtracing/coresight/coresight-platform.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-platform.c b/drivers/hwtracing/coresight/coresight-platform.c index e337b6e2bf327..93c2d075cad66 100644 --- a/drivers/hwtracing/coresight/coresight-platform.c +++ b/drivers/hwtracing/coresight/coresight-platform.c @@ -45,9 +45,8 @@ coresight_add_out_conn(struct device *dev, } } - pdata->nr_outconns++; pdata->out_conns = - devm_krealloc_array(dev, pdata->out_conns, pdata->nr_outconns, + devm_krealloc_array(dev, pdata->out_conns, pdata->nr_outconns + 1, sizeof(*pdata->out_conns), GFP_KERNEL); if (!pdata->out_conns) return ERR_PTR(-ENOMEM); @@ -63,7 +62,8 @@ coresight_add_out_conn(struct device *dev, * used right away. */ *conn = *new_conn; - pdata->out_conns[pdata->nr_outconns - 1] = conn; + pdata->out_conns[pdata->nr_outconns] = conn; + pdata->nr_outconns++; return conn; } EXPORT_SYMBOL_GPL(coresight_add_out_conn); @@ -86,13 +86,13 @@ int coresight_add_in_conn(struct coresight_connection *out_conn) return 0; } - pdata->nr_inconns++; pdata->in_conns = - devm_krealloc_array(dev, pdata->in_conns, pdata->nr_inconns, + devm_krealloc_array(dev, pdata->in_conns, pdata->nr_inconns + 1, sizeof(*pdata->in_conns), GFP_KERNEL); if (!pdata->in_conns) return -ENOMEM; - pdata->in_conns[pdata->nr_inconns - 1] = out_conn; + pdata->in_conns[pdata->nr_inconns] = out_conn; + pdata->nr_inconns++; return 0; } EXPORT_SYMBOL_GPL(coresight_add_in_conn); From e31173a194662047fb929faaa55542e2b1387ebf Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 12 May 2026 13:17:55 +0300 Subject: [PATCH 1556/2758] PCI: dwc: Fix signedness bug in fault injection test code [ Upstream commit 94ac934d2c054fba4a22d8dc84749094c5fa0ec0 ] The kstrtou32() function returns negative error code or zero on success. However, in this case "val" is a u32 and the function returns signed long, so negative error codes from kstrtou32() are returned as high positive values. Store the error code in an int instead. Fixes: d20ee8e2dbd6 ("PCI: dwc: Add debugfs based Error Injection support for DWC") Signed-off-by: Dan Carpenter Signed-off-by: Manivannan Sadhasivam Reviewed-by: Hans Zhang <18255117159@163.com> Link: https://patch.msgid.link/agL-Uwfn26SI4Gb0@stanley.mountain Signed-off-by: Sasha Levin --- drivers/pci/controller/dwc/pcie-designware-debugfs.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-debugfs.c b/drivers/pci/controller/dwc/pcie-designware-debugfs.c index d0884253be97e..4bfc1748f2d73 100644 --- a/drivers/pci/controller/dwc/pcie-designware-debugfs.c +++ b/drivers/pci/controller/dwc/pcie-designware-debugfs.c @@ -306,6 +306,7 @@ static ssize_t err_inj_write(struct file *file, const char __user *buf, u32 val, counter, vc_num, err_group, type_mask; int val_diff = 0; char *kern_buf; + int ret; err_group = err_inj_list[pdata->idx].err_inj_group; type_mask = err_inj_type_mask[err_group]; @@ -327,10 +328,10 @@ static ssize_t err_inj_write(struct file *file, const char __user *buf, return -EINVAL; } } else { - val = kstrtou32(kern_buf, 0, &counter); - if (val) { + ret = kstrtou32(kern_buf, 0, &counter); + if (ret) { kfree(kern_buf); - return val; + return ret; } } From ce5633204a4b1652a9191537e55b951425b32300 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 12 May 2026 18:33:45 +0800 Subject: [PATCH 1557/2758] PCI: mediatek-gen3: Fix incorrectly skipped pwrctrl error message [ Upstream commit 8ba433753d9b131c2e43b1ff7ba8c5730cef8231 ] When pwrctrl integration was added, the error message for pci_pwrctrl_create_devices() failure was incorrectly added after the goto statement, causing it to be skipped. Move the goto statement after the dev_err_probe() call so that the error message actually gets printed (or saved if probe is deferred). Fixes: 1a152e21940a ("PCI: mediatek-gen3: Integrate new pwrctrl API") Reported-by: Dan Carpenter Closes: https://lore.kernel.org/all/adjNaKB5KGpl6qIp@stanley.mountain/ Signed-off-by: Chen-Yu Tsai Signed-off-by: Manivannan Sadhasivam Reviewed-by: Hans Zhang <18255117159@163.com> Link: https://patch.msgid.link/20260512103347.1751080-1-wenst@chromium.org Signed-off-by: Sasha Levin --- drivers/pci/controller/pcie-mediatek-gen3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c index e6acca7e79a0f..88e3fdcb74c06 100644 --- a/drivers/pci/controller/pcie-mediatek-gen3.c +++ b/drivers/pci/controller/pcie-mediatek-gen3.c @@ -1222,8 +1222,8 @@ static int mtk_pcie_probe(struct platform_device *pdev) err = pci_pwrctrl_create_devices(pcie->dev); if (err) { - goto err_tear_down_irq; dev_err_probe(dev, err, "failed to create pwrctrl devices\n"); + goto err_tear_down_irq; } err = mtk_pcie_setup(pcie); From b670ac2731ddfc802a89ce7151d1478715535250 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 11 May 2026 22:03:34 +0200 Subject: [PATCH 1558/2758] platform/x86: classmate-laptop: Address memory leaks on driver removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 4baf44b4051940ba1abc68ef5136d25cb1806521 ] Switch over cmpc_accel_add_v4() and cmpc_accel_add() to using devm_kzalloc() for allocating the accel object which will cause it to be freed automatically on device removal, so it won't be leaked any more. This also simplifies the rollback paths in these functions somewhat. Fixes: 529aa8cb0a59 ("classmate-laptop: add support for Classmate PC ACPI devices") Signed-off-by: Rafael J. Wysocki Acked-by: Thadeu Lima de Souza Cascardo Link: https://patch.msgid.link/10846403.nUPlyArG6x@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Sasha Levin --- drivers/platform/x86/classmate-laptop.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index e6eed3d65580a..a5fe34211afb9 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -400,7 +400,7 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) struct input_dev *inputdev; struct cmpc_accel *accel; - accel = kmalloc_obj(*accel); + accel = devm_kzalloc(&acpi->dev, sizeof(*accel), GFP_KERNEL); if (!accel) return -ENOMEM; @@ -411,7 +411,7 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); if (error) - goto failed_sensitivity; + return error; accel->g_select = CMPC_ACCEL_G_SELECT_DEFAULT; cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select); @@ -434,8 +434,6 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4); failed_g_select: device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); -failed_sensitivity: - kfree(accel); return error; } @@ -650,7 +648,7 @@ static int cmpc_accel_add(struct acpi_device *acpi) struct input_dev *inputdev; struct cmpc_accel *accel; - accel = kmalloc_obj(*accel); + accel = devm_kzalloc(&acpi->dev, sizeof(*accel), GFP_KERNEL); if (!accel) return -ENOMEM; @@ -659,7 +657,7 @@ static int cmpc_accel_add(struct acpi_device *acpi) error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr); if (error) - goto failed_file; + return error; error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel", cmpc_accel_idev_init); @@ -673,8 +671,6 @@ static int cmpc_accel_add(struct acpi_device *acpi) failed_input: device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); -failed_file: - kfree(accel); return error; } From 8b54808fcced126326a1d36d2398bfa9e5c9b205 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Thu, 30 Apr 2026 19:30:28 +0100 Subject: [PATCH 1559/2758] clk: microchip: mpfs-ccc: fix peripheral driver registration failures after oob fix [ Upstream commit c8a3be5bc2b2f2d53c56a8b9cab731e917b95c07 ] Commit 2f7ae8ab6aa73 ("clk: microchip: mpfs-ccc: fix out of bounds access during output registration") fixed the out of bounds access, but it did so by packing sparse indices into a linear space. When peripheral drivers request clocks, they obviously don't care for this compression and use the sparse indices, and therefore try to request the wrong clocks or clocks that don't exist. The most straightforward fix here seems to stop being clever with the packing and just overallocate the array. Fixes: 2f7ae8ab6aa73 ("clk: microchip: mpfs-ccc: fix out of bounds access during output registration") Fixes: d39fb172760e ("clk: microchip: add PolarFire SoC fabric clock support") Reviewed-by: Brian Masney Signed-off-by: Conor Dooley Signed-off-by: Sasha Levin --- drivers/clk/microchip/clk-mpfs-ccc.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/drivers/clk/microchip/clk-mpfs-ccc.c b/drivers/clk/microchip/clk-mpfs-ccc.c index 0a76a1aaa50f7..40c17593e5941 100644 --- a/drivers/clk/microchip/clk-mpfs-ccc.c +++ b/drivers/clk/microchip/clk-mpfs-ccc.c @@ -32,6 +32,7 @@ #define MPFS_CCC_FIXED_DIV 4 #define MPFS_CCC_OUTPUTS_PER_PLL 4 #define MPFS_CCC_REFS_PER_PLL 2 +#define MPFS_CCC_NUM_CLKS 16 struct mpfs_ccc_data { void __iomem **pll_base; @@ -178,7 +179,7 @@ static int mpfs_ccc_register_outputs(struct device *dev, struct mpfs_ccc_out_hw_ return dev_err_probe(dev, ret, "failed to register clock id: %d\n", out_hw->id); - data->hw_data.hws[out_hw->id - 2] = &out_hw->divider.hw; + data->hw_data.hws[out_hw->id] = &out_hw->divider.hw; } return 0; @@ -231,17 +232,9 @@ static int mpfs_ccc_probe(struct platform_device *pdev) { struct mpfs_ccc_data *clk_data; void __iomem *pll_base[ARRAY_SIZE(mpfs_ccc_pll_clks)]; - unsigned int num_clks; int ret; - /* - * If DLLs get added here, mpfs_ccc_register_outputs() currently packs - * sparse clock IDs in the hws array - */ - num_clks = ARRAY_SIZE(mpfs_ccc_pll_clks) + ARRAY_SIZE(mpfs_ccc_pll0out_clks) + - ARRAY_SIZE(mpfs_ccc_pll1out_clks); - - clk_data = devm_kzalloc(&pdev->dev, struct_size(clk_data, hw_data.hws, num_clks), + clk_data = devm_kzalloc(&pdev->dev, struct_size(clk_data, hw_data.hws, MPFS_CCC_NUM_CLKS), GFP_KERNEL); if (!clk_data) return -ENOMEM; @@ -255,7 +248,7 @@ static int mpfs_ccc_probe(struct platform_device *pdev) return PTR_ERR(pll_base[1]); clk_data->pll_base = pll_base; - clk_data->hw_data.num = num_clks; + clk_data->hw_data.num = MPFS_CCC_NUM_CLKS; clk_data->dev = &pdev->dev; ret = mpfs_ccc_register_plls(clk_data->dev, mpfs_ccc_pll_clks, From 7e374ac7702bee491e8b1303b98effcf8b3fe2c6 Mon Sep 17 00:00:00 2001 From: Michael Petlan Date: Wed, 20 May 2026 00:38:55 +0200 Subject: [PATCH 1560/2758] perf build-id: Fix off-by-one bug when printing kernel/module build-id [ Upstream commit 017bca78e4d72b1ff027d368c20a1b2c654edaf7 ] When changing sprintf functions to snprintf, one byte got lost. Since snprintf ones do not handle the '\0' terminating character, the number of printed characters is 40, while sizeof(sbuild_id) is 41, including the terminating '\0' character. This makes the later check fail so that nothing is printed. Fix that. Before: [Michael@Carbon ~]$ perf buildid-list -k [Michael@Carbon ~]$ After: [Michael@Carbon ~]$ perf buildid-list -k a527806324d543c4bc3ff2f9c9519d494fed5f68 [Michael@Carbon ~]$ Fixes: fccaaf6fbbc59910 ("perf build-id: Change sprintf functions to snprintf") Signed-off-by: Michael Petlan Tested-by: Ian Rogers Cc: Ian Rogers Cc: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-buildid-list.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index a91bbb34ac946..e0881b0ac38ff 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c @@ -61,7 +61,7 @@ static int sysfs__fprintf_build_id(FILE *fp) int ret; ret = sysfs__snprintf_build_id("/", sbuild_id, sizeof(sbuild_id)); - if (ret != sizeof(sbuild_id)) + if (ret + 1 != sizeof(sbuild_id)) return ret < 0 ? ret : -EINVAL; return fprintf(fp, "%s\n", sbuild_id); @@ -73,7 +73,7 @@ static int filename__fprintf_build_id(const char *name, FILE *fp) int ret; ret = filename__snprintf_build_id(name, sbuild_id, sizeof(sbuild_id)); - if (ret != sizeof(sbuild_id)) + if (ret + 1 != sizeof(sbuild_id)) return ret < 0 ? ret : -EINVAL; return fprintf(fp, "%s\n", sbuild_id); From 034182b6310860858927b6daecc2f3b218c57c6e Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 18 May 2026 15:43:24 -0700 Subject: [PATCH 1561/2758] perf event: Fix size of synthesized sample with branch stacks [ Upstream commit 059e9100d82aae2254f1b06835a55755936b1417 ] Synthesizing branch stacks for Intel-PT highlighted an issue where PERF_SAMPLE_BRANCH_HW_INDEX was assumed to always be set in the perf_event_attr branch_sample_type. This caused an incorrect size calculation. Fix the writing of the nr and hw_idx values during sample event synthesis by passing the branch_sample_type into the sample size and synthesis functions. Also update hardware tracers (Intel PT, ARM SPE, CS-ETM) to retrieve and pass their branch_sample_type dynamically to prevent payload misalignment. Fixes: d3f85437ad6a5511 ("perf evsel: Support PERF_SAMPLE_BRANCH_HW_INDEX") Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Dapeng Mi Cc: Ingo Molnar Cc: James Clark Cc: Kan Liang Cc: Leo Yan Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Thomas Falcon Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/bench/inject-buildid.c | 9 ++++++--- tools/perf/builtin-inject.c | 12 +++++++++--- tools/perf/tests/dlfilter-test.c | 8 ++++++-- tools/perf/tests/sample-parsing.c | 5 +++-- tools/perf/util/arm-spe.c | 28 ++++++++++++++++++++++++---- tools/perf/util/cs-etm.c | 28 +++++++++++++++++++++++----- tools/perf/util/intel-bts.c | 3 ++- tools/perf/util/intel-pt.c | 27 +++++++++++++++++++++++---- tools/perf/util/synthetic-events.c | 25 ++++++++++++++++++------- tools/perf/util/synthetic-events.h | 6 ++++-- 10 files changed, 118 insertions(+), 33 deletions(-) diff --git a/tools/perf/bench/inject-buildid.c b/tools/perf/bench/inject-buildid.c index aad572a78d7fc..bfd2c5ec9488e 100644 --- a/tools/perf/bench/inject-buildid.c +++ b/tools/perf/bench/inject-buildid.c @@ -228,9 +228,12 @@ static ssize_t synthesize_sample(struct bench_data *data, struct bench_dso *dso, event.header.type = PERF_RECORD_SAMPLE; event.header.misc = PERF_RECORD_MISC_USER; - event.header.size = perf_event__sample_event_size(&sample, bench_sample_type, 0); - - perf_event__synthesize_sample(&event, bench_sample_type, 0, &sample); + event.header.size = perf_event__sample_event_size(&sample, bench_sample_type, + /*read_format=*/0, + /*branch_sample_type=*/0); + perf_event__synthesize_sample(&event, bench_sample_type, + /*read_format=*/0, + /*branch_sample_type=*/0, &sample); return writen(data->input_pipe[1], &event, event.header.size); } diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index a2493f1097df9..2f20e782c7f27 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -465,8 +465,13 @@ static int perf_event__convert_sample_callchain(const struct perf_tool *tool, /* remove sample_type {STACK,REGS}_USER for synthesize */ sample_type &= ~(PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER); - perf_event__synthesize_sample(event_copy, sample_type, - evsel->core.attr.read_format, sample); + ret = perf_event__synthesize_sample(event_copy, sample_type, + evsel->core.attr.read_format, + evsel->core.attr.branch_sample_type, sample); + if (ret) { + pr_err("Failed to synthesize sample\n"); + return ret; + } return perf_event__repipe_synth(tool, event_copy); } @@ -1102,7 +1107,8 @@ static int perf_inject__sched_stat(const struct perf_tool *tool, sample_sw.period = sample->period; sample_sw.time = sample->time; perf_event__synthesize_sample(event_sw, evsel->core.attr.sample_type, - evsel->core.attr.read_format, &sample_sw); + evsel->core.attr.read_format, + evsel->core.attr.branch_sample_type, &sample_sw); build_id__mark_dso_hit(tool, event_sw, &sample_sw, evsel, machine); ret = perf_event__repipe(tool, event_sw, &sample_sw, machine); perf_sample__exit(&sample_sw); diff --git a/tools/perf/tests/dlfilter-test.c b/tools/perf/tests/dlfilter-test.c index e63790c61d53a..204663571943c 100644 --- a/tools/perf/tests/dlfilter-test.c +++ b/tools/perf/tests/dlfilter-test.c @@ -188,8 +188,12 @@ static int write_sample(struct test_data *td, u64 sample_type, u64 id, pid_t pid event->header.type = PERF_RECORD_SAMPLE; event->header.misc = PERF_RECORD_MISC_USER; - event->header.size = perf_event__sample_event_size(&sample, sample_type, 0); - err = perf_event__synthesize_sample(event, sample_type, 0, &sample); + event->header.size = perf_event__sample_event_size(&sample, sample_type, + /*read_format=*/0, + /*branch_sample_type=*/0); + err = perf_event__synthesize_sample(event, sample_type, + /*read_format=*/0, + /*branch_sample_type=*/0, &sample); if (err) return test_result("perf_event__synthesize_sample() failed", TEST_FAIL); diff --git a/tools/perf/tests/sample-parsing.c b/tools/perf/tests/sample-parsing.c index a7327c942ca20..55f0b73ca20e0 100644 --- a/tools/perf/tests/sample-parsing.c +++ b/tools/perf/tests/sample-parsing.c @@ -310,7 +310,8 @@ static int do_test(u64 sample_type, u64 sample_regs, u64 read_format) sample.read.one.lost = 1; } - sz = perf_event__sample_event_size(&sample, sample_type, read_format); + sz = perf_event__sample_event_size(&sample, sample_type, read_format, + evsel.core.attr.branch_sample_type); bufsz = sz + 4096; /* Add a bit for overrun checking */ event = malloc(bufsz); if (!event) { @@ -324,7 +325,7 @@ static int do_test(u64 sample_type, u64 sample_regs, u64 read_format) event->header.size = sz; err = perf_event__synthesize_sample(event, sample_type, read_format, - &sample); + evsel.core.attr.branch_sample_type, &sample); if (err) { pr_debug("%s failed for sample_type %#"PRIx64", error %d\n", "perf_event__synthesize_sample", sample_type, err); diff --git a/tools/perf/util/arm-spe.c b/tools/perf/util/arm-spe.c index e5835042acdf7..3e54ed41653bd 100644 --- a/tools/perf/util/arm-spe.c +++ b/tools/perf/util/arm-spe.c @@ -482,10 +482,30 @@ static void arm_spe__prep_branch_stack(struct arm_spe_queue *speq) bstack->hw_idx = -1ULL; } -static int arm_spe__inject_event(union perf_event *event, struct perf_sample *sample, u64 type) +static int arm_spe__inject_event(struct arm_spe *spe, union perf_event *event, + struct perf_sample *sample, u64 type) { - event->header.size = perf_event__sample_event_size(sample, type, 0); - return perf_event__synthesize_sample(event, type, 0, sample); + struct evsel *evsel = sample->evsel; + u64 branch_sample_type = 0; + size_t sz; + + if (!evsel && spe->session && spe->session->evlist) + evsel = evlist__id2evsel(spe->session->evlist, sample->id); + + if (evsel) + branch_sample_type = evsel->core.attr.branch_sample_type; + + event->header.type = PERF_RECORD_SAMPLE; + sz = perf_event__sample_event_size(sample, type, /*read_format=*/0, + branch_sample_type); + if (sz >= PERF_SAMPLE_MAX_SIZE) { + pr_err("Sample size %zu exceeds max size %d\n", sz, PERF_SAMPLE_MAX_SIZE); + return -EFAULT; + } + event->header.size = sz; + + return perf_event__synthesize_sample(event, type, /*read_format=*/0, + branch_sample_type, sample); } static inline int @@ -497,7 +517,7 @@ arm_spe_deliver_synth_event(struct arm_spe *spe, int ret; if (spe->synth_opts.inject) { - ret = arm_spe__inject_event(event, sample, spe->sample_type); + ret = arm_spe__inject_event(spe, event, sample, spe->sample_type); if (ret) return ret; } diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index 8a639d2e51a4c..6ec48de294410 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -1422,11 +1422,29 @@ static void cs_etm__update_last_branch_rb(struct cs_etm_queue *etmq, bs->nr += 1; } -static int cs_etm__inject_event(union perf_event *event, +static int cs_etm__inject_event(struct cs_etm_auxtrace *etm, union perf_event *event, struct perf_sample *sample, u64 type) { - event->header.size = perf_event__sample_event_size(sample, type, 0); - return perf_event__synthesize_sample(event, type, 0, sample); + struct evsel *evsel = sample->evsel; + u64 branch_sample_type = 0; + size_t sz; + + if (!evsel && etm->session && etm->session->evlist) + evsel = evlist__id2evsel(etm->session->evlist, sample->id); + + if (evsel) + branch_sample_type = evsel->core.attr.branch_sample_type; + + sz = perf_event__sample_event_size(sample, type, /*read_format=*/0, + branch_sample_type); + if (sz >= PERF_SAMPLE_MAX_SIZE) { + pr_err("Sample size %zu exceeds max size %d\n", sz, PERF_SAMPLE_MAX_SIZE); + return -EFAULT; + } + event->header.size = sz; + + return perf_event__synthesize_sample(event, type, /*read_format=*/0, + branch_sample_type, sample); } @@ -1592,7 +1610,7 @@ static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq, sample.branch_stack = tidq->last_branch; if (etm->synth_opts.inject) { - ret = cs_etm__inject_event(event, &sample, + ret = cs_etm__inject_event(etm, event, &sample, etm->instructions_sample_type); if (ret) return ret; @@ -1667,7 +1685,7 @@ static int cs_etm__synth_branch_sample(struct cs_etm_queue *etmq, } if (etm->synth_opts.inject) { - ret = cs_etm__inject_event(event, &sample, + ret = cs_etm__inject_event(etm, event, &sample, etm->branches_sample_type); if (ret) return ret; diff --git a/tools/perf/util/intel-bts.c b/tools/perf/util/intel-bts.c index 382255393fb3b..0b18ebd13f7c8 100644 --- a/tools/perf/util/intel-bts.c +++ b/tools/perf/util/intel-bts.c @@ -303,7 +303,8 @@ static int intel_bts_synth_branch_sample(struct intel_bts_queue *btsq, event.sample.header.size = bts->branches_event_size; ret = perf_event__synthesize_sample(&event, bts->branches_sample_type, - 0, &sample); + /*read_format=*/0, /*branch_sample_type=*/0, + &sample); if (ret) return ret; } diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index fc9eec8b54b82..dd2637678b405 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c @@ -1728,11 +1728,30 @@ static void intel_pt_prep_b_sample(struct intel_pt *pt, event->sample.header.misc = sample->cpumode; } -static int intel_pt_inject_event(union perf_event *event, +static int intel_pt_inject_event(struct intel_pt *pt, union perf_event *event, struct perf_sample *sample, u64 type) { - event->header.size = perf_event__sample_event_size(sample, type, 0); - return perf_event__synthesize_sample(event, type, 0, sample); + struct evsel *evsel = sample->evsel; + u64 branch_sample_type = 0; + size_t sz; + + if (!evsel && pt->session && pt->session->evlist) + evsel = evlist__id2evsel(pt->session->evlist, sample->id); + + if (evsel) + branch_sample_type = evsel->core.attr.branch_sample_type; + + event->header.type = PERF_RECORD_SAMPLE; + sz = perf_event__sample_event_size(sample, type, /*read_format=*/0, + branch_sample_type); + if (sz >= PERF_SAMPLE_MAX_SIZE) { + pr_err("Sample size %zu exceeds max size %d\n", sz, PERF_SAMPLE_MAX_SIZE); + return -EFAULT; + } + event->header.size = sz; + + return perf_event__synthesize_sample(event, type, /*read_format=*/0, + branch_sample_type, sample); } static inline int intel_pt_opt_inject(struct intel_pt *pt, @@ -1742,7 +1761,7 @@ static inline int intel_pt_opt_inject(struct intel_pt *pt, if (!pt->synth_opts.inject) return 0; - return intel_pt_inject_event(event, sample, type); + return intel_pt_inject_event(pt, event, sample, type); } static int intel_pt_deliver_synth_event(struct intel_pt *pt, diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c index 85bee747f4cd2..2461f25a4d7dc 100644 --- a/tools/perf/util/synthetic-events.c +++ b/tools/perf/util/synthetic-events.c @@ -1455,7 +1455,8 @@ int perf_event__synthesize_stat_round(const struct perf_tool *tool, return process(tool, (union perf_event *) &event, NULL, machine); } -size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type, u64 read_format) +size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type, u64 read_format, + u64 branch_sample_type) { size_t sz, result = sizeof(struct perf_record_sample); @@ -1515,8 +1516,10 @@ size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type, if (type & PERF_SAMPLE_BRANCH_STACK) { sz = sample->branch_stack->nr * sizeof(struct branch_entry); - /* nr, hw_idx */ - sz += 2 * sizeof(u64); + /* nr */ + sz += sizeof(u64); + if (branch_sample_type & PERF_SAMPLE_BRANCH_HW_INDEX) + sz += sizeof(u64); result += sz; } @@ -1605,7 +1608,7 @@ static __u64 *copy_read_group_values(__u64 *array, __u64 read_format, } int perf_event__synthesize_sample(union perf_event *event, u64 type, u64 read_format, - const struct perf_sample *sample) + u64 branch_sample_type, const struct perf_sample *sample) { __u64 *array; size_t sz; @@ -1719,9 +1722,17 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type, u64 read_fo if (type & PERF_SAMPLE_BRANCH_STACK) { sz = sample->branch_stack->nr * sizeof(struct branch_entry); - /* nr, hw_idx */ - sz += 2 * sizeof(u64); - memcpy(array, sample->branch_stack, sz); + + *array++ = sample->branch_stack->nr; + + if (branch_sample_type & PERF_SAMPLE_BRANCH_HW_INDEX) { + if (sample->no_hw_idx) + *array++ = 0; + else + *array++ = sample->branch_stack->hw_idx; + } + + memcpy(array, perf_sample__branch_entries((struct perf_sample *)sample), sz); array = (void *)array + sz; } diff --git a/tools/perf/util/synthetic-events.h b/tools/perf/util/synthetic-events.h index b0edad0c31001..8c7f49f9ccf54 100644 --- a/tools/perf/util/synthetic-events.h +++ b/tools/perf/util/synthetic-events.h @@ -81,7 +81,8 @@ int perf_event__synthesize_mmap_events(const struct perf_tool *tool, union perf_ int perf_event__synthesize_modules(const struct perf_tool *tool, perf_event__handler_t process, struct machine *machine); int perf_event__synthesize_namespaces(const struct perf_tool *tool, union perf_event *event, pid_t pid, pid_t tgid, perf_event__handler_t process, struct machine *machine); int perf_event__synthesize_cgroups(const struct perf_tool *tool, perf_event__handler_t process, struct machine *machine); -int perf_event__synthesize_sample(union perf_event *event, u64 type, u64 read_format, const struct perf_sample *sample); +int perf_event__synthesize_sample(union perf_event *event, u64 type, u64 read_format, + u64 branch_sample_type, const struct perf_sample *sample); int perf_event__synthesize_stat_config(const struct perf_tool *tool, struct perf_stat_config *config, perf_event__handler_t process, struct machine *machine); int perf_event__synthesize_stat_events(struct perf_stat_config *config, const struct perf_tool *tool, struct evlist *evlist, perf_event__handler_t process, bool attrs); int perf_event__synthesize_stat_round(const struct perf_tool *tool, u64 time, u64 type, perf_event__handler_t process, struct machine *machine); @@ -97,7 +98,8 @@ void perf_event__synthesize_final_bpf_metadata(struct perf_session *session, int perf_tool__process_synth_event(const struct perf_tool *tool, union perf_event *event, struct machine *machine, perf_event__handler_t process); -size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type, u64 read_format); +size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type, + u64 read_format, u64 branch_sample_type); int __machine__synthesize_threads(struct machine *machine, const struct perf_tool *tool, struct target *target, struct perf_thread_map *threads, From b1493c42183fc6b0ef2b05ca6f8c9bfea1b44393 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 18 May 2026 15:43:25 -0700 Subject: [PATCH 1562/2758] perf inject: Fix itrace branch stack synthesis [ Upstream commit daac18e7c42c012e289bfd310503f9417e4a9481 ] When using "perf inject --itrace=L" to synthesize branch stacks from AUX data, several issues caused failures with the generated file: 1. The synthesized samples were delivered without the PERF_SAMPLE_BRANCH_STACK flag if it was not in the original event's sample_type. Fixed by using sample_type | evsel->synth_sample_type in intel_pt_do_synth_pebs_sample. 2. Modifying evsel->core.attr.sample_type early in __cmd_inject caused parse failures for subsequent records in the input file. Fixed by moving this modification to just before writing the header. 3. perf_event__repipe_sample was narrowed to only synthesize samples when branch stack injection was requested, and restored the use of perf_inject__cut_auxtrace_sample as a fallback to preserve functionality. 4. Potential Heap Overflow in perf_event__repipe_sample: Addressed by adding a check that prints an error and returns -EFAULT if the calculated event size exceeds PERF_SAMPLE_MAX_SIZE. 5. Header vs Payload Mismatch in __cmd_inject: Addressed by narrowing the condition so that HEADER_BRANCH_STACK is only set in the file header if add_last_branch was true. 6. NULL Pointer Dereference in intel-pt.c: When branch stack injection is requested (add_last_branch is true) but last_branch is false (e.g., perf inject --itrace=L), ptq->last_branch was not allocated. However, PEBS branch stack synthesis (via synth_sample_type) still forced LBR handling in do_synth_pebs_sample(), dereferencing the NULL ptq->last_branch pointer. Guarding the dereference is not sufficient because downstream sample size calculation and synthesis strictly require a non-NULL branch_stack when the bit is set. Fixed by ensuring ptq->last_branch is allocated in intel_pt_alloc_queue() when add_last_branch is requested. 7. Modifying event attributes in perf_event__repipe_attr in-place caused SIGSEGV on read-only mmap buffers in file mode and downstream parser breakage in pipe mode. Fixed by processing the unmodified attribute first, returning immediately in non-pipe mode, and correctly synthesizing a new attribute event for pipe output using perf_event__synthesize_attr. Also: - Added a size validation check and integer underflow protection when parsing n_ids. - Prevented Trailing ID memory corruption by zero-initializing the local attr copy and safely copying using min_t(size_t, sizeof(attr), event->attr.attr.size). - Resolved ID array parsing mismatch downstream by expanding attr.size to sizeof(struct perf_event_attr) before synthesis to guarantee perfect header/attribute size alignment. 8. Potential dangling pointer vulnerability in perf_event__repipe_sample: Addressed by restoring the original sample->branch_stack pointer before returning, including on early error return paths. 9. Off-by-one error in sample size check in perf_event__repipe_sample: Fixed by checking if sz >= PERF_SAMPLE_MAX_SIZE instead of >. 10. Unadvertised size field left in payload by cut_auxtrace_sample: Addressed by excluding the 8-byte size field from the copied payload to correctly match the cleared PERF_SAMPLE_AUX bit. Cut the AUX sample payload even if size is 0. 11. Inaccurate sample size calculation and uninitialized memory leaks in convert_sample_callchain: Fixed by replacing manual arithmetic with perf_event__sample_event_size and adding a bounds check against PERF_SAMPLE_MAX_SIZE. 12. Omission of branch_sample_type in file headers: Addressed by expanding older, smaller attributes to PERF_ATTR_SIZE_VER2 in __cmd_inject to ensure branch_sample_type is not silently omitted. Fixes: 0f0aa5e0693ce400 ("perf inject: Add Instruction Tracing support") Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Dapeng Mi Cc: Ingo Molnar Cc: James Clark Cc: Leo Yan Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Thomas Falcon Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-inject.c | 153 ++++++++++++++++++++++++++++++++---- tools/perf/util/intel-pt.c | 8 +- 2 files changed, 142 insertions(+), 19 deletions(-) diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 2f20e782c7f27..7a64935b7e2b0 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -216,12 +216,23 @@ static int perf_event__repipe_op4_synth(const struct perf_tool *tool, return perf_event__repipe_synth(tool, event); } +static int perf_event__repipe_synth_cb(const struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) +{ + return perf_event__repipe_synth(tool, event); +} + static int perf_event__repipe_attr(const struct perf_tool *tool, union perf_event *event, struct evlist **pevlist) { struct perf_inject *inject = container_of(tool, struct perf_inject, tool); + struct perf_event_attr attr; + size_t n_ids; + u64 *ids; int ret; ret = perf_event__process_attr(tool, event, pevlist); @@ -232,7 +243,37 @@ static int perf_event__repipe_attr(const struct perf_tool *tool, if (!inject->output.is_pipe) return 0; - return perf_event__repipe_synth(tool, event); + if (!inject->itrace_synth_opts.set) + return perf_event__repipe_synth(tool, event); + + if (event->header.size < sizeof(struct perf_event_header) + sizeof(u64)) { + pr_err("Attribute event size %u is too small\n", event->header.size); + return -EINVAL; + } + + if (event->header.size - sizeof(event->header) < event->attr.attr.size) { + pr_err("Attribute event size %u is too small for attr.size %u\n", + event->header.size, event->attr.attr.size); + return -EINVAL; + } + + memset(&attr, 0, sizeof(attr)); + memcpy(&attr, &event->attr.attr, + min_t(size_t, sizeof(attr), (size_t)event->attr.attr.size)); + + n_ids = event->header.size - sizeof(event->header) - event->attr.attr.size; + n_ids /= sizeof(u64); + ids = perf_record_header_attr_id(event); + + attr.size = sizeof(struct perf_event_attr); + attr.sample_type &= ~PERF_SAMPLE_AUX; + + if (inject->itrace_synth_opts.add_last_branch) { + attr.sample_type |= PERF_SAMPLE_BRANCH_STACK; + attr.branch_sample_type |= PERF_SAMPLE_BRANCH_HW_INDEX; + } + return perf_event__synthesize_attr(tool, &attr, (u32)n_ids, ids, + perf_event__repipe_synth_cb); } static int perf_event__repipe_event_update(const struct perf_tool *tool, @@ -331,8 +372,8 @@ perf_inject__cut_auxtrace_sample(struct perf_inject *inject, union perf_event *event, struct perf_sample *sample) { - size_t sz1 = sample->aux_sample.data - (void *)event; - size_t sz2 = event->header.size - sample->aux_sample.size - sz1; + size_t sz1 = sample->aux_sample.data - (void *)event - sizeof(u64); + size_t sz2 = event->header.size - sample->aux_sample.size - (sz1 + sizeof(u64)); union perf_event *ev; if (inject->event_copy == NULL) { @@ -343,13 +384,12 @@ perf_inject__cut_auxtrace_sample(struct perf_inject *inject, ev = (union perf_event *)inject->event_copy; if (sz1 > event->header.size || sz2 > event->header.size || sz1 + sz2 > event->header.size || - sz1 < sizeof(struct perf_event_header) + sizeof(u64)) + sz1 < sizeof(struct perf_event_header)) return event; memcpy(ev, event, sz1); memcpy((void *)ev + sz1, (void *)event + event->header.size - sz2, sz2); ev->header.size = sz1 + sz2; - ((u64 *)((void *)ev + sz1))[-1] = 0; return ev; } @@ -369,14 +409,77 @@ static int perf_event__repipe_sample(const struct perf_tool *tool, struct perf_inject *inject = container_of(tool, struct perf_inject, tool); - if (evsel && evsel->handler) { + if (evsel == NULL) + return perf_event__repipe_synth(tool, event); + + if (evsel->handler) { inject_handler f = evsel->handler; return f(tool, event, sample, evsel, machine); } build_id__mark_dso_hit(tool, event, sample, evsel, machine); - if (inject->itrace_synth_opts.set && sample->aux_sample.size) { + if (inject->itrace_synth_opts.set && + (inject->itrace_synth_opts.last_branch || + inject->itrace_synth_opts.add_last_branch)) { + union perf_event *event_copy = (void *)inject->event_copy; + struct branch_stack dummy_bs = { .nr = 0, .hw_idx = 0 }; + int err; + size_t sz; + u64 orig_type = evsel->core.attr.sample_type; + u64 orig_branch_type = evsel->core.attr.branch_sample_type; + + struct branch_stack *orig_bs = sample->branch_stack; + + if (event_copy == NULL) { + inject->event_copy = malloc(PERF_SAMPLE_MAX_SIZE); + if (!inject->event_copy) + return -ENOMEM; + + event_copy = (void *)inject->event_copy; + } + + if (!sample->branch_stack) + sample->branch_stack = &dummy_bs; + + if (inject->itrace_synth_opts.add_last_branch) { + /* Temporarily add in type bits for synthesis. */ + evsel->core.attr.sample_type |= PERF_SAMPLE_BRANCH_STACK; + evsel->core.attr.branch_sample_type |= PERF_SAMPLE_BRANCH_HW_INDEX; + } + evsel->core.attr.sample_type &= ~PERF_SAMPLE_AUX; + + sz = perf_event__sample_event_size(sample, evsel->core.attr.sample_type, + evsel->core.attr.read_format, + evsel->core.attr.branch_sample_type); + + if (sz >= PERF_SAMPLE_MAX_SIZE) { + pr_err("Sample size %zu exceeds max size %d\n", sz, PERF_SAMPLE_MAX_SIZE); + evsel->core.attr.sample_type = orig_type; + evsel->core.attr.branch_sample_type = orig_branch_type; + sample->branch_stack = orig_bs; + return -EFAULT; + } + + event_copy->header.type = PERF_RECORD_SAMPLE; + event_copy->header.misc = event->header.misc; + event_copy->header.size = sz; + + err = perf_event__synthesize_sample(event_copy, evsel->core.attr.sample_type, + evsel->core.attr.read_format, + evsel->core.attr.branch_sample_type, sample); + + evsel->core.attr.sample_type = orig_type; + evsel->core.attr.branch_sample_type = orig_branch_type; + sample->branch_stack = orig_bs; + + if (err) { + pr_err("Failed to synthesize sample\n"); + return err; + } + event = event_copy; + } else if (inject->itrace_synth_opts.set && + (evsel->core.attr.sample_type & PERF_SAMPLE_AUX)) { event = perf_inject__cut_auxtrace_sample(inject, event, sample); if (IS_ERR(event)) return PTR_ERR(event); @@ -397,7 +500,7 @@ static int perf_event__convert_sample_callchain(const struct perf_tool *tool, struct callchain_cursor_node *node; struct thread *thread; u64 sample_type = evsel->core.attr.sample_type; - u32 sample_size = event->header.size; + size_t sz; u64 i, k; int ret; @@ -456,15 +559,18 @@ static int perf_event__convert_sample_callchain(const struct perf_tool *tool, out: memcpy(event_copy, event, sizeof(event->header)); - /* adjust sample size for stack and regs */ - sample_size -= sample->user_stack.size; - sample_size -= (hweight64(evsel->core.attr.sample_regs_user) + 1) * sizeof(u64); - sample_size += (sample->callchain->nr + 1) * sizeof(u64); - event_copy->header.size = sample_size; - /* remove sample_type {STACK,REGS}_USER for synthesize */ sample_type &= ~(PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER); + sz = perf_event__sample_event_size(sample, sample_type, + evsel->core.attr.read_format, + evsel->core.attr.branch_sample_type); + if (sz >= PERF_SAMPLE_MAX_SIZE) { + pr_err("Sample size %zu exceeds max size %d\n", sz, PERF_SAMPLE_MAX_SIZE); + return -EFAULT; + } + event_copy->header.size = sz; + ret = perf_event__synthesize_sample(event_copy, sample_type, evsel->core.attr.read_format, evsel->core.attr.branch_sample_type, sample); @@ -2442,12 +2548,27 @@ static int __cmd_inject(struct perf_inject *inject) * synthesized hardware events, so clear the feature flag. */ if (inject->itrace_synth_opts.set) { + struct evsel *evsel; + perf_header__clear_feat(&session->header, HEADER_AUXTRACE); - if (inject->itrace_synth_opts.last_branch || - inject->itrace_synth_opts.add_last_branch) + + evlist__for_each_entry(session->evlist, evsel) { + evsel->core.attr.sample_type &= ~PERF_SAMPLE_AUX; + } + + if (inject->itrace_synth_opts.add_last_branch) { perf_header__set_feat(&session->header, HEADER_BRANCH_STACK); + + evlist__for_each_entry(session->evlist, evsel) { + evsel->core.attr.sample_type |= PERF_SAMPLE_BRANCH_STACK; + if (evsel->core.attr.size < PERF_ATTR_SIZE_VER2) + evsel->core.attr.size = PERF_ATTR_SIZE_VER2; + evsel->core.attr.branch_sample_type |= + PERF_SAMPLE_BRANCH_HW_INDEX; + } + } } /* diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index dd2637678b405..d9c86ac497486 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c @@ -1307,7 +1307,8 @@ static struct intel_pt_queue *intel_pt_alloc_queue(struct intel_pt *pt, goto out_free; } - if (pt->synth_opts.last_branch || pt->synth_opts.other_events) { + if (pt->synth_opts.last_branch || pt->synth_opts.add_last_branch || + pt->synth_opts.other_events) { unsigned int entry_cnt = max(LBRS_MAX, pt->br_stack_sz); ptq->last_branch = intel_pt_alloc_br_stack(entry_cnt); @@ -2505,7 +2506,7 @@ static int intel_pt_do_synth_pebs_sample(struct intel_pt_queue *ptq, struct evse intel_pt_add_xmm(intr_regs, pos, items, regs_mask); } - if (sample_type & PERF_SAMPLE_BRANCH_STACK) { + if ((sample_type | evsel->synth_sample_type) & PERF_SAMPLE_BRANCH_STACK) { if (items->mask[INTEL_PT_LBR_0_POS] || items->mask[INTEL_PT_LBR_1_POS] || items->mask[INTEL_PT_LBR_2_POS]) { @@ -2576,7 +2577,8 @@ static int intel_pt_do_synth_pebs_sample(struct intel_pt_queue *ptq, struct evse sample.transaction = txn; } - ret = intel_pt_deliver_synth_event(pt, event, &sample, sample_type); + ret = intel_pt_deliver_synth_event(pt, event, &sample, + sample_type | evsel->synth_sample_type); perf_sample__exit(&sample); return ret; } From 7fc162453cfb7b8e93edb16287a84ba0130cac38 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Sun, 17 May 2026 19:12:18 +0800 Subject: [PATCH 1563/2758] staging: most: video: avoid double free on video register failure [ Upstream commit 7cb1c5b32a2bfde961fff8d5204526b609bcb30a ] comp_register_videodev() allocates a video_device with video_device_alloc() and releases it if video_register_device() fails. This can double free the video_device when __video_register_device() reaches device_register() and that call fails: video_register_device() -> __video_register_device() -> device_register() fails -> put_device(&vdev->dev) -> v4l2_device_release() -> vdev->release(vdev) -> video_device_release(vdev) comp_register_videodev() -> video_device_release(mdev->vdev) Use video_device_release_empty() while registering the device so that registration failure paths do not free mdev->vdev through vdev->release(). comp_register_videodev() then releases mdev->vdev exactly once on failure. Restore video_device_release() after successful registration so the registered device keeps its normal lifetime handling. This issue was found by a static analysis tool I am developing. Fixes: eab231c0398a ("staging: most: v4l2-aim: remove unnecessary label err_vbi_dev") Signed-off-by: Guangshuo Li Link: https://patch.msgid.link/20260517111218.945796-1-lgs201920130244@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/staging/most/video/video.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/staging/most/video/video.c b/drivers/staging/most/video/video.c index 04351f8ccccf1..aab57768f4f70 100644 --- a/drivers/staging/most/video/video.c +++ b/drivers/staging/most/video/video.c @@ -420,6 +420,7 @@ static int comp_register_videodev(struct most_video_dev *mdev) /* Fill the video capture device struct */ *mdev->vdev = comp_videodev_template; + mdev->vdev->release = video_device_release_empty; mdev->vdev->v4l2_dev = &mdev->v4l2_dev; mdev->vdev->lock = &mdev->lock; snprintf(mdev->vdev->name, sizeof(mdev->vdev->name), "MOST: %s", @@ -432,9 +433,13 @@ static int comp_register_videodev(struct most_video_dev *mdev) v4l2_err(&mdev->v4l2_dev, "video_register_device failed (%d)\n", ret); video_device_release(mdev->vdev); + return ret; } - return ret; + mdev->vdev->release = video_device_release; + + return 0; + } static void comp_unregister_videodev(struct most_video_dev *mdev) From 4da073d57176d8e1c2bca34febfbc81d2560c1a5 Mon Sep 17 00:00:00 2001 From: Seungjin Bae Date: Mon, 18 May 2026 18:49:00 -0400 Subject: [PATCH 1564/2758] usb: host: max3421: Fix shift-out-of-bounds in max3421_hub_control() [ Upstream commit cff06b03b530ae1fe8a13e93a7848f2130e00fb4 ] The `max3421_hub_control()` function handles USB hub class requests to the virtual root hub. In the `default` branches of both the `ClearPortFeature` and `SetPortFeature` switch statements, it modifies `max3421_hcd->port_status` by left shifting 1 by the request's `value` parameter. However, it does not validate whether this shift will exceed the width of `port_status`. So if a malicious userspace task with access to the root hub via /dev/bus/usb/.../001 issues a USBDEVFS_CONTROL ioctl with `wValue` greater than or equal to 32, the left shift operation invokes shift-out-of-bounds undefined behavior. This results in arbitrary bit corruption of `port_status`, including the normally-immutable change bits, which can bypass internal state checks and confuse the hub status. Fix this by rejecting requests whose `value` exceeds the shift width before performing the shift. This issue was found using a KLEE-based symbolic execution tool for kernel drivers that I'm currently developing. Fixes: 2d53139f3162 ("Add support for using a MAX3421E chip as a host driver.") Signed-off-by: Seungjin Bae Link: https://patch.msgid.link/20260518224901.1887013-1-eeodqql09@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/usb/host/max3421-hcd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c index 0e17c988d36a2..3d6b351dcb1a2 100644 --- a/drivers/usb/host/max3421-hcd.c +++ b/drivers/usb/host/max3421-hcd.c @@ -1694,6 +1694,8 @@ max3421_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index, !pdata->vbus_active_level); fallthrough; default: + if (value >= 32) + goto error; max3421_hcd->port_status &= ~(1 << value); } break; @@ -1747,6 +1749,8 @@ max3421_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index, max3421_reset_port(hcd); fallthrough; default: + if (value >= 32) + goto error; if ((max3421_hcd->port_status & USB_PORT_STAT_POWER) != 0) max3421_hcd->port_status |= (1 << value); From 684a58dd845e4ca510d8409d2daa5f0645b0b484 Mon Sep 17 00:00:00 2001 From: Seungjin Bae Date: Mon, 18 May 2026 18:49:02 -0400 Subject: [PATCH 1565/2758] usb: host: max3421: Reject hub port requests for non-existent ports [ Upstream commit 11b5c101e2fd206104b05bc92554d356989423a8 ] The `max3421_hub_control()` function handles USB hub class requests to the virtual root hub. The `GetPortStatus` case correctly rejects requests with `index != 1`, since the virtual root hub has only a single port. However, the `ClearPortFeature` and `SetPortFeature` cases lack the same check. Fix this by extending the `index != 1` rejection to both cases, matching the existing behavior of `GetPortStatus`. Fixes: 2d53139f3162 ("Add support for using a MAX3421E chip as a host driver.") Suggested-by: Alan Stern Reviewed-by: Alan Stern Signed-off-by: Seungjin Bae Link: https://patch.msgid.link/20260518224901.1887013-3-eeodqql09@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/usb/host/max3421-hcd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c index 3d6b351dcb1a2..73e76d0e69738 100644 --- a/drivers/usb/host/max3421-hcd.c +++ b/drivers/usb/host/max3421-hcd.c @@ -1685,6 +1685,8 @@ max3421_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index, case ClearHubFeature: break; case ClearPortFeature: + if (index != 1) + goto error; switch (value) { case USB_PORT_FEAT_SUSPEND: break; @@ -1728,6 +1730,8 @@ max3421_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index, break; case SetPortFeature: + if (index != 1) + goto error; switch (value) { case USB_PORT_FEAT_LINK_STATE: case USB_PORT_FEAT_U1_TIMEOUT: From ecdd8af41197c00ff8edc00664b06174ecb0f0a4 Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Fri, 8 May 2026 05:59:57 +0000 Subject: [PATCH 1566/2758] perf test amd ibs: Fix incorrect kernel version check [ Upstream commit 0b97e92393a178765ee1ea01fe5087efece2c425 ] "AMD IBS sample period" unit test is getting skipped on kernel v7.x. Fix the kernel version >= v6.15 check. Fixes: 21fb366b2f457611 ("perf test amd: Skip amd-ibs-period test on kernel < v6.15") Signed-off-by: Ravi Bangoria Acked-by: Namhyung Kim Cc: Ananth Narayan Cc: Dapeng Mi Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Manali Shukla Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Sandipan Das Cc: Santosh Shukla Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/arch/x86/tests/amd-ibs-period.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/arch/x86/tests/amd-ibs-period.c b/tools/perf/arch/x86/tests/amd-ibs-period.c index cee9e11c05e08..6a92b3a23ed7a 100644 --- a/tools/perf/arch/x86/tests/amd-ibs-period.c +++ b/tools/perf/arch/x86/tests/amd-ibs-period.c @@ -932,7 +932,7 @@ static bool kernel_v6_15_or_newer(void) endptr++; minor = strtol(endptr, NULL, 10); - return major >= 6 && minor >= 15; + return major > 6 || (major == 6 && minor >= 15); } int test__amd_ibs_period(struct test_suite *test __maybe_unused, From 49489a18afa55db2cb8c3685e07e22e7447dca72 Mon Sep 17 00:00:00 2001 From: Dave Penkler Date: Sat, 11 Apr 2026 12:20:25 +0200 Subject: [PATCH 1567/2758] gpib: Fix inappropriate ioctl error return [ Upstream commit 70ea440324e8c1a10837f721352f5bd469c85007 ] The driver was returning -ENOTTY in the case the ioctl command was not recognised. Change it to -EBADRQC. Fixes: 9dde4559e939 ("staging: gpib: Add GPIB common core driver") Signed-off-by: Dave Penkler Link: https://patch.msgid.link/20260411102025.2000-3-dpenkler@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/gpib/common/gpib_os.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpib/common/gpib_os.c b/drivers/gpib/common/gpib_os.c index 5909274ddc122..7dca60488872f 100644 --- a/drivers/gpib/common/gpib_os.c +++ b/drivers/gpib/common/gpib_os.c @@ -613,7 +613,7 @@ long ibioctl(struct file *filep, unsigned int cmd, unsigned long arg) unsigned int minor = iminor(file_inode(filep)); struct gpib_board *board; struct gpib_file_private *file_priv = filep->private_data; - long retval = -ENOTTY; + long retval = -EBADRQC; if (minor >= GPIB_MAX_NUM_BOARDS) { pr_err("gpib: invalid minor number of device file\n"); @@ -806,7 +806,6 @@ long ibioctl(struct file *filep, unsigned int cmd, unsigned long arg) mutex_unlock(&board->big_gpib_mutex); return write_ioctl(file_priv, board, arg); default: - retval = -ENOTTY; goto done; } From 42223445607a9a5df3cb1c4729abfe3a5085e7ce Mon Sep 17 00:00:00 2001 From: James Kim Date: Sun, 3 May 2026 19:11:31 +0900 Subject: [PATCH 1568/2758] char: tlclk: fix use-after-free in tlclk_cleanup() [ Upstream commit bbf003b7794d6ad6f939fdd29f1f1bde8ac554c1 ] This patch improves the module cleanup process in the tlclk driver to prevent potential use-after-free and race conditions. Currently, the file_operations structure does not specify the .owner field, which could allow the module to be unloaded while user-space processes are still interacting with the device. Additionally, the tlclk_cleanup() function frees the alarm_events memory before ensuring that blocked processes in the waitqueue are fully awakened and that the switchover_timer has completed. To address these cases, this patch: - Sets '.owner = THIS_MODULE' in tlclk_fops to safely defer module unloading while the device is in use. - Updates tlclk_cleanup() to explicitly wake up all blocked readers (wake_up_all), properly release hardware I/O regions, and safely delete the timer (timer_delete_sync) prior to freeing memory. Fixes: 1a80ba882730 ("[PATCH] Telecom Clock Driver for MPCBL0010 ATCA computer blade") Signed-off-by: James Kim Link: https://patch.msgid.link/20260503101131.64219-1-james010kim@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/char/tlclk.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/char/tlclk.c b/drivers/char/tlclk.c index 677d230a226cf..dd45fe5eb6f27 100644 --- a/drivers/char/tlclk.c +++ b/drivers/char/tlclk.c @@ -264,6 +264,7 @@ static ssize_t tlclk_read(struct file *filp, char __user *buf, size_t count, } static const struct file_operations tlclk_fops = { + .owner = THIS_MODULE, .read = tlclk_read, .open = tlclk_open, .release = tlclk_release, @@ -837,6 +838,9 @@ static void __exit tlclk_cleanup(void) misc_deregister(&tlclk_miscdev); unregister_chrdev(tlclk_major, "telco_clock"); + got_event = 1; + wake_up_all(&wq); + release_region(TLCLK_BASE, 8); timer_delete_sync(&switchover_timer); kfree(alarm_events); From 99a34d028293600afaa7176be04acd3cb86b0508 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 12 May 2026 18:20:09 +0200 Subject: [PATCH 1569/2758] hpet: Check ACPI_COMPANION() against NULL at probe time [ Upstream commit fa5e2952341b792b72d5410dbfcd0b2ac8046d2a ] Every platform driver can be forced to match a device that doesn't match its list of device IDs because of device_match_driver_override(), so platform drivers that rely on the existence of a device's ACPI companion object need to verify its presence. Accordingly, add a requisite ACPI_COMPANION() check against NULL to the hpet driver. Fixes: 71f0a267346b ("hpet: Convert ACPI driver to a platform one") Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/4750803.LvFx2qVVIh@rafael.j.wysocki Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/char/hpet.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index 46c84e5df00fd..285c6037417ab 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -976,10 +976,14 @@ static acpi_status hpet_resources(struct acpi_resource *res, void *data) static int hpet_acpi_probe(struct platform_device *pdev) { - struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + struct acpi_device *device; acpi_status result; struct hpet_data data; + device = ACPI_COMPANION(&pdev->dev); + if (!device) + return -ENODEV; + memset(&data, 0, sizeof(data)); result = From a41f0fbd77ae1b95bb9ed679429e6fd883a526e7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 12 May 2026 18:20:53 +0200 Subject: [PATCH 1570/2758] sonypi: Check ACPI_COMPANION() against NULL at probe time [ Upstream commit 950f35211b85db26fc1aae67bf1e14ccef393a0a ] Every platform driver can be forced to match a device that doesn't match its list of device IDs because of device_match_driver_override(), so platform drivers that rely on the existence of a device's ACPI companion object need to verify its presence. Accordingly, add a requisite ACPI_COMPANION() check against NULL to the sonypi driver. Fixes: 7e488b0af021 ("sonypi: Convert ACPI driver to a platform one") Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/5087721.GXAFRqVoOG@rafael.j.wysocki Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/char/sonypi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index ccda997a9098a..24c1b26f34d6b 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -1117,7 +1117,11 @@ static int sonypi_disable(void) #ifdef CONFIG_ACPI static int sonypi_acpi_probe(struct platform_device *pdev) { - struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + struct acpi_device *device; + + device = ACPI_COMPANION(&pdev->dev); + if (!device) + return -ENODEV; sonypi_acpi_device = device; strcpy(acpi_device_name(device), "Sony laptop hotkeys"); From 8b5f1d295dda8677e4545ce340053fcfa8b634c7 Mon Sep 17 00:00:00 2001 From: Adam Crosser Date: Fri, 24 Apr 2026 19:37:47 +0700 Subject: [PATCH 1571/2758] gpib: fix double decrement of descriptor_busy in command_ioctl() [ Upstream commit c4faab452b3c1ada003d49c477609dd80523b9bf ] commit d1857f8296dc ("gpib: fix use-after-free in IO ioctl handlers") introduced a descriptor_busy reference counter to pin struct gpib_descriptor across IO ioctl operations. In command_ioctl(), the error path inside the loop decrements descriptor_busy and breaks, but execution then falls through to the unconditional decrement after the loop, underflowing the counter to -1. This re-enables the use-after-free that the original fix was meant to prevent: a concurrent close_dev_ioctl() sees descriptor_busy == 0 on an actively-used descriptor and frees it. Remove the early decrement from the error path. The post-loop decrement already handles all exit paths, matching the correct pattern used in read_ioctl() and write_ioctl(). Fixes: d1857f8296dc ("gpib: fix use-after-free in IO ioctl handlers") Reported-by: Ruikai Peng Signed-off-by: Adam Crosser Link: https://patch.msgid.link/20260424123750.855863-1-adam.r.crosser@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/gpib/common/gpib_os.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpib/common/gpib_os.c b/drivers/gpib/common/gpib_os.c index 7dca60488872f..fcf13432c7dd5 100644 --- a/drivers/gpib/common/gpib_os.c +++ b/drivers/gpib/common/gpib_os.c @@ -1017,7 +1017,6 @@ static int command_ioctl(struct gpib_file_private *file_priv, userbuf += bytes_written; if (retval < 0) { atomic_set(&desc->io_in_progress, 0); - atomic_dec(&desc->descriptor_busy); wake_up_interruptible(&board->wait); break; From f15a545f7518bc0942d14daff88f1ec38e816a7c Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Sun, 3 May 2026 17:30:36 +0800 Subject: [PATCH 1572/2758] gpib: cb7210: Fix region leak when request_irq fails [ Upstream commit 5ad28496055858166eb2268344c8fda2c26d3561 ] When request_irq() fails, the region allocated by request_region() is not released. Fix this by calling release_region() before returning. Smatch warning: drivers/gpib/cb7210/cb7210.c:1068 cb_isa_attach() warn: 'config->ibbase' from __request_region() not released on lines: 1064. Fixes: 82e3508046f9 ("staging: gpib: cb7210 console messaging cleanup") Signed-off-by: Hongling Zeng Link: https://patch.msgid.link/20260503093036.283546-1-zenghongling@kylinos.cn Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/gpib/cb7210/cb7210.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpib/cb7210/cb7210.c b/drivers/gpib/cb7210/cb7210.c index 6dd8637c5964b..05058bf2cd50b 100644 --- a/drivers/gpib/cb7210/cb7210.c +++ b/drivers/gpib/cb7210/cb7210.c @@ -1062,6 +1062,7 @@ static int cb_isa_attach(struct gpib_board *board, const struct gpib_board_confi // install interrupt handler if (request_irq(config->ibirq, cb7210_interrupt, isr_flags, DRV_NAME, board)) { dev_err(board->gpib_dev, "failed to obtain IRQ %d\n", config->ibirq); + release_region(nec7210_iobase(cb_priv), cb7210_iosize); return -EBUSY; } cb_priv->irq = config->ibirq; From 789d1b0e111838f802f3260c78df0f3e3d854004 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 20 May 2026 10:29:47 +0100 Subject: [PATCH 1573/2758] clk: renesas: rzg2l: Rename iterator in for_each_mod_clock() to avoid shadowing [ Upstream commit 1f10c4509649e7c5f6d5d3acccf3ef6fbb5cdd46 ] Rename the internal loop iterator variable in the for_each_mod_clock() macro from 'i' to '__i'. The current naming conflicts with local loop variables named 'i' inside code blocks that utilize the macro, triggering compiler warnings due to variable shadowing: drivers/clk/renesas/rzg2l-cpg.c:1494:36: warning: declaration of `i` shadows a previous local [-Wshadow] 1494 | for (unsigned int i = 0; i < clk->num_shared_mstop_clks; i++) Using a unique identifier for the macro-internal iterator resolves the shadowing warnings globally across all macro expansions. Fixes: 3fd4a8bb4b63 ("clk: renesas: rzg2l: Add macro to loop through module clocks") Signed-off-by: Lad Prabhakar Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260520092947.70596-1-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Geert Uytterhoeven Signed-off-by: Sasha Levin --- drivers/clk/renesas/rzg2l-cpg.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c index abfd8634d2bef..c7bb5b97e7c45 100644 --- a/drivers/clk/renesas/rzg2l-cpg.c +++ b/drivers/clk/renesas/rzg2l-cpg.c @@ -1340,10 +1340,10 @@ struct mod_clock { #define to_mod_clock(_hw) container_of(_hw, struct mod_clock, hw) #define for_each_mod_clock(mod_clock, hw, priv) \ - for (unsigned int i = 0; (priv) && i < (priv)->num_mod_clks; i++) \ - if ((priv)->clks[(priv)->num_core_clks + i] == ERR_PTR(-ENOENT)) \ + for (unsigned int __i = 0; (priv) && __i < (priv)->num_mod_clks; __i++) \ + if ((priv)->clks[(priv)->num_core_clks + __i] == ERR_PTR(-ENOENT)) \ continue; \ - else if (((hw) = __clk_get_hw((priv)->clks[(priv)->num_core_clks + i])) && \ + else if (((hw) = __clk_get_hw((priv)->clks[(priv)->num_core_clks + __i])) && \ ((mod_clock) = to_mod_clock(hw))) /* Need to be called with a lock held to avoid concurrent access to mstop->usecnt. */ From 0e1db8dc462328d4256fd4d8e42c27ab982376c1 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Sun, 17 May 2026 18:41:41 +0300 Subject: [PATCH 1574/2758] docs: threat-model: add missing closing parenthesis [ Upstream commit 89f332c1ef348e260a885085cd7821d6d72db7ec ] Fixes: a03ef333fbd6 ("Documentation: security-bugs: explain what is and is not a security bug") Signed-off-by: Baruch Siach Acked-by: Willy Tarreau Signed-off-by: Jonathan Corbet Message-ID: Signed-off-by: Sasha Levin --- Documentation/process/threat-model.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/process/threat-model.rst b/Documentation/process/threat-model.rst index f177b8d3c1caf..9dd8011dde828 100644 --- a/Documentation/process/threat-model.rst +++ b/Documentation/process/threat-model.rst @@ -176,7 +176,7 @@ regular bug: * problems seen only under development simulators, emulators, or combinations that do not exist on real systems at the time of reporting (issues involving tens of millions of threads, tens of thousands of CPUs, - unrealistic CPU frequencies, RAM sizes or disk capacities, network speeds. + unrealistic CPU frequencies, RAM sizes or disk capacities, network speeds). * issues whose reproduction requires hardware modification or emulation, including fake USB devices that pretend to be another one. From c764d5092b92c558d579662bc8bae052bfb82106 Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Mon, 4 May 2026 20:43:20 +0530 Subject: [PATCH 1575/2758] powerpc tools perf: Initialize error code in auxtrace_record_init function [ Upstream commit 789d22d77879eabb042627f6627cdb62787bc142 ] perf trace record fails some cases in powerpc # perf test "perf trace record and replay" 128: perf trace record and replay : FAILED! # perf trace record sleep 1 # echo $? 32 This is happening because of non-zero err value from auxtrace_record__init() function. static int record__auxtrace_init(struct record *rec) { int err; if ((rec->opts.auxtrace_snapshot_opts || rec->opts.auxtrace_sample_opts) && record__threads_enabled(rec)) { pr_err("AUX area tracing options are not available in parallel streaming mode.\n"); return -EINVAL; } if (!rec->itr) { rec->itr = auxtrace_record__init(rec->evlist, &err); if (err) return err; } Here "int err" is not initialised. The code expects "err" to be set from auxtrace_record__init() function. Update auxtrace_record__init() in arch/powerpc/util/auxtrace.c to clear err value in the beginning. - Clear err value in beginning of function. Any fail later will set appropriate return code to err. - Even if we haven't found any event for auxtrace, perf record should continue for other events. NULL return will indicate that there is no auxtrace record initialized. - Not having "err" set here will affect monitoring of other events also because perf record will fail seeing random value in err. Set err to -EINVAL before invoking auxtrace_record__init() in builtin-record.c With the fix, # perf trace record sleep 1 [ perf record: Woken up 2 times to write data ] [ perf record: Captured and wrote 0.033 MB perf.data (228 samples) ] Fixes: 1dbfaf94cf66ec4b ("perf powerpc: Add basic CONFIG_AUXTRACE support for VPA pmu on powerpc") Reviewed-by: Adrian Hunter Signed-off-by: Athira Rajeev Acked-by: Namhyung Kim Cc: Athira Rajeev Cc: Hari Bathini Cc: Ian Rogers Cc: Jiri Olsa Cc: linuxppc-dev@lists.ozlabs.org Cc: Madhavan Srinivasan Cc: Michael Petlan Cc: Shivani Nittor Cc: Tanushree Shah Cc: Tejas Manhas Cc: Thomas Richter Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/arch/powerpc/util/auxtrace.c | 6 ++++++ tools/perf/builtin-record.c | 1 + 2 files changed, 7 insertions(+) diff --git a/tools/perf/arch/powerpc/util/auxtrace.c b/tools/perf/arch/powerpc/util/auxtrace.c index e39deff6c857a..4600a1661b4fe 100644 --- a/tools/perf/arch/powerpc/util/auxtrace.c +++ b/tools/perf/arch/powerpc/util/auxtrace.c @@ -71,6 +71,12 @@ struct auxtrace_record *auxtrace_record__init(struct evlist *evlist, struct evsel *pos; int found = 0; + /* + * Set err value to zero here. Any fail later + * will set appropriate return code to err. + */ + *err = 0; + evlist__for_each_entry(evlist, pos) { if (strstarts(pos->name, "vpa_dtl")) { found = 1; diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 4a5eba498c025..708825747af5d 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -865,6 +865,7 @@ static int record__auxtrace_init(struct record *rec) } if (!rec->itr) { + err = -EINVAL; rec->itr = auxtrace_record__init(rec->evlist, &err); if (err) return err; From d61e42f63a00ec2af96ebb2814c48030e1f867b0 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sun, 19 Apr 2026 17:39:34 +0800 Subject: [PATCH 1576/2758] PCI: qcom: Disable ASPM L0s for SA8775P [ Upstream commit 29f692985819f4089f02a86e151a72f6d4cdd90d ] Due to a hardware issue, L0s is not properly supported by the PCIe controller on the SA8775p SoC. If enabled, the L0s to L0 transition triggers below correctable AER errors and may also affect link stability: pcieport 0000:00:00.0: PME: Signaling with IRQ 332 pcieport 0000:00:00.0: AER: enabled with IRQ 332 pcieport 0000:00:00.0: AER: Correctable error message received from 0000:01:00.0 pci 0000:01:00.0: PCIe Bus Error: severity=Correctable, type=Data Link Layer, (Transmitter ID) pci 0000:01:00.0: device [17cb:1103] error status/mask=00001000/0000e000 pci 0000:01:00.0: [12] Timeout pcieport 0000:00:00.0: AER: Multiple Correctable error message received from 0000:01:00.0 pcieport 0000:00:00.0: PCIe Bus Error: severity=Correctable, type=Data Link Layer, (Transmitter ID) pcieport 0000:00:00.0: device [17cb:0115] error status/mask=00001000/0000e000 pcieport 0000:00:00.0: [12] Timeout Hence, disable L0s for the SA8775p SoC to allow it to properly function by sacrificing a little bit of power saving. Fixes: 58d0d3e032b3 ("PCI: qcom-ep: Add support for SA8775P SOC") Assisted-by: Claude:claude-4-6-sonnet Signed-off-by: Shawn Guo [mani: commit log, corrected fixes tag] Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260419093934.1223027-1-shengchao.guo@oss.qualcomm.com Signed-off-by: Sasha Levin --- drivers/pci/controller/dwc/pcie-qcom.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 8604aa564dce6..2af7573979751 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -1473,6 +1473,7 @@ static const struct qcom_pcie_cfg cfg_1_9_0 = { static const struct qcom_pcie_cfg cfg_1_34_0 = { .ops = &ops_1_9_0, .override_no_snoop = true, + .no_l0s = true, }; static const struct qcom_pcie_cfg cfg_2_1_0 = { From 27ca3f615c1a8b5aec79f8c4706cc1c06605745a Mon Sep 17 00:00:00 2001 From: Zhan Xusheng Date: Tue, 26 May 2026 10:21:06 +0800 Subject: [PATCH 1577/2758] timers/migration: Update stale @online doc to @available [ Upstream commit 45a13ba52c82dbec9715222c51e629e85daa37d7 ] Commit 8312cab5ff47 ("timers/migration: Rename 'online' bit to 'available'") renamed the 'online' field of struct tmigr_cpu to 'available'. The kernel doc comment above the struct still describes the old field name. Update it to reflect the actual field name and use the 'available' wording in the description. Fixes: 8312cab5ff47 ("timers/migration: Rename 'online' bit to 'available'") Signed-off-by: Zhan Xusheng Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260526022106.1302279-1-zhanxusheng@xiaomi.com Signed-off-by: Sasha Levin --- kernel/time/timer_migration.h | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/kernel/time/timer_migration.h b/kernel/time/timer_migration.h index 70879cde6fdd0..4c0073f3d3212 100644 --- a/kernel/time/timer_migration.h +++ b/kernel/time/timer_migration.h @@ -75,15 +75,17 @@ struct tmigr_group { /** * struct tmigr_cpu - timer migration per CPU group * @lock: Lock protecting the tmigr_cpu group information - * @online: Indicates whether the CPU is online; In deactivate path - * it is required to know whether the migrator in the top - * level group is to be set offline, while a timer is - * pending. Then another online CPU needs to be notified to - * take over the migrator role. Furthermore the information - * is required in CPU hotplug path as the CPU is able to go - * idle before the timer migration hierarchy hotplug AP is - * reached. During this phase, the CPU has to handle the + * @available: Indicates whether the CPU is available for handling + * global timers. In the deactivate path it is required to + * know whether the migrator in the top level group is to + * be set offline, while a timer is pending. Then another + * available CPU needs to be notified to take over the + * migrator role. Furthermore the information is required + * in the CPU hotplug path as the CPU is able to go idle + * before the timer migration hierarchy hotplug callback is + * reached. During this phase, the CPU has to handle the * global timers on its own and must not act as a migrator. + * @idle: Indicates whether the CPU is idle in the timer migration * hierarchy * @remote: Is set when timers of the CPU are expired remotely From ea63c57eb2f1ebda5c7dfc0409eb89bb8dd56dd1 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 2 May 2026 14:37:39 -0300 Subject: [PATCH 1578/2758] perf header: Sanity check HEADER_EVENT_DESC attr.size before swap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 944f65c8b8231d26d4db6be67bcb641142603cb4 ] read_event_desc() reads nre (event count), sz (attr size), and nr (IDs per event) from the file and uses them to control allocations and loops without validating them against the section size. A crafted perf.data could trigger large allocations or many loop iterations before __do_read() eventually rejects the reads. Add bounds checks in read_event_desc(): - Reject sz smaller than PERF_ATTR_SIZE_VER0. - Require at least one event (nre > 0). - Check that nre events fit in the remaining section, using the minimum per-event footprint of sz + sizeof(u32). - Pre-swap attr->size to native byte order, then reject values below PERF_ATTR_SIZE_VER0 or above sz before calling perf_event__attr_swap() to prevent heap out-of-bounds access. - Handle ABI0 (attr.size == 0): substitute PERF_ATTR_SIZE_VER0, and on native-endian files write the value back so free_event_desc() does not treat the zero as its end-of-array sentinel (it iterates while attr.size != 0). The swap path skips the write-back — perf_event__attr_swap() has its own ABI0 fallback that sets VER0 after swapping. - Check that nr IDs fit in the remaining section before allocating. Fixes: b30b61729246 ("perf tools: Fix a problem when opening old perf.data with different byte order") Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Cc: Wang Nan Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/header.c | 54 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index f30e48eb3fc32..30e5e50ab98b8 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -2135,9 +2135,28 @@ static struct evsel *read_event_desc(struct feat_fd *ff) if (do_read_u32(ff, &nre)) goto error; + /* Size of each of the nre attributes. */ if (do_read_u32(ff, &sz)) goto error; + /* + * Require at least one event with an attr no smaller than the + * first published struct, and reject sz values where + * sz + sizeof(u32) would overflow size_t (possible on 32-bit) + * or nre == UINT32_MAX where nre + 1 wraps to 0 in the calloc. + * + * The minimum section footprint per event is sz bytes for the + * attr plus a u32 for the id count, check that nre events fit. + */ + if (!nre || sz < PERF_ATTR_SIZE_VER0 || + sz > ff->size || (size_t)sz > SIZE_MAX - sizeof(u32) || + nre == UINT32_MAX || + nre > (ff->size - ff->offset) / (sz + sizeof(u32))) { + pr_err("Invalid HEADER_EVENT_DESC: nre=%u sz=%u (min %d)\n", + nre, sz, PERF_ATTR_SIZE_VER0); + goto error; + } + /* buffer to hold on file attr struct */ buf = malloc(sz); if (!buf) @@ -2153,6 +2172,9 @@ static struct evsel *read_event_desc(struct feat_fd *ff) msz = sz; for (i = 0, evsel = events; i < nre; evsel++, i++) { + struct perf_event_attr *attr = buf; + u32 attr_size; + evsel->core.idx = i; /* @@ -2162,6 +2184,32 @@ static struct evsel *read_event_desc(struct feat_fd *ff) if (__do_read(ff, buf, sz)) goto error; + /* Reject before attr_swap to prevent OOB via bswap_safe() */ + attr_size = ff->ph->needs_swap ? bswap_32(attr->size) : attr->size; + /* ABI0: size == 0 means the producer didn't set it */ + if (!attr_size) { + attr_size = PERF_ATTR_SIZE_VER0; + /* + * Write back so free_event_desc() doesn't + * treat this event as the end-of-array sentinel + * (it iterates while attr.size != 0). + * + * Only for native — the swap path must NOT + * write native-endian VER0 here because + * perf_event__attr_swap() would re-swap it + * to 0x40000000, defeating bswap_safe() bounds. + * perf_event__attr_swap() has its own ABI0 + * fallback that sets VER0 after swapping. + */ + if (!ff->ph->needs_swap) + attr->size = attr_size; + } + if (attr_size < PERF_ATTR_SIZE_VER0 || attr_size > sz) { + pr_err("Event %d attr.size (%u) invalid (min: %d, max: %u)\n", + i, attr_size, PERF_ATTR_SIZE_VER0, sz); + goto error; + } + if (ff->ph->needs_swap) perf_event__attr_swap(buf); @@ -2183,6 +2231,12 @@ static struct evsel *read_event_desc(struct feat_fd *ff) if (!nr) continue; + /* Prevent oversized allocation from crafted nr */ + if (nr > (ff->size - ff->offset) / sizeof(*id)) { + pr_err("Event %d: id count %u exceeds remaining section\n", i, nr); + goto error; + } + id = calloc(nr, sizeof(*id)); if (!id) goto error; From 8e8b52ad5ab500a52016b519ce54c1822b5951df Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 2 May 2026 14:41:46 -0300 Subject: [PATCH 1579/2758] perf header: Validate bitmap size before allocating in do_read_bitmap() [ Upstream commit 3669697bda41c562d90eb38f54881cc02ef3d51c ] do_read_bitmap() reads a u64 bit count from the file and passes it to bitmap_zalloc() without checking it against the remaining section size. A crafted perf.data could trigger a large allocation that would only fail later when the per-element reads exceed section bounds. Additionally, bitmap_zalloc() takes an int parameter, so a crafted size with bits set above bit 31 (e.g. 0x100000040) would pass the section bounds check but truncate when passed to bitmap_zalloc(), allocating a much smaller buffer than the subsequent read loop expects. Reject size values that exceed INT_MAX, and check that the data needed (BITS_TO_U64(size) u64 values) fits in the remaining section before allocating. Switch from bitmap_zalloc() to calloc() of u64 units so the allocation size matches the u64 read/write granularity and avoids unsigned long vs u64 mismatch on 32-bit architectures. Fix do_write_bitmap() to use memcpy to read u64-sized chunks from the unsigned long bitmap, preventing out-of-bounds reads on 32-bit systems where sizeof(unsigned long) is 4 but the bitmap is stored in u64 units. Fix process_mem_topology() minimum section size: the check used nr * 2 * sizeof(u64) per node, but do_read_bitmap() reads an additional u64 for the bitmap size, so the minimum is 3 * sizeof(u64). Fix memory leak in process_mem_topology() error paths: replace free(nodes) with memory_node__delete_nodes() to free per-node bitmaps allocated by do_read_bitmap(). Currently used by process_mem_topology() for HEADER_MEM_TOPOLOGY. Fixes: a881fc56038a ("perf header: Sanity check HEADER_MEM_TOPOLOGY") Closes: https://lore.kernel.org/linux-perf-users/20260414224622.2AE69C19425@smtp.kernel.org/ Closes: https://lore.kernel.org/linux-perf-users/20260410223242.DD76FC19421@smtp.kernel.org/ Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/header.c | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 30e5e50ab98b8..7b641a569af13 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -158,15 +158,25 @@ int do_write(struct feat_fd *ff, const void *buf, size_t size) /* Return: 0 if succeeded, -ERR if failed. */ static int do_write_bitmap(struct feat_fd *ff, unsigned long *set, u64 size) { - u64 *p = (u64 *) set; + size_t byte_size = BITS_TO_LONGS(size) * sizeof(unsigned long); int i, ret; ret = do_write(ff, &size, sizeof(size)); if (ret < 0) return ret; + /* + * The on-disk format uses u64 elements, but the in-memory bitmap + * uses unsigned long, which is only 4 bytes on 32-bit architectures. + * Copy with bounded size so the last element doesn't read past the + * bitmap allocation when BITS_TO_LONGS(size) is odd. + */ for (i = 0; (u64) i < BITS_TO_U64(size); i++) { - ret = do_write(ff, p + i, sizeof(*p)); + u64 val = 0; + size_t off = i * sizeof(val); + + memcpy(&val, (char *)set + off, min(sizeof(val), byte_size - off)); + ret = do_write(ff, &val, sizeof(val)); if (ret < 0) return ret; } @@ -297,7 +307,20 @@ static int do_read_bitmap(struct feat_fd *ff, unsigned long **pset, u64 *psize) if (ret) return ret; - set = bitmap_zalloc(size); + /* Bitmap APIs use int for nbits; reject u64 values that truncate. */ + if (size > INT_MAX || + BITS_TO_U64(size) > (ff->size - ff->offset) / sizeof(u64)) { + pr_debug("do_read_bitmap: size %" PRIu64 " exceeds section bounds\n", size); + return -1; + } + + /* + * bitmap_zalloc() allocates in unsigned long units, which are only + * 4 bytes on 32-bit architectures. The read loop below casts the + * buffer to u64 * and writes 8-byte elements, so allocate in u64 + * units to ensure the buffer is large enough. + */ + set = calloc(BITS_TO_U64(size), sizeof(u64)); if (!set) return -ENOMEM; @@ -3411,7 +3434,8 @@ static int process_mem_topology(struct feat_fd *ff, return -1; } - if (ff->size < 3 * sizeof(u64) + nr * 2 * sizeof(u64)) { + /* Per node: node_id(u64) + mem_size(u64) + bitmap_nr_bits(u64) */ + if (ff->size < 3 * sizeof(u64) + nr * 3 * sizeof(u64)) { pr_err("Invalid HEADER_MEM_TOPOLOGY: section too small (%zu) for %llu nodes\n", ff->size, (unsigned long long)nr); return -1; @@ -3446,7 +3470,7 @@ static int process_mem_topology(struct feat_fd *ff, out: if (ret) - free(nodes); + memory_node__delete_nodes(nodes, nr); return ret; } From 2413ede67e398e2a3b953b8c7bb65f5ebfba631e Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Tue, 5 May 2026 09:31:26 +0200 Subject: [PATCH 1580/2758] iio: light: si1133: reset counter to prevent race condition [ Upstream commit 0a5f45ed2342aabae1e32c72558d15be28940a95 ] Sashiko reported a potential race condition happening when the driver returns an errno after a timeout in the si1133_command() function. The premature exit causes the hardware and software counters to become out of sync by not updating data->rsp_seq, therefore the internal hardware counter keeps incrementing. Fix this by adding a call to si1133_cmd_reset_counter() before returning from timeout. Fixes: e01e7eaf37d8 ("iio: light: introduce si1133") Reported-by: sashiko-bot Closes: https://sashiko.dev/#/message/20260428-si1133-checkup-v2-5-70ad14bfefe2%40gmail.com Assisted-by: gemini:gemini-3.1-pro-preview Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron Signed-off-by: Sasha Levin --- drivers/iio/light/si1133.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/iio/light/si1133.c b/drivers/iio/light/si1133.c index 44fa152dbd24c..c88c79202be2e 100644 --- a/drivers/iio/light/si1133.c +++ b/drivers/iio/light/si1133.c @@ -427,6 +427,11 @@ static int si1133_command(struct si1133_data *data, u8 cmd) dev_warn(dev, "Failed to read command 0x%02x, ret=%d\n", cmd, err); + /* + * Reset counter on err to prevent software and hardware + * counters being out of sync. + */ + si1133_cmd_reset_counter(data); goto out; } } From 7f167853ef3c04a1cf303bd743de303381ac1b60 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Tue, 5 May 2026 09:31:27 +0200 Subject: [PATCH 1581/2758] iio: light: si1133: prevent race condition on timeout [ Upstream commit 8c50a95ceb230d17801758a9e41ffbbbe46f8b4d ] Sashiko reported a bug where the si1133_command exits on timeout without halting the sensor or masking the interrupt. If the sensor completes the command later, any subsequent command to the sensor will cause the IRQ handler to complete immediately, returning stale data to the driver all while the command hasn't finished yet, shifting all potential reads in the future. Fix this by masking the IRQ if wait_for_completion_timeout() fails. When initiating a new command, do a dummy read of the IRQ_STATUS register and turn the IRQ back on. Fixes: e01e7eaf37d8 ("iio: light: introduce si1133") Reported-by: sashiko-bot Closes: https://sashiko.dev/#/message/20260428-si1133-checkup-v2-5-70ad14bfefe2%40gmail.com Assisted-by: gemini:gemini-3.1-pro-preview Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron Signed-off-by: Sasha Levin --- drivers/iio/light/si1133.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/iio/light/si1133.c b/drivers/iio/light/si1133.c index c88c79202be2e..bf7bf0f1631d4 100644 --- a/drivers/iio/light/si1133.c +++ b/drivers/iio/light/si1133.c @@ -395,8 +395,14 @@ static int si1133_command(struct si1133_data *data, u8 cmd) expected_seq = (data->rsp_seq + 1) & SI1133_MAX_CMD_CTR; - if (cmd == SI1133_CMD_FORCE) + if (cmd == SI1133_CMD_FORCE) { + /* Flush pending IRQs from a previous timeout. */ + regmap_read(data->regmap, SI1133_REG_IRQ_STATUS, &resp); + regmap_write(data->regmap, SI1133_REG_IRQ_ENABLE, + SI1133_IRQ_CHANNEL_ENABLE); + reinit_completion(&data->completion); + } err = regmap_write(data->regmap, SI1133_REG_COMMAND, cmd); if (err) { @@ -409,6 +415,7 @@ static int si1133_command(struct si1133_data *data, u8 cmd) /* wait for irq */ if (!wait_for_completion_timeout(&data->completion, msecs_to_jiffies(SI1133_COMPLETION_TIMEOUT_MS))) { + regmap_write(data->regmap, SI1133_REG_IRQ_ENABLE, 0); err = -ETIMEDOUT; goto out; } From fb27ebf81136e796c7b719303ef6fd7e1ae5d488 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Fri, 15 May 2026 12:28:23 +0200 Subject: [PATCH 1582/2758] iio: magnetometer: ak8975: fix potential kernel stack memory leak [ Upstream commit a9a00d727b7bbc5e913a919530a9dd468935bf95 ] Currently in the AK8975 driver there are four instances where potential uninitialized kernel stack memory leaks can occur. If i2c_smbus_read_i2c_block_data_or_emulated() returns a value less than the size of the buffer, uninitialized bytes are retained in the buffer and later the buffer is passed on to IIO buffers, potentially leaking memory to userspace. Fix this by adding checks whether the return value of the function is equal to the size of the buffer and subsequently if the value is lesser than zero to distinguish from a returned error code. Fixes: bc11ca4a0b84 ("iio:magnetometer:ak8975: triggered buffer support") Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260513-ak8975-fix-v1-1-104ea605dd54%40gmail.com Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron Signed-off-by: Sasha Levin --- drivers/iio/magnetometer/ak8975.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index b3468756bdf1e..ddf6dcc6fbdda 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -495,6 +495,10 @@ static int ak8975_who_i_am(struct i2c_client *client, dev_err(&client->dev, "Error reading WIA\n"); return ret; } + if (ret != sizeof(wia_val)) { + dev_err(&client->dev, "Error reading WIA\n"); + return -EIO; + } if (wia_val[0] != AK8975_DEVICE_ID) return -ENODEV; @@ -619,6 +623,10 @@ static int ak8975_setup(struct i2c_client *client) dev_err(&client->dev, "Not able to read asa data\n"); return ret; } + if (ret != sizeof(data->asa)) { + dev_err(&client->dev, "Error reading asa data\n"); + return -EIO; + } /* After reading fuse ROM data set power-down mode */ ret = ak8975_set_mode(data, POWER_DOWN); @@ -758,6 +766,10 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val) sizeof(rval), (u8*)&rval); if (ret < 0) goto exit; + if (ret != sizeof(rval)) { + ret = -EIO; + goto exit; + } /* Read out ST2 for release lock on measurement data. */ ret = i2c_smbus_read_byte_data(client, data->def->ctrl_regs[ST2]); @@ -874,6 +886,8 @@ static void ak8975_fill_buffer(struct iio_dev *indio_dev) (u8 *)fval); if (ret < 0) goto unlock; + if (ret != sizeof(fval)) + goto unlock; mutex_unlock(&data->lock); From 3c374d33f1338dbb5676919c5194caa9c5aa1631 Mon Sep 17 00:00:00 2001 From: Guilherme Ivo Bozi Date: Tue, 12 May 2026 21:31:50 -0300 Subject: [PATCH 1583/2758] iio: adc: xilinx-ams: fix out-of-bounds channel lookup in event handling [ Upstream commit 947eb6f0a274f8b15a0248051a65b069effd5057 ] ams_event_to_channel() may return a pointer past the end of dev->channels when no matching scan_index is found. This can lead to invalid memory access in ams_handle_event(). Add a bounds check in ams_event_to_channel() and return NULL when no channel is found. Also guard the caller to safely handle this case. Fixes: d5c70627a794 ("iio: adc: Add Xilinx AMS driver") Signed-off-by: Guilherme Ivo Bozi Reviewed-by: Salih Erim Tested-by: Salih Erim Signed-off-by: Jonathan Cameron Signed-off-by: Sasha Levin --- drivers/iio/adc/xilinx-ams.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/iio/adc/xilinx-ams.c b/drivers/iio/adc/xilinx-ams.c index 124470c925297..6191cd1b29a51 100644 --- a/drivers/iio/adc/xilinx-ams.c +++ b/drivers/iio/adc/xilinx-ams.c @@ -871,6 +871,9 @@ static const struct iio_chan_spec *ams_event_to_channel(struct iio_dev *dev, if (dev->channels[i].scan_index == scan_index) break; + if (i == dev->num_channels) + return NULL; + return &dev->channels[i]; } @@ -1012,6 +1015,8 @@ static void ams_handle_event(struct iio_dev *indio_dev, u32 event) const struct iio_chan_spec *chan; chan = ams_event_to_channel(indio_dev, event); + if (!chan) + return; if (chan->type == IIO_TEMP) { /* From f3d413e701c5e54bef736b638967724dda880365 Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Tue, 5 May 2026 23:16:31 +0530 Subject: [PATCH 1584/2758] iio: accel: mma8452: handle I2C read error(s) in mma8452_read() [ Upstream commit 5bdff291d20c31b365d9ddfe9c426fbfb41da5bb ] Currently, If i2c_smbus_read_i2c_block_data() fails but mma8452_set_runtime_pm_state() succeeds, mma8452_read() returns 0. As a result, the caller mma8452_read_raw() assumes the read was successful and proceeds to use a buffer containing uninitialized stack memory. Add proper checking of the I2C read return value and propagate errors to the caller. Fixes: 96c0cb2bbfe0 ("iio: mma8452: add support for runtime power management") Signed-off-by: Sanjay Chitroda Signed-off-by: Jonathan Cameron Signed-off-by: Sasha Levin --- drivers/iio/accel/mma8452.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 15172ba2972c5..cefc7cf4bd835 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -252,6 +252,8 @@ static int mma8452_read(struct mma8452_data *data, __be16 buf[3]) ret = i2c_smbus_read_i2c_block_data(data->client, MMA8452_OUT_X, 3 * sizeof(__be16), (u8 *)buf); + if (ret < 0) + return ret; ret = mma8452_set_runtime_pm_state(data->client, false); From 8c00cabb19824fe108f83401117b356eaade97ec Mon Sep 17 00:00:00 2001 From: Aldo Conte Date: Fri, 22 May 2026 14:34:12 +0200 Subject: [PATCH 1585/2758] iio: tcs3472: power down chip on probe failure [ Upstream commit b39f3bb9f5580d19bc0c10dea9224d75fed45f1d ] If tcs3472_probe() fails after enabling the chip (by writing PON | AEN to the ENABLE register), the error paths return without powering down the device. Add an 'error_powerdown' label at the end of the cleanup chain that calls tcs3472_powerdown() to power down the chip. The existing label cascade is rerouted to fall through to the new label. Move tcs3472_powerdown() above tcs3472_probe() so the probe can call it without a forward declaration. Found by code inspection while reviewing the probe error paths in preparation for the devm_ conversion. Fixes: eb869ade30a6 ("iio: Add tcs3472 color light sensor driver") Signed-off-by: Aldo Conte Signed-off-by: Jonathan Cameron Signed-off-by: Sasha Levin --- drivers/iio/light/tcs3472.c | 38 +++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c index 12429a3261b38..849ca7885d719 100644 --- a/drivers/iio/light/tcs3472.c +++ b/drivers/iio/light/tcs3472.c @@ -440,6 +440,23 @@ static const struct iio_info tcs3472_info = { .attrs = &tcs3472_attribute_group, }; +static int tcs3472_powerdown(struct tcs3472_data *data) +{ + int ret; + u8 enable_mask = TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON; + + mutex_lock(&data->lock); + + ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE, + data->enable & ~enable_mask); + if (!ret) + data->enable &= ~enable_mask; + + mutex_unlock(&data->lock); + + return ret; +} + static int tcs3472_probe(struct i2c_client *client) { struct tcs3472_data *data; @@ -513,7 +530,7 @@ static int tcs3472_probe(struct i2c_client *client) ret = iio_triggered_buffer_setup(indio_dev, NULL, tcs3472_trigger_handler, NULL); if (ret < 0) - return ret; + goto error_powerdown; if (client->irq) { ret = request_threaded_irq(client->irq, NULL, @@ -536,23 +553,8 @@ static int tcs3472_probe(struct i2c_client *client) free_irq(client->irq, indio_dev); buffer_cleanup: iio_triggered_buffer_cleanup(indio_dev); - return ret; -} - -static int tcs3472_powerdown(struct tcs3472_data *data) -{ - int ret; - u8 enable_mask = TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON; - - mutex_lock(&data->lock); - - ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE, - data->enable & ~enable_mask); - if (!ret) - data->enable &= ~enable_mask; - - mutex_unlock(&data->lock); - +error_powerdown: + tcs3472_powerdown(data); return ret; } From b7ef2eb239369c123bbac875a861c5e38892042c Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Fri, 29 May 2026 00:20:51 -0400 Subject: [PATCH 1586/2758] clk: at91: keep securam node alive while mapping it [ Upstream commit 22fa1c39ba6fe726b547c877c924379b7fee260a ] pmc_register_ops() gets an owned reference to the "atmel,sama5d2-securam" node with of_find_compatible_node(). The success path dropped that reference before passing the node to of_iomap(), leaving of_iomap() to consume a node pointer after the caller had released its reference. Move of_node_put() after of_iomap() so the node remains referenced for the mapping operation. The unavailable-node error path already releases the reference. Fixes: 4d21be864092 ("clk: at91: pmc: execute suspend/resume only for backup mode") Signed-off-by: Yuho Choi Reviewed-by: Alexandre Belloni Link: https://patch.msgid.link/20260529042051.1626978-1-dbgh9129@gmail.com Signed-off-by: Claudiu Beznea Signed-off-by: Sasha Levin --- drivers/clk/at91/pmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index b618a5e00b006..03a6c31d6aa8b 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -180,9 +180,9 @@ static int __init pmc_register_ops(void) of_node_put(np); return -ENODEV; } - of_node_put(np); at91_pmc_backup_suspend = of_iomap(np, 0); + of_node_put(np); if (!at91_pmc_backup_suspend) { pr_warn("%s(): unable to map securam\n", __func__); return -ENOMEM; From 83d87cbfa3aa042c8fe61f93583687cf7af4d634 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sat, 30 May 2026 17:01:50 -0700 Subject: [PATCH 1587/2758] HID: logitech-hidpp: remove excess kernel-doc member in hidpp_scroll_counter [ Upstream commit f22a5db8a7d38152556f230d6d68e59dbc27971b ] The @dev member described in the kernel-doc does not exist in the struct. Remove the stale entry. Fixes: 0610430e3dea ("HID: logitech-hidpp: add input_device ptr to struct hidpp_device") Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Signed-off-by: Benjamin Tissoires Signed-off-by: Sasha Levin --- drivers/hid/hid-logitech-hidpp.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index ccbf28869a968..1990ba5b26ea6 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -164,7 +164,6 @@ struct hidpp_battery { /** * struct hidpp_scroll_counter - Utility class for processing high-resolution * scroll events. - * @dev: the input device for which events should be reported. * @wheel_multiplier: the scalar multiplier to be applied to each wheel event * @remainder: counts the number of high-resolution units moved since the last * low-resolution event (REL_WHEEL or REL_HWHEEL) was sent. Should From d823ab4592b34ee00100d253260b43c8021b7732 Mon Sep 17 00:00:00 2001 From: Zhan Xusheng Date: Tue, 26 May 2026 10:20:33 +0800 Subject: [PATCH 1588/2758] docs: changes.rst: restore pahole 1.26 minimum (regressed by sort) [ Upstream commit 2c1ccd9a1d786503086e83fe83e5c3b3c953b70e ] Commit 9edd04c4189e ("docs: Raise minimum pahole version to 1.26 for KF_IMPLICIT_ARGS kfuncs") raised the minimum required pahole version from 1.22 to 1.26 in the requirements table and added a paragraph explaining the failure mode for distributions still shipping pahole v1.25 (e.g. Ubuntu 24.04 LTS). The next day, commit ece7e57afd51 ("docs: changes.rst and ver_linux: sort the lists") came through a different tree (docs vs sched_ext) and re-flowed the table alphabetically, but its base did not include 9edd04c4189e. When the two commits met in mainline, the textual rewrite of the table won and the version bump was lost. The added "Since Linux 7.0..." paragraph also disappeared. The result is that changes.rst on master (v7.1-rc5) lists pahole 1.22 again, even though sched_ext kfuncs annotated with KF_IMPLICIT_ARGS genuinely require v1.26 to produce a correct vmlinux BTF. Users on distributions with pahole v1.25 hit "func_proto incompatible with vmlinux" when loading any sched_ext BPF program (scx_simple, scx_qmap, ...) and have no documentation pointing them at the version gap. Restore both changes from 9edd04c4189e. Fixes: ece7e57afd51 ("docs: changes.rst and ver_linux: sort the lists") Signed-off-by: Zhan Xusheng Signed-off-by: Jonathan Corbet Message-ID: <20260526022033.1301884-1-zhanxusheng@xiaomi.com> Signed-off-by: Sasha Levin --- Documentation/process/changes.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Documentation/process/changes.rst b/Documentation/process/changes.rst index 9a99037270ff2..a4db8f7b3afb7 100644 --- a/Documentation/process/changes.rst +++ b/Documentation/process/changes.rst @@ -53,7 +53,7 @@ mcelog 0.6 mcelog --version mkimage (optional) 2017.01 mkimage --version nfs-utils 1.0.5 showmount --version openssl & libcrypto 1.0.0 openssl version -pahole 1.22 pahole --version +pahole 1.26 pahole --version pcmciautils 004 pccardctl -V PPP 2.4.0 pppd --version procps 3.2.0 ps --version @@ -147,6 +147,11 @@ Since Linux 5.2, if CONFIG_DEBUG_INFO_BTF is selected, the build system generates BTF (BPF Type Format) from DWARF in vmlinux, a bit later from kernel modules as well. This requires pahole v1.22 or later. +Since Linux 7.0, kfuncs annotated with KF_IMPLICIT_ARGS require pahole v1.26 +or later. Without it, such kfuncs will have incorrect BTF prototypes in +vmlinux, causing BPF programs to fail to load with a "func_proto incompatible +with vmlinux" error. Many sched_ext kfuncs are affected. + It is found in the 'dwarves' or 'pahole' distro packages or from https://fedorapeople.org/~acme/dwarves/. From 0b4739fc72dbcd4387198a245b249aa2eb3a9203 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Mon, 11 May 2026 02:59:09 +0000 Subject: [PATCH 1589/2758] clk: spacemit: k3: Switch to pll2_d6 as parent for PCIe clock [ Upstream commit d8a4cef90b1a4ae9196a5bfba683eb9a0c75acdc ] According to SpacemiT updated docs, the PCIe master and slave clock's parent is the pll2_d6 clock, so fix it. Fixes: e371a77255b8 ("clk: spacemit: k3: add the clock tree") Link: https://patch.msgid.link/20260511-06-pci-clk-fix-v2-1-c9a5e563bab3@kernel.org Signed-off-by: Yixun Lan Signed-off-by: Sasha Levin --- drivers/clk/spacemit/ccu-k3.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/clk/spacemit/ccu-k3.c b/drivers/clk/spacemit/ccu-k3.c index bb8b75bdbdb30..1a53b14739fed 100644 --- a/drivers/clk/spacemit/ccu-k3.c +++ b/drivers/clk/spacemit/ccu-k3.c @@ -947,16 +947,16 @@ static const struct clk_parent_data edp1_pclk_parents[] = { }; CCU_MUX_GATE_DEFINE(edp1_pxclk, edp1_pclk_parents, APMU_LCD_EDP_CTRL, 18, 1, BIT(17), 0); -CCU_GATE_DEFINE(pciea_mstr_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_A, BIT(2), 0); -CCU_GATE_DEFINE(pciea_slv_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_A, BIT(1), 0); -CCU_GATE_DEFINE(pcieb_mstr_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_B, BIT(2), 0); -CCU_GATE_DEFINE(pcieb_slv_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_B, BIT(1), 0); -CCU_GATE_DEFINE(pciec_mstr_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_C, BIT(2), 0); -CCU_GATE_DEFINE(pciec_slv_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_C, BIT(1), 0); -CCU_GATE_DEFINE(pcied_mstr_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_D, BIT(2), 0); -CCU_GATE_DEFINE(pcied_slv_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_D, BIT(1), 0); -CCU_GATE_DEFINE(pciee_mstr_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_E, BIT(2), 0); -CCU_GATE_DEFINE(pciee_slv_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_E, BIT(1), 0); +CCU_GATE_DEFINE(pciea_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_A, BIT(2), 0); +CCU_GATE_DEFINE(pciea_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_A, BIT(1), 0); +CCU_GATE_DEFINE(pcieb_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_B, BIT(2), 0); +CCU_GATE_DEFINE(pcieb_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_B, BIT(1), 0); +CCU_GATE_DEFINE(pciec_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_C, BIT(2), 0); +CCU_GATE_DEFINE(pciec_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_C, BIT(1), 0); +CCU_GATE_DEFINE(pcied_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_D, BIT(2), 0); +CCU_GATE_DEFINE(pcied_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_D, BIT(1), 0); +CCU_GATE_DEFINE(pciee_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_E, BIT(2), 0); +CCU_GATE_DEFINE(pciee_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_E, BIT(1), 0); static const struct clk_parent_data emac_1588_parents[] = { CCU_PARENT_NAME(vctcxo_24m), From 3dcdf8ddb5093a09c53cf30843f117ca484c743d Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Mon, 11 May 2026 02:59:10 +0000 Subject: [PATCH 1590/2758] clk: spacemit: k3: Fix PCIe clock register offset [ Upstream commit 2f20c859a82a291483a8b3f01cbfbb1642782a14 ] The offset of PCIe Clock CTRL register for port B and C controller was wrongly swapped, correct it here. Fixes: 091d19cc2401 ("clk: spacemit: k3: extract common header") Acked-by: Conor Dooley Link: https://patch.msgid.link/20260511-06-pci-clk-fix-v2-2-c9a5e563bab3@kernel.org Signed-off-by: Yixun Lan Signed-off-by: Sasha Levin --- include/soc/spacemit/k3-syscon.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/soc/spacemit/k3-syscon.h b/include/soc/spacemit/k3-syscon.h index 0299bea065a05..a68255dd641f3 100644 --- a/include/soc/spacemit/k3-syscon.h +++ b/include/soc/spacemit/k3-syscon.h @@ -168,8 +168,8 @@ #define APMU_CPU_C2_CLK_CTRL 0x394 #define APMU_CPU_C3_CLK_CTRL 0x208 #define APMU_PCIE_CLK_RES_CTRL_A 0x1f0 -#define APMU_PCIE_CLK_RES_CTRL_B 0x1c8 -#define APMU_PCIE_CLK_RES_CTRL_C 0x1d0 +#define APMU_PCIE_CLK_RES_CTRL_B 0x1d0 +#define APMU_PCIE_CLK_RES_CTRL_C 0x1c8 #define APMU_PCIE_CLK_RES_CTRL_D 0x1e0 #define APMU_PCIE_CLK_RES_CTRL_E 0x1e8 #define APMU_EMAC0_CLK_RES_CTRL 0x3e4 From 41081202eb823f5b27ff164b12010b24428100ad Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Thu, 30 Apr 2026 14:30:13 +0200 Subject: [PATCH 1591/2758] fs/ntfs3: add bounds check to run_get_highest_vcn() [ Upstream commit bb11485a87fbb2254b62cfed630b699d50e57da8 ] run_get_highest_vcn() parses a packed NTFS mapping-pairs buffer without any length bound, relying solely on a 0x00 terminator to stop. A crafted $LogFile UpdateMappingPairs record whose embedded attribute contains mapping-pairs runs without a terminator causes the function to read past the slab allocation, triggering a KASAN slab-out-of-bounds read on mount. The sibling function run_unpack() received an analogous bounds-check in commit b62567bca474 ("ntfs3: add buffer boundary checks to run_unpack()"), but run_get_highest_vcn() was missed. Take a run_buf_size parameter and reject any run header whose payload would extend past the buffer end, mirroring the pattern used by run_unpack(). The caller in fslog.c passes the remaining attribute bytes after the mapping-pairs offset. KASAN report (on mainline v7.1 merge window HEAD): BUG: KASAN: slab-out-of-bounds in run_get_highest_vcn+0x3c0/0x410 Read of size 1 at addr ffff88800e2d5400 by task mount/72 Call Trace: run_get_highest_vcn+0x3c0/0x410 do_action.isra.0+0x3ba8/0x7b50 log_replay+0x9ddd/0x10200 ntfs_loadlog_and_replay+0x4ad/0x610 ntfs_fill_super+0x214a/0x4540 Fixes: b62567bca474 ("ntfs3: add buffer boundary checks to run_unpack()") Signed-off-by: Jaeyeong Lee Signed-off-by: Konstantin Komarov Signed-off-by: Sasha Levin --- fs/ntfs3/fslog.c | 5 ++++- fs/ntfs3/ntfs_fs.h | 3 ++- fs/ntfs3/run.c | 9 +++++++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index ca78cfe2b37f8..0504e5e8a5087 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -3368,7 +3368,10 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, memmove(Add2Ptr(attr, aoff), data, dlen); if (run_get_highest_vcn(le64_to_cpu(attr->nres.svcn), - attr_run(attr), &t64)) { + attr_run(attr), + le32_to_cpu(attr->size) - + le16_to_cpu(attr->nres.run_off), + &t64)) { goto dirty_vol; } diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h index bbf3b6a1dcbee..d53febc2559c0 100644 --- a/fs/ntfs3/ntfs_fs.h +++ b/fs/ntfs3/ntfs_fs.h @@ -877,7 +877,8 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, #else #define run_unpack_ex run_unpack #endif -int run_get_highest_vcn(CLST vcn, const u8 *run_buf, u64 *highest_vcn); +int run_get_highest_vcn(CLST vcn, const u8 *run_buf, size_t run_buf_size, + u64 *highest_vcn); int run_clone(const struct runs_tree *run, struct runs_tree *new_run); bool run_remove_range(struct runs_tree *run, CLST vcn, CLST len, CLST *done); CLST run_len(const struct runs_tree *run); diff --git a/fs/ntfs3/run.c b/fs/ntfs3/run.c index 1ce7d92fb2748..19aa044fd1fcc 100644 --- a/fs/ntfs3/run.c +++ b/fs/ntfs3/run.c @@ -1205,18 +1205,23 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, * Return the highest vcn from a mapping pairs array * it used while replaying log file. */ -int run_get_highest_vcn(CLST vcn, const u8 *run_buf, u64 *highest_vcn) +int run_get_highest_vcn(CLST vcn, const u8 *run_buf, size_t run_buf_size, + u64 *highest_vcn) { + const u8 *run_last = run_buf + run_buf_size; u64 vcn64 = vcn; u8 size_size; - while ((size_size = *run_buf & 0xF)) { + while (run_buf < run_last && (size_size = *run_buf & 0xF)) { u8 offset_size = *run_buf++ >> 4; u64 len; if (size_size > 8 || offset_size > 8) return -EINVAL; + if (run_buf + size_size + offset_size > run_last) + return -EINVAL; + len = run_unpack_s64(run_buf, size_size, 0); if (!len) return -EINVAL; From 1f6111ad30d2e967e7b8310e23d7647484ccd09c Mon Sep 17 00:00:00 2001 From: Zhan Xusheng Date: Fri, 8 May 2026 17:52:45 +0800 Subject: [PATCH 1592/2758] fs/ntfs3: fix wrong LCN in run_remove_range() when splitting a run [ Upstream commit 36c7276816ed4266c155b71b1fa747b2785f23f7 ] When run_remove_range() removes a middle portion of a non-sparse run, it splits the run into head and tail parts. The tail is inserted via run_add_entry() but uses the original r->lcn as its starting LCN instead of advancing it by the split offset. For example, removing VCN range [10, 20) from a run {vcn=0, lcn=100, len=30} should produce: {vcn=0, lcn=100, len=10} (head) {vcn=20, lcn=120, len=10} (tail, lcn advanced by 20) But the current code produces: {vcn=0, lcn=100, len=10} {vcn=20, lcn=100, len=10} (wrong: points to same physical clusters) This creates overlapping physical mappings in the in-memory run tree, which can corrupt cluster allocation decisions and lead to data corruption. The correct pattern is already used in run_insert_range(): CLST lcn2 = r->lcn == SPARSE_LCN ? SPARSE_LCN : (r->lcn + len1); Apply the same logic in run_remove_range(). Fixes: 10d7c95af043 ("fs/ntfs3: add delayed-allocation (delalloc) support") Signed-off-by: Zhan Xusheng Signed-off-by: Konstantin Komarov Signed-off-by: Sasha Levin --- fs/ntfs3/run.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/ntfs3/run.c b/fs/ntfs3/run.c index 19aa044fd1fcc..ad7db67514ef7 100644 --- a/fs/ntfs3/run.c +++ b/fs/ntfs3/run.c @@ -1297,9 +1297,12 @@ bool run_remove_range(struct runs_tree *run, CLST vcn, CLST len, CLST *done) if (r_end > end) { /* Remove a middle part, split. */ + CLST tail_lcn = r->lcn == SPARSE_LCN ? + SPARSE_LCN : (r->lcn + (end - r->vcn)); + *done += len; r->len = d; - return run_add_entry(run, end, r->lcn, r_end - end, + return run_add_entry(run, end, tail_lcn, r_end - end, false); } /* Remove tail of run .*/ From ff825bf0521f6da2f30878cbad18ab7b341bc31b Mon Sep 17 00:00:00 2001 From: Helen Koike Date: Wed, 6 May 2026 14:08:22 -0300 Subject: [PATCH 1593/2758] fs/ntfs3: call _ntfs_bad_inode() when failing to rename [ Upstream commit e8ed78f40eecd0176fda71d673f6957c98e7ffbe ] It is safe to call _ntfs_bad_inode on live inodes since: commit 519b078998ce ("fs/ntfs3: Exclude call make_bad_inode for live nodes.") The WARN_ON was added when it wasn't safe by: commit d99208b91933 ("fs/ntfs3: cancle set bad inode after removing name fails") Replace the WARN_ON with a call to _ntfs_bad_inode() to prevent further operations on the inconsistent inode. Reported-by: syzbot+4d8e30dbafb5c1260479@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=4d8e30dbafb5c1260479 Fixes: 519b078998ce ("fs/ntfs3: Exclude call make_bad_inode for live nodes.") Signed-off-by: Helen Koike Signed-off-by: Konstantin Komarov Signed-off-by: Sasha Levin --- fs/ntfs3/frecord.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c index 7b035da63c121..78eb065c7e431 100644 --- a/fs/ntfs3/frecord.c +++ b/fs/ntfs3/frecord.c @@ -2800,8 +2800,8 @@ int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni, err = ni_add_name(new_dir_ni, ni, new_de); if (!err) { err = ni_remove_name(dir_ni, ni, de, &de2, &undo); - WARN_ON(err && - ni_remove_name(new_dir_ni, ni, new_de, &de2, &undo)); + if (err && ni_remove_name(new_dir_ni, ni, new_de, &de2, &undo)) + _ntfs_bad_inode(&ni->vfs_inode); } /* From 3cd2212012c06d2b1fd03a6bf84f6202ab70056c Mon Sep 17 00:00:00 2001 From: Mihai Brodschi Date: Mon, 11 May 2026 20:19:04 +0300 Subject: [PATCH 1594/2758] ntfs3: Allocate iomap inline_data using alloc_page [ Upstream commit 70d3855594cf6e8791970714b65cac3202d6160e ] This fixes a BUG reported in iomap_write_end_inline: iomap_inline_data_valid checks that the inline_data fits within a page. If the inline_data is allocated with kmemdup there's no guarantee that it's page-aligned, so the check sometimes fails. Allocate it with alloc_page to ensure it's page-aligned. Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221446 Fixes: 099ef9ab9203 ("fs/ntfs3: implement iomap-based file operations") Signed-off-by: Mihai Brodschi Signed-off-by: Konstantin Komarov Signed-off-by: Sasha Levin --- fs/ntfs3/attrib.c | 10 +++++++--- fs/ntfs3/inode.c | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c index e61c5bf7e27e4..ff0bf45759480 100644 --- a/fs/ntfs3/attrib.c +++ b/fs/ntfs3/attrib.c @@ -1004,6 +1004,7 @@ int attr_data_get_block_locked(struct ntfs_inode *ni, CLST vcn, CLST clen, struct ATTRIB *attr, *attr_b; struct ATTR_LIST_ENTRY *le, *le_b; struct mft_inode *mi, *mi_b; + struct page *page; CLST hint, svcn, to_alloc, evcn1, next_svcn, asize, end, vcn0; CLST alloc, evcn; unsigned fr; @@ -1042,10 +1043,13 @@ int attr_data_get_block_locked(struct ntfs_inode *ni, CLST vcn, CLST clen, *lcn = RESIDENT_LCN; *len = data_size; if (res && data_size) { - *res = kmemdup(resident_data(attr_b), data_size, - GFP_KERNEL); - if (!*res) + page = alloc_page(GFP_KERNEL); + if (!page) { err = -ENOMEM; + } else { + *res = page_address(page); + memcpy(*res, resident_data(attr_b), data_size); + } } goto out; } diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c index 42af1abe17f88..031f85fd53d4e 100644 --- a/fs/ntfs3/inode.c +++ b/fs/ntfs3/inode.c @@ -801,7 +801,7 @@ static int ntfs_iomap_begin(struct inode *inode, loff_t offset, loff_t length, if (lcn == RESIDENT_LCN) { if (offset >= clen) { - kfree(res); + __free_page(virt_to_page(res)); if (flags & IOMAP_REPORT) { /* special code for report. */ return -ENOENT; @@ -921,7 +921,7 @@ static int ntfs_iomap_end(struct inode *inode, loff_t pos, loff_t length, out: if (iomap->type == IOMAP_INLINE) { - kfree(iomap->private); + __free_page(virt_to_page(iomap->private)); iomap->private = NULL; } From bc95e2f611926bd321d8ac39b2bb451923d96593 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 15 May 2026 11:09:50 +0200 Subject: [PATCH 1595/2758] ntfs3: avoid another -Wmaybe-uninitialized warning [ Upstream commit 1bf15dd17385e3730521d17e9e158c475cd7474b ] The ntfs3 specific -Wmaybe-uninitialized flag found one more false-postive, this time with gcc-10 on s390: fs/ntfs3/frecord.c: In function 'ni_expand_list': fs/ntfs3/frecord.c:1370:16: error: 'ins_attr' may be used uninitialized in this function [-Werror=maybe-uninitialized] Add an explicit NULL pointer check before using the pointer, and initialize it to NULL. Fixes: 48d9b57b169f ("fs/ntfs3: add a subset of W=1 warnings for stricter checks") Signed-off-by: Arnd Bergmann Signed-off-by: Konstantin Komarov Signed-off-by: Sasha Levin --- fs/ntfs3/frecord.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c index 78eb065c7e431..bb3348f256d99 100644 --- a/fs/ntfs3/frecord.c +++ b/fs/ntfs3/frecord.c @@ -1330,7 +1330,7 @@ int ni_expand_list(struct ntfs_inode *ni) { int err = 0; u32 asize, done = 0; - struct ATTRIB *attr, *ins_attr; + struct ATTRIB *attr, *ins_attr = NULL; struct ATTR_LIST_ENTRY *le; bool is_mft = ni->mi.rno == MFT_REC_MFT; struct MFT_REF ref; @@ -1363,7 +1363,7 @@ int ni_expand_list(struct ntfs_inode *ni) le16_to_cpu(attr->name_off), true, &ins_attr, NULL, NULL); - if (err) + if (err || !ins_attr) goto out; memcpy(ins_attr, attr, asize); From 668791009a218a46dfe89ee7054053eb10543e69 Mon Sep 17 00:00:00 2001 From: Jamie Nguyen Date: Tue, 19 May 2026 12:42:20 -0700 Subject: [PATCH 1596/2758] fs/ntfs3: fix mount failure on 64K page-size kernels [ Upstream commit b7a9125cac8645245d2473c6c0a50e338280ad23 ] On 64K page-size kernels, mounting NTFS volumes smaller than ~650 MB fails with EINVAL. The issue is in log_replay(): the initial log page size probe uses PAGE_SIZE (65536) instead of DefaultLogPageSize (4096) when PAGE_SIZE exceeds DefaultLogPageSize * 2. This makes norm_file_page() require the $LogFile to be at least 50 * 65536 = 3.2 MB, but mkfs.ntfs creates a $LogFile of only ~1.5 MB for a typical 300 MB volume. norm_file_page() returns 0 and the mount is rejected with EINVAL. On 4K kernels the #if guard evaluates to true, so use_default=true is passed and DefaultLogPageSize (4096) is used, requiring only ~200 KB. This path works fine. Fix this by always passing use_default=true, which forces the initial probe to use DefaultLogPageSize regardless of the kernel's PAGE_SIZE. This is safe because, after reading the on-disk restart area, log_replay() already re-adjusts log->page_size to match the volume's actual sys_page_size. Also fix read_log_page() to pass log->page_size instead of PAGE_SIZE to ntfs_fix_post_read(), matching the actual buffer size. Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Tested-by: Matthew R. Ochs Signed-off-by: Jamie Nguyen Signed-off-by: Konstantin Komarov Signed-off-by: Sasha Levin --- fs/ntfs3/fslog.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index 0504e5e8a5087..3c320e7a4c12f 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -1172,7 +1172,7 @@ static int read_log_page(struct ntfs_log *log, u32 vbo, goto out; if (page_buf->rhdr.sign != NTFS_FFFF_SIGNATURE) - ntfs_fix_post_read(&page_buf->rhdr, PAGE_SIZE, false); + ntfs_fix_post_read(&page_buf->rhdr, log->page_size, false); if (page_buf != *buffer) memcpy(*buffer, Add2Ptr(page_buf, page_off), bytes); @@ -3799,11 +3799,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) log->l_size = log->orig_file_size = ni->vfs_inode.i_size; /* Get the size of page. NOTE: To replay we can use default page. */ -#if PAGE_SIZE >= DefaultLogPageSize && PAGE_SIZE <= DefaultLogPageSize * 2 log->page_size = norm_file_page(PAGE_SIZE, &log->l_size, true); -#else - log->page_size = norm_file_page(PAGE_SIZE, &log->l_size, false); -#endif if (!log->page_size) { err = -EINVAL; goto out; From 994a42b890cef46cd576a3bf700f1dbd6bb21346 Mon Sep 17 00:00:00 2001 From: Srinivasan Shanmugam Date: Mon, 1 Jun 2026 19:42:15 +0530 Subject: [PATCH 1597/2758] drm/amd/display: Add missing kdoc for ALLM parameters [ Upstream commit d4c6ec729fb7a8bf8a27b19bd70a1b945ad93dac ] Add descriptions for the missing parameters for ALLMEnabled and ALLMValue to keep the function documentation synchronized with the function prototype mod_build_hf_vsif_infopacket(). Fixes the below with gcc W=1: ../display/modules/info_packet/info_packet.c:507 function parameter 'ALLMEnabled' not described in 'mod_build_hf_vsif_infopacket' ../display/modules/info_packet/info_packet.c:507 function parameter 'ALLMValue' not described in 'mod_build_hf_vsif_infopacket' Fixes: 3c2381b92cba ("drm/amd/display: add support for VSIP info packet") Cc: Roman Li Cc: Alex Hung Cc: Tom Chung Cc: Aurabindo Pillai Cc: Wayne Lin Cc: Nicholas Kazlauskas Cc: Bhawanpreet Lakha Signed-off-by: Srinivasan Shanmugam Reviewed-by: Alex Hung Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c index 00473c6284d5c..614db22d62f38 100644 --- a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c +++ b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c @@ -502,6 +502,8 @@ void mod_build_vsc_infopacket(const struct dc_stream_state *stream, * * @stream: contains data we may need to construct VSIF (i.e. timing_3d_format, etc.) * @info_packet: output structure where to store VSIF + * @ALLMEnabled: indicates whether ALLM HF-VSIF should be generated + * @ALLMValue: ALLM bit value to advertise in HF-VSIF */ void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream, struct dc_info_packet *info_packet) From 3ea71aa629a78b1aa9cb8e4704a86c4e0b9e02f9 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Wed, 3 Jun 2026 17:59:57 +0800 Subject: [PATCH 1598/2758] thunderbolt: debugfs: Fix margining error counter buffer leak [ Upstream commit 503c5ae1e72aa9ed91925dafa3d82ee2e992747f ] When USB4 lane margining debugfs write support is enabled, margining_error_counter_write() copies the user input with validate_and_copy_from_user(). This allocates a temporary page that is only needed while parsing the requested error counter mode. The function currently returns without freeing that page. This leaks one page per write to the error_counter debugfs file, including successful writes and writes that later fail while taking the domain lock or because software margining is not enabled. Free the temporary page once parsing has completed, and also before returning from the invalid-input path. Fixes: 10904df3f20c ("thunderbolt: Improve software receiver lane margining") Signed-off-by: Xu Rao Signed-off-by: Mika Westerberg Signed-off-by: Sasha Levin --- drivers/thunderbolt/debugfs.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c index 042f6a0d0f7f0..25f6ea6ea0947 100644 --- a/drivers/thunderbolt/debugfs.c +++ b/drivers/thunderbolt/debugfs.c @@ -956,7 +956,9 @@ margining_error_counter_write(struct file *file, const char __user *user_buf, else if (!strcmp(buf, "stop")) error_counter = USB4_MARGIN_SW_ERROR_COUNTER_STOP; else - return -EINVAL; + goto err_free; + + free_page((unsigned long)buf); scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { if (!margining->software) @@ -966,6 +968,10 @@ margining_error_counter_write(struct file *file, const char __user *user_buf, } return count; + +err_free: + free_page((unsigned long)buf); + return -EINVAL; } static int margining_error_counter_show(struct seq_file *s, void *not_used) From 4e8f512e2b8fdf20641d9e6ead73bcd9a35b62d1 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Tue, 7 Apr 2026 11:27:55 +0800 Subject: [PATCH 1599/2758] dmaengine: imx-sdma: Refine spba bus searching in probe [ Upstream commit d52d42e2e5d9f13166e81ac837ebb023d1306e61 ] There are multi spba-busses for i.MX8M* platforms, if only search for the first spba-bus in DT, the found spba-bus may not the real bus of audio devices, which cause issue for sdma p2p case, as the sdma p2p script presently does not deal with the transactions involving two devices connected to the AIPS bus. Search the SDMA parent node first, which should be the AIPS bus, then search the child node whose compatible string is spba-bus under that AIPS bus for the above multi spba-busses case. Fixes: 8391ecf465ec ("dmaengine: imx-sdma: Add device to device support") Signed-off-by: Shengjiu Wang Reviewed-by: Frank Li Link: https://patch.msgid.link/20260407032755.2758049-1-shengjiu.wang@nxp.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/dma/imx-sdma.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 3d527883776b4..36368835a845c 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -2364,7 +2364,9 @@ static int sdma_probe(struct platform_device *pdev) return dev_err_probe(&pdev->dev, ret, "failed to register controller\n"); - spba_bus = of_find_compatible_node(NULL, NULL, "fsl,spba-bus"); + struct device_node *sdma_parent_np __free(device_node) = of_get_parent(np); + + spba_bus = of_get_compatible_child(sdma_parent_np, "fsl,spba-bus"); ret = of_address_to_resource(spba_bus, 0, &spba_res); if (!ret) { sdma->spba_start_addr = spba_res.start; From 4442e8c8f20fec3ed580c3ff4fc100f207925c1b Mon Sep 17 00:00:00 2001 From: Akhil R Date: Tue, 31 Mar 2026 15:52:54 +0530 Subject: [PATCH 1600/2758] dt-bindings: dma: nvidia,tegra186-gpc-dma: Make reset optional [ Upstream commit cc6049bd3fa8501ee27042df469a19ed69cf406d ] On Tegra264, GPCDMA reset control is not exposed to Linux and is handled by the boot firmware. Although reset was not exposed in Tegra234 as well, the firmware supported a dummy reset which just returns success on reset without doing an actual reset. This is also not supported in Tegra264 BPMP. Therefore mark 'reset' and 'reset-names' properties as required only for devices prior to Tegra264. This also necessitates that the Tegra264 compatible be standalone and cannot have the fallback compatible of Tegra186. Since there is no functional impact, we keep reset as required for Tegra234 to avoid breaking the ABI. Fixes: bb8c97571db5 ("dt-bindings: dma: Add Tegra264 compatible string") Signed-off-by: Akhil R Acked-by: Rob Herring (Arm) Acked-by: Thierry Reding Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20260331102303.33181-2-akhilrajeev@nvidia.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- .../bindings/dma/nvidia,tegra186-gpc-dma.yaml | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml index 0dabe9bbb219b..64f1e9d9896df 100644 --- a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml +++ b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml @@ -15,16 +15,14 @@ maintainers: - Jon Hunter - Rajesh Gumasta -allOf: - - $ref: dma-controller.yaml# - properties: compatible: oneOf: - - const: nvidia,tegra186-gpcdma + - enum: + - nvidia,tegra264-gpcdma + - nvidia,tegra186-gpcdma - items: - enum: - - nvidia,tegra264-gpcdma - nvidia,tegra234-gpcdma - nvidia,tegra194-gpcdma - const: nvidia,tegra186-gpcdma @@ -60,12 +58,23 @@ required: - compatible - reg - interrupts - - resets - - reset-names - "#dma-cells" - iommus - dma-channel-mask +allOf: + - $ref: dma-controller.yaml# + - if: + properties: + compatible: + contains: + enum: + - nvidia,tegra186-gpcdma + then: + required: + - resets + - reset-names + additionalProperties: false examples: From 25b1f78ef3527e179c0f5b0239f65720c0e6dc51 Mon Sep 17 00:00:00 2001 From: Rui Qi Date: Thu, 28 May 2026 14:23:55 +0800 Subject: [PATCH 1601/2758] perf: Fix off-by-one stack buffer overflow in kallsyms__parse() [ Upstream commit 68018df3f55eba96a20dd703f5f276a6518f4963 ] In kallsyms__parse(), the loop reading symbol names iterates with i < sizeof(symbol_name), which allows i to reach sizeof(symbol_name) upon loop exit. The subsequent symbol_name[i] = '\0' then writes one byte past the end of the stack-allocated symbol_name[] array. Fix this by changing the loop bound to KSYM_NAME_LEN, so the null terminator always lands within the array. The overflow is triggerable by a kallsyms entry with a symbol name of KSYM_NAME_LEN+1 or more characters (e.g., long Rust mangled names or a malicious /proc/kallsyms). Fixes: 53df2b9344128984 ("libsymbols kallsyms: Parse using io api") Signed-off-by: Rui Qi Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/lib/symbol/kallsyms.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/lib/symbol/kallsyms.c b/tools/lib/symbol/kallsyms.c index e335ac2b9e197..d64bd9cc82a90 100644 --- a/tools/lib/symbol/kallsyms.c +++ b/tools/lib/symbol/kallsyms.c @@ -60,7 +60,7 @@ int kallsyms__parse(const char *filename, void *arg, read_to_eol(&io); continue; } - for (i = 0; i < sizeof(symbol_name); i++) { + for (i = 0; i < KSYM_NAME_LEN; i++) { ch = io__get_char(&io); if (ch < 0 || ch == '\n') break; @@ -68,6 +68,9 @@ int kallsyms__parse(const char *filename, void *arg, } symbol_name[i] = '\0'; + if (i == KSYM_NAME_LEN) + read_to_eol(&io); + err = process_symbol(arg, symbol_name, symbol_type, start); if (err) break; From 885bd036cbdf99f78913ab704bca41a3bbaf06f6 Mon Sep 17 00:00:00 2001 From: James Clark Date: Mon, 20 Apr 2026 12:52:17 +0100 Subject: [PATCH 1602/2758] perf annotate: Fix crashes on empty annotate windows [ Upstream commit 74802634e4a7e556429417963a8cbda27dd8b4b3 ] Annotate can open with an empty window if the disassembly tool fails. After the linked change, the TUI started assuming there was a current annotation line and could assert or segfault in the seek, refresh, and source-toggle paths. Handle empty annotate windows explicitly: set the asm entry count before resetting the browser, return early when refreshing an empty list, and ignore source line toggle when there is no current annotation line. Fixes the following when opening an annotation: perf: ui/browser.c:125: ui_browser__list_head_seek: Assertion `pos != NULL' failed. Aborted Fixes: e201757f7a0a901e ("perf annotate: Fix source code annotate with objdump") Assisted-by: GitHub Copilot:GPT-5.4 Signed-off-by: James Clark Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mark Rutland Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/ui/browser.c | 3 +++ tools/perf/ui/browsers/annotate.c | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c index dc88427b4ae5a..321187b204d38 100644 --- a/tools/perf/ui/browser.c +++ b/tools/perf/ui/browser.c @@ -513,6 +513,9 @@ unsigned int ui_browser__list_head_refresh(struct ui_browser *browser) struct list_head *head = browser->entries; int row = 0; + if (browser->nr_entries == 0) + return 0; + if (browser->top == NULL || browser->top == browser->entries) browser->top = ui_browser__list_head_filter_entries(browser, head->next); diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index ea17e6d29a7e4..0261cd9221834 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -449,6 +449,9 @@ static bool annotate_browser__toggle_source(struct annotate_browser *browser, struct annotation_line *al; off_t offset = browser->b.index - browser->b.top_idx; + if (browser->b.nr_entries == 0) + return false; + browser->b.seek(&browser->b, offset, SEEK_CUR); al = list_entry(browser->b.top, struct annotation_line, node); @@ -542,8 +545,8 @@ static void annotate_browser__show_full_location(struct ui_browser *browser) static void ui_browser__init_asm_mode(struct ui_browser *browser) { struct annotation *notes = browser__annotation(browser); - ui_browser__reset_index(browser); browser->nr_entries = notes->src->nr_asm_entries; + ui_browser__reset_index(browser); } static int sym_title(struct symbol *sym, struct map *map, char *title, From d88a630bacfaa4ece35448b0b3f7247f0381b791 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 4 Jun 2026 12:55:06 -0300 Subject: [PATCH 1603/2758] perf tools: Guard test_bit from out-of-bounds sample CPU [ Upstream commit a5498ccf8079fc91c938f122ff9697b0c526b2fd ] When PERF_SAMPLE_CPU is absent from a perf.data file, sample->cpu is initialized to (u32)-1 by evsel__parse_sample(). Five call sites pass this value directly to test_bit(sample->cpu, cpu_bitmap), reading massively out of bounds past the DECLARE_BITMAP(..., MAX_NR_CPUS) allocation of 4096 bits. Add a sample->cpu >= MAX_NR_CPUS guard before each test_bit() call, matching the existing safe pattern in builtin-kwork.c. This catches both the (u32)-1 sentinel and any corrupted CPU value exceeding the bitmap size. Fixes: 5d67be97f890 ("perf report/annotate/script: Add option to specify a CPU range") Cc: Anton Blanchard Reported-by: sashiko-bot Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-annotate.c | 3 ++- tools/perf/builtin-diff.c | 3 ++- tools/perf/builtin-report.c | 3 ++- tools/perf/builtin-sched.c | 6 ++++-- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 5e57b78548f42..bdc7adc83b471 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -298,7 +298,8 @@ static int process_sample_event(const struct perf_tool *tool, goto out_put; } - if (ann->cpu_list && !test_bit(sample->cpu, ann->cpu_bitmap)) + if (ann->cpu_list && (sample->cpu >= MAX_NR_CPUS || + !test_bit(sample->cpu, ann->cpu_bitmap))) goto out_put; if (!al.filtered && diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 1b3df868849a9..29db0afc53ca0 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -416,7 +416,8 @@ static int diff__process_sample_event(const struct perf_tool *tool, goto out; } - if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) { + if (cpu_list && (sample->cpu >= MAX_NR_CPUS || + !test_bit(sample->cpu, cpu_bitmap))) { ret = 0; goto out; } diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 0b0966d94128e..3a5cf1b64adb0 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -300,7 +300,8 @@ static int process_sample_event(const struct perf_tool *tool, if (symbol_conf.hide_unresolved && al.sym == NULL) goto out_put; - if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap)) + if (rep->cpu_list && (sample->cpu >= MAX_NR_CPUS || + !test_bit(sample->cpu, rep->cpu_bitmap))) goto out_put; if (sort__mode == SORT_MODE__BRANCH) { diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 241c2f808f7b0..3dcd1f3525fde 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -2175,7 +2175,8 @@ static void timehist_print_sample(struct perf_sched *sched, char nstr[30]; u64 wait_time; - if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) + if (cpu_list && (sample->cpu >= MAX_NR_CPUS || + !test_bit(sample->cpu, cpu_bitmap))) return; timestamp__scnprintf_usec(t, tstr, sizeof(tstr)); @@ -2857,7 +2858,8 @@ static int timehist_sched_change_event(const struct perf_tool *tool, } if (!sched->idle_hist || thread__tid(thread) == 0) { - if (!cpu_list || test_bit(sample->cpu, cpu_bitmap)) + if (!cpu_list || (sample->cpu < MAX_NR_CPUS && + test_bit(sample->cpu, cpu_bitmap))) timehist_update_runtime_stats(tr, t, tprev); if (sched->idle_hist) { From b189fce8d2ac6928cd8ada8e1823cd9bf6f424de Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 4 Jun 2026 12:55:29 -0300 Subject: [PATCH 1604/2758] perf sched: Fix thread reference leak in latency_switch_event [ Upstream commit 66ea9de60396a4dea5276bc87025884691876c36 ] In latency_switch_event(), after acquiring thread references for sched_out and sched_in via machine__findnew_thread(), the first add_sched_out_event() failure path does 'return -1', bypassing the out_put label that calls thread__put() on both references. The second and third add_sched_out_event() failures correctly use 'goto out_put'. Fix the first one to match. Fixes: b91fc39f4ad7 ("perf machine: Protect the machine->threads with a rwlock") Reported-by: sashiko-bot Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-sched.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 3dcd1f3525fde..c6a96d0dd77d9 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1177,7 +1177,7 @@ static int latency_switch_event(struct perf_sched *sched, } } if (add_sched_out_event(out_events, prev_state, timestamp)) - return -1; + goto out_put; in_events = thread_atoms_search(&sched->atom_root, sched_in, &sched->cmp_pid); if (!in_events) { From a4ec6bf241456acfc235bbc6981cdfcc3d420d1f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 1 Jun 2026 13:07:52 -0300 Subject: [PATCH 1605/2758] perf sample: Add file_offset field to struct perf_sample commit 7a490187f22b4bfae7ef752edbe3fb13017ca11c upstream. Add a file_offset field to struct perf_sample so that event processing callbacks can report the byte offset of the problematic event in perf.data, letting users cross-reference with 'perf report -D' output. Set sample.file_offset in perf_session__deliver_event(), which is the common entry point for both file mode (mmap'd offset) and pipe mode (running byte counter from __perf_session__process_pipe_events). The assignment is placed after evsel__parse_sample(), which zeroes the struct via memset. Preserve file_offset through the deferred callchain delivery path by storing it in struct deferred_event and restoring it after evlist__parse_sample() in both evlist__deliver_deferred_callchain() and session__flush_deferred_samples(). Subsequent patches will use this field in skip/stop warning messages. Reviewed-by: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Greg Kroah-Hartman --- tools/perf/util/sample.h | 2 ++ tools/perf/util/session.c | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/tools/perf/util/sample.h b/tools/perf/util/sample.h index e556c9b656ea9..c4eae8b2fd060 100644 --- a/tools/perf/util/sample.h +++ b/tools/perf/util/sample.h @@ -158,6 +158,8 @@ struct perf_sample { u64 code_page_size; /** @cgroup: The sample event PERF_SAMPLE_CGROUP value. */ u64 cgroup; + /** @file_offset: Byte offset of this event in the perf.data file. */ + u64 file_offset; /** @flags: Extra flag data from auxiliary events like intel-pt. */ u32 flags; /** @machine_pid: The guest machine pid derived from the sample id. */ diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index fe0de2a0277f0..2bc093bd2f241 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1352,6 +1352,7 @@ static int evlist__deliver_sample(struct evlist *evlist, const struct perf_tool struct deferred_event { struct list_head list; union perf_event *event; + u64 file_offset; }; /* @@ -1387,6 +1388,7 @@ static int evlist__deliver_deferred_callchain(struct evlist *evlist, perf_sample__exit(&orig_sample); break; } + orig_sample.file_offset = de->file_offset; if (sample->tid != orig_sample.tid) { perf_sample__exit(&orig_sample); @@ -1435,6 +1437,7 @@ static int session__flush_deferred_samples(struct perf_session *session, perf_sample__exit(&sample); break; } + sample.file_offset = de->file_offset; sample.evsel = evlist__id2evsel(evlist, sample.id); ret = evlist__deliver_sample(evlist, tool, de->event, @@ -1496,6 +1499,7 @@ static int machines__deliver_event(struct machines *machines, return -ENOMEM; } memcpy(de->event, event, sz); + de->file_offset = sample->file_offset; list_add_tail(&de->list, &evlist->deferred_samples); return 0; } @@ -1589,6 +1593,7 @@ static int perf_session__deliver_event(struct perf_session *session, ret = 0; goto out; } + sample.file_offset = file_offset; ret = machines__deliver_event(&session->machines, session->evlist, event, &sample, tool, file_offset, file_path); From bbaa0a0441d2638bd59923864f4b2cee9df6b8bf Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 1 Jun 2026 19:53:41 -0300 Subject: [PATCH 1606/2758] perf sched: Replace BUG_ON on invalid CPU with graceful skip [ Upstream commit 1e2c83f732deb329ebce23e26cbc482f4c4bf194 ] latency_switch_event(), latency_runtime_event(), and map_switch_event() use BUG_ON(cpu >= MAX_CPUS || cpu < 0) to validate the sample CPU. When PERF_SAMPLE_CPU is absent from the sample type, evsel__parse_sample() initializes sample->cpu to (u32)-1. Casting this to int yields -1, which triggers the BUG_ON and aborts perf sched. The central CPU validation in perf_session__deliver_event() intentionally preserves the (u32)-1 sentinel for downstream tools like perf script and perf inject, so leaf callbacks must handle it themselves. Replace the three BUG_ON calls with graceful skips using pr_warning(), matching the existing pattern in process_sched_switch_event() and process_sched_runtime_event() earlier in the same file. Include the file offset for cross-referencing with perf report -D. Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Stable-dep-of: 8cbca8a480e1 ("perf sched: Fix NULL dereference in latency_runtime_event") Signed-off-by: Sasha Levin --- tools/perf/builtin-sched.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index c6a96d0dd77d9..adff5d40a15c9 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1147,7 +1147,12 @@ static int latency_switch_event(struct perf_sched *sched, int cpu = sample->cpu, err = -1; s64 delta; - BUG_ON(cpu >= MAX_CPUS || cpu < 0); + /* perf.data is untrusted input — CPU may be absent or corrupted */ + if (cpu >= MAX_CPUS || cpu < 0) { + pr_warning("WARNING: at offset %#" PRIx64 ": out-of-bound sample CPU %d, skipping sample\n", + sample->file_offset, cpu); + return 0; + } timestamp0 = sched->cpu_last_switched[cpu]; sched->cpu_last_switched[cpu] = timestamp; @@ -1218,7 +1223,13 @@ static int latency_runtime_event(struct perf_sched *sched, if (thread == NULL) return -1; - BUG_ON(cpu >= MAX_CPUS || cpu < 0); + /* perf.data is untrusted input — CPU may be absent or corrupted */ + if (cpu >= MAX_CPUS || cpu < 0) { + pr_warning("WARNING: at offset %#" PRIx64 ": out-of-bound sample CPU %d, skipping sample\n", + sample->file_offset, cpu); + err = 0; + goto out_put; + } if (!atoms) { if (thread_atoms_insert(sched, thread)) goto out_put; @@ -1647,7 +1658,12 @@ static int map_switch_event(struct perf_sched *sched, struct evsel *evsel, const char *str; int ret = -1; - BUG_ON(this_cpu.cpu >= MAX_CPUS || this_cpu.cpu < 0); + /* perf.data is untrusted input — CPU may be absent or corrupted */ + if (this_cpu.cpu >= MAX_CPUS || this_cpu.cpu < 0) { + pr_warning("WARNING: at offset %#" PRIx64 ": out-of-bound sample CPU %d, skipping sample\n", + sample->file_offset, this_cpu.cpu); + return 0; + } if (this_cpu.cpu > sched->max_cpu.cpu) sched->max_cpu = this_cpu; From 70d31bdd3789c055b81585f09a72afd3b2cf1597 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 4 Jun 2026 12:56:02 -0300 Subject: [PATCH 1607/2758] perf sched: Fix NULL dereference in latency_runtime_event [ Upstream commit 8cbca8a480e15f6326ce94287570993f27a4b2d5 ] latency_runtime_event() passes the return value of machine__findnew_thread() directly to thread_atoms_search() at line 1216, before checking for NULL at line 1220. thread_atoms_search() calls pid_cmp() which dereferences the thread pointer via thread__tid(), causing a NULL pointer dereference if the allocation fails. All other callers of thread_atoms_search() in this file (latency_switch_event, latency_wakeup_event, latency_migrate_task_event) correctly check for NULL first. Move the atoms assignment after the NULL check to match the pattern used by the other callers. Fixes: b91fc39f4ad7 ("perf machine: Protect the machine->threads with a rwlock") Reported-by: sashiko-bot Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-sched.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index adff5d40a15c9..13d9a15a553ec 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1216,13 +1216,15 @@ static int latency_runtime_event(struct perf_sched *sched, const u32 pid = evsel__intval(evsel, sample, "pid"); const u64 runtime = evsel__intval(evsel, sample, "runtime"); struct thread *thread = machine__findnew_thread(machine, -1, pid); - struct work_atoms *atoms = thread_atoms_search(&sched->atom_root, thread, &sched->cmp_pid); + struct work_atoms *atoms; u64 timestamp = sample->time; int cpu = sample->cpu, err = -1; if (thread == NULL) return -1; + atoms = thread_atoms_search(&sched->atom_root, thread, &sched->cmp_pid); + /* perf.data is untrusted input — CPU may be absent or corrupted */ if (cpu >= MAX_CPUS || cpu < 0) { pr_warning("WARNING: at offset %#" PRIx64 ": out-of-bound sample CPU %d, skipping sample\n", From e94a56aac6b4457847247e88148b7caa20ca86fd Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 4 Jun 2026 13:05:10 -0300 Subject: [PATCH 1608/2758] perf sched: Fix comp_cpus heap overflow with cross-machine recordings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit c8b04142078a9ccb9e402ab7c38de7123256f4a5 ] setup_map_cpus() allocates comp_cpus based on sysconf(_SC_NPROCESSORS_CONF), the host machine's CPU count. But map_switch_event() indexes comp_cpus using cpus_nr derived from bitmap_weight(comp_cpus_mask, MAX_CPUS), where comp_cpus_mask is declared as DECLARE_BITMAP(..., MAX_CPUS) with MAX_CPUS=4096. When analyzing a perf.data recording from a machine with more CPUs than the analysis host (e.g. 128-CPU server recording analyzed on an 8-CPU laptop), cpus_nr exceeds the allocation size, causing a heap buffer overflow. Also fix a type mismatch: comp_cpus is 'struct perf_cpu *' (2 bytes per element) but was allocated with sizeof(int) (4 bytes per element). Allocate comp_cpus with MAX_CPUS entries using the correct element size, matching the comp_cpus_mask bitmap bounds. Remove the sysconf(_SC_NPROCESSORS_CONF) initialization of max_cpu — its only consumer was the comp_cpus allocation, and max_cpu is dynamically updated from the recording's events during processing. Fix the non-compact path to use max_cpu.cpu + 1 as cpus_nr, converting from 0-based index to count — sysconf() returned a count which masked this off-by-one. Fixes: 99623c628f54 ("perf sched: Add compact display option") Reported-by: sashiko-bot Cc: Jiri Olsa Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-sched.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 13d9a15a553ec..6f5d5f3ce715c 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1677,7 +1677,7 @@ static int map_switch_event(struct perf_sched *sched, struct evsel *evsel, new_cpu = true; } } else - cpus_nr = sched->max_cpu.cpu; + cpus_nr = sched->max_cpu.cpu + 1; timestamp0 = sched->cpu_last_switched[this_cpu.cpu]; sched->cpu_last_switched[this_cpu.cpu] = timestamp; @@ -3577,10 +3577,8 @@ static int perf_sched__lat(struct perf_sched *sched) static int setup_map_cpus(struct perf_sched *sched) { - sched->max_cpu.cpu = sysconf(_SC_NPROCESSORS_CONF); - if (sched->map.comp) { - sched->map.comp_cpus = calloc(sched->max_cpu.cpu, sizeof(int)); + sched->map.comp_cpus = calloc(MAX_CPUS, sizeof(*sched->map.comp_cpus)); if (!sched->map.comp_cpus) return -1; } From bd027a461624f3b9a99ed07c5ad8ce5964174863 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 4 Jun 2026 18:11:41 -0300 Subject: [PATCH 1609/2758] perf tools: Guard remaining test_bit calls from OOB sample CPU [ Upstream commit 7ccd2e6cecd5bb02a5a15f0dd7199d1e81380bda ] auxtrace.c:filter_cpu() and builtin-script.c:filter_cpu() call test_bit(cpu, cpu_bitmap) where cpu_bitmap is declared with MAX_NR_CPUS bits. When the CPU value from a perf.data event is corrupt or absent (e.g. negative or >= MAX_NR_CPUS), test_bit reads out of bounds. Add bounds checks before test_bit(): >= 0 for the int16_t cpu.cpu in auxtrace (which also covers the -1 sentinel), and < MAX_NR_CPUS for both sites. Matches the pattern applied in the previous series for builtin-annotate.c, builtin-diff.c, builtin-report.c, and builtin-sched.c. Fixes: 644e0840ad46 ("perf auxtrace: Add CPU filter support") Fixes: 5d67be97f890 ("perf report/annotate/script: Add option to specify a CPU range") Reported-by: sashiko-bot Cc: Adrian Hunter Cc: Anton Blanchard Cc: Jiri Olsa Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-script.c | 2 +- tools/perf/util/auxtrace.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index fd0b4609516b3..ed372762276bc 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -2638,7 +2638,7 @@ static int cleanup_scripting(void) static bool filter_cpu(struct perf_sample *sample) { - if (cpu_list && sample->cpu != (u32)-1) + if (cpu_list && sample->cpu != (u32)-1 && sample->cpu < MAX_NR_CPUS) return !test_bit(sample->cpu, cpu_bitmap); return false; } diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c index a224687ffbc1b..47e2004b91d73 100644 --- a/tools/perf/util/auxtrace.c +++ b/tools/perf/util/auxtrace.c @@ -372,7 +372,8 @@ static bool filter_cpu(struct perf_session *session, struct perf_cpu cpu) { unsigned long *cpu_bitmap = session->itrace_synth_opts->cpu_bitmap; - return cpu_bitmap && cpu.cpu != -1 && !test_bit(cpu.cpu, cpu_bitmap); + return cpu_bitmap && cpu.cpu >= 0 && cpu.cpu < MAX_NR_CPUS && + !test_bit(cpu.cpu, cpu_bitmap); } static int auxtrace_queues__add_buffer(struct auxtrace_queues *queues, From 6587c61570f4c15eb4314355e88f67857be31221 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 4 Jun 2026 18:14:23 -0300 Subject: [PATCH 1610/2758] perf tools: Add bounds check to cpu__get_node() [ Upstream commit 1e7921d7227de5da0dfc167943092c823ec7e49b ] cpu__get_node() accesses cpunode_map[cpu.cpu] without checking against max_cpu_num, the allocation size of cpunode_map. Callers such as builtin-kmem.c:evsel__process_alloc_event() pass sample->cpu from perf.data events, which may exceed the host's CPU count when analyzing cross-machine recordings. Add a bounds check against max_cpu_num before indexing, returning -1 for out-of-range values. This is a central fix that protects all callers. Fixes: 86895b480a2f ("perf stat: Add --per-node agregation support") Reported-by: sashiko-bot Cc: Jiri Olsa Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/cpumap.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 11922e1ded844..19783c8376572 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -548,6 +548,10 @@ int cpu__get_node(struct perf_cpu cpu) return -1; } + /* cpunode_map allocated for max_cpu_num entries; input may be untrusted */ + if (cpu.cpu < 0 || cpu.cpu >= max_cpu_num.cpu) + return -1; + return cpunode_map[cpu.cpu]; } From 380ad7297fa0c24e0de0235075d986af21cec9bc Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 4 Jun 2026 18:18:05 -0300 Subject: [PATCH 1611/2758] perf sched: Fix thread reference leaks in timehist_get_thread() [ Upstream commit fa20c1f8f4e094abe0169d39fce8181bc26d6dab ] timehist_get_thread() acquires a thread reference via machine__findnew_thread() and an idle thread reference via get_idle_thread() (which calls thread__get()). Two error paths in the idle_hist block return NULL without releasing these references: - When get_idle_thread() fails, the thread reference leaks. - When thread__priv(idle) returns NULL, both idle and thread leak. Additionally, the idle thread reference acquired on the success path is never released, leaking a reference on every sample when --idle-hist is active. Add thread__put() calls on both error paths and release the idle reference after use on the success path. Fixes: 5d8f17fb5822 ("perf sched timehist: Add -I/--idle-hist option") Reported-by: sashiko-bot Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-sched.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 6f5d5f3ce715c..7503fc00ff20a 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -2547,12 +2547,16 @@ static struct thread *timehist_get_thread(struct perf_sched *sched, idle = get_idle_thread(sample->cpu); if (idle == NULL) { pr_err("Failed to get idle thread for cpu %d.\n", sample->cpu); + thread__put(thread); return NULL; } itr = thread__priv(idle); - if (itr == NULL) + if (itr == NULL) { + thread__put(idle); + thread__put(thread); return NULL; + } thread__put(itr->last_thread); itr->last_thread = thread__get(thread); @@ -2560,6 +2564,8 @@ static struct thread *timehist_get_thread(struct perf_sched *sched, /* copy task callchain when entering to idle */ if (evsel__intval(evsel, sample, "next_pid") == 0) save_idle_callchain(sched, itr, sample); + + thread__put(idle); } } From 68b6157d2c626aeed825be74f6db539504f033d2 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 4 Jun 2026 18:23:35 -0300 Subject: [PATCH 1612/2758] perf sched: Cap max_cpu at MAX_CPUS in timehist sample processing [ Upstream commit 06e7994427ab56e32699a5e45d048ff0826f3d53 ] perf_timehist__process_sample() updates sched->max_cpu from the sample CPU without bounds checking. Later code uses max_cpu + 1 as an iteration count over arrays allocated with MAX_CPUS entries (curr_thread, cpu_last_switched). A recording with CPU IDs >= MAX_CPUS causes out-of-bounds array accesses. Also cap the env->nr_cpus_online initialization of max_cpu in perf_sched__timehist(), which could exceed MAX_CPUS on very large systems. Add bounds checks before both max_cpu updates, matching the pattern already used in map_switch_event(). Fixes: 49394a2a24c7 ("perf sched timehist: Introduce timehist command") Reviewed-by: David Ahern Reported-by: sashiko-bot Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-sched.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 7503fc00ff20a..7cb27fba12a90 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -3219,7 +3219,9 @@ static int perf_timehist__process_sample(const struct perf_tool *tool, .cpu = sample->cpu, }; - if (this_cpu.cpu > sched->max_cpu.cpu) + /* max_cpu indexes arrays allocated with MAX_CPUS entries */ + if (this_cpu.cpu >= 0 && this_cpu.cpu < MAX_CPUS && + this_cpu.cpu > sched->max_cpu.cpu) sched->max_cpu = this_cpu; if (evsel->handler != NULL) { @@ -3389,8 +3391,8 @@ static int perf_sched__timehist(struct perf_sched *sched) perf_session__set_tracepoints_handlers(session, migrate_handlers)) goto out; - /* pre-allocate struct for per-CPU idle stats */ - sched->max_cpu.cpu = env->nr_cpus_online; + /* pre-allocate struct for per-CPU idle stats; cap to array bounds */ + sched->max_cpu.cpu = min(env->nr_cpus_online, MAX_CPUS); if (sched->max_cpu.cpu == 0) sched->max_cpu.cpu = 4; if (init_idle_threads(sched->max_cpu.cpu)) From 5ea1dcc9418c4e06ce29ed5170596f497ba86872 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 4 Jun 2026 18:25:57 -0300 Subject: [PATCH 1613/2758] perf sched: Fix register_pid() overflow, strcpy, and BUG_ON [ Upstream commit 5949d339f5ec98752d56dcd4e36f619a59d513a5 ] register_pid() has several issues when processing untrusted perf.data: 1. Integer overflow: (pid + 1) * sizeof(struct task_desc *) can wrap to a small value on 32-bit systems when pid is large (e.g. 0x40000000), causing realloc to return a tiny buffer followed by out-of-bounds writes in the initialization loop. 2. Heap buffer overflow: strcpy(task->comm, comm) copies the untrusted comm string into a fixed 20-byte COMM_LEN buffer with no length check. 3. BUG_ON on allocation failure: perf.data is untrusted input, so allocation failures should be handled gracefully rather than killing the process. 4. Realloc of sched->tasks assigned directly back, leaking the old pointer on failure; nr_tasks incremented before the realloc, leaving corrupted state on failure. Cap pid at PID_MAX_LIMIT (4194304, matching the kernel's maximum on 64-bit), replace strcpy with strlcpy, guard against NULL comm, replace BUG_ON with NULL returns using safe realloc patterns, and add NULL checks in callers that dereference the result. Fixes: ec156764d424 ("perf sched: Import schedbench.c") Reported-by: sashiko-bot Cc: Ingo Molnar Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-sched.c | 40 ++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 7cb27fba12a90..cca9719558463 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -54,6 +54,7 @@ #define COMM_LEN 20 #define SYM_LEN 129 #define MAX_PID 1024000 +#define PID_MAX_LIMIT 4194304 /* kernel limit on 64-bit */ #define MAX_PRIO 140 #define SEP_LEN 100 @@ -448,17 +449,28 @@ static void add_sched_event_sleep(struct perf_sched *sched, struct task_desc *ta static struct task_desc *register_pid(struct perf_sched *sched, unsigned long pid, const char *comm) { - struct task_desc *task; + struct task_desc *task, **tasks_p; static int pid_max; + /* perf.data is untrusted — cap pid to prevent overflow in size calculations */ + if (pid >= PID_MAX_LIMIT) { + pr_err("pid %lu exceeds limit %d, skipping\n", pid, PID_MAX_LIMIT); + return NULL; + } + if (sched->pid_to_task == NULL) { if (sysctl__read_int("kernel/pid_max", &pid_max) < 0) pid_max = MAX_PID; - BUG_ON((sched->pid_to_task = calloc(pid_max, sizeof(struct task_desc *))) == NULL); + sched->pid_to_task = calloc(pid_max, sizeof(struct task_desc *)); + if (sched->pid_to_task == NULL) + return NULL; } if (pid >= (unsigned long)pid_max) { - BUG_ON((sched->pid_to_task = realloc(sched->pid_to_task, (pid + 1) * - sizeof(struct task_desc *))) == NULL); + void *p = realloc(sched->pid_to_task, (pid + 1) * sizeof(struct task_desc *)); + + if (p == NULL) + return NULL; + sched->pid_to_task = p; while (pid >= (unsigned long)pid_max) sched->pid_to_task[pid_max++] = NULL; } @@ -469,9 +481,11 @@ static struct task_desc *register_pid(struct perf_sched *sched, return task; task = zalloc(sizeof(*task)); + if (task == NULL) + return NULL; task->pid = pid; - task->nr = sched->nr_tasks; - strcpy(task->comm, comm); + if (comm) + strlcpy(task->comm, comm, sizeof(task->comm)); /* * every task starts in sleeping state - this gets ignored * if there's no wakeup pointing to this sleep state: @@ -479,10 +493,12 @@ static struct task_desc *register_pid(struct perf_sched *sched, add_sched_event_sleep(sched, task, 0); sched->pid_to_task[pid] = task; - sched->nr_tasks++; - sched->tasks = realloc(sched->tasks, sched->nr_tasks * sizeof(struct task_desc *)); - BUG_ON(!sched->tasks); - sched->tasks[task->nr] = task; + tasks_p = realloc(sched->tasks, (sched->nr_tasks + 1) * sizeof(struct task_desc *)); + if (!tasks_p) + return NULL; + sched->tasks = tasks_p; + sched->tasks[sched->nr_tasks] = task; + task->nr = sched->nr_tasks++; if (verbose > 0) printf("registered task #%ld, PID %ld (%s)\n", sched->nr_tasks, pid, comm); @@ -841,6 +857,8 @@ replay_wakeup_event(struct perf_sched *sched, waker = register_pid(sched, sample->tid, ""); wakee = register_pid(sched, pid, comm); + if (waker == NULL || wakee == NULL) + return -1; add_sched_event_wakeup(sched, waker, sample->time, wakee); return 0; @@ -882,6 +900,8 @@ static int replay_switch_event(struct perf_sched *sched, prev = register_pid(sched, prev_pid, prev_comm); next = register_pid(sched, next_pid, next_comm); + if (prev == NULL || next == NULL) + return -1; sched->cpu_last_switched[cpu] = timestamp; From 5257dfb9619cc1a67aae5223a44d976e99701e1b Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 5 Jun 2026 10:56:33 -0300 Subject: [PATCH 1614/2758] perf mmap: Guard cpu__get_node() return in aio_bind() [ Upstream commit f32dc302a090f48893477ef297a888db109ec0bd ] perf_mmap__aio_bind() passes the cpu__get_node() return value directly to an unsigned long variable (node_index). When cpu__get_node() returns -1 for an unknown CPU, the implicit int-to-unsigned-long conversion sign-extends it to ULONG_MAX. This causes bitmap_zalloc(ULONG_MAX + 1) which wraps to bitmap_zalloc(0), returning a zero-sized allocation. The subsequent __set_bit(ULONG_MAX, node_mask) then writes massively out of bounds. Check the return value in a signed temporary before assigning to node_index, and skip the NUMA binding when the node is unknown. Fixes: c44a8b44ca9f ("perf record: Bind the AIO user space buffers to nodes") Reported-by: sashiko-bot Cc: Alexey Budankov Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/mmap.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/mmap.c b/tools/perf/util/mmap.c index b69f926d314b1..4404a99eee45f 100644 --- a/tools/perf/util/mmap.c +++ b/tools/perf/util/mmap.c @@ -104,9 +104,15 @@ static int perf_mmap__aio_bind(struct mmap *map, int idx, struct perf_cpu cpu, i int err = 0; if (affinity != PERF_AFFINITY_SYS && cpu__max_node() > 1) { + int node; + data = map->aio.data[idx]; mmap_len = mmap__mmap_len(map); - node_index = cpu__get_node(cpu); + node = cpu__get_node(cpu); + /* -1 sign-extends to ULONG_MAX, wrapping bitmap_zalloc(0) and OOB __set_bit */ + if (node < 0) + return 0; + node_index = node; node_mask = bitmap_zalloc(node_index + 1); if (!node_mask) { pr_err("Failed to allocate node mask for mbind: error %m\n"); From 9cbb9f3e532eb1bb79c6559fa551c9fe2579ff3d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 5 Jun 2026 10:59:11 -0300 Subject: [PATCH 1615/2758] perf stat: Bounds-check CPU index in topology aggregation callbacks [ Upstream commit 52e69b1c5b606b513d403dd4addc784c27a0c8e2 ] Six perf_env__get_*_aggr_by_cpu() functions access env->cpu[cpu.cpu] after only checking cpu.cpu != -1. env->cpu[] is allocated with env->nr_cpus_avail entries, so a CPU index from an untrusted perf.data file that exceeds that count causes an out-of-bounds heap read. Replace the != -1 guard with >= 0 && < env->nr_cpus_avail in all six functions. The >= 0 check also catches -1 and any other negative values that could bypass the old check. Affected functions: - perf_env__get_socket_aggr_by_cpu() - perf_env__get_die_aggr_by_cpu() - perf_env__get_cache_aggr_by_cpu() - perf_env__get_cluster_aggr_by_cpu() - perf_env__get_core_aggr_by_cpu() - perf_env__get_cpu_aggr_by_cpu() Fixes: 68d702f7a120 ("perf stat report: Add support to initialize aggr_map from file") Reported-by: sashiko-bot Cc: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-stat.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 99d7db372b480..9a045811c4197 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1638,7 +1638,8 @@ static struct aggr_cpu_id perf_env__get_socket_aggr_by_cpu(struct perf_cpu cpu, struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); - if (cpu.cpu != -1) + /* env->cpu[] has env->nr_cpus_avail entries; reject untrusted indices */ + if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) id.socket = env->cpu[cpu.cpu].socket_id; return id; @@ -1649,7 +1650,7 @@ static struct aggr_cpu_id perf_env__get_die_aggr_by_cpu(struct perf_cpu cpu, voi struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); - if (cpu.cpu != -1) { + if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { /* * die_id is relative to socket, so start * with the socket ID and then add die to @@ -1705,7 +1706,7 @@ static struct aggr_cpu_id perf_env__get_cache_aggr_by_cpu(struct perf_cpu cpu, struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); - if (cpu.cpu != -1) { + if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { u32 cache_level = (perf_stat.aggr_level) ?: stat_config.aggr_level; id.socket = env->cpu[cpu.cpu].socket_id; @@ -1722,7 +1723,7 @@ static struct aggr_cpu_id perf_env__get_cluster_aggr_by_cpu(struct perf_cpu cpu, struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); - if (cpu.cpu != -1) { + if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { id.socket = env->cpu[cpu.cpu].socket_id; id.die = env->cpu[cpu.cpu].die_id; id.cluster = env->cpu[cpu.cpu].cluster_id; @@ -1736,7 +1737,7 @@ static struct aggr_cpu_id perf_env__get_core_aggr_by_cpu(struct perf_cpu cpu, vo struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); - if (cpu.cpu != -1) { + if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { /* * core_id is relative to socket, die and cluster, we need a * global id. So we set socket, die id, cluster id and core id. @@ -1755,7 +1756,7 @@ static struct aggr_cpu_id perf_env__get_cpu_aggr_by_cpu(struct perf_cpu cpu, voi struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); - if (cpu.cpu != -1) { + if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { /* * core_id is relative to socket and die, * we need a global id. So we set From 937be22cf6d211dd87da2635b89a455aeecb6e50 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 5 Jun 2026 11:05:13 -0300 Subject: [PATCH 1616/2758] perf c2c: Bounds-check CPU and node IDs before bitmap and array access [ Upstream commit 65117c3da50f749f4c22eb7a7effc53453dff57f ] c2c_he__set_cpu() passes sample->cpu directly to __set_bit(cpu, cpuset) after only checking for the (u32)-1 sentinel. The cpuset bitmap is allocated with c2c.cpus_cnt bits (from env->nr_cpus_avail), so a crafted perf.data with CPU IDs exceeding that count causes out-of-bounds heap writes. c2c_he__set_node() similarly passes the node ID from mem2node__node() to __set_bit(node, nodeset) after only checking for negative values. The nodeset bitmap is sized to c2c.nodes_cnt (from env->nr_numa_nodes), so a node ID exceeding that causes OOB writes. process_sample_event() indexes c2c.cpu2node[cpu] and c2c_he->node_stats[node] without bounds checking. Both arrays are sized to c2c.cpus_cnt and c2c.nodes_cnt respectively. Add bounds checks in all three paths: - c2c_he__set_cpu(): return if sample->cpu >= c2c.cpus_cnt - c2c_he__set_node(): return if node >= c2c.nodes_cnt - process_sample_event(): clamp cpu to 0 if >= cpus_cnt, guard node_stats access with bounds check Fixes: 1e181b92a2da ("perf c2c report: Add 'node' sort key") Reported-by: sashiko-bot Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-c2c.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index 72a7802775ee9..e2a7ffd4192e4 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -241,6 +241,10 @@ static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he, "WARNING: no sample cpu value")) return; + /* cpuset bitmap has c2c.cpus_cnt bits from env->nr_cpus_avail */ + if (sample->cpu >= (unsigned int)c2c.cpus_cnt) + return; + __set_bit(sample->cpu, c2c_he->cpuset); } @@ -258,6 +262,10 @@ static void c2c_he__set_node(struct c2c_hist_entry *c2c_he, if (WARN_ONCE(node < 0, "WARNING: failed to find node\n")) return; + /* nodeset bitmap has c2c.nodes_cnt bits from env->nr_numa_nodes */ + if (node >= c2c.nodes_cnt) + return; + __set_bit(node, c2c_he->nodeset); if (c2c_he->paddr != sample->phys_addr) { @@ -386,7 +394,12 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, * Doing node stats only for single callchain data. */ int cpu = sample->cpu == (unsigned int) -1 ? 0 : sample->cpu; - int node = c2c.cpu2node[cpu]; + int node; + + /* cpu2node[] has c2c.cpus_cnt entries; large u32 wraps signed negative */ + if (cpu < 0 || cpu >= c2c.cpus_cnt) + cpu = 0; + node = c2c.cpu2node[cpu]; c2c_hists = he__get_c2c_hists(he, c2c.cl_sort, 2, machine->env); if (!c2c_hists) { @@ -405,7 +418,9 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, c2c_he = container_of(he, struct c2c_hist_entry, he); c2c_add_stats(&c2c_he->stats, &stats); c2c_add_stats(&c2c_hists->stats, &stats); - c2c_add_stats(&c2c_he->node_stats[node], &stats); + /* node_stats[] has c2c.nodes_cnt entries */ + if (node >= 0 && node < c2c.nodes_cnt) + c2c_add_stats(&c2c_he->node_stats[node], &stats); compute_stats(c2c_he, &stats, sample->weight); From 4884cfb0d36d0fe92c2189aff5a9ca03073f6e21 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 5 Jun 2026 11:06:30 -0300 Subject: [PATCH 1617/2758] perf c2c: Bounds-check CPU IDs in setup_nodes() topology loop [ Upstream commit 5fb2e6ad8c5d6b3b380f94c7456595511f3731be ] setup_nodes() iterates CPU maps from the perf.data topology header and uses cpu.cpu directly as an array index into cpu2node[] (allocated with c2c.cpus_cnt = env->nr_cpus_avail entries) and __set_bit(cpu.cpu, set) (bitmap also sized to c2c.cpus_cnt). A crafted perf.data with topology CPU IDs exceeding nr_cpus_avail causes out-of-bounds heap writes into both the cpu2node array and the per-node bitmap. Add a bounds check to skip CPU IDs that fall outside the valid range. Fixes: 1e181b92a2da ("perf c2c report: Add 'node' sort key") Reported-by: sashiko-bot Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-c2c.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index e2a7ffd4192e4..8296ec85e8595 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -2366,6 +2366,10 @@ static int setup_nodes(struct perf_session *session) nodes[node] = set; perf_cpu_map__for_each_cpu_skip_any(cpu, idx, map) { + /* topology CPU IDs from perf.data may exceed nr_cpus_avail */ + if (cpu.cpu < 0 || cpu.cpu >= c2c.cpus_cnt) + continue; + __set_bit(cpu.cpu, set); if (WARN_ONCE(cpu2node[cpu.cpu] != -1, "node/cpu topology bug")) From 340b08cfa751dfdb0020b51ab3f931d1a871d1b9 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 5 Jun 2026 11:12:08 -0300 Subject: [PATCH 1618/2758] perf sched: Clean up idle_threads entry on init failure [ Upstream commit cda5a94ad9181cd60cbf04be11d524201bf489a2 ] get_idle_thread() allocates a thread via thread__new() and stores it in idle_threads[cpu], then calls init_idle_thread() to set up the private data. If init_idle_thread() fails (e.g. OOM for the idle_thread_runtime struct), the function returns NULL but leaves the partially initialized thread in idle_threads[cpu]. On subsequent calls for the same CPU, get_idle_thread() finds a non-NULL idle_threads[cpu], skips allocation, and returns thread__get() on a thread that has no priv data. Callers then get a thread whose thread__priv() returns NULL, leading to unexpected behavior. Release the thread and reset the slot to NULL on init failure so the entry doesn't persist in a corrupted state. Fixes: 49394a2a24c7 ("perf sched timehist: Introduce timehist command") Reported-by: sashiko-bot Cc: David Ahern Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-sched.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index cca9719558463..fc8ae0342dd6c 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -2514,8 +2514,11 @@ static struct thread *get_idle_thread(int cpu) idle_threads[cpu] = thread__new(0, 0); if (idle_threads[cpu]) { - if (init_idle_thread(idle_threads[cpu]) < 0) + if (init_idle_thread(idle_threads[cpu]) < 0) { + /* clean up so next call doesn't find a half-initialized thread */ + thread__zput(idle_threads[cpu]); return NULL; + } } } From 3ba9b69aef73cdecd016d6a2d0284c03248d8f3f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 5 Jun 2026 19:01:35 -0300 Subject: [PATCH 1619/2758] perf sched: Use is_idle_sample() for idle thread runtime cast guard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit c9b3054c99cafd5f5f92158101760992a83e5a5e ] timehist_sched_change_event() uses thread__tid(thread) == 0 to decide whether to cast thread_runtime to idle_thread_runtime. However, a crafted perf.data can set common_pid=0 and common_tid=0 (the perf_sample fields) while prev_pid != 0 (the tracepoint field). is_idle_sample() returns false (it checks prev_pid for sched_switch), so timehist_get_thread() goes through machine__findnew_thread() and returns the machine's TID 0 thread — whose priv data is a regular thread_runtime, not the larger idle_thread_runtime allocated by init_idle_thread(). The subsequent cast to idle_thread_runtime reads past the thread_runtime allocation, accessing itr->last_thread, itr->cursor, and itr->callchain from adjacent heap memory. Writing to itr->last_thread corrupts the heap; calling thread__put() on the OOB value frees an arbitrary pointer. Replace the thread__tid() == 0 check with is_idle_sample(), which uses the tracepoint-specific prev_pid field and correctly identifies whether the sample originated from an idle thread with idle_thread_runtime priv. Fixes: 5d8f17fb5822 ("perf sched timehist: Add -I/--idle-hist option") Reported-by: sashiko-bot Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-sched.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index fc8ae0342dd6c..19a1b13cebacc 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -2904,7 +2904,13 @@ static int timehist_sched_change_event(const struct perf_tool *tool, t = ptime->end; } - if (!sched->idle_hist || thread__tid(thread) == 0) { + /* + * Use is_idle_sample() not thread__tid() == 0: a crafted perf.data + * can set common_pid=0 with prev_pid!=0, giving us a machine thread + * whose priv is thread_runtime, not idle_thread_runtime — the cast + * below would read past the allocation. + */ + if (!sched->idle_hist || is_idle_sample(sample, evsel)) { if (!cpu_list || (sample->cpu < MAX_NR_CPUS && test_bit(sample->cpu, cpu_bitmap))) timehist_update_runtime_stats(tr, t, tprev); From 1f0a529864d8097ede023ba2204455e2de5d72dd Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 5 Jun 2026 11:15:58 -0300 Subject: [PATCH 1620/2758] perf sched: Fix thread reference leak in idle hist processing [ Upstream commit 662d56d48e527ee21a0b03082ee318258a6f7919 ] timehist_sched_change_event() sets itr->last_thread to NULL at the end of idle hist processing without calling thread__put() first. The thread reference was acquired via thread__get() in timehist_get_thread() (line 2581), so every idle context switch leaks a thread reference when --idle-hist is active. Use thread__zput() to properly release the reference before clearing the pointer. Fixes: 5d8f17fb5822 ("perf sched timehist: Add -I/--idle-hist option") Reported-by: sashiko-bot Cc: David Ahern Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-sched.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 19a1b13cebacc..476c54bbb9e37 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -2942,7 +2942,7 @@ static int timehist_sched_change_event(const struct perf_tool *tool, if (itr->cursor.nr) callchain_append(&itr->callchain, &itr->cursor, t - tprev); - itr->last_thread = NULL; + thread__zput(itr->last_thread); } if (!sched->summary_only) From 6380a4f550dc4f4e98f51d167cb4f28650561cc0 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 5 Jun 2026 11:17:03 -0300 Subject: [PATCH 1621/2758] perf sched: Use thread__put() in free_idle_threads() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit a99d6394cd48fed75b1d24733d5afe6837a61a3f ] free_idle_threads() calls thread__delete() directly instead of thread__put(), bypassing the reference counting lifecycle. Under REFCNT_CHECKING builds, this leaks the pointer handle since thread__delete() frees the object without going through the refcount wrapper. The idle threads are created via thread__new() (refcount=1) in get_idle_thread(). Callers get additional references via thread__get() which they release with thread__put(). free_idle_threads() drops the base reference — thread__put() is the correct call, matching the thread__new() acquisition. Fixes: 49394a2a24c7 ("perf sched timehist: Introduce timehist command") Reported-by: sashiko-bot Cc: David Ahern Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-sched.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 476c54bbb9e37..2d424150986d0 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -2481,7 +2481,7 @@ static void free_idle_threads(void) if (itr) thread__put(itr->last_thread); - thread__delete(idle); + thread__put(idle); } } From f09f7be6bba10138727b58b207ab0d7977ec1c82 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 5 Jun 2026 11:26:28 -0300 Subject: [PATCH 1622/2758] perf sched: Replace BUG_ON and add NULL checks in replay event helpers [ Upstream commit 75eafe4a3a93a0143a20c0cc286bfb9008ac1478 ] get_new_event() has three issues: 1. The zalloc() result is dereferenced without a NULL check, crashing on allocation failure. 2. BUG_ON(!task->atoms) kills the process when realloc() fails. Since perf.data is untrusted input, this should be a graceful error. 3. The realloc pattern assigns directly to task->atoms, losing the old pointer on failure. task->nr_events is also incremented before the realloc, leaving corrupted state on failure. Fix get_new_event() to: - Check the zalloc() result before dereferencing - Use a temporary for realloc() to avoid losing the old pointer - Increment nr_events only after successful realloc - Return NULL instead of calling BUG_ON on failure Also fix add_sched_event_wakeup() where zalloc() for wait_sem is passed to sem_init() without a NULL check. Update all callers (add_sched_event_run, add_sched_event_wakeup, add_sched_event_sleep) to handle NULL returns by returning early. The replay may produce incomplete output on OOM but will not crash. Fixes: ec156764d424 ("perf sched: Import schedbench.c") Reported-by: sashiko-bot Cc: Ingo Molnar Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-sched.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 2d424150986d0..d27c2413efc44 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -365,14 +365,25 @@ get_new_event(struct task_desc *task, u64 timestamp) struct sched_atom *event = zalloc(sizeof(*event)); unsigned long idx = task->nr_events; size_t size; + struct sched_atom **atoms_p; + + if (event == NULL) { + pr_err("ERROR: sched: failed to allocate event\n"); + return NULL; + } event->timestamp = timestamp; event->nr = idx; + size = sizeof(struct sched_atom *) * (task->nr_events + 1); + atoms_p = realloc(task->atoms, size); + if (!atoms_p) { + pr_err("ERROR: sched: failed to grow atoms array\n"); + free(event); + return NULL; + } + task->atoms = atoms_p; task->nr_events++; - size = sizeof(struct sched_atom *) * task->nr_events; - task->atoms = realloc(task->atoms, size); - BUG_ON(!task->atoms); task->atoms[idx] = event; @@ -403,6 +414,8 @@ static void add_sched_event_run(struct perf_sched *sched, struct task_desc *task } event = get_new_event(task, timestamp); + if (event == NULL) + return; event->type = SCHED_EVENT_RUN; event->duration = duration; @@ -416,6 +429,8 @@ static void add_sched_event_wakeup(struct perf_sched *sched, struct task_desc *t struct sched_atom *event, *wakee_event; event = get_new_event(task, timestamp); + if (event == NULL) + return; event->type = SCHED_EVENT_WAKEUP; event->wakee = wakee; @@ -430,6 +445,10 @@ static void add_sched_event_wakeup(struct perf_sched *sched, struct task_desc *t } wakee_event->wait_sem = zalloc(sizeof(*wakee_event->wait_sem)); + if (!wakee_event->wait_sem) { + pr_err("ERROR: sched: failed to allocate semaphore\n"); + return; + } sem_init(wakee_event->wait_sem, 0, 0); event->wait_sem = wakee_event->wait_sem; @@ -441,6 +460,9 @@ static void add_sched_event_sleep(struct perf_sched *sched, struct task_desc *ta { struct sched_atom *event = get_new_event(task, timestamp); + if (event == NULL) + return; + event->type = SCHED_EVENT_SLEEP; sched->nr_sleep_events++; From 6cfa75ce9a8232ca350d774cdd63c6bcded6f3b2 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 11:03:29 -0300 Subject: [PATCH 1623/2758] perf mmap: Fix NULL deref in aio cleanup on alloc failure [ Upstream commit 25627346b10e6a564610ea2c49dc6dd54812226d ] perf_mmap__aio_mmap() sets map->aio.nr_cblocks before allocating the data array. If calloc() for aiocb or cblocks fails before the data array is allocated, the return -1 path leads to perf_mmap__aio_munmap() which loops nr_cblocks times calling perf_mmap__aio_free(). Both versions of perf_mmap__aio_free() (NUMA and non-NUMA) dereference map->aio.data[idx] without checking if data is NULL, causing a NULL pointer dereference. Add NULL checks for map->aio.data at the top of both perf_mmap__aio_free() variants so the cleanup path is safe when allocation fails partway through perf_mmap__aio_mmap(). Fixes: d3d1af6f011a553a ("perf record: Enable asynchronous trace writing") Reported-by: sashiko-bot Cc: Alexey Budankov Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/mmap.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/mmap.c b/tools/perf/util/mmap.c index 4404a99eee45f..d64aec6c7c843 100644 --- a/tools/perf/util/mmap.c +++ b/tools/perf/util/mmap.c @@ -89,10 +89,10 @@ static int perf_mmap__aio_alloc(struct mmap *map, int idx) static void perf_mmap__aio_free(struct mmap *map, int idx) { - if (map->aio.data[idx]) { - munmap(map->aio.data[idx], mmap__mmap_len(map)); - map->aio.data[idx] = NULL; - } + if (!map->aio.data || !map->aio.data[idx]) + return; + munmap(map->aio.data[idx], mmap__mmap_len(map)); + map->aio.data[idx] = NULL; } static int perf_mmap__aio_bind(struct mmap *map, int idx, struct perf_cpu cpu, int affinity) @@ -141,6 +141,8 @@ static int perf_mmap__aio_alloc(struct mmap *map, int idx) static void perf_mmap__aio_free(struct mmap *map, int idx) { + if (!map->aio.data) + return; zfree(&(map->aio.data[idx])); } From 423c520416d7ea069c2386a83503b60017158134 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 11:17:45 -0300 Subject: [PATCH 1624/2758] perf stat: Introduce perf_env__get_cpu_topology() to guard NULL env->cpu [ Upstream commit afa4363a91a19dff65dceb7fbce7bba689bbc854 ] process_cpu_topology() in header.c frees env->cpu on old-format perf.data files that predate topology information, but leaves nr_cpus_avail set. The six perf_env__get_*_aggr_by_cpu() functions in builtin-stat.c pass the bounds check but dereference a NULL env->cpu pointer, crashing on old recordings. Introduce perf_env__get_cpu_topology() as a safe accessor that validates env->cpu, cpu.cpu >= 0, and cpu.cpu < nr_cpus_avail in one place, returning a struct cpu_topology_map pointer or NULL. Convert all six topology aggregation callbacks to use it. Fixes: 88031a0de7d68d13 ("perf stat: Switch to cpu version of cpu_map__get()") Reported-by: sashiko-bot Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-stat.c | 51 +++++++++++++++++++++------------------ tools/perf/util/env.h | 14 +++++++++++ 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 9a045811c4197..a04466ea3b0a0 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1637,10 +1637,10 @@ static struct aggr_cpu_id perf_env__get_socket_aggr_by_cpu(struct perf_cpu cpu, { struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); + struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - /* env->cpu[] has env->nr_cpus_avail entries; reject untrusted indices */ - if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) - id.socket = env->cpu[cpu.cpu].socket_id; + if (topo) + id.socket = topo->socket_id; return id; } @@ -1649,15 +1649,16 @@ static struct aggr_cpu_id perf_env__get_die_aggr_by_cpu(struct perf_cpu cpu, voi { struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); + struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { + if (topo) { /* * die_id is relative to socket, so start * with the socket ID and then add die to * make a unique ID. */ - id.socket = env->cpu[cpu.cpu].socket_id; - id.die = env->cpu[cpu.cpu].die_id; + id.socket = topo->socket_id; + id.die = topo->die_id; } return id; @@ -1705,12 +1706,13 @@ static struct aggr_cpu_id perf_env__get_cache_aggr_by_cpu(struct perf_cpu cpu, { struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); + struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { + if (topo) { u32 cache_level = (perf_stat.aggr_level) ?: stat_config.aggr_level; - id.socket = env->cpu[cpu.cpu].socket_id; - id.die = env->cpu[cpu.cpu].die_id; + id.socket = topo->socket_id; + id.die = topo->die_id; perf_env__get_cache_id_for_cpu(cpu, env, cache_level, &id); } @@ -1722,11 +1724,12 @@ static struct aggr_cpu_id perf_env__get_cluster_aggr_by_cpu(struct perf_cpu cpu, { struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); + struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { - id.socket = env->cpu[cpu.cpu].socket_id; - id.die = env->cpu[cpu.cpu].die_id; - id.cluster = env->cpu[cpu.cpu].cluster_id; + if (topo) { + id.socket = topo->socket_id; + id.die = topo->die_id; + id.cluster = topo->cluster_id; } return id; @@ -1736,16 +1739,17 @@ static struct aggr_cpu_id perf_env__get_core_aggr_by_cpu(struct perf_cpu cpu, vo { struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); + struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { + if (topo) { /* * core_id is relative to socket, die and cluster, we need a * global id. So we set socket, die id, cluster id and core id. */ - id.socket = env->cpu[cpu.cpu].socket_id; - id.die = env->cpu[cpu.cpu].die_id; - id.cluster = env->cpu[cpu.cpu].cluster_id; - id.core = env->cpu[cpu.cpu].core_id; + id.socket = topo->socket_id; + id.die = topo->die_id; + id.cluster = topo->cluster_id; + id.core = topo->core_id; } return id; @@ -1755,18 +1759,19 @@ static struct aggr_cpu_id perf_env__get_cpu_aggr_by_cpu(struct perf_cpu cpu, voi { struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); + struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { + if (topo) { /* * core_id is relative to socket and die, * we need a global id. So we set * socket, die id and core id */ - id.socket = env->cpu[cpu.cpu].socket_id; - id.die = env->cpu[cpu.cpu].die_id; - id.core = env->cpu[cpu.cpu].core_id; - id.cpu = cpu; + id.socket = topo->socket_id; + id.die = topo->die_id; + id.core = topo->core_id; } + id.cpu = cpu; return id; } diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h index c7052ac1f8562..25ba16290280d 100644 --- a/tools/perf/util/env.h +++ b/tools/perf/util/env.h @@ -185,6 +185,20 @@ const char *perf_env__pmu_mappings(struct perf_env *env); int perf_env__read_cpu_topology_map(struct perf_env *env); +/* + * Safe accessor for env->cpu[] topology array. env->cpu can be NULL when + * reading old-format perf.data that predates topology information — + * process_cpu_topology() in header.c frees it while nr_cpus_avail remains + * set, so callers must not index env->cpu[] without this check. + */ +static inline struct cpu_topology_map * +perf_env__get_cpu_topology(struct perf_env *env, struct perf_cpu cpu) +{ + if (env->cpu && cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) + return &env->cpu[cpu.cpu]; + return NULL; +} + void cpu_cache_level__free(struct cpu_cache_level *cache); const char *perf_env__arch(struct perf_env *env); From 231acb6d0e14609ea83bdf4bc3eca738085eefdd Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 11:19:10 -0300 Subject: [PATCH 1625/2758] perf c2c: Fix use-after-free in he__get_c2c_hists() error path [ Upstream commit 5e5e6196d737c5be03d20647428316b36621608d ] he__get_c2c_hists() assigns c2c_he->hists before calling c2c_hists__init(). If init fails, the error path calls free(hists) but leaves c2c_he->hists pointing to freed memory. On teardown, c2c_he_free() finds the non-NULL pointer and calls hists__delete_entries() on it, causing a use-after-free. Set c2c_he->hists to NULL before freeing so teardown skips the already-freed allocation. Fixes: b2252ae67b687d2b ("perf c2c report: Decode c2c_stats for hist entries") Reported-by: sashiko-bot Cc: Jiri Olsa Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-c2c.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index 8296ec85e8595..74ded7d238c80 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -221,6 +221,7 @@ he__get_c2c_hists(struct hist_entry *he, ret = c2c_hists__init(hists, sort, nr_header_lines, env); if (ret) { + c2c_he->hists = NULL; free(hists); return NULL; } From cb47a3546f523bcbf5d77d23ed83aa53f7acb43a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 11:21:32 -0300 Subject: [PATCH 1626/2758] perf timechart: Fix cpu2y() OOB read on untrusted CPU index [ Upstream commit e2496db45bfd8dfb6154ec415798fee330f1cc0a ] cpu2y() indexes topology_map[cpu] without bounds checking. The array is allocated with nr_cpus entries (from env->nr_cpus_online), but callers pass sample CPU values from perf.data which can exceed that size with cross-machine recordings. Track the topology_map allocation size and bounds-check the CPU argument in cpu2y() before indexing. Out-of-bounds CPUs fall back to the identity mapping (cpu2slot(cpu)), which is the same behavior as when no topology is available. Fixes: c507999790438cde ("perf timechart: Add support for topology") Reported-by: sashiko-bot Cc: Stanislav Fomichev Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/svghelper.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/svghelper.c b/tools/perf/util/svghelper.c index e360e7736c7ba..826bd2577344b 100644 --- a/tools/perf/util/svghelper.c +++ b/tools/perf/util/svghelper.c @@ -47,13 +47,13 @@ static double cpu2slot(int cpu) } static int *topology_map; +static int topology_map_size; static double cpu2y(int cpu) { - if (topology_map) + if (topology_map && cpu >= 0 && cpu < topology_map_size) return cpu2slot(topology_map[cpu]) * SLOT_MULT; - else - return cpu2slot(cpu) * SLOT_MULT; + return cpu2slot(cpu) * SLOT_MULT; } static double time2pixels(u64 __time) @@ -736,7 +736,8 @@ static int str_to_bitmap(char *s, cpumask_t *b, int nr_cpus) return -1; perf_cpu_map__for_each_cpu(cpu, idx, map) { - if (cpu.cpu >= nr_cpus) { + /* perf_cpu_map__new("") returns cpu.cpu == -1 */ + if (cpu.cpu < 0 || cpu.cpu >= nr_cpus) { ret = -1; break; } @@ -794,6 +795,7 @@ int svg_build_topology_map(struct perf_env *env) fprintf(stderr, "topology: no memory\n"); goto exit; } + topology_map_size = nr_cpus; for (i = 0; i < nr_cpus; i++) topology_map[i] = -1; From 5e7c076511bf4738c20ef35aa56e59d71f8ac9cf Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 11:25:40 -0300 Subject: [PATCH 1627/2758] perf tools: Fix int16_t truncation of max_cpu_num in set_max_cpu_num() [ Upstream commit 33fa2bf5608fc36bc25231592145f4738f14f11b ] set_max_cpu_num() assigns the sysfs "possible" CPU count to max_cpu_num.cpu which is int16_t (struct perf_cpu). On systems with >32767 possible CPUs the value silently truncates, potentially wrapping negative. This causes cpunode_map to be underallocated and subsequent cpu__get_node() calls to read out of bounds. The matching check for max_present_cpu_num was added by commit c760174401f6 ("perf cpumap: Reduce cpu size from int to int16_t") but max_cpu_num was missed. Add the same INT16_MAX guard. Fixes: c760174401f605cf ("perf cpumap: Reduce cpu size from int to int16_t") Reported-by: sashiko-bot Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/cpumap.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 19783c8376572..b754e331bdfe8 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -466,6 +466,16 @@ static void set_max_cpu_num(void) if (ret) goto out; + /* + * struct perf_cpu.cpu is int16_t (libperf ABI) — clamp to avoid + * truncation to negative. See tools/lib/perf/TODO for the ABI + * widening plan. + */ + if (max > INT16_MAX) { + pr_warning("WARNING: max possible cpus %d exceeds int16_t, clamping to %d\n", + max, INT16_MAX); + max = INT16_MAX; + } max_cpu_num.cpu = max; /* get the highest present cpu number for a sparse allocation */ @@ -478,11 +488,12 @@ static void set_max_cpu_num(void) ret = get_max_num(path, &max); if (!ret && max > INT16_MAX) { - pr_err("Read out of bounds max cpus of %d\n", max); - ret = -1; + pr_warning("WARNING: max present cpus %d exceeds int16_t, clamping to %d\n", + max, INT16_MAX); + max = INT16_MAX; } if (!ret) - max_present_cpu_num.cpu = (int16_t)max; + max_present_cpu_num.cpu = max; out: if (ret) pr_err("Failed to read max cpus, using default of %d\n", max_cpu_num.cpu); @@ -619,7 +630,9 @@ int cpu__setup_cpunode_map(void) while ((dent2 = readdir(dir2)) != NULL) { if (dent2->d_type != DT_LNK || sscanf(dent2->d_name, "cpu%u", &cpu) < 1) continue; - cpunode_map[cpu] = mem; + /* cpunode_map allocated for max_cpu_num entries */ + if (cpu < (unsigned int)max_cpu_num.cpu) + cpunode_map[cpu] = mem; } closedir(dir2); } From a0c08cdaf63a09cf16ca0efd2a02c89f1fa1c1a8 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 11:33:41 -0300 Subject: [PATCH 1628/2758] perf sched: Free callchain nodes in idle thread cleanup [ Upstream commit c3e51ed45ffa7547495a851e33ce332f81ef3665 ] free_idle_threads() relies on the thread priv destructor (free()) to clean up idle_thread_runtime structs. But free() doesn't walk the callchain_cursor linked list or the callchain_root tree allocated by callchain_cursor__copy() and callchain_append() during --idle-hist processing. Every idle thread with callchain data leaks these nodes. Introduce callchain_cursor_cleanup() to free the cursor's linked list of callchain_cursor_node entries, and call it together with free_callchain() in free_idle_threads() before thread__put(). Fixes: 225b24f569980ac9 ("perf sched timehist: Save callchain when entering idle") Reported-by: sashiko-bot Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-sched.c | 5 ++++- tools/perf/util/callchain.c | 15 +++++++++++++++ tools/perf/util/callchain.h | 1 + 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index d27c2413efc44..165c24d9781cb 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -2500,8 +2500,11 @@ static void free_idle_threads(void) struct idle_thread_runtime *itr; itr = thread__priv(idle); - if (itr) + if (itr) { thread__put(itr->last_thread); + free_callchain(&itr->callchain); + callchain_cursor_cleanup(&itr->cursor); + } thread__put(idle); } diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index f031cbbeeba8b..5e0498a1faf8a 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -1578,6 +1578,21 @@ void free_callchain(struct callchain_root *root) free_callchain_node(&root->node); } +void callchain_cursor_cleanup(struct callchain_cursor *cursor) +{ + struct callchain_cursor_node *node, *next; + + callchain_cursor_reset(cursor); + + for (node = cursor->first; node; node = next) { + next = node->next; + free(node); + } + cursor->first = NULL; + cursor->last = &cursor->first; + cursor->curr = NULL; +} + static u64 decay_callchain_node(struct callchain_node *node) { struct callchain_node *child; diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 06d463ccc7a04..60008f379b228 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -290,6 +290,7 @@ int callchain_list_counts__printf_value(struct callchain_list *clist, FILE *fp, char *bf, int bfsize); void free_callchain(struct callchain_root *root); +void callchain_cursor_cleanup(struct callchain_cursor *cursor); void decay_callchain(struct callchain_root *root); int callchain_node__make_parent_list(struct callchain_node *node); From ca461a2a7390084360c865a1338c0e9b4f1a4de7 Mon Sep 17 00:00:00 2001 From: Jagadeesh Kona Date: Thu, 7 May 2026 11:08:27 +0530 Subject: [PATCH 1629/2758] dt-bindings: clock: qcom: Add X1P42100 camera clock controller [ Upstream commit 97a5e120be5d3d7cf7d221b8703921046b73f0d2 ] Add X1P42100 camera clock controller support and clock bindings for camera QDSS debug clocks which are applicable for both X1E80100 and X1P42100 platforms. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Jagadeesh Kona Link: https://lore.kernel.org/r/20260507-purwa-videocc-camcc-v5-2-fc3af4130282@oss.qualcomm.com Signed-off-by: Bjorn Andersson Stable-dep-of: 1e6ae74ac6f2 ("clk: qcom: camcc-x1e80100: Add support for camera QDSS debug clocks") Signed-off-by: Sasha Levin --- .../devicetree/bindings/clock/qcom,x1e80100-camcc.yaml | 1 + include/dt-bindings/clock/qcom,x1e80100-camcc.h | 3 +++ 2 files changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/clock/qcom,x1e80100-camcc.yaml b/Documentation/devicetree/bindings/clock/qcom,x1e80100-camcc.yaml index 938a2f1ff3fca..b28614186cc09 100644 --- a/Documentation/devicetree/bindings/clock/qcom,x1e80100-camcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,x1e80100-camcc.yaml @@ -23,6 +23,7 @@ properties: compatible: enum: - qcom,x1e80100-camcc + - qcom,x1p42100-camcc reg: maxItems: 1 diff --git a/include/dt-bindings/clock/qcom,x1e80100-camcc.h b/include/dt-bindings/clock/qcom,x1e80100-camcc.h index d72fdfb06a7c7..06c316022fb0d 100644 --- a/include/dt-bindings/clock/qcom,x1e80100-camcc.h +++ b/include/dt-bindings/clock/qcom,x1e80100-camcc.h @@ -115,6 +115,9 @@ #define CAM_CC_SLEEP_CLK_SRC 105 #define CAM_CC_SLOW_AHB_CLK_SRC 106 #define CAM_CC_XO_CLK_SRC 107 +#define CAM_CC_QDSS_DEBUG_CLK 108 +#define CAM_CC_QDSS_DEBUG_CLK_SRC 109 +#define CAM_CC_QDSS_DEBUG_XO_CLK 110 /* CAM_CC power domains */ #define CAM_CC_BPS_GDSC 0 From 2ce2a2e19b62d962fd7df766784b59d70634066d Mon Sep 17 00:00:00 2001 From: Jagadeesh Kona Date: Thu, 7 May 2026 11:08:29 +0530 Subject: [PATCH 1630/2758] clk: qcom: camcc-x1e80100: Add support for camera QDSS debug clocks [ Upstream commit 1e6ae74ac6f28ace7a0eb84897c6e17bb044e5de ] Add support for camera QDSS debug clocks on X1E80100 platform which are required to be voted for camera icp and cpas usecases. This change aligns the camcc driver to the new ABI exposed from X1E80100 camcc bindings that supports these camcc QDSS debug clocks. Reviewed-by: Konrad Dybcio Reviewed-by: Bryan O'Donoghue Signed-off-by: Jagadeesh Kona Fixes: 76126a5129b5 ("clk: qcom: Add camcc clock driver for x1e80100") Link: https://lore.kernel.org/r/20260507-purwa-videocc-camcc-v5-4-fc3af4130282@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- drivers/clk/qcom/camcc-x1e80100.c | 64 +++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/drivers/clk/qcom/camcc-x1e80100.c b/drivers/clk/qcom/camcc-x1e80100.c index 81f579ff69933..c12994af42cfc 100644 --- a/drivers/clk/qcom/camcc-x1e80100.c +++ b/drivers/clk/qcom/camcc-x1e80100.c @@ -1052,6 +1052,31 @@ static struct clk_rcg2 cam_cc_mclk7_clk_src = { }, }; +static const struct freq_tbl ftbl_cam_cc_qdss_debug_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(60000000, P_CAM_CC_PLL8_OUT_EVEN, 8, 0, 0), + F(75000000, P_CAM_CC_PLL0_OUT_EVEN, 8, 0, 0), + F(150000000, P_CAM_CC_PLL0_OUT_EVEN, 4, 0, 0), + F(300000000, P_CAM_CC_PLL0_OUT_MAIN, 4, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_qdss_debug_clk_src = { + .cmd_rcgr = 0x13938, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_qdss_debug_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_qdss_debug_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + static const struct freq_tbl ftbl_cam_cc_sfe_0_clk_src[] = { F(345600000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0), F(432000000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0), @@ -2182,6 +2207,42 @@ static struct clk_branch cam_cc_mclk7_clk = { }, }; +static struct clk_branch cam_cc_qdss_debug_clk = { + .halt_reg = 0x13a64, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13a64, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_qdss_debug_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_qdss_debug_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_qdss_debug_xo_clk = { + .halt_reg = 0x13a68, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13a68, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_qdss_debug_xo_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + static struct clk_branch cam_cc_sfe_0_clk = { .halt_reg = 0x133c0, .halt_check = BRANCH_HALT, @@ -2398,6 +2459,9 @@ static struct clk_regmap *cam_cc_x1e80100_clocks[] = { [CAM_CC_PLL6_OUT_EVEN] = &cam_cc_pll6_out_even.clkr, [CAM_CC_PLL8] = &cam_cc_pll8.clkr, [CAM_CC_PLL8_OUT_EVEN] = &cam_cc_pll8_out_even.clkr, + [CAM_CC_QDSS_DEBUG_CLK] = &cam_cc_qdss_debug_clk.clkr, + [CAM_CC_QDSS_DEBUG_CLK_SRC] = &cam_cc_qdss_debug_clk_src.clkr, + [CAM_CC_QDSS_DEBUG_XO_CLK] = &cam_cc_qdss_debug_xo_clk.clkr, [CAM_CC_SFE_0_CLK] = &cam_cc_sfe_0_clk.clkr, [CAM_CC_SFE_0_CLK_SRC] = &cam_cc_sfe_0_clk_src.clkr, [CAM_CC_SFE_0_FAST_AHB_CLK] = &cam_cc_sfe_0_fast_ahb_clk.clkr, From 89acfa8ad3af8c3e0152d427932daebd468ec466 Mon Sep 17 00:00:00 2001 From: "Pratyush Yadav (Google)" Date: Fri, 5 Jun 2026 18:06:44 +0200 Subject: [PATCH 1631/2758] docs: memfd_preservation: fix rendering of ABI documentation [ Upstream commit 3a358c78093f98a70d84c934b7054f636bc846f2 ] The "memfd Live Update ABI" section in include/linux/kho/abi/memfd.h currently does not render in the exported documentation. This is because it should not include the "DOC:" in its reference. Drop it to ensure correct rendering. Tested by running make htmldocs. Fixes: 15fc11bb2cb6 ("docs: add documentation for memfd preservation via LUO") Signed-off-by: Pratyush Yadav (Google) Tested-by: Randy Dunlap Acked-by: Randy Dunlap Link: https://patch.msgid.link/20260605160645.3650271-1-pratyush@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Signed-off-by: Sasha Levin --- Documentation/mm/memfd_preservation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/mm/memfd_preservation.rst b/Documentation/mm/memfd_preservation.rst index a8a5b476afd30..c908a12dffa7c 100644 --- a/Documentation/mm/memfd_preservation.rst +++ b/Documentation/mm/memfd_preservation.rst @@ -11,7 +11,7 @@ Memfd Preservation ABI ====================== .. kernel-doc:: include/linux/kho/abi/memfd.h - :doc: DOC: memfd Live Update ABI + :doc: memfd Live Update ABI .. kernel-doc:: include/linux/kho/abi/memfd.h :internal: From fb372cbccab661a6beeb7d25ce9b91bbae8b1321 Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Thu, 16 Apr 2026 22:18:05 +0800 Subject: [PATCH 1632/2758] mshv: add bounds check on vp_index in mshv_intercept_isr() [ Upstream commit a4ffc59238be84dd1c26bf1c001543e832674fc6 ] mshv_intercept_isr() extracts vp_index from the hypervisor message payload and uses it directly to index into pt_vp_array without validation. handle_bitset_message() and handle_pair_message() already validate vp_index against MSHV_MAX_VPS before array access. Add the same MSHV_MAX_VPS bounds check for consistency with the other message handlers. Fixes: 621191d709b1 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") Reported-by: Yuhao Jiang Signed-off-by: Junrui Luo Signed-off-by: Wei Liu Signed-off-by: Sasha Levin --- drivers/hv/mshv_synic.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/hv/mshv_synic.c b/drivers/hv/mshv_synic.c index 88170ce6b83f0..7c168e5a740dd 100644 --- a/drivers/hv/mshv_synic.c +++ b/drivers/hv/mshv_synic.c @@ -384,6 +384,11 @@ mshv_intercept_isr(struct hv_message *msg) */ vp_index = ((struct hv_opaque_intercept_message *)msg->u.payload)->vp_index; + /* This shouldn't happen, but just in case. */ + if (unlikely(vp_index >= MSHV_MAX_VPS)) { + pr_debug("VP index %u out of bounds\n", vp_index); + goto unlock_out; + } vp = partition->pt_vp_array[vp_index]; if (unlikely(!vp)) { pr_debug("failed to find VP %u\n", vp_index); From c1a2159c11004c338f2f3ae16857f478cd9809ed Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Tue, 2 Jun 2026 15:03:44 +0800 Subject: [PATCH 1633/2758] dmaengine: qcom: gpi: set DMA_PRIVATE capability [ Upstream commit 4e351f408743354d54ee1af5193fc78234f2044e ] The GPI DMA controller is only responsible for QUP peripherals, and cannot work as a general-purpose DMA accelerator. Set DMA_PRIVATE capability for it. This fixes error messages about GPI being shown when an async-tx consumer is loaded. Fixes: 5d0c3533a19f ("dmaengine: qcom: Add GPI dma driver") Signed-off-by: Icenowy Zheng Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260602070344.3707256-1-zhengxingda@iscas.ac.cn Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/dma/qcom/gpi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/dma/qcom/gpi.c b/drivers/dma/qcom/gpi.c index c9a6f610ffd9f..a5055a6273af6 100644 --- a/drivers/dma/qcom/gpi.c +++ b/drivers/dma/qcom/gpi.c @@ -2260,6 +2260,7 @@ static int gpi_probe(struct platform_device *pdev) /* clear and Set capabilities */ dma_cap_zero(gpi_dev->dma_device.cap_mask); dma_cap_set(DMA_SLAVE, gpi_dev->dma_device.cap_mask); + dma_cap_set(DMA_PRIVATE, gpi_dev->dma_device.cap_mask); /* configure dmaengine apis */ gpi_dev->dma_device.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); From 0bc191050c3253da8ef4bfaf157c44efed6e22cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Fri, 24 Apr 2026 18:40:14 +0100 Subject: [PATCH 1634/2758] dmaengine: Fix possible use after free MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 92f853f0645aebf1d05d333e97ab7c342ace1892 ] In dma_release_channel(), check chan->device->privatecnt after call dma_chan_put(). However, dma_chan_put() call dma_device_put() which could release the last reference of the device if the DMA provider is already gone and hence free it. Fixes it by moving dma_chan_put() after the check. Fixes: 0f571515c332 ("dmaengine: Add privatecnt to revert DMA_PRIVATE property") Signed-off-by: Nuno Sá Reviewed-by: Frank Li Link: https://patch.msgid.link/20260424-dma-dmac-handle-vunmap-v4-1-90f43412fdc0@analog.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/dma/dmaengine.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 405bd2fbb4a3b..9049171df8578 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -905,11 +905,12 @@ void dma_release_channel(struct dma_chan *chan) mutex_lock(&dma_list_mutex); WARN_ONCE(chan->client_count != 1, "chan reference count %d != 1\n", chan->client_count); - dma_chan_put(chan); /* drop PRIVATE cap enabled by __dma_request_channel() */ if (--chan->device->privatecnt == 0) dma_cap_clear(DMA_PRIVATE, chan->device->cap_mask); + dma_chan_put(chan); + if (chan->slave) { sysfs_remove_link(&chan->dev->device.kobj, DMA_SLAVE_NAME); sysfs_remove_link(&chan->slave->kobj, chan->name); From f055829151eed5a9ec4af98a6c1ce75dbaf2e372 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Fri, 24 Apr 2026 18:40:15 +0100 Subject: [PATCH 1635/2758] dmaengine: dma-axi-dmac: Properly free struct axi_dmac_desc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 4910ce1b3b35687bb2a5e742c4bfbea3c647c980 ] Use axi_dmac_free_desc() to free fully the descriptor at fail path when call axi_dmac_alloc_desc() in axi_dmac_prep_peripheral_dma_vec(). Fixes: 74609e568670 ("dmaengine: dma-axi-dmac: Implement device_prep_peripheral_dma_vec") Signed-off-by: Nuno Sá Reviewed-by: Frank Li Link: https://patch.msgid.link/20260424-dma-dmac-handle-vunmap-v4-2-90f43412fdc0@analog.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/dma/dma-axi-dmac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c index 45c2c8e4bc451..127c3cf80a0e5 100644 --- a/drivers/dma/dma-axi-dmac.c +++ b/drivers/dma/dma-axi-dmac.c @@ -769,7 +769,7 @@ axi_dmac_prep_peripheral_dma_vec(struct dma_chan *c, const struct dma_vec *vecs, for (i = 0; i < nb; i++) { if (!axi_dmac_check_addr(chan, vecs[i].addr) || !axi_dmac_check_len(chan, vecs[i].len)) { - kfree(desc); + axi_dmac_free_desc(desc); return NULL; } From 65e82fa24965b2eb6ad9412f6c530ed9a50a625f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Fri, 24 Apr 2026 18:40:17 +0100 Subject: [PATCH 1636/2758] dmaengine: dma-axi-dmac: use DMA pool to manange DMA descriptor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 9e942c8579130e62734c14338e9f451780669164 ] For architectures like Microblaze or arm64 (where this IP is used), DMA_DIRECT_REMAP is set which means that dma_alloc_coherent() might remap (and hence vmalloc()) some memory. This became visible in a design where dma_direct_use_pool() is not possible. With the above, when calling dma_free_coherent(), vunmap() would be called from softirq context and thus leading to a BUG(). To fix it, use a dma pool that is allocated in .device_alloc_chan_resources() and allocate blocks from it. The key point is that now dma_pool_free() is used in axi_dmac_free_desc() to free the blocks and that just frees the blocks from the pool in the sense they can be used again. In other words, no actual call to dma_free_coherent() happens. That only happens when destroying the pool in axi_dmac_free_chan_resources() which does not happen in any interrupt context. Fixes: 3f8fd25936ee ("dmaengine: axi-dmac: Allocate hardware descriptors") Signed-off-by: Nuno Sá Reviewed-by: Frank Li Link: https://patch.msgid.link/20260424-dma-dmac-handle-vunmap-v4-4-90f43412fdc0@analog.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/dma/dma-axi-dmac.c | 66 +++++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c index 127c3cf80a0e5..ce5cabc460651 100644 --- a/drivers/dma/dma-axi-dmac.c +++ b/drivers/dma/dma-axi-dmac.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -147,6 +148,7 @@ struct axi_dmac_chan { struct virt_dma_chan vchan; struct axi_dmac_desc *next_desc; + void *pool; struct list_head active_descs; enum dma_transfer_direction direction; @@ -650,11 +652,17 @@ static void axi_dmac_issue_pending(struct dma_chan *c) spin_unlock_irqrestore(&chan->vchan.lock, flags); } +static void axi_dmac_free_desc(struct axi_dmac_desc *desc) +{ + for (unsigned int i = 0; i < desc->num_sgs; i++) + dma_pool_free(desc->chan->pool, desc->sg[i].hw, desc->sg[i].hw_phys); + + kfree(desc); +} + static struct axi_dmac_desc * axi_dmac_alloc_desc(struct axi_dmac_chan *chan, unsigned int num_sgs) { - struct axi_dmac *dmac = chan_to_axi_dmac(chan); - struct device *dev = dmac->dma_dev.dev; struct axi_dmac_hw_desc *hws; struct axi_dmac_desc *desc; dma_addr_t hw_phys; @@ -666,22 +674,22 @@ axi_dmac_alloc_desc(struct axi_dmac_chan *chan, unsigned int num_sgs) desc->num_sgs = num_sgs; desc->chan = chan; - hws = dma_alloc_coherent(dev, PAGE_ALIGN(num_sgs * sizeof(*hws)), - &hw_phys, GFP_ATOMIC); - if (!hws) { - kfree(desc); - return NULL; - } - for (i = 0; i < num_sgs; i++) { - desc->sg[i].hw = &hws[i]; - desc->sg[i].hw_phys = hw_phys + i * sizeof(*hws); + hws = dma_pool_zalloc(chan->pool, GFP_NOWAIT, &hw_phys); + if (!hws) { + desc->num_sgs = i; + axi_dmac_free_desc(desc); + return NULL; + } - hws[i].id = AXI_DMAC_SG_UNUSED; - hws[i].flags = 0; + desc->sg[i].hw = hws; + desc->sg[i].hw_phys = hw_phys; + + hws->id = AXI_DMAC_SG_UNUSED; /* Link hardware descriptors */ - hws[i].next_sg_addr = hw_phys + (i + 1) * sizeof(*hws); + if (i) + desc->sg[i - 1].hw->next_sg_addr = hw_phys; } /* The last hardware descriptor will trigger an interrupt */ @@ -690,18 +698,6 @@ axi_dmac_alloc_desc(struct axi_dmac_chan *chan, unsigned int num_sgs) return desc; } -static void axi_dmac_free_desc(struct axi_dmac_desc *desc) -{ - struct axi_dmac *dmac = chan_to_axi_dmac(desc->chan); - struct device *dev = dmac->dma_dev.dev; - struct axi_dmac_hw_desc *hw = desc->sg[0].hw; - dma_addr_t hw_phys = desc->sg[0].hw_phys; - - dma_free_coherent(dev, PAGE_ALIGN(desc->num_sgs * sizeof(*hw)), - hw, hw_phys); - kfree(desc); -} - static struct axi_dmac_sg *axi_dmac_fill_linear_sg(struct axi_dmac_chan *chan, enum dma_transfer_direction direction, dma_addr_t addr, unsigned int num_periods, unsigned int period_len, @@ -935,9 +931,26 @@ static struct dma_async_tx_descriptor *axi_dmac_prep_interleaved( return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags); } +static int axi_dmac_alloc_chan_resources(struct dma_chan *c) +{ + struct axi_dmac_chan *chan = to_axi_dmac_chan(c); + struct device *dev = c->device->dev; + + chan->pool = dma_pool_create(dev_name(dev), dev, + sizeof(struct axi_dmac_hw_desc), + __alignof__(struct axi_dmac_hw_desc), 0); + if (!chan->pool) + return -ENOMEM; + + return 0; +} + static void axi_dmac_free_chan_resources(struct dma_chan *c) { + struct axi_dmac_chan *chan = to_axi_dmac_chan(c); + vchan_free_chan_resources(to_virt_chan(c)); + dma_pool_destroy(chan->pool); } static void axi_dmac_desc_free(struct virt_dma_desc *vdesc) @@ -1239,6 +1252,7 @@ static int axi_dmac_probe(struct platform_device *pdev) dma_cap_set(DMA_SLAVE, dma_dev->cap_mask); dma_cap_set(DMA_CYCLIC, dma_dev->cap_mask); dma_cap_set(DMA_INTERLEAVE, dma_dev->cap_mask); + dma_dev->device_alloc_chan_resources = axi_dmac_alloc_chan_resources; dma_dev->device_free_chan_resources = axi_dmac_free_chan_resources; dma_dev->device_tx_status = dma_cookie_status; dma_dev->device_issue_pending = axi_dmac_issue_pending; From e2414f2a3f1282b726a90b494137afb57bb86208 Mon Sep 17 00:00:00 2001 From: Phillip Varney Date: Fri, 5 Jun 2026 00:55:45 +0000 Subject: [PATCH 1637/2758] clk: qcom: a53: Corrected frequency multiplier for 1152MHz [ Upstream commit bb56147ea9fce98ebde1d367335ba006cba61fbd ] The 1152MHz frequency entry for the a53 currently selects a multiplier of 62, giving 1190MHz. This changes the mulitiplier to 60 giving the intended 1152MHz. Signed-off-by: Phillip Varney Reviewed-by: Konrad Dybcio Fixes: 0c6ab1b8f894 ("clk: qcom: Add A53 PLL support") Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260605005502.313928-1-pbvarney@protonmail.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- drivers/clk/qcom/a53-pll.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/qcom/a53-pll.c b/drivers/clk/qcom/a53-pll.c index 724a642311e50..0549b214fcfc8 100644 --- a/drivers/clk/qcom/a53-pll.c +++ b/drivers/clk/qcom/a53-pll.c @@ -20,7 +20,7 @@ static const struct pll_freq_tbl a53pll_freq[] = { { 998400000, 52, 0x0, 0x1, 0 }, { 1094400000, 57, 0x0, 0x1, 0 }, - { 1152000000, 62, 0x0, 0x1, 0 }, + { 1152000000, 60, 0x0, 0x1, 0 }, { 1209600000, 63, 0x0, 0x1, 0 }, { 1248000000, 65, 0x0, 0x1, 0 }, { 1363200000, 71, 0x0, 0x1, 0 }, From f0dfbca47b9e342a4649d0b7d33131d58aeaf2c0 Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Wed, 13 May 2026 17:28:59 +0800 Subject: [PATCH 1638/2758] sunrpc: Fix error handling in rpc_sysfs_xprt_switch_add_xprt_store() [ Upstream commit 37957478be021b92981aa4c99b69f308d3b784d0 ] xprt_create_transport() never returns NULL, only valid pointers or error pointers. Using IS_ERR_OR_NULL() is incorrect, and PTR_ERR(NULL) would return 0, which indicates EOF in a sysfs store function. Fix this by using IS_ERR() instead of IS_ERR_OR_NULL(). Fixes: df210d9b0951 ("sunrpc: Add a sysfs file for adding a new xprt") Signed-off-by: Hongling Zeng Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- net/sunrpc/sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sunrpc/sysfs.c b/net/sunrpc/sysfs.c index a90480f801548..49686bf740e69 100644 --- a/net/sunrpc/sysfs.c +++ b/net/sunrpc/sysfs.c @@ -348,7 +348,7 @@ static ssize_t rpc_sysfs_xprt_switch_add_xprt_store(struct kobject *kobj, xprt_create_args.reconnect_timeout = xprt->max_reconnect_timeout; new = xprt_create_transport(&xprt_create_args); - if (IS_ERR_OR_NULL(new)) { + if (IS_ERR(new)) { count = PTR_ERR(new); goto out_put_xprt; } From fa977d37765bb2746d26ea5bc00d287c241de162 Mon Sep 17 00:00:00 2001 From: Sagi Grimberg Date: Wed, 13 May 2026 09:58:24 +0300 Subject: [PATCH 1639/2758] pNFS/filelayout: fix cheking if a layout is striped [ Upstream commit 91668417d4e925c98cae4a55b1b9860380ddbf16 ] A layout can still be striped with num_fh = 1 as it is perfectly possible that both MDS and DSs can handle the same filehandle. Hence check according to stripe_count > 1, which is the correct check to begin with. We should not be called with flseg->dsaddr = NULL, but if for some reason we do, return our best guess with is flseg->num_fh > 1. Fixes: a6b9d2fa0024 ("pNFS/filelayout: Fix coalescing test for single DS") Signed-off-by: Sagi Grimberg Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- fs/nfs/filelayout/filelayout.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/nfs/filelayout/filelayout.c b/fs/nfs/filelayout/filelayout.c index e85380e3b11d7..70f2cbd46960f 100644 --- a/fs/nfs/filelayout/filelayout.c +++ b/fs/nfs/filelayout/filelayout.c @@ -778,6 +778,8 @@ filelayout_alloc_lseg(struct pnfs_layout_hdr *layoutid, static bool filelayout_lseg_is_striped(const struct nfs4_filelayout_segment *flseg) { + if (flseg->dsaddr) + return flseg->dsaddr->stripe_count > 1; return flseg->num_fh > 1; } From 7c42bc9cb7d3d9b573b0b3b4fb9017c374587780 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 26 May 2026 10:14:01 -0400 Subject: [PATCH 1640/2758] xprtrdma: Use sendctx DMA state for Send signaling [ Upstream commit 2797ae7c929610fb2d2303a996a08173fa096730 ] Send signaling matters only when the prepared Send has page mappings to unmap. Today that test is expressed indirectly with rl_kref, because the Send-side reference is taken only for Sends with mapped SGEs. Split the SGE DMA unmap loop into its own helper and use sc_unmap_count directly for the signaling decision. This keeps the current behavior but removes one dependency on the old rl_kref semantics before the request lifetime rules are changed. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker Stable-dep-of: e786233d2e0b ("xprtrdma: Decouple req recycling from RPC completion") Signed-off-by: Sasha Levin --- net/sunrpc/xprtrdma/frwr_ops.c | 2 +- net/sunrpc/xprtrdma/rpc_rdma.c | 22 +++++++++++++--------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/net/sunrpc/xprtrdma/frwr_ops.c b/net/sunrpc/xprtrdma/frwr_ops.c index 7f79a0a2601e6..e5c71cf705a33 100644 --- a/net/sunrpc/xprtrdma/frwr_ops.c +++ b/net/sunrpc/xprtrdma/frwr_ops.c @@ -474,7 +474,7 @@ int frwr_send(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req) ++num_wrs; } - if ((kref_read(&req->rl_kref) > 1) || num_wrs > ep->re_send_count) { + if (req->rl_sendctx->sc_unmap_count || num_wrs > ep->re_send_count) { send_wr->send_flags |= IB_SEND_SIGNALED; ep->re_send_count = min_t(unsigned int, ep->re_send_batch, num_wrs - ep->re_send_count); diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index 0e0f219747109..16b9987858d68 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -477,19 +477,11 @@ static void rpcrdma_sendctx_done(struct kref *kref) rep->rr_rxprt->rx_stats.reply_waits_for_send++; } -/** - * rpcrdma_sendctx_unmap - DMA-unmap Send buffer - * @sc: sendctx containing SGEs to unmap - * - */ -void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc) +static void rpcrdma_sendctx_dma_unmap(struct rpcrdma_sendctx *sc) { struct rpcrdma_regbuf *rb = sc->sc_req->rl_sendbuf; struct ib_sge *sge; - if (!sc->sc_unmap_count) - return; - /* The first two SGEs contain the transport header and * the inline buffer. These are always left mapped so * they can be cheaply re-used. @@ -498,7 +490,19 @@ void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc) ++sge, --sc->sc_unmap_count) ib_dma_unmap_page(rdmab_device(rb), sge->addr, sge->length, DMA_TO_DEVICE); +} + +/** + * rpcrdma_sendctx_unmap - DMA-unmap Send buffer + * @sc: sendctx containing SGEs to unmap + * + */ +void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc) +{ + if (!sc->sc_unmap_count) + return; + rpcrdma_sendctx_dma_unmap(sc); kref_put(&sc->sc_req->rl_kref, rpcrdma_sendctx_done); } From 8203f760a72bd39a3b66bc4eff0aa272a99fe22b Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 26 May 2026 10:14:02 -0400 Subject: [PATCH 1641/2758] xprtrdma: Decouple req recycling from RPC completion [ Upstream commit e786233d2e0bbff9a82e43f02ae3a46ab4b08ec3 ] rl_kref formerly served two distinct lifetimes through a single refcount: it gated when a Reply could wake its RPC task, and it gated when an rpcrdma_req could return to its free pool. The marshal path took the Send-side reference only when SGEs needed DMA-unmap (sc_unmap_count > 0), which made a Send carrying only pre-registered buffers an exception: the Reply handler dropped rl_kref from 1 to 0 and freed the req while the HCA might still be DMA-reading from its send buffer. Give rl_kref a narrower job. The RPC layer takes one reference when slot allocation hands a req out. rpcrdma_prepare_send_sges() takes a Send-side reference unconditionally after WR preparation succeeds. xprt_rdma_free_slot() and xprt_rdma_bc_free_rqst() drop the RPC-layer reference; rpcrdma_sendctx_unmap() drops the Send-side reference. The req returns to its free pool only after both owners have signed off. The existing kref_init(&req->rl_kref) call in rpcrdma_prepare_send_sges() is removed. Initialization moves to the slot-allocation paths (xprt_rdma_alloc_slot and rpcrdma_bc_rqst_get), and the release callback re-arms rl_kref before the req returns to a free pool. A re-init in the marshal path would discard the RPC-layer reference that already exists on entry. Three invariants follow: - Any rpcrdma_req held by an rpc_rqst has rl_kref >= 1. xprt_rdma_alloc_slot(), rpcrdma_bc_rqst_get(), and the backlog-wake branch in xprt_rdma_alloc_slot() each kref_init rl_kref before publishing the req. Without this invariant, an RPC task that aborts between slot allocation and marshal (gss_refresh failure or signal during call_connect, for example) would drive xprt_release() -> xprt_rdma_free_slot() -> kref_put against a refcount of zero, saturating refcount_t and stranding the slot. - The Send-side reference is taken only after WR prep succeeds. A mapping failure in rpcrdma_prepare_send_sges() runs rpcrdma_sendctx_cancel(), which DMA-unmaps the sendctx and clears sc_req without touching rl_kref. The sendctx ring walks in rpcrdma_sendctx_put_locked() and rpcrdma_sendctxs_destroy() skip entries with sc_req == NULL, so a burst of -EIO marshal failures cannot hold reqs off rb_send_bufs. - The release callback re-arms rl_kref so the next consumer enters with the invariant satisfied. Replies now complete the RPC directly. rpcrdma_reply_handler() calls rpcrdma_complete_rqst() in place of kref_put on the non-LocalInv branch. The LocalInv branch already completes the RPC from frwr_unmap_async() and is unaffected. Because Send-side references can now outlive RPC completion, connection teardown drains sendctx entries whose unsignaled Sends never had a later signaled completion to walk the ring. rpcrdma_sendctxs_destroy() walks the active range and runs rpcrdma_sendctx_unmap() on each entry with a non-NULL sc_req before the request buffers are reset, and is moved ahead of rpcrdma_reqs_reset() in rpcrdma_xprt_disconnect() so the reqs are still in their pre-reset state when the Send-side refs are released. The drain creates a teardown-ordering hazard on the backchannel path. With the new lifetime, releasing a bc_prealloc req from rpcrdma_req_release() re-adds it to bc_pa_list. The disconnect in xprt_rdma_destroy() runs after xprt_destroy_backchannel() has already emptied bc_pa_list, so the drained reqs would otherwise leak. xprt_rdma_destroy() now runs xprt_rdma_bc_destroy(xprt, 0) a second time after the disconnect to reclaim them. Fixes: 0ab115237025 ("xprtrdma: Wake RPCs directly in rpcrdma_wc_send path") Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- net/sunrpc/xprtrdma/backchannel.c | 5 ++- net/sunrpc/xprtrdma/rpc_rdma.c | 47 +++++++++++--------------- net/sunrpc/xprtrdma/transport.c | 55 ++++++++++++++++++++++++++++--- net/sunrpc/xprtrdma/verbs.c | 29 +++++++++++++--- net/sunrpc/xprtrdma/xprt_rdma.h | 2 +- 5 files changed, 97 insertions(+), 41 deletions(-) diff --git a/net/sunrpc/xprtrdma/backchannel.c b/net/sunrpc/xprtrdma/backchannel.c index 2f0f9618dd058..e5b3463da25f9 100644 --- a/net/sunrpc/xprtrdma/backchannel.c +++ b/net/sunrpc/xprtrdma/backchannel.c @@ -159,9 +159,7 @@ void xprt_rdma_bc_free_rqst(struct rpc_rqst *rqst) rpcrdma_rep_put(&r_xprt->rx_buf, rep); req->rl_reply = NULL; - spin_lock(&xprt->bc_pa_lock); - list_add_tail(&rqst->rq_bc_pa_list, &xprt->bc_pa_list); - spin_unlock(&xprt->bc_pa_lock); + rpcrdma_req_put(req); xprt_put(xprt); } @@ -203,6 +201,7 @@ static struct rpc_rqst *rpcrdma_bc_rqst_get(struct rpcrdma_xprt *r_xprt) rqst->rq_xprt = xprt; __set_bit(RPC_BC_PA_IN_USE, &rqst->rq_bc_pa_state); xdr_buf_init(&rqst->rq_snd_buf, rdmab_data(req->rl_sendbuf), size); + kref_init(&req->rl_kref); return rqst; } diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index 16b9987858d68..69380f9dfa499 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -467,16 +467,6 @@ static int rpcrdma_encode_reply_chunk(struct rpcrdma_xprt *r_xprt, return 0; } -static void rpcrdma_sendctx_done(struct kref *kref) -{ - struct rpcrdma_req *req = - container_of(kref, struct rpcrdma_req, rl_kref); - struct rpcrdma_rep *rep = req->rl_reply; - - rpcrdma_complete_rqst(rep); - rep->rr_rxprt->rx_stats.reply_waits_for_send++; -} - static void rpcrdma_sendctx_dma_unmap(struct rpcrdma_sendctx *sc) { struct rpcrdma_regbuf *rb = sc->sc_req->rl_sendbuf; @@ -493,17 +483,26 @@ static void rpcrdma_sendctx_dma_unmap(struct rpcrdma_sendctx *sc) } /** - * rpcrdma_sendctx_unmap - DMA-unmap Send buffer + * rpcrdma_sendctx_unmap - DMA-unmap Send buffer and release Send owner * @sc: sendctx containing SGEs to unmap * */ void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc) { - if (!sc->sc_unmap_count) - return; + struct rpcrdma_req *req = sc->sc_req; rpcrdma_sendctx_dma_unmap(sc); - kref_put(&sc->sc_req->rl_kref, rpcrdma_sendctx_done); + sc->sc_req = NULL; + rpcrdma_req_put(req); +} + +/* No Send was posted. Release DMA mappings prepared for this + * sendctx, but leave the request reference count alone. + */ +static void rpcrdma_sendctx_cancel(struct rpcrdma_sendctx *sc) +{ + rpcrdma_sendctx_dma_unmap(sc); + sc->sc_req = NULL; } /* Prepare an SGE for the RPC-over-RDMA transport header. @@ -695,8 +694,6 @@ static bool rpcrdma_prepare_noch_mapped(struct rpcrdma_xprt *r_xprt, tail->iov_len)) return false; - if (req->rl_sendctx->sc_unmap_count) - kref_get(&req->rl_kref); return true; } @@ -726,7 +723,6 @@ static bool rpcrdma_prepare_readch(struct rpcrdma_xprt *r_xprt, len -= len & 3; if (!rpcrdma_prepare_tail_iov(req, xdr, page_base, len)) return false; - kref_get(&req->rl_kref); } return true; @@ -755,7 +751,6 @@ inline int rpcrdma_prepare_send_sges(struct rpcrdma_xprt *r_xprt, goto out_nosc; req->rl_sendctx->sc_unmap_count = 0; req->rl_sendctx->sc_req = req; - kref_init(&req->rl_kref); req->rl_wr.wr_cqe = &req->rl_sendctx->sc_cqe; req->rl_wr.sg_list = req->rl_sendctx->sc_sges; req->rl_wr.num_sge = 0; @@ -783,10 +778,14 @@ inline int rpcrdma_prepare_send_sges(struct rpcrdma_xprt *r_xprt, goto out_unmap; } + /* The Send-side owner releases this reference when the + * Send has completed. + */ + kref_get(&req->rl_kref); return 0; out_unmap: - rpcrdma_sendctx_unmap(req->rl_sendctx); + rpcrdma_sendctx_cancel(req->rl_sendctx); out_nosc: trace_xprtrdma_prepsend_failed(&req->rl_slot, ret); return ret; @@ -1364,14 +1363,6 @@ void rpcrdma_complete_rqst(struct rpcrdma_rep *rep) goto out; } -static void rpcrdma_reply_done(struct kref *kref) -{ - struct rpcrdma_req *req = - container_of(kref, struct rpcrdma_req, rl_kref); - - rpcrdma_complete_rqst(req->rl_reply); -} - /** * rpcrdma_reply_handler - Process received RPC/RDMA messages * @rep: Incoming rpcrdma_rep object to process @@ -1443,7 +1434,7 @@ void rpcrdma_reply_handler(struct rpcrdma_rep *rep) frwr_unmap_async(r_xprt, req); /* LocalInv completion will complete the RPC */ else - kref_put(&req->rl_kref, rpcrdma_reply_done); + rpcrdma_complete_rqst(rep); out_post: rpcrdma_post_recvs(r_xprt, diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c index 61706df5e485a..5569f17fdd9b5 100644 --- a/net/sunrpc/xprtrdma/transport.c +++ b/net/sunrpc/xprtrdma/transport.c @@ -279,6 +279,13 @@ xprt_rdma_destroy(struct rpc_xprt *xprt) cancel_delayed_work_sync(&r_xprt->rx_connect_worker); rpcrdma_xprt_disconnect(r_xprt); + + /* The disconnect's sendctx drain can return bc_prealloc reqs + * to bc_pa_list after xprt_destroy_backchannel() emptied it. + */ +#if defined(CONFIG_SUNRPC_BACKCHANNEL) + xprt_rdma_bc_destroy(xprt, 0); +#endif rpcrdma_buffer_destroy(&r_xprt->rx_buf); xprt_rdma_free_addresses(xprt); @@ -487,6 +494,45 @@ xprt_rdma_connect(struct rpc_xprt *xprt, struct rpc_task *task) queue_delayed_work(system_long_wq, &r_xprt->rx_connect_worker, delay); } +/* rl_kref has two owners while a Send is outstanding: the rpc_rqst + * owner and the sendctx. Replies complete the RPC but do not drop + * either reference. The req returns to its free pool only after + * xprt_rdma_free_slot() or xprt_rdma_bc_free_rqst() has dropped the + * RPC-layer reference and rpcrdma_sendctx_unmap() has dropped the + * Send-side reference. + */ +static void rpcrdma_req_release(struct kref *kref) +{ + struct rpcrdma_req *req = + container_of(kref, struct rpcrdma_req, rl_kref); + struct rpc_rqst *rqst = &req->rl_slot; + struct rpc_xprt *xprt = rqst->rq_xprt; + struct rpcrdma_xprt *r_xprt; + + kref_init(&req->rl_kref); + +#if defined(CONFIG_SUNRPC_BACKCHANNEL) + if (bc_prealloc(rqst)) { + spin_lock(&xprt->bc_pa_lock); + list_add_tail(&rqst->rq_bc_pa_list, &xprt->bc_pa_list); + spin_unlock(&xprt->bc_pa_lock); + return; + } +#endif + + if (xprt_wake_up_backlog(xprt, rqst)) + return; + + r_xprt = rpcx_to_rdmax(xprt); + memset(rqst, 0, sizeof(*rqst)); + rpcrdma_buffer_put(&r_xprt->rx_buf, req); +} + +void rpcrdma_req_put(struct rpcrdma_req *req) +{ + kref_put(&req->rl_kref, rpcrdma_req_release); +} + /** * xprt_rdma_alloc_slot - allocate an rpc_rqst * @xprt: controlling RPC transport @@ -505,6 +551,7 @@ xprt_rdma_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task) req = rpcrdma_buffer_get(&r_xprt->rx_buf); if (!req) goto out_sleep; + kref_init(&req->rl_kref); task->tk_rqstp = &req->rl_slot; task->tk_status = 0; return; @@ -520,6 +567,7 @@ xprt_rdma_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task) if (req) { struct rpc_rqst *rqst = &req->rl_slot; + kref_init(&req->rl_kref); if (!xprt_wake_up_backlog(xprt, rqst)) { memset(rqst, 0, sizeof(*rqst)); rpcrdma_buffer_put(&r_xprt->rx_buf, req); @@ -540,10 +588,7 @@ xprt_rdma_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *rqst) container_of(xprt, struct rpcrdma_xprt, rx_xprt); rpcrdma_reply_put(&r_xprt->rx_buf, rpcr_to_rdmar(rqst)); - if (!xprt_wake_up_backlog(xprt, rqst)) { - memset(rqst, 0, sizeof(*rqst)); - rpcrdma_buffer_put(&r_xprt->rx_buf, rpcr_to_rdmar(rqst)); - } + rpcrdma_req_put(rpcr_to_rdmar(rqst)); } static bool rpcrdma_check_regbuf(struct rpcrdma_xprt *r_xprt, @@ -716,7 +761,7 @@ void xprt_rdma_print_stats(struct rpc_xprt *xprt, struct seq_file *seq) r_xprt->rx_stats.mrs_allocated, r_xprt->rx_stats.local_inv_needed, r_xprt->rx_stats.empty_sendctx_q, - r_xprt->rx_stats.reply_waits_for_send); + 0LU); /* was reply_waits_for_send; column preserved */ } static int diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index aecf9c0a153f3..97b8b2376602c 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -65,6 +65,8 @@ static int rpcrdma_sendctxs_create(struct rpcrdma_xprt *r_xprt); static void rpcrdma_sendctxs_destroy(struct rpcrdma_xprt *r_xprt); +static unsigned long rpcrdma_sendctx_next(struct rpcrdma_buffer *buf, + unsigned long item); static void rpcrdma_sendctx_put_locked(struct rpcrdma_xprt *r_xprt, struct rpcrdma_sendctx *sc); static int rpcrdma_reqs_setup(struct rpcrdma_xprt *r_xprt); @@ -571,9 +573,9 @@ void rpcrdma_xprt_disconnect(struct rpcrdma_xprt *r_xprt) rpcrdma_xprt_drain(r_xprt); rpcrdma_reps_unmap(r_xprt); + rpcrdma_sendctxs_destroy(r_xprt); rpcrdma_reqs_reset(r_xprt); rpcrdma_mrs_destroy(r_xprt); - rpcrdma_sendctxs_destroy(r_xprt); if (rpcrdma_ep_put(ep)) rdma_destroy_id(id); @@ -605,6 +607,20 @@ static void rpcrdma_sendctxs_destroy(struct rpcrdma_xprt *r_xprt) if (!buf->rb_sc_ctxs) return; + + /* The QP is drained, but the final unsignaled Sends might not + * have been walked by a signaled Send completion. Release those + * Send owners before request buffers are reset. + */ + for (i = rpcrdma_sendctx_next(buf, buf->rb_sc_tail); + i != rpcrdma_sendctx_next(buf, buf->rb_sc_head); + i = rpcrdma_sendctx_next(buf, i)) { + struct rpcrdma_sendctx *sc = buf->rb_sc_ctxs[i]; + + if (sc && sc->sc_req) + rpcrdma_sendctx_unmap(sc); + } + for (i = 0; i <= buf->rb_sc_last; i++) kfree(buf->rb_sc_ctxs[i]); kfree(buf->rb_sc_ctxs); @@ -739,15 +755,20 @@ static void rpcrdma_sendctx_put_locked(struct rpcrdma_xprt *r_xprt, struct rpcrdma_buffer *buf = &r_xprt->rx_buf; unsigned long next_tail; - /* Unmap SGEs of previously completed but unsignaled - * Sends by walking up the queue until @sc is found. + /* Release previously completed but unsignaled Sends by walking + * up the queue until @sc is found. Entries left behind by a + * failed rpcrdma_prepare_send_sges() have sc_req cleared. */ next_tail = buf->rb_sc_tail; do { + struct rpcrdma_sendctx *cur; + next_tail = rpcrdma_sendctx_next(buf, next_tail); /* ORDER: item must be accessed _before_ tail is updated */ - rpcrdma_sendctx_unmap(buf->rb_sc_ctxs[next_tail]); + cur = buf->rb_sc_ctxs[next_tail]; + if (cur->sc_req) + rpcrdma_sendctx_unmap(cur); } while (buf->rb_sc_ctxs[next_tail] != sc); diff --git a/net/sunrpc/xprtrdma/xprt_rdma.h b/net/sunrpc/xprtrdma/xprt_rdma.h index f53a774727245..f879d9b9f57ef 100644 --- a/net/sunrpc/xprtrdma/xprt_rdma.h +++ b/net/sunrpc/xprtrdma/xprt_rdma.h @@ -427,7 +427,6 @@ struct rpcrdma_stats { /* accessed when receiving a reply */ unsigned long long total_rdma_reply; unsigned long long fixup_copy_count; - unsigned long reply_waits_for_send; unsigned long local_inv_needed; unsigned long nomsg_call_count; unsigned long bcall_count; @@ -505,6 +504,7 @@ void rpcrdma_buffer_put(struct rpcrdma_buffer *buffers, struct rpcrdma_req *req); void rpcrdma_rep_put(struct rpcrdma_buffer *buf, struct rpcrdma_rep *rep); void rpcrdma_reply_put(struct rpcrdma_buffer *buffers, struct rpcrdma_req *req); +void rpcrdma_req_put(struct rpcrdma_req *req); bool rpcrdma_regbuf_realloc(struct rpcrdma_regbuf *rb, size_t size, gfp_t flags); From 72c578ca2f9e5dba948d9f8166a9abda38695728 Mon Sep 17 00:00:00 2001 From: Dai Ngo Date: Tue, 26 May 2026 16:29:53 -0700 Subject: [PATCH 1642/2758] NFSv4/pnfs: defer return_range callbacks until after inode unlock [ Upstream commit 77b160b2d863d37f36b6c38e80a7d259ce939e69 ] Sometimes unmounting an NFS filesystem mounted with pNFS SCSI layouts triggers the following warning: BUG: scheduling while atomic: umount.nfs4/... __schedule_bug+0xbd/0x100 schedule_debug.constprop.0+0x19f/0x220 __schedule+0x10d/0x10a0 schedule+0x74/0x190 schedule_timeout+0xf5/0x220 io_schedule_timeout+0xd5/0x160 __wait_for_common+0x186/0x4b0 blk_execute_rq+0x2ef/0x3a0 scsi_execute_cmd+0x1ff/0x700 sd_pr_out_command.isra.0+0x242/0x380 [sd_mod] bl_unregister_scsi.constprop.0+0x109/0x3c0 [blocklayoutdriver] bl_unregister_dev+0x175/0x1c0 [blocklayoutdriver] bl_free_device+0x1f/0x1b0 [blocklayoutdriver] bl_free_deviceid_node+0x12/0x30 [blocklayoutdriver] nfs4_put_deviceid_node+0x171/0x360 [nfsv4] ext_tree_remove+0x11c/0x1d0 [blocklayoutdriver] _pnfs_return_layout+0x416/0x900 [nfsv4] nfs4_evict_inode+0x108/0x130 [nfsv4] evict+0x316/0x750 dispose_list+0xf1/0x1a0 evict_inodes+0x33f/0x440 generic_shutdown_super+0xc9/0x4e0 kill_anon_super+0x3a/0x90 nfs_kill_super+0x44/0x60 [nfs] deactivate_locked_super+0xb8/0x1b0 cleanup_mnt+0x25a/0x380 task_work_run+0x13e/0x210 exit_to_user_mode_loop+0x169/0x400 do_syscall_64+0x467/0x1550 entry_SYSCALL_64_after_hwframe+0x76/0x7e The warning occurs because the block layout driver unregisters the SCSI device while the inode lock is still held. Device unregistration issues a SCSI PR command, which may sleep, resulting in a "scheduling while atomic" warning. During layout return, ext_tree_remove() invokes the layout driver's return_range callback while holding the inode lock. For block layouts, this callback eventually calls bl_unregister_scsi(), which may block in scsi_execute_cmd() while issuing PR commands to the device. Fix this by deferring the return_range callbacks until after the inode lock has been released. The layout header reference count is incremented before invoking return_range(), ensuring that the layout header remains valid while the layout driver removes extents from the extent tree. Fixes: c88953d87f5c8 ("pnfs: add return_range method") Signed-off-by: Dai Ngo Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- fs/nfs/callback_proc.c | 9 +++++---- fs/nfs/pnfs.c | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index 4ea9221ded426..10f2354ba3048 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c @@ -257,6 +257,7 @@ static u32 initiate_file_draining(struct nfs_client *clp, struct pnfs_layout_hdr *lo; u32 rv = NFS4ERR_NOMATCHING_LAYOUT; LIST_HEAD(free_me_list); + bool return_range = false; ino = nfs_layout_find_inode(clp, &args->cbl_fh, &args->cbl_stateid); if (IS_ERR(ino)) { @@ -301,13 +302,13 @@ static u32 initiate_file_draining(struct nfs_client *clp, /* Embrace your forgetfulness! */ rv = NFS4ERR_NOMATCHING_LAYOUT; - if (NFS_SERVER(ino)->pnfs_curr_ld->return_range) { - NFS_SERVER(ino)->pnfs_curr_ld->return_range(lo, - &args->cbl_range); - } + return_range = true; } unlock: spin_unlock(&ino->i_lock); + if (return_range && NFS_SERVER(ino)->pnfs_curr_ld->return_range) + NFS_SERVER(ino)->pnfs_curr_ld->return_range(lo, + &args->cbl_range); pnfs_free_lseg_list(&free_me_list); /* Free all lsegs that are attached to commit buckets */ nfs_commit_inode(ino, 0); diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index aee523134c0f3..b7dcf58f21c39 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -1463,8 +1463,6 @@ _pnfs_return_layout(struct inode *ino) pnfs_clear_layoutcommit(ino, &tmp_list); pnfs_mark_matching_lsegs_return(lo, &tmp_list, &range, 0); - if (NFS_SERVER(ino)->pnfs_curr_ld->return_range) - NFS_SERVER(ino)->pnfs_curr_ld->return_range(lo, &range); /* Don't send a LAYOUTRETURN if list was initially empty */ if (!test_bit(NFS_LAYOUT_RETURN_REQUESTED, &lo->plh_flags) || @@ -1476,6 +1474,8 @@ _pnfs_return_layout(struct inode *ino) send = pnfs_prepare_layoutreturn(lo, &stateid, &cred, NULL); spin_unlock(&ino->i_lock); + if (NFS_SERVER(ino)->pnfs_curr_ld->return_range) + NFS_SERVER(ino)->pnfs_curr_ld->return_range(lo, &range); if (send) status = pnfs_send_layoutreturn(lo, &stateid, &cred, IOMODE_ANY, 0); From 7471673936d1848f263c6a7f07bac50d474e3440 Mon Sep 17 00:00:00 2001 From: Clark Wang Date: Tue, 2 Jun 2026 19:04:38 +0800 Subject: [PATCH 1643/2758] nfs: keep PG_UPTODATE clear after read errors in page groups [ Upstream commit 3ff72e1cdf5c337b6acfcf3fcef748c5b9a5316b ] When a read request is split into multiple subrequests, earlier completions may advance PG_UPTODATE state for the page group once their bytes fall within hdr->good_bytes. If a later subrequest in the same group then completes with NFS_IOHDR_ERROR, the read path needs to clear any accumulated PG_UPTODATE state and keep later completions from rebuilding it. Otherwise, a subsequent successful subrequest can re-enter nfs_page_group_set_uptodate(), restore the page-group sync state, and leave stale PG_UPTODATE behind for nfs_page_group_destroy() to trip over in nfs_free_request(). Add a sticky page-group read-failed flag. Once any subrequest in the group is known to be bad, mark the group failed, clear any accumulated PG_UPTODATE state, and refuse further PG_UPTODATE synchronization for the rest of the completion walk. Fixes: 67d0338edd71 ("nfs: page group syncing in read path") Signed-off-by: Clark Wang Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- fs/nfs/read.c | 25 ++++++++++++++++++++++++- include/linux/nfs_page.h | 1 + 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/fs/nfs/read.c b/fs/nfs/read.c index e1fe78d7b8d0f..2b70bd2b934b9 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -132,10 +132,32 @@ static void nfs_readpage_release(struct nfs_page *req, int error) static void nfs_page_group_set_uptodate(struct nfs_page *req) { - if (nfs_page_group_sync_on_bit(req, PG_UPTODATE)) + bool uptodate = false; + + nfs_page_group_lock(req); + if (!test_bit(PG_READ_FAILED, &req->wb_head->wb_flags) && + nfs_page_group_sync_on_bit_locked(req, PG_UPTODATE)) + uptodate = true; + nfs_page_group_unlock(req); + + if (uptodate) folio_mark_uptodate(nfs_page_to_folio(req)); } +static void nfs_page_group_mark_read_failed(struct nfs_page *req) +{ + struct nfs_page *tmp; + + nfs_page_group_lock(req); + set_bit(PG_READ_FAILED, &req->wb_head->wb_flags); + tmp = req; + do { + clear_bit(PG_UPTODATE, &tmp->wb_flags); + tmp = tmp->wb_this_page; + } while (tmp != req); + nfs_page_group_unlock(req); +} + static void nfs_read_completion(struct nfs_pgio_header *hdr) { unsigned long bytes = 0; @@ -172,6 +194,7 @@ static void nfs_read_completion(struct nfs_pgio_header *hdr) if (bytes <= hdr->good_bytes) nfs_page_group_set_uptodate(req); else { + nfs_page_group_mark_read_failed(req); error = hdr->error; xchg(&nfs_req_openctx(req)->error, error); } diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h index afe1d8f09d89f..4b9a35dbc062d 100644 --- a/include/linux/nfs_page.h +++ b/include/linux/nfs_page.h @@ -33,6 +33,7 @@ enum { PG_TEARDOWN, /* page group sync for destroy */ PG_UNLOCKPAGE, /* page group sync bit in read path */ PG_UPTODATE, /* page group sync bit in read path */ + PG_READ_FAILED, /* page group saw a read error */ PG_WB_END, /* page group sync bit in write path */ PG_REMOVE, /* page group sync bit in write path */ PG_CONTENDED1, /* Is someone waiting for a lock? */ From 0fe1ac2bda64813ab5baa93310051a51fc3c95e1 Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Thu, 4 Jun 2026 16:24:02 -0400 Subject: [PATCH 1644/2758] NFSv4/flexfiles: honor FF_FLAGS_NO_IO_THRU_MDS on fatal DS connect errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 7a375cafc14ed151508f908ea5681caf0a9cc1d6 ] Commit f06bedfa62d5 ("pNFS/flexfiles: don't attempt pnfs on fatal DS errors") teaches ff_layout_{read,write}_pagelist() to return PNFS_NOT_ATTEMPTED when nfs4_ff_layout_prepare_ds() fails with a nfs_error_is_fatal() errno (e.g. -ETIMEDOUT from a SOFTCONN connect deadline, -ENOMEM, -ERESTARTSYS), so that the client gives up instead of spinning. pnfs_do_{read,write}() then dispatches the I/O through pnfs_{read,write}_through_mds() → nfs_pageio_reset_{read,write}_mds(). That fallback is unconditional and silently violates FF_FLAGS_NO_IO_THRU_MDS: when the layout segment carries the flag (typically single-mirror appliance layouts where MDS I/O is explicitly forbidden), the out_failed: path's \`&& !ds_fatal_error\` clause overrides the flag's short-circuit through ff_layout_avoid_mds_available_ds() and routes the I/O to the MDS file handle anyway. This is reachable in practice during a data-server restart: SOFTCONN exhaustion produces -ETIMEDOUT, which is fatal per nfs_error_is_fatal(), which triggers PNFS_NOT_ATTEMPTED, which silently goes to MDS. Preserve the upstream "don't spin on fatal errors" intent for layouts that permit MDS fallback. For layouts with FF_FLAGS_NO_IO_THRU_MDS set, mark the layout for return and request PNFS_TRY_AGAIN instead; if the server cannot supply a usable layout the failure now surfaces cleanly via pnfs_update_layout(), rather than via silent MDS I/O that contradicts the flag. Fixes: f06bedfa62d5 ("pNFS/flexfiles: don't attempt pnfs on fatal DS errors") Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Mike Snitzer Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- fs/nfs/flexfilelayout/flexfilelayout.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c index e22a8e0daf2c5..80a3c43aaac05 100644 --- a/fs/nfs/flexfilelayout/flexfilelayout.c +++ b/fs/nfs/flexfilelayout/flexfilelayout.c @@ -2204,6 +2204,14 @@ ff_layout_read_pagelist(struct nfs_pgio_header *hdr) out_failed: if (ff_layout_avoid_mds_available_ds(lseg) && !ds_fatal_error) return PNFS_TRY_AGAIN; + if (ff_layout_no_fallback_to_mds(lseg)) { + /* + * FF_FLAGS_NO_IO_THRU_MDS: force fresh LAYOUTGET, + * never fall through to MDS I/O. + */ + pnfs_error_mark_layout_for_return(hdr->inode, lseg); + return PNFS_TRY_AGAIN; + } trace_pnfs_mds_fallback_read_pagelist(hdr->inode, hdr->args.offset, hdr->args.count, IOMODE_READ, NFS_I(hdr->inode)->layout, lseg); @@ -2289,6 +2297,14 @@ ff_layout_write_pagelist(struct nfs_pgio_header *hdr, int sync) out_failed: if (ff_layout_avoid_mds_available_ds(lseg) && !ds_fatal_error) return PNFS_TRY_AGAIN; + if (ff_layout_no_fallback_to_mds(lseg)) { + /* + * FF_FLAGS_NO_IO_THRU_MDS: force fresh LAYOUTGET, + * never fall through to MDS I/O. + */ + pnfs_error_mark_layout_for_return(hdr->inode, lseg); + return PNFS_TRY_AGAIN; + } trace_pnfs_mds_fallback_write_pagelist(hdr->inode, hdr->args.offset, hdr->args.count, IOMODE_RW, NFS_I(hdr->inode)->layout, lseg); From 51e5adebef61bb2f9df7f48af572089b1353be7a Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Thu, 4 Jun 2026 16:24:03 -0400 Subject: [PATCH 1645/2758] NFSv4/flexfiles: honor FF_FLAGS_NO_IO_THRU_MDS in pg_get_mirror_count_write [ Upstream commit 1d62e659c0bf11649cf48e002c2a55d148f2610a ] The FF_FLAGS_NO_IO_THRU_MDS flag lives on each lseg, so any fallback decision made when there is no current lseg (e.g. between LAYOUTRETURN and the next LAYOUTGET) cannot run the per-lseg check. Introduce a sticky hdr-level ditto for FF_FLAGS_NO_IO_THRU_MDS in struct nfs4_flexfile_layout::flags (NFS4_FF_HDR_NO_IO_THRU_MDS bit), set whenever ff_layout_alloc_lseg() parses an lseg with the flag. The bit is never cleared for the lifetime of the layout hdr; the server is assumed to be consistent in its no-fallback policy per file. kzalloc() in ff_layout_alloc_layout_hdr() zero-initializes the field. Use the new ff_layout_hdr_no_fallback_to_mds() helper to gate ff_layout_pg_get_mirror_count_write(): when pnfs_update_layout() returns NULL (e.g. NFS_LAYOUT_BULK_RECALL, pnfs_layout_io_test_failed, pnfs_layoutgets_blocked) the existing code unconditionally calls nfs_pageio_reset_write_mds(). This is a source of unwanted WRITE to MDS. Fix it by checking NFS4_FF_HDR_NO_IO_THRU_MDS bit, and if set surface -EAGAIN instead; the writepage-side caller (nfs_do_writepage() for buffered, nfs_direct_write_reschedule() for O_DIRECT) then redirties the request so writeback retries via pNFS. Fixes: 260074cd8413 ("pNFS/flexfiles: Add support for FF_FLAGS_NO_IO_THRU_MDS") Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Mike Snitzer Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- fs/nfs/flexfilelayout/flexfilelayout.c | 13 +++++++++++++ fs/nfs/flexfilelayout/flexfilelayout.h | 16 ++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c index 80a3c43aaac05..2e7f857d5a8cb 100644 --- a/fs/nfs/flexfilelayout/flexfilelayout.c +++ b/fs/nfs/flexfilelayout/flexfilelayout.c @@ -636,6 +636,9 @@ ff_layout_alloc_lseg(struct pnfs_layout_hdr *lh, if (!p) goto out_sort_mirrors; fls->flags = be32_to_cpup(p); + if (fls->flags & FF_FLAGS_NO_IO_THRU_MDS) + set_bit(NFS4_FF_HDR_NO_IO_THRU_MDS, + &FF_LAYOUT_FROM_HDR(lh)->flags); p = xdr_inline_decode(&stream, 4); if (!p) @@ -1185,6 +1188,16 @@ ff_layout_pg_get_mirror_count_write(struct nfs_pageio_descriptor *pgio, 0, NFS4_MAX_UINT64, IOMODE_RW, NFS_I(pgio->pg_inode)->layout, pgio->pg_lseg); + if (NFS_I(pgio->pg_inode)->layout && + ff_layout_hdr_no_fallback_to_mds(NFS_I(pgio->pg_inode)->layout)) { + /* + * FF_FLAGS_NO_IO_THRU_MDS: no current lseg but the server's + * policy forbids MDS fallback. Surface -EAGAIN so writeback + * retries rather than silently issuing the WRITE via MDS. + */ + pgio->pg_error = -EAGAIN; + goto out; + } /* no lseg means that pnfs is not in use, so no mirroring here */ nfs_pageio_reset_write_mds(pgio); out: diff --git a/fs/nfs/flexfilelayout/flexfilelayout.h b/fs/nfs/flexfilelayout/flexfilelayout.h index 17a008c8e97ce..a5bd00f69e824 100644 --- a/fs/nfs/flexfilelayout/flexfilelayout.h +++ b/fs/nfs/flexfilelayout/flexfilelayout.h @@ -112,12 +112,16 @@ struct nfs4_ff_layout_segment { struct nfs4_ff_layout_mirror *mirror_array[] __counted_by(mirror_array_cnt); }; +/* nfs4_flexfile_layout::flags bit indices */ +#define NFS4_FF_HDR_NO_IO_THRU_MDS 0 /* any lseg has had FF_FLAGS_NO_IO_THRU_MDS */ + struct nfs4_flexfile_layout { struct pnfs_layout_hdr generic_hdr; struct pnfs_ds_commit_info commit_info; struct list_head mirrors; struct list_head error_list; /* nfs4_ff_layout_ds_err */ ktime_t last_report_time; /* Layoutstat report times */ + unsigned long flags; }; struct nfs4_flexfile_layoutreturn_args { @@ -184,6 +188,18 @@ ff_layout_no_fallback_to_mds(struct pnfs_layout_segment *lseg) return FF_LAYOUT_LSEG(lseg)->flags & FF_FLAGS_NO_IO_THRU_MDS; } +/* + * Sticky hdr-level mirror of FF_FLAGS_NO_IO_THRU_MDS so callers that have + * no current lseg (e.g. between LAYOUTRETURN and the next LAYOUTGET) can + * still honor the no-MDS-fallback policy. + */ +static inline bool +ff_layout_hdr_no_fallback_to_mds(struct pnfs_layout_hdr *lo) +{ + return test_bit(NFS4_FF_HDR_NO_IO_THRU_MDS, + &FF_LAYOUT_FROM_HDR(lo)->flags); +} + static inline bool ff_layout_no_read_on_rw(struct pnfs_layout_segment *lseg) { From e68035178e65e2b3aa386ce61202ff33724ae418 Mon Sep 17 00:00:00 2001 From: Yang Erkun Date: Thu, 26 Feb 2026 09:22:03 +0800 Subject: [PATCH 1646/2758] nfs: use nfsi->rwsem to protect traversal of the file lock list [ Upstream commit 4837fb36219e6c08b666bc31a86841bad8526358 ] Lingfeng identified a bug and suggested two solutions, but both appear to have issues. Generally, we cannot release flc_lock while iterating over the file lock list to avoid use-after-free (UAF) problems with file locks. However, functions like nfs_delegation_claim_locks and nfs4_reclaim_locks cannot adhere to this rule because recover_lock or nfs4_lock_delegation_recall may take a long time. To resolve this, NFS switches to using nfsi->rwsem for the same protection, and nfs_reclaim_locks follows this approach. Although nfs_delegation_claim_locks uses so_delegreturn_mutex instead, this is inadequate since a single inode can have multiple nfs4_state instances. Therefore, the fix is to also use nfsi->rwsem in this case. Furthermore, after commit c69899a17ca4 ("NFSv4: Update of VFS byte range lock must be atomic with the stateid update"), the functions nfs4_locku_done and nfs4_lock_done also break this rule because they call locks_lock_inode_wait without holding nfsi->rwsem. Simply adding this protection could cause many deadlocks, so instead, the call to locks_lock_inode_wait is moved into _nfs4_proc_setlk. Regarding the bug fixed by commit c69899a17ca4 ("NFSv4: Update of VFS byte range lock must be atomic with the stateid update"), it has been resolved after commit 0460253913e5 ("NFSv4: nfs4_do_open() is incorrectly triggering state recovery") because all slots are drained before calling nfs4_do_reclaim, which prevents concurrent stateid changes along this path. Also, nfs_delegation_claim_locks does not cause this concurrency either since when _nfs4_proc_setlk is called with NFS_DELEGATED_STATE, no RPC is sent, so nfs4_lock_done is not called. Therefore, nfs4_lock_delegation_recall from nfs_delegation_claim_locks is the first time the stateid is set. Reported-by: Li Lingfeng Closes: https://lore.kernel.org/all/20250419085709.1452492-1-lilingfeng3@huawei.com/ Closes: https://lore.kernel.org/all/20250715030559.2906634-1-lilingfeng3@huawei.com/ Fixes: c69899a17ca4 ("NFSv4: Update of VFS byte range lock must be atomic with the stateid update") Signed-off-by: Yang Erkun Reviewed-by: Jeff Layton Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- fs/nfs/delegation.c | 9 ++++++++- fs/nfs/nfs4proc.c | 22 +++++++++++----------- include/linux/nfs_xdr.h | 1 - 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 122fb3f14ffb8..9546d2195c25f 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -173,6 +173,7 @@ int nfs4_check_delegation(struct inode *inode, fmode_t type) static int nfs_delegation_claim_locks(struct nfs4_state *state, const nfs4_stateid *stateid) { struct inode *inode = state->inode; + struct nfs_inode *nfsi = NFS_I(inode); struct file_lock *fl; struct file_lock_context *flctx = locks_inode_context(inode); struct list_head *list; @@ -182,6 +183,9 @@ static int nfs_delegation_claim_locks(struct nfs4_state *state, const nfs4_state goto out; list = &flctx->flc_posix; + + /* Guard against reclaim and new lock/unlock calls */ + down_write(&nfsi->rwsem); spin_lock(&flctx->flc_lock); restart: for_each_file_lock(fl, list) { @@ -189,8 +193,10 @@ static int nfs_delegation_claim_locks(struct nfs4_state *state, const nfs4_state continue; spin_unlock(&flctx->flc_lock); status = nfs4_lock_delegation_recall(fl, state, stateid); - if (status < 0) + if (status < 0) { + up_write(&nfsi->rwsem); goto out; + } spin_lock(&flctx->flc_lock); } if (list == &flctx->flc_posix) { @@ -198,6 +204,7 @@ static int nfs_delegation_claim_locks(struct nfs4_state *state, const nfs4_state goto restart; } spin_unlock(&flctx->flc_lock); + up_write(&nfsi->rwsem); out: return status; } diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index c354b7b902939..4db27f4eb01e9 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -7084,7 +7084,6 @@ static void nfs4_locku_done(struct rpc_task *task, void *data) switch (task->tk_status) { case 0: renew_lease(calldata->server, calldata->timestamp); - locks_lock_inode_wait(calldata->lsp->ls_state->inode, &calldata->fl); if (nfs4_update_lock_stateid(calldata->lsp, &calldata->res.stateid)) break; @@ -7352,11 +7351,6 @@ static void nfs4_lock_done(struct rpc_task *task, void *calldata) case 0: renew_lease(NFS_SERVER(d_inode(data->ctx->dentry)), data->timestamp); - if (data->arg.new_lock && !data->cancelled) { - data->fl.c.flc_flags &= ~(FL_SLEEP | FL_ACCESS); - if (locks_lock_inode_wait(lsp->ls_state->inode, &data->fl) < 0) - goto out_restart; - } if (data->arg.new_lock_owner != 0) { nfs_confirm_seqid(&lsp->ls_seqid, 0); nfs4_stateid_copy(&lsp->ls_stateid, &data->res.stateid); @@ -7467,11 +7461,10 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f msg.rpc_argp = &data->arg; msg.rpc_resp = &data->res; task_setup_data.callback_data = data; - if (recovery_type > NFS_LOCK_NEW) { - if (recovery_type == NFS_LOCK_RECLAIM) - data->arg.reclaim = NFS_LOCK_RECLAIM; - } else - data->arg.new_lock = 1; + + if (recovery_type == NFS_LOCK_RECLAIM) + data->arg.reclaim = NFS_LOCK_RECLAIM; + task = rpc_run_task(&task_setup_data); if (IS_ERR(task)) return PTR_ERR(task); @@ -7581,6 +7574,13 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock up_read(&nfsi->rwsem); mutex_unlock(&sp->so_delegreturn_mutex); status = _nfs4_do_setlk(state, cmd, request, NFS_LOCK_NEW); + if (status) + goto out; + + down_read(&nfsi->rwsem); + request->c.flc_flags &= ~(FL_SLEEP | FL_ACCESS); + status = locks_lock_inode_wait(state->inode, request); + up_read(&nfsi->rwsem); out: request->c.flc_flags = flags; return status; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index fcbd21b5685f4..40417e3a7f85a 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -580,7 +580,6 @@ struct nfs_lock_args { struct nfs_lowner lock_owner; unsigned char block : 1; unsigned char reclaim : 1; - unsigned char new_lock : 1; unsigned char new_lock_owner : 1; }; From fa7ce7dfbd2d88e080f50adf3f2c5d647fe40c3c Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Thu, 14 May 2026 20:55:52 -0400 Subject: [PATCH 1647/2758] PCI: mediatek: Fix operator precedence in PCIE_FTS_NUM_L0 macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 282305d7e9c0e27fd8b4df34b7cd5506a1eccdd6 ] The original PCIE_FTS_NUM_L0(x) macro was buggy due to improper operator precedence, where ((x) & 0xff << 8) was evaluated as ((x) & 0xff00). Instead of just fixing the parentheses, use the standard FIELD_PREP() macro. This makes the code more robust by automatically handling masks and shifts, while also adding compile-time type and range checking to ensure the value fits within PCIE_FTS_NUM_MASK. Fixes: 637cfacae96f ("PCI: mediatek: Add MediaTek PCIe host controller support") Signed-off-by: Li RongQing [mani: added the bitfield header include spotted by Sashiko] Signed-off-by: Manivannan Sadhasivam Reviewed-by: Krzysztof Wilczyński Link: https://patch.msgid.link/20260515005552.2343-1-lirongqing@baidu.com Signed-off-by: Sasha Levin --- drivers/pci/controller/pcie-mediatek.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index 907ae4285ecb1..f34d91e495bc1 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -7,6 +7,7 @@ * Honghui Zhang */ +#include #include #include #include @@ -61,7 +62,7 @@ /* MediaTek specific configuration registers */ #define PCIE_FTS_NUM 0x70c #define PCIE_FTS_NUM_MASK GENMASK(15, 8) -#define PCIE_FTS_NUM_L0(x) ((x) & 0xff << 8) +#define PCIE_FTS_NUM_L0(x) FIELD_PREP(PCIE_FTS_NUM_MASK, x) #define PCIE_FC_CREDIT 0x73c #define PCIE_FC_CREDIT_MASK (GENMASK(31, 31) | GENMASK(28, 16)) From f966db2568c4b075a34243768b0c3ede1ea89d5e Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 4 Jun 2026 10:56:34 +0100 Subject: [PATCH 1648/2758] pwm: rzg2l-gpt: Add missing newlines to dev_err_probe() messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 898ab0f30e008e411ce93ddf81c4099abd9d4e46 ] dev_err_probe() internally calls dev_err() which uses pr_fmt() and printk(). Kernel log messages should end with a newline character to ensure proper log formatting. Add missing '\n' at the end of the error strings in rzg2l_gpt_probe(). Signed-off-by: Biju Das Link: https://patch.msgid.link/20260604095647.108654-5-biju.das.jz@bp.renesas.com Fixes: 061f087f5d0b ("pwm: Add support for RZ/G2L GPT") Signed-off-by: Uwe Kleine-König Signed-off-by: Sasha Levin --- drivers/pwm/pwm-rzg2l-gpt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pwm/pwm-rzg2l-gpt.c b/drivers/pwm/pwm-rzg2l-gpt.c index 4856af080e8e9..8534a2a9cf750 100644 --- a/drivers/pwm/pwm-rzg2l-gpt.c +++ b/drivers/pwm/pwm-rzg2l-gpt.c @@ -408,14 +408,14 @@ static int rzg2l_gpt_probe(struct platform_device *pdev) rate = clk_get_rate(clk); if (!rate) - return dev_err_probe(dev, -EINVAL, "The gpt clk rate is 0"); + return dev_err_probe(dev, -EINVAL, "The gpt clk rate is 0\n"); /* * Refuse clk rates > 1 GHz to prevent overflow later for computing * period and duty cycle. */ if (rate > NSEC_PER_SEC) - return dev_err_probe(dev, -EINVAL, "The gpt clk rate is > 1GHz"); + return dev_err_probe(dev, -EINVAL, "The gpt clk rate is > 1GHz\n"); /* * Rate is in MHz and is always integer for peripheral clk @@ -424,7 +424,7 @@ static int rzg2l_gpt_probe(struct platform_device *pdev) */ rzg2l_gpt->rate_khz = rate / KILO; if (rzg2l_gpt->rate_khz * KILO != rate) - return dev_err_probe(dev, -EINVAL, "Rate is not multiple of 1000"); + return dev_err_probe(dev, -EINVAL, "Rate is not multiple of 1000\n"); mutex_init(&rzg2l_gpt->lock); From 221972a90c56b64e665181893d08ffbde20d0fad Mon Sep 17 00:00:00 2001 From: Shuvam Pandey Date: Mon, 18 May 2026 13:49:40 +0545 Subject: [PATCH 1649/2758] PCI: meson: Propagate devm_add_action_or_reset() failure [ Upstream commit b12341b98d5ac52f48ca1390e1e371aed81346c8 ] meson_pcie_probe_clock() enables a clock and then registers a devres action to disable it during teardown. If devm_add_action_or_reset() fails, it runs the action immediately, disabling the clock. The return value is currently ignored, so on that failure path, meson_pcie_probe_clock() returns the disabled clock and probe continues. Return the error so the existing probe error path unwinds normally. Fixes: 9c0ef6d34fdbf ("PCI: amlogic: Add the Amlogic Meson PCIe controller driver") Signed-off-by: Shuvam Pandey Signed-off-by: Manivannan Sadhasivam Reviewed-by: Neil Armstrong Link: https://patch.msgid.link/177909148011.9588.6639767953842842291@gmail.com Signed-off-by: Sasha Levin --- drivers/pci/controller/dwc/pci-meson.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pci-meson.c b/drivers/pci/controller/dwc/pci-meson.c index 0694084f612b7..8d495bcc3a41a 100644 --- a/drivers/pci/controller/dwc/pci-meson.c +++ b/drivers/pci/controller/dwc/pci-meson.c @@ -204,7 +204,9 @@ static inline struct clk *meson_pcie_probe_clock(struct device *dev, return ERR_PTR(ret); } - devm_add_action_or_reset(dev, meson_pcie_disable_clock, clk); + ret = devm_add_action_or_reset(dev, meson_pcie_disable_clock, clk); + if (ret) + return ERR_PTR(ret); return clk; } From ec52cdcbf23f67fd2b98cac07da80ae8a70cad75 Mon Sep 17 00:00:00 2001 From: Shuvam Pandey Date: Mon, 18 May 2026 22:44:18 +0545 Subject: [PATCH 1650/2758] PCI: meson: Add missing remove callback [ Upstream commit 4b0dc84b293984f75598881809fb2d3daf54a2a8 ] meson_pcie_probe() powers on the PHY and registers the DesignWare host bridge with dw_pcie_host_init(), but the driver has no remove callback. On driver unbind or module unload, the driver core therefore proceeds to devres cleanup without first unregistering the host bridge or powering off the PHY. Add a remove callback that deinitializes the DesignWare host bridge and powers off the PHY while device-managed resources are still valid. Fixes: 9c0ef6d34fdb ("PCI: amlogic: Add the Amlogic Meson PCIe controller driver") Signed-off-by: Shuvam Pandey Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/1a0c86ab264cdc1c79c917e984b90991af51d827.1779123847.git.shuvampandey1@gmail.com Signed-off-by: Sasha Levin --- drivers/pci/controller/dwc/pci-meson.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/pci/controller/dwc/pci-meson.c b/drivers/pci/controller/dwc/pci-meson.c index 8d495bcc3a41a..225d887cd0a3e 100644 --- a/drivers/pci/controller/dwc/pci-meson.c +++ b/drivers/pci/controller/dwc/pci-meson.c @@ -453,6 +453,14 @@ static int meson_pcie_probe(struct platform_device *pdev) return ret; } +static void meson_pcie_remove(struct platform_device *pdev) +{ + struct meson_pcie *mp = platform_get_drvdata(pdev); + + dw_pcie_host_deinit(&mp->pci.pp); + meson_pcie_power_off(mp); +} + static const struct of_device_id meson_pcie_of_match[] = { { .compatible = "amlogic,axg-pcie", @@ -466,6 +474,7 @@ MODULE_DEVICE_TABLE(of, meson_pcie_of_match); static struct platform_driver meson_pcie_driver = { .probe = meson_pcie_probe, + .remove = meson_pcie_remove, .driver = { .name = "meson-pcie", .of_match_table = meson_pcie_of_match, From a2cc03ee5d34f9273fd16b2a716abd4ea5e63626 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 12 May 2026 14:13:37 -0400 Subject: [PATCH 1651/2758] lockd: Correct kernel-doc status descriptions for NLMv4 GRANTED [ Upstream commit 5412049208e669925f7b08bbfabe3cd28a598c5b ] NLM_GRANTED is a server-to-client callback; the local node responds in the role of the client. The kernel-doc for nlm4svc_proc_granted attributes NLM4_DENIED and NLM4_DENIED_GRACE_PERIOD to "the server", but per the Open Group XNFS specification the responder for this procedure is the client host, and NLM4_DENIED_GRACE_PERIOD identifies the client's own grace period after a reboot, not the server's. Rewrite the descriptions to match the spec: NLM4_DENIED reflects the generic internal-resource-constraint failure, and NLM4_DENIED_GRACE_PERIOD attributes the grace period to the client host that received the callback. Fixes: 7a9f7c8f934e ("lockd: Use xdrgen XDR functions for the NLMv4 GRANTED procedure") Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Sasha Levin --- fs/lockd/svc4proc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index 3bf6cccae5685..f3ba2615ae77d 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -516,12 +516,12 @@ nlm4svc_proc_unlock(struct svc_rqst *rqstp) * nlm4_res NLMPROC4_GRANTED(nlm4_testargs) = 5; * * Permissible procedure status codes: - * %NLM4_GRANTED: The requested lock was granted. - * %NLM4_DENIED: The server could not allocate the resources - * needed to process the request. - * %NLM4_DENIED_GRACE_PERIOD: The server has recently restarted and is - * re-establishing existing locks, and is not - * yet ready to accept normal service requests. + * %NLM4_GRANTED: The granted lock was accepted. + * %NLM4_DENIED: The procedure failed, possibly due to + * internal resource constraints. + * %NLM4_DENIED_GRACE_PERIOD: The client host recently restarted and + * its NLM is re-establishing existing locks, + * so it is not yet ready to accept callbacks. */ static __be32 nlm4svc_proc_granted(struct svc_rqst *rqstp) From b052df3a595348e917564293f926c83b4d3f3224 Mon Sep 17 00:00:00 2001 From: Christian Fontanez Date: Mon, 18 May 2026 21:33:20 -0400 Subject: [PATCH 1652/2758] virtio: add missing kernel-doc for map and vmap members [ Upstream commit ac2c52e9f869897b6f4c0a54cf07da380ef2b8d6 ] Commit bee8c7c24b73 ("virtio: introduce map ops in virtio core") and commit b16060c5c7d5 ("virtio: introduce virtio_map container union") added 'map' and 'vmap' members to struct virtio_device but did not update the kernel-doc comment block. This caused 'make htmldocs' to emit warnings: ./include/linux/virtio.h:188 struct member 'map' not described in 'virtio_device' ./include/linux/virtio.h:188 struct member 'vmap' not described in 'virtio_device' Add the missing entries in struct-declaration order to match the existing convention in the file. After this patch, 'make htmldocs' no longer emits these warnings. Fixes: bee8c7c24b73 ("virtio: introduce map ops in virtio core") Fixes: b16060c5c7d5 ("virtio: introduce virtio_map container union") Reported-by: Luis Felipe Hernandez Signed-off-by: Christian Fontanez Signed-off-by: Michael S. Tsirkin Message-ID: <20260519013321.32511-1-christfontanez@gmail.com> Signed-off-by: Sasha Levin --- include/linux/virtio.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/virtio.h b/include/linux/virtio.h index 3bbc4cb6a6727..bf089e51970ec 100644 --- a/include/linux/virtio.h +++ b/include/linux/virtio.h @@ -157,11 +157,13 @@ struct virtio_admin_cmd { * @id: the device type identification (used to match it with a driver). * @config: the configuration ops for this device. * @vringh_config: configuration ops for host vrings. + * @map: the map operations for mapping virtio device memory. * @vqs: the list of virtqueues for this device. * @features: the 64 lower features supported by both driver and device. * @features_array: the full features space supported by both driver and * device. * @priv: private pointer for the driver's use. + * @vmap: the map container with transport- or device-specific metadata. * @debugfs_dir: debugfs directory entry. * @debugfs_filter_features: features to be filtered set by debugfs. */ From 7ae7e98b71438c494532492cbf58fc0d7f7988bb Mon Sep 17 00:00:00 2001 From: Edward Adam Davis Date: Tue, 26 May 2026 16:08:04 +0800 Subject: [PATCH 1653/2758] fs/ntfs3: prevent potential lcn remains uninitialized [ Upstream commit 57ac2831c8e0f168090d38e3de758c6a59db44db ] The target VCN being sought was not found within runs[0], causing run_lookup() to return false. This causes run_lookup_entry() to return false, which in turn results in a len value of 0, and the new parameter passed to attr_data_get_block() is NULL. Collectively, these factors ultimately cause attr_data_get_block_locked() to exit prematurely without initializing lcn, thereby triggering [1]. To prevent [1], the clen check within ni_seek_data_or_hole() has been moved to occur before the lcn check. [1] BUG: KMSAN: uninit-value in ni_seek_data_or_hole+0x24f/0x5f0 fs/ntfs3/frecord.c:2862 ni_seek_data_or_hole+0x24f/0x5f0 fs/ntfs3/frecord.c:2862 ntfs_llseek+0x22a/0x4a0 fs/ntfs3/file.c:1530 vfs_llseek fs/read_write.c:391 [inline] Fixes: c61326967728 ("fs/ntfs3: implement llseek SEEK_DATA/SEEK_HOLE by scanning data runs") Reported-by: syzbot+c2cfe997245202e46f10@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=c2cfe997245202e46f10 Signed-off-by: Edward Adam Davis Signed-off-by: Konstantin Komarov Signed-off-by: Sasha Levin --- fs/ntfs3/frecord.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c index bb3348f256d99..974c55ae2c014 100644 --- a/fs/ntfs3/frecord.c +++ b/fs/ntfs3/frecord.c @@ -2859,6 +2859,11 @@ loff_t ni_seek_data_or_hole(struct ntfs_inode *ni, loff_t offset, bool data) return err; } + if (!clen) { + /* Corrupted file. */ + return -EINVAL; + } + if (lcn == RESIDENT_LCN) { /* clen - resident size in bytes. clen == ni->vfs_inode.i_size */ if (offset >= clen) { @@ -2909,10 +2914,6 @@ loff_t ni_seek_data_or_hole(struct ntfs_inode *ni, loff_t offset, bool data) } } - if (!clen) { - /* Corrupted file. */ - return -EINVAL; - } } } From c99444f6dfca893f6d310aae4a53c620f98f7b4f Mon Sep 17 00:00:00 2001 From: Jamie Nguyen Date: Thu, 4 Jun 2026 21:19:30 -0700 Subject: [PATCH 1654/2758] fs/ntfs3: resize log->one_page_buf when adopting on-disk page size [ Upstream commit 5a35454179fe1041d9cd286f5d320ce0d448c12a ] log_replay() allocates log->one_page_buf using the page size that was chosen from the host PAGE_SIZE: log->one_page_buf = kmalloc(log->page_size, GFP_NOFS); Later, when a restart area is found, the log page size recorded on disk is adopted: t32 = le32_to_cpu(log->rst_info.r_page->sys_page_size); if (log->page_size != t32) { log->l_size = log->orig_file_size; log->page_size = norm_file_page(t32, &log->l_size, t32 == DefaultLogPageSize); } If the on-disk page size is larger than the size used for the initial allocation, log->page_size grows but one_page_buf is left at its original, smaller size. A subsequent unaligned read_log_page() then reads log->page_size bytes into the undersized scratch buffer: page_buf = page_off ? log->one_page_buf : *buffer; err = ntfs_read_run_nb_ra(ni->mi.sbi, &ni->file.run, page_vbo, page_buf, log->page_size, NULL, &log->read_ahead); overflowing the allocation. This is reachable when mounting a dirty NTFS volume whose log was formatted with a page size larger than the buffer initially allocated on the mounting host (for example a 64K-log volume mounted on a host that allocated a 4K scratch buffer). Grow one_page_buf when the adopted on-disk page size exceeds the size used for the initial allocation. On krealloc() failure the original buffer is left intact and freed by the existing error path. Fixes: b46acd6a6a627 ("fs/ntfs3: Add NTFS journal") Reported-by: Carol L Soto Signed-off-by: Jamie Nguyen Signed-off-by: Konstantin Komarov Signed-off-by: Sasha Levin --- fs/ntfs3/fslog.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index 3c320e7a4c12f..a76d4c3cd3946 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -3937,9 +3937,28 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) */ t32 = le32_to_cpu(log->rst_info.r_page->sys_page_size); if (log->page_size != t32) { + u32 old_page_size = log->page_size; + log->l_size = log->orig_file_size; log->page_size = norm_file_page(t32, &log->l_size, t32 == DefaultLogPageSize); + + /* + * If the adopted on-disk page size is larger than the size used + * to allocate one_page_buf above, grow the scratch buffer so a + * later read_log_page() cannot overflow it. + */ + if (log->page_size > old_page_size) { + void *buf; + + buf = krealloc(log->one_page_buf, log->page_size, + GFP_NOFS); + if (!buf) { + err = -ENOMEM; + goto out; + } + log->one_page_buf = buf; + } } if (log->page_size != t32 || From 962c7a1f8f1e1bf8547bc5f4de77d0614cf69038 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 29 May 2026 11:31:49 -0700 Subject: [PATCH 1655/2758] platform/x86/intel/vsec: Restore BAR fallback for header walk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 375bbbbd112af028ee0b45d833a6233c23d19bbf ] The base_addr refactor changed intel_vsec_walk_header() to pass info->base_addr as the discovery-table base address. For the PCI VSEC driver this info comes from driver_data, but exported callers may provide their own static headers and leave base_addr unset. For xe, this made the discovery-table base address zero instead of the BAR selected by header->tbir, preventing PMT endpoints from being created. Restore the previous behavior for the header-walk path by falling back to pci_resource_start(pdev, header->tbir) when base_addr is not specified. Keep explicit base_addr override behavior unchanged. This preserves the refactor structure while fixing the functional regression in manual-header users. Fixes: 904b333fc51c ("platform/x86/intel/vsec: Refactor base_addr handling") Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: David E. Box Reviewed-by: Michael J. Ruhl Link: https://patch.msgid.link/20260529183150.129744-1-david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Sasha Levin --- drivers/platform/x86/intel/vsec.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 18e4a892bf0f0..439c0c8ac896c 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -488,10 +488,25 @@ static int intel_vsec_walk_header(struct device *dev, const struct intel_vsec_platform_info *info) { struct intel_vsec_header **header = info->headers; + u64 base_addr; int ret; for ( ; *header; header++) { - ret = intel_vsec_register_device(dev, *header, info, info->base_addr); + if (info->base_addr) { + base_addr = info->base_addr; + } else { + struct pci_dev *pdev; + + if (!dev_is_pci(dev)) { + dev_err(dev, "non-PCI device without a base address\n"); + return -EINVAL; + } + + pdev = to_pci_dev(dev); + base_addr = pci_resource_start(pdev, (*header)->tbir); + } + + ret = intel_vsec_register_device(dev, *header, info, base_addr); if (ret) return ret; } From 678bb88bb97744fd14cf4b194153fcf035560b98 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 20:37:52 -0300 Subject: [PATCH 1656/2758] perf tools: Fix get_max_num() size_t underflow on empty sysfs file [ Upstream commit 0a012113bb3a44482c163f16f4db03ccaa37a339 ] get_max_num() reads a sysfs file (cpu/possible, cpu/present, or node/possible) and scans backward from the end to find the last number. If the file is empty, filename__read_str() returns num == 0. The loop `while (--num)` decrements the size_t from 0 to SIZE_MAX, reading backward across the heap until a comma or hyphen is found or unmapped memory is hit. Add an early return for empty files before the backward scan. Fixes: 7780c25bae59fd04 ("perf tools: Allow ability to map cpus to nodes easily") Reported-by: sashiko-bot Reviewed-by: Ian Rogers Cc: Don Zickus Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/cpumap.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index b754e331bdfe8..59173db168f16 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -420,6 +420,12 @@ static int get_max_num(char *path, int *max) buf[num] = '\0'; + /* empty file — nothing to parse */ + if (num == 0) { + err = -1; + goto out; + } + /* start on the right, to find highest node num */ while (--num) { if ((buf[num] == ',') || (buf[num] == '-')) { From 36d2c15a33ece8507678129f3c8e1d339ee52280 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 20:43:52 -0300 Subject: [PATCH 1657/2758] perf tools: Use scnprintf() in cpu_map__snprint() to prevent overflow [ Upstream commit 7953a3a9b8e02e98c6e6958f291d0ae22393e46a ] cpu_map__snprint() accumulates snprintf() return values in ret. snprintf() returns the number of characters that *would have been written* on truncation, not the actual count. When a fragmented CPU list exceeds the buffer, ret grows past size, causing `size - ret` to underflow (both are size_t), and subsequent snprintf() calls write past the end of the caller's stack buffer. Switch to scnprintf() which returns the actual number of characters written, making ret accumulation safe by construction. Fixes: a24020e6b7cf6eb8 ("perf tools: Change cpu_map__fprintf output") Reported-by: sashiko-bot Reviewed-by: Ian Rogers Cc: Jiri Olsa Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/cpumap.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 59173db168f16..dbbec195cffdc 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -664,21 +664,21 @@ size_t cpu_map__snprint(struct perf_cpu_map *map, char *buf, size_t size) if (start == -1) { start = i; if (last) { - ret += snprintf(buf + ret, size - ret, - "%s%d", COMMA, - perf_cpu_map__cpu(map, i).cpu); + ret += scnprintf(buf + ret, size - ret, + "%s%d", COMMA, + perf_cpu_map__cpu(map, i).cpu); } } else if (((i - start) != (cpu.cpu - perf_cpu_map__cpu(map, start).cpu)) || last) { int end = i - 1; if (start == end) { - ret += snprintf(buf + ret, size - ret, - "%s%d", COMMA, - perf_cpu_map__cpu(map, start).cpu); + ret += scnprintf(buf + ret, size - ret, + "%s%d", COMMA, + perf_cpu_map__cpu(map, start).cpu); } else { - ret += snprintf(buf + ret, size - ret, - "%s%d-%d", COMMA, - perf_cpu_map__cpu(map, start).cpu, perf_cpu_map__cpu(map, end).cpu); + ret += scnprintf(buf + ret, size - ret, + "%s%d-%d", COMMA, + perf_cpu_map__cpu(map, start).cpu, perf_cpu_map__cpu(map, end).cpu); } first = false; start = i; From eb266a14c16a93eb4db7b56a452d6be93f8bdcd4 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 20:57:49 -0300 Subject: [PATCH 1658/2758] perf tools: Use perf_env__get_cpu_topology() in machine__resolve() [ Upstream commit 5484b43a0ec8231c36fba6ead654cb72dbba8b8f ] machine__resolve() accesses env->cpu[al->cpu].socket_id after checking al->cpu >= 0 and env->cpu != NULL, but without validating al->cpu against env->nr_cpus_avail. Since al->cpu comes from the untrusted perf.data sample, a crafted file with a large CPU index causes an out-of-bounds heap read. Use perf_env__get_cpu_topology() which validates both NULL and bounds. Also bounds-check al->cpu before the cast to struct perf_cpu (int16_t): without this, values like 65536 silently truncate to 0, bypassing the accessor's internal check and returning CPU 0's topology. Fixes: 0c4c4debb0adda4c ("perf tools: Add processor socket info to hist_entry and addr_location") Reported-by: sashiko-bot Reviewed-by: Ian Rogers Cc: Kan Liang Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/event.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 66f4843bb235d..ea75816d126a1 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -14,6 +14,7 @@ #include #include "cpumap.h" #include "dso.h" +#include "env.h" #include "event.h" #include "debug.h" #include "hist.h" @@ -836,8 +837,18 @@ int machine__resolve(struct machine *machine, struct addr_location *al, if (al->cpu >= 0) { struct perf_env *env = machine->env; - if (env && env->cpu) - al->socket = env->cpu[al->cpu].socket_id; + /* + * Bounds-check al->cpu (s32) before casting to struct perf_cpu + * (int16_t): without this, e.g. 65536 truncates to 0 and silently + * returns CPU 0's topology. Can go once perf_cpu.cpu is widened. + */ + if (env && al->cpu < env->nr_cpus_avail) { + struct cpu_topology_map *topo; + + topo = perf_env__get_cpu_topology(env, (struct perf_cpu){ al->cpu }); + if (topo) + al->socket = topo->socket_id; + } } /* Account for possible out-of-order switch events. */ From 027c177da2b559bc8e724732a8df6c9acc1a4444 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 21:31:51 -0300 Subject: [PATCH 1659/2758] perf tools: NULL bitmap pointers after bitmap_free() [ Upstream commit a9e900bc5c5914aca750afafa459363e575d3046 ] Two call sites free bitmaps without NULLing the pointer, risking double-free if the structure is reused or cleanup is called twice: - mmap__munmap(): map->affinity_mask.bits - record__mmap_cpu_mask_free(): mask->bits Set each pointer to NULL after bitmap_free(). Fixes: 8384a2600c7ddfc8 ("perf record: Adapt affinity to machines with #CPUs > 1K") Fixes: f466e5ed6c356d1d ("perf record: Extend --threads command line option") Reported-by: sashiko-bot Reviewed-by: Ian Rogers Cc: Alexey Budankov Cc: Alexey Bayduraev Cc: Arnaldo Carvalho de Melo Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-record.c | 1 + tools/perf/util/mmap.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 708825747af5d..1593cf3a834bd 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -3080,6 +3080,7 @@ static int record__mmap_cpu_mask_alloc(struct mmap_cpu_mask *mask, int nr_bits) static void record__mmap_cpu_mask_free(struct mmap_cpu_mask *mask) { bitmap_free(mask->bits); + mask->bits = NULL; mask->nbits = 0; } diff --git a/tools/perf/util/mmap.c b/tools/perf/util/mmap.c index d64aec6c7c843..358e70c4f3edd 100644 --- a/tools/perf/util/mmap.c +++ b/tools/perf/util/mmap.c @@ -238,6 +238,8 @@ static void perf_mmap__aio_munmap(struct mmap *map __maybe_unused) void mmap__munmap(struct mmap *map) { bitmap_free(map->affinity_mask.bits); + map->affinity_mask.bits = NULL; + map->affinity_mask.nbits = 0; zstd_fini(&map->zstd_data); From 2c0461f5393b335cb18b178931b33482ed251bb4 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Thu, 21 May 2026 10:12:56 +0100 Subject: [PATCH 1660/2758] PCI: rcar-host: Remove unused LIST_HEAD(res) [ Upstream commit 6ba90ce2069ae923b0ec787aebdf2d786e5d2a58 ] Remove the unused LIST_HEAD(res) declaration from rcar_pcie_hw_enable(). The macro instantiation defines an unused 'struct list_head res' variable, which conflicts with a valid resource loop-local 'struct resource *res' declaration further down in the function, triggering a compiler variable shadowing warning: drivers/pci/controller/pcie-rcar-host.c:357:34: warning: declaration of 'res' shadows a previous local [-Wshadow] 357 | struct resource *res = win->res; Fixes: ce351636c67f75a9 ("PCI: rcar: Add suspend/resume") Signed-off-by: Lad Prabhakar Signed-off-by: Manivannan Sadhasivam Reviewed-by: Geert Uytterhoeven Reviewed-by: Marek Vasut Link: https://patch.msgid.link/20260521091256.15737-1-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Sasha Levin --- drivers/pci/controller/pcie-rcar-host.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pci/controller/pcie-rcar-host.c b/drivers/pci/controller/pcie-rcar-host.c index 213028052aa58..cd9171eebc289 100644 --- a/drivers/pci/controller/pcie-rcar-host.c +++ b/drivers/pci/controller/pcie-rcar-host.c @@ -346,7 +346,6 @@ static void rcar_pcie_hw_enable(struct rcar_pcie_host *host) struct rcar_pcie *pcie = &host->pcie; struct pci_host_bridge *bridge = pci_host_bridge_from_priv(host); struct resource_entry *win; - LIST_HEAD(res); int i = 0; /* Try setting 5 GT/s link speed */ From 23af74f538b74090cb4aabf516d75b70c6d1adf8 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 21:48:13 -0300 Subject: [PATCH 1661/2758] perf sched: Bounds-check prio before test_bit() in timehist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 4477dc01fcfc7f404772a67e0c1e056541ceb61d ] timehist_skip_sample() reads prio from untrusted tracepoint data via perf_sample__intval(sample, "prev_prio") without bounds validation. A crafted perf.data with prev_prio >= MAX_PRIO (140) causes test_bit() to read past the end of the prio_bitmap, which is only MAX_PRIO bits. Add a prio >= 0 guard before the test_bit() call and skip out-of-range values (>= MAX_PRIO) that can never match the user's filter set. The original prio != -1 already let all negatives other than -1 through (after an undefined-behavior bitmap read); the new prio >= 0 guard preserves that pass-through behavior — negative means "no priority info", so the event is shown unfiltered — while fixing the OOB. Values >= MAX_PRIO are skipped because they cannot be represented in the filter bitmap. Fixes: 9b3a48bbe20d9692 ("perf sched timehist: Add --prio option") Reported-by: sashiko-bot Reviewed-by: Ian Rogers Cc: Yang Jihong Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-sched.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 165c24d9781cb..3b796860418f4 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -2647,7 +2647,9 @@ static bool timehist_skip_sample(struct perf_sched *sched, else if (evsel__name_is(evsel, "sched:sched_switch")) prio = evsel__intval(evsel, sample, "prev_prio"); - if (prio != -1 && !test_bit(prio, sched->prio_bitmap)) { + /* negative prio means no info; out-of-range prio can't match the filter */ + if (prio >= 0 && + (prio >= MAX_PRIO || !test_bit(prio, sched->prio_bitmap))) { rc = true; sched->skipped_samples++; } From a757d523741d0e050740d3ca0694948dcba96ca2 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 21:49:16 -0300 Subject: [PATCH 1662/2758] perf sched: Fix idle-hist callchain display using wrong rb_first variant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit d9b99dc8148e0c1f5da3942131b47e0d21187a32 ] timehist_print_idlehist_callchain() calls rb_first_cached() on sorted_root, but the sort function (callchain_param.sort) populates it via rb_insert_color() on the plain rb_root member — not the cached variant. This means rb_leftmost is never set, so rb_first_cached() always returns NULL and the entire callchain summary is silently dropped from --idle-hist output. The original code in ba957ebb54893aca ("perf sched timehist: Show callchains for idle stat") was correct — it used struct rb_root and rb_first(). The bug was introduced when sorted_root was converted to rb_root_cached without converting the sort insertion path to use rb_insert_color_cached(). Use rb_first(&root->rb_root) to match how the tree was populated. Fixes: cb4c13a5137766c3 ("perf sched: Use cached rbtrees") Reported-by: sashiko-bot Cc: Davidlohr Bueso Cc: Namhyung Kim Acked-by: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-sched.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 3b796860418f4..f48df27f1feae 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -3133,7 +3133,8 @@ static size_t timehist_print_idlehist_callchain(struct rb_root_cached *root) size_t ret = 0; FILE *fp = stdout; struct callchain_node *chain; - struct rb_node *rb_node = rb_first_cached(root); + /* sort() uses rb_insert_color() on rb_root, not rb_root_cached */ + struct rb_node *rb_node = rb_first(&root->rb_root); printf(" %16s %8s %s\n", "Idle time (msec)", "Count", "Callchains"); printf(" %.16s %.8s %.50s\n", graph_dotted_line, graph_dotted_line, From 01d67b6f44ed0e94a531442a0e77d6e8eccebb18 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 21:51:42 -0300 Subject: [PATCH 1663/2758] perf tools: Add O_CLOEXEC to open() calls in DSO and ELF code [ Upstream commit b145137fec13dc8fc7fcb14193ce395a1164e3a1 ] open() calls in dso.c and symbol-elf.c omit O_CLOEXEC, which leaks file descriptors to child processes spawned during symbol resolution (e.g., addr2line, objdump). This can exhaust the fd limit during long profiling sessions or when processing many DSOs. Add O_CLOEXEC to all open() calls in both files (12 call sites). Fixes: cdd059d731eeb466 ("perf tools: Move dso_* related functions into dso object") Fixes: e5a1845fc0aeca85 ("perf symbols: Split out util/symbol-elf.c") Reported-by: sashiko-bot Reviewed-by: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/dso.c | 4 ++-- tools/perf/util/symbol-elf.c | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index b791e1b6b2cf0..511e91843fbd8 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -344,7 +344,7 @@ int filename__decompress(const char *name, char *pathname, * descriptor to the uncompressed file. */ if (!compressions[comp].is_compressed(name)) - return open(name, O_RDONLY); + return open(name, O_RDONLY | O_CLOEXEC); fd = mkstemp(tmpbuf); if (fd < 0) { @@ -1904,7 +1904,7 @@ static const u8 *__dso__read_symbol(struct dso *dso, const char *symfs_filename, int saved_errno; nsinfo__mountns_enter(dso__nsinfo(dso), &nsc); - fd = open(symfs_filename, O_RDONLY); + fd = open(symfs_filename, O_RDONLY | O_CLOEXEC); saved_errno = errno; nsinfo__mountns_exit(&nsc); if (fd < 0) { diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 7afa8a1171396..0ba2c60ea5aab 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -217,7 +217,7 @@ bool filename__has_section(const char *filename, const char *sec) GElf_Shdr shdr; bool found = false; - fd = open(filename, O_RDONLY); + fd = open(filename, O_RDONLY | O_CLOEXEC); if (fd < 0) return false; @@ -871,7 +871,7 @@ static int read_build_id(const char *filename, struct build_id *bid) if (size < BUILD_ID_SIZE) goto out; - fd = open(filename, O_RDONLY); + fd = open(filename, O_RDONLY | O_CLOEXEC); if (fd < 0) goto out; @@ -934,7 +934,7 @@ int sysfs__read_build_id(const char *filename, struct build_id *bid) size_t size = sizeof(bid->data); int fd, err = -1; - fd = open(filename, O_RDONLY); + fd = open(filename, O_RDONLY | O_CLOEXEC); if (fd < 0) goto out; @@ -994,7 +994,7 @@ int filename__read_debuglink(const char *filename, char *debuglink, if (err >= 0) goto out; - fd = open(filename, O_RDONLY); + fd = open(filename, O_RDONLY | O_CLOEXEC); if (fd < 0) goto out; @@ -1152,7 +1152,7 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, type = dso__symtab_type(dso); } else { - fd = open(name, O_RDONLY); + fd = open(name, O_RDONLY | O_CLOEXEC); if (fd < 0) { *dso__load_errno(dso) = errno; return -1; @@ -1945,7 +1945,7 @@ static int kcore__open(struct kcore *kcore, const char *filename) { GElf_Ehdr *ehdr; - kcore->fd = open(filename, O_RDONLY); + kcore->fd = open(filename, O_RDONLY | O_CLOEXEC); if (kcore->fd == -1) return -1; @@ -1978,7 +1978,7 @@ static int kcore__init(struct kcore *kcore, char *filename, int elfclass, if (temp) kcore->fd = mkstemp(filename); else - kcore->fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0400); + kcore->fd = open(filename, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0400); if (kcore->fd == -1) return -1; @@ -2454,11 +2454,11 @@ static int kcore_copy__compare_files(const char *from_filename, { int from, to, err = -1; - from = open(from_filename, O_RDONLY); + from = open(from_filename, O_RDONLY | O_CLOEXEC); if (from < 0) return -1; - to = open(to_filename, O_RDONLY); + to = open(to_filename, O_RDONLY | O_CLOEXEC); if (to < 0) goto out_close_from; @@ -2876,7 +2876,7 @@ int get_sdt_note_list(struct list_head *head, const char *target) Elf *elf; int fd, ret; - fd = open(target, O_RDONLY); + fd = open(target, O_RDONLY | O_CLOEXEC); if (fd < 0) return -EBADF; From 5f3b8ff3f6320e8066131e896a961f9c1371c35e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 14:23:15 -0300 Subject: [PATCH 1664/2758] perf bpf: Use scnprintf() in snprintf_hex() and synthesize_bpf_prog_name() [ Upstream commit cab3a9331ed0b3f884dd61c8a25b3cf123705982 ] Both functions accumulate formatted output via ret += snprintf(buf + ret, size - ret, ...). If the buffer is too small and snprintf() returns more than the remaining space, ret exceeds size and the next 'size - ret' underflows, causing snprintf() to write past the buffer end. Switch to scnprintf() which returns the actual number of bytes written, making the accumulation safe. Fixes: 7b612e291a5affb1 ("perf tools: Synthesize PERF_RECORD_* for loaded BPF programs") Reported-by: sashiko-bot Reviewed-by: Ian Rogers Cc: Song Liu Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/bpf-event.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index a27945c279efb..2c09842469f1f 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c @@ -36,7 +36,7 @@ static int snprintf_hex(char *buf, size_t size, unsigned char *data, size_t len) size_t i; for (i = 0; i < len; i++) - ret += snprintf(buf + ret, size - ret, "%02x", data[i]); + ret += scnprintf(buf + ret, size - ret, "%02x", data[i]); return ret; } @@ -140,7 +140,7 @@ static int synthesize_bpf_prog_name(char *buf, int size, const struct btf_type *t; int name_len; - name_len = snprintf(buf, size, "bpf_prog_"); + name_len = scnprintf(buf, size, "bpf_prog_"); name_len += snprintf_hex(buf + name_len, size - name_len, prog_tags[sub_id], BPF_TAG_SIZE); if (btf) { @@ -153,9 +153,10 @@ static int synthesize_bpf_prog_name(char *buf, int size, short_name = info->name; } else short_name = "F"; - if (short_name) - name_len += snprintf(buf + name_len, size - name_len, - "_%s", short_name); + if (short_name) { + name_len += scnprintf(buf + name_len, size - name_len, + "_%s", short_name); + } return name_len; } From a6d9b818465688afce14444e8abb27c22b657947 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 14:35:28 -0300 Subject: [PATCH 1665/2758] perf hists: Fix snprintf() in hists__scnprintf_title() UID filter path [ Upstream commit 227a8748742f0263f1fe3131449b44563b77a209 ] hists__scnprintf_title() accumulates formatted output into a buffer using scnprintf() for all filter clauses except the UID filter, which uses snprintf(). If the buffer fills up and snprintf() returns more than the remaining space, printed exceeds size and the next 'size - printed' underflows, causing later scnprintf() calls to write past the buffer. Switch the UID filter clause to scnprintf() to match the rest of the function. Fixes: 25c312dbf88ca402 ("perf hists: Move hists__scnprintf_title() away from the TUI code") Reported-by: sashiko-bot Reviewed-by: Ian Rogers Cc: Arnaldo Carvalho de Melo Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/hist.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 747fdc455c80e..2038c1d20acff 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -2963,9 +2963,10 @@ int __hists__scnprintf_title(struct hists *hists, char *bf, size_t size, bool sh ev_name, sample_freq_str, enable_ref ? ref : " ", nr_events); - if (hists->uid_filter_str) - printed += snprintf(bf + printed, size - printed, - ", UID: %s", hists->uid_filter_str); + if (hists->uid_filter_str) { + printed += scnprintf(bf + printed, size - printed, + ", UID: %s", hists->uid_filter_str); + } if (thread) { if (hists__has(hists, thread)) { printed += scnprintf(bf + printed, size - printed, From f025990647c8bc3ea880df9eeeb5317d009558d3 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 14:36:57 -0300 Subject: [PATCH 1666/2758] perf tools: Use scnprintf() in build_id__snprintf() and hwmon read_events() [ Upstream commit e33711d5e757011bb6d3506af4d6c97dad412b8f ] build_id__snprintf() and hwmon_pmu__read_events() accumulate formatted output via snprintf(), which returns the would-have-been-written count on truncation. In build_id__snprintf(), this inflates the return value beyond the buffer size. In hwmon_pmu__read_events(), len overshoots out_buf_len and the next 'out_buf_len - len' underflows. Switch both to scnprintf() which returns actual bytes written. In build_id__snprintf(), also tighten the loop guard from 'offs < bf_size' to 'offs + 1 < bf_size': since scnprintf() returns at most size-1, offs never reaches bf_size, and the original condition would spin doing zero-byte writes once the buffer fills. Fixes: fccaaf6fbbc59910 ("perf build-id: Change sprintf functions to snprintf") Fixes: 53cc0b351ec99278 ("perf hwmon_pmu: Add a tool PMU exposing events from hwmon in sysfs") Reported-by: sashiko-bot Reviewed-by: Ian Rogers Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/build-id.c | 7 +++++-- tools/perf/util/hwmon_pmu.c | 12 ++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index fdb35133fde43..3c286cdbe6e0e 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -93,8 +93,11 @@ int build_id__snprintf(const struct build_id *build_id, char *bf, size_t bf_size return 0; } - for (size_t i = 0; i < build_id->size && offs < bf_size; ++i) - offs += snprintf(bf + offs, bf_size - offs, "%02x", build_id->data[i]); + if (bf_size > 0) + bf[0] = '\0'; + + for (size_t i = 0; i < build_id->size && offs + 1 < bf_size; ++i) + offs += scnprintf(bf + offs, bf_size - offs, "%02x", build_id->data[i]); return offs; } diff --git a/tools/perf/util/hwmon_pmu.c b/tools/perf/util/hwmon_pmu.c index fb3ffa8d32ad2..dbf6a71af47f9 100644 --- a/tools/perf/util/hwmon_pmu.c +++ b/tools/perf/util/hwmon_pmu.c @@ -442,12 +442,12 @@ static size_t hwmon_pmu__describe_items(struct hwmon_pmu *hwm, char *out_buf, si buf[read_len] = '\0'; val = strtoll(buf, /*endptr=*/NULL, 10); - len += snprintf(out_buf + len, out_buf_len - len, "%s%s%s=%g%s", - len == 0 ? " " : ", ", - hwmon_item_strs[bit], - is_alarm ? "_alarm" : "", - (double)val / 1000.0, - hwmon_units[key.type]); + len += scnprintf(out_buf + len, out_buf_len - len, "%s%s%s=%g%s", + len == 0 ? " " : ", ", + hwmon_item_strs[bit], + is_alarm ? "_alarm" : "", + (double)val / 1000.0, + hwmon_units[key.type]); } close(fd); } From ffc07790539736a5d029f6a3c966b46c529f93a8 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Thu, 4 Jun 2026 13:06:33 -0400 Subject: [PATCH 1667/2758] xprtrdma: Fix ep kref imbalance on ADDR_CHANGE [ Upstream commit af9b65b29af341932625c4283dc7a23cdb62688a ] rpcrdma_cm_event_handler() falls through to the disconnected: label on RDMA_CM_EVENT_ADDR_CHANGE and calls rpcrdma_ep_put() with no matching get when the event arrives before RDMA_CM_EVENT_ESTABLISHED. The kref then underflows during connect teardown and rpcrdma_xprt_disconnect() operates on a freed ep. Reference counts across a normal connection lifecycle: rpcrdma_ep_create() kref_init ->1 rpcrdma_xprt_connect() ep_get ->2 (before post_recvs) RDMA_CM_EVENT_ESTABLISHED ep_get ->3 RDMA_CM_EVENT_DISCONNECTED ep_put ->2 rpcrdma_xprt_drain() ep_put ->1 rpcrdma_xprt_disconnect() tail ep_put ->0 (ep_destroy) The connect-time get in rpcrdma_xprt_connect(), taken just before rpcrdma_post_recvs() "while there are outstanding Receives," is balanced by rpcrdma_xprt_drain. ADDR_CHANGE before ESTABLISHED has no get to consume, so its put drops the count to 1 and the drain put then frees the ep while rpcrdma_xprt_disconnect() still holds a pointer to it. Fix by dispatching on the prior re_connect_status via xchg(): for prev == 0 (pre-ESTABLISHED) wake the connect waiter and return with no put; for prev == 1 call rpcrdma_force_disconnect() and return. The case-1 arm relies on the subsequent RDMA_CM_EVENT_DISCONNECTED event -- reliably delivered when rdma_disconnect() is called on a still-connected cm_id -- to balance the ESTABLISHED get; rpcrdma_xprt_drain() continues to balance only that connect-time get. Any other prior value means teardown is already in flight. Fixes: 2acc5cae2923 ("xprtrdma: Prevent dereferencing r_xprt->rx_ep after it is freed") Assisted-by: kres:claude-opus-4-7 Signed-off-by: Chris Mason Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- net/sunrpc/xprtrdma/verbs.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index 97b8b2376602c..5a36f35792e0d 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -245,8 +245,17 @@ rpcrdma_cm_event_handler(struct rdma_cm_id *id, struct rdma_cm_event *event) complete(&ep->re_done); return 0; case RDMA_CM_EVENT_ADDR_CHANGE: - ep->re_connect_status = -ENODEV; - goto disconnected; + switch (xchg(&ep->re_connect_status, -ENODEV)) { + case 0: + goto wake_connect_worker; + case 1: + /* The later DISCONNECTED event balances the + * ESTABLISHED get; do not put here. + */ + rpcrdma_force_disconnect(ep); + return 0; + } + return 0; case RDMA_CM_EVENT_ESTABLISHED: rpcrdma_ep_get(ep); ep->re_connect_status = 1; @@ -269,7 +278,6 @@ rpcrdma_cm_event_handler(struct rdma_cm_id *id, struct rdma_cm_event *event) return 0; case RDMA_CM_EVENT_DISCONNECTED: ep->re_connect_status = -ECONNABORTED; -disconnected: rpcrdma_force_disconnect(ep); return rpcrdma_ep_put(ep); default: From 264ccd7871915749bee55fe0c39467a7f08d5479 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Thu, 4 Jun 2026 13:06:34 -0400 Subject: [PATCH 1668/2758] xprtrdma: Initialize re_id before removal registration [ Upstream commit bb7caa63e1db22fd03e8dc591b12169e99169dff ] rpcrdma_create_id() registers ep->re_rn with the rpcrdma ib_client before returning the new rdma_cm_id to rpcrdma_ep_create(). However rpcrdma_ep_create() currently stores that pointer in ep->re_id only after rpcrdma_create_id() returns. A local administrator can race an NFS/RDMA mount against RDMA device removal. If rpcrdma_remove_one() observes the just-registered notification before rpcrdma_ep_create() assigns ep->re_id, rpcrdma_ep_removal_done() calls trace_xprtrdma_device_removal(NULL). The tracepoint dereferences id->device->name and copies id->route.addr.dst_addr, so the callback can crash the kernel with a NULL pointer dereference. Store the rdma_cm_id in ep->re_id immediately before publishing ep->re_rn. The existing error path still destroys the id directly if registration fails; ep is then freed by the caller without using ep->re_id. Remove the later duplicate assignment in rpcrdma_ep_create(). Fixes: 3f4eb9ff9234 ("xprtrdma: Handle device removal outside of the CM event handler") Assisted-by: kres:openai-gpt-5 Signed-off-by: Chris Mason Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- net/sunrpc/xprtrdma/verbs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index 5a36f35792e0d..9b2c186fd221b 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -334,6 +334,7 @@ static struct rdma_cm_id *rpcrdma_create_id(struct rpcrdma_xprt *r_xprt, if (rc) goto out; + ep->re_id = id; rc = rpcrdma_rn_register(id->device, &ep->re_rn, rpcrdma_ep_removal_done); if (rc) goto out; @@ -406,7 +407,6 @@ static int rpcrdma_ep_create(struct rpcrdma_xprt *r_xprt) } __module_get(THIS_MODULE); device = id->device; - ep->re_id = id; reinit_completion(&ep->re_done); ep->re_max_requests = r_xprt->rx_xprt.max_reqs; From ef3b79edf14b6bfb0d21a26ccb0463f9cf82c6a9 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Jun 2026 13:06:35 -0400 Subject: [PATCH 1669/2758] xprtrdma: Check frwr_wp_create() during connect [ Upstream commit 0f13fc7c7d2e0427517e63c739277a4cd338b0c5 ] frwr_wp_create() creates the singleton Memory Region used to encode padding for Write chunks whose payload length is not XDR-aligned. Its failure paths return a negative errno and leave ep->re_write_pad_mr set to NULL. rpcrdma_xprt_connect() currently ignores that return value. If frwr_wp_create() fails after the rest of the connection setup succeeds, xprt_rdma_connect_worker() treats the connection attempt as successful and sets XPRT_CONNECTED. A later NFS/RDMA read with a non-4-byte-aligned receive page length reaches rpcrdma_encode_write_list(), passes the NULL write-pad MR to encode_rdma_segment(), and dereferences it. This is locally triggerable on an NFS/RDMA client after a connect or reconnect hits a local MR allocation, DMA-map, MR-map, or post-send failure; a remote peer alone cannot force the local MR setup failure. Check the return value and fail the connect as -ENOTCONN, matching the adjacent setup failures. This keeps XPRT_CONNECTED clear and lets the normal reconnect path retry. Fixes: 21037b8c2258 ("xprtrdma: Provide a buffer to pad Write chunks of unaligned length") Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- net/sunrpc/xprtrdma/verbs.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index 9b2c186fd221b..d48d681bb49f4 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -549,7 +549,17 @@ int rpcrdma_xprt_connect(struct rpcrdma_xprt *r_xprt) goto out; } rpcrdma_mrs_create(r_xprt); - frwr_wp_create(r_xprt); + + /* + * rpcrdma_encode_write_list() dereferences the write-pad + * MR with no NULL check, so fail the connect rather than + * publish a transport whose write-pad MR is NULL. + */ + rc = frwr_wp_create(r_xprt); + if (rc) { + rc = -ENOTCONN; + goto out; + } out: trace_xprtrdma_connect(r_xprt, rc); From 96da53e7d6f9da54dba0c1f13c85e018ba3d5eba Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 26 May 2026 10:14:05 -0400 Subject: [PATCH 1670/2758] xprtrdma: Document and assert reply-handler invariants [ Upstream commit 797943e8bd1ffcc63bfe79d24faad9a77054ec40 ] The xprtrdma reply path has been the subject of recurring LLM-driven review claims that 'an RPC can complete while receive buffers are still DMA-mapped' or that 'the req can be freed while the HCA still owns the send buffer.' No runtime reproducer has surfaced, but the absence of a written-down invariant set lets each pass of automated review reach the same hypothetical conclusion. Subsequent fixes against ce2f9a4d9ccc ('xprtrdma: Decouple req recycling from RPC completion') closed the underlying races but did not document the closure where future readers will look for it. State the invariants explicitly in a comment above rpcrdma_reply_handler() and back four of them with WARN_ON_ONCE() probes positioned where each invariant is locally checkable on the previous patch's cleaned-up ownership state: - I1 (Receive WR ownership): WARN at rpcrdma_post_recvs() that a rep pulled from rb_free_reps carries rr_rqst == NULL. - I2 (rep attachment): WARN at rpcrdma_reply_put() that req->rl_reply was NULLed before the matching rep_put. - I3 (Registered-MR fence): WARN at rpcrdma_complete_rqst() that req->rl_registered is empty. Strong send-queue ordering of the LocalInv WR chain makes the last completion observe the ib_dma_unmap_sg() of every earlier MR, so 'list empty' implies 'all MRs unmapped'. - I4 (Send-buffer release): WARN at rpcrdma_req_release() that req->rl_sendctx is NULL. Reaching the kref release callback requires both the RPC-layer and Send-side references to have dropped; the Send-side drop runs in rpcrdma_sendctx_unmap(), which clears rl_sendctx (previous patch). A non-NULL rl_sendctx here would mean the Send-side owner had not run -- a contradiction. The XXX comment in xprt_rdma_free() about signal-driven release racing the Send completion described the pre-decouple state. Replace it with a one-line note pointing at the invariant set, since the kref scheme now holds the req across the in-flight Send regardless of which path released the rpc_task. I5 (req lifecycle) is stated in the comment but not probed: making it locally assertible would require moving kref_init out of rpcrdma_req_release(), which in turn requires adding kref_init to the bc_pa_list and backlog-wake reuse paths. That restructuring is deferred -- the invariant is unchanged either way. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker Stable-dep-of: 234c0ff695ef ("xprtrdma: Resize reply buffers before reposting receives") Signed-off-by: Sasha Levin --- net/sunrpc/xprtrdma/rpc_rdma.c | 69 +++++++++++++++++++++++++++++++++ net/sunrpc/xprtrdma/transport.c | 13 +++++-- net/sunrpc/xprtrdma/verbs.c | 6 +++ 3 files changed, 84 insertions(+), 4 deletions(-) diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index 69380f9dfa499..8121069472af2 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -1332,6 +1332,11 @@ void rpcrdma_complete_rqst(struct rpcrdma_rep *rep) struct rpc_rqst *rqst = rep->rr_rqst; int status; + /* I3: every registered MR has been invalidated and + * ib_dma_unmap_sg()'d before complete_rqst runs. + */ + WARN_ON_ONCE(!list_empty(&rpcr_to_rdmar(rqst)->rl_registered)); + switch (rep->rr_proc) { case rdma_msg: status = rpcrdma_decode_msg(r_xprt, rep, rqst); @@ -1363,6 +1368,70 @@ void rpcrdma_complete_rqst(struct rpcrdma_rep *rep) goto out; } +/* Reply-side ownership invariants + * + * I1 (Receive WR ownership). A struct rpcrdma_rep is owned by the + * HCA between ib_post_recv() and the matching Receive completion. + * After ib_dma_sync_single_for_cpu() in rpcrdma_wc_receive() it is + * owned by the CPU until rpcrdma_rep_put() returns it to + * rb_free_reps; a rep on rb_free_reps is not re-posted until + * rpcrdma_post_recvs() pulls it off. Asserted: rpcrdma_post_recvs() + * WARNs that a pulled rep has rr_rqst == NULL. + * + * I2 (rep attachment). While req->rl_reply == rep, the rep cannot be + * re-posted. rpcrdma_reply_put() NULLs req->rl_reply before handing + * the rep to rpcrdma_rep_put(). Asserted: rpcrdma_reply_put() WARNs + * that rl_reply is NULL after the put. + * + * I3 (Registered-MR fence). On entry to rpcrdma_complete_rqst() every + * MR that was on req->rl_registered has had its rkey invalidated + * (remotely via IB_WC_WITH_INVALIDATE or locally via IB_WR_LOCAL_INV) + * and its pages ib_dma_unmap_sg()'d. The LocalInv chain is posted + * on a single QP; strong send-queue ordering makes the last + * completion (frwr_wc_localinv_done) observe the + * ib_dma_unmap_sg() that ran from each earlier completion's + * frwr_mr_put() before complete_rqst is called. The inline + * frwr_reminv() path unmaps its one MR synchronously before + * rpcrdma_reply_handler() reaches complete_rqst. Asserted: + * rpcrdma_complete_rqst() WARNs that rl_registered is empty. + * + * I4 (Send-buffer release). req->rl_kref carries two unconditional + * owners while a Send is outstanding: the RPC-layer reference (set + * at xprt_rdma_alloc_slot / xprt_rdma_bc_rqst_get / rpcrdma_req_release + * pool-entry) and the Send-side reference (kref_get() in + * rpcrdma_prepare_send_sges()). rpcrdma_req_release() runs only + * after both have dropped, so the req does not return to its free + * pool until rpcrdma_sendctx_unmap() has fired -- the HCA has + * released the send buffer before the req can be reused. Asserted: + * rpcrdma_req_release() WARNs that rl_sendctx is NULL. + * + * I5 (req lifecycle). A req is owned by the RPC layer between slot + * acquisition and the matching xprt_rdma_free_slot() (or, for the + * backchannel, xprt_rdma_bc_free_rqst()). While owned, rl_kref >= 1. + * The pools (rb_send_bufs, bc_pa_list, backlog wake target) never + * contain a req with outstanding Send-side or Reply-side work. + * + * Non-hazards. The following claims have been raised by adversarial + * review and are each closed by the invariants above: + * + * * "Reply completes the RPC while the HCA still holds the send + * buffer" -- excluded by I4. The Send-side kref reference is held + * until rpcrdma_sendctx_unmap() runs from Send completion. + * + * * "Signal-driven release races the in-flight Send" -- same + * resolution. xprt_rdma_free() does not touch rl_kref; the + * Send-side reference keeps the req out of its pool until Send + * completion fires. + * + * * "Receive completion races rep reuse" -- excluded by I1. A rep + * is on rb_free_reps only after rpcrdma_rep_put() has been called + * and rpcrdma_post_recvs() owns the next transition back to the HCA. + * + * * "Pages still DMA-mapped when call_decode reads them" -- excluded + * by I3. The matching ib_dma_unmap_sg() for every MR has run on + * the same CPU thread that calls rpcrdma_complete_rqst(). + */ + /** * rpcrdma_reply_handler - Process received RPC/RDMA messages * @rep: Incoming rpcrdma_rep object to process diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c index 5569f17fdd9b5..5ff8e5126a6c2 100644 --- a/net/sunrpc/xprtrdma/transport.c +++ b/net/sunrpc/xprtrdma/transport.c @@ -509,6 +509,11 @@ static void rpcrdma_req_release(struct kref *kref) struct rpc_xprt *xprt = rqst->rq_xprt; struct rpcrdma_xprt *r_xprt; + /* I4: both the RPC-layer and Send-side owners have dropped, + * so rpcrdma_sendctx_unmap() has cleared rl_sendctx. + */ + WARN_ON_ONCE(req->rl_sendctx); + kref_init(&req->rl_kref); #if defined(CONFIG_SUNRPC_BACKCHANNEL) @@ -652,10 +657,10 @@ xprt_rdma_free(struct rpc_task *task) frwr_unmap_sync(rpcx_to_rdmax(rqst->rq_xprt), req); } - /* XXX: If the RPC is completing because of a signal and - * not because a reply was received, we ought to ensure - * that the Send completion has fired, so that memory - * involved with the Send is not still visible to the NIC. + /* The Send-side rl_kref owner keeps req out of its free pool + * until rpcrdma_sendctx_unmap() has fired -- see I4 above + * rpcrdma_reply_handler() -- so signal-driven release here + * does not let the HCA touch a recycled send buffer. */ } diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index d48d681bb49f4..ee6c4b18105ba 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -1250,6 +1250,10 @@ void rpcrdma_reply_put(struct rpcrdma_buffer *buffers, struct rpcrdma_req *req) rpcrdma_rep_put(buffers, req->rl_reply); req->rl_reply = NULL; } + /* I2: rl_reply NULL after the put closes the + * 'rep on rb_free_reps still referenced by req' window. + */ + WARN_ON_ONCE(req->rl_reply); } /** @@ -1426,6 +1430,8 @@ void rpcrdma_post_recvs(struct rpcrdma_xprt *r_xprt, int needed) rep = rpcrdma_rep_create(r_xprt); if (!rep) break; + /* I1: a rep on rb_free_reps must carry no rqst pointer. */ + WARN_ON_ONCE(rep->rr_rqst); if (!rpcrdma_regbuf_dma_map(r_xprt, rep->rr_rdmabuf)) { rpcrdma_rep_put(buf, rep); break; From 69c956c1b67d58e9ffd120319c31664bdecc5609 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Jun 2026 13:06:36 -0400 Subject: [PATCH 1671/2758] xprtrdma: Resize reply buffers before reposting receives [ Upstream commit 234c0ff695ef3ffb656931000e6b823d0c2f30fd ] Commit 0e13dd9ea8be ("xprtrdma: Remove temp allocation of rpcrdma_rep objects") made rpcrdma_rep objects survive disconnects. That is normally fine, but it also means their receive regbufs keep the size they had when they were first allocated. Each rep's receive buffer is sized to ep->re_inline_recv when the rep is created. rpcrdma_ep_create() resets that threshold to the rdma_max_inline_read ceiling for every new endpoint, and the connect handshake then shrinks it to the peer's advertised inline send size. A rep allocated under a smaller negotiated threshold keeps that size: on disconnect, rpcrdma_xprt_disconnect() drains and DMA-unmaps the surviving reps but does not free or resize them. The threshold can come back larger on the next connection. The first peer may supply no RPC-over-RDMA CM private data, defaulting its send size to 1024, while the reconnect target is an ordinary server offering 4096; or, with rdma_max_inline_read raised above its default, the reconnect target may advertise a larger svcrdma_max_req_size than the first. rpcrdma_post_recvs() then reposts a surviving rep whose SGE length is still the old, smaller value, and a larger inline Reply hits a receive length error and forces another disconnect. The undersized rep returns to the free list when its failed Receive flushes, so the following reconnect reposts the same rep and fails the same way. The transport flaps without making forward progress for as long as the peer keeps advertising the larger inline size. This is local/admin-triggerable rather than remote-triggerable: a local administrator must create and maintain the NFS/RDMA mount, while the server or reconnect target has to advertise a larger inline send size and return a reply that uses it. Fix this by checking each rep before it is reposted. If the receive regbuf is smaller than the current endpoint's inline receive size, reallocate it on the current RDMA device's NUMA node and reinitialize the rep's xdr_buf before DMA-mapping and posting the Receive WR. Fixes: 0e13dd9ea8be ("xprtrdma: Remove temp allocation of rpcrdma_rep objects") Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- net/sunrpc/xprtrdma/verbs.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index ee6c4b18105ba..6a8370c8f43d9 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -81,6 +81,8 @@ rpcrdma_regbuf_alloc_node(size_t size, enum dma_data_direction direction, int node); static struct rpcrdma_regbuf * rpcrdma_regbuf_alloc(size_t size, enum dma_data_direction direction); +static bool rpcrdma_regbuf_realloc_node(struct rpcrdma_regbuf *rb, + size_t size, gfp_t flags, int node); static void rpcrdma_regbuf_dma_unmap(struct rpcrdma_regbuf *rb); static void rpcrdma_regbuf_free(struct rpcrdma_regbuf *rb); @@ -1334,10 +1336,16 @@ rpcrdma_regbuf_alloc(size_t size, enum dma_data_direction direction) * returned, @rb is left untouched. */ bool rpcrdma_regbuf_realloc(struct rpcrdma_regbuf *rb, size_t size, gfp_t flags) +{ + return rpcrdma_regbuf_realloc_node(rb, size, flags, NUMA_NO_NODE); +} + +static bool rpcrdma_regbuf_realloc_node(struct rpcrdma_regbuf *rb, + size_t size, gfp_t flags, int node) { void *buf; - buf = kmalloc(size, flags); + buf = kmalloc_node(size, flags, node); if (!buf) return false; @@ -1349,6 +1357,23 @@ bool rpcrdma_regbuf_realloc(struct rpcrdma_regbuf *rb, size_t size, gfp_t flags) return true; } +static bool rpcrdma_rep_resize(struct rpcrdma_xprt *r_xprt, + struct rpcrdma_rep *rep) +{ + struct rpcrdma_regbuf *rb = rep->rr_rdmabuf; + struct rpcrdma_ep *ep = r_xprt->rx_ep; + size_t size = ep->re_inline_recv; + + if (likely(rdmab_length(rb) >= size)) + return true; + if (!rpcrdma_regbuf_realloc_node(rb, size, XPRTRDMA_GFP_FLAGS, + ibdev_to_node(ep->re_id->device))) + return false; + + xdr_buf_init(&rep->rr_hdrbuf, rdmab_data(rb), rdmab_length(rb)); + return true; +} + /** * __rpcrdma_regbuf_dma_map - DMA-map a regbuf * @r_xprt: controlling transport instance @@ -1432,6 +1457,10 @@ void rpcrdma_post_recvs(struct rpcrdma_xprt *r_xprt, int needed) break; /* I1: a rep on rb_free_reps must carry no rqst pointer. */ WARN_ON_ONCE(rep->rr_rqst); + if (!rpcrdma_rep_resize(r_xprt, rep)) { + rpcrdma_rep_put(buf, rep); + break; + } if (!rpcrdma_regbuf_dma_map(r_xprt, rep->rr_rdmabuf)) { rpcrdma_rep_put(buf, rep); break; From 118a16a18c59f7ad8084b2d13988839b669fca10 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Thu, 4 Jun 2026 13:06:37 -0400 Subject: [PATCH 1672/2758] xprtrdma: Fix bcall rep leak and unbounded peek [ Upstream commit c7653d5cebc8492c77ec0415b5e9c0fb3e644bc6 ] rpcrdma_is_bcall() decodes a reply's first words to decide whether the frame is a backchannel call. Two issues in that decode path let a short or malformed reply leak the receive buffer and drain the Receive queue. First, the speculative peek p = xdr_inline_decode(xdr, 0); /* five p++ reads follow */ asks xdr_inline_decode() for zero bytes, which returns xdr->p without consulting xdr->end. The five subsequent __be32 reads can then walk up to 20 bytes past the wire payload into stale regbuf contents and misclassify the reply as a backchannel call. Second, after the post-peek p = xdr_inline_decode(xdr, 3 * sizeof(*p)); if (unlikely(!p)) return true; the short-header arm returns true without calling rpcrdma_bc_receive_call(). The contract with the caller is that a true return transfers ownership of rep to the backchannel path: rpcrdma_reply_handler() if (rpcrdma_is_bcall(r_xprt, rep)) return; /* bare return, skips out_post */ ... out_post: rpcrdma_post_recvs(r_xprt, credits + ...); Because rpcrdma_bc_receive_call() never ran, no one took rep, but rpcrdma_reply_handler still bare-returns past rpcrdma_rep_put() and rpcrdma_post_recvs(). The rep, with its persistently DMA-mapped receive buffer, is orphaned on rb_all_reps and freed only at transport teardown. This completion reposts nothing, so its slot is reclaimed only when a later forward-channel reply reaches out_post and rpcrdma_post_recvs() allocates a fresh rep to backfill; absent that traffic the Receive queue drains and the peer's Sends draw RNR NAKs. Fix by consulting xdr->end after the zero-length peek so the five __be32 reads cannot run unless 20 bytes of wire payload remain. A byte-precise comparison against xdr->end is required because a non-4-aligned receive rounds the stream's word count up past the true payload. Also return false from the short-header arm so the reply falls through the normal out_norqst cleanup chain (rpcrdma_rep_put() plus rpcrdma_post_recvs()). Fixes: 41c8f70f5a3d ("xprtrdma: Harden backchannel call decoding") Assisted-by: kres:claude-opus-4-7 Signed-off-by: Chris Mason Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- net/sunrpc/xprtrdma/rpc_rdma.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index 8121069472af2..912d5de60abe8 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -1084,6 +1084,8 @@ rpcrdma_is_bcall(struct rpcrdma_xprt *r_xprt, struct rpcrdma_rep *rep) /* Peek at stream contents without advancing. */ p = xdr_inline_decode(xdr, 0); + if ((char *)xdr->end - (char *)p < 5 * XDR_UNIT) + return false; /* Chunk lists */ if (xdr_item_is_present(p++)) @@ -1108,7 +1110,7 @@ rpcrdma_is_bcall(struct rpcrdma_xprt *r_xprt, struct rpcrdma_rep *rep) */ p = xdr_inline_decode(xdr, 3 * sizeof(*p)); if (unlikely(!p)) - return true; + return false; rpcrdma_bc_receive_call(r_xprt, rep); return true; From 33db78b1b24fc6a464ae08aa4d2538c5f883eb5e Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Jun 2026 13:06:38 -0400 Subject: [PATCH 1673/2758] xprtrdma: Sanitize the reply credit grant after parsing [ Upstream commit c3a628aab2dc8f5fd7bff86ceaeae64de590e60a ] The out_norqst exit in rpcrdma_reply_handler() branches away before the credit clamp, so a reply that matches no pending request reaches out_post carrying the raw credit value parsed from the wire. rpcrdma_post_recvs() does not bound its @needed argument: the refill loop allocates and chains Receive WRs until the count is satisfied or allocation fails. A peer that sends a well-formed reply carrying an unknown XID and an inflated credit grant therefore drives rep allocation and Receive posting past re_max_requests on every such reply. Move the clamp to immediately after the credit field is parsed, ahead of the first branch that can reach out_post, so every later consumer sees a sanitized value. The cwnd update stays on the matched-request path. Fixes: 704f3f640f72 ("xprtrdma: Post receive buffers after RPC completion") Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- net/sunrpc/xprtrdma/rpc_rdma.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index 912d5de60abe8..9255d44960cca 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -1468,6 +1468,14 @@ void rpcrdma_reply_handler(struct rpcrdma_rep *rep) credits = be32_to_cpu(*p++); rep->rr_proc = *p++; + /* The credit grant from the wire is not trustworthy; + * sanitize it before any code path consumes it. + */ + if (credits == 0) + credits = 1; /* don't deadlock */ + else if (credits > r_xprt->rx_ep->re_max_requests) + credits = r_xprt->rx_ep->re_max_requests; + if (rep->rr_vers != rpcrdma_version) goto out_badversion; @@ -1484,10 +1492,6 @@ void rpcrdma_reply_handler(struct rpcrdma_rep *rep) xprt_pin_rqst(rqst); spin_unlock(&xprt->queue_lock); - if (credits == 0) - credits = 1; /* don't deadlock */ - else if (credits > r_xprt->rx_ep->re_max_requests) - credits = r_xprt->rx_ep->re_max_requests; if (buf->rb_credits != credits) rpcrdma_update_cwnd(r_xprt, credits); From d7c531ab477ae94fd03771d707fd29c787408039 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Jun 2026 13:06:39 -0400 Subject: [PATCH 1674/2758] xprtrdma: Repost Receive buffers for malformed replies [ Upstream commit abc011ddaf1617e3e82d8a1e87daa7ddbfb9bac5 ] rpcrdma_wc_receive() decrements the transport's Receive count for every completion before it dispatches a successful Receive to rpcrdma_reply_handler(). The handler must post a replacement Receive WR before returning unless ownership of the rep has moved elsewhere, as on the backchannel path. Commit 2ae50ad68cd7 ("xprtrdma: Close window between waking RPC senders and posting Receives") moved the Receive refill out of rpcrdma_wc_receive(), where it had run ahead of every reply, into rpcrdma_reply_handler() so that the responder's credit grant could be parsed before reposting. The bad-version and short-reply exits never reach that refill: they recycle the rep and return without calling rpcrdma_post_recvs(). A remote peer can therefore drain the client's posted Receive queue by sending a sustained stream of replies that are shorter than the fixed transport header or that carry an unrecognized RPC/RDMA version. Each such reply consumes one posted Receive without replacing it. Once the queue empties, the peer's next Send finds no posted Receive and the transport stalls until reconnect. Route both malformed-reply exits through the shared repost tail after recycling the rep, refilling against buf->rb_credits, the most recent accepted credit grant. Neither exit updates the congestion window, so RPCs admitted under the previous grant remain in flight awaiting replies. A smaller refill target would let a stream of malformed replies ratchet the posted Receive count down to the batch floor while the congestion window still admits rb_credits RPCs; a burst of valid replies to those RPCs could then overrun the posted Receives, and because the client connects with rnr_retry_count of zero, a single RNR NAK terminates the connection. Refilling against rb_credits also restores the target that applied to malformed replies before commit 2ae50ad68cd7 ("xprtrdma: Close window between waking RPC senders and posting Receives") when rpcrdma_post_recvs() computed it from rb_credits internally. rb_credits is at least one from connection establishment onward, so the repost path always keeps Receives posted. Fixes: 2ae50ad68cd7 ("xprtrdma: Close window between waking RPC senders and posting Receives") Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- net/sunrpc/xprtrdma/rpc_rdma.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index 9255d44960cca..ae90e6dc1487e 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -1524,11 +1524,13 @@ void rpcrdma_reply_handler(struct rpcrdma_rep *rep) out_badversion: trace_xprtrdma_reply_vers_err(rep); - goto out; + rpcrdma_rep_put(buf, rep); + credits = buf->rb_credits; + goto out_post; out_shortreply: trace_xprtrdma_reply_short_err(rep); - -out: rpcrdma_rep_put(buf, rep); + credits = buf->rb_credits; + goto out_post; } From 31298d37687b24e33e3d408aa38b3641cb095dc2 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Jun 2026 13:06:40 -0400 Subject: [PATCH 1675/2758] xprtrdma: Return sendctx slot after Send preparation failure [ Upstream commit 60e7870052f417d83965db144f70ae21fcfcf37f ] rpcrdma_prepare_send_sges() gets a sendctx before it maps the SGEs for the Send WR. If one of the mapping helpers fails, no Send WR is posted, so no Send completion is guaranteed to advance rb_sc_tail. Current cleanup clears sc_req so a later completion can sweep over that slot, but a consecutive run of preparation failures can still advance rb_sc_head until the ring appears full. At that point rpcrdma_sendctx_get_locked() returns NULL and no Send can be posted to produce the completion needed to recover the ring. The trigger requires CONFIG_SUNRPC_XPRT_RDMA and an NFS/RDMA mount. Mount setup and reliable DMA-map fault injection require local admin authority. Unprivileged I/O on an existing mount can exercise the send path, but a remote peer alone cannot force this local DMA-map failure. Add rpcrdma_sendctx_unget_locked() for the single-consumer send path to rewind rb_sc_head when the just-acquired sendctx is canceled before ib_post_send(). Wake waiters after making the slot available again. After the rewind, every slot the completion sweep visits belongs to a posted Send, so rpcrdma_sendctx_put_locked() no longer needs to test sc_req before unmapping. Fixes: ae72950abf99 ("xprtrdma: Add data structure to manage RDMA Send arguments") Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- net/sunrpc/xprtrdma/rpc_rdma.c | 5 ++++- net/sunrpc/xprtrdma/verbs.c | 40 +++++++++++++++++++++++++++++---- net/sunrpc/xprtrdma/xprt_rdma.h | 2 ++ 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index ae90e6dc1487e..e8b5fc86a01bb 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -743,6 +743,7 @@ inline int rpcrdma_prepare_send_sges(struct rpcrdma_xprt *r_xprt, struct xdr_buf *xdr, enum rpcrdma_chunktype rtype) { + struct rpcrdma_sendctx *sc; int ret; ret = -EAGAIN; @@ -785,7 +786,9 @@ inline int rpcrdma_prepare_send_sges(struct rpcrdma_xprt *r_xprt, return 0; out_unmap: - rpcrdma_sendctx_cancel(req->rl_sendctx); + sc = req->rl_sendctx; + rpcrdma_sendctx_cancel(sc); + rpcrdma_sendctx_unget_locked(r_xprt, sc); out_nosc: trace_xprtrdma_prepsend_failed(&req->rl_slot, ret); return ret; diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index 6a8370c8f43d9..0ac82f26bf6ec 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -631,6 +631,11 @@ static void rpcrdma_sendctxs_destroy(struct rpcrdma_xprt *r_xprt) /* The QP is drained, but the final unsignaled Sends might not * have been walked by a signaled Send completion. Release those * Send owners before request buffers are reset. + * + * Unlike the completion sweep, this walk can visit slots with + * no Send posted: after a partial rpcrdma_sendctxs_create() + * failure on reconnect, rb_sc_head and rb_sc_tail are stale, + * and slots between them can be NULL or have sc_req clear. */ for (i = rpcrdma_sendctx_next(buf, buf->rb_sc_tail); i != rpcrdma_sendctx_next(buf, buf->rb_sc_head); @@ -703,6 +708,12 @@ static unsigned long rpcrdma_sendctx_next(struct rpcrdma_buffer *buf, return likely(item < buf->rb_sc_last) ? item + 1 : 0; } +static unsigned long rpcrdma_sendctx_prev(struct rpcrdma_buffer *buf, + unsigned long item) +{ + return item > 0 ? item - 1 : buf->rb_sc_last; +} + /** * rpcrdma_sendctx_get_locked - Acquire a send context * @r_xprt: controlling transport instance @@ -759,6 +770,29 @@ struct rpcrdma_sendctx *rpcrdma_sendctx_get_locked(struct rpcrdma_xprt *r_xprt) return NULL; } +/** + * rpcrdma_sendctx_unget_locked - Release an unposted send context + * @r_xprt: controlling transport instance + * @sc: send context to release + * + * Usage: Called when no Send is posted for the sendctx most + * recently returned by rpcrdma_sendctx_get_locked(). + * + * The caller serializes calls to this function and to + * rpcrdma_sendctx_get_locked() (per transport). + */ +void rpcrdma_sendctx_unget_locked(struct rpcrdma_xprt *r_xprt, + struct rpcrdma_sendctx *sc) +{ + struct rpcrdma_buffer *buf = &r_xprt->rx_buf; + + if (WARN_ON_ONCE(buf->rb_sc_ctxs[buf->rb_sc_head] != sc)) + return; + + buf->rb_sc_head = rpcrdma_sendctx_prev(buf, buf->rb_sc_head); + xprt_write_space(&r_xprt->rx_xprt); +} + /** * rpcrdma_sendctx_put_locked - Release a send context * @r_xprt: controlling transport instance @@ -776,8 +810,7 @@ static void rpcrdma_sendctx_put_locked(struct rpcrdma_xprt *r_xprt, unsigned long next_tail; /* Release previously completed but unsignaled Sends by walking - * up the queue until @sc is found. Entries left behind by a - * failed rpcrdma_prepare_send_sges() have sc_req cleared. + * up the queue until @sc is found. */ next_tail = buf->rb_sc_tail; do { @@ -787,8 +820,7 @@ static void rpcrdma_sendctx_put_locked(struct rpcrdma_xprt *r_xprt, /* ORDER: item must be accessed _before_ tail is updated */ cur = buf->rb_sc_ctxs[next_tail]; - if (cur->sc_req) - rpcrdma_sendctx_unmap(cur); + rpcrdma_sendctx_unmap(cur); } while (buf->rb_sc_ctxs[next_tail] != sc); diff --git a/net/sunrpc/xprtrdma/xprt_rdma.h b/net/sunrpc/xprtrdma/xprt_rdma.h index f879d9b9f57ef..57be5776aaff1 100644 --- a/net/sunrpc/xprtrdma/xprt_rdma.h +++ b/net/sunrpc/xprtrdma/xprt_rdma.h @@ -495,6 +495,8 @@ void rpcrdma_req_destroy(struct rpcrdma_req *req); int rpcrdma_buffer_create(struct rpcrdma_xprt *); void rpcrdma_buffer_destroy(struct rpcrdma_buffer *); struct rpcrdma_sendctx *rpcrdma_sendctx_get_locked(struct rpcrdma_xprt *r_xprt); +void rpcrdma_sendctx_unget_locked(struct rpcrdma_xprt *r_xprt, + struct rpcrdma_sendctx *sc); struct rpcrdma_mr *rpcrdma_mr_get(struct rpcrdma_xprt *r_xprt); void rpcrdma_mrs_refresh(struct rpcrdma_xprt *r_xprt); From d625d9b320c20681df6917225a0e2f42389bc687 Mon Sep 17 00:00:00 2001 From: Jens Remus Date: Wed, 10 Jun 2026 13:24:51 +0200 Subject: [PATCH 1676/2758] perf s390: Fix TEXTREL in Python extension by compiling as PIC [ Upstream commit 49f5f6ae67dec54014584bb3126a5a94f14e2a5c ] On s390 the Python extension build fails as follows when using a linker that is configured to treat text relocations (TEXTREL) in shared libraries as error by default: GEN python/perf.cpython-314-s390x-linux-gnu.so /usr/bin/ld.bfd: error: read-only segment has dynamic relocations This occurrs because util/llvm-c-helpers.o is erroneously built from util/llvm-c-helpers.cpp without compiler option -fPIC but linked into the shared library (via libperf-util.a(perf-util-in.o)). On s390, object files must be compiled as position-indepedent code (PIC) in order to be linked into shared libraries. Commit a9a3f1d18a6c ("perf s390: Always build with -fPIC") added compiler option -fPIC to CFLAGS for s390, which is used in C compiles. Add -fPIC to CXXFLAGS for s390 as well, so that it is also used in C++ compiles. Fixes: a9a3f1d18a6c9ccf ("perf s390: Always build with -fPIC") Reported-by: Thomas Richter Reviewed-by: Ian Rogers Reviewed-by: James Clark Signed-off-by: Jens Remus Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Bill Wendling Cc: Heiko Carstens Cc: Hendrik Brueckner Cc: Ingo Molnar Cc: Jan Polensky Cc: Jiri Olsa Cc: Justin Stitt Cc: Mark Rutland Cc: Namhyung Kim Cc: Nathan Chancellor Cc: Nick Desaulniers Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/Makefile.config | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 333ddd0e4bd81..33f90968c8a95 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -109,6 +109,7 @@ endif ifeq ($(ARCH),s390) CFLAGS += -fPIC + CXXFLAGS += -fPIC endif ifeq ($(ARCH),mips) From c091fe7073b0bb86f838459db9099f3223e99126 Mon Sep 17 00:00:00 2001 From: Tanushree Shah Date: Sat, 6 Jun 2026 17:45:29 +0530 Subject: [PATCH 1677/2758] perf data convert json: Fix addr_location leak on time-filtered samples [ Upstream commit 2857a5dca750ea989c6fb70b4c14e801e4b7b4ad ] When samples are skipped due to time filtering in process_sample_event(), the early return path bypasses addr_location__exit(), causing memory leaks of thread, map, and maps references acquired by machine__resolve(). These references must be released through addr_location__exit() before returning. Fixes: 8e746e95c3e4eb56 ("perf data: Allow filtering conversion by time range") Reviewed-by: Ian Rogers Signed-off-by: Tanushree Shah Cc: Adrian Hunter Cc: Athira Rajeev Cc: Derek Foreman Cc: Hari Bathini Cc: Jiri Olsa Cc: Madhavan Srinivasan Cc: Michael Petlan Cc: Namhyung Kim Cc: Shivani.Nittor@ibm.com Cc: Tanushree.Shah@ibm.com Cc: Tejas.Manhas1@ibm.com Cc: Thomas Richter Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/data-convert-json.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/util/data-convert-json.c b/tools/perf/util/data-convert-json.c index d526c91312ed8..6a8d00c803942 100644 --- a/tools/perf/util/data-convert-json.c +++ b/tools/perf/util/data-convert-json.c @@ -177,6 +177,7 @@ static int process_sample_event(const struct perf_tool *tool, if (perf_time__ranges_skip_sample(c->ptime_range, c->range_num, sample->time)) { ++c->skipped; + addr_location__exit(&al); return 0; } From 4d72f46d420fba747d3a11928297f9f6877d92da Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 9 Jun 2026 15:40:06 +0100 Subject: [PATCH 1678/2758] perf cs-etm: Queue context packets for frontend [ Upstream commit 68ca50bc0fa64841cd73b8a1538df1d7f7eb4108 ] PE_CONTEXT elements update the context ID and exception level, but the decoder may still have prior packets cached for frontend processing. Updating the context immediately in the decoder backend can make those cached packets get consumed with the wrong thread or EL state. Add a CS_ETM_CONTEXT packet carrying the TID and EL to the frontend, this keeps context changes ordered with the rest of the packet stream and avoids mismatches when synthesizing samples from cached packets. Separate the memory access function into one for the frontend and one for decoding. The frontend also needs memory access to attach the instruction to samples. Because the frontend does memory access for both previous and current packets, change all the frontend memory access function signatures to take both a tidq and packet. But backend always uses the current backend EL and thread from the tidq. Treat context packets as a boundary for branch sample generation and remove tidq->prev_packet_thread because it's not possible to branch to a different thread, so only tracking the current thread is required for sample generation. Fixes: e573e978fb12e160 ("perf cs-etm: Inject capabilitity for CoreSight traces") Reported-by: Amir Ayupov Closes: https://lore.kernel.org/linux-perf-users/20260515021135.1729028-1-aaupov@meta.com/ Co-authored-by: James Clark Signed-off-by: Leo Yan Cc: Ian Rogers Cc: Jiri Olsa Cc: Jonathan Corbet Cc: linux-doc@vger.kernel.org Cc: Mike Leach Cc: Namhyung Kim Cc: Paschalis Mpeis Cc: Robert Walker Cc: Shuah Khan Cc: Suzuki Poulouse Signed-off-by: James Clark Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- .../perf/util/cs-etm-decoder/cs-etm-decoder.c | 21 +- tools/perf/util/cs-etm.c | 236 +++++++++++------- tools/perf/util/cs-etm.h | 8 +- 3 files changed, 163 insertions(+), 102 deletions(-) diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c index dee3020ceaa91..26940f1f1b0bf 100644 --- a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c +++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c @@ -402,6 +402,8 @@ cs_etm_decoder__buffer_packet(struct cs_etm_queue *etmq, packet_queue->packet_buffer[et].flags = 0; packet_queue->packet_buffer[et].exception_number = UINT32_MAX; packet_queue->packet_buffer[et].trace_chan_id = trace_chan_id; + packet_queue->packet_buffer[et].el = ocsd_EL_unknown; + packet_queue->packet_buffer[et].tid = -1; if (packet_queue->packet_count == CS_ETM_PACKET_MAX_BUFFER - 1) return OCSD_RESP_WAIT; @@ -449,6 +451,7 @@ cs_etm_decoder__buffer_range(struct cs_etm_queue *etmq, packet->last_instr_type = elem->last_i_type; packet->last_instr_subtype = elem->last_i_subtype; packet->last_instr_cond = elem->last_instr_cond; + packet->el = elem->context.exception_level; if (elem->last_i_type == OCSD_INSTR_BR || elem->last_i_type == OCSD_INSTR_BR_INDIRECT) packet->last_instr_taken_branch = elem->last_instr_exec; @@ -525,7 +528,9 @@ cs_etm_decoder__set_tid(struct cs_etm_queue *etmq, const ocsd_generic_trace_elem *elem, const uint8_t trace_chan_id) { + struct cs_etm_packet *packet; pid_t tid = -1; + int ret; /* * Process the PE_CONTEXT packets if we have a valid contextID or VMID. @@ -546,12 +551,18 @@ cs_etm_decoder__set_tid(struct cs_etm_queue *etmq, break; } - if (cs_etm__etmq_set_tid_el(etmq, tid, trace_chan_id, - elem->context.exception_level)) + if (cs_etm__etmq_update_decode_context(etmq, trace_chan_id, + elem->context.exception_level, tid)) return OCSD_RESP_FATAL_SYS_ERR; - if (tid == -1) - return OCSD_RESP_CONT; + ret = cs_etm_decoder__buffer_packet(etmq, packet_queue, trace_chan_id, + CS_ETM_CONTEXT); + if (ret != OCSD_RESP_CONT && ret != OCSD_RESP_WAIT) + return ret; + + packet = &packet_queue->packet_buffer[packet_queue->tail]; + packet->tid = tid; + packet->el = elem->context.exception_level; /* * A timestamp is generated after a PE_CONTEXT element so make sure @@ -559,7 +570,7 @@ cs_etm_decoder__set_tid(struct cs_etm_queue *etmq, */ cs_etm_decoder__reset_timestamp(packet_queue); - return OCSD_RESP_CONT; + return ret; } static ocsd_datapath_resp_t cs_etm_decoder__gen_trace_elem_printer( diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index 6ec48de294410..f6ffdf0796971 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -85,15 +85,22 @@ struct cs_etm_traceid_queue { u64 period_instructions; size_t last_branch_pos; union perf_event *event_buf; - struct thread *thread; - struct thread *prev_packet_thread; - ocsd_ex_level prev_packet_el; - ocsd_ex_level el; struct branch_stack *last_branch; struct branch_stack *last_branch_rb; struct cs_etm_packet *prev_packet; struct cs_etm_packet *packet; struct cs_etm_packet_queue packet_queue; + + struct thread *decode_thread; + ocsd_ex_level decode_el; + + /* + * The frontend accesses the EL from '[prev_]packet' because it needs + * previous EL for branch and current EL for instruction samples. It's + * not possible to change thread in a single branch sample so no need to + * store or access the thread through the packet. + */ + struct thread *frontend_thread; }; enum cs_etm_format { @@ -614,10 +621,11 @@ static int cs_etm__init_traceid_queue(struct cs_etm_queue *etmq, queue = &etmq->etm->queues.queue_array[etmq->queue_nr]; tidq->trace_chan_id = trace_chan_id; - tidq->el = tidq->prev_packet_el = ocsd_EL_unknown; - tidq->thread = machine__findnew_thread(&etm->session->machines.host, -1, + tidq->decode_el = ocsd_EL_unknown; + tidq->frontend_thread = machine__findnew_thread(&etm->session->machines.host, -1, + queue->tid); + tidq->decode_thread = machine__findnew_thread(&etm->session->machines.host, -1, queue->tid); - tidq->prev_packet_thread = machine__idle_thread(&etm->session->machines.host); tidq->packet = zalloc(sizeof(struct cs_etm_packet)); if (!tidq->packet) @@ -750,21 +758,10 @@ static void cs_etm__packet_swap(struct cs_etm_auxtrace *etm, /* * Swap PACKET with PREV_PACKET: PACKET becomes PREV_PACKET for * the next incoming packet. - * - * Threads and exception levels are also tracked for both the - * previous and current packets. This is because the previous - * packet is used for the 'from' IP for branch samples, so the - * thread at that time must also be assigned to that sample. - * Across discontinuity packets the thread can change, so by - * tracking the thread for the previous packet the branch sample - * will have the correct info. */ tmp = tidq->packet; tidq->packet = tidq->prev_packet; tidq->prev_packet = tmp; - tidq->prev_packet_el = tidq->el; - thread__put(tidq->prev_packet_thread); - tidq->prev_packet_thread = thread__get(tidq->thread); } } @@ -937,8 +934,8 @@ static void cs_etm__free_traceid_queues(struct cs_etm_queue *etmq) /* Free this traceid_queue from the array */ tidq = etmq->traceid_queues[idx]; - thread__zput(tidq->thread); - thread__zput(tidq->prev_packet_thread); + thread__zput(tidq->frontend_thread); + thread__zput(tidq->decode_thread); zfree(&tidq->event_buf); zfree(&tidq->last_branch); zfree(&tidq->last_branch_rb); @@ -1083,47 +1080,43 @@ static u8 cs_etm__cpu_mode(struct cs_etm_queue *etmq, u64 address, } } -static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id, - u64 address, size_t size, u8 *buffer, - const ocsd_mem_space_acc_t mem_space) +static u32 __cs_etm__mem_access(struct cs_etm_queue *etmq, + u64 address, size_t size, u8 *buffer, + const ocsd_mem_space_acc_t mem_space, + ocsd_ex_level el, struct thread *thread) { u8 cpumode; u64 offset; int len; struct addr_location al; struct dso *dso; - struct cs_etm_traceid_queue *tidq; int ret = 0; if (!etmq) return 0; addr_location__init(&al); - tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id); - if (!tidq) - goto out; /* - * We've already tracked EL along side the PID in cs_etm__set_thread() - * so double check that it matches what OpenCSD thinks as well. It - * doesn't distinguish between EL0 and EL1 for this mem access callback - * so we had to do the extra tracking. Skip validation if it's any of - * the 'any' values. + * We track EL for the frontend and the backend when receiving context + * and range packets. OpenCSD doesn't distinguish between EL0 and EL1 + * for this mem access callback so we had to do the extra tracking. Skip + * validation if it's any of the 'any' values. */ if (!(mem_space == OCSD_MEM_SPACE_ANY || mem_space == OCSD_MEM_SPACE_N || mem_space == OCSD_MEM_SPACE_S)) { if (mem_space & OCSD_MEM_SPACE_EL1N) { /* Includes both non secure EL1 and EL0 */ - assert(tidq->el == ocsd_EL1 || tidq->el == ocsd_EL0); + assert(el == ocsd_EL1 || el == ocsd_EL0); } else if (mem_space & OCSD_MEM_SPACE_EL2) - assert(tidq->el == ocsd_EL2); + assert(el == ocsd_EL2); else if (mem_space & OCSD_MEM_SPACE_EL3) - assert(tidq->el == ocsd_EL3); + assert(el == ocsd_EL3); } - cpumode = cs_etm__cpu_mode(etmq, address, tidq->el); + cpumode = cs_etm__cpu_mode(etmq, address, el); - if (!thread__find_map(tidq->thread, cpumode, address, &al)) + if (!thread__find_map(thread, cpumode, address, &al)) goto out; dso = map__dso(al.map); @@ -1138,7 +1131,7 @@ static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id, map__load(al.map); - len = dso__data_read_offset(dso, maps__machine(thread__maps(tidq->thread)), + len = dso__data_read_offset(dso, maps__machine(thread__maps(thread)), offset, buffer, size); if (len <= 0) { @@ -1158,6 +1151,30 @@ static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id, return ret; } +static u32 cs_etm__frontend_mem_access(struct cs_etm_queue *etmq, + struct cs_etm_traceid_queue *tidq, + struct cs_etm_packet *packet, + u64 address, size_t size, u8 *buffer) +{ + return __cs_etm__mem_access(etmq, address, size, buffer, 0, packet->el, + tidq->frontend_thread); +} + +static u32 cs_etm__decoder_mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id, + u64 address, size_t size, u8 *buffer, + const ocsd_mem_space_acc_t mem_space) +{ + struct cs_etm_traceid_queue *tidq; + + tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id); + if (!tidq) + return 0; + + return __cs_etm__mem_access(etmq, address, size, buffer, + mem_space, tidq->decode_el, + tidq->decode_thread); +} + static struct cs_etm_queue *cs_etm__alloc_queue(void) { struct cs_etm_queue *etmq = zalloc(sizeof(*etmq)); @@ -1333,12 +1350,13 @@ void cs_etm__reset_last_branch_rb(struct cs_etm_traceid_queue *tidq) } static inline int cs_etm__t32_instr_size(struct cs_etm_queue *etmq, - u8 trace_chan_id, u64 addr) + struct cs_etm_traceid_queue *tidq, + struct cs_etm_packet *packet, u64 addr) { u8 instrBytes[2]; - cs_etm__mem_access(etmq, trace_chan_id, addr, ARRAY_SIZE(instrBytes), - instrBytes, 0); + cs_etm__frontend_mem_access(etmq, tidq, packet, addr, + ARRAY_SIZE(instrBytes), instrBytes); /* * T32 instruction size is indicated by bits[15:11] of the first * 16-bit word of the instruction: 0b11101, 0b11110 and 0b11111 @@ -1371,16 +1389,16 @@ u64 cs_etm__last_executed_instr(const struct cs_etm_packet *packet) } static inline u64 cs_etm__instr_addr(struct cs_etm_queue *etmq, - u64 trace_chan_id, - const struct cs_etm_packet *packet, + struct cs_etm_traceid_queue *tidq, + struct cs_etm_packet *packet, u64 offset) { if (packet->isa == CS_ETM_ISA_T32) { u64 addr = packet->start_addr; while (offset) { - addr += cs_etm__t32_instr_size(etmq, - trace_chan_id, addr); + addr += cs_etm__t32_instr_size(etmq, tidq, packet, + addr); offset--; } return addr; @@ -1490,34 +1508,51 @@ cs_etm__get_trace(struct cs_etm_queue *etmq) return etmq->buf_len; } -static void cs_etm__set_thread(struct cs_etm_queue *etmq, - struct cs_etm_traceid_queue *tidq, pid_t tid, - ocsd_ex_level el) +/* + * Convert a raw thread number to a thread struct and assign it to **thread. + */ +static int cs_etm__etmq_update_thread(struct cs_etm_queue *etmq, + ocsd_ex_level el, pid_t tid, + struct thread **thread) { struct machine *machine = cs_etm__get_machine(etmq, el); + if (!machine || !*thread) + return -EINVAL; + if (tid != -1) { - thread__zput(tidq->thread); - tidq->thread = machine__find_thread(machine, -1, tid); + thread__zput(*thread); + *thread = machine__find_thread(machine, -1, tid); } /* Couldn't find a known thread */ - if (!tidq->thread) - tidq->thread = machine__idle_thread(machine); + if (!*thread) + *thread = machine__idle_thread(machine); - tidq->el = el; + return 0; } -int cs_etm__etmq_set_tid_el(struct cs_etm_queue *etmq, pid_t tid, - u8 trace_chan_id, ocsd_ex_level el) +/* + * Set the thread and EL of the decode context which is ahead in time of the + * frontend context. + */ +int cs_etm__etmq_update_decode_context(struct cs_etm_queue *etmq, + u8 trace_chan_id, + ocsd_ex_level el, pid_t tid) { struct cs_etm_traceid_queue *tidq; + int ret; tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id); if (!tidq) return -EINVAL; - cs_etm__set_thread(etmq, tidq, tid, el); + ret = cs_etm__etmq_update_thread(etmq, el, tid, + &tidq->decode_thread); + if (ret) + return ret; + + tidq->decode_el = el; return 0; } @@ -1527,8 +1562,8 @@ bool cs_etm__etmq_is_timeless(struct cs_etm_queue *etmq) } static void cs_etm__copy_insn(struct cs_etm_queue *etmq, - u64 trace_chan_id, - const struct cs_etm_packet *packet, + struct cs_etm_traceid_queue *tidq, + struct cs_etm_packet *packet, struct perf_sample *sample) { /* @@ -1545,14 +1580,14 @@ static void cs_etm__copy_insn(struct cs_etm_queue *etmq, * cs_etm__t32_instr_size(). */ if (packet->isa == CS_ETM_ISA_T32) - sample->insn_len = cs_etm__t32_instr_size(etmq, trace_chan_id, + sample->insn_len = cs_etm__t32_instr_size(etmq, tidq, packet, sample->ip); /* Otherwise, A64 and A32 instruction size are always 32-bit. */ else sample->insn_len = 4; - cs_etm__mem_access(etmq, trace_chan_id, sample->ip, sample->insn_len, - (void *)sample->insn, 0); + cs_etm__frontend_mem_access(etmq, tidq, packet, sample->ip, + sample->insn_len, (void *)sample->insn); } u64 cs_etm__convert_sample_time(struct cs_etm_queue *etmq, u64 cs_timestamp) @@ -1579,6 +1614,7 @@ static inline u64 cs_etm__resolve_sample_time(struct cs_etm_queue *etmq, static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq, struct cs_etm_traceid_queue *tidq, + struct cs_etm_packet *packet, u64 addr, u64 period) { int ret = 0; @@ -1588,23 +1624,23 @@ static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq, perf_sample__init(&sample, /*all=*/true); event->sample.header.type = PERF_RECORD_SAMPLE; - event->sample.header.misc = cs_etm__cpu_mode(etmq, addr, tidq->el); + event->sample.header.misc = cs_etm__cpu_mode(etmq, addr, packet->el); event->sample.header.size = sizeof(struct perf_event_header); /* Set time field based on etm auxtrace config. */ sample.time = cs_etm__resolve_sample_time(etmq, tidq); sample.ip = addr; - sample.pid = thread__pid(tidq->thread); - sample.tid = thread__tid(tidq->thread); + sample.pid = thread__pid(tidq->frontend_thread); + sample.tid = thread__tid(tidq->frontend_thread); sample.id = etmq->etm->instructions_id; sample.stream_id = etmq->etm->instructions_id; sample.period = period; - sample.cpu = tidq->packet->cpu; + sample.cpu = packet->cpu; sample.flags = tidq->prev_packet->flags; sample.cpumode = event->sample.header.misc; - cs_etm__copy_insn(etmq, tidq->trace_chan_id, tidq->packet, &sample); + cs_etm__copy_insn(etmq, tidq, packet, &sample); if (etm->synth_opts.last_branch) sample.branch_stack = tidq->last_branch; @@ -1649,15 +1685,15 @@ static int cs_etm__synth_branch_sample(struct cs_etm_queue *etmq, event->sample.header.type = PERF_RECORD_SAMPLE; event->sample.header.misc = cs_etm__cpu_mode(etmq, ip, - tidq->prev_packet_el); + tidq->prev_packet->el); event->sample.header.size = sizeof(struct perf_event_header); /* Set time field based on etm auxtrace config. */ sample.time = cs_etm__resolve_sample_time(etmq, tidq); sample.ip = ip; - sample.pid = thread__pid(tidq->prev_packet_thread); - sample.tid = thread__tid(tidq->prev_packet_thread); + sample.pid = thread__pid(tidq->frontend_thread); + sample.tid = thread__tid(tidq->frontend_thread); sample.addr = cs_etm__first_executed_instr(tidq->packet); sample.id = etmq->etm->branches_id; sample.stream_id = etmq->etm->branches_id; @@ -1666,8 +1702,7 @@ static int cs_etm__synth_branch_sample(struct cs_etm_queue *etmq, sample.flags = tidq->prev_packet->flags; sample.cpumode = event->sample.header.misc; - cs_etm__copy_insn(etmq, tidq->trace_chan_id, tidq->prev_packet, - &sample); + cs_etm__copy_insn(etmq, tidq, tidq->prev_packet, &sample); /* * perf report cannot handle events without a branch stack @@ -1788,7 +1823,6 @@ static int cs_etm__sample(struct cs_etm_queue *etmq, { struct cs_etm_auxtrace *etm = etmq->etm; int ret; - u8 trace_chan_id = tidq->trace_chan_id; u64 instrs_prev; /* Get instructions remainder from previous packet */ @@ -1874,10 +1908,10 @@ static int cs_etm__sample(struct cs_etm_queue *etmq, * been executed, but PC has not advanced to next * instruction) */ - addr = cs_etm__instr_addr(etmq, trace_chan_id, - tidq->packet, offset - 1); + addr = cs_etm__instr_addr(etmq, tidq, tidq->packet, + offset - 1); ret = cs_etm__synth_instruction_sample( - etmq, tidq, addr, + etmq, tidq, tidq->packet, addr, etm->instructions_sample_period); if (ret) return ret; @@ -1959,7 +1993,7 @@ static int cs_etm__flush(struct cs_etm_queue *etmq, addr = cs_etm__last_executed_instr(tidq->prev_packet); err = cs_etm__synth_instruction_sample( - etmq, tidq, addr, + etmq, tidq, tidq->prev_packet, addr, tidq->period_instructions); if (err) return err; @@ -2014,7 +2048,7 @@ static int cs_etm__end_block(struct cs_etm_queue *etmq, addr = cs_etm__last_executed_instr(tidq->prev_packet); err = cs_etm__synth_instruction_sample( - etmq, tidq, addr, + etmq, tidq, tidq->prev_packet, addr, tidq->period_instructions); if (err) return err; @@ -2051,9 +2085,9 @@ static int cs_etm__get_data_block(struct cs_etm_queue *etmq) return etmq->buf_len; } -static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id, - struct cs_etm_packet *packet, - u64 end_addr) +static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, + struct cs_etm_traceid_queue *tidq, + struct cs_etm_packet *packet, u64 end_addr) { /* Initialise to keep compiler happy */ u16 instr16 = 0; @@ -2075,8 +2109,8 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id, * so below only read 2 bytes as instruction size for T32. */ addr = end_addr - 2; - cs_etm__mem_access(etmq, trace_chan_id, addr, sizeof(instr16), - (u8 *)&instr16, 0); + cs_etm__frontend_mem_access(etmq, tidq, packet, addr, + sizeof(instr16), (u8 *)&instr16); if ((instr16 & 0xFF00) == 0xDF00) return true; @@ -2091,8 +2125,8 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id, * +---------+---------+-------------------------+ */ addr = end_addr - 4; - cs_etm__mem_access(etmq, trace_chan_id, addr, sizeof(instr32), - (u8 *)&instr32, 0); + cs_etm__frontend_mem_access(etmq, tidq, packet, addr, + sizeof(instr32), (u8 *)&instr32); if ((instr32 & 0x0F000000) == 0x0F000000 && (instr32 & 0xF0000000) != 0xF0000000) return true; @@ -2108,8 +2142,8 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id, * +-----------------------+---------+-----------+ */ addr = end_addr - 4; - cs_etm__mem_access(etmq, trace_chan_id, addr, sizeof(instr32), - (u8 *)&instr32, 0); + cs_etm__frontend_mem_access(etmq, tidq, packet, addr, + sizeof(instr32), (u8 *)&instr32); if ((instr32 & 0xFFE0001F) == 0xd4000001) return true; @@ -2125,7 +2159,6 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id, static bool cs_etm__is_syscall(struct cs_etm_queue *etmq, struct cs_etm_traceid_queue *tidq, u64 magic) { - u8 trace_chan_id = tidq->trace_chan_id; struct cs_etm_packet *packet = tidq->packet; struct cs_etm_packet *prev_packet = tidq->prev_packet; @@ -2140,7 +2173,7 @@ static bool cs_etm__is_syscall(struct cs_etm_queue *etmq, */ if (magic == __perf_cs_etmv4_magic) { if (packet->exception_number == CS_ETMV4_EXC_CALL && - cs_etm__is_svc_instr(etmq, trace_chan_id, prev_packet, + cs_etm__is_svc_instr(etmq, tidq, prev_packet, prev_packet->end_addr)) return true; } @@ -2178,7 +2211,6 @@ static bool cs_etm__is_sync_exception(struct cs_etm_queue *etmq, struct cs_etm_traceid_queue *tidq, u64 magic) { - u8 trace_chan_id = tidq->trace_chan_id; struct cs_etm_packet *packet = tidq->packet; struct cs_etm_packet *prev_packet = tidq->prev_packet; @@ -2204,7 +2236,7 @@ static bool cs_etm__is_sync_exception(struct cs_etm_queue *etmq, * (SMC, HVC) are taken as sync exceptions. */ if (packet->exception_number == CS_ETMV4_EXC_CALL && - !cs_etm__is_svc_instr(etmq, trace_chan_id, prev_packet, + !cs_etm__is_svc_instr(etmq, tidq, prev_packet, prev_packet->end_addr)) return true; @@ -2228,7 +2260,6 @@ static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq, { struct cs_etm_packet *packet = tidq->packet; struct cs_etm_packet *prev_packet = tidq->prev_packet; - u8 trace_chan_id = tidq->trace_chan_id; u64 magic; int ret; @@ -2309,11 +2340,11 @@ static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq, if (prev_packet->flags == (PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_INTERRUPT) && - cs_etm__is_svc_instr(etmq, trace_chan_id, - packet, packet->start_addr)) + cs_etm__is_svc_instr(etmq, tidq, packet, packet->start_addr)) { prev_packet->flags = PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_SYSCALLRET; + } break; case CS_ETM_DISCONTINUITY: /* @@ -2394,6 +2425,7 @@ static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq, PERF_IP_FLAG_RETURN | PERF_IP_FLAG_INTERRUPT; break; + case CS_ETM_CONTEXT: case CS_ETM_EMPTY: default: break; @@ -2469,6 +2501,19 @@ static int cs_etm__process_traceid_queue(struct cs_etm_queue *etmq, */ cs_etm__sample(etmq, tidq); break; + case CS_ETM_CONTEXT: + /* + * Update context but don't swap packet. Keep the + * previous one for branch source address info, if + * tracing the kernel the context packet will be emitted + * between two ranges. + */ + ret = cs_etm__etmq_update_thread(etmq, tidq->packet->el, + tidq->packet->tid, + &tidq->frontend_thread); + if (ret) + goto out; + break; case CS_ETM_EXCEPTION: case CS_ETM_EXCEPTION_RET: /* @@ -2497,6 +2542,7 @@ static int cs_etm__process_traceid_queue(struct cs_etm_queue *etmq, } } +out: return ret; } @@ -2620,7 +2666,7 @@ static int cs_etm__process_timeless_queues(struct cs_etm_auxtrace *etm, if (!tidq) continue; - if (tid == -1 || thread__tid(tidq->thread) == tid) + if (tid == -1 || thread__tid(tidq->frontend_thread) == tid) cs_etm__run_per_thread_timeless_decoder(etmq); } else cs_etm__run_per_cpu_timeless_decoder(etmq); @@ -3328,7 +3374,7 @@ static int cs_etm__create_queue_decoders(struct cs_etm_queue *etmq) */ if (cs_etm_decoder__add_mem_access_cb(etmq->decoder, 0x0L, ((u64) -1L), - cs_etm__mem_access)) + cs_etm__decoder_mem_access)) goto out_free_decoder; zfree(&t_params); diff --git a/tools/perf/util/cs-etm.h b/tools/perf/util/cs-etm.h index aa9bb4a32ecaf..b81099c2b301c 100644 --- a/tools/perf/util/cs-etm.h +++ b/tools/perf/util/cs-etm.h @@ -158,6 +158,7 @@ enum cs_etm_sample_type { CS_ETM_DISCONTINUITY, CS_ETM_EXCEPTION, CS_ETM_EXCEPTION_RET, + CS_ETM_CONTEXT, }; enum cs_etm_isa { @@ -184,6 +185,8 @@ struct cs_etm_packet { u8 last_instr_size; u8 trace_chan_id; int cpu; + int el; + pid_t tid; }; #define CS_ETM_PACKET_MAX_BUFFER 1024 @@ -259,8 +262,9 @@ enum cs_etm_pid_fmt { #include int cs_etm__get_cpu(struct cs_etm_queue *etmq, u8 trace_chan_id, int *cpu); enum cs_etm_pid_fmt cs_etm__get_pid_fmt(struct cs_etm_queue *etmq); -int cs_etm__etmq_set_tid_el(struct cs_etm_queue *etmq, pid_t tid, - u8 trace_chan_id, ocsd_ex_level el); +int cs_etm__etmq_update_decode_context(struct cs_etm_queue *etmq, + u8 trace_chan_id, ocsd_ex_level el, + pid_t tid); bool cs_etm__etmq_is_timeless(struct cs_etm_queue *etmq); void cs_etm__etmq_set_traceid_queue_timestamp(struct cs_etm_queue *etmq, u8 trace_chan_id); From 1202ebd3a9b49f24ee904c1386dd6bc8c1ce2c9e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 21:01:43 -0300 Subject: [PATCH 1679/2758] perf pmu: Fix pmu_id() heap underwrite on empty identifier file [ Upstream commit 836455e6dbd34eb3d12eeab5e2d2b9a7f1512459 ] pmu_id() calls filename__read_str() then strips the trailing newline via str[len - 1] = 0. If the PMU identifier file is empty, filename__read_str() succeeds with len = 0. len - 1 underflows size_t to SIZE_MAX, writing a null byte before the heap allocation. Add a len == 0 check before the newline stripping. Fixes: 51d548471510843e ("perf pmu: Add pmu_id()") Reported-by: sashiko-bot Cc: John Garry Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/pmu.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 23337d2fa2819..dc12e39ceb5fc 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -865,6 +865,12 @@ static char *pmu_id(const char *name) if (filename__read_str(path, &str, &len) < 0) return NULL; + /* empty identifier file — nothing useful */ + if (len == 0) { + free(str); + return NULL; + } + str[len - 1] = 0; /* remove line feed */ return str; From a407a5177cd19a9b584e5d5ba8384cf3e46494c7 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 21:03:13 -0300 Subject: [PATCH 1680/2758] perf pmu: Fix perf_pmu__parse_scale/unit() OOB access on empty sysfs file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 33035f7dd4e49f3f117e70c5e36c8c1ae88d37f2 ] perf_pmu__parse_scale() reads a PMU scale file then accesses scale[sret - 1] to strip a trailing newline. Only sret < 0 is guarded, so an empty file (sret == 0) causes scale[-1] — a stack buffer underflow that reads and potentially writes out of bounds. perf_pmu__parse_unit() has the same pattern: alias->unit[sret - 1] with sret == 0 accesses the byte before the struct member, which may corrupt the adjacent pmu_name pointer field. Change both guards from sret < 0 to sret <= 0 so that empty files are treated as read errors. Fixes: 410136f5dd96b601 ("tools/perf/stat: Add event unit and scale support") Reported-by: sashiko-bot Cc: Stephane Eranian Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/pmu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index dc12e39ceb5fc..64544e1e6810f 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -314,7 +314,7 @@ static int perf_pmu__parse_scale(struct perf_pmu *pmu, struct perf_pmu_alias *al goto error; sret = read(fd, scale, sizeof(scale)-1); - if (sret < 0) + if (sret <= 0) goto error; if (scale[sret - 1] == '\n') @@ -346,7 +346,7 @@ static int perf_pmu__parse_unit(struct perf_pmu *pmu, struct perf_pmu_alias *ali return -1; sret = read(fd, alias->unit, UNIT_MAX_LEN); - if (sret < 0) + if (sret <= 0) goto error; close(fd); From b4333af83c12cdf4efc882e991851beb17432324 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 21:03:55 -0300 Subject: [PATCH 1681/2758] tools lib api: Fix missing null termination in filename__read_int/ull() [ Upstream commit 52b1f9678499b13b7aeb0186d9c6f486c043283f ] filename__read_int() passes a stack buffer to read() using the full sizeof(line) and then hands it to atoi() without null-terminating. If a sysfs file fills the 64-byte buffer exactly, atoi() reads past the array into uninitialized stack memory. filename__read_ull_base() has the same issue with strtoull(). Fix both by reading sizeof(line) - 1 bytes and explicitly null-terminating after a successful read. Fixes: 3a351127cbc682c3 ("tools lib fs: Adopt filename__read_int from tools/perf/") Reported-by: sashiko-bot Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/lib/api/fs/fs.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tools/lib/api/fs/fs.c b/tools/lib/api/fs/fs.c index edec23406dbc6..3cc302d4c47b1 100644 --- a/tools/lib/api/fs/fs.c +++ b/tools/lib/api/fs/fs.c @@ -294,11 +294,14 @@ int filename__read_int(const char *filename, int *value) { char line[64]; int fd = open(filename, O_RDONLY), err = -1; + ssize_t n; if (fd < 0) return -errno; - if (read(fd, line, sizeof(line)) > 0) { + n = read(fd, line, sizeof(line) - 1); + if (n > 0) { + line[n] = '\0'; *value = atoi(line); err = 0; } @@ -312,11 +315,14 @@ static int filename__read_ull_base(const char *filename, { char line[64]; int fd = open(filename, O_RDONLY), err = -1; + ssize_t n; if (fd < 0) return -errno; - if (read(fd, line, sizeof(line)) > 0) { + n = read(fd, line, sizeof(line) - 1); + if (n > 0) { + line[n] = '\0'; *value = strtoull(line, NULL, base); if (*value != ULLONG_MAX) err = 0; From 519b4ad15b2c21425732bce152b6f7be342a0bf1 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 21:04:50 -0300 Subject: [PATCH 1682/2758] perf symbols: Fix signed overflow in sysfs__read_build_id() size check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 6eaa8ee3e2abe5112e80e94c27196bb175689469 ] sysfs__read_build_id() reads ELF note headers from sysfs files. The note's namesz and descsz fields are used to compute the skip size: int n = namesz + descsz; if (n > (int)sizeof(bf)) Both namesz and descsz are size_t from NOTE_ALIGN() of 32-bit note header fields. Their sum can exceed INT_MAX, overflowing the signed int n to a negative value. The check n > sizeof(bf) then evaluates false (negative < positive in signed comparison), and read(fd, bf, n) reinterprets the negative n as a huge size_t count — the kernel writes up to MAX_RW_COUNT bytes into the 8192-byte stack buffer. In practice the overflow is bounded by the sysfs file's actual size, so a real sysfs notes file won't trigger it organically. But crafted input (e.g. via a mounted debugfs/sysfs image) could. Fix by validating namesz and descsz individually against the buffer size before summing, and change n to size_t to avoid the signed overflow entirely. Fixes: f1617b40596cb341 ("perf symbols: Record the build_ids of kernel modules too") Reported-by: sashiko-bot Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/symbol-elf.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 0ba2c60ea5aab..792d270c3a70d 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -963,14 +963,17 @@ int sysfs__read_build_id(const char *filename, struct build_id *bid) } else if (read(fd, bf, descsz) != (ssize_t)descsz) break; } else { - int n = namesz + descsz; + size_t n; - if (n > (int)sizeof(bf)) { + /* int sum of namesz+descsz can overflow negative, bypassing size check */ + if (namesz > sizeof(bf) || descsz > sizeof(bf) - namesz) { n = sizeof(bf); pr_debug("%s: truncating reading of build id in sysfs file %s: n_namesz=%u, n_descsz=%u.\n", __func__, filename, nhdr.n_namesz, nhdr.n_descsz); + } else { + n = namesz + descsz; } - if (read(fd, bf, n) != n) + if (read(fd, bf, n) != (ssize_t)n) break; } } From 51d3124590bcfe028ea7bb4700f8529f21d068c6 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 21:05:15 -0300 Subject: [PATCH 1683/2758] perf symbols: Bounds-check .gnu_debuglink section data [ Upstream commit 9c74f0aab398cb32ab250401f323c0fdc9a3a496 ] filename__read_debuglink() copies .gnu_debuglink section data into a caller-provided buffer via: strncpy(debuglink, data->d_buf, size); where size is PATH_MAX. If the ELF section is smaller than size and lacks a null terminator, strncpy reads past data->d_buf into adjacent memory. A malformed ELF file can trigger this, potentially causing a segfault or leaking heap data. Additionally, strncpy does not guarantee null termination when the source fills the buffer. Replace with an explicit memcpy bounded by both the output buffer size and the actual section data size (data->d_size), followed by explicit null termination. Fixes: e5a1845fc0aeca85 ("perf symbols: Split out util/symbol-elf.c") Reported-by: sashiko-bot Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/symbol-elf.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 792d270c3a70d..3b2e31367196c 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -1026,7 +1026,14 @@ int filename__read_debuglink(const char *filename, char *debuglink, goto out_elf_end; /* the start of this section is a zero-terminated string */ - strncpy(debuglink, data->d_buf, size); + if (data->d_size > 0) { + size_t len = min(size - 1, data->d_size); + + memcpy(debuglink, data->d_buf, len); + debuglink[len] = '\0'; + } else { + debuglink[0] = '\0'; + } err = 0; From 5e5b4cfffb4af863021cd6a378f4b213e64df04e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 21:06:12 -0300 Subject: [PATCH 1684/2758] perf tools: Use mkostemp() for O_CLOEXEC on temporary files [ Upstream commit b8acc68f1382a3f380a0e7320a95d328ac5e9027 ] mkstemp() creates file descriptors without the close-on-exec flag. These fds leak to child processes spawned during symbol resolution (addr2line, objdump), wasting descriptors and potentially exposing temporary file contents. Replace mkstemp() with mkostemp(tmpbuf, O_CLOEXEC) at all three call sites: - filename__decompress() in dso.c - read_gnu_debugdata() in symbol-elf.c - kcore__init() in symbol-elf.c Fixes: 42b3fa670825983f ("perf tools: Introduce dso__decompress_kmodule_{fd,path}") Fixes: b10f74308e130527 ("perf symbol: Support .gnu_debugdata for symbols") Fixes: afba19d9dc8eba66 ("perf symbols: Workaround objdump difficulties with kcore") Reported-by: sashiko-bot Cc: Adrian Hunter Cc: Namhyung Kim Cc: Stephen Brennan Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/dso.c | 2 +- tools/perf/util/symbol-elf.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 511e91843fbd8..d878c46de52a7 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -346,7 +346,7 @@ int filename__decompress(const char *name, char *pathname, if (!compressions[comp].is_compressed(name)) return open(name, O_RDONLY | O_CLOEXEC); - fd = mkstemp(tmpbuf); + fd = mkostemp(tmpbuf, O_CLOEXEC); if (fd < 0) { *err = errno; return -1; diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 3b2e31367196c..c415670453bed 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -1118,9 +1118,9 @@ static Elf *read_gnu_debugdata(struct dso *dso, Elf *elf, const char *name, int return NULL; } - temp_fd = mkstemp(temp_filename); + temp_fd = mkostemp(temp_filename, O_CLOEXEC); if (temp_fd < 0) { - pr_debug("%s: mkstemp: %m\n", __func__); + pr_debug("%s: mkostemp: %m\n", __func__); *dso__load_errno(dso) = -errno; fclose(wrapped); return NULL; @@ -1986,7 +1986,7 @@ static int kcore__init(struct kcore *kcore, char *filename, int elfclass, kcore->elfclass = elfclass; if (temp) - kcore->fd = mkstemp(filename); + kcore->fd = mkostemp(filename, O_CLOEXEC); else kcore->fd = open(filename, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0400); if (kcore->fd == -1) From 8532c1725abbb994c75c25f3168dfdac8bf34ac8 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 21:06:54 -0300 Subject: [PATCH 1685/2758] perf intel-pt: Fix snprintf size tracking bug in insn decoder [ Upstream commit b6bb3b005dcdd960b8e0b7f9d6869132b3de08d5 ] dump_insn() tracks remaining buffer space with a 'left' variable, but the loop subtracts the cumulative offset 'n' each iteration instead of just the per-iteration delta: n += snprintf(x->out + n, left, "%02x ", inbuf[i]); left -= n; /* BUG: n is cumulative, not the delta */ After two iterations left goes massively negative, wrapping to a huge value when passed as size_t to snprintf(), disabling all bounds checking for the rest of the loop. Switch to scnprintf() accumulation using sizeof(x->out) - n as the remaining space, which is always correct and eliminates the separate 'left' variable entirely. Fixes: 48d02a1d5c137d36 ("perf script: Add 'brstackinsn' for branch stacks") Reported-by: sashiko-bot Cc: Adrian Hunter Cc: Andi Kleen Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- .../util/intel-pt-decoder/intel-pt-insn-decoder.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c index 72c7a4e15d617..f90fcbc4302df 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c @@ -220,7 +220,6 @@ const char *dump_insn(struct perf_insn *x, uint64_t ip __maybe_unused, { struct insn insn; int n, i, ret; - int left; ret = insn_decode(&insn, inbuf, inlen, x->is64bit ? INSN_MODE_64 : INSN_MODE_32); @@ -229,13 +228,9 @@ const char *dump_insn(struct perf_insn *x, uint64_t ip __maybe_unused, return ""; if (lenp) *lenp = insn.length; - left = sizeof(x->out); - n = snprintf(x->out, left, "insn: "); - left -= n; - for (i = 0; i < insn.length; i++) { - n += snprintf(x->out + n, left, "%02x ", inbuf[i]); - left -= n; - } + n = scnprintf(x->out, sizeof(x->out), "insn: "); + for (i = 0; i < insn.length; i++) + n += scnprintf(x->out + n, sizeof(x->out) - n, "%02x ", inbuf[i]); return x->out; } From 6290c0c0fb2b642d6a09bd113ece03618de2818d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 22:37:55 -0300 Subject: [PATCH 1686/2758] perf tools: Fix thread__set_comm_from_proc() on empty comm file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 31d596054550f793508abe7dd593853ece47d428 ] thread__set_comm_from_proc() calls procfs__read_str() then strips the trailing newline via comm[sz - 1] = '\0'. procfs__read_str() allocates the buffer before reading, so on an empty /proc/pid/comm (reachable during late exit teardown) it returns success with sz = 0 and an unterminated heap buffer. The sz - 1 underflow was the original sashiko finding: it writes a null byte before the allocation. But even with a sz > 0 guard on the newline strip, the unterminated buffer would still be passed to thread__set_comm() which calls strlen() — an unbounded heap read. Fix by treating sz == 0 as failure: free the buffer and return -1. This is consistent with pmu.c's perf_pmu__parse_scale/unit which already treat len == 0 from filename__read_str as an error. Fixes: 2f3027ac28bf6bc3 ("perf thread: Introduce method to set comm from /proc/pid/self") Reported-by: sashiko-bot Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/thread.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 22be77225bb0c..2faa258f9c15f 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -294,6 +294,11 @@ int thread__set_comm_from_proc(struct thread *thread) if (!(snprintf(path, sizeof(path), "%d/task/%d/comm", thread__pid(thread), thread__tid(thread)) >= (int)sizeof(path)) && procfs__read_str(path, &comm, &sz) == 0) { + /* sz==0: read got nothing, e.g. race during exit teardown */ + if (sz == 0) { + free(comm); + return -1; + } comm[sz - 1] = '\0'; err = thread__set_comm(thread, comm, 0); } From 4c7ed5f4ff3673087afaf9ef1b8ccc65d7f6044d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 22:38:26 -0300 Subject: [PATCH 1687/2758] perf hwmon: Fix off-by-one null termination on sysfs reads [ Upstream commit 1847c5fae344a0fb9cc0f95be40b378359c6fc3b ] Three functions read sysfs files into fixed-size stack buffers using the full buffer size, then null-terminate at buf[read_len]. If the read fills the buffer exactly, read_len equals sizeof(buf) and the null byte writes one past the array, corrupting an adjacent stack variable. Fix all three by reading sizeof(buf) - 1 bytes, reserving space for the null terminator: - hwmon_pmu__read_events(): buf[128] - hwmon_pmu__describe_items(): buf[64] - evsel__hwmon_pmu_read(): buf[32] Fixes: 53cc0b351ec99278 ("perf hwmon_pmu: Add a tool PMU exposing events from hwmon in sysfs") Reported-by: sashiko-bot Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/hwmon_pmu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/hwmon_pmu.c b/tools/perf/util/hwmon_pmu.c index dbf6a71af47f9..d895cd74f2a05 100644 --- a/tools/perf/util/hwmon_pmu.c +++ b/tools/perf/util/hwmon_pmu.c @@ -289,7 +289,7 @@ static int hwmon_pmu__read_events(struct hwmon_pmu *pmu) if (fd < 0) continue; - read_len = read(fd, buf, sizeof(buf)); + read_len = read(fd, buf, sizeof(buf) - 1); while (read_len > 0 && buf[read_len - 1] == '\n') read_len--; @@ -432,7 +432,7 @@ static size_t hwmon_pmu__describe_items(struct hwmon_pmu *hwm, char *out_buf, si is_alarm ? "_alarm" : ""); fd = openat(dir, buf, O_RDONLY); if (fd > 0) { - ssize_t read_len = read(fd, buf, sizeof(buf)); + ssize_t read_len = read(fd, buf, sizeof(buf) - 1); while (read_len > 0 && buf[read_len - 1] == '\n') read_len--; @@ -816,7 +816,7 @@ int evsel__hwmon_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread) count = perf_counts(evsel->counts, cpu_map_idx, thread); fd = FD(evsel, cpu_map_idx, thread); - len = pread(fd, buf, sizeof(buf), 0); + len = pread(fd, buf, sizeof(buf) - 1, 0); if (len <= 0) { count->lost++; return -EINVAL; From f99e250f8085f5302b474c936a91049a91a35230 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 22:38:48 -0300 Subject: [PATCH 1688/2758] perf hwmon: Use scnprintf() in hwmon_pmu__for_each_event() [ Upstream commit ae75956c166fe169b8c137bf375305fc97820a62 ] hwmon_pmu__for_each_event() formats description strings via: len = snprintf(desc_buf, sizeof(desc_buf), "%s in unit %s named %s.", ...); len += hwmon_pmu__describe_items(hwm, desc_buf + len, sizeof(desc_buf) - len, ...); If value->label is long enough to cause snprintf() to truncate, it returns the would-have-been-written count, making len exceed sizeof(desc_buf). The subsequent sizeof(desc_buf) - len underflows to a huge size_t value, disabling bounds checking in hwmon_pmu__describe_items(). The alias_buf snprintf has the same issue. Switch both to scnprintf() which returns actual bytes written. Fixes: 53cc0b351ec99278 ("perf hwmon_pmu: Add a tool PMU exposing events from hwmon in sysfs") Reported-by: sashiko-bot Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/hwmon_pmu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/hwmon_pmu.c b/tools/perf/util/hwmon_pmu.c index d895cd74f2a05..fbfb872ceb182 100644 --- a/tools/perf/util/hwmon_pmu.c +++ b/tools/perf/util/hwmon_pmu.c @@ -514,14 +514,14 @@ int hwmon_pmu__for_each_event(struct perf_pmu *pmu, void *state, pmu_event_callb int ret; size_t len; - len = snprintf(alias_buf, sizeof(alias_buf), "%s%d", - hwmon_type_strs[key.type], key.num); + scnprintf(alias_buf, sizeof(alias_buf), "%s%d", + hwmon_type_strs[key.type], key.num); if (!info.name) { info.name = info.alias; info.alias = NULL; } - len = snprintf(desc_buf, sizeof(desc_buf), "%s in unit %s named %s.", + len = scnprintf(desc_buf, sizeof(desc_buf), "%s in unit %s named %s.", hwmon_desc[key.type], pmu->name + 6, value->label ?: info.name); From 354a61c752ea4f649eec818c0b9a296dedc9373a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 22:39:53 -0300 Subject: [PATCH 1689/2758] perf hwmon: Fix parse_hwmon_filename() strlcpy buffer overflow [ Upstream commit e1a2c9d70b312acc262f6be936dd5bbd9bbc6236 ] parse_hwmon_filename() strips the "_alarm" suffix from event names by copying into a 24-byte stack buffer: strlcpy(fn_type, fn_item, fn_item_len - 5); The third argument is the source length minus the suffix, not the destination buffer capacity. A long event name ending in "_alarm" can have fn_item_len - 5 > sizeof(fn_type), causing strlcpy() to write past the 24-byte fn_type[] array. The assert() only validates that the longest *valid* hwmon item fits, but does not protect against crafted input. Clamp the strlcpy size to min(fn_item_len - 5, sizeof(fn_type)). Fixes: 4810b761f812da3c ("perf hwmon_pmu: Add hwmon filename parser") Reported-by: sashiko-bot Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/hwmon_pmu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/hwmon_pmu.c b/tools/perf/util/hwmon_pmu.c index fbfb872ceb182..bdcbe887579a1 100644 --- a/tools/perf/util/hwmon_pmu.c +++ b/tools/perf/util/hwmon_pmu.c @@ -202,7 +202,8 @@ bool parse_hwmon_filename(const char *filename, fn_item_len = strlen(fn_item); if (fn_item_len > 6 && !strcmp(&fn_item[fn_item_len - 6], "_alarm")) { assert(strlen(LONGEST_HWMON_ITEM_STR) < sizeof(fn_type)); - strlcpy(fn_type, fn_item, fn_item_len - 5); + /* fn_item_len - 5 strips "_alarm"; clamp to buffer size */ + strlcpy(fn_type, fn_item, min_t(size_t, fn_item_len - 5, sizeof(fn_type))); fn_item = fn_type; *alarm = true; } From a11731df15af0a7bd62a5f5a06eec4b499615f90 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 22:40:20 -0300 Subject: [PATCH 1690/2758] perf symbols: Bounds-check descsz in sysfs__read_build_id() GNU fallback [ Upstream commit 1b4e9fbdeabc549965e70ac0cd8095d57ff6df06 ] When sysfs__read_build_id() matches NT_GNU_BUILD_ID with the right namesz but the name content is not "GNU", it falls back to reading descsz bytes into the stack buffer bf[BUFSIZ]: } else if (read(fd, bf, descsz) != (ssize_t)descsz) Unlike the else branch which validates namesz + descsz against sizeof(bf), this path passes descsz directly to read() without any bounds check. A crafted sysfs file with a large n_descsz overflows the 8192-byte stack buffer. Add a descsz > sizeof(bf) check before the read, breaking out of the loop on oversized values. Fixes: e5a1845fc0aeca85 ("perf symbols: Split out util/symbol-elf.c") Reported-by: sashiko-bot Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/symbol-elf.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index c415670453bed..70f3b553baf08 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -960,8 +960,13 @@ int sysfs__read_build_id(const char *filename, struct build_id *bid) err = 0; break; } - } else if (read(fd, bf, descsz) != (ssize_t)descsz) - break; + } else { + /* descsz from untrusted file — clamp to buffer */ + if (descsz > sizeof(bf)) + break; + if (read(fd, bf, descsz) != (ssize_t)descsz) + break; + } } else { size_t n; From 2bfaa207732a17ea215adf86d14a9bb1d37ec2d0 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 22:43:09 -0300 Subject: [PATCH 1691/2758] perf tools: Fix uninitialized pathname on uncompressed fallback in filename__decompress() [ Upstream commit 51cdb188edeaf389e4377859b9c483c19ce5a259 ] filename__decompress() has an early return path for files that are not actually compressed. This path returns the fd from open() directly but never writes to the pathname output parameter, leaving the caller with an uninitialized buffer despite a successful return. Callers like dso__decompress_kmodule_path() pass pathname to decompress_kmodule() which uses it to set the decompressed file path. If pathname is uninitialized, subsequent operations on the path produce undefined behavior. Fix by setting pathname to an empty string on the uncompressed path. Callers already check for an empty pathname to distinguish temporary decompressed files (which need unlink) from the original file. Reported-by: sashiko-bot Fixes: 7ac22b088afe26a4 ("perf tools: Add filename__decompress function") Cc: Jiri Olsa Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/tests/code-reading.c | 7 +++++-- tools/perf/util/disasm.c | 7 +++++-- tools/perf/util/dso.c | 12 +++++++++--- tools/perf/util/symbol-elf.c | 6 ++++-- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index 47043a3a2fb4f..e82ecdc957778 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c @@ -471,8 +471,11 @@ static int read_object_code(u64 addr, size_t len, u8 cpumode, goto out; } - decomp = true; - objdump_name = decomp_name; + /* empty pathname means file wasn't actually compressed */ + if (decomp_name[0] != '\0') { + decomp = true; + objdump_name = decomp_name; + } } /* Read the object code using objdump */ diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c index 59ba88e1f7443..0a1a7e9cf3efe 100644 --- a/tools/perf/util/disasm.c +++ b/tools/perf/util/disasm.c @@ -1577,8 +1577,11 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args) if (dso__decompress_kmodule_path(dso, symfs_filename, tmp, sizeof(tmp)) < 0) return -1; - decomp = true; - strcpy(symfs_filename, tmp); + /* empty pathname means file wasn't actually compressed */ + if (tmp[0] != '\0') { + decomp = true; + strcpy(symfs_filename, tmp); + } } /* diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index d878c46de52a7..99199c68d9b5c 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -343,8 +343,11 @@ int filename__decompress(const char *name, char *pathname, * To keep this transparent, we detect this and return the file * descriptor to the uncompressed file. */ - if (!compressions[comp].is_compressed(name)) + if (!compressions[comp].is_compressed(name)) { + if (pathname && len > 0) + pathname[0] = '\0'; return open(name, O_RDONLY | O_CLOEXEC); + } fd = mkostemp(tmpbuf, O_CLOEXEC); if (fd < 0) { @@ -598,8 +601,11 @@ static char *dso__get_filename(struct dso *dso, const char *root_dir, goto out; } - *decomp = true; - strcpy(name, newpath); + /* empty pathname means file wasn't actually compressed */ + if (newpath[0] != '\0') { + *decomp = true; + strcpy(name, newpath); + } } return name; diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 70f3b553baf08..161539b06727a 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -919,12 +919,14 @@ int filename__read_build_id(const char *filename, struct build_id *bid) return -1; } close(fd); - filename = path; + /* non-empty path means a temp file was created */ + if (path[0] != '\0') + filename = path; } err = read_build_id(filename, bid); - if (m.comp) + if (m.comp && filename == path) unlink(filename); return err; } From 1577822e1fa1985bf31838930f414688a6481df3 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 8 Jun 2026 07:03:04 -0300 Subject: [PATCH 1692/2758] perf hwmon: Guard label read against empty or failed reads [ Upstream commit 34d3d93fac6d92237cb9d730ca04c37ed361c7a6 ] hwmon_pmu__read_events() reads label files with read() into a stack buffer, strips trailing newlines, then checks buf[0] == '\0'. When read() returns 0 (empty file) or -1 (error), the buffer is never written, so buf[0] reads uninitialized stack memory. If the garbage byte is non-zero, the code falls through to strdup(buf) which copies arbitrary stack data as the label string. Fix by checking read_len <= 0 before accessing buf contents, closing the fd and skipping the entry. Reported-by: sashiko-bot Fixes: 53cc0b351ec99278 ("perf hwmon_pmu: Add a tool PMU exposing events from hwmon in sysfs") Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/hwmon_pmu.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/hwmon_pmu.c b/tools/perf/util/hwmon_pmu.c index bdcbe887579a1..efd3067a2e591 100644 --- a/tools/perf/util/hwmon_pmu.c +++ b/tools/perf/util/hwmon_pmu.c @@ -295,8 +295,11 @@ static int hwmon_pmu__read_events(struct hwmon_pmu *pmu) while (read_len > 0 && buf[read_len - 1] == '\n') read_len--; - if (read_len > 0) - buf[read_len] = '\0'; + if (read_len <= 0) { + close(fd); + continue; + } + buf[read_len] = '\0'; if (buf[0] == '\0') { pr_debug("hwmon_pmu: empty label file %s %s\n", From 908bc5238979a65e7b39de07cbc86d710920728e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 8 Jun 2026 07:04:25 -0300 Subject: [PATCH 1693/2758] perf tools: Use snprintf() in dso__read_running_kernel_build_id() [ Upstream commit 2ea64782a428bed74f595961e651ceb8c4c5bf22 ] dso__read_running_kernel_build_id() uses sprintf() to format a sysfs path from machine->root_dir into a PATH_MAX buffer. If root_dir is close to PATH_MAX in length, appending "/sys/kernel/notes" (18 bytes) overflows the stack buffer. Switch to snprintf() with sizeof(path) to prevent the overflow. Reported-by: sashiko-bot Fixes: cdd059d731eeb466 ("perf tools: Move dso_* related functions into dso object") Cc: Jiri Olsa Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/dso.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 99199c68d9b5c..7200722464e71 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -1772,7 +1772,7 @@ void dso__read_running_kernel_build_id(struct dso *dso, struct machine *machine) if (machine__is_default_guest(machine)) return; - sprintf(path, "%s/sys/kernel/notes", machine->root_dir); + snprintf(path, sizeof(path), "%s/sys/kernel/notes", machine->root_dir); sysfs__read_build_id(path, &bid); dso__set_build_id(dso, &bid); } From 62adda4bb1b8e73b726bc431d684c5c3714c69a1 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 8 Jun 2026 07:05:19 -0300 Subject: [PATCH 1694/2758] tools lib api: Fix filename__write_int() writing uninitialized stack data [ Upstream commit 438ece06185696e14c63c6113d5e2d34ec0a9680 ] filename__write_int() formats an integer into a 64-byte buffer with sprintf() then passes sizeof(buf) (64) as the write length. This writes all 64 bytes including uninitialized stack data past the formatted string. Most sysfs files reject the oversized write, making the function always return -1. Fix by capturing the sprintf() return value and using it as the write length. Reported-by: sashiko-bot Fixes: 3b00ea938653d136 ("tools lib api fs: Add sysfs__write_int function") Cc: Kan Liang Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/lib/api/fs/fs.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/lib/api/fs/fs.c b/tools/lib/api/fs/fs.c index 3cc302d4c47b1..d16911818d4d3 100644 --- a/tools/lib/api/fs/fs.c +++ b/tools/lib/api/fs/fs.c @@ -376,12 +376,13 @@ int filename__write_int(const char *filename, int value) { int fd = open(filename, O_WRONLY), err = -1; char buf[64]; + int len; if (fd < 0) return -errno; - sprintf(buf, "%d", value); - if (write(fd, buf, sizeof(buf)) == sizeof(buf)) + len = sprintf(buf, "%d", value); + if (write(fd, buf, len) == len) err = 0; close(fd); From df7d723d66bde3ddfd586b09d26751e72d255aba Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 8 Jun 2026 08:10:18 -0300 Subject: [PATCH 1695/2758] tools lib api: Fix mount_overload() snprintf truncation and toupper range [ Upstream commit fd1f70776add263f8ef38a87ae593c75303f1dcd ] mount_overload() builds an environment variable name like "PERF_SYSFS_ENVIRONMENT" from fs->name. Two bugs: 1) snprintf() uses name_len as the buffer size instead of sizeof(upper_name). For fs->name = "sysfs" (len=5), the output is truncated to "PERF" (4 chars + null), so getenv() never finds the intended variable. 2) mem_toupper() only uppercases name_len bytes, converting just the "PERF" prefix rather than the full string including the filesystem name portion. Fix by using sizeof(upper_name) for snprintf and strlen(upper_name) for mem_toupper, so the full "PERF_SYSFS_ENVIRONMENT" string is correctly formatted and uppercased. Reported-by: sashiko-bot Fixes: 73ca85ad364769ff ("tools lib api fs: Add FSTYPE__mount() method") Cc: Jiri Olsa Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/lib/api/fs/fs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/lib/api/fs/fs.c b/tools/lib/api/fs/fs.c index d16911818d4d3..cbd8eab0d1df0 100644 --- a/tools/lib/api/fs/fs.c +++ b/tools/lib/api/fs/fs.c @@ -261,8 +261,8 @@ static const char *mount_overload(struct fs *fs) /* "PERF_" + name + "_ENVIRONMENT" + '\0' */ char upper_name[5 + name_len + 12 + 1]; - snprintf(upper_name, name_len, "PERF_%s_ENVIRONMENT", fs->name); - mem_toupper(upper_name, name_len); + snprintf(upper_name, sizeof(upper_name), "PERF_%s_ENVIRONMENT", fs->name); + mem_toupper(upper_name, strlen(upper_name)); return getenv(upper_name) ?: *fs->mounts; } From 77373bfa25646430816a1c5944053e58b8635492 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 8 Jun 2026 08:10:43 -0300 Subject: [PATCH 1696/2758] perf bpf: Add NULL check for btf__type_by_id() in synthesize_bpf_prog_name() [ Upstream commit 903b0526dcf86d030c5970b4b0a67f9c227368e2 ] synthesize_bpf_prog_name() calls btf__type_by_id() and immediately dereferences the result via t->name_off without checking for NULL. btf__type_by_id() returns NULL when the type_id is invalid or out of range. When processing perf.data files, finfo->type_id comes from untrusted input, so an invalid ID causes a NULL pointer dereference. Fix by checking t for NULL before dereferencing. Reported-by: sashiko-bot Fixes: fc462ac75b36daaa ("perf bpf: Extract logic to create program names from perf_event__synthesize_one_bpf_prog()") Cc: Song Liu Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/bpf-event.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index 2c09842469f1f..fe01551dc3e6c 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c @@ -146,7 +146,8 @@ static int synthesize_bpf_prog_name(char *buf, int size, if (btf) { finfo = func_infos + sub_id * info->func_info_rec_size; t = btf__type_by_id(btf, finfo->type_id); - short_name = btf__name_by_offset(btf, t->name_off); + if (t) + short_name = btf__name_by_offset(btf, t->name_off); } else if (sub_id == 0 && sub_prog_cnt == 1) { /* no subprog */ if (info->name[0]) From 7e841b7b10147363a8cef9c59da8392f36a67f6f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 8 Jun 2026 08:11:28 -0300 Subject: [PATCH 1697/2758] perf bpf: Fix map data leak in bpf_metadata_create() on alloc failure [ Upstream commit aece2b8966fc8de5be46ee9287d0f60d6690c300 ] bpf_metadata_create() calls bpf_metadata_read_map_data() which allocates map.btf and map.rodata. If the subsequent bpf_metadata_alloc() fails, the code does 'continue' which skips bpf_metadata_free_map_data(), permanently leaking both allocations. Fix by calling bpf_metadata_free_map_data() before continue. Reported-by: sashiko-bot Fixes: ab38e84ba9a80581 ("perf record: collect BPF metadata from existing BPF programs") Cc: Blake Jones Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/bpf-event.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index fe01551dc3e6c..b65ad28cd950f 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c @@ -395,8 +395,10 @@ static struct bpf_metadata *bpf_metadata_create(struct bpf_prog_info *info) continue; metadata = bpf_metadata_alloc(info->nr_prog_tags, map.num_vars); - if (!metadata) + if (!metadata) { + bpf_metadata_free_map_data(&map); continue; + } bpf_metadata_fill_event(&map, &metadata->event->bpf_metadata); From 802257fbe4ea694968f1d8ea3f37852f12cc8b5c Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 8 Jun 2026 08:12:04 -0300 Subject: [PATCH 1698/2758] perf bpf: Fix metadata leak in perf_env__add_bpf_info() on duplicate insert [ Upstream commit a0e4362a3e7b592f1d58949ffe3d6decad39a17c ] When perf_env__insert_bpf_prog_info() returns false (duplicate program), the error path frees info_linear and info_node but not info_node->metadata. If bpf_metadata_create() had succeeded, the metadata allocation is permanently leaked. Fix by calling bpf_metadata_free() on info_node->metadata before freeing info_node. bpf_metadata_free() handles NULL, so this is safe even when bpf_metadata_create() returned NULL. Reported-by: sashiko-bot Fixes: fdc3441f2d317b40 ("perf record: collect BPF metadata from new programs") Cc: Blake Jones Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/bpf-event.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index b65ad28cd950f..c4594969d7677 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c @@ -873,6 +873,7 @@ static int perf_env__add_bpf_info(struct perf_env *env, u32 id) if (!perf_env__insert_bpf_prog_info(env, info_node)) { pr_debug("%s: duplicate add bpf info request for id %u\n", __func__, btf_id); + bpf_metadata_free(info_node->metadata); free(info_linear); free(info_node); goto out; From e525be3207edf8d450db03c75aca7259291f9ab6 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 8 Jun 2026 08:12:34 -0300 Subject: [PATCH 1699/2758] perf symbols: Add bounds checks to elf_read_build_id() note iteration [ Upstream commit acc56d3941fc2997a5a21ea9233a8ac3d87c4f2f ] elf_read_build_id() iterates ELF notes using pointer arithmetic driven by n_namesz and n_descsz from the note headers. Neither the note header read nor the subsequent name/desc advances are checked against the section boundary. A malformed ELF file with oversized note sizes causes out-of-bounds reads past the section data buffer. Add two bounds checks: verify the note header fits within the remaining section data, and verify that namesz + descsz (after alignment) fits before advancing the pointer. Fixes: fd7a346ea292074e ("perf symbols: Filename__read_build_id should look at .notes section too") Reported-by: sashiko-bot Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/symbol-elf.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 161539b06727a..cd7833e364020 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -834,10 +834,24 @@ static int elf_read_build_id(Elf *elf, void *bf, size_t size) ptr = data->d_buf; while (ptr < (data->d_buf + data->d_size)) { GElf_Nhdr *nhdr = ptr; - size_t namesz = NOTE_ALIGN(nhdr->n_namesz), - descsz = NOTE_ALIGN(nhdr->n_descsz); + size_t namesz, descsz, remaining; const char *name; + /* ensure the note header fits within the section */ + if (ptr + sizeof(*nhdr) > data->d_buf + data->d_size) + break; + + namesz = NOTE_ALIGN(nhdr->n_namesz); + descsz = NOTE_ALIGN(nhdr->n_descsz); + + /* validate individually to avoid size_t overflow on 32-bit */ + remaining = data->d_buf + data->d_size - ptr - sizeof(*nhdr); + if (namesz > remaining || descsz > remaining - namesz) { + pr_warning("%s: oversized note: n_namesz=%u, n_descsz=%u\n", + __func__, nhdr->n_namesz, nhdr->n_descsz); + break; + } + ptr += sizeof(*nhdr); name = ptr; ptr += namesz; From 65a406f5bbd9275cdbf614a8c0d5e6676d3aa238 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 16:09:45 -0300 Subject: [PATCH 1700/2758] perf symbols: Add bounds checks to read_build_id() note iteration in minimal build [ Upstream commit 52e582e316c48c53bb3082c29f7862ebc554087e ] symbol-minimal.c's read_build_id() iterates ELF notes with the same pattern as symbol-elf.c's elf_read_build_id(): pointer arithmetic driven by n_namesz and n_descsz from 32-bit note header fields, without validating that the name and desc fit within the note section data. A malformed ELF file with oversized note sizes causes out-of-bounds reads past the section data buffer. Add the same bounds check as the libelf path: validate namesz and descsz individually against remaining data before advancing the pointer, avoiding size_t overflow on 32-bit. Fixes: b691f64360ecec49 ("perf symbols: Implement poor man's ELF parser") Reported-by: sashiko-bot Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/symbol-minimal.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index 8221dc9868f7c..091071d06416e 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c @@ -1,3 +1,4 @@ +#include "debug.h" #include "dso.h" #include "symbol.h" #include "symsrc.h" @@ -44,7 +45,7 @@ static int read_build_id(void *note_data, size_t note_len, struct build_id *bid, ptr = note_data; while ((ptr + sizeof(*nhdr)) < (note_data + note_len)) { const char *name; - size_t namesz, descsz; + size_t namesz, descsz, remaining; nhdr = ptr; if (need_swap) { @@ -56,6 +57,14 @@ static int read_build_id(void *note_data, size_t note_len, struct build_id *bid, namesz = NOTE_ALIGN(nhdr->n_namesz); descsz = NOTE_ALIGN(nhdr->n_descsz); + /* validate individually to avoid size_t overflow on 32-bit */ + remaining = note_data + note_len - ptr - sizeof(*nhdr); + if (namesz > remaining || descsz > remaining - namesz) { + pr_warning("%s: oversized note: n_namesz=%u, n_descsz=%u\n", + __func__, nhdr->n_namesz, nhdr->n_descsz); + break; + } + ptr += sizeof(*nhdr); name = ptr; ptr += namesz; From e30fa32cd0789de847f4965793d944231c6f739e Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Mon, 11 May 2026 14:38:16 +0800 Subject: [PATCH 1701/2758] dt-bindings: dma: snps,dw-axi-dmac: Add fallback compatible for CV1800B [ Upstream commit 7524fe142b5a772f8421aeee2132cf7e21a00103 ] The previous version of the binding change only add compatible string without adding the fallback compatible, this breaks backward compatibility. Add the needed fallback compatible to fix this. Fixes: be3e2a0419c6 ("dt-bindings: dma: snps,dw-axi-dmac: Add CV1800B compatible") Signed-off-by: Inochi Amaoto Acked-by: Conor Dooley Link: https://patch.msgid.link/20260511063818.463877-2-inochiama@gmail.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml b/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml index 804514732dbe6..0a30a455b0ee5 100644 --- a/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml +++ b/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml @@ -21,11 +21,12 @@ properties: - enum: - snps,axi-dma-1.01a - intel,kmb-axi-dma - - sophgo,cv1800b-axi-dma - starfive,jh7110-axi-dma - starfive,jh8100-axi-dma - items: - - const: altr,agilex5-axi-dma + - enum: + - altr,agilex5-axi-dma + - sophgo,cv1800b-axi-dma - const: snps,axi-dma-1.01a reg: From f66b4e65c4cc5a98d873c74c1e33c5bc2bb88450 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 10 Jun 2026 11:45:12 +0200 Subject: [PATCH 1702/2758] dt-bindings: phy: sc8280xp-qmp-pcie: Disallow bifurcation register on Purwa [ Upstream commit b3ee497970c63cea37976aeaa84bac39611fe0eb ] Neither of the two Gen4x4 PHYs found on Purwa supports bifurcation. The PHY is however physically laid out as if it were to, since there are two separate ports (A/B). Split out a new if-then block to un-require the bifurcation register handle to squash this warning: purwa-iot-evk.dtb: phy@1bd4000 (qcom,x1p42100-qmp-gen4x4-pcie-phy): 'qcom,4ln-config-sel' is a required property Fixes: 2e1ffd4c1805 ("dt-bindings: phy: qcom,qmp-pcie: Add X1P42100 PCIe Gen4x4 PHY") Reported-by: Rob Herring Closes: https://lore.kernel.org/linux-arm-msm/176857775469.1631885.16133311938753588148.robh@kernel.org/ Signed-off-by: Konrad Dybcio Acked-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260610-topic-purwa_phy_shutup_warning-v2-1-951c1fbfe9b2@oss.qualcomm.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- .../bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml index 3a35120a77ec0..431e8cb5df84e 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml @@ -136,13 +136,22 @@ allOf: items: - description: port a - description: port b - required: - - qcom,4ln-config-sel else: properties: reg: maxItems: 1 + - if: + properties: + compatible: + contains: + enum: + - qcom,sc8280xp-qmp-gen3x4-pcie-phy + - qcom,x1e80100-qmp-gen4x4-pcie-phy + then: + required: + - qcom,4ln-config-sel + - if: properties: compatible: From 393f0bb615459fab64c845398fbb43f3594469f8 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 21 May 2026 17:19:49 +0000 Subject: [PATCH 1703/2758] PCI: mediatek: Use actual physical address instead of virt_to_phys() [ Upstream commit ebc1d9906894703286d12306a6f242d90cfb49e8 ] The driver previously used virt_to_phys() on the ioremapped register base (port->base) to compute the MSI message address. Using virt_to_phys() on an IO mapped address is incorrect because it expects a kernel virtual address. To fix it, store the physical start of the I/O register region in mtk_pcie_port->phys_base and use it to build the MSI address. This replaces the incorrect virt_to_phys() usage and ensures MSI addresses are generated correctly. Fixes: 43e6409db64d ("PCI: mediatek: Add MSI support for MT2712 and MT7622") Signed-off-by: Manivannan Sadhasivam Signed-off-by: Manivannan Sadhasivam Tested-by: Caleb James DeLisle Link: https://patch.msgid.link/20260521171951.1495781-2-cjd@cjdns.fr Signed-off-by: Sasha Levin --- drivers/pci/controller/pcie-mediatek.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index f34d91e495bc1..1bb8839c3cb0f 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -176,6 +176,7 @@ struct mtk_pcie_soc { /** * struct mtk_pcie_port - PCIe port information * @base: IO mapped register base + * @phys_base: Physical address of the I/O register base region * @list: port list * @pcie: pointer to PCIe host info * @reset: pointer to port reset control @@ -197,6 +198,7 @@ struct mtk_pcie_soc { */ struct mtk_pcie_port { void __iomem *base; + phys_addr_t phys_base; struct list_head list; struct mtk_pcie *pcie; struct reset_control *reset; @@ -406,7 +408,7 @@ static void mtk_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) phys_addr_t addr; /* MT2712/MT7622 only support 32-bit MSI addresses */ - addr = virt_to_phys(port->base + PCIE_MSI_VECTOR); + addr = port->phys_base + PCIE_MSI_VECTOR; msg->address_hi = 0; msg->address_lo = lower_32_bits(addr); @@ -521,7 +523,7 @@ static void mtk_pcie_enable_msi(struct mtk_pcie_port *port) u32 val; phys_addr_t msg_addr; - msg_addr = virt_to_phys(port->base + PCIE_MSI_VECTOR); + msg_addr = port->phys_base + PCIE_MSI_VECTOR; val = lower_32_bits(msg_addr); writel(val, port->base + PCIE_IMSI_ADDR); @@ -962,6 +964,7 @@ static int mtk_pcie_parse_port(struct mtk_pcie *pcie, struct mtk_pcie_port *port; struct device *dev = pcie->dev; struct platform_device *pdev = to_platform_device(dev); + struct resource *res; char name[20]; int err; @@ -970,7 +973,14 @@ static int mtk_pcie_parse_port(struct mtk_pcie *pcie, return -ENOMEM; snprintf(name, sizeof(name), "port%d", slot); - port->base = devm_platform_ioremap_resource_byname(pdev, name); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); + if (!res) { + dev_err(dev, "failed to get port%d base\n", slot); + return -EINVAL; + } + + port->phys_base = res->start; + port->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(port->base)) { dev_err(dev, "failed to map port%d base\n", slot); return PTR_ERR(port->base); From d31244d1732e12a97dd83abe1ad19f5b4f24a5eb Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Tue, 9 Jun 2026 22:48:50 +0800 Subject: [PATCH 1704/2758] phy: freescale: phy-fsl-imx8qm-lvds-phy: Use synchronous PM runtime put in reset [ Upstream commit b28ec8ce03d8f9a0f7a9ec84f1ed9b5a6f393791 ] The mixel_lvds_phy_reset() function pairs pm_runtime_resume_and_get() with pm_runtime_put(). The asynchronous variant queues a work item to handle the idle check and potential suspend, which can be cancelled by a subsequent pm_runtime_disable() call if probe fails after the reset. Switch to pm_runtime_put_sync() to run the idle check and suspend synchronously. Fixes: 06ff622d61d2 ("phy: freescale: Add i.MX8qm Mixel LVDS PHY support") Reported-by: sashiko Closes: https://sashiko.dev/#/patchset/20260605-lvds-v2-1-3ce7539d1104%40gmail.com Signed-off-by: Felix Gu Reviewed-by: Frank Li Link: https://patch.msgid.link/20260609-lvds-phy-v1-1-6ad790c6d0ea@gmail.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c b/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c index ece3574435217..e2a1645000ae5 100644 --- a/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c +++ b/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c @@ -286,7 +286,7 @@ static int mixel_lvds_phy_reset(struct device *dev) regmap_write(priv->regmap, PHY_CTRL, CTRL_RESET_VAL); - pm_runtime_put(dev); + pm_runtime_put_sync(dev); return 0; } From 9720527646720e90a19df55f6c441f92a4830bf1 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Fri, 5 Jun 2026 19:57:20 +0800 Subject: [PATCH 1705/2758] phy: freescale: phy-fsl-imx8qm-lvds-phy: Fix missing pm_runtime_disable() on probe error path [ Upstream commit 799e7cf2f0b50b34660b5ffce0f7d8dec376a0d5 ] If mixel_lvds_phy_reset() fails in probe after pm_runtime_enable(), the function returns directly without calling pm_runtime_disable(), leaving runtime PM permanently enabled for the device. Fix this by using devm_pm_runtime_enable() so that cleanup is automatic on any probe failure or driver unbind. This also allows removing the manual err label and the .remove callback. Fixes: 06ff622d61d2 ("phy: freescale: Add i.MX8qm Mixel LVDS PHY support") Acked-by: Liu Ying Signed-off-by: Felix Gu Reviewed-by: Frank Li Link: https://patch.msgid.link/20260605-lvds-v2-1-3ce7539d1104@gmail.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- .../phy/freescale/phy-fsl-imx8qm-lvds-phy.c | 24 ++++++------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c b/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c index e2a1645000ae5..368679436d862 100644 --- a/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c +++ b/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c @@ -345,7 +345,9 @@ static int mixel_lvds_phy_probe(struct platform_device *pdev) dev_set_drvdata(dev, priv); - pm_runtime_enable(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; ret = mixel_lvds_phy_reset(dev); if (ret) { @@ -355,17 +357,15 @@ static int mixel_lvds_phy_probe(struct platform_device *pdev) for (i = 0; i < PHY_NUM; i++) { lvds_phy = devm_kzalloc(dev, sizeof(*lvds_phy), GFP_KERNEL); - if (!lvds_phy) { - ret = -ENOMEM; - goto err; - } + if (!lvds_phy) + return -ENOMEM; phy = devm_phy_create(dev, NULL, &mixel_lvds_phy_ops); if (IS_ERR(phy)) { ret = PTR_ERR(phy); dev_err(dev, "failed to create PHY for channel%d: %d\n", i, ret); - goto err; + return ret; } lvds_phy->phy = phy; @@ -379,19 +379,10 @@ static int mixel_lvds_phy_probe(struct platform_device *pdev) if (IS_ERR(phy_provider)) { ret = PTR_ERR(phy_provider); dev_err(dev, "failed to register PHY provider: %d\n", ret); - goto err; + return ret; } return 0; -err: - pm_runtime_disable(dev); - - return ret; -} - -static void mixel_lvds_phy_remove(struct platform_device *pdev) -{ - pm_runtime_disable(&pdev->dev); } static int __maybe_unused mixel_lvds_phy_runtime_suspend(struct device *dev) @@ -432,7 +423,6 @@ MODULE_DEVICE_TABLE(of, mixel_lvds_phy_of_match); static struct platform_driver mixel_lvds_phy_driver = { .probe = mixel_lvds_phy_probe, - .remove = mixel_lvds_phy_remove, .driver = { .pm = &mixel_lvds_phy_pm_ops, .name = "mixel-lvds-phy", From 855870e8c59b97fd424e8fa3543c9e289bb48fac Mon Sep 17 00:00:00 2001 From: Shuvam Pandey Date: Mon, 18 May 2026 22:44:17 +0545 Subject: [PATCH 1706/2758] PCI: dwc: Avoid dwc_pcie_rasdes_debugfs_deinit() NULL dereference when no RAS DES capability [ Upstream commit 26b67fa10ef84ea667942491b50e6261a45f098d ] dwc_pcie_rasdes_debugfs_init() returns success when the controller has no RAS DES capability, leaving pci->debugfs->rasdes_info unset. The common debugfs teardown path still calls dwc_pcie_rasdes_debugfs_deinit(), which dereferences rasdes_info unconditionally. Return early when no RAS DES state was allocated. In that case no RAS DES mutex was initialized, so there is nothing to destroy. Fixes: 4fbfa17f9a07 ("PCI: dwc: Add debugfs based Silicon Debug support for DWC") Signed-off-by: Shuvam Pandey [mani: reworded subject] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/0f97352506d8d813f70f441de4d63fcd5b7d1c3e.1779123847.git.shuvampandey1@gmail.com Signed-off-by: Sasha Levin --- drivers/pci/controller/dwc/pcie-designware-debugfs.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-designware-debugfs.c b/drivers/pci/controller/dwc/pcie-designware-debugfs.c index 4bfc1748f2d73..449c67e7930d3 100644 --- a/drivers/pci/controller/dwc/pcie-designware-debugfs.c +++ b/drivers/pci/controller/dwc/pcie-designware-debugfs.c @@ -558,6 +558,9 @@ static void dwc_pcie_rasdes_debugfs_deinit(struct dw_pcie *pci) { struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info; + if (!rinfo) + return; + mutex_destroy(&rinfo->reg_event_lock); } From 63a30015199912bd5055bead8001b1ae68a67cdb Mon Sep 17 00:00:00 2001 From: Dong Chenchen Date: Tue, 9 Jun 2026 17:21:17 +0800 Subject: [PATCH 1707/2758] xfrm: Fix dev use-after-free in xfrm async resumption [ Upstream commit 8045c0df98d4f14c54e5cb875f1c9c0ce89fe4ff ] xfrm async resumption hold skb->dev refcnt until after transport_finish. However, xfrm_rcv_cb may modify skb->dev to tunnel dev without taking device reference, such as vti_rcv_cb. The subsequent async resumption will decrement the tunnel device's reference count, which lead to uaf of tunnel dev and refcnt leak of orig dev as below: unregister_netdevice: waiting for vti1 to become free. Usage count = -2 Stash the original skb->dev to fix refcnt imbalance. The new skb->dev set by xfrm_rcv_cb can race with device teardown. Extend rcu protection over xfrm_rcv_cb and transport_finish to prevent races. Fixes: 1c428b038400 ("xfrm: hold dev ref until after transport_finish NF_HOOK") Reported-by: Xu Chunxiao Signed-off-by: Dong Chenchen Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin --- net/ipv4/xfrm4_input.c | 2 -- net/ipv6/xfrm6_input.c | 2 -- net/xfrm/xfrm_input.c | 29 ++++++++++++++++------------- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c index c2eac844bcdb4..f6f2a8ef3f884 100644 --- a/net/ipv4/xfrm4_input.c +++ b/net/ipv4/xfrm4_input.c @@ -76,8 +76,6 @@ int xfrm4_transport_finish(struct sk_buff *skb, int async) NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, dev_net(dev), NULL, skb, dev, NULL, xfrm4_rcv_encap_finish); - if (async) - dev_put(dev); return 0; } diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c index 699a001ac1662..89d0443b53073 100644 --- a/net/ipv6/xfrm6_input.c +++ b/net/ipv6/xfrm6_input.c @@ -71,8 +71,6 @@ int xfrm6_transport_finish(struct sk_buff *skb, int async) NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, dev_net(dev), NULL, skb, dev, NULL, xfrm6_transport_finish2); - if (async) - dev_put(dev); return 0; } diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c index e4c2cd24936d3..eecab337bd0a7 100644 --- a/net/xfrm/xfrm_input.c +++ b/net/xfrm/xfrm_input.c @@ -467,6 +467,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) { const struct xfrm_state_afinfo *afinfo; struct net *net = dev_net(skb->dev); + struct net_device *dev = skb->dev; int err; __be32 seq; __be32 seq_hi; @@ -493,7 +494,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) LINUX_MIB_XFRMINSTATEINVALID); if (encap_type == -1) - dev_put(skb->dev); + dev_put(dev); goto drop; } @@ -655,16 +656,16 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) if (!crypto_done) { spin_unlock(&x->lock); - dev_hold(skb->dev); + dev_hold(dev); nexthdr = x->type->input(x, skb); if (nexthdr == -EINPROGRESS) { if (async) - dev_put(skb->dev); + dev_put(dev); return 0; } - dev_put(skb->dev); + dev_put(dev); spin_lock(&x->lock); } resume: @@ -699,7 +700,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) err = xfrm_inner_mode_input(x, skb); if (err == -EINPROGRESS) { if (async) - dev_put(skb->dev); + dev_put(dev); return 0; } else if (err) { XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMODEERROR); @@ -726,9 +727,12 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) crypto_done = false; } while (!err); + rcu_read_lock(); err = xfrm_rcv_cb(skb, family, x->type->proto, 0); - if (err) + if (err) { + rcu_read_unlock(); goto drop; + } nf_reset_ct(skb); @@ -739,8 +743,9 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) if (skb_valid_dst(skb)) skb_dst_drop(skb); if (async) - dev_put(skb->dev); + dev_put(dev); gro_cells_receive(&gro_cells, skb); + rcu_read_unlock(); return 0; } else { xo = xfrm_offload(skb); @@ -748,23 +753,21 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) xfrm_gro = xo->flags & XFRM_GRO; err = -EAFNOSUPPORT; - rcu_read_lock(); afinfo = xfrm_state_afinfo_get_rcu(x->props.family); if (likely(afinfo)) err = afinfo->transport_finish(skb, xfrm_gro || async); - rcu_read_unlock(); if (xfrm_gro) { sp = skb_sec_path(skb); if (sp) sp->olen = 0; if (skb_valid_dst(skb)) skb_dst_drop(skb); - if (async) - dev_put(skb->dev); gro_cells_receive(&gro_cells, skb); - return err; } + if (async) + dev_put(dev); + rcu_read_unlock(); return err; } @@ -772,7 +775,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) spin_unlock(&x->lock); drop: if (async) - dev_put(skb->dev); + dev_put(dev); xfrm_rcv_cb(skb, family, x && x->type ? x->type->proto : nexthdr, -1); kfree_skb(skb); return 0; From 375e1defdeb86ce6ea3af2d7bd106c98cd5e2e27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 25 May 2026 10:36:21 +0200 Subject: [PATCH 1708/2758] sparc: Avoid -Wunused-but-set-parameter in clear_user_page() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 9bc0bd9617964804dc02e6ee2413af42c4a50d6b ] The loop in clear_user_pages() iterates over all pages and calls clear_user_page() for each of them. During the loop "vaddr" is modified. However on sparc clear_user() is a macro which does not use "vaddr". The compiler sees a variable which is modified but never used and emits a warning for that: include/linux/highmem.h: In function 'clear_user_pages': include/linux/highmem.h:234:63: warning: parameter 'vaddr' set but not used [-Wunused-but-set-parameter=] static inline void clear_user_pages(void *addr, unsigned long vaddr, Other architectures use an inline function for clear_user_page() which avoids the warning. This is not possible on sparc, as sparc_flush_page_to_ram() is not yet declared where clear_user_page() is defined. Including cacheflush_32.h will trigger recursive and lots of other issues. So hide the warning with a cast to (void) instead. While we are here, do the same for copy_user_page(). Fixes: 62a9f5a85b98 ("mm: introduce clear_pages() and clear_user_pages()") Signed-off-by: Thomas Weißschuh Reviewed-by: Andreas Larsson Signed-off-by: Andreas Larsson Signed-off-by: Sasha Levin --- arch/sparc/include/asm/page_32.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/sparc/include/asm/page_32.h b/arch/sparc/include/asm/page_32.h index c1bccbedf567e..9f0b54f709088 100644 --- a/arch/sparc/include/asm/page_32.h +++ b/arch/sparc/include/asm/page_32.h @@ -20,10 +20,12 @@ #define clear_user_page(addr, vaddr, page) \ do { clear_page(addr); \ sparc_flush_page_to_ram(page); \ + (void)(vaddr); \ } while (0) #define copy_user_page(to, from, vaddr, page) \ do { copy_page(to, from); \ sparc_flush_page_to_ram(page); \ + (void)(vaddr); \ } while (0) /* The following structure is used to hold the physical From 91fbf0de91bc2a9178603a222e39829a1fad3ee6 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 26 Apr 2026 16:27:05 -0700 Subject: [PATCH 1709/2758] Documentation: ABI: sysfs-class-reboot-mode-reboot_modes: fix doc warnings [ Upstream commit a888754e51e915731c8974c4d6d62709facb35d3 ] Repair the docs build warnings in this file by unindenting the description, adding blank lines, and using `` to quote *arg. WARNING: Documentation/ABI/testing/sysfs-class-reboot-mode-reboot_modes:36: abi_sys_class_reboot_mode_driver_reboot_modes doesn't have a description Documentation/ABI/testing/sysfs-class-reboot-mode-reboot_modes:1: ERROR: Unexpected indentation. [docutils] Documentation/ABI/testing/sysfs-class-reboot-mode-reboot_modes:1: ERROR: Unexpected indentation. [docutils] Documentation/ABI/testing/sysfs-class-reboot-mode-reboot_modes:1: WARNING: Inline emphasis start-string without end-string. [docutils] Documentation/ABI/testing/sysfs-class-reboot-mode-reboot_modes:1: ERROR: Unexpected indentation. [docutils] Fixes: d3da03025e6d ("Documentation: ABI: Add sysfs-class-reboot-mode-reboot_modes") Signed-off-by: Randy Dunlap Reviewed-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260426232705.422938-1-rdunlap@infradead.org Signed-off-by: Sebastian Reichel Signed-off-by: Sasha Levin --- .../testing/sysfs-class-reboot-mode-reboot_modes | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-class-reboot-mode-reboot_modes b/Documentation/ABI/testing/sysfs-class-reboot-mode-reboot_modes index a16c54ab841bb..4306966b7fcc3 100644 --- a/Documentation/ABI/testing/sysfs-class-reboot-mode-reboot_modes +++ b/Documentation/ABI/testing/sysfs-class-reboot-mode-reboot_modes @@ -2,33 +2,36 @@ What: /sys/class/reboot-mode//reboot_modes Date: March 2026(TBD) KernelVersion: TBD Contact: linux-pm@vger.kernel.org - Description: +Description: This interface exposes the reboot-mode arguments registered with the reboot-mode framework. It is a read-only interface and provides a space separated list of reboot-mode arguments supported on the current platform. Example: + recovery fastboot bootloader The exact sysfs path may vary depending on the name of the driver that registers the arguments. - Example: + Example:: + /sys/class/reboot-mode/nvmem-reboot-mode/reboot_modes /sys/class/reboot-mode/syscon-reboot-mode/reboot_modes /sys/class/reboot-mode/qcom-pon/reboot_modes The supported arguments can be used by userspace to invoke device reset using the standard reboot() system - call interface, with the "argument" as string to "*arg" - parameter along with LINUX_REBOOT_CMD_RESTART2. + call interface, with the "argument" as string to ``*arg`` + parameter along with ``LINUX_REBOOT_CMD_RESTART2``. A driver can expose the supported arguments by registering them with the reboot-mode framework using the property names that follow the mode- format. Example: - mode-bootloader, mode-recovery. + + mode-bootloader, mode-recovery This attribute is useful for scripts or initramfs logic that need to programmatically determine From 3691a82be2095abaf3326a1cbf7b25858a1d959c Mon Sep 17 00:00:00 2001 From: Yuanhe Shu Date: Thu, 11 Jun 2026 10:59:01 +0800 Subject: [PATCH 1710/2758] Revert "PCI/MSI: Unmap MSI-X region on error" [ Upstream commit f64e03da0d83cb173743888bff4a7e61476a8fc2 ] This reverts commit 1a8d4c6ecb4c81261bcdf13556abd4a958eca202. Commit 1a8d4c6ecb4c ("PCI/MSI: Unmap MSI-X region on error") added an iounmap(dev->msix_base) on the error path of msix_capability_init() to release the MSI-X region when msix_setup_interrupts() fails. When msix_setup_interrupts() fails, the call chain is: msix_setup_interrupts() -> __msix_setup_interrupts() struct pci_dev *dev __free(free_msi_irqs) = __dev; ... return ret; // __free cleanup fires on error The __free(free_msi_irqs) cleanup calls pci_free_msi_irqs(), which already handles the unmap: void pci_free_msi_irqs(struct pci_dev *dev) { pci_msi_teardown_msi_irqs(dev); if (dev->msix_base) { iounmap(dev->msix_base); // already unmapped here dev->msix_base = NULL; // and set to NULL } } So dev->msix_base is unmapped and set to NULL before msix_setup_interrupts() returns to msix_capability_init(). The "goto out_unmap" introduced by commit 1a8d4c6ecb4c ("PCI/MSI: Unmap MSI-X region on error") then calls iounmap() a second time on a NULL pointer. This was reproduced on Intel Emerald Rapids (192 CPUs) while running tools/testing/selftests/kexec/test_kexec_jump.sh: WARNING: CPU#44 at iounmap+0x2a/0xe0 RIP: 0010:iounmap+0x2a/0xe0 RDI: 0000000000000000 Call Trace: msix_capability_init+0x317/0x3f0 __pci_enable_msix_range+0x21d/0x2c0 pci_alloc_irq_vectors_affinity+0xa9/0x130 nvme_setup_io_queues+0x2a8/0x420 [nvme] nvme_reset_work+0x151/0x340 [nvme] ... RDI=0 confirms iounmap() is called with NULL. Restore the original "goto out_disable" and leave the unmap to the existing __free(free_msi_irqs) cleanup. Fixes: 1a8d4c6ecb4c ("PCI/MSI: Unmap MSI-X region on error") Reported-by: Guenter Roeck Signed-off-by: Yuanhe Shu Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/all/20260610194406.GA380991@bhelgaas/ Link: https://patch.msgid.link/20260611025901.1105209-1-xiangzao@linux.alibaba.com Closes: https://lore.kernel.org/all/4fc6208d-513b-4f41-a13a-4a0829ab50ad@roeck-us.net/ Signed-off-by: Sasha Levin --- drivers/pci/msi/msi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/pci/msi/msi.c b/drivers/pci/msi/msi.c index 81d24a270a795..a3e0daff09880 100644 --- a/drivers/pci/msi/msi.c +++ b/drivers/pci/msi/msi.c @@ -749,7 +749,7 @@ static int msix_capability_init(struct pci_dev *dev, struct msix_entry *entries, ret = msix_setup_interrupts(dev, entries, nvec, affd); if (ret) - goto out_unmap; + goto out_disable; /* Disable INTX */ pci_intx_for_msi(dev, 0); @@ -770,8 +770,6 @@ static int msix_capability_init(struct pci_dev *dev, struct msix_entry *entries, pcibios_free_irq(dev); return 0; -out_unmap: - iounmap(dev->msix_base); out_disable: dev->msix_enabled = 0; pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL | PCI_MSIX_FLAGS_ENABLE, 0); From ec95dec9ae2c30e99bbb3e59950d7493021961ec Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 22 Oct 2025 23:46:19 -0700 Subject: [PATCH 1711/2758] apparmor: fix shadowing of plabel that prevents cache from being updated [ Upstream commit 4483efe4f21510b30c24bc97d9fd0e8feab94125 ] Unfortunately the plabel was being shadowed by an unused local var. This didn't affect the mediation check but did cauase the cache to not correctly be updated resulting in extra mediation checks. Fixes: 88fec3526e841 ("apparmor: make sure unix socket labeling is correctly updated.") Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/af_unix.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/security/apparmor/af_unix.c b/security/apparmor/af_unix.c index fdb4a9f212c3b..09ce3f6da20ce 100644 --- a/security/apparmor/af_unix.c +++ b/security/apparmor/af_unix.c @@ -758,11 +758,10 @@ int aa_unix_file_perm(const struct cred *subj_cred, struct aa_label *label, unix_fs_perm(op, request, subj_cred, label, is_unix_fs(peer_sk) ? &peer_path : NULL)); } else if (!is_sk_fs) { - struct aa_label *plabel; struct aa_sk_ctx *pctx = aa_sock(peer_sk); rcu_read_lock(); - plabel = aa_get_label_rcu(&pctx->label); + plabel = aa_get_newest_label(pctx->label); rcu_read_unlock(); /* no fs check of aa_unix_peer_perm because conditions above * ensure they will never be done From d680472db98823d90fc91362910901e468269318 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 24 Oct 2025 12:25:38 -0700 Subject: [PATCH 1712/2758] apparmor: fix race in unix socket mediation when peer_path is used [ Upstream commit b1aea2c1960771a276d7e68c7424168eccd0c3da ] The holding a reference to the peer_sk is not enough to ensure access to the peer sk path. Accessing the path outside of the state lock allows for a race with unix_release_sock(). Fix this by taking the state lock and getting a reference to the path under lock. Ideally for connected sockets we would cache this information so we don't have to take the lock here. But for now just fix the race. Fixes: bc6e5f6933b8e ("apparmor: Remove use of the double lock") Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/af_unix.c | 64 ++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/security/apparmor/af_unix.c b/security/apparmor/af_unix.c index 09ce3f6da20ce..23753cb2096e5 100644 --- a/security/apparmor/af_unix.c +++ b/security/apparmor/af_unix.c @@ -748,41 +748,47 @@ int aa_unix_file_perm(const struct cred *subj_cred, struct aa_label *label, if (!peer_sk) goto out; - peer_addr = aa_sunaddr(unix_sk(peer_sk), &peer_addrlen); - - struct path peer_path; - - peer_path = unix_sk(peer_sk)->path; - if (!is_sk_fs && is_unix_fs(peer_sk)) { - last_error(error, - unix_fs_perm(op, request, subj_cred, label, - is_unix_fs(peer_sk) ? &peer_path : NULL)); - } else if (!is_sk_fs) { - struct aa_sk_ctx *pctx = aa_sock(peer_sk); - - rcu_read_lock(); - plabel = aa_get_newest_label(pctx->label); - rcu_read_unlock(); - /* no fs check of aa_unix_peer_perm because conditions above - * ensure they will never be done - */ - last_error(error, - xcheck(unix_peer_perm(subj_cred, label, op, + if (!is_sk_fs) { + bool is_peer_fs = is_unix_fs(peer_sk); + + peer_addr = aa_sunaddr(unix_sk(peer_sk), &peer_addrlen); + if (is_peer_fs) { + struct path peer_path; + + unix_state_lock(peer_sk); + peer_path = unix_sk(peer_sk)->path; + if (peer_path.dentry) + path_get(&peer_path); + unix_state_unlock(peer_sk); + + last_error(error, + unix_fs_perm(op, request, subj_cred, label, + &peer_path)); + if (peer_path.dentry) + path_put(&peer_path); + } else { + struct aa_sk_ctx *pctx = aa_sock(peer_sk); + + rcu_read_lock(); + plabel = aa_get_newest_label(pctx->label); + rcu_read_unlock(); + /* no fs check of aa_unix_peer_perm because conditions + * above ensure they will never be done + */ + last_error(error, + xcheck(unix_peer_perm(subj_cred, label, op, MAY_READ | MAY_WRITE, sock->sk, is_sk_fs ? &path : NULL, peer_addr, peer_addrlen, - is_unix_fs(peer_sk) ? - &peer_path : NULL, - plabel), - unix_peer_perm(file->f_cred, plabel, op, + NULL, plabel), + unix_peer_perm(file->f_cred, plabel, op, MAY_READ | MAY_WRITE, peer_sk, - is_unix_fs(peer_sk) ? - &peer_path : NULL, - addr, addrlen, + NULL, addr, addrlen, is_sk_fs ? &path : NULL, label))); - if (!error && !__aa_subj_label_is_cached(plabel, label)) - update_peer_ctx(peer_sk, pctx, label); + if (!error && !__aa_subj_label_is_cached(plabel, label)) + update_peer_ctx(peer_sk, pctx, label); + } } sock_put(peer_sk); From b8642f1478982a97ca2eb59f70f631a9de42ae11 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 24 Oct 2025 12:59:51 -0700 Subject: [PATCH 1713/2758] apparmor: fix refcount leak when updating the sk_ctx [ Upstream commit 6d25e7b47616cb2db43351210929c8f19dc305a3 ] Currently update_sk_ctx() transfers the plabel reference, unfortunately it is also unconditionally put in the caller. Ideally we would make the caller conditionally put the reference based on whether it was transferred but for now just fix the bug by getting a reference. Fixes: 88fec3526e841 ("apparmor: make sure unix socket labeling is correctly updated.") Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/af_unix.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/security/apparmor/af_unix.c b/security/apparmor/af_unix.c index 23753cb2096e5..834a3b1c2f0af 100644 --- a/security/apparmor/af_unix.c +++ b/security/apparmor/af_unix.c @@ -674,9 +674,11 @@ static void update_sk_ctx(struct sock *sk, struct aa_label *label, old = rcu_dereference_protected(ctx->peer, lockdep_is_held(&unix_sk(sk)->lock)); if (old == plabel) { - rcu_assign_pointer(ctx->peer_lastupdate, plabel); + rcu_assign_pointer(ctx->peer_lastupdate, + aa_get_label(plabel)); } else if (aa_label_is_subset(plabel, old)) { - rcu_assign_pointer(ctx->peer_lastupdate, plabel); + rcu_assign_pointer(ctx->peer_lastupdate, + aa_get_label(plabel)); rcu_assign_pointer(ctx->peer, aa_get_label(plabel)); aa_put_label(old); } /* else race or a subset - don't update */ From 25b262492539f020efa0f1d1d3d2c3bc6c4df8fa Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 3 Jun 2026 13:30:46 -0700 Subject: [PATCH 1714/2758] security/apparmor/apparmorfs.c: conditionally compile get_loaddata_common_ref() [ Upstream commit d62d9bfe050f44f772d05a32079dba3e3523ab2a ] Some config did this: security/apparmor/apparmorfs.c:177:28: warning: 'get_loaddata_common_ref' defined but not used [-Wunused-function] 177 | static struct aa_loaddata *get_loaddata_common_ref(struct aa_common_ref *ref) get_loaddata_common_ref() is only used if CONFIG_SECURITY_APPARMOR_EXPORT_BINARY=y. (Or of course move the function into that block if maintainers perfer) Fixes: 8e135b8aee5a0 ("apparmor: fix race between freeing data and fs accessing it") Cc: John Johansen Cc: Paul Moore Cc: James Morris Cc: "Serge E. Hallyn" Signed-off-by: Andrew Morton Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/apparmorfs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index ededaf46f3caa..aeff3d2d863bc 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -174,6 +174,7 @@ static struct aa_proxy *get_proxy_common_ref(struct aa_common_ref *ref) return NULL; } +#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY static struct aa_loaddata *get_loaddata_common_ref(struct aa_common_ref *ref) { if (ref) @@ -181,6 +182,7 @@ static struct aa_loaddata *get_loaddata_common_ref(struct aa_common_ref *ref) count)); return NULL; } +#endif static void aa_put_common_ref(struct aa_common_ref *ref) { From ec926b2a351eeeb31e6c9aee02e0c32f94b5588f Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Mon, 8 Jun 2026 14:36:31 +0800 Subject: [PATCH 1715/2758] apparmor: check label build before no_new_privs test [ Upstream commit a58cafd38b46fb1a2220e2fbbcfe291ea75fa147 ] aa_change_profile() builds a replacement label with fn_label_build_in_scope() before the no_new_privs subset check. The build helper can fail and return NULL or an ERR_PTR, but the result was passed to aa_label_is_unconfined_subset() before the existing IS_ERR_OR_NULL() check. Reuse the existing target-label build failure handling immediately after the build. This preserves the current audit handling while preventing the subset helper from dereferencing an invalid label. Fixes: e00b02bb6ac2a ("apparmor: move change_profile mediation to using labels") Signed-off-by: Ruoyu Wang Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/domain.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index f02bf770f6385..6748ac74b060b 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -1527,6 +1527,8 @@ int aa_change_profile(const char *fqname, int flags) new = fn_label_build_in_scope(label, profile, GFP_KERNEL, aa_get_label(target), aa_get_label(&profile->label)); + if (IS_ERR_OR_NULL(new)) + goto build_fail; /* * no new privs prevents domain transitions that would * reduce restrictions. @@ -1545,16 +1547,8 @@ int aa_change_profile(const char *fqname, int flags) /* only transition profiles in the current ns */ if (stack) new = aa_label_merge(label, target, GFP_KERNEL); - if (IS_ERR_OR_NULL(new)) { - info = "failed to build target label"; - if (!new) - error = -ENOMEM; - else - error = PTR_ERR(new); - new = NULL; - perms.allow = 0; - goto audit; - } + if (IS_ERR_OR_NULL(new)) + goto build_fail; error = aa_replace_current_label(new); } else { if (new) { @@ -1566,6 +1560,17 @@ int aa_change_profile(const char *fqname, int flags) aa_set_current_onexec(target, stack); } + goto audit; + +build_fail: + info = "failed to build target label"; + if (!new) + error = -ENOMEM; + else + error = PTR_ERR(new); + new = NULL; + perms.allow = 0; + audit: error = fn_for_each_in_scope(label, profile, aa_audit_file(subj_cred, From 6d91479174240f39e9edea250d95fa08c678a207 Mon Sep 17 00:00:00 2001 From: Zygmunt Krynicki Date: Sat, 2 May 2026 13:21:33 +0200 Subject: [PATCH 1716/2758] apparmor: aa_label_alloc use aa_label_free on alloc failure [ Upstream commit 654fe7505dc6889724d4094fa64f89991afabfc3 ] aa_label_alloc() allocates a secid before allocating or taking the label proxy. If the later proxy step fails, the error path only freed the label memory, leaking any resources initialized by aa_label_init(). Use aa_label_free() on the failure path so partially initialized labels release their secid and other label resources before the backing memory is freed. Fixes: f1bd904175e81 ("apparmor: add the base fns() for domain labels") Signed-off-by: Zygmunt Krynicki Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/label.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/label.c b/security/apparmor/label.c index 3a721fdf18339..c6a96355e8d9e 100644 --- a/security/apparmor/label.c +++ b/security/apparmor/label.c @@ -458,7 +458,7 @@ struct aa_label *aa_label_alloc(int size, struct aa_proxy *proxy, gfp_t gfp) return new; fail: - kfree(new); + aa_label_free(new); return NULL; } From 4a2c4f2b45dca0fc791e5d56b91df2fdd3b447b8 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sun, 1 Mar 2026 12:24:06 -0800 Subject: [PATCH 1717/2758] apparmor: fix rawdata_f_data implicit flex array [ Upstream commit ad213bbbc0e3e270ce7df2d9d80d4ce3826993d7 ] rawdata_f_data has a blob of data that is allocated at its end but not explicitly declared. Makes sure it is correctly declared as a flex_rray. Fixes: 63c16c3a76085 ("apparmor: Initial implementation of raw policy blob compression") Reviewed-by: Georgia Garcia Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/apparmorfs.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index aeff3d2d863bc..725a69aa1c2a3 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -71,10 +71,10 @@ struct rawdata_f_data { struct aa_loaddata *loaddata; + DECLARE_FLEX_ARRAY(char, data); }; #ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY -#define RAWDATA_F_DATA_BUF(p) (char *)(p + 1) static void rawdata_f_data_free(struct rawdata_f_data *private) { @@ -1436,7 +1436,7 @@ static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size, struct rawdata_f_data *private = file->private_data; return simple_read_from_buffer(buf, size, ppos, - RAWDATA_F_DATA_BUF(private), + private->data, private->loaddata->size); } @@ -1469,8 +1469,7 @@ static int rawdata_open(struct inode *inode, struct file *file) private->loaddata = loaddata; error = decompress_zstd(loaddata->data, loaddata->compressed_size, - RAWDATA_F_DATA_BUF(private), - loaddata->size); + private->data, loaddata->size); if (error) goto fail_decompress; From 67ee65ec1a3eebdde76ccaa664d20dcf5b125a2f Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Fri, 13 Feb 2026 11:29:38 -0800 Subject: [PATCH 1718/2758] apparmor: grab ns lock and refresh when looking up changehat child profiles [ Upstream commit 32e92764d6f8d251c1bca62be33793287b453a81 ] There was a race condition involving change_hat and profile replacement in which replacement of the parent profile during a changehat operation could result in the list of children becoming empty and the changehat operation failing. To prevent this: - grab the namespace lock until we've built the hat transition, and - use aa_get_newest_profile to avoid using stale profile objects. Link: https://bugs.launchpad.net/bugs/2139664 Fixes: 89dbf1962aa63 ("apparmor: move change_hat mediation to using labels") Reviewed-by: Georgia Garcia Signed-off-by: Ryan Lee Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/domain.c | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 6748ac74b060b..c2652165a5886 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -1109,6 +1110,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred, int count, int flags) { struct aa_profile *profile, *root, *hat = NULL; + struct aa_ns *ns, *new_ns; struct aa_label *new; struct label_it it; bool sibling = false; @@ -1119,6 +1121,32 @@ static struct aa_label *change_hat(const struct cred *subj_cred, AA_BUG(!hats); AA_BUG(count < 1); + /* + * Acquire the newest label and then hold the lock until we choose a + * hat, so that profile replacement doesn't atomically truncate the + * list of potential hats. Because we are getting the namespaces from + * the profiles and label, we can rely on the namespaces being live + * and avoid incrementing their refcounts while grabbing the lock. + */ + label = aa_get_label(label); + ns = labels_ns(label); + +retry: + mutex_lock_nested(&ns->lock, ns->level); + if (label_is_stale(label)) { + new = aa_get_newest_label(label); + new_ns = labels_ns(new); + if (new_ns != ns) { + aa_put_label(new); + mutex_unlock(&ns->lock); + ns = new_ns; + label = new; + goto retry; + } + aa_put_label(label); + label = new; + } + if (PROFILE_IS_HAT(labels_profile(label))) sibling = true; @@ -1127,7 +1155,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred, name = hats[i]; label_for_each_in_scope(it, labels_ns(label), label, profile) { if (sibling && PROFILE_IS_HAT(profile)) { - root = aa_get_profile_rcu(&profile->parent); + root = aa_get_profile(profile->parent); } else if (!sibling && !PROFILE_IS_HAT(profile)) { root = aa_get_profile(profile); } else { /* conflicting change type */ @@ -1187,6 +1215,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred, GLOBAL_ROOT_UID, info, error); } } + mutex_unlock(&ns->lock); return ERR_PTR(error); build: @@ -1199,7 +1228,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred, error = -ENOMEM; goto fail; } /* else if (IS_ERR) build_change_hat has logged error so return new */ - + mutex_unlock(&ns->lock); return new; } From dd5f1202f45a2dbe2c7dd10093f5c6bb2d8ac5bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20B=C3=A9lair?= Date: Wed, 18 Feb 2026 10:27:34 +0100 Subject: [PATCH 1719/2758] apparmor: fix potential UAF in aa_replace_profiles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 7b42f95813dc9ceb6bda35afcf914630909a19f9 ] The function aa_replace_profiles was accessing udata->size after calling aa_put_loaddata(udata), causing a potential UAF. Fixed this by saving the size to a local variable before dropping the reference. Fixes: 5ac8c355ae001 ("apparmor: allow introspecting the loaded policy pre internal transform") Reviewed-by: Georgia Garcia Signed-off-by: Maxime Bélair Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/policy.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index e103cce6f4af5..6effddb46994a 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1375,13 +1375,15 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, mutex_unlock(&ns->lock); out: + ssize_t udata_sz = udata->size; + aa_put_ns(ns); aa_put_profile_loaddata(udata); kfree(ns_name); if (error) return error; - return udata->size; + return udata_sz; fail_lock: mutex_unlock(&ns->lock); From 73d86ca950b80cd39ca948e2c518d135bf2308c8 Mon Sep 17 00:00:00 2001 From: Georgia Garcia Date: Wed, 6 May 2026 16:02:11 -0300 Subject: [PATCH 1720/2758] apparmor: fix NULL pointer dereference in unpack_pdb [ Upstream commit 7681ca43d2b1c776e62fe77e3167835fb1ab8319 ] pdb->dfa could be NULL if unpack_dfa fails, causing a NULL pointer dereference. Fixes: 2e12c5f06017 ("apparmor: add additional flags to extended permission.") Signed-off-by: Georgia Garcia Signed-off-by: Manuel Diewald Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/policy_unpack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 9f45d5513d2ca..3643c058d6f89 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -1045,7 +1045,7 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy, } /* accept2 is in some cases being allocated, even with perms */ - if (pdb->perms && !pdb->dfa->tables[YYTD_ID_ACCEPT2]) { + if (pdb->dfa && pdb->perms && !pdb->dfa->tables[YYTD_ID_ACCEPT2]) { /* add dfa flags table missing in v2 */ u32 noents = pdb->dfa->tables[YYTD_ID_ACCEPT]->td_lolen; u16 tdflags = pdb->dfa->tables[YYTD_ID_ACCEPT]->td_flags; From 2118a9f7a7edf62eec83c21623593ffbbe375eaf Mon Sep 17 00:00:00 2001 From: Georgia Garcia Date: Thu, 29 Jan 2026 15:39:42 -0300 Subject: [PATCH 1721/2758] apparmor: remove or add symlinks to rawdata according to export_binary [ Upstream commit 59fe6fbc4cd45582bc8893de0a382a36562317b3 ] When the export_binary parameter is set, then rawdata is available and there should be a symbolic link for the rawdata in the profile directory in apparmorfs. If the parameter is unset, then the symlinks should not exist. The issue arises when changing the value of export_binary on runtime and replacing profiles. If export_binary was set when the profile was originally loaded, then changed to 0 and the profile was reloaded, then the symbolic links would still exist but would return ENOENT because the rawdata no longer exists. On the opposite side, if export_binary was unset when the profile was originally loaded, then changed to 1 and the profile was reloaded, then the symbolic links would not exist, even though the rawdata does. Fixes: d61c57fde8191 ("apparmor: make export of raw binary profile to userspace optional") Signed-off-by: Georgia Garcia Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/apparmorfs.c | 102 +++++++++++++++++++------ security/apparmor/include/apparmorfs.h | 12 +++ security/apparmor/policy.c | 15 ++++ 3 files changed, 104 insertions(+), 25 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 725a69aa1c2a3..be924700ba6c9 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -1757,6 +1757,80 @@ static const struct inode_operations rawdata_link_abi_iops = { static const struct inode_operations rawdata_link_data_iops = { .get_link = rawdata_get_link_data, }; + +/* + * Requires: @profile->ns->lock held + */ +void __aa_remove_rawdata_symlink_dents(struct aa_profile *profile) +{ + aafs_remove(profile->dents[AAFS_PROF_RAW_HASH]); + profile->dents[AAFS_PROF_RAW_HASH] = NULL; + aafs_remove(profile->dents[AAFS_PROF_RAW_ABI]); + profile->dents[AAFS_PROF_RAW_ABI] = NULL; + aafs_remove(profile->dents[AAFS_PROF_RAW_DATA]); + profile->dents[AAFS_PROF_RAW_DATA] = NULL; +} + +static inline int create_symlink_dent(struct aa_profile *profile, + const char *name, + enum aafs_prof_type type, + const struct inode_operations *iops) +{ + struct dentry *dent = NULL; + struct dentry *dir = prof_dir(profile); + + if (profile->dents[type]) + return 0; + + dent = aafs_create(name, S_IFLNK | 0444, dir, + &profile->label.proxy->count, NULL, NULL, iops); + if (IS_ERR(dent)) + return PTR_ERR(dent); + + profile->dents[type] = dent; + return 0; +} + +/* + * Requires: @profile->ns->lock held + */ +int __aa_create_rawdata_symlink_dents(struct aa_profile *profile) +{ + int error; + + if (!profile || + (profile->dents[AAFS_PROF_RAW_HASH] && + profile->dents[AAFS_PROF_RAW_ABI] && + profile->dents[AAFS_PROF_RAW_DATA])) + return 0; + + if (!profile->rawdata) + return 0; + + if (aa_g_hash_policy) { + error = create_symlink_dent(profile, "raw_sha256", + AAFS_PROF_RAW_HASH, + &rawdata_link_sha256_iops); + if (error) + return error; + } + + error = create_symlink_dent(profile, "raw_abi", + AAFS_PROF_RAW_ABI, + &rawdata_link_abi_iops); + if (error) + return error; + + + error = create_symlink_dent(profile, "raw_data", + AAFS_PROF_RAW_DATA, + &rawdata_link_data_iops); + if (error) + return error; + + return 0; +} + #endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */ /* @@ -1832,31 +1906,9 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent) profile->dents[AAFS_PROF_HASH] = dent; } -#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY - if (profile->rawdata) { - if (aa_g_hash_policy) { - dent = aafs_create("raw_sha256", S_IFLNK | 0444, dir, - &profile->label.proxy->count, NULL, - NULL, &rawdata_link_sha256_iops); - if (IS_ERR(dent)) - goto fail; - profile->dents[AAFS_PROF_RAW_HASH] = dent; - } - dent = aafs_create("raw_abi", S_IFLNK | 0444, dir, - &profile->label.proxy->count, NULL, NULL, - &rawdata_link_abi_iops); - if (IS_ERR(dent)) - goto fail; - profile->dents[AAFS_PROF_RAW_ABI] = dent; - - dent = aafs_create("raw_data", S_IFLNK | 0444, dir, - &profile->label.proxy->count, NULL, NULL, - &rawdata_link_data_iops); - if (IS_ERR(dent)) - goto fail; - profile->dents[AAFS_PROF_RAW_DATA] = dent; - } -#endif /*CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */ + error = __aa_create_rawdata_symlink_dents(profile); + if (error) + goto fail2; list_for_each_entry(child, &profile->base.profiles, base.list) { error = __aafs_profile_mkdir(child, prof_child_dir(profile)); diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index dd580594dfb70..33243d11fd106 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -120,6 +120,8 @@ struct aa_loaddata; #ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata); int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata); +void __aa_remove_rawdata_symlink_dents(struct aa_profile *profile); +int __aa_create_rawdata_symlink_dents(struct aa_profile *profile); #else static inline void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata) { @@ -131,6 +133,16 @@ static inline int __aa_fs_create_rawdata(struct aa_ns *ns, { return 0; } + +static inline void __aa_remove_rawdata_symlink_dents(struct aa_profile *profile) +{ + /* empty stub */ +} + +static inline int __aa_create_rawdata_symlink_dents(struct aa_profile *profile) +{ + return 0; +} #endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */ #endif /* __AA_APPARMORFS_H */ diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 6effddb46994a..73101ddf0b871 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1346,6 +1346,16 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, goto skip; } + if (!aa_g_export_binary) { + if (ent->old && ent->old->rawdata && + ent->old->dents[AAFS_LOADDATA_DIR]) { + /* remove rawdata symlinks because the symlink + * target will be removed + */ + __aa_remove_rawdata_symlink_dents(ent->old); + } + } + /* * TODO: finer dedup based on profile range in data. Load set * can differ but profile may remain unchanged @@ -1356,6 +1366,11 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, if (ent->old) { share_name(ent->old, ent->new); __replace_profile(ent->old, ent->new); + if (aa_g_export_binary) { + /* recreate rawdata symlinks */ + if (!ent->old->rawdata) + __aa_create_rawdata_symlink_dents(ent->new); + } } else { struct list_head *lh; From bd30d91f9f22b184d613b22fe37a470234b032b0 Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Sun, 3 May 2026 12:12:43 +0800 Subject: [PATCH 1722/2758] apparmor: Fix return in ns_mkdir_op [ Upstream commit b7a2b49bba4e5994a476c49d662b796818079e5e ] Return NULL instead of passing to ERR_PTR while error is zero. Fixes smatch warning: - security/apparmor/apparmorfs.c:1846 ns_mkdir_op() warn: passing zero to 'ERR_PTR' Fixes: 88d5baf69082 ("Change inode_operations.mkdir to return struct dentry *") Reviewed-by: Ryan Lee Signed-off-by: Hongling Zeng Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/apparmorfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index be924700ba6c9..95deb867a76b5 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -1975,7 +1975,7 @@ static struct dentry *ns_mkdir_op(struct mnt_idmap *idmap, struct inode *dir, mutex_unlock(&parent->lock); aa_put_ns(parent); - return ERR_PTR(error); + return error ? ERR_PTR(error) : NULL; } static int ns_rmdir_op(struct inode *dir, struct dentry *dentry) From 106e909e12baf059538f0b0f1a59f87781d25b3c Mon Sep 17 00:00:00 2001 From: Zygmunt Krynicki Date: Mon, 4 May 2026 08:32:37 +0200 Subject: [PATCH 1723/2758] apparmor: fail policy unpack on accept2 allocation failure [ Upstream commit 45cf568241048e560a81aa2053f06a62069f5640 ] unpack_pdb() may need to allocate a missing ACCEPT2 table for older policy data. If that allocation failed, it set an error message but jumped to the success path, returning a policydb with the required table missing. Return -ENOMEM through the normal failure path when the ACCEPT2 allocation fails. Remove the now-unused out label. Fixes: 2e12c5f06017 ("apparmor: add additional flags to extended permission.") Reviewed-by: Ryan Lee Signed-off-by: Zygmunt Krynicki Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/policy_unpack.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 3643c058d6f89..d9dcff167c480 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -1054,7 +1054,8 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy, pdb->dfa->tables[YYTD_ID_ACCEPT2] = kvzalloc(tsize, GFP_KERNEL); if (!pdb->dfa->tables[YYTD_ID_ACCEPT2]) { *info = "failed to alloc dfa flags table"; - goto out; + error = -ENOMEM; + goto fail; } pdb->dfa->tables[YYTD_ID_ACCEPT2]->td_lolen = noents; pdb->dfa->tables[YYTD_ID_ACCEPT2]->td_flags = tdflags; @@ -1079,7 +1080,6 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy, * - move free of unneeded trans table here, has to be done * after perm mapping. */ -out: *policy = pdb; return 0; From 393809a05cfb60309ad63ee09138db8296d6b97e Mon Sep 17 00:00:00 2001 From: Zygmunt Krynicki Date: Mon, 4 May 2026 13:13:24 +0200 Subject: [PATCH 1724/2758] apparmor: release exe file resources on path failure [ Upstream commit 7306c41672487a6c28430714be063bc6942c28f2 ] get_current_exe_path() takes both an exe_file reference and a path reference before resolving the path name. If aa_path_name() failed, it returned immediately and leaked both references. Route the failure through the common cleanup path so fput() and path_put() always run after the references are acquired. Fixes: 8d34e16f7f2b ("apparmor: userns: Add support for execpath in userns") Reviewed-by: Ryan Lee Signed-off-by: Zygmunt Krynicki Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/task.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/security/apparmor/task.c b/security/apparmor/task.c index 0db0e81b46001..6445cb5f85266 100644 --- a/security/apparmor/task.c +++ b/security/apparmor/task.c @@ -313,9 +313,12 @@ static const char *get_current_exe_path(char *buffer, int buffer_size) p = exe_file->f_path; path_get(&p); - if (aa_path_name(&p, FLAG_VIEW_SUBNS, buffer, &path_str, NULL, NULL)) - return ERR_PTR(-ENOMEM); + if (aa_path_name(&p, FLAG_VIEW_SUBNS, buffer, &path_str, NULL, NULL)) { + path_str = ERR_PTR(-ENOMEM); + goto out; + } +out: fput(exe_file); path_put(&p); From de91788aa6b88bd71de36c7ac453383991ef54c5 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 6 May 2026 01:17:38 -0700 Subject: [PATCH 1725/2758] apparmor: remove unnecessary goto and associated label [ Upstream commit e27bfb2ae9ad8522aea82d435fd6d73cccee7e17 ] There is no need for a goto a label immediately following the conditional block when the jump is the last statement in the block. Fixes: 7306c41672487 ("apparmor: release exe file resources on path failure") Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/task.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/security/apparmor/task.c b/security/apparmor/task.c index 6445cb5f85266..b9fb3738124e0 100644 --- a/security/apparmor/task.c +++ b/security/apparmor/task.c @@ -313,12 +313,9 @@ static const char *get_current_exe_path(char *buffer, int buffer_size) p = exe_file->f_path; path_get(&p); - if (aa_path_name(&p, FLAG_VIEW_SUBNS, buffer, &path_str, NULL, NULL)) { + if (aa_path_name(&p, FLAG_VIEW_SUBNS, buffer, &path_str, NULL, NULL)) path_str = ERR_PTR(-ENOMEM); - goto out; - } -out: fput(exe_file); path_put(&p); From b1abb53407370a74a3d375f68a251e06b38d4a3d Mon Sep 17 00:00:00 2001 From: Zygmunt Krynicki Date: Sat, 2 May 2026 13:37:14 +0200 Subject: [PATCH 1726/2758] apparmor: aa_getprocattr free procattr leak on format failure [ Upstream commit fea23bf73f0cae8ccb1d0684e4a3003874771f41 ] aa_getprocattr() allocates the output string before rendering the label into it. If the second aa_label_snxprint() call fails, the function returned without freeing that allocation. Free and clear the output pointer on the uncommon formatting failure path before dropping the namespace reference. Fixes: 76a1d263aba3 ("apparmor: switch getprocattr to using label_print fns()") Reviewed-by: Tyler Hicks Reviewed-by: Ryan Lee Signed-off-by: Zygmunt Krynicki Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/procattr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c index ce40f15d4952d..c07b6e8fd9c93 100644 --- a/security/apparmor/procattr.c +++ b/security/apparmor/procattr.c @@ -54,6 +54,8 @@ int aa_getprocattr(struct aa_label *label, char **string, bool newline) FLAG_SHOW_MODE | FLAG_VIEW_SUBNS | FLAG_HIDDEN_UNCONFINED); if (len < 0) { + kfree(*string); + *string = NULL; aa_put_ns(current_ns); return len; } From 859ba6c7fc6e743422b055f40dd76c464ad7942c Mon Sep 17 00:00:00 2001 From: Zygmunt Krynicki Date: Tue, 5 May 2026 05:40:53 +0200 Subject: [PATCH 1727/2758] apparmor: put secmark label after secid lookup [ Upstream commit 340372688bb87da45ff8d4e2f82ccfd1b64c65ff ] apparmor_secmark_init() parses a configured secmark label to obtain its secid. aa_label_strn_parse() returns a refcounted label, but the success path kept that reference after copying the secid. Fixes: ab9f2115081a ("apparmor: Allow filtering based on secmark policy") Signed-off-by: Zygmunt Krynicki Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/net.c | 1 + 1 file changed, 1 insertion(+) diff --git a/security/apparmor/net.c b/security/apparmor/net.c index 1fc6145ccbb8d..cf590dd08540c 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -356,6 +356,7 @@ static int apparmor_secmark_init(struct aa_secmark *secmark) return PTR_ERR(label); secmark->secid = label->secid; + aa_put_label(label); return 0; } From 3f172fbbe3570e5d24c56f3a9308649f8c4a4a95 Mon Sep 17 00:00:00 2001 From: Georgia Garcia Date: Thu, 28 May 2026 16:04:12 -0300 Subject: [PATCH 1728/2758] apparmor: don't audit files pointing to aa_null.dentry [ Upstream commit add2b70038bea194bcdef8a680f9153ee7f93ac0 ] In commit 4a134723f9f1 ("apparmor: move check for aa_null file to cover all cases") there was a change to not audit files pointing to aa_null.dentry because they provide no value, but setting the error variable instead of returning -EACCES was still causing them to be audited. Fixes: 4a134723f9f1 ("apparmor: move check for aa_null file to cover all cases") Acked-by: David Disseldorp Signed-off-by: Georgia Garcia Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/file.c b/security/apparmor/file.c index 694e157149e85..fc5abd5473c85 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -157,7 +157,7 @@ static int path_name(const char *op, const struct cred *subj_cred, /* don't reaudit files closed during inheritance */ if (unlikely(path->dentry == aa_null.dentry)) - error = -EACCES; + return -EACCES; else error = aa_path_name(path, flags, buffer, name, &info, labels_profile(label)->disconnected); From a5c79d44ef19203bd7cf6f3de8ff088112ebff95 Mon Sep 17 00:00:00 2001 From: Maciek Borzecki Date: Fri, 8 May 2026 10:30:16 +0200 Subject: [PATCH 1729/2758] apparmor: fix uninitialised pointer passed to audit_log_untrustedstring() [ Upstream commit bcd1b34c21748531a3febaf7440632b89d8deab7 ] Commit 4a134723f9f1 ("apparmor: move check for aa_null file to cover all cases") intrdouced a small bug, where path_name() may pass a potentially uninitialized *name to aa_audit_file() if the path->dentry had been replaced with aa_null.dentry earlier on. This can lead to page fault like one observed on 7.0.2 openSUSE Tumbleweed kernel: [51692.242756] [ T24690] BUG: unable to handle page fault for address: 0000000f00000003 [51692.242762] [ T24690] #PF: supervisor read access in kernel mode [51692.242763] [ T24690] #PF: error_code(0x0000) - not-present page [51692.242765] [ T24690] PGD 0 P4D 0 [51692.242768] [ T24690] Oops: Oops: 0000 [#1] SMP NOPTI [51692.242772] [ T24690] CPU: 3 UID: 1020 PID: 24690 Comm: snap-confine Tainted: G O 7.0.2-1-default #1 PREEMPT(full) openSUSE Tumbleweed ab90b4c9940707f9cafa19bdad80b2cec52dbe51 [51692.242775] [ T24690] Tainted: [O]=OOT_MODULE [51692.242777] [ T24690] Hardware name: Framework Laptop 13 (AMD Ryzen 7040Series)/FRANMDCP05, BIOS 03.18 01/08/2026 [51692.242778] [ T24690] RIP: 0010:strlen+0x4/0x30 [51692.242783] [ T24690] Code: f7 75 ec 31 c0 e9 17 9f 00 ff 48 89 f8 e9 0f 9f 00 ff 0f 1f 40 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa <80> 3f 00 74 18 48 89 f8 0f 1f 40 00 48 83 c0 01 80 38 00 75 f7 48 [51692.242785] [ T24690] RSP: 0018:ffffd015eb1e3608 EFLAGS: 00010282 [51692.242787] [ T24690] RAX: 0000000000000000 RBX: ffff89796198a360 RCX: 0000000000000000 [51692.242788] [ T24690] RDX: 00000000000000d1 RSI: 0000000f00000003 RDI: 0000000f00000003 [51692.242790] [ T24690] RBP: ffffffffb7ede090 R08: 00000000000005f5 R09: 0000000000000000 [51692.242791] [ T24690] R10: 0000000000000000 R11: 0000000000000000 R12: ffffd015eb1e3700 [51692.242792] [ T24690] R13: ffff8977a22bc380 R14: ffffffffb7ec5190 R15: ffff8977a0c8aa80 [51692.242794] [ T24690] FS: 0000000000000000(0000) GS:ffff897f640d8000(0000) knlGS:0000000000000000 [51692.242796] [ T24690] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [51692.242797] [ T24690] CR2: 0000000f00000003 CR3: 00000006ad15f000 CR4: 0000000000f50ef0 [51692.242799] [ T24690] PKRU: 55555554 [51692.242800] [ T24690] Call Trace: [51692.242802] [ T24690] [51692.242804] [ T24690] audit_log_untrustedstring+0x1d/0x40 [51692.242811] [ T24690] common_lsm_audit+0x71/0x1d0 [51692.242816] [ T24690] aa_audit+0x5a/0x170 [51692.242819] [ T24690] aa_audit_file+0x18a/0x1b0 [51692.242825] [ T24690] path_name+0xd2/0x100 [51692.242829] [ T24690] profile_path_perm.part.0+0x58/0xb0 [51692.242832] [ T24690] aa_path_perm+0xef/0x150 [51692.242837] [ T24690] apparmor_file_open+0x153/0x2e0 [51692.242840] [ T24690] security_file_open+0x46/0xd0 [51692.242844] [ T24690] do_dentry_open+0xe9/0x4d0 [51692.242848] [ T24690] vfs_open+0x30/0x100 While here, initialise variables which are passed down to path_name(). Fixes: 4a134723f9f1 ("apparmor: move check for aa_null file to cover all cases") Signed-off-by: Maciek Borzecki Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/file.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/security/apparmor/file.c b/security/apparmor/file.c index fc5abd5473c85..c9d55fe1086f9 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -158,9 +158,9 @@ static int path_name(const char *op, const struct cred *subj_cred, /* don't reaudit files closed during inheritance */ if (unlikely(path->dentry == aa_null.dentry)) return -EACCES; - else - error = aa_path_name(path, flags, buffer, name, &info, - labels_profile(label)->disconnected); + + error = aa_path_name(path, flags, buffer, name, &info, + labels_profile(label)->disconnected); if (error) { fn_for_each_confined(label, profile, aa_audit_file(subj_cred, @@ -250,7 +250,7 @@ static int profile_path_perm(const char *op, const struct cred *subj_cred, struct path_cond *cond, int flags, struct aa_perms *perms) { - const char *name; + const char *name = NULL; int error; if (profile_unconfined(profile)) @@ -328,7 +328,7 @@ static int profile_path_link(const struct cred *subj_cred, struct path_cond *cond) { struct aa_ruleset *rules = profile->label.rules[0]; - const char *lname, *tname = NULL; + const char *lname = NULL, *tname = NULL; struct aa_perms lperms = {}, perms; const char *info = NULL; u32 request = AA_MAY_LINK; From 4238195ed98939a9e7623b8705a29242ff15e887 Mon Sep 17 00:00:00 2001 From: Eduardo Vasconcelos Date: Thu, 21 May 2026 12:13:06 -0300 Subject: [PATCH 1730/2758] apparmor: Fix inverted comparison in cache_hold_inc() [ Upstream commit 5112ed5258b8d5e0769ae7d2bf9c9dea14c59703 ] cache_hold_inc() prevents the per-CPU cache hold counter from rising above MAX_HOLD_COUNT, but the comparison is inverted (> MAX_HOLD_COUNT instead of <), so the counter never rises above 0. This breaks the cache mechanism because since the hold counter is always 0, the global pool is always attempted first before falling back to the local cache. The decrement also never occurs, thus the hold counter is effectively dead. Fix by changing > to < in cache_hold_inc(). Fixes: 0b6a6b72b329 ("apparmor: document the buffer hold, add an overflow guard") Signed-off-by: Eduardo Vasconcelos Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/lsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index e01efdf50efa2..1f2bbc3175d20 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -2143,7 +2143,7 @@ static int param_set_mode(const char *val, const struct kernel_param *kp) */ static void cache_hold_inc(unsigned int *hold) { - if (*hold > MAX_HOLD_COUNT) + if (*hold < MAX_HOLD_COUNT) (*hold)++; } From 49a230c2aea4c1dd3217293dc11ad5e6f0a444d6 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 3 Jun 2026 12:07:38 +0300 Subject: [PATCH 1731/2758] i3c: mipi-i3c-hci: Fix suspend behavior when bus disable falls back to software reset [ Upstream commit 57e181af13de36571380ef3cc74000559826fa9b ] Software reset was introduced as a fallback if bus disable failed. The change was made in 2 places: the cleanup path and the suspend path. For the cleanup path (i3c_hci_bus_cleanup()), after software reset the function continues to do cleanup for the current I/O mode. For the suspend path (i3c_hci_rpm_suspend()), after software reset the function returns early. However software reset does not reset any Ring Headers in the Host Controller, so returning early is not the right thing to do. Instead, continue to call suspend for the current I/O mode, which for DMA mode will reset any Ring Headers. Note, although Ring Headers should not be active at this stage, performing this reset follows the procedure defined by the specification and keeps the suspend path consistent with the cleanup path. Note also, i3c_hci_sync_irq_inactive() is still called via the PIO and DMA hci->io->suspend() callbacks. Always return 0 because the device is quiesced as much as possible and returning a negative error code would unnecessarily prevent system suspend. Fixes: 9a258d1336f7 ("i3c: mipi-i3c-hci: Fallback to software reset when bus disable fails") Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260603090754.16252-2-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/i3c/master/mipi-i3c-hci/core.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c index b781dbed21650..afb0764b5e1fb 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -762,15 +762,10 @@ static int i3c_hci_reset_and_init(struct i3c_hci *hci) int i3c_hci_rpm_suspend(struct device *dev) { struct i3c_hci *hci = dev_get_drvdata(dev); - int ret; - ret = i3c_hci_bus_disable(hci); - if (ret) { - /* Fall back to software reset to disable the bus */ - ret = i3c_hci_software_reset(hci); - i3c_hci_sync_irq_inactive(hci); - return ret; - } + /* Fall back to software reset to disable the bus */ + if (i3c_hci_bus_disable(hci)) + i3c_hci_software_reset(hci); hci->io->suspend(hci); From 88116f41086a5e09c21c95b12d37fdc1380bc580 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 3 Jun 2026 12:07:39 +0300 Subject: [PATCH 1732/2758] i3c: mipi-i3c-hci: Preserve RUN bit when aborting DMA ring [ Upstream commit 093eb8e73c90aa0c8cfb0421aa85bd70c23488be ] The MIPI I3C HCI specification does not require the DMA ring RUN bit (RUN_STOP) to be cleared when issuing an ABORT. That allows the DMA ring to continue to receive IBIs, although an IBI is anyway not lost because it can be received once the ring restarts if the I3C device has not given up. Note, currently ABORT is only used on a timeout error path so the change has very little effect in practice. In the more common case of a transfer error, the ring (bundle) operation is halted by the controller anyway. Adjust the RING_CONTROL handling to set ABORT without clearing RUN_STOP, bringing the driver into alignment with the specification. Fixes: b795e68bf3073 ("i3c: mipi-i3c-hci: Correct RING_CTRL_ABORT handling in DMA dequeue") Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260603090754.16252-3-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/i3c/master/mipi-i3c-hci/dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c index e4daaa6120550..bdffdd8b89236 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -554,7 +554,7 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci, if (ring_status & RING_STATUS_RUNNING) { /* stop the ring */ reinit_completion(&rh->op_done); - rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE | RING_CTRL_ABORT); + rh_reg_write(RING_CONTROL, rh_reg_read(RING_CONTROL) | RING_CTRL_ABORT); wait_for_completion_timeout(&rh->op_done, HZ); ring_status = rh_reg_read(RING_STATUS); if (ring_status & RING_STATUS_RUNNING) { From af6df5d506078fe78caf73d597bea68b3774f189 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 8 Jun 2026 08:43:05 +0300 Subject: [PATCH 1733/2758] i3c: master: Make hot-join workqueue freezable to block hot-join during suspend [ Upstream commit 527756cb9ebb277dca12fff00af9fbb3b9ec8cc8 ] The I3C master workqueue (master->wq) is used to defer work that needs thread context and the bus maintenance lock, most notably Hot Join processing (which calls i3c_master_do_daa() to assign dynamic addresses to newly joined devices). Currently the workqueue keeps running across system suspend, which can race with the suspend path: - do_daa() may execute after the controller has been suspended, issuing bus transactions on a powered-down or otherwise unusable controller. - New I3C devices can be enumerated and added to the bus mid-suspend, registering driver model objects at a point where the I3C subsystem and its consumers are not prepared to handle them. Mark the workqueue WQ_FREEZABLE so its workers are frozen for the duration of system suspend/hibernate and resumed afterwards. This naturally defers any pending or newly queued Hot Join work until the system (and the controller) is fully resumed, closing both races without adding explicit suspend/resume synchronization in the master drivers. Update the kerneldoc for struct i3c_master_controller::wq to reflect that the workqueue is freezable. Fixes: 3a379bbcea0af ("i3c: Add core I3C infrastructure") Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260608054312.10604-2-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/i3c/master.c | 2 +- include/linux/i3c/master.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 5cd4e5da22333..ab11e2d79aabb 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -3079,7 +3079,7 @@ int i3c_master_register(struct i3c_master_controller *master, if (ret) goto err_put_dev; - master->wq = alloc_workqueue("%s", WQ_PERCPU, 0, dev_name(parent)); + master->wq = alloc_workqueue("%s", WQ_PERCPU | WQ_FREEZABLE, 0, dev_name(parent)); if (!master->wq) { ret = -ENOMEM; goto err_put_dev; diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index 592b646f6134b..e6112e5f6608d 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -515,7 +515,7 @@ struct i3c_master_controller_ops { * @boardinfo.i2c: list of I2C boardinfo objects * @boardinfo: board-level information attached to devices connected on the bus * @bus: I3C bus exposed by this master - * @wq: workqueue which can be used by master + * @wq: freezable workqueue which can be used by master * drivers if they need to postpone operations that need to take place * in a thread context. Typical examples are Hot Join processing which * requires taking the bus lock in maintenance, which in turn, can only From 21cf9175b3700523d2920dc369c5d09abc4f6b98 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 8 Jun 2026 08:43:06 +0300 Subject: [PATCH 1734/2758] i3c: master: Serialize i3c_set_hotjoin() with the maintenance lock [ Upstream commit 5b130aadc36b6a935b90937dcd67b8ed4ba57831 ] i3c_set_hotjoin() dispatches the controller's enable_hotjoin() or disable_hotjoin() op and updates master->hotjoin under i3c_bus_normaluse_lock(). That lock is a read-side acquisition of bus->lock (down_read()), so it does not exclude concurrent callers. The hotjoin sysfs attribute can be opened multiple times, and writes through different opens are not serialized. Two concurrent writers to "hotjoin" can therefore race in i3c_set_hotjoin(), with the controller op and the master->hotjoin store from one call interleaving with the other. The hardware enable/disable state and the value reported by hotjoin_show() can end up out of sync. Take i3c_bus_maintenance_lock() instead. Toggling Hot Join enable changes bus state and is conceptually a maintenance operation, so the write-side acquisition of bus->lock is the appropriate lock and serializes concurrent callers against each other and against other maintenance operations. Fixes: 317bacf960a48 ("i3c: master: add enable(disable) hot join in sys entry") Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260608054312.10604-3-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/i3c/master.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index ab11e2d79aabb..38ffc87131678 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -649,7 +649,7 @@ static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable) return ret; } - i3c_bus_normaluse_lock(&master->bus); + i3c_bus_maintenance_lock(&master->bus); if (enable) ret = master->ops->enable_hotjoin(master); @@ -659,7 +659,7 @@ static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable) if (!ret) master->hotjoin = enable; - i3c_bus_normaluse_unlock(&master->bus); + i3c_bus_maintenance_unlock(&master->bus); if ((enable && ret) || (!enable && !ret) || master->rpm_ibi_allowed) i3c_master_rpm_put(master); From 08b33dfd457bad48de9c65d2e8e059a81a40b089 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 8 Jun 2026 08:43:07 +0300 Subject: [PATCH 1735/2758] i3c: master: Consolidate Hot-Join DAA work in the core [ Upstream commit 828c6130235db8144f4810b329b61390dc82719b ] Three master drivers (dw-i3c-master, i3c-master-cdns, svc-i3c-master) each carry an essentially identical Hot-Join handler: a struct work_struct embedded in their private state, a work function that just calls i3c_master_do_daa() on the embedded i3c_master_controller, plus matching INIT_WORK()/cancel_work_sync() boilerplate in probe/remove (and shutdown for dw-i3c). The IBI/ISR paths then queue that work onto master->wq, which already lives in the core. Move this pattern into the I3C core: - Add struct work_struct hj_work to struct i3c_master_controller and initialise it in i3c_master_register() with a core-provided handler i3c_master_hj_work_fn() that performs i3c_master_do_daa(). - Cancel the work in i3c_master_unregister() so all controllers get correct teardown ordering against the workqueue for free. - Export i3c_master_queue_hotjoin() as the single entry point drivers call from their Hot-Join IBI handler. Convert the three existing users to the new API: drop their private hj_work fields, work functions, INIT_WORK() and cancel_work_sync() calls, and replace the queue_work(master->wq, &drv->hj_work) call sites with i3c_master_queue_hotjoin(&drv->base). The dw-i3c shutdown path still needs to flush pending Hot-Join work before tearing down the hardware, so it is updated to cancel master->base.hj_work directly. No functional change intended: the work is still queued on the same master->wq, runs the same i3c_master_do_daa(), and is cancelled at controller teardown. Future Hot-Join improvements now only need to be made in one place. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260608054312.10604-4-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Stable-dep-of: 3f79dac3ea1c ("i3c: master: Defer new-device registration out of DAA caller context") Signed-off-by: Sasha Levin --- drivers/i3c/master.c | 21 +++++++++++++++++++++ drivers/i3c/master/dw-i3c-master.c | 15 ++------------- drivers/i3c/master/dw-i3c-master.h | 2 -- drivers/i3c/master/i3c-master-cdns.c | 14 +------------- drivers/i3c/master/svc-i3c-master.c | 14 +------------- include/linux/i3c/master.h | 4 ++++ 6 files changed, 29 insertions(+), 41 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 38ffc87131678..cdb5cb2aa65db 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -633,6 +633,13 @@ static ssize_t i2c_scl_frequency_show(struct device *dev, } static DEVICE_ATTR_RO(i2c_scl_frequency); +static void i3c_master_hj_work_fn(struct work_struct *work) +{ + struct i3c_master_controller *master = container_of(work, typeof(*master), hj_work); + + i3c_master_do_daa(master); +} + static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable) { int ret; @@ -711,6 +718,18 @@ int i3c_master_disable_hotjoin(struct i3c_master_controller *master) } EXPORT_SYMBOL_GPL(i3c_master_disable_hotjoin); +/** + * i3c_master_queue_hotjoin - Queue DAA processing after a Hot-Join event + * @master: I3C master object + * + * Queue the hot-join worker on the master's workqueue. + */ +void i3c_master_queue_hotjoin(struct i3c_master_controller *master) +{ + queue_work(master->wq, &master->hj_work); +} +EXPORT_SYMBOL_GPL(i3c_master_queue_hotjoin); + static ssize_t hotjoin_show(struct device *dev, struct device_attribute *da, char *buf) { struct i3c_bus *i3cbus = dev_to_i3cbus(dev); @@ -3084,6 +3103,7 @@ int i3c_master_register(struct i3c_master_controller *master, ret = -ENOMEM; goto err_put_dev; } + INIT_WORK(&master->hj_work, i3c_master_hj_work_fn); ret = i3c_master_bus_init(master); if (ret) @@ -3146,6 +3166,7 @@ EXPORT_SYMBOL_GPL(i3c_master_register); void i3c_master_unregister(struct i3c_master_controller *master) { i3c_bus_notify(&master->bus, I3C_NOTIFY_BUS_REMOVE); + cancel_work_sync(&master->hj_work); if (master->ops->set_dev_nack_retry) device_remove_file(&master->dev, &dev_attr_dev_nack_retry_count); diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index 655693a2187e5..eb9a13a736848 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -1445,7 +1445,7 @@ static void dw_i3c_master_irq_handle_ibis(struct dw_i3c_master *master) if (IBI_TYPE_SIRQ(reg)) { dw_i3c_master_handle_ibi_sir(master, reg); } else if (IBI_TYPE_HJ(reg)) { - queue_work(master->base.wq, &master->hj_work); + i3c_master_queue_hotjoin(&master->base); } else { len = IBI_QUEUE_STATUS_DATA_LEN(reg); dev_info(&master->base.dev, @@ -1554,14 +1554,6 @@ static const struct dw_i3c_platform_ops dw_i3c_platform_ops_default = { .set_dat_ibi = dw_i3c_platform_set_dat_ibi_nop, }; -static void dw_i3c_hj_work(struct work_struct *work) -{ - struct dw_i3c_master *master = - container_of(work, typeof(*master), hj_work); - - i3c_master_do_daa(&master->base); -} - int dw_i3c_common_probe(struct dw_i3c_master *master, struct platform_device *pdev) { @@ -1636,8 +1628,6 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, if (master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK) pm_runtime_get_noresume(&pdev->dev); - INIT_WORK(&master->hj_work, dw_i3c_hj_work); - device_set_of_node_from_dev(&master->base.i2c.dev, &pdev->dev); ret = i3c_master_register(&master->base, &pdev->dev, &dw_mipi_i3c_ops, false); @@ -1659,7 +1649,6 @@ EXPORT_SYMBOL_GPL(dw_i3c_common_probe); void dw_i3c_common_remove(struct dw_i3c_master *master) { - cancel_work_sync(&master->hj_work); i3c_master_unregister(&master->base); /* Balance pm_runtime_get_noresume() from probe() */ @@ -1804,7 +1793,7 @@ static void dw_i3c_shutdown(struct platform_device *pdev) return; } - cancel_work_sync(&master->hj_work); + cancel_work_sync(&master->base.hj_work); /* Disable interrupts */ writel((u32)~INTR_ALL, master->regs + INTR_STATUS_EN); diff --git a/drivers/i3c/master/dw-i3c-master.h b/drivers/i3c/master/dw-i3c-master.h index c5cb695c16ab8..2f029bd362326 100644 --- a/drivers/i3c/master/dw-i3c-master.h +++ b/drivers/i3c/master/dw-i3c-master.h @@ -68,8 +68,6 @@ struct dw_i3c_master { /* platform-specific data */ const struct dw_i3c_platform_ops *platform_ops; - - struct work_struct hj_work; }; struct dw_i3c_platform_ops { diff --git a/drivers/i3c/master/i3c-master-cdns.c b/drivers/i3c/master/i3c-master-cdns.c index 5cfec6761494d..6d221596ea357 100644 --- a/drivers/i3c/master/i3c-master-cdns.c +++ b/drivers/i3c/master/i3c-master-cdns.c @@ -398,7 +398,6 @@ struct cdns_i3c_data { }; struct cdns_i3c_master { - struct work_struct hj_work; struct i3c_master_controller base; u32 free_rr_slots; unsigned int maxdevs; @@ -1357,7 +1356,7 @@ static void cnds_i3c_master_demux_ibis(struct cdns_i3c_master *master) case IBIR_TYPE_HJ: WARN_ON(IBIR_XFER_BYTES(ibir) || (ibir & IBIR_ERROR)); - queue_work(master->base.wq, &master->hj_work); + i3c_master_queue_hotjoin(&master->base); break; case IBIR_TYPE_MR: @@ -1528,15 +1527,6 @@ static const struct i3c_master_controller_ops cdns_i3c_master_ops = { .recycle_ibi_slot = cdns_i3c_master_recycle_ibi_slot, }; -static void cdns_i3c_master_hj(struct work_struct *work) -{ - struct cdns_i3c_master *master = container_of(work, - struct cdns_i3c_master, - hj_work); - - i3c_master_do_daa(&master->base); -} - static struct cdns_i3c_data cdns_i3c_devdata = { .thd_delay_ns = 10, }; @@ -1584,7 +1574,6 @@ static int cdns_i3c_master_probe(struct platform_device *pdev) spin_lock_init(&master->xferqueue.lock); INIT_LIST_HEAD(&master->xferqueue.list); - INIT_WORK(&master->hj_work, cdns_i3c_master_hj); writel(0xffffffff, master->regs + MST_IDR); writel(0xffffffff, master->regs + SLV_IDR); ret = devm_request_irq(&pdev->dev, irq, cdns_i3c_master_interrupt, 0, @@ -1627,7 +1616,6 @@ static void cdns_i3c_master_remove(struct platform_device *pdev) { struct cdns_i3c_master *master = platform_get_drvdata(pdev); - cancel_work_sync(&master->hj_work); i3c_master_unregister(&master->base); } diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c index 63063741fbd12..893710d05a2e8 100644 --- a/drivers/i3c/master/svc-i3c-master.c +++ b/drivers/i3c/master/svc-i3c-master.c @@ -208,7 +208,6 @@ struct svc_i3c_drvdata { * @free_slots: Bit array of available slots * @addrs: Array containing the dynamic addresses of each attached device * @descs: Array of descriptors, one per attached device - * @hj_work: Hot-join work * @irq: Main interrupt * @num_clks: I3C clock number * @fclk: Fast clock (bus) @@ -235,7 +234,6 @@ struct svc_i3c_master { u32 free_slots; u8 addrs[SVC_I3C_MAX_DEVS]; struct i3c_dev_desc *descs[SVC_I3C_MAX_DEVS]; - struct work_struct hj_work; int irq; int num_clks; struct clk *fclk; @@ -366,14 +364,6 @@ to_svc_i3c_master(struct i3c_master_controller *master) return container_of(master, struct svc_i3c_master, base); } -static void svc_i3c_master_hj_work(struct work_struct *work) -{ - struct svc_i3c_master *master; - - master = container_of(work, struct svc_i3c_master, hj_work); - i3c_master_do_daa(&master->base); -} - static struct i3c_dev_desc * svc_i3c_master_dev_from_addr(struct svc_i3c_master *master, unsigned int ibiaddr) @@ -651,7 +641,7 @@ static void svc_i3c_master_ibi_isr(struct svc_i3c_master *master) case SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN: svc_i3c_master_emit_stop(master); if (is_events_enabled(master, SVC_I3C_EVENT_HOTJOIN)) - queue_work(master->base.wq, &master->hj_work); + i3c_master_queue_hotjoin(&master->base); break; case SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST: svc_i3c_master_emit_stop(master); @@ -2030,7 +2020,6 @@ static int svc_i3c_master_probe(struct platform_device *pdev) if (ret) return dev_err_probe(dev, ret, "can't enable I3C clocks\n"); - INIT_WORK(&master->hj_work, svc_i3c_master_hj_work); mutex_init(&master->lock); ret = devm_request_irq(dev, master->irq, svc_i3c_master_irq_handler, @@ -2089,7 +2078,6 @@ static void svc_i3c_master_remove(struct platform_device *pdev) { struct svc_i3c_master *master = platform_get_drvdata(pdev); - cancel_work_sync(&master->hj_work); i3c_master_unregister(&master->base); pm_runtime_dont_use_autosuspend(&pdev->dev); diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index e6112e5f6608d..eb5c51608bd70 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -520,6 +520,8 @@ struct i3c_master_controller_ops { * in a thread context. Typical examples are Hot Join processing which * requires taking the bus lock in maintenance, which in turn, can only * be done from a sleep-able context + * @hj_work: work item used to run DAA after a Hot-Join event is detected. + * Queued to @wq by i3c_master_queue_hotjoin() * @dev_nack_retry_count: retry count when slave device nack * * A &struct i3c_master_controller has to be registered to the I3C subsystem @@ -543,6 +545,7 @@ struct i3c_master_controller { } boardinfo; struct i3c_bus bus; struct workqueue_struct *wq; + struct work_struct hj_work; unsigned int dev_nack_retry_count; }; @@ -623,6 +626,7 @@ int i3c_master_register(struct i3c_master_controller *master, void i3c_master_unregister(struct i3c_master_controller *master); int i3c_master_enable_hotjoin(struct i3c_master_controller *master); int i3c_master_disable_hotjoin(struct i3c_master_controller *master); +void i3c_master_queue_hotjoin(struct i3c_master_controller *master); /** * i3c_dev_get_master_data() - get master private data attached to an I3C From fef9bdaa0df025a26d1b65be93caaf39d7631700 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 8 Jun 2026 08:43:08 +0300 Subject: [PATCH 1736/2758] i3c: master: Ensure Hot-Join operations are stopped on shutdown [ Upstream commit 8323e783dc3904839e64cb08cfcc7571ef9212c4 ] System shutdown invokes each device's bus shutdown callback to quiesce hardware, but the I3C bus type does not currently implement one. As a result, on shutdown the controller's Hot-Join work and any in-flight i3c_master_do_daa() can keep running (or be newly triggered) while the rest of the system is being torn down. A similar window exists at i3c_master_unregister() time: cancel_work_sync() on hj_work prevents queued work from completing, but does not stop a fresh Hot-Join IBI from re-queueing the worker, nor a concurrent sysfs writer from toggling Hot-Join via i3c_set_hotjoin(). Introduce a single "shutting down" gate in the I3C core, set under the bus maintenance lock so it is observed by any in-progress DAA path before pending work is cancelled. Install an i3c_bus_type shutdown callback that engages this gate for master devices during system shutdown, and use the same gate in i3c_master_unregister() so both paths get identical guarantees. Once the gate is engaged, the Hot-Join worker, i3c_master_do_daa_ext() and i3c_set_hotjoin() all bail out cleanly, so Hot-Join IBIs that race with shutdown become no-ops, direct DAA callers see -ENODEV, and sysfs writers can no longer re-enable Hot-Join through ops->enable_hotjoin() while the controller is going away. No functional change for the steady-state runtime path; the new checks only take effect once the controller has been marked as shutting down. Note, this patch depends on patch "i3c: master: Consolidate Hot-Join DAA work in the core". Fixes: 3a379bbcea0af ("i3c: Add core I3C infrastructure") Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260608054312.10604-5-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Stable-dep-of: 3f79dac3ea1c ("i3c: master: Defer new-device registration out of DAA caller context") Signed-off-by: Sasha Levin --- drivers/i3c/master.c | 52 +++++++++++++++++++++++++++----------- include/linux/i3c/master.h | 2 ++ 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index cdb5cb2aa65db..a59c4b744b369 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -368,14 +368,6 @@ static void i3c_device_remove(struct device *dev) driver->remove(i3cdev); } -const struct bus_type i3c_bus_type = { - .name = "i3c", - .match = i3c_device_match, - .probe = i3c_device_probe, - .remove = i3c_device_remove, -}; -EXPORT_SYMBOL_GPL(i3c_bus_type); - static enum i3c_addr_slot_status i3c_bus_get_addr_slot_status_mask(struct i3c_bus *bus, u16 addr, u32 mask) { @@ -637,7 +629,8 @@ static void i3c_master_hj_work_fn(struct work_struct *work) { struct i3c_master_controller *master = container_of(work, typeof(*master), hj_work); - i3c_master_do_daa(master); + if (!master->shutting_down) + i3c_master_do_daa(master); } static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable) @@ -658,7 +651,9 @@ static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable) i3c_bus_maintenance_lock(&master->bus); - if (enable) + if (master->shutting_down) + ret = -ENODEV; + else if (enable) ret = master->ops->enable_hotjoin(master); else ret = master->ops->disable_hotjoin(master); @@ -837,6 +832,30 @@ static const struct device_type i3c_masterdev_type = { .groups = i3c_masterdev_groups, }; +static void i3c_master_shutdown(struct i3c_master_controller *master) +{ + i3c_bus_maintenance_lock(&master->bus); + master->shutting_down = true; + i3c_bus_maintenance_unlock(&master->bus); + + cancel_work_sync(&master->hj_work); +} + +static void i3c_device_shutdown(struct device *dev) +{ + if (dev->type == &i3c_masterdev_type) + i3c_master_shutdown(dev_to_i3cmaster(dev)); +} + +const struct bus_type i3c_bus_type = { + .name = "i3c", + .match = i3c_device_match, + .probe = i3c_device_probe, + .remove = i3c_device_remove, + .shutdown = i3c_device_shutdown, +}; +EXPORT_SYMBOL_GPL(i3c_bus_type); + static int i3c_bus_set_mode(struct i3c_bus *i3cbus, enum i3c_bus_mode mode, unsigned long max_i2c_scl_rate) { @@ -1846,10 +1865,13 @@ int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa) i3c_bus_maintenance_lock(&master->bus); - if (rstdaa) - rstret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR); - - ret = master->ops->do_daa(master); + if (master->shutting_down) { + ret = -ENODEV; + } else { + if (rstdaa) + rstret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR); + ret = master->ops->do_daa(master); + } i3c_bus_maintenance_unlock(&master->bus); @@ -3166,7 +3188,7 @@ EXPORT_SYMBOL_GPL(i3c_master_register); void i3c_master_unregister(struct i3c_master_controller *master) { i3c_bus_notify(&master->bus, I3C_NOTIFY_BUS_REMOVE); - cancel_work_sync(&master->hj_work); + i3c_master_shutdown(master); if (master->ops->set_dev_nack_retry) device_remove_file(&master->dev, &dev_attr_dev_nack_retry_count); diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index eb5c51608bd70..77e63082b06e4 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -511,6 +511,7 @@ struct i3c_master_controller_ops { * @hotjoin: true if the master support hotjoin * @rpm_allowed: true if Runtime PM allowed * @rpm_ibi_allowed: true if IBI and Hot-Join allowed while runtime suspended + * @shutting_down: set to true when master begins shutdown or unregister * @boardinfo.i3c: list of I3C boardinfo objects * @boardinfo.i2c: list of I2C boardinfo objects * @boardinfo: board-level information attached to devices connected on the bus @@ -539,6 +540,7 @@ struct i3c_master_controller { unsigned int hotjoin: 1; unsigned int rpm_allowed: 1; unsigned int rpm_ibi_allowed: 1; + bool shutting_down; struct { struct list_head i3c; struct list_head i2c; From 50eabb91d2de8ceda6fd8ed624719681794cf3cc Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 8 Jun 2026 08:43:10 +0300 Subject: [PATCH 1737/2758] i3c: master: Defer new-device registration out of DAA caller context [ Upstream commit 3f79dac3ea1c30516fcc791770af034387c7f917 ] Master drivers may invoke i3c_master_do_daa_ext() during resume to re-run Dynamic Address Assignment. As well as assigning addresses to any newly arrived devices, this restores the dynamic address of devices that lost it across system suspend, so it has to run as part of the controller's resume path. A side effect of i3c_master_do_daa_ext() today is that it also registers any newly discovered I3C devices with the driver model inline, via i3c_master_register_new_i3c_devs(). Doing that from the resume path is problematic: a hot-join-capable device may join the bus during this same DAA, and registering it immediately would push driver model work (probing, sysfs, etc.) into the controller's resume context, where the rest of the system is not yet fully resumed and the controller driver is still partway through its own resume sequence. Decouple discovery from registration: add a reg_work work item to struct i3c_master_controller and have i3c_master_do_daa_ext() queue it on master->wq (the freezable workqueue) instead of calling i3c_master_register_new_i3c_devs() directly. The worker performs the registration only when the controller is not shutting_down, and is cancelled alongside hj_work in i3c_master_shutdown(). Because wq is freezable, any newly observed devices end up being registered after the system has finished resuming. i3c_master_register() also routes its initial post-bus-init registration through reg_work, using flush_work() to keep probe-time behavior synchronous. This keeps a single registration code path and ensures the worker is the only writer of desc->dev. Fixes: 3a379bbcea0af ("i3c: Add core I3C infrastructure") Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260608054312.10604-7-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/i3c/master.c | 27 ++++++++++++++++++++------- include/linux/i3c/master.h | 6 ++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index a59c4b744b369..c9685379e8686 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -839,6 +839,7 @@ static void i3c_master_shutdown(struct i3c_master_controller *master) i3c_bus_maintenance_unlock(&master->bus); cancel_work_sync(&master->hj_work); + cancel_work_sync(&master->reg_work); } static void i3c_device_shutdown(struct device *dev) @@ -1838,6 +1839,16 @@ i3c_master_register_new_i3c_devs(struct i3c_master_controller *master) } } +static void i3c_master_reg_work_fn(struct work_struct *work) +{ + struct i3c_master_controller *master = container_of(work, typeof(*master), reg_work); + + i3c_bus_normaluse_lock(&master->bus); + if (!master->shutting_down) + i3c_master_register_new_i3c_devs(master); + i3c_bus_normaluse_unlock(&master->bus); +} + /** * i3c_master_do_daa_ext() - Dynamic Address Assignment (extended version) * @master: controller @@ -1878,9 +1889,7 @@ int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa) if (ret) goto out; - i3c_bus_normaluse_lock(&master->bus); - i3c_master_register_new_i3c_devs(master); - i3c_bus_normaluse_unlock(&master->bus); + queue_work(master->wq, &master->reg_work); out: i3c_master_rpm_put(master); @@ -3126,6 +3135,7 @@ int i3c_master_register(struct i3c_master_controller *master, goto err_put_dev; } INIT_WORK(&master->hj_work, i3c_master_hj_work_fn); + INIT_WORK(&master->reg_work, i3c_master_reg_work_fn); ret = i3c_master_bus_init(master); if (ret) @@ -3151,12 +3161,15 @@ int i3c_master_register(struct i3c_master_controller *master, /* * We're done initializing the bus and the controller, we can now - * register I3C devices discovered during the initial DAA. + * register I3C devices discovered during the initial DAA. Device + * registration is done via reg_work because that keeps a single + * registration code path and ensures the worker is the only writer + * of desc->dev. Flush the work to preserve synchronous probe-time + * behavior. */ master->init_done = true; - i3c_bus_normaluse_lock(&master->bus); - i3c_master_register_new_i3c_devs(master); - i3c_bus_normaluse_unlock(&master->bus); + queue_work(master->wq, &master->reg_work); + flush_work(&master->reg_work); if (master->ops->set_dev_nack_retry) device_create_file(&master->dev, &dev_attr_dev_nack_retry_count); diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index 77e63082b06e4..8cdd7be505d31 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -523,6 +523,11 @@ struct i3c_master_controller_ops { * be done from a sleep-able context * @hj_work: work item used to run DAA after a Hot-Join event is detected. * Queued to @wq by i3c_master_queue_hotjoin() + * @reg_work: work item used to register newly discovered I3C devices with + * the driver model. Queued to @wq by i3c_master_do_daa_ext() so + * that device registration is deferred out of the DAA caller's + * context (notably the resume path), and is skipped if the + * controller is shutting down * @dev_nack_retry_count: retry count when slave device nack * * A &struct i3c_master_controller has to be registered to the I3C subsystem @@ -548,6 +553,7 @@ struct i3c_master_controller { struct i3c_bus bus; struct workqueue_struct *wq; struct work_struct hj_work; + struct work_struct reg_work; unsigned int dev_nack_retry_count; }; From 8f851cab401c28287d536b1347d76f6e219c0db6 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 12 Jun 2026 11:01:01 +0300 Subject: [PATCH 1738/2758] i3c: mipi-i3c-hci: Fix race in i3c_hci_addr_to_dev() [ Upstream commit 650716f23eac488c6696babdc7805f6a6b7427ad ] i3c_hci_addr_to_dev() walks bus->devs.i3c, which is protected by bus.lock (rwsem). However, it is invoked from the MIPI I3C HCI IRQ handler, which cannot take bus.lock. This allows concurrent device addition/removal in the I3C core to modify the list while it is being traversed, potentially leading to use-after-free or crashes. Remove the dependency on the bus device list and introduce a dedicated lookup table. Add an ibi_devs[] array indexed by DAT entry, maintained under hci->lock. Update the array when IBIs are enabled or disabled, so that it always reflects the set of devices allowed to generate IBIs. Also update when IBIs are freed, to cover the corner case when an IBI is freed without first being disabled (e.g. oldedev in i3c_master_add_i3c_dev_locked()). Move i3c_hci_addr_to_dev() into core.c, reimplement it using the new array, and add a lockdep assertion to enforce that hci->lock is held by callers. Demote a message in PIO and DMA IBI handling, from an error to a debug message, because there is a race window when the condition can arise normally. Fixes: 9ad9a52cce282 ("i3c/master: introduce the mipi-i3c-hci driver") Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260612080107.11606-2-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/i3c/master/mipi-i3c-hci/core.c | 37 ++++++++++++++++++++++++-- drivers/i3c/master/mipi-i3c-hci/dma.c | 7 +++-- drivers/i3c/master/mipi-i3c-hci/hci.h | 1 + drivers/i3c/master/mipi-i3c-hci/ibi.h | 13 +-------- drivers/i3c/master/mipi-i3c-hci/pio.c | 7 +++-- 5 files changed, 47 insertions(+), 18 deletions(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c index afb0764b5e1fb..388a42f4d2ec7 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -22,6 +22,7 @@ #include "ext_caps.h" #include "cmd.h" #include "dat.h" +#include "ibi.h" /* * Host Controller Capabilities and Operation Registers @@ -124,6 +125,7 @@ static void i3c_hci_set_master_dyn_addr(struct i3c_hci *hci) static int i3c_hci_bus_init(struct i3c_master_controller *m) { struct i3c_hci *hci = to_i3c_hci(m); + struct device *dev = hci->master.dev.parent; struct i3c_device_info info; int ret; @@ -144,6 +146,10 @@ static int i3c_hci_bus_init(struct i3c_master_controller *m) if (ret) return ret; + hci->ibi_devs = devm_kcalloc(dev, hci->DAT_entries, sizeof(*hci->ibi_devs), GFP_KERNEL); + if (!hci->ibi_devs) + return -ENOMEM; + ret = hci->io->init(hci); if (ret) return ret; @@ -556,14 +562,40 @@ static int i3c_hci_request_ibi(struct i3c_dev_desc *dev, return hci->io->request_ibi(hci, dev, req); } +static void __i3c_hci_disable_ibi(struct i3c_hci *hci, struct i3c_dev_desc *dev) +{ + struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev); + + mipi_i3c_hci_dat_v1.set_flags(hci, dev_data->dat_idx, DAT_0_SIR_REJECT, 0); + scoped_guard(spinlock_irqsave, &hci->lock) + hci->ibi_devs[dev_data->dat_idx] = NULL; +} + static void i3c_hci_free_ibi(struct i3c_dev_desc *dev) { struct i3c_master_controller *m = i3c_dev_get_master(dev); struct i3c_hci *hci = to_i3c_hci(m); + /* Must ensure the IBI has been disabled */ + __i3c_hci_disable_ibi(hci, dev); hci->io->free_ibi(hci, dev); } +struct i3c_dev_desc *i3c_hci_addr_to_dev(struct i3c_hci *hci, unsigned int addr) +{ + int dat_idx; + + lockdep_assert_held(&hci->lock); + + for (dat_idx = 0; dat_idx < hci->DAT_entries; dat_idx++) { + struct i3c_dev_desc *dev = hci->ibi_devs[dat_idx]; + + if (dev && dev->info.dyn_addr == addr) + return dev; + } + return NULL; +} + static int i3c_hci_enable_ibi(struct i3c_dev_desc *dev) { struct i3c_master_controller *m = i3c_dev_get_master(dev); @@ -571,6 +603,8 @@ static int i3c_hci_enable_ibi(struct i3c_dev_desc *dev) struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev); mipi_i3c_hci_dat_v1.clear_flags(hci, dev_data->dat_idx, DAT_0_SIR_REJECT, 0); + scoped_guard(spinlock_irqsave, &hci->lock) + hci->ibi_devs[dev_data->dat_idx] = dev; return i3c_master_enec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR); } @@ -578,9 +612,8 @@ static int i3c_hci_disable_ibi(struct i3c_dev_desc *dev) { struct i3c_master_controller *m = i3c_dev_get_master(dev); struct i3c_hci *hci = to_i3c_hci(m); - struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev); - mipi_i3c_hci_dat_v1.set_flags(hci, dev_data->dat_idx, DAT_0_SIR_REJECT, 0); + __i3c_hci_disable_ibi(hci, dev); return i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR); } diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c index bdffdd8b89236..8517d2c14e8bf 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -783,8 +783,11 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh) /* determine who this is for */ dev = i3c_hci_addr_to_dev(hci, ibi_addr); if (!dev) { - dev_err(&hci->master.dev, - "IBI for unknown device %#x\n", ibi_addr); + /* + * Either an IBI received just before IBI's were disabled, or + * the controller is broken. Assume the former. + */ + dev_dbg(&hci->master.dev, "IBI when not enabled at address %#x\n", ibi_addr); goto done; } diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h index f17f43494c1b5..b623e4c22c6c9 100644 --- a/drivers/i3c/master/mipi-i3c-hci/hci.h +++ b/drivers/i3c/master/mipi-i3c-hci/hci.h @@ -60,6 +60,7 @@ struct i3c_hci { unsigned int DAT_entry_size; void *DAT_data; struct dat_words *DAT; + struct i3c_dev_desc **ibi_devs; unsigned int DCT_entries; unsigned int DCT_entry_size; u8 version_major; diff --git a/drivers/i3c/master/mipi-i3c-hci/ibi.h b/drivers/i3c/master/mipi-i3c-hci/ibi.h index e1f98e264da0e..073ca67b7d04d 100644 --- a/drivers/i3c/master/mipi-i3c-hci/ibi.h +++ b/drivers/i3c/master/mipi-i3c-hci/ibi.h @@ -26,17 +26,6 @@ #define IBI_DATA_LENGTH GENMASK(7, 0) /* handy helpers */ -static inline struct i3c_dev_desc * -i3c_hci_addr_to_dev(struct i3c_hci *hci, unsigned int addr) -{ - struct i3c_bus *bus = i3c_master_get_bus(&hci->master); - struct i3c_dev_desc *dev; - - i3c_bus_for_each_i3cdev(bus, dev) { - if (dev->info.dyn_addr == addr) - return dev; - } - return NULL; -} +struct i3c_dev_desc *i3c_hci_addr_to_dev(struct i3c_hci *hci, unsigned int addr); #endif diff --git a/drivers/i3c/master/mipi-i3c-hci/pio.c b/drivers/i3c/master/mipi-i3c-hci/pio.c index 8f48a81e65abe..c99c85693214c 100644 --- a/drivers/i3c/master/mipi-i3c-hci/pio.c +++ b/drivers/i3c/master/mipi-i3c-hci/pio.c @@ -863,8 +863,11 @@ static bool hci_pio_prep_new_ibi(struct i3c_hci *hci, struct hci_pio_data *pio) dev = i3c_hci_addr_to_dev(hci, ibi->addr); if (!dev) { - dev_err(&hci->master.dev, - "IBI for unknown device %#x\n", ibi->addr); + /* + * Either an IBI received just before IBI's were disabled, or + * the controller is broken. Assume the former. + */ + dev_dbg(&hci->master.dev, "IBI when not enabled at address %#x\n", ibi->addr); return true; } From 4b0c345217476ebaae8f7a13c7830d87bc5136ca Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 12 Jun 2026 11:01:03 +0300 Subject: [PATCH 1739/2758] i3c: master: Prevent reuse of dynamic address on device add failure [ Upstream commit b3ba8383da4d0cff15810e32ea785eceb0a80813 ] i3c_master_add_i3c_dev_locked() is called after a device has already been assigned a dynamic address. If the function fails, the address remains marked as free and may be reallocated to another device, leading to address conflicts on the bus. Ensure the address is not marked as free on failure, by updating the address slot state to prevent the address from being re-used. Emit an error message to inform of the failure. Opportunistically remove the !master check because it is impossible. Note, directly resetting the device's dynamic address is no longer an option, since Direct RSTDAA was deprecated from I3C starting from version 1.1 and v1.1 (or later) target devices are meant to NACK it. Fixes: 3a379bbcea0af ("i3c: Add core I3C infrastructure") Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260612080107.11606-4-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/i3c/master.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index c9685379e8686..7952a38ebc378 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -2324,12 +2324,11 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, bool enable_ibi = false; int ret; - if (!master) - return -EINVAL; - newdev = i3c_master_alloc_i3c_dev(master, &info); - if (IS_ERR(newdev)) - return PTR_ERR(newdev); + if (IS_ERR(newdev)) { + ret = PTR_ERR(newdev); + goto err_prevent_addr_reuse; + } ret = i3c_master_attach_i3c_dev(master, newdev); if (ret) @@ -2451,6 +2450,16 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, err_free_dev: i3c_master_free_i3c_dev(newdev); +err_prevent_addr_reuse: + /* + * Although the device has not been added, the address has been + * assigned. Prevent the address from being used again. + */ + if (i3c_bus_get_addr_slot_status(&master->bus, addr) == I3C_ADDR_SLOT_FREE) + i3c_bus_set_addr_slot_status(&master->bus, addr, I3C_ADDR_SLOT_I3C_DEV); + + dev_err(&master->dev, "Failed to add I3C device at address %u, error %d\n", addr, ret); + return ret; } EXPORT_SYMBOL_GPL(i3c_master_add_i3c_dev_locked); From 30521e7ec4d8309be4a1cb49a4d10d5ba5d83ac0 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sun, 14 Jun 2026 16:16:59 -0700 Subject: [PATCH 1740/2758] apparmor: fix label can not be immediately before a declaration [ Upstream commit 1ed40bd525c00d22af666016af9aef7167f8085f ] Fix error reported by kernel test robot security/apparmor/policy.c:1381:2: error: a label can only be part of a statement and a declaration is not a statement All errors (new ones prefixed by >>): security/apparmor/policy.c: In function 'aa_replace_profiles': >> security/apparmor/policy.c:1381:2: error: a label can only be part of a statement and a declaration is not a statement ssize_t udata_sz = udata->size; ^~~~~ Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202606150525.npax8WiH-lkp@intel.com/ Fixes: 7b42f95813dc9 ("apparmor: fix potential UAF in aa_replace_profiles") Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/policy.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 73101ddf0b871..567f6a1c2d659 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1390,9 +1390,10 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, mutex_unlock(&ns->lock); out: + aa_put_ns(ns); + ssize_t udata_sz = udata->size; - aa_put_ns(ns); aa_put_profile_loaddata(udata); kfree(ns_name); From f55b1ff899383a88a11bd3f1d2308343c8bfa538 Mon Sep 17 00:00:00 2001 From: Karol Wachowski Date: Thu, 11 Jun 2026 07:51:40 +0200 Subject: [PATCH 1741/2758] accel/ivpu: fix HWS command queue leak on registration failure [ Upstream commit e7ab91e2bf01b024691d6ce488546533943e7a6b ] A command queue is considered valid and usable by the driver only when it has a doorbell ID assigned (db_id != 0), meaning both the FW cmdq creation and doorbell registration completed successfully. However, when either ivpu_register_db() or set_context_sched_properties() fails after ivpu_hws_cmdq_init() has already created the cmdq in FW, the command queue is left registered in FW while the driver treats it as uninitialized (db_id remains 0). On the next submission attempt the driver tries to register the same cmdq again, which fails because FW already has an entry for it. Fix by calling ivpu_jsm_hws_destroy_cmdq() on error paths to properly unwind FW state and allow subsequent registration attempts to succeed. Fixes: 465a3914b254 ("accel/ivpu: Add API for command queue create/destroy/submit") Reviewed-by: Andrzej Kacprowski Signed-off-by: Karol Wachowski Link: https://patch.msgid.link/20260611055140.948684-1-karol.wachowski@linux.intel.com Signed-off-by: Sasha Levin --- drivers/accel/ivpu/ivpu_job.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/accel/ivpu/ivpu_job.c b/drivers/accel/ivpu/ivpu_job.c index 521931d1f7fca..b24f31a8b5672 100644 --- a/drivers/accel/ivpu/ivpu_job.c +++ b/drivers/accel/ivpu/ivpu_job.c @@ -208,9 +208,9 @@ static int ivpu_hws_cmdq_init(struct ivpu_file_priv *file_priv, struct ivpu_cmdq ret = ivpu_jsm_hws_set_context_sched_properties(vdev, file_priv->ctx.id, cmdq->id, priority); if (ret) - return ret; + ivpu_jsm_hws_destroy_cmdq(vdev, file_priv->ctx.id, cmdq->id); - return 0; + return ret; } static int ivpu_register_db(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq) @@ -281,10 +281,10 @@ static int ivpu_cmdq_register(struct ivpu_file_priv *file_priv, struct ivpu_cmdq } ret = ivpu_register_db(file_priv, cmdq); - if (ret) - return ret; + if (ret && vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW) + ivpu_jsm_hws_destroy_cmdq(vdev, file_priv->ctx.id, cmdq->id); - return 0; + return ret; } static int ivpu_cmdq_unregister(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq) From abbdd94e6a1065cd00e282836c8dd6cb810e0f86 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 15 Jun 2026 13:58:50 +0800 Subject: [PATCH 1742/2758] sparc: led: avoid trimming a newline from empty writes [ Upstream commit 7eb475e8a738ee6fd1260aa59ddccb610fdd4300 ] led_proc_write() duplicates up to LED_MAX_LENGTH bytes with memdup_user_nul() and then unconditionally inspects buf[count - 1] to strip a trailing newline. A zero-length write therefore reads one byte before the duplicated buffer. The previous version rejected empty writes, but empty input already falls through to the existing default case and turns the LED off like any other unrecognized string. Preserve that behavior and only skip the newline trim when there is no input byte to inspect. Fixes: ee1858d3122d ("[SPARC]: Add sun4m LED driver.") Suggested-by: Andreas Larsson Signed-off-by: Pengpeng Hou Signed-off-by: Andreas Larsson Signed-off-by: Sasha Levin --- arch/sparc/kernel/led.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/sparc/kernel/led.c b/arch/sparc/kernel/led.c index f4fb82b019bb9..9b53ac1fe533d 100644 --- a/arch/sparc/kernel/led.c +++ b/arch/sparc/kernel/led.c @@ -78,7 +78,7 @@ static ssize_t led_proc_write(struct file *file, const char __user *buffer, return PTR_ERR(buf); /* work around \n when echo'ing into proc */ - if (buf[count - 1] == '\n') + if (count > 0 && buf[count - 1] == '\n') buf[count - 1] = '\0'; /* before we change anything we want to stop any running timers, From a06241a08631f2247a8af0da90d75b55ca1fe4ac Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 11 Jun 2026 09:41:18 -0700 Subject: [PATCH 1743/2758] perf maps: Add maps__mutate_mapping [ Upstream commit 75a4888b7029a1f98613aef91f517b2ee1f03d43 ] During kernel ELF symbol parsing (dso__process_kernel_symbol), proc kallsyms image loading (dso__load_kernel_sym, dso__load_guest_kernel_sym), and dynamic kernel memory map alignment updates (machine__update_kernel_mmap), the loader directly modifies live virtual address boundary keys fields on map objects. If these boundaries are mutated while the map pointer actively resides inside the parent maps cache array list (kmaps) outside of any lock closure, an unsafe concurrent window is exposed where parallel worker lookup threads (e.g., inside perf top) can mistakenly assume the cache remains sorted based on stale parameters, executing binary search queries (bsearch) across an unsorted range and triggering lookup failures. Fix this by introducing maps__mutate_mapping() that explicitly acquires the parent maps write semaphore lock, executes an incoming mutation callback block to perform the field updates under lock protection, and invalidates the sorted tracking flags prior to releasing the write lock. This guarantees synchronization invariants, closing the concurrent lookup race window. The adjacent module alignment pass inside machine__create_kernel_maps() is safely preserved as a high-performance lockless pass, as its invocation lifecycle bounds remain strictly single-threaded by contract during session initialization construction. To safely support this unconditional down_write write lock mutator without recursive read-to-write self-deadlock upgrades during lazy symbol loading, we introduce a public maps__load_maps() API. It copies map pointers under a brief read lock and force-loads all modules locklessly outside the lock. Callers (such as perf inject) must pre-load all kernel symbol maps up front at startup using maps__load_maps(), completely bypassing dynamic runtime mutations. Fixes: 39b12f781271 ("perf tools: Make it possible to read object code from vmlinux") Assisted-by: Antigravity:gemini-3.1-pro Signed-off-by: Ian Rogers Tested-by: James Clark Cc: Adrian Hunter Cc: Gabriel Marin Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/machine.c | 32 +++++--- tools/perf/util/maps.c | 148 ++++++++++++++++++++++++++++------- tools/perf/util/maps.h | 3 + tools/perf/util/symbol-elf.c | 41 ++++++---- tools/perf/util/symbol.c | 17 +++- 5 files changed, 183 insertions(+), 58 deletions(-) diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index e76f8c86e62a6..ea918f75e3ad1 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -1522,22 +1522,30 @@ static void machine__set_kernel_mmap(struct machine *machine, map__set_end(machine->vmlinux_map, ~0ULL); } -static int machine__update_kernel_mmap(struct machine *machine, - u64 start, u64 end) +struct kernel_mmap_mutation_ctx { + u64 start; + u64 end; +}; + +static int kernel_mmap_mutate_cb(struct map *map, void *data) { - struct map *orig, *updated; - int err; + struct kernel_mmap_mutation_ctx *ctx = data; - orig = machine->vmlinux_map; - updated = map__get(orig); + map__set_start(map, ctx->start); + map__set_end(map, ctx->end); + if (ctx->start == 0 && ctx->end == 0) + map__set_end(map, ~0ULL); + return 0; +} - machine->vmlinux_map = updated; - maps__remove(machine__kernel_maps(machine), orig); - machine__set_kernel_mmap(machine, start, end); - err = maps__insert(machine__kernel_maps(machine), updated); - map__put(orig); +static int machine__update_kernel_mmap(struct machine *machine, + u64 start, u64 end) +{ + struct kernel_mmap_mutation_ctx ctx = { .start = start, .end = end }; - return err; + return maps__mutate_mapping(machine__kernel_maps(machine), + machine->vmlinux_map, + kernel_mmap_mutate_cb, &ctx); } int machine__create_kernel_maps(struct machine *machine) diff --git a/tools/perf/util/maps.c b/tools/perf/util/maps.c index 81a97ac34077e..6cdafded61bcb 100644 --- a/tools/perf/util/maps.c +++ b/tools/perf/util/maps.c @@ -575,6 +575,48 @@ void maps__remove(struct maps *maps, struct map *map) #endif } +/** + * maps__mutate_mapping - Apply write-protected mutations to a map. + * @maps: The maps collection containing the map. + * @map: The map to mutate. + * @mutate_cb: Callback function that performs the actual mutations. + * @data: Private data passed to the callback. + * + * This acquires the write lock on the maps semaphore to safely protect + * concurrent readers from seeing partially mutated or unsorted map boundaries. + * + * WARNING: Acquiring down_write() here can trigger a recursive self-deadlock if + * the caller already holds the read lock (e.g., during maps__for_each_map() or + * maps__find() iteration paths that trigger lazy symbol loading). To completely + * avoid this deadlock, all kernel/module maps must be pre-loaded up-front (via + * maps__load_maps()) under a clean, single-threaded context before entering + * multi-threaded event processing loops. + */ +int maps__mutate_mapping(struct maps *maps, struct map *map, + int (*mutate_cb)(struct map *map, void *data), void *data) +{ + int err = 0; + + if (maps) { + down_write(maps__lock(maps)); + + err = mutate_cb(map, data); + + RC_CHK_ACCESS(maps)->maps_by_address_sorted = false; + RC_CHK_ACCESS(maps)->maps_by_name_sorted = false; + + up_write(maps__lock(maps)); + +#ifdef HAVE_LIBDW_SUPPORT + libdw__invalidate_dwfl(maps, maps__libdw_addr_space_dwfl(maps)); +#endif + } else { + err = mutate_cb(map, data); + } + + return err; +} + bool maps__empty(struct maps *maps) { bool res; @@ -625,6 +667,41 @@ int maps__for_each_map(struct maps *maps, int (*cb)(struct map *map, void *data) return ret; } +int maps__load_maps(struct maps *maps) +{ + struct map **maps_copy; + unsigned int nr_maps; + int err = 0; + + if (!maps) + return 0; + + down_read(maps__lock(maps)); + nr_maps = maps__nr_maps(maps); + if (nr_maps == 0) { + up_read(maps__lock(maps)); + return 0; + } + maps_copy = calloc(nr_maps, sizeof(*maps_copy)); + if (!maps_copy) { + up_read(maps__lock(maps)); + return -ENOMEM; + } + for (unsigned int i = 0; i < nr_maps; i++) + maps_copy[i] = map__get(maps__maps_by_address(maps)[i]); + up_read(maps__lock(maps)); + + for (unsigned int i = 0; i < nr_maps; i++) { + if (map__load(maps_copy[i]) < 0) { + pr_warning("Failed to load map %s\n", dso__name(map__dso(maps_copy[i]))); + err = -1; + } + map__put(maps_copy[i]); + } + free(maps_copy); + return err; +} + void maps__remove_maps(struct maps *maps, bool (*cb)(struct map *map, void *data), void *data) { struct map **maps_by_address; @@ -667,40 +744,57 @@ struct symbol *maps__find_symbol(struct maps *maps, u64 addr, struct map **mapp) return result; } -struct maps__find_symbol_by_name_args { - struct map **mapp; - const char *name; - struct symbol *sym; -}; - -static int maps__find_symbol_by_name_cb(struct map *map, void *data) +struct symbol *maps__find_symbol_by_name(struct maps *maps, const char *name, struct map **mapp) { - struct maps__find_symbol_by_name_args *args = data; + struct map **maps_copy; + unsigned int nr_maps; + struct symbol *sym = NULL; - args->sym = map__find_symbol_by_name(map, args->name); - if (!args->sym) - return 0; + if (!maps) + return NULL; - if (!map__contains_symbol(map, args->sym)) { - args->sym = NULL; - return 0; + /* + * First, ensure all maps are loaded. We pre-load them outside of any + * read-to-write locks to avoid deadlocks. Even if some fail, we proceed. + */ + maps__load_maps(maps); + + /* + * Create a local snapshot of the maps while holding the read lock. + * This prevents deadlocking if iteration triggers further map insertions. + */ + down_read(maps__lock(maps)); + nr_maps = maps__nr_maps(maps); + maps_copy = calloc(nr_maps, sizeof(*maps_copy)); + if (maps_copy) { + for (unsigned int i = 0; i < nr_maps; i++) { + struct map *map = maps__maps_by_address(maps)[i]; + + maps_copy[i] = map__get(map); + } } + up_read(maps__lock(maps)); - if (args->mapp != NULL) - *args->mapp = map__get(map); - return 1; -} + if (!maps_copy) + return NULL; -struct symbol *maps__find_symbol_by_name(struct maps *maps, const char *name, struct map **mapp) -{ - struct maps__find_symbol_by_name_args args = { - .mapp = mapp, - .name = name, - .sym = NULL, - }; + for (unsigned int i = 0; i < nr_maps; i++) { + struct map *map = maps_copy[i]; + + sym = map__find_symbol_by_name(map, name); + if (sym && map__contains_symbol(map, sym)) { + if (mapp) + *mapp = map__get(map); + break; + } + sym = NULL; + } + + for (unsigned int i = 0; i < nr_maps; i++) + map__put(maps_copy[i]); - maps__for_each_map(maps, maps__find_symbol_by_name_cb, &args); - return args.sym; + free(maps_copy); + return sym; } int maps__find_ams(struct maps *maps, struct addr_map_symbol *ams) diff --git a/tools/perf/util/maps.h b/tools/perf/util/maps.h index 20c52084ba9e6..b3f00f5c32fcf 100644 --- a/tools/perf/util/maps.h +++ b/tools/perf/util/maps.h @@ -59,8 +59,11 @@ void maps__set_libdw_addr_space_dwfl(struct maps *maps, void *dwfl); size_t maps__fprintf(struct maps *maps, FILE *fp); +int maps__load_maps(struct maps *maps); int maps__insert(struct maps *maps, struct map *map); void maps__remove(struct maps *maps, struct map *map); +int maps__mutate_mapping(struct maps *maps, struct map *map, + int (*mutate_cb)(struct map *map, void *data), void *data); struct map *maps__find(struct maps *maps, u64 addr); struct symbol *maps__find_symbol(struct maps *maps, u64 addr, struct map **mapp); diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index cd7833e364020..41ef08b490183 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -1372,6 +1372,24 @@ static u64 ref_reloc(struct kmap *kmap) void __weak arch__sym_update(struct symbol *s __maybe_unused, GElf_Sym *sym __maybe_unused) { } +struct remap_kernel_ctx { + u64 sh_addr; + u64 sh_size; + u64 sh_offset; + struct kmap *kmap; +}; + +static int remap_kernel_cb(struct map *map, void *data) +{ + struct remap_kernel_ctx *ctx = data; + + map__set_start(map, ctx->sh_addr + ref_reloc(ctx->kmap)); + map__set_end(map, map__start(map) + ctx->sh_size); + map__set_pgoff(map, ctx->sh_offset); + map__set_mapping_type(map, MAPPING_TYPE__DSO); + return 0; +} + static int dso__process_kernel_symbol(struct dso *dso, struct map *map, GElf_Sym *sym, GElf_Shdr *shdr, struct maps *kmaps, struct kmap *kmap, @@ -1402,22 +1420,15 @@ static int dso__process_kernel_symbol(struct dso *dso, struct map *map, * map to the kernel dso. */ if (*remap_kernel && dso__kernel(dso) && !kmodule) { + struct remap_kernel_ctx ctx = { + .sh_addr = shdr->sh_addr, + .sh_size = shdr->sh_size, + .sh_offset = shdr->sh_offset, + .kmap = kmap + }; + *remap_kernel = false; - map__set_start(map, shdr->sh_addr + ref_reloc(kmap)); - map__set_end(map, map__start(map) + shdr->sh_size); - map__set_pgoff(map, shdr->sh_offset); - map__set_mapping_type(map, MAPPING_TYPE__DSO); - /* Ensure maps are correctly ordered */ - if (kmaps) { - int err; - struct map *tmp = map__get(map); - - maps__remove(kmaps, map); - err = maps__insert(kmaps, map); - map__put(tmp); - if (err) - return err; - } + maps__mutate_mapping(kmaps, map, remap_kernel_cb, &ctx); } /* diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index fcaeeddbbb6b9..09b93e844887d 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -48,6 +48,13 @@ #include #include +static int map_fixup_cb(struct map *map, void *data __maybe_unused) +{ + map__fixup_start(map); + map__fixup_end(map); + return 0; +} + static int dso__load_kernel_sym(struct dso *dso, struct map *map); static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map); static bool symbol__is_idle(const char *name); @@ -2121,10 +2128,11 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map) free(kallsyms_allocated_filename); if (err > 0 && !dso__is_kcore(dso)) { + struct maps *kmaps = map__kmaps(map); + dso__set_binary_type(dso, DSO_BINARY_TYPE__KALLSYMS); dso__set_long_name(dso, DSO__NAME_KALLSYMS, false); - map__fixup_start(map); - map__fixup_end(map); + maps__mutate_mapping(kmaps, map, map_fixup_cb, NULL); } return err; @@ -2164,10 +2172,11 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map) if (err > 0) pr_debug("Using %s for symbols\n", kallsyms_filename); if (err > 0 && !dso__is_kcore(dso)) { + struct maps *kmaps = map__kmaps(map); + dso__set_binary_type(dso, DSO_BINARY_TYPE__GUEST_KALLSYMS); dso__set_long_name(dso, machine->mmap_name, false); - map__fixup_start(map); - map__fixup_end(map); + maps__mutate_mapping(kmaps, map, map_fixup_cb, NULL); } return err; From 1ce03f1d990ec5b4adffebccf45715bb4b07b6b1 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 19:28:43 -0300 Subject: [PATCH 1744/2758] perf symbols: Fix bswap copy-paste error for 32-bit ELF p_filesz [ Upstream commit 081b387c7397498c583b1ba7c2fdaf4c6da6b538 ] filename__read_build_id() byte-swaps 32-bit ELF program headers on cross-endian files, but line 178 passes p_offset to bswap_32() instead of p_filesz: hdrs.phdr32[i].p_filesz = bswap_32(hdrs.phdr32[i].p_offset); This clobbers p_filesz with the already-swapped p_offset value. The 64-bit path on line 182 is correct and swaps p_filesz from p_filesz. The consequence is that the PT_NOTE segment read uses the wrong size, which can cause either a short read (missing the build-id) or an oversized read (reading past the segment into adjacent data). Fix by swapping the correct field. Reported-by: sashiko-bot Fixes: fef8f648bb47726d ("perf symbol: Fix use-after-free in filename__read_build_id") Reviewed-by: Ian Rogers Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/symbol-minimal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index 091071d06416e..f4b0a711a62cf 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c @@ -175,7 +175,7 @@ int filename__read_build_id(const char *filename, struct build_id *bid) if (elf32) { hdrs.phdr32[i].p_type = bswap_32(hdrs.phdr32[i].p_type); hdrs.phdr32[i].p_offset = bswap_32(hdrs.phdr32[i].p_offset); - hdrs.phdr32[i].p_filesz = bswap_32(hdrs.phdr32[i].p_offset); + hdrs.phdr32[i].p_filesz = bswap_32(hdrs.phdr32[i].p_filesz); } else { hdrs.phdr64[i].p_type = bswap_32(hdrs.phdr64[i].p_type); hdrs.phdr64[i].p_offset = bswap_64(hdrs.phdr64[i].p_offset); From 6172d92a7f1542561964e2ec87cc7cb5a7c0bcbe Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 19:29:34 -0300 Subject: [PATCH 1745/2758] perf symbols: Validate p_filesz before use in filename__read_build_id() [ Upstream commit 2a3716544359d4312c81b0fa909a13301186da17 ] filename__read_build_id() stores ELF p_filesz in a ssize_t variable. A crafted 32-bit ELF with p_filesz = 0xFFFFFFFF produces ssize_t value -1. The comparison `p_filesz > buf_size` evaluates false because signed -1 is less than any non-negative buf_size, so the realloc is skipped and buf remains NULL. The subsequent read(fd, NULL, -1) returns -1, which equals p_filesz, passing the error check. read_build_id() then dereferences the NULL buffer. Add an explicit check for p_filesz <= 0 before using the value, catching both zero-length and sign-wrapped negative sizes from crafted ELF files. Reported-by: sashiko-bot Fixes: ba0b7081f7a521d7 ("perf symbol-minimal: Fix ehdr reading in filename__read_build_id") Reviewed-by: Ian Rogers Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/symbol-minimal.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index f4b0a711a62cf..0a71d14639527 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c @@ -186,6 +186,9 @@ int filename__read_build_id(const char *filename, struct build_id *bid) continue; p_filesz = elf32 ? hdrs.phdr32[i].p_filesz : hdrs.phdr64[i].p_filesz; + /* ssize_t can go negative with crafted ELF p_filesz values */ + if (p_filesz <= 0) + continue; if (p_filesz > buf_size) { void *tmp; From df77307da9da7e584966740966df606bd95f9386 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 19:32:22 -0300 Subject: [PATCH 1746/2758] perf symbols: Break infinite loop on zero-filled notes in sysfs__read_build_id() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 063c647b24f640657d6d9e2e90d620ea3ee19ae6 ] sysfs__read_build_id() iterates ELF note headers from sysfs files in a while(1) loop. If the file contains a zero-filled note header (both n_namesz and n_descsz are 0), the code computes n = namesz + descsz = 0 and calls read(fd, bf, 0). read() with count 0 returns 0, which matches the expected (ssize_t)n value, so the error check passes and the loop repeats — reading the same zero bytes and spinning forever. This can happen with corrupted or zero-padded sysfs pseudo-files. Add a check for n == 0 before the read, since no valid ELF note has both name and description of zero length. Reported-by: sashiko-bot Fixes: f1617b40596cb341 ("perf symbols: Record the build_ids of kernel modules too") Reviewed-by: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/symbol-elf.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 41ef08b490183..2007f8f003e3b 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -994,6 +994,9 @@ int sysfs__read_build_id(const char *filename, struct build_id *bid) } else { n = namesz + descsz; } + /* no valid note has both namesz and descsz zero */ + if (n == 0) + break; if (read(fd, bf, n) != (ssize_t)n) break; } From c5dcbd5cf0079a7a1359516b832923446b79423e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 19:33:48 -0300 Subject: [PATCH 1747/2758] perf dso: Fix heap overflow in dso__get_filename() on decompressed path [ Upstream commit f973e52a99776fcc473488984828d1fce56d5382 ] dso__get_filename() allocates name with malloc(PATH_MAX), but the dso__filename_with_chroot() path replaces name with an asprintf'd exact-size string (e.g. 8 bytes for "/a/b.ko"). When the DSO needs decompression, dso__decompress_kmodule_path() writes the temp path ("/tmp/perf-kmod-XXXXXX", 22 bytes) into newpath, and strcpy(name, newpath) overflows the smaller allocation. Replace the strcpy with strdup(newpath) + free(name) so the buffer is always correctly sized for its content. Reported-by: sashiko-bot Fixes: 1d6b3c9ba756a513 ("perf tools: Decompress kernel module when reading DSO data") Reviewed-by: Ian Rogers Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/dso.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 7200722464e71..e896e6da81e96 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -603,8 +603,15 @@ static char *dso__get_filename(struct dso *dso, const char *root_dir, /* empty pathname means file wasn't actually compressed */ if (newpath[0] != '\0') { + char *tmp = strdup(newpath); + + if (!tmp) { + unlink(newpath); + goto out; + } + free(name); + name = tmp; *decomp = true; - strcpy(name, newpath); } } return name; From 2367ebcd0d4bea2694ac7f6cb5efb80adc265233 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 20:16:02 -0300 Subject: [PATCH 1748/2758] perf dso: Set error code when open() fails on uncompressed fallback path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit d2c6069d68ee9d53b05fe38bc2049cc4286fbb16 ] filename__decompress() has an early return for files that are not actually compressed, where it calls open() directly. When open() fails, the function returns -1 but never sets *err. The caller chain (decompress_kmodule → dso__decompress_kmodule_path → dso__get_filename) then reads *dso__load_errno(dso) to set errno, but that field was never populated, so errno gets a stale or zero value. With errno=0, __open_dso() computes fd = -errno = 0, which is non- negative, so callers treat fd 0 (stdin) as a valid DSO file descriptor. Set *err = errno when open() fails on the uncompressed path, matching the error handling on the compressed path at line 354. Reported-by: sashiko-bot Fixes: 8b42b7e5e8b5692b ("perf tools: Add is_compressed callback to compressions array") Cc: Jiri Olsa Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/dso.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index e896e6da81e96..233c313bf3311 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -344,9 +344,12 @@ int filename__decompress(const char *name, char *pathname, * descriptor to the uncompressed file. */ if (!compressions[comp].is_compressed(name)) { + fd = open(name, O_RDONLY | O_CLOEXEC); + if (fd < 0) + *err = errno; if (pathname && len > 0) pathname[0] = '\0'; - return open(name, O_RDONLY | O_CLOEXEC); + return fd; } fd = mkostemp(tmpbuf, O_CLOEXEC); From 97584371d5d8219ece84ed2602cde1e154f4cb73 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 20:34:38 -0300 Subject: [PATCH 1749/2758] perf tools: Use snprintf() for root_dir path construction [ Upstream commit 7b0df6f4d498b1608afccfd6dffb264e6da91693 ] get_kernel_version() in machine.c and dso__load_guest_kernel_sym() in symbol.c use sprintf() to construct paths by prepending root_dir to "/proc/version" and "/proc/kallsyms" respectively. Both write into PATH_MAX stack buffers, but root_dir comes from --guestmount or KVM configuration and is not length-checked. A root_dir at or near PATH_MAX causes a stack buffer overflow. Switch to snprintf() with sizeof(path) to prevent overflow. Reported-by: sashiko-bot Fixes: a1645ce12adb6c9c ("perf: 'perf kvm' tool for monitoring guest performance from host") Cc: Zhang Yanmin Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/machine.c | 2 +- tools/perf/util/symbol.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index ea918f75e3ad1..e8bce1b4fe7d5 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -1319,7 +1319,7 @@ static char *get_kernel_version(const char *root_dir) char *name, *tmp; const char *prefix = "Linux version "; - sprintf(version, "%s/proc/version", root_dir); + snprintf(version, sizeof(version), "%s/proc/version", root_dir); file = fopen(version, "r"); if (!file) return NULL; diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 09b93e844887d..85bdb3cbe0b82 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -2164,7 +2164,7 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map) if (!kallsyms_filename) return -1; } else { - sprintf(path, "%s/proc/kallsyms", machine->root_dir); + snprintf(path, sizeof(path), "%s/proc/kallsyms", machine->root_dir); kallsyms_filename = path; } From dd8e455fd91ee7c875d6667384332f63059681ba Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 20:40:34 -0300 Subject: [PATCH 1750/2758] perf hwmon: Fix fd check to accept fd 0 in hwmon_pmu__describe_items() [ Upstream commit cfafef390ca9c753b34c7e97b5abee4cab0ce270 ] hwmon_pmu__describe_items() checks 'if (fd > 0)' after openat(), which incorrectly rejects fd 0. While fd 0 is normally stdin, if stdin has been closed (common in daemon/service contexts), the kernel reuses fd 0 for the next open. With fd > 0, the sysfs file is not read and the fd is leaked. Change to 'if (fd >= 0)' to match the standard openat() error check. Reported-by: sashiko-bot Fixes: 53cc0b351ec99278 ("perf hwmon_pmu: Add a tool PMU exposing events from hwmon in sysfs") Reviewed-by: Ian Rogers Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/hwmon_pmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/hwmon_pmu.c b/tools/perf/util/hwmon_pmu.c index efd3067a2e591..ed544dca70c38 100644 --- a/tools/perf/util/hwmon_pmu.c +++ b/tools/perf/util/hwmon_pmu.c @@ -435,7 +435,7 @@ static size_t hwmon_pmu__describe_items(struct hwmon_pmu *hwm, char *out_buf, si hwmon_item_strs[bit], is_alarm ? "_alarm" : ""); fd = openat(dir, buf, O_RDONLY); - if (fd > 0) { + if (fd >= 0) { ssize_t read_len = read(fd, buf, sizeof(buf) - 1); while (read_len > 0 && buf[read_len - 1] == '\n') From eaab676863cbe41a15e1db6c03cfc55b5c48e840 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 21:00:11 -0300 Subject: [PATCH 1751/2758] perf sched: Replace (void*)1 sentinel with proper runtime allocation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 500f5dd0a8b6f7bd174102587c7dff5a7d2fecbf ] map__findnew_thread() marks color-pid threads by storing (void*)1 as the thread private data via thread__set_priv(). This sentinel value causes two problems: 1. thread__get_runtime() returns (void*)1 as a struct thread_runtime pointer. Any field access (e.g. tr->shortname) dereferences address 1, which is an unmapped page — immediate segfault. 2. cmd_sched() registers free() as the thread priv destructor, so thread cleanup calls free((void*)1) — undefined behavior that corrupts the heap on many allocators. Fix by adding a 'color' flag to struct thread_runtime and allocating a real runtime struct for color-pid threads. thread__has_color() now checks the flag instead of relying on priv being non-NULL. Reported-by: sashiko-bot Fixes: 58a606149c60d5da ("perf sched: Avoid union type punning undefined behavior") Reviewed-by: Ian Rogers Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-sched.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index f48df27f1feae..b97b41c7655c6 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -274,6 +274,7 @@ struct thread_runtime { u64 migrations; int prio; + bool color; }; /* per event run time data */ @@ -1596,22 +1597,32 @@ static int process_sched_wakeup_ignore(const struct perf_tool *tool __maybe_unus static bool thread__has_color(struct thread *thread) { - return thread__priv(thread) != NULL; + struct thread_runtime *tr = thread__priv(thread); + + return tr != NULL && tr->color; } static struct thread* map__findnew_thread(struct perf_sched *sched, struct machine *machine, pid_t pid, pid_t tid) { struct thread *thread = machine__findnew_thread(machine, pid, tid); - bool color = false; - if (!sched->map.color_pids || !thread || thread__priv(thread)) + if (!sched->map.color_pids || !thread) return thread; - if (thread_map__has(sched->map.color_pids, tid)) - color = true; + /* + * Always check the color-pids map, even if thread__priv() is + * already set. COMM events processed before the first sched_switch + * allocate a thread_runtime via thread__get_runtime(), so priv is + * non-NULL before we ever get here. Skipping the check on non-NULL + * priv would prevent those threads from being colored. + */ + if (thread_map__has(sched->map.color_pids, tid)) { + struct thread_runtime *tr = thread__get_runtime(thread); - thread__set_priv(thread, color ? ((void*)1) : NULL); + if (tr) + tr->color = true; + } return thread; } From 3fe6751a0697143857ca6d36cdafa2af9a1635b6 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 21:01:15 -0300 Subject: [PATCH 1752/2758] perf bpf: Validate func_info_rec_size and sub_id in synthesize_bpf_prog_name() [ Upstream commit 10b3c3d63ecc17c6acb855bac5f40367f1115765 ] synthesize_bpf_prog_name() computes a pointer into the func_info array using sub_id * info->func_info_rec_size without validating either value. Both come from perf.data and are untrusted: - A func_info_rec_size smaller than sizeof(struct bpf_func_info) means the finfo pointer would reference a truncated entry, reading past it into adjacent data. - A sub_id >= nr_func_info computes an offset past the func_info buffer, causing an out-of-bounds read. Add bounds checks for both values before computing the pointer offset. When validation fails, fall through to the non-BTF name path instead of reading garbage. Reported-by: sashiko-bot Fixes: 7b612e291a5affb1 ("perf tools: Synthesize PERF_RECORD_* for loaded BPF programs") Cc: Song Liu Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/bpf-event.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index c4594969d7677..fe6fbca508c51 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c @@ -143,7 +143,9 @@ static int synthesize_bpf_prog_name(char *buf, int size, name_len = scnprintf(buf, size, "bpf_prog_"); name_len += snprintf_hex(buf + name_len, size - name_len, prog_tags[sub_id], BPF_TAG_SIZE); - if (btf) { + if (btf && + info->func_info_rec_size >= sizeof(*finfo) && + sub_id < info->nr_func_info) { finfo = func_infos + sub_id * info->func_info_rec_size; t = btf__type_by_id(btf, finfo->type_id); if (t) From f9ec0eda83ea1cae928aa36d7dbdf4603bc6fa2f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 21:02:25 -0300 Subject: [PATCH 1753/2758] perf bpf: Reject oversized BPF metadata events that truncate header.size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 2d6ea0875093da9033fcb62c09a9e2f1de49fe91 ] bpf_metadata_alloc() computes event_size from the number of BPF metadata variables and stores it in header.size, which is __u16. With 204 or more .rodata variables prefixed "bpf_metadata_", event_size exceeds 65535 and silently truncates. The truncated header.size causes synthesize_perf_record_bpf_metadata() to allocate a buffer sized by the truncated value, then memcpy the full event data into it — a heap buffer overflow. Add a check that event_size fits in __u16 before proceeding. BPF programs with that many metadata variables are exotic enough that silently dropping the metadata is acceptable. Reported-by: sashiko-bot Fixes: ab38e84ba9a80581 ("perf record: collect BPF metadata from existing BPF programs") Reviewed-by: Ian Rogers Cc: Blake Jones Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/bpf-event.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index fe6fbca508c51..57d53ba848359 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c @@ -369,6 +369,15 @@ static struct bpf_metadata *bpf_metadata_alloc(__u32 nr_prog_tags, event_size = sizeof(metadata->event->bpf_metadata) + nr_variables * sizeof(metadata->event->bpf_metadata.entries[0]); + /* + * header.size is __u16. synthesize_perf_record_bpf_metadata() + * adds machine->id_hdr_size (up to ~64 bytes) after this, so + * leave headroom to prevent the final size from wrapping. + */ + if (event_size > UINT16_MAX - 256) { + bpf_metadata_free(metadata); + return NULL; + } metadata->event = zalloc(event_size); if (!metadata->event) { bpf_metadata_free(metadata); From 62a11653847fbdee368f1b4e7d57870f2988e122 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 21:03:16 -0300 Subject: [PATCH 1754/2758] perf bpf: Bounds-check array offsets in bpil_offs_to_addr() [ Upstream commit 033e85edfbf271f92979d2a39aeaf40f8472a795 ] bpil_offs_to_addr() converts offsets stored in perf.data's bpf_prog_info_linear structure into heap pointers by adding the offset to the data allocation base. The offsets come from untrusted file input and are not validated against data_len. If an offset exceeds data_len, the computed address points outside the allocated data buffer. Callers like synthesize_bpf_prog_name() then dereference prog_tags[sub_id] or func_info pointers, reading arbitrary heap memory. Add a bounds check: when an offset exceeds data_len, zero the field and skip the conversion. This prevents out-of-bounds pointer construction from crafted perf.data files. Reported-by: sashiko-bot Fixes: 6ac22d036f86c4e2 ("perf bpf: Pull in bpf_program__get_prog_info_linear()") Cc: Dave Marchevsky Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/bpf-utils.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tools/perf/util/bpf-utils.c b/tools/perf/util/bpf-utils.c index d6d2c9c190f7a..69148197b1ef9 100644 --- a/tools/perf/util/bpf-utils.c +++ b/tools/perf/util/bpf-utils.c @@ -264,12 +264,28 @@ void bpil_offs_to_addr(struct perf_bpil *info_linear) for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) { const struct bpil_array_desc *desc = &bpil_array_desc[i]; __u64 addr, offs; + __u32 count, size; if ((info_linear->arrays & (1UL << i)) == 0) continue; offs = bpf_prog_info_read_offset_u64(&info_linear->info, desc->array_offset); + count = bpf_prog_info_read_offset_u32(&info_linear->info, + desc->count_offset); + size = bpf_prog_info_read_offset_u32(&info_linear->info, + desc->size_offset); + /* offset and extent from perf.data are untrusted — keep within data[] */ + if (offs >= info_linear->data_len || + (u64)count * size > info_linear->data_len - offs) { + bpf_prog_info_set_offset_u64(&info_linear->info, + desc->array_offset, 0); + bpf_prog_info_set_offset_u32(&info_linear->info, + desc->count_offset, 0); + /* clear the bit so bpil_addr_to_offs() won't reverse a zeroed address */ + info_linear->arrays &= ~(1UL << i); + continue; + } addr = offs + ptr_to_u64(info_linear->data); bpf_prog_info_set_offset_u64(&info_linear->info, desc->array_offset, addr); From 6d2aa8dfea1fc9f548a2f27710b2103c31bdfe31 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 21:23:35 -0300 Subject: [PATCH 1755/2758] perf c2c: Free format list entries when releasing c2c hist entries [ Upstream commit 4a7500d772fe59653053db22ca83c9e2232b22e1 ] c2c_hists__init() calls hpp_list__parse() which allocates and registers format entries on hists->list. When c2c_he_free() destroys a c2c hist entry, it deletes the histogram entries and frees the hists container but never unregisters the format list entries, leaking them. Call perf_hpp__reset_output_field() before freeing the hists to properly unregister and free all format entries. Fixes: f485e33c4543ac31 ("perf c2c report: Add cacheline hists processing") Reported-by: sashiko-bot Cc: Jiri Olsa Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-c2c.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index 74ded7d238c80..17161278ce94c 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -181,6 +181,7 @@ static void c2c_he_free(void *he) c2c_he = container_of(he, struct c2c_hist_entry, he); if (c2c_he->hists) { hists__delete_entries(&c2c_he->hists->hists); + perf_hpp__reset_output_field(&c2c_he->hists->list); zfree(&c2c_he->hists); } From 99ab295d80255998dedaab3009a690b6f404fa9d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 22:45:08 -0300 Subject: [PATCH 1756/2758] perf cs-etm: Reject CPU IDs that would overflow signed comparison [ Upstream commit 542e88a4c6f7b6edd1326ce767d4cb3c2ea9d61d ] metadata[j][CS_ETM_CPU] is a u64 from perf.data, but the comparison with max_cpu casts it to (int). A crafted value like 0xFFFFFFFF becomes -1 after the cast, which compares less than max_cpu (0), so the queue array is never sized to accommodate it. When the value is later passed to cs_etm__get_queue(), it indexes queue_array with the original large value, causing an out-of-bounds access. Validate that CS_ETM_CPU fits in an int before using it in the signed comparison. Fixes: 57880a7966be510c ("perf: cs-etm: Allocate queues for all CPUs") Reported-by: sashiko-bot Cc: James Clark Cc: Adrian Hunter Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/cs-etm.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index f6ffdf0796971..c1169254fadb7 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -6,6 +6,7 @@ * Author: Mathieu Poirier */ +#include #include #include #include @@ -3468,7 +3469,13 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, goto err_free_metadata; } - if ((int) metadata[j][CS_ETM_CPU] > max_cpu) + /* CPU id comes from perf.data and must fit max_cpu + 1 without overflow */ + if (metadata[j][CS_ETM_CPU] >= INT_MAX) { + err = -EINVAL; + goto err_free_metadata; + } + + if ((int)metadata[j][CS_ETM_CPU] > max_cpu) max_cpu = metadata[j][CS_ETM_CPU]; } From e06ad4356915f595c01ac7c7481c7c62c6d78c3f Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 15 Jun 2026 17:19:18 +0800 Subject: [PATCH 1757/2758] gpio: mlxbf3: fail probe if gpiochip registration fails [ Upstream commit 0482862a90169f4daaba0ed31a85d8304bf51e04 ] mlxbf3_gpio_probe() logs a devm_gpiochip_add_data() failure but still returns success. That leaves the platform device bound even though the GPIO chip was not registered. Return the registration error so probe failure matches the missing gpiochip state. Fixes: cd33f216d241 ("gpio: mlxbf3: Add gpio driver support") Signed-off-by: Pengpeng Hou Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260615091918.43333-1-pengpeng@iscas.ac.cn Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpio-mlxbf3.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-mlxbf3.c b/drivers/gpio/gpio-mlxbf3.c index 4770578269bae..566326644a2c1 100644 --- a/drivers/gpio/gpio-mlxbf3.c +++ b/drivers/gpio/gpio-mlxbf3.c @@ -255,7 +255,8 @@ static int mlxbf3_gpio_probe(struct platform_device *pdev) ret = devm_gpiochip_add_data(dev, gc, gs); if (ret) - dev_err_probe(dev, ret, "Failed adding memory mapped gpiochip\n"); + return dev_err_probe(dev, ret, + "Failed adding memory mapped gpiochip\n"); return 0; } From 9f171aa115ec76be02c76c5765cd2c865279e34a Mon Sep 17 00:00:00 2001 From: Francesco Lavra Date: Tue, 16 Jun 2026 13:44:29 +0200 Subject: [PATCH 1758/2758] regcache: Do not overwrite error code when finalizing cache after error [ Upstream commit 9108f7fa493b4c88cbc09503e0c164244456bad5 ] During regcache initialization, if an error occurs in the cache_ops->populate callback, and if cache operations include an exit callback, the error code from populate() is overwritten with the return value from exit(). This hides the error condition from the caller of regcache_init(), and can cause NULL pointer dereferences when the regcache is later accessed. Fixes: 94a3a95f0315 ("regcache: Add ->populate() callback to separate from ->init()") Signed-off-by: Francesco Lavra Link: https://patch.msgid.link/20260616114429.1852456-1-flavra@baylibre.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/base/regmap/regcache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 27616b05111cc..aa8f2efed7798 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -245,7 +245,7 @@ int regcache_init(struct regmap *map, const struct regmap_config *config) if (map->cache_ops->exit) { dev_dbg(map->dev, "Destroying %s cache\n", map->cache_ops->name); map->lock(map->lock_arg); - ret = map->cache_ops->exit(map); + map->cache_ops->exit(map); map->unlock(map->lock_arg); } err_free_reg_defaults: From ac554ad943610031a25795d6ef71316f6164c136 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Fri, 12 Jun 2026 11:53:10 +0800 Subject: [PATCH 1759/2758] drm/i915: clear CRTC color blob pointers after dropping refs [ Upstream commit 31f077088e0faae6be8377741f356dea1b94ba46 ] intel_crtc_put_color_blobs() drops the CRTC color blob references, but leaves the corresponding pointers unchanged. This can matter in intel_crtc_prepare_cleared_state(), which frees the old CRTC hw state before calling intel_dp_tunnel_atomic_clear_stream_bw(). The latter can fail while looking up the DP tunnel group state, for example with -EDEADLK. If that happens, the function returns without completing the cleared state preparation. The failed atomic state will then be cleared by the atomic core and intel_crtc_free_hw_state() can be called again for the same state, dropping the same blob references again. Clear the blob pointers after dropping the references so repeated cleanup of the same CRTC hw state is safe. Fixes: 77fcf58df15e ("drm/i915/dp_tunnel: Fix error handling when clearing stream BW in atomic state") Suggested-by: Imre Deak Signed-off-by: Guangshuo Li Reviewed-by: Imre Deak Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260612035310.3013066-1-lgs201920130244@gmail.com (cherry picked from commit d5005addb5f68e8a0edce249506757bdc9e3d8c8) Signed-off-by: Rodrigo Vivi Signed-off-by: Sasha Levin --- drivers/gpu/drm/i915/display/intel_atomic.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_atomic.c b/drivers/gpu/drm/i915/display/intel_atomic.c index 71b7325917b6e..a16e1cb7d41e5 100644 --- a/drivers/gpu/drm/i915/display/intel_atomic.c +++ b/drivers/gpu/drm/i915/display/intel_atomic.c @@ -289,6 +289,12 @@ static void intel_crtc_put_color_blobs(struct intel_crtc_state *crtc_state) drm_property_blob_put(crtc_state->pre_csc_lut); drm_property_blob_put(crtc_state->post_csc_lut); + + crtc_state->hw.degamma_lut = NULL; + crtc_state->hw.gamma_lut = NULL; + crtc_state->hw.ctm = NULL; + crtc_state->pre_csc_lut = NULL; + crtc_state->post_csc_lut = NULL; } void intel_crtc_free_hw_state(struct intel_crtc_state *crtc_state) From a5c5676ad3b0588161d24f4091d6cbb49c4506e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Hellstr=C3=B6m?= Date: Thu, 4 Jun 2026 09:45:00 +0200 Subject: [PATCH 1760/2758] drm/xe: Fix wa_oob codegen recipe for external module builds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 92dc59ab2a09097cdf249e0288ff9b69261761c6 ] When building with 'make M=drivers/gpu/drm/xe modules', kbuild invokes scripts/Makefile.build with obj=., causing $(obj) to expand to '.'. Make normalizes './xe_gen_wa_oob' to 'xe_gen_wa_oob' when constructing the $^ automatic variable (target name normalization), so the recipe command becomes just 'xe_gen_wa_oob ...' without any path prefix, and the shell cannot find the tool. Fix by replacing $^ with explicit $(obj)/xe_gen_wa_oob and $(src)/ references in both wa_oob recipe commands. In recipe strings, make does not apply target name normalization, so $(obj)/xe_gen_wa_oob correctly expands to './xe_gen_wa_oob' and the shell can execute it. This matches the pattern already used by other DRM drivers (e.g. radeon's mkregtable). Fixes: f037e0b78e6d ("drm/xe: add xe_device_wa infrastructure") Cc: Matt Atwood Cc: Matthew Brost Cc: Rodrigo Vivi Cc: intel-xe@lists.freedesktop.org Assisted-by: GitHub_Copilot:claude-sonnet-4.6 Signed-off-by: Thomas Hellström Reviewed-by: Rodrigo Vivi Link: https://patch.msgid.link/20260604074501.172129-1-thomas.hellstrom@linux.intel.com (cherry picked from commit 3a11a63cc16660d514ff584e7551589655337e87) Signed-off-by: Matthew Brost Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile index 03242e8b3d876..de36c8b640baf 100644 --- a/drivers/gpu/drm/xe/Makefile +++ b/drivers/gpu/drm/xe/Makefile @@ -16,14 +16,14 @@ subdir-ccflags-y += -I$(obj) -I$(src) hostprogs := xe_gen_wa_oob generated_oob := $(obj)/generated/xe_wa_oob.c $(obj)/generated/xe_wa_oob.h quiet_cmd_wa_oob = GEN $(notdir $(generated_oob)) - cmd_wa_oob = mkdir -p $(@D); $^ $(generated_oob) + cmd_wa_oob = mkdir -p $(@D); $(obj)/xe_gen_wa_oob $(src)/xe_wa_oob.rules $(generated_oob) $(obj)/generated/%_wa_oob.c $(obj)/generated/%_wa_oob.h: $(obj)/xe_gen_wa_oob \ $(src)/xe_wa_oob.rules $(call cmd,wa_oob) generated_device_oob := $(obj)/generated/xe_device_wa_oob.c $(obj)/generated/xe_device_wa_oob.h quiet_cmd_device_wa_oob = GEN $(notdir $(generated_device_oob)) - cmd_device_wa_oob = mkdir -p $(@D); $^ $(generated_device_oob) + cmd_device_wa_oob = mkdir -p $(@D); $(obj)/xe_gen_wa_oob $(src)/xe_device_wa_oob.rules $(generated_device_oob) $(obj)/generated/%_device_wa_oob.c $(obj)/generated/%_device_wa_oob.h: $(obj)/xe_gen_wa_oob \ $(src)/xe_device_wa_oob.rules $(call cmd,device_wa_oob) From 7f29c063c53f0a8bc43fe8f6c256403938e3cf25 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Fri, 12 Jun 2026 08:28:35 +0800 Subject: [PATCH 1761/2758] spi: dw: fix wrong BAUDR setting after resume [ Upstream commit 66b6605bcea7af7aca3d1d858b9c5f14903f9f9a ] After resuming from suspend to ram, spi transfer stops working. Further debugging shows that the BAUDR register isn't correctly set, this is due to dws->current_freq doesn't match the HW BAUDR setting, specifically, the dws->current_freq equals to speed_hz, but BAUDR is 0. so the dw_spi_set_clk() in below code won't be called: if (dws->current_freq != speed_hz) { dw_spi_set_clk(dws, clk_div); dws->current_freq = speed_hz; } The mismatch comes from dw_spi_shutdown_chip() when suspending. Fix this mismatch by setting dws->current_freq to 0 as well when clearing BAUDR reg in dw_spi_shutdown_chip(). Fixes: e24c74527207 ("spi: controller driver for Designware SPI core") Signed-off-by: Jisheng Zhang Link: https://patch.msgid.link/20260612002835.5240-1-jszhang@kernel.org Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-dw.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 9cc79c566a70c..2f2debc64e737 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -282,6 +282,7 @@ static inline void dw_spi_shutdown_chip(struct dw_spi *dws) { dw_spi_enable_chip(dws, 0); dw_spi_set_clk(dws, 0); + dws->current_freq = 0; } extern void dw_spi_set_cs(struct spi_device *spi, bool enable); From b5d5cfea4f235f68b7ad9927a29381b5ed766580 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 16 Jun 2026 14:37:50 +0300 Subject: [PATCH 1762/2758] i3c: master: Update dev_nack_retry_count under maintenance lock [ Upstream commit ab5f9c5cb527c03790a92142ad368881a9100aaf ] Protect master->dev_nack_retry_count against concurrent sysfs updates by updating it while holding the bus maintenance lock. Consequently, combine adjacent return statements into one. For consistency, read dev_nack_retry_count while holding the bus normaluse lock. Fixes: b58f47eb39268 ("i3c: add sysfs entry and attribute for Device NACK Retry count") Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260616113752.196140-2-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/i3c/master.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 7952a38ebc378..4d8e1d724c52e 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -742,7 +742,14 @@ static DEVICE_ATTR_RW(hotjoin); static ssize_t dev_nack_retry_count_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sysfs_emit(buf, "%u\n", dev_to_i3cmaster(dev)->dev_nack_retry_count); + struct i3c_bus *i3cbus = dev_to_i3cbus(dev); + ssize_t ret; + + i3c_bus_normaluse_lock(i3cbus); + ret = sysfs_emit(buf, "%u\n", dev_to_i3cmaster(dev)->dev_nack_retry_count); + i3c_bus_normaluse_unlock(i3cbus); + + return ret; } static ssize_t dev_nack_retry_count_store(struct device *dev, @@ -760,14 +767,11 @@ static ssize_t dev_nack_retry_count_store(struct device *dev, i3c_bus_maintenance_lock(i3cbus); ret = master->ops->set_dev_nack_retry(master, val); + if (!ret) + master->dev_nack_retry_count = val; i3c_bus_maintenance_unlock(i3cbus); - if (ret) - return ret; - - master->dev_nack_retry_count = val; - - return count; + return ret ?: count; } static DEVICE_ATTR_RW(dev_nack_retry_count); From a6b17b34aedc34ce022bd5655f51ef8664d24de5 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 16 Jun 2026 14:37:51 +0300 Subject: [PATCH 1763/2758] i3c: master: Add missing runtime PM get in dev_nack_retry_count_store() [ Upstream commit 79ce29e100ab3de0cad66eb48d32a7de4043e2ae ] Ensure the device is runtime resumed while updating the retry configuration to avoid accessing the controller while suspended. Call i3c_master_rpm_get() before accessing the controller in dev_nack_retry_count_store() and release it with i3c_master_rpm_put() afterwards. Fixes: 990c149c61ee4 ("i3c: master: Introduce optional Runtime PM support") Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260616113752.196140-3-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/i3c/master.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 4d8e1d724c52e..74c04e19f86c5 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -765,12 +765,18 @@ static ssize_t dev_nack_retry_count_store(struct device *dev, if (ret) return ret; + ret = i3c_master_rpm_get(master); + if (ret) + return ret; + i3c_bus_maintenance_lock(i3cbus); ret = master->ops->set_dev_nack_retry(master, val); if (!ret) master->dev_nack_retry_count = val; i3c_bus_maintenance_unlock(i3cbus); + i3c_master_rpm_put(master); + return ret ?: count; } From 9104559db16bf4b46a383d8111495856f83a4837 Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Wed, 17 Jun 2026 11:14:59 +0800 Subject: [PATCH 1764/2758] erofs: call erofs_exit_ishare() before rcu_barrier() [ Upstream commit 289cf458a69ccda4a4aee2b0274d233d415814ca ] Ensure all inode free callbacks have completed before destroying the inode slab cache. Fixes: 5ef3208e3be5 ("erofs: introduce the page cache share feature") Reviewed-by: Hongbo Li Signed-off-by: Gao Xiang Signed-off-by: Sasha Levin --- fs/erofs/super.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/erofs/super.c b/fs/erofs/super.c index 802add6652fda..579443e6acfeb 100644 --- a/fs/erofs/super.c +++ b/fs/erofs/super.c @@ -1048,11 +1048,11 @@ static int __init erofs_module_init(void) static void __exit erofs_module_exit(void) { unregister_filesystem(&erofs_fs_type); + erofs_exit_ishare(); - /* Ensure all RCU free inodes / pclusters are safe to be destroyed. */ + /* ensure all delayed rcu free inodes & pclusters are flushed */ rcu_barrier(); - erofs_exit_ishare(); erofs_exit_sysfs(); z_erofs_exit_subsystem(); erofs_exit_shrinker(); From f83ef148a94bd75a02c9238014b008e38822a604 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Tue, 16 Jun 2026 19:59:16 +0800 Subject: [PATCH 1765/2758] ALSA: usb-audio: qcom: Free sideband sg_table objects [ Upstream commit 7d69804a35103a50852eae41bfe6a2e0061c68fd ] The Qualcomm USB audio offload driver obtains an endpoint transfer-ring table by calling xhci_sideband_get_endpoint_buffer(). This getter passes the endpoint ring to xhci_ring_to_sgtable(), which allocates the outer struct sg_table with kzalloc_obj(*sgt). The event-ring path is equivalent: xhci_sideband_get_event_buffer() also returns the result of xhci_ring_to_sgtable(). Inside xhci_ring_to_sgtable(), sg_alloc_table_from_pages() separately allocates the scatterlist storage referenced by sgt->sgl. The returned object therefore has two allocation layers: the outer struct sg_table and its internal scatterlist storage. The Qualcomm caller only invokes sg_free_table(sgt). sg_free_table() releases the scatterlist storage owned by the table, but it does not free the separately allocated outer struct sg_table. The local sgt pointer is then discarded, so every successful endpoint or event-ring query leaks the outer object. Call kfree(sgt) after sg_free_table(sgt) in both setup paths, after the required page and DMA addresses have been copied out. Fixes: 326bbc348298 ("ALSA: usb-audio: qcom: Introduce QC USB SND offloading support") Signed-off-by: Xu Rao Link: https://patch.msgid.link/90B353283AA150C4+20260616115916.1222915-1-raoxu@uniontech.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/usb/qcom/qc_audio_offload.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c index 649228421e2f6..ccb5ffd73a66c 100644 --- a/sound/usb/qcom/qc_audio_offload.c +++ b/sound/usb/qcom/qc_audio_offload.c @@ -1165,6 +1165,7 @@ uaudio_endpoint_setup(struct snd_usb_substream *subs, tr_pa = page_to_phys(pg); mem_info->dma = sg_dma_address(sgt->sgl); sg_free_table(sgt); + kfree(sgt); /* data transfer ring */ iova = uaudio_iommu_map_pa(MEM_XFER_RING, dma_coherent, tr_pa, @@ -1234,6 +1235,7 @@ static int uaudio_event_ring_setup(struct snd_usb_substream *subs, er_pa = page_to_phys(pg); mem_info->dma = sg_dma_address(sgt->sgl); sg_free_table(sgt); + kfree(sgt); iova = uaudio_iommu_map_pa(MEM_EVENT_RING, dma_coherent, er_pa, PAGE_SIZE); From 041859fd55c81ea55e76d051e39b7b79975b8c7d Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Fri, 12 Jun 2026 12:58:59 +0800 Subject: [PATCH 1766/2758] xfrm: Fix xfrm state cache insertion race [ Upstream commit ddd3d0132920319ac426e12456013eadbae67e15 ] The xfrm input state cache insertion code checks the validity of the state before acquiring the global xfrm_state_lock. Thus it's possible for someone else to kill the state after it passed the validity check, and then the insertion will add the dead state to the cache. Fix this by moving the validity check inside the lock. This entire function is called on the input path, where BH must be off (e.g., the caller of this function xfrm_input acquires its spinlocks without disabling BH). So there is no need to disable BH here or take the RCU read lock. Remove both and replace them with an assertion that trips if BH is accidentally enabled on some future calling path. Fixes: 81a331a0e72d ("xfrm: Add an inbound percpu state cache.") Reported-by: Zero Day Initiative Signed-off-by: Herbert Xu Reviewed-by: Simon Horman Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin --- net/xfrm/xfrm_state.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 4c7eed689cd44..ae4fdc60c2a62 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -1207,9 +1207,11 @@ struct xfrm_state *xfrm_input_state_lookup(struct net *net, u32 mark, struct hlist_head *state_cache_input; struct xfrm_state *x = NULL; + /* BH is always disabled on the input path. */ + lockdep_assert_in_softirq(); + state_cache_input = raw_cpu_ptr(net->xfrm.state_cache_input); - rcu_read_lock(); hlist_for_each_entry_rcu(x, state_cache_input, state_cache_input) { if (x->props.family != family || x->id.spi != spi || @@ -1227,20 +1229,25 @@ struct xfrm_state *xfrm_input_state_lookup(struct net *net, u32 mark, xfrm_hash_ptrs_get(net, &state_ptrs); x = __xfrm_state_lookup(&state_ptrs, mark, daddr, spi, proto, family); - - if (x && x->km.state == XFRM_STATE_VALID) { - spin_lock_bh(&net->xfrm.xfrm_state_lock); - if (hlist_unhashed(&x->state_cache_input)) { + if (x) { + spin_lock(&net->xfrm.xfrm_state_lock); + if (x->km.state != XFRM_STATE_VALID) { + /* + * The state is about to be destroyed. + * + * Don't add it to the cache but still + * return it to the caller. + */ + } else if (hlist_unhashed(&x->state_cache_input)) { hlist_add_head_rcu(&x->state_cache_input, state_cache_input); } else { hlist_del_rcu(&x->state_cache_input); hlist_add_head_rcu(&x->state_cache_input, state_cache_input); } - spin_unlock_bh(&net->xfrm.xfrm_state_lock); + spin_unlock(&net->xfrm.xfrm_state_lock); } out: - rcu_read_unlock(); return x; } EXPORT_SYMBOL(xfrm_input_state_lookup); From 7394a276f86939e23b6c3621b3d5d961efa0698a Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 12 Jun 2026 05:56:34 +0000 Subject: [PATCH 1767/2758] xfrm: annotate data-races around xfrm_policy_count[] and xfrm_policy_default[] [ Upstream commit 68de007d5ac9df0e3f4f187a179c5c842bb5a2be ] KCSAN reported a data race involving net->xfrm.policy_count access. Add missing READ_ONCE()/WRITE_ONCE() annotations on xfrm_policy_count and xfrm_policy_default. Fixes: 2518c7c2b3d7 ("[XFRM]: Hash policies when non-prefixed.") Reported-by: syzbot+d85ba1c732720b9a4097@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6a2b9e96.99669fcc.12a77b.0006.GAE@google.com/T/#u Signed-off-by: Eric Dumazet Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin --- include/net/xfrm.h | 8 ++++---- net/xfrm/xfrm_policy.c | 24 ++++++++++++------------ net/xfrm/xfrm_user.c | 18 +++++++++--------- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 874409127e292..35a7431293298 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1250,8 +1250,8 @@ int __xfrm_policy_check(struct sock *, int dir, struct sk_buff *skb, static inline bool __xfrm_check_nopolicy(struct net *net, struct sk_buff *skb, int dir) { - if (!net->xfrm.policy_count[dir] && !secpath_exists(skb)) - return net->xfrm.policy_default[dir] == XFRM_USERPOLICY_ACCEPT; + if (!READ_ONCE(net->xfrm.policy_count[dir]) && !secpath_exists(skb)) + return READ_ONCE(net->xfrm.policy_default[dir]) == XFRM_USERPOLICY_ACCEPT; return false; } @@ -1351,8 +1351,8 @@ static inline int xfrm_route_forward(struct sk_buff *skb, unsigned short family) { struct net *net = dev_net(skb->dev); - if (!net->xfrm.policy_count[XFRM_POLICY_OUT] && - net->xfrm.policy_default[XFRM_POLICY_OUT] == XFRM_USERPOLICY_ACCEPT) + if (!READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_OUT]) && + READ_ONCE(net->xfrm.policy_default[XFRM_POLICY_OUT]) == XFRM_USERPOLICY_ACCEPT) return true; return (skb_dst(skb)->flags & DST_NOXFRM) || diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 9595444256929..1f4afd580105f 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -685,7 +685,7 @@ static void xfrm_byidx_resize(struct net *net) static inline int xfrm_bydst_should_resize(struct net *net, int dir, int *total) { - unsigned int cnt = net->xfrm.policy_count[dir]; + unsigned int cnt = READ_ONCE(net->xfrm.policy_count[dir]); unsigned int hmask = net->xfrm.policy_bydst[dir].hmask; if (total) @@ -711,12 +711,12 @@ static inline int xfrm_byidx_should_resize(struct net *net, int total) void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si) { - si->incnt = net->xfrm.policy_count[XFRM_POLICY_IN]; - si->outcnt = net->xfrm.policy_count[XFRM_POLICY_OUT]; - si->fwdcnt = net->xfrm.policy_count[XFRM_POLICY_FWD]; - si->inscnt = net->xfrm.policy_count[XFRM_POLICY_IN+XFRM_POLICY_MAX]; - si->outscnt = net->xfrm.policy_count[XFRM_POLICY_OUT+XFRM_POLICY_MAX]; - si->fwdscnt = net->xfrm.policy_count[XFRM_POLICY_FWD+XFRM_POLICY_MAX]; + si->incnt = READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_IN]); + si->outcnt = READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_OUT]); + si->fwdcnt = READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_FWD]); + si->inscnt = READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_IN+XFRM_POLICY_MAX]); + si->outscnt = READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_OUT+XFRM_POLICY_MAX]); + si->fwdscnt = READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_FWD+XFRM_POLICY_MAX]); si->spdhcnt = net->xfrm.policy_idx_hmask; si->spdhmcnt = xfrm_policy_hashmax; } @@ -2318,7 +2318,7 @@ static void __xfrm_policy_link(struct xfrm_policy *pol, int dir) } list_add(&pol->walk.all, &net->xfrm.policy_all); - net->xfrm.policy_count[dir]++; + WRITE_ONCE(net->xfrm.policy_count[dir], net->xfrm.policy_count[dir] + 1); xfrm_pol_hold(pol); } @@ -2337,7 +2337,7 @@ static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol, } list_del_init(&pol->walk.all); - net->xfrm.policy_count[dir]--; + WRITE_ONCE(net->xfrm.policy_count[dir], net->xfrm.policy_count[dir] - 1); return pol; } @@ -3222,7 +3222,7 @@ struct dst_entry *xfrm_lookup_with_ifid(struct net *net, /* To accelerate a bit... */ if (!if_id && ((dst_orig->flags & DST_NOXFRM) || - !net->xfrm.policy_count[XFRM_POLICY_OUT])) + !READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_OUT]))) goto nopol; xdst = xfrm_bundle_lookup(net, fl, family, dir, &xflo, if_id); @@ -3296,7 +3296,7 @@ struct dst_entry *xfrm_lookup_with_ifid(struct net *net, nopol: if ((!dst_orig->dev || !(dst_orig->dev->flags & IFF_LOOPBACK)) && - net->xfrm.policy_default[dir] == XFRM_USERPOLICY_BLOCK) { + READ_ONCE(net->xfrm.policy_default[dir]) == XFRM_USERPOLICY_BLOCK) { err = -EPERM; goto error; } @@ -3750,7 +3750,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, const bool is_crypto_offload = sp && (xfrm_input_state(skb)->xso.type == XFRM_DEV_OFFLOAD_CRYPTO); - if (net->xfrm.policy_default[dir] == XFRM_USERPOLICY_BLOCK) { + if (READ_ONCE(net->xfrm.policy_default[dir]) == XFRM_USERPOLICY_BLOCK) { XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOPOLS); return 0; } diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 71a4b7278eba9..8de2ebb0f9d79 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -2485,9 +2485,9 @@ static int xfrm_notify_userpolicy(struct net *net) } up = nlmsg_data(nlh); - up->in = net->xfrm.policy_default[XFRM_POLICY_IN]; - up->fwd = net->xfrm.policy_default[XFRM_POLICY_FWD]; - up->out = net->xfrm.policy_default[XFRM_POLICY_OUT]; + up->in = READ_ONCE(net->xfrm.policy_default[XFRM_POLICY_IN]); + up->fwd = READ_ONCE(net->xfrm.policy_default[XFRM_POLICY_FWD]); + up->out = READ_ONCE(net->xfrm.policy_default[XFRM_POLICY_OUT]); nlmsg_end(skb, nlh); @@ -2511,13 +2511,13 @@ static int xfrm_set_default(struct sk_buff *skb, struct nlmsghdr *nlh, struct xfrm_userpolicy_default *up = nlmsg_data(nlh); if (xfrm_userpolicy_is_valid(up->in)) - net->xfrm.policy_default[XFRM_POLICY_IN] = up->in; + WRITE_ONCE(net->xfrm.policy_default[XFRM_POLICY_IN], up->in); if (xfrm_userpolicy_is_valid(up->fwd)) - net->xfrm.policy_default[XFRM_POLICY_FWD] = up->fwd; + WRITE_ONCE(net->xfrm.policy_default[XFRM_POLICY_FWD], up->fwd); if (xfrm_userpolicy_is_valid(up->out)) - net->xfrm.policy_default[XFRM_POLICY_OUT] = up->out; + WRITE_ONCE(net->xfrm.policy_default[XFRM_POLICY_OUT], up->out); rt_genid_bump_all(net); @@ -2547,9 +2547,9 @@ static int xfrm_get_default(struct sk_buff *skb, struct nlmsghdr *nlh, } r_up = nlmsg_data(r_nlh); - r_up->in = net->xfrm.policy_default[XFRM_POLICY_IN]; - r_up->fwd = net->xfrm.policy_default[XFRM_POLICY_FWD]; - r_up->out = net->xfrm.policy_default[XFRM_POLICY_OUT]; + r_up->in = READ_ONCE(net->xfrm.policy_default[XFRM_POLICY_IN]); + r_up->fwd = READ_ONCE(net->xfrm.policy_default[XFRM_POLICY_FWD]); + r_up->out = READ_ONCE(net->xfrm.policy_default[XFRM_POLICY_OUT]); nlmsg_end(r_skb, r_nlh); return nlmsg_unicast(xfrm_net_nlsk(net, skb), r_skb, portid); From 5a03a2ee17e8259dde631ed84fd8322db06cb2ae Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 15 Jun 2026 09:02:37 +0000 Subject: [PATCH 1768/2758] xfrm: validate selector family and prefixlen during match [ Upstream commit 40f0b1047918539f0b0f795ac65e35336b4c2c78 ] syzbot reported a shift-out-of-bounds in xfrm_selector_match() due to AF_UNSPEC selector with large prefixlen (e.g. 128) matched against IPv4 flow (when XFRM_STATE_AF_UNSPEC is set). Fix this by: - Rejecting mismatched families in xfrm_selector_match. - Returning false in addr4_match if prefixlen > 32. - Returning false in addr_match if prefixlen > 128 (prevents overflow). Fixes: 3f0ab59e6537 ("xfrm: validate new SA's prefixlen using SA family when sel.family is unset") Reported-by: syzbot+9383b1ff0df4b29ca5e6@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6a2fbe35.be3f099c.2836ae.0018.GAE@google.com/T/#u Signed-off-by: Eric Dumazet Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin --- include/net/xfrm.h | 7 +++++++ net/xfrm/xfrm_policy.c | 3 +++ 2 files changed, 10 insertions(+) diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 35a7431293298..f8c909b0f0c34 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -943,6 +943,9 @@ static inline bool addr_match(const void *token1, const void *token2, unsigned int pdw; unsigned int pbi; + if (prefixlen > 128) + return false; + pdw = prefixlen >> 5; /* num of whole u32 in prefix */ pbi = prefixlen & 0x1f; /* num of bits in incomplete u32 in prefix */ @@ -967,6 +970,10 @@ static inline bool addr4_match(__be32 a1, __be32 a2, u8 prefixlen) /* C99 6.5.7 (3): u32 << 32 is undefined behaviour */ if (sizeof(long) == 4 && prefixlen == 0) return true; + + if (prefixlen > 32) + return false; + return !((a1 ^ a2) & htonl(~0UL << (32 - prefixlen))); } diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 1f4afd580105f..639934f300167 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -242,6 +242,9 @@ __xfrm6_selector_match(const struct xfrm_selector *sel, const struct flowi *fl) bool xfrm_selector_match(const struct xfrm_selector *sel, const struct flowi *fl, unsigned short family) { + if (family != sel->family && sel->family != AF_UNSPEC) + return false; + switch (family) { case AF_INET: return __xfrm4_selector_match(sel, fl); From 7c7245321599412d7013a6cd2c50fcd6f1209cf0 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 13 Jun 2026 13:59:39 -0300 Subject: [PATCH 1769/2758] perf machine: Use snprintf() for guestmount path construction [ Upstream commit fe63d3bca288c5bb983304efd5fc3a5ff3183403 ] machines__findnew() and machines__create_guest_kernel_maps() use sprintf() to build paths by prepending symbol_conf.guestmount. Both write into PATH_MAX stack buffers, but guestmount comes from user configuration and is not length-checked. A guestmount path at or near PATH_MAX causes a stack buffer overflow. Switch to snprintf() with sizeof() to prevent overflow. The subsequent access()/fopen() calls will fail on a truncated path. Fixes: a1645ce12adb6c9c ("perf: 'perf kvm' tool for monitoring guest performance from host") Reported-by: sashiko-bot Cc: Zhang, Yanmin Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/machine.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index e8bce1b4fe7d5..d48d96e4b37dc 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -327,7 +327,7 @@ struct machine *machines__findnew(struct machines *machines, pid_t pid) if ((pid != HOST_KERNEL_ID) && (pid != DEFAULT_GUEST_KERNEL_ID) && (symbol_conf.guestmount)) { - sprintf(path, "%s/%d", symbol_conf.guestmount, pid); + snprintf(path, sizeof(path), "%s/%d", symbol_conf.guestmount, pid); if (access(path, R_OK)) { static struct strlist *seen; @@ -1239,9 +1239,9 @@ int machines__create_guest_kernel_maps(struct machines *machines) namelist[i]->d_name); continue; } - sprintf(path, "%s/%s/proc/kallsyms", - symbol_conf.guestmount, - namelist[i]->d_name); + snprintf(path, sizeof(path), "%s/%s/proc/kallsyms", + symbol_conf.guestmount, + namelist[i]->d_name); ret = access(path, R_OK); if (ret) { pr_debug("Can't access file %s\n", path); From f22dbfb71c3d1e4bcb81d9ffc992e8cec2186004 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 13 Jun 2026 14:16:45 -0300 Subject: [PATCH 1770/2758] perf cs-etm: Validate num_cpu before metadata allocation [ Upstream commit 312d91329b8fc6989a916a3f9a12d0674167b7e4 ] cs_etm__process_auxtrace_info_full() reads num_cpu from untrusted perf.data and uses it to allocate the metadata pointer array: metadata = zalloc(sizeof(*metadata) * num_cpu); On 32-bit, sizeof(*metadata) is 4, so num_cpu = 0x40000000 overflows the multiplication to 0, causing zalloc(0) to return a valid zero-sized allocation followed by out-of-bounds writes in the population loop. Fix by computing priv_size early and using it to bound num_cpu: each CPU needs at least one u64 metadata entry, so num_cpu cannot exceed the total number of u64 entries in the event's private data area. Fixes: cd8bfd8c973eaff8 ("perf tools: Add processing of coresight metadata") Reported-by: sashiko-bot Cc: Adrian Hunter Cc: James Clark Cc: Leo Yan Cc: Tor Jeremiassen Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/cs-etm.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index c1169254fadb7..2000a03657c84 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -3431,6 +3431,18 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, /* First the global part */ ptr = (u64 *) auxtrace_info->priv; num_cpu = ptr[CS_PMU_TYPE_CPUS] & 0xffffffff; + + /* + * Bound num_cpu by the event size: the global header consumes + * CS_ETM_HEADER_SIZE bytes, and each CPU needs at least one u64 + * metadata entry after that. + */ + priv_size = total_size - event_header_size - INFO_HEADER_SIZE - + CS_ETM_HEADER_SIZE; + if (num_cpu <= 0 || priv_size <= 0 || + num_cpu > priv_size / (int)sizeof(u64)) + return -EINVAL; + metadata = zalloc(sizeof(*metadata) * num_cpu); if (!metadata) return -ENOMEM; From 2d5a695a9d19b11c633607a937c4dd6a2a81c604 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 13 Jun 2026 14:40:36 -0300 Subject: [PATCH 1771/2758] perf cs-etm: Require full global header in auxtrace_info size check [ Upstream commit 78d8ba680126f3545e8d0fba667e12d79fd4353b ] cs_etm__process_auxtrace_info() checks that header.size covers event_header_size + INFO_HEADER_SIZE (16 bytes total), but then accesses ptr[CS_PMU_TYPE_CPUS] at offset 24 from the start of the event. A crafted 16-byte auxtrace_info event passes the size check but reads out-of-bounds. Include CS_ETM_HEADER_SIZE in the minimum size check so that the global header entries (version, pmu_type_cpus, snapshot) are guaranteed to fit within the event. Fixes: 55c1de9973d66516 ("perf cs-etm: Print auxtrace info even if OpenCSD isn't linked") Reported-by: sashiko-bot Cc: Adrian Hunter Cc: James Clark Cc: Leo Yan Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/cs-etm-base.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/cs-etm-base.c b/tools/perf/util/cs-etm-base.c index 4abe416e3febd..aebef71d3a0a1 100644 --- a/tools/perf/util/cs-etm-base.c +++ b/tools/perf/util/cs-etm-base.c @@ -170,7 +170,9 @@ int cs_etm__process_auxtrace_info(union perf_event *event, u64 *ptr = NULL; u64 hdr_version; - if (auxtrace_info->header.size < (event_header_size + INFO_HEADER_SIZE)) + /* Ensure priv[] is large enough for the global header entries */ + if (auxtrace_info->header.size < (event_header_size + INFO_HEADER_SIZE + + CS_ETM_HEADER_SIZE)) return -EINVAL; /* First the global part */ From 79b92b298b5cfaa805a28df11ccc45ca86344be3 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 13 Jun 2026 14:55:32 -0300 Subject: [PATCH 1772/2758] perf cs-etm: Bounds-check CPU in cs_etm__get_queue() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 9a989e60cc6e29d98aed2087425cba53bf4b392d ] cs_etm__get_queue() indexes etm->queues.queue_array[cpu] without validating that cpu is within nr_queues. When processing AUX_OUTPUT_HW_ID events, the cpu value comes from untrusted perf.data trace payload and flows through cs_etm__process_trace_id_v0_1() and cs_etm__queue_aux_fragment() without bounds checking, allowing an out-of-bounds read with a crafted file. Add a bounds check in cs_etm__get_queue() and NULL checks in all callers. Also add NULL checks for queue_array[i].priv in the queue iteration loops in cs_etm__map_trace_id_v0() and cs_etm__process_trace_id_v0_1() — after auxtrace_queues__grow() new entries are zero-initialized so .priv can be NULL. Add a get_cpu_data() NULL check in cs_etm__process_trace_id_v0_1(), matching the existing check in cs_etm__process_trace_id_v0(). Fixes: 77c123f53e97ad4b ("perf: cs-etm: Move traceid_list to each queue") Reported-by: sashiko-bot Cc: Adrian Hunter Cc: James Clark Cc: Leo Yan Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/cs-etm.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index 2000a03657c84..5ca55b5271839 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -292,8 +292,11 @@ static struct cs_etm_queue *cs_etm__get_queue(struct cs_etm_auxtrace *etm, int c { if (etm->per_thread_decoding) return etm->queues.queue_array[0].priv; - else - return etm->queues.queue_array[cpu].priv; + + if (cpu < 0 || cpu >= (int)etm->queues.nr_queues) + return NULL; + + return etm->queues.queue_array[cpu].priv; } static int cs_etm__map_trace_id_v0(struct cs_etm_auxtrace *etm, u8 trace_chan_id, @@ -306,6 +309,9 @@ static int cs_etm__map_trace_id_v0(struct cs_etm_auxtrace *etm, u8 trace_chan_id * queue associated with that CPU so only one decoder is made. */ etmq = cs_etm__get_queue(etm, cpu_metadata[CS_ETM_CPU]); + if (!etmq) + return -EINVAL; + if (etmq->format == UNFORMATTED) return cs_etm__insert_trace_id_node(etmq, trace_chan_id, cpu_metadata); @@ -318,6 +324,9 @@ static int cs_etm__map_trace_id_v0(struct cs_etm_auxtrace *etm, u8 trace_chan_id int ret; etmq = etm->queues.queue_array[i].priv; + if (!etmq) + continue; + ret = cs_etm__insert_trace_id_node(etmq, trace_chan_id, cpu_metadata); if (ret) @@ -358,6 +367,9 @@ static int cs_etm__process_trace_id_v0_1(struct cs_etm_auxtrace *etm, int cpu, u32 sink_id = FIELD_GET(CS_AUX_HW_ID_SINK_ID_MASK, hw_id); u8 trace_id = FIELD_GET(CS_AUX_HW_ID_TRACE_ID_MASK, hw_id); + if (!etmq) + return -EINVAL; + /* * Check sink id hasn't changed in per-cpu mode. In per-thread mode, * let it pass for now until an actual overlapping trace ID is hit. In @@ -375,6 +387,9 @@ static int cs_etm__process_trace_id_v0_1(struct cs_etm_auxtrace *etm, int cpu, for (unsigned int i = 0; i < etm->queues.nr_queues; ++i) { struct cs_etm_queue *other_etmq = etm->queues.queue_array[i].priv; + if (!other_etmq) + continue; + /* Different sinks, skip */ if (other_etmq->sink_id != etmq->sink_id) continue; @@ -396,6 +411,9 @@ static int cs_etm__process_trace_id_v0_1(struct cs_etm_auxtrace *etm, int cpu, } cpu_data = get_cpu_data(etm, cpu); + if (!cpu_data) + return -EINVAL; + ret = cs_etm__insert_trace_id_node(etmq, trace_id, cpu_data); if (ret) return ret; @@ -3144,6 +3162,9 @@ static int cs_etm__queue_aux_fragment(struct perf_session *session, off_t file_o aux_offset + aux_size <= auxtrace_event->offset + auxtrace_event->size) { struct cs_etm_queue *etmq = cs_etm__get_queue(etm, auxtrace_event->cpu); + if (!etmq) + return -EINVAL; + /* * If this AUX event was inside this buffer somewhere, create a new auxtrace event * based on the sizes of the aux event, and queue that fragment. From 502ee1fe757aeaa0e1d0a56f3f42e7d2e5b88b7c Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 13 Jun 2026 15:13:54 -0300 Subject: [PATCH 1773/2758] perf c2c: Free format list entries when c2c_hists__init() fails [ Upstream commit 61a21d11afd8dd83b2260ae00541bed62a843219 ] When c2c_hists__init() fails partway through hpp_list__parse(), dynamically allocated format structures that were already added to hists->list are leaked because he__get_c2c_hists() frees the hists container without first unregistering the format entries. Call perf_hpp__reset_output_field() before freeing the hists container on the error path, matching what c2c_he_free() already does on the normal destruction path. Fixes: 17a7c5946d79a12c ("perf c2c report: Decode c2c_stats for hist entries") Reported-by: sashiko-bot Cc: Jiri Olsa Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-c2c.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index 17161278ce94c..219de790c3766 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -222,6 +222,7 @@ he__get_c2c_hists(struct hist_entry *he, ret = c2c_hists__init(hists, sort, nr_header_lines, env); if (ret) { + perf_hpp__reset_output_field(&hists->list); c2c_he->hists = NULL; free(hists); return NULL; From bfc764f9de658fd7751da61d32f3de5fa39a75ba Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 13 Jun 2026 15:16:42 -0300 Subject: [PATCH 1774/2758] perf c2c: Fix hist entry and format list leaks in c2c_he_free() [ Upstream commit fe68cf349fb343c0a7cb6c4fe6c3de4f4afe8d1c ] c2c_he_free() calls hists__delete_entries() which only walks the output-sorted entries tree. During c2c resort, when cacheline entries are merged and the redundant entry is freed, the inner hists have not been output-resorted yet, so hists->entries is empty. The actual inner hist_entry objects live in entries_in_array[] and entries_collapsed, which are never walked, leaking all inner hist_entry objects for every merged cacheline. Additionally, the dynamically allocated format entries on hists->list are never unregistered or freed. Fix both issues by switching to hists__delete_all_entries() which walks all rb_root trees, and calling perf_hpp__reset_output_field() to clean up format entries. Fixes: bf0e0d407ea09ce5 ("perf c2c report: Add sample processing") Reported-by: sashiko-bot Cc: Jiri Olsa Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-c2c.c | 2 +- tools/perf/util/hist.c | 2 +- tools/perf/util/hist.h | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index 219de790c3766..e7b9909e1ac24 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -180,7 +180,7 @@ static void c2c_he_free(void *he) c2c_he = container_of(he, struct c2c_hist_entry, he); if (c2c_he->hists) { - hists__delete_entries(&c2c_he->hists->hists); + hists__delete_all_entries(&c2c_he->hists->hists); perf_hpp__reset_output_field(&c2c_he->hists->list); zfree(&c2c_he->hists); } diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 2038c1d20acff..aca9ff15518a2 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -3041,7 +3041,7 @@ static void hists__delete_remaining_entries(struct rb_root_cached *root) } } -static void hists__delete_all_entries(struct hists *hists) +void hists__delete_all_entries(struct hists *hists) { hists__delete_entries(hists); hists__delete_remaining_entries(&hists->entries_in_array[0]); diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index d97a4efb92505..3a59200d2517e 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -392,6 +392,7 @@ int hists__collapse_resort(struct hists *hists, struct ui_progress *prog); void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); void hists__delete_entries(struct hists *hists); +void hists__delete_all_entries(struct hists *hists); void hists__output_recalc_col_len(struct hists *hists, int max_rows); struct hist_entry *hists__get_entry(struct hists *hists, int idx); From aa967ae8b256b223a9ae6993f1dd1596ac76b8bf Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 13 Jun 2026 15:25:11 -0300 Subject: [PATCH 1775/2758] perf bpf: Validate array presence before casting BPF prog info pointers [ Upstream commit 5ebf4137d23a4fd6c0cc6a6fb766ee60d2b09193 ] Several functions cast bpf_prog_info fields (jited_ksyms, jited_func_lens, jited_prog_insns) from u64 to pointers and dereference them. These fields are only valid pointers if bpil_offs_to_addr() converted their file offsets to addresses, which only happens when the corresponding PERF_BPIL_* bits are set in info_linear->arrays. A crafted perf.data can leave these bits unset while setting non-zero counts and offset values, causing the functions to dereference raw file offsets as pointers. Add array bitmask validation to all perf.data processing paths: - __bpf_event__print_bpf_prog_info(): check JITED_KSYMS and JITED_FUNC_LENS (changed to take struct perf_bpil *) - machine__process_bpf_event_load(): check JITED_KSYMS - bpf_read(): check JITED_INSNS before memcpy from jited_prog_insns - dso__disassemble_filename(): check JITED_INSNS before returning jited_prog_insns pointer Fixes: f8dfeae009effc0b ("perf bpf: Show more BPF program info in print_bpf_prog_info()") Reported-by: sashiko-bot Cc: Song Liu Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/bpf-event.c | 20 +++++++++++++++++--- tools/perf/util/bpf-event.h | 4 ++-- tools/perf/util/dso.c | 10 ++++++++++ tools/perf/util/header.c | 3 +-- 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index 57d53ba848359..fa3ebc8ea7f09 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c @@ -59,6 +59,10 @@ static int machine__process_bpf_event_load(struct machine *machine, return 0; info_linear = info_node->info_linear; + /* jited_ksyms is only valid if bpil_offs_to_addr() converted it */ + if (!(info_linear->arrays & (1UL << PERF_BPIL_JITED_KSYMS))) + return 0; + for (i = 0; i < info_linear->info.nr_jited_ksyms; i++) { u64 *addrs = (u64 *)(uintptr_t)(info_linear->info.jited_ksyms); u64 addr = addrs[i]; @@ -959,12 +963,15 @@ int evlist__add_bpf_sb_event(struct evlist *evlist, struct perf_env *env) return evlist__add_sb_event(evlist, &attr, bpf_event__sb_cb, env); } -void __bpf_event__print_bpf_prog_info(struct bpf_prog_info *info, +void __bpf_event__print_bpf_prog_info(struct perf_bpil *info_linear, struct perf_env *env, FILE *fp) { - __u32 *prog_lens = (__u32 *)(uintptr_t)(info->jited_func_lens); - __u64 *prog_addrs = (__u64 *)(uintptr_t)(info->jited_ksyms); + struct bpf_prog_info *info = &info_linear->info; + __u64 required_arrays = (1UL << PERF_BPIL_JITED_KSYMS) | + (1UL << PERF_BPIL_JITED_FUNC_LENS); + __u32 *prog_lens; + __u64 *prog_addrs; char name[KSYM_NAME_LEN]; struct btf *btf = NULL; u32 sub_prog_cnt, i; @@ -974,6 +981,13 @@ void __bpf_event__print_bpf_prog_info(struct bpf_prog_info *info, sub_prog_cnt != info->nr_jited_func_lens) return; + /* Ensure the arrays were present and converted by bpil_offs_to_addr() */ + if ((info_linear->arrays & required_arrays) != required_arrays) + return; + + prog_lens = (__u32 *)(uintptr_t)(info->jited_func_lens); + prog_addrs = (__u64 *)(uintptr_t)(info->jited_ksyms); + if (info->btf_id) { struct btf_node *node; diff --git a/tools/perf/util/bpf-event.h b/tools/perf/util/bpf-event.h index 60d2c6637af5d..da4eeb4a1a732 100644 --- a/tools/perf/util/bpf-event.h +++ b/tools/perf/util/bpf-event.h @@ -40,7 +40,7 @@ struct btf_node { int machine__process_bpf(struct machine *machine, union perf_event *event, struct perf_sample *sample); int evlist__add_bpf_sb_event(struct evlist *evlist, struct perf_env *env); -void __bpf_event__print_bpf_prog_info(struct bpf_prog_info *info, +void __bpf_event__print_bpf_prog_info(struct perf_bpil *info_linear, struct perf_env *env, FILE *fp); void bpf_metadata_free(struct bpf_metadata *metadata); @@ -58,7 +58,7 @@ static inline int evlist__add_bpf_sb_event(struct evlist *evlist __maybe_unused, return 0; } -static inline void __bpf_event__print_bpf_prog_info(struct bpf_prog_info *info __maybe_unused, +static inline void __bpf_event__print_bpf_prog_info(struct perf_bpil *info_linear __maybe_unused, struct perf_env *env __maybe_unused, FILE *fp __maybe_unused) { diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 233c313bf3311..3547cbd5ca8d2 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -880,6 +880,12 @@ static ssize_t bpf_read(struct dso *dso, u64 offset, char *data) return -1; } + /* jited_prog_insns is only valid if bpil_offs_to_addr() converted it */ + if (!(node->info_linear->arrays & (1UL << PERF_BPIL_JITED_INSNS))) { + dso__data(dso)->status = DSO_DATA_STATUS_ERROR; + return -1; + } + len = node->info_linear->info.jited_prog_len; buf = (u8 *)(uintptr_t)node->info_linear->info.jited_prog_insns; @@ -1988,6 +1994,10 @@ const u8 *dso__read_symbol(struct dso *dso, const char *symfs_filename, return NULL; } info_linear = info_node->info_linear; + if (!(info_linear->arrays & (1UL << PERF_BPIL_JITED_INSNS))) { + errno = SYMBOL_ANNOTATE_ERRNO__BPF_MISSING_BTF; + return NULL; + } assert(len <= info_linear->info.jited_prog_len); *out_buf_len = len; return (const u8 *)(uintptr_t)(info_linear->info.jited_prog_insns); diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 7b641a569af13..7bea1c41ced79 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -2058,8 +2058,7 @@ static void print_bpf_prog_info(struct feat_fd *ff __maybe_unused, FILE *fp) node = rb_entry(next, struct bpf_prog_info_node, rb_node); next = rb_next(&node->rb_node); - __bpf_event__print_bpf_prog_info(&node->info_linear->info, - env, fp); + __bpf_event__print_bpf_prog_info(node->info_linear, env, fp); } up_read(&env->bpf_progs.lock); From 77961e12ea16d9f8ba568c652ed2848c2a728b75 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 13 Jun 2026 15:55:33 -0300 Subject: [PATCH 1776/2758] perf dso: Set standard errno on decompression failure [ Upstream commit 1a5f9334a45a6b0c1cd7341cc72a3b87adad1d27 ] dso__get_filename() sets errno to a negative custom DSO_LOAD_ERRNO value when kernel module decompression fails: errno = *dso__load_errno(dso); /* e.g. -9996 */ The caller __open_dso() then computes fd = -errno, producing a large positive value (9996) that looks like a valid file descriptor. This can cause close_data_fd() to close an unrelated fd used by another subsystem. Set errno to EIO instead. The detailed error code is already stored in dso__load_errno(dso) for diagnostic messages. Fixes: 1d6b3c9ba756a513 ("perf tools: Decompress kernel module when reading DSO data") Reported-by: sashiko-bot Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/dso.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 3547cbd5ca8d2..170637a9b8e0a 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -600,7 +600,13 @@ static char *dso__get_filename(struct dso *dso, const char *root_dir, size_t len = sizeof(newpath); if (dso__decompress_kmodule_path(dso, name, newpath, len) < 0) { - errno = *dso__load_errno(dso); + /* + * Use a standard errno value, not the negative custom + * DSO_LOAD_ERRNO stored in dso__load_errno(dso): + * __open_dso() computes fd = -errno, so a negative + * errno produces a positive fd that looks valid. + */ + errno = EIO; goto out; } From e2ab48e8591d7bbd3981db62d3acf335085ef52d Mon Sep 17 00:00:00 2001 From: Sen Wang Date: Tue, 16 Jun 2026 18:33:22 -0500 Subject: [PATCH 1777/2758] ASoC: tlv320aic3x: restrict CLKDIV bypass Q values in dual-rate mode [ Upstream commit fdf043f5f3bae150b678feae3d7bb1beed87ec14 ] The datasheet documents that when the PLL is disabled and dual-rate mode is enabled, only Q values {4, 8, 9, 12, 16} are valid for the CLKDIV bypass path; all other Q values produce invalid bitclock output. The existing loop iterates Q from 2 to 17 without this restriction, causing silent audio failure when an out-of-spec Q is picked. Restrict the Q search to the allowed set in dual-rate mode. Fixes: 4f9c16ccfa26 ("[ALSA] soc - tlv320aic3x - revisit clock setup") Suggested-by: Mir Jeffres Signed-off-by: Sen Wang Link: https://patch.msgid.link/20260616233322.873081-1-sen@ti.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/tlv320aic3x.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index ce22298b43efc..f8e847d28258b 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1049,11 +1049,13 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { + static const u8 dual_rate_q[] = {4, 8, 9, 12, 16}; struct snd_soc_component *component = dai->component; struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component); int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0; u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1; u16 d, pll_d = 1; + bool dual_rate; int clk; int width = aic3x->slot_width; @@ -1079,14 +1081,25 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, /* Fsref can be 44100 or 48000 */ fsref = (params_rate(params) % 11025 == 0) ? 44100 : 48000; + dual_rate = params_rate(params) >= 64000; /* Try to find a value for Q which allows us to bypass the PLL and * generate CODEC_CLK directly. */ - for (pll_q = 2; pll_q < 18; pll_q++) - if (aic3x->sysclk / (128 * pll_q) == fsref) { - bypass_pll = 1; - break; + if (dual_rate) { + for (int i = 0; i < ARRAY_SIZE(dual_rate_q); i++) { + pll_q = dual_rate_q[i]; + if (aic3x->sysclk / (128 * pll_q) == fsref) { + bypass_pll = 1; + break; + } } + } else { + for (pll_q = 2; pll_q < 18; pll_q++) + if (aic3x->sysclk / (128 * pll_q) == fsref) { + bypass_pll = 1; + break; + } + } if (bypass_pll) { pll_q &= 0xf; @@ -1106,13 +1119,13 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, * right DAC to right channel input */ data = (LDAC2LCH | RDAC2RCH); data |= (fsref == 44100) ? FSREF_44100 : FSREF_48000; - if (params_rate(params) >= 64000) + if (dual_rate) data |= DUAL_RATE_MODE; snd_soc_component_write(component, AIC3X_CODEC_DATAPATH_REG, data); /* codec sample rate select */ data = (fsref * 20) / params_rate(params); - if (params_rate(params) < 64000) + if (!dual_rate) data /= 2; data /= 5; data -= 2; From 1d12ae8b079e173107abec0e5817332954c8e95f Mon Sep 17 00:00:00 2001 From: Yunxiang Li Date: Thu, 4 Jun 2026 12:59:11 -0400 Subject: [PATCH 1778/2758] drm/amdkfd: Avoid double-unpin of DOORBELL/MMIO BOs on free [ Upstream commit 3f0cc1735273a57c5116710cf0202e12152f59cc ] amdgpu_amdkfd_gpuvm_free_memory_of_gpu() unpinned DOORBELL and MMIO remap BOs (which are pinned at allocation time) before checking whether the BO is still mapped to the GPU. When the BO is still mapped, the function returns -EBUSY and leaves the BO alive, but it has already been unpinned. The BO is then unpinned again when it is finally freed during process teardown, triggering a ttm_bo_unpin() underflow warning: WARNING: CPU: 18 PID: 15066 at ttm/ttm_bo.c:650 amdttm_bo_unpin+0x6d/0x80 [amdttm] Workqueue: kfd_process_wq kfd_process_wq_release [amdgpu] RIP: 0010:amdttm_bo_unpin+0x6d/0x80 [amdttm] Call Trace: amdgpu_bo_unpin+0x1a/0x90 [amdgpu] amdgpu_amdkfd_gpuvm_unpin_bo+0x31/0xb0 [amdgpu] amdgpu_amdkfd_gpuvm_free_memory_of_gpu+0x3bf/0x460 [amdgpu] kfd_process_free_outstanding_kfd_bos+0xd4/0x170 [amdgpu] kfd_process_wq_release+0x109/0x1b0 [amdgpu] process_one_work+0x1e2/0x3b0 worker_thread+0x50/0x3a0 kthread+0xdd/0x100 ret_from_fork+0x29/0x50 Move the unpin after the mapped_to_gpu_memory check so it only happens once we are committed to freeing the BO. Fixes: d25e35bc26c3 ("drm/amdgpu: Pin MMIO/DOORBELL BO's in GTT domain") Signed-off-by: Yunxiang Li Reviewed-by: Felix Kuehling Signed-off-by: Alex Deucher (cherry picked from commit 927c5b2defb9b09856444d94bebfd056a002bd75) Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c index 72a5a29e63f6d..abc3194889692 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c @@ -1914,13 +1914,6 @@ int amdgpu_amdkfd_gpuvm_free_memory_of_gpu( mutex_lock(&mem->lock); - /* Unpin MMIO/DOORBELL BO's that were pinned during allocation */ - if (mem->alloc_flags & - (KFD_IOC_ALLOC_MEM_FLAGS_DOORBELL | - KFD_IOC_ALLOC_MEM_FLAGS_MMIO_REMAP)) { - amdgpu_amdkfd_gpuvm_unpin_bo(mem->bo); - } - mapped_to_gpu_memory = mem->mapped_to_gpu_memory; is_imported = mem->is_imported; mutex_unlock(&mem->lock); @@ -1934,6 +1927,15 @@ int amdgpu_amdkfd_gpuvm_free_memory_of_gpu( return -EBUSY; } + /* At this point the BO is guaranteed to be freed, so unpin the + * MMIO/DOORBELL BOs that were pinned during allocation. + */ + if (mem->alloc_flags & + (KFD_IOC_ALLOC_MEM_FLAGS_DOORBELL | + KFD_IOC_ALLOC_MEM_FLAGS_MMIO_REMAP)) { + amdgpu_amdkfd_gpuvm_unpin_bo(mem->bo); + } + /* Make sure restore workers don't access the BO any more */ mutex_lock(&process_info->lock); if (!list_empty(&mem->validate_list)) From 7f20ce7b2bcf6b85b8e160366ec0f2a3e55926c3 Mon Sep 17 00:00:00 2001 From: Qiang Yu Date: Tue, 26 May 2026 14:45:48 +0800 Subject: [PATCH 1779/2758] drm/amdgpu: initialize iter.start in amdgpu_devcoredump_format [ Upstream commit 1c2d7a656655a50bd1b7227fb26d173959a1955d ] This fixes read /sys/class/drm/cardN/device/devcoredump/data return empty content sometimes. amdgpu_devcoredump_format() leaves struct drm_print_iterator's .start field uninitialized on the stack before passing it to drm_coredump_printer(). __drm_puts_coredump() compares the running .offset against .start to decide whether to skip or copy each chunk: if (iterator->offset < iterator->start) { if (iterator->offset + len <= iterator->start) { iterator->offset += len; return; } ... } Fixes: 4bbba79a7f1d ("drm/amdgpu: move devcoredump generation to a worker") Acked-by: Alex Deucher Signed-off-by: Qiang Yu Signed-off-by: Alex Deucher (cherry picked from commit cd6397b7af8262a380e188dc32e9de11ff897ed2) Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c index 3d5a2abf27c63..f89f3a37a45b1 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c @@ -229,6 +229,7 @@ amdgpu_devcoredump_format(char *buffer, size_t count, struct amdgpu_coredump_inf sizing_pass = buffer == NULL; iter.data = buffer; + iter.start = 0; iter.offset = 0; iter.remain = count; From db70b4a08211b9134d0ed65216a8f8fc7e783fbb Mon Sep 17 00:00:00 2001 From: Roman Li Date: Wed, 20 May 2026 16:50:34 -0400 Subject: [PATCH 1780/2758] drm/amd/display: Skip PHY SSC reduction on some 8K panels [ Upstream commit fe7945d092a1d3c340febc2ab176cee50d0f6c80 ] [Why] Some 8K displays cannot tolerate the reduced phy ssc value at high link utilization and show corruption or black screen. [How] Add an EDID panel-id quirk to utilize existing skip_phy_ssc_reduction flag. To pass the link into the quirk handler, change the signature of apply_edid_quirks() to take link as an argument. The dev local in dm_helpers_parse_edid_caps() becomes unused and is removed. Fixes: 5fa62c87cffd ("drm/amd/display: Add option to disable PHY SSC reduction on transmitter enable") Reviewed-by: Alex Hung Signed-off-by: Roman Li Signed-off-by: Aurabindo Pillai Tested-by: Dan Wheeler Signed-off-by: Alex Deucher (cherry picked from commit 144169e7be0831e09958a906d08d1856751aa6c6) Signed-off-by: Sasha Levin --- .../drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c index a3cb05490dc91..ee214f7592e50 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c @@ -95,8 +95,11 @@ static u32 edid_extract_panel_id(struct edid *edid) (u32)EDID_PRODUCT_ID(edid); } -static void apply_edid_quirks(struct drm_device *dev, struct edid *edid, struct dc_edid_caps *edid_caps) +static void apply_edid_quirks(struct dc_link *link, struct edid *edid, + struct dc_edid_caps *edid_caps) { + struct amdgpu_dm_connector *aconnector = link->priv; + struct drm_device *dev = aconnector->base.dev; uint32_t panel_id = edid_extract_panel_id(edid); switch (panel_id) { @@ -126,6 +129,11 @@ static void apply_edid_quirks(struct drm_device *dev, struct edid *edid, struct drm_dbg_driver(dev, "Disabling VSC on monitor with panel id %X\n", panel_id); edid_caps->panel_patch.disable_colorimetry = true; break; + /* Workaround for monitors that get corrupted by the PHY SSC reduction */ + case drm_edid_encode_panel_id('D', 'E', 'L', 0x4147): + drm_dbg_driver(dev, "Skip PHY SSC reduction on panel id %X\n", panel_id); + link->wa_flags.skip_phy_ssc_reduction = true; + break; default: return; } @@ -147,7 +155,6 @@ enum dc_edid_status dm_helpers_parse_edid_caps( { struct amdgpu_dm_connector *aconnector = link->priv; struct drm_connector *connector = &aconnector->base; - struct drm_device *dev = connector->dev; struct edid *edid_buf = edid ? (struct edid *) edid->raw_edid : NULL; struct cea_sad *sads; int sad_count = -1; @@ -181,7 +188,7 @@ enum dc_edid_status dm_helpers_parse_edid_caps( if (edid_caps->edid_hdmi) populate_hdmi_info_from_connector(&connector->display_info.hdmi, edid_caps); - apply_edid_quirks(dev, edid_buf, edid_caps); + apply_edid_quirks(link, edid_buf, edid_caps); sad_count = drm_edid_to_sad((struct edid *) edid->raw_edid, &sads); if (sad_count <= 0) From db803223edc4def4d97a753191fb3a7ffe3981f1 Mon Sep 17 00:00:00 2001 From: Matthew Schwartz Date: Thu, 11 Jun 2026 08:44:38 -0700 Subject: [PATCH 1781/2758] drm/amd/display: Fix mem_type change detection for async flips [ Upstream commit 8e792f018e10e68f488f279fbd4f38009a2e066d ] [Why] amdgpu_dm_crtc_mem_type_changed() fetches the "old" and "new" plane state with two drm_atomic_get_plane_state() calls, which both return the new state. It compares a state against itself, so it never detects a mem_type change and never rejects the async flip. On DCN 3.0.1, this shows up as intermittent corruption when a single DCC plane is scanned out with immediate flips under gamescope and its buffer moves between the VRAM carveout and GTT. [How] Use drm_atomic_get_old_plane_state() and drm_atomic_get_new_plane_state() to compare the actual old and new states. These return NULL rather than an error pointer for a plane that is not part of the commit, so the IS_ERR() check becomes a NULL check that skips those planes, such as an unmodified cursor still in the CRTC's plane_mask. Fixes: 4caacd1671b7 ("drm/amd/display: Do not elevate mem_type change to full update") Reviewed-by: Harry Wentland Reviewed-by: Melissa Wen Signed-off-by: Matthew Schwartz Signed-off-by: Alex Deucher (cherry picked from commit 13158e5dbd896281f3e9982b5437cffa5fd621b2) Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index f8c13bad4ac23..1ecd7bef9ed85 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -12553,13 +12553,11 @@ static bool amdgpu_dm_crtc_mem_type_changed(struct drm_device *dev, struct drm_plane_state *new_plane_state, *old_plane_state; drm_for_each_plane_mask(plane, dev, crtc_state->plane_mask) { - new_plane_state = drm_atomic_get_plane_state(state, plane); - old_plane_state = drm_atomic_get_plane_state(state, plane); + new_plane_state = drm_atomic_get_new_plane_state(state, plane); + old_plane_state = drm_atomic_get_old_plane_state(state, plane); - if (IS_ERR(new_plane_state) || IS_ERR(old_plane_state)) { - drm_err(dev, "Failed to get plane state for plane %s\n", plane->name); - return false; - } + if (!old_plane_state || !new_plane_state) + continue; if (old_plane_state->fb && new_plane_state->fb && get_mem_type(old_plane_state->fb) != get_mem_type(new_plane_state->fb)) From e33a3bd5cb8d0cf1557dee014115f812c9686130 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Fri, 12 Jun 2026 21:22:04 -0500 Subject: [PATCH 1782/2758] drm/amdkfd: fix list_del corruption in kfd_criu_resume_svm [ Upstream commit 8fa5655da368d0306c03e9dc9cda8ae2a7840926 ] The cleanup tail of kfd_criu_resume_svm() walks svms->criu_svm_metadata_list and kfree()s each struct criu_svm_metadata without removing it from the list. The list head is left pointing at freed kmalloc-96 objects. A second AMDKFD_IOC_CRIU_OP from the same process re-enters: list_empty() reads the dangling ->next (use-after-free), the loop walks freed entries, and each is kfree()'d again (double-free). This is reachable by an unprivileged render-group user via /dev/kfd with no capabilities required. Add list_del() before the kfree() so the list is properly emptied. The list_for_each_entry_safe() iterator already caches the next pointer, so unlinking during the walk is safe. Fixes: 2a909ae71871 ("drm/amdkfd: CRIU resume shared virtual memory ranges") Reviewed-by: Alex Deucher Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher (cherry picked from commit 6322d278a298e2c1430b9d2697743d3a04b788b1) Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdkfd/kfd_svm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c index 3841943da5ece..0b54ad8036b64 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c @@ -4115,6 +4115,7 @@ int kfd_criu_resume_svm(struct kfd_process *p) list_for_each_entry_safe(criu_svm_md, next, &svms->criu_svm_metadata_list, list) { pr_debug("freeing criu_svm_md[]\n\tstart: 0x%llx\n", criu_svm_md->data.start_addr); + list_del(&criu_svm_md->list); kfree(criu_svm_md); } From bc88744dc55615a1be8ab1d07ff2cec1d31adfa1 Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Mon, 8 Jun 2026 16:22:35 -0300 Subject: [PATCH 1783/2758] drm/amdgpu: initialize irq.lock spinlock earlier [ Upstream commit a2b270c0ecf6d95bcd14ef4c20d0301a88143ff5 ] If there is an early failure during amdgpu probe, like missing firmware, it will end up calling amdgpu_irq_disable_all, which takes irq.lock spinlock without it being initialized. Initializing irq.lock earlier at amdgpu_device_init fixes the issue. [ 79.334079] INFO: trying to register non-static key. [ 79.334081] The code is fine but needs lockdep annotation, or maybe [ 79.334083] you didn't initialize this object before use? [ 79.334084] turning off the locking correctness validator. [ 79.334088] CPU: 2 UID: 0 PID: 1819 Comm: bash Not tainted 7.1.0-rc5-gfd06300b2348 #96 PREEMPT 8e8f461221633dae3c832d6689eaf0546c0ed4cd [ 79.334092] Hardware name: Valve Jupiter/Jupiter, BIOS F7A0133 08/05/2024 [ 79.334094] Call Trace: [ 79.334095] [ 79.334097] dump_stack_lvl+0x5d/0x80 [ 79.334103] register_lock_class+0x7af/0x7c0 [ 79.334109] __lock_acquire+0x416/0x2610 [ 79.334114] lock_acquire+0xcf/0x310 [ 79.334117] ? amdgpu_irq_disable_all+0x3b/0xf0 [amdgpu c88bab43d391d519ad0d5c8e5a099b4aceefa180] [ 79.334503] ? _raw_spin_lock_irqsave+0x53/0x60 [ 79.334508] _raw_spin_lock_irqsave+0x3f/0x60 [ 79.334510] ? amdgpu_irq_disable_all+0x3b/0xf0 [amdgpu c88bab43d391d519ad0d5c8e5a099b4aceefa180] [ 79.334881] amdgpu_irq_disable_all+0x3b/0xf0 [amdgpu c88bab43d391d519ad0d5c8e5a099b4aceefa180] [ 79.335240] amdgpu_device_fini_hw+0x90/0x32c [amdgpu c88bab43d391d519ad0d5c8e5a099b4aceefa180] [ 79.335704] amdgpu_driver_load_kms.cold+0x22/0x44 [amdgpu c88bab43d391d519ad0d5c8e5a099b4aceefa180] [ 79.336159] amdgpu_pci_probe+0x204/0x440 [amdgpu c88bab43d391d519ad0d5c8e5a099b4aceefa180] [ 79.336494] local_pci_probe+0x3c/0x80 [ 79.336500] pci_call_probe+0x55/0x2e0 [ 79.336505] ? _raw_spin_unlock+0x2d/0x50 [ 79.336508] ? pci_match_device+0x157/0x180 [ 79.336512] pci_device_probe+0x9b/0x170 [ 79.336516] really_probe+0xd5/0x370 [ 79.336521] __driver_probe_device+0x84/0x150 [ 79.336525] device_driver_attach+0x47/0xb0 [ 79.336528] bind_store+0x73/0xc0 [ 79.336531] kernfs_fop_write_iter+0x176/0x250 [ 79.336536] vfs_write+0x24d/0x560 [ 79.336542] ksys_write+0x71/0xe0 [ 79.336546] do_syscall_64+0x122/0x710 [ 79.336550] ? do_syscall_64+0xd1/0x710 [ 79.336553] entry_SYSCALL_64_after_hwframe+0x4b/0x53 [ 79.336557] RIP: 0033:0x7f92fd675006 [ 79.336561] Code: 5d e8 41 8b 93 08 03 00 00 59 5e 48 83 f8 fc 75 19 83 e2 39 83 fa 08 75 11 e8 26 ff ff ff 66 0f 1f 44 00 00 48 8b 45 10 0f 05 <48> 8b 5d f8 c9 c3 0f 1f 40 00 f3 0f 1e fa 55 48 89 e5 48 83 ec 08 [ 79.336562] RSP: 002b:00007ffe4fa867a0 EFLAGS: 00000202 ORIG_RAX: 0000000000000001 [ 79.336565] RAX: ffffffffffffffda RBX: 000000000000000d RCX: 00007f92fd675006 [ 79.336567] RDX: 000000000000000d RSI: 000055b2dfce59b0 RDI: 0000000000000001 [ 79.336568] RBP: 00007ffe4fa867c0 R08: 0000000000000000 R09: 0000000000000000 [ 79.336569] R10: 0000000000000000 R11: 0000000000000202 R12: 000000000000000d [ 79.336570] R13: 000055b2dfce59b0 R14: 00007f92fd7ca5c0 R15: 000055b2dfdbaf70 [ 79.336574] Fixes: 9950cda2a018 ("drm/amdgpu: drop the drm irq pre/post/un install callbacks") Reviewed-by: Tvrtko Ursulin Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: Alex Deucher (cherry picked from commit 7dba3e10ecdeec85208e255853fcd3890880b10e) Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 2 ++ drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index feab90e3efd1e..4f8489626624d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -3747,6 +3747,8 @@ int amdgpu_device_init(struct amdgpu_device *adev, mutex_init(&adev->gfx.workload_profile_mutex); mutex_init(&adev->vcn.workload_profile_mutex); + spin_lock_init(&adev->irq.lock); + amdgpu_device_init_apu_flags(adev); r = amdgpu_device_check_arguments(adev); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c index 254a4e983f40a..40b8506ac66f9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c @@ -309,8 +309,6 @@ int amdgpu_irq_init(struct amdgpu_device *adev) unsigned int irq, flags; int r; - spin_lock_init(&adev->irq.lock); - /* Enable MSI if not disabled by module parameter */ adev->irq.msi_enabled = false; From e5c6debdad284f361f8a15255664930e00dc1d07 Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Mon, 15 Jun 2026 17:11:00 +0200 Subject: [PATCH 1784/2758] net: ethernet: mtk_eth_soc: fix supported_interface set after phylink_create [ Upstream commit e4b4d8410c7ccea25f4b332a077c6b9f5d263228 ] Everything configured in phylink_config it's assumed to be set before calling phylink_create() to permit correct parsing of all the different modes and capabilities. Commit 51cf06ddafc9 ("net: ethernet: mtk_eth_soc: add support for MT7988 internal 2.5G PHY") while introducing support for 2.5G phy for MT7988, probably due to an auto-rebase, placed the configuration of the INTERNAL interface mode for the supported_interfaces for phylink_config right after phylink_create() introducing a possible problem with supported interfaces parsing. While this doesn't currently create any problem/bug, move setting this bit before phylink_create() to prevent any possible regression in future code change in phylink core. Fixes: 51cf06ddafc9 ("net: ethernet: mtk_eth_soc: add support for MT7988 internal 2.5G PHY") Signed-off-by: Christian Marangi Reviewed-by: Maxime Chevallier Reviewed-by: Daniel Golle Link: https://patch.msgid.link/20260615151106.15438-1-ansuelsmth@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 7d771168b9901..5d291e50a47bb 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -4960,6 +4960,11 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) mac_ops = &rt5350_phylink_ops; + if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_2P5GPHY) && + id == MTK_GMAC2_ID) + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + mac->phylink_config.supported_interfaces); + phylink = phylink_create(&mac->phylink_config, of_fwnode_handle(mac->of_node), phy_mode, mac_ops); @@ -4970,11 +4975,6 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) mac->phylink = phylink; - if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_2P5GPHY) && - id == MTK_GMAC2_ID) - __set_bit(PHY_INTERFACE_MODE_INTERNAL, - mac->phylink_config.supported_interfaces); - SET_NETDEV_DEV(eth->netdev[id], eth->dev); eth->netdev[id]->watchdog_timeo = 5 * HZ; eth->netdev[id]->netdev_ops = &mtk_netdev_ops; From 043ed6924c632954f579054b81a68f2c847f29d4 Mon Sep 17 00:00:00 2001 From: Tianchen Ding Date: Mon, 1 Jun 2026 10:32:51 +0800 Subject: [PATCH 1785/2758] selftests/ftrace: Fix trace_marker_raw test on 64K page kernels [ Upstream commit 65f26d15f7db80b0a3f995c518cdddb50e6dea99 ] On ARM64 kernels with 64K pages, the trace_marker_raw test fails because bash's printf builtin uses stdio buffering which splits output into multiple small write() calls to the tracefs file. Since each individual write is within TRACE_MARKER_MAX_SIZE (4096), they all succeed, causing the "too big" write test to incorrectly pass. Fix by writing through dd with iflag=fullblock to guarantee a single atomic write() syscall to trace_marker_raw. Link: https://lore.kernel.org/r/20260601023251.1916483-1-dtcccc@linux.alibaba.com Fixes: 37f46601383a ("selftests/tracing: Add basic test for trace_marker_raw file") Signed-off-by: Tianchen Ding Reviewed-by: Steven Rostedt Signed-off-by: Shuah Khan Signed-off-by: Sasha Levin --- .../ftrace/test.d/00basic/trace_marker_raw.tc | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/ftrace/test.d/00basic/trace_marker_raw.tc b/tools/testing/selftests/ftrace/test.d/00basic/trace_marker_raw.tc index 8e905d4fe6dd2..f985ff391463a 100644 --- a/tools/testing/selftests/ftrace/test.d/00basic/trace_marker_raw.tc +++ b/tools/testing/selftests/ftrace/test.d/00basic/trace_marker_raw.tc @@ -36,15 +36,23 @@ make_str() { data=`printf -- 'X%.0s' $(seq $cnt)` - printf "${val}${data}" + # Return escape-sequence text (e.g. "\003\000..."); the caller + # converts to binary. Shell command substitution strips NUL bytes, + # so the binary form cannot survive being captured into a variable. + printf '%s' "${val}${data}" } write_buffer() { id=$1 size=$2 - # write the string into the raw marker - make_str $id $size > trace_marker_raw + str=`make_str $id $size` + len=`printf "$str" | wc -c` + # Pipe through dd to ensure a single atomic write() syscall + # on architectures with 64K pages, where shell's printf builtin + # uses stdio buffering which may split the output into multiple + # writes. + printf "$str" | dd of=trace_marker_raw bs=$len iflag=fullblock } From 452ec5058ea4ed63adae8d6beeac9ee687fe8061 Mon Sep 17 00:00:00 2001 From: Ratheesh Kannoth Date: Mon, 15 Jun 2026 08:37:04 +0530 Subject: [PATCH 1786/2758] octeontx2-pf: Fix leak of SQ timestamp buffer on teardown [ Upstream commit a056db30de92945ff8ee6033096678bfbae878e3 ] The send-queue timestamp ring is allocated with qmem_alloc() when timestamping is used, but otx2_free_sq_res() never freed sq->timestamps, leaking that memory across ifdown and device removal. Add the missing qmem_free() alongside the other SQ companion buffers. Fixes: c9c12d339d93 ("octeontx2-pf: Add support for PTP clock") Cc: Aleksey Makarov Signed-off-by: Ratheesh Kannoth Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260615030704.504536-1-rkannoth@marvell.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index f9fbf0c176482..0c2da974ac6d5 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -1578,6 +1578,7 @@ static void otx2_free_sq_res(struct otx2_nic *pf) qmem_free(pf->dev, sq->sqe_ring); qmem_free(pf->dev, sq->cpt_resp); qmem_free(pf->dev, sq->tso_hdrs); + qmem_free(pf->dev, sq->timestamps); kfree(sq->sg); kfree(sq->sqb_ptrs); } From e19d38d397d440a0dbf2eeb36c93d5fa0955381e Mon Sep 17 00:00:00 2001 From: Ratheesh Kannoth Date: Mon, 15 Jun 2026 09:01:57 +0530 Subject: [PATCH 1787/2758] octeontx2-af: npc: Log successful MCAM drop-on-non-hit install at debug level [ Upstream commit 4f6ac65e81625165257131ec2574bb6bb09bd7d8 ] npc_install_mcam_drop_rule() used dev_err() after a successful rvu_mbox_handler_npc_mcam_write_entry() call, so normal installs appeared as errors in dmesg. Use dev_dbg() for the success path and keep dev_err() for real failures. Fixes: 3571fe07a090 ("octeontx2-af: Drop rules for NPC MCAM") Signed-off-by: Ratheesh Kannoth Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260615033157.535237-1-rkannoth@marvell.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c index 34f1e066707bd..3d4d3ab5183b8 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c @@ -2215,7 +2215,7 @@ int npc_install_mcam_drop_rule(struct rvu *rvu, int mcam_idx, u16 *counter_idx, return err; } - dev_err(rvu->dev, + dev_dbg(rvu->dev, "%s: Installed single drop on non hit rule at %d, cntr=%d\n", __func__, mcam_idx, req.cntr); From 794a0d8bdbb39e083ed42caccb86d687a9b53570 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 15 Jun 2026 17:30:46 -0700 Subject: [PATCH 1788/2758] net: psample: fix info leak in PSAMPLE_ATTR_DATA [ Upstream commit aedd02af1f8b0bceb7f42f5a21c41634ca9ed390 ] psample open codes nla_put() presumably to avoid wiping the data with 0s just to override it with packet data. This open coding is missing clearing the pad, however, each netlink attr is padded to 4B and data_len may not be divisible by 4B. Fixes: 6ae0a6286171 ("net: Introduce psample, a new genetlink channel for packet sampling") Reported-by: Weiming Shi Reviewed-by: Jiri Pirko Link: https://patch.msgid.link/20260616003046.1099490-1-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/psample/psample.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/psample/psample.c b/net/psample/psample.c index 7763662036fb7..c112e1f0ccacb 100644 --- a/net/psample/psample.c +++ b/net/psample/psample.c @@ -476,15 +476,17 @@ void psample_sample_packet(struct psample_group *group, goto error; if (data_len) { - int nla_len = nla_total_size(data_len); + int nla_len = nla_attr_size(data_len); struct nlattr *nla; nla = skb_put(nl_skb, nla_len); nla->nla_type = PSAMPLE_ATTR_DATA; - nla->nla_len = nla_attr_size(data_len); + nla->nla_len = nla_len; if (skb_copy_bits(skb, 0, nla_data(nla), data_len)) goto error; + + skb_put_zero(nl_skb, nla_padlen(data_len)); } #ifdef CONFIG_INET From f09a245f33e567b604efa1960b7a2d25dd9c8713 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Mon, 15 Jun 2026 15:36:30 -0400 Subject: [PATCH 1789/2758] sctp: hold socket lock when dumping endpoints in sctp_diag [ Upstream commit 7d8297e26b4e20b5d1c3c3fe51fe81a1c7fbc823 ] SCTP_DIAG endpoint dumping was traversing endpoint address lists without holding lock_sock(), while those lists could change concurrently via socket operations (e.g., bindx changes). This creates a race where nla_reserve() counts addresses under RCU protection, but the subsequent copy may see fewer entries, potentially leaking uninitialized memory to userspace. Fix this by: - Taking a reference on each endpoint during hash traversal - Moving socket operations (lock_sock()) outside read_lock_bh() - Serializing address list access during dump - Reworking sctp_for_each_endpoint() to support restart-based traversal with (net, pos) tracking Also: - Add WARN_ON_ONCE() for inconsistent address counts - Fix idiag_states filtering for LISTEN vs association cases - Skip dumping endpoints being freed (ep->base.dead) - Move dump position tracking into iterator, removing cb->args[4] and its comment for sctp_ep_dump()., - Update the comment for cb->args[4] and remove the comment for unused cb->args[5] for sctp_sock_dump(). Note: traversal is restart-based and may re-scan buckets multiple times, but this is acceptable due to small bucket sizes and required to support sleeping-safe callbacks. This issue was reported by Nico Yip (@_cyeaa_) working with TrendAI Zero Day Initiative. Reported-by: Zero Day Initiative Fixes: 8f840e47f190 ("sctp: add the sctp_diag.c file") Signed-off-by: Xin Long Link: https://patch.msgid.link/4c1b49ab87e0f7d552ebd8172b364b1994e913c9.1781552190.git.lucien.xin@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- include/net/sctp/sctp.h | 3 +- net/sctp/diag.c | 67 ++++++++++++++++++++--------------------- net/sctp/socket.c | 29 +++++++++++++----- 3 files changed, 56 insertions(+), 43 deletions(-) diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 58242b37b47a1..cd82b05354a38 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -111,7 +111,8 @@ int sctp_transport_lookup_process(sctp_callback_t cb, struct net *net, const union sctp_addr *paddr, void *p, int dif); int sctp_transport_traverse_process(sctp_callback_t cb, sctp_callback_t cb_done, struct net *net, int *pos, void *p); -int sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *), void *p); +int sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *), + struct net *net, int *pos, void *p); int sctp_get_sctp_info(struct sock *sk, struct sctp_association *asoc, struct sctp_info *info); diff --git a/net/sctp/diag.c b/net/sctp/diag.c index d758f5c3e06e5..c2a0de2adf6fd 100644 --- a/net/sctp/diag.c +++ b/net/sctp/diag.c @@ -92,6 +92,7 @@ static int inet_diag_msg_sctpladdrs_fill(struct sk_buff *skb, if (!--addrcnt) break; } + WARN_ON_ONCE(addrcnt); rcu_read_unlock(); return 0; @@ -373,42 +374,39 @@ static int sctp_ep_dump(struct sctp_endpoint *ep, void *p) struct sk_buff *skb = commp->skb; struct netlink_callback *cb = commp->cb; const struct inet_diag_req_v2 *r = commp->r; - struct net *net = sock_net(skb->sk); struct inet_sock *inet = inet_sk(sk); int err = 0; - if (!net_eq(sock_net(sk), net)) + lock_sock(sk); + if (ep->base.dead) goto out; - if (cb->args[4] < cb->args[1]) - goto next; - - if (!(r->idiag_states & TCPF_LISTEN) && !list_empty(&ep->asocs)) - goto next; + /* Skip eps with assocs if non-LISTEN states were requested, since + * they'll be dumped by sctp_sock_dump() during assoc traversal. + */ + if ((r->idiag_states & ~(TCPF_LISTEN | TCPF_CLOSE)) && + !list_empty(&ep->asocs)) + goto out; if (r->sdiag_family != AF_UNSPEC && sk->sk_family != r->sdiag_family) - goto next; + goto out; if (r->id.idiag_sport != inet->inet_sport && r->id.idiag_sport) - goto next; + goto out; if (r->id.idiag_dport != inet->inet_dport && r->id.idiag_dport) - goto next; - - if (inet_sctp_diag_fill(sk, NULL, skb, r, - sk_user_ns(NETLINK_CB(cb->skb).sk), - NETLINK_CB(cb->skb).portid, - cb->nlh->nlmsg_seq, NLM_F_MULTI, - cb->nlh, commp->net_admin) < 0) { - err = 2; goto out; - } -next: - cb->args[4]++; + + err = inet_sctp_diag_fill(sk, NULL, skb, r, + sk_user_ns(NETLINK_CB(cb->skb).sk), + NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + cb->nlh, commp->net_admin); out: + release_sock(sk); return err; } @@ -479,41 +477,40 @@ static void sctp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, .r = r, .net_admin = netlink_net_capable(cb->skb, CAP_NET_ADMIN), }; - int pos = cb->args[2]; + int pos; /* eps hashtable dumps * args: * 0 : if it will traversal listen sock * 1 : to record the sock pos of this time's traversal - * 4 : to work as a temporary variable to traversal list */ if (cb->args[0] == 0) { - if (!(idiag_states & TCPF_LISTEN)) - goto skip; - if (sctp_for_each_endpoint(sctp_ep_dump, &commp)) - goto done; -skip: + if (idiag_states & TCPF_LISTEN) { + pos = cb->args[1]; + if (sctp_for_each_endpoint(sctp_ep_dump, net, &pos, + &commp)) { + cb->args[1] = pos; + return; + } + } cb->args[0] = 1; cb->args[1] = 0; - cb->args[4] = 0; } + if (!(idiag_states & ~(TCPF_LISTEN | TCPF_CLOSE))) + return; + /* asocs by transport hashtable dump * args: * 1 : to record the assoc pos of this time's traversal * 2 : to record the transport pos of this time's traversal * 3 : to mark if we have dumped the ep info of the current asoc - * 4 : to work as a temporary variable to traversal list - * 5 : to save the sk we get from travelsing the tsp list. + * 4 : to track position within ep->asocs list in sctp_sock_dump() */ - if (!(idiag_states & ~(TCPF_LISTEN | TCPF_CLOSE))) - goto done; - + pos = cb->args[2]; sctp_transport_traverse_process(sctp_sock_filter, sctp_sock_dump, net, &pos, &commp); cb->args[2] = pos; - -done: cb->args[1] = cb->args[4]; cb->args[4] = 0; } diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 66e12fb0c646a..c8481461f7d8b 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -5369,24 +5369,39 @@ struct sctp_transport *sctp_transport_get_idx(struct net *net, } int sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *), - void *p) { - int err = 0; - int hash = 0; - struct sctp_endpoint *ep; + struct net *net, int *pos, void *p) { + int err, hash = 0, idx = 0, start; struct sctp_hashbucket *head; + struct sctp_endpoint *ep; for (head = sctp_ep_hashtable; hash < sctp_ep_hashsize; hash++, head++) { + start = idx; +again: read_lock_bh(&head->lock); sctp_for_each_hentry(ep, &head->chain) { - err = cb(ep, p); - if (err) + if (sock_net(ep->base.sk) != net) + continue; + if (idx++ >= *pos) { + sctp_endpoint_hold(ep); break; + } } read_unlock_bh(&head->lock); + + if (ep) { + err = cb(ep, p); + sctp_endpoint_put(ep); + if (err) + return err; + (*pos)++; + + idx = start; + goto again; + } } - return err; + return 0; } EXPORT_SYMBOL_GPL(sctp_for_each_endpoint); From a22356d1f731553e99aa2707dbd38c659bdd28d8 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 17 Jun 2026 22:51:25 -0400 Subject: [PATCH 1790/2758] ALSA: usb-audio: qcom: reject stream disable with no active interface [ Upstream commit bdb640be82e645e2828731648f485224d0c2587b ] handle_uaudio_stream_req() resolves an interface index with info_idx_from_ifnum(), which returns -EINVAL when no interface matches. The enable branch and the response: cleanup label both guard against a negative index, but the disable branch does not: it forms info = &uadev[pcm_card_num].info[info_idx] and dereferences it. uadev[].info is a pointer allocated only when a stream is first enabled, so a negative info_idx on the disable path is unsafe in two ways: - If the card was never enabled, .info is NULL and &info[-EINVAL] is a wild pointer; reading info->data_ep_pipe faults (kernel oops). - If the card was enabled at least once (.info allocated) and the disable names an interface that does not match, &info[-EINVAL] points before the allocation; info->data_ep_pipe / info->sync_ep_pipe are an out-of-bounds slab read and, when non-zero, an out-of-bounds 4-byte write (both pipe fields are cleared to 0). That is memory corruption, not just a NULL dereference. The request is reachable from unprivileged local userspace over AF_QIPCRTR. Reject a disable request with no resolved interface, matching the guard the enable path already has. Fixes: 326bbc348298a ("ALSA: usb-audio: qcom: Introduce QC USB SND offloading support") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Link: https://patch.msgid.link/20260618025126.1862954-2-michael.bommarito@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/usb/qcom/qc_audio_offload.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c index ccb5ffd73a66c..621dca3587f85 100644 --- a/sound/usb/qcom/qc_audio_offload.c +++ b/sound/usb/qcom/qc_audio_offload.c @@ -1647,6 +1647,11 @@ static void handle_uaudio_stream_req(struct qmi_handle *handle, subs->opened = 0; } } else { + if (info_idx < 0) { + ret = -EINVAL; + goto response; + } + info = &uadev[pcm_card_num].info[info_idx]; if (info->data_ep_pipe) { ep = usb_pipe_endpoint(uadev[pcm_card_num].udev, From ec6fb1ecada8ddfc3d0e1728e4792d16ae55dcdb Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 17 Jun 2026 22:51:26 -0400 Subject: [PATCH 1791/2758] ALSA: usb-audio: qcom: clear opened when stream enable fails [ Upstream commit 3c7af07943b2718087ae791cad450af5cf646d90 ] On enable, subs->opened is set before the service_interval is validated; an invalid interval jumps to the response label without clearing it, so the substream is wedged at -EBUSY until a disable or disconnect. Clear subs->opened on the enable error path. Fixes: 326bbc348298a ("ALSA: usb-audio: qcom: Introduce QC USB SND offloading support") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Link: https://patch.msgid.link/20260618025126.1862954-3-michael.bommarito@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/usb/qcom/qc_audio_offload.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c index 621dca3587f85..ed833a43db635 100644 --- a/sound/usb/qcom/qc_audio_offload.c +++ b/sound/usb/qcom/qc_audio_offload.c @@ -1625,8 +1625,13 @@ static void handle_uaudio_stream_req(struct qmi_handle *handle, if (req_msg->service_interval_valid) { ret = get_data_interval_from_si(subs, req_msg->service_interval); - if (ret == -EINVAL) + if (ret == -EINVAL) { + if (req_msg->enable) { + guard(mutex)(&chip->mutex); + subs->opened = 0; + } goto response; + } datainterval = ret; } From aa80fca32cf7e0202bc8090314629c1d5adbb04c Mon Sep 17 00:00:00 2001 From: Mark Tomlinson Date: Thu, 30 Apr 2026 14:16:28 +1200 Subject: [PATCH 1792/2758] PCI: iproc: Restore .map_irq() for the platform bus driver [ Upstream commit 3c2e6cc6affa8acdb99a580be1f8f297edf54204 ] Commit b64aa11eb2dd ("PCI: Set bridge map_irq and swizzle_irq to default functions") moved the assignment of default .map_irq() callback to devm_of_pci_bridge_init() and removed the initialization of 'iproc_pcie::map_irq' in platform bus driver. This led to the callback getting assigned the NULL pointer for platform bus driver, thereby breaking the INTx functionality, since 'iproc_pcie::map_irq' overrides the 'pci_host_bridge::map_irq' callback in iproc_pcie_setup(). This issue only affected the iproc platform bus driver as this driver relies on the default callback for non-PAXC controllers. iproc-brcm driver was already providing the custom mapping function, so it was unaffected. Restore the original (and intended) behaviour to use the default map_irq function by removing the local 'iproc_pcie::map_irq' pointer and directly assigning the 'pci_host_bridge::map_irq' callback in iproc-bcma driver. This ensures that the default 'map_irq' callback is used for platform bus driver and only iproc-brcm driver overrides it with a custom one. Fixes: b64aa11eb2dd ("PCI: Set bridge map_irq and swizzle_irq to default functions") Signed-off-by: Mark Tomlinson [mani: commit log] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Acked-by: Ray Jui Link: https://patch.msgid.link/20260430021628.1343154-1-mark.tomlinson@alliedtelesis.co.nz Signed-off-by: Sasha Levin --- drivers/pci/controller/pcie-iproc-bcma.c | 2 +- drivers/pci/controller/pcie-iproc-platform.c | 2 +- drivers/pci/controller/pcie-iproc.c | 1 - drivers/pci/controller/pcie-iproc.h | 2 -- 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/pci/controller/pcie-iproc-bcma.c b/drivers/pci/controller/pcie-iproc-bcma.c index 99a99900444de..593418c2bc3aa 100644 --- a/drivers/pci/controller/pcie-iproc-bcma.c +++ b/drivers/pci/controller/pcie-iproc-bcma.c @@ -64,7 +64,7 @@ static int iproc_bcma_pcie_probe(struct bcma_device *bdev) if (ret) return ret; - pcie->map_irq = iproc_bcma_pcie_map_irq; + bridge->map_irq = iproc_bcma_pcie_map_irq; bcma_set_drvdata(bdev, pcie); diff --git a/drivers/pci/controller/pcie-iproc-platform.c b/drivers/pci/controller/pcie-iproc-platform.c index 0cb78c583c7ea..4c9a0c4bb923e 100644 --- a/drivers/pci/controller/pcie-iproc-platform.c +++ b/drivers/pci/controller/pcie-iproc-platform.c @@ -98,7 +98,7 @@ static int iproc_pltfm_pcie_probe(struct platform_device *pdev) switch (pcie->type) { case IPROC_PCIE_PAXC: case IPROC_PCIE_PAXC_V2: - pcie->map_irq = NULL; + bridge->map_irq = NULL; break; default: break; diff --git a/drivers/pci/controller/pcie-iproc.c b/drivers/pci/controller/pcie-iproc.c index ccf71993ea359..c22d0aecaaac5 100644 --- a/drivers/pci/controller/pcie-iproc.c +++ b/drivers/pci/controller/pcie-iproc.c @@ -1502,7 +1502,6 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) host->ops = &iproc_pcie_ops; host->sysdata = pcie; - host->map_irq = pcie->map_irq; ret = pci_host_probe(host); if (ret < 0) { diff --git a/drivers/pci/controller/pcie-iproc.h b/drivers/pci/controller/pcie-iproc.h index 969ded03b8c2d..c4443f236ca3b 100644 --- a/drivers/pci/controller/pcie-iproc.h +++ b/drivers/pci/controller/pcie-iproc.h @@ -61,7 +61,6 @@ struct iproc_msi; * @base_addr: PCIe host controller register base physical address * @mem: host bridge memory window resource * @phy: optional PHY device that controls the Serdes - * @map_irq: function callback to map interrupts * @ep_is_internal: indicates an internal emulated endpoint device is connected * @iproc_cfg_read: indicates the iProc config read function should be used * @rej_unconfig_pf: indicates the root complex needs to detect and reject @@ -91,7 +90,6 @@ struct iproc_pcie { phys_addr_t base_addr; struct resource mem; struct phy *phy; - int (*map_irq)(const struct pci_dev *, u8, u8); bool ep_is_internal; bool iproc_cfg_read; bool rej_unconfig_pf; From d654af91739a0b21c17044e8c726274cd9c48d37 Mon Sep 17 00:00:00 2001 From: Quang Nguyen Date: Thu, 18 Jun 2026 09:19:30 +0100 Subject: [PATCH 1793/2758] spi: rpc-if: Use correct device for hardware reinitialization on resume [ Upstream commit 7b25dbafa2fce50b1a48c1d057adb35da3563f9b ] rpcif_spi_resume() currently passes the SPI controller device to rpcif_hw_init(), but the function should be called with the RPC interface device. Retrieve the rpcif private data from the SPI controller and pass rpc->dev instead. Also propagate the return value of rpcif_hw_init() so that a failure during resume is properly reported rather than silently ignored. Fixes: ad4728740bd6 ("spi: rpc-if: Add resume support for RZ/G3E") Signed-off-by: Quang Nguyen Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260618081932.172168-1-biju.das.jz@bp.renesas.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-rpc-if.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-rpc-if.c b/drivers/spi/spi-rpc-if.c index 6edc0c4db854d..c2f9c86c1d8d4 100644 --- a/drivers/spi/spi-rpc-if.c +++ b/drivers/spi/spi-rpc-if.c @@ -206,8 +206,12 @@ static int rpcif_spi_suspend(struct device *dev) static int rpcif_spi_resume(struct device *dev) { struct spi_controller *ctlr = dev_get_drvdata(dev); + struct rpcif *rpc = spi_controller_get_devdata(ctlr); + int ret; - rpcif_hw_init(dev, false); + ret = rpcif_hw_init(rpc->dev, false); + if (ret) + return ret; return spi_controller_resume(ctlr); } From e6b8463b7d791f3886d7584259d6e9f06a69f12e Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Mon, 15 Jun 2026 21:28:37 -0700 Subject: [PATCH 1794/2758] virtio-net: fix len check in receive_big() [ Upstream commit 9e5ad06ea826322ce8c58b4a68442a96f600c3c4 ] receive_big() bounds the device-announced length by (big_packets_num_skbfrags + 1) * PAGE_SIZE. That is still too loose: add_recvbuf_big() sets sg[1] to start at offset sizeof(struct padded_vnet_hdr) into the first page, so the chain actually carries hdr_len + (PAGE_SIZE - sizeof(padded_vnet_hdr)) + big_packets_num_skbfrags * PAGE_SIZE bytes -- 20 bytes less than the check allows for the common hdr_len == 12 case. A malicious virtio backend can announce a len in that gap. page_to_skb() then walks one frag past the page chain, storing a NULL page->private into skb_shinfo()->frags[MAX_SKB_FRAGS], which is both an out-of-bounds write past the static frag array and a NULL frag handed up the rx path. Bound len by the size add_recvbuf_big() actually advertised. Fixes: 0c716703965f ("virtio-net: fix received length check in big packets") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Reviewed-by: Xuan Zhuo Acked-by: Michael S. Tsirkin Reviewed-by: Bui Quang Minh Link: https://patch.msgid.link/20260616042837.2249468-1-xmei5@asu.edu Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/virtio_net.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 7d2eeb9b12266..26afa6341d161 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -1999,15 +1999,18 @@ static struct sk_buff *receive_big(struct net_device *dev, struct virtnet_rq_stats *stats) { struct page *page = buf; + unsigned long max_len; struct sk_buff *skb; + max_len = (vi->big_packets_num_skbfrags + 1) * PAGE_SIZE - + sizeof(struct padded_vnet_hdr) + vi->hdr_len; + /* Make sure that len does not exceed the size allocated in * add_recvbuf_big. */ - if (unlikely(len > (vi->big_packets_num_skbfrags + 1) * PAGE_SIZE)) { + if (unlikely(len > max_len)) { pr_debug("%s: rx error: len %u exceeds allocated size %lu\n", - dev->name, len, - (vi->big_packets_num_skbfrags + 1) * PAGE_SIZE); + dev->name, len, max_len); goto err; } From dbb6321c297776422fa20751f6a9906dfe89887f Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Tue, 16 Jun 2026 13:54:30 +0300 Subject: [PATCH 1795/2758] dpaa2-switch: fix VLAN upper check not rejecting bridge join [ Upstream commit ed2294f94e34e97342850c40b320833d881c3819 ] The blamed commit refactored the prechangeupper event handling but failed to actually return an error in case dpaa2_switch_prevent_bridging_with_8021q_upper() detected a 802.1q upper on a port which tries to join a bridge. Fix this by returning err instead of 0. Fixes: 45035febc495 ("net: dpaa2-switch: refactor prechangeupper sanity checks") Signed-off-by: Ioana Ciornei Link: https://patch.msgid.link/20260616105430.3725910-1-ioana.ciornei@nxp.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index 52c1cb9cb7e02..46ae81c2fa013 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -2177,7 +2177,7 @@ dpaa2_switch_prechangeupper_sanity_checks(struct net_device *netdev, if (err) { NL_SET_ERR_MSG_MOD(extack, "Cannot join a bridge while VLAN uppers are present"); - return 0; + return err; } netdev_for_each_lower_dev(upper_dev, other_dev, iter) { From 21f7e96cf1645596a21c2b94213eb4d3e9751b44 Mon Sep 17 00:00:00 2001 From: Cosmin Ratiu Date: Tue, 16 Jun 2026 14:06:32 +0300 Subject: [PATCH 1796/2758] devlink: Fix parent ref leak in devl_rate_node_create() [ Upstream commit ba45106342bbdd905651cb9fcefb8c11871d4c25 ] In the original commit the function bails out on kstrdup failure, forgetting to decrement the refcnt of the parent. Fix that by moving the parent refcnt setting after kstrdup. Fixes: caba177d7f4d ("devlink: Enable creation of the devlink-rate nodes from the driver") Signed-off-by: Cosmin Ratiu Reviewed-by: Carolina Jubran Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260616110633.1449432-2-cratiu@nvidia.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/devlink/rate.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/devlink/rate.c b/net/devlink/rate.c index 41be2d6c2954e..210e26c6cfa04 100644 --- a/net/devlink/rate.c +++ b/net/devlink/rate.c @@ -725,11 +725,6 @@ devl_rate_node_create(struct devlink *devlink, void *priv, char *node_name, if (!rate_node) return ERR_PTR(-ENOMEM); - if (parent) { - rate_node->parent = parent; - refcount_inc(&rate_node->parent->refcnt); - } - rate_node->type = DEVLINK_RATE_TYPE_NODE; rate_node->devlink = devlink; rate_node->priv = priv; @@ -740,6 +735,11 @@ devl_rate_node_create(struct devlink *devlink, void *priv, char *node_name, return ERR_PTR(-ENOMEM); } + if (parent) { + rate_node->parent = parent; + refcount_inc(&rate_node->parent->refcnt); + } + refcount_set(&rate_node->refcnt, 1); list_add(&rate_node->list, &devlink->rate_list); devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_NEW); From f5adcb9245ae131474b8b0a7927816426153d49c Mon Sep 17 00:00:00 2001 From: Cosmin Ratiu Date: Tue, 16 Jun 2026 14:06:33 +0300 Subject: [PATCH 1797/2758] devlink: Fix parent ref leak on tc-bw failure [ Upstream commit ba81a8b80f042038b9f73a4e5bb135de890b59bb ] When a node is created via rate-new with tc-bw and a parent node, devlink_nl_rate_set() executes the sequence of ops. It bails out on the first failure and doesn't rollback anything. For most things that is fine (setting some numbers), but the parent set can leak if there's another failure after that. That is precisely what happens when parent setting isn't the last block in the function. After the referenced "Fixes" commit, when tc-bw fails to be set the function bails out after having set the parent and incremented its refcount. There are two callers: - devlink_nl_rate_set_doit() is fine, it just reports the error. - but devlink_nl_rate_new_doit() frees the newly created node and leaks the parent refcnt. Fix that by reordering the blocks so parent setting is last and adding a comment explaining this so future modification preserve the ordering (hopefully). Fixes: 566e8f108fc7 ("devlink: Extend devlink rate API with traffic classes bandwidth management") Signed-off-by: Cosmin Ratiu Reviewed-by: Carolina Jubran Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260616110633.1449432-3-cratiu@nvidia.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/devlink/rate.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/net/devlink/rate.c b/net/devlink/rate.c index 210e26c6cfa04..533d21b028a7b 100644 --- a/net/devlink/rate.c +++ b/net/devlink/rate.c @@ -486,16 +486,19 @@ static int devlink_nl_rate_set(struct devlink_rate *devlink_rate, devlink_rate->tx_weight = weight; } - nla_parent = attrs[DEVLINK_ATTR_RATE_PARENT_NODE_NAME]; - if (nla_parent) { - err = devlink_nl_rate_parent_node_set(devlink_rate, info, - nla_parent); + if (attrs[DEVLINK_ATTR_RATE_TC_BWS]) { + err = devlink_nl_rate_tc_bw_set(devlink_rate, info); if (err) return err; } - if (attrs[DEVLINK_ATTR_RATE_TC_BWS]) { - err = devlink_nl_rate_tc_bw_set(devlink_rate, info); + /* Keep parent setting last because it takes a reference. This function + * has no rollback, so failing after taking the ref would leak it. + */ + nla_parent = attrs[DEVLINK_ATTR_RATE_PARENT_NODE_NAME]; + if (nla_parent) { + err = devlink_nl_rate_parent_node_set(devlink_rate, info, + nla_parent); if (err) return err; } From 9f7cd1e26d2f1766438edc9b9254f2c618f9ae98 Mon Sep 17 00:00:00 2001 From: Wayen Yan Date: Tue, 16 Jun 2026 19:52:36 +0800 Subject: [PATCH 1798/2758] net: airoha: fix foe_check_time allocation size [ Upstream commit 5c121ee635680c93d7074becf14cfbaac140f80d ] foe_check_time is declared as u16 pointer but was allocated with only ppe_num_entries bytes instead of ppe_num_entries * sizeof(u16). When airoha_ppe_foe_verify_entry() is called with hash >= ppe_num_entries/2, it writes beyond the allocated buffer, causing heap buffer overflow and potential kernel crash. Fixes: 6d5b601d52a2 ("net: airoha: ppe: Dynamically allocate foe_check_time array in airoha_ppe struct") Signed-off-by: Wayen Yan Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/178161119471.2163752.14373384830691569758@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/airoha/airoha_ppe.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c index a124f15345a91..a1f9939c21f20 100644 --- a/drivers/net/ethernet/airoha/airoha_ppe.c +++ b/drivers/net/ethernet/airoha/airoha_ppe.c @@ -1578,7 +1578,8 @@ int airoha_ppe_init(struct airoha_eth *eth) return -ENOMEM; } - ppe->foe_check_time = devm_kzalloc(eth->dev, ppe_num_entries, + ppe->foe_check_time = devm_kzalloc(eth->dev, + ppe_num_entries * sizeof(*ppe->foe_check_time), GFP_KERNEL); if (!ppe->foe_check_time) return -ENOMEM; From 9acbcb89190a311bd76fe45cf3ce284f7595978e Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Tue, 16 Jun 2026 15:23:03 +0200 Subject: [PATCH 1799/2758] net: macb: add TX stall timeout callback to recover from lost TSTART write MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit e438ec3e9e95cd3f49a8120e5f63ae3f9606e6fa ] The MACB found in the Raspberry Pi RP1 suffers from sporadic stalls on the TX queue. While the exact root cause is not yet fully understood, it is likely related to a hardware issue where a TSTART write to the NCR register is missed, preventing the transmission from being kicked off. Implement a timeout callback to handle TX queue stalls, triggering the existing restart mechanism to recover. Link: https://lore.kernel.org/all/20260514215459.36109-1-lukasz@raczylo.com/ Fixes: dc110d1b23564 ("net: cadence: macb: Add support for Raspberry Pi RP1 ethernet controller") Signed-off-by: Lukasz Raczylo Co-developed-by: Steffen Jaeckel Signed-off-by: Steffen Jaeckel Co-developed-by: Andrea della Porta Signed-off-by: Andrea della Porta Reviewed-by: Nicolai Buchwitz Reviewed-by: Théo Lebrun Link: https://patch.msgid.link/468f480454a314303bac6a54780b153f689f2267.1781598350.git.andrea.porta@suse.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/cadence/macb_main.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index a12aa21244e83..fd282a1700fb9 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -4522,6 +4522,13 @@ static int macb_setup_tc(struct net_device *dev, enum tc_setup_type type, } } +static void macb_tx_timeout(struct net_device *dev, unsigned int q) +{ + struct macb *bp = netdev_priv(dev); + + macb_tx_restart(&bp->queues[q]); +} + static const struct net_device_ops macb_netdev_ops = { .ndo_open = macb_open, .ndo_stop = macb_close, @@ -4540,6 +4547,7 @@ static const struct net_device_ops macb_netdev_ops = { .ndo_hwtstamp_set = macb_hwtstamp_set, .ndo_hwtstamp_get = macb_hwtstamp_get, .ndo_setup_tc = macb_setup_tc, + .ndo_tx_timeout = macb_tx_timeout, }; /* Configure peripheral capabilities according to device tree From 825de39f0c35a112148799b3cbe45af3766c018a Mon Sep 17 00:00:00 2001 From: Yun Zhou Date: Tue, 16 Jun 2026 20:30:57 +0800 Subject: [PATCH 1800/2758] flow_dissector: check device type before reading ETH_ADDRS [ Upstream commit bf6e8af2c8be77489bedeae9f8a9654cb710e500 ] __skb_flow_dissect() unconditionally reads 12 bytes from eth_hdr(skb) when FLOW_DISSECTOR_KEY_ETH_ADDRS is requested. This assumes the skb has a valid Ethernet header at mac_header, which is not always the case. The problem can be triggered by: 1. Creating a TUN device in L3 mode (IFF_TUN, hard_header_len=0) 2. Attaching a multiq qdisc with a flower filter matching on eth_src 3. Sending a packet through AF_PACKET Since TUN in L3 mode has no link-layer header, mac_header points to the L3 data area. The flow dissector reads 12 bytes of uninitialized skb memory, which then propagates through fl_set_masked_key() and is used as a rhashtable lookup key in __fl_lookup(), as reported by KMSAN. Rejecting the filter in the control path (at tc filter add time) is not feasible because TC filter blocks can be shared between arbitrary devices -- a filter installed on an Ethernet device may later classify packets on a headerless device through a shared block. The device association is not fixed at filter creation time. Fix this by gating the memcpy on dev->type == ARPHRD_ETHER, which ensures only true Ethernet-framed packets have their addresses read. This is more precise than the previous hard_header_len >= 12 check, which would incorrectly pass for non-Ethernet link types like IPoIB (ARPHRD_INFINIBAND, hard_header_len=24) and FDDI (hard_header_len=21) whose L2 headers are not in Ethernet format. Additionally check skb_mac_header_was_set() to guard against the pathological case where mac_header is the unset sentinel (~0U), which would cause eth_hdr() to return a wild pointer. For the act_mirred redirect case (Ethernet packet redirected to a non-Ethernet device sharing a TC block), zeroing the key is the correct behavior: the packet is now being classified on the target device, where Ethernet address matching is not semantically meaningful. Note: on non-Ethernet devices, the zeroed key will match a filter configured with all-zero MAC addresses. This is an improvement over the previous behavior where uninitialized memory could randomly match any filter. Reported-by: syzbot+fa2f5b1fb06147be5e16@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=fa2f5b1fb06147be5e16 Fixes: 67a900cc0436 ("flow_dissector: introduce support for Ethernet addresses") Signed-off-by: Yun Zhou Link: https://patch.msgid.link/20260616123057.482154-1-yun.zhou@windriver.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/core/flow_dissector.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 2a98f5fa74eb0..8aa4f9b4df810 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -1173,13 +1173,21 @@ bool __skb_flow_dissect(const struct net *net, if (dissector_uses_key(flow_dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { - struct ethhdr *eth = eth_hdr(skb); struct flow_dissector_key_eth_addrs *key_eth_addrs; key_eth_addrs = skb_flow_dissector_target(flow_dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS, target_container); - memcpy(key_eth_addrs, eth, sizeof(*key_eth_addrs)); + /* TC filter blocks can be shared across devices with + * different link types, so we cannot validate this + * when the filter is installed -- check at dissect time. + */ + if (skb && skb->dev && + skb->dev->type == ARPHRD_ETHER && + skb_mac_header_was_set(skb)) + memcpy(key_eth_addrs, eth_hdr(skb), sizeof(*key_eth_addrs)); + else + memset(key_eth_addrs, 0, sizeof(*key_eth_addrs)); } if (dissector_uses_key(flow_dissector, From 0b17f320893a415dcc94238a5f9c81452869bbf3 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Tue, 16 Jun 2026 09:09:52 -0700 Subject: [PATCH 1801/2758] netconsole: don't drop the last byte of a full-sized message [ Upstream commit c0ebe492329a4d29592e2240df17e56724849f1f ] nt->buf is exactly MAX_PRINT_CHUNK bytes, but scnprintf() reserves one byte for its NUL terminator, so a non-fragmented payload of exactly MAX_PRINT_CHUNK loses its last byte (emitted as a stray NUL in the release path). Grow nt->buf to MAX_PRINT_CHUNK + 1 and bound the scnprintf() calls with sizeof(nt->buf); the transmitted length stays capped at MAX_PRINT_CHUNK. Alternatively, nt->buf could be left at MAX_PRINT_CHUNK and the NUL byte reserved by routing exactly-MAX_PRINT_CHUNK payloads to fragmentation ('len < MAX_PRINT_CHUNK'), at the cost of fragmenting those messages. But it would look less sane, thus the current approach. Fixes: c62c0a17f9b7 ("netconsole: Append kernel version to message") Signed-off-by: Breno Leitao Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260616-max_print_chunk-v1-1-8dc125d67083@debian.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/netconsole.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index 57dd6821a8aa9..bfab0a47678c9 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -184,8 +184,10 @@ struct netconsole_target { bool extended; bool release; struct netpoll np; - /* protected by target_list_lock */ - char buf[MAX_PRINT_CHUNK]; + /* protected by target_list_lock; +1 gives scnprintf() room for its + * NUL terminator so a full MAX_PRINT_CHUNK payload is not truncated + */ + char buf[MAX_PRINT_CHUNK + 1]; struct work_struct resume_wq; }; @@ -1692,7 +1694,7 @@ static void send_msg_no_fragmentation(struct netconsole_target *nt, if (release_len) { release = init_utsname()->release; - scnprintf(nt->buf, MAX_PRINT_CHUNK, "%s,%.*s", release, + scnprintf(nt->buf, sizeof(nt->buf), "%s,%.*s", release, msg_len, msg); msg_len += release_len; } else { @@ -1701,12 +1703,12 @@ static void send_msg_no_fragmentation(struct netconsole_target *nt, if (userdata) msg_len += scnprintf(&nt->buf[msg_len], - MAX_PRINT_CHUNK - msg_len, "%s", + sizeof(nt->buf) - msg_len, "%s", userdata); if (sysdata) msg_len += scnprintf(&nt->buf[msg_len], - MAX_PRINT_CHUNK - msg_len, "%s", + sizeof(nt->buf) - msg_len, "%s", sysdata); send_udp(nt, nt->buf, msg_len); From 41b70eff03924e7aa1dae76c4c33abf27bf8eea1 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 17 Jun 2026 13:43:23 +0300 Subject: [PATCH 1802/2758] selftests: vlan_bridge_binding: Fix flaky operational state check [ Upstream commit 4045f1c3d68ef4b589ae2587e6ff66ce8017daf2 ] check_operstate() busy waits for up to one second for the operational state to change to the expected state. This is not enough since carrier loss events can be delayed by the kernel for up to one second (see __linkwatch_run_queue()), leading to sporadic failures. Fix by increasing the busy wait period to two seconds. Fixes: dca12e9ab760 ("selftests: net: Add a VLAN bridge binding selftest") Reported-by: Jakub Kicinski Closes: https://lore.kernel.org/netdev/20260616092733.3a31be4d@kernel.org/ Signed-off-by: Ido Schimmel Reviewed-by: Nikolay Aleksandrov Reviewed-by: Petr Machata Link: https://patch.msgid.link/20260617104323.1069457-1-idosch@nvidia.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- tools/testing/selftests/net/vlan_bridge_binding.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/vlan_bridge_binding.sh b/tools/testing/selftests/net/vlan_bridge_binding.sh index e8c02c64e03a4..d04caa14202d0 100755 --- a/tools/testing/selftests/net/vlan_bridge_binding.sh +++ b/tools/testing/selftests/net/vlan_bridge_binding.sh @@ -64,7 +64,7 @@ check_operstate() local expect=$1; shift local operstate - operstate=$(busywait 1000 \ + operstate=$(busywait 2000 \ operstate_is "$dev" "$expect") check_err $? "Got operstate of $operstate, expected $expect" } From d9ac4239157e6ce3b61afc09ced3517349c92c5e Mon Sep 17 00:00:00 2001 From: Daniel Zahka Date: Wed, 17 Jun 2026 03:39:49 -0700 Subject: [PATCH 1803/2758] eth: fbnic: take netif_addr_lock_bh() around rx mode address programming [ Upstream commit 96e7f9122aae0ed000ee321f324b812a447906d9 ] When __fbnic_set_rx_mode() is called from contexts other than .ndo_set_rx_mode_async(), the uc and mc addr lists are accessed without the addr lock that __hw_addr_sync_dev() and __hw_addr_unsync_dev() require. Wrap these unprotected accesses with netif_addr_lock_bh(). fbnic_clear_rx_mode() has similar issues. Fixes: eb690ef8d1c2 ("eth: fbnic: Add L2 address programming") Signed-off-by: Daniel Zahka Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260617-linux-fbnic-hwaddr-v1-1-3f9f5dee7f99@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/meta/fbnic/fbnic_netdev.c | 7 ++++++- drivers/net/ethernet/meta/fbnic/fbnic_pci.c | 4 ++++ drivers/net/ethernet/meta/fbnic/fbnic_rpc.c | 2 ++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c index 4dea2bb58d2fc..44c046b253126 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c @@ -262,8 +262,11 @@ static int fbnic_set_mac(struct net_device *netdev, void *p) eth_hw_addr_set(netdev, addr->sa_data); - if (netif_running(netdev)) + if (netif_running(netdev)) { + netif_addr_lock_bh(netdev); __fbnic_set_rx_mode(fbn->fbd, &netdev->uc, &netdev->mc); + netif_addr_unlock_bh(netdev); + } return 0; } @@ -308,8 +311,10 @@ void fbnic_clear_rx_mode(struct fbnic_dev *fbd) /* Write updates to hardware */ fbnic_write_macda(fbd); + netif_addr_lock_bh(netdev); __dev_uc_unsync(netdev, NULL); __dev_mc_unsync(netdev, NULL); + netif_addr_unlock_bh(netdev); } static int fbnic_hwtstamp_get(struct net_device *netdev, diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_pci.c b/drivers/net/ethernet/meta/fbnic/fbnic_pci.c index 7e85b480203c8..8b9bc9e8ea56c 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_pci.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_pci.c @@ -135,7 +135,9 @@ void fbnic_up(struct fbnic_net *fbn) fbnic_rss_reinit_hw(fbn->fbd, fbn); + netif_addr_lock_bh(fbn->netdev); __fbnic_set_rx_mode(fbn->fbd, &fbn->netdev->uc, &fbn->netdev->mc); + netif_addr_unlock_bh(fbn->netdev); /* Enable Tx/Rx processing */ fbnic_napi_enable(fbn); @@ -180,7 +182,9 @@ static int fbnic_fw_config_after_crash(struct fbnic_dev *fbd) } fbnic_rpc_reset_valid_entries(fbd); + netif_addr_lock_bh(fbd->netdev); __fbnic_set_rx_mode(fbd, &fbd->netdev->uc, &fbd->netdev->mc); + netif_addr_unlock_bh(fbd->netdev); return 0; } diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_rpc.c b/drivers/net/ethernet/meta/fbnic/fbnic_rpc.c index fe95b6f696463..bc0f38b6a2b2b 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_rpc.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_rpc.c @@ -244,7 +244,9 @@ void fbnic_bmc_rpc_check(struct fbnic_dev *fbd) if (fbd->fw_cap.need_bmc_tcam_reinit) { fbnic_bmc_rpc_init(fbd); + netif_addr_lock_bh(fbd->netdev); __fbnic_set_rx_mode(fbd, &fbd->netdev->uc, &fbd->netdev->mc); + netif_addr_unlock_bh(fbd->netdev); fbd->fw_cap.need_bmc_tcam_reinit = false; } From 68286258698e15fe75073fb8d017003f8e493db1 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Fri, 19 Jun 2026 01:00:10 +0800 Subject: [PATCH 1804/2758] ALSA: usb-audio: Kill MIDI 2.0 URBs before freeing endpoints [ Upstream commit f199c8a8bdd54296d3458777e70fe82a78bd9817 ] MIDI 2.0 input URBs are started during snd_usb_midi_v2_create(). A later setup failure can still jump to snd_usb_midi_v2_free(), which currently frees each endpoint and its coherent URB buffers without first stopping the submitted URBs. A completion can then dereference the embedded URB context and endpoint state after they have been freed, or try to resubmit from the stale endpoint. This was observed as a KASAN slab-use-after-free in input_urb_complete(). The buggy scenario involves two paths, with each column showing the order within that path: probe error path: USB completion path: 1. start_input_streams() submits 1. The HCD still owns a input URBs. submitted input URB. 2. A later setup helper returns 2. input_urb_complete() runs an error. with urb->context in ep. 3. snd_usb_midi_v2_free() frees 3. The completion reads ep endpoint storage and URB buffers. state and can requeue URBs. Make the endpoint destructor follow the same teardown ordering used for disconnect when the endpoint has not already been disconnected: publish ep->disconnected, kill the URBs synchronously, and drain the endpoint before freeing URB buffers and endpoint storage. The guard avoids repeating the stop sequence after the normal snd_usb_midi_v2_disconnect_all() path, while still synchronizing the direct MIDI 2.0 create-error free path. Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in input_urb_complete+0x37/0x1b0 Workqueue: usb_hub_wq hub_event RIP: 0010:_raw_spin_unlock_irq+0x2e/0x50 Read of size 8 Call trace: dump_stack_lvl+0x77/0xb0 print_report+0xce/0x5f0 input_urb_complete+0x37/0x1b0 (sound/usb/midi2.c:186) srso_alias_return_thunk+0x5/0xfbef5 __virt_addr_valid+0x19f/0x330 kasan_report+0xe0/0x110 __usb_hcd_giveback_urb+0x112/0x1d0 dummy_timer+0xaaa/0x19a0 lock_is_held_type+0x9a/0x110 __lock_acquire+0x467/0x28b0 mark_held_locks+0x40/0x70 _raw_spin_unlock_irqrestore+0x44/0x60 lockdep_hardirqs_on_prepare+0xbb/0x1a0 __hrtimer_run_queues+0x101/0x520 hrtimer_run_softirq+0xd0/0x130 handle_softirqs+0x15b/0x670 __irq_exit_rcu+0xd0/0x170 irq_exit_rcu+0xe/0x20 sysvec_apic_timer_interrupt+0x6c/0x80 asm_sysvec_apic_timer_interrupt+0x1a/0x20 Fixes: d9c99876868c ("ALSA: usb-audio: Create UMP blocks from USB MIDI GTBs") Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Link: https://patch.msgid.link/20260618170010.191433-1-zzzccc427@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/usb/midi2.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/usb/midi2.c b/sound/usb/midi2.c index 04aeb9052f139..3ec6332917726 100644 --- a/sound/usb/midi2.c +++ b/sound/usb/midi2.c @@ -470,6 +470,11 @@ static int create_midi2_endpoint(struct snd_usb_midi2_interface *umidi, static void free_midi2_endpoint(struct snd_usb_midi2_endpoint *ep) { list_del(&ep->list); + if (!ep->disconnected) { + ep->disconnected = 1; + kill_midi_urbs(ep, false); + drain_urb_queue(ep); + } free_midi_urbs(ep); kfree(ep); } From 7f8d816a9aa2729d270418f00c9ef5e85bfc1b31 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Mon, 15 Jun 2026 11:18:25 +0200 Subject: [PATCH 1805/2758] netfilter: flowtable: fix and simplify IP6IP6 tunnel handling [ Upstream commit f4c2d8668d85ed125985da663c824a9c25498257 ] Fix nf_flow_ip6_tunnel_proto() to use pskb_may_pull() instead of skb_header_pointer() to ensure the outer IPv6 header is in the skb headroom, which is required for subsequent packet processing. Move ctx->offset update inside the IPPROTO_IPV6 conditional block since it should only be adjusted when an IP6IP6 tunnel is actually detected. Simplify the rx path by removing ipv6_skip_exthdr() and checking ip6h->nexthdr directly, as the flowtable fast path only handles simple IP6IP6 encapsulation without extension headers. Drop the tunnel encapsulation limit destination option support from the tx path to match, since the rx path no longer handles extension headers. Remove the encap_limit parameter from nf_flow_offload_ipv6_forward(), nf_flow_tunnel_ip6ip6_push() and nf_flow_tunnel_v6_push(), along with the ipv6_tel_txoption struct and related headroom/MTU adjustments. Fixes: d98103575dcdd ("netfilter: flowtable: Add IP6IP6 rx sw acceleration") Signed-off-by: Lorenzo Bianconi Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/ipv6/ip6_tunnel.c | 7 ++ net/netfilter/nf_flow_table_ip.c | 80 +++++-------------- .../selftests/net/netfilter/nft_flowtable.sh | 8 +- 3 files changed, 30 insertions(+), 65 deletions(-) diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 9d1037ac082f6..bf1e77f95f181 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -1850,6 +1850,13 @@ static int ip6_tnl_fill_forward_path(struct net_device_path_ctx *ctx, struct dst_entry *dst; int err; + if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) { + /* encaplimit option is currently not supported is + * sw-acceleration path. + */ + return -EOPNOTSUPP; + } + dst = ip6_route_output(dev_net(ctx->dev), NULL, &fl6); if (!dst->error) { path->type = DEV_PATH_TUN; diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c index 9c05a50d60138..e7a3fb2b2d946 100644 --- a/net/netfilter/nf_flow_table_ip.c +++ b/net/netfilter/nf_flow_table_ip.c @@ -347,29 +347,23 @@ static bool nf_flow_ip6_tunnel_proto(struct nf_flowtable_ctx *ctx, struct sk_buff *skb) { #if IS_ENABLED(CONFIG_IPV6) - struct ipv6hdr *ip6h, _ip6h; - __be16 frag_off; - u8 nexthdr; - int hdrlen; + struct ipv6hdr *ip6h; - ip6h = skb_header_pointer(skb, ctx->offset, sizeof(*ip6h), &_ip6h); - if (!ip6h) + if (!pskb_may_pull(skb, sizeof(*ip6h) + ctx->offset)) return false; + ip6h = (struct ipv6hdr *)(skb_network_header(skb) + ctx->offset); if (ip6h->hop_limit <= 1) return false; - nexthdr = ip6h->nexthdr; - hdrlen = ipv6_skip_exthdr(skb, sizeof(*ip6h) + ctx->offset, &nexthdr, - &frag_off); - if (hdrlen < 0) + if (ipv6_ext_hdr(ip6h->nexthdr)) return false; - if (nexthdr == IPPROTO_IPV6) { - ctx->tun.hdr_size = hdrlen; - ctx->tun.proto = IPPROTO_IPV6; + if (ip6h->nexthdr == IPPROTO_IPV6) { + ctx->tun.proto = ip6h->nexthdr; + ctx->tun.hdr_size = sizeof(*ip6h); + ctx->offset += ctx->tun.hdr_size; } - ctx->offset += ctx->tun.hdr_size; return true; #else @@ -648,25 +642,19 @@ static int nf_flow_tunnel_v4_push(struct net *net, struct sk_buff *skb, return 0; } -struct ipv6_tel_txoption { - struct ipv6_txoptions ops; - __u8 dst_opt[8]; -}; - static int nf_flow_tunnel_ip6ip6_push(struct net *net, struct sk_buff *skb, struct flow_offload_tuple *tuple, - struct in6_addr **ip6_daddr, - int encap_limit) + struct in6_addr **ip6_daddr) { struct ipv6hdr *ip6h = (struct ipv6hdr *)skb_network_header(skb); - u8 hop_limit = ip6h->hop_limit, proto = IPPROTO_IPV6; struct rtable *rt = dst_rtable(tuple->dst_cache); __u8 dsfield = ipv6_get_dsfield(ip6h); struct flowi6 fl6 = { .daddr = tuple->tun.src_v6, .saddr = tuple->tun.dst_v6, - .flowi6_proto = proto, + .flowi6_proto = IPPROTO_IPV6, }; + u8 hop_limit = ip6h->hop_limit; int err, mtu; u32 headroom; @@ -674,41 +662,18 @@ static int nf_flow_tunnel_ip6ip6_push(struct net *net, struct sk_buff *skb, if (err) return err; - skb_set_inner_ipproto(skb, proto); + skb_set_inner_ipproto(skb, IPPROTO_IPV6); headroom = sizeof(*ip6h) + LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len; - if (encap_limit) - headroom += 8; err = skb_cow_head(skb, headroom); if (err) return err; skb_scrub_packet(skb, true); mtu = dst_mtu(&rt->dst) - sizeof(*ip6h); - if (encap_limit) - mtu -= 8; mtu = max(mtu, IPV6_MIN_MTU); skb_dst_update_pmtu_no_confirm(skb, mtu); - if (encap_limit > 0) { - struct ipv6_tel_txoption opt = { - .dst_opt[2] = IPV6_TLV_TNL_ENCAP_LIMIT, - .dst_opt[3] = 1, - .dst_opt[4] = encap_limit, - .dst_opt[5] = IPV6_TLV_PADN, - .dst_opt[6] = 1, - }; - struct ipv6_opt_hdr *hopt; - - opt.ops.dst1opt = (struct ipv6_opt_hdr *)opt.dst_opt; - opt.ops.opt_nflen = 8; - - hopt = skb_push(skb, ipv6_optlen(opt.ops.dst1opt)); - memcpy(hopt, opt.ops.dst1opt, ipv6_optlen(opt.ops.dst1opt)); - hopt->nexthdr = IPPROTO_IPV6; - proto = NEXTHDR_DEST; - } - skb_push(skb, sizeof(*ip6h)); skb_reset_network_header(skb); @@ -716,7 +681,7 @@ static int nf_flow_tunnel_ip6ip6_push(struct net *net, struct sk_buff *skb, ip6_flow_hdr(ip6h, dsfield, ip6_make_flowlabel(net, skb, fl6.flowlabel, true, &fl6)); ip6h->hop_limit = hop_limit; - ip6h->nexthdr = proto; + ip6h->nexthdr = IPPROTO_IPV6; ip6h->daddr = tuple->tun.src_v6; ip6h->saddr = tuple->tun.dst_v6; ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(*ip6h)); @@ -729,12 +694,10 @@ static int nf_flow_tunnel_ip6ip6_push(struct net *net, struct sk_buff *skb, static int nf_flow_tunnel_v6_push(struct net *net, struct sk_buff *skb, struct flow_offload_tuple *tuple, - struct in6_addr **ip6_daddr, - int encap_limit) + struct in6_addr **ip6_daddr) { if (tuple->tun_num) - return nf_flow_tunnel_ip6ip6_push(net, skb, tuple, ip6_daddr, - encap_limit); + return nf_flow_tunnel_ip6ip6_push(net, skb, tuple, ip6_daddr); return 0; } @@ -1089,7 +1052,7 @@ static int nf_flow_tuple_ipv6(struct nf_flowtable_ctx *ctx, struct sk_buff *skb, static int nf_flow_offload_ipv6_forward(struct nf_flowtable_ctx *ctx, struct nf_flowtable *flow_table, struct flow_offload_tuple_rhash *tuplehash, - struct sk_buff *skb, int encap_limit) + struct sk_buff *skb) { enum flow_offload_tuple_dir dir; struct flow_offload *flow; @@ -1100,11 +1063,8 @@ static int nf_flow_offload_ipv6_forward(struct nf_flowtable_ctx *ctx, flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]); mtu = flow->tuplehash[dir].tuple.mtu + ctx->offset; - if (flow->tuplehash[!dir].tuple.tun_num) { + if (flow->tuplehash[!dir].tuple.tun_num) mtu -= sizeof(*ip6h); - if (encap_limit > 0) - mtu -= 8; /* encap limit option */ - } if (unlikely(nf_flow_exceeds_mtu(skb, mtu))) return 0; @@ -1158,7 +1118,6 @@ unsigned int nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { - int encap_limit = IPV6_DEFAULT_TNL_ENCAP_LIMIT; struct flow_offload_tuple_rhash *tuplehash; struct nf_flowtable *flow_table = priv; struct flow_offload_tuple *other_tuple; @@ -1177,8 +1136,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb, if (tuplehash == NULL) return NF_ACCEPT; - ret = nf_flow_offload_ipv6_forward(&ctx, flow_table, tuplehash, skb, - encap_limit); + ret = nf_flow_offload_ipv6_forward(&ctx, flow_table, tuplehash, skb); if (ret < 0) return NF_DROP; else if (ret == 0) @@ -1198,7 +1156,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb, ip6_daddr = &other_tuple->src_v6; if (nf_flow_tunnel_v6_push(state->net, skb, other_tuple, - &ip6_daddr, encap_limit) < 0) + &ip6_daddr) < 0) return NF_DROP; switch (tuplehash->tuple.xmit_type) { diff --git a/tools/testing/selftests/net/netfilter/nft_flowtable.sh b/tools/testing/selftests/net/netfilter/nft_flowtable.sh index 7a34ef468975d..08ad07500e8a7 100755 --- a/tools/testing/selftests/net/netfilter/nft_flowtable.sh +++ b/tools/testing/selftests/net/netfilter/nft_flowtable.sh @@ -592,7 +592,7 @@ ip -net "$nsr1" link set tun0 up ip -net "$nsr1" addr add 192.168.100.1/24 dev tun0 ip netns exec "$nsr1" sysctl net.ipv4.conf.tun0.forwarding=1 > /dev/null -ip -net "$nsr1" link add name tun6 type ip6tnl local fee1:2::1 remote fee1:2::2 +ip -net "$nsr1" link add name tun6 type ip6tnl local fee1:2::1 remote fee1:2::2 encaplimit none ip -net "$nsr1" link set tun6 up ip -net "$nsr1" addr add fee1:3::1/64 dev tun6 nodad @@ -601,7 +601,7 @@ ip -net "$nsr2" link set tun0 up ip -net "$nsr2" addr add 192.168.100.2/24 dev tun0 ip netns exec "$nsr2" sysctl net.ipv4.conf.tun0.forwarding=1 > /dev/null -ip -net "$nsr2" link add name tun6 type ip6tnl local fee1:2::2 remote fee1:2::1 || ret=1 +ip -net "$nsr2" link add name tun6 type ip6tnl local fee1:2::2 remote fee1:2::1 encaplimit none || ret=1 ip -net "$nsr2" link set tun6 up ip -net "$nsr2" addr add fee1:3::2/64 dev tun6 nodad @@ -651,7 +651,7 @@ ip -net "$nsr1" route change default via 192.168.200.2 ip netns exec "$nsr1" sysctl net.ipv4.conf.tun0/10.forwarding=1 > /dev/null ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif tun0.10 accept' -ip -net "$nsr1" link add name tun6.10 type ip6tnl local fee1:4::1 remote fee1:4::2 +ip -net "$nsr1" link add name tun6.10 type ip6tnl local fee1:4::1 remote fee1:4::2 encaplimit none ip -net "$nsr1" link set tun6.10 up ip -net "$nsr1" addr add fee1:5::1/64 dev tun6.10 nodad ip -6 -net "$nsr1" route delete default @@ -670,7 +670,7 @@ ip -net "$nsr2" addr add 192.168.200.2/24 dev tun0.10 ip -net "$nsr2" route change default via 192.168.200.1 ip netns exec "$nsr2" sysctl net.ipv4.conf.tun0/10.forwarding=1 > /dev/null -ip -net "$nsr2" link add name tun6.10 type ip6tnl local fee1:4::2 remote fee1:4::1 || ret=1 +ip -net "$nsr2" link add name tun6.10 type ip6tnl local fee1:4::2 remote fee1:4::1 encaplimit none || ret=1 ip -net "$nsr2" link set tun6.10 up ip -net "$nsr2" addr add fee1:5::2/64 dev tun6.10 nodad ip -6 -net "$nsr2" route delete default From fde42e9f5c591cc2a26ffa68c7dc02c78e8ca235 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 11 Jun 2026 15:21:44 +0200 Subject: [PATCH 1806/2758] arm64: static_call: include asm/insns.h [ Upstream commit 998d4d789d2d625e1d8c77c1b6c3951e30e81bb0 ] I came a cross a missing declaration in a randconfig build: arch/arm64/kernel/static_call.c:16:5: error: call to undeclared function 'aarch64_insn_adrp_get_offset'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 16 | aarch64_insn_adrp_get_offset(le32_to_cpup(tramp + 4)) + | ^ Include the header that contains this definition explicitly, rather than relying on it to come indirectly through another header. Fixes: 54ac9ff8f119 ("arm64: Use static call trampolines when kCFI is enabled") Signed-off-by: Arnd Bergmann Signed-off-by: Will Deacon Signed-off-by: Sasha Levin --- arch/arm64/kernel/static_call.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/kernel/static_call.c b/arch/arm64/kernel/static_call.c index 8b3a19e108713..c126edced022f 100644 --- a/arch/arm64/kernel/static_call.c +++ b/arch/arm64/kernel/static_call.c @@ -2,6 +2,7 @@ #include #include #include +#include void arch_static_call_transform(void *site, void *tramp, void *func, bool tail) { From 02f1d4b40eb42d112cb079fe55c9899004086d64 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Tue, 9 Jun 2026 06:15:53 -0700 Subject: [PATCH 1807/2758] arm64/hw_breakpoint: reject unaligned watchpoints that would truncate BAS [ Upstream commit 4cc70f75853bebac022334b6a86b953348072f74 ] hw_breakpoint_arch_parse() positions the BAS bit pattern in hw->ctrl.len with offset = hw->address & alignment_mask; /* 0..7 */ hw->ctrl.len <<= offset; ctrl.len is an 8-bit bitfield (struct arch_hw_breakpoint_ctrl::len is u32 :8), so the shift silently drops any bits past bit 7. For non-compat AArch64 watchpoints the offset is unbounded relative to ctrl.len: a perf_event_open(PERF_TYPE_BREAKPOINT) caller asking for HW_BREAKPOINT_W with bp_addr=page+1 and bp_len=HW_BREAKPOINT_LEN_8 ends up with 0xff << 1 = 0x1fe, stored as 0xfe. The kernel programs WCR.BAS=0xfe and the hardware watches bytes [1..7] instead of the requested [1..8] -- the eighth byte is silently dropped. The syscall still returns success, leaving userspace to discover the gap by empirical probing. The same class affects HW_BREAKPOINT_LEN_{2,4} when offset pushes the high BAS bit past bit 7 (e.g. LEN_4 with offset=5 yields 0xe0 instead of 0x1e0). No memory-safety impact -- the value is masked into 8 bits before encoding -- but debuggers and perf users observe missed events on bytes they thought they were watching. The AArch32 branch immediately above already rejects unrepresentable (offset, len) combinations via an explicit switch. Mirror that for the non-compat branch by checking that the shifted pattern fits in the BAS field, returning -EINVAL when it does not. GDB and similar debuggers are unaffected by the stricter check. aarch64_linux_set_debug_regs() already treats EINVAL on NT_ARM_HW_WATCH as a downgrade signal: it clears kernel_supports_any_contiguous_range, calls aarch64_downgrade_regs() to round the BAS up to a legacy 0x01/03/0f/ff mask with an aligned base, and retries -- the same fallback path that PR-20207 introduced. The new -EINVAL is therefore reachable only from a raw perf_event_open() that pairs an unaligned base with an oversized bp_len, which is precisely the bug. Reproducer: struct perf_event_attr a = { .type = PERF_TYPE_BREAKPOINT, .size = sizeof(a), .bp_type = HW_BREAKPOINT_W, .bp_addr = (uintptr_t)(buf + 1), .bp_len = HW_BREAKPOINT_LEN_8, .exclude_kernel = 1, .exclude_hv = 1, }; int fd = perf_event_open(&a, 0, -1, -1, 0); /* before this fix: succeeds, watches 7 bytes (buf+1..buf+7) */ /* after this fix: fails with EINVAL */ Fixes: b08fb180bb88 ("arm64: Allow hw watchpoint at varied offset from base address") Signed-off-by: Breno Leitao Signed-off-by: Will Deacon Signed-off-by: Sasha Levin --- arch/arm64/kernel/hw_breakpoint.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c index ab76b36dce820..73cce8ac83681 100644 --- a/arch/arm64/kernel/hw_breakpoint.c +++ b/arch/arm64/kernel/hw_breakpoint.c @@ -559,6 +559,15 @@ int hw_breakpoint_arch_parse(struct perf_event *bp, else alignment_mask = 0x7; offset = hw->address & alignment_mask; + + /* + * BAS is an 8-bit field in WCR/BCR; the shift below would + * silently drop the high bits of ctrl.len when offset + len + * exceeds 8, programming hardware to watch fewer bytes than + * the user requested. + */ + if (((u32)hw->ctrl.len << offset) > ARM_BREAKPOINT_LEN_8) + return -EINVAL; } hw->address &= ~alignment_mask; From 172e690bab7a965effcb2448ec3f20dd4dc990d2 Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Sat, 13 Jun 2026 15:17:47 -0700 Subject: [PATCH 1808/2758] thermal: intel: Fix dangling resources on thermal_throttle_online() failure [ Upstream commit b91d287fa7a1ba0727eed5823c6ee4924ee5fa31 ] The function thermal_throttle_add_dev() may fail and abort a CPU hotplug online operation. Since the failure occurs within the online callback, thermal_throttle_online(), the CPU hotplug framework does not invoke the corresponding offline callback. As a result, the hardware and software resources set up during the failed operation are not torn down. Since only thermal_throttle_add_dev() can fail, call it before setting up the rest of the resources. Fixes: f6656208f04e ("x86/mce/therm_throt: Optimize notifications of thermal throttle") Signed-off-by: Ricardo Neri Link: https://patch.msgid.link/20260613-rneri-directed-therm-intr-v3-1-3a26d1e47fc8@linux.intel.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/thermal/intel/therm_throt.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/thermal/intel/therm_throt.c b/drivers/thermal/intel/therm_throt.c index 44fa4dd15dd1c..45a8ef4a608b1 100644 --- a/drivers/thermal/intel/therm_throt.c +++ b/drivers/thermal/intel/therm_throt.c @@ -529,8 +529,13 @@ static int thermal_throttle_online(unsigned int cpu) { struct thermal_state *state = &per_cpu(thermal_state, cpu); struct device *dev = get_cpu_device(cpu); + int err; u32 l; + err = thermal_throttle_add_dev(dev, cpu); + if (err) + return err; + state->package_throttle.level = PACKAGE_LEVEL; state->core_throttle.level = CORE_LEVEL; @@ -548,7 +553,7 @@ static int thermal_throttle_online(unsigned int cpu) l = apic_read(APIC_LVTTHMR); apic_write(APIC_LVTTHMR, l & ~APIC_LVT_MASKED); - return thermal_throttle_add_dev(dev, cpu); + return err; } static int thermal_throttle_offline(unsigned int cpu) From 8dcf676092ff48a75e0efe14bd2b678b4cd7f26d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 17 Jun 2026 11:05:55 +0200 Subject: [PATCH 1809/2758] ACPI: resource: Amend kernel-doc style [ Upstream commit 78ad5c7722b7bed9d35ffc5b45eb0f12e2c22fee ] The functions are referred as func() in the kernel-doc. The % (percent) character makes the rendering for constants as described in the respective documentation. Amend all these. Fixes: 8e345c991c8c ("ACPI: Centralized processing of ACPI device resources") Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20260617090555.2648709-1-andriy.shevchenko@linux.intel.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/acpi/resource.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index bc8050d8a6f51..56df4599d3604 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -871,7 +871,7 @@ bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index, EXPORT_SYMBOL_GPL(acpi_dev_resource_interrupt); /** - * acpi_dev_free_resource_list - Free resource from %acpi_dev_get_resources(). + * acpi_dev_free_resource_list - Free resource from acpi_dev_get_resources(). * @list: The head of the resource list to free. */ void acpi_dev_free_resource_list(struct list_head *list) @@ -991,7 +991,7 @@ static int __acpi_dev_get_resources(struct acpi_device *adev, * * The resultant struct resource objects are put on the list pointed to by * @list, that must be empty initially, as members of struct resource_entry - * objects. Callers of this routine should use %acpi_dev_free_resource_list() to + * objects. Callers of this routine should use acpi_dev_free_resource_list() to * free that list. * * The number of resources in the output list is returned on success, an error @@ -1032,7 +1032,7 @@ static int is_memory(struct acpi_resource *ares, void *not_used) * The resultant struct resource objects are put on the list pointed to * by @list, that must be empty initially, as members of struct * resource_entry objects. Callers of this routine should use - * %acpi_dev_free_resource_list() to free that list. + * acpi_dev_free_resource_list() to free that list. * * The number of resources in the output list is returned on success, * an error code reflecting the error condition is returned otherwise. From 315e1efc3f16b64c9b6c30bcc44fd622cfc7195f Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Tue, 16 Jun 2026 17:36:21 +0800 Subject: [PATCH 1810/2758] ACPI: IPMI: Fix inverted interface check in ipmi_bmc_gone() [ Upstream commit 71b57aca295d61276a60e131d8f62b0cc7cf1a35 ] Before commit a1a69b297e47 ("ACPI / IPMI: Fix race caused by the unprotected ACPI IPMI user"), ipmi_bmc_gone() skipped entries whose interface number did not match the SMI being removed, then killed the matching entry: if (ipmi_device->ipmi_ifnum != iface) continue; __ipmi_dev_kill(ipmi_device); That commit folded the removal block into the existing non-match test while converting the object lifetime handling, but left the comparison unchanged. The old != meant "continue past this entry"; after the refactor it meant "kill this entry". As a result, a single ACPI IPMI interface is never removed when its SMI disappears. If multiple interfaces are tracked, the first interface whose number differs from iface is removed instead, while the interface that actually disappeared remains on driver_data.ipmi_devices. The stale entry is not marked dead and can continue to be selected for ACPI IPMI transactions. It can also prevent the same ACPI handle from being registered again. Change the comparison to == so ipmi_bmc_gone() removes exactly the interface reported as gone by the SMI watcher. This restores the pre-a1a69b297e47 behavior and is the correct interface matching logic. Fixes: a1a69b297e47 ("ACPI / IPMI: Fix race caused by the unprotected ACPI IPMI user") Signed-off-by: Xu Rao Link: https://patch.msgid.link/B486593E06E6F6E0+20260616093621.1039943-1-raoxu@uniontech.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/acpi/acpi_ipmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/acpi_ipmi.c b/drivers/acpi/acpi_ipmi.c index 79ce6e72bf295..2dbed92d54b30 100644 --- a/drivers/acpi/acpi_ipmi.c +++ b/drivers/acpi/acpi_ipmi.c @@ -490,7 +490,7 @@ static void ipmi_bmc_gone(int iface) mutex_lock(&driver_data.ipmi_lock); list_for_each_entry_safe(iter, temp, &driver_data.ipmi_devices, head) { - if (iter->ipmi_ifnum != iface) { + if (iter->ipmi_ifnum == iface) { ipmi_device = iter; __ipmi_dev_kill(iter); break; From 8a4eae78287a4074e70fdb38932929eaf1f55115 Mon Sep 17 00:00:00 2001 From: Ivan Abramov Date: Thu, 3 Apr 2025 13:19:32 +0300 Subject: [PATCH 1811/2758] ieee802154: Restore initial state on failed device_rename() in cfg802154_switch_netns() [ Upstream commit a2e06b4bef20b59446d5088e938c2be53cc4e6c6 ] Currently, the return value of device_rename() is not acted upon. To avoid an inconsistent state in case of failure, roll back the changes made before the device_rename() call. Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: 66e5c2672cd1 ("ieee802154: add netns support") Reviewed-by: Miquel Raynal Signed-off-by: Ivan Abramov Link: https://lore.kernel.org/20250403101935.991385-2-i.abramov@mt-integration.ru Signed-off-by: Stefan Schmidt Signed-off-by: Sasha Levin --- net/ieee802154/core.c | 45 ++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c index 89b671b12600f..84d514430e45a 100644 --- a/net/ieee802154/core.c +++ b/net/ieee802154/core.c @@ -233,31 +233,36 @@ int cfg802154_switch_netns(struct cfg802154_registered_device *rdev, wpan_dev->netdev->netns_immutable = true; } - if (err) { - /* failed -- clean up to old netns */ - net = wpan_phy_net(&rdev->wpan_phy); - - list_for_each_entry_continue_reverse(wpan_dev, - &rdev->wpan_dev_list, - list) { - if (!wpan_dev->netdev) - continue; - wpan_dev->netdev->netns_immutable = false; - err = dev_change_net_namespace(wpan_dev->netdev, net, - "wpan%d"); - WARN_ON(err); - wpan_dev->netdev->netns_immutable = true; - } - - return err; - } - - wpan_phy_net_set(&rdev->wpan_phy, net); + if (err) + goto errout; err = device_rename(&rdev->wpan_phy.dev, dev_name(&rdev->wpan_phy.dev)); WARN_ON(err); + if (err) + goto errout; + + wpan_phy_net_set(&rdev->wpan_phy, net); + return 0; + +errout: + /* failed -- clean up to old netns */ + net = wpan_phy_net(&rdev->wpan_phy); + + list_for_each_entry_continue_reverse(wpan_dev, + &rdev->wpan_dev_list, + list) { + if (!wpan_dev->netdev) + continue; + wpan_dev->netdev->netns_immutable = false; + err = dev_change_net_namespace(wpan_dev->netdev, net, + "wpan%d"); + WARN_ON(err); + wpan_dev->netdev->netns_immutable = true; + } + + return err; } void cfg802154_dev_free(struct cfg802154_registered_device *rdev) From 6fcba77571c53f656a2f109259601a7570d4c74a Mon Sep 17 00:00:00 2001 From: Ivan Abramov Date: Thu, 3 Apr 2025 13:19:33 +0300 Subject: [PATCH 1812/2758] ieee802154: Avoid calling WARN_ON() on -ENOMEM in cfg802154_switch_netns() [ Upstream commit 0569f67ed6a7af838e2141da93c68e6b6013f483 ] It's pointless to call WARN_ON() in case of an allocation failure in dev_change_net_namespace() and device_rename(), since it only leads to useless splats caused by deliberate fault injections, so avoid it. Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: 66e5c2672cd1 ("ieee802154: add netns support") Reported-by: syzbot+e0bd4e4815a910c0daa8@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/000000000000f4a1b7061f9421de@google.com/#t Reviewed-by: Kuniyuki Iwashima Reviewed-by: Miquel Raynal Signed-off-by: Ivan Abramov Link: https://lore.kernel.org/20250403101935.991385-3-i.abramov@mt-integration.ru Signed-off-by: Stefan Schmidt Signed-off-by: Sasha Levin --- net/ieee802154/core.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c index 84d514430e45a..987c633e2c549 100644 --- a/net/ieee802154/core.c +++ b/net/ieee802154/core.c @@ -228,8 +228,10 @@ int cfg802154_switch_netns(struct cfg802154_registered_device *rdev, continue; wpan_dev->netdev->netns_immutable = false; err = dev_change_net_namespace(wpan_dev->netdev, net, "wpan%d"); - if (err) + if (err) { + WARN_ON(err && err != -ENOMEM); break; + } wpan_dev->netdev->netns_immutable = true; } @@ -237,7 +239,7 @@ int cfg802154_switch_netns(struct cfg802154_registered_device *rdev, goto errout; err = device_rename(&rdev->wpan_phy.dev, dev_name(&rdev->wpan_phy.dev)); - WARN_ON(err); + WARN_ON(err && err != -ENOMEM); if (err) goto errout; @@ -258,7 +260,7 @@ int cfg802154_switch_netns(struct cfg802154_registered_device *rdev, wpan_dev->netdev->netns_immutable = false; err = dev_change_net_namespace(wpan_dev->netdev, net, "wpan%d"); - WARN_ON(err); + WARN_ON(err && err != -ENOMEM); wpan_dev->netdev->netns_immutable = true; } From 4c3717546878c5f271943c574d1dc1fb6fd51b8f Mon Sep 17 00:00:00 2001 From: Ivan Abramov Date: Thu, 3 Apr 2025 13:19:34 +0300 Subject: [PATCH 1813/2758] ieee802154: Remove WARN_ON() in cfg802154_pernet_exit() [ Upstream commit e69ed6fc9fb3b386b5fcdb9f51623f122cee2ebd ] There's no need to call WARN_ON() in cfg802154_pernet_exit(), since every point of failure in cfg802154_switch_netns() is covered with WARN_ON(), so remove it. Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: 66e5c2672cd1 ("ieee802154: add netns support") Reviewed-by: Miquel Raynal Signed-off-by: Ivan Abramov Link: https://lore.kernel.org/20250403101935.991385-4-i.abramov@mt-integration.ru Signed-off-by: Stefan Schmidt Signed-off-by: Sasha Levin --- net/ieee802154/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c index 987c633e2c549..c0b8712018a16 100644 --- a/net/ieee802154/core.c +++ b/net/ieee802154/core.c @@ -358,7 +358,7 @@ static void __net_exit cfg802154_pernet_exit(struct net *net) rtnl_lock(); list_for_each_entry(rdev, &cfg802154_rdev_list, list) { if (net_eq(wpan_phy_net(&rdev->wpan_phy), net)) - WARN_ON(cfg802154_switch_netns(rdev, &init_net)); + cfg802154_switch_netns(rdev, &init_net); } rtnl_unlock(); } From f14802465f5956baafe5f4b4541eb626b06b41f1 Mon Sep 17 00:00:00 2001 From: Aleksandr Nogikh Date: Wed, 27 May 2026 20:18:18 +0000 Subject: [PATCH 1814/2758] ieee802154: fix kernel-infoleak in dgram_recvmsg() [ Upstream commit 4db86f8ab11b5a41bfc36680be837e6ac1375ec6 ] KMSAN reported a kernel-infoleak in move_addr_to_user(): BUG: KMSAN: kernel-infoleak in instrument_copy_to_user include/linux/instrumented.h:131 [inline] BUG: KMSAN: kernel-infoleak in _inline_copy_to_user include/linux/uaccess.h:205 [inline] BUG: KMSAN: kernel-infoleak in _copy_to_user+0xcc/0x120 lib/usercopy.c:26 instrument_copy_to_user include/linux/instrumented.h:131 [inline] _inline_copy_to_user include/linux/uaccess.h:205 [inline] _copy_to_user+0xcc/0x120 lib/usercopy.c:26 copy_to_user include/linux/uaccess.h:236 [inline] move_addr_to_user+0x2e7/0x440 net/socket.c:302 ____sys_recvmsg+0x232/0x610 net/socket.c:2925 ... Uninit was stored to memory at: ieee802154_addr_to_sa include/net/ieee802154_netdev.h:369 [inline] dgram_recvmsg+0xa09/0xbe0 net/ieee802154/socket.c:739 The issue occurs because the `pan_id` field of `struct ieee802154_addr` is left uninitialized when the address mode is `IEEE802154_ADDR_NONE`. The execution flow is as follows: 1. `__ieee802154_rx_handle_packet()` declares a local `struct ieee802154_hdr hdr` on the stack. 2. `ieee802154_hdr_pull()` calls `ieee802154_hdr_get_addr()` to parse the source and destination addresses into this structure. 3. If the address mode is `IEEE802154_ADDR_NONE`, `ieee802154_hdr_get_addr()` previously only set the `mode` field, leaving the `pan_id` field containing uninitialized stack memory. 4. This uninitialized `pan_id` is later copied into a `struct sockaddr_ieee802154` in `dgram_recvmsg()` via `ieee802154_addr_to_sa()`. 5. Finally, `move_addr_to_user()` copies the socket address structure to user space, leaking the uninitialized bytes. Fix this by using `memset` to zero out the address structure in `ieee802154_hdr_get_addr()` when the mode is `IEEE802154_ADDR_NONE`. Fixes: 94b4f6c21cf5 ("ieee802154: add header structs with endiannes and operations") Assisted-by: Gemini:gemini-3.1-pro-preview Gemini:gemini-3-flash-preview syzbot Reported-by: syzbot+346474e3bf0b26bd3090@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=346474e3bf0b26bd3090 Link: https://syzkaller.appspot.com/ai_job?id=a507a109-d683-4a2c-bc03-93394f491b17 Signed-off-by: Aleksandr Nogikh Reviewed-by: Miquel Raynal Link: https://lore.kernel.org/62795fd9-fc0c-48eb-bb82-05ffc5a57104@mail.kernel.org Signed-off-by: Stefan Schmidt Signed-off-by: Sasha Levin --- net/ieee802154/header_ops.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/net/ieee802154/header_ops.c b/net/ieee802154/header_ops.c index 41a556be10179..a9f0c8df5ae4a 100644 --- a/net/ieee802154/header_ops.c +++ b/net/ieee802154/header_ops.c @@ -173,10 +173,13 @@ ieee802154_hdr_get_addr(const u8 *buf, int mode, bool omit_pan, { int pos = 0; - addr->mode = mode; - - if (mode == IEEE802154_ADDR_NONE) + if (mode == IEEE802154_ADDR_NONE) { + memset(addr, 0, sizeof(*addr)); + addr->mode = IEEE802154_ADDR_NONE; return 0; + } + + addr->mode = mode; if (!omit_pan) { memcpy(&addr->pan_id, buf + pos, 2); From 4fe0635fe6042ec79899e7ae3202370f1a5ca999 Mon Sep 17 00:00:00 2001 From: Robertus Diawan Chris Date: Tue, 2 Jun 2026 12:41:33 +0700 Subject: [PATCH 1815/2758] mac802154: Prevent overwrite return code in mac802154_perform_association() [ Upstream commit 649147cb3f8b3c0c9aeba5d89d69a6ef221c12c2 ] When assoc_status not equal to IEEE802154_ASSOCIATION_SUCCESSFUL, the return value assigned to either "-ERANGE" or "-EPERM" but this return value will be overwritten to 0 after exiting the conditional scope. So, jump to clear_assoc label to preserve the return value when assoc_status not equal to IEEE802154_ASSOCIATION_SUCCESSFUL. This is reported by Coverity Scan as "Unused value". Fixes: fefd19807fe9 ("mac802154: Handle associating") Signed-off-by: Robertus Diawan Chris Reviewed-by: Miquel Raynal Link: https://lore.kernel.org/20260602054133.470293-1-robertusdchris@gmail.com Signed-off-by: Stefan Schmidt Signed-off-by: Sasha Levin --- net/mac802154/scan.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/mac802154/scan.c b/net/mac802154/scan.c index 0a31ac8d84155..300d4584533e6 100644 --- a/net/mac802154/scan.c +++ b/net/mac802154/scan.c @@ -594,6 +594,7 @@ int mac802154_perform_association(struct ieee802154_sub_if_data *sdata, "Negative ASSOC RESP received from %8phC: %s\n", &ceaddr, local->assoc_status == IEEE802154_PAN_AT_CAPACITY ? "PAN at capacity" : "access denied"); + goto clear_assoc; } ret = 0; From bffbbfcbd9393e315a7a4286dcd70e875265db9a Mon Sep 17 00:00:00 2001 From: Abd-Alrhman Masalkhi Date: Sat, 13 Jun 2026 18:28:07 +0000 Subject: [PATCH 1816/2758] md/raid1: fix writes_pending and barrier reference leaks on write failures [ Upstream commit 8e065a1602511282fc0da2dc89445e0eb71a681c ] raid1_make_request() acquires a writes_pending reference with md_write_start() before calling raid1_write_request(). Several failure paths in raid1_write_request() complete the bio and return without reaching the normal write completion path, causing the corresponding md_write_end() to be skipped. Make raid1_write_request() return a status indicating whether the write request was successfully queued. This allows raid1_make_request() to call md_write_end() when raid1_write_request() fails. Additionally, if wait_blocked_rdev() fails after wait_barrier() succeeds, the associated barrier reference is not released. Call allow_barrier() before returning from that path to keep the barrier accounting balanced. Fixes: b1a7ad8b5c4f ("md/raid1: Handle bio_split() errors") Fixes: f2a38abf5f1c ("md/raid1: Atomic write support") Fixes: 5aa705039c4f ("md: raid1 add nowait support") Reported-by: sashiko-bot Closes: https://sashiko.dev/#/patchset/20260611083514.754922-1-abd.masalkhi@gmail.com?part=1 Closes: https://sashiko.dev/#/patchset/20260611132500.763528-1-abd.masalkhi@gmail.com?part=1 Signed-off-by: Abd-Alrhman Masalkhi Link: https://patch.msgid.link/20260613182810.1317258-2-abd.masalkhi@gmail.com Signed-off-by: Yu Kuai Signed-off-by: Sasha Levin --- drivers/md/raid1.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 1f22250334bca..eb72dd31380ab 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1501,7 +1501,7 @@ static void raid1_start_write_behind(struct mddev *mddev, struct r1bio *r1_bio, } -static void raid1_write_request(struct mddev *mddev, struct bio *bio, +static bool raid1_write_request(struct mddev *mddev, struct bio *bio, int max_write_sectors) { struct r1conf *conf = mddev->private; @@ -1512,6 +1512,7 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, int max_sectors; bool write_behind = false; bool is_discard = (bio_op(bio) == REQ_OP_DISCARD); + sector_t sector = bio->bi_iter.bi_sector; if (mddev_is_clustered(mddev) && mddev->cluster_ops->area_resyncing(mddev, WRITE, @@ -1519,7 +1520,7 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, if (bio->bi_opf & REQ_NOWAIT) { bio_wouldblock_error(bio); - return; + return false; } wait_event_idle(conf->wait_barrier, !mddev->cluster_ops->area_resyncing(mddev, WRITE, @@ -1535,12 +1536,13 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, if (!wait_barrier(conf, bio->bi_iter.bi_sector, bio->bi_opf & REQ_NOWAIT)) { bio_wouldblock_error(bio); - return; + return false; } if (!wait_blocked_rdev(mddev, bio)) { bio_wouldblock_error(bio); - return; + allow_barrier(conf, sector); + return false; } r1_bio = alloc_r1bio(mddev, bio); @@ -1699,7 +1701,8 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, /* In case raid1d snuck in to freeze_array */ wake_up_barrier(conf); - return; + return true; + err_handle: for (k = 0; k < i; k++) { if (r1_bio->bios[k]) { @@ -1709,6 +1712,7 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, } raid_end_bio_io(r1_bio); + return false; } static bool raid1_make_request(struct mddev *mddev, struct bio *bio) @@ -1732,8 +1736,9 @@ static bool raid1_make_request(struct mddev *mddev, struct bio *bio) if (bio_data_dir(bio) == READ) raid1_read_request(mddev, bio, sectors, NULL); else { - md_write_start(mddev,bio); - raid1_write_request(mddev, bio, sectors); + md_write_start(mddev, bio); + if (!raid1_write_request(mddev, bio, sectors)) + md_write_end(mddev); } return true; } From f94031c94eaebe14a7c9e91720064de0c5a54a6c Mon Sep 17 00:00:00 2001 From: Abd-Alrhman Masalkhi Date: Sat, 13 Jun 2026 18:28:08 +0000 Subject: [PATCH 1817/2758] md/raid10: fix writes_pending leak on write request failures [ Upstream commit e045d6ed33f8faa3e3dd6dc33c62ec01e3ad275d ] raid10_make_request() acquires a writes_pending reference with md_write_start() before dispatching write requests. Several failure paths in raid10_write_request() complete the bio and return without reaching the normal write completion path, causing the corresponding md_write_end() to be skipped. Make raid10_write_request() return a status indicating whether the write request was successfully queued. This allows raid10_make_request() to release the writes_pending reference with md_write_end() when a write request fails. Fixes: 4cf58d952909 ("md/raid10: Handle bio_split() errors") Fixes: c9aa889b035f ("md: raid10 add nowait support") Signed-off-by: Abd-Alrhman Masalkhi Link: https://patch.msgid.link/20260613182810.1317258-3-abd.masalkhi@gmail.com Signed-off-by: Yu Kuai Signed-off-by: Sasha Levin --- drivers/md/raid10.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 5bd7698e0a1b5..5ad1b0c6207a7 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1349,7 +1349,7 @@ static void wait_blocked_dev(struct mddev *mddev, struct r10bio *r10_bio) } } -static void raid10_write_request(struct mddev *mddev, struct bio *bio, +static bool raid10_write_request(struct mddev *mddev, struct bio *bio, struct r10bio *r10_bio) { struct r10conf *conf = mddev->private; @@ -1365,7 +1365,7 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio, /* Bail out if REQ_NOWAIT is set for the bio */ if (bio->bi_opf & REQ_NOWAIT) { bio_wouldblock_error(bio); - return; + return false; } for (;;) { prepare_to_wait(&conf->wait_barrier, @@ -1381,7 +1381,7 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio, sectors = r10_bio->sectors; if (!regular_request_wait(mddev, conf, bio, sectors)) { free_r10bio(r10_bio); - return; + return false; } if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) && @@ -1398,7 +1398,7 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio, if (bio->bi_opf & REQ_NOWAIT) { allow_barrier(conf); bio_wouldblock_error(bio); - return; + return false; } mddev_add_trace_msg(conf->mddev, "raid10 wait reshape metadata"); @@ -1514,7 +1514,8 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio, raid10_write_one_disk(mddev, r10_bio, bio, true, i); } one_write_done(r10_bio); - return; + return true; + err_handle: for (k = 0; k < i; k++) { int d = r10_bio->devs[k].devnum; @@ -1532,10 +1533,12 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio, } raid_end_bio_io(r10_bio); + return false; } -static void __make_request(struct mddev *mddev, struct bio *bio, int sectors) +static bool __make_request(struct mddev *mddev, struct bio *bio, int sectors) { + bool ret; struct r10conf *conf = mddev->private; struct r10bio *r10_bio; @@ -1551,10 +1554,13 @@ static void __make_request(struct mddev *mddev, struct bio *bio, int sectors) memset(r10_bio->devs, 0, sizeof(r10_bio->devs[0]) * conf->geo.raid_disks); + ret = true; if (bio_data_dir(bio) == READ) raid10_read_request(mddev, bio, r10_bio); else - raid10_write_request(mddev, bio, r10_bio); + ret = raid10_write_request(mddev, bio, r10_bio); + + return ret; } static void raid_end_discard_bio(struct r10bio *r10bio) @@ -1900,7 +1906,8 @@ static bool raid10_make_request(struct mddev *mddev, struct bio *bio) sectors = chunk_sects - (bio->bi_iter.bi_sector & (chunk_sects - 1)); - __make_request(mddev, bio, sectors); + if (!__make_request(mddev, bio, sectors)) + md_write_end(mddev); /* In case raid10d snuck in to freeze_array */ wake_up_barrier(conf); From d1324b41dabd26787559efaeb430643c627c1eb0 Mon Sep 17 00:00:00 2001 From: Abd-Alrhman Masalkhi Date: Sat, 13 Jun 2026 18:28:09 +0000 Subject: [PATCH 1818/2758] md/raid10: fix writes_pending and barrier reference leaks on discard failures [ Upstream commit 393d687131d8aa8c7e4de2cb494438e145d20fc2 ] raid10_make_request() acquires a writes_pending reference with md_write_start() before calling raid10_handle_discard(). Several failure paths in raid10_handle_discard() complete the bio and return without releasing the corresponding reference, causing md_write_end() to be skipped. Call md_write_end() before returning from these failure paths to keep writes_pending accounting balanced. Additionally, discard split allocation failures can occur after wait_barrier() succeeds. Those paths return without calling allow_barrier(), leaking the associated barrier reference. Release the barrier before returning from those paths. Fixes: c9aa889b035f ("md: raid10 add nowait support") Fixes: 4cf58d952909 ("md/raid10: Handle bio_split() errors") Signed-off-by: Abd-Alrhman Masalkhi Link: https://patch.msgid.link/20260613182810.1317258-4-abd.masalkhi@gmail.com Signed-off-by: Yu Kuai Signed-off-by: Sasha Levin --- drivers/md/raid10.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 5ad1b0c6207a7..aacf160ee9f20 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1639,6 +1639,7 @@ static int raid10_handle_discard(struct mddev *mddev, struct bio *bio) if (!wait_barrier(conf, bio->bi_opf & REQ_NOWAIT)) { bio_wouldblock_error(bio); + md_write_end(mddev); return 0; } @@ -1681,6 +1682,8 @@ static int raid10_handle_discard(struct mddev *mddev, struct bio *bio) if (IS_ERR(split)) { bio->bi_status = errno_to_blk_status(PTR_ERR(split)); bio_endio(bio); + md_write_end(mddev); + allow_barrier(conf); return 0; } @@ -1698,6 +1701,8 @@ static int raid10_handle_discard(struct mddev *mddev, struct bio *bio) if (IS_ERR(split)) { bio->bi_status = errno_to_blk_status(PTR_ERR(split)); bio_endio(bio); + md_write_end(mddev); + allow_barrier(conf); return 0; } From 937c3e44ecaf5425c892256007dbfc1f78748d1f Mon Sep 17 00:00:00 2001 From: Abd-Alrhman Masalkhi Date: Thu, 11 Jun 2026 08:35:14 +0000 Subject: [PATCH 1819/2758] md/raid1: honor REQ_NOWAIT when waiting for behind writes [ Upstream commit a286cb88ddb26c5f4377859d8e77233d9181eb82 ] raid1 supports REQ_NOWAIT reads by avoiding waits in the barrier path through wait_read_barrier(). However, a read can still block on a WriteMostly device when the array uses a bitmap and there are outstanding behind writes. In that case raid1 unconditionally calls wait_behind_writes(), which may sleep until all behind writes complete. As a result, a REQ_NOWAIT read can block despite the caller explicitly requesting non-blocking behavior. This ensures that raid1 consistently honors REQ_NOWAIT reads across all paths that may otherwise wait for behind writes. Fixes: 5aa705039c4f ("md: raid1 add nowait support") Signed-off-by: Abd-Alrhman Masalkhi Link: https://patch.msgid.link/20260611083514.754922-1-abd.masalkhi@gmail.com Signed-off-by: Yu Kuai Signed-off-by: Sasha Levin --- drivers/md/md-bitmap.c | 9 +++++++-- drivers/md/md-bitmap.h | 2 +- drivers/md/md-llbitmap.c | 13 ++++++++----- drivers/md/md.c | 2 +- drivers/md/raid1.c | 10 +++++++--- 5 files changed, 24 insertions(+), 12 deletions(-) diff --git a/drivers/md/md-bitmap.c b/drivers/md/md-bitmap.c index 028b9ca8ce52d..1206e31f323ad 100644 --- a/drivers/md/md-bitmap.c +++ b/drivers/md/md-bitmap.c @@ -2063,18 +2063,23 @@ static void bitmap_end_behind_write(struct mddev *mddev) bitmap->mddev->bitmap_info.max_write_behind); } -static void bitmap_wait_behind_writes(struct mddev *mddev) +static bool bitmap_wait_behind_writes(struct mddev *mddev, bool nowait) { struct bitmap *bitmap = mddev->bitmap; /* wait for behind writes to complete */ if (bitmap && atomic_read(&bitmap->behind_writes) > 0) { + if (nowait) + return false; + pr_debug("md:%s: behind writes in progress - waiting to stop.\n", mdname(mddev)); /* need to kick something here to make sure I/O goes? */ wait_event(bitmap->behind_wait, atomic_read(&bitmap->behind_writes) == 0); } + + return true; } static void bitmap_destroy(struct mddev *mddev) @@ -2084,7 +2089,7 @@ static void bitmap_destroy(struct mddev *mddev) if (!bitmap) /* there was no bitmap */ return; - bitmap_wait_behind_writes(mddev); + bitmap_wait_behind_writes(mddev, false); if (!test_bit(MD_SERIALIZE_POLICY, &mddev->flags)) mddev_destroy_serial_pool(mddev, NULL); diff --git a/drivers/md/md-bitmap.h b/drivers/md/md-bitmap.h index 214f623c7e790..f46674bdfeb91 100644 --- a/drivers/md/md-bitmap.h +++ b/drivers/md/md-bitmap.h @@ -98,7 +98,7 @@ struct bitmap_operations { void (*start_behind_write)(struct mddev *mddev); void (*end_behind_write)(struct mddev *mddev); - void (*wait_behind_writes)(struct mddev *mddev); + bool (*wait_behind_writes)(struct mddev *mddev, bool nowait); md_bitmap_fn *start_write; md_bitmap_fn *end_write; diff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c index 1adc5b1178216..5a4e2abaa7577 100644 --- a/drivers/md/md-llbitmap.c +++ b/drivers/md/md-llbitmap.c @@ -1574,16 +1574,19 @@ static void llbitmap_end_behind_write(struct mddev *mddev) wake_up(&llbitmap->behind_wait); } -static void llbitmap_wait_behind_writes(struct mddev *mddev) +static bool llbitmap_wait_behind_writes(struct mddev *mddev, bool nowait) { struct llbitmap *llbitmap = mddev->bitmap; - if (!llbitmap) - return; + if (llbitmap && atomic_read(&llbitmap->behind_writes) > 0) { + if (nowait) + return false; - wait_event(llbitmap->behind_wait, - atomic_read(&llbitmap->behind_writes) == 0); + wait_event(llbitmap->behind_wait, + atomic_read(&llbitmap->behind_writes) == 0); + } + return true; } static ssize_t bits_show(struct mddev *mddev, char *page) diff --git a/drivers/md/md.c b/drivers/md/md.c index 518d1d9c22031..6d1ddbdbbf8d2 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -7049,7 +7049,7 @@ EXPORT_SYMBOL_GPL(md_stop_writes); static void mddev_detach(struct mddev *mddev) { if (md_bitmap_enabled(mddev, false)) - mddev->bitmap_ops->wait_behind_writes(mddev); + mddev->bitmap_ops->wait_behind_writes(mddev, false); if (mddev->pers && mddev->pers->quiesce && !is_md_suspended(mddev)) { mddev->pers->quiesce(mddev, 1); mddev->pers->quiesce(mddev, 0); diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index eb72dd31380ab..9a3df3cb7bdfe 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1341,6 +1341,7 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio, int max_sectors; int rdisk; bool r1bio_existed = !!r1_bio; + bool nowait = bio->bi_opf & REQ_NOWAIT; /* * An md cloned bio indicates we are in the error path. @@ -1360,8 +1361,7 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio, * Still need barrier for READ in case that whole * array is frozen. */ - if (!wait_read_barrier(conf, bio->bi_iter.bi_sector, - bio->bi_opf & REQ_NOWAIT)) { + if (!wait_read_barrier(conf, bio->bi_iter.bi_sector, nowait)) { bio_wouldblock_error(bio); return; } @@ -1402,7 +1402,11 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio, * over-take any writes that are 'behind' */ mddev_add_trace_msg(mddev, "raid1 wait behind writes"); - mddev->bitmap_ops->wait_behind_writes(mddev); + if (!mddev->bitmap_ops->wait_behind_writes(mddev, nowait)) { + bio_wouldblock_error(bio); + set_bit(R1BIO_Returned, &r1_bio->state); + goto err_handle; + } } if (max_sectors < bio_sectors(bio)) { From c6e6354295845698d2fdfa20b71fc404786f7e93 Mon Sep 17 00:00:00 2001 From: Abd-Alrhman Masalkhi Date: Thu, 11 Jun 2026 10:13:50 +0000 Subject: [PATCH 1820/2758] md/raid1: free r1_bio when REQ_NOWAIT is set and read would block on retry [ Upstream commit 69ad6ce47f9bf2b9fe0ed69b042db993d33bbf12 ] When a read is retried, raid1_read_request() may be called with a pre-allocated r1_bio. If wait_read_barrier() fails for a REQ_NOWAIT read, the bio is completed and the function returns immediately. In this case the existing r1_bio is leaked. This fixes a leak of pre-allocated r1_bio structures for retried reads. Fixes: 5aa705039c4f ("md: raid1 add nowait support") Reported-by: sashiko-bot Closes: https://sashiko.dev/#/patchset/20260611083514.754922-1-abd.masalkhi@gmail.com?part=1 Signed-off-by: Abd-Alrhman Masalkhi Link: https://patch.msgid.link/20260611101350.759154-1-abd.masalkhi@gmail.com Signed-off-by: Yu Kuai Signed-off-by: Sasha Levin --- drivers/md/raid1.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 9a3df3cb7bdfe..1f67e4efa09af 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1363,6 +1363,12 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio, */ if (!wait_read_barrier(conf, bio->bi_iter.bi_sector, nowait)) { bio_wouldblock_error(bio); + + if (r1bio_existed) { + set_bit(R1BIO_Returned, &r1_bio->state); + raid_end_bio_io(r1_bio); + } + return; } From 3219d74e4536658c937fd878a327257b86ce80dd Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Wed, 17 Jun 2026 10:41:22 +0200 Subject: [PATCH 1821/2758] netfilter: ipset: Don't use test_bit() in lockless RCU readers in hash types [ Upstream commit e4b4984e28c16406ecb318444dea4a8bf47def3e ] Sashiko pointed out that there are a few lockless RCU readers using test_bit() which is a relaxed atomic operation and provides no memory barrier guarantees. Use test_bit_acquire() instead where the operation may run parallel with add/del/gc, i.e. is not one from the next cases - protected by region lock - in a set destroy phase - in a new/temporary set creation phase Fixes: 18f84d41d34f ("netfilter: ipset: Introduce RCU locking in hash:* types") Signed-off-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/ipset/ip_set_hash_gen.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index 04e4627ddfc10..00c27b95207f7 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -689,7 +689,7 @@ mtype_resize(struct ip_set *set, bool retried) continue; pos = smp_load_acquire(&n->pos); for (j = 0; j < pos; j++) { - if (!test_bit(j, n->used)) + if (!test_bit_acquire(j, n->used)) continue; data = ahash_data(n, j, dsize); if (SET_ELEM_EXPIRED(set, data)) @@ -826,7 +826,7 @@ mtype_ext_size(struct ip_set *set, u32 *elements, size_t *ext_size) continue; pos = smp_load_acquire(&n->pos); for (j = 0; j < pos; j++) { - if (!test_bit(j, n->used)) + if (!test_bit_acquire(j, n->used)) continue; data = ahash_data(n, j, set->dsize); if (!SET_ELEM_EXPIRED(set, data)) @@ -1201,7 +1201,7 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d, continue; pos = smp_load_acquire(&n->pos); for (i = 0; i < pos; i++) { - if (!test_bit(i, n->used)) + if (!test_bit_acquire(i, n->used)) continue; data = ahash_data(n, i, set->dsize); if (!mtype_data_equal(data, d, &multi)) @@ -1259,7 +1259,7 @@ mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext, } pos = smp_load_acquire(&n->pos); for (i = 0; i < pos; i++) { - if (!test_bit(i, n->used)) + if (!test_bit_acquire(i, n->used)) continue; data = ahash_data(n, i, set->dsize); if (!mtype_data_equal(data, d, &multi)) @@ -1396,7 +1396,7 @@ mtype_list(const struct ip_set *set, continue; pos = smp_load_acquire(&n->pos); for (i = 0; i < pos; i++) { - if (!test_bit(i, n->used)) + if (!test_bit_acquire(i, n->used)) continue; e = ahash_data(n, i, set->dsize); if (SET_ELEM_EXPIRED(set, e)) From 7efd8a1c96c73c29fd77431c88673eb49b0696ac Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Wed, 17 Jun 2026 10:41:23 +0200 Subject: [PATCH 1822/2758] netfilter: ipset: Don't use test_bit() in lockless RCU readers in bitmap types [ Upstream commit 1171192ac9af46ddf65caf4162f1b64c58ae37f4 ] The pair of the patch "netfilter: ipset: Don't use test_bit() in lockless RCU readers in hash types" for the bitmap types. Fixes: 02a3231b6d82 ("netfilter: nf_conntrack_expect: store netns and zone in expectation") Fixes: b0da3905bb1e ("netfilter: ipset: Bitmap types using the unified code base") Signed-off-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/ipset/ip_set_bitmap_gen.h | 4 +++- net/netfilter/ipset/ip_set_bitmap_ip.c | 2 +- net/netfilter/ipset/ip_set_bitmap_ipmac.c | 2 +- net/netfilter/ipset/ip_set_bitmap_port.c | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/net/netfilter/ipset/ip_set_bitmap_gen.h b/net/netfilter/ipset/ip_set_bitmap_gen.h index 798c7993635e6..bb9b5bed10e19 100644 --- a/net/netfilter/ipset/ip_set_bitmap_gen.h +++ b/net/netfilter/ipset/ip_set_bitmap_gen.h @@ -165,6 +165,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, ip_set_init_skbinfo(ext_skbinfo(x, set), ext); /* Activate element */ + smp_mb__before_atomic(); set_bit(e->id, map->members); set->elements++; @@ -219,7 +220,7 @@ mtype_list(const struct ip_set *set, cond_resched_rcu(); id = cb->args[IPSET_CB_ARG0]; x = get_ext(set, map, id); - if (!test_bit(id, map->members) || + if (!test_bit_acquire(id, map->members) || (SET_WITH_TIMEOUT(set) && #ifdef IP_SET_BITMAP_STORED_TIMEOUT mtype_is_filled(x) && @@ -278,6 +279,7 @@ mtype_gc(struct timer_list *t) x = get_ext(set, map, id); if (ip_set_timeout_expired(ext_timeout(x, set))) { clear_bit(id, map->members); + smp_mb__after_atomic(); ip_set_ext_destroy(set, x); set->elements--; } diff --git a/net/netfilter/ipset/ip_set_bitmap_ip.c b/net/netfilter/ipset/ip_set_bitmap_ip.c index 5988b9bb9029d..ac7febce074f1 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ip.c +++ b/net/netfilter/ipset/ip_set_bitmap_ip.c @@ -67,7 +67,7 @@ static int bitmap_ip_do_test(const struct bitmap_ip_adt_elem *e, struct bitmap_ip *map, size_t dsize) { - return !!test_bit(e->id, map->members); + return !!test_bit_acquire(e->id, map->members); } static int diff --git a/net/netfilter/ipset/ip_set_bitmap_ipmac.c b/net/netfilter/ipset/ip_set_bitmap_ipmac.c index 752f59ef87442..5921fd9d2dca0 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ipmac.c +++ b/net/netfilter/ipset/ip_set_bitmap_ipmac.c @@ -86,7 +86,7 @@ bitmap_ipmac_do_test(const struct bitmap_ipmac_adt_elem *e, { const struct bitmap_ipmac_elem *elem; - if (!test_bit(e->id, map->members)) + if (!test_bit_acquire(e->id, map->members)) return 0; elem = get_const_elem(map->extensions, e->id, dsize); if (e->add_mac && elem->filled == MAC_FILLED) diff --git a/net/netfilter/ipset/ip_set_bitmap_port.c b/net/netfilter/ipset/ip_set_bitmap_port.c index 7138e080def4c..ca875c9824245 100644 --- a/net/netfilter/ipset/ip_set_bitmap_port.c +++ b/net/netfilter/ipset/ip_set_bitmap_port.c @@ -58,7 +58,7 @@ static int bitmap_port_do_test(const struct bitmap_port_adt_elem *e, const struct bitmap_port *map, size_t dsize) { - return !!test_bit(e->id, map->members); + return !!test_bit_acquire(e->id, map->members); } static int From 93a775fd67f3ef34949a9523bfa69403ee74efdd Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Wed, 17 Jun 2026 10:41:24 +0200 Subject: [PATCH 1823/2758] netfilter: ipset: fix order of kfree_rcu() and rcu_assign_pointer() [ Upstream commit 3ca9982a8882470aa0ac4e8bb9a552b181d1efcd ] Sashiko pointed out that kfree_rcu() was called before rcu_assign_pointer() in handling the comment extension. Fix the order so that rcu_assign_pointer() called first. Fixes: b57b2d1fa53f ("netfilter: ipset: Prepare the ipset core to use RCU at set level") Signed-off-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/ipset/ip_set_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 955556513eeb6..6cfad152d7d1b 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -351,8 +351,8 @@ ip_set_init_comment(struct ip_set *set, struct ip_set_comment *comment, if (unlikely(c)) { set->ext_size -= sizeof(*c) + strlen(c->str) + 1; - kfree_rcu(c, rcu); rcu_assign_pointer(comment->c, NULL); + kfree_rcu(c, rcu); } if (!len) return; @@ -393,8 +393,8 @@ ip_set_comment_free(struct ip_set *set, void *ptr) if (unlikely(!c)) return; set->ext_size -= sizeof(*c) + strlen(c->str) + 1; - kfree_rcu(c, rcu); rcu_assign_pointer(comment->c, NULL); + kfree_rcu(c, rcu); } typedef void (*destroyer)(struct ip_set *, void *); From c78bd5195a5998094a5866df702fbeafda60dafb Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Wed, 17 Jun 2026 10:41:26 +0200 Subject: [PATCH 1824/2758] netfilter: ipset: make sure gc is properly stopped [ Upstream commit 4a597a87e2e2f608edb6be2c510dc826b4fdfb53 ] Sashiko noticed that when destroying a set, cancel_delayed_work_sync() was called while gc calls queue_delayed_work() unconditionally which can lead not to properly shutting down the gc. Fixes: f66ee0410b1c ("netfilter: ipset: Fix "INFO: rcu detected stall in hash_xxx" reports") Signed-off-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/ipset/ip_set_hash_gen.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index 00c27b95207f7..dedf59b661ddf 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -606,7 +606,7 @@ mtype_cancel_gc(struct ip_set *set) struct htype *h = set->data; if (SET_WITH_TIMEOUT(set)) - cancel_delayed_work_sync(&h->gc.dwork); + disable_delayed_work_sync(&h->gc.dwork); } static int From 94daa48ea7b6af36839a4d425e0cf201aea24176 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 18 Jun 2026 06:58:24 +0200 Subject: [PATCH 1825/2758] netfilter: nft_payload: reject offsets exceeding 65535 bytes [ Upstream commit 213be32f46a29ca15a314df06c3424ecffd6c90a ] Large offsets were rejected based on netlink policy, but blamed commit removed the policy without updating nft_payload_inner_init() to use the truncation-check helper. Silent truncation is not a problem, but not wanted either, so add a check. Fixes: 077dc4a27579 ("netfilter: nft_payload: extend offset to 65535 bytes") Signed-off-by: Florian Westphal Reviewed-by: Fernando Fernandez Mancera Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nft_payload.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index 484a5490832e4..4438d05f8018c 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c @@ -224,11 +224,17 @@ static int nft_payload_init(const struct nft_ctx *ctx, const struct nlattr * const tb[]) { struct nft_payload *priv = nft_expr_priv(expr); + u32 offset; + int err; priv->base = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_BASE])); - priv->offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_OFFSET])); priv->len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN])); + err = nft_parse_u32_check(tb[NFTA_PAYLOAD_OFFSET], U16_MAX, &offset); + if (err < 0) + return err; + priv->offset = offset; + return nft_parse_register_store(ctx, tb[NFTA_PAYLOAD_DREG], &priv->dreg, NULL, NFT_DATA_VALUE, priv->len); @@ -621,7 +627,8 @@ static int nft_payload_inner_init(const struct nft_ctx *ctx, const struct nlattr * const tb[]) { struct nft_payload *priv = nft_expr_priv(expr); - u32 base; + u32 base, offset; + int err; if (!tb[NFTA_PAYLOAD_BASE] || !tb[NFTA_PAYLOAD_OFFSET] || !tb[NFTA_PAYLOAD_LEN] || !tb[NFTA_PAYLOAD_DREG]) @@ -639,8 +646,11 @@ static int nft_payload_inner_init(const struct nft_ctx *ctx, } priv->base = base; - priv->offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_OFFSET])); priv->len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN])); + err = nft_parse_u32_check(tb[NFTA_PAYLOAD_OFFSET], U16_MAX, &offset); + if (err < 0) + return err; + priv->offset = offset; return nft_parse_register_store(ctx, tb[NFTA_PAYLOAD_DREG], &priv->dreg, NULL, NFT_DATA_VALUE, From c3167c9c6433a3586290571d327203cb9a4ca2a3 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 18 Jun 2026 08:16:18 +0200 Subject: [PATCH 1826/2758] netfilter: nft_meta_bridge: add validate callback for get operations [ Upstream commit bff1c8b49a9cb5c04af20f4e7d43bf4af5863bc6 ] Blamed commit added NFT_META_BRI_IIFHWADDR to the set validate callback, yet this is a get operation. Add a get validate callback and move the NFT_META_BRI_IIFHWADDR key there. AFAICS this is harmless, NFT_META_BRI_IIFHWADDR can deal with a NULL input device and the set handler ignores a NFT_META_BRI_IIFHWADDR operation, but it allows to read 4 bytes off bridge skb->cb[]. Fixes: cbd2257dc96e ("netfilter: nft_meta_bridge: introduce NFT_META_BRI_IIFHWADDR support") Signed-off-by: Florian Westphal Reviewed-by: Fernando Fernandez Mancera Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- include/net/netfilter/nft_meta.h | 2 ++ net/bridge/netfilter/nft_meta_bridge.c | 19 ++++++++++++++++++- net/netfilter/nft_meta.c | 5 +++-- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/include/net/netfilter/nft_meta.h b/include/net/netfilter/nft_meta.h index f74e63290603d..6cf1d910bbf8d 100644 --- a/include/net/netfilter/nft_meta.h +++ b/include/net/netfilter/nft_meta.h @@ -40,6 +40,8 @@ void nft_meta_set_eval(const struct nft_expr *expr, void nft_meta_set_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr); +int nft_meta_get_validate(const struct nft_ctx *ctx, + const struct nft_expr *expr); int nft_meta_set_validate(const struct nft_ctx *ctx, const struct nft_expr *expr); diff --git a/net/bridge/netfilter/nft_meta_bridge.c b/net/bridge/netfilter/nft_meta_bridge.c index 219c406802602..3d95f68e0906a 100644 --- a/net/bridge/netfilter/nft_meta_bridge.c +++ b/net/bridge/netfilter/nft_meta_bridge.c @@ -107,12 +107,30 @@ static int nft_meta_bridge_get_init(const struct nft_ctx *ctx, NULL, NFT_DATA_VALUE, len); } +static int nft_meta_bridge_get_validate(const struct nft_ctx *ctx, + const struct nft_expr *expr) +{ + struct nft_meta *priv = nft_expr_priv(expr); + unsigned int hooks; + + switch (priv->key) { + case NFT_META_BRI_IIFHWADDR: + hooks = 1 << NF_BR_PRE_ROUTING; + break; + default: + return nft_meta_get_validate(ctx, expr); + } + + return nft_chain_validate_hooks(ctx->chain, hooks); +} + static struct nft_expr_type nft_meta_bridge_type; static const struct nft_expr_ops nft_meta_bridge_get_ops = { .type = &nft_meta_bridge_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), .eval = nft_meta_bridge_get_eval, .init = nft_meta_bridge_get_init, + .validate = nft_meta_bridge_get_validate, .dump = nft_meta_get_dump, }; @@ -168,7 +186,6 @@ static int nft_meta_bridge_set_validate(const struct nft_ctx *ctx, switch (priv->key) { case NFT_META_BRI_BROUTE: - case NFT_META_BRI_IIFHWADDR: hooks = 1 << NF_BR_PRE_ROUTING; break; default: diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index 5b25851381e52..db937d66210d9 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -635,8 +635,8 @@ static int nft_meta_get_validate_xfrm(const struct nft_ctx *ctx) #endif } -static int nft_meta_get_validate(const struct nft_ctx *ctx, - const struct nft_expr *expr) +int nft_meta_get_validate(const struct nft_ctx *ctx, + const struct nft_expr *expr) { const struct nft_meta *priv = nft_expr_priv(expr); @@ -652,6 +652,7 @@ static int nft_meta_get_validate(const struct nft_ctx *ctx, return 0; } +EXPORT_SYMBOL_GPL(nft_meta_get_validate); int nft_meta_set_validate(const struct nft_ctx *ctx, const struct nft_expr *expr) From a75f7745dc8fe8b4120eac64cc8bc6305c0df968 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 18 Jun 2026 08:25:47 +0200 Subject: [PATCH 1827/2758] netfilter: nft_flow_offload: zero device address for non-ether case [ Upstream commit e409c23c2d0630f3b95efd12428b2e58800b7645 ] LLM points out that the skip causes unitialised stack array to propagate down into dev_fill_forward_path(). Its not clear to me that there is a guarantee that a later ctx.dev->netdev_ops->ndo_fill_forward_path() would always fix this up. Cc: Felix Fietkau Fixes: 45ca3e61999e ("netfilter: nft_flow_offload: skip dst neigh lookup for ppp devices") Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_flow_table_path.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/netfilter/nf_flow_table_path.c b/net/netfilter/nf_flow_table_path.c index a3e6b82f2f8e9..14e8a3e4a6f87 100644 --- a/net/netfilter/nf_flow_table_path.c +++ b/net/netfilter/nf_flow_table_path.c @@ -53,8 +53,10 @@ static int nft_dev_fill_forward_path(const struct nf_flow_route *route, struct neighbour *n; u8 nud_state; - if (!nft_is_valid_ether_device(dev)) + if (!nft_is_valid_ether_device(dev)) { + eth_zero_addr(ha); goto out; + } n = dst_neigh_lookup(dst_cache, daddr); if (!n) From a259780ddf1d98372c531ab0cc38e80565c34666 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 18 Jun 2026 10:49:24 +0200 Subject: [PATCH 1828/2758] netfilter: nf_reject: skip iphdr options when looking for icmp header [ Upstream commit af8d6ae09c0a5f8b8a0d5680203c74b3c1daa85b ] Not a big deal but this hould have used the real ip header length and not the base header size. As-is, if there are options then nf_skb_is_icmp_unreach() result will be random. Fixes: db99b2f2b3e2 ("netfilter: nf_reject: don't reply to icmp error messages") Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/ipv4/netfilter/nf_reject_ipv4.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/netfilter/nf_reject_ipv4.c b/net/ipv4/netfilter/nf_reject_ipv4.c index fecf6621f679f..4626dc46808f3 100644 --- a/net/ipv4/netfilter/nf_reject_ipv4.c +++ b/net/ipv4/netfilter/nf_reject_ipv4.c @@ -89,7 +89,7 @@ static bool nf_skb_is_icmp_unreach(const struct sk_buff *skb) if (iph->protocol != IPPROTO_ICMP) return false; - thoff = skb_network_offset(skb) + sizeof(*iph); + thoff = skb_network_offset(skb) + ip_hdrlen(skb); tp = skb_header_pointer(skb, thoff + offsetof(struct icmphdr, type), From 4dce8bf588a89bef99b446136d15685a837e7acf Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Fri, 19 Jun 2026 00:34:49 +0200 Subject: [PATCH 1829/2758] netfilter: nft_meta_bridge: fix NFT_META_BRI_IIFPVID stack leak [ Upstream commit 27dd2997746d54ebc079bb13161cc1bdd401d4a6 ] This needs to test for nonzero retval. Fixes: c54c7c685494 ("netfilter: nft_meta_bridge: add NFT_META_BRI_IIFPVID support") Closes: https://sashiko.dev/#/patchset/20260618061631.21919-1-fw%40strlen.de Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/bridge/netfilter/nft_meta_bridge.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/bridge/netfilter/nft_meta_bridge.c b/net/bridge/netfilter/nft_meta_bridge.c index 3d95f68e0906a..e4c9aa1f64e25 100644 --- a/net/bridge/netfilter/nft_meta_bridge.c +++ b/net/bridge/netfilter/nft_meta_bridge.c @@ -44,7 +44,9 @@ static void nft_meta_bridge_get_eval(const struct nft_expr *expr, if (!br_dev || !br_vlan_enabled(br_dev)) goto err; - br_vlan_get_pvid_rcu(in, &p_pvid); + if (br_vlan_get_pvid_rcu(in, &p_pvid)) + goto err; + nft_reg_store16(dest, p_pvid); return; } From c041d2be785feb9b5e36331921eadaefbe65349d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 12 May 2026 18:16:23 +0200 Subject: [PATCH 1830/2758] tpm_crb: Check ACPI_COMPANION() against NULL during probe [ Upstream commit ddd33806b8911fa2ef849e8bbbab1e3fcb26adc0 ] Every platform driver can be forced to match a device that doesn't match its list of device IDs because of device_match_driver_override(), so platform drivers that rely on the existence of a device's ACPI companion object need to verify its presence. Accordingly, add a requisite ACPI_COMPANION() check against NULL to the tpm_crb driver. Fixes: 48fe2cddc85c ("tpm_crb: Convert ACPI driver to a platform one") Signed-off-by: Rafael J. Wysocki Link: https://lore.kernel.org/r/2848144.mvXUDI8C0e@rafael.j.wysocki Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen Signed-off-by: Sasha Levin --- drivers/char/tpm/tpm_crb.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 7d1377e8e616a..ceb4100ba400f 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -786,8 +786,8 @@ static int crb_map_pluton(struct device *dev, struct crb_priv *priv, static int crb_acpi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct acpi_device *device = ACPI_COMPANION(dev); struct acpi_table_tpm2 *buf; + struct acpi_device *device; struct crb_priv *priv; struct tpm_chip *chip; struct tpm2_crb_smc *crb_smc; @@ -797,6 +797,10 @@ static int crb_acpi_probe(struct platform_device *pdev) u32 sm; int rc; + device = ACPI_COMPANION(dev); + if (!device) + return -ENODEV; + status = acpi_get_table(ACPI_SIG_TPM2, 1, (struct acpi_table_header **) &buf); if (ACPI_FAILURE(status) || buf->header.length < sizeof(*buf)) { From 74b19383580d63dea43644a851fefb203691a9c4 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 17 Jun 2026 08:55:26 +0200 Subject: [PATCH 1831/2758] mailbox: imx: Forward the timeout/ error in imx_mu_generic_tx() [ Upstream commit 82ef9a635d7130ca27ec9dd88c16afc39c83a4e8 ] imx_mu_generic_tx() for the IMX_MU_TYPE_TXDB_V2 type polls on a register which may timeout and is recognized as an error. This error is siltently dropped and not dropped to the caller. Forward the error to the caller. Fixes: b5ef17917f3a7 ("mailbox: imx: fix TXDB_V2 channel race condition") Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Jassi Brar Signed-off-by: Sasha Levin --- drivers/mailbox/imx-mailbox.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index 246a9a9e39520..0028073be4a71 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -227,6 +227,7 @@ static int imx_mu_generic_tx(struct imx_mu_priv *priv, u32 val; int ret, count; + ret = 0; switch (cp->type) { case IMX_MU_TYPE_TX: imx_mu_write(priv, *arg, priv->dcfg->xTR + cp->idx * 4); @@ -259,7 +260,7 @@ static int imx_mu_generic_tx(struct imx_mu_priv *priv, return -EINVAL; } - return 0; + return ret; } static int imx_mu_generic_rx(struct imx_mu_priv *priv, From 8107796231041e08677c74eb4bb46d82688c0f3f Mon Sep 17 00:00:00 2001 From: Bhargav Joshi Date: Sat, 20 Jun 2026 17:39:17 +0530 Subject: [PATCH 1832/2758] irqchip/crossbar: Fix parent domain resource leak [ Upstream commit a1074dd62faa6572921d387e8a21589ccea00efc ] irq_domain_alloc_irqs_parent() is called in allocate_gic_irq() but irq_domain_free_irqs_parent() is never called which causes a resource leak. Fix this by calling irq_domain_free_irqs_parent() in crossbar_domain_free(). Fixes: 783d31863fb82 ("irqchip: crossbar: Convert dra7 crossbar to stacked domains") Signed-off-by: Bhargav Joshi Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260620-irq-crossbar-fix-v2-2-b8e8499f468a@gmail.com Signed-off-by: Sasha Levin --- drivers/irqchip/irq-crossbar.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index cd1134101ace0..3035f8ecfac85 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -163,6 +163,7 @@ static void crossbar_domain_free(struct irq_domain *domain, unsigned int virq, cb->write(d->hwirq, cb->safe_map); } raw_spin_unlock(&cb->lock); + irq_domain_free_irqs_parent(domain, virq, nr_irqs); } static int crossbar_domain_translate(struct irq_domain *d, From 008ceffd44040f809aead6d7bef7cb1210c4149a Mon Sep 17 00:00:00 2001 From: Hao Ge Date: Thu, 4 Jun 2026 14:59:38 +0800 Subject: [PATCH 1833/2758] alloc_tag: fix use-after-free in /proc/allocinfo after module unload [ Upstream commit 2956268efc457cb05d29c1bf94de1e8e684d7bbc ] allocinfo_start() only reinitializes the codetag iterator at position 0. For subsequent reads (position > 0), it reuses cached iterator state from the previous batch. allocinfo_stop() drops mod_lock between read batches, which allows module unload to complete and free the module memory that the cached iterator still references: CPU0 (read) CPU1 (rmmod) ---- ---- allocinfo_start(pos=0) down_read(mod_lock) allocinfo_show() ... allocinfo_stop() up_read(mod_lock) codetag_unload_module() kfree(cmod) release_module_tags() ... free_mod_mem() allocinfo_start(pos=N) down_read(mod_lock) // reuses cached iter, skips re-init allocinfo_show() ct->filename <-- UAF After free_mod_mem() frees the module's .rodata, allocinfo_show() dereferences ct->filename, ct->function which point there. Save the iterator state in allocinfo_next() and resume from it in allocinfo_start() with codetag_next_ct(), which detects module removal via idr_find() returning NULL and skips to the next module. Link: https://lore.kernel.org/20260604065938.105991-1-hao.ge@linux.dev Fixes: 9f44df50fee4 ("alloc_tag: keep codetag iterator active between read()") Signed-off-by: Hao Ge Suggested-by: Suren Baghdasaryan Acked-by: Suren Baghdasaryan Cc: Kent Overstreet Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- lib/alloc_tag.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/alloc_tag.c b/lib/alloc_tag.c index ed1bdcf1f8abf..a9ab88f416b9f 100644 --- a/lib/alloc_tag.c +++ b/lib/alloc_tag.c @@ -45,6 +45,7 @@ int alloc_tag_ref_offs; struct allocinfo_private { struct codetag_iterator iter; + struct codetag_iterator reported_iter; bool print_header; }; @@ -58,16 +59,20 @@ static void *allocinfo_start(struct seq_file *m, loff_t *pos) if (node == 0) { priv->print_header = true; priv->iter = codetag_get_ct_iter(alloc_tag_cttype); - codetag_next_ct(&priv->iter); + } else { + priv->iter = priv->reported_iter; } + codetag_next_ct(&priv->iter); return priv->iter.ct ? priv : NULL; } static void *allocinfo_next(struct seq_file *m, void *arg, loff_t *pos) { struct allocinfo_private *priv = (struct allocinfo_private *)arg; - struct codetag *ct = codetag_next_ct(&priv->iter); + struct codetag *ct; + priv->reported_iter = priv->iter; + ct = codetag_next_ct(&priv->iter); (*pos)++; if (!ct) return NULL; From 83d9d5f63cc974d2e7449a3974df7f809cb6260b Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:03:27 +0530 Subject: [PATCH 1834/2758] selftests/mm: restore default nr_hugepages value via exit trap in charge_reserved_hugetlb.sh [ Upstream commit a3f66e9b6e4dad38444656ffc28c28c33a4d4e1f ] Patch series "selftests/mm: fix failures and robustness improvements", v7. Powerpc systems with a 64K base page size exposed several issues while running mm selftests. Some tests assume specific hugetlb configurations, use incorrect interfaces, or fail instead of skipping when the required kernel features are not available. This series fixes these issues and improves test robustness. This patch (of 13): cleanup() resets nr_hugepages to 0 on every invocation, while the test reconfigures it again in the next iteration. This leads to repeated allocation and freeing of large numbers of hugepages, especially when the original value is high. Additionally, with set -e, failures in earlier cleanup steps (e.g., rmdir or umount returning EBUSY while background activity is still ongoing) can cause the script to exit before restoring the original value, leaving the system in a modified state. Introduce a trap on EXIT, INT, and TERM to restore the original nr_hugepages value once at script termination. This avoids unnecessary allocation churn and ensures the original value is reliably restored on all exit paths. Link: https://lore.kernel.org/cover.1779296493.git.sayalip@linux.ibm.com Link: https://lore.kernel.org/5b8fbb29cd6ceffe6752e0af104f60cec072aa10.1779296493.git.sayalip@linux.ibm.com Fixes: 7d695b1c3695 ("selftests/mm: save and restore nr_hugepages value") Signed-off-by: Sayali Patil Acked-by: Zi Yan Tested-by: Venkat Rao Bagalkote Cc: David Hildenbrand Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- tools/testing/selftests/mm/charge_reserved_hugetlb.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/testing/selftests/mm/charge_reserved_hugetlb.sh b/tools/testing/selftests/mm/charge_reserved_hugetlb.sh index 44f4e703deb9b..e1945901fd207 100755 --- a/tools/testing/selftests/mm/charge_reserved_hugetlb.sh +++ b/tools/testing/selftests/mm/charge_reserved_hugetlb.sh @@ -17,6 +17,7 @@ if ! command -v killall >/dev/null 2>&1; then fi nr_hugepgs=$(cat /proc/sys/vm/nr_hugepages) +trap 'echo "$nr_hugepgs" > /proc/sys/vm/nr_hugepages' EXIT INT TERM fault_limit_file=limit_in_bytes reservation_limit_file=rsvd.limit_in_bytes @@ -70,7 +71,6 @@ function cleanup() { if [[ -e $cgroup_path/hugetlb_cgroup_test2 ]]; then rmdir $cgroup_path/hugetlb_cgroup_test2 fi - echo 0 >/proc/sys/vm/nr_hugepages echo CLEANUP DONE } @@ -599,4 +599,3 @@ if [[ $do_umount ]]; then rmdir $cgroup_path fi -echo "$nr_hugepgs" > /proc/sys/vm/nr_hugepages From 65a7bc39d4a209c39495b91b7ffb2357c77495fa Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:03:28 +0530 Subject: [PATCH 1835/2758] selftests/mm: fix hugetlb pathname construction in charge_reserved_hugetlb.sh [ Upstream commit 404c04e010da2edc93cada07d4d50deda12a68e4 ] The charge_reserved_hugetlb.sh script assumes hugetlb cgroup memory interface file names use the "MB" format (e.g. hugetlb.1024MB.current). This assumption breaks on systems with larger huge pages such as 1GB, where the kernel exposes normalized units: hugetlb.1GB.current hugetlb.1GB.max hugetlb.1GB.rsvd.max ... As a result, the script attempts to access files like hugetlb.1024MB.current, which do not exist when the kernel reports the size in GB. Normalize the huge page size and construct the pathname using the appropriate unit (MB or GB), matching the hugetlb controller naming. Link: https://lore.kernel.org/04b6b49e4a2acf46319f627caf82b09e6dc1ad7f.1779296493.git.sayalip@linux.ibm.com Fixes: 209376ed2a84 ("selftests/vm: make charge_reserved_hugetlb.sh work with existing cgroup setting") Fixes: 29750f71a9b4 ("hugetlb_cgroup: add hugetlb_cgroup reservation tests") Signed-off-by: Sayali Patil Reviewed-by: Zi Yan Acked-by: David Hildenbrand (Arm) Tested-by: Venkat Rao Bagalkote Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- .../selftests/mm/charge_reserved_hugetlb.sh | 42 +++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/tools/testing/selftests/mm/charge_reserved_hugetlb.sh b/tools/testing/selftests/mm/charge_reserved_hugetlb.sh index e1945901fd207..a1cfd3a349db6 100755 --- a/tools/testing/selftests/mm/charge_reserved_hugetlb.sh +++ b/tools/testing/selftests/mm/charge_reserved_hugetlb.sh @@ -94,6 +94,15 @@ function get_machine_hugepage_size() { } MB=$(get_machine_hugepage_size) +if (( MB >= 1024 )); then + # For 1GB hugepages + UNIT="GB" + MB_DISPLAY=$((MB / 1024)) +else + # For 2MB hugepages + UNIT="MB" + MB_DISPLAY=$MB +fi function setup_cgroup() { local name="$1" @@ -103,11 +112,12 @@ function setup_cgroup() { mkdir $cgroup_path/$name echo writing cgroup limit: "$cgroup_limit" - echo "$cgroup_limit" >$cgroup_path/$name/hugetlb.${MB}MB.$fault_limit_file + echo "$cgroup_limit" > \ + $cgroup_path/$name/hugetlb.${MB_DISPLAY}${UNIT}.$fault_limit_file echo writing reservation limit: "$reservation_limit" echo "$reservation_limit" > \ - $cgroup_path/$name/hugetlb.${MB}MB.$reservation_limit_file + $cgroup_path/$name/hugetlb.${MB_DISPLAY}${UNIT}.$reservation_limit_file if [ -e "$cgroup_path/$name/cpuset.cpus" ]; then echo 0 >$cgroup_path/$name/cpuset.cpus @@ -142,7 +152,7 @@ function wait_for_file_value() { function wait_for_hugetlb_memory_to_get_depleted() { local cgroup="$1" - local path="$cgroup_path/$cgroup/hugetlb.${MB}MB.$reservation_usage_file" + local path="$cgroup_path/$cgroup/hugetlb.${MB_DISPLAY}${UNIT}.$reservation_usage_file" wait_for_file_value "$path" "0" } @@ -150,7 +160,7 @@ function wait_for_hugetlb_memory_to_get_depleted() { function wait_for_hugetlb_memory_to_get_reserved() { local cgroup="$1" local size="$2" - local path="$cgroup_path/$cgroup/hugetlb.${MB}MB.$reservation_usage_file" + local path="$cgroup_path/$cgroup/hugetlb.${MB_DISPLAY}${UNIT}.$reservation_usage_file" wait_for_file_value "$path" "$size" } @@ -158,7 +168,7 @@ function wait_for_hugetlb_memory_to_get_reserved() { function wait_for_hugetlb_memory_to_get_written() { local cgroup="$1" local size="$2" - local path="$cgroup_path/$cgroup/hugetlb.${MB}MB.$fault_usage_file" + local path="$cgroup_path/$cgroup/hugetlb.${MB_DISPLAY}${UNIT}.$fault_usage_file" wait_for_file_value "$path" "$size" } @@ -180,8 +190,8 @@ function write_hugetlbfs_and_get_usage() { hugetlb_difference=0 reserved_difference=0 - local hugetlb_usage=$cgroup_path/$cgroup/hugetlb.${MB}MB.$fault_usage_file - local reserved_usage=$cgroup_path/$cgroup/hugetlb.${MB}MB.$reservation_usage_file + local hugetlb_usage=$cgroup_path/$cgroup/hugetlb.${MB_DISPLAY}${UNIT}.$fault_usage_file + local reserved_usage=$cgroup_path/$cgroup/hugetlb.${MB_DISPLAY}${UNIT}.$reservation_usage_file local hugetlb_before=$(cat $hugetlb_usage) local reserved_before=$(cat $reserved_usage) @@ -312,8 +322,10 @@ function run_test() { cleanup_hugetlb_memory "hugetlb_cgroup_test" - local final_hugetlb=$(cat $cgroup_path/hugetlb_cgroup_test/hugetlb.${MB}MB.$fault_usage_file) - local final_reservation=$(cat $cgroup_path/hugetlb_cgroup_test/hugetlb.${MB}MB.$reservation_usage_file) + local final_hugetlb=$(cat \ + $cgroup_path/hugetlb_cgroup_test/hugetlb.${MB_DISPLAY}${UNIT}.$fault_usage_file) + local final_reservation=$(cat \ + $cgroup_path/hugetlb_cgroup_test/hugetlb.${MB_DISPLAY}${UNIT}.$reservation_usage_file) echo $hugetlb_difference echo $reserved_difference @@ -369,10 +381,14 @@ function run_multiple_cgroup_test() { reservation_failed1=$reservation_failed oom_killed1=$oom_killed - local cgroup1_hugetlb_usage=$cgroup_path/hugetlb_cgroup_test1/hugetlb.${MB}MB.$fault_usage_file - local cgroup1_reservation_usage=$cgroup_path/hugetlb_cgroup_test1/hugetlb.${MB}MB.$reservation_usage_file - local cgroup2_hugetlb_usage=$cgroup_path/hugetlb_cgroup_test2/hugetlb.${MB}MB.$fault_usage_file - local cgroup2_reservation_usage=$cgroup_path/hugetlb_cgroup_test2/hugetlb.${MB}MB.$reservation_usage_file + local cgroup1_hugetlb_usage=\ + $cgroup_path/hugetlb_cgroup_test1/hugetlb.${MB_DISPLAY}${UNIT}.$fault_usage_file + local cgroup1_reservation_usage=\ + $cgroup_path/hugetlb_cgroup_test1/hugetlb.${MB_DISPLAY}${UNIT}.$reservation_usage_file + local cgroup2_hugetlb_usage=\ + $cgroup_path/hugetlb_cgroup_test2/hugetlb.${MB_DISPLAY}${UNIT}.$fault_usage_file + local cgroup2_reservation_usage=\ + $cgroup_path/hugetlb_cgroup_test2/hugetlb.${MB_DISPLAY}${UNIT}.$reservation_usage_file local usage_before_second_write=$(cat $cgroup1_hugetlb_usage) local reservation_usage_before_second_write=$(cat $cgroup1_reservation_usage) From 95f64f30431edfeb22ceeba4c33e27784f28861d Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:43 +0530 Subject: [PATCH 1836/2758] selftests/mm: restore default nr_hugepages value via exit trap in hugetlb_reparenting_test.sh [ Upstream commit 7f9c0920ff3debae3cb4d60260e3daf56ab68395 ] The test modifies nr_hugepages during execution and restores it from cleanup() and again reconfigure it setup, which is invoked multiple times across test flow. This can lead to repeated allocation/freeing of hugepages. With set -e, failures in cleanup (e.g., rmdir/umount) can also cause early exit before restoring the original value at the end. Move restoration of the original nr_hugepages value to a trap handler registered for EXIT, INT, and TERM signals so it is always restored on all exit paths. This also avoids unnecessary allocation churn across repeated cleanup/setup cycles. Link: https://lore.kernel.org/29db637c3c6ba6c168f6b33f59f059a0b39c35c8.1779296493.git.sayalip@linux.ibm.com Fixes: 585a9145886a ("selftests/mm: restore default nr_hugepages value during cleanup in hugetlb_reparenting_test.sh") Signed-off-by: Sayali Patil Acked-by: Zi Yan Tested-by: Venkat Rao Bagalkote Cc: David Hildenbrand (Arm) Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- tools/testing/selftests/mm/hugetlb_reparenting_test.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/mm/hugetlb_reparenting_test.sh b/tools/testing/selftests/mm/hugetlb_reparenting_test.sh index 0dd31892ff679..11f914831146f 100755 --- a/tools/testing/selftests/mm/hugetlb_reparenting_test.sh +++ b/tools/testing/selftests/mm/hugetlb_reparenting_test.sh @@ -12,6 +12,8 @@ if [[ $(id -u) -ne 0 ]]; then fi nr_hugepgs=$(cat /proc/sys/vm/nr_hugepages) +trap 'echo "$nr_hugepgs" > /proc/sys/vm/nr_hugepages' EXIT INT TERM + usage_file=usage_in_bytes if [[ "$1" == "-cgroup-v2" ]]; then @@ -56,7 +58,6 @@ function cleanup() { rmdir "$CGROUP_ROOT"/a/b 2>/dev/null rmdir "$CGROUP_ROOT"/a 2>/dev/null rmdir "$CGROUP_ROOT"/test1 2>/dev/null - echo $nr_hugepgs >/proc/sys/vm/nr_hugepages set -e } @@ -240,4 +241,3 @@ if [[ $do_umount ]]; then rm -rf $CGROUP_ROOT fi -echo "$nr_hugepgs" > /proc/sys/vm/nr_hugepages From 4a1e9beaff98bbb826f8891aee10c9081709c187 Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:44 +0530 Subject: [PATCH 1837/2758] selftests/mm: fix hugetlb pathname construction in hugetlb_reparenting_test.sh [ Upstream commit 76f9a212a0a93572a28b58d9869e9187c4022342 ] The hugetlb_reparenting_test.sh script constructs hugetlb cgroup memory interface file names based on the configured huge page size. The script formats the size only in MB units, which causes mismatches on systems using larger huge pages where the kernel exposes normalized units (e.g. "1GB" instead of "1024MB"). As a result, the test fails to locate the corresponding cgroup files when 1GB huge pages are configured. Update the script to detect the huge page size and select the appropriate unit (MB or GB) so that the constructed paths match the kernel's hugetlb controller naming. Also print an explicit "Fail" message when a test failure occurs to improve result visibility. Link: https://lore.kernel.org/837ce751965c93f74c95d89587debf1e93281364.1779296493.git.sayalip@linux.ibm.com Fixes: e487a5d513cb ("selftest/mm: make hugetlb_reparenting_test tolerant to async reparenting") Signed-off-by: Sayali Patil Reviewed-by: Zi Yan Acked-by: David Hildenbrand (Arm) Tested-by: Venkat Rao Bagalkote Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- .../selftests/mm/hugetlb_reparenting_test.sh | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/mm/hugetlb_reparenting_test.sh b/tools/testing/selftests/mm/hugetlb_reparenting_test.sh index 11f914831146f..d724b6e45432f 100755 --- a/tools/testing/selftests/mm/hugetlb_reparenting_test.sh +++ b/tools/testing/selftests/mm/hugetlb_reparenting_test.sh @@ -48,6 +48,13 @@ function get_machine_hugepage_size() { } MB=$(get_machine_hugepage_size) +if (( MB >= 1024 )); then + UNIT="GB" + MB_DISPLAY=$((MB / 1024)) +else + UNIT="MB" + MB_DISPLAY=$MB +fi function cleanup() { echo cleanup @@ -88,6 +95,7 @@ function assert_with_retry() { if [[ $elapsed -ge $timeout ]]; then echo "actual = $((${actual%% *} / 1024 / 1024)) MB" echo "expected = $((${expected%% *} / 1024 / 1024)) MB" + echo FAIL cleanup exit 1 fi @@ -108,11 +116,13 @@ function assert_state() { fi assert_with_retry "$CGROUP_ROOT/a/memory.$usage_file" "$expected_a" - assert_with_retry "$CGROUP_ROOT/a/hugetlb.${MB}MB.$usage_file" "$expected_a_hugetlb" + assert_with_retry \ + "$CGROUP_ROOT/a/hugetlb.${MB_DISPLAY}${UNIT}.$usage_file" "$expected_a_hugetlb" if [[ -n "$expected_b" && -n "$expected_b_hugetlb" ]]; then assert_with_retry "$CGROUP_ROOT/a/b/memory.$usage_file" "$expected_b" - assert_with_retry "$CGROUP_ROOT/a/b/hugetlb.${MB}MB.$usage_file" "$expected_b_hugetlb" + assert_with_retry \ + "$CGROUP_ROOT/a/b/hugetlb.${MB_DISPLAY}${UNIT}.$usage_file" "$expected_b_hugetlb" fi } From aef0f2059a97174be1cb50801331fa12580420af Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:45 +0530 Subject: [PATCH 1838/2758] selftests/mm: fix cgroup task placement and drop memory.current checks in hugetlb_reparenting_test.sh [ Upstream commit 7bbdc459527075c4d0b96133de25a53822397af0 ] The test currently moves the calling shell ($$) into the target cgroup before executing write_to_hugetlbfs. This results in the shell and any intermediate allocations being charged to the cgroup, introducing noise and nondeterminism in accounting. It also requires moving the shell back to the root cgroup after execution. Spawn a helper process that joins the target cgroup and exec()'s write_to_hugetlbfs. This ensures that only the workload is accounted to the cgroup and avoids unintended charging from the shell. The test currently validates both hugetlb usage and memory.current. However, memory.current includes internal memcg allocations and per-CPU batched accounting (MEMCG_CHARGE_BATCH), which are not synchronized and can vary across systems, leading to non-deterministic results. Since hugetlb memory is accounted via hugetlb..current, memory.current is not a reliable indicator here. Drop memory.current checks and rely only on hugetlb controller statistics for stable and accurate validation. Link: https://lore.kernel.org/fb57491ba83cb0a499c72922e1579b61bee514db.1779296493.git.sayalip@linux.ibm.com Fixes: 29750f71a9b4 ("hugetlb_cgroup: add hugetlb_cgroup reservation tests") Signed-off-by: Sayali Patil Cc: David Hildenbrand (Arm) Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Cc: Venkat Rao Bagalkote Cc: Zi Yan Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- .../selftests/mm/hugetlb_reparenting_test.sh | 42 ++++++++----------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/tools/testing/selftests/mm/hugetlb_reparenting_test.sh b/tools/testing/selftests/mm/hugetlb_reparenting_test.sh index d724b6e45432f..95f517c3bd167 100755 --- a/tools/testing/selftests/mm/hugetlb_reparenting_test.sh +++ b/tools/testing/selftests/mm/hugetlb_reparenting_test.sh @@ -105,22 +105,17 @@ function assert_with_retry() { } function assert_state() { - local expected_a="$1" - local expected_a_hugetlb="$2" - local expected_b="" + local expected_a_hugetlb="$1" local expected_b_hugetlb="" - if [ ! -z ${3:-} ] && [ ! -z ${4:-} ]; then - expected_b="$3" - expected_b_hugetlb="$4" + if [ ! -z ${2:-} ]; then + expected_b_hugetlb="$2" fi - assert_with_retry "$CGROUP_ROOT/a/memory.$usage_file" "$expected_a" assert_with_retry \ "$CGROUP_ROOT/a/hugetlb.${MB_DISPLAY}${UNIT}.$usage_file" "$expected_a_hugetlb" - if [[ -n "$expected_b" && -n "$expected_b_hugetlb" ]]; then - assert_with_retry "$CGROUP_ROOT/a/b/memory.$usage_file" "$expected_b" + if [[ -n "$expected_b_hugetlb" ]]; then assert_with_retry \ "$CGROUP_ROOT/a/b/hugetlb.${MB_DISPLAY}${UNIT}.$usage_file" "$expected_b_hugetlb" fi @@ -154,18 +149,17 @@ write_hugetlbfs() { local size="$3" if [[ $cgroup2 ]]; then - echo $$ >$CGROUP_ROOT/$cgroup/cgroup.procs + cg_file="$CGROUP_ROOT/$cgroup/cgroup.procs" else echo 0 >$CGROUP_ROOT/$cgroup/cpuset.mems echo 0 >$CGROUP_ROOT/$cgroup/cpuset.cpus - echo $$ >"$CGROUP_ROOT/$cgroup/tasks" - fi - ./write_to_hugetlbfs -p "$path" -s "$size" -m 0 -o - if [[ $cgroup2 ]]; then - echo $$ >$CGROUP_ROOT/cgroup.procs - else - echo $$ >"$CGROUP_ROOT/tasks" + cg_file="$CGROUP_ROOT/$cgroup/tasks" fi + + # Spawn helper to join cgroup before exec to ensure correct cgroup accounting + bash -c 'echo $$ > "$1"; exec ./write_to_hugetlbfs -p "$2" -s "$3" -m 0 -o' _ \ + "$cg_file" "$path" "$size" & pid=$! + wait "$pid" echo } @@ -203,21 +197,21 @@ if [[ ! $cgroup2 ]]; then write_hugetlbfs a "$MNT"/test $size echo Assert memory charged correctly for parent use. - assert_state 0 $size 0 0 + assert_state $size 0 write_hugetlbfs a/b "$MNT"/test2 $size echo Assert memory charged correctly for child use. - assert_state 0 $(($size * 2)) 0 $size + assert_state $(($size * 2)) $size rmdir "$CGROUP_ROOT"/a/b echo Assert memory reparent correctly. - assert_state 0 $(($size * 2)) + assert_state $(($size * 2)) rm -rf "$MNT"/* umount "$MNT" echo Assert memory uncharged correctly. - assert_state 0 0 + assert_state 0 cleanup fi @@ -231,16 +225,16 @@ echo write write_hugetlbfs a/b "$MNT"/test2 $size echo Assert memory charged correctly for child only use. -assert_state 0 $(($size)) 0 $size +assert_state $(($size)) $size rmdir "$CGROUP_ROOT"/a/b echo Assert memory reparent correctly. -assert_state 0 $size +assert_state $size rm -rf "$MNT"/* umount "$MNT" echo Assert memory uncharged correctly. -assert_state 0 0 +assert_state 0 cleanup From 5b136718617aab1b494dc199edf84305230100ed Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:46 +0530 Subject: [PATCH 1839/2758] selftests/mm: size tmpfs according to PMD page size in split_huge_page_test [ Upstream commit a2dde8a065d5c8d63b53b1c8f035017e4aeac0c2 ] The split_file_backed_thp() test mounts a tmpfs with a fixed size of "4m". This works on systems with smaller PMD page sizes, but fails on configurations where the PMD huge page size is larger (e.g. 16MB). On such systems, the fixed 4MB tmpfs is insufficient to allocate even a single PMD-sized THP, causing the test to fail. Fix this by sizing the tmpfs dynamically based on the runtime pmd_pagesize, allocating space for two PMD-sized pages. Before patch: running ./split_huge_page_test /tmp/xfs_dir_YTrI5E -------------------------------------------------- TAP version 13 1..55 ok 1 Split zero filled huge pages successful ok 2 Split huge pages to order 0 successful ok 3 Split huge pages to order 2 successful ok 4 Split huge pages to order 3 successful ok 5 Split huge pages to order 4 successful ok 6 Split huge pages to order 5 successful ok 7 Split huge pages to order 6 successful ok 8 Split huge pages to order 7 successful ok 9 Split PTE-mapped huge pages successful Please enable pr_debug in split_huge_pages_in_file() for more info. Failed to write data to testing file: Success (0) Bail out! Error occurred Planned tests != run tests (55 != 9) Totals: pass:9 fail:0 xfail:0 xpass:0 skip:0 error:0 [FAIL] After patch: running ./split_huge_page_test /tmp/xfs_dir_bMvj6o -------------------------------------------------- TAP version 13 1..55 ok 1 Split zero filled huge pages successful ok 2 Split huge pages to order 0 successful ok 3 Split huge pages to order 2 successful ok 4 Split huge pages to order 3 successful ok 5 Split huge pages to order 4 successful ok 6 Split huge pages to order 5 successful ok 7 Split huge pages to order 6 successful ok 8 Split huge pages to order 7 successful ok 9 Split PTE-mapped huge pages successful Please enable pr_debug in split_huge_pages_in_file() for more info. Please check dmesg for more information ok 10 File-backed THP split to order 0 test done Please enable pr_debug in split_huge_pages_in_file() for more info. Please check dmesg for more information ok 11 File-backed THP split to order 1 test done Please enable pr_debug in split_huge_pages_in_file() for more info. Please check dmesg for more information ok 12 File-backed THP split to order 2 test done ... ok 55 Split PMD-mapped pagecache folio to order 7 at in-folio offset 128 passed Totals: pass:55 fail:0 xfail:0 xpass:0 skip:0 error:0 [PASS] ok 1 split_huge_page_test /tmp/xfs_dir_bMvj6o Link: https://lore.kernel.org/33e1bc10753fe82d1217613d8cd496020778cf2b.1779296493.git.sayalip@linux.ibm.com Fixes: fbe37501b252 ("mm: huge_memory: debugfs for file-backed THP split") Signed-off-by: Sayali Patil Reviewed-by: Zi Yan Reviewed-by: David Hildenbrand (Arm) Tested-by: Venkat Rao Bagalkote Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- tools/testing/selftests/mm/split_huge_page_test.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c index 500d07c4938b1..02938f38b880c 100644 --- a/tools/testing/selftests/mm/split_huge_page_test.c +++ b/tools/testing/selftests/mm/split_huge_page_test.c @@ -470,6 +470,8 @@ static void split_file_backed_thp(int order) char tmpfs_template[] = "/tmp/thp_split_XXXXXX"; const char *tmpfs_loc = mkdtemp(tmpfs_template); char testfile[INPUT_MAX]; + unsigned long size = 2 * pmd_pagesize; + char opts[64]; ssize_t num_written, num_read; char *file_buf1, *file_buf2; uint64_t pgoff_start = 0, pgoff_end = 1024; @@ -489,7 +491,8 @@ static void split_file_backed_thp(int order) file_buf1[i] = (char)i; memset(file_buf2, 0, pmd_pagesize); - status = mount("tmpfs", tmpfs_loc, "tmpfs", 0, "huge=always,size=4m"); + snprintf(opts, sizeof(opts), "huge=always,size=%lu", size); + status = mount("tmpfs", tmpfs_loc, "tmpfs", 0, opts); if (status) ksft_exit_fail_msg("Unable to create a tmpfs for testing\n"); From 7c0ba2376d40fa729b915eb628847e61bfa9e3b3 Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:47 +0530 Subject: [PATCH 1840/2758] selftests/mm: free dynamically allocated PMD-sized buffers in split_huge_page_test [ Upstream commit e5d3e1422e92d54e28e81fad8bfc7c1ecb41a353 ] Dynamically allocated buffers of PMD size for file-backed THP operations (file_buf1 and file_buf2) were not freed on the success path and some failure paths. Since the function is called repeatedly in a loop for each split order, this can cause significant memory leaks. On architectures with large PMD sizes, repeated leaks could exhaust system memory and trigger the OOM killer during test execution. Ensure all allocated buffers are freed to maintain stable repeated test runs. Link: https://lore.kernel.org/060c673b376bbeeed2b1fb1d48a825e846654191.1779296493.git.sayalip@linux.ibm.com Fixes: 035a112e5fd5 ("selftests/mm: make file-backed THP split work by writing PMD size data") Signed-off-by: Sayali Patil Reviewed-by: Zi Yan Tested-by: Venkat Rao Bagalkote Cc: David Hildenbrand (Arm) Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- .../selftests/mm/split_huge_page_test.c | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c index 02938f38b880c..ee256afecd8fe 100644 --- a/tools/testing/selftests/mm/split_huge_page_test.c +++ b/tools/testing/selftests/mm/split_huge_page_test.c @@ -473,12 +473,15 @@ static void split_file_backed_thp(int order) unsigned long size = 2 * pmd_pagesize; char opts[64]; ssize_t num_written, num_read; - char *file_buf1, *file_buf2; + char *file_buf1 = NULL, *file_buf2 = NULL; uint64_t pgoff_start = 0, pgoff_end = 1024; int i; ksft_print_msg("Please enable pr_debug in split_huge_pages_in_file() for more info.\n"); + if (!tmpfs_loc) + ksft_exit_fail_msg("mkdtemp failed\n"); + file_buf1 = (char *)malloc(pmd_pagesize); file_buf2 = (char *)malloc(pmd_pagesize); @@ -494,8 +497,10 @@ static void split_file_backed_thp(int order) snprintf(opts, sizeof(opts), "huge=always,size=%lu", size); status = mount("tmpfs", tmpfs_loc, "tmpfs", 0, opts); - if (status) - ksft_exit_fail_msg("Unable to create a tmpfs for testing\n"); + if (status) { + ksft_print_msg("Unable to create a tmpfs for testing\n"); + goto out; + } status = snprintf(testfile, INPUT_MAX, "%s/thp_file", tmpfs_loc); if (status >= INPUT_MAX) { @@ -547,10 +552,13 @@ static void split_file_backed_thp(int order) status = umount(tmpfs_loc); if (status) { - rmdir(tmpfs_loc); - ksft_exit_fail_msg("Unable to umount %s\n", tmpfs_loc); + ksft_print_msg("Unable to umount %s\n", tmpfs_loc); + goto out; } + free(file_buf1); + free(file_buf2); + status = rmdir(tmpfs_loc); if (status) ksft_exit_fail_msg("cannot remove tmp dir: %s\n", strerror(errno)); @@ -563,8 +571,10 @@ static void split_file_backed_thp(int order) close(fd); cleanup: umount(tmpfs_loc); - rmdir(tmpfs_loc); out: + free(file_buf1); + free(file_buf2); + rmdir(tmpfs_loc); ksft_exit_fail_msg("Error occurred\n"); } From e0f39f7671a9fd43ae2a9c4ea9cbd7e229ccf2c3 Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:48 +0530 Subject: [PATCH 1841/2758] selftest/mm: register existing mapping with userfaultfd in hugetlb-mremap [ Upstream commit 1df31d5ef58dc6bdf0f2a8b809152d835c60b28e ] Previously, register_region_with_uffd() created a new anonymous mapping and overwrote the address supplied by the caller before registering the range with userfaultfd. As a result, userfaultfd was applied to an unrelated anonymous mapping instead of the hugetlb region used by the test. Remove the extra mmap() and register the caller-provided address range directly using UFFDIO_REGISTER_MODE_MISSING, so that faults are generated for the hugetlb mapping used by the test. This ensures userfaultfd operates on the actual hugetlb test region and validates the expected fault handling. Before patch: running ./hugetlb-mremap ------------------------- TAP version 13 1..1 Map haddr: Returned address is 0x7eaa40000000 Map daddr: Returned address is 0x7daa40000000 Map vaddr: Returned address is 0x7faa40000000 Address returned by mmap() = 0x7fff9d000000 Mremap: Returned address is 0x7faa40000000 First hex is 0 First hex is 3020100 ok 1 Read same data Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 [PASS] ok 1 hugetlb-mremap After patch: running ./hugetb-mremap ------------------------- TAP version 13 1..1 Map haddr: Returned address is 0x7eaa40000000 Map daddr: Returned address is 0x7daa40000000 Map vaddr: Returned address is 0x7faa40000000 Registered memory at address 0x7eaa40000000 with userfaultfd Mremap: Returned address is 0x7faa40000000 First hex is 0 First hex is 3020100 ok 1 Read same data Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 [PASS] ok 1 hugetlb-mremap Link: https://lore.kernel.org/13845da872ed174316173e8996dbb5f181994017.1779296493.git.sayalip@linux.ibm.com Fixes: 12b613206474 ("mm, hugepages: add hugetlb vma mremap() test") Signed-off-by: Sayali Patil Acked-by: David Hildenbrand (Arm) Tested-by: Venkat Rao Bagalkote Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Cc: Zi Yan Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- tools/testing/selftests/mm/hugepage-mremap.c | 21 +++++--------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/tools/testing/selftests/mm/hugepage-mremap.c b/tools/testing/selftests/mm/hugepage-mremap.c index b8f7d92e5a351..e611249080d6d 100644 --- a/tools/testing/selftests/mm/hugepage-mremap.c +++ b/tools/testing/selftests/mm/hugepage-mremap.c @@ -85,25 +85,14 @@ static void register_region_with_uffd(char *addr, size_t len) if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1) ksft_exit_fail_msg("ioctl-UFFDIO_API: %s\n", strerror(errno)); - /* Create a private anonymous mapping. The memory will be - * demand-zero paged--that is, not yet allocated. When we - * actually touch the memory, it will be allocated via - * the userfaultfd. - */ - - addr = mmap(NULL, len, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if (addr == MAP_FAILED) - ksft_exit_fail_msg("mmap: %s\n", strerror(errno)); - - ksft_print_msg("Address returned by mmap() = %p\n", addr); - - /* Register the memory range of the mapping we just created for - * handling by the userfaultfd object. In mode, we request to track - * missing pages (i.e., pages that have not yet been faulted in). + /* Register the passed memory range for handling by the userfaultfd object. + * In mode, we request to track missing pages + * (i.e., pages that have not yet been faulted in). */ if (uffd_register(uffd, addr, len, true, false, false)) ksft_exit_fail_msg("ioctl-UFFDIO_REGISTER: %s\n", strerror(errno)); + + ksft_print_msg("Registered memory at address %p with userfaultfd\n", addr); } int main(int argc, char *argv[]) From e186a9ac7af40419f1dad33c0c0c80de790f321e Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:49 +0530 Subject: [PATCH 1842/2758] selftests/mm: ensure destination is hugetlb-backed in hugetlb-mremap [ Upstream commit f20ca8972e83f111c4300c60d74314568c8964bc ] The hugetlb-mremap selftest reserves the destination address using a anonymous base-page mapping before calling mremap() with MREMAP_FIXED, while the source region is hugetlb-backed. When remapping a hugetlb mapping into a base-page VMA may fail with: mremap: Device or resource busy This is observed on powerpc hash MMU systems where slice constraints and page size incompatibilities prevent the remap. Ensure the destination region is created using MAP_HUGETLB so that both source and destination VMAs are hugetlb-backed and compatible. Update the FLAGS macro to include MAP_HUGETLB | MAP_SHARED so that both mappings are hugetlb-backed and compatible. Also use the macro for the mmap() calls to avoid repeating the flag combination. This ensures the test reliably exercises hugetlb mremap instead of failing due to VMA type mismatch. Link: https://lore.kernel.org/367644df45c65098f23e3945c6a80f4b8a8964a6.1779296493.git.sayalip@linux.ibm.com Fixes: 12b613206474 ("mm, hugepages: add hugetlb vma mremap() test") Signed-off-by: Sayali Patil Tested-by: Venkat Rao Bagalkote Cc: David Hildenbrand (Arm) Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Cc: Zi Yan Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- tools/testing/selftests/mm/hugepage-mremap.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tools/testing/selftests/mm/hugepage-mremap.c b/tools/testing/selftests/mm/hugepage-mremap.c index e611249080d6d..d8be8329868f2 100644 --- a/tools/testing/selftests/mm/hugepage-mremap.c +++ b/tools/testing/selftests/mm/hugepage-mremap.c @@ -31,7 +31,7 @@ #define MB_TO_BYTES(x) (x * 1024 * 1024) #define PROTECTION (PROT_READ | PROT_WRITE | PROT_EXEC) -#define FLAGS (MAP_SHARED | MAP_ANONYMOUS) +#define FLAGS (MAP_HUGETLB | MAP_SHARED) static void check_bytes(char *addr) { @@ -121,23 +121,20 @@ int main(int argc, char *argv[]) /* mmap to a PUD aligned address to hopefully trigger pmd sharing. */ unsigned long suggested_addr = 0x7eaa40000000; - void *haddr = mmap((void *)suggested_addr, length, PROTECTION, - MAP_HUGETLB | MAP_SHARED | MAP_POPULATE, fd, 0); + void *haddr = mmap((void *)suggested_addr, length, PROTECTION, FLAGS, fd, 0); ksft_print_msg("Map haddr: Returned address is %p\n", haddr); if (haddr == MAP_FAILED) ksft_exit_fail_msg("mmap1: %s\n", strerror(errno)); /* mmap again to a dummy address to hopefully trigger pmd sharing. */ suggested_addr = 0x7daa40000000; - void *daddr = mmap((void *)suggested_addr, length, PROTECTION, - MAP_HUGETLB | MAP_SHARED | MAP_POPULATE, fd, 0); + void *daddr = mmap((void *)suggested_addr, length, PROTECTION, FLAGS, fd, 0); ksft_print_msg("Map daddr: Returned address is %p\n", daddr); if (daddr == MAP_FAILED) ksft_exit_fail_msg("mmap3: %s\n", strerror(errno)); suggested_addr = 0x7faa40000000; - void *vaddr = - mmap((void *)suggested_addr, length, PROTECTION, FLAGS, -1, 0); + void *vaddr = mmap((void *)suggested_addr, length, PROTECTION, FLAGS, fd, 0); ksft_print_msg("Map vaddr: Returned address is %p\n", vaddr); if (vaddr == MAP_FAILED) ksft_exit_fail_msg("mmap2: %s\n", strerror(errno)); From de72caed5077aabe63dcf88b7ea45cac2d804fbc Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:51 +0530 Subject: [PATCH 1843/2758] selftests/mm: skip uffd-stress test when nr_pages_per_cpu is zero [ Upstream commit f3bd00507f226a419125df6064632bcbd47d8e70 ] uffd-stress currently fails when the computed nr_pages_per_cpu evaluates to zero: nr_pages_per_cpu = bytes / page_size / nr_parallel This can occur on systems with large hugepage sizes (e.g. 1GB) and a high number of CPUs, where the total allocated memory is sufficient overall but not enough to provide at least one page per cpu. In such cases, the failure is due to insufficient test resources rather than incorrect kernel behaviour. Update the test to treat this condition as a test skip instead of reporting an error. [sayalip@linux.ibm.com: use ksft_exit_skip() instead of KSFT_SKIP] Link: https://lore.kernel.org/88202b56-1dc5-43e2-9d1f-a0823a9531f0@linux.ibm.com Link: https://lore.kernel.org/0707e9a0f1b3dd904c4a069b91db317f9c160faa.1779296493.git.sayalip@linux.ibm.com Fixes: db0f1c138f18 ("selftests/mm: print some details when uffd-stress gets bad params") Signed-off-by: Sayali Patil Acked-by: Zi Yan Acked-by: David Hildenbrand (Arm) Tested-by: Venkat Rao Bagalkote Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- tools/testing/selftests/mm/uffd-stress.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/mm/uffd-stress.c b/tools/testing/selftests/mm/uffd-stress.c index 700fbaa18d44b..dabaade1eaa0d 100644 --- a/tools/testing/selftests/mm/uffd-stress.c +++ b/tools/testing/selftests/mm/uffd-stress.c @@ -491,9 +491,8 @@ int main(int argc, char **argv) gopts->nr_pages_per_cpu = bytes / gopts->page_size / gopts->nr_parallel; if (!gopts->nr_pages_per_cpu) { - _err("pages_per_cpu = 0, cannot test (%lu / %lu / %lu)", - bytes, gopts->page_size, gopts->nr_parallel); - usage(); + ksft_exit_skip("pages_per_cpu = 0, cannot test (%zu / %lu / %lu)\n", + bytes, gopts->page_size, gopts->nr_parallel); } bounces = atoi(argv[3]); From 835ef922f3baba7500c3cf51790d9c75643b48b9 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:38 +0300 Subject: [PATCH 1844/2758] selftests/mm: run_vmtests.sh: free memory if available memory is low [ Upstream commit 157adc22f01cdd0c0dc1b9e4389ed2377d9b69d3 ] Currently when running THP and HugeTLB tests, if HAVE_HUGEPAGES is set run_test() drops caches, compacts memory and runs the test. But if HAVE_HUGEPAGES is not set it skips the tests entirely, even if THP tests have nothing to do with HAVE_HUGEPAGES. Replace the check if HAVE_HUGEPAGES is set with a check of how much memory is available. If there is less than 256 MB of available memory, drop caches and run compaction and then continue to run a test regardless of HAVE_HUGEPAGES value. Link: https://lore.kernel.org/20260511162840.375890-55-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton Stable-dep-of: 90c132f8379b ("selftests/mm: move hwpoison setup into run_test() and silence modprobe output for memory-failure category") Signed-off-by: Sasha Levin --- tools/testing/selftests/mm/run_vmtests.sh | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh index c17b133a81d24..913a32a47baf8 100755 --- a/tools/testing/selftests/mm/run_vmtests.sh +++ b/tools/testing/selftests/mm/run_vmtests.sh @@ -234,19 +234,17 @@ pretty_name() { # Usage: run_test [test binary] [arbitrary test arguments...] run_test() { if test_selected ${CATEGORY}; then - local skip=0 - # On memory constrainted systems some tests can fail to allocate hugepages. # perform some cleanup before the test for a higher success rate. if [ ${CATEGORY} == "thp" -o ${CATEGORY} == "hugetlb" ]; then - if [ "${HAVE_HUGEPAGES}" = "1" ]; then + mem_kb=$(awk '/MemAvailable/ {print $2}' /proc/meminfo) + mem_Mb=$((mem_kb / 1024)) + + if (( $mem_Mb < 256 )); then echo 3 > /proc/sys/vm/drop_caches sleep 2 echo 1 > /proc/sys/vm/compact_memory sleep 2 - else - echo "hugepages not supported" | tap_prefix - skip=1 fi fi @@ -255,12 +253,8 @@ run_test() { local sep=$(echo -n "$title" | tr "[:graph:][:space:]" -) printf "%s\n%s\n%s\n" "$sep" "$title" "$sep" | tap_prefix - if [ "${skip}" != "1" ]; then - ("$@" 2>&1) | tap_prefix - local ret=${PIPESTATUS[0]} - else - local ret=$ksft_skip - fi + ("$@" 2>&1) | tap_prefix + local ret=${PIPESTATUS[0]} count_total=$(( count_total + 1 )) if [ $ret -eq 0 ]; then count_pass=$(( count_pass + 1 )) From 2a018e29ac5ff55806cce76cdb5e2e76c021e537 Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:52 +0530 Subject: [PATCH 1845/2758] selftests/mm: move hwpoison setup into run_test() and silence modprobe output for memory-failure category [ Upstream commit 90c132f8379b788a0482dbeabc884d3ae5d82205 ] run_vmtests.sh contains special handling to ensure the hwpoison_inject module is available for the memory-failure tests. This logic was implemented outside of run_test(), making the setup category-specific but managed globally. Move the hwpoison_inject handling into run_test() and restrict it to the memory-failure category so that: 1. the module is checked and loaded only when memory-failure tests run, 2. the test is skipped if the module or the debugfs interface (/sys/kernel/debug/hwpoison/) is not available. 3. the module is unloaded after the test if it was loaded by the script. This localizes category-specific setup and makes the test flow consistent with other per-category preparations. While updating this logic, fix the module availability check. The script previously used: modprobe -R hwpoison_inject The -R option prints the resolved module name to stdout, causing every run to print: hwpoison_inject in the test output, even when no action is required, introducing unnecessary noise. Replace this with: modprobe -n hwpoison_inject which verifies that the module is loadable without producing output, keeping the selftest logs clean and consistent. Also, ensure that skipped tests do not override a previously recorded failure. A skipped test currently sets exitcode to ksft_skip even if a prior test has failed, which can mask failures in the final exit status. Update the logic to only set exitcode to ksft_skip when no failure has been recorded. Link: https://lore.kernel.org/93441f34f7ef5add47d1a130d03daa79e21b5050.1779296493.git.sayalip@linux.ibm.com Fixes: ff4ef2fbd101 ("selftests/mm: add memory failure anonymous page test") Signed-off-by: Sayali Patil Reviewed-by: Miaohe Lin Tested-by: Venkat Rao Bagalkote Cc: David Hildenbrand (Arm) Cc: Dev Jain Cc: Liam Howlett Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Cc: Zi Yan Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- tools/testing/selftests/mm/run_vmtests.sh | 62 +++++++++++++++-------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh index 913a32a47baf8..59e6eada431f3 100755 --- a/tools/testing/selftests/mm/run_vmtests.sh +++ b/tools/testing/selftests/mm/run_vmtests.sh @@ -234,6 +234,9 @@ pretty_name() { # Usage: run_test [test binary] [arbitrary test arguments...] run_test() { if test_selected ${CATEGORY}; then + local skip=0 + local LOADED_HWPOISON_INJECT_MOD=0 + # On memory constrainted systems some tests can fail to allocate hugepages. # perform some cleanup before the test for a higher success rate. if [ ${CATEGORY} == "thp" -o ${CATEGORY} == "hugetlb" ]; then @@ -248,13 +251,45 @@ run_test() { fi fi + # Ensure hwpoison_inject is available for memory-failure tests + if [ "${CATEGORY}" = "memory-failure" ]; then + # Try to load hwpoison_inject if not present. + HWPOISON_DIR=/sys/kernel/debug/hwpoison/ + if [ ! -d "$HWPOISON_DIR" ]; then + if ! modprobe -n hwpoison_inject > /dev/null 2>&1; then + echo "Module hwpoison_inject not found, skipping..." \ + | tap_prefix + skip=1 + else + modprobe hwpoison_inject > /dev/null 2>&1 + LOADED_HWPOISON_INJECT_MOD=1 + if [ ! -d "$HWPOISON_DIR" ]; then + echo "hwpoison debugfs interface not present" \ + | tap_prefix + skip=1 + fi + fi + fi + + fi + local test=$(pretty_name "$*") local title="running $*" local sep=$(echo -n "$title" | tr "[:graph:][:space:]" -) printf "%s\n%s\n%s\n" "$sep" "$title" "$sep" | tap_prefix - ("$@" 2>&1) | tap_prefix - local ret=${PIPESTATUS[0]} + if [ $skip -eq 1 ]; then + local ret=$ksft_skip + else + ("$@" 2>&1) | tap_prefix + local ret=${PIPESTATUS[0]} + fi + + # Unload hwpoison_inject if we loaded it + if [ "${LOADED_HWPOISON_INJECT_MOD}" = "1" ]; then + modprobe -r hwpoison_inject > /dev/null 2>&1 + fi + count_total=$(( count_total + 1 )) if [ $ret -eq 0 ]; then count_pass=$(( count_pass + 1 )) @@ -264,7 +299,9 @@ run_test() { count_skip=$(( count_skip + 1 )) echo "[SKIP]" | tap_prefix echo "ok ${count_total} ${test} # SKIP" | tap_output - exitcode=$ksft_skip + if [ $exitcode -eq 0 ]; then + exitcode=$ksft_skip + fi else count_fail=$(( count_fail + 1 )) echo "[FAIL]" | tap_prefix @@ -525,24 +562,7 @@ CATEGORY="page_frag" run_test ./test_page_frag.sh nonaligned CATEGORY="rmap" run_test ./rmap -# Try to load hwpoison_inject if not present. -HWPOISON_DIR=/sys/kernel/debug/hwpoison/ -if [ ! -d "$HWPOISON_DIR" ]; then - if ! modprobe -q -R hwpoison_inject; then - echo "Module hwpoison_inject not found, skipping..." - else - modprobe hwpoison_inject > /dev/null 2>&1 - LOADED_MOD=1 - fi -fi - -if [ -d "$HWPOISON_DIR" ]; then - CATEGORY="memory-failure" run_test ./memory-failure -fi - -if [ -n "${LOADED_MOD}" ]; then - modprobe -r hwpoison_inject > /dev/null 2>&1 -fi +CATEGORY="memory-failure" run_test ./memory-failure if [ "${HAVE_HUGEPAGES}" = 1 ]; then echo "$orig_nr_hugepgs" > /proc/sys/vm/nr_hugepages From 0481f4bad161f46e4e6490f457297aa04e4787e6 Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:53 +0530 Subject: [PATCH 1846/2758] selftests/mm: clarify alternate unmapping in compaction_test [ Upstream commit 69ba1d76d93ab818245333cf400928477ba72053 ] Add a comment explaining that every other entry in the list is unmapped to intentionally create fragmentation with locked pages before invoking check_compaction(). Link: https://lore.kernel.org/da5e0a8d5152e54152c0d2f456aac2fac35af291.1779296493.git.sayalip@linux.ibm.com Fixes: bd67d5c15cc1 ("Test compaction of mlocked memory") Signed-off-by: Sayali Patil Tested-by: Venkat Rao Bagalkote Cc: David Hildenbrand (Arm) Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Cc: Zi Yan Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- tools/testing/selftests/mm/compaction_test.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/testing/selftests/mm/compaction_test.c b/tools/testing/selftests/mm/compaction_test.c index 30209c40b6977..e1610e2fbdf64 100644 --- a/tools/testing/selftests/mm/compaction_test.c +++ b/tools/testing/selftests/mm/compaction_test.c @@ -261,6 +261,9 @@ int main(int argc, char **argv) mem_fragmentable_MB -= MAP_SIZE_MB; } + /* Unmap every other entry in the list to create fragmentation with + * locked pages before invoking check_compaction(). + */ for (entry = list; entry != NULL; entry = entry->next) { munmap(entry->map, MAP_SIZE); if (!entry->next) From 679642fa56d524dc86253ce3761872df49be355c Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 11 Jun 2026 09:11:00 +0530 Subject: [PATCH 1847/2758] selftests/mm: allow PUD-level entries in compound testcase of hmm tests [ Upstream commit 224ed0e019b122d08580362abe57574a78f2b5fa ] Patch series "selftests/mm: assorted fixes for hmm-tests", v3. This series fixes a few issues in hmm-tests that show up when page-size and huge-page configuration differ from the hardcoded assumptions the tests were written for (PMD/THP sizing, default hugepage size, and related cases). It also includes a fix to exclusive_cow: the test ignored the return value of fork(), so both parent and child ran the same teardown path. This patch (of 3): The HMM compound testcase currently assumes only PMD-level mappings and fails on systems where default_hugepagesz=1G is set, because the region is then reported by the device at PUD level. Determine the mapping level (PMD or PUD) the device reports for the first page of the range and require every page to match that level exactly via ASSERT_EQ(). This accepts PUD-level mappings while preserving the expected/observed protection values printed on failure, and rejects a fragmented mapping that mixes PMD- and PUD-level entries within the same range (which a per-page OR check would have let pass). Link: https://lore.kernel.org/20260611034102.1030738-1-aboorvad@linux.ibm.com Link: https://lore.kernel.org/20260611034102.1030738-2-aboorvad@linux.ibm.com Fixes: e478425bec93 ("mm/hmm: add tests for hmm_pfn_to_map_order()") Signed-off-by: Sayali Patil Co-developed-by: Aboorva Devarajan Signed-off-by: Aboorva Devarajan Cc: Alex Sierra Cc: Alistair Popple Cc: Balbir Singh Cc: David Hildenbrand Cc: Jason Gunthorpe Cc: Leon Romanovsky Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Matthew Brost Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Ralph Campbell Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- tools/testing/selftests/mm/hmm-tests.c | 32 +++++++++++++++++++------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c index 77fb4c5d871bb..c7fda556f2a14 100644 --- a/tools/testing/selftests/mm/hmm-tests.c +++ b/tools/testing/selftests/mm/hmm-tests.c @@ -1599,8 +1599,8 @@ TEST_F(hmm2, snapshot) } /* - * Test the hmm_range_fault() HMM_PFN_PMD flag for large pages that - * should be mapped by a large page table entry. + * Test the hmm_range_fault() handling of large pages (PMD or PUD) + * that should be mapped by a large page table entry. */ TEST_F(hmm, compound) { @@ -1610,6 +1610,7 @@ TEST_F(hmm, compound) unsigned long default_hsize = default_huge_page_size(); int *ptr; unsigned char *m; + unsigned char prot; int ret; unsigned long i; @@ -1646,11 +1647,20 @@ TEST_F(hmm, compound) ASSERT_EQ(ret, 0); ASSERT_EQ(buffer->cpages, npages); - /* Check what the device saw. */ + /* + * Check what the device saw. The region is backed by a single huge + * page that the device reports either at PMD or at PUD level depending + * on the configured default hugepage size. Determine that level from + * the first page and require every page in the range to match it + * exactly, so that a fragmented mapping mixing levels (or a missing + * large-page bit) is still caught and reported with its actual value. + */ m = buffer->mirror; + prot = HMM_DMIRROR_PROT_WRITE | + ((m[0] & HMM_DMIRROR_PROT_PUD) ? HMM_DMIRROR_PROT_PUD : + HMM_DMIRROR_PROT_PMD); for (i = 0; i < npages; ++i) - ASSERT_EQ(m[i], HMM_DMIRROR_PROT_WRITE | - HMM_DMIRROR_PROT_PMD); + ASSERT_EQ(m[i], prot); /* Make the region read-only. */ ret = mprotect(buffer->ptr, size, PROT_READ); @@ -1661,11 +1671,17 @@ TEST_F(hmm, compound) ASSERT_EQ(ret, 0); ASSERT_EQ(buffer->cpages, npages); - /* Check what the device saw. */ + /* + * Check what the device saw after mprotect(PROT_READ). Same + * approach as above: determine the mapping level from the first + * page and require every page to match it exactly. + */ m = buffer->mirror; + prot = HMM_DMIRROR_PROT_READ | + ((m[0] & HMM_DMIRROR_PROT_PUD) ? HMM_DMIRROR_PROT_PUD : + HMM_DMIRROR_PROT_PMD); for (i = 0; i < npages; ++i) - ASSERT_EQ(m[i], HMM_DMIRROR_PROT_READ | - HMM_DMIRROR_PROT_PMD); + ASSERT_EQ(m[i], prot); munmap(buffer->ptr, buffer->size); buffer->ptr = NULL; From 214ba45968877f3b61f49ebf6fe2691895385aca Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 11 Jun 2026 09:11:01 +0530 Subject: [PATCH 1848/2758] selftests/mm: remove hardcoded THP sizing assumptions in hmm tests [ Upstream commit 8edb0e769ce2a996774df83485781dd9f4bc2d44 ] migrate_partial_unmap_fault() and migrate_remap_fault() use hardcoded offsets based on a 2MB PMD size. Similarly, benchmark_thp_migration() assumes a fixed 2MB THP size when generating test buffer sizes. Derive offsets and test sizes from the runtime PMD page size returned by read_pmd_pagesize(). If unavailable, fall back to TWOMEG. This allows the tests to adapt correctly on systems where PMD-sized THP differs from 2MB. Also replace the fixed 1MB unmap size with a PMD-relative value derived from the runtime PMD size. On systems with larger PMD sizes, computed test buffer sizes can exceed INT_MAX. Skip such test cases to avoid overflow. Link: https://lore.kernel.org/20260611034102.1030738-3-aboorvad@linux.ibm.com Fixes: 24c2c5b8ffbd ("selftests/mm/hmm-tests: partial unmap, mremap and anon_write tests") Fixes: 271a7b2e3c13 ("selftests/mm/hmm-tests: new throughput tests including THP") Signed-off-by: Sayali Patil Signed-off-by: Aboorva Devarajan Acked-by: Balbir Singh Cc: Alex Sierra Cc: Alistair Popple Cc: David Hildenbrand Cc: Jason Gunthorpe Cc: Leon Romanovsky Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Matthew Brost Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Ralph Campbell Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- tools/testing/selftests/mm/hmm-tests.c | 62 ++++++++++++++++++-------- 1 file changed, 43 insertions(+), 19 deletions(-) diff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c index c7fda556f2a14..64e981548f72e 100644 --- a/tools/testing/selftests/mm/hmm-tests.c +++ b/tools/testing/selftests/mm/hmm-tests.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -2348,12 +2349,21 @@ TEST_F(hmm, migrate_partial_unmap_fault) struct hmm_buffer *buffer; unsigned long npages; unsigned long size = read_pmd_pagesize(); + unsigned long unmap_size; + unsigned long offsets[3]; unsigned long i; void *old_ptr; void *map; int *ptr; int ret, j, use_thp; - int offsets[] = { 0, 512 * ONEKB, ONEMEG }; + + if (!size) + size = TWOMEG; + + unmap_size = size / 2; + offsets[0] = 0; + offsets[1] = size / 4; + offsets[2] = size / 2; for (use_thp = 0; use_thp < 2; ++use_thp) { for (j = 0; j < ARRAY_SIZE(offsets); ++j) { @@ -2395,12 +2405,12 @@ TEST_F(hmm, migrate_partial_unmap_fault) for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) ASSERT_EQ(ptr[i], i); - munmap(buffer->ptr + offsets[j], ONEMEG); + munmap(buffer->ptr + offsets[j], unmap_size); /* Fault pages back to system memory and check them. */ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) if (i * sizeof(int) < offsets[j] || - i * sizeof(int) >= offsets[j] + ONEMEG) + i * sizeof(int) >= offsets[j] + unmap_size) ASSERT_EQ(ptr[i], i); buffer->ptr = old_ptr; @@ -2414,12 +2424,19 @@ TEST_F(hmm, migrate_remap_fault) struct hmm_buffer *buffer; unsigned long npages; unsigned long size = read_pmd_pagesize(); + unsigned long offsets[3]; unsigned long i; void *old_ptr, *new_ptr = NULL; void *map; int *ptr; int ret, j, use_thp, dont_unmap, before; - int offsets[] = { 0, 512 * ONEKB, ONEMEG }; + + if (!size) + size = TWOMEG; + + offsets[0] = 0; + offsets[1] = size / 4; + offsets[2] = size / 2; for (before = 0; before < 2; ++before) { for (dont_unmap = 0; dont_unmap < 2; ++dont_unmap) { @@ -2822,38 +2839,45 @@ static inline int run_migration_benchmark(int fd, int use_thp, size_t buffer_siz TEST_F_TIMEOUT(hmm, benchmark_thp_migration, 120) { struct benchmark_results thp_results, regular_results; - size_t thp_size = 2 * 1024 * 1024; /* 2MB - typical THP size */ + size_t thp_size = read_pmd_pagesize(); int iterations = 5; + if (!thp_size) + thp_size = TWOMEG; + printf("\nHMM THP Migration Benchmark\n"); printf("---------------------------\n"); printf("System page size: %ld bytes\n", sysconf(_SC_PAGESIZE)); /* Test different buffer sizes */ size_t test_sizes[] = { - thp_size / 4, /* 512KB - smaller than THP */ - thp_size / 2, /* 1MB - half THP */ - thp_size, /* 2MB - single THP */ - thp_size * 2, /* 4MB - two THPs */ - thp_size * 4, /* 8MB - four THPs */ - thp_size * 8, /* 16MB - eight THPs */ - thp_size * 128, /* 256MB - one twenty eight THPs */ + thp_size / 4, /* quarter THP */ + thp_size / 2, /* half THP */ + thp_size, /* single THP */ + thp_size * 2, /* two THPs */ + thp_size * 4, /* four THPs */ + thp_size * 8, /* eight THPs */ + thp_size * 128, /* one twenty eight THPs */ }; static const char *const test_names[] = { - "Small Buffer (512KB)", - "Half THP Size (1MB)", - "Single THP Size (2MB)", - "Two THP Size (4MB)", - "Four THP Size (8MB)", - "Eight THP Size (16MB)", - "One twenty eight THP Size (256MB)" + "Small Buffer", + "Half THP Size", + "Single THP Size", + "Two THP Size", + "Four THP Size", + "Eight THP Size", + "One twenty eight THP Size" }; int num_tests = ARRAY_SIZE(test_sizes); /* Run all tests */ for (int i = 0; i < num_tests; i++) { + /* Skip test sizes exceeding INT_MAX to avoid overflow */ + if (test_sizes[i] > INT_MAX) + break; + /* Test with THP */ ASSERT_EQ(run_migration_benchmark(self->fd, 1, test_sizes[i], iterations, &thp_results), 0); From 8c439591f70352a596c4e5cc414e6b001790e95c Mon Sep 17 00:00:00 2001 From: Aboorva Devarajan Date: Thu, 11 Jun 2026 09:11:02 +0530 Subject: [PATCH 1849/2758] selftests/mm: fix exclusive_cow test fork() handling [ Upstream commit cea5702144615878600d3a39b5d8b3cc34719012 ] The test ignores the return value of fork(), so both the parent and the (newly created) child run the COW verification loops and then call hmm_buffer_free() before returning into the kselftest harness, which _exit()s each side. This duplicated teardown sequence has been observed to manifest as a SIGSEGV in the test child, e.g.: hmm-tests[360141]: segfault (11) at 0 nip 10006964 lr 1000ac3c code 1 in hmm-tests[6964,10000000+30000] Fix this by adopting the same fork()-then-wait pattern already used by the nearby anon_write_child / anon_write_child_shared tests in this file: the child performs the COW verification and then _exit(0)s so it does not run the test teardown, while the parent independently verifies COW, waits for the child, and only then frees the buffer. Link: https://lore.kernel.org/20260611034102.1030738-4-aboorvad@linux.ibm.com Fixes: b659baea75469 ("mm: selftests for exclusive device memory") Signed-off-by: Aboorva Devarajan Cc: Alex Sierra Cc: Alistair Popple Cc: Balbir Singh Cc: David Hildenbrand Cc: Jason Gunthorpe Cc: Leon Romanovsky Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Matthew Brost Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Ralph Campbell Cc: Sayali Patil Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- tools/testing/selftests/mm/hmm-tests.c | 31 +++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c index 64e981548f72e..f16da3267d486 100644 --- a/tools/testing/selftests/mm/hmm-tests.c +++ b/tools/testing/selftests/mm/hmm-tests.c @@ -1882,6 +1882,8 @@ TEST_F(hmm, exclusive_cow) unsigned long i; int *ptr; int ret; + pid_t pid; + int status; npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; ASSERT_NE(npages, 0); @@ -1910,14 +1912,37 @@ TEST_F(hmm, exclusive_cow) ASSERT_EQ(ret, 0); ASSERT_EQ(buffer->cpages, npages); - fork(); + pid = fork(); + if (pid == -1) + ASSERT_EQ(pid, 0); - /* Fault pages back to system memory and check them. */ + if (pid == 0) { + /* + * Child verifies COW independently, then _exit(0)s so it does + * not run the test teardown. A failed ASSERT_* here makes the + * harness abort() the child, so the parent sees + * !WIFEXITED(status) below and fails in turn. + */ + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) + ASSERT_EQ(ptr[i]++, i); + + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) + ASSERT_EQ(ptr[i], i + 1); + + _exit(0); + } + + /* Parent: also increment to verify COW works for both processes. */ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) ASSERT_EQ(ptr[i]++, i); for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) - ASSERT_EQ(ptr[i], i+1); + ASSERT_EQ(ptr[i], i + 1); + + /* Parent: wait for child and then free the buffer. */ + ASSERT_EQ(waitpid(pid, &status, 0), pid); + ASSERT_TRUE(WIFEXITED(status)); + ASSERT_EQ(WEXITSTATUS(status), 0); hmm_buffer_free(buffer); } From 31d486562062936af812c8892ece0a616ab9b544 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Thu, 18 Jun 2026 03:32:28 +0800 Subject: [PATCH 1850/2758] net: marvell: prestera: initialize err in prestera_port_sfp_bind [ Upstream commit 62b01f72d93c7bc8fde3b2e5b5f783eca5f53324 ] prestera_port_sfp_bind() returns err after walking the ports node. If no child node matches the port's front-panel id, err is never assigned. Initialize err to 0 because absence of a matching optional port device tree node is not an error. In that case no phylink is created and port creation should continue with port->phy_link left NULL. Errors from malformed matched nodes and phylink_create() still propagate. Fixes: 52323ef75414 ("net: marvell: prestera: add phylink support") Signed-off-by: Ruoyu Wang Reviewed-by: Maxime Chevallier Acked-by: Elad Nachman Link: https://patch.msgid.link/20260617193228.1653582-1-ruoyuw560@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/marvell/prestera/prestera_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/prestera/prestera_main.c b/drivers/net/ethernet/marvell/prestera/prestera_main.c index 41e19e9ad28d4..a82e7a8029851 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_main.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_main.c @@ -373,7 +373,7 @@ static int prestera_port_sfp_bind(struct prestera_port *port) struct device_node *ports, *node; struct fwnode_handle *fwnode; struct phylink *phy_link; - int err; + int err = 0; if (!sw->np) return 0; From b65289e1c3f352a9f92c6e19713ddd647e033253 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Wed, 17 Jun 2026 21:57:45 +0800 Subject: [PATCH 1851/2758] tipc: fix use-after-free of the discoverer in tipc_disc_rcv() [ Upstream commit 1579342d71133da7f00daa02c75cebec7372097b ] bearer_disable() frees b->disc with tipc_disc_delete()'s plain kfree(), but tipc_disc_rcv() still dereferences b->disc in RX softirq under rcu_read_lock() (tipc_udp_recv -> tipc_rcv -> tipc_disc_rcv). L2 bearers are safe thanks to the synchronize_net() in tipc_disable_l2_media(), but the UDP bearer defers that call to the cleanup_bearer() workqueue, so the discoverer is freed with no grace period: BUG: KASAN: slab-use-after-free in tipc_disc_rcv (net/tipc/discover.c:149) Read of size 8 at addr ffff88802348b728 by task poc_tipc/184 tipc_disc_rcv (net/tipc/discover.c:149) tipc_rcv (net/tipc/node.c:2126) tipc_udp_recv (net/tipc/udp_media.c:391) udp_rcv (net/ipv4/udp.c:2643) ip_local_deliver_finish (net/ipv4/ip_input.c:241) Freed by task 181: kfree (mm/slub.c:6565) bearer_disable (net/tipc/bearer.c:418) tipc_nl_bearer_disable (net/tipc/bearer.c:1001) The bearer is freed with kfree_rcu(); free the discoverer the same way. Add an rcu_head to struct tipc_discoverer and free it and its skb from an RCU callback. Because the RCU callback (tipc_disc_free_rcu) lives in module text, a call_rcu() that is still pending when the tipc module is unloaded would invoke a freed function. Add an rcu_barrier() to tipc_exit() after the bearer subsystem has been torn down, so all pending discoverer callbacks have run before the module text goes away. Reachable from an unprivileged user namespace: the TIPCv2 genl family is netnsok and its bearer commands have no GENL_ADMIN_PERM. Needs CONFIG_TIPC and CONFIG_TIPC_MEDIA_UDP. Fixes: 25b0b9c4e835 ("tipc: handle collisions of 32-bit node address hash values") Reported-by: Xiang Mei Signed-off-by: Weiming Shi Reviewed-by: Tung Nguyen Link: https://patch.msgid.link/20260617135744.3383175-3-bestswngs@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/tipc/core.c | 5 +++++ net/tipc/discover.c | 14 ++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/net/tipc/core.c b/net/tipc/core.c index 434e70eabe081..1ddecea1df6e9 100644 --- a/net/tipc/core.c +++ b/net/tipc/core.c @@ -218,6 +218,11 @@ static void __exit tipc_exit(void) unregister_pernet_device(&tipc_net_ops); tipc_unregister_sysctl(); + /* TODO: Wait for all timers that called call_rcu() to finish before + * calling rcu_barrier(). + */ + rcu_barrier(); + pr_info("Deactivated\n"); } diff --git a/net/tipc/discover.c b/net/tipc/discover.c index 3e54d2df5683a..b9d06595b0673 100644 --- a/net/tipc/discover.c +++ b/net/tipc/discover.c @@ -58,6 +58,7 @@ * @skb: request message to be (repeatedly) sent * @timer: timer governing period between requests * @timer_intv: current interval between requests (in ms) + * @rcu: RCU head for deferred freeing */ struct tipc_discoverer { u32 bearer_id; @@ -69,6 +70,7 @@ struct tipc_discoverer { struct sk_buff *skb; struct timer_list timer; unsigned long timer_intv; + struct rcu_head rcu; }; /** @@ -382,6 +384,15 @@ int tipc_disc_create(struct net *net, struct tipc_bearer *b, return 0; } +static void tipc_disc_free_rcu(struct rcu_head *rp) +{ + struct tipc_discoverer *d = container_of(rp, struct tipc_discoverer, + rcu); + + kfree_skb(d->skb); + kfree(d); +} + /** * tipc_disc_delete - destroy object sending periodic link setup requests * @d: ptr to link dest structure @@ -389,8 +400,7 @@ int tipc_disc_create(struct net *net, struct tipc_bearer *b, void tipc_disc_delete(struct tipc_discoverer *d) { timer_shutdown_sync(&d->timer); - kfree_skb(d->skb); - kfree(d); + call_rcu(&d->rcu, tipc_disc_free_rcu); } /** From 7ce31739fe88a558370135db95bbeec1e7ddfc29 Mon Sep 17 00:00:00 2001 From: Ilya Maximets Date: Tue, 16 Jun 2026 12:03:29 +0200 Subject: [PATCH 1852/2758] net: dst_metadata: fix false-positive memcpy overflow in tun_dst_unclone [ Upstream commit 4c6d43db2a4d2cef3921e885cf34798f790d34ea ] kmalloc_flex() in metadata_dst_alloc() sets __counted_by for the structure to the options_len, which is then initialized to zero. Later, we're initializing the structure by copying the tunnel info together with the options, and this triggers a warning for a potential memcpy overflow, since the compiler estimates that the options can't fit into the structure, even though the memory for them is actually allocated. memcpy: detected buffer overflow: 104 byte write of buffer size 96 WARNING: CPU: X PID: Y at lib/string_helpers.c:1036 __fortify_report skb_tunnel_info_unclone+0x179/0x190 geneve_xmit+0x7fe/0xe00 The issue is triggered when built with clang and source fortification. Fix that by doing the copy in two stages: first - the main data with the options_len, then the options. This way the correct length should be known at the time of the copy. It would be better if the options_len never changed after allocation, but the allocation code is a little separate from the initialization and it would be awkward and potentially dangerous to return a struct with options_len set to a non-zero value from the metadata_dst_alloc(). Another option would be to use ip_tunnel_info_opts_set(), but it is doing too many unnecessary operations for the use case here. Fixes: 69050f8d6d07 ("treewide: Replace kmalloc with kmalloc_obj for non-scalar types") Reported-by: Johan Thomsen Closes: https://lore.kernel.org/netdev/CAKv6aAM8_EWgXScnKmKYm_4SwGDVBK++dzfP+Y6msUXbp99QUw@mail.gmail.com/ Signed-off-by: Ilya Maximets Link: https://patch.msgid.link/20260616100332.1308294-1-i.maximets@ovn.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- include/net/dst_metadata.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/include/net/dst_metadata.h b/include/net/dst_metadata.h index 1fc2fb03ce3f9..f45d1e3163f00 100644 --- a/include/net/dst_metadata.h +++ b/include/net/dst_metadata.h @@ -164,8 +164,11 @@ static inline struct metadata_dst *tun_dst_unclone(struct sk_buff *skb) if (!new_md) return ERR_PTR(-ENOMEM); - memcpy(&new_md->u.tun_info, &md_dst->u.tun_info, - sizeof(struct ip_tunnel_info) + md_size); + /* Copy in two stages to keep the __counted_by happy. */ + new_md->u.tun_info = md_dst->u.tun_info; + memcpy(ip_tunnel_info_opts(&new_md->u.tun_info), + ip_tunnel_info_opts(&md_dst->u.tun_info), md_size); + #ifdef CONFIG_DST_CACHE /* Unclone the dst cache if there is one */ if (new_md->u.tun_info.dst_cache.cache) { From c36cecf9903f882f6d83a842ee0cb31a13ab747e Mon Sep 17 00:00:00 2001 From: Wayen Yan Date: Wed, 17 Jun 2026 13:48:13 +0800 Subject: [PATCH 1853/2758] net: ethernet: mtk_ppe: Fix rhashtable leak in mtk_ppe_init error paths [ Upstream commit 41782770be567abc6509169d0ffdada31c783a66 ] In mtk_ppe_init(), when accounting is enabled, the error paths for dmam_alloc_coherent(mib) and devm_kzalloc(acct) failures return NULL directly, bypassing the err_free_l2_flows label that destroys the rhashtable initialized earlier. While this leak only occurs during probe (not runtime) and the leaked memory is minimal (an empty rhash table), fixing it ensures proper error path cleanup consistency. Fix by changing the two return NULL statements to goto err_free_l2_flows. Fixes: 603ea5e7ffa7 ("net: ethernet: mtk_eth_soc: fix memory leak in error path") Signed-off-by: Wayen Yan Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/178167550101.2217645.14579307712717502425@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/mediatek/mtk_ppe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c index 18279e2a7022e..8451dc3fd00aa 100644 --- a/drivers/net/ethernet/mediatek/mtk_ppe.c +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c @@ -918,7 +918,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index) mib = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*mib), &ppe->mib_phys, GFP_KERNEL); if (!mib) - return NULL; + goto err_free_l2_flows; ppe->mib_table = mib; @@ -926,7 +926,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index) GFP_KERNEL); if (!acct) - return NULL; + goto err_free_l2_flows; ppe->acct_table = acct; } From e129d1a4c2bad55fd082c9c67c928ad57846461f Mon Sep 17 00:00:00 2001 From: Ratheesh Kannoth Date: Wed, 17 Jun 2026 15:51:49 +0530 Subject: [PATCH 1854/2758] octeontx2-af: npc: cn20k: fix NPC defrag [ Upstream commit 48b67c0e8af65acd59d81ddaedd3442b5e4c27b7 ] npc_defrag_alloc_free_slots() always passed NPC_MCAM_KEY_X2 into __npc_subbank_alloc(), which must match sb->key_type, so defrag never allocated replacement slots on X4 banks. Pass the subbank key type for bank 0, and only extend the search into bank 1 for X2 (X4 MCAM indices are confined to b0b..b0t). Fixes: 645c6e3c1999 ("octeontx2-af: npc: cn20k: virtual index support") Signed-off-by: Ratheesh Kannoth Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260617102149.1309913-1-rkannoth@marvell.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c index fe8c4ffcd8f73..5dfdfbabfaddc 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c @@ -3542,15 +3542,18 @@ static int npc_defrag_alloc_free_slots(struct rvu *rvu, alloc_cnt2 = 0; rc = __npc_subbank_alloc(rvu, sb, - NPC_MCAM_KEY_X2, sb->b0b, + f->key_type, sb->b0b, sb->b0t, NPC_MCAM_LOWER_PRIO, false, cnt, save, cnt, true, &alloc_cnt1); - if (alloc_cnt1 < cnt) { + /* X4 entries only occupy bank 0 (b0b..b0t); see npc_subbank_idx_2_mcam_idx(). + * X2 uses both halves of the subbank, so spill into bank 1 if needed. + */ + if (alloc_cnt1 < cnt && f->key_type == NPC_MCAM_KEY_X2) { rc = __npc_subbank_alloc(rvu, sb, - NPC_MCAM_KEY_X2, sb->b1b, + f->key_type, sb->b1b, sb->b1t, NPC_MCAM_LOWER_PRIO, false, cnt - alloc_cnt1, From a56fd8449de815546c67d8fabba7611d0bf9ec5f Mon Sep 17 00:00:00 2001 From: Geetha sowjanya Date: Wed, 17 Jun 2026 00:30:18 +0530 Subject: [PATCH 1855/2758] octeontx2-af: mcs: Fix unsupported secy stats read [ Upstream commit d4b7440f7316e76f013f57d8b6da069a1b9c34e7 ] Secy control stats counter doesn't exist for CNF10KB platform. Skip reading this respective register for CNF10KB silicon while fetching secy stats. Fixes: 9312150af8da ("octeontx2-af: cn10k: mcs: Support for stats collection") Signed-off-by: Geetha sowjanya Signed-off-by: Subbaraya Sundeep Link: https://patch.msgid.link/1781636420-19816-1-git-send-email-sbhatta@marvell.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/marvell/octeontx2/af/mcs.c | 6 +++--- drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mcs.c b/drivers/net/ethernet/marvell/octeontx2/af/mcs.c index c1775bd01c2b4..a07e0b3d8d000 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/mcs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/mcs.c @@ -120,13 +120,13 @@ void mcs_get_rx_secy_stats(struct mcs *mcs, struct mcs_secy_stats *stats, int id reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYUNTAGGEDX(id); stats->pkt_untaged_cnt = mcs_reg_read(mcs, reg); - reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYCTLX(id); - stats->pkt_ctl_cnt = mcs_reg_read(mcs, reg); - if (mcs->hw->mcs_blks > 1) { reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYNOTAGX(id); stats->pkt_notag_cnt = mcs_reg_read(mcs, reg); + return; } + reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYCTLX(id); + stats->pkt_ctl_cnt = mcs_reg_read(mcs, reg); } void mcs_get_flowid_stats(struct mcs *mcs, struct mcs_flowid_stats *stats, diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c index fa461489acdd6..ca2704b188a5c 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c @@ -482,10 +482,11 @@ static int rvu_dbg_mcs_rx_secy_stats_display(struct seq_file *filp, void *unused seq_printf(filp, "secy%d: Tagged ctrl pkts: %lld\n", secy_id, stats.pkt_tagged_ctl_cnt); seq_printf(filp, "secy%d: Untaged pkts: %lld\n", secy_id, stats.pkt_untaged_cnt); - seq_printf(filp, "secy%d: Ctrl pkts: %lld\n", secy_id, stats.pkt_ctl_cnt); if (mcs->hw->mcs_blks > 1) seq_printf(filp, "secy%d: pkts notag: %lld\n", secy_id, stats.pkt_notag_cnt); + else + seq_printf(filp, "secy%d: Ctrl pkts: %lld\n", secy_id, stats.pkt_ctl_cnt); } mutex_unlock(&mcs->stats_lock); return 0; From e7f1311e7ef3371e0f8e9b24ef407aff48626d9d Mon Sep 17 00:00:00 2001 From: Subbaraya Sundeep Date: Wed, 17 Jun 2026 00:30:19 +0530 Subject: [PATCH 1856/2758] octeontx2-pf: Clear stats of all resources when freeing resources [ Upstream commit fd4460721fb4062ef470ecdfdaedadfe7e415c09 ] When all MCS resources mapped to a PF are being freed then clear stats of all those resources too. Fixes: 815debbbf7b5 ("octeontx2-pf: mcs: Clear stats before freeing resource") Signed-off-by: Subbaraya Sundeep Link: https://patch.msgid.link/1781636420-19816-2-git-send-email-sbhatta@marvell.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c index 2cc1bdfd9b2e8..702cf07490e79 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c @@ -182,6 +182,7 @@ static void cn10k_mcs_free_rsrc(struct otx2_nic *pfvf, enum mcs_direction dir, clear_req->id = hw_rsrc_id; clear_req->type = type; clear_req->dir = dir; + clear_req->all = all; req = otx2_mbox_alloc_msg_mcs_free_resources(mbox); if (!req) From d0ab67f7e7cfca53eb9818caabcfd747584319e9 Mon Sep 17 00:00:00 2001 From: Geetha sowjanya Date: Wed, 17 Jun 2026 00:30:20 +0530 Subject: [PATCH 1857/2758] octeontx2-pf: mcs: Fix mcs resources free on PF shutdown [ Upstream commit 450d0e90b10393bd9f50c127875a9fdd4cc81c30 ] On PF shutdown, the current driver free mcs hardware resources though mcs resources are not allocated to it. This patch checks the mcs resources status and if resources are allocated then only sends mailbox message to free them. Fixes: c54ffc73601c ("octeontx2-pf: mcs: Introduce MACSEC hardware offloading") Signed-off-by: Geetha sowjanya Signed-off-by: Subbaraya Sundeep Link: https://patch.msgid.link/1781636420-19816-3-git-send-email-sbhatta@marvell.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- .../net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c index 702cf07490e79..9524d38f15822 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c @@ -1777,11 +1777,16 @@ int cn10k_mcs_init(struct otx2_nic *pfvf) void cn10k_mcs_free(struct otx2_nic *pfvf) { + struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; + if (!test_bit(CN10K_HW_MACSEC, &pfvf->hw.cap_flag)) return; - cn10k_mcs_free_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_SECY, 0, true); - cn10k_mcs_free_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_SECY, 0, true); + if (!list_empty(&cfg->txsc_list)) { + cn10k_mcs_free_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_SECY, 0, true); + cn10k_mcs_free_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_SECY, 0, true); + } + kfree(pfvf->macsec_cfg); pfvf->macsec_cfg = NULL; } From 44068b6863fbda78fc810921ddf61642c377b3ca Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 17 Jun 2026 19:34:05 -0700 Subject: [PATCH 1858/2758] net: emac: Fix NULL pointer dereference in emac_probe [ Upstream commit f623d38fe6c4e8c40b23f42cc6fe6963fa49997b ] Move devm_request_irq() after devm_platform_ioremap_resource() so that dev->emacp is mapped before the interrupt handler can fire. An early interrupt hitting emac_irq() would dereference the NULL dev->emacp and crash. Also remove redundant error message. devm_platform_ioremap_resource() already returns an error message with dev_err_probe(). Fixes: dcc34ef7c834 ("net: ibm: emac: manage emac_irq with devm") Signed-off-by: Rosen Penev Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20260618023405.415644-1-rosenp@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/ibm/emac/core.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c index 4e503b3d0d2d3..e1047915e101e 100644 --- a/drivers/net/ethernet/ibm/emac/core.c +++ b/drivers/net/ethernet/ibm/emac/core.c @@ -3035,6 +3035,12 @@ static int emac_probe(struct platform_device *ofdev) if (err) goto err_gone; + dev->emacp = devm_platform_ioremap_resource(ofdev, 0); + if (IS_ERR(dev->emacp)) { + err = PTR_ERR(dev->emacp); + goto err_gone; + } + /* Setup error IRQ handler */ dev->emac_irq = platform_get_irq(ofdev, 0); err = devm_request_irq(&ofdev->dev, dev->emac_irq, emac_irq, 0, "EMAC", @@ -3047,13 +3053,6 @@ static int emac_probe(struct platform_device *ofdev) ndev->irq = dev->emac_irq; - dev->emacp = devm_platform_ioremap_resource(ofdev, 0); - if (IS_ERR(dev->emacp)) { - dev_err(&ofdev->dev, "can't map device registers"); - err = PTR_ERR(dev->emacp); - goto err_gone; - } - /* Wait for dependent devices */ err = emac_wait_deps(dev); if (err) From 0c3d8fc87e10e38fe054ece009d6d1f66bef2cd4 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 17 Jun 2026 17:57:08 -0400 Subject: [PATCH 1859/2758] net/sched: act_ct: fix nf_connlabels leak on two error paths [ Upstream commit 16e088016f38cf728a0de709c3335cc5a3850476 ] tcf_ct_fill_params() calls nf_connlabels_get() (setting put_labels) when TCA_CT_LABELS is present, but two later error sites use a bare return instead of "goto err", skipping the err: nf_connlabels_put() cleanup. They also precede the "p->put_labels = put_labels" assignment, so the tcf_ct_params_free() fallback does not release the count either. Each failed RTM_NEWACTION on these paths leaks one nf_connlabels reference: net->ct.labels_used is incremented and never released. The action is reachable with CAP_NET_ADMIN over the netns, i.e. from an unprivileged user namespace on default-userns kernels. Impact: an unprivileged user with CAP_NET_ADMIN over a network namespace (e.g. via user namespaces) leaks one nf_connlabels reference per failed RTM_NEWACTION on the two error paths; net->ct.labels_used is never released. The err: label is safe to reach from both sites: p->tmpl is still NULL there (kzalloc'd, not yet assigned) and nf_ct_put(NULL) is a no-op, so no inline release is needed. Fixes: 70f06c115bcc ("sched: act_ct: switch to per-action label counting") Signed-off-by: Michael Bommarito Acked-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260617215708.1115818-1-michael.bommarito@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/act_ct.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index 6158e13c98d35..f5866a364a74a 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -1295,7 +1295,8 @@ static int tcf_ct_fill_params(struct net *net, if (tb[TCA_CT_ZONE]) { if (!IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES)) { NL_SET_ERR_MSG_MOD(extack, "Conntrack zones isn't enabled."); - return -EOPNOTSUPP; + err = -EOPNOTSUPP; + goto err; } tcf_ct_set_key_val(tb, @@ -1308,7 +1309,8 @@ static int tcf_ct_fill_params(struct net *net, tmpl = nf_ct_tmpl_alloc(net, &zone, GFP_KERNEL); if (!tmpl) { NL_SET_ERR_MSG_MOD(extack, "Failed to allocate conntrack template"); - return -ENOMEM; + err = -ENOMEM; + goto err; } p->tmpl = tmpl; if (tb[TCA_CT_HELPER_NAME]) { From f4d7d8fdcc5976749555873aa5853cfe3e5b4ac3 Mon Sep 17 00:00:00 2001 From: Wayen Yan Date: Sat, 20 Jun 2026 16:17:44 +0800 Subject: [PATCH 1860/2758] net: airoha: Fix skb->priority underflow in airoha_dev_select_queue() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 86e51aa24686cc95bb35613059e8b94b9b81e3f0 ] In airoha_dev_select_queue(), the expression: queue = (skb->priority - 1) % AIROHA_NUM_QOS_QUEUES; implicitly converts to unsigned arithmetic: when skb->priority is 0 (the default for unclassified traffic), (0u - 1u) wraps to UINT_MAX, and UINT_MAX % 8 = 7, routing default best-effort packets to the highest-priority QoS queue. This causes QoS inversion where the majority of traffic on a PON gateway starves actual high-priority flows (VoIP, gaming, etc.). The "- 1" offset was a leftover from the ETS offload implementation that has since been removed. The correct mapping is a direct modulo: queue = skb->priority % AIROHA_NUM_QOS_QUEUES; This maps priority 0 → queue 0 (lowest), priority 7 → queue 7 (highest), with higher priorities wrapping around. This is the standard Linux sk_prio → HW queue mapping used by other drivers. Fixes: 2b288b81560b ("net: airoha: Introduce ndo_select_queue callback") Link: https://lore.kernel.org/netdev/178185573207.2378135.3729126358670287878@gmail.com/ Acked-by: Lorenzo Bianconi Reviewed-by: Joe Damato Signed-off-by: Wayen Yan Link: https://patch.msgid.link/178194366700.2485734.5368768965976693502@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/airoha/airoha_eth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index 7b8d0c5e262ab..1e361a35ebd49 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -1933,7 +1933,7 @@ static u16 airoha_dev_select_queue(struct net_device *dev, struct sk_buff *skb, */ channel = netdev_uses_dsa(dev) ? skb_get_queue_mapping(skb) : port->id; channel = channel % AIROHA_NUM_QOS_CHANNELS; - queue = (skb->priority - 1) % AIROHA_NUM_QOS_QUEUES; /* QoS queue */ + queue = skb->priority % AIROHA_NUM_QOS_QUEUES; queue = channel * AIROHA_NUM_QOS_QUEUES + queue; return queue < dev->num_tx_queues ? queue : 0; From 63d1c23764de2309cedbb779c75188d257a09d9b Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Wed, 17 Jun 2026 14:55:13 +0800 Subject: [PATCH 1861/2758] ipv6: ndisc: fix NULL deref in accept_untracked_na() [ Upstream commit d186e942365acece7c56d39da05dd63bf95b280a ] accept_untracked_na() re-fetches the inet6_dev with __in6_dev_get(dev) and dereferences idev->cnf.accept_untracked_na without a NULL check, even though its only caller ndisc_recv_na() already fetched and NULL-checked idev for the same device. Both reads of dev->ip6_ptr run in the same RCU read-side critical section, but a concurrent addrconf_ifdown() can clear dev->ip6_ptr between them: lowering the MTU below IPV6_MIN_MTU calls addrconf_ifdown() without the synchronize_net() that orders the unregister path, so the re-fetch returns NULL and oopses: BUG: KASAN: null-ptr-deref in ndisc_recv_na (net/ipv6/ndisc.c:974) Read of size 4 at addr 0000000000000364 Call Trace: ndisc_recv_na (net/ipv6/ndisc.c:974) icmpv6_rcv (net/ipv6/icmp.c:1193) ip6_protocol_deliver_rcu (net/ipv6/ip6_input.c:479) ip6_input_finish (net/ipv6/ip6_input.c:534) ip6_input (net/ipv6/ip6_input.c:545) ip6_mc_input (net/ipv6/ip6_input.c:635) ipv6_rcv (net/ipv6/ip6_input.c:351) It is reachable by an unprivileged user via a network namespace. Pass the caller's already validated idev instead of re-fetching it; the idev stays alive for the whole RCU critical section, so it is safe even after dev->ip6_ptr has been cleared. Fixes: aaa5f515b16b ("net: ipv6: new accept_untracked_na option to accept na only if in-network") Reported-by: Xiang Mei Signed-off-by: Weiming Shi Reviewed-by: Jiayuan Chen Link: https://patch.msgid.link/20260617065512.2529757-2-bestswngs@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/ndisc.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index e7ad13c5bd267..f867ec8d3d905 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -967,10 +967,8 @@ static enum skb_drop_reason ndisc_recv_ns(struct sk_buff *skb) return reason; } -static int accept_untracked_na(struct net_device *dev, struct in6_addr *saddr) +static int accept_untracked_na(struct inet6_dev *idev, struct in6_addr *saddr) { - struct inet6_dev *idev = __in6_dev_get(dev); - switch (READ_ONCE(idev->cnf.accept_untracked_na)) { case 0: /* Don't accept untracked na (absent in neighbor cache) */ return 0; @@ -980,7 +978,7 @@ static int accept_untracked_na(struct net_device *dev, struct in6_addr *saddr) * same subnet as an address configured on the interface that * received the na */ - return !!ipv6_chk_prefix(saddr, dev); + return !!ipv6_chk_prefix(saddr, idev->dev); default: return 0; } @@ -1078,7 +1076,7 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb) */ new_state = msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE; if (!neigh && lladdr && idev && READ_ONCE(idev->cnf.forwarding)) { - if (accept_untracked_na(dev, saddr)) { + if (accept_untracked_na(idev, saddr)) { neigh = neigh_create(&nd_tbl, &msg->target, dev); new_state = NUD_STALE; } From 5a3b2ee1e96d0580a8ed8deda6dfa430604f9ab0 Mon Sep 17 00:00:00 2001 From: Jiayuan Chen Date: Thu, 18 Jun 2026 18:43:35 +0800 Subject: [PATCH 1862/2758] ipv6: ioam: fix type confusion of dst_entry [ Upstream commit 9ed19e11d2146076d117d51a940643990118449b ] IOAM uses a dummy dst_entry(null_dst) to mark that the destination should not be changed after the transformation. This dst is stored in the IOAM lwt state and may be passed to dst_cache_set_ip6(). However, the IPv6 dst cache path eventually calls rt6_get_cookie(), which treats the dst_entry as part of a struct rt6_info. Since the null_dst was embedded directly as a struct dst_entry in struct ioam6_lwt, this resulted in an invalid cast and rt6_get_cookie() reading fields from the wrong object. In practice, the wrong cookie is not used while dst->obsolete is zero, but rt6_get_cookie() may also access per-cpu value when rt->sernum is zero. In this case, rt->sernum aliases ioam6_lwt::cache::reset_ts, which can become zero, making this a potential invalid pointer access. Fix this by embedding a full struct rt6_info for the dummy IPv6 route and passing its dst member to the dst APIs. Fixes: 47ce7c854563 ("net: ipv6: ioam6: fix double reallocation") Signed-off-by: Jiayuan Chen Reviewed-by: Justin Iurman Link: https://patch.msgid.link/20260618104336.48934-1-jiayuan.chen@linux.dev Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/ioam6_iptunnel.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/ipv6/ioam6_iptunnel.c b/net/ipv6/ioam6_iptunnel.c index b9f6d892a566c..cfb2c41634a0b 100644 --- a/net/ipv6/ioam6_iptunnel.c +++ b/net/ipv6/ioam6_iptunnel.c @@ -35,7 +35,7 @@ struct ioam6_lwt_freq { }; struct ioam6_lwt { - struct dst_entry null_dst; + struct rt6_info null_rt; struct dst_cache cache; struct ioam6_lwt_freq freq; atomic_t pkt_cnt; @@ -176,7 +176,7 @@ static int ioam6_build_state(struct net *net, struct nlattr *nla, * it is stored in the cache. Then, +1/-1 each time we read the cache * and release it. Long story short, we're fine. */ - dst_init(&ilwt->null_dst, NULL, NULL, DST_OBSOLETE_NONE, DST_NOCOUNT); + dst_init(&ilwt->null_rt.dst, NULL, NULL, DST_OBSOLETE_NONE, DST_NOCOUNT); atomic_set(&ilwt->pkt_cnt, 0); ilwt->freq.k = freq_k; @@ -360,7 +360,7 @@ static int ioam6_output(struct net *net, struct sock *sk, struct sk_buff *skb) /* This is how we notify that the destination does not change after * transformation and that we need to use orig_dst instead of the cache */ - if (dst == &ilwt->null_dst) { + if (dst == &ilwt->null_rt.dst) { dst_release(dst); dst = orig_dst; @@ -429,7 +429,7 @@ static int ioam6_output(struct net *net, struct sock *sk, struct sk_buff *skb) local_bh_disable(); if (orig_dst->lwtstate == dst->lwtstate) dst_cache_set_ip6(&ilwt->cache, - &ilwt->null_dst, &fl6.saddr); + &ilwt->null_rt.dst, &fl6.saddr); else dst_cache_set_ip6(&ilwt->cache, dst, &fl6.saddr); local_bh_enable(); From 84ac7a0f956247e5cc4b30bc84edc54d2516c976 Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Thu, 18 Jun 2026 12:28:12 +0300 Subject: [PATCH 1863/2758] dpaa2-switch: do not accept VLAN uppers while bridged [ Upstream commit d07d80b6a129a44538cda1549b7acf95154fb197 ] The dpaa2-switch driver does not support VLAN uppers while its ports are bridged. This scenario tried to be prevented by rejecting a bridge join while VLAN uppers exist but the reverse order was still possible. This patches adds a check so that the dpaa2-switch also does not accept VLAN uppers while bridged. Fixes: f48298d3fbfa ("staging: dpaa2-switch: move the driver out of staging") Signed-off-by: Ioana Ciornei Link: https://patch.msgid.link/20260618092813.432535-2-ioana.ciornei@nxp.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index 46ae81c2fa013..fa4e4f4797823 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -2198,6 +2198,7 @@ dpaa2_switch_prechangeupper_sanity_checks(struct net_device *netdev, static int dpaa2_switch_port_prechangeupper(struct net_device *netdev, struct netdev_notifier_changeupper_info *info) { + struct ethsw_port_priv *port_priv; struct netlink_ext_ack *extack; struct net_device *upper_dev; int err; @@ -2216,6 +2217,13 @@ static int dpaa2_switch_port_prechangeupper(struct net_device *netdev, if (!info->linking) dpaa2_switch_port_pre_bridge_leave(netdev); + } else if (is_vlan_dev(upper_dev)) { + port_priv = netdev_priv(netdev); + if (port_priv->fdb->bridge_dev) { + NL_SET_ERR_MSG_MOD(extack, + "Cannot accept VLAN uppers while bridged"); + return -EOPNOTSUPP; + } } return 0; From fc4f78e8f03442cc76e91b182cbeb12bfa22d975 Mon Sep 17 00:00:00 2001 From: Antoni Pokusinski Date: Wed, 15 Apr 2026 18:06:11 +0200 Subject: [PATCH 1864/2758] rtc: abx80x: fix the RTC_VL_CLR clearing all status flags [ Upstream commit 419719c514252a2dbb2e2976f564c83417dd6d0a ] The RTC_VL_CLR ioctl intends to clear only the battery low flag (BLF), however the current implementation writes 0 to the status register, clearing all status bits. Fix this by writing back the masked status value so that only BLF is cleared, preserving other status flags. Fixes: ffe1c5a2d427 ("rtc: abx80x: Implement RTC_VL_READ,CLR ioctls") Signed-off-by: Antoni Pokusinski Link: https://patch.msgid.link/20260415160610.127155-2-apokusinski01@gmail.com Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/rtc/rtc-abx80x.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c index 00d7de64ed3e7..008a70baa69fe 100644 --- a/drivers/rtc/rtc-abx80x.c +++ b/drivers/rtc/rtc-abx80x.c @@ -545,7 +545,8 @@ static int abx80x_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) status &= ~ABX8XX_STATUS_BLF; - tmp = i2c_smbus_write_byte_data(client, ABX8XX_REG_STATUS, 0); + tmp = i2c_smbus_write_byte_data(client, ABX8XX_REG_STATUS, + status); if (tmp < 0) return tmp; From 9242939dd6d9e0b7dd840ee5c8f0ebb99f9a382c Mon Sep 17 00:00:00 2001 From: Ronan Dalton Date: Fri, 8 May 2026 15:24:49 +1200 Subject: [PATCH 1865/2758] rtc: ds1307: handle oscillator stop flag for ds1337/ds1339/ds3231 [ Upstream commit a091e1ba3b68cabc9caedafc6f81d9fe9b3b2200 ] Prior to commit 48458654659c ("rtc: ds1307: remove clear of oscillator stop flag (OSF) in probe"), the oscillator stop flag (OSF) bit was checked during device probe for the ds1337, ds1339, ds1341, and ds3231 chips; if it was set, it would be cleared and a warning would be logged saying "SET TIME!". Since that commit, the OSF bit is no longer cleared, but the warning is still printed. Directly following that commit, there was no way to get rid of this warning because nothing cleared the OSF bit on these chips. The commit associated with the previous commit, 523923cfd5d6 ("rtc: ds1307: handle oscillator stop flag (OSF) for ds1341"), made proper use of the OSF when getting and setting the time in the RTC. However, the other RTC variants ds1337, ds1339 and ds3231 didn't have a corresponding change made. Given that the OSF bit is no longer cleared at probe time when it is set, the remaining three chips should have the same handling as the ds1341 chip has for the OSF bit. Fix the issue on the ds1337, ds1339 and ds3231 chips by applying the same logic as the ds1341 has to these chips. Note that any devices brought up between the first referenced commit and this one may begin mistrusting the time reported by the RTC until it is set again, if the bit was never explicitly cleared. Note that only the ds1339 was tested with this change, but the datasheets for the other chips contain essentially identical descriptions of the OSF bit so the same change should work. Signed-off-by: Ronan Dalton Cc: linux-rtc@vger.kernel.org Cc: linux-kernel@vger.kernel.org Cc: Alexandre Belloni Cc: Tyler Hicks Cc: Sasha Levin Cc: Meagan Lloyd Cc: Rodolfo Giometti Cc: Chris Packham Fixes: 48458654659c ("rtc: ds1307: remove clear of oscillator stop flag (OSF) in probe") Reviewed-by: Meagan Lloyd Reviewed-by: Tyler Hicks Link: https://patch.msgid.link/20260508032518.3696705-2-ronan.dalton@alliedtelesis.co.nz Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/rtc/rtc-ds1307.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 7205c59ff7294..edf81b975decb 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -269,6 +269,16 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t) if (tmp & DS1338_BIT_OSF) return -EINVAL; break; + case ds_1337: + case ds_1339: + case ds_1341: + case ds_3231: + ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &tmp); + if (ret) + return ret; + if (tmp & DS1337_BIT_OSF) + return -EINVAL; + break; case ds_1340: if (tmp & DS1340_BIT_nEOSC) return -EINVAL; @@ -279,13 +289,6 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t) if (tmp & DS1340_BIT_OSF) return -EINVAL; break; - case ds_1341: - ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &tmp); - if (ret) - return ret; - if (tmp & DS1337_BIT_OSF) - return -EINVAL; - break; case ds_1388: ret = regmap_read(ds1307->regmap, DS1388_REG_FLAG, &tmp); if (ret) @@ -380,14 +383,17 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t) regmap_update_bits(ds1307->regmap, DS1307_REG_CONTROL, DS1338_BIT_OSF, 0); break; - case ds_1340: - regmap_update_bits(ds1307->regmap, DS1340_REG_FLAG, - DS1340_BIT_OSF, 0); - break; + case ds_1337: + case ds_1339: case ds_1341: + case ds_3231: regmap_update_bits(ds1307->regmap, DS1337_REG_STATUS, DS1337_BIT_OSF, 0); break; + case ds_1340: + regmap_update_bits(ds1307->regmap, DS1340_REG_FLAG, + DS1340_BIT_OSF, 0); + break; case ds_1388: regmap_update_bits(ds1307->regmap, DS1388_REG_FLAG, DS1388_BIT_OSF, 0); From db8f1dcf5950b91886b9df4270bc816370d292c1 Mon Sep 17 00:00:00 2001 From: Nuoqi Gui Date: Thu, 18 Jun 2026 01:50:26 +0800 Subject: [PATCH 1866/2758] bpf: Fix stack slot index in nospec checks [ Upstream commit d1d53aa30ab3b5ae89161c9cc840b3f7489ad386 ] check_stack_write_fixed_off() computes the byte slot for a fixed-offset stack write as -off - 1, and records each written byte in slot_type[] with (slot - i) % BPF_REG_SIZE. The Spectre v4 sanitization pre-check uses slot_type[i] instead. For a 4-byte write at fp-8 after the lower half of fp-8 has been zeroed, the pre-check scans bytes 0..3 and sees STACK_ZERO while the actual write updates bytes 7..4. That can leave the second half-slot write without nospec_result even though the bytes being overwritten still require sanitization. Use the same slot index in the sanitization pre-check that the write path uses when updating slot_type[]. Fixes: 2039f26f3aca ("bpf: Fix leakage due to insufficient speculative store bypass mitigation") Acked-by: Luis Gerhorst Reviewed-by: Jiayuan Chen Reviewed-by: Emil Tsalapatis Signed-off-by: Nuoqi Gui Link: https://lore.kernel.org/r/20260618-f01-11-stack-nospec-slot-index-v3-1-780297041721@mails.tsinghua.edu.cn Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- kernel/bpf/verifier.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index c34bf4169d5c9..bda2b2f149468 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -3836,7 +3836,8 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env, bool sanitize = reg && is_spillable_regtype(reg->type); for (i = 0; i < size; i++) { - u8 type = state->stack[spi].slot_type[i]; + u8 type = state->stack[spi].slot_type[(slot - i) % + BPF_REG_SIZE]; if (type != STACK_MISC && type != STACK_ZERO) { sanitize = true; From 89cf4d0c71a2c68c7190228309c399433ef6c4c7 Mon Sep 17 00:00:00 2001 From: Sun Jian Date: Wed, 17 Jun 2026 17:35:56 +0800 Subject: [PATCH 1867/2758] bpf: Fix partial copy of non-linear test_run output [ Upstream commit b5f3534268e3f91c9d3e9dc79ee5a32555880ee9 ] For non-linear test_run output, bpf_test_finish() derives the linear data copy length from copy_size - frag_size. This only matches the linear data length when copy_size is the full packet size. When userspace provides a short data_out buffer, copy_size is clamped to that buffer size. If copy_size is smaller than frag_size, the computed length becomes negative and bpf_test_finish() returns -ENOSPC before copying the packet prefix or updating data_size_out. Compute the linear data length from the packet layout instead, and clamp the linear copy length to copy_size. This preserves the expected partial-copy semantics: return -ENOSPC, copy the packet prefix that fits in data_out, and report the full packet length through data_size_out. Fixes: 7855e0db150ad ("bpf: test_run: add xdp_shared_info pointer in bpf_test_finish signature") Signed-off-by: Sun Jian Acked-by: Paul Chaignon Link: https://lore.kernel.org/r/20260617093557.63880-2-sun.jian.kdev@gmail.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- net/bpf/test_run.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c index 2bc04feadfabe..f15c613aaa4ee 100644 --- a/net/bpf/test_run.c +++ b/net/bpf/test_run.c @@ -453,12 +453,8 @@ static int bpf_test_finish(const union bpf_attr *kattr, } if (data_out) { - int len = sinfo ? copy_size - frag_size : copy_size; - - if (len < 0) { - err = -ENOSPC; - goto out; - } + u32 head_len = size - frag_size; + u32 len = min(copy_size, head_len); if (copy_to_user(data_out, data, len)) goto out; From 7faf89ed5b4c248608ff7471515603970dc331be Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Wed, 17 Jun 2026 17:01:17 +0800 Subject: [PATCH 1868/2758] bpftool: Fix vmlinux BTF leak in cgroup commands [ Upstream commit bda6a7308ef8e79cfbb7d09e48e1c7ffaa522269 ] bpftool cgroup show and tree call libbpf_find_kernel_btf() to resolve attach_btf names, but never release the returned BTF object. For cgroup tree, do_show_tree_fn() is called once for each cgroup visited by nftw(). When more than one cgroup has attached programs, each callback overwrites btf_vmlinux with a new object and loses the previous allocation. Load vmlinux BTF only once during a tree walk and release it when cgroup show or tree completes. Reset btf_vmlinux_id at the same time so batch mode starts with clean state. Fixes: 596f5fb2ea2a ("bpftool: implement cgroup tree for BPF_LSM_CGROUP") Signed-off-by: Yichong Chen Reviewed-by: Quentin Monnet Link: https://lore.kernel.org/r/24357C69B4405079+20260617090117.280222-1-chenyichong@uniontech.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- tools/bpf/bpftool/cgroup.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tools/bpf/bpftool/cgroup.c b/tools/bpf/bpftool/cgroup.c index ec356deb27c9e..ce69d1e5468e3 100644 --- a/tools/bpf/bpftool/cgroup.c +++ b/tools/bpf/bpftool/cgroup.c @@ -78,6 +78,13 @@ static unsigned int query_flags; static struct btf *btf_vmlinux; static __u32 btf_vmlinux_id; +static void free_btf_vmlinux(void) +{ + btf__free(btf_vmlinux); + btf_vmlinux = NULL; + btf_vmlinux_id = 0; +} + static enum bpf_attach_type parse_attach_type(const char *str) { const char *attach_type_str; @@ -388,6 +395,8 @@ static int do_show(int argc, char **argv) if (json_output) jsonw_end_array(json_wtr); + free_btf_vmlinux(); + exit_cgroup: close(cgroup_fd); exit: @@ -437,7 +446,9 @@ static int do_show_tree_fn(const char *fpath, const struct stat *sb, printf("%s\n", fpath); } - btf_vmlinux = libbpf_find_kernel_btf(); + if (!btf_vmlinux) + btf_vmlinux = libbpf_find_kernel_btf(); + for (i = 0; i < ARRAY_SIZE(cgroup_attach_types); i++) show_bpf_progs(cgroup_fd, cgroup_attach_types[i], ftw->level); @@ -540,6 +551,7 @@ static int do_show_tree(int argc, char **argv) if (json_output) jsonw_end_array(json_wtr); + free_btf_vmlinux(); free(cgroup_alloced); return ret; From 8b996c5555750c5538d7aca249bd3d6bb6094580 Mon Sep 17 00:00:00 2001 From: Avinash Duduskar Date: Thu, 18 Jun 2026 04:17:19 +0530 Subject: [PATCH 1869/2758] bpf: zero-initialize the fib lookup flow struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 0dfcb68a6a5ac517b22dff6a1f01cb4f126dfc57 ] bpf_ipv4_fib_lookup() and bpf_ipv6_fib_lookup() build the flow key on the stack with a bare "struct flowi4 fl4;" / "struct flowi6 fl6;" and fill it field by field, but never set flowi4_l3mdev / flowi6_l3mdev. On the non-DIRECT path the lookup goes through the fib rules whenever the netns has custom rules, which a VRF installs: bpf_ipv4_fib_lookup() -> fib_lookup() -> __fib_lookup() -> l3mdev_update_flow() reads !fl->flowi_l3mdev -> fib_rules_lookup() -> fib_rule_match() -> l3mdev_fib_rule_match() uses fl->flowi_l3mdev l3mdev_update_flow() resolves the l3mdev master from the ingress device only while the field is still zero. Left at a nonzero stack value the resolution is skipped, and l3mdev_fib_rule_match() then tests that value as an ifindex, so the VRF master is not resolved and the rule fails to match: an ingress enslaved to a VRF can fail to select its table. FIB rules matching on an L3 master device (l3mdev_fib_rule_iif_match()/ _oif_match()) read the same value, so an "ip rule iif/oif " mismatches the same way. Zero-initialize the whole flow struct rather than adding one more field assignment, so any flowi field added later is covered too. ip_route_input_slow() likewise zeroes the field before its input lookup. CONFIG_INIT_STACK_ALL_ZERO masks this by default, but it depends on compiler support (CC_HAS_AUTO_VAR_INIT_ZERO), so INIT_STACK_NONE builds, including older toolchains that fall back to it, are exposed. Built with INIT_STACK_ALL_PATTERN, a plain bpf_fib_lookup (no VLAN, no DIRECT) over a VRF slave whose destination is routed only in the VRF table returns BPF_FIB_LKUP_RET_NOT_FWDED, and resolves with this patch. On the default config the lookup succeeds either way, so ordinary testing does not catch the bug. Fixes: 40867d74c374 ("net: Add l3mdev index to flow struct and avoid oif reset for port devices") Signed-off-by: Avinash Duduskar Reviewed-by: Toke Høiland-Jørgensen Link: https://lore.kernel.org/r/20260617224719.1428599-1-avinash.duduskar@gmail.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- net/core/filter.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/core/filter.c b/net/core/filter.c index c140e2ed5d291..77984b5fada4c 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -6182,7 +6182,7 @@ static int bpf_ipv4_fib_lookup(struct net *net, struct bpf_fib_lookup *params, struct in_device *in_dev; struct net_device *dev; struct fib_result res; - struct flowi4 fl4; + struct flowi4 fl4 = {}; u32 mtu = 0; int err; @@ -6322,7 +6322,7 @@ static int bpf_ipv6_fib_lookup(struct net *net, struct bpf_fib_lookup *params, struct neighbour *neigh; struct net_device *dev; struct inet6_dev *idev; - struct flowi6 fl6; + struct flowi6 fl6 = {}; int strict = 0; int oif, err; u32 mtu = 0; From d977b2aff9f738a973e39139a6935af35b4062bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thi=C3=A9baud=20Weksteen?= Date: Thu, 18 Jun 2026 14:09:33 +1000 Subject: [PATCH 1870/2758] bpf: Fix BPF_PROG_ASSOC_STRUCT_OPS last field check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 8405c4626460503027461652f96d8bb10c2a9173 ] When struct prog_assoc_struct_ops was added, BPF_PROG_ASSOC_STRUCT_OPS_LAST_FIELD referenced prog_fd instead of the actual last field, flags. Fixes: b5709f6d26d6 ("bpf: Support associating BPF program with struct_ops") Signed-off-by: Thiébaud Weksteen Reviewed-by: Jakub Sitnicki Acked-by: Yonghong Song Link: https://lore.kernel.org/r/20260618040934.4113938-1-tweek@google.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- kernel/bpf/syscall.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index ddf0571f476bf..96d1b797e1523 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -6208,7 +6208,7 @@ static int prog_stream_read(union bpf_attr *attr) return ret; } -#define BPF_PROG_ASSOC_STRUCT_OPS_LAST_FIELD prog_assoc_struct_ops.prog_fd +#define BPF_PROG_ASSOC_STRUCT_OPS_LAST_FIELD prog_assoc_struct_ops.flags static int prog_assoc_struct_ops(union bpf_attr *attr) { From b584f107ab90222bd825dcb4c5977326ff684109 Mon Sep 17 00:00:00 2001 From: Amery Hung Date: Thu, 18 Jun 2026 23:35:19 -0700 Subject: [PATCH 1871/2758] bpf: Fix effective prog array index with BPF_F_PREORDER [ Upstream commit f08aaee3152d0dfc578b3f2586932d82062701dd ] replace_effective_prog() and purge_effective_progs() located the slot in the effective array by walking the program hlist and counting entries linearly. That count does not match the array layout: compute_effective_ progs() places BPF_F_PREORDER programs at the front (ancestor cgroup first, attach order within a cgroup) and the rest after them (descendant cgroup first). So when a preorder program is present, the linear hlist position no longer equals the program's index in the effective array. For replace_effective_prog() (bpf_link_update()) this overwrote the wrong slot, corrupting the effective order. For purge_effective_progs(), it could dummy out a slot belonging to a different program and leave the detached program in the array while bpf_prog_put() drops its reference, i.e. a use-after-free. Fix both by replaying compute_effective_progs()'s placement (including the per-cgroup preorder reversal) in a shared effective_prog_pos() helper. Identify the entry by its struct bpf_prog_list pointer rather than by (prog, link) value, so the lookup resolves to exactly the attachment the syscall selected even when the same bpf_prog is attached to several cgroups in the hierarchy. Fixes: 4b82b181a26c ("bpf: Allow pre-ordering for bpf cgroup progs") Signed-off-by: Amery Hung Acked-by: Yonghong Song Link: https://lore.kernel.org/r/20260619063520.2690547-2-ameryhung@gmail.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- kernel/bpf/cgroup.c | 108 +++++++++++++++++++++++++------------------- 1 file changed, 62 insertions(+), 46 deletions(-) diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index 90921459e2c43..2f87c273e6429 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -939,19 +939,65 @@ static int cgroup_bpf_attach(struct cgroup *cgrp, return ret; } +static int effective_prog_pos(struct cgroup *cgrp, + enum cgroup_bpf_attach_type atype, + struct bpf_prog_list *target_pl) +{ + int cnt = 0, preorder_cnt = 0, fstart, bstart, init_bstart, pos = -1; + struct bpf_prog_list *pl; + struct cgroup *p = cgrp; + + /* count effective programs to find where the preorder region ends */ + do { + if (cnt == 0 || (p->bpf.flags[atype] & BPF_F_ALLOW_MULTI)) + cnt += prog_list_length(&p->bpf.progs[atype], &preorder_cnt); + p = cgroup_parent(p); + } while (p); + + /* replay compute_effective_progs() placement and record target's slot */ + cnt = 0; + p = cgrp; + fstart = preorder_cnt; + bstart = preorder_cnt - 1; + do { + if (cnt > 0 && !(p->bpf.flags[atype] & BPF_F_ALLOW_MULTI)) + continue; + + init_bstart = bstart; + hlist_for_each_entry(pl, &p->bpf.progs[atype], node) { + if (!prog_list_prog(pl)) + continue; + + if (pl->flags & BPF_F_PREORDER) { + if (pl == target_pl) + pos = bstart; + bstart--; + } else { + if (pl == target_pl) + pos = fstart; + fstart++; + } + cnt++; + } + + /* reverse pre-ordering progs at this cgroup level */ + if (pos >= bstart + 1 && pos <= init_bstart) + pos = bstart + 1 + init_bstart - pos; + } while ((p = cgroup_parent(p))); + + return pos; +} + /* Swap updated BPF program for given link in effective program arrays across * all descendant cgroups. This function is guaranteed to succeed. */ static void replace_effective_prog(struct cgroup *cgrp, enum cgroup_bpf_attach_type atype, - struct bpf_cgroup_link *link) + struct bpf_prog_list *pl) { struct bpf_prog_array_item *item; struct cgroup_subsys_state *css; struct bpf_prog_array *progs; - struct bpf_prog_list *pl; - struct hlist_head *head; - struct cgroup *cg; int pos; css_for_each_descendant_pre(css, &cgrp->self) { @@ -960,27 +1006,15 @@ static void replace_effective_prog(struct cgroup *cgrp, if (percpu_ref_is_zero(&desc->bpf.refcnt)) continue; - /* find position of link in effective progs array */ - for (pos = 0, cg = desc; cg; cg = cgroup_parent(cg)) { - if (pos && !(cg->bpf.flags[atype] & BPF_F_ALLOW_MULTI)) - continue; + pos = effective_prog_pos(desc, atype, pl); + if (WARN_ON_ONCE(pos < 0)) + continue; - head = &cg->bpf.progs[atype]; - hlist_for_each_entry(pl, head, node) { - if (!prog_list_prog(pl)) - continue; - if (pl->link == link) - goto found; - pos++; - } - } -found: - BUG_ON(!cg); progs = rcu_dereference_protected( desc->bpf.effective[atype], lockdep_is_held(&cgroup_mutex)); item = &progs->items[pos]; - WRITE_ONCE(item->prog, link->link.prog); + WRITE_ONCE(item->prog, pl->link->link.prog); } } @@ -1024,7 +1058,7 @@ static int __cgroup_bpf_replace(struct cgroup *cgrp, cgrp->bpf.revisions[atype] += 1; old_prog = xchg(&link->link.prog, new_prog); - replace_effective_prog(cgrp, atype, link); + replace_effective_prog(cgrp, atype, pl); bpf_prog_put(old_prog); return 0; } @@ -1091,19 +1125,14 @@ static struct bpf_prog_list *find_detach_entry(struct hlist_head *progs, * recomputing the array in place. * * @cgrp: The cgroup which descendants to travers - * @prog: A program to detach or NULL - * @link: A link to detach or NULL + * @pl: The prog_list entry being detached * @atype: Type of detach operation */ -static void purge_effective_progs(struct cgroup *cgrp, struct bpf_prog *prog, - struct bpf_cgroup_link *link, +static void purge_effective_progs(struct cgroup *cgrp, struct bpf_prog_list *pl, enum cgroup_bpf_attach_type atype) { struct cgroup_subsys_state *css; struct bpf_prog_array *progs; - struct bpf_prog_list *pl; - struct hlist_head *head; - struct cgroup *cg; int pos; /* recompute effective prog array in place */ @@ -1113,24 +1142,11 @@ static void purge_effective_progs(struct cgroup *cgrp, struct bpf_prog *prog, if (percpu_ref_is_zero(&desc->bpf.refcnt)) continue; - /* find position of link or prog in effective progs array */ - for (pos = 0, cg = desc; cg; cg = cgroup_parent(cg)) { - if (pos && !(cg->bpf.flags[atype] & BPF_F_ALLOW_MULTI)) - continue; - - head = &cg->bpf.progs[atype]; - hlist_for_each_entry(pl, head, node) { - if (!prog_list_prog(pl)) - continue; - if (pl->prog == prog && pl->link == link) - goto found; - pos++; - } - } - + pos = effective_prog_pos(desc, atype, pl); /* no link or prog match, skip the cgroup of this layer */ - continue; -found: + if (pos < 0) + continue; + progs = rcu_dereference_protected( desc->bpf.effective[atype], lockdep_is_held(&cgroup_mutex)); @@ -1196,7 +1212,7 @@ static int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog, /* if update effective array failed replace the prog with a dummy prog*/ pl->prog = old_prog; pl->link = link; - purge_effective_progs(cgrp, old_prog, link, atype); + purge_effective_progs(cgrp, pl, atype); } /* now can actually delete it from this cgroup list */ From 7dba66caf98e75c76980f127270e378e533c2ea3 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 18 Jun 2026 10:45:12 +0200 Subject: [PATCH 1872/2758] power: sequencing: fix ABBA deadlock in pwrseq_device_unregister() [ Upstream commit 2d5a7d406ecece5837af1e278ffbbf6c0315560a ] The pwrseq core takes three locks in consistent order everywhere: pwrseq_sem -> pwrseq->rw_lock -> pwrseq->state_lock pwrseq_get() -> pwrseq_match_device() takes pwrseq_sem for reading, then rw_lock for reading. pwrseq_power_on()/pwrseq_power_off() take rw_lock for reading and then state_lock. pwrseq_device_unregister() is the only exception, it takes: state_lock, then rw_lock for writing and finally pwrseq_sem for writing. This created two potential ABBA deadlock situations that sashiko pointed out. - pwrseq_power_on/off() take rw_lock for reading then state_lock, while pwrseq_unregister() takes state_lock then rw_lock for writing - pwrseq_get() takes pwrseq_sem for reading then rw_lock for reading, while pwrseq_unregister() takes rw_lock for writing then pwrseq_sem for writing Reorder the unregister path to taking pwrseq_sem for writing -> rw_lock for writing and drop the state_lock entirely. This is safe as enable_count is only ever written under rw_lock held for read (via pwrseq_unit_enable()/disable(), reached only from pwrseq_power_on/off()), so holding rw_lock for writing already excludes every other writer and reader and the active-users WARN() stays race-free without state_lock. Fixes: 249ebf3f65f8 ("power: sequencing: implement the pwrseq core") Closes: https://sashiko.dev/#/patchset/20260616151049.1705503-1-vulab%40iscas.ac.cn Link: https://patch.msgid.link/20260618-pwrseq-abba-deadlock-v1-1-943a3fd81c06@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/power/sequencing/core.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/power/sequencing/core.c b/drivers/power/sequencing/core.c index 9b512a055b5a4..c12aa494698a6 100644 --- a/drivers/power/sequencing/core.c +++ b/drivers/power/sequencing/core.c @@ -543,15 +543,18 @@ void pwrseq_device_unregister(struct pwrseq_device *pwrseq) struct device *dev = &pwrseq->dev; struct pwrseq_target *target; - scoped_guard(mutex, &pwrseq->state_lock) { + scoped_guard(rwsem_write, &pwrseq_sem) { guard(rwsem_write)(&pwrseq->rw_lock); + /* + * Holding rw_lock for write excludes all power on/off callers + * (they hold it for read), so it's safe to read enable_count + * here without taking the state_lock. + */ list_for_each_entry(target, &pwrseq->targets, list) WARN(target->unit->enable_count, "REMOVING POWER SEQUENCER WITH ACTIVE USERS\n"); - guard(rwsem_write)(&pwrseq_sem); - device_del(dev); } From 6558811274c8c7993fa6da8fa9c0ae9aeedf7006 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Sat, 20 Jun 2026 23:53:19 +0800 Subject: [PATCH 1873/2758] gpiolib: initialize return value in gpiochip_set_multiple() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 99dfa46baba29513d1094c8f30bc86c6ef88543a ] gpiochip_set_multiple() falls back to setting lines one by one when the chip does not provide set_multiple(). If the fallback path receives an empty mask, the loop is skipped and ret is returned without being initialized. Initialize ret to 0 so an empty mask is treated as a successful no-op. Fixes: 9b407312755f ("gpiolib: rework the wrapper around gpio_chip::set_multiple()") Signed-off-by: Ruoyu Wang Acked-by: Uwe Kleine-König Link: https://patch.msgid.link/20260620155319.79994-1-ruoyuw560@gmail.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpiolib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index c1f9c0d367d5c..58479931a6906 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3786,7 +3786,7 @@ static int gpiochip_set_multiple(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits) { unsigned int i; - int ret; + int ret = 0; lockdep_assert_held(&gc->gpiodev->srcu); From 4137e1ecec9c8cb6c4fcee28ffabbbc7409eb7fb Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Mon, 15 Jun 2026 11:47:37 -0700 Subject: [PATCH 1874/2758] drm/edid: fix OOB read in drm_parse_tiled_block() [ Upstream commit faaa1e1155833e7d4ce7e3cfaf64c0d636b190db ] drm_parse_tiled_block() casts the DisplayID block to a struct displayid_tiled_block and reads the full fixed layout up to tile->topology_id[7] without checking block->num_bytes. The DisplayID iterator only validates the declared payload length, so a crafted EDID can advertise a tiled-display block (tag DATA_BLOCK_TILED_DISPLAY, or DATA_BLOCK_2_TILED_DISPLAY_TOPOLOGY for v2.0) with a small num_bytes at the end of a DisplayID extension. The read then runs past the end of the exact-sized kmemdup()'d EDID allocation, a heap out-of-bounds read. Reject blocks shorter than the spec's 22-byte tiled payload before reading the fixed struct, as drm_parse_vesa_mso_data() already does. BUG: KASAN: slab-out-of-bounds in drm_edid_connector_update Read of size 2 at addr ffff888010077700 by task exploit/147 dump_stack_lvl (lib/dump_stack.c:94 ...) print_report (mm/kasan/report.c:378 ...) kasan_report (mm/kasan/report.c:595) drm_edid_connector_update (drivers/gpu/drm/drm_edid.c:7581) bochs_connector_helper_get_modes (drivers/gpu/drm/tiny/bochs.c:574) drm_helper_probe_single_connector_modes (drivers/gpu/drm/drm_probe_helper.c:426) status_store (drivers/gpu/drm/drm_sysfs.c:219) ... vfs_write (fs/read_write.c:595 fs/read_write.c:688) ksys_write (fs/read_write.c:740) Fixes: 40d9b043a89e ("drm/connector: store tile information from displayid (v3)") Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Reviewed-by: Jani Nikula Link: https://patch.msgid.link/20260615184737.899892-1-xmei5@asu.edu Signed-off-by: Jani Nikula Signed-off-by: Sasha Levin --- drivers/gpu/drm/drm_edid.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 404208bf23a6b..df3c25bac761d 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -7575,6 +7575,14 @@ static void drm_parse_tiled_block(struct drm_connector *connector, u8 num_v_tile, num_h_tile; struct drm_tile_group *tg; + /* tiled block payload per spec: cap 1 + topo 3 + size 4 + bezel 5 + id 9 = 22 */ + if (block->num_bytes < 22) { + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] Unexpected tiled block size %u\n", + connector->base.id, connector->name, block->num_bytes); + return; + } + w = tile->tile_size[0] | tile->tile_size[1] << 8; h = tile->tile_size[2] | tile->tile_size[3] << 8; From 50456f445feeeaf05eceb04663217c1040a4dade Mon Sep 17 00:00:00 2001 From: Zhan Xusheng Date: Mon, 22 Jun 2026 16:11:36 +0800 Subject: [PATCH 1875/2758] erofs: handle 48-bit blocks_hi for compressed inodes [ Upstream commit 803d09a554055aba160a62abd1e4b1260b899dc1 ] Combine i_nb.blocks_hi with i_u.blocks_lo when computing inode->i_blocks for compressed inodes, mirroring the startblk_hi handling for unencoded inodes a few lines above. Also evaluate the shift in u64 to avoid truncation. Fixes: efb2aef569b3 ("erofs: add encoded extent on-disk definition") Fixes: 1d191b4ca51d ("erofs: implement encoded extent metadata") Reviewed-by: Gao Xiang Signed-off-by: Zhan Xusheng Signed-off-by: Gao Xiang Signed-off-by: Sasha Levin --- fs/erofs/inode.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/erofs/inode.c b/fs/erofs/inode.c index a188c570087ae..e0c47da4f09e0 100644 --- a/fs/erofs/inode.c +++ b/fs/erofs/inode.c @@ -191,8 +191,9 @@ static int erofs_read_inode(struct inode *inode) err = -EFSCORRUPTED; goto err_out; } else { - inode->i_blocks = le32_to_cpu(copied.i_u.blocks_lo) << - (sb->s_blocksize_bits - 9); + inode->i_blocks = (le32_to_cpu(copied.i_u.blocks_lo) | + ((u64)le16_to_cpu(copied.i_nb.blocks_hi) << 32)) << + (sb->s_blocksize_bits - 9); } if (vi->datalayout == EROFS_INODE_CHUNK_BASED) { From 937f77a7963681254e48f30a369b55a129f9854e Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Wed, 17 Jun 2026 16:47:53 +0200 Subject: [PATCH 1876/2758] ASoC: cs530x: Fix expected MCLK rates for CS5302/4/8 [ Upstream commit cf81b260916c92cf610c907d41627a175e86e37d ] When this driver was first added, it accepted rates of 24.56 MHz and 22.572 MHz for the MCLK when PLL bypass is enabled. These rates seem to have no basis in the datasheets and were thus replaced with 45.1584 MHz and 49.152 MHz, respectively, in commit e7ab858390f2 ("ASoC: cs530x: Correct MCLK reference frequency values"). While the new rates are indeed correct for the CS4xxx ICs[0][1][2][3], they are incorrect for the CS530x ICs the driver was originally written to support as the MCLK frequencies are halved there[4][5][6]. Fix this by checking against the correct type-appropriate rates. While at it, drop the CS530X_SYSCLK_REF_* macros. They arguably confuse more than they help, especially as they are not applicable to the cs5302/4/8. [0]: https://statics.cirrus.com/pubs/proDatasheet/CS4282P_DS1318F1.pdf [1]: https://statics.cirrus.com/pubs/proDatasheet/CS4302P_DS1315F1.pdf [2]: https://statics.cirrus.com/pubs/proDatasheet/CS4304P_DS1316F1.pdf [3]: https://statics.cirrus.com/pubs/proDatasheet/CS4308P_DS1317F1.pdf [4]: https://statics.cirrus.com/pubs/proDatasheet/CS5302P_DS1312F1.pdf [5]: https://statics.cirrus.com/pubs/proDatasheet/CS5304P_DS1313F1.pdf [6]: https://statics.cirrus.com/pubs/proDatasheet/CS5308P_DS1314F1.pdf Fixes: 2884c29152c0 ("ASoC: cs530x: Support for cs530x ADCs") Signed-off-by: Ahmad Fatoum Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20260617-cs530x-mclk-v1-1-0215b5f1a0a4@pengutronix.de Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/cs530x.c | 29 ++++++++++++++++++++++++----- sound/soc/codecs/cs530x.h | 6 ------ 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/sound/soc/codecs/cs530x.c b/sound/soc/codecs/cs530x.c index 18b5ff75feec4..2c7e331359117 100644 --- a/sound/soc/codecs/cs530x.c +++ b/sound/soc/codecs/cs530x.c @@ -1093,6 +1093,29 @@ static int cs530x_component_probe(struct snd_soc_component *component) return 0; } +static bool cs530x_mclk_freq_is_valid(struct cs530x_priv *cs530x, + unsigned int freq) +{ + /* + * All these chips support 48 kHz- and 44.1 kHz-related sample rates, + * but they differ in what MCLK frequency is required for achieving + * the sample rate. + */ + switch (cs530x->devtype) { + case CS4282: + case CS4302: + case CS4304: + case CS4308: + return freq == 49152000 || freq == 45158400; + case CS5302: + case CS5304: + case CS5308: + return freq == 24576000 || freq == 22579200; + } + + return false; +} + static int cs530x_set_sysclk(struct snd_soc_component *component, int clk_id, int source, unsigned int freq, int dir) { @@ -1101,11 +1124,7 @@ static int cs530x_set_sysclk(struct snd_soc_component *component, int clk_id, switch (source) { case CS530X_SYSCLK_SRC_MCLK: - switch (freq) { - case CS530X_SYSCLK_REF_45_1MHZ: - case CS530X_SYSCLK_REF_49_1MHZ: - break; - default: + if (!cs530x_mclk_freq_is_valid(cs530x, freq)) { dev_err(component->dev, "Invalid MCLK source rate %d\n", freq); return -EINVAL; } diff --git a/sound/soc/codecs/cs530x.h b/sound/soc/codecs/cs530x.h index 1e2f6a7a589c1..18aa4dfd0c860 100644 --- a/sound/soc/codecs/cs530x.h +++ b/sound/soc/codecs/cs530x.h @@ -200,12 +200,6 @@ /* IN_VOL_CTL5 and OUT_VOL_CTL5 */ #define CS530X_INOUT_VU BIT(0) -/* MCLK Reference Source Frequency */ -/* 41KHz related */ -#define CS530X_SYSCLK_REF_45_1MHZ 45158400 -/* 48KHz related */ -#define CS530X_SYSCLK_REF_49_1MHZ 49152000 - /* System Clock Source */ #define CS530X_SYSCLK_SRC_MCLK 0 #define CS530X_SYSCLK_SRC_PLL 1 From 750dd7546de3e5a765b530cab002ae7be21fef64 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 7 Apr 2026 18:14:20 +0530 Subject: [PATCH 1877/2758] PCI: endpoint: pci-epf-vntb: Add check to detect 'db_count' value of 0 [ Upstream commit 33bd1ea748bc897c4d13437284e08c658e8d1340 ] epf_ntb->db_count value should be within 1 to MAX_DB_COUNT. Current code only checks for the upper bound, while the lower bound is unchecked. This can cause a lot of issues in the driver if the user passes 'db_count' as 0. Add a check for 0 also. While at it, remove the redundant 'db_count' assignment. Fixes: e35f56bb0330 ("PCI: endpoint: Support NTB transfer between RC and EP") Signed-off-by: Manivannan Sadhasivam Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Koichiro Den Reviewed-by: Frank Li Link: https://patch.msgid.link/20260407124421.282766-2-mani@kernel.org Signed-off-by: Sasha Levin --- drivers/pci/endpoint/functions/pci-epf-vntb.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index 2256c3062b1ae..3d30aa4dbb845 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -483,7 +483,6 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb) { const struct pci_epc_features *epc_features; struct device *dev; - u32 db_count; int ret; dev = &ntb->epf->dev; @@ -495,14 +494,12 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb) return -EINVAL; } - db_count = ntb->db_count; - if (db_count > MAX_DB_COUNT) { - dev_err(dev, "DB count cannot be more than %d\n", MAX_DB_COUNT); + if (!ntb->db_count || ntb->db_count > MAX_DB_COUNT) { + dev_err(dev, "DB count %d out of range (1 - %d)\n", + ntb->db_count, MAX_DB_COUNT); return -EINVAL; } - ntb->db_count = db_count; - if (epc_features->msi_capable) { ret = pci_epc_set_msi(ntb->epf->epc, ntb->epf->func_no, From a58543f1e1cc6edb28c13d5b9aabd04005aa45a2 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 7 Apr 2026 18:14:21 +0530 Subject: [PATCH 1878/2758] PCI: endpoint: pci-epf-ntb: Add check to detect 'db_count' value of 0 [ Upstream commit d1db6d7c2485ee475a04d8354fbb5b26ea10d978 ] epf_ntb->db_count value should be within 1 to MAX_DB_COUNT. Current code only checks for the upper bound, while the lower bound is unchecked. This can cause a lot of issues in the driver if the user passes 'db_count' as 0. Add a check for 0 also. While at it, remove the redundant 'db_count' variable from epf_ntb_configure_interrupt(). Fixes: 8b821cf76150 ("PCI: endpoint: Add EP function driver to provide NTB functionality") Signed-off-by: Manivannan Sadhasivam Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260407124421.282766-3-mani@kernel.org Signed-off-by: Sasha Levin --- drivers/pci/endpoint/functions/pci-epf-ntb.c | 21 ++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-ntb.c b/drivers/pci/endpoint/functions/pci-epf-ntb.c index 2bdcc35b652cf..5314aca2188a0 100644 --- a/drivers/pci/endpoint/functions/pci-epf-ntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-ntb.c @@ -559,12 +559,15 @@ static int epf_ntb_configure_db(struct epf_ntb *ntb, struct pci_epc *epc; int ret; - if (db_count > MAX_DB_COUNT) - return -EINVAL; - ntb_epc = ntb->epc[type]; epc = ntb_epc->epc; + if (!db_count || db_count > MAX_DB_COUNT) { + dev_err(&epc->dev, "DB count %d out of range (1 - %d)\n", + db_count, MAX_DB_COUNT); + return -EINVAL; + } + if (msix) ret = epf_ntb_configure_msix(ntb, type, db_count); else @@ -1278,7 +1281,6 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb, u8 func_no, vfunc_no; struct pci_epc *epc; struct device *dev; - u32 db_count; int ret; ntb_epc = ntb->epc[type]; @@ -1296,17 +1298,16 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb, func_no = ntb_epc->func_no; vfunc_no = ntb_epc->vfunc_no; - db_count = ntb->db_count; - if (db_count > MAX_DB_COUNT) { - dev_err(dev, "DB count cannot be more than %d\n", MAX_DB_COUNT); + if (!ntb->db_count || ntb->db_count > MAX_DB_COUNT) { + dev_err(dev, "DB count %d out of range (1 - %d)\n", + ntb->db_count, MAX_DB_COUNT); return -EINVAL; } - ntb->db_count = db_count; epc = ntb_epc->epc; if (msi_capable) { - ret = pci_epc_set_msi(epc, func_no, vfunc_no, db_count); + ret = pci_epc_set_msi(epc, func_no, vfunc_no, ntb->db_count); if (ret) { dev_err(dev, "%s intf: MSI configuration failed\n", pci_epc_interface_string(type)); @@ -1315,7 +1316,7 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb, } if (msix_capable) { - ret = pci_epc_set_msix(epc, func_no, vfunc_no, db_count, + ret = pci_epc_set_msix(epc, func_no, vfunc_no, ntb->db_count, ntb_epc->msix_bar, ntb_epc->msix_table_offset); if (ret) { From 528cfbcc47bbc84d036d6669585e62c75628f59e Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 13 May 2026 11:49:12 +0900 Subject: [PATCH 1879/2758] PCI: endpoint: pci-epf-vntb: Document legacy MSI doorbell offset [ Upstream commit 6a7db4fcc6c23b1d25e676400d764bdcb7dc1ccb ] vntb_epf_peer_db_set() raises an MSI interrupt to notify the RC side of a doorbell event. pci_epc_raise_irq(..., PCI_IRQ_MSI, interrupt_num) takes a 1-based MSI interrupt number. The ntb_hw_epf driver reserves MSI #1 for link events, so doorbells would naturally start at MSI #2 (doorbell bit 0 -> MSI #2). However, pci-epf-vntb has historically applied an extra offset and mapped doorbell bit 0 to MSI #3. This matches the legacy behavior of ntb_hw_epf and has been preserved since commit e35f56bb0330 ("PCI: endpoint: Support NTB transfer between RC and EP"). This offset has not surfaced as a functional issue because: - ntb_hw_epf typically allocates enough MSI vectors, so the off-by-one still hits a valid MSI vector, and - ntb_hw_epf does not implement .db_vector_count()/.db_vector_mask(), so client drivers such as ntb_transport effectively ignore the vector number and schedule all QPs. Correcting the MSI number would break interoperability with peers running older kernels. Document the legacy offset to avoid confusion when enabling per-db-vector handling. Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Frank Li Link: https://patch.msgid.link/20260513024923.451765-2-den@valinux.co.jp Stable-dep-of: 91fb4488cd61 ("PCI: endpoint: pci-epf-vntb: Report 0-based doorbell vector via ntb_db_event()") Signed-off-by: Sasha Levin --- drivers/pci/endpoint/functions/pci-epf-vntb.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index 3d30aa4dbb845..8a80961916862 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -1364,6 +1364,25 @@ static int vntb_epf_peer_db_set(struct ntb_dev *ndev, u64 db_bits) func_no = ntb->epf->func_no; vfunc_no = ntb->epf->vfunc_no; + /* + * pci_epc_raise_irq() for MSI expects a 1-based interrupt number. + * ffs() returns a 1-based index (bit 0 -> 1). interrupt_num has already + * been computed as ffs(db_bits) + 1 above. Adding one more +1 when + * calling pci_epc_raise_irq() therefore results in: + * + * doorbell bit 0 -> MSI #3 + * + * Legacy mapping (kept for compatibility): + * + * MSI #1 : link event (reserved) + * MSI #2 : unused (historical offset) + * MSI #3 : doorbell bit 0 (DB#0) + * MSI #4 : doorbell bit 1 (DB#1) + * ... + * + * Do not change this mapping to avoid breaking interoperability with + * older peers. + */ ret = pci_epc_raise_irq(ntb->epf->epc, func_no, vfunc_no, PCI_IRQ_MSI, interrupt_num + 1); if (ret) From 93a85a6aca1974f7af89accc5a0220e85d64095a Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 13 May 2026 11:49:13 +0900 Subject: [PATCH 1880/2758] PCI: endpoint: pci-epf-vntb: Defer pci_epc_raise_irq() out of atomic context [ Upstream commit 18355c1e986582aaff2c488b1a2ce79bac8f3cf9 ] The NTB .peer_db_set() callback may be invoked from atomic context. pci-epf-vntb currently calls pci_epc_raise_irq() directly, but pci_epc_raise_irq() may sleep (it takes epc->lock). Avoid sleeping in atomic context by coalescing doorbell bits into an atomic64 pending mask and raising MSIs from a work item. Limit the amount of work per run to avoid monopolizing the workqueue under a doorbell storm. Clear stale pending bits before enabling the work item and after disabling it during cleanup. Also mask requested doorbells against the currently valid doorbell mask before queueing work, and iterate the pending u64 with __ffs64() so high doorbell bits are handled correctly. Fixes: e35f56bb0330 ("PCI: endpoint: Support NTB transfer between RC and EP") Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Frank Li Link: https://patch.msgid.link/20260513024923.451765-3-den@valinux.co.jp Stable-dep-of: 91fb4488cd61 ("PCI: endpoint: pci-epf-vntb: Report 0-based doorbell vector via ntb_db_event()") Signed-off-by: Sasha Levin --- drivers/pci/endpoint/functions/pci-epf-vntb.c | 112 +++++++++++++----- 1 file changed, 85 insertions(+), 27 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index 8a80961916862..314793a1137f0 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -37,6 +37,7 @@ */ #include +#include #include #include #include @@ -69,6 +70,9 @@ static struct workqueue_struct *kpcintb_workqueue; #define MAX_DB_COUNT 32 #define MAX_MW 4 +/* Limit per-work execution to avoid monopolizing kworker on doorbell storms. */ +#define VNTB_PEER_DB_WORK_BUDGET 5 + enum epf_ntb_bar { BAR_CONFIG, BAR_DB, @@ -129,6 +133,8 @@ struct epf_ntb { u32 spad_count; u64 mws_size[MAX_MW]; atomic64_t db; + atomic64_t peer_db_pending; + struct work_struct peer_db_work; u32 vbus_number; u16 vntb_pid; u16 vntb_vid; @@ -917,6 +923,9 @@ static int epf_ntb_epc_init(struct epf_ntb *ntb) INIT_DELAYED_WORK(&ntb->cmd_handler, epf_ntb_cmd_handler); queue_work(kpcintb_workqueue, &ntb->cmd_handler.work); + atomic64_set(&ntb->peer_db_pending, 0); + enable_work(&ntb->peer_db_work); + return 0; err_write_header: @@ -940,6 +949,8 @@ static int epf_ntb_epc_init(struct epf_ntb *ntb) static void epf_ntb_epc_cleanup(struct epf_ntb *ntb) { disable_delayed_work_sync(&ntb->cmd_handler); + disable_work_sync(&ntb->peer_db_work); + atomic64_set(&ntb->peer_db_pending, 0); epf_ntb_mw_bar_clear(ntb, ntb->num_mws); epf_ntb_db_bar_clear(ntb); epf_ntb_config_sspad_bar_clear(ntb); @@ -1354,41 +1365,84 @@ static int vntb_epf_peer_spad_write(struct ntb_dev *ndev, int pidx, int idx, u32 return 0; } -static int vntb_epf_peer_db_set(struct ntb_dev *ndev, u64 db_bits) +static void vntb_epf_peer_db_work(struct work_struct *work) { - u32 interrupt_num = ffs(db_bits) + 1; - struct epf_ntb *ntb = ntb_ndev(ndev); + struct epf_ntb *ntb = container_of(work, struct epf_ntb, peer_db_work); + struct pci_epf *epf = ntb->epf; + unsigned int budget = VNTB_PEER_DB_WORK_BUDGET; u8 func_no, vfunc_no; + unsigned int db_bit; + u32 interrupt_num; + u64 db_bits; int ret; - func_no = ntb->epf->func_no; - vfunc_no = ntb->epf->vfunc_no; + if (!epf || !epf->epc) + return; + + func_no = epf->func_no; + vfunc_no = epf->vfunc_no; /* - * pci_epc_raise_irq() for MSI expects a 1-based interrupt number. - * ffs() returns a 1-based index (bit 0 -> 1). interrupt_num has already - * been computed as ffs(db_bits) + 1 above. Adding one more +1 when - * calling pci_epc_raise_irq() therefore results in: - * - * doorbell bit 0 -> MSI #3 - * - * Legacy mapping (kept for compatibility): - * - * MSI #1 : link event (reserved) - * MSI #2 : unused (historical offset) - * MSI #3 : doorbell bit 0 (DB#0) - * MSI #4 : doorbell bit 1 (DB#1) - * ... - * - * Do not change this mapping to avoid breaking interoperability with - * older peers. + * Drain doorbells from peer_db_pending in snapshots (atomic64_xchg()). + * Limit the number of snapshots handled per run so we don't monopolize + * the workqueue under a doorbell storm. */ - ret = pci_epc_raise_irq(ntb->epf->epc, func_no, vfunc_no, - PCI_IRQ_MSI, interrupt_num + 1); - if (ret) - dev_err(&ntb->ntb.dev, "Failed to raise IRQ\n"); + while (budget--) { + db_bits = atomic64_xchg(&ntb->peer_db_pending, 0); + if (!db_bits) + return; + + while (db_bits) { + /* + * pci_epc_raise_irq() for MSI expects a 1-based + * interrupt number. db_bit is zero-based, so add 3 to + * preserve the historical slot offset. + * + * Legacy mapping (kept for compatibility): + * + * MSI #1 : link event (reserved) + * MSI #2 : unused (historical offset) + * MSI #3 : doorbell bit 0 (DB#0) + * MSI #4 : doorbell bit 1 (DB#1) + * ... + * + * Do not change this mapping to avoid breaking + * interoperability with older peers. + */ + db_bit = __ffs64(db_bits); + interrupt_num = db_bit + 3; + db_bits &= ~BIT_ULL(db_bit); + + ret = pci_epc_raise_irq(epf->epc, func_no, vfunc_no, + PCI_IRQ_MSI, interrupt_num); + if (ret) + dev_err(&ntb->ntb.dev, + "Failed to raise IRQ for interrupt_num %u: %d\n", + interrupt_num, ret); + } + } - return ret; + if (atomic64_read(&ntb->peer_db_pending)) + queue_work(kpcintb_workqueue, &ntb->peer_db_work); +} + +static int vntb_epf_peer_db_set(struct ntb_dev *ndev, u64 db_bits) +{ + struct epf_ntb *ntb = ntb_ndev(ndev); + + db_bits &= vntb_epf_db_valid_mask(ndev); + if (!db_bits) + return 0; + + /* + * .peer_db_set() may be called from atomic context. pci_epc_raise_irq() + * can sleep (it takes epc->lock), so defer MSI raising to process + * context. Doorbell requests are coalesced in peer_db_pending. + */ + atomic64_or(db_bits, &ntb->peer_db_pending); + queue_work(kpcintb_workqueue, &ntb->peer_db_work); + + return 0; } static u64 vntb_epf_db_read(struct ntb_dev *ndev) @@ -1635,6 +1689,10 @@ static int epf_ntb_probe(struct pci_epf *epf, ntb->epf = epf; ntb->vbus_number = 0xff; + INIT_WORK(&ntb->peer_db_work, vntb_epf_peer_db_work); + disable_work(&ntb->peer_db_work); + atomic64_set(&ntb->peer_db_pending, 0); + /* Initially, no bar is assigned */ for (i = 0; i < VNTB_BAR_NUM; i++) ntb->epf_ntb_bar[i] = NO_BAR; From 233170ad54d339a6e21d7bfe5113f1e14227c453 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 13 May 2026 11:49:14 +0900 Subject: [PATCH 1881/2758] PCI: endpoint: pci-epf-vntb: Report 0-based doorbell vector via ntb_db_event() [ Upstream commit 91fb4488cd615a39360bc4160a10cb3236189ba1 ] ntb_db_event() expects the vector number to be relative to the first doorbell vector starting at 0. pci-epf-vntb reserves vector 0 for link events and uses higher vector indices for doorbells. By passing the raw slot index to ntb_db_event(), it effectively assumes that doorbell 0 maps to vector 1. However, because the host uses a legacy slot layout and writes doorbell 0 into the third slot, doorbell 0 ultimately appears as vector 2 from the NTB core perspective. Adjust pci-epf-vntb to: - skip the unused second slot, and - report doorbells as 0-based vectors (DB#0 -> vector 0). This change does not introduce a behavioral difference until .db_vector_count()/.db_vector_mask() are implemented, because without those callbacks NTB clients effectively ignore the vector number. Fixes: e35f56bb0330 ("PCI: endpoint: Support NTB transfer between RC and EP") Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Frank Li Link: https://patch.msgid.link/20260513024923.451765-4-den@valinux.co.jp Signed-off-by: Sasha Levin --- drivers/pci/endpoint/functions/pci-epf-vntb.c | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index 314793a1137f0..01d28a7d35df4 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -83,6 +83,12 @@ enum epf_ntb_bar { VNTB_BAR_NUM, }; +enum epf_irq_slot { + EPF_IRQ_LINK = 0, + EPF_IRQ_RESERVED_DB, /* Historically skipped slot */ + EPF_IRQ_DB_START, +}; + /* * +--------------------------------------------------+ Base * | | @@ -267,10 +273,11 @@ static void epf_ntb_cmd_handler(struct work_struct *work) ntb = container_of(work, struct epf_ntb, cmd_handler.work); - for (i = 1; i < ntb->db_count && !ntb->msi_doorbell; i++) { + for (i = EPF_IRQ_DB_START; i < ntb->db_count && !ntb->msi_doorbell; + i++) { if (ntb->epf_db[i]) { - atomic64_or(1 << (i - 1), &ntb->db); - ntb_db_event(&ntb->ntb, i); + atomic64_or(1 << (i - EPF_IRQ_DB_START), &ntb->db); + ntb_db_event(&ntb->ntb, i - EPF_IRQ_DB_START); ntb->epf_db[i] = 0; } } @@ -336,10 +343,10 @@ static irqreturn_t epf_ntb_doorbell_handler(int irq, void *data) struct epf_ntb *ntb = data; int i; - for (i = 1; i < ntb->db_count; i++) + for (i = EPF_IRQ_DB_START; i < ntb->db_count; i++) if (irq == ntb->epf->db_msg[i].virq) { - atomic64_or(1 << (i - 1), &ntb->db); - ntb_db_event(&ntb->ntb, i); + atomic64_or(1 << (i - EPF_IRQ_DB_START), &ntb->db); + ntb_db_event(&ntb->ntb, i - EPF_IRQ_DB_START); } return IRQ_HANDLED; @@ -1395,8 +1402,8 @@ static void vntb_epf_peer_db_work(struct work_struct *work) while (db_bits) { /* * pci_epc_raise_irq() for MSI expects a 1-based - * interrupt number. db_bit is zero-based, so add 3 to - * preserve the historical slot offset. + * interrupt number. The first usable doorbell starts + * at EPF_IRQ_DB_START in the legacy slot layout. * * Legacy mapping (kept for compatibility): * @@ -1410,7 +1417,7 @@ static void vntb_epf_peer_db_work(struct work_struct *work) * interoperability with older peers. */ db_bit = __ffs64(db_bits); - interrupt_num = db_bit + 3; + interrupt_num = db_bit + EPF_IRQ_DB_START + 1; db_bits &= ~BIT_ULL(db_bit); ret = pci_epc_raise_irq(epf->epc, func_no, vfunc_no, From 0f9278b22cda6fd2525049930157b79b4036b4ef Mon Sep 17 00:00:00 2001 From: Nuoqi Gui Date: Wed, 17 Jun 2026 23:20:20 +0800 Subject: [PATCH 1882/2758] bpf: Preserve pointer spill metadata during half-slot cleanup [ Upstream commit 3a354149bceacadbcf7d7b4766f5ef26a85892ab ] __clean_func_state() cleans dead stack slots in 4-byte halves. When the high half of a STACK_SPILL slot is dead and the low half remains live, cleanup converts the live low half to STACK_MISC or STACK_ZERO and clears the saved spilled_ptr metadata. That conversion is safe only for scalar spills. For a pointer spill, this metadata clear lets a later 32-bit fill from the still-live half avoid the normal non-scalar register-fill check and be treated as an ordinary scalar stack read. Leave non-scalar spill slots intact in this half-live shape. This is conservative for pruning and preserves the existing check_stack_read_fixed_off() rejection path for partial fills from pointer spills. Fixes: be23266b4a08 ("bpf: 4-byte precise clean_verifier_state") Acked-by: Eduard Zingerman Signed-off-by: Nuoqi Gui Link: https://lore.kernel.org/r/20260617-f01-06-half-slot-pointer-spill-v2-1-42b9cdc3cf64@mails.tsinghua.edu.cn Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- kernel/bpf/states.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/kernel/bpf/states.c b/kernel/bpf/states.c index 8478d2c6ed5b6..1dd99b3194d7c 100644 --- a/kernel/bpf/states.c +++ b/kernel/bpf/states.c @@ -437,12 +437,10 @@ static void __clean_func_state(struct bpf_verifier_env *env, continue; /* - * Only destroy spilled_ptr when hi half is dead. - * If hi half is still live with STACK_SPILL, the - * spilled_ptr metadata is needed for correct state - * comparison in stacksafe(). - * is_spilled_reg() is using slot_type[7], but - * is_spilled_scalar_after() check either slot_type[0] or [4] + * Only scalar spills can be degraded to raw stack bytes + * when their high half is dead. Pointer spills need the + * saved spilled_ptr metadata so partial fills keep + * rejecting as non-scalar register fills. */ if (!hi_live) { struct bpf_reg_state *spill = &st->stack[i].spilled_ptr; @@ -450,6 +448,9 @@ static void __clean_func_state(struct bpf_verifier_env *env, if (lo_live && stype == STACK_SPILL) { u8 val = STACK_MISC; + if (spill->type != SCALAR_VALUE) + continue; + /* * 8 byte spill of scalar 0 where half slot is dead * should become STACK_ZERO in lo 4 bytes. From b1fc5bafbc5f84df457b6f987ac993e0802f8d93 Mon Sep 17 00:00:00 2001 From: Dawid Osuchowski Date: Fri, 27 Mar 2026 08:22:32 +0100 Subject: [PATCH 1883/2758] ice: fix FDIR CTRL VSI resource leak in ice_reset_all_vfs() [ Upstream commit ebbe8868cf473f698e0fbaf436d2618b2bcda806 ] Resetting all VFs causes resource leak on VFs with FDIR filters enabled as CTRL VSIs are only invalidated and not freed. Fix by using ice_vf_ctrl_vsi_release() instead of ice_vf_ctrl_invalidate_vsi() which aligns behavior with the ice_reset_vf() function. Reproduction: echo 1 > /sys/class/net/$pf/device/sriov_numvfs ethtool -N $vf flow-type ether proto 0x9000 action 0 echo 1 > /sys/class/net/$pf/device/reset Fixes: da62c5ff9dcd ("ice: Add support for per VF ctrl VSI enabling") Signed-off-by: Dawid Osuchowski Signed-off-by: Aleksandr Loktionov Reviewed-by: Simon Horman Tested-by: Rafal Romanowski Signed-off-by: Tony Nguyen Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/ice/ice_vf_lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.c b/drivers/net/ethernet/intel/ice/ice_vf_lib.c index b1f46707dcc00..27e4acb1620f0 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.c @@ -801,7 +801,7 @@ void ice_reset_all_vfs(struct ice_pf *pf) * setup only when VF creates its first FDIR rule. */ if (vf->ctrl_vsi_idx != ICE_NO_VSI) - ice_vf_ctrl_invalidate_vsi(vf); + ice_vf_ctrl_vsi_release(vf); ice_vf_pre_vsi_rebuild(vf); if (ice_vf_rebuild_vsi(vf)) { From 4f13a0a479b55d23e3f0b111efdb24b379bf80f0 Mon Sep 17 00:00:00 2001 From: Lukasz Czapnik Date: Fri, 27 Mar 2026 08:22:35 +0100 Subject: [PATCH 1884/2758] ice: fix AQ error code comparison in ice_set_pauseparam() [ Upstream commit 2bf7744bc3221a63b95c76c94eab1dad832fa401 ] Fix unreachable code: the conditionals in ice_set_pauseparam() used the bitwise-AND operator suggesting aq_failures is a bitmap, but it is actually an enum, making the third condition logically unreachable. Replace the if-else ladder with a switch statement. Also move the aq_failures initialization to the variable declaration and remove the redundant zeroing from ice_set_fc(). Fixes: fcea6f3da546 ("ice: Add stats and ethtool support") Signed-off-by: Lukasz Czapnik Signed-off-by: Aleksandr Loktionov Reviewed-by: Simon Horman Tested-by: Rinitha S (A Contingent worker at Intel) Signed-off-by: Tony Nguyen Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/ice/ice_common.c | 1 - drivers/net/ethernet/intel/ice/ice_ethtool.c | 12 ++++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c index b617a6bff8913..98960d3cbdd1d 100644 --- a/drivers/net/ethernet/intel/ice/ice_common.c +++ b/drivers/net/ethernet/intel/ice/ice_common.c @@ -3882,7 +3882,6 @@ ice_set_fc(struct ice_port_info *pi, u8 *aq_failures, bool ena_auto_link_update) if (!pi || !aq_failures) return -EINVAL; - *aq_failures = 0; hw = pi->hw; pcaps = kzalloc_obj(*pcaps); diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c index f28416a707d77..cf6f3bd29f840 100644 --- a/drivers/net/ethernet/intel/ice/ice_ethtool.c +++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c @@ -3508,7 +3508,7 @@ ice_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause) struct ice_vsi *vsi = np->vsi; struct ice_hw *hw = &pf->hw; struct ice_port_info *pi; - u8 aq_failures; + u8 aq_failures = 0; bool link_up; u32 is_an; int err; @@ -3579,18 +3579,22 @@ ice_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause) /* Set the FC mode and only restart AN if link is up */ err = ice_set_fc(pi, &aq_failures, link_up); - if (aq_failures & ICE_SET_FC_AQ_FAIL_GET) { + switch (aq_failures) { + case ICE_SET_FC_AQ_FAIL_GET: netdev_info(netdev, "Set fc failed on the get_phy_capabilities call with err %d aq_err %s\n", err, libie_aq_str(hw->adminq.sq_last_status)); err = -EAGAIN; - } else if (aq_failures & ICE_SET_FC_AQ_FAIL_SET) { + break; + case ICE_SET_FC_AQ_FAIL_SET: netdev_info(netdev, "Set fc failed on the set_phy_config call with err %d aq_err %s\n", err, libie_aq_str(hw->adminq.sq_last_status)); err = -EAGAIN; - } else if (aq_failures & ICE_SET_FC_AQ_FAIL_UPDATE) { + break; + case ICE_SET_FC_AQ_FAIL_UPDATE: netdev_info(netdev, "Set fc failed on the get_link_info call with err %d aq_err %s\n", err, libie_aq_str(hw->adminq.sq_last_status)); err = -EAGAIN; + break; } return err; From 6c70914ab629957ec0f60be11d642ad7b01c17b6 Mon Sep 17 00:00:00 2001 From: Marcin Szycik Date: Wed, 8 Apr 2026 16:14:29 +0200 Subject: [PATCH 1885/2758] ice: call netif_keep_dst() once when entering switchdev mode [ Upstream commit c0d00c882bc432990d57052a659f9a8bd1f60687 ] netif_keep_dst() only needs to be called once for the uplink VSI, not once for each port representor. Move it from ice_eswitch_setup_repr() to ice_eswitch_enable_switchdev(). Fixes: defd52455aee ("ice: do Tx through PF netdev in slow-path") Signed-off-by: Marcin Szycik Signed-off-by: Aleksandr Loktionov Reviewed-by: Paul Menzel Tested-by: Patryk Holda Signed-off-by: Tony Nguyen Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/ice/ice_eswitch.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_eswitch.c b/drivers/net/ethernet/intel/ice/ice_eswitch.c index 2e4f0969035f7..c30e27bbfe6e2 100644 --- a/drivers/net/ethernet/intel/ice/ice_eswitch.c +++ b/drivers/net/ethernet/intel/ice/ice_eswitch.c @@ -117,8 +117,6 @@ static int ice_eswitch_setup_repr(struct ice_pf *pf, struct ice_repr *repr) if (!repr->dst) return -ENOMEM; - netif_keep_dst(uplink_vsi->netdev); - dst = repr->dst; dst->u.port_info.port_id = vsi->vsi_num; dst->u.port_info.lower_dev = uplink_vsi->netdev; @@ -312,6 +310,8 @@ static int ice_eswitch_enable_switchdev(struct ice_pf *pf) if (ice_eswitch_br_offloads_init(pf)) goto err_br_offloads; + netif_keep_dst(uplink_vsi->netdev); + pf->eswitch.is_running = true; return 0; From 19ec35b79913f6c43bea23d8d117415786a1cdb8 Mon Sep 17 00:00:00 2001 From: John Madieu Date: Sat, 25 Apr 2026 15:49:59 +0000 Subject: [PATCH 1886/2758] rtc: isl1208: Balance enable_irq_wake() with disable_irq_wake() on cleanup [ Upstream commit 1afe4f19d6ad404621150f0e91feeccf12fb1037 ] isl1208_setup_irq() calls enable_irq_wake() after a successful IRQ request, but the driver has no remove path that balances it. The driver is devm-only, so on unbind devm releases the IRQ - but enable_irq_wake() is not undone by IRQ release, so the wake count for that IRQ stays incremented. Each rebind therefore leaks one wake reference; the leak doubles for the chip variant that has a separate evdet IRQ, since isl1208_setup_irq() is then called twice during probe. Register a devm action that calls disable_irq_wake() per IRQ. While at it, check enable_irq_wake()'s return value: on failure, propagate the error rather than silently registering a disable action for an IRQ whose wake state was never enabled. Fixes: 9ece7cd833a3 ("rtc: isl1208: Add "evdet" interrupt source for isl1219") Signed-off-by: John Madieu Link: https://patch.msgid.link/20260425154959.2796261-3-john.madieu.xa@bp.renesas.com Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/rtc/rtc-isl1208.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c index f71a6bb77b2a1..a0df53991fbdb 100644 --- a/drivers/rtc/rtc-isl1208.c +++ b/drivers/rtc/rtc-isl1208.c @@ -822,6 +822,11 @@ static const struct nvmem_config isl1208_nvmem_config = { .reg_write = isl1208_nvmem_write, }; +static void isl1208_disable_irq_wake_action(void *data) +{ + disable_irq_wake((unsigned long)data); +} + static int isl1208_setup_irq(struct i2c_client *client, int irq) { int rc = devm_request_threaded_irq(&client->dev, irq, NULL, @@ -831,7 +836,15 @@ static int isl1208_setup_irq(struct i2c_client *client, int irq) client); if (!rc) { device_init_wakeup(&client->dev, true); - enable_irq_wake(irq); + rc = enable_irq_wake(irq); + if (rc) + return rc; + + rc = devm_add_action_or_reset(&client->dev, + isl1208_disable_irq_wake_action, + (void *)(unsigned long)irq); + if (rc) + return rc; } else { dev_err(&client->dev, "Unable to request irq %d, no alarm support\n", From 17c0a9db05e3fa93ecf49a143e1316c6ea0422b2 Mon Sep 17 00:00:00 2001 From: ZhaoJinming Date: Fri, 29 May 2026 13:37:32 +0800 Subject: [PATCH 1887/2758] ice: dpll: set pointers to NULL after kfree in ice_dpll_deinit_info [ Upstream commit a903afff66d7379c6ece42bd18b2a17f4c79d1a9 ] ice_dpll_deinit_info() calls kfree() on several pf->dplls fields (inputs, outputs, eec.input_prio, pps.input_prio) but does not set the pointers to NULL afterward. This leaves dangling pointers in the pf->dplls structure. While not currently exploitable through existing code paths, this is unsafe because: 1. If ice_dpll_init_info() is called again after a deinit (e.g. during driver recovery), and a subsequent allocation within init fails, the error path will jump to deinit_info and call ice_dpll_deinit_info() again. Since some pointers still hold the old freed addresses, this would result in a double-free. 2. Any future code that checks these pointers before use or after free would be unprotected against use-after-free. Follow the common kernel convention of setting pointers to NULL after kfree() so that: - kfree(NULL) is a safe no-op, preventing double-free - NULL checks on these pointers become meaningful This is a preparatory fix for a subsequent patch that routes additional error paths in ice_dpll_init_info() to the deinit_info label. Fixes: d7999f5ea64b ("ice: implement dpll interface to control cgu") Signed-off-by: ZhaoJinming Reviewed-by: Aleksandr Loktionov Tested-by: Rinitha S (A Contingent worker at Intel) Signed-off-by: Tony Nguyen Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/ice/ice_dpll.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c index 0704e92ab0430..ff5053abb854b 100644 --- a/drivers/net/ethernet/intel/ice/ice_dpll.c +++ b/drivers/net/ethernet/intel/ice/ice_dpll.c @@ -4249,9 +4249,13 @@ ice_dpll_init_pins_info(struct ice_pf *pf, enum ice_dpll_pin_type pin_type) static void ice_dpll_deinit_info(struct ice_pf *pf) { kfree(pf->dplls.inputs); + pf->dplls.inputs = NULL; kfree(pf->dplls.outputs); + pf->dplls.outputs = NULL; kfree(pf->dplls.eec.input_prio); + pf->dplls.eec.input_prio = NULL; kfree(pf->dplls.pps.input_prio); + pf->dplls.pps.input_prio = NULL; } /** From 40c68e35e700f43197145e3a1d96c298a28aacfe Mon Sep 17 00:00:00 2001 From: ZhaoJinming Date: Fri, 29 May 2026 13:37:33 +0800 Subject: [PATCH 1888/2758] ice: dpll: fix memory leak in ice_dpll_init_info error paths [ Upstream commit 20da495f2df0fd4adc435d3d621366e8c807539c ] Several error return paths in ice_dpll_init_info() directly return without freeing previously allocated resources, causing memory leaks: - When de->input_prio allocation fails, d->inputs is leaked - When dp->input_prio allocation fails, d->inputs and de->input_prio are leaked - When ice_get_cgu_rclk_pin_info() fails, all previously allocated inputs/outputs/input_prio are leaked - When ice_dpll_init_pins_info(RCLK_INPUT) fails, same resources are leaked Fix this by jumping to the deinit_info label which properly calls ice_dpll_deinit_info() to free all allocated resources. Fixes: d7999f5ea64b ("ice: implement dpll interface to control cgu") Signed-off-by: ZhaoJinming Reviewed-by: Aleksandr Loktionov Tested-by: Rinitha S (A Contingent worker at Intel) Signed-off-by: Tony Nguyen Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/ice/ice_dpll.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c index ff5053abb854b..5f6f29142e301 100644 --- a/drivers/net/ethernet/intel/ice/ice_dpll.c +++ b/drivers/net/ethernet/intel/ice/ice_dpll.c @@ -4347,12 +4347,16 @@ static int ice_dpll_init_info(struct ice_pf *pf, bool cgu) alloc_size = sizeof(*de->input_prio) * d->num_inputs; de->input_prio = kzalloc(alloc_size, GFP_KERNEL); - if (!de->input_prio) - return -ENOMEM; + if (!de->input_prio) { + ret = -ENOMEM; + goto deinit_info; + } dp->input_prio = kzalloc(alloc_size, GFP_KERNEL); - if (!dp->input_prio) - return -ENOMEM; + if (!dp->input_prio) { + ret = -ENOMEM; + goto deinit_info; + } ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_INPUT); if (ret) @@ -4377,12 +4381,12 @@ static int ice_dpll_init_info(struct ice_pf *pf, bool cgu) ret = ice_get_cgu_rclk_pin_info(&pf->hw, &d->base_rclk_idx, &pf->dplls.rclk.num_parents); if (ret) - return ret; + goto deinit_info; for (i = 0; i < pf->dplls.rclk.num_parents; i++) pf->dplls.rclk.parent_idx[i] = d->base_rclk_idx + i; ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_RCLK_INPUT); if (ret) - return ret; + goto deinit_info; de->mode = DPLL_MODE_AUTOMATIC; dp->mode = DPLL_MODE_AUTOMATIC; From 939756efe5056cc78cba7878179652ae34c0579d Mon Sep 17 00:00:00 2001 From: Mohamed Khalfella Date: Wed, 6 May 2026 15:41:23 -0700 Subject: [PATCH 1889/2758] i40e: Fix i40e_debug() to use struct i40e_hw argument [ Upstream commit 798f94603eb0737033861efd320a9159f382a7c5 ] i40e_debug() macro takes struct i40e_hw *h as first argument. But the macro body uses hw instead of h. This has been working so far because hw happens to be the name of the variable in the context where the macro is expanded. Fix the macro to use the passed argument. Fixes: 5dfd37c37a44 ("i40e: Split i40e_osdep.h") Signed-off-by: Mohamed Khalfella Reviewed-by: Aleksandr Loktionov Reviewed-by: Paul Menzel Tested-by: Alexander Nowlin Signed-off-by: Tony Nguyen Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/i40e/i40e_debug.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_debug.h b/drivers/net/ethernet/intel/i40e/i40e_debug.h index e9871dfb32bd4..01fd70db90866 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_debug.h +++ b/drivers/net/ethernet/intel/i40e/i40e_debug.h @@ -42,7 +42,7 @@ struct device *i40e_hw_to_dev(struct i40e_hw *hw); #define i40e_debug(h, m, s, ...) \ do { \ if (((m) & (h)->debug_mask)) \ - dev_info(i40e_hw_to_dev(hw), s, ##__VA_ARGS__); \ + dev_info(i40e_hw_to_dev(h), s, ##__VA_ARGS__); \ } while (0) #endif /* _I40E_DEBUG_H_ */ From 5f2cfe30af5a481b46d5eaea6a500e475b97e348 Mon Sep 17 00:00:00 2001 From: Dima Ruinskiy Date: Fri, 17 Apr 2026 13:43:30 +0300 Subject: [PATCH 1890/2758] e1000e: Reconfigure PLL clock gate timeout and re-enable K1 on Meteor Lake [ Upstream commit 578294b8b60d2c630991f221838f9ec61ae89df0 ] Commit 3c7bf5af21960 ("e1000e: Introduce private flag to disable K1") disabled K1 by default on Meteor Lake and newer systems due to packet loss observed on various platforms. However, disabling K1 caused an increase in power consumption. To mitigate this, reconfigure the PLL clock gate value so that K1 can remain enabled without incurring the additional power consumption. Re-enable K1 by default, but keep the private flag to support disabling it via ethtool. Additionally, introduce a DMI quirk table, so that K1 may be disabled by default on known problematic systems. Currently, this includes the Dell Pro 16 Plus, where the issue has been reported to persist despite the changes to the PLL lock timeout. Link: https://bugzilla.kernel.org/show_bug.cgi?id=220954 Link: https://lists.osuosl.org/pipermail/intel-wired-lan/Week-of-Mon-20250623/048860.html Link: https://lists.osuosl.org/pipermail/intel-wired-lan/Week-of-Mon-20260330/054059.html Signed-off-by: Dima Ruinskiy Co-developed-by: Vitaly Lifshits Signed-off-by: Vitaly Lifshits Fixes: 3c7bf5af21960 ("e1000e: Introduce private flag to disable K1") Tested-by: Moriya Kadosh Tested-by: Todd Brandt Signed-off-by: Tony Nguyen Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/e1000e/ich8lan.c | 3 +++ drivers/net/ethernet/intel/e1000e/netdev.c | 15 ++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c index dea208db1be5b..aa90e0ce8acaf 100644 --- a/drivers/net/ethernet/intel/e1000e/ich8lan.c +++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c @@ -1594,6 +1594,9 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw) phy_reg &= ~I217_PLL_CLOCK_GATE_MASK; if (speed == SPEED_100 || speed == SPEED_10) phy_reg |= 0x3E8; + else if (hw->mac.type == e1000_pch_mtp || + hw->mac.type == e1000_pch_ptp) + phy_reg |= 0x1D5; else phy_reg |= 0xFA; e1e_wphy_locked(hw, I217_PLL_CLOCK_GATE_REG, phy_reg); diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 7ce0cc8ab8f4c..7612ddd066fe5 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "e1000.h" #define CREATE_TRACE_POINTS @@ -58,6 +59,17 @@ static const struct e1000_info *e1000_info_tbl[] = { [board_pch_ptp] = &e1000_pch_ptp_info, }; +static const struct dmi_system_id disable_k1_list[] = { + { + .ident = "Dell Pro 16 Plus PB16250", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Dell Pro 16 Plus PB16250"), + }, + }, + {} +}; + struct e1000_reg_info { u32 ofs; char *name; @@ -7670,7 +7682,8 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* init PTP hardware clock */ e1000e_ptp_init(adapter); - if (hw->mac.type >= e1000_pch_mtp) + /* disable K1 by default on known problematic systems */ + if (hw->mac.type >= e1000_pch_mtp && dmi_check_system(disable_k1_list)) adapter->flags2 |= FLAG2_DISABLE_K1; /* reset the hardware with the new settings */ From bb3e624808c981fe881cb79037be63e680ed7d38 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Mon, 11 May 2026 08:27:03 +0500 Subject: [PATCH 1891/2758] rtc: msc313: fix NULL deref in shared IRQ handler at probe [ Upstream commit a369f48be8de426a7d2bca18dbd46c2ad1138803 ] msc313_rtc_probe() calls devm_request_irq() with IRQF_SHARED and &pdev->dev as the cookie, but platform_set_drvdata() is only called later after the clock setup. With a shared IRQ line, another device on the same line can trigger the handler in that window. The handler does dev_get_drvdata() on the cookie, gets NULL, and dereferences priv->rtc_base in interrupt context. Pass priv as the cookie directly so the handler reads it from dev_id without the lookup, removing the dependency on probe order. Fixes: be7d9c9161b9 ("rtc: Add support for the MSTAR MSC313 RTC") Signed-off-by: Stepan Ionichev Link: https://patch.msgid.link/20260511032703.48262-1-sozdayvek@gmail.com Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/rtc/rtc-msc313.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/rtc/rtc-msc313.c b/drivers/rtc/rtc-msc313.c index 8d7737e0e2e02..6ef9c4efd7c92 100644 --- a/drivers/rtc/rtc-msc313.c +++ b/drivers/rtc/rtc-msc313.c @@ -160,7 +160,7 @@ static const struct rtc_class_ops msc313_rtc_ops = { static irqreturn_t msc313_rtc_interrupt(s32 irq, void *dev_id) { - struct msc313_rtc *priv = dev_get_drvdata(dev_id); + struct msc313_rtc *priv = dev_id; u16 reg; reg = readw(priv->rtc_base + REG_RTC_STATUS_INT); @@ -206,7 +206,7 @@ static int msc313_rtc_probe(struct platform_device *pdev) priv->rtc_dev->range_max = U32_MAX; ret = devm_request_irq(dev, irq, msc313_rtc_interrupt, IRQF_SHARED, - dev_name(&pdev->dev), &pdev->dev); + dev_name(&pdev->dev), priv); if (ret) { dev_err(dev, "Could not request IRQ\n"); return ret; From dd74c80203842a21b2ebb9f70d1260d9aa20fa05 Mon Sep 17 00:00:00 2001 From: Yiyang Chen Date: Thu, 18 Jun 2026 10:18:43 +0000 Subject: [PATCH 1892/2758] bpf: Guard conntrack opts error writes [ Upstream commit 6f6183a39533d727deaa5061cadae6dd9e6744d0 ] The conntrack lookup and allocation kfuncs take an opts pointer together with an opts__sz argument. The verifier checks only the memory range described by opts__sz, but the wrappers unconditionally write opts->error whenever the internal lookup or allocation helper returns an error. For an invalid size smaller than the end of opts->error, that write can land outside the verifier-checked range. Keep returning NULL for invalid arguments, but only report the error through opts->error when the supplied size includes the field. This preserves error reporting for the supported 12-byte and 16-byte layouts, and for other invalid sizes that still include opts->error. Fixes: b4c2b9593a1c ("net/netfilter: Add unstable CT lookup helpers for XDP and TC-BPF") Fixes: d7e79c97c00c ("net: netfilter: Add kfuncs to allocate and insert CT") Signed-off-by: Yiyang Chen Link: https://lore.kernel.org/r/9535e781fe14449b1d4e9bbc3baa7566a93bf512.1781765747.git.chenyy23@mails.tsinghua.edu.cn Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- net/netfilter/nf_conntrack_bpf.c | 35 ++++++++++++-------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/net/netfilter/nf_conntrack_bpf.c b/net/netfilter/nf_conntrack_bpf.c index 40c261cd0af38..f98d1d4b42c3d 100644 --- a/net/netfilter/nf_conntrack_bpf.c +++ b/net/netfilter/nf_conntrack_bpf.c @@ -65,6 +65,15 @@ enum { NF_BPF_CT_OPTS_SZ = 16, }; +static void *bpf_ct_opts_result(struct bpf_ct_opts *opts, u32 opts__sz, void *ret) +{ + if (!IS_ERR(ret)) + return ret; + if (opts__sz >= offsetofend(struct bpf_ct_opts, error)) + opts->error = PTR_ERR(ret); + return NULL; +} + static int bpf_nf_ct_tuple_parse(struct bpf_sock_tuple *bpf_tuple, u32 tuple_len, u8 protonum, u8 dir, struct nf_conntrack_tuple *tuple) @@ -297,12 +306,7 @@ bpf_xdp_ct_alloc(struct xdp_md *xdp_ctx, struct bpf_sock_tuple *bpf_tuple, nfct = __bpf_nf_ct_alloc_entry(dev_net(ctx->rxq->dev), bpf_tuple, tuple__sz, opts, opts__sz, 10); - if (IS_ERR(nfct)) { - opts->error = PTR_ERR(nfct); - return NULL; - } - - return (struct nf_conn___init *)nfct; + return (struct nf_conn___init *)bpf_ct_opts_result(opts, opts__sz, nfct); } /* bpf_xdp_ct_lookup - Lookup CT entry for the given tuple, and acquire a @@ -331,11 +335,7 @@ bpf_xdp_ct_lookup(struct xdp_md *xdp_ctx, struct bpf_sock_tuple *bpf_tuple, caller_net = dev_net(ctx->rxq->dev); nfct = __bpf_nf_ct_lookup(caller_net, bpf_tuple, tuple__sz, opts, opts__sz); - if (IS_ERR(nfct)) { - opts->error = PTR_ERR(nfct); - return NULL; - } - return nfct; + return bpf_ct_opts_result(opts, opts__sz, nfct); } /* bpf_skb_ct_alloc - Allocate a new CT entry @@ -363,12 +363,7 @@ bpf_skb_ct_alloc(struct __sk_buff *skb_ctx, struct bpf_sock_tuple *bpf_tuple, net = skb->dev ? dev_net(skb->dev) : sock_net(skb->sk); nfct = __bpf_nf_ct_alloc_entry(net, bpf_tuple, tuple__sz, opts, opts__sz, 10); - if (IS_ERR(nfct)) { - opts->error = PTR_ERR(nfct); - return NULL; - } - - return (struct nf_conn___init *)nfct; + return (struct nf_conn___init *)bpf_ct_opts_result(opts, opts__sz, nfct); } /* bpf_skb_ct_lookup - Lookup CT entry for the given tuple, and acquire a @@ -397,11 +392,7 @@ bpf_skb_ct_lookup(struct __sk_buff *skb_ctx, struct bpf_sock_tuple *bpf_tuple, caller_net = skb->dev ? dev_net(skb->dev) : sock_net(skb->sk); nfct = __bpf_nf_ct_lookup(caller_net, bpf_tuple, tuple__sz, opts, opts__sz); - if (IS_ERR(nfct)) { - opts->error = PTR_ERR(nfct); - return NULL; - } - return nfct; + return bpf_ct_opts_result(opts, opts__sz, nfct); } /* bpf_ct_insert_entry - Add the provided entry into a CT map From 03ae998ae6237d6403d7a8b91cecccf586d0bade Mon Sep 17 00:00:00 2001 From: Yiyang Chen Date: Thu, 18 Jun 2026 10:18:44 +0000 Subject: [PATCH 1893/2758] selftests/bpf: Cover small conntrack opts error writes [ Upstream commit 38ba6d43af3844ae502092ee9dcc47214e82acb8 ] Add a conntrack kfunc regression check for opts__sz values that do not cover opts->error. The BPF program initializes opts->error with a guard value, calls the lookup and allocation kfuncs with opts__sz set to sizeof(opts->netns_id), and verifies that the guard is still intact after the kfunc returns NULL. Without the conntrack wrapper guard, the kfunc error path overwrites that guard with -EINVAL even though the verifier checked only the first four bytes of the options object. Fixes: b4c2b9593a1c ("net/netfilter: Add unstable CT lookup helpers for XDP and TC-BPF") Fixes: d7e79c97c00c ("net: netfilter: Add kfuncs to allocate and insert CT") Signed-off-by: Yiyang Chen Link: https://lore.kernel.org/r/007dfd0341cd84560e4795a2a951cc56d4adff1d.1781765747.git.chenyy23@mails.tsinghua.edu.cn Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- .../testing/selftests/bpf/prog_tests/bpf_nf.c | 6 +++++ .../testing/selftests/bpf/progs/test_bpf_nf.c | 26 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_nf.c b/tools/testing/selftests/bpf/prog_tests/bpf_nf.c index 215878ea04dee..4b06a54907144 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpf_nf.c +++ b/tools/testing/selftests/bpf/prog_tests/bpf_nf.c @@ -5,6 +5,8 @@ #include "test_bpf_nf.skel.h" #include "test_bpf_nf_fail.skel.h" +#define CT_OPTS_ERROR_GUARD 0x12345678 + static char log_buf[1024 * 1024]; struct { @@ -119,6 +121,10 @@ static void test_bpf_nf_ct(int mode) ASSERT_EQ(skel->bss->test_einval_reserved_new, -EINVAL, "Test EINVAL for reserved in new struct not set to 0"); ASSERT_EQ(skel->bss->test_einval_netns_id, -EINVAL, "Test EINVAL for netns_id < -1"); ASSERT_EQ(skel->bss->test_einval_len_opts, -EINVAL, "Test EINVAL for len__opts != NF_BPF_CT_OPTS_SZ"); + ASSERT_EQ(skel->bss->test_einval_len_opts_small_lookup, CT_OPTS_ERROR_GUARD, + "Test no error write for lookup opts__sz before error field"); + ASSERT_EQ(skel->bss->test_einval_len_opts_small_alloc, CT_OPTS_ERROR_GUARD, + "Test no error write for alloc opts__sz before error field"); ASSERT_EQ(skel->bss->test_eproto_l4proto, -EPROTO, "Test EPROTO for l4proto != TCP or UDP"); ASSERT_EQ(skel->bss->test_enonet_netns_id, -ENONET, "Test ENONET for bad but valid netns_id"); ASSERT_EQ(skel->bss->test_enoent_lookup, -ENOENT, "Test ENOENT for failed lookup"); diff --git a/tools/testing/selftests/bpf/progs/test_bpf_nf.c b/tools/testing/selftests/bpf/progs/test_bpf_nf.c index 076fbf03a1268..df43649ecb785 100644 --- a/tools/testing/selftests/bpf/progs/test_bpf_nf.c +++ b/tools/testing/selftests/bpf/progs/test_bpf_nf.c @@ -10,6 +10,8 @@ #define EINVAL 22 #define ENOENT 2 +#define CT_OPTS_ERROR_GUARD 0x12345678 + #define NF_CT_ZONE_DIR_ORIG (1 << IP_CT_DIR_ORIGINAL) #define NF_CT_ZONE_DIR_REPL (1 << IP_CT_DIR_REPLY) @@ -19,6 +21,8 @@ int test_einval_reserved = 0; int test_einval_reserved_new = 0; int test_einval_netns_id = 0; int test_einval_len_opts = 0; +int test_einval_len_opts_small_lookup = 0; +int test_einval_len_opts_small_alloc = 0; int test_eproto_l4proto = 0; int test_enonet_netns_id = 0; int test_enoent_lookup = 0; @@ -124,6 +128,28 @@ nf_ct_test(struct nf_conn *(*lookup_fn)(void *, struct bpf_sock_tuple *, u32, else test_einval_len_opts = opts_def.error; + opts_def.error = CT_OPTS_ERROR_GUARD; + ct = lookup_fn(ctx, &bpf_tuple, sizeof(bpf_tuple.ipv4), &opts_def, + sizeof(opts_def.netns_id)); + if (ct) { + bpf_ct_release(ct); + test_einval_len_opts_small_lookup = -EINVAL; + } else { + test_einval_len_opts_small_lookup = opts_def.error; + } + + opts_def.error = CT_OPTS_ERROR_GUARD; + ct = alloc_fn(ctx, &bpf_tuple, sizeof(bpf_tuple.ipv4), &opts_def, + sizeof(opts_def.netns_id)); + if (ct) { + ct = bpf_ct_insert_entry(ct); + if (ct) + bpf_ct_release(ct); + test_einval_len_opts_small_alloc = -EINVAL; + } else { + test_einval_len_opts_small_alloc = opts_def.error; + } + opts_def.l4proto = IPPROTO_ICMP; ct = lookup_fn(ctx, &bpf_tuple, sizeof(bpf_tuple.ipv4), &opts_def, sizeof(opts_def)); From 1c89da3baa2b1f269178afa87dc30479b8535776 Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Fri, 19 Jun 2026 08:25:46 +0900 Subject: [PATCH 1894/2758] ksmbd: fix use-after-free of conn->preauth_info in concurrent SMB2 NEGOTIATE [ Upstream commit 0c054227479ed7e36ebccb3a558bc0ef698264f6 ] conn->preauth_info is shared connection state (struct preauth_integrity_info, kmalloc-96) that is allocated and freed by the SMB2 NEGOTIATE handler and read by the response send path. smb2_handle_negotiate() allocates conn->preauth_info, and on a deassemble_neg_contexts() failure kfrees it and sets it to NULL. Both the allocation and the free/NULL happen under ksmbd_conn_lock(conn) (the connection srv_mutex), which is held across the whole handler body. The response send path smb3_preauth_hash_rsp(), called from the send: block of __handle_ksmbd_work(), reads conn->preauth_info and dereferences conn->preauth_info->Preauth_HashValue (via ksmbd_gen_preauth_integrity_hash()) without taking conn_lock. When a client drives two SMB2 NEGOTIATE requests on the same connection, one worker can free conn->preauth_info on the failing-negotiate path while a concurrent send-path worker is reading it, producing a slab use-after-free read (KASAN-confirmed). The send-path read tested conn->preauth_info for NULL but raced with the free that occurs between the NULL check and the dereference, so the NULL guard alone does not close the window. Serialize the NEGOTIATE-branch read in smb3_preauth_hash_rsp() under ksmbd_conn_lock(conn) and re-check conn->preauth_info inside the lock. Because the negotiate handler holds conn_lock across its kfree + NULL assignment, a reader that also takes conn_lock either runs fully before the allocation or fully after the NULL store, and can never observe the freed-but-not-yet-NULLed pointer. ksmbd_gen_preauth_integrity_hash() takes no locks itself (it only computes a SHA-512 over the buffer), so no lock-ordering inversion is introduced, and conn_lock is a sleepable mutex which is safe on this send path (it already performs network I/O). Fixes: aa7253c2393f ("ksmbd: fix memory leak in smb2_handle_negotiate") Signed-off-by: Gil Portnoy Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/server/smb2pdu.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index a1b163763dade..e5e2bd7a67265 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -9220,10 +9220,13 @@ void smb3_preauth_hash_rsp(struct ksmbd_work *work) WORK_BUFFERS(work, req, rsp); - if (le16_to_cpu(req->Command) == SMB2_NEGOTIATE_HE && - conn->preauth_info) - ksmbd_gen_preauth_integrity_hash(conn, work->response_buf, - conn->preauth_info->Preauth_HashValue); + if (le16_to_cpu(req->Command) == SMB2_NEGOTIATE_HE) { + ksmbd_conn_lock(conn); + if (conn->preauth_info) + ksmbd_gen_preauth_integrity_hash(conn, work->response_buf, + conn->preauth_info->Preauth_HashValue); + ksmbd_conn_unlock(conn); + } if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && sess) { __u8 *hash_value; From 6428634f7a0b7878144b4925c37856bef3224967 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Thu, 18 Jun 2026 21:53:34 -0700 Subject: [PATCH 1895/2758] ipv6: Fix null-ptr-deref in fib6_nh_mtu_change(). [ Upstream commit 46c3b8191aad3d032776bf3bebf03efdf5f4b905 ] fib6_nh_mtu_change() re-fetches idev via __in6_dev_get(arg->dev) and dereferences idev->cnf.mtu6 without a NULL check. addrconf_ifdown() clears dev->ip6_ptr with RCU_INIT_POINTER() after rt6_disable_ip() has released tb6_lock, so the RA-driven MTU walk can observe a NULL idev and oops. The caller rt6_mtu_change_route() guards its own __in6_dev_get(), but this re-fetch is unguarded; nexthop-backed routes survive addrconf_ifdown()'s flush, so the walk still reaches it after ip6_ptr is nulled. Return 0 when idev is NULL, matching rt6_mtu_change_route() and the fib6_mtu() fix in commit 5ad509c1fdad ("ipv6: Fix null-ptr-deref in fib6_mtu()."). Oops: general protection fault, ... KASAN: null-ptr-deref in range [0x00000000000002a8-0x00000000000002af] RIP: 0010:fib6_nh_mtu_change+0x203/0x990 rt6_mtu_change_route+0x141/0x1d0 __fib6_clean_all+0xd0/0x160 rt6_mtu_change+0xb4/0x100 ndisc_router_discovery+0x24b5/0x2cb0 icmpv6_rcv+0x12e9/0x1710 ipv6_rcv+0x39b/0x410 Fixes: c0b220cf7d80 ("ipv6: Refactor exception functions") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Reviewed-by: Fernando Fernandez Mancera Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260619045334.2427073-1-xmei5@asu.edu Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/route.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 3c97ba01297aa..88b46967bb67d 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -5053,6 +5053,9 @@ static int fib6_nh_mtu_change(struct fib6_nh *nh, void *_arg) struct inet6_dev *idev = __in6_dev_get(arg->dev); u32 mtu = f6i->fib6_pmtu; + if (!idev) + return 0; + if (mtu >= arg->mtu || (mtu < arg->mtu && mtu == idev->cnf.mtu6)) fib6_metric_set(f6i, RTAX_MTU, arg->mtu); From c5bd84c6cd77dee5503ecb9f0f4f35c4f992e347 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 19 Jun 2026 12:15:38 -0700 Subject: [PATCH 1896/2758] eth: bnxt: improve the timing of stats [ Upstream commit 40529e58629baa9ce72143cb46cf1b3d2ca0d465 ] Kernel selftests wait 1.25x of the promised stats refresh time (as read from ethtool -c). bnxt reports 1sec by default, but the stats update process has two steps. First device DMAs the new values, then the service task performs update in full-width SW counters. So the worst case delay is actually 2x. Note that the behavior is different for ring stats and port stats. Port stats are fetched synchronously by the service worker, so there's no risk of doubling up the delay there. The problem of stale stats impacts not only tests but real workloads which monitor egress bandwidth of a NIC. The inaccuracy causes double counting in the next cycle and spurious overload alarms. Try to read from the DMA buffer more aggressively, to mitigate timing issues between DMA and service task. The SW update should be cheap. Fixes: 51f307856b60 ("bnxt_en: Allow statistics DMA to be configurable using ethtool -C.") Reviewed-by: Michael Chan Link: https://patch.msgid.link/20260619191538.104165-1-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 48 ++++++++++++++++++- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 5 ++ .../net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 1 + 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 1125130449887..d44d3ac255aa4 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -10530,7 +10530,7 @@ static void bnxt_accumulate_stats(struct bnxt_stats_mem *stats) stats->hw_masks, stats->len / 8, false); } -static void bnxt_accumulate_all_stats(struct bnxt *bp) +static void bnxt_accumulate_ring_stats(struct bnxt *bp) { struct bnxt_stats_mem *ring0_stats; bool ignore_zero = false; @@ -10553,6 +10553,10 @@ static void bnxt_accumulate_all_stats(struct bnxt *bp) ring0_stats->hw_masks, ring0_stats->len / 8, ignore_zero); } +} + +static void bnxt_accumulate_port_stats(struct bnxt *bp) +{ if (bp->flags & BNXT_FLAG_PORT_STATS) { struct bnxt_stats_mem *stats = &bp->port_stats; __le64 *hw_stats = stats->hw_stats; @@ -10575,6 +10579,41 @@ static void bnxt_accumulate_all_stats(struct bnxt *bp) } } +static void bnxt_accumulate_all_stats(struct bnxt *bp) +{ + bnxt_accumulate_ring_stats(bp); + bnxt_accumulate_port_stats(bp); +} + +/* Re-accumulate ring stats from DMA buffers if stale. + * uAPIs for reading sw_stats should call this first. + * + * We promise user space update frequency of bp->stats_coal_ticks but + * the update is a two step process - first device updates the DMA buffer, + * then we have to update from that buffer to driver stats in the service work. + * Worst case we would be 2x off from the desired frequency. + * Sync the stats sooner, if stale. The 20% threshold was chosen arbitrarily. + * + * Ideally we would split the user-configured time into two portions, + * i.e. also lower the DMA period by the 20%. But the DMA timer seems to have + * too coarse granularity to play such tricks. + */ +void bnxt_sync_ring_stats(struct bnxt *bp) +{ + unsigned long stale; + + if (!netif_running(bp->dev) || !bp->stats_coal_ticks) + return; + + spin_lock(&bp->stats_lock); + stale = usecs_to_jiffies(bp->stats_coal_ticks / 5); + if (time_after_eq(jiffies, bp->stats_updated_jiffies + stale)) { + bnxt_accumulate_ring_stats(bp); + bp->stats_updated_jiffies = jiffies; + } + spin_unlock(&bp->stats_lock); +} + static int bnxt_hwrm_port_qstats(struct bnxt *bp, u8 flags) { struct hwrm_port_qstats_input *req; @@ -13573,6 +13612,7 @@ bnxt_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) return; } + bnxt_sync_ring_stats(bp); bnxt_get_ring_stats(bp, stats); bnxt_add_prev_stats(bp, stats); @@ -14754,7 +14794,10 @@ static void bnxt_sp_task(struct work_struct *work) if (test_and_clear_bit(BNXT_PERIODIC_STATS_SP_EVENT, &bp->sp_event)) { bnxt_hwrm_port_qstats(bp, 0); bnxt_hwrm_port_qstats_ext(bp, 0); + spin_lock(&bp->stats_lock); bnxt_accumulate_all_stats(bp); + bp->stats_updated_jiffies = jiffies; + spin_unlock(&bp->stats_lock); } if (test_and_clear_bit(BNXT_LINK_CHNG_SP_EVENT, &bp->sp_event)) { @@ -15496,6 +15539,7 @@ static int bnxt_init_board(struct pci_dev *pdev, struct net_device *dev) INIT_DELAYED_WORK(&bp->fw_reset_task, bnxt_fw_reset_task); spin_lock_init(&bp->ntp_fltr_lock); + spin_lock_init(&bp->stats_lock); #if BITS_PER_LONG == 32 spin_lock_init(&bp->db_lock); #endif @@ -16064,6 +16108,7 @@ static void bnxt_get_queue_stats_rx(struct net_device *dev, int i, if (!bp->bnapi) return; + bnxt_sync_ring_stats(bp); cpr = &bp->bnapi[i]->cp_ring; sw = cpr->stats.sw_stats; @@ -16092,6 +16137,7 @@ static void bnxt_get_queue_stats_tx(struct net_device *dev, int i, if (!bp->tx_ring) return; + bnxt_sync_ring_stats(bp); bnapi = bp->tx_ring[bp->tx_ring_map[i]].bnapi; sw = bnapi->cp_ring.stats.sw_stats; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index a2e75cfb65ab0..fa13cee557798 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -2621,6 +2621,10 @@ struct bnxt { #define BNXT_MIN_STATS_COAL_TICKS 250000 #define BNXT_MAX_STATS_COAL_TICKS 1000000 + /* Protects stats_updated_jiffies and writes to sw_stats */ + spinlock_t stats_lock; + unsigned long stats_updated_jiffies; + struct work_struct sp_task; unsigned long sp_event; #define BNXT_RX_MASK_SP_EVENT 0 @@ -3029,6 +3033,7 @@ void bnxt_reenable_sriov(struct bnxt *bp); void bnxt_close_nic(struct bnxt *, bool, bool); void bnxt_get_ring_drv_stats(struct bnxt *bp, struct bnxt_total_ring_drv_stats *stats); +void bnxt_sync_ring_stats(struct bnxt *bp); bool bnxt_rfs_capable(struct bnxt *bp, bool new_rss_ctx); int bnxt_dbg_hwrm_rd_reg(struct bnxt *bp, u32 reg_off, u16 num_words, u32 *reg_buf); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 9b14134d62d25..b98761d55a2b0 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -606,6 +606,7 @@ static void bnxt_get_ethtool_stats(struct net_device *dev, goto skip_ring_stats; } + bnxt_sync_ring_stats(bp); tpa_stats = bnxt_get_num_tpa_ring_stats(bp); for (i = 0; i < bp->cp_nr_rings; i++) { struct bnxt_napi *bnapi = bp->bnapi[i]; From a668fa160247d7bbe921548cb845f607b8b9305f Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Fri, 19 Jun 2026 21:27:20 +0000 Subject: [PATCH 1897/2758] ipv4: fib: Don't ignore error route in local/main tables. [ Upstream commit b72f0db64205d9ce462038ba995d5d31eff32dc1 ] When CONFIG_IP_MULTIPLE_TABLES is enabled but no rule is added, fib_lookup() performs route lookup directly on two tables. Since the first lookup does not properly bail out, the result of an error route in the merged local/main table could be overwritten by another route in the default table: # unshare -n # ip link set lo up # ip route add 192.168.0.0/24 dev lo table 253 # ip route add unreachable 192.168.0.0/24 # ip route get 192.168.0.1 192.168.0.1 dev lo table default uid 0 cache Once a random rule is added, the error route is respected: # ip rule add table 0 # ip rule del table 0 # ip route get 192.168.0.1 RTNETLINK answers: No route to host Let's fix the inconsistent behaviour. Fixes: f4530fa574df ("ipv4: Avoid overhead when no custom FIB rules are installed.") Signed-off-by: Kuniyuki Iwashima Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260619212753.3367244-1-kuniyu@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- include/net/ip_fib.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 541da2dde6266..040eed88fc672 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -375,7 +375,7 @@ static inline int fib_lookup(struct net *net, struct flowi4 *flp, struct fib_result *res, unsigned int flags) { struct fib_table *tb; - int err = -ENETUNREACH; + int err = -EAGAIN; flags |= FIB_LOOKUP_NOREF; if (net->ipv4.fib_has_custom_rules) @@ -389,17 +389,16 @@ static inline int fib_lookup(struct net *net, struct flowi4 *flp, if (tb) err = fib_table_lookup(tb, flp, res, flags); - if (!err) + if (err != -EAGAIN) goto out; tb = rcu_dereference_rtnl(net->ipv4.fib_default); if (tb) err = fib_table_lookup(tb, flp, res, flags); -out: if (err == -EAGAIN) err = -ENETUNREACH; - +out: rcu_read_unlock(); return err; From 4465211d195d81df6e9e513bf308c8ca5d13f05a Mon Sep 17 00:00:00 2001 From: Chen Cheng Date: Thu, 18 Jun 2026 21:47:48 +0800 Subject: [PATCH 1898/2758] md/raid5: use stripe state snapshot in break_stripe_batch_list() [ Upstream commit 9b249f5ffbeda24c57e6c56ed896c5ca4fc70549 ] The patch just suppress KCSAN noise. No functional change. RAID-5 can group multi full-stripe-write aka stripe_head into a batch aka batch_list, with one head_sh leading them. Call break_stripe_batch_list() when the batch is finished, or, a stripe has to be dropped out of the batch. break_stripe_batch_list() reads stripe state several times while request paths can update thost state words concurrently with lockless bitops, which reported by KCSAN. Use a snapshot to guarantees that the value used for warning, copying, and handle checks is internally consistent at current read moment. KCSAN report: ============================================== BUG: KCSAN: data-race in __add_stripe_bio / break_stripe_batch_list write (marked) to 0xffff8e89d4f0b988 of 8 bytes by task 4323 on cpu 3: __add_stripe_bio+0x35e/0x400 raid5_make_request+0x6ac/0x2930 md_handle_request+0x4a2/0xa40 md_submit_bio+0x109/0x1a0 __submit_bio+0x2ec/0x390 submit_bio_noacct_nocheck+0x457/0x710 submit_bio_noacct+0x2a7/0xc20 submit_bio+0x56/0x250 blkdev_direct_IO+0x54c/0xda0 blkdev_write_iter+0x38f/0x570 aio_write+0x22b/0x490 io_submit_one+0xa51/0xf70 read to 0xffff8e89d4f0b988 of 8 bytes by task 4290 on cpu 4: break_stripe_batch_list+0x3ce/0x480 handle_stripe_clean_event+0x720/0x9b0 handle_stripe+0x32fb/0x4500 handle_active_stripes.isra.0+0x6e0/0xa50 raid5d+0x7e0/0xba0 Signed-off-by: Chen Cheng Link: https://patch.msgid.link/20260618134748.1168360-1-chencheng@fnnas.com Signed-off-by: Yu Kuai Stable-dep-of: 55b77337bdd0 ("md/raid5: avoid R5_Overlap races while breaking stripe batches") Signed-off-by: Sasha Levin --- drivers/md/raid5.c | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 0d76e82f4506e..133d5e5f0dce5 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -4837,31 +4837,35 @@ static void break_stripe_batch_list(struct stripe_head *head_sh, { struct stripe_head *sh, *next; int i; + unsigned long state; list_for_each_entry_safe(sh, next, &head_sh->batch_list, batch_list) { list_del_init(&sh->batch_list); - WARN_ONCE(sh->state & ((1 << STRIPE_ACTIVE) | - (1 << STRIPE_SYNCING) | - (1 << STRIPE_REPLACED) | - (1 << STRIPE_DELAYED) | - (1 << STRIPE_BIT_DELAY) | - (1 << STRIPE_FULL_WRITE) | - (1 << STRIPE_BIOFILL_RUN) | - (1 << STRIPE_COMPUTE_RUN) | - (1 << STRIPE_DISCARD) | - (1 << STRIPE_BATCH_READY) | - (1 << STRIPE_BATCH_ERR)), - "stripe state: %lx\n", sh->state); - WARN_ONCE(head_sh->state & ((1 << STRIPE_DISCARD) | - (1 << STRIPE_REPLACED)), - "head stripe state: %lx\n", head_sh->state); + state = READ_ONCE(sh->state); + WARN_ONCE(state & ((1 << STRIPE_ACTIVE) | + (1 << STRIPE_SYNCING) | + (1 << STRIPE_REPLACED) | + (1 << STRIPE_DELAYED) | + (1 << STRIPE_BIT_DELAY) | + (1 << STRIPE_FULL_WRITE) | + (1 << STRIPE_BIOFILL_RUN) | + (1 << STRIPE_COMPUTE_RUN) | + (1 << STRIPE_DISCARD) | + (1 << STRIPE_BATCH_READY) | + (1 << STRIPE_BATCH_ERR)), + "stripe state: %lx\n", state); + + state = READ_ONCE(head_sh->state); + WARN_ONCE(state & ((1 << STRIPE_DISCARD) | + (1 << STRIPE_REPLACED)), + "head stripe state: %lx\n", state); set_mask_bits(&sh->state, ~(STRIPE_EXPAND_SYNC_FLAGS | (1 << STRIPE_PREREAD_ACTIVE) | (1 << STRIPE_ON_UNPLUG_LIST)), - head_sh->state & (1 << STRIPE_INSYNC)); + state & (1 << STRIPE_INSYNC)); sh->check_state = head_sh->check_state; sh->reconstruct_state = head_sh->reconstruct_state; @@ -4874,8 +4878,9 @@ static void break_stripe_batch_list(struct stripe_head *head_sh, sh->dev[i].flags = head_sh->dev[i].flags & (~((1 << R5_WriteError) | (1 << R5_Overlap))); } - if (handle_flags == 0 || - sh->state & handle_flags) + + state = READ_ONCE(sh->state); + if (handle_flags == 0 || (state & handle_flags)) set_bit(STRIPE_HANDLE, &sh->state); raid5_release_stripe(sh); } @@ -4885,7 +4890,9 @@ static void break_stripe_batch_list(struct stripe_head *head_sh, for (i = 0; i < head_sh->disks; i++) if (test_and_clear_bit(R5_Overlap, &head_sh->dev[i].flags)) wake_up_bit(&head_sh->dev[i].flags, R5_Overlap); - if (head_sh->state & handle_flags) + + state = READ_ONCE(head_sh->state); + if (state & handle_flags) set_bit(STRIPE_HANDLE, &head_sh->state); } From d684b72dfbd320623ccaab0779aa841190488e7c Mon Sep 17 00:00:00 2001 From: Chen Cheng Date: Fri, 19 Jun 2026 12:10:13 +0800 Subject: [PATCH 1899/2758] md/raid5: avoid R5_Overlap races while breaking stripe batches [ Upstream commit 55b77337bdd088c77461588e5ec094421b89911b ] KCSAN report a race in break_stripe_batch_list() vs. raid5_make_request() on sh->dev[i].flags (plain word write vs. atomic bit op).. and .. one possible scenario is: CPU1 CPU2 break_stripe_batch_list(sh1) -> handle sh2 -> lock(sh2) -> sh2->batch_head = NULL -> unlock(sh2) -> test_and_clear_bit(R5_Overlap, sh2->dev[i].flags) -> wake_up_bit(sh2->dev[i].flags) raid5_make_request() -> add_all_stripe_bios(sh2) -> lock(sh2) -> stripe_bio_overlaps(sh2) returns true batch_head is NULL, so new bio overlap exist bio on sh2 -> true -> set_bit(R5_Overlap, sh2->dev[i].flags) -> unlock(sh2) -> wait_on_bit(sh2->dev[i].flags) -> sh2->dev[i].flags = sh1->dev[i].flags & ~R5_Overlap No wait_up_bit(), CPU2 could be wait_on_bit() forever... Fix by : - Expand the protect zone. - Use batch_head's device flag's snaphot when no held head_sh->stripe_lock. - Move sh/head_sh->batch_head = NULL to the end of protected zone , and , any concurrent add_all_stripe_bios() grabs sh->stripe_lock now either: - see batch_head != null, and , is rejected by stripe_bio_overlaps() under the lock (no R5_Overlap wait ) , or , - sees batch_head == NULL, only after dev[i].flags has already been set and the prior R5_Overlap waiters worken. KCSAN report: ================================================ BUG: KCSAN: data-race in break_stripe_batch_list / raid5_make_request write (marked) to 0xffff8e89c8117548 of 8 bytes by task 4042 on cpu 0: raid5_make_request+0xea0/0x2930 md_handle_request+0x4a2/0xa40 md_submit_bio+0x109/0x1a0 __submit_bio+0x2ec/0x390 submit_bio_noacct_nocheck+0x457/0x710 submit_bio_noacct+0x2a7/0xc20 submit_bio+0x56/0x250 blkdev_direct_IO+0x54c/0xda0 blkdev_write_iter+0x38f/0x570 aio_write+0x22b/0x490 io_submit_one+0xa51/0xf70 __x64_sys_io_submit+0xf7/0x220 x64_sys_call+0x1907/0x1c60 do_syscall_64+0x130/0x570 entry_SYSCALL_64_after_hwframe+0x76/0x7e read to 0xffff8e89c8117548 of 8 bytes by task 4010 on cpu 5: break_stripe_batch_list+0x249/0x480 handle_stripe_clean_event+0x720/0x9b0 handle_stripe+0x32fb/0x4500 handle_active_stripes.isra.0+0x6e0/0xa50 raid5d+0x7e0/0xba0 md_thread+0x15a/0x2d0 kthread+0x1e3/0x220 ret_from_fork+0x37a/0x410 ret_from_fork_asm+0x1a/0x30 value changed: 0x0000000000000019 -> 0x0000000000000099 --> R5_Overlap Fixes: fb642b92c267 ("md/raid5: duplicate some more handle_stripe_clean_event code in break_stripe_batch_list") Signed-off-by: Chen Cheng Link: https://patch.msgid.link/20260619041013.1207148-1-chencheng@fnnas.com Signed-off-by: Yu Kuai Signed-off-by: Sasha Levin --- drivers/md/raid5.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 133d5e5f0dce5..5ae7ed98b6548 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -4870,14 +4870,14 @@ static void break_stripe_batch_list(struct stripe_head *head_sh, sh->check_state = head_sh->check_state; sh->reconstruct_state = head_sh->reconstruct_state; spin_lock_irq(&sh->stripe_lock); - sh->batch_head = NULL; - spin_unlock_irq(&sh->stripe_lock); for (i = 0; i < sh->disks; i++) { if (test_and_clear_bit(R5_Overlap, &sh->dev[i].flags)) wake_up_bit(&sh->dev[i].flags, R5_Overlap); - sh->dev[i].flags = head_sh->dev[i].flags & + sh->dev[i].flags = READ_ONCE(head_sh->dev[i].flags) & (~((1 << R5_WriteError) | (1 << R5_Overlap))); } + sh->batch_head = NULL; + spin_unlock_irq(&sh->stripe_lock); state = READ_ONCE(sh->state); if (handle_flags == 0 || (state & handle_flags)) @@ -4885,11 +4885,11 @@ static void break_stripe_batch_list(struct stripe_head *head_sh, raid5_release_stripe(sh); } spin_lock_irq(&head_sh->stripe_lock); - head_sh->batch_head = NULL; - spin_unlock_irq(&head_sh->stripe_lock); for (i = 0; i < head_sh->disks; i++) if (test_and_clear_bit(R5_Overlap, &head_sh->dev[i].flags)) wake_up_bit(&head_sh->dev[i].flags, R5_Overlap); + head_sh->batch_head = NULL; + spin_unlock_irq(&head_sh->stripe_lock); state = READ_ONCE(head_sh->state); if (state & handle_flags) From 1bb3b6a5c3c5cc814eae4ff0212fdced36f8bce9 Mon Sep 17 00:00:00 2001 From: Bradley Morgan Date: Fri, 19 Jun 2026 13:03:03 +0000 Subject: [PATCH 1900/2758] bpf: Disable xfrm_decode_session hook attachment [ Upstream commit 12091470c6b4c1c14b2de12dcbae2ada6cb6d20b ] BPF LSM programs can currently attach to xfrm_decode_session(). That hook may return an error, but security_skb_classify_flow() calls it from a void path and triggers BUG_ON() if an error is returned. Disable BPF attachment to the hook to prevent a BPF LSM program from turning packet classification into a full panic. Fixes: 9e4e01dfd325 ("bpf: lsm: Implement attach, detach and execution") Signed-off-by: Bradley Morgan Link: https://lore.kernel.org/r/20260619130305.27779-1-include@grrlz.net Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- kernel/bpf/bpf_lsm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c index 564071a92d7d2..1433809bb166a 100644 --- a/kernel/bpf/bpf_lsm.c +++ b/kernel/bpf/bpf_lsm.c @@ -51,6 +51,9 @@ BTF_ID(func, bpf_lsm_key_getsecurity) #ifdef CONFIG_AUDIT BTF_ID(func, bpf_lsm_audit_rule_match) #endif +#ifdef CONFIG_SECURITY_NETWORK_XFRM +BTF_ID(func, bpf_lsm_xfrm_decode_session) +#endif BTF_ID(func, bpf_lsm_ismaclabel) BTF_ID(func, bpf_lsm_file_alloc_security) BTF_SET_END(bpf_lsm_disabled_hooks) From eb14aba91163c33d8c99f9d7c06690e08b56a250 Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Tue, 28 Apr 2026 11:09:17 +0200 Subject: [PATCH 1901/2758] netfilter: nf_nat: avoid invalid nat_net pointer use on failed nf_nat_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 069cfe3de2a5e16069485893cd04665ab769c1d8 ] We ran into below KASAN splat, which is mostly uninteresting, beside for having nf_nat_register_fn() in the call chain as a cause for the offending access: ================================================================== BUG: KASAN: slab-out-of-bounds in nf_nat_register_fn+0x5f9/0x640 Read of size 8 at addr ffff890031e54c20 by task iptables/9510 CPU: 0 UID: 0 PID: 9510 Comm: iptables Not tainted 6.18.18-grsec-full-20260320181326 #1 PREEMPT(voluntary) Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 Call Trace: […] dump_stack_lvl+0xee/0x160 ffff88004117eeb8 […] print_report+0x6e/0x640 ffff88004117eee0 […] ? __phys_addr+0x8e/0x140 ffff88004117eef0 […] ? kasan_addr_to_slab+0x51/0xe0 ffff88004117ef08 […] ? complete_report_info+0xec/0x1c0 ffff88004117ef20 […] ? nf_nat_register_fn+0x5f9/0x640 ffff88004117ef48 […] kasan_report+0xbc/0x140 ffff88004117ef50 […] ? nf_nat_register_fn+0x5f9/0x640 ffff88004117ef90 […] nf_nat_register_fn+0x5f9/0x640 ffff88004117eff8 […] ? nf_nat_icmp_reply_translation+0x6e0/0x6e0 ffff88004117f070 […] nf_tables_register_hook.part.0+0xa0/0x220 ffff88004117f080 […] nf_tables_addchain.constprop.0+0x1054/0x1fc0 ffff88004117f0b8 […] ? nft_chain_lookup.part.0+0x4ce/0xac0 ffff88004117f130 […] ? nf_tables_abort+0x3d80/0x3d80 ffff88004117f190 […] ? nf_tables_dumpreset_obj+0x100/0x100 ffff88004117f1c8 […] ? nft_table_lookup.part.0+0x255/0x300 ffff88004117f310 […] ? nf_tables_newchain+0x21a4/0x2fa0 ffff88004117f358 […] nf_tables_newchain+0x21a4/0x2fa0 ffff88004117f360 […] ? nf_tables_addchain.constprop.0+0x1fc0/0x1fc0 ffff88004117f458 […] ? nla_get_range_signed+0x4a0/0x4a0 ffff88004117f488 […] ? lock_acquire+0x16f/0x320 ffff88004117f490 […] ? find_held_lock+0x3b/0xe0 ffff88004117f4b0 […] ? __nla_parse+0x45/0x80 ffff88004117f500 […] nfnetlink_rcv_batch+0xbca/0x19a0 ffff88004117f550 […] ? nfnetlink_net_exit_batch+0x120/0x120 ffff88004117f618 […] ? __sanitizer_cov_trace_switch+0x63/0xe0 ffff88004117f720 […] ? gr_acl_handle_mmap+0x1c4/0x320 ffff88004117f7c0 […] ? nla_get_range_signed+0x4a0/0x4a0 ffff88004117f7e8 […] ? gr_is_capable+0x6f/0xe0 ffff88004117f830 […] ? __nla_parse+0x45/0x80 ffff88004117f860 […] ? skb_pull+0x103/0x1a0 ffff88004117f880 […] nfnetlink_rcv+0x3db/0x4a0 ffff88004117f8b0 […] ? nfnetlink_rcv_batch+0x19a0/0x19a0 ffff88004117f8d8 […] ? netlink_lookup+0xe2/0x240 ffff88004117f900 […] netlink_unicast+0x74b/0xb00 ffff88004117f930 […] ? netlink_attachskb+0xb20/0xb20 ffff88004117f980 […] ? __check_object_size+0x3e/0xaa0 ffff88004117f998 […] ? security_netlink_send+0x51/0x160 ffff88004117f9c8 […] netlink_sendmsg+0xa03/0x1200 ffff88004117f9f8 […] ? netlink_unicast+0xb00/0xb00 ffff88004117fa70 […] ? netlink_unicast+0xb00/0xb00 ffff88004117fac8 […] ? ____sys_sendmsg+0xe2a/0x1040 ffff88004117faf8 […] ____sys_sendmsg+0xe2a/0x1040 ffff88004117fb00 […] ? kernel_recvmsg+0x300/0x300 ffff88004117fb60 […] ? reacquire_held_locks+0xe9/0x260 ffff88004117fbc8 […] ___sys_sendmsg+0x138/0x200 ffff88004117fbf8 […] ? do_recvmmsg+0x7e0/0x7e0 ffff88004117fc30 […] ? lockdep_hardirqs_on_prepare+0x101/0x1e0 ffff88004117fc50 […] ? lock_acquire+0x16f/0x320 ffff88004117fd20 […] ? lock_acquire+0x16f/0x320 ffff88004117fd58 […] ? find_held_lock+0x3b/0xe0 ffff88004117fd70 […] __sys_sendmsg+0x17a/0x260 ffff88004117fdc8 […] ? __sys_sendmsg_sock+0x80/0x80 ffff88004117fdf0 […] ? syscall_trace_enter+0x15e/0x2c0 ffff88004117fe98 […] do_syscall_64+0x7d/0x400 ffff88004117fec8 […] entry_SYSCALL_64_safe_stack+0x4a/0x60 ffff88004117fef8 ================================================================== The out-of-bounds report, though, is a red herring as it is for an access that shouldn't have happened in the first place. When nf_nat_init() fails to register its BPF kfuncs, it'll unwind and, among others, call unregister_pernet_subsys() to deregister its per-net ops. This makes the previously allocated net id available for reuse by the next caller of register_pernet_subsys(), in our case, synproxy. However, 'nat_net_id' will still hold the previously allocated value. If nf_nat.o gets build as a module, all this doesn't matter. A failed initialization routine makes the module fail to load and any dependent module won't be able to load either. However, if nf_nat.o is built-in, a failing init won't /completely/ make its functionality unavailable to dependent modules, namely the code and static data is still there, free to be called by modules like nft_chain_nat.ko. Case in point, nft_chain_nat registers hooks that'll call into nf_nat which, in our case, failed to initialize and therefore won't have a valid net id nor related net_nat object any more. Code in nf_nat, namely nf_nat_register_fn() and nf_nat_unregister_fn(), still making use of the reallocated net id, lead to a type confusion as the call to net_generic() will no longer return memory belonging to an object suited to fit 'struct nat_net' but 'struct synproxy_net' instead. The latter is only 24 bytes on 64-bit systems, much smaller than struct nat_net which is 176 bytes, perfectly explaining the OOB KASAN report. Detect and handle a failed nf_nat_init() by testing the 'nf_nat_hook' pointer which will be reset to NULL on initialization errors to prevent the usage of an invalid nat_net pointer. As this check is only needed when nf_nat.o is built-in, guard it by '#ifndef MODULE...'. Fixes: cbc1dd5b659f ("netfilter: nf_nat: Fix possible memory leak in nf_nat_init()") Signed-off-by: Mathias Krause Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_nat_core.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index 2bbf5163c0e27..63ff6b4d5d214 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -1181,6 +1181,16 @@ int nf_nat_register_fn(struct net *net, u8 pf, const struct nf_hook_ops *ops, struct nf_hook_ops *nat_ops; int i, ret; +#ifndef MODULE + /* If nf_nat_core is built-in and nf_nat_init() fails, dependent + * modules like nft_chain_nat.ko may still call this function. + * However, nat_net would be invalid, likely pointing to some other + * per-net structure. + */ + if (WARN_ON_ONCE(!nf_nat_hook)) + return -EOPNOTSUPP; +#endif + if (WARN_ON_ONCE(pf >= ARRAY_SIZE(nat_net->nat_proto_net))) return -EINVAL; From be52572c6d55f677ba76869d3c63805c0d4891a3 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Thu, 14 May 2026 16:16:28 +0200 Subject: [PATCH 1902/2758] netfilter: nf_conncount: prevent connlimit drops for early confirmed ct [ Upstream commit c8b6f36f766991e3ebebec6596daee4b04dcbc49 ] Commit 69894e5b4c5e ("netfilter: nft_connlimit: update the count if add was skipped") introduced a regression where packets for valid connections are dropped when using connlimit for soft-limiting scenarios. The issue occurs when a new connection reuses a socket currently in the TIME_WAIT state. In this scenario, the connection tracking entry is evaluated as already confirmed. Previously, __nf_conncount_add() assumed that if a connection was confirmed and did not originate from the loopback interface, it should skip the addition and return -EEXIST. Skipping the addition triggers a garbage collection run that cleans up the TIME_WAIT connection. Consequently, the active connection count drops to 0, which xt_connlimit mishandles, leading to the false rejection of the perfectly valid new connection. Fix this by replacing the interface check with protocol-agnostic state checks. We now skip the tree insertion and preserve the lockless garbage collection optimization only if the connection is IPS_ASSURED. This allows early-confirmed setup packets (such as reused TIME_WAIT sockets or locally generated SYN-ACKs) to be properly evaluated and counted without falsely dropping. The goto check_connections path is maintained to ensure these setup packets are deduplicated correctly. This has been tested with slowhttptest and HTTP server configured locally to ensure we are not breaking soft-limiting scenarios for local or external connections. In addition, it was tested with a OVS zone limit too. Fixes: 69894e5b4c5e ("netfilter: nft_connlimit: update the count if add was skipped") Reported-by: Alejandro Olivan Alvarez Closes: https://lore.kernel.org/netfilter-devel/177349610461.3071718.4083978280323144323@eldamar.lan/ Signed-off-by: Fernando Fernandez Mancera Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_conncount.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/net/netfilter/nf_conncount.c b/net/netfilter/nf_conncount.c index dac29e933553d..c6d3f1c06b330 100644 --- a/net/netfilter/nf_conncount.c +++ b/net/netfilter/nf_conncount.c @@ -179,17 +179,16 @@ static int __nf_conncount_add(struct net *net, return -ENOENT; if (ct && nf_ct_is_confirmed(ct)) { - /* local connections are confirmed in postrouting so confirmation - * might have happened before hitting connlimit + /* Connection is confirmed but might still be in the setup phase. + * Only skip the tracking if it is fully assured. This guarantees + * that setup packets or retransmissions are properly counted and + * deduplicated. */ - if (skb->skb_iif != LOOPBACK_IFINDEX) { + if (test_bit(IPS_ASSURED_BIT, &ct->status)) { err = -EEXIST; goto out_put; } - /* this is likely a local connection, skip optimization to avoid - * adding duplicates from a 'packet train' - */ goto check_connections; } From 025a41e76b51fbc7b8eaa5bacbaa9621d00e6aa7 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Mon, 8 Jun 2026 14:33:23 +0200 Subject: [PATCH 1903/2758] netfilter: flowtable: Validate iph->ihl in nf_flow_ip4_tunnel_proto() [ Upstream commit 84460b644329e25809b4a6d9279d6359d7fd8ebc ] Add sanity check for iph->ihl field in nf_flow_ip4_tunnel_proto() before using it to compute the header size, avoiding out-of-bounds access with malformed IP headers. While at it, use iph->protocol instead of the hardcoded IPPROTO_IPIP constant when setting ctx->tun.proto and reference ctx->tun.hdr_size when updating ctx->offset. Fixes: ab427db178858 ("netfilter: flowtable: Add IPIP rx sw acceleration") Signed-off-by: Lorenzo Bianconi Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_flow_table_ip.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c index e7a3fb2b2d946..29e93ac1e2e40 100644 --- a/net/netfilter/nf_flow_table_ip.c +++ b/net/netfilter/nf_flow_table_ip.c @@ -326,8 +326,10 @@ static bool nf_flow_ip4_tunnel_proto(struct nf_flowtable_ctx *ctx, return false; iph = (struct iphdr *)(skb_network_header(skb) + ctx->offset); - size = iph->ihl << 2; + if (iph->ihl < 5) + return false; + size = iph->ihl << 2; if (ip_is_fragment(iph) || unlikely(ip_has_options(size))) return false; @@ -335,9 +337,9 @@ static bool nf_flow_ip4_tunnel_proto(struct nf_flowtable_ctx *ctx, return false; if (iph->protocol == IPPROTO_IPIP) { - ctx->tun.proto = IPPROTO_IPIP; + ctx->tun.proto = iph->protocol; ctx->tun.hdr_size = size; - ctx->offset += size; + ctx->offset += ctx->tun.hdr_size; } return true; From 2f71ca368ffdea0d5245f45d06ba8b31e13b6e6e Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Thu, 11 Jun 2026 12:21:20 +0800 Subject: [PATCH 1904/2758] netfilter: nft_synproxy: stop bypassing the priv->info snapshot [ Upstream commit 11d4bc4e26fb66040a5b5d95e9abf37deac2b1fc ] nft_synproxy_eval_v4() and nft_synproxy_eval_v6() already take a whole-object READ_ONCE() snapshot of the shared priv->info state before building the SYNACK reply, but nft_synproxy_tcp_options() still masks opts->options with priv->info.options from the live shared object. When a named synproxy object is updated concurrently with SYN traffic, the eval path can then mix mss and timestamp handling from the local snapshot with an options mask taken from a newer configuration, so one SYNACK no longer reflects a coherent synproxy configuration. Use info->options so nft_synproxy_tcp_options() stays on the same local snapshot that the eval path already copied from priv->info. Fixes: ee394f96ad75 ("netfilter: nft_synproxy: add synproxy stateful object support") Signed-off-by: Runyu Xiao Reviewed-by: Fernando Fernandez Mancera Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nft_synproxy.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/net/netfilter/nft_synproxy.c b/net/netfilter/nft_synproxy.c index 7641f249614c9..9ed288c9d1688 100644 --- a/net/netfilter/nft_synproxy.c +++ b/net/netfilter/nft_synproxy.c @@ -24,14 +24,13 @@ static const struct nla_policy nft_synproxy_policy[NFTA_SYNPROXY_MAX + 1] = { static void nft_synproxy_tcp_options(struct synproxy_options *opts, const struct tcphdr *tcp, struct synproxy_net *snet, - struct nf_synproxy_info *info, - const struct nft_synproxy *priv) + struct nf_synproxy_info *info) { this_cpu_inc(snet->stats->syn_received); if (tcp->ece && tcp->cwr) opts->options |= NF_SYNPROXY_OPT_ECN; - opts->options &= priv->info.options; + opts->options &= info->options; opts->mss_encode = opts->mss_option; opts->mss_option = info->mss; if (opts->options & NF_SYNPROXY_OPT_TIMESTAMP) @@ -56,7 +55,7 @@ static void nft_synproxy_eval_v4(const struct nft_synproxy *priv, if (tcp->syn) { /* Initial SYN from client */ - nft_synproxy_tcp_options(opts, tcp, snet, &info, priv); + nft_synproxy_tcp_options(opts, tcp, snet, &info); synproxy_send_client_synack(net, skb, tcp, opts); consume_skb(skb); regs->verdict.code = NF_STOLEN; @@ -87,7 +86,7 @@ static void nft_synproxy_eval_v6(const struct nft_synproxy *priv, if (tcp->syn) { /* Initial SYN from client */ - nft_synproxy_tcp_options(opts, tcp, snet, &info, priv); + nft_synproxy_tcp_options(opts, tcp, snet, &info); synproxy_send_client_synack_ipv6(net, skb, tcp, opts); consume_skb(skb); regs->verdict.code = NF_STOLEN; From c129b0185e707dce405968e21afccd5728b2ce63 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 15 Jun 2026 20:10:44 +0200 Subject: [PATCH 1905/2758] netfilter: nft_compat: ebtables emulation must reject non-bridge targets [ Upstream commit 9dbba7e694ec045f21ede2f892fb42b81b4e1692 ] xtables targets return netfilter verdicts: NF_ACCEPT, NF_DROP, and so on. ebtables targets return incompatible verdicts: EBT_ACCEPT, EBT_DROP, ... We cannot allow fallback to NFPROTO_UNSPEC. ebtables doesn't permit this since 11ff7288beb2 ("netfilter: ebtables: reject non-bridge targets") but that commit missed the nft_compat layer. Reported-by: Ren Wei Reported-by: Wyatt Feng Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Zhengchuan Liang Reported-by: Xin Liu Fixes: 0ca743a55991 ("netfilter: nf_tables: add compatibility layer for x_tables") Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nft_compat.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c index 0caa9304d2d03..63864b9282590 100644 --- a/net/netfilter/nft_compat.c +++ b/net/netfilter/nft_compat.c @@ -397,6 +397,22 @@ static int nft_target_validate(const struct nft_ctx *ctx, return 0; } +static int nft_target_bridge_validate(const struct nft_ctx *ctx, + const struct nft_expr *expr) +{ + struct xt_target *target = expr->ops->data; + + /* Do not allow UNSPEC to stand-in for NFPROTO_BRIDGE + * targets: they are incompatible. ebtables targets return + * EBT_ACCEPT, DROP and so on which are not compatible with + * NF_ACCEPT, NF_DROP and so on. + */ + if (target->family != NFPROTO_BRIDGE) + return -ENOENT; + + return nft_target_validate(ctx, expr); +} + static void __nft_match_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt, @@ -932,13 +948,15 @@ nft_target_select_ops(const struct nft_ctx *ctx, ops->init = nft_target_init; ops->destroy = nft_target_destroy; ops->dump = nft_target_dump; - ops->validate = nft_target_validate; ops->data = target; - if (family == NFPROTO_BRIDGE) + if (family == NFPROTO_BRIDGE) { ops->eval = nft_target_eval_bridge; - else + ops->validate = nft_target_bridge_validate; + } else { ops->eval = nft_target_eval_xt; + ops->validate = nft_target_validate; + } return ops; err: From 40c14ce49963b264414b2f4f85add383d84f7d5f Mon Sep 17 00:00:00 2001 From: Qingshuang Fu Date: Tue, 23 Jun 2026 10:31:06 +0800 Subject: [PATCH 1906/2758] gpio: davinci: fix IRQ domain leak on devm_kzalloc failure [ Upstream commit 4e8eb6952aa6749726c6c3763ae0032a6332c24f ] In davinci_gpio_irq_setup(), after successfully creating an IRQ domain with irq_domain_create_legacy(), a subsequent devm_kzalloc() failure in the bank loop causes the function to return -ENOMEM without removing the IRQ domain. Unlike devm-managed resources, irq_domain_create_legacy() does not auto-clean up on probe failure, so the domain is leaked. Fix by calling irq_domain_remove() before returning on allocation failure. Fixes: b5cf3fd827d2 ("gpio: davinci: Redesign driver to accommodate ngpios in one gpio chip") Signed-off-by: Qingshuang Fu Link: https://patch.msgid.link/20260623023106.117229-1-fffsqian@163.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpio-davinci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index 97780f27ce5b5..270cd7c888120 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -568,8 +568,10 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev) sizeof(struct davinci_gpio_irq_data), GFP_KERNEL); - if (!irqdata) + if (!irqdata) { + irq_domain_remove(chips->irq_domain); return -ENOMEM; + } irqdata->regs = g; irqdata->bank_num = bank; From a1572284b14e5762cf821ee85f81dd9d27668cb7 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 4 Jun 2026 08:21:09 +0200 Subject: [PATCH 1907/2758] netfilter: nf_conntrack_helper: dynamically allocate struct nf_conntrack_helper [ Upstream commit 6031487d4e273d7e7c8c7deea5061b7a9aaa9db3 ] Adapt all existing helpers to use a modified version of nf_ct_helper_init(), to dynamically allocate struct nf_conntrack_helper. Allocate expect_policy[] built-in into the helper to ensure this area is reachable after helper removal since a follow up patch adds refcount to track use of the nf_conntrack_helper structure from packet path so it remains around until last reference from ct helper extension is dropped. Export __nf_conntrack_helper_register() which allows to register nfnetlink_cthelper dynamically allocated helper. Adapt nfnetlink_cthelper to use the built-in expect_policy[]. This is a preparation patch to add packet path refcounting to helpers. Signed-off-by: Pablo Neira Ayuso Stable-dep-of: 979c13114c0b ("netfilter: nf_conntrack_expect: store master_tuple in expectation") Signed-off-by: Sasha Levin --- include/net/netfilter/nf_conntrack_helper.h | 16 +-- net/ipv4/netfilter/nf_nat_snmp_basic_main.c | 27 +++-- net/netfilter/nf_conntrack_amanda.c | 39 +++---- net/netfilter/nf_conntrack_ftp.c | 5 +- net/netfilter/nf_conntrack_h323_main.c | 107 ++++++++------------ net/netfilter/nf_conntrack_helper.c | 75 +++++++++++--- net/netfilter/nf_conntrack_irc.c | 5 +- net/netfilter/nf_conntrack_netbios_ns.c | 20 ++-- net/netfilter/nf_conntrack_pptp.c | 22 ++-- net/netfilter/nf_conntrack_sane.c | 5 +- net/netfilter/nf_conntrack_sip.c | 5 +- net/netfilter/nf_conntrack_snmp.c | 21 ++-- net/netfilter/nf_conntrack_tftp.c | 5 +- net/netfilter/nfnetlink_cthelper.c | 47 ++++----- 14 files changed, 210 insertions(+), 189 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index 24cf3d2d97450..18dfb3a287cf1 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h @@ -29,13 +29,16 @@ enum nf_ct_helper_flags { #define NF_CT_HELPER_NAME_LEN 16 +/* Must be kept in sync with the classes defined by helpers */ +#define NF_CT_MAX_EXPECT_CLASSES 4 + struct nf_conntrack_helper { struct hlist_node hnode; /* Internal use. */ char name[NF_CT_HELPER_NAME_LEN]; /* name of the module */ refcount_t refcnt; struct module *me; /* pointer to self */ - const struct nf_conntrack_expect_policy *expect_policy; + struct nf_conntrack_expect_policy expect_policy[NF_CT_MAX_EXPECT_CLASSES]; /* Tuple of things we will help (compared against server response) */ struct nf_conntrack_tuple tuple; @@ -63,9 +66,6 @@ struct nf_conntrack_helper { char nat_mod_name[NF_CT_HELPER_NAME_LEN]; }; -/* Must be kept in sync with the classes defined by helpers */ -#define NF_CT_MAX_EXPECT_CLASSES 4 - /* nf_conn feature for connections that have a helper */ struct nf_conn_help { /* Helper. if any */ @@ -103,11 +103,13 @@ void nf_ct_helper_init(struct nf_conntrack_helper *helper, struct nf_conn *ct), struct module *module); -int nf_conntrack_helper_register(struct nf_conntrack_helper *); +int nf_conntrack_helper_register(struct nf_conntrack_helper *, struct nf_conntrack_helper **); +int __nf_conntrack_helper_register(struct nf_conntrack_helper *); void nf_conntrack_helper_unregister(struct nf_conntrack_helper *); -int nf_conntrack_helpers_register(struct nf_conntrack_helper *, unsigned int); -void nf_conntrack_helpers_unregister(struct nf_conntrack_helper *, +int nf_conntrack_helpers_register(struct nf_conntrack_helper *, unsigned int, + struct nf_conntrack_helper **); +void nf_conntrack_helpers_unregister(struct nf_conntrack_helper **, unsigned int); struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp); diff --git a/net/ipv4/netfilter/nf_nat_snmp_basic_main.c b/net/ipv4/netfilter/nf_nat_snmp_basic_main.c index 717b726504feb..0ede138dfd29e 100644 --- a/net/ipv4/netfilter/nf_nat_snmp_basic_main.c +++ b/net/ipv4/netfilter/nf_nat_snmp_basic_main.c @@ -202,29 +202,34 @@ static const struct nf_conntrack_expect_policy snmp_exp_policy = { .timeout = 180, }; -static struct nf_conntrack_helper snmp_trap_helper __read_mostly = { - .me = THIS_MODULE, - .help = help, - .expect_policy = &snmp_exp_policy, - .name = "snmp_trap", - .tuple.src.l3num = AF_INET, - .tuple.src.u.udp.port = cpu_to_be16(SNMP_TRAP_PORT), - .tuple.dst.protonum = IPPROTO_UDP, -}; +static struct nf_conntrack_helper snmp_trap_helper __read_mostly; +static struct nf_conntrack_helper *snmp_trap_helper_ptr __read_mostly; static int __init nf_nat_snmp_basic_init(void) { + int err; + BUG_ON(nf_nat_snmp_hook != NULL); RCU_INIT_POINTER(nf_nat_snmp_hook, help); - return nf_conntrack_helper_register(&snmp_trap_helper); + nf_ct_helper_init(&snmp_trap_helper, AF_INET, IPPROTO_UDP, + "snmp_trap", SNMP_TRAP_PORT, SNMP_TRAP_PORT, SNMP_TRAP_PORT, + &snmp_exp_policy, 0, help, NULL, THIS_MODULE); + + err = nf_conntrack_helper_register(&snmp_trap_helper, &snmp_trap_helper_ptr); + if (err < 0) { + RCU_INIT_POINTER(nf_nat_snmp_hook, NULL); + return err; + } + + return 0; } static void __exit nf_nat_snmp_basic_fini(void) { RCU_INIT_POINTER(nf_nat_snmp_hook, NULL); synchronize_rcu(); - nf_conntrack_helper_unregister(&snmp_trap_helper); + nf_conntrack_helper_unregister(snmp_trap_helper_ptr); } module_init(nf_nat_snmp_basic_init); diff --git a/net/netfilter/nf_conntrack_amanda.c b/net/netfilter/nf_conntrack_amanda.c index d2c09e8dd8726..ddafbdfc96dc3 100644 --- a/net/netfilter/nf_conntrack_amanda.c +++ b/net/netfilter/nf_conntrack_amanda.c @@ -169,35 +169,15 @@ static const struct nf_conntrack_expect_policy amanda_exp_policy = { .timeout = 180, }; -static struct nf_conntrack_helper amanda_helper[2] __read_mostly = { - { - .name = HELPER_NAME, - .me = THIS_MODULE, - .help = amanda_help, - .tuple.src.l3num = AF_INET, - .tuple.src.u.udp.port = cpu_to_be16(10080), - .tuple.dst.protonum = IPPROTO_UDP, - .expect_policy = &amanda_exp_policy, - .nat_mod_name = NF_NAT_HELPER_NAME(HELPER_NAME), - }, - { - .name = "amanda", - .me = THIS_MODULE, - .help = amanda_help, - .tuple.src.l3num = AF_INET6, - .tuple.src.u.udp.port = cpu_to_be16(10080), - .tuple.dst.protonum = IPPROTO_UDP, - .expect_policy = &amanda_exp_policy, - .nat_mod_name = NF_NAT_HELPER_NAME(HELPER_NAME), - }, -}; +static struct nf_conntrack_helper amanda_helper[2] __read_mostly; +static struct nf_conntrack_helper *amanda_helper_ptr[2] __read_mostly; static void __exit nf_conntrack_amanda_fini(void) { int i; - nf_conntrack_helpers_unregister(amanda_helper, - ARRAY_SIZE(amanda_helper)); + nf_conntrack_helpers_unregister(amanda_helper_ptr, + ARRAY_SIZE(amanda_helper_ptr)); for (i = 0; i < ARRAY_SIZE(search); i++) textsearch_destroy(search[i].ts); } @@ -217,8 +197,17 @@ static int __init nf_conntrack_amanda_init(void) goto err1; } } + + nf_ct_helper_init(&amanda_helper[0], AF_INET, IPPROTO_UDP, + HELPER_NAME, 10080, 10080, 10080, + &amanda_exp_policy, 0, amanda_help, NULL, THIS_MODULE); + nf_ct_helper_init(&amanda_helper[1], AF_INET6, IPPROTO_UDP, + HELPER_NAME, 10080, 10080, 10080, + &amanda_exp_policy, 0, amanda_help, NULL, THIS_MODULE); + ret = nf_conntrack_helpers_register(amanda_helper, - ARRAY_SIZE(amanda_helper)); + ARRAY_SIZE(amanda_helper), + amanda_helper_ptr); if (ret < 0) goto err1; return 0; diff --git a/net/netfilter/nf_conntrack_ftp.c b/net/netfilter/nf_conntrack_ftp.c index de83bf9e6c61a..b21da0c788458 100644 --- a/net/netfilter/nf_conntrack_ftp.c +++ b/net/netfilter/nf_conntrack_ftp.c @@ -552,6 +552,7 @@ static int nf_ct_ftp_from_nlattr(struct nlattr *attr, struct nf_conn *ct) } static struct nf_conntrack_helper ftp[MAX_PORTS * 2] __read_mostly; +static struct nf_conntrack_helper *ftp_ptr[MAX_PORTS * 2] __read_mostly; static const struct nf_conntrack_expect_policy ftp_exp_policy = { .max_expected = 1, @@ -560,7 +561,7 @@ static const struct nf_conntrack_expect_policy ftp_exp_policy = { static void __exit nf_conntrack_ftp_fini(void) { - nf_conntrack_helpers_unregister(ftp, ports_c * 2); + nf_conntrack_helpers_unregister(ftp_ptr, ports_c * 2); } static int __init nf_conntrack_ftp_init(void) @@ -585,7 +586,7 @@ static int __init nf_conntrack_ftp_init(void) nf_ct_ftp_from_nlattr, THIS_MODULE); } - ret = nf_conntrack_helpers_register(ftp, ports_c * 2); + ret = nf_conntrack_helpers_register(ftp, ports_c * 2, ftp_ptr); if (ret < 0) { pr_err("failed to register helpers\n"); return ret; diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c index b2fe6554b9cf4..ebae9fdab8978 100644 --- a/net/netfilter/nf_conntrack_h323_main.c +++ b/net/netfilter/nf_conntrack_h323_main.c @@ -577,14 +577,8 @@ static const struct nf_conntrack_expect_policy h245_exp_policy = { .timeout = 240, }; -static struct nf_conntrack_helper nf_conntrack_helper_h245 __read_mostly = { - .name = "H.245", - .me = THIS_MODULE, - .tuple.src.l3num = AF_UNSPEC, - .tuple.dst.protonum = IPPROTO_UDP, - .help = h245_help, - .expect_policy = &h245_exp_policy, -}; +static struct nf_conntrack_helper nf_conntrack_helper_h245 __read_mostly; +static struct nf_conntrack_helper *nf_conntrack_helper_h245_ptr __read_mostly; int get_h225_addr(struct nf_conn *ct, unsigned char *data, TransportAddress *taddr, @@ -643,7 +637,7 @@ static int expect_h245(struct sk_buff *skb, struct nf_conn *ct, &ct->tuplehash[!dir].tuple.src.u3, &ct->tuplehash[!dir].tuple.dst.u3, IPPROTO_TCP, NULL, &port); - rcu_assign_pointer(exp->assign_helper, &nf_conntrack_helper_h245); + rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_h245_ptr); nathook = rcu_dereference(nfct_h323_nat_hook); if (memcmp(&ct->tuplehash[dir].tuple.src.u3, @@ -732,6 +726,9 @@ static int callforward_do_filter(struct net *net, } +static struct nf_conntrack_helper nf_conntrack_helper_q931[2] __read_mostly; +static struct nf_conntrack_helper *nf_conntrack_helper_q931_ptr[2] __read_mostly; + static int expect_callforwarding(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, @@ -767,7 +764,7 @@ static int expect_callforwarding(struct sk_buff *skb, nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct), &ct->tuplehash[!dir].tuple.src.u3, &addr, IPPROTO_TCP, NULL, &port); - rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931); + rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931_ptr[0]); nathook = rcu_dereference(nfct_h323_nat_hook); if (memcmp(&ct->tuplehash[dir].tuple.src.u3, @@ -1140,27 +1137,6 @@ static const struct nf_conntrack_expect_policy q931_exp_policy = { .timeout = 240, }; -static struct nf_conntrack_helper nf_conntrack_helper_q931[] __read_mostly = { - { - .name = "Q.931", - .me = THIS_MODULE, - .tuple.src.l3num = AF_INET, - .tuple.src.u.tcp.port = cpu_to_be16(Q931_PORT), - .tuple.dst.protonum = IPPROTO_TCP, - .help = q931_help, - .expect_policy = &q931_exp_policy, - }, - { - .name = "Q.931", - .me = THIS_MODULE, - .tuple.src.l3num = AF_INET6, - .tuple.src.u.tcp.port = cpu_to_be16(Q931_PORT), - .tuple.dst.protonum = IPPROTO_TCP, - .help = q931_help, - .expect_policy = &q931_exp_policy, - }, -}; - static unsigned char *get_udp_data(struct sk_buff *skb, unsigned int protoff, int *datalen) { @@ -1234,7 +1210,7 @@ static int expect_q931(struct sk_buff *skb, struct nf_conn *ct, &ct->tuplehash[!dir].tuple.src.u3 : NULL, &ct->tuplehash[!dir].tuple.dst.u3, IPPROTO_TCP, NULL, &port); - rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931); + rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931_ptr[0]); exp->flags = NF_CT_EXPECT_PERMANENT; /* Accept multiple calls */ nathook = rcu_dereference(nfct_h323_nat_hook); @@ -1275,6 +1251,9 @@ static int process_grq(struct sk_buff *skb, struct nf_conn *ct, return 0; } +static struct nf_conntrack_helper nf_conntrack_helper_ras[2] __read_mostly; +static struct nf_conntrack_helper *nf_conntrack_helper_ras_ptr[2] __read_mostly; + static int process_gcf(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, unsigned int protoff, @@ -1306,7 +1285,7 @@ static int process_gcf(struct sk_buff *skb, struct nf_conn *ct, nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct), &ct->tuplehash[!dir].tuple.src.u3, &addr, IPPROTO_UDP, NULL, &port); - rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_ras); + rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_ras_ptr[0]); if (nf_ct_expect_related(exp, 0) == 0) { pr_debug("nf_ct_ras: expect RAS "); @@ -1523,7 +1502,7 @@ static int process_acf(struct sk_buff *skb, struct nf_conn *ct, &ct->tuplehash[!dir].tuple.src.u3, &addr, IPPROTO_TCP, NULL, &port); exp->flags = NF_CT_EXPECT_PERMANENT; - rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931); + rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931_ptr[0]); if (nf_ct_expect_related(exp, 0) == 0) { pr_debug("nf_ct_ras: expect Q.931 "); @@ -1577,7 +1556,7 @@ static int process_lcf(struct sk_buff *skb, struct nf_conn *ct, &ct->tuplehash[!dir].tuple.src.u3, &addr, IPPROTO_TCP, NULL, &port); exp->flags = NF_CT_EXPECT_PERMANENT; - rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931); + rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931_ptr[0]); if (nf_ct_expect_related(exp, 0) == 0) { pr_debug("nf_ct_ras: expect Q.931 "); @@ -1711,59 +1690,57 @@ static const struct nf_conntrack_expect_policy ras_exp_policy = { .timeout = 240, }; -static struct nf_conntrack_helper nf_conntrack_helper_ras[] __read_mostly = { - { - .name = "RAS", - .me = THIS_MODULE, - .tuple.src.l3num = AF_INET, - .tuple.src.u.udp.port = cpu_to_be16(RAS_PORT), - .tuple.dst.protonum = IPPROTO_UDP, - .help = ras_help, - .expect_policy = &ras_exp_policy, - }, - { - .name = "RAS", - .me = THIS_MODULE, - .tuple.src.l3num = AF_INET6, - .tuple.src.u.udp.port = cpu_to_be16(RAS_PORT), - .tuple.dst.protonum = IPPROTO_UDP, - .help = ras_help, - .expect_policy = &ras_exp_policy, - }, -}; - static int __init h323_helper_init(void) { int ret; - ret = nf_conntrack_helper_register(&nf_conntrack_helper_h245); + nf_ct_helper_init(&nf_conntrack_helper_ras[0], AF_INET, IPPROTO_UDP, + "RAS", RAS_PORT, RAS_PORT, RAS_PORT, + &ras_exp_policy, 0, ras_help, NULL, THIS_MODULE); + nf_ct_helper_init(&nf_conntrack_helper_ras[1], AF_INET6, IPPROTO_UDP, + "RAS", RAS_PORT, RAS_PORT, RAS_PORT, + &ras_exp_policy, 0, ras_help, NULL, THIS_MODULE); + nf_ct_helper_init(&nf_conntrack_helper_h245, AF_UNSPEC, IPPROTO_UDP, + "H.245", 0, 0, 0, + &h245_exp_policy, 0, h245_help, NULL, THIS_MODULE); + nf_ct_helper_init(&nf_conntrack_helper_q931[0], AF_INET, IPPROTO_TCP, + "Q.931", Q931_PORT, Q931_PORT, Q931_PORT, + &q931_exp_policy, 0, q931_help, NULL, THIS_MODULE); + nf_ct_helper_init(&nf_conntrack_helper_q931[1], AF_INET6, IPPROTO_TCP, + "Q.931", Q931_PORT, Q931_PORT, Q931_PORT, + &q931_exp_policy, 0, q931_help, NULL, THIS_MODULE); + + ret = nf_conntrack_helper_register(&nf_conntrack_helper_h245, + &nf_conntrack_helper_h245_ptr); if (ret < 0) return ret; ret = nf_conntrack_helpers_register(nf_conntrack_helper_q931, - ARRAY_SIZE(nf_conntrack_helper_q931)); + ARRAY_SIZE(nf_conntrack_helper_q931), + nf_conntrack_helper_q931_ptr); if (ret < 0) goto err1; ret = nf_conntrack_helpers_register(nf_conntrack_helper_ras, - ARRAY_SIZE(nf_conntrack_helper_ras)); + ARRAY_SIZE(nf_conntrack_helper_ras), + nf_conntrack_helper_ras_ptr); if (ret < 0) goto err2; return 0; err2: - nf_conntrack_helpers_unregister(nf_conntrack_helper_q931, - ARRAY_SIZE(nf_conntrack_helper_q931)); + nf_conntrack_helpers_unregister(nf_conntrack_helper_q931_ptr, + ARRAY_SIZE(nf_conntrack_helper_q931_ptr)); err1: - nf_conntrack_helper_unregister(&nf_conntrack_helper_h245); + nf_conntrack_helper_unregister(nf_conntrack_helper_h245_ptr); return ret; } static void __exit h323_helper_exit(void) { - nf_conntrack_helpers_unregister(nf_conntrack_helper_ras, + nf_conntrack_helpers_unregister(nf_conntrack_helper_ras_ptr, ARRAY_SIZE(nf_conntrack_helper_ras)); - nf_conntrack_helpers_unregister(nf_conntrack_helper_q931, + nf_conntrack_helpers_unregister(nf_conntrack_helper_q931_ptr, ARRAY_SIZE(nf_conntrack_helper_q931)); - nf_conntrack_helper_unregister(&nf_conntrack_helper_h245); + nf_conntrack_helper_unregister(nf_conntrack_helper_h245_ptr); } static void __exit nf_conntrack_h323_fini(void) diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 2c5a717355612..5e2bb104eb49d 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -366,14 +366,13 @@ void nf_ct_helper_log(struct sk_buff *skb, const struct nf_conn *ct, } EXPORT_SYMBOL_GPL(nf_ct_helper_log); -int nf_conntrack_helper_register(struct nf_conntrack_helper *me) +int __nf_conntrack_helper_register(struct nf_conntrack_helper *me) { struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) }; unsigned int h = helper_hash(&me->tuple); struct nf_conntrack_helper *cur; int ret = 0, i; - BUG_ON(me->expect_policy == NULL); BUG_ON(me->expect_class_max >= NF_CT_MAX_EXPECT_CLASSES); BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - 1); @@ -413,6 +412,33 @@ int nf_conntrack_helper_register(struct nf_conntrack_helper *me) mutex_unlock(&nf_ct_helper_mutex); return ret; } +EXPORT_SYMBOL_GPL(__nf_conntrack_helper_register); + +int nf_conntrack_helper_register(struct nf_conntrack_helper *me, + struct nf_conntrack_helper **helper_ptr) +{ + struct nf_conntrack_helper *new_helper; + int err; + + new_helper = kzalloc_obj(*new_helper, GFP_KERNEL_ACCOUNT); + if (!new_helper) + return -ENOMEM; + + memcpy(new_helper, me, sizeof(*new_helper)); + *helper_ptr = new_helper; + + err = __nf_conntrack_helper_register(new_helper); + if (err < 0) + goto err_helper; + + return 0; + +err_helper: + *helper_ptr = NULL; + kfree(new_helper); + + return err; +} EXPORT_SYMBOL_GPL(nf_conntrack_helper_register); static bool expect_iter_me(struct nf_conntrack_expect *exp, void *data) @@ -449,6 +475,7 @@ void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me) * last step, this ensures rcu readers of exp->helper are done. * No need for another synchronize_rcu() here. */ + kfree(me); } EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister); @@ -464,11 +491,12 @@ void nf_ct_helper_init(struct nf_conntrack_helper *helper, struct nf_conn *ct), struct module *module) { + memset(helper, 0, sizeof(*helper)); + helper->tuple.src.l3num = l3num; helper->tuple.dst.protonum = protonum; helper->tuple.src.u.all = htons(spec_port); - helper->expect_policy = exp_pol; - helper->expect_class_max = expect_class_max; + helper->help = help; helper->from_nlattr = from_nlattr; helper->me = module; @@ -479,34 +507,57 @@ void nf_ct_helper_init(struct nf_conntrack_helper *helper, snprintf(helper->name, sizeof(helper->name), "%s", name); else snprintf(helper->name, sizeof(helper->name), "%s-%u", name, id); + + if (WARN_ON_ONCE(expect_class_max >= NF_CT_MAX_EXPECT_CLASSES)) + return; + + memcpy(helper->expect_policy, exp_pol, + (expect_class_max + 1) * sizeof(*exp_pol)); + helper->expect_class_max = expect_class_max; } EXPORT_SYMBOL_GPL(nf_ct_helper_init); int nf_conntrack_helpers_register(struct nf_conntrack_helper *helper, - unsigned int n) + unsigned int n, struct nf_conntrack_helper **helper_ptr) { + struct nf_conntrack_helper *new_helper; unsigned int i; int err = 0; for (i = 0; i < n; i++) { - err = nf_conntrack_helper_register(&helper[i]); - if (err < 0) + new_helper = kzalloc_obj(*new_helper, GFP_KERNEL_ACCOUNT); + if (!new_helper) { + err = -ENOMEM; goto err; + } + + memcpy(new_helper, &helper[i], sizeof(*new_helper)); + helper_ptr[i] = new_helper; + + err = __nf_conntrack_helper_register(new_helper); + if (err < 0) { + helper_ptr[i] = NULL; + goto err_helper; + } } return err; +err_helper: + kfree(new_helper); err: if (i > 0) - nf_conntrack_helpers_unregister(helper, i); + nf_conntrack_helpers_unregister(helper_ptr, i); return err; } EXPORT_SYMBOL_GPL(nf_conntrack_helpers_register); -void nf_conntrack_helpers_unregister(struct nf_conntrack_helper *helper, - unsigned int n) +void nf_conntrack_helpers_unregister(struct nf_conntrack_helper **helper, + unsigned int n) { - while (n-- > 0) - nf_conntrack_helper_unregister(&helper[n]); + while (n-- > 0) { + nf_conntrack_helper_unregister(helper[n]); + helper[n] = NULL; + } } EXPORT_SYMBOL_GPL(nf_conntrack_helpers_unregister); diff --git a/net/netfilter/nf_conntrack_irc.c b/net/netfilter/nf_conntrack_irc.c index 2ebe4cb47cf61..6a72848bd3513 100644 --- a/net/netfilter/nf_conntrack_irc.c +++ b/net/netfilter/nf_conntrack_irc.c @@ -255,6 +255,7 @@ static int help(struct sk_buff *skb, unsigned int protoff, } static struct nf_conntrack_helper irc[MAX_PORTS] __read_mostly; +static struct nf_conntrack_helper *irc_ptr[MAX_PORTS] __read_mostly; static struct nf_conntrack_expect_policy irc_exp_policy; static int __init nf_conntrack_irc_init(void) @@ -289,7 +290,7 @@ static int __init nf_conntrack_irc_init(void) 0, help, NULL, THIS_MODULE); } - ret = nf_conntrack_helpers_register(&irc[0], ports_c); + ret = nf_conntrack_helpers_register(&irc[0], ports_c, irc_ptr); if (ret) { pr_err("failed to register helpers\n"); kfree(irc_buffer); @@ -301,7 +302,7 @@ static int __init nf_conntrack_irc_init(void) static void __exit nf_conntrack_irc_fini(void) { - nf_conntrack_helpers_unregister(irc, ports_c); + nf_conntrack_helpers_unregister(irc_ptr, ports_c); kfree(irc_buffer); } diff --git a/net/netfilter/nf_conntrack_netbios_ns.c b/net/netfilter/nf_conntrack_netbios_ns.c index 55415f011943d..89d1cf7d6512e 100644 --- a/net/netfilter/nf_conntrack_netbios_ns.c +++ b/net/netfilter/nf_conntrack_netbios_ns.c @@ -44,27 +44,25 @@ static int netbios_ns_help(struct sk_buff *skb, unsigned int protoff, return nf_conntrack_broadcast_help(skb, ct, ctinfo, timeout); } -static struct nf_conntrack_helper helper __read_mostly = { - .name = HELPER_NAME, - .tuple.src.l3num = NFPROTO_IPV4, - .tuple.src.u.udp.port = cpu_to_be16(NMBD_PORT), - .tuple.dst.protonum = IPPROTO_UDP, - .me = THIS_MODULE, - .help = netbios_ns_help, - .expect_policy = &exp_policy, -}; +static struct nf_conntrack_helper helper __read_mostly; +static struct nf_conntrack_helper *helper_ptr __read_mostly; static int __init nf_conntrack_netbios_ns_init(void) { NF_CT_HELPER_BUILD_BUG_ON(0); exp_policy.timeout = timeout; - return nf_conntrack_helper_register(&helper); + + nf_ct_helper_init(&helper, AF_INET, IPPROTO_UDP, HELPER_NAME, + NMBD_PORT, NMBD_PORT, NMBD_PORT, + &exp_policy, 0, netbios_ns_help, NULL, THIS_MODULE); + + return nf_conntrack_helper_register(&helper, &helper_ptr); } static void __exit nf_conntrack_netbios_ns_fini(void) { - nf_conntrack_helper_unregister(&helper); + nf_conntrack_helper_unregister(helper_ptr); } module_init(nf_conntrack_netbios_ns_init); diff --git a/net/netfilter/nf_conntrack_pptp.c b/net/netfilter/nf_conntrack_pptp.c index dc23e4181618a..edc85a3eef1e1 100644 --- a/net/netfilter/nf_conntrack_pptp.c +++ b/net/netfilter/nf_conntrack_pptp.c @@ -582,27 +582,25 @@ static const struct nf_conntrack_expect_policy pptp_exp_policy = { }; /* control protocol helper */ -static struct nf_conntrack_helper pptp __read_mostly = { - .name = "pptp", - .me = THIS_MODULE, - .tuple.src.l3num = AF_INET, - .tuple.src.u.tcp.port = cpu_to_be16(PPTP_CONTROL_PORT), - .tuple.dst.protonum = IPPROTO_TCP, - .help = conntrack_pptp_help, - .destroy = pptp_destroy_siblings, - .expect_policy = &pptp_exp_policy, -}; +static struct nf_conntrack_helper pptp __read_mostly; +static struct nf_conntrack_helper *pptp_ptr __read_mostly; static int __init nf_conntrack_pptp_init(void) { NF_CT_HELPER_BUILD_BUG_ON(sizeof(struct nf_ct_pptp_master)); - return nf_conntrack_helper_register(&pptp); + nf_ct_helper_init(&pptp, AF_INET, IPPROTO_TCP, + "pptp", PPTP_CONTROL_PORT, PPTP_CONTROL_PORT, PPTP_CONTROL_PORT, + &pptp_exp_policy, 0, conntrack_pptp_help, NULL, THIS_MODULE); + + pptp.destroy = pptp_destroy_siblings; + + return nf_conntrack_helper_register(&pptp, &pptp_ptr); } static void __exit nf_conntrack_pptp_fini(void) { - nf_conntrack_helper_unregister(&pptp); + nf_conntrack_helper_unregister(pptp_ptr); } module_init(nf_conntrack_pptp_init); diff --git a/net/netfilter/nf_conntrack_sane.c b/net/netfilter/nf_conntrack_sane.c index 13dc421fc4f52..a7f7b07ba0c2b 100644 --- a/net/netfilter/nf_conntrack_sane.c +++ b/net/netfilter/nf_conntrack_sane.c @@ -167,6 +167,7 @@ static int help(struct sk_buff *skb, } static struct nf_conntrack_helper sane[MAX_PORTS * 2] __read_mostly; +static struct nf_conntrack_helper *sane_ptr[MAX_PORTS * 2] __read_mostly; static const struct nf_conntrack_expect_policy sane_exp_policy = { .max_expected = 1, @@ -175,7 +176,7 @@ static const struct nf_conntrack_expect_policy sane_exp_policy = { static void __exit nf_conntrack_sane_fini(void) { - nf_conntrack_helpers_unregister(sane, ports_c * 2); + nf_conntrack_helpers_unregister(sane_ptr, ports_c * 2); } static int __init nf_conntrack_sane_init(void) @@ -200,7 +201,7 @@ static int __init nf_conntrack_sane_init(void) THIS_MODULE); } - ret = nf_conntrack_helpers_register(sane, ports_c * 2); + ret = nf_conntrack_helpers_register(sane, ports_c * 2, sane_ptr); if (ret < 0) { pr_err("failed to register helpers\n"); return ret; diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index e69941f1a1016..2c78a3e1dab5e 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -1731,6 +1731,7 @@ static int sip_help_udp(struct sk_buff *skb, unsigned int protoff, } static struct nf_conntrack_helper sip[MAX_PORTS * 4] __read_mostly; +static struct nf_conntrack_helper *sip_ptr[MAX_PORTS * 4] __read_mostly; static const struct nf_conntrack_expect_policy sip_exp_policy[SIP_EXPECT_MAX + 1] = { [SIP_EXPECT_SIGNALLING] = { @@ -1757,7 +1758,7 @@ static const struct nf_conntrack_expect_policy sip_exp_policy[SIP_EXPECT_MAX + 1 static void __exit nf_conntrack_sip_fini(void) { - nf_conntrack_helpers_unregister(sip, ports_c * 4); + nf_conntrack_helpers_unregister(sip_ptr, ports_c * 4); } static int __init nf_conntrack_sip_init(void) @@ -1788,7 +1789,7 @@ static int __init nf_conntrack_sip_init(void) NULL, THIS_MODULE); } - ret = nf_conntrack_helpers_register(sip, ports_c * 4); + ret = nf_conntrack_helpers_register(sip, ports_c * 4, sip_ptr); if (ret < 0) { pr_err("failed to register helpers\n"); return ret; diff --git a/net/netfilter/nf_conntrack_snmp.c b/net/netfilter/nf_conntrack_snmp.c index 7b7eed43c54f1..b6fce5703fce7 100644 --- a/net/netfilter/nf_conntrack_snmp.c +++ b/net/netfilter/nf_conntrack_snmp.c @@ -47,25 +47,24 @@ static struct nf_conntrack_expect_policy exp_policy = { .max_expected = 1, }; -static struct nf_conntrack_helper helper __read_mostly = { - .name = "snmp", - .tuple.src.l3num = NFPROTO_IPV4, - .tuple.src.u.udp.port = cpu_to_be16(SNMP_PORT), - .tuple.dst.protonum = IPPROTO_UDP, - .me = THIS_MODULE, - .help = snmp_conntrack_help, - .expect_policy = &exp_policy, -}; +static struct nf_conntrack_helper helper __read_mostly; +static struct nf_conntrack_helper *helper_ptr __read_mostly; static int __init nf_conntrack_snmp_init(void) { exp_policy.timeout = timeout; - return nf_conntrack_helper_register(&helper); + + nf_ct_helper_init(&helper, AF_INET, IPPROTO_UDP, + "snmp", SNMP_PORT, SNMP_PORT, SNMP_PORT, + &exp_policy, 0, snmp_conntrack_help, NULL, + THIS_MODULE); + + return nf_conntrack_helper_register(&helper, &helper_ptr); } static void __exit nf_conntrack_snmp_fini(void) { - nf_conntrack_helper_unregister(&helper); + nf_conntrack_helper_unregister(helper_ptr); } module_init(nf_conntrack_snmp_init); diff --git a/net/netfilter/nf_conntrack_tftp.c b/net/netfilter/nf_conntrack_tftp.c index a2e6833a0bf76..4393c435aa354 100644 --- a/net/netfilter/nf_conntrack_tftp.c +++ b/net/netfilter/nf_conntrack_tftp.c @@ -96,6 +96,7 @@ static int tftp_help(struct sk_buff *skb, } static struct nf_conntrack_helper tftp[MAX_PORTS * 2] __read_mostly; +static struct nf_conntrack_helper *tftp_ptr[MAX_PORTS * 2] __read_mostly; static const struct nf_conntrack_expect_policy tftp_exp_policy = { .max_expected = 1, @@ -104,7 +105,7 @@ static const struct nf_conntrack_expect_policy tftp_exp_policy = { static void __exit nf_conntrack_tftp_fini(void) { - nf_conntrack_helpers_unregister(tftp, ports_c * 2); + nf_conntrack_helpers_unregister(tftp_ptr, ports_c * 2); } static int __init nf_conntrack_tftp_init(void) @@ -127,7 +128,7 @@ static int __init nf_conntrack_tftp_init(void) THIS_MODULE); } - ret = nf_conntrack_helpers_register(tftp, ports_c * 2); + ret = nf_conntrack_helpers_register(tftp, ports_c * 2, tftp_ptr); if (ret < 0) { pr_err("failed to register helpers\n"); return ret; diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index 61a2407b53bd3..a74078ced52da 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -32,7 +32,7 @@ MODULE_DESCRIPTION("nfnl_cthelper: User-space connection tracking helpers"); struct nfnl_cthelper { struct list_head list; - struct nf_conntrack_helper helper; + struct nf_conntrack_helper *helper; }; static LIST_HEAD(nfnl_cthelper_list); @@ -176,7 +176,6 @@ nfnl_cthelper_parse_expect_policy(struct nf_conntrack_helper *helper, const struct nlattr *attr) { int i, ret; - struct nf_conntrack_expect_policy *expect_policy; struct nlattr *tb[NFCTH_POLICY_SET_MAX+1]; unsigned int class_max; @@ -195,26 +194,19 @@ nfnl_cthelper_parse_expect_policy(struct nf_conntrack_helper *helper, if (class_max > NF_CT_MAX_EXPECT_CLASSES) return -EOVERFLOW; - expect_policy = kzalloc_objs(struct nf_conntrack_expect_policy, - class_max); - if (expect_policy == NULL) - return -ENOMEM; - for (i = 0; i < class_max; i++) { if (!tb[NFCTH_POLICY_SET+i]) goto err; - ret = nfnl_cthelper_expect_policy(&expect_policy[i], + ret = nfnl_cthelper_expect_policy(&helper->expect_policy[i], tb[NFCTH_POLICY_SET+i]); if (ret < 0) goto err; } helper->expect_class_max = class_max - 1; - helper->expect_policy = expect_policy; return 0; err: - kfree(expect_policy); return -EINVAL; } @@ -230,21 +222,28 @@ nfnl_cthelper_create(const struct nlattr * const tb[], if (!tb[NFCTH_TUPLE] || !tb[NFCTH_POLICY] || !tb[NFCTH_PRIV_DATA_LEN]) return -EINVAL; - nfcth = kzalloc_obj(*nfcth); + nfcth = kzalloc_obj(*nfcth, GFP_KERNEL_ACCOUNT); if (nfcth == NULL) return -ENOMEM; - helper = &nfcth->helper; + + helper = kzalloc_obj(*helper, GFP_KERNEL_ACCOUNT); + if (!helper) { + ret = -ENOMEM; + goto err_cth; + } + + nfcth->helper = helper; ret = nfnl_cthelper_parse_expect_policy(helper, tb[NFCTH_POLICY]); if (ret < 0) - goto err1; + goto err_helper; nla_strscpy(helper->name, tb[NFCTH_NAME], NF_CT_HELPER_NAME_LEN); size = ntohl(nla_get_be32(tb[NFCTH_PRIV_DATA_LEN])); if (size > sizeof_field(struct nf_conn_help, data)) { ret = -ENOMEM; - goto err2; + goto err_helper; } helper->data_len = size; @@ -273,15 +272,15 @@ nfnl_cthelper_create(const struct nlattr * const tb[], } } - ret = nf_conntrack_helper_register(helper); + ret = __nf_conntrack_helper_register(helper); if (ret < 0) - goto err2; + goto err_helper; list_add_tail(&nfcth->list, &nfnl_cthelper_list); return 0; -err2: - kfree(helper->expect_policy); -err1: +err_helper: + kfree(helper); +err_cth: kfree(nfcth); return ret; } @@ -439,7 +438,7 @@ static int nfnl_cthelper_new(struct sk_buff *skb, const struct nfnl_info *info, return ret; list_for_each_entry(nlcth, &nfnl_cthelper_list, list) { - cur = &nlcth->helper; + cur = nlcth->helper; if (strncmp(cur->name, helper_name, NF_CT_HELPER_NAME_LEN)) continue; @@ -650,7 +649,7 @@ static int nfnl_cthelper_get(struct sk_buff *skb, const struct nfnl_info *info, } list_for_each_entry(nlcth, &nfnl_cthelper_list, list) { - cur = &nlcth->helper; + cur = nlcth->helper; if (helper_name && strncmp(cur->name, helper_name, NF_CT_HELPER_NAME_LEN)) continue; @@ -708,7 +707,7 @@ static int nfnl_cthelper_del(struct sk_buff *skb, const struct nfnl_info *info, ret = -ENOENT; list_for_each_entry_safe(nlcth, n, &nfnl_cthelper_list, list) { - cur = &nlcth->helper; + cur = nlcth->helper; j++; if (helper_name && @@ -723,7 +722,6 @@ static int nfnl_cthelper_del(struct sk_buff *skb, const struct nfnl_info *info, if (refcount_dec_if_one(&cur->refcnt)) { found = true; nf_conntrack_helper_unregister(cur); - kfree(cur->expect_policy); list_del(&nlcth->list); kfree(nlcth); @@ -796,10 +794,9 @@ static void __exit nfnl_cthelper_exit(void) nfnetlink_subsys_unregister(&nfnl_cthelper_subsys); list_for_each_entry_safe(nlcth, n, &nfnl_cthelper_list, list) { - cur = &nlcth->helper; + cur = nlcth->helper; nf_conntrack_helper_unregister(cur); - kfree(cur->expect_policy); kfree(nlcth); } } From ae568b6f16e0425f6c50c166850d389a85c62856 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 4 Jun 2026 08:21:10 +0200 Subject: [PATCH 1908/2758] netfilter: nf_conntrack_pptp: move GRE specific cleanup to GRE tracker [ Upstream commit fe97fd540a03034a780224f24b0b2f0e21c9c763 ] Move the GRE specific cleanup to nf_conntrack_proto_gre.c to ensure that the .destroy callback for the pptp helper is still reachable by existing conntrack entries while pptp module is being removed. This is a preparation patch, no functional changes are intended. Signed-off-by: Pablo Neira Ayuso Stable-dep-of: 979c13114c0b ("netfilter: nf_conntrack_expect: store master_tuple in expectation") Signed-off-by: Sasha Levin --- .../net/netfilter/ipv4/nf_conntrack_ipv4.h | 4 ++ net/netfilter/nf_conntrack_pptp.c | 63 +------------------ net/netfilter/nf_conntrack_proto_gre.c | 61 ++++++++++++++++++ 3 files changed, 67 insertions(+), 61 deletions(-) diff --git a/include/net/netfilter/ipv4/nf_conntrack_ipv4.h b/include/net/netfilter/ipv4/nf_conntrack_ipv4.h index b39417ad955ec..0b07d5e69c159 100644 --- a/include/net/netfilter/ipv4/nf_conntrack_ipv4.h +++ b/include/net/netfilter/ipv4/nf_conntrack_ipv4.h @@ -20,4 +20,8 @@ extern const struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp; extern const struct nf_conntrack_l4proto nf_conntrack_l4proto_gre; #endif +#if IS_ENABLED(CONFIG_NF_CONNTRACK_PPTP) +void gre_pptp_destroy_siblings(struct nf_conn *ct); +#endif + #endif /*_NF_CONNTRACK_IPV4_H*/ diff --git a/net/netfilter/nf_conntrack_pptp.c b/net/netfilter/nf_conntrack_pptp.c index edc85a3eef1e1..ed567a1cf7fd7 100644 --- a/net/netfilter/nf_conntrack_pptp.c +++ b/net/netfilter/nf_conntrack_pptp.c @@ -124,65 +124,6 @@ static void pptp_expectfn(struct nf_conn *ct, } } -static int destroy_sibling_or_exp(struct net *net, struct nf_conn *ct, - const struct nf_conntrack_tuple *t) -{ - const struct nf_conntrack_tuple_hash *h; - const struct nf_conntrack_zone *zone; - struct nf_conntrack_expect *exp; - struct nf_conn *sibling; - - pr_debug("trying to timeout ct or exp for tuple "); - nf_ct_dump_tuple(t); - - zone = nf_ct_zone(ct); - h = nf_conntrack_find_get(net, zone, t); - if (h) { - sibling = nf_ct_tuplehash_to_ctrack(h); - pr_debug("setting timeout of conntrack %p to 0\n", sibling); - sibling->proto.gre.timeout = 0; - sibling->proto.gre.stream_timeout = 0; - nf_ct_kill(sibling); - nf_ct_put(sibling); - return 1; - } else { - exp = nf_ct_expect_find_get(net, zone, t); - if (exp) { - pr_debug("unexpect_related of expect %p\n", exp); - nf_ct_unexpect_related(exp); - nf_ct_expect_put(exp); - return 1; - } - } - return 0; -} - -/* timeout GRE data connections */ -static void pptp_destroy_siblings(struct nf_conn *ct) -{ - struct net *net = nf_ct_net(ct); - const struct nf_ct_pptp_master *ct_pptp_info = nfct_help_data(ct); - struct nf_conntrack_tuple t; - - nf_ct_gre_keymap_destroy(ct); - - /* try original (pns->pac) tuple */ - memcpy(&t, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, sizeof(t)); - t.dst.protonum = IPPROTO_GRE; - t.src.u.gre.key = ct_pptp_info->pns_call_id; - t.dst.u.gre.key = ct_pptp_info->pac_call_id; - if (!destroy_sibling_or_exp(net, ct, &t)) - pr_debug("failed to timeout original pns->pac ct/exp\n"); - - /* try reply (pac->pns) tuple */ - memcpy(&t, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, sizeof(t)); - t.dst.protonum = IPPROTO_GRE; - t.src.u.gre.key = ct_pptp_info->pac_call_id; - t.dst.u.gre.key = ct_pptp_info->pns_call_id; - if (!destroy_sibling_or_exp(net, ct, &t)) - pr_debug("failed to timeout reply pac->pns ct/exp\n"); -} - /* expect GRE connections (PNS->PAC and PAC->PNS direction) */ static int exp_gre(struct nf_conn *ct, __be16 callid, __be16 peer_callid) { @@ -343,7 +284,7 @@ pptp_inbound_pkt(struct sk_buff *skb, unsigned int protoff, info->cstate = PPTP_CALL_NONE; /* untrack this call id, unexpect GRE packets */ - pptp_destroy_siblings(ct); + gre_pptp_destroy_siblings(ct); break; case PPTP_WAN_ERROR_NOTIFY: @@ -593,7 +534,7 @@ static int __init nf_conntrack_pptp_init(void) "pptp", PPTP_CONTROL_PORT, PPTP_CONTROL_PORT, PPTP_CONTROL_PORT, &pptp_exp_policy, 0, conntrack_pptp_help, NULL, THIS_MODULE); - pptp.destroy = pptp_destroy_siblings; + pptp.destroy = gre_pptp_destroy_siblings; return nf_conntrack_helper_register(&pptp, &pptp_ptr); } diff --git a/net/netfilter/nf_conntrack_proto_gre.c b/net/netfilter/nf_conntrack_proto_gre.c index 35e22082d65ac..473658259f1a2 100644 --- a/net/netfilter/nf_conntrack_proto_gre.c +++ b/net/netfilter/nf_conntrack_proto_gre.c @@ -349,6 +349,67 @@ gre_timeout_nla_policy[CTA_TIMEOUT_GRE_MAX+1] = { }; #endif /* CONFIG_NF_CONNTRACK_TIMEOUT */ +#if IS_ENABLED(CONFIG_NF_CONNTRACK_PPTP) +static int destroy_sibling_or_exp(struct net *net, struct nf_conn *ct, + const struct nf_conntrack_tuple *t) +{ + const struct nf_conntrack_tuple_hash *h; + const struct nf_conntrack_zone *zone; + struct nf_conntrack_expect *exp; + struct nf_conn *sibling; + + pr_debug("trying to timeout ct or exp for tuple "); + nf_ct_dump_tuple(t); + + zone = nf_ct_zone(ct); + h = nf_conntrack_find_get(net, zone, t); + if (h) { + sibling = nf_ct_tuplehash_to_ctrack(h); + pr_debug("setting timeout of conntrack %p to 0\n", sibling); + sibling->proto.gre.timeout = 0; + sibling->proto.gre.stream_timeout = 0; + nf_ct_kill(sibling); + nf_ct_put(sibling); + return 1; + } else { + exp = nf_ct_expect_find_get(net, zone, t); + if (exp) { + pr_debug("unexpect_related of expect %p\n", exp); + nf_ct_unexpect_related(exp); + nf_ct_expect_put(exp); + return 1; + } + } + return 0; +} + +void gre_pptp_destroy_siblings(struct nf_conn *ct) +{ + struct net *net = nf_ct_net(ct); + const struct nf_ct_pptp_master *ct_pptp_info = nfct_help_data(ct); + struct nf_conntrack_tuple t; + + nf_ct_gre_keymap_destroy(ct); + + /* try original (pns->pac) tuple */ + memcpy(&t, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, sizeof(t)); + t.dst.protonum = IPPROTO_GRE; + t.src.u.gre.key = ct_pptp_info->pns_call_id; + t.dst.u.gre.key = ct_pptp_info->pac_call_id; + if (!destroy_sibling_or_exp(net, ct, &t)) + pr_debug("failed to timeout original pns->pac ct/exp\n"); + + /* try reply (pac->pns) tuple */ + memcpy(&t, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, sizeof(t)); + t.dst.protonum = IPPROTO_GRE; + t.src.u.gre.key = ct_pptp_info->pac_call_id; + t.dst.u.gre.key = ct_pptp_info->pns_call_id; + if (!destroy_sibling_or_exp(net, ct, &t)) + pr_debug("failed to timeout reply pac->pns ct/exp\n"); +} +EXPORT_SYMBOL_GPL(gre_pptp_destroy_siblings); +#endif + void nf_conntrack_gre_init_net(struct net *net) { struct nf_gre_net *net_gre = gre_pernet(net); From 743209358ff8ea044e5cca540c7d38b1da71c17b Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 8 Jun 2026 18:14:36 +0200 Subject: [PATCH 1909/2758] netfilter: conntrack: check NULL when retrieving ct extension [ Upstream commit e3cd138e560764299965fba5ec5240281a7faca2 ] nf_ct_ext_find() might return NULL if ct extension is not found. Add also the null checks to: - nfct_help() - nfct_help_data() - nfct_seqadj() - nfct_nat() This is defensive, for safety reasons. nf_ct_ext_find() used to return NULL if the extension is stale for unconfirmed conntracks if the genid validation fails. Skip NULL check in nf_nat_inet_fn() given this is valid to be NULL for non-initialized ct nat extensions. While at it, fetch ct helper area in nf_ct_expect_related_report() only once and pass it on to other ancilliary functions. Replace WARN_ON() by WARN_ON_ONCE() in nf_ct_unlink_expect_report(). Signed-off-by: Pablo Neira Ayuso Stable-dep-of: 979c13114c0b ("netfilter: nf_conntrack_expect: store master_tuple in expectation") Signed-off-by: Sasha Levin --- include/net/netfilter/nf_conntrack_helper.h | 2 + net/ipv4/netfilter/nf_nat_h323.c | 12 ++++++ net/ipv4/netfilter/nf_nat_pptp.c | 14 +++++-- net/netfilter/nf_conntrack_broadcast.c | 3 ++ net/netfilter/nf_conntrack_expect.c | 33 +++++++++-------- net/netfilter/nf_conntrack_ftp.c | 6 +++ net/netfilter/nf_conntrack_h323_main.c | 18 +++++++++ net/netfilter/nf_conntrack_pptp.c | 9 +++++ net/netfilter/nf_conntrack_proto_gre.c | 9 +++++ net/netfilter/nf_conntrack_sane.c | 3 ++ net/netfilter/nf_conntrack_seqadj.c | 17 ++++++--- net/netfilter/nf_conntrack_sip.c | 41 ++++++++++++++++++++- net/netfilter/nf_nat_sip.c | 12 ++++++ net/netfilter/nfnetlink_cthelper.c | 6 +++ 14 files changed, 158 insertions(+), 27 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index 18dfb3a287cf1..295c9d2a08907 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h @@ -134,6 +134,8 @@ static inline void *nfct_help_data(const struct nf_conn *ct) struct nf_conn_help *help; help = nf_ct_ext_find(ct, NF_CT_EXT_HELPER); + if (!help) + return NULL; return (void *)help->data; } diff --git a/net/ipv4/netfilter/nf_nat_h323.c b/net/ipv4/netfilter/nf_nat_h323.c index 10e1b0837731b..183e8a3ff2bab 100644 --- a/net/ipv4/netfilter/nf_nat_h323.c +++ b/net/ipv4/netfilter/nf_nat_h323.c @@ -100,6 +100,9 @@ static int set_sig_addr(struct sk_buff *skb, struct nf_conn *ct, __be16 port; union nf_inet_addr addr; + if (!info) + return -1; + for (i = 0; i < count; i++) { if (get_h225_addr(ct, *data, &taddr[i], &addr, &port)) { if (addr.ip == ct->tuplehash[dir].tuple.src.u3.ip && @@ -184,6 +187,9 @@ static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct, int i; u_int16_t nated_port; + if (!info) + return -1; + /* Set expectations for NAT */ rtp_exp->saved_proto.udp.port = rtp_exp->tuple.dst.u.udp.port; rtp_exp->expectfn = nf_nat_follow_master; @@ -325,6 +331,9 @@ static int nat_h245(struct sk_buff *skb, struct nf_conn *ct, int dir = CTINFO2DIR(ctinfo); u_int16_t nated_port = ntohs(port); + if (!info) + return -1; + /* Set expectations for NAT */ exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; exp->expectfn = nf_nat_follow_master; @@ -404,6 +413,9 @@ static int nat_q931(struct sk_buff *skb, struct nf_conn *ct, u_int16_t nated_port = ntohs(port); union nf_inet_addr addr; + if (!info) + return -1; + /* Set expectations for NAT */ exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; exp->expectfn = ip_nat_q931_expect; diff --git a/net/ipv4/netfilter/nf_nat_pptp.c b/net/ipv4/netfilter/nf_nat_pptp.c index fab357cc85594..fed5249001a49 100644 --- a/net/ipv4/netfilter/nf_nat_pptp.c +++ b/net/ipv4/netfilter/nf_nat_pptp.c @@ -53,11 +53,13 @@ static void pptp_nat_expected(struct nf_conn *ct, struct nf_conn_nat *nat; nat = nf_ct_nat_ext_add(ct); - if (WARN_ON_ONCE(!nat)) + if (!nat) return; nat_pptp_info = &nat->help.nat_pptp_info; ct_pptp_info = nfct_help_data(master); + if (!ct_pptp_info) + return; /* And here goes the grand finale of corrosion... */ if (exp->dir == IP_CT_DIR_ORIGINAL) { @@ -132,11 +134,13 @@ pptp_outbound_pkt(struct sk_buff *skb, __be16 new_callid; unsigned int cid_off; - if (WARN_ON_ONCE(!nat)) + if (!nat) return NF_DROP; nat_pptp_info = &nat->help.nat_pptp_info; ct_pptp_info = nfct_help_data(ct); + if (!ct_pptp_info) + return NF_DROP; new_callid = ct_pptp_info->pns_call_id; @@ -204,11 +208,13 @@ pptp_exp_gre(struct nf_conntrack_expect *expect_orig, struct nf_ct_pptp_master *ct_pptp_info; struct nf_nat_pptp *nat_pptp_info; - if (WARN_ON_ONCE(!nat)) + if (!nat) return; nat_pptp_info = &nat->help.nat_pptp_info; ct_pptp_info = nfct_help_data(ct); + if (!ct_pptp_info) + return; /* save original PAC call ID in nat_info */ nat_pptp_info->pac_call_id = ct_pptp_info->pac_call_id; @@ -241,7 +247,7 @@ pptp_inbound_pkt(struct sk_buff *skb, __be16 new_pcid; unsigned int pcid_off; - if (WARN_ON_ONCE(!nat)) + if (!nat) return NF_DROP; nat_pptp_info = &nat->help.nat_pptp_info; diff --git a/net/netfilter/nf_conntrack_broadcast.c b/net/netfilter/nf_conntrack_broadcast.c index 75e53fde6b297..400119b6320e7 100644 --- a/net/netfilter/nf_conntrack_broadcast.c +++ b/net/netfilter/nf_conntrack_broadcast.c @@ -29,6 +29,9 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb, struct nf_conn_help *help = nfct_help(ct); __be32 mask = 0; + if (!help) + goto out; + /* we're only interested in locally generated packets */ if (skb->sk == NULL || !net_eq(nf_ct_net(ct), sock_net(skb->sk))) goto out; diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index 8e943efbdf0a5..5c9b17835c280 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -52,8 +52,7 @@ void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp, struct nf_conntrack_net *cnet; lockdep_nfct_expect_lock_held(); - WARN_ON(!master_help); - WARN_ON(timer_pending(&exp->timeout)); + WARN_ON_ONCE(timer_pending(&exp->timeout)); hlist_del_rcu(&exp->hnode); @@ -61,7 +60,8 @@ void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp, cnet->expect_count--; hlist_del_rcu(&exp->lnode); - master_help->expecting[exp->class]--; + if (master_help) + master_help->expecting[exp->class]--; nf_ct_expect_event_report(IPEXP_DESTROY, exp, portid, report); nf_ct_expect_put(exp); @@ -405,10 +405,10 @@ void nf_ct_expect_put(struct nf_conntrack_expect *exp) } EXPORT_SYMBOL_GPL(nf_ct_expect_put); -static void nf_ct_expect_insert(struct nf_conntrack_expect *exp) +static void nf_ct_expect_insert(struct nf_conntrack_expect *exp, + struct nf_conn_help *master_help) { struct nf_conntrack_net *cnet; - struct nf_conn_help *master_help = nfct_help(exp->master); struct nf_conntrack_helper *helper; struct net *net = nf_ct_exp_net(exp); unsigned int h = nf_ct_expect_dst_hash(net, &exp->tuple); @@ -436,10 +436,9 @@ static void nf_ct_expect_insert(struct nf_conntrack_expect *exp) } /* Race with expectations being used means we could have none to find; OK. */ -static void evict_oldest_expect(struct nf_conn *master, +static void evict_oldest_expect(struct nf_conn_help *master_help, struct nf_conntrack_expect *new) { - struct nf_conn_help *master_help = nfct_help(master); struct nf_conntrack_expect *exp, *last = NULL; hlist_for_each_entry(exp, &master_help->expectations, lnode) { @@ -452,13 +451,12 @@ static void evict_oldest_expect(struct nf_conn *master, } static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect, + struct nf_conn_help *master_help, unsigned int flags) { const struct nf_conntrack_expect_policy *p; struct nf_conntrack_expect *i; struct nf_conntrack_net *cnet; - struct nf_conn *master = expect->master; - struct nf_conn_help *master_help = nfct_help(master); struct nf_conntrack_helper *helper; struct net *net = nf_ct_exp_net(expect); struct hlist_node *next; @@ -467,10 +465,6 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect, lockdep_nfct_expect_lock_held(); - if (!master_help) { - ret = -ESHUTDOWN; - goto out; - } h = nf_ct_expect_dst_hash(net, &expect->tuple); hlist_for_each_entry_safe(i, next, &nf_ct_expect_hash[h], hnode) { if (master_matches(i, expect, flags) && @@ -493,7 +487,7 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect, p = &helper->expect_policy[expect->class]; if (p->max_expected && master_help->expecting[expect->class] >= p->max_expected) { - evict_oldest_expect(master, expect); + evict_oldest_expect(master_help, expect); if (master_help->expecting[expect->class] >= p->max_expected) { ret = -EMFILE; @@ -514,14 +508,21 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect, int nf_ct_expect_related_report(struct nf_conntrack_expect *expect, u32 portid, int report, unsigned int flags) { + struct nf_conn_help *master_help; int ret; spin_lock_bh(&nf_conntrack_expect_lock); - ret = __nf_ct_expect_check(expect, flags); + master_help = nfct_help(expect->master); + if (!master_help) { + ret = -ESHUTDOWN; + goto out; + } + + ret = __nf_ct_expect_check(expect, master_help, flags); if (ret < 0) goto out; - nf_ct_expect_insert(expect); + nf_ct_expect_insert(expect, master_help); nf_ct_expect_event_report(IPEXP_NEW, expect, portid, report); spin_unlock_bh(&nf_conntrack_expect_lock); diff --git a/net/netfilter/nf_conntrack_ftp.c b/net/netfilter/nf_conntrack_ftp.c index b21da0c788458..6e3c06de8a6eb 100644 --- a/net/netfilter/nf_conntrack_ftp.c +++ b/net/netfilter/nf_conntrack_ftp.c @@ -381,6 +381,9 @@ static int help(struct sk_buff *skb, int found = 0, ends_in_nl; nf_nat_ftp_hook_fn *nf_nat_ftp; + if (!ct_ftp_info) + return NF_DROP; + /* Until there's been traffic both ways, don't look in packets. */ if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED_REPLY) { @@ -542,6 +545,9 @@ static int nf_ct_ftp_from_nlattr(struct nlattr *attr, struct nf_conn *ct) { struct nf_ct_ftp_master *ftp = nfct_help_data(ct); + if (!ftp) + return -ENOENT; + /* This conntrack has been injected from user-space, always pick up * sequence tracking. Otherwise, the first FTP command after the * failover breaks. diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c index ebae9fdab8978..7f189dceb3c4d 100644 --- a/net/netfilter/nf_conntrack_h323_main.c +++ b/net/netfilter/nf_conntrack_h323_main.c @@ -76,6 +76,9 @@ static int get_tpkt_data(struct sk_buff *skb, unsigned int protoff, int tpktlen; int tpktoff; + if (!info) + return 0; + /* Get TCP header */ th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph); if (th == NULL) @@ -1191,6 +1194,9 @@ static int expect_q931(struct sk_buff *skb, struct nf_conn *ct, union nf_inet_addr addr; struct nf_conntrack_expect *exp; + if (!info) + return -1; + /* Look for the first related address */ for (i = 0; i < count; i++) { if (get_h225_addr(ct, *data, &taddr[i], &addr, &port) && @@ -1307,6 +1313,9 @@ static int process_rrq(struct sk_buff *skb, struct nf_conn *ct, const struct nfct_h323_nat_hooks *nathook; int ret; + if (!info) + return -1; + pr_debug("nf_ct_ras: RRQ\n"); ret = expect_q931(skb, ct, ctinfo, protoff, data, @@ -1345,6 +1354,9 @@ static int process_rcf(struct sk_buff *skb, struct nf_conn *ct, int ret; struct nf_conntrack_expect *exp; + if (!info) + return -1; + pr_debug("nf_ct_ras: RCF\n"); nathook = rcu_dereference(nfct_h323_nat_hook); @@ -1395,6 +1407,9 @@ static int process_urq(struct sk_buff *skb, struct nf_conn *ct, int dir = CTINFO2DIR(ctinfo); int ret; + if (!info) + return -1; + pr_debug("nf_ct_ras: URQ\n"); nathook = rcu_dereference(nfct_h323_nat_hook); @@ -1429,6 +1444,9 @@ static int process_arq(struct sk_buff *skb, struct nf_conn *ct, __be16 port; union nf_inet_addr addr; + if (!info) + return 0; + pr_debug("nf_ct_ras: ARQ\n"); nathook = rcu_dereference(nfct_h323_nat_hook); diff --git a/net/netfilter/nf_conntrack_pptp.c b/net/netfilter/nf_conntrack_pptp.c index ed567a1cf7fd7..776505a78e645 100644 --- a/net/netfilter/nf_conntrack_pptp.c +++ b/net/netfilter/nf_conntrack_pptp.c @@ -198,6 +198,9 @@ pptp_inbound_pkt(struct sk_buff *skb, unsigned int protoff, u_int16_t msg; __be16 cid = 0, pcid = 0; + if (!info) + return NF_DROP; + msg = ntohs(ctlh->messageType); pr_debug("inbound control message %s\n", pptp_msg_name(msg)); @@ -325,6 +328,9 @@ pptp_outbound_pkt(struct sk_buff *skb, unsigned int protoff, u_int16_t msg; __be16 cid = 0, pcid = 0; + if (!info) + return NF_DROP; + msg = ntohs(ctlh->messageType); pr_debug("outbound control message %s\n", pptp_msg_name(msg)); @@ -443,6 +449,9 @@ conntrack_pptp_help(struct sk_buff *skb, unsigned int protoff, int ret; u_int16_t msg; + if (!info) + return NF_DROP; + #if IS_ENABLED(CONFIG_NF_NAT) if (!nf_ct_is_confirmed(ct) && (ct->status & IPS_NAT_MASK)) { struct nf_conn_nat *nat = nf_ct_ext_find(ct, NF_CT_EXT_NAT); diff --git a/net/netfilter/nf_conntrack_proto_gre.c b/net/netfilter/nf_conntrack_proto_gre.c index 473658259f1a2..616ab1e2fc5ed 100644 --- a/net/netfilter/nf_conntrack_proto_gre.c +++ b/net/netfilter/nf_conntrack_proto_gre.c @@ -131,6 +131,9 @@ bool nf_ct_gre_keymap_add(struct nf_conn *ct, struct nf_ct_gre_keymap *km_orig, *km_repl; bool ret = false; + if (!ct_pptp_info) + return false; + km_orig = kmalloc_obj(*km_orig, GFP_ATOMIC); if (!km_orig) return false; @@ -187,6 +190,9 @@ void nf_ct_gre_keymap_destroy(struct nf_conn *ct) struct nf_ct_pptp_master *ct_pptp_info = nfct_help_data(ct); enum ip_conntrack_dir dir; + if (!ct_pptp_info) + return; + pr_debug("entering for ct %p\n", ct); spin_lock_bh(&keymap_lock); @@ -389,6 +395,9 @@ void gre_pptp_destroy_siblings(struct nf_conn *ct) const struct nf_ct_pptp_master *ct_pptp_info = nfct_help_data(ct); struct nf_conntrack_tuple t; + if (!ct_pptp_info) + return; + nf_ct_gre_keymap_destroy(ct); /* try original (pns->pac) tuple */ diff --git a/net/netfilter/nf_conntrack_sane.c b/net/netfilter/nf_conntrack_sane.c index a7f7b07ba0c2b..39085acf7a718 100644 --- a/net/netfilter/nf_conntrack_sane.c +++ b/net/netfilter/nf_conntrack_sane.c @@ -74,6 +74,9 @@ static int help(struct sk_buff *skb, struct sane_reply_net_start repl; } buf; + if (!ct_sane_info) + return NF_DROP; + /* Until there's been traffic both ways, don't look in packets. */ if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED_REPLY) diff --git a/net/netfilter/nf_conntrack_seqadj.c b/net/netfilter/nf_conntrack_seqadj.c index b7e99f34dfce8..220216a4edc51 100644 --- a/net/netfilter/nf_conntrack_seqadj.c +++ b/net/netfilter/nf_conntrack_seqadj.c @@ -18,9 +18,12 @@ int nf_ct_seqadj_init(struct nf_conn *ct, enum ip_conntrack_info ctinfo, return 0; spin_lock_bh(&ct->lock); - set_bit(IPS_SEQ_ADJUST_BIT, &ct->status); - seqadj = nfct_seqadj(ct); + if (!seqadj) { + spin_unlock_bh(&ct->lock); + return 0; + } + set_bit(IPS_SEQ_ADJUST_BIT, &ct->status); this_way = &seqadj->seq[dir]; this_way->offset_before = off; this_way->offset_after = off; @@ -39,10 +42,8 @@ int nf_ct_seqadj_set(struct nf_conn *ct, enum ip_conntrack_info ctinfo, if (off == 0) return 0; - if (unlikely(!seqadj)) { - WARN_ONCE(1, "Missing nfct_seqadj_ext_add() setup call\n"); + if (unlikely(!seqadj)) return 0; - } set_bit(IPS_SEQ_ADJUST_BIT, &ct->status); @@ -125,6 +126,9 @@ static unsigned int nf_ct_sack_adjust(struct sk_buff *skb, struct nf_conn_seqadj *seqadj = nfct_seqadj(ct); unsigned int dir, optoff, optend; + if (!seqadj) + return 0; + optoff = protoff + sizeof(struct tcphdr); optend = protoff + tcph->doff * 4; @@ -175,6 +179,9 @@ int nf_ct_seq_adjust(struct sk_buff *skb, struct nf_ct_seqadj *this_way, *other_way; int res = 1; + if (!seqadj) + return 0; + this_way = &seqadj->seq[dir]; other_way = &seqadj->seq[!dir]; diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index 2c78a3e1dab5e..c606d1f60b589 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -887,6 +887,9 @@ static int refresh_signalling_expectation(struct nf_conn *ct, struct hlist_node *next; int found = 0; + if (!help) + return 0; + spin_lock_bh(&nf_conntrack_expect_lock); hlist_for_each_entry_safe(exp, next, &help->expectations, lnode) { if (exp->class != SIP_EXPECT_SIGNALLING || @@ -910,6 +913,9 @@ static void flush_expectations(struct nf_conn *ct, bool media) struct nf_conntrack_expect *exp; struct hlist_node *next; + if (!help) + return; + spin_lock_bh(&nf_conntrack_expect_lock); hlist_for_each_entry_safe(exp, next, &help->expectations, lnode) { if ((exp->class != SIP_EXPECT_SIGNALLING) ^ media) @@ -940,6 +946,11 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, u_int16_t base_port; __be16 rtp_port, rtcp_port; const struct nf_nat_sip_hooks *hooks; + struct nf_conn_help *help; + + help = nfct_help(ct); + if (!help) + return NF_DROP; saddr = NULL; if (sip_direct_media) { @@ -1002,7 +1013,7 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, exp = __nf_ct_expect_find(net, nf_ct_zone(ct), &tuple); if (!exp || exp->master == ct || - exp->helper != nfct_help(ct)->helper || + exp->helper != help->helper || exp->class != class) break; #if IS_ENABLED(CONFIG_NF_NAT) @@ -1227,6 +1238,9 @@ static int process_invite_response(struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct = nf_ct_get(skb, &ctinfo); struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); + if (!ct_sip_info) + return NF_DROP; + if ((code >= 100 && code <= 199) || (code >= 200 && code <= 299)) return process_sdp(skb, protoff, dataoff, dptr, datalen, cseq); @@ -1244,6 +1258,9 @@ static int process_update_response(struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct = nf_ct_get(skb, &ctinfo); struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); + if (!ct_sip_info) + return NF_DROP; + if ((code >= 100 && code <= 199) || (code >= 200 && code <= 299)) return process_sdp(skb, protoff, dataoff, dptr, datalen, cseq); @@ -1261,6 +1278,9 @@ static int process_prack_response(struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct = nf_ct_get(skb, &ctinfo); struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); + if (!ct_sip_info) + return NF_DROP; + if ((code >= 100 && code <= 199) || (code >= 200 && code <= 299)) return process_sdp(skb, protoff, dataoff, dptr, datalen, cseq); @@ -1279,6 +1299,9 @@ static int process_invite_request(struct sk_buff *skb, unsigned int protoff, struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); unsigned int ret; + if (!ct_sip_info) + return NF_DROP; + flush_expectations(ct, true); ret = process_sdp(skb, protoff, dataoff, dptr, datalen, cseq); if (ret == NF_ACCEPT) @@ -1316,11 +1339,15 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff, union nf_inet_addr *saddr, daddr; const struct nf_nat_sip_hooks *hooks; struct nf_conntrack_helper *helper; + struct nf_conn_help *help; __be16 port; u8 proto; unsigned int expires = 0; int ret; + if (!ct_sip_info) + return NF_DROP; + /* Expected connections can not register again. */ if (ct->status & IPS_EXPECTED) return NF_ACCEPT; @@ -1366,7 +1393,11 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff, goto store_cseq; } - helper = rcu_dereference(nfct_help(ct)->helper); + help = nfct_help(ct); + if (!help) + return NF_DROP; + + helper = rcu_dereference(help->helper); if (!helper) return NF_DROP; @@ -1421,6 +1452,9 @@ static int process_register_response(struct sk_buff *skb, unsigned int protoff, unsigned int expires = 0; int in_contact = 0, ret; + if (!ct_sip_info) + return NF_DROP; + /* According to RFC 3261, "UAs MUST NOT send a new registration until * they have received a final response from the registrar for the * previous one or the previous REGISTER request has timed out". @@ -1550,6 +1584,9 @@ static int process_sip_request(struct sk_buff *skb, unsigned int protoff, union nf_inet_addr addr; __be16 port; + if (!ct_sip_info) + return NF_DROP; + /* Many Cisco IP phones use a high source port for SIP requests, but * listen for the response on port 5060. If we are the local * router for one of these phones, save the port number from the diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c index 00838c0cc5bb2..67c04d8143ab9 100644 --- a/net/netfilter/nf_nat_sip.c +++ b/net/netfilter/nf_nat_sip.c @@ -106,6 +106,9 @@ static int map_addr(struct sk_buff *skb, unsigned int protoff, union nf_inet_addr newaddr; __be16 newport; + if (!ct_sip_info) + return 0; + if (nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3, addr) && ct->tuplehash[dir].tuple.src.u.udp.port == port) { newaddr = ct->tuplehash[!dir].tuple.dst.u3; @@ -158,6 +161,9 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, __be16 port; int request, in_header; + if (!ct_sip_info) + return NF_DROP; + /* Basic rules: requests and responses. */ if (strncasecmp(*dptr, "SIP/2.0", strlen("SIP/2.0")) != 0) { if (ct_sip_parse_request(ct, *dptr, *datalen, @@ -326,6 +332,9 @@ static void nf_nat_sip_expected(struct nf_conn *ct, int range_set_for_snat = 0; struct nf_nat_range2 range; + if (!help) + return; + /* This must be a fresh one. */ BUG_ON(ct->status & IPS_NAT_DONE_MASK); @@ -390,6 +399,9 @@ static unsigned int nf_nat_sip_expect(struct sk_buff *skb, unsigned int protoff, char buffer[INET6_ADDRSTRLEN + sizeof("[]:nnnnn")]; unsigned int buflen; + if (!ct_sip_info) + return NF_DROP; + /* Connection will come from reply */ if (nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3, &ct->tuplehash[!dir].tuple.dst.u3)) diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index a74078ced52da..96fc65414502c 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -101,6 +101,9 @@ nfnl_cthelper_from_nlattr(struct nlattr *attr, struct nf_conn *ct) struct nf_conn_help *help = nfct_help(ct); const struct nf_conntrack_helper *helper; + if (!help) + return -EINVAL; + if (attr == NULL) return -EINVAL; @@ -118,6 +121,9 @@ nfnl_cthelper_to_nlattr(struct sk_buff *skb, const struct nf_conn *ct) const struct nf_conn_help *help = nfct_help(ct); const struct nf_conntrack_helper *helper; + if (!help) + return 0; + helper = rcu_dereference(help->helper); if (helper && helper->data_len && nla_put(skb, CTA_HELP_INFO, helper->data_len, &help->data)) From 7ec786f4230c2a9b2eaf97a2d45368933b49d2b2 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 18 Jun 2026 13:56:38 +0200 Subject: [PATCH 1910/2758] netfilter: nf_conntrack_expect: use conntrack GC to reap expectations [ Upstream commit b8b09dc2bf35a00d4e0556b5d6308c7b917ebda2 ] This patch replaces the timer API by GC worker approach for expectations, as it already happened in many other subsystems. Use the existing conntrack GC worker to iterate over the local list of expectations in the master conntrack to reap expired expectations. Check IPS_HELPER_BIT to run GC for expectations, set it on for nft_ct expectation which nevers sets it. Hold the expectation spinlock while iterating over the master conntrack expectation list to synchronize with nf_ct_remove_expectations(). This also performs runtime packet path garbage collection through the expectation insertion and lookup functions while walking over one of the chains of the global expectation hashtables. Unconfirmed conntrack entries are skipped since ct->ext can be reallocated and dying are skipped since those will be gone soon. Set on IPS_HELPER_BIT if the helper ct extension is added, then the new GC worker does not need to bump the ct refcount to check if the ct->ext helper is available. This removes the extra bump on the refcount for expectation timers, this allows to remove several nf_ct_expect_put() calls after the unlink, after this update only refcount remains at 1 while on the expectation hashes. This patch implicitly addresses a race with the existing timer API allowing an expectation to access a stale exp->master pointer which has been already released when expectation removal loses races with an expiring timer, ie. timer_del() reporting false. Add a new NF_CT_EXPECT_DEAD flag to reap this expectation via GC. This is needed by nf_conntrack_unexpect_related() which is called in error paths to invalidate newly created expectations that has been added into the hashes. These expectactions cannot be inmediately released as GC or nf_ct_remove_expectations() could race to make it. On expectation insert, the runtime GC reaps stale expectations before checking the expectation limit set by policy. Set current timestamp in nf_ct_expect_alloc(), then add the expectation policy timeout (or custom timeout specified added on top of this) to specify the expectation lifetime. Fixes: bffcaad9afdf ("netfilter: ctnetlink: ensure safe access to master conntrack") Signed-off-by: Pablo Neira Ayuso Stable-dep-of: 979c13114c0b ("netfilter: nf_conntrack_expect: store master_tuple in expectation") Signed-off-by: Sasha Levin --- include/net/netfilter/nf_conntrack_expect.h | 16 +- .../linux/netfilter/nf_conntrack_common.h | 1 + net/netfilter/nf_conntrack_core.c | 33 +++- net/netfilter/nf_conntrack_expect.c | 145 +++++++++--------- net/netfilter/nf_conntrack_h323_main.c | 4 +- net/netfilter/nf_conntrack_helper.c | 10 +- net/netfilter/nf_conntrack_netlink.c | 22 ++- net/netfilter/nf_conntrack_sip.c | 13 +- net/netfilter/nft_ct.c | 3 +- 9 files changed, 139 insertions(+), 108 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h index 80f50fd0f7ad2..be4a120d549eb 100644 --- a/include/net/netfilter/nf_conntrack_expect.h +++ b/include/net/netfilter/nf_conntrack_expect.h @@ -54,8 +54,8 @@ struct nf_conntrack_expect { /* The conntrack of the master connection */ struct nf_conn *master; - /* Timer function; deletes the expectation. */ - struct timer_list timeout; + /* jiffies32 when this expectation expires */ + u32 timeout; #if IS_ENABLED(CONFIG_NF_NAT) union nf_inet_addr saved_addr; @@ -69,6 +69,14 @@ struct nf_conntrack_expect { struct rcu_head rcu; }; +static inline bool nf_ct_exp_is_expired(const struct nf_conntrack_expect *exp) +{ + if (READ_ONCE(exp->flags) & NF_CT_EXPECT_DEAD) + return true; + + return (__s32)(READ_ONCE(exp->timeout) - nfct_time_stamp) <= 0; +} + static inline struct net *nf_ct_exp_net(struct nf_conntrack_expect *exp) { return read_pnet(&exp->net); @@ -130,7 +138,6 @@ static inline void nf_ct_unlink_expect(struct nf_conntrack_expect *exp) void nf_ct_remove_expectations(struct nf_conn *ct); void nf_ct_unexpect_related(struct nf_conntrack_expect *exp); -bool nf_ct_remove_expect(struct nf_conntrack_expect *exp); void nf_ct_expect_iterate_destroy(bool (*iter)(struct nf_conntrack_expect *e, void *data), void *data); void nf_ct_expect_iterate_net(struct net *net, @@ -153,5 +160,8 @@ static inline int nf_ct_expect_related(struct nf_conntrack_expect *expect, return nf_ct_expect_related_report(expect, 0, 0, flags); } +struct nf_conn_help; +void nf_ct_expectation_gc(struct nf_conn_help *master_help); + #endif /*_NF_CONNTRACK_EXPECT_H*/ diff --git a/include/uapi/linux/netfilter/nf_conntrack_common.h b/include/uapi/linux/netfilter/nf_conntrack_common.h index 56b6b60a814f5..ee51045ae1d62 100644 --- a/include/uapi/linux/netfilter/nf_conntrack_common.h +++ b/include/uapi/linux/netfilter/nf_conntrack_common.h @@ -160,6 +160,7 @@ enum ip_conntrack_expect_events { #define NF_CT_EXPECT_USERSPACE 0x4 #ifdef __KERNEL__ +#define NF_CT_EXPECT_DEAD 0x8 #define NF_CT_EXPECT_MASK (NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE | \ NF_CT_EXPECT_USERSPACE) #endif diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 75c91636bf619..1f7b0d5a55ecf 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1471,6 +1471,31 @@ static bool gc_worker_can_early_drop(const struct nf_conn *ct) return false; } +static void nf_ct_help_gc(struct nf_conn *ct) +{ + struct nf_conn_help *help; + + if (!refcount_inc_not_zero(&ct->ct_general.use)) + return; + + /* load ->status after refcount increase */ + smp_acquire__after_ctrl_dep(); + + if (!nf_ct_is_confirmed(ct) || nf_ct_is_dying(ct)) { + nf_ct_put(ct); + return; + } + + /* re-check helper due to SLAB_TYPESAFE_BY_RCU */ + if (test_bit(IPS_HELPER_BIT, &ct->status)) { + help = nfct_help(ct); + if (help) + nf_ct_expectation_gc(help); + } + + nf_ct_put(ct); +} + static void gc_worker(struct work_struct *work) { unsigned int i, hashsz, nf_conntrack_max95 = 0; @@ -1543,7 +1568,13 @@ static void gc_worker(struct work_struct *work) expires = (expires - (long)next_run) / ++count; next_run += expires; - if (nf_conntrack_max95 == 0 || gc_worker_skip_ct(tmp)) + if (gc_worker_skip_ct(tmp)) + continue; + + if (test_bit(IPS_HELPER_BIT, &tmp->status)) + nf_ct_help_gc(tmp); + + if (nf_conntrack_max95 == 0) continue; net = nf_ct_net(tmp); diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index 5c9b17835c280..49e18eda037ef 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -43,6 +43,24 @@ unsigned int nf_ct_expect_max __read_mostly; static struct kmem_cache *nf_ct_expect_cachep __read_mostly; static siphash_aligned_key_t nf_ct_expect_hashrnd; +void nf_ct_expectation_gc(struct nf_conn_help *master_help) +{ + struct nf_conntrack_expect *exp; + struct hlist_node *next; + + if (hlist_empty(&master_help->expectations)) + return; + + spin_lock_bh(&nf_conntrack_expect_lock); + hlist_for_each_entry_safe(exp, next, &master_help->expectations, lnode) { + if (!nf_ct_exp_is_expired(exp)) + continue; + + nf_ct_unlink_expect(exp); + } + spin_unlock_bh(&nf_conntrack_expect_lock); +} + /* nf_conntrack_expect helper functions */ void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp, u32 portid, int report) @@ -52,7 +70,6 @@ void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp, struct nf_conntrack_net *cnet; lockdep_nfct_expect_lock_held(); - WARN_ON_ONCE(timer_pending(&exp->timeout)); hlist_del_rcu(&exp->hnode); @@ -70,16 +87,6 @@ void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp, } EXPORT_SYMBOL_GPL(nf_ct_unlink_expect_report); -static void nf_ct_expectation_timed_out(struct timer_list *t) -{ - struct nf_conntrack_expect *exp = timer_container_of(exp, t, timeout); - - spin_lock_bh(&nf_conntrack_expect_lock); - nf_ct_unlink_expect(exp); - spin_unlock_bh(&nf_conntrack_expect_lock); - nf_ct_expect_put(exp); -} - static unsigned int nf_ct_expect_dst_hash(const struct net *n, const struct nf_conntrack_tuple *tuple) { struct { @@ -117,19 +124,6 @@ nf_ct_exp_equal(const struct nf_conntrack_tuple *tuple, nf_ct_exp_zone_equal_any(i, zone); } -bool nf_ct_remove_expect(struct nf_conntrack_expect *exp) -{ - lockdep_nfct_expect_lock_held(); - - if (timer_delete(&exp->timeout)) { - nf_ct_unlink_expect(exp); - nf_ct_expect_put(exp); - return true; - } - return false; -} -EXPORT_SYMBOL_GPL(nf_ct_remove_expect); - struct nf_conntrack_expect * __nf_ct_expect_find(struct net *net, const struct nf_conntrack_zone *zone, @@ -144,6 +138,8 @@ __nf_ct_expect_find(struct net *net, h = nf_ct_expect_dst_hash(net, tuple); hlist_for_each_entry_rcu(i, &nf_ct_expect_hash[h], hnode) { + if (nf_ct_exp_is_expired(i)) + continue; if (nf_ct_exp_equal(tuple, i, zone, net)) return i; } @@ -178,6 +174,7 @@ nf_ct_find_expectation(struct net *net, { struct nf_conntrack_net *cnet = nf_ct_pernet(net); struct nf_conntrack_expect *i, *exp = NULL; + struct hlist_node *next; unsigned int h; lockdep_nfct_expect_lock_held(); @@ -186,7 +183,11 @@ nf_ct_find_expectation(struct net *net, return NULL; h = nf_ct_expect_dst_hash(net, tuple); - hlist_for_each_entry(i, &nf_ct_expect_hash[h], hnode) { + hlist_for_each_entry_safe(i, next, &nf_ct_expect_hash[h], hnode) { + if (nf_ct_exp_is_expired(i)) { + nf_ct_unlink_expect(i); + continue; + } if (!(i->flags & NF_CT_EXPECT_INACTIVE) && nf_ct_exp_equal(tuple, i, zone, net)) { exp = i; @@ -196,13 +197,16 @@ nf_ct_find_expectation(struct net *net, if (!exp) return NULL; + if (!refcount_inc_not_zero(&exp->use)) + return NULL; + /* If master is not in hash table yet (ie. packet hasn't left this machine yet), how can other end know about expected? Hence these are not the droids you are looking for (if master ct never got confirmed, we'd hold a reference to it and weird things would happen to future packets). */ if (!nf_ct_is_confirmed(exp->master)) - return NULL; + goto err_release_exp; /* Avoid race with other CPUs, that for exp->master ct, is * about to invoke ->destroy(), or nf_ct_delete() via timeout @@ -214,18 +218,17 @@ nf_ct_find_expectation(struct net *net, */ if (unlikely(nf_ct_is_dying(exp->master) || !refcount_inc_not_zero(&exp->master->ct_general.use))) - return NULL; + goto err_release_exp; - if (exp->flags & NF_CT_EXPECT_PERMANENT || !unlink) { - refcount_inc(&exp->use); - return exp; - } else if (timer_delete(&exp->timeout)) { - nf_ct_unlink_expect(exp); + if (exp->flags & NF_CT_EXPECT_PERMANENT || !unlink) return exp; - } - /* Undo exp->master refcnt increase, if timer_delete() failed */ - nf_ct_put(exp->master); + nf_ct_unlink_expect(exp); + + return exp; + +err_release_exp: + nf_ct_expect_put(exp); return NULL; } @@ -241,9 +244,8 @@ void nf_ct_remove_expectations(struct nf_conn *ct) return; spin_lock_bh(&nf_conntrack_expect_lock); - hlist_for_each_entry_safe(exp, next, &help->expectations, lnode) { - nf_ct_remove_expect(exp); - } + hlist_for_each_entry_safe(exp, next, &help->expectations, lnode) + nf_ct_unlink_expect(exp); spin_unlock_bh(&nf_conntrack_expect_lock); } EXPORT_SYMBOL_GPL(nf_ct_remove_expectations); @@ -292,7 +294,7 @@ static bool master_matches(const struct nf_conntrack_expect *a, void nf_ct_unexpect_related(struct nf_conntrack_expect *exp) { spin_lock_bh(&nf_conntrack_expect_lock); - nf_ct_remove_expect(exp); + WRITE_ONCE(exp->flags, exp->flags | NF_CT_EXPECT_DEAD); spin_unlock_bh(&nf_conntrack_expect_lock); } EXPORT_SYMBOL_GPL(nf_ct_unexpect_related); @@ -308,6 +310,7 @@ struct nf_conntrack_expect *nf_ct_expect_alloc(struct nf_conn *me) if (!new) return NULL; + new->timeout = nfct_time_stamp; new->master = me; refcount_set(&new->use, 1); return new; @@ -413,17 +416,12 @@ static void nf_ct_expect_insert(struct nf_conntrack_expect *exp, struct net *net = nf_ct_exp_net(exp); unsigned int h = nf_ct_expect_dst_hash(net, &exp->tuple); - /* two references : one for hash insert, one for the timer */ - refcount_add(2, &exp->use); + refcount_inc(&exp->use); - timer_setup(&exp->timeout, nf_ct_expectation_timed_out, 0); helper = rcu_dereference_protected(master_help->helper, lockdep_is_held(&nf_conntrack_expect_lock)); - if (helper) { - exp->timeout.expires = jiffies + - helper->expect_policy[exp->class].timeout * HZ; - } - add_timer(&exp->timeout); + if (helper) + exp->timeout += helper->expect_policy[exp->class].timeout * HZ; hlist_add_head_rcu(&exp->lnode, &master_help->expectations); master_help->expecting[exp->class]++; @@ -435,19 +433,26 @@ static void nf_ct_expect_insert(struct nf_conntrack_expect *exp, NF_CT_STAT_INC(net, expect_create); } -/* Race with expectations being used means we could have none to find; OK. */ static void evict_oldest_expect(struct nf_conn_help *master_help, - struct nf_conntrack_expect *new) + struct nf_conntrack_expect *new, + const struct nf_conntrack_expect_policy *p) { struct nf_conntrack_expect *exp, *last = NULL; + struct hlist_node *next; - hlist_for_each_entry(exp, &master_help->expectations, lnode) { + hlist_for_each_entry_safe(exp, next, &master_help->expectations, lnode) { + if (nf_ct_exp_is_expired(exp)) { + nf_ct_unlink_expect(exp); + continue; + } if (exp->class == new->class) last = exp; } - if (last) - nf_ct_remove_expect(last); + /* Still worth to evict oldest expectation after garbage collection? */ + if (last && + master_help->expecting[last->class] >= p->max_expected) + nf_ct_unlink_expect(last); } static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect, @@ -467,14 +472,18 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect, h = nf_ct_expect_dst_hash(net, &expect->tuple); hlist_for_each_entry_safe(i, next, &nf_ct_expect_hash[h], hnode) { + if (nf_ct_exp_is_expired(i)) { + nf_ct_unlink_expect(i); + continue; + } if (master_matches(i, expect, flags) && expect_matches(i, expect)) { if (i->class != expect->class || i->master != expect->master) return -EALREADY; - if (nf_ct_remove_expect(i)) - break; + nf_ct_unlink_expect(i); + break; } else if (expect_clash(i, expect)) { ret = -EBUSY; goto out; @@ -486,14 +495,8 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect, if (helper) { p = &helper->expect_policy[expect->class]; if (p->max_expected && - master_help->expecting[expect->class] >= p->max_expected) { - evict_oldest_expect(master_help, expect); - if (master_help->expecting[expect->class] - >= p->max_expected) { - ret = -EMFILE; - goto out; - } - } + master_help->expecting[expect->class] >= p->max_expected) + evict_oldest_expect(master_help, expect, p); } cnet = nf_ct_pernet(net); @@ -547,10 +550,8 @@ void nf_ct_expect_iterate_destroy(bool (*iter)(struct nf_conntrack_expect *e, vo hlist_for_each_entry_safe(exp, next, &nf_ct_expect_hash[i], hnode) { - if (iter(exp, data) && timer_delete(&exp->timeout)) { + if (iter(exp, data)) nf_ct_unlink_expect(exp); - nf_ct_expect_put(exp); - } } } @@ -577,10 +578,8 @@ void nf_ct_expect_iterate_net(struct net *net, if (!net_eq(nf_ct_exp_net(exp), net)) continue; - if (iter(exp, data) && timer_delete(&exp->timeout)) { + if (iter(exp, data)) nf_ct_unlink_expect_report(exp, portid, report); - nf_ct_expect_put(exp); - } } } @@ -657,17 +656,17 @@ static int exp_seq_show(struct seq_file *s, void *v) struct net *net = seq_file_net(s); struct hlist_node *n = v; char *delim = ""; + __s32 timeout; expect = hlist_entry(n, struct nf_conntrack_expect, hnode); if (!net_eq(nf_ct_exp_net(expect), net)) return 0; + if (nf_ct_exp_is_expired(expect)) + return 0; - if (expect->timeout.function) - seq_printf(s, "%ld ", timer_pending(&expect->timeout) - ? (long)(expect->timeout.expires - jiffies)/HZ : 0); - else - seq_puts(s, "- "); + timeout = (__s32)(READ_ONCE(expect->timeout) - nfct_time_stamp) / HZ; + seq_printf(s, "%d ", timeout > 0 ? timeout : 0); seq_printf(s, "l3proto = %u proto=%u ", expect->tuple.src.l3num, expect->tuple.dst.protonum); diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c index 7f189dceb3c4d..24931e379985b 100644 --- a/net/netfilter/nf_conntrack_h323_main.c +++ b/net/netfilter/nf_conntrack_h323_main.c @@ -1388,8 +1388,8 @@ static int process_rcf(struct sk_buff *skb, struct nf_conn *ct, "timeout to %u seconds for", info->timeout); nf_ct_dump_tuple(&exp->tuple); - mod_timer_pending(&exp->timeout, - jiffies + info->timeout * HZ); + WRITE_ONCE(exp->timeout, + nfct_time_stamp + (info->timeout * HZ)); } spin_unlock_bh(&nf_conntrack_expect_lock); } diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 5e2bb104eb49d..ec0aafcb19851 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -180,10 +180,10 @@ nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp) struct nf_conn_help *help; help = nf_ct_ext_add(ct, NF_CT_EXT_HELPER, gfp); - if (help) + if (help) { + __set_bit(IPS_HELPER_BIT, &ct->status); INIT_HLIST_HEAD(&help->expectations); - else - pr_debug("failed to add helper extension area"); + } return help; } EXPORT_SYMBOL_GPL(nf_ct_helper_ext_add); @@ -202,10 +202,8 @@ int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl, return 0; help = nfct_help(tmpl); - if (help != NULL) { + if (help) helper = rcu_dereference(help->helper); - set_bit(IPS_HELPER_BIT, &ct->status); - } help = nfct_help(ct); diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index d429f9c9546cd..211f361188f18 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -3006,8 +3006,8 @@ static int ctnetlink_exp_dump_expect(struct sk_buff *skb, const struct nf_conntrack_expect *exp) { + __s32 timeout = (__s32)(READ_ONCE(exp->timeout) - nfct_time_stamp) / HZ; struct nf_conn *master = exp->master; - long timeout = ((long)exp->timeout.expires - (long)jiffies) / HZ; struct nf_conntrack_helper *helper; #if IS_ENABLED(CONFIG_NF_NAT) struct nlattr *nest_parms; @@ -3170,6 +3170,9 @@ ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb) restart: hlist_for_each_entry_rcu(exp, &nf_ct_expect_hash[cb->args[0]], hnode) { + if (nf_ct_exp_is_expired(exp)) + continue; + if (l3proto && exp->tuple.src.l3num != l3proto) continue; @@ -3448,11 +3451,8 @@ static int ctnetlink_del_expect(struct sk_buff *skb, } /* after list removal, usage count == 1 */ - if (timer_delete(&exp->timeout)) { - nf_ct_unlink_expect_report(exp, NETLINK_CB(skb).portid, - nlmsg_report(info->nlh)); - nf_ct_expect_put(exp); - } + nf_ct_unlink_expect_report(exp, NETLINK_CB(skb).portid, + nlmsg_report(info->nlh)); spin_unlock_bh(&nf_conntrack_expect_lock); /* have to put what we 'get' above. * after this line usage count == 0 */ @@ -3476,14 +3476,10 @@ static int ctnetlink_change_expect(struct nf_conntrack_expect *x, const struct nlattr * const cda[]) { - if (cda[CTA_EXPECT_TIMEOUT]) { - if (!timer_delete(&x->timeout)) - return -ETIME; + if (cda[CTA_EXPECT_TIMEOUT]) + WRITE_ONCE(x->timeout, nfct_time_stamp + + ntohl(nla_get_be32(cda[CTA_EXPECT_TIMEOUT])) * HZ); - x->timeout.expires = jiffies + - ntohl(nla_get_be32(cda[CTA_EXPECT_TIMEOUT])) * HZ; - add_timer(&x->timeout); - } return 0; } diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index c606d1f60b589..5ec3a4a4bbd70 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -897,11 +897,10 @@ static int refresh_signalling_expectation(struct nf_conn *ct, exp->tuple.dst.protonum != proto || exp->tuple.dst.u.udp.port != port) continue; - if (mod_timer_pending(&exp->timeout, jiffies + expires * HZ)) { - exp->flags &= ~NF_CT_EXPECT_INACTIVE; - found = 1; - break; - } + WRITE_ONCE(exp->timeout, nfct_time_stamp + (expires * HZ)); + WRITE_ONCE(exp->flags, exp->flags & ~NF_CT_EXPECT_INACTIVE); + found = 1; + break; } spin_unlock_bh(&nf_conntrack_expect_lock); return found; @@ -920,8 +919,7 @@ static void flush_expectations(struct nf_conn *ct, bool media) hlist_for_each_entry_safe(exp, next, &help->expectations, lnode) { if ((exp->class != SIP_EXPECT_SIGNALLING) ^ media) continue; - if (!nf_ct_remove_expect(exp)) - continue; + nf_ct_unlink_expect(exp); if (!media) break; } @@ -1413,7 +1411,6 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff, nf_ct_expect_init(exp, SIP_EXPECT_SIGNALLING, nf_ct_l3num(ct), saddr, &daddr, proto, NULL, &port); - exp->timeout.expires = sip_timeout * HZ; rcu_assign_pointer(exp->assign_helper, helper); exp->flags = NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE; diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index 801c01c6af95f..f31e18639b358 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c @@ -1146,7 +1146,6 @@ static void nft_ct_helper_obj_eval(struct nft_object *obj, help = nf_ct_helper_ext_add(ct, GFP_ATOMIC); if (help) { rcu_assign_pointer(help->helper, to_assign); - set_bit(IPS_HELPER_BIT, &ct->status); if ((ct->status & IPS_NAT_MASK) && !nfct_seqadj(ct)) if (!nfct_seqadj_ext_add(ct)) @@ -1327,7 +1326,7 @@ static void nft_ct_expect_obj_eval(struct nft_object *obj, &ct->tuplehash[!dir].tuple.src.u3, &ct->tuplehash[!dir].tuple.dst.u3, priv->l4proto, NULL, &priv->dport); - exp->timeout.expires = jiffies + priv->timeout * HZ; + exp->timeout += priv->timeout * HZ; if (nf_ct_expect_related(exp, 0) != 0) regs->verdict.code = NF_DROP; From 3401ab813d27b8e8a1f0d5a9a8b936a4bec43726 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 22 Jun 2026 21:35:14 +0200 Subject: [PATCH 1911/2758] netfilter: nf_conntrack_expect: store master_tuple in expectation [ Upstream commit 979c13114c0bb6ab9135e2c93e00c79c412aef09 ] Store master conntrack tuple in the expectation since exp->master might refer to a different conntrack when accessed from rcu read side lock area due to typesafe rcu rules. Fixes: 02a3231b6d82 ("netfilter: nf_conntrack_expect: store netns and zone in expectation") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- include/net/netfilter/nf_conntrack_expect.h | 1 + net/netfilter/nf_conntrack_broadcast.c | 1 + net/netfilter/nf_conntrack_expect.c | 2 ++ net/netfilter/nf_conntrack_netlink.c | 10 ++++------ 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h index be4a120d549eb..c024345c9bd86 100644 --- a/include/net/netfilter/nf_conntrack_expect.h +++ b/include/net/netfilter/nf_conntrack_expect.h @@ -26,6 +26,7 @@ struct nf_conntrack_expect { possible_net_t net; /* We expect this tuple, with the following mask */ + struct nf_conntrack_tuple master_tuple; struct nf_conntrack_tuple tuple; struct nf_conntrack_tuple_mask mask; diff --git a/net/netfilter/nf_conntrack_broadcast.c b/net/netfilter/nf_conntrack_broadcast.c index 400119b6320e7..bf78828c7549d 100644 --- a/net/netfilter/nf_conntrack_broadcast.c +++ b/net/netfilter/nf_conntrack_broadcast.c @@ -62,6 +62,7 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb, if (exp == NULL) goto out; + exp->master_tuple = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; exp->tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple; helper = rcu_dereference(help->helper); diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index 49e18eda037ef..9454913e1b337 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -355,6 +355,8 @@ void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class, exp->tuple.src.l3num = family; exp->tuple.dst.protonum = proto; + exp->master_tuple = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; + if (saddr) { memcpy(&exp->tuple.src.u3, saddr, len); if (sizeof(exp->tuple.src.u3) > len) diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 211f361188f18..b11b8b7f31aaa 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -3007,7 +3007,6 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb, const struct nf_conntrack_expect *exp) { __s32 timeout = (__s32)(READ_ONCE(exp->timeout) - nfct_time_stamp) / HZ; - struct nf_conn *master = exp->master; struct nf_conntrack_helper *helper; #if IS_ENABLED(CONFIG_NF_NAT) struct nlattr *nest_parms; @@ -3022,9 +3021,7 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb, goto nla_put_failure; if (ctnetlink_exp_dump_mask(skb, &exp->tuple, &exp->mask) < 0) goto nla_put_failure; - if (ctnetlink_exp_dump_tuple(skb, - &master->tuplehash[IP_CT_DIR_ORIGINAL].tuple, - CTA_EXPECT_MASTER) < 0) + if (ctnetlink_exp_dump_tuple(skb, &exp->master_tuple, CTA_EXPECT_MASTER) < 0) goto nla_put_failure; #if IS_ENABLED(CONFIG_NF_NAT) @@ -3037,9 +3034,9 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb, if (nla_put_be32(skb, CTA_EXPECT_NAT_DIR, htonl(exp->dir))) goto nla_put_failure; - nat_tuple.src.l3num = nf_ct_l3num(master); + nat_tuple.src.l3num = exp->master_tuple.src.l3num; nat_tuple.src.u3 = exp->saved_addr; - nat_tuple.dst.protonum = nf_ct_protonum(master); + nat_tuple.dst.protonum = exp->master_tuple.dst.protonum; nat_tuple.src.u = exp->saved_proto; if (ctnetlink_exp_dump_tuple(skb, &nat_tuple, @@ -3581,6 +3578,7 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct, #endif rcu_assign_pointer(exp->helper, helper); rcu_assign_pointer(exp->assign_helper, assign_helper); + exp->master_tuple = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; exp->tuple = *tuple; exp->mask.src.u3 = mask->src.u3; exp->mask.src.u.all = mask->src.u.all; From f32e644fe36569ed653eb52f6c8d0a66bcc2a2d3 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 22 Jun 2026 21:35:18 +0200 Subject: [PATCH 1912/2758] netfilter: nf_conntrack_expect: run expectation eviction with no helper [ Upstream commit be57dd9c1c1796e368582313af2b3849f78ac224 ] Run expectation eviction if no helper is specified to deal with the nft_ct expectation support. Cap the maximum expectation limit per master conntrack to NF_CT_EXPECT_MAX_CNT (255). Fixes: 857b46027d6f ("netfilter: nft_ct: add ct expectations support") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_conntrack_expect.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index 9454913e1b337..113bb1cb1683f 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -499,6 +499,13 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect, if (p->max_expected && master_help->expecting[expect->class] >= p->max_expected) evict_oldest_expect(master_help, expect, p); + } else { + const struct nf_conntrack_expect_policy default_exp_policy = { + .max_expected = NF_CT_EXPECT_MAX_CNT, + }; + + if (master_help->expecting[expect->class] >= default_exp_policy.max_expected) + evict_oldest_expect(master_help, expect, &default_exp_policy); } cnet = nf_ct_pernet(net); From e3b7789be80d9b9c0f9306d867499a1f9cedcc08 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 23 Jun 2026 07:30:34 +0200 Subject: [PATCH 1913/2758] netfilter: nft_ct: expectation timeouts are passed in milliseconds [ Upstream commit 6fb421bd07f156cdf0cdede062d31f1c21def326 ] Userspace passes '5000' in case user asks for 5 seconds. Allowing for sub-second expectation lifetimes makes sense to me. so fix up the kernel side instead of munging nft to send a value rounded up to next second. Also note that this violates nft convention of passing integers in network byte order, but we can't change this anymore. Fixes: 857b46027d6f ("netfilter: nft_ct: add ct expectations support") Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nft_ct.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index f31e18639b358..8a79e9c1e1aed 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c @@ -1216,11 +1216,23 @@ struct nft_ct_expect_obj { u32 timeout; }; +static int nft_ct_expect_timeout_get(const struct nlattr *attr, u32 *val) +{ + unsigned long jiffies_val = msecs_to_jiffies(nla_get_u32(attr)); + + if (jiffies_val > UINT_MAX) + return -ERANGE; + + *val = jiffies_val; + return 0; +} + static int nft_ct_expect_obj_init(const struct nft_ctx *ctx, const struct nlattr * const tb[], struct nft_object *obj) { struct nft_ct_expect_obj *priv = nft_obj_data(obj); + int err; if (!tb[NFTA_CT_EXPECT_L4PROTO] || !tb[NFTA_CT_EXPECT_DPORT] || @@ -1255,8 +1267,11 @@ static int nft_ct_expect_obj_init(const struct nft_ctx *ctx, return -EOPNOTSUPP; } + err = nft_ct_expect_timeout_get(tb[NFTA_CT_EXPECT_TIMEOUT], &priv->timeout); + if (err) + return err; + priv->dport = nla_get_be16(tb[NFTA_CT_EXPECT_DPORT]); - priv->timeout = nla_get_u32(tb[NFTA_CT_EXPECT_TIMEOUT]); priv->size = nla_get_u8(tb[NFTA_CT_EXPECT_SIZE]); return nf_ct_netns_get(ctx->net, ctx->family); @@ -1276,7 +1291,7 @@ static int nft_ct_expect_obj_dump(struct sk_buff *skb, if (nla_put_be16(skb, NFTA_CT_EXPECT_L3PROTO, htons(priv->l3num)) || nla_put_u8(skb, NFTA_CT_EXPECT_L4PROTO, priv->l4proto) || nla_put_be16(skb, NFTA_CT_EXPECT_DPORT, priv->dport) || - nla_put_u32(skb, NFTA_CT_EXPECT_TIMEOUT, priv->timeout) || + nla_put_u32(skb, NFTA_CT_EXPECT_TIMEOUT, jiffies_to_msecs(priv->timeout)) || nla_put_u8(skb, NFTA_CT_EXPECT_SIZE, priv->size)) return -1; @@ -1326,7 +1341,7 @@ static void nft_ct_expect_obj_eval(struct nft_object *obj, &ct->tuplehash[!dir].tuple.src.u3, &ct->tuplehash[!dir].tuple.dst.u3, priv->l4proto, NULL, &priv->dport); - exp->timeout += priv->timeout * HZ; + exp->timeout += priv->timeout; if (nf_ct_expect_related(exp, 0) != 0) regs->verdict.code = NF_DROP; From c239f2d879abd069282bc4910f83f0a37475695f Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 23 Jun 2026 12:56:55 +0200 Subject: [PATCH 1914/2758] netfilter: nf_conntrack_helper: cap maximum number of expectation at helper registration [ Upstream commit 397c8300972f6e1486fd1afd99a044648a401cd5 ] On helper registration, the maximum number of expectations cannot go over NF_CT_EXPECT_MAX_CNT (255), but zero can be specified then nf_conntrack_expect_max applies. Turn zero into NF_CT_EXPECT_MAX_CNT otherwise, expectation LRU eviction on insertion is disabled. Moreover, expand this sanity check all expectation classes. This max_expecy policy is only tunable since userspace helpers are available, set Fixes: tag to the commit that adds such infrastructure. Remove the check for p->max_expected given this field must always be non-zero after this patch. Fixes: 12f7a505331e ("netfilter: add user-space connection tracking helper infrastructure") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_conntrack_expect.c | 3 +-- net/netfilter/nf_conntrack_helper.c | 9 +++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index 113bb1cb1683f..38630c5e006f0 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -496,8 +496,7 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect, lockdep_is_held(&nf_conntrack_expect_lock)); if (helper) { p = &helper->expect_policy[expect->class]; - if (p->max_expected && - master_help->expecting[expect->class] >= p->max_expected) + if (master_help->expecting[expect->class] >= p->max_expected) evict_oldest_expect(master_help, expect, p); } else { const struct nf_conntrack_expect_policy default_exp_policy = { diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index ec0aafcb19851..d1392b8e09af8 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -377,8 +377,13 @@ int __nf_conntrack_helper_register(struct nf_conntrack_helper *me) if (!nf_ct_helper_hash) return -ENOENT; - if (me->expect_policy->max_expected > NF_CT_EXPECT_MAX_CNT) - return -EINVAL; + for (i = 0; i <= me->expect_class_max; i++) { + if (!me->expect_policy[i].max_expected) + me->expect_policy[i].max_expected = NF_CT_EXPECT_MAX_CNT; + + if (me->expect_policy[i].max_expected > NF_CT_EXPECT_MAX_CNT) + return -EINVAL; + } mutex_lock(&nf_ct_helper_mutex); for (i = 0; i < nf_ct_helper_hsize; i++) { From 0ae6e70edc33e9f5b6f3b4ff14e7893bdac7b851 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 17 Jun 2026 18:16:16 +0200 Subject: [PATCH 1915/2758] cpuidle: Allow exit latency to exceed target residency [ Upstream commit 68ff4a3ccda9f98c74f23c70c8c7c581f9eee931 ] Commit 76934e495cdc ("cpuidle: Add sanity check for exit latency and target residency") had added a check to prevent the exit latency of an idle state from exceeding its target residency that later was limited to printing a warning message in that case in commit 4bf944f3fcb6 ("cpuidle: Warn instead of bailing out if target residency check fails"). However, a thorough code inspection with that in mind leads to the conclusion that actually there are no assumptions in cpuidle regarding the relationship between the exit latency and target residency of a given idle state. It is generally assumed that the idle states table provided by a cpuidle driver will be sorted by both the target residency and exit latency in ascending order, but that's a different matter. Accordingly, drop the check in question along with the message printed when it triggers and the inaccurate comment preceding it. Fixes: 4bf944f3fcb6 ("cpuidle: Warn instead of bailing out if target residency check fails") Signed-off-by: Rafael J. Wysocki Reviewed-by: Christian Loehle [ rjw: Subject fixup ] Link: https://patch.msgid.link/3444162.aeNJFYEL58@rafael.j.wysocki Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/cpuidle/driver.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c index 370664c47e659..e355b42043cf2 100644 --- a/drivers/cpuidle/driver.c +++ b/drivers/cpuidle/driver.c @@ -195,14 +195,6 @@ static void __cpuidle_driver_init(struct cpuidle_driver *drv) s->exit_latency_ns = 0; else s->exit_latency = div_u64(s->exit_latency_ns, NSEC_PER_USEC); - - /* - * Warn if the exit latency of a CPU idle state exceeds its - * target residency which is assumed to never happen in cpuidle - * in multiple places. - */ - if (s->exit_latency_ns > s->target_residency_ns) - pr_warn("Idle state %d target residency too low\n", i); } } From 33387bf9bb6116a0429f823f8dab3accf8f8e09c Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Tue, 23 Jun 2026 20:05:26 +0900 Subject: [PATCH 1916/2758] ASoC: SDCA: Validate written enum value in ge_put_enum_double() [ Upstream commit 1ce42a11bed134903e352010a01fa53073a6b395 ] ge_put_enum_double() passes the user-supplied enumeration index item[0] to snd_soc_enum_item_to_val() without checking it against the number of items in the enum: ret = snd_soc_enum_item_to_val(e, item[0]); snd_soc_enum_item_to_val() indexes the heap-allocated e->values[] array with that index (e->values is set from a devm_kcalloc() of e->items entries), so a control write with an out-of-range item[0] reads past the end of the values buffer. The bounds check in snd_soc_dapm_put_enum_double() only runs afterwards, so it does not prevent the read here. Reject an out-of-range item before using it, matching the other enum put handlers. This issue was pointed out by the Sashiko AI review bot while reviewing a related enum-validation series: https://lore.kernel.org/all/20260609125735.CEB651F00893@smtp.kernel.org/ Fixes: 812ff1baa764 ("ASoC: SDCA: Limit values user can write to Selected Mode") Signed-off-by: HyeongJun An Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20260623110526.813217-1-sammiee5311@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/sdca/sdca_asoc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index 2bfc8e5aee31d..243b83bb2bcf3 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -160,6 +160,9 @@ static int ge_put_enum_double(struct snd_kcontrol *kcontrol, unsigned int reg = e->reg; int ret; + if (item[0] >= e->items) + return -EINVAL; + reg &= ~SDW_SDCA_CTL_CSEL(0x3F); reg |= SDW_SDCA_CTL_CSEL(SDCA_CTL_GE_DETECTED_MODE); From 9e105f6a14fbe23755ed04df1122b89800466617 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Tue, 23 Jun 2026 18:25:14 +0800 Subject: [PATCH 1917/2758] ASoC: rt5575: Use __le32 for SPI burst write address [ Upstream commit d0c415f0076b71b956f62ff6f31de885f0fa2489 ] The addr field in the SPI burst write buffer represents on-wire little-endian data. Define it as __le32 so that the assignment of cpu_to_le32() is type correct and avoids sparse endian warnings. Closes: https://lore.kernel.org/oe-kbuild-all/202606230139.rFZUVpCa-lkp@intel.com/ Fixes: 420739112e95 ("ASoC: rt5575: Add the codec driver for the ALC5575") Reported-by: kernel test robot Signed-off-by: Oder Chiou Link: https://patch.msgid.link/20260623102514.2422990-1-oder_chiou@realtek.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/rt5575-spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/rt5575-spi.c b/sound/soc/codecs/rt5575-spi.c index 9a349965435b6..d5b3a57c88666 100644 --- a/sound/soc/codecs/rt5575-spi.c +++ b/sound/soc/codecs/rt5575-spi.c @@ -17,7 +17,7 @@ struct rt5575_spi_burst_write { u8 cmd; - u32 addr; + __le32 addr; u8 data[RT5575_SPI_BUF_LEN]; u8 dummy; } __packed; From 9787c2d171110d5a0b35ea812be68aad11e7a396 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 13 May 2026 11:49:17 +0900 Subject: [PATCH 1918/2758] PCI: endpoint: pci-epf-vntb: Exclude reserved slots from db_valid_mask [ Upstream commit 823468a4ea1613f1c1235bd16af8b9c6eb9c9677 ] In pci-epf-vntb, db_count represents the total number of doorbell slots exposed to the peer, including: - slot #0 reserved for link events, and - slot #1 historically unused (kept for compatibility). Only the remaining slots correspond to actual doorbell bits. The current db_valid_mask() exposes all slots as valid doorbells. Limit db_valid_mask() to the real doorbell bits by returning BIT_ULL(db_count - 2) - 1, and guard against db_count < 2. Fixes: e35f56bb0330 ("PCI: endpoint: Support NTB transfer between RC and EP") Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Frank Li Link: https://patch.msgid.link/20260513024923.451765-7-den@valinux.co.jp Signed-off-by: Sasha Levin --- drivers/pci/endpoint/functions/pci-epf-vntb.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index 01d28a7d35df4..ff9db8f980af5 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -1268,7 +1268,10 @@ static int vntb_epf_peer_mw_count(struct ntb_dev *ntb) static u64 vntb_epf_db_valid_mask(struct ntb_dev *ntb) { - return BIT_ULL(ntb_ndev(ntb)->db_count) - 1; + if (ntb_ndev(ntb)->db_count < EPF_IRQ_DB_START) + return 0; + + return BIT_ULL(ntb_ndev(ntb)->db_count - EPF_IRQ_DB_START) - 1; } static int vntb_epf_db_set_mask(struct ntb_dev *ntb, u64 db_bits) From 583a4a19eefc3b5c0fe6af9dc66e4ab6880e0ffe Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 13 May 2026 11:49:20 +0900 Subject: [PATCH 1919/2758] NTB: epf: Make db_valid_mask cover only real doorbell bits [ Upstream commit 6eb7e28f1f24a28234add38755687a7ed8bc2654 ] ndev->db_count includes an unused doorbell slot due to the legacy extra offset in the peer doorbell path. db_valid_mask must cover only the real doorbell bits and exclude the unused slot. Set db_valid_mask to BIT_ULL(db_count - 1) - 1. Fixes: 812ce2f8d14e ("NTB: Add support for EPF PCI Non-Transparent Bridge") Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Frank Li Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260513024923.451765-10-den@valinux.co.jp Signed-off-by: Sasha Levin --- drivers/ntb/hw/epf/ntb_hw_epf.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c index 15d99d08f84f9..051af2a45c6f9 100644 --- a/drivers/ntb/hw/epf/ntb_hw_epf.c +++ b/drivers/ntb/hw/epf/ntb_hw_epf.c @@ -560,7 +560,11 @@ static int ntb_epf_init_dev(struct ntb_epf_dev *ndev) return ret; } - ndev->db_valid_mask = BIT_ULL(ndev->db_count) - 1; + /* + * ndev->db_count includes an extra skipped slot due to the legacy + * doorbell layout, hence -1. + */ + ndev->db_valid_mask = BIT_ULL(ndev->db_count - 1) - 1; ndev->mw_count = readl(ndev->ctrl_reg + NTB_EPF_MW_COUNT); ndev->spad_count = readl(ndev->ctrl_reg + NTB_EPF_SPAD_COUNT); From bfe11cd91ab0218abee5f4e1e41215015105405e Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 13 May 2026 11:49:21 +0900 Subject: [PATCH 1920/2758] NTB: epf: Report 0-based doorbell vector via ntb_db_event() [ Upstream commit 3147f0964cecca15e349cbfbdd6a28eb6d50379d ] ntb_db_event() expects the vector number to be relative to the first doorbell vector starting at 0. Vector 0 is reserved for link events in the EPF driver, so doorbells start at vector 1. However, both supported peers (ntb_hw_epf with pci-epf-ntb, and pci-epf-vntb) have historically skipped vector 1 and started doorbells at vector 2. Pass (irq_no - 2) to ntb_db_event() so doorbells are reported as 0..N-1. If irq_no == 1 is ever observed, warn and ignore it, since the slot is reserved in the legacy layout and reporting it as DB#0 would collide with the real DB#0 slot. Fixes: 812ce2f8d14e ("NTB: Add support for EPF PCI Non-Transparent Bridge") Suggested-by: Dave Jiang Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Dave Jiang Reviewed-by: Frank Li Link: https://patch.msgid.link/20260513024923.451765-11-den@valinux.co.jp Signed-off-by: Sasha Levin --- drivers/ntb/hw/epf/ntb_hw_epf.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c index 051af2a45c6f9..292e2ca059ac3 100644 --- a/drivers/ntb/hw/epf/ntb_hw_epf.c +++ b/drivers/ntb/hw/epf/ntb_hw_epf.c @@ -69,6 +69,12 @@ enum epf_ntb_bar { NTB_BAR_NUM, }; +enum epf_irq_slot { + EPF_IRQ_LINK = 0, + EPF_IRQ_RESERVED_DB, /* Historically skipped slot */ + EPF_IRQ_DB_START, +}; + #define NTB_EPF_MAX_MW_COUNT (NTB_BAR_NUM - BAR_MW1) struct ntb_epf_dev { @@ -322,10 +328,14 @@ static irqreturn_t ntb_epf_vec_isr(int irq, void *dev) irq_no = irq - ndev->irq_base; ndev->db_val = irq_no + 1; - if (irq_no == 0) + if (irq_no == EPF_IRQ_LINK) { ntb_link_event(&ndev->ntb); - else - ntb_db_event(&ndev->ntb, irq_no); + } else if (irq_no == EPF_IRQ_RESERVED_DB) { + dev_warn_ratelimited(ndev->dev, + "Unexpected reserved doorbell slot IRQ received\n"); + } else { + ntb_db_event(&ndev->ntb, irq_no - EPF_IRQ_DB_START); + } return IRQ_HANDLED; } From 75d7a27c506e4ffe73d7d753929955a06a3e86cf Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 13 May 2026 11:49:22 +0900 Subject: [PATCH 1921/2758] NTB: epf: Fix doorbell bitmask and IRQ vector handling [ Upstream commit 4fdea8dbb62d746429498e07385a3ba0b0f6d1ab ] The EPF driver currently stores the incoming doorbell as a vector number (irq_no + 1) in db_val and db_clear() clears all bits unconditionally. This breaks db_read()/db_clear() semantics when multiple doorbells are used. Store doorbells as a bitmask (BIT_ULL(vector)) and make db_clear(db_bits) clear only the specified bits. Use atomic64 operations as db_val is updated from interrupt context. Once db_val is stored as a bitmask, the ISR's doorbell vector is used not only for ntb_db_event(), but also as the bit index for BIT_ULL(). The existing ISR derives that vector by subtracting pci_irq_vector(pdev, 0) from the Linux IRQ number passed to the handler, but Linux IRQ numbers are not guaranteed to be contiguous. Pass per-vector context as request_irq() dev_id instead, so the ISR gets the device vector directly. Validate the doorbell vector before updating db_val or calling ntb_db_event(), so an unexpected vector cannot create an invalid shift or be reported to NTB clients. While at it, read and validate mw_count before requesting interrupt vectors. An unsupported memory-window count does not need IRQs, and failing before ntb_epf_init_isr() keeps the probe error path simple. Fixes: 812ce2f8d14e ("NTB: Add support for EPF PCI Non-Transparent Bridge") Suggested-by: Dave Jiang Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Dave Jiang Reviewed-by: Frank Li Link: https://patch.msgid.link/20260513024923.451765-12-den@valinux.co.jp Signed-off-by: Sasha Levin --- drivers/ntb/hw/epf/ntb_hw_epf.c | 61 +++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c index 292e2ca059ac3..200409b4e4ddf 100644 --- a/drivers/ntb/hw/epf/ntb_hw_epf.c +++ b/drivers/ntb/hw/epf/ntb_hw_epf.c @@ -6,6 +6,7 @@ * Author: Kishon Vijay Abraham I */ +#include #include #include #include @@ -77,6 +78,13 @@ enum epf_irq_slot { #define NTB_EPF_MAX_MW_COUNT (NTB_BAR_NUM - BAR_MW1) +struct ntb_epf_dev; + +struct ntb_epf_irq_ctx { + struct ntb_epf_dev *ndev; + unsigned int irq_no; +}; + struct ntb_epf_dev { struct ntb_dev ntb; struct device *dev; @@ -96,9 +104,9 @@ struct ntb_epf_dev { unsigned int self_spad; unsigned int peer_spad; - int db_val; + atomic64_t db_val; u64 db_valid_mask; - int irq_base; + struct ntb_epf_irq_ctx irq_ctx[NTB_EPF_MAX_DB_COUNT + 1]; }; #define ntb_ndev(__ntb) container_of(__ntb, struct ntb_epf_dev, ntb) @@ -322,11 +330,10 @@ static int ntb_epf_link_disable(struct ntb_dev *ntb) static irqreturn_t ntb_epf_vec_isr(int irq, void *dev) { - struct ntb_epf_dev *ndev = dev; - int irq_no; - - irq_no = irq - ndev->irq_base; - ndev->db_val = irq_no + 1; + struct ntb_epf_irq_ctx *ctx = dev; + struct ntb_epf_dev *ndev = ctx->ndev; + unsigned int db_vector; + unsigned int irq_no = ctx->irq_no; if (irq_no == EPF_IRQ_LINK) { ntb_link_event(&ndev->ntb); @@ -334,7 +341,17 @@ static irqreturn_t ntb_epf_vec_isr(int irq, void *dev) dev_warn_ratelimited(ndev->dev, "Unexpected reserved doorbell slot IRQ received\n"); } else { - ntb_db_event(&ndev->ntb, irq_no - EPF_IRQ_DB_START); + db_vector = irq_no - EPF_IRQ_DB_START; + if (ndev->db_count < NTB_EPF_MIN_DB_COUNT || + db_vector >= ndev->db_count - 1) { + dev_warn_ratelimited(ndev->dev, + "Unexpected doorbell vector %u (db_count %u)\n", + db_vector, ndev->db_count); + return IRQ_HANDLED; + } + + atomic64_or(BIT_ULL(db_vector), &ndev->db_val); + ntb_db_event(&ndev->ntb, db_vector); } return IRQ_HANDLED; @@ -361,18 +378,18 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max) argument &= ~MSIX_ENABLE; } - ndev->irq_base = pci_irq_vector(pdev, 0); + ndev->db_count = irq - 1; for (i = 0; i < irq; i++) { + ndev->irq_ctx[i].ndev = ndev; + ndev->irq_ctx[i].irq_no = i; ret = request_irq(pci_irq_vector(pdev, i), ntb_epf_vec_isr, - 0, "ntb_epf", ndev); + 0, "ntb_epf", &ndev->irq_ctx[i]); if (ret) { dev_err(dev, "Failed to request irq\n"); goto err_free_irq; } } - ndev->db_count = irq - 1; - ret = ntb_epf_send_command(ndev, CMD_CONFIGURE_DOORBELL, argument | irq); if (ret) { @@ -384,7 +401,7 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max) err_free_irq: while (i--) - free_irq(pci_irq_vector(pdev, i), ndev); + free_irq(pci_irq_vector(pdev, i), &ndev->irq_ctx[i]); pci_free_irq_vectors(pdev); return ret; @@ -509,7 +526,7 @@ static u64 ntb_epf_db_read(struct ntb_dev *ntb) { struct ntb_epf_dev *ndev = ntb_ndev(ntb); - return ndev->db_val; + return atomic64_read(&ndev->db_val); } static int ntb_epf_db_clear_mask(struct ntb_dev *ntb, u64 db_bits) @@ -521,7 +538,7 @@ static int ntb_epf_db_clear(struct ntb_dev *ntb, u64 db_bits) { struct ntb_epf_dev *ndev = ntb_ndev(ntb); - ndev->db_val = 0; + atomic64_and(~db_bits, &ndev->db_val); return 0; } @@ -562,6 +579,12 @@ static int ntb_epf_init_dev(struct ntb_epf_dev *ndev) struct device *dev = ndev->dev; int ret; + ndev->mw_count = readl(ndev->ctrl_reg + NTB_EPF_MW_COUNT); + if (ndev->mw_count > NTB_EPF_MAX_MW_COUNT) { + dev_err(dev, "Unsupported MW count: %u\n", ndev->mw_count); + return -EINVAL; + } + /* One Link interrupt and rest doorbell interrupt */ ret = ntb_epf_init_isr(ndev, NTB_EPF_MIN_DB_COUNT + 1, NTB_EPF_MAX_DB_COUNT + 1); @@ -575,14 +598,8 @@ static int ntb_epf_init_dev(struct ntb_epf_dev *ndev) * doorbell layout, hence -1. */ ndev->db_valid_mask = BIT_ULL(ndev->db_count - 1) - 1; - ndev->mw_count = readl(ndev->ctrl_reg + NTB_EPF_MW_COUNT); ndev->spad_count = readl(ndev->ctrl_reg + NTB_EPF_SPAD_COUNT); - if (ndev->mw_count > NTB_EPF_MAX_MW_COUNT) { - dev_err(dev, "Unsupported MW count: %u\n", ndev->mw_count); - return -EINVAL; - } - return 0; } @@ -677,7 +694,7 @@ static void ntb_epf_cleanup_isr(struct ntb_epf_dev *ndev) ntb_epf_send_command(ndev, CMD_TEARDOWN_DOORBELL, ndev->db_count + 1); for (i = 0; i < ndev->db_count + 1; i++) - free_irq(pci_irq_vector(pdev, i), ndev); + free_irq(pci_irq_vector(pdev, i), &ndev->irq_ctx[i]); pci_free_irq_vectors(pdev); } From 257b55dc3d18d7ef01f62a8ff7317871f7597e28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Fri, 8 May 2026 04:35:30 +0000 Subject: [PATCH 1922/2758] alpha/PCI: Add security_locked_down() check to pci_mmap_resource() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 78a228f0aa0e9eba31955950c8a40a9945e2c8bb ] Currently, Alpha's pci_mmap_resource() does not check security_locked_down(LOCKDOWN_PCI_ACCESS) before allowing userspace to mmap PCI BARs. The generic version has had this check since commit eb627e17727e ("PCI: Lock down BAR access when the kernel is locked down") to prevent DMA attacks when the kernel is locked down. Add the same check to Alpha's pci_mmap_resource(). Fixes: eb627e17727e ("PCI: Lock down BAR access when the kernel is locked down") Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Tested-by: Magnus Lindholm Tested-by: Shivaprasad G Bhat Reviewed-by: Ilpo Järvinen Acked-by: Magnus Lindholm Link: https://patch.msgid.link/20260508043543.217179-12-kwilczynski@kernel.org Signed-off-by: Sasha Levin --- arch/alpha/kernel/pci-sysfs.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c index 3048758304b57..2324720c3e83f 100644 --- a/arch/alpha/kernel/pci-sysfs.c +++ b/arch/alpha/kernel/pci-sysfs.c @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -71,7 +72,11 @@ static int pci_mmap_resource(struct kobject *kobj, struct resource *res = attr->private; enum pci_mmap_state mmap_type; struct pci_bus_region bar; - int i; + int i, ret; + + ret = security_locked_down(LOCKDOWN_PCI_ACCESS); + if (ret) + return ret; for (i = 0; i < PCI_STD_NUM_BARS; i++) if (res == &pdev->resource[i]) From c19faa40b37d7fb6b52ce055ca5714108fdb897e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Fri, 8 May 2026 04:35:33 +0000 Subject: [PATCH 1923/2758] alpha/PCI: Fix __pci_mmap_fits() overflow for zero-length BARs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 802a3b3f470b9bc1148f26e01fc9cbfeb4dfcb57 ] Currently, __pci_mmap_fits() computes the BAR size using "pci_resource_len() - 1", which wraps to a large value when the BAR length is zero, causing the bounds check to incorrectly succeed. Add an early return for empty resources. Fixes: 10a0ef39fbd1 ("PCI/alpha: pci sysfs resources") Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Tested-by: Magnus Lindholm Tested-by: Shivaprasad G Bhat Reviewed-by: Ilpo Järvinen Acked-by: Magnus Lindholm Link: https://patch.msgid.link/20260508043543.217179-15-kwilczynski@kernel.org Signed-off-by: Sasha Levin --- arch/alpha/kernel/pci-sysfs.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c index 2324720c3e83f..21833d1c867d7 100644 --- a/arch/alpha/kernel/pci-sysfs.c +++ b/arch/alpha/kernel/pci-sysfs.c @@ -37,12 +37,16 @@ static int hose_mmap_page_range(struct pci_controller *hose, static int __pci_mmap_fits(struct pci_dev *pdev, int num, struct vm_area_struct *vma, int sparse) { + resource_size_t len = pci_resource_len(pdev, num); unsigned long nr, start, size; int shift = sparse ? 5 : 0; + if (!len) + return 0; + nr = vma_pages(vma); start = vma->vm_pgoff; - size = ((pci_resource_len(pdev, num) - 1) >> (PAGE_SHIFT - shift)) + 1; + size = ((len - 1) >> (PAGE_SHIFT - shift)) + 1; if (start < size && size - start >= nr) return 1; From 752b781b0c0ae5a725d108f3cefa828deeff1415 Mon Sep 17 00:00:00 2001 From: Doehyun Baek Date: Mon, 22 Jun 2026 18:18:21 +0000 Subject: [PATCH 1924/2758] Docs/driver-api/uio-howto: document mmap_prepare callback [ Upstream commit de5c46373eb8148aa92c024cf30d26a6d495e278 ] The UIO howto still documents an mmap callback in struct uio_info. That field was replaced by mmap_prepare, which takes a struct vm_area_desc. A UIO driver following the current howto no longer builds because struct uio_info has no mmap member. Update the documented callback signature and matching text to match the current API. Fixes: 933f05f58ac6 ("uio: replace deprecated mmap hook with mmap_prepare in uio_info") Signed-off-by: Doehyun Baek Reviewed-by: Lorenzo Stoakes Acked-by: Randy Dunlap Signed-off-by: Jonathan Corbet Message-ID: <20260622181821.1195257-1-doehyunbaek@gmail.com> Signed-off-by: Sasha Levin --- Documentation/driver-api/uio-howto.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/driver-api/uio-howto.rst b/Documentation/driver-api/uio-howto.rst index 907ffa3b38f5b..c08472dfbcfeb 100644 --- a/Documentation/driver-api/uio-howto.rst +++ b/Documentation/driver-api/uio-howto.rst @@ -246,10 +246,10 @@ the members are required, others are optional. hardware interrupt number. The flags given here will be used in the call to :c:func:`request_irq()`. -- ``int (*mmap)(struct uio_info *info, struct vm_area_struct *vma)``: +- ``int (*mmap_prepare)(struct uio_info *info, struct vm_area_desc *desc)``: Optional. If you need a special :c:func:`mmap()` function, you can set it here. If this pointer is not NULL, your - :c:func:`mmap()` will be called instead of the built-in one. + ``mmap_prepare`` will be called instead of the built-in one. - ``int (*open)(struct uio_info *info, struct inode *inode)``: Optional. You might want to have your own :c:func:`open()`, From 89c103d702b25ceb2d097faf854deb47b53b17ff Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sat, 20 Jun 2026 13:15:31 -0700 Subject: [PATCH 1925/2758] net, bpf: check master for NULL in xdp_master_redirect() [ Upstream commit e82d8cc4321c373dc46e741cd2dfdaa7921fddb7 ] xdp_master_redirect() dereferences the result of netdev_master_upper_dev_get_rcu() without a NULL check, but that helper returns NULL when the receiving device has no upper-master adjacency. The reach guard only checks netif_is_bond_slave(). On bond slave release bond_upper_dev_unlink() drops the upper-master adjacency before clearing IFF_SLAVE, so an XDP_TX reaching xdp_master_redirect() in that window still passes netif_is_bond_slave() while master is already NULL, and faults on master->flags at offset 0xb0: BUG: kernel NULL pointer dereference, address: 00000000000000b0 RIP: 0010:xdp_master_redirect (net/core/filter.c:4432) Call Trace: xdp_master_redirect (net/core/filter.c:4432) bpf_prog_run_generic_xdp (include/net/xdp.h:700) do_xdp_generic (net/core/dev.c:5608) __netif_receive_skb_one_core (net/core/dev.c:6204) process_backlog (net/core/dev.c:6319) __napi_poll (net/core/dev.c:7729) net_rx_action (net/core/dev.c:7792) handle_softirqs (kernel/softirq.c:622) __dev_queue_xmit (include/linux/bottom_half.h:33) packet_sendmsg (net/packet/af_packet.c:3082) __sys_sendto (net/socket.c:2252) Kernel panic - not syncing: Fatal exception in interrupt The missing check dates back to the original code; commit 1921f91298d1 ("net, bpf: fix null-ptr-deref in xdp_master_redirect() for down master") later added the master->flags read where the fault now lands but kept the unconditional deref. Check master for NULL before use; a NULL master is treated the same as one that is not up. Fixes: 879af96ffd72 ("net, core: Add support for XDP redirection to slave device") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Reviewed-by: Jiayuan Chen Link: https://patch.msgid.link/20260620201531.180123-1-xmei5@asu.edu Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/core/filter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/core/filter.c b/net/core/filter.c index 77984b5fada4c..f21aeff99cc98 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -4433,7 +4433,7 @@ u32 xdp_master_redirect(struct xdp_buff *xdp) struct net_device *master, *slave; master = netdev_master_upper_dev_get_rcu(xdp->rxq->dev); - if (unlikely(!(master->flags & IFF_UP))) + if (unlikely(!master || !(master->flags & IFF_UP))) return XDP_ABORTED; slave = master->netdev_ops->ndo_xdp_get_xmit_slave(master, xdp); if (slave && slave != xdp->rxq->dev) { From a3d0c8b437efe4af31ad5a02f648cf1b3a533531 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 22 Jun 2026 11:01:08 +0000 Subject: [PATCH 1926/2758] net: do not acquire dev->tx_global_lock in netdev_watchdog_up() [ Upstream commit d09a78a2a469e4fab75108325efb813c49520809 ] Marek Szyprowski reported a deadlock during system resume when virtio_net driver is used. The deadlock occurs because netif_device_attach() is called while holding dev->tx_global_lock (via netif_tx_lock_bh() in virtnet_restore_up()). netif_device_attach() calls __netdev_watchdog_up(), which now also tries to acquire dev->tx_global_lock to synchronize with dev_watchdog(). This recursive lock acquisition results in a deadlock. Fix this by removing the tx_global_lock acquisition from netdev_watchdog_up(). The critical state (watchdog_timer and watchdog_ref_held) is already protected by dev->watchdog_lock, which was introduced in the blamed commit. Fixes: 8eed5519e496 ("net: watchdog: fix refcount tracking races") Reported-by: Marek Szyprowski Closes: https://lore.kernel.org/netdev/a443376e-5187-4268-93b3-58047ef113a8@samsung.com/ Signed-off-by: Eric Dumazet Tested-by: Marek Szyprowski Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260622110108.69541-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/sch_generic.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 3a0f3bb6a1ff4..fe73ac40d409e 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -594,9 +594,8 @@ void netdev_watchdog_up(struct net_device *dev) return; if (dev->watchdog_timeo <= 0) dev->watchdog_timeo = 5*HZ; - spin_lock_bh(&dev->tx_global_lock); - spin_lock(&dev->watchdog_lock); + spin_lock_bh(&dev->watchdog_lock); if (!mod_timer(&dev->watchdog_timer, round_jiffies(jiffies + dev->watchdog_timeo))) { if (!dev->watchdog_ref_held) { @@ -605,9 +604,7 @@ void netdev_watchdog_up(struct net_device *dev) dev->watchdog_ref_held = true; } } - spin_unlock(&dev->watchdog_lock); - - spin_unlock_bh(&dev->tx_global_lock); + spin_unlock_bh(&dev->watchdog_lock); } EXPORT_SYMBOL_GPL(netdev_watchdog_up); From 09efce96c909dff1a9317ec3714b7ddb4751b7af Mon Sep 17 00:00:00 2001 From: Aleksandrova Alyona Date: Thu, 18 Jun 2026 14:05:08 +0300 Subject: [PATCH 1927/2758] net: dsa: sja1105: round up PTP perout pin duration [ Upstream commit aee5836273b07b439fb245fb43930664d8b78518 ] pin_duration is converted from the user-provided period to SJA1105 clock ticks and is later passed as the cycle_time argument to future_base_time(). Very small period values may become zero after the conversion, which can lead to a division by zero in future_base_time(). Round zero pin_duration up to 1 tick so that the smallest unsupported periods use the minimum non-zero hardware duration instead of passing zero to future_base_time(). Fixes: 747e5eb31d59 ("net: dsa: sja1105: configure the PTP_CLK pin as EXT_TS or PER_OUT") Signed-off-by: Aleksandrova Alyona Link: https://patch.msgid.link/20260618110508.53094-1-aga@itb.spb.ru Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/dsa/sja1105/sja1105_ptp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/dsa/sja1105/sja1105_ptp.c b/drivers/net/dsa/sja1105/sja1105_ptp.c index fefe46e2a5e61..350f958dcb2af 100644 --- a/drivers/net/dsa/sja1105/sja1105_ptp.c +++ b/drivers/net/dsa/sja1105/sja1105_ptp.c @@ -755,7 +755,7 @@ static int sja1105_per_out_enable(struct sja1105_private *priv, * 2 edges on PTP_CLK. So check for truncation which happens * at periods larger than around 68.7 seconds. */ - pin_duration = ns_to_sja1105_ticks(pin_duration / 2); + pin_duration = max_t(u64, ns_to_sja1105_ticks(pin_duration / 2), 1); if (pin_duration > U32_MAX) { rc = -ERANGE; goto out; From 20d4a9dea55b7a939d11470ffd2eeda2d9733d46 Mon Sep 17 00:00:00 2001 From: Meghana Malladi Date: Thu, 18 Jun 2026 15:33:48 +0530 Subject: [PATCH 1928/2758] net: ti: icssg: Fix XSK zero copy TX during application wakeup [ Upstream commit d95ea4bc09e88fe00f8e1c4e27c021313a4139c7 ] emac_xsk_xmit_zc() handles tx xmit for zero copy and gets called inside napi context. User application wakes up the kernel while initiating the transmit which triggers napi to start processing the tx packets. The num_tx check inside emac_tx_complete_packets() returns early if no packet transfer happen hindering the call to emac_xsk_xmit_zc(). Remove this check to let application wakeup initiate zero copy xmit traffic. Add __netif_tx_lock() to ensure that the TX queue is protected from concurrent access during the transmission of XDP frames. This fixes netdev watchdog timeout for long runs. Fixes: e2dc7bfd677f ("net: ti: icssg-prueth: Move common functions into a separate file") Signed-off-by: Meghana Malladi Link: https://patch.msgid.link/20260618100348.2209907-1-m-malladi@ti.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/ti/icssg/icssg_common.c | 23 ++++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/ti/icssg/icssg_common.c b/drivers/net/ethernet/ti/icssg/icssg_common.c index 82ddef9c17d54..6973d47142463 100644 --- a/drivers/net/ethernet/ti/icssg/icssg_common.c +++ b/drivers/net/ethernet/ti/icssg/icssg_common.c @@ -93,8 +93,8 @@ void prueth_ndev_del_tx_napi(struct prueth_emac *emac, int num) } EXPORT_SYMBOL_GPL(prueth_ndev_del_tx_napi); -static int emac_xsk_xmit_zc(struct prueth_emac *emac, - unsigned int q_idx) +static void emac_xsk_xmit_zc(struct prueth_emac *emac, + unsigned int q_idx) { struct prueth_tx_chn *tx_chn = &emac->tx_chns[q_idx]; struct xsk_buff_pool *pool = tx_chn->xsk_pool; @@ -115,7 +115,7 @@ static int emac_xsk_xmit_zc(struct prueth_emac *emac, * necessary */ if (descs_avail <= MAX_SKB_FRAGS) - return 0; + return; descs_avail -= MAX_SKB_FRAGS; @@ -170,8 +170,8 @@ static int emac_xsk_xmit_zc(struct prueth_emac *emac, num_tx++; } - xsk_tx_release(tx_chn->xsk_pool); - return num_tx; + if (num_tx) + xsk_tx_release(tx_chn->xsk_pool); } void prueth_xmit_free(struct prueth_tx_chn *tx_chn, @@ -279,9 +279,6 @@ int emac_tx_complete_packets(struct prueth_emac *emac, int chn, num_tx++; } - if (!num_tx) - return 0; - netif_txq = netdev_get_tx_queue(ndev, chn); netdev_tx_completed_queue(netif_txq, num_tx, total_bytes); @@ -297,16 +294,18 @@ int emac_tx_complete_packets(struct prueth_emac *emac, int chn, __netif_tx_unlock(netif_txq); } - if (tx_chn->xsk_pool) { - if (xsk_frames_done) + if (budget && tx_chn->xsk_pool) { + if (xsk_frames_done) { xsk_tx_completed(tx_chn->xsk_pool, xsk_frames_done); + txq_trans_cond_update(netif_txq); + } if (xsk_uses_need_wakeup(tx_chn->xsk_pool)) xsk_set_tx_need_wakeup(tx_chn->xsk_pool); - netif_txq = netdev_get_tx_queue(ndev, chn); - txq_trans_cond_update(netif_txq); + __netif_tx_lock(netif_txq, smp_processor_id()); emac_xsk_xmit_zc(emac, chn); + __netif_tx_unlock(netif_txq); } return num_tx; From 4bd2e5dbe62334aae1182d0f0d260f334a49d739 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 22 Jun 2026 11:18:25 +0000 Subject: [PATCH 1929/2758] veth: fix NAPI leak in XDP enable error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 6739027cb72da26890edd424c77080d187b2a92e ] During XDP enablement in veth, if xdp_rxq_info_reg() or xdp_rxq_info_reg_mem_model() fails, the driver rolls back the changes. However, the rollback loop: for (i--; i >= start; i--) { decrements the loop index 'i' before the first iteration. This correctly skips unregistering the rxq for the failed index 'i' (as registration failed or was already cleaned up), but it also erroneously skips calling netif_napi_deli() for rq[i].xdp_napi. Since netif_napi_add() was already called for index 'i', this leaves a dangling napi_struct in the device's napi_list. When the veth device is later destroyed, the freed queue memory (which contains the leaked NAPI structure) can be reused. The subsequent device teardown iterates the NAPI list and corrupts the reallocated memory, leading to UAF. Fix this by explicitly deleting the NAPI association for the failed index 'i' before rolling back the successfully configured queues. Fixes: b02e5a0ebb17 ("xsk: Propagate napi_id to XDP socket Rx path") Reported-by: Guenter Roeck Signed-off-by: Eric Dumazet Cc: Björn Töpel Cc: Daniel Borkmann Cc: Ilias Apalodimas Cc: Michael S. Tsirkin Cc: Tariq Toukan Reviewed-by: Pavan Chebbi Link: https://patch.msgid.link/20260622111825.88337-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/veth.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 0cfb19b760dd5..1c51421491753 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -1137,6 +1137,8 @@ static int veth_enable_xdp_range(struct net_device *dev, int start, int end, err_reg_mem: xdp_rxq_info_unreg(&priv->rq[i].xdp_rxq); err_rxq_reg: + if (!napi_already_on) + netif_napi_del(&priv->rq[i].xdp_napi); for (i--; i >= start; i--) { struct veth_rq *rq = &priv->rq[i]; From cc27e4514e6e67324176215689b8f9e8994ea756 Mon Sep 17 00:00:00 2001 From: Nicolai Buchwitz Date: Mon, 22 Jun 2026 12:29:11 +0200 Subject: [PATCH 1930/2758] net: usb: lan78xx: restore VLAN and hash filters after link up [ Upstream commit 5c12248673c76f10ab900f70724c5da288c7efa5 ] Configured VLANs intermittently stop receiving traffic after a link down/up cycle, e.g. when the network cable is unplugged and plugged back in. VLAN filtering stays enabled but all VLAN-tagged frames are dropped until a VLAN is added or removed again. The LAN7801 datasheet (DS00002123E) states: "A portion of the MAC operates on clocks generated by the Ethernet PHY. During a PHY reset event, this portion of the MAC is designed to not be taken out of reset until the PHY clocks are operational" (section 8.10, MAC Reset Watchdog Timer) "After a reset event, the RFE will automatically initialize the contents of the VHF to 0h." (section 7.1.4, VHF Organization) Thus a link down/up cycle stops and restarts the PHY clock, resets the PHY-clocked portion of the MAC, and the RFE clears its VLAN/DA hash filter (VHF) memory. The VHF holds both the VLAN filter table and the multicast hash table, but the driver never reprograms either from its shadow copy once the link is back, so both stay empty. Reprogram the VLAN filter and multicast hash tables on link up. Reported-by: Sven Schuchmann Closes: https://lore.kernel.org/netdev/BEZP281MB224501E38B30BFDC4BD3D364D9E32@BEZP281MB2245.DEUP281.PROD.OUTLOOK.COM/T/#u Tested-by: Sven Schuchmann Fixes: 55d7de9de6c3 ("Microchip's LAN7800 family USB 2/3 to 10/100/1000 Ethernet device driver") Signed-off-by: Nicolai Buchwitz Link: https://patch.msgid.link/20260622102911.484045-1-nb@tipi-net.de Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/usb/lan78xx.c | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index bcf293ea1bd38..c4cebacabcb53 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -1452,6 +1452,15 @@ static inline u32 lan78xx_hash(char addr[ETH_ALEN]) return (ether_crc(ETH_ALEN, addr) >> 23) & 0x1ff; } +static int lan78xx_write_mchash_table(struct lan78xx_net *dev) +{ + struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]); + + return lan78xx_dataport_write(dev, DP_SEL_RSEL_VLAN_DA_, + DP_SEL_VHF_VLAN_LEN, + DP_SEL_VHF_HASH_LEN, pdata->mchash_table); +} + static void lan78xx_deferred_multicast_write(struct work_struct *param) { struct lan78xx_priv *pdata = @@ -1462,9 +1471,7 @@ static void lan78xx_deferred_multicast_write(struct work_struct *param) netif_dbg(dev, drv, dev->net, "deferred multicast write 0x%08x\n", pdata->rfe_ctl); - ret = lan78xx_dataport_write(dev, DP_SEL_RSEL_VLAN_DA_, - DP_SEL_VHF_VLAN_LEN, - DP_SEL_VHF_HASH_LEN, pdata->mchash_table); + ret = lan78xx_write_mchash_table(dev); if (ret < 0) goto multicast_write_done; @@ -1557,6 +1564,7 @@ static void lan78xx_set_multicast(struct net_device *netdev) } static void lan78xx_rx_urb_submit_all(struct lan78xx_net *dev); +static int lan78xx_write_vlan_table(struct lan78xx_net *dev); static int lan78xx_mac_reset(struct lan78xx_net *dev) { @@ -2514,6 +2522,17 @@ static void lan78xx_mac_link_up(struct phylink_config *config, if (ret < 0) goto link_up_fail; + /* The RFE clears the VLAN/DA hash filter (VHF) on a link down/up + * cycle, so reprogram both tables from their shadow copies. + */ + ret = lan78xx_write_vlan_table(dev); + if (ret < 0) + goto link_up_fail; + + ret = lan78xx_write_mchash_table(dev); + if (ret < 0) + goto link_up_fail; + netif_start_queue(net); return; @@ -3065,14 +3084,20 @@ static int lan78xx_set_features(struct net_device *netdev, return lan78xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl); } +static int lan78xx_write_vlan_table(struct lan78xx_net *dev) +{ + struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]); + + return lan78xx_dataport_write(dev, DP_SEL_RSEL_VLAN_DA_, 0, + DP_SEL_VHF_VLAN_LEN, pdata->vlan_table); +} + static void lan78xx_deferred_vlan_write(struct work_struct *param) { struct lan78xx_priv *pdata = container_of(param, struct lan78xx_priv, set_vlan); - struct lan78xx_net *dev = pdata->dev; - lan78xx_dataport_write(dev, DP_SEL_RSEL_VLAN_DA_, 0, - DP_SEL_VHF_VLAN_LEN, pdata->vlan_table); + lan78xx_write_vlan_table(pdata->dev); } static int lan78xx_vlan_rx_add_vid(struct net_device *netdev, From 19eec11f3ab5dd29ba58f5f209c24e946c95ef12 Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Fri, 19 Jun 2026 19:09:28 +0200 Subject: [PATCH 1931/2758] net: lwtunnel: Drop skb metadata before LWT encapsulation [ Upstream commit c00320b0e355c4bf0ae4743a53b4180fea237546 ] skb metadata is meant for passing information between XDP and TC. It lives in the skb headroom, immediately before skb->data. LWT programs cannot access the __sk_buff->data_meta pseudo-pointer to metadata. However, LWT encapsulation prepends outer headers, moving skb->data back over the headroom where the metadata sits. On an RX-originated (forwarded) packet that still carries XDP metadata this goes wrong in two different ways, depending on the encap type: 1. Non-BPF LWT encaps (mpls, seg6, ioam6 ...) call skb_push()/skb_pull() and silently overwrite the metadata that sits in the headroom. 2) BPF LWT xmit calls bpf_skb_change_head(), which uses skb_data_move(). That helper expects metadata immediately before skb->data. But since the IP output path runs LWT xmit before neighbour output has built the outgoing L2 header, for forwarded packets skb->data points at the L3 header while skb_mac_header() still points at the old L2 header. skb_data_move() sees metadata ending at skb_mac_header(), not before skb->data, warns and clears metadata: WARNING: CPU: 21 PID: 454557 at include/linux/skbuff.h:4609 skb_data_move+0x47/0x90 CPU: 21 UID: 0 PID: 454557 Comm: napi/iconduit-g Tainted: G O 6.18.21 #1 RIP: 0010:skb_data_move+0x47/0x90 Call Trace: bpf_skb_change_head+0xe6/0x1a0 bpf_prog_...+0x213/0x2e3 run_lwt_bpf.isra.0+0x1d3/0x360 bpf_xmit+0x46/0xe0 lwtunnel_xmit+0xa1/0xf0 ip_finish_output2+0x1e7/0x5e0 ip_output+0x63/0x100 __netif_receive_skb_one_core+0x85/0xa0 process_backlog+0x9c/0x150 __napi_poll+0x2b/0x190 net_rx_action+0x40b/0x7f0 handle_softirqs+0xd2/0x270 do_softirq+0x3f/0x60 That is what happens, as for how to fix it - a received packet that carries metadata can reach an encap through any of the three LWT redirect modes: LWTUNNEL_STATE_INPUT_REDIRECT ip6_rcv_finish dst_input lwtunnel_input LWTUNNEL_STATE_OUTPUT_REDIRECT ip6_rcv_finish dst_input ip6_forward ip6_forward_finish dst_output lwtunnel_output LWTUNNEL_STATE_XMIT_REDIRECT ip6_rcv_finish dst_input ip6_forward ip6_forward_finish dst_output ip6_output ip6_finish_output ip6_finish_output2 lwtunnel_xmit Every encap funnels through the three LWT dispatch helpers, so drop the metadata there, right before handing the skb to the encap op. This single chokepoint covers all encap types and all three redirect modes: - lwtunnel_input(): seg6, rpl, ila, seg6_local - lwtunnel_output(): ioam6 - lwtunnel_xmit(): mpls, LWT BPF xmit Alternatively, we could clear the metadata right after TC ingress hook. That would require a compromise, however. Metadata would become inaccessible from TC egress (in setups where it actually reaches the hook it tact, that is without any L2 tunnels on path). Fixes: 8989d328dfe7 ("net: Helper to move packet data and metadata after skb_push/pull") Signed-off-by: Jakub Sitnicki Link: https://patch.msgid.link/20260619-bpf-lwt-drop-skb-metadata-v3-1-71d6a33ab76b@cloudflare.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/core/lwtunnel.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/core/lwtunnel.c b/net/core/lwtunnel.c index f9d76d85d04fd..b01a395d9a966 100644 --- a/net/core/lwtunnel.c +++ b/net/core/lwtunnel.c @@ -350,6 +350,8 @@ int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb) rcu_read_lock(); ops = rcu_dereference(lwtun_encaps[lwtstate->type]); if (likely(ops && ops->output)) { + /* Encap pushes outer headers over the metadata; drop it. */ + skb_metadata_clear(skb); dev_xmit_recursion_inc(); ret = ops->output(net, sk, skb); dev_xmit_recursion_dec(); @@ -404,6 +406,8 @@ int lwtunnel_xmit(struct sk_buff *skb) rcu_read_lock(); ops = rcu_dereference(lwtun_encaps[lwtstate->type]); if (likely(ops && ops->xmit)) { + /* Encap pushes outer headers over the metadata; drop it. */ + skb_metadata_clear(skb); dev_xmit_recursion_inc(); ret = ops->xmit(skb); dev_xmit_recursion_dec(); @@ -455,6 +459,8 @@ int lwtunnel_input(struct sk_buff *skb) rcu_read_lock(); ops = rcu_dereference(lwtun_encaps[lwtstate->type]); if (likely(ops && ops->input)) { + /* Encap pushes outer headers over the metadata; drop it. */ + skb_metadata_clear(skb); dev_xmit_recursion_inc(); ret = ops->input(skb); dev_xmit_recursion_dec(); From e28aedab9488343924d227b5a896faed67ce84d5 Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Sat, 20 Jun 2026 09:07:49 -0400 Subject: [PATCH 1932/2758] net/sched: cls_api: Handle TC_ACT_CONSUMED in tcf_qevent_handle [ Upstream commit a8a02897f2b479127db261de05cbf0c28b98d159 ] tcf_classify() can return TC_ACT_CONSUMED while the skb is held by the defragmentation engine (e.g. act_ct on out-of-order fragments). When that happens the skb is no longer owned by the caller and must not be touched again. tcf_qevent_handle() did not handle TC_ACT_CONSUMED: it fell through the switch and returned the skb to the caller as if classification had passed. The only qdisc that wires up qevents today is RED, via three call sites (qe_mark on RED_PROB_MARK/HARD_MARK, qe_early_drop on congestion_drop) red_enqueue() was continuing to operate on an skb it no longer owns in this case -- enqueueing it, dropping it, or updating statistics. Resulting in a UAF. tc qdisc add dev eth0 root handle 1: red ... qevent early_drop block 10 tc filter add block 10 ... action ct (with ct defrag enabled and traffic that produces out-of-order fragments, e.g. a fragmented UDP stream) Handle TC_ACT_CONSUMED in tcf_qevent_handle() the same way the ingress and egress fast paths do: treat it as stolen and return NULL without touching the skb. Unlike the TC_ACT_STOLEN case, the skb must not be dropped/freed here, as it is no longer owned by us. Fixes: 3f14b377d01d ("net/sched: act_ct: fix skb leak and crash on ooo frags") Reported-by: Zero Day Initiative Tested-by: Victor Nogueira Signed-off-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260620130749.226642-1-jhs@mojatatu.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/cls_api.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 20f7f9ee0b353..3e67600a4a1a1 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -4049,6 +4049,9 @@ struct sk_buff *tcf_qevent_handle(struct tcf_qevent *qe, struct Qdisc *sch, stru skb_do_redirect(skb); *ret = __NET_XMIT_STOLEN; return NULL; + case TC_ACT_CONSUMED: + *ret = __NET_XMIT_STOLEN; + return NULL; } return skb; From 94f55994e19e8f0676990b9d5015b58ab6a97e00 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sat, 20 Jun 2026 11:48:54 -0400 Subject: [PATCH 1933/2758] sctp: fix err_chunk memory leaks in INIT handling [ Upstream commit 9f58a0a4d6c2ed5d341bba64f058f15d1b0c36f2 ] When sctp_verify_init() encounters unrecognized parameters, it allocates an err_chunk to report them. However, this chunk is leaked in several code paths: 1. In sctp_sf_do_5_1B_init(), if security_sctp_assoc_request() fails after sctp_verify_init() has populated err_chunk, the function returns immediately without freeing it. 2. In sctp_sf_do_unexpected_init(), the same leak occurs on the security_sctp_assoc_request() failure path. 3. In sctp_sf_do_unexpected_init(), on the success path after copying unrecognized parameters to the INIT-ACK, the function returns without freeing err_chunk, unlike sctp_sf_do_5_1B_init() which properly frees it. Fix all three leaks by adding sctp_chunk_free(err_chunk) calls before returning in the error paths and on the success path in sctp_sf_do_unexpected_init(). Fixes: c081d53f97a1 ("security: pass asoc to sctp_assoc_request and sctp_sk_clone") Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: Sashiko Signed-off-by: Xin Long Reviewed-by: Simon Horman Link: https://patch.msgid.link/0656704f1b0158287c98aec09ba36c83e4a537ab.1781970534.git.lucien.xin@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sctp/sm_statefuns.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 9b23c11cbb9ea..8e920cef0858c 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -415,6 +415,8 @@ enum sctp_disposition sctp_sf_do_5_1B_init(struct net *net, /* Update socket peer label if first association. */ if (security_sctp_assoc_request(new_asoc, chunk->skb)) { sctp_association_free(new_asoc); + if (err_chunk) + sctp_chunk_free(err_chunk); return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); } @@ -1606,6 +1608,8 @@ static enum sctp_disposition sctp_sf_do_unexpected_init( /* Update socket peer label if first association. */ if (security_sctp_assoc_request(new_asoc, chunk->skb)) { sctp_association_free(new_asoc); + if (err_chunk) + sctp_chunk_free(err_chunk); return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); } @@ -1671,6 +1675,7 @@ static enum sctp_disposition sctp_sf_do_unexpected_init( * parameter type. */ sctp_addto_chunk(repl, len, unk_param); + sctp_chunk_free(err_chunk); } sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, SCTP_ASOC(new_asoc)); From e28bada56f4ffbdeab6ca6be9724a1e2cf793b20 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Mon, 22 Jun 2026 15:08:52 +0200 Subject: [PATCH 1934/2758] ipv6: fix error handling in disable_ipv6 sysctl [ Upstream commit c779441e5070e2268bdfe77f6e2e0de926c431e3 ] When writing to the disable_ipv6 sysctl, if proc_dointvec() fails to parse the input, it returns a negative error code. The current implementation is overwriting that error for write operations. This results in a silent failure, it returns a successful write although the configuration was not modified at all. When modifying the "all" variant it can also modify the configuration of existing interfaces to the wrong value. Fix this by checking the return value of proc_dointvec() and returning early on failure. Fixes: 56d417b12e57 ("IPv6: Add 'autoconf' and 'disable_ipv6' module parameters") Reviewed-by: Nicolas Dichtel Signed-off-by: Fernando Fernandez Mancera Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260622130857.5115-2-fmancera@suse.de Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/addrconf.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 7953f5653451b..0cd225c9d7a84 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -6459,6 +6459,8 @@ static int addrconf_sysctl_disable(const struct ctl_table *ctl, int write, lctl.data = &val; ret = proc_dointvec(&lctl, write, buffer, lenp, ppos); + if (ret) + return ret; if (write) ret = addrconf_disable_ipv6(ctl, valp, val); From a39ff02a241cf5c65465cc11ae158f3cece7e186 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Mon, 22 Jun 2026 15:08:53 +0200 Subject: [PATCH 1935/2758] ipv6: fix error handling in ignore_routes_with_linkdown sysctl [ Upstream commit cf4f2b14401f29ccac56393ca9e4b42a2505f540 ] When writing to the ignore_routes_with_linkdown sysctl, if proc_dointvec() fails to parse the input, it returns a negative error code. The current implementation is overwriting that error for write operations. This results in a silent failure, it returns a successful write although the configuration was not modified at all. When modifying the "all" variant it can also modify the configuration of existing interfaces to the wrong value. Fix this by checking the return value of proc_dointvec() and returning early on failure. Fixes: 35103d11173b ("net: ipv6 sysctl option to ignore routes when nexthop link is down") Reviewed-by: Nicolas Dichtel Signed-off-by: Fernando Fernandez Mancera Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260622130857.5115-3-fmancera@suse.de Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/addrconf.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 0cd225c9d7a84..da6531f5d0ef1 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -6663,6 +6663,8 @@ int addrconf_sysctl_ignore_routes_with_linkdown(const struct ctl_table *ctl, lctl.data = &val; ret = proc_dointvec(&lctl, write, buffer, lenp, ppos); + if (ret) + return ret; if (write) ret = addrconf_fixup_linkdown(ctl, valp, val); From 420e895fb41de2188cb288c564b4589793579eca Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Mon, 22 Jun 2026 15:08:54 +0200 Subject: [PATCH 1936/2758] ipv6: fix error handling in forwarding sysctl [ Upstream commit 058b9b19f9639fe1e1225a17c540f61b65bee6ad ] When writing to the forwarding sysctl, if proc_dointvec() fails to parse the input, it returns a negative error code. The current implementation is overwriting that error for write operations. This results in a silent failure, it returns a successful write although the configuration was not modified at all. When modifying the "all" variant it can also modify the configuration of existing interfaces to the wrong value. Fix this by checking the return value of proc_dointvec() and returning early on failure. In addition, adjust return code of addrconf_fixup_forwarding() for successful operation. Fixes: b325fddb7f86 ("ipv6: Fix sysctl unregistration deadlock") Reviewed-by: Nicolas Dichtel Signed-off-by: Fernando Fernandez Mancera Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260622130857.5115-4-fmancera@suse.de Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/addrconf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index da6531f5d0ef1..c15c9e63a2ccf 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -913,7 +913,7 @@ static int addrconf_fixup_forwarding(const struct ctl_table *table, int *p, int if (newf) rt6_purge_dflt_routers(net); - return 1; + return 0; } static void addrconf_linkdown_change(struct net *net, __s32 newf) @@ -6362,6 +6362,8 @@ static int addrconf_sysctl_forward(const struct ctl_table *ctl, int write, lctl.data = &val; ret = proc_dointvec(&lctl, write, buffer, lenp, ppos); + if (ret) + return ret; if (write) ret = addrconf_fixup_forwarding(ctl, valp, val); From 764ac02cbd3b4464df7fc4237d7e9753f0bf6c08 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Mon, 22 Jun 2026 15:08:55 +0200 Subject: [PATCH 1937/2758] ipv6: fix error handling in disable_policy sysctl [ Upstream commit 3e0e51c0ee1d08cc9d48dc17f3248d5b31cf8066 ] When writing to the disable_policy sysctl, if proc_dointvec() fails to parse the input, it returns a negative error code. The current implementation is resetting the position argument even if an error occurred during proc_dointvec() and not only during sysctl restart. Fix this by checking the return value of proc_dointvec() and returning early on failure. Fixes: df789fe75206 ("ipv6: Provide ipv6 version of "disable_policy" sysctl") Reviewed-by: Nicolas Dichtel Signed-off-by: Fernando Fernandez Mancera Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260622130857.5115-5-fmancera@suse.de Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/addrconf.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index c15c9e63a2ccf..9d8268f36c7ed 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -6761,6 +6761,8 @@ static int addrconf_sysctl_disable_policy(const struct ctl_table *ctl, int write lctl = *ctl; lctl.data = &val; ret = proc_dointvec(&lctl, write, buffer, lenp, ppos); + if (ret) + return ret; if (write && (*valp != val)) ret = addrconf_disable_policy(ctl, valp, val); From b91ac71fc2a2a3ab749d48b0c51a6b94d4d48b19 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Mon, 22 Jun 2026 15:08:56 +0200 Subject: [PATCH 1938/2758] ipv6: fix state corruption during proxy_ndp sysctl restart [ Upstream commit 6a1b50e585f033f3e201f42a18b37f070095fb80 ] When handling proxy_ndp, if rtnl_net_trylock() fails, the operation is retried but as the value was already modified by the initial proc_dointvec() call, the restarted syscall will read the newly modified value as the 'old' state. Fix this by taking the RTNL lock before parsing the input value if the operation is a write. Fixes: c92d5491a6d9 ("netconf: add support for IPv6 proxy_ndp") Reviewed-by: Nicolas Dichtel Signed-off-by: Fernando Fernandez Mancera Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260622130857.5115-6-fmancera@suse.de Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/addrconf.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 9d8268f36c7ed..400cde904737c 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -6474,20 +6474,19 @@ static int addrconf_sysctl_disable(const struct ctl_table *ctl, int write, static int addrconf_sysctl_proxy_ndp(const struct ctl_table *ctl, int write, void *buffer, size_t *lenp, loff_t *ppos) { + struct net *net = ctl->extra2; int *valp = ctl->data; - int ret; int old, new; + int ret; + + if (write && !rtnl_net_trylock(net)) + return restart_syscall(); old = *valp; ret = proc_dointvec(ctl, write, buffer, lenp, ppos); new = *valp; if (write && old != new) { - struct net *net = ctl->extra2; - - if (!rtnl_net_trylock(net)) - return restart_syscall(); - if (valp == &net->ipv6.devconf_dflt->proxy_ndp) { inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_PROXY_NEIGH, @@ -6506,8 +6505,9 @@ static int addrconf_sysctl_proxy_ndp(const struct ctl_table *ctl, int write, idev->dev->ifindex, &idev->cnf); } - rtnl_net_unlock(net); } + if (write) + rtnl_net_unlock(net); return ret; } From 7a368c754a96690d9a8fb456aebecacbb24320fb Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Mon, 22 Jun 2026 15:08:57 +0200 Subject: [PATCH 1939/2758] ipv6: fix missing notification for ignore_routes_with_linkdown [ Upstream commit 17dc3b245de45b1f2012e3a48ec51889f544e67b ] When changing the ignore_routes_with_linkdown sysctl for a specific interface, the RTM_NEWNETCONF netlink notification was not being emitted to userspace. Fix this by emitting the notification when needed. In addition, fix bogus return value for successful "all" and specific interface write operation leading to a wrong reset of the position pointer. Fixes: 35103d11173b ("net: ipv6 sysctl option to ignore routes when nexthop link is down") Reviewed-by: Nicolas Dichtel Signed-off-by: Fernando Fernandez Mancera Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260622130857.5115-7-fmancera@suse.de Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/addrconf.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 400cde904737c..defa4277e783f 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -955,11 +955,7 @@ static int addrconf_fixup_linkdown(const struct ctl_table *table, int *p, int ne NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, NETCONFA_IFINDEX_DEFAULT, net->ipv6.devconf_dflt); - rtnl_net_unlock(net); - return 0; - } - - if (p == &net->ipv6.devconf_all->ignore_routes_with_linkdown) { + } else if (p == &net->ipv6.devconf_all->ignore_routes_with_linkdown) { WRITE_ONCE(net->ipv6.devconf_dflt->ignore_routes_with_linkdown, newf); addrconf_linkdown_change(net, newf); if ((!newf) ^ (!old)) @@ -968,11 +964,21 @@ static int addrconf_fixup_linkdown(const struct ctl_table *table, int *p, int ne NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, NETCONFA_IFINDEX_ALL, net->ipv6.devconf_all); + } else { + if (!newf ^ !old) { + struct inet6_dev *idev = table->extra1; + + inet6_netconf_notify_devconf(net, + RTM_NEWNETCONF, + NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, + idev->dev->ifindex, + &idev->cnf); + } } rtnl_net_unlock(net); - return 1; + return 0; } #endif From ca2dbee8fea61341fd04b4fc79ac128a2961ccd6 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 22 Jun 2026 08:47:53 -0700 Subject: [PATCH 1940/2758] eth: fbnic: fix ordering of heartbeat vs ownership [ Upstream commit d87363b0edfc7504ff2b144fe4cdd8154f90f42e ] When requesting ownership of the NIC (MAC/PHY control), we set up the heartbeat to look stale: /* Initialize heartbeat, set last response to 1 second in the past * so that we will trigger a timeout if the firmware doesn't respond */ fbd->last_heartbeat_response = req_time - HZ; fbd->last_heartbeat_request = req_time; The response handler then sets: fbd->last_heartbeat_response = jiffies; for which we wait via: fbnic_fw_init_heartbeat() -> fbnic_fw_heartbeat_current() The scheme is a bit odd, but it should work in principle. Fix the ordering of operations. We have to set up the stale heartbeat before we send the message. Otherwise if the response is very fast we will override it. This triggers on QEMU if we run on the core that handles the IRQ, and results in ndo_open failing with ETIMEDOUT. The change in ordering doesn't impact releasing the ownership. Both ndo_stop and heartbeat check are under rtnl_lock. Fixes: 20d2e88cc746 ("eth: fbnic: Add initial messaging to notify FW of our presence") Reviewed-by: Alexander Duyck Reviewed-by: Pavan Chebbi Link: https://patch.msgid.link/20260622154753.827506-1-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/meta/fbnic/fbnic_fw.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_fw.c b/drivers/net/ethernet/meta/fbnic/fbnic_fw.c index 0c6812fcf1851..283d25fae79e7 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_fw.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_fw.c @@ -526,15 +526,10 @@ int fbnic_fw_xmit_ownership_msg(struct fbnic_dev *fbd, bool take_ownership) goto free_message; } - err = fbnic_mbx_map_tlv_msg(fbd, msg); - if (err) - goto free_message; - /* Initialize heartbeat, set last response to 1 second in the past * so that we will trigger a timeout if the firmware doesn't respond */ fbd->last_heartbeat_response = req_time - HZ; - fbd->last_heartbeat_request = req_time; /* Set prev_firmware_time to 0 to avoid triggering firmware crash @@ -542,6 +537,10 @@ int fbnic_fw_xmit_ownership_msg(struct fbnic_dev *fbd, bool take_ownership) */ fbd->prev_firmware_time = 0; + err = fbnic_mbx_map_tlv_msg(fbd, msg); + if (err) + goto free_message; + /* Set heartbeat detection based on if we are taking ownership */ fbd->fw_heartbeat_enabled = take_ownership; From fda07c8e4b54b9105f1ca73f0adea7b244d405f4 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Tue, 23 Jun 2026 17:33:22 +0200 Subject: [PATCH 1941/2758] s390/mm: Fix handling of _PAGE_UNUSED pte bit [ Upstream commit d4bb00704a66024502261fa7a523c07420249fea ] The _PAGE_UNUSED softbit should not really be lying around. Its sole purpose is to signal to try_to_unmap_one() and try_to_migrate_one() that the page can be discarded instead of being moved / swapped. KVM has no way to know why a page is being unmapped, so it sets the bit on userspace ptes corresponding to unused guest pages every time they get unmapped. KVM has no reasonable way to clear the bit once the page is in use again. While set_ptes() checks and clears the bit, other paths that set new ptes did not. This led to used pages being thrown out as if they were unused, causing guest corruption. Fix the issue by clearing the _PAGE_UNUSED bit for present ptes in set_pte(), i.e. whenever a present pte is getting set. The check in set_ptes() is then redundant and can be removed. Also fix gmap_helper_try_set_pte_unused() to only set the bit if the pte is present; the _PAGE_UNUSED bit is only defined for present ptes and thus should not be set for non-present ptes. Fixes: c98175b7917f ("KVM: s390: Add gmap_helper_set_unused()") Acked-by: Heiko Carstens Signed-off-by: Claudio Imbrenda Message-ID: <20260623153331.233784-2-imbrenda@linux.ibm.com> Signed-off-by: Sasha Levin --- arch/s390/include/asm/pgtable.h | 4 ++-- arch/s390/mm/gmap_helpers.c | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index 2c6cee8241e04..4740c75649ebb 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h @@ -980,6 +980,8 @@ static inline void set_pmd(pmd_t *pmdp, pmd_t pmd) static inline void set_pte(pte_t *ptep, pte_t pte) { + if (pte_present(pte)) + pte = clear_pte_bit(pte, __pgprot(_PAGE_UNUSED)); WRITE_ONCE(*ptep, pte); } @@ -1332,8 +1334,6 @@ pgprot_t pgprot_writecombine(pgprot_t prot); static inline void set_ptes(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t entry, unsigned int nr) { - if (pte_present(entry)) - entry = clear_pte_bit(entry, __pgprot(_PAGE_UNUSED)); page_table_check_ptes_set(mm, addr, ptep, entry, nr); for (;;) { set_pte(ptep, entry); diff --git a/arch/s390/mm/gmap_helpers.c b/arch/s390/mm/gmap_helpers.c index 1cfe4724fbe2a..60023b6fdcb13 100644 --- a/arch/s390/mm/gmap_helpers.c +++ b/arch/s390/mm/gmap_helpers.c @@ -181,7 +181,8 @@ void gmap_helper_try_set_pte_unused(struct mm_struct *mm, unsigned long vmaddr) if (IS_ERR_OR_NULL(ptep)) return; - __atomic64_or(_PAGE_UNUSED, (long *)ptep); + if (pte_present(*ptep)) + __atomic64_or(_PAGE_UNUSED, (long *)ptep); pte_unmap_unlock(ptep, ptl); } EXPORT_SYMBOL_GPL(gmap_helper_try_set_pte_unused); From 156af6606f366ab7fe748573605ecec819a6722f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 19 Jun 2026 17:01:27 +0200 Subject: [PATCH 1942/2758] thermal: testing: zone: Flush work items during cleanup [ Upstream commit fb1a5dfe86d3af1e1c3ce168cf0d8d43897e0f77 ] To prevent freed module code from being executed during the thermal testing module unload, make it add a dedicated workqueue for thermal testing work items and flush it in thermal_testing_exit(). Fixes: f6a034f2df42 ("thermal: Introduce a debugfs-based testing facility") Link: https://sashiko.dev/#/patchset/20260605185212.2491144-1-sam.moelius%40trailofbits.com Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/1959388.tdWV9SEqCh@rafael.j.wysocki [ rjw: Make variable d_command static ] Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/thermal/testing/command.c | 36 ++++++++++++++++++++--- drivers/thermal/testing/thermal_testing.h | 8 +++++ drivers/thermal/testing/zone.c | 7 ++--- 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/drivers/thermal/testing/command.c b/drivers/thermal/testing/command.c index fbf7ab9729b5a..90e06c5933278 100644 --- a/drivers/thermal/testing/command.c +++ b/drivers/thermal/testing/command.c @@ -86,7 +86,10 @@ #include "thermal_testing.h" -struct dentry *d_testing; +struct workqueue_struct *tt_wq __ro_after_init; + +struct dentry *d_testing __ro_after_init; +static struct dentry *d_command __ro_after_init; #define TT_COMMAND_SIZE 16 @@ -203,17 +206,42 @@ static const struct file_operations tt_command_fops = { static int __init thermal_testing_init(void) { + int error; + + tt_wq = alloc_workqueue("thermal_testing", WQ_UNBOUND, 0); + if (!tt_wq) + return -ENOMEM; + d_testing = debugfs_create_dir("thermal-testing", NULL); - if (!IS_ERR(d_testing)) - debugfs_create_file("command", 0200, d_testing, NULL, - &tt_command_fops); + if (IS_ERR(d_testing)) { + error = PTR_ERR(d_testing); + goto destroy_wq; + } + + d_command = debugfs_create_file("command", 0200, d_testing, NULL, &tt_command_fops); + if (IS_ERR(d_command)) { + error = PTR_ERR(d_command); + goto remove_d_testing; + } return 0; + +remove_d_testing: + debugfs_remove(d_testing); +destroy_wq: + destroy_workqueue(tt_wq); + return error; } module_init(thermal_testing_init); static void __exit thermal_testing_exit(void) { + /* First, prevent new commands from being entered. */ + debugfs_remove(d_command); + /* Flush commands in progress (if any). */ + flush_workqueue(tt_wq); + destroy_workqueue(tt_wq); + /* Remove the directory structure and clean up. */ debugfs_remove(d_testing); tt_zone_cleanup(); } diff --git a/drivers/thermal/testing/thermal_testing.h b/drivers/thermal/testing/thermal_testing.h index c790a32aae4ed..5880c9a63dba9 100644 --- a/drivers/thermal/testing/thermal_testing.h +++ b/drivers/thermal/testing/thermal_testing.h @@ -1,4 +1,12 @@ /* SPDX-License-Identifier: GPL-2.0 */ +#include + +extern struct workqueue_struct *tt_wq; + +static inline void tt_queue_work(struct work_struct *work) +{ + queue_work(tt_wq, work); +} extern struct dentry *d_testing; diff --git a/drivers/thermal/testing/zone.c b/drivers/thermal/testing/zone.c index 3c339242f52d4..51f1c0806d214 100644 --- a/drivers/thermal/testing/zone.c +++ b/drivers/thermal/testing/zone.c @@ -13,7 +13,6 @@ #include #include #include -#include #include "thermal_testing.h" @@ -207,7 +206,7 @@ int tt_add_tz(void) INIT_WORK(&tt_work->work, tt_add_tz_work_fn); tt_work->tt_zone = no_free_ptr(tt_zone); - schedule_work(&(no_free_ptr(tt_work)->work)); + tt_queue_work(&(no_free_ptr(tt_work)->work)); return 0; } @@ -269,7 +268,7 @@ int tt_del_tz(const char *arg) INIT_WORK(&tt_work->work, tt_del_tz_work_fn); tt_work->tt_zone = tt_zone; - schedule_work(&(no_free_ptr(tt_work)->work)); + tt_queue_work(&(no_free_ptr(tt_work)->work)); return 0; } @@ -358,7 +357,7 @@ int tt_zone_add_trip(const char *arg) INIT_WORK(&tt_work->work, tt_zone_add_trip_work_fn); tt_work->tt_zone = no_free_ptr(tt_zone); tt_work->tt_trip = no_free_ptr(tt_trip); - schedule_work(&(no_free_ptr(tt_work)->work)); + tt_queue_work(&(no_free_ptr(tt_work)->work)); return 0; } From 42d4fc933280ccdb48ddbaa768a51db3fbed5ce8 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 22 Jun 2026 20:23:09 +0200 Subject: [PATCH 1943/2758] ACPICA: Unbreak tools build after switching over to strscpy_pad() [ Upstream commit 292db66afd20dd0b7a3c9a3dad9b864a64c8bddf ] Commit 97f7d3f9c9ac ("ACPICA: Replace strncpy() with strscpy_pad() in acpi_ut_safe_strncpy()") switched over the ACPICA code in the kernel to using strscpy_pad() instead of a combination of strncpy() and manual NUL-termination of the destination string, but it overlooked the fact that tools also use the code in question and strscpy_pad() is not defined in those builds. Address that by using the original ACPICA code in non-kernel builds. Fixes: 97f7d3f9c9ac ("ACPICA: Replace strncpy() with strscpy_pad() in acpi_ut_safe_strncpy()") Reported-by: Jiri Slaby Closes: https://lore.kernel.org/all/79e9e913-0fb1-4110-804b-c3b5d0edafe4@kernel Signed-off-by: Rafael J. Wysocki Reviewed-by: Kees Cook [ rjw: Fixed up the number of added code lines ] Link: https://patch.msgid.link/12923581.O9o76ZdvQC@rafael.j.wysocki Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/acpi/acpica/utnonansi.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/acpi/acpica/utnonansi.c b/drivers/acpi/acpica/utnonansi.c index 3a7952be65457..93867ad7f3421 100644 --- a/drivers/acpi/acpica/utnonansi.c +++ b/drivers/acpi/acpica/utnonansi.c @@ -168,7 +168,16 @@ void acpi_ut_safe_strncpy(char *dest, char *source, acpi_size dest_size) { /* Always terminate destination string */ +#ifdef __KERNEL__ strscpy_pad(dest, source, dest_size); +#else + /* + * strscpy_pad() is not defined in ACPICA tools builds, so use strncpy() + * and directly NUL-terminate the destination string in that case. + */ + strncpy(dest, source, dest_size); + dest[dest_size - 1] = 0; +#endif } #endif From 8bbe4dd7964552f74c271c3a8d95a4331a8ab5f9 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Tue, 16 Jun 2026 15:26:17 +0800 Subject: [PATCH 1944/2758] ACPI: processor_idle: Mark LPI enter functions as __cpuidle [ Upstream commit 956ca5d72c76504824c8eb601879da9476973e15 ] When function tracing or Kprobes is enabled, entering an ACPI Low Power Idle (LPI) state triggers the following RCU splat: RCU not on for: acpi_idle_lpi_enter+0x4/0xd8 WARNING: CPU: 8 PID: 0 at include/linux/trace_recursion.h:162 function_trace_call+0x1e8/0x228 The acpi_idle_lpi_enter() function is invoked within the cpuidle path after RCU has already been disabled for the current local CPU. Consequently, ftrace's function_trace_call() expects RCU to be actively watching before recording trace data, emitting a warning if it is not. Fix this by annotating acpi_idle_lpi_enter(), the generic __weak stub, and the RISC-V implementation of acpi_processor_ffh_lpi_enter() with __cpuidle. This moves these functions into the '.cpuidle.text' section, implicitly disabling ftrace instrumentation (notrace) along this sensitive path and preventing trace-induced RCU warnings during idle entry. Fixes: a36a7fecfe60 ("ACPI / processor_idle: Add support for Low Power Idle(LPI) states") Signed-off-by: Li RongQing Acked-by: lihuisong@huawei.com Link: https://patch.msgid.link/20260616072617.2272-1-lirongqing@baidu.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/acpi/processor_idle.c | 4 ++-- drivers/acpi/riscv/cpuidle.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 390ab5f1d3137..4482cf28f56ae 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -1143,7 +1143,7 @@ static int acpi_processor_get_lpi_info(struct acpi_processor *pr) return 0; } -int __weak acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi) +int __weak __cpuidle acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi) { return -ENODEV; } @@ -1156,7 +1156,7 @@ int __weak acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi) * * Return: 0 for success or negative value for error */ -static int acpi_idle_lpi_enter(struct cpuidle_device *dev, +static int __cpuidle acpi_idle_lpi_enter(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { struct acpi_processor *pr; diff --git a/drivers/acpi/riscv/cpuidle.c b/drivers/acpi/riscv/cpuidle.c index 624f9bbdb58c4..c76dbabff702e 100644 --- a/drivers/acpi/riscv/cpuidle.c +++ b/drivers/acpi/riscv/cpuidle.c @@ -66,7 +66,7 @@ int acpi_processor_ffh_lpi_probe(unsigned int cpu) return acpi_cpu_init_idle(cpu); } -int acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi) +int __cpuidle acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi) { u32 state = lpi->address; From cddbfbc71085005c75d975d8729f56ff0d55e986 Mon Sep 17 00:00:00 2001 From: Huiwen He Date: Wed, 24 Jun 2026 10:15:43 +0800 Subject: [PATCH 1945/2758] smb/client: preserve errors from smb2_set_sparse() [ Upstream commit 2a4b3d2db5c6fcdba889baf7b2ae5661b0beac89 ] smb2_set_sparse() converts every FSCTL_SET_SPARSE failure to false and marks sparse support as broken for the share. Callers therefore report EOPNOTSUPP even for errors such as ENOSPC or EACCES, and later sparse operations remain disabled until the share is unmounted. Return the SMB2 ioctl error directly. Set broken_sparse_sup only for EOPNOTSUPP, which indicates that the server does not support the request. Update smb3_punch_hole() to propagate the error returned by smb2_set_sparse(). Fixes: 3d1a3745d8ca ("Add sparse file support to SMB2/SMB3 mounts") Signed-off-by: Huiwen He Reviewed-by: ChenXiaoSong Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/client/smb2ops.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index ce34b3fbfca3d..10664380f351a 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -2117,8 +2117,9 @@ smb2_sync_write(const unsigned int xid, struct cifs_fid *pfid, } /* Set or clear the SPARSE_FILE attribute based on value passed in setsparse */ -static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *cfile, struct inode *inode, __u8 setsparse) +static int smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, struct inode *inode, + __u8 setsparse) { struct cifsInodeInfo *cifsi; int rc; @@ -2127,31 +2128,31 @@ static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon, /* if file already sparse don't bother setting sparse again */ if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && setsparse) - return true; /* already sparse */ + return 0; /* already sparse */ if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && !setsparse) - return true; /* already not sparse */ + return 0; /* already not sparse */ /* * Can't check for sparse support on share the usual way via the * FS attribute info (FILE_SUPPORTS_SPARSE_FILES) on the share * since Samba server doesn't set the flag on the share, yet * supports the set sparse FSCTL and returns sparse correctly - * in the file attributes. If we fail setting sparse though we - * mark that server does not support sparse files for this share - * to avoid repeatedly sending the unsupported fsctl to server - * if the file is repeatedly extended. + * in the file attributes. If the server returns EOPNOTSUPP, mark + * that sparse files are not supported on this share to avoid + * repeatedly sending the unsupported FSCTL. */ if (tcon->broken_sparse_sup) - return false; + return -EOPNOTSUPP; rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, cfile->fid.volatile_fid, FSCTL_SET_SPARSE, &setsparse, 1, CIFSMaxBufSize, NULL, NULL); if (rc) { - tcon->broken_sparse_sup = true; + if (rc == -EOPNOTSUPP) + tcon->broken_sparse_sup = true; cifs_dbg(FYI, "set sparse rc = %d\n", rc); - return false; + return rc; } if (setsparse) @@ -2159,7 +2160,7 @@ static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon, else cifsi->cifsAttrs &= (~FILE_ATTRIBUTE_SPARSE_FILE); - return true; + return 0; } static int @@ -3483,10 +3484,9 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon, /* Need to make file sparse, if not already, before freeing range. */ /* Consider adding equivalent for compressed since it could also work */ - if (!smb2_set_sparse(xid, tcon, cfile, inode, set_sparse)) { - rc = -EOPNOTSUPP; + rc = smb2_set_sparse(xid, tcon, cfile, inode, set_sparse); + if (rc) goto out; - } filemap_invalidate_lock(inode->i_mapping); /* From 5904fd94f919083151afffb830e9407372a60342 Mon Sep 17 00:00:00 2001 From: Fredrik M Olsson Date: Wed, 20 May 2026 16:48:54 +0200 Subject: [PATCH 1946/2758] rtc: ds1307: Fix off-by-one issue with wday for rx8130 [ Upstream commit 6882aab3c66112b33b251be95c09c8ead3e8d580 ] The RTC represent each weekday with a individual bit set in the WDAY register, where the 0th bit represent the first day of the week and the 6th bit represents the last day of the week. For each passed day the chip performs a rotary-left-shift by one to advance the weekday by one. The tm_wday field represent weekdays by a value in the range of 0-6. The fls() function return the bit index of the last bit set. To handle when there are no bits set it will return 0, and if the 0th bit is set it will return 1, and if the 1st bit is set it will return 2, and so on. In order to make the result of the fls() function fall into the expected range of 0-6 (instead of 1-7) this patch subtracts one from the result (which matches how the value is written in ds1307_set_time()). Fixes: 204756f016726 ("rtc: ds1307: Fix wday settings for rx8130") Reviewed-by: Nobuhiro Iwamatsu Signed-off-by: Fredrik M Olsson Link: https://patch.msgid.link/20260520-ds1307-rx8901-add-v2-2-e069ea32e1db@axis.com Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/rtc/rtc-ds1307.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index edf81b975decb..9ba5958ca7a6e 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -311,7 +311,7 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t) t->tm_hour = bcd2bin(tmp); /* rx8130 is bit position, not BCD */ if (ds1307->type == rx_8130) - t->tm_wday = fls(regs[DS1307_REG_WDAY] & 0x7f); + t->tm_wday = fls(regs[DS1307_REG_WDAY] & 0x7f) - 1; else t->tm_wday = bcd2bin(regs[DS1307_REG_WDAY] & 0x07) - 1; t->tm_mday = bcd2bin(regs[DS1307_REG_MDAY] & 0x3f); From a427cfa417962a1c6cbdde335091a51f28a7cf5c Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Tue, 23 Jun 2026 18:08:48 +0800 Subject: [PATCH 1947/2758] rtc: cmos: unregister HPET IRQ handler on probe failure [ Upstream commit a5bb580df018b5d1c5668f05f7979044fb19e23a ] cmos_do_probe() registers cmos_interrupt() as the HPET RTC IRQ handler before requesting the RTC IRQ and registering the RTC device. If either request_irq() or devm_rtc_register_device() fails afterwards, the error path leaves the HPET RTC IRQ handler installed. This leaves a stale handler behind and make a later hpet_register_irq_handler() fail with -EBUSY. Track whether the HPET handler was registered successfully and undo the registration on the probe error path. Also mask the HPET RTC IRQ bits to match the normal shutdown cleanup. Fixes: 9d8af78b0797 ("rtc: add HPET RTC emulation to RTC_DRV_CMOS") Signed-off-by: Haoxiang Li Link: https://patch.msgid.link/20260623100848.2127281-1-haoxiang_li2024@163.com Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/rtc/rtc-cmos.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index f89ab58f5048a..fa04ece151b8b 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -934,6 +934,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) unsigned char rtc_control; unsigned address_space; u32 flags = 0; + bool hpet_registered = false; struct nvmem_config nvmem_cfg = { .name = "cmos_nvram", .word_size = 1, @@ -1091,6 +1092,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) " failed in rtc_init()."); goto cleanup1; } + hpet_registered = true; } else rtc_cmos_int_handler = cmos_interrupt; @@ -1140,6 +1142,10 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) if (is_valid_irq(rtc_irq)) free_irq(rtc_irq, cmos_rtc.rtc); cleanup1: + if (hpet_registered) { + hpet_mask_rtc_irq_bit(RTC_IRQMASK); + hpet_unregister_irq_handler(cmos_interrupt); + } cmos_rtc.dev = NULL; cleanup0: if (RTC_IOMAPPED) From c21f7ee511ae8851dd053269220db985b1ded0c8 Mon Sep 17 00:00:00 2001 From: David Yang Date: Thu, 18 Jun 2026 22:01:55 +0800 Subject: [PATCH 1948/2758] net: dsa: realtek: fix memory leak in rtl8366rb_setup_led() [ Upstream commit 056a5087d87ead77dedbe9cf5bde53b7cd4b4651 ] led_classdev_register_ext() only reads init_data.devicename - it never stores the pointer. However, the caller allocated devicename with kasprintf() but never freed it, leaking the string memory. Fix it with a stack buffer to avoid dynamic buffers completely. Fixes: 32d617005475 ("net: dsa: realtek: add LED drivers for rtl8366rb") Signed-off-by: David Yang Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260618140200.1888707-1-mmyangfl@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/dsa/realtek/rtl8366rb-leds.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/dsa/realtek/rtl8366rb-leds.c b/drivers/net/dsa/realtek/rtl8366rb-leds.c index 509ffd3f8db5c..ba50d311cb151 100644 --- a/drivers/net/dsa/realtek/rtl8366rb-leds.c +++ b/drivers/net/dsa/realtek/rtl8366rb-leds.c @@ -89,6 +89,7 @@ static int rtl8366rb_setup_led(struct realtek_priv *priv, struct dsa_port *dp, struct led_init_data init_data = { }; enum led_default_state state; struct rtl8366rb_led *led; + char name[64]; u32 led_group; int ret; @@ -129,10 +130,9 @@ static int rtl8366rb_setup_led(struct realtek_priv *priv, struct dsa_port *dp, init_data.fwnode = led_fwnode; init_data.devname_mandatory = true; - init_data.devicename = kasprintf(GFP_KERNEL, "Realtek-%d:0%d:%d", - dp->ds->index, dp->index, led_group); - if (!init_data.devicename) - return -ENOMEM; + snprintf(name, sizeof(name), "Realtek-%d:0%d:%d", + dp->ds->index, dp->index, led_group); + init_data.devicename = name; ret = devm_led_classdev_register_ext(priv->dev, &led->cdev, &init_data); if (ret) { From 245c6c8a2958e9ece688ade7df9c8d8f2809984e Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Fri, 19 Jun 2026 04:39:25 +0100 Subject: [PATCH 1949/2758] net: dsa: mxl862xx: avoid unaligned 16-bit access in api_wrap [ Upstream commit 6b3f7af57881f6d6250c6dcc4d910fe8e855a607 ] The MXL862XX_API_* macros pass the address of a stack-allocated, __packed firmware-ABI struct to mxl862xx_api_wrap() as a void *. The struct has an alignment of 1, so the compiler is free to place it at an odd address. mxl862xx_api_wrap() reinterprets that buffer as a __le16 * and accesses it with data[i], for which the compiler assumes the natural 2-byte alignment of __le16 and emits aligned 16-bit loads/stores (e.g. lhu/sh on MIPS). When the buffer lands on an odd address these fault on architectures that do not support unaligned access, such as MIPS32. -Waddress-of-packed-member does not catch this: the packed origin is laundered through the void * parameter, so the cast inside api_wrap looks alignment-safe to the compiler and no warning is emitted. Use get_unaligned_le16()/put_unaligned_le16() for the three 16-bit word accesses. The byte accesses (*(u8 *)&data[i], crc16()) are already safe and are left unchanged. Link: https://sashiko.dev/#/patchset/cover.1781319534.git.daniel%40makrotopia.org?part=4 Fixes: 23794bec1cb6 ("net: dsa: add basic initial driver for MxL862xx switches") Signed-off-by: Daniel Golle Link: https://patch.msgid.link/599327521db465a534d277de53ab9b6cac01928b.1781702256.git.daniel@makrotopia.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/dsa/mxl862xx/mxl862xx-host.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-host.c b/drivers/net/dsa/mxl862xx/mxl862xx-host.c index d55f9dff6433e..882c5d9609418 100644 --- a/drivers/net/dsa/mxl862xx/mxl862xx-host.c +++ b/drivers/net/dsa/mxl862xx/mxl862xx-host.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include "mxl862xx.h" #include "mxl862xx-host.h" @@ -349,7 +350,7 @@ int mxl862xx_api_wrap(struct mxl862xx_priv *priv, u16 cmd, void *_data, * zero words individually. */ for (i = 0, zeros = 0; i < size / 2 && zeros < RST_DATA_THRESHOLD; i++) - if (!data[i]) + if (!get_unaligned_le16(&data[i])) zeros++; if (zeros < RST_DATA_THRESHOLD && (size & 1) && !*(u8 *)&data[i]) @@ -395,7 +396,7 @@ int mxl862xx_api_wrap(struct mxl862xx_priv *priv, u16 cmd, void *_data, */ val = *(u8 *)&data[i] | ((crc & 0xff) << 8); } else { - val = le16_to_cpu(data[i]); + val = get_unaligned_le16(&data[i]); } /* After RST_DATA, skip zero data words as the registers @@ -453,7 +454,7 @@ int mxl862xx_api_wrap(struct mxl862xx_priv *priv, u16 cmd, void *_data, *(uint8_t *)&data[i] = ret & 0xff; crc = (ret >> 8) & 0xff; } else { - data[i] = cpu_to_le16((u16)ret); + put_unaligned_le16((u16)ret, &data[i]); } } From cf52622fbc274eb4ca9a2066b258da2ae3dc7406 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Fri, 19 Jun 2026 04:40:14 +0100 Subject: [PATCH 1950/2758] net: dsa: mxl862xx: fix use-after-free of DSA ports in crc_err_work [ Upstream commit bcb3b8314611ed9cb4ff4bff484ef9b154fd1b83 ] Upon an MDIO CRC error mxl862xx_crc_err_work_fn() walks the DSA ports and closes the CPU port conduits: dsa_switch_for_each_cpu_port(dp, priv->ds) dev_close(dp->conduit); mxl862xx_remove() unregisters the switch before cancelling this work: set_bit(MXL862XX_FLAG_WORK_STOPPED, &priv->flags); cancel_delayed_work_sync(&priv->stats_work); dsa_unregister_switch(ds); mxl862xx_host_shutdown(priv); dsa_unregister_switch() frees the dsa_port objects. If a CRC error schedules the work during teardown it can run after the ports have been freed and dereference freed memory. Guard the port walk with MXL862XX_FLAG_WORK_STOPPED, which is already set before dsa_unregister_switch(). DSA tears the ports down under rtnl_lock(), so checking the flag under rtnl_lock() means the work either runs before teardown and sees valid ports, or runs afterwards, observes the flag and skips the walk. This mirrors the host_flood_work handler, which skips torn-down ports under rtnl_lock(). Link: https://sashiko.dev/#/patchset/cover.1780968180.git.daniel%40makrotopia.org?part=2 Fixes: a319d0c8c8ce ("net: dsa: mxl862xx: add CRC for MDIO communication") Signed-off-by: Daniel Golle Link: https://patch.msgid.link/5e55169926c02f2b914e5ada529d7453b943cda4.1781702256.git.daniel@makrotopia.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/dsa/mxl862xx/mxl862xx-host.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-host.c b/drivers/net/dsa/mxl862xx/mxl862xx-host.c index 882c5d9609418..4acd216f7cc00 100644 --- a/drivers/net/dsa/mxl862xx/mxl862xx-host.c +++ b/drivers/net/dsa/mxl862xx/mxl862xx-host.c @@ -41,12 +41,13 @@ static void mxl862xx_crc_err_work_fn(struct work_struct *work) crc_err_work); struct dsa_port *dp; - dev_warn(&priv->mdiodev->dev, - "MDIO CRC error detected, shutting down all ports\n"); - rtnl_lock(); - dsa_switch_for_each_cpu_port(dp, priv->ds) - dev_close(dp->conduit); + if (!test_bit(MXL862XX_FLAG_WORK_STOPPED, &priv->flags)) { + dev_warn(&priv->mdiodev->dev, + "MDIO CRC error detected, shutting down all ports\n"); + dsa_switch_for_each_cpu_port(dp, priv->ds) + dev_close(dp->conduit); + } rtnl_unlock(); clear_bit(MXL862XX_FLAG_CRC_ERR, &priv->flags); From 7d47925c2c64b3eddc81067552caf486c743ab7e Mon Sep 17 00:00:00 2001 From: Erni Sri Satya Vennela Date: Thu, 18 Jun 2026 22:53:38 -0700 Subject: [PATCH 1951/2758] net: mana: Fall back to standard MTU when PF reports adapter_mtu of 0 [ Upstream commit 6bd81a5b4e0dbec2feb94a456bca9d9f00bc14b3 ] Commit d7709812e13d ("net: mana: hardening: Validate adapter_mtu from MANA_QUERY_DEV_CONFIG") rejected any adapter_mtu value smaller than ETH_MIN_MTU + ETH_HLEN, including 0, returning -EPROTO and failing mana_probe(). Some older PF firmware versions still in the field report adapter_mtu as 0 in the MANA_QUERY_DEV_CONFIG response. With the hardening check in place, the MANA VF driver now fails to load on those hosts, breaking networking entirely for guests. MANA hardware always supports the standard Ethernet MTU. Treat a reported adapter_mtu of 0 as "the PF did not advertise a value" and fall back to ETH_FRAME_LEN, the same value used for the pre-V2 message version path. Only jumbo frames remain unavailable until the PF reports a valid MTU. Other small-but-nonzero bogus values are still rejected, preserving the original protection against the unsigned-subtraction wrap that would otherwise let ndev->max_mtu underflow to a huge value. Fixes: d7709812e13d ("net: mana: hardening: Validate adapter_mtu from MANA_QUERY_DEV_CONFIG") Signed-off-by: Erni Sri Satya Vennela Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260619055348.467224-1-ernis@linux.microsoft.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/microsoft/mana/mana_bpf.c | 3 ++- drivers/net/ethernet/microsoft/mana/mana_en.c | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/microsoft/mana/mana_bpf.c b/drivers/net/ethernet/microsoft/mana/mana_bpf.c index 7697c9b52ed34..d59dec89a8077 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_bpf.c +++ b/drivers/net/ethernet/microsoft/mana/mana_bpf.c @@ -237,7 +237,8 @@ static int mana_xdp_set(struct net_device *ndev, struct bpf_prog *prog, bpf_prog_put(old_prog); if (prog) - ndev->max_mtu = MANA_XDP_MTU_MAX; + ndev->max_mtu = min_t(unsigned int, MANA_XDP_MTU_MAX, + gc->adapter_mtu - ETH_HLEN); else ndev->max_mtu = gc->adapter_mtu - ETH_HLEN; diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index d7de4c4d25bbe..8c66290e9ed6d 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -1215,12 +1215,24 @@ static int mana_query_device_cfg(struct mana_context *ac, u32 proto_major_ver, *max_num_vports = resp.max_num_vports; if (resp.hdr.response.msg_version >= GDMA_MESSAGE_V2) { - if (resp.adapter_mtu < ETH_MIN_MTU + ETH_HLEN) { + if (resp.adapter_mtu == 0) { + /* + * Some older PF firmware versions report an + * adapter_mtu of 0. MANA hardware always supports the + * standard Ethernet MTU, so fall back to ETH_FRAME_LEN. + * Jumbo frames will not be available in this case. + */ + dev_info(dev, + "PF reported adapter_mtu of 0, falling back to %u (jumbo frames disabled)\n", + ETH_FRAME_LEN); + gc->adapter_mtu = ETH_FRAME_LEN; + } else if (resp.adapter_mtu < ETH_MIN_MTU + ETH_HLEN) { dev_err(dev, "Adapter MTU too small: %u\n", resp.adapter_mtu); return -EPROTO; + } else { + gc->adapter_mtu = resp.adapter_mtu; } - gc->adapter_mtu = resp.adapter_mtu; } else { gc->adapter_mtu = ETH_FRAME_LEN; } From ba933c5f3568518f36695bb1970fceb20d2cd7d4 Mon Sep 17 00:00:00 2001 From: Jan Klos Date: Sat, 20 Jun 2026 03:19:53 +0200 Subject: [PATCH 1952/2758] net: phy: realtek: Clear MDIO_AN_10GBT_CTRL_ADV10G bit [ Upstream commit 510a283f4d12367a3f811f382a2c89202954bbd1 ] On RTL8127A connected to a link partner that advertises 10000baseT speed cannot be changed to anything other than 10000baseT as 10GbE is always advertised regardless of any setting. Fix this by clearing MDIO_AN_10GBT_CTRL_ADV10G bit in rtl822x_config_aneg()'s call to phy_modify_mmd_changed(). Fixes: 83d962316128 ("net: phy: realtek: add RTL8127-internal PHY") Reviewed-by: Maxime Chevallier Signed-off-by: Jan Klos Link: https://patch.msgid.link/20260620011956.37181-1-honza.klos@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/phy/realtek/realtek_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c index 79c867ef64dad..01bbddeb4a306 100644 --- a/drivers/net/phy/realtek/realtek_main.c +++ b/drivers/net/phy/realtek/realtek_main.c @@ -1632,7 +1632,8 @@ static int rtl822x_config_aneg(struct phy_device *phydev) ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2, RTL_MDIO_AN_10GBT_CTRL, MDIO_AN_10GBT_CTRL_ADV2_5G | - MDIO_AN_10GBT_CTRL_ADV5G, adv); + MDIO_AN_10GBT_CTRL_ADV5G | + MDIO_AN_10GBT_CTRL_ADV10G, adv); if (ret < 0) return ret; } From b1f6381acf9d55fab208e8b4c252a5a671820bd9 Mon Sep 17 00:00:00 2001 From: Subbaraya Sundeep Date: Mon, 22 Jun 2026 04:30:53 +0530 Subject: [PATCH 1953/2758] octeontx2-af: Validate NIX maximum LFs correctly [ Upstream commit 1576d12a39860418d6a68b402fda71a48f04a57c ] NIX maximum number of LFs can be set via devlink command but that can be done before assigning any LFs to a PF/VF. The condition used to check whether any LFs are assigned is incorrect. This patch fixes that condition. Fixes: dd7842878633 ("octeontx2-af: Add new devlink param to configure maximum usable NIX block LFs") Signed-off-by: Subbaraya Sundeep Link: https://patch.msgid.link/1782082853-6941-1-git-send-email-sbhatta@marvell.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- .../marvell/octeontx2/af/rvu_devlink.c | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c index 6494a9ee2f0dd..3b47ecb44d51b 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c @@ -1510,7 +1510,9 @@ static int rvu_af_dl_nix_maxlf_validate(struct devlink *devlink, u32 id, struct rvu_devlink *rvu_dl = devlink_priv(devlink); struct rvu *rvu = rvu_dl->rvu; u16 max_nix0_lf, max_nix1_lf; - struct npc_mcam *mcam; + struct rvu_block *block; + int blkaddr = 0; + int free_lfs; u64 cfg; cfg = rvu_read64(rvu, BLKADDR_NIX0, NIX_AF_CONST2); @@ -1518,14 +1520,23 @@ static int rvu_af_dl_nix_maxlf_validate(struct devlink *devlink, u32 id, cfg = rvu_read64(rvu, BLKADDR_NIX1, NIX_AF_CONST2); max_nix1_lf = cfg & 0xFFF; - /* Do not allow user to modify maximum NIX LFs while mcam entries - * have already been assigned. + /* Do not allow user to modify maximum NIX LFs while NIX LFs + * have already been assigned. Note that modifying NIX LFs count + * can be done only before any LF attach requests from PFs and VFs + * and not later or concurrently. */ - mcam = &rvu->hw->mcam; - if (mcam->bmap_fcnt < mcam->bmap_entries) { - NL_SET_ERR_MSG_MOD(extack, - "mcam entries have already been assigned, can't resize"); - return -EPERM; + blkaddr = rvu_get_next_nix_blkaddr(rvu, blkaddr); + while (blkaddr) { + block = &rvu->hw->block[blkaddr]; + + free_lfs = rvu_rsrc_free_count(&block->lf); + if (free_lfs != block->lf.max) { + NL_SET_ERR_MSG_MOD(extack, + "NIX LFs already assigned, can't resize"); + return -EPERM; + } + + blkaddr = rvu_get_next_nix_blkaddr(rvu, blkaddr); } if (max_nix0_lf && val.vu16 > max_nix0_lf) { From 0fd234bc1264da86aebd57f02955d5ac8e26ee73 Mon Sep 17 00:00:00 2001 From: Ratheesh Kannoth Date: Mon, 22 Jun 2026 09:12:29 +0530 Subject: [PATCH 1954/2758] octeontx2-af: fix CGX debugfs RVU AF PCI reference leaks [ Upstream commit 469f4462ec83119e8bc0e4699ad01d80a9a4c61a ] CGX per-lmac debugfs seq readers obtained struct rvu via pci_get_drvdata(pci_get_device(..., PCI_DEVID_OCTEONTX2_RVU_AF, ...)), which leaks a PCI device reference on every read. Store rvu and the CGX handle in debugfs inode private data when creating stats, mac_filter, and fwdata files (one context per CGX), and use debugfs aux numbers for fwdata so lmac_id matches the other CGX debugfs entries. Fixes: f967488d095e ("octeontx2-af: Add per CGX port level NIX Rx/Tx counters") Fixes: dbc52debf95f ("octeontx2-af: Debugfs support for DMAC filters") Fixes: 49f02e6877d1 ("Octeontx2-af: Debugfs support for firmware data") Cc: Linu Cherian Reported-by: Yuho Choi Reviewed-by: Simon Horman Signed-off-by: Ratheesh Kannoth Link: https://patch.msgid.link/20260622034229.2254145-1-rkannoth@marvell.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- .../marvell/octeontx2/af/rvu_debugfs.c | 56 ++++++++++--------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c index ca2704b188a5c..3456313d3b3c0 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c @@ -2810,6 +2810,14 @@ static void rvu_dbg_npa_init(struct rvu *rvu) &rvu_dbg_npa_ndc_hits_miss_fops); } +/* Per-lmac CGX debugfs files need both RVU and CGX handle; inode->i_private + * points here so seq_file ops avoid pci_get_device(PCI_DEVID_OCTEONTX2_RVU_AF). + */ +struct rvu_cgx_lmac_dbgfs_ctx { + struct rvu *rvu; + void *cgxd; +}; + #define PRINT_CGX_CUML_NIXRX_STATUS(idx, name) \ ({ \ u64 cnt; \ @@ -2832,18 +2840,14 @@ static void rvu_dbg_npa_init(struct rvu *rvu) static int cgx_print_stats(struct seq_file *s, int lmac_id) { + struct rvu_cgx_lmac_dbgfs_ctx *dctx = s->private; struct cgx_link_user_info linfo; + struct rvu *rvu = dctx->rvu; struct mac_ops *mac_ops; - void *cgxd = s->private; + void *cgxd = dctx->cgxd; u64 ucast, mcast, bcast; int stat = 0, err = 0; u64 tx_stat, rx_stat; - struct rvu *rvu; - - rvu = pci_get_drvdata(pci_get_device(PCI_VENDOR_ID_CAVIUM, - PCI_DEVID_OCTEONTX2_RVU_AF, NULL)); - if (!rvu) - return -ENODEV; mac_ops = get_mac_ops(cgxd); /* There can be no CGX devices at all */ @@ -2950,20 +2954,16 @@ RVU_DEBUG_SEQ_FOPS(cgx_stat, cgx_stat_display, NULL); static int cgx_print_dmac_flt(struct seq_file *s, int lmac_id) { + struct rvu_cgx_lmac_dbgfs_ctx *dctx = s->private; + struct rvu *rvu = dctx->rvu; struct pci_dev *pdev = NULL; - void *cgxd = s->private; + void *cgxd = dctx->cgxd; char *bcast, *mcast; u16 index, domain; u8 dmac[ETH_ALEN]; - struct rvu *rvu; u64 cfg, mac; int pf; - rvu = pci_get_drvdata(pci_get_device(PCI_VENDOR_ID_CAVIUM, - PCI_DEVID_OCTEONTX2_RVU_AF, NULL)); - if (!rvu) - return -ENODEV; - pf = cgxlmac_to_pf(rvu, cgx_get_cgxid(cgxd), lmac_id); domain = 2; @@ -3010,17 +3010,13 @@ RVU_DEBUG_SEQ_FOPS(cgx_dmac_flt, cgx_dmac_flt_display, NULL); static int cgx_print_fwdata(struct seq_file *s, int lmac_id) { + struct rvu_cgx_lmac_dbgfs_ctx *dctx = s->private; struct cgx_lmac_fwdata_s *fwdata; - void *cgxd = s->private; + struct rvu *rvu = dctx->rvu; + void *cgxd = dctx->cgxd; struct phy_s *phy; - struct rvu *rvu; int cgx_id, i; - rvu = pci_get_drvdata(pci_get_device(PCI_VENDOR_ID_CAVIUM, - PCI_DEVID_OCTEONTX2_RVU_AF, NULL)); - if (!rvu) - return -ENODEV; - if (!rvu->fwdata) return -EAGAIN; @@ -3101,6 +3097,7 @@ RVU_DEBUG_SEQ_FOPS(cgx_fwdata, cgx_fwdata_display, NULL); static void rvu_dbg_cgx_init(struct rvu *rvu) { + struct rvu_cgx_lmac_dbgfs_ctx *ctx; struct mac_ops *mac_ops; unsigned long lmac_bmap; int i, lmac_id; @@ -3127,6 +3124,13 @@ static void rvu_dbg_cgx_init(struct rvu *rvu) rvu->rvu_dbg.cgx = debugfs_create_dir(dname, rvu->rvu_dbg.cgx_root); + ctx = devm_kzalloc(rvu->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + continue; + + ctx->rvu = rvu; + ctx->cgxd = cgx; + for_each_set_bit(lmac_id, &lmac_bmap, rvu->hw->lmac_per_cgx) { /* lmac debugfs dir */ sprintf(dname, "lmac%d", lmac_id); @@ -3134,13 +3138,13 @@ static void rvu_dbg_cgx_init(struct rvu *rvu) debugfs_create_dir(dname, rvu->rvu_dbg.cgx); debugfs_create_file_aux_num("stats", 0600, rvu->rvu_dbg.lmac, - cgx, lmac_id, &rvu_dbg_cgx_stat_fops); + ctx, lmac_id, &rvu_dbg_cgx_stat_fops); debugfs_create_file_aux_num("mac_filter", 0600, - rvu->rvu_dbg.lmac, cgx, lmac_id, + rvu->rvu_dbg.lmac, ctx, lmac_id, &rvu_dbg_cgx_dmac_flt_fops); - debugfs_create_file("fwdata", 0600, - rvu->rvu_dbg.lmac, cgx, - &rvu_dbg_cgx_fwdata_fops); + debugfs_create_file_aux_num("fwdata", 0600, + rvu->rvu_dbg.lmac, ctx, + lmac_id, &rvu_dbg_cgx_fwdata_fops); } } } From 5bdb33ff6e58bdc43632e98b30723eb65352d671 Mon Sep 17 00:00:00 2001 From: Yun Zhou Date: Mon, 22 Jun 2026 15:43:50 +0800 Subject: [PATCH 1955/2758] net: mvneta: re-enable percpu interrupt on resume [ Upstream commit fd398d6480987e4c84fff0aaab6b9d6642a93343 ] On Marvell MPIC platforms (Armada 370/XP/38x), mvneta uses a percpu IRQ disable/enable scheme for NAPI: the ISR (mvneta_percpu_isr) calls disable_percpu_irq() to mask the MPIC per-CPU interrupt and schedules NAPI poll, which calls enable_percpu_irq() on completion to unmask. If suspend occurs while NAPI poll is pending (between disable_percpu_irq in the ISR and enable_percpu_irq in poll completion), the interrupt is never re-enabled: 1. mvneta_percpu_isr: disable_percpu_irq() + napi_schedule() => MPIC masked, percpu_enabled cpumask bit cleared 2. NAPI poll does not complete before suspend proceeds (on PREEMPT_RT this is highly likely since softirqs run in ksoftirqd which gets frozen; on non-RT it can happen when softirq processing is deferred to ksoftirqd) 3. mvneta_stop_dev => napi_disable(): cancels the pending poll without executing the completion path 4. suspend_device_irqs => IRQCHIP_MASK_ON_SUSPEND: masks MPIC (already masked, but records IRQS_SUSPENDED) 5. Resume: mpic_resume checks irq_percpu_is_enabled() => false (bit was cleared in step 1) => skips unmask 6. mvneta_start_dev only restores device-level INTR_NEW_MASK, does not touch the MPIC per-CPU mask Result: MPIC per-CPU interrupt stays masked permanently. The NIC generates interrupts (INTR_NEW_CAUSE != 0) but the CPU never receives them, causing complete loss of network connectivity. Fix by calling on_each_cpu(mvneta_percpu_enable) in the resume path to unconditionally unmask the MPIC per-CPU interrupt regardless of pre-suspend state. Fixes: 12bb03b436da ("net: mvneta: Handle per-cpu interrupts") Signed-off-by: Yun Zhou Reviewed-by: Sebastian Andrzej Siewior Link: https://patch.msgid.link/20260622074350.1666290-1-yun.zhou@windriver.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/marvell/mvneta.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 0c061fb0ed072..744d6585a949d 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -5900,6 +5900,9 @@ static int mvneta_resume(struct device *device) rtnl_unlock(); mvneta_set_rx_mode(dev); + if (!pp->neta_armada3700) + on_each_cpu(mvneta_percpu_enable, pp, true); + return 0; } #endif From 49c2e7c0a69999a75ef5eaebe1559a20d0b3c15a Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Wed, 17 Jun 2026 20:26:21 -0700 Subject: [PATCH 1956/2758] geneve: gate GRO hint in geneve_gro_complete() on gs->gro_hint [ Upstream commit 2651c174445884ac9e85622aeade9c1f7b98d8e5 ] geneve_gro_receive() reads the GRO hint through geneve_sk_gro_hint_off(), which honours it only when the socket enabled IFLA_GENEVE_GRO_HINT (gs->gro_hint). geneve_gro_complete() instead calls the low-level geneve_opt_gro_hint_off() and acts on the hint unconditionally. On a tunnel without the hint, receive aggregates the frames as plain ETH_P_TEB while complete still honours an attacker-supplied hint option: it inflates gh_len by gro_hint->nested_hdr_len (u8) and redirects the dispatch type, so the inner gro_complete handler runs at nhoff + gh_len, an offset receive never pulled nor validated, reading out of bounds of the skb head: BUG: KASAN: slab-out-of-bounds in ipv6_gro_complete (net/ipv6/ip6_offload.c:196) Read of size 1 at addr ffff88800fe91980 by task exploit/153 ipv6_gro_complete (net/ipv6/ip6_offload.c:196) geneve_gro_complete (drivers/net/geneve.c:965) udp_gro_complete (net/ipv4/udp_offload.c:940) inet_gro_complete (net/ipv4/af_inet.c:1621) __gro_flush (net/core/gro.c:306) Gate the complete path on gs->gro_hint too via geneve_sk_gro_hint_off(), so both paths agree. Tunnels that enable the hint are unaffected. Fixes: fd0dd796576e ("geneve: use GRO hint option in the RX path") Reported-by: Weiming Shi Reported-by: Kyle Zeng Signed-off-by: Xiang Mei Link: https://patch.msgid.link/20260618032622.484720-1-xmei5@asu.edu Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/geneve.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index b2e74e815e577..a48f6c52f1269 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -948,13 +948,13 @@ static int geneve_gro_complete(struct sock *sk, struct sk_buff *skb, struct genevehdr *gh; struct packet_offload *ptype; __be16 type; - int gh_len; + unsigned int gh_len; int err = -ENOSYS; gh = (struct genevehdr *)(skb->data + nhoff); gh_len = geneve_hlen(gh); type = gh->proto_type; - geneve_opt_gro_hint_off(gh, &type, &gh_len); + geneve_sk_gro_hint_off(sk, gh, &type, &gh_len); /* since skb->encapsulation is set, eth_gro_complete() sets the inner mac header */ if (likely(type == htons(ETH_P_TEB))) From e2087447f562692ff0cd08a0554d8d4ad083aa5c Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Wed, 17 Jun 2026 20:26:22 -0700 Subject: [PATCH 1957/2758] geneve: validate inner network offset in geneve_gro_complete() [ Upstream commit cbb0d30a1ad6fc9439b1dc9b4f5a7a9140d3b11f ] Even with both paths gated on gs->gro_hint, geneve_gro_complete() re-derives the inner dispatch type and length from the packet and the current gs->gro_hint, independently of geneve_gro_receive(). The two can disagree if gs->gro_hint flips under a concurrent geneve_quiesce()/ geneve_unquiesce() (sk_user_data is NULL across a synchronize_net()), or if the re-read option bytes differ from the ones receive parsed. geneve_gro_receive() already records the inner network header position in NAPI_GRO_CB()->inner_network_offset. Have geneve_gro_complete() compute the offset it is about to dispatch at, adding ETH_HLEN in the ETH_P_TEB case where eth_gro_complete() steps over the inner MAC header, and bail out if it lands past inner_network_offset. Use a lower bound rather than exact equality: between gh_len and the inner L3 header, geneve_gro_receive() may also have pulled an inner VLAN tag (vlan_gro_receive() advances the recorded offset past it), which only moves inner_network_offset further out. A valid frame therefore always satisfies inner_nh <= inner_network_offset, while a gh_len inflated by a hint gro_receive() did not honour dispatches past the validated inner header, i.e. the out-of-bounds completion. Only the latter is rejected. Fixes: fd0dd796576e ("geneve: use GRO hint option in the RX path") Suggested-by: Paolo Abeni Co-developed-by: Weiming Shi Signed-off-by: Xiang Mei Link: https://patch.msgid.link/20260618032622.484720-2-xmei5@asu.edu Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/geneve.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index a48f6c52f1269..c760c0655c8a1 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -956,6 +956,20 @@ static int geneve_gro_complete(struct sock *sk, struct sk_buff *skb, type = gh->proto_type; geneve_sk_gro_hint_off(sk, gh, &type, &gh_len); + /* Bail out if we are about to dispatch past the inner network header + * gro_receive() validated. An inner VLAN tag only pushes + * inner_network_offset out, so use a lower bound. + */ + if (skb->encapsulation) { + unsigned int inner_nh = nhoff + gh_len; + + if (type == htons(ETH_P_TEB)) + inner_nh += ETH_HLEN; + + if (unlikely(inner_nh > NAPI_GRO_CB(skb)->inner_network_offset)) + return -EINVAL; + } + /* since skb->encapsulation is set, eth_gro_complete() sets the inner mac header */ if (likely(type == htons(ETH_P_TEB))) return eth_gro_complete(skb, nhoff + gh_len); From fb42560afec5058b459853f554d019a9e144ddc9 Mon Sep 17 00:00:00 2001 From: Greg Thelen Date: Mon, 22 Jun 2026 09:16:59 -0700 Subject: [PATCH 1958/2758] tools: ynl: build archives with $(AR) [ Upstream commit 8c37e76f960b7a201556e954b987b431820995ee ] Use $(AR) to allow build system to override the archiver tool (e.g., when cross-compiling for a different architecture) by setting the AR environment variable. GNU Make defaults AR to ar, so this change will not break existing build environments that do not explicitly set AR. Fixes: 07c3cc51a085 ("tools: net: package libynl for use in selftests") Fixes: 86878f14d71a ("tools: ynl: user space helpers") Signed-off-by: Greg Thelen Link: https://patch.msgid.link/20260622161659.145047-1-gthelen@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- tools/net/ynl/Makefile | 2 +- tools/net/ynl/generated/Makefile | 2 +- tools/net/ynl/lib/Makefile | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/net/ynl/Makefile b/tools/net/ynl/Makefile index d514a48dae270..3cefe4ed96cb9 100644 --- a/tools/net/ynl/Makefile +++ b/tools/net/ynl/Makefile @@ -22,7 +22,7 @@ tests: | lib generated libynl.a ynltool: | lib generated libynl.a libynl.a: | lib generated @echo -e "\tAR $@" - @ar rcs $@ lib/ynl.o generated/*-user.o + @$(AR) rcs $@ lib/ynl.o generated/*-user.o $(SUBDIRS): @if [ -f "$@/Makefile" ] ; then \ diff --git a/tools/net/ynl/generated/Makefile b/tools/net/ynl/generated/Makefile index 86e1e4a959a75..ea4128f612d6c 100644 --- a/tools/net/ynl/generated/Makefile +++ b/tools/net/ynl/generated/Makefile @@ -37,7 +37,7 @@ all: protos.a $(HDRS) $(SRCS) $(KHDRS) $(KSRCS) $(UAPI) $(RSTS) protos.a: $(OBJS) @echo -e "\tAR $@" - @ar rcs $@ $(OBJS) + @$(AR) rcs $@ $(OBJS) %-user.h: $(SPECS_DIR)/%.yaml $(TOOL) @echo -e "\tGEN $@" diff --git a/tools/net/ynl/lib/Makefile b/tools/net/ynl/lib/Makefile index 4b2b98704ff9b..9b98c05996000 100644 --- a/tools/net/ynl/lib/Makefile +++ b/tools/net/ynl/lib/Makefile @@ -15,7 +15,7 @@ all: ynl.a ynl.a: $(OBJS) @echo -e "\tAR $@" - @ar rcs $@ $(OBJS) + @$(AR) rcs $@ $(OBJS) clean: rm -f *.o *.d *~ From bc49e8746584564dba47d963d1916cc876fc6f6b Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Tue, 23 Jun 2026 10:57:59 +0800 Subject: [PATCH 1959/2758] net: sungem: fix probe error cleanup [ Upstream commit 36dea2f639249460d13f6ca66b2a9064187cd34d ] gem_init_one() calls gem_remove_one() when register_netdev() fails. gem_remove_one() unregisters and frees resources owned by the net_device, including the DMA block, MMIO mapping, PCI regions, and the net_device itself. gem_init_one() then falls through to its own cleanup labels and frees the same resources again. Keep the register_netdev() error path in gem_init_one(): clear drvdata so PM/remove paths do not see a half-registered device, remove the NAPI instance added during probe, and let the existing cleanup labels release the resources once. The issue was found by a local static-analysis checker for probe error paths. The reported path was manually inspected before sending this fix. Compile-tested with CONFIG_SUNGEM=y. Runtime testing was not performed because no sungem hardware is available. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Ruoyu Wang Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260623025759.3468566-1-ruoyuw560@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/sun/sungem.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/sun/sungem.c b/drivers/net/ethernet/sun/sungem.c index 8e69d917d827c..26974ee713525 100644 --- a/drivers/net/ethernet/sun/sungem.c +++ b/drivers/net/ethernet/sun/sungem.c @@ -2986,10 +2986,10 @@ static int gem_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev->max_mtu = GEM_MAX_MTU; /* Register with kernel */ - if (register_netdev(dev)) { + err = register_netdev(dev); + if (err) { pr_err("Cannot register net device, aborting\n"); - err = -ENOMEM; - goto err_out_free_consistent; + goto err_out_clear_drvdata; } /* Undo the get_cell with appropriate locking (we could use @@ -3003,8 +3003,13 @@ static int gem_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev->dev_addr); return 0; +err_out_clear_drvdata: + pci_set_drvdata(pdev, NULL); + netif_napi_del(&gp->napi); + err_out_free_consistent: - gem_remove_one(pdev); + dma_free_coherent(&pdev->dev, sizeof(struct gem_init_block), + gp->init_block, gp->gblock_dvma); err_out_iounmap: gem_put_cell(gp); iounmap(gp->regs); From c3e27e4ee524e0d683e90b992b4981fcdc9a747e Mon Sep 17 00:00:00 2001 From: Shitalkumar Gandhi Date: Tue, 23 Jun 2026 12:11:42 +0530 Subject: [PATCH 1960/2758] net: ethernet: sunplus: spl2sw: fix phy_node refcount leak in remove [ Upstream commit a9e29dcd8a84081177f693439d663ca9e216c9fa ] mac->phy_node is acquired via of_parse_phandle() in spl2sw_probe() and stored in the mac private data, transferring ownership of the device_node reference to mac. On driver removal, spl2sw_phy_remove() disconnects the PHY but never drops that reference, so each probe-then-remove cycle leaks one of_node refcount per port permanently. Drop the reference after phy_disconnect(). While at it, remove the redundant inner "if (ndev)" check; comm->ndev[i] was just verified non-NULL on the line above. Compile-tested only; no SP7021 hardware available. Fixes: fd3040b9394c ("net: ethernet: Add driver for Sunplus SP7021") Signed-off-by: Shitalkumar Gandhi Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/f3bdd4c91f3e2269b4e256075f9dc70808b1b8e9.1782195965.git.shitalkumar.gandhi@cambiumnetworks.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/sunplus/spl2sw_phy.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/sunplus/spl2sw_phy.c b/drivers/net/ethernet/sunplus/spl2sw_phy.c index 6f899e48f51df..a4889c52e00e4 100644 --- a/drivers/net/ethernet/sunplus/spl2sw_phy.c +++ b/drivers/net/ethernet/sunplus/spl2sw_phy.c @@ -79,12 +79,14 @@ int spl2sw_phy_connect(struct spl2sw_common *comm) void spl2sw_phy_remove(struct spl2sw_common *comm) { struct net_device *ndev; + struct spl2sw_mac *mac; int i; for (i = 0; i < MAX_NETDEV_NUM; i++) if (comm->ndev[i]) { ndev = comm->ndev[i]; - if (ndev) - phy_disconnect(ndev->phydev); + mac = netdev_priv(ndev); + phy_disconnect(ndev->phydev); + of_node_put(mac->phy_node); } } From a77abd7a34906a86523d7bea06dfe3eab638a379 Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Tue, 23 Jun 2026 15:46:34 +0800 Subject: [PATCH 1961/2758] net: stmmac: dwmac-spacemit: Fix wrong phy interface definition [ Upstream commit d1e3a4c3b24d4430d9987a3f349c6c2a959a389a ] The current MII interface register definition from the vendor is wrong, use the right number for the macro. Also, correct the interface mask in spacemit_set_phy_intf_sel() so it can update the register with the right number Fixes: 30f0ba420ed3 ("net: stmmac: Add glue layer for Spacemit K3 SoC") Signed-off-by: Inochi Amaoto Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260623074637.503864-2-inochiama@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/stmicro/stmmac/dwmac-spacemit.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-spacemit.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-spacemit.c index 223754cc5c79b..3bfb6d49be6c0 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-spacemit.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-spacemit.c @@ -18,8 +18,10 @@ #include "stmmac_platform.h" /* ctrl register bits */ -#define CTRL_PHY_INTF_RGMII BIT(3) -#define CTRL_PHY_INTF_MII BIT(4) +#define CTRL_PHY_INTF_MODE GENMASK(4, 3) +#define CTRL_PHY_INTF_RMII FIELD_PREP(CTRL_PHY_INTF_MODE, 0) +#define CTRL_PHY_INTF_RGMII FIELD_PREP(CTRL_PHY_INTF_MODE, 1) +#define CTRL_PHY_INTF_MII FIELD_PREP(CTRL_PHY_INTF_MODE, 3) #define CTRL_WAKE_IRQ_EN BIT(9) #define CTRL_PHY_IRQ_EN BIT(12) @@ -118,7 +120,7 @@ static void spacemit_get_interfaces(struct stmmac_priv *priv, void *bsp_priv, static int spacemit_set_phy_intf_sel(void *bsp_priv, u8 phy_intf_sel) { - unsigned int mask = CTRL_PHY_INTF_MII | CTRL_PHY_INTF_RGMII; + unsigned int mask = CTRL_PHY_INTF_MODE; struct spacmit_dwmac *dwmac = bsp_priv; unsigned int val = 0; @@ -128,6 +130,7 @@ static int spacemit_set_phy_intf_sel(void *bsp_priv, u8 phy_intf_sel) break; case PHY_INTF_SEL_RMII: + val = CTRL_PHY_INTF_RMII; break; case PHY_INTF_SEL_RGMII: From fe0669928f27bb45e31dc298edf17245e4c42bc6 Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Tue, 23 Jun 2026 15:46:35 +0800 Subject: [PATCH 1962/2758] net: stmmac: dwmac-spacemit: Fix wrong irq definition [ Upstream commit bf5cd5d4ca423d348e01b3bbcdccd0e02b1b408b ] The current irq definition of the wake irq and the lpi irq is wrong, replace them with the right number and name. Fixes: 30f0ba420ed3 ("net: stmmac: Add glue layer for Spacemit K3 SoC") Signed-off-by: Inochi Amaoto Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260623074637.503864-3-inochiama@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/stmicro/stmmac/dwmac-spacemit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-spacemit.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-spacemit.c index 3bfb6d49be6c0..322bdf167a4a6 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-spacemit.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-spacemit.c @@ -22,8 +22,8 @@ #define CTRL_PHY_INTF_RMII FIELD_PREP(CTRL_PHY_INTF_MODE, 0) #define CTRL_PHY_INTF_RGMII FIELD_PREP(CTRL_PHY_INTF_MODE, 1) #define CTRL_PHY_INTF_MII FIELD_PREP(CTRL_PHY_INTF_MODE, 3) -#define CTRL_WAKE_IRQ_EN BIT(9) -#define CTRL_PHY_IRQ_EN BIT(12) +#define CTRL_LPI_IRQ_EN BIT(9) +#define CTRL_WAKE_IRQ_EN BIT(12) /* dline register bits */ #define RGMII_RX_DLINE_EN BIT(0) From ee79d03aafb517fe2eab5782816b49ffd65a6d16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E8=87=B4=E9=82=A6=20=28XIE=20Zhibang=29?= Date: Thu, 25 Jun 2026 13:03:49 +0800 Subject: [PATCH 1963/2758] LoongArch: Move struct kimage forward declaration before use MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit d4e58d2c21d94282d512979dfa7e045c5034b0be ] arch_kimage_file_post_load_cleanup() and load_other_segments(), both inside the CONFIG_KEXEC_FILE block, take a struct kimage pointer before the forward declaration appears. Move the forward declaration above so it precedes its first use instead of relying on a transitive include. Fixes: d162feec6b6e ("LoongArch: Add preparatory infrastructure for kexec_file") Signed-off-by: 谢致邦 (XIE Zhibang) Signed-off-by: Huacai Chen Signed-off-by: Sasha Levin --- arch/loongarch/include/asm/kexec.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/loongarch/include/asm/kexec.h b/arch/loongarch/include/asm/kexec.h index 209fa43222e1c..6be136e9f0a06 100644 --- a/arch/loongarch/include/asm/kexec.h +++ b/arch/loongarch/include/asm/kexec.h @@ -41,6 +41,8 @@ struct kimage_arch { unsigned long systable_ptr; }; +struct kimage; + #ifdef CONFIG_KEXEC_FILE extern const struct kexec_file_ops kexec_efi_ops; extern const struct kexec_file_ops kexec_elf_ops; @@ -59,7 +61,6 @@ typedef void (*do_kexec_t)(unsigned long efi_boot, unsigned long start_addr, unsigned long first_ind_entry); -struct kimage; extern const unsigned char relocate_new_kernel[]; extern const size_t relocate_new_kernel_size; extern void kexec_reboot(void); From 09068613dd0dfbfacf50ff297b900be6ba500c50 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Thu, 25 Jun 2026 13:03:53 +0800 Subject: [PATCH 1964/2758] LoongArch: BPF: Fix outdated tail call comments [ Upstream commit 25d9127bb0e27275d55a5b3d0fd30b04bafffd5a ] The current LoongArch BPF JIT implementation hardcodes the number of prologue instructions skipped during a tail call as a magic number '7' in the jirl instruction. However, the accompanying comment explaining this offset is completely outdated. It inaccurately states that only a single TCC initialization instruction is bypassed, but in reality, multiple setup slots are skipped, so fix these outdated comments in __build_epilogue(). While at it, refine the comments in build_prologue() to describe the skipped setup slots (RA saving, fentry nops, and the TCC register slot) using proper dynamic tracing context. Also, remove the magic number '7' by introducing descriptive macros to formally define the prologue layout and make the tail call jump offset self-documenting. Fixes: 61319d15a560 ("LoongArch: BPF: Adjust the jump offset of tail calls") Signed-off-by: Tiezhu Yang Signed-off-by: Huacai Chen Signed-off-by: Sasha Levin --- arch/loongarch/net/bpf_jit.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c index e14a8aa47fc8e..64a7f7e60a01e 100644 --- a/arch/loongarch/net/bpf_jit.c +++ b/arch/loongarch/net/bpf_jit.c @@ -9,7 +9,13 @@ #define LOONGARCH_MAX_REG_ARGS 8 +#define LOONGARCH_SAVE_RA_NINSNS 1 #define LOONGARCH_LONG_JUMP_NINSNS 5 +#define LOONGARCH_TCC_SLOT_NINSNS 1 + +#define LOONGARCH_PROLOGUE_SKIP_INSNS \ + (LOONGARCH_SAVE_RA_NINSNS + LOONGARCH_LONG_JUMP_NINSNS + LOONGARCH_TCC_SLOT_NINSNS) + #define LOONGARCH_LONG_JUMP_NBYTES (LOONGARCH_LONG_JUMP_NINSNS * 4) #define LOONGARCH_FENTRY_NINSNS 2 @@ -143,8 +149,13 @@ static void build_prologue(struct jit_ctx *ctx) stack_adjust = round_up(stack_adjust, 16); stack_adjust += bpf_stack_adjust; + /* + * Save the original return address to a temporary register to prevent + * it from being overwritten, then reserve space for the long jump and + * fentry trampoline slot for dynamically patching by ftrace at runtime. + * These instructions are bypassed during a tail call invocation. + */ move_reg(ctx, LOONGARCH_GPR_T0, LOONGARCH_GPR_RA); - /* Reserve space for the move_imm + jirl instruction */ for (i = 0; i < LOONGARCH_LONG_JUMP_NINSNS; i++) emit_insn(ctx, nop); @@ -253,10 +264,11 @@ static void __build_epilogue(struct jit_ctx *ctx, bool is_tail_call) emit_insn(ctx, jirl, LOONGARCH_GPR_ZERO, LOONGARCH_GPR_RA, 0); } else { /* - * Call the next bpf prog and skip the first instruction - * of TCC initialization. + * Tail call to the next BPF program, passing offset in number + * of instructions to jirl to bypass the initial setup slots. */ - emit_insn(ctx, jirl, LOONGARCH_GPR_ZERO, LOONGARCH_GPR_T3, 7); + emit_insn(ctx, jirl, LOONGARCH_GPR_ZERO, + LOONGARCH_GPR_T3, LOONGARCH_PROLOGUE_SKIP_INSNS); } } From 0ddb9dcabf0b72b83f62f67c3d959af344a684f7 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Thu, 25 Jun 2026 13:03:53 +0800 Subject: [PATCH 1965/2758] LoongArch: BPF: Fix off-by-one error in tail call [ Upstream commit 0379d10f09bc21ba739636796669dfb4936172a3 ] The current code updates the tail call counter (TCC) using a pre-increment approach, it stores the incremented value back to memory before performing any boundary or target validation checks. This causes two major issues: 1. When a tail call fails because the target program is NULL, the TCC is incorrectly incremented and saved in memory anyway. 2. This dummy increment implicitly consumes one slot of the allowed tail call budget. As a result, the subsequent loop reaches the maximum limit prematurely, leading to a test failure where the actual loop count is 32 instead of the expected 33. Fix this by deferring the counter update. Change the branch condition to BPF_JSGE (greater or equal) so that we check the boundary first. The TCC is only incremented and stored back to memory after the boundary check and the NULL-target check both pass. Before: $ sudo ./test_progs -t tailcalls/tailcall_3 ... test_tailcall_count:FAIL:tailcall count unexpected tailcall count: actual 32 != expected 33 ... #465/3 tailcalls/tailcall_3:FAIL #465 tailcalls:FAIL After: $ sudo ./test_progs -t tailcalls/tailcall_3 #465/3 tailcalls/tailcall_3:OK #465 tailcalls:OK Summary: 1/1 PASSED, 0 SKIPPED, 0 FAILED Fixes: c0fcc955ff82 ("LoongArch: BPF: Fix the tailcall hierarchy") Signed-off-by: Tiezhu Yang Signed-off-by: Huacai Chen Signed-off-by: Sasha Levin --- arch/loongarch/net/bpf_jit.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c index 64a7f7e60a01e..e4966c178abc5 100644 --- a/arch/loongarch/net/bpf_jit.c +++ b/arch/loongarch/net/bpf_jit.c @@ -324,12 +324,12 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx, int insn) */ emit_insn(ctx, ldd, REG_TCC, LOONGARCH_GPR_SP, tcc_ptr_off); emit_insn(ctx, ldd, t3, REG_TCC, 0); - emit_insn(ctx, addid, t3, t3, 1); - emit_insn(ctx, std, t3, REG_TCC, 0); emit_insn(ctx, addid, t2, LOONGARCH_GPR_ZERO, MAX_TAIL_CALL_CNT); - if (emit_tailcall_jmp(ctx, BPF_JSGT, t3, t2, jmp_offset) < 0) + if (emit_tailcall_jmp(ctx, BPF_JSGE, t3, t2, jmp_offset) < 0) goto toofar; + emit_insn(ctx, addid, t3, t3, 1); + /* * prog = array->ptrs[index]; * if (!prog) @@ -342,6 +342,8 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx, int insn) if (emit_tailcall_jmp(ctx, BPF_JEQ, t2, LOONGARCH_GPR_ZERO, jmp_offset) < 0) goto toofar; + emit_insn(ctx, std, t3, REG_TCC, 0); + /* goto *(prog->bpf_func + 4); */ off = offsetof(struct bpf_prog, bpf_func); emit_insn(ctx, ldd, t3, t2, off); From c5fafece300c26ca1ba563e4cc35dcd84e86464f Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Thu, 25 Jun 2026 18:24:16 +0800 Subject: [PATCH 1966/2758] ASoC: fsl_asrc_dma: fix eDMA maxburst misalignment with channel count [ Upstream commit cf6f56990ea21172e085f0588e5bbf2089ce8f58 ] The back-end consumes data in units of the number of channels. When the maxburst value is not evenly divisible by the channel count, the DMA transfer length does not align with the FIFO frame boundary, causing wrong data to be copied and audible noise at the end of the stream. This is specific to eDMA: eDMA only responds to DMA requests from the back-end, whereas SDMA handles requests from both the front-end and the back-end and is not affected. For eDMA, when the back-end maxburst is not evenly divisible by the channel count, align it to the nearest valid boundary: - If maxburst >= channel count, override to the channel count so each transfer corresponds to exactly one audio frame. - If maxburst < channel count, override to 1 to avoid partial-frame transfers. Retain the original maxburst for SDMA or when it already aligns with the channel count. Fixes: c05f10f28ef6 ("ASoC: fsl_asrc: Add support for imx8qm & imx8qxp") Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20260625102416.424911-1-shengjiu.wang@oss.nxp.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/fsl/fsl_asrc_dma.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c index 5aa96af994c41..38f2b7c63133a 100644 --- a/sound/soc/fsl/fsl_asrc_dma.c +++ b/sound/soc/fsl/fsl_asrc_dma.c @@ -288,6 +288,26 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component, config_be.dst_addr_width = buswidth; config_be.dst_maxburst = dma_params_be->maxburst; + /* + * For eDMA, the back-end may report a maxburst size that is not evenly + * divisible by the channel count. This causes the DMA transfer length + * to misalign with the FIFO boundary, resulting in wrong data and + * audible noise. Align maxburst to the nearest valid boundary: + * - If maxburst >= channel count, override to the channel count so + * each transfer equals exactly one audio frame. + * - If maxburst < channel count, override to 1 to avoid partial-frame + * transfers. + */ + if (asrc->use_edma && (dma_params_be->maxburst % params_channels(params))) { + if (dma_params_be->maxburst >= params_channels(params)) { + config_be.src_maxburst = params_channels(params); + config_be.dst_maxburst = params_channels(params); + } else { + config_be.src_maxburst = 1; + config_be.dst_maxburst = 1; + } + } + memset(&audio_config, 0, sizeof(audio_config)); config_be.peripheral_config = &audio_config; config_be.peripheral_size = sizeof(audio_config); From 54292b167466cdf42176b7b6f01da66c184deb12 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 25 Jun 2026 06:59:36 +0000 Subject: [PATCH 1967/2758] net: udp_tunnel: prevent double queueing in udp_tunnel_nic_device_sync [ Upstream commit ecf69d4b43370c587e48d4d70289dbdb7e039d4d ] Yue Sun reported a use-after-free and debugobjects warning in udp_tunnel_nic_device_sync_work() during concurrent device operations. The workqueue core clears the internal pending bit before invoking the worker. At that point, a concurrent thread can queue the work again. When the already running worker eventually clears the work_pending flag to 0, it mistakenly clears the flag for the newly queued instance. udp_tunnel_nic_unregister() then observes work_pending as 0 and frees the structure while the second work item is still active in the queue, leading to UAF. Fix this by returning early in udp_tunnel_nic_device_sync() if work_pending is already set, preventing redundant work queueing. Fixes: cc4e3835eff4 ("udp_tunnel: add central NIC RX port offload infrastructure") Reported-by: Yue Sun Suggested-by: Jakub Kicinski Signed-off-by: Eric Dumazet Link: https://patch.msgid.link/20260625065938.654652-2-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv4/udp_tunnel_nic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/udp_tunnel_nic.c b/net/ipv4/udp_tunnel_nic.c index 9944ed923ddfd..3b32a0afa9798 100644 --- a/net/ipv4/udp_tunnel_nic.c +++ b/net/ipv4/udp_tunnel_nic.c @@ -301,7 +301,7 @@ __udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn) static void udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn) { - if (!utn->need_sync) + if (!utn->need_sync || utn->work_pending) return; queue_work(udp_tunnel_nic_workqueue, &utn->work); From 2542ce01d8114931657cdba55a5d17c3589ef06f Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Wed, 24 Jun 2026 10:02:50 -0500 Subject: [PATCH 1968/2758] dt-bindings: net: renesas,ether: Drop example "ethernet-phy-ieee802.3-c22" fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 14eb1d2c03b38ce3427f299967f7a4d97ebff4c2 ] Fix the Micrel PHY in the example which shouldn't have the fallback "ethernet-phy-ieee802.3-c22" compatible: Documentation/devicetree/bindings/net/renesas,ether.example.dtb: ethernet-phy@1 \ (ethernet-phy-id0022.1537): compatible: ['ethernet-phy-id0022.1537', 'ethernet-phy-ieee802.3-c22'] is too long from schema $id: http://devicetree.org/schemas/net/micrel.yaml Signed-off-by: Rob Herring (Arm) Reviewed-by: Andrew Lunn Acked-by: Conor Dooley Acked-by: Niklas Söderlund Fixes: 37a2fce09001 ("dt-bindings: sh_eth convert bindings to json-schema") Link: https://patch.msgid.link/20260624150250.131966-2-robh@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- Documentation/devicetree/bindings/net/renesas,ether.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/net/renesas,ether.yaml b/Documentation/devicetree/bindings/net/renesas,ether.yaml index f0a52f47f95a0..dd7187f12a675 100644 --- a/Documentation/devicetree/bindings/net/renesas,ether.yaml +++ b/Documentation/devicetree/bindings/net/renesas,ether.yaml @@ -121,8 +121,7 @@ examples: #size-cells = <0>; phy1: ethernet-phy@1 { - compatible = "ethernet-phy-id0022.1537", - "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id0022.1537"; reg = <1>; interrupt-parent = <&irqc0>; interrupts = <0 IRQ_TYPE_LEVEL_LOW>; From cd37bcb67f909a8683cc8c38a63ac7367a7f6161 Mon Sep 17 00:00:00 2001 From: Wei Fang Date: Wed, 24 Jun 2026 15:27:26 +0800 Subject: [PATCH 1969/2758] net: enetc: fix potential divide-by-zero when num_vsi is zero [ Upstream commit 5da65537792b68b6052ffcab65e04c27aea6dfe4 ] For i.MX94 series, all the standalone ENETCs do not support SR-IOV, so pf->caps.num_vsi is zero. This leads to a divide-by-zero in enetc4_default_rings_allocation() when distributing rings among PF and VFs. Division by zero is undefined behavior in C. On ARM64, the UDIV/SDIV instructions silently return zero rather than raising an exception, so the issue does not cause a visible crash. However, relying on this behavior is incorrect and poses a cross-platform compatibility risk. Add an explicit check for num_vsi == 0 and return early after the PF's rings have been configured. Fixes: 2d673b0e2f8d ("net: enetc: add standalone ENETC support for i.MX94") Signed-off-by: Wei Fang Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260624072726.1238903-1-wei.fang@oss.nxp.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/freescale/enetc/enetc4_pf.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c index 56899f2254aaa..71866d240b40c 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c +++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c @@ -326,6 +326,9 @@ static void enetc4_default_rings_allocation(struct enetc_pf *pf) val = enetc4_psicfgr0_val_construct(false, num_tx_bdr, num_rx_bdr); enetc_port_wr(hw, ENETC4_PSICFGR0(0), val); + if (!pf->caps.num_vsi) + return; + num_rx_bdr = pf->caps.num_rx_bdr - num_rx_bdr; rx_rem = num_rx_bdr % pf->caps.num_vsi; num_rx_bdr = num_rx_bdr / pf->caps.num_vsi; From ea0eb61029e08aa44d70f2ad9a5c5bd984a1efbe Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Sat, 2 May 2026 03:12:54 +0000 Subject: [PATCH 1970/2758] udp_tunnel: Pass struct sock to udp_tunnel_sock_release(). [ Upstream commit 944bfc1b1c6fe9417668006aae7124886bcca038 ] None of the udp_tunnel users need struct socket in their fast paths; it is only used for tunnel setup / teardown. While the UDP tunnel interface accepts struct socket, this encourages users to store the pointer unnecessarily. This leads to extra dereferences when accessing struct sock fields (e.g., sk->sk_user_data instead of sock->sk->sk_user_data). Furthermore, these dereferences necessitate synchronize_rcu() in udp_tunnel_sock_release() to protect the fast paths from sock_orphan() setting sk->sk_socket to NULL. This overhead can be avoided if users store the struct sock pointer directly in their private structures. As a prep, let's change udp_tunnel_sock_release() to take struct sock instead of struct socket. Signed-off-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260502031401.3557229-2-kuniyu@google.com Signed-off-by: Jakub Kicinski Stable-dep-of: c1481c94e74c ("tipc: avoid busy looping in tipc_exit_net()") Signed-off-by: Sasha Levin --- drivers/infiniband/sw/rxe/rxe_net.c | 4 ++-- drivers/infiniband/sw/rxe/rxe_ns.c | 4 ++-- drivers/net/amt.c | 2 +- drivers/net/bareudp.c | 2 +- drivers/net/geneve.c | 2 +- drivers/net/gtp.c | 6 +++--- drivers/net/pfcp.c | 2 +- drivers/net/vxlan/vxlan_core.c | 4 ++-- drivers/net/wireguard/socket.c | 4 ++-- include/net/udp_tunnel.h | 2 +- net/ipv4/fou_core.c | 7 ++----- net/ipv4/udp_tunnel_core.c | 6 ++++-- net/sctp/protocol.c | 6 +++--- net/tipc/udp_media.c | 4 ++-- 14 files changed, 27 insertions(+), 28 deletions(-) diff --git a/drivers/infiniband/sw/rxe/rxe_net.c b/drivers/infiniband/sw/rxe/rxe_net.c index 50a2cb5405e22..b454e4a179977 100644 --- a/drivers/infiniband/sw/rxe/rxe_net.c +++ b/drivers/infiniband/sw/rxe/rxe_net.c @@ -288,7 +288,7 @@ static struct socket *rxe_setup_udp_tunnel(struct net *net, __be16 port, return sock; } -static void rxe_release_udp_tunnel(struct socket *sk) +static void rxe_release_udp_tunnel(struct sock *sk) { if (sk) udp_tunnel_sock_release(sk); @@ -636,7 +636,7 @@ static void rxe_sock_put(struct sock *sk, if (refcount_read(&sk->sk_refcnt) > SK_REF_FOR_TUNNEL) { __sock_put(sk); } else { - rxe_release_udp_tunnel(sk->sk_socket); + rxe_release_udp_tunnel(sk); sk = NULL; set_sk(net, sk); } diff --git a/drivers/infiniband/sw/rxe/rxe_ns.c b/drivers/infiniband/sw/rxe/rxe_ns.c index 8b9d734229b24..64621c89f8bf1 100644 --- a/drivers/infiniband/sw/rxe/rxe_ns.c +++ b/drivers/infiniband/sw/rxe/rxe_ns.c @@ -47,7 +47,7 @@ static void rxe_ns_exit(struct net *net) rcu_read_unlock(); if (sk) { rcu_assign_pointer(ns_sk->rxe_sk4, NULL); - udp_tunnel_sock_release(sk->sk_socket); + udp_tunnel_sock_release(sk); } #if IS_ENABLED(CONFIG_IPV6) @@ -56,7 +56,7 @@ static void rxe_ns_exit(struct net *net) rcu_read_unlock(); if (sk) { rcu_assign_pointer(ns_sk->rxe_sk6, NULL); - udp_tunnel_sock_release(sk->sk_socket); + udp_tunnel_sock_release(sk); } #endif } diff --git a/drivers/net/amt.c b/drivers/net/amt.c index f2f3139e38a59..fc415072864b7 100644 --- a/drivers/net/amt.c +++ b/drivers/net/amt.c @@ -3032,7 +3032,7 @@ static int amt_dev_stop(struct net_device *dev) RCU_INIT_POINTER(amt->sock, NULL); synchronize_net(); if (sock) - udp_tunnel_sock_release(sock); + udp_tunnel_sock_release(sock->sk); cancel_work_sync(&amt->event_wq); for (i = 0; i < AMT_MAX_EVENTS; i++) { diff --git a/drivers/net/bareudp.c b/drivers/net/bareudp.c index da5866ba06999..f3025a5c52618 100644 --- a/drivers/net/bareudp.c +++ b/drivers/net/bareudp.c @@ -290,7 +290,7 @@ static void bareudp_sock_release(struct bareudp_dev *bareudp) sock = bareudp->sock; rcu_assign_pointer(bareudp->sock, NULL); synchronize_net(); - udp_tunnel_sock_release(sock); + udp_tunnel_sock_release(sock->sk); } static int bareudp_stop(struct net_device *dev) diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index c760c0655c8a1..15380119f0e89 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -1032,7 +1032,7 @@ static void __geneve_sock_release(struct geneve_sock *gs) list_del(&gs->list); udp_tunnel_notify_del_rx_port(gs->sock, UDP_TUNNEL_TYPE_GENEVE); - udp_tunnel_sock_release(gs->sock); + udp_tunnel_sock_release(gs->sock->sk); kfree_rcu(gs, rcu); } diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 5150f2e4f66be..064ce1029d331 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -885,8 +885,8 @@ static void gtp_encap_disable_sock(struct sock *sk) static void gtp_encap_disable(struct gtp_dev *gtp) { if (gtp->sk_created) { - udp_tunnel_sock_release(gtp->sk0->sk_socket); - udp_tunnel_sock_release(gtp->sk1u->sk_socket); + udp_tunnel_sock_release(gtp->sk0); + udp_tunnel_sock_release(gtp->sk1u); gtp->sk_created = false; gtp->sk0 = NULL; gtp->sk1u = NULL; @@ -1451,7 +1451,7 @@ static int gtp_create_sockets(struct gtp_dev *gtp, const struct nlattr *nla, sk1u = gtp_create_sock(UDP_ENCAP_GTP1U, gtp, nla, family); if (IS_ERR(sk1u)) { - udp_tunnel_sock_release(sk0->sk_socket); + udp_tunnel_sock_release(sk0); return PTR_ERR(sk1u); } diff --git a/drivers/net/pfcp.c b/drivers/net/pfcp.c index a6aa30ae0af76..efa258ec14c99 100644 --- a/drivers/net/pfcp.c +++ b/drivers/net/pfcp.c @@ -104,7 +104,7 @@ static int pfcp_encap_recv(struct sock *sk, struct sk_buff *skb) static void pfcp_del_sock(struct pfcp_dev *pfcp) { - udp_tunnel_sock_release(pfcp->sock); + udp_tunnel_sock_release(pfcp->sock->sk); pfcp->sock = NULL; } diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 89397447a14d4..dfdce1fa261eb 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -1523,13 +1523,13 @@ static void vxlan_sock_release(struct vxlan_dev *vxlan) vxlan_vs_del_dev(vxlan); if (__vxlan_sock_release_prep(sock4)) { - udp_tunnel_sock_release(sock4->sock); + udp_tunnel_sock_release(sock4->sock->sk); kfree(sock4); } #if IS_ENABLED(CONFIG_IPV6) if (__vxlan_sock_release_prep(sock6)) { - udp_tunnel_sock_release(sock6->sock); + udp_tunnel_sock_release(sock6->sock->sk); kfree(sock6); } #endif diff --git a/drivers/net/wireguard/socket.c b/drivers/net/wireguard/socket.c index c362c78d908eb..4a4c177af1707 100644 --- a/drivers/net/wireguard/socket.c +++ b/drivers/net/wireguard/socket.c @@ -335,7 +335,7 @@ static void sock_free(struct sock *sock) if (unlikely(!sock)) return; sk_clear_memalloc(sock); - udp_tunnel_sock_release(sock->sk_socket); + udp_tunnel_sock_release(sock); } static void set_sock_opts(struct socket *sock) @@ -396,7 +396,7 @@ int wg_socket_init(struct wg_device *wg, u16 port) port6.local_udp_port = inet_sk(new4->sk)->inet_sport; ret = udp_sock_create(net, &port6, &new6); if (ret < 0) { - udp_tunnel_sock_release(new4); + udp_tunnel_sock_release(new4->sk); if (ret == -EADDRINUSE && !port && retries++ < 100) goto retry; pr_err("%s: Could not create IPv6 socket\n", diff --git a/include/net/udp_tunnel.h b/include/net/udp_tunnel.h index 47c23d4a17408..dbbd56280f505 100644 --- a/include/net/udp_tunnel.h +++ b/include/net/udp_tunnel.h @@ -176,7 +176,7 @@ static inline void udp_tunnel_set_inner_protocol(struct sk_buff *skb, skb_set_inner_protocol(skb, inner_proto); } -void udp_tunnel_sock_release(struct socket *sock); +void udp_tunnel_sock_release(struct sock *sk); struct rtable *udp_tunnel_dst_lookup(struct sk_buff *skb, struct net_device *dev, diff --git a/net/ipv4/fou_core.c b/net/ipv4/fou_core.c index 5bae3cf7fe769..422f86291b427 100644 --- a/net/ipv4/fou_core.c +++ b/net/ipv4/fou_core.c @@ -558,11 +558,8 @@ static int fou_add_to_port_list(struct net *net, struct fou *fou, static void fou_release(struct fou *fou) { - struct socket *sock = fou->sock; - list_del(&fou->list); - udp_tunnel_sock_release(sock); - + udp_tunnel_sock_release(fou->sock->sk); kfree_rcu(fou, rcu); } @@ -634,7 +631,7 @@ static int fou_create(struct net *net, struct fou_cfg *cfg, error: kfree(fou); if (sock) - udp_tunnel_sock_release(sock); + udp_tunnel_sock_release(sock->sk); return err; } diff --git a/net/ipv4/udp_tunnel_core.c b/net/ipv4/udp_tunnel_core.c index b1f667c52cb21..1159a6a6fbb2f 100644 --- a/net/ipv4/udp_tunnel_core.c +++ b/net/ipv4/udp_tunnel_core.c @@ -195,9 +195,11 @@ void udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb } EXPORT_SYMBOL_GPL(udp_tunnel_xmit_skb); -void udp_tunnel_sock_release(struct socket *sock) +void udp_tunnel_sock_release(struct sock *sk) { - rcu_assign_sk_user_data(sock->sk, NULL); + struct socket *sock = sk->sk_socket; + + rcu_assign_sk_user_data(sk, NULL); synchronize_rcu(); kernel_sock_shutdown(sock, SHUT_RDWR); sock_release(sock); diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 5800e7ee7ea02..ffe594ad44147 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -878,7 +878,7 @@ int sctp_udp_sock_start(struct net *net) err = udp_sock_create(net, &udp_conf, &sock); if (err) { pr_err("Failed to create the SCTP UDP tunneling v6 sock\n"); - udp_tunnel_sock_release(net->sctp.udp4_sock->sk_socket); + udp_tunnel_sock_release(net->sctp.udp4_sock); net->sctp.udp4_sock = NULL; return err; } @@ -896,11 +896,11 @@ int sctp_udp_sock_start(struct net *net) void sctp_udp_sock_stop(struct net *net) { if (net->sctp.udp4_sock) { - udp_tunnel_sock_release(net->sctp.udp4_sock->sk_socket); + udp_tunnel_sock_release(net->sctp.udp4_sock); net->sctp.udp4_sock = NULL; } if (net->sctp.udp6_sock) { - udp_tunnel_sock_release(net->sctp.udp6_sock->sk_socket); + udp_tunnel_sock_release(net->sctp.udp6_sock); net->sctp.udp6_sock = NULL; } } diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c index 2c66b356025a7..d7c050ff58044 100644 --- a/net/tipc/udp_media.c +++ b/net/tipc/udp_media.c @@ -793,7 +793,7 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b, free: dst_cache_destroy(&ub->rcast.dst_cache); - udp_tunnel_sock_release(ub->ubsock); + udp_tunnel_sock_release(ub->ubsock->sk); err: kfree(ub); return err; @@ -815,7 +815,7 @@ static void cleanup_bearer(struct work_struct *work) tn = tipc_net(sock_net(ub->ubsock->sk)); dst_cache_destroy(&ub->rcast.dst_cache); - udp_tunnel_sock_release(ub->ubsock); + udp_tunnel_sock_release(ub->ubsock->sk); /* Note: could use a call_rcu() to avoid another synchronize_net() */ synchronize_net(); From 80e9adfed05d687a984b18d2f6ab2ee2bba370cf Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Sat, 2 May 2026 03:12:55 +0000 Subject: [PATCH 1971/2758] udp_tunnel: Pass struct sock to setup_udp_tunnel_sock(). [ Upstream commit 2cba193628fe523cee6dd61938db2c4563ce15a9 ] None of the udp_tunnel users need struct socket in their fast paths; it is only used for tunnel setup / teardown. Even setup_udp_tunnel_sock() does not need struct socket. Let's change setup_udp_tunnel_sock() to take struct sock instead of struct socket. Signed-off-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260502031401.3557229-3-kuniyu@google.com Signed-off-by: Jakub Kicinski Stable-dep-of: c1481c94e74c ("tipc: avoid busy looping in tipc_exit_net()") Signed-off-by: Sasha Levin --- drivers/infiniband/sw/rxe/rxe_net.c | 2 +- drivers/net/amt.c | 2 +- drivers/net/bareudp.c | 2 +- drivers/net/geneve.c | 2 +- drivers/net/gtp.c | 4 ++-- drivers/net/ovpn/udp.c | 2 +- drivers/net/pfcp.c | 2 +- drivers/net/vxlan/vxlan_core.c | 2 +- drivers/net/wireguard/socket.c | 4 ++-- include/net/udp_tunnel.h | 2 +- net/ipv4/fou_core.c | 2 +- net/ipv4/udp_tunnel_core.c | 4 +--- net/l2tp/l2tp_core.c | 2 +- net/rxrpc/local_object.c | 2 +- net/sctp/protocol.c | 4 ++-- net/tipc/udp_media.c | 2 +- 16 files changed, 19 insertions(+), 21 deletions(-) diff --git a/drivers/infiniband/sw/rxe/rxe_net.c b/drivers/infiniband/sw/rxe/rxe_net.c index b454e4a179977..082ff387d081a 100644 --- a/drivers/infiniband/sw/rxe/rxe_net.c +++ b/drivers/infiniband/sw/rxe/rxe_net.c @@ -283,7 +283,7 @@ static struct socket *rxe_setup_udp_tunnel(struct net *net, __be16 port, tnl_cfg.encap_rcv = rxe_udp_encap_recv; /* Setup UDP tunnel */ - setup_udp_tunnel_sock(net, sock, &tnl_cfg); + setup_udp_tunnel_sock(net, sock->sk, &tnl_cfg); return sock; } diff --git a/drivers/net/amt.c b/drivers/net/amt.c index fc415072864b7..c03aa7c207e64 100644 --- a/drivers/net/amt.c +++ b/drivers/net/amt.c @@ -2979,7 +2979,7 @@ static int amt_socket_create(struct amt_dev *amt) tunnel_cfg.encap_rcv = amt_rcv; tunnel_cfg.encap_err_lookup = amt_err_lookup; tunnel_cfg.encap_destroy = NULL; - setup_udp_tunnel_sock(amt->net, sock, &tunnel_cfg); + setup_udp_tunnel_sock(amt->net, sock->sk, &tunnel_cfg); rcu_assign_pointer(amt->sock, sock); return 0; diff --git a/drivers/net/bareudp.c b/drivers/net/bareudp.c index f3025a5c52618..169ab90393cc4 100644 --- a/drivers/net/bareudp.c +++ b/drivers/net/bareudp.c @@ -268,7 +268,7 @@ static int bareudp_socket_create(struct bareudp_dev *bareudp, __be16 port) tunnel_cfg.encap_rcv = bareudp_udp_encap_recv; tunnel_cfg.encap_err_lookup = bareudp_err_lookup; tunnel_cfg.encap_destroy = NULL; - setup_udp_tunnel_sock(bareudp->net, sock, &tunnel_cfg); + setup_udp_tunnel_sock(bareudp->net, sock->sk, &tunnel_cfg); rcu_assign_pointer(bareudp->sock, sock); return 0; diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 15380119f0e89..c55bdb805a644 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -1020,7 +1020,7 @@ static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port, tunnel_cfg.encap_rcv = geneve_udp_encap_recv; tunnel_cfg.encap_err_lookup = geneve_udp_encap_err_lookup; tunnel_cfg.encap_destroy = NULL; - setup_udp_tunnel_sock(net, sock, &tunnel_cfg); + setup_udp_tunnel_sock(net, sock->sk, &tunnel_cfg); list_add(&gs->list, &gn->sock_list); return gs; } diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 064ce1029d331..a60ef32b35b82 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -1434,7 +1434,7 @@ static struct sock *gtp_create_sock(int type, struct gtp_dev *gtp, tuncfg.encap_rcv = gtp_encap_recv; tuncfg.encap_destroy = NULL; - setup_udp_tunnel_sock(net, sock, &tuncfg); + setup_udp_tunnel_sock(net, sock->sk, &tuncfg); return sock->sk; } @@ -1689,7 +1689,7 @@ static struct sock *gtp_encap_enable_socket(int fd, int type, tuncfg.encap_rcv = gtp_encap_recv; tuncfg.encap_destroy = gtp_encap_destroy; - setup_udp_tunnel_sock(sock_net(sock->sk), sock, &tuncfg); + setup_udp_tunnel_sock(sock_net(sock->sk), sk, &tuncfg); out_rel_sock: release_sock(sock->sk); diff --git a/drivers/net/ovpn/udp.c b/drivers/net/ovpn/udp.c index 8811aa9eedeba..7f69e8890b5b5 100644 --- a/drivers/net/ovpn/udp.c +++ b/drivers/net/ovpn/udp.c @@ -399,7 +399,7 @@ int ovpn_udp_socket_attach(struct ovpn_socket *ovpn_sock, struct socket *sock, if (!old_data) { /* socket is currently unused - we can take it */ rcu_read_unlock(); - setup_udp_tunnel_sock(sock_net(ovpn_sock->sk), sock, &cfg); + setup_udp_tunnel_sock(sock_net(ovpn_sock->sk), sock->sk, &cfg); return 0; } diff --git a/drivers/net/pfcp.c b/drivers/net/pfcp.c index efa258ec14c99..5559bac41a9e2 100644 --- a/drivers/net/pfcp.c +++ b/drivers/net/pfcp.c @@ -173,7 +173,7 @@ static struct socket *pfcp_create_sock(struct pfcp_dev *pfcp) tuncfg.encap_rcv = pfcp_encap_recv; tuncfg.encap_type = 1; - setup_udp_tunnel_sock(net, sock, &tuncfg); + setup_udp_tunnel_sock(net, sock->sk, &tuncfg); return sock; } diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index dfdce1fa261eb..dae82a4d6027f 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -3624,7 +3624,7 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6, tunnel_cfg.gro_complete = vxlan_gro_complete; } - setup_udp_tunnel_sock(net, sock, &tunnel_cfg); + setup_udp_tunnel_sock(net, sock->sk, &tunnel_cfg); return vs; } diff --git a/drivers/net/wireguard/socket.c b/drivers/net/wireguard/socket.c index 4a4c177af1707..0028ef17dc716 100644 --- a/drivers/net/wireguard/socket.c +++ b/drivers/net/wireguard/socket.c @@ -389,7 +389,7 @@ int wg_socket_init(struct wg_device *wg, u16 port) goto out; } set_sock_opts(new4); - setup_udp_tunnel_sock(net, new4, &cfg); + setup_udp_tunnel_sock(net, new4->sk, &cfg); #if IS_ENABLED(CONFIG_IPV6) if (ipv6_mod_enabled()) { @@ -404,7 +404,7 @@ int wg_socket_init(struct wg_device *wg, u16 port) goto out; } set_sock_opts(new6); - setup_udp_tunnel_sock(net, new6, &cfg); + setup_udp_tunnel_sock(net, new6->sk, &cfg); } #endif diff --git a/include/net/udp_tunnel.h b/include/net/udp_tunnel.h index dbbd56280f505..49324e28ec275 100644 --- a/include/net/udp_tunnel.h +++ b/include/net/udp_tunnel.h @@ -94,7 +94,7 @@ struct udp_tunnel_sock_cfg { }; /* Setup the given (UDP) sock to receive UDP encapsulated packets */ -void setup_udp_tunnel_sock(struct net *net, struct socket *sock, +void setup_udp_tunnel_sock(struct net *net, struct sock *sk, struct udp_tunnel_sock_cfg *sock_cfg); /* -- List of parsable UDP tunnel types -- diff --git a/net/ipv4/fou_core.c b/net/ipv4/fou_core.c index 422f86291b427..6bed0e1dbe0e9 100644 --- a/net/ipv4/fou_core.c +++ b/net/ipv4/fou_core.c @@ -615,7 +615,7 @@ static int fou_create(struct net *net, struct fou_cfg *cfg, goto error; } - setup_udp_tunnel_sock(net, sock, &tunnel_cfg); + setup_udp_tunnel_sock(net, sk, &tunnel_cfg); sk->sk_allocation = GFP_ATOMIC; diff --git a/net/ipv4/udp_tunnel_core.c b/net/ipv4/udp_tunnel_core.c index 1159a6a6fbb2f..3090b4745d470 100644 --- a/net/ipv4/udp_tunnel_core.c +++ b/net/ipv4/udp_tunnel_core.c @@ -68,11 +68,9 @@ static bool sk_saddr_any(struct sock *sk) #endif } -void setup_udp_tunnel_sock(struct net *net, struct socket *sock, +void setup_udp_tunnel_sock(struct net *net, struct sock *sk, struct udp_tunnel_sock_cfg *cfg) { - struct sock *sk = sock->sk; - /* Disable multicast loopback */ inet_clear_bit(MC_LOOP, sk); diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 9419c8555d229..f940914959b18 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -1684,7 +1684,7 @@ int l2tp_tunnel_register(struct l2tp_tunnel *tunnel, struct net *net, .encap_destroy = l2tp_udp_encap_destroy, }; - setup_udp_tunnel_sock(net, sock, &udp_cfg); + setup_udp_tunnel_sock(net, sock->sk, &udp_cfg); } sk->sk_allocation = GFP_ATOMIC; diff --git a/net/rxrpc/local_object.c b/net/rxrpc/local_object.c index 111f574fe667d..169f9dfdaa77f 100644 --- a/net/rxrpc/local_object.c +++ b/net/rxrpc/local_object.c @@ -194,7 +194,7 @@ static int rxrpc_open_socket(struct rxrpc_local *local, struct net *net) tuncfg.encap_rcv = rxrpc_encap_rcv; tuncfg.encap_err_rcv = rxrpc_encap_err_rcv; tuncfg.sk_user_data = local; - setup_udp_tunnel_sock(net, local->socket, &tuncfg); + setup_udp_tunnel_sock(net, local->socket->sk, &tuncfg); /* set the socket up */ usk = local->socket->sk; diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index ffe594ad44147..5c6fa8e8d34d3 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -864,7 +864,7 @@ int sctp_udp_sock_start(struct net *net) tuncfg.encap_type = 1; tuncfg.encap_rcv = sctp_udp_rcv; tuncfg.encap_err_lookup = sctp_udp_v4_err; - setup_udp_tunnel_sock(net, sock, &tuncfg); + setup_udp_tunnel_sock(net, sock->sk, &tuncfg); net->sctp.udp4_sock = sock->sk; #if IS_ENABLED(CONFIG_IPV6) @@ -886,7 +886,7 @@ int sctp_udp_sock_start(struct net *net) tuncfg.encap_type = 1; tuncfg.encap_rcv = sctp_udp_rcv; tuncfg.encap_err_lookup = sctp_udp_v6_err; - setup_udp_tunnel_sock(net, sock, &tuncfg); + setup_udp_tunnel_sock(net, sock->sk, &tuncfg); net->sctp.udp6_sock = sock->sk; #endif diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c index d7c050ff58044..0db172f1a41aa 100644 --- a/net/tipc/udp_media.c +++ b/net/tipc/udp_media.c @@ -771,7 +771,7 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b, tuncfg.encap_type = 1; tuncfg.encap_rcv = tipc_udp_recv; tuncfg.encap_destroy = NULL; - setup_udp_tunnel_sock(net, ub->ubsock, &tuncfg); + setup_udp_tunnel_sock(net, ub->ubsock->sk, &tuncfg); err = dst_cache_init(&ub->rcast.dst_cache, GFP_ATOMIC); if (err) From 46d8d5b02f897903a820f007c645c229d06bace5 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Sat, 2 May 2026 03:13:07 +0000 Subject: [PATCH 1972/2758] tipc: Store struct sock in struct udp_bearer. [ Upstream commit 1ae552c7b6658c23fba8e964e687785297078880 ] tipc udp_bearer does not need to access struct socket itself in the fast path; it only reads struct sock, and struct socket is only used for tunnel setup and teardown. Let's store struct sock directly in struct udp_bearer. Note that cleanup_bearer() calls synchronize_net() after udp_tunnel_sock_release(), so udp_bearer is not freed until inflight fast paths finish. Note also that synchronize_rcu() is added in the error path of tipc_udp_enable() since udp_bearer will be kfree()d immediately once we remove synchronize_rcu() in udp_tunnel_sock_release(). This can be later converted to kfree_rcu(). Signed-off-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260502031401.3557229-15-kuniyu@google.com Signed-off-by: Jakub Kicinski Stable-dep-of: c1481c94e74c ("tipc: avoid busy looping in tipc_exit_net()") Signed-off-by: Sasha Levin --- net/tipc/udp_media.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c index 0db172f1a41aa..988b8a7f953ad 100644 --- a/net/tipc/udp_media.c +++ b/net/tipc/udp_media.c @@ -89,14 +89,14 @@ struct udp_replicast { /** * struct udp_bearer - ip/udp bearer data structure * @bearer: associated generic tipc bearer - * @ubsock: bearer associated socket + * @sk: bearer associated socket * @ifindex: local address scope * @work: used to schedule deferred work on a bearer * @rcast: associated udp_replicast container */ struct udp_bearer { struct tipc_bearer __rcu *bearer; - struct socket *ubsock; + struct sock *sk; u32 ifindex; struct work_struct work; struct udp_replicast rcast; @@ -194,7 +194,7 @@ static int tipc_udp_xmit(struct net *net, struct sk_buff *skb, } ttl = ip4_dst_hoplimit(&rt->dst); - udp_tunnel_xmit_skb(rt, ub->ubsock->sk, skb, src->ipv4.s_addr, + udp_tunnel_xmit_skb(rt, ub->sk, skb, src->ipv4.s_addr, dst->ipv4.s_addr, 0, ttl, 0, src->port, dst->port, false, true, 0); #if IS_ENABLED(CONFIG_IPV6) @@ -206,7 +206,7 @@ static int tipc_udp_xmit(struct net *net, struct sk_buff *skb, .saddr = src->ipv6, .flowi6_proto = IPPROTO_UDP }; - ndst = ip6_dst_lookup_flow(net, ub->ubsock->sk, + ndst = ip6_dst_lookup_flow(net, ub->sk, &fl6, NULL); if (IS_ERR(ndst)) { err = PTR_ERR(ndst); @@ -215,7 +215,7 @@ static int tipc_udp_xmit(struct net *net, struct sk_buff *skb, dst_cache_set_ip6(cache, ndst, &fl6.saddr); } ttl = ip6_dst_hoplimit(ndst); - udp_tunnel6_xmit_skb(ndst, ub->ubsock->sk, skb, NULL, + udp_tunnel6_xmit_skb(ndst, ub->sk, skb, NULL, &src->ipv6, &dst->ipv6, 0, ttl, 0, src->port, dst->port, false, 0); #endif @@ -405,9 +405,9 @@ static int tipc_udp_recv(struct sock *sk, struct sk_buff *skb) static int enable_mcast(struct udp_bearer *ub, struct udp_media_addr *remote) { - int err = 0; + struct sock *sk = ub->sk; struct ip_mreqn mreqn; - struct sock *sk = ub->ubsock->sk; + int err = 0; if (ntohs(remote->proto) == ETH_P_IP) { mreqn.imr_multiaddr = remote->ipv4; @@ -670,6 +670,7 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b, struct nlattr *opts[TIPC_NLA_UDP_MAX + 1]; u8 node_id[NODE_ID_LEN] = {0,}; struct net_device *dev; + struct socket *sock; int rmcast = 0; ub = kzalloc_obj(*ub, GFP_ATOMIC); @@ -764,14 +765,16 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b, goto err; } udp_conf.local_udp_port = local.port; - err = udp_sock_create(net, &udp_conf, &ub->ubsock); + err = udp_sock_create(net, &udp_conf, &sock); if (err) goto err; + + ub->sk = sock->sk; tuncfg.sk_user_data = ub; tuncfg.encap_type = 1; tuncfg.encap_rcv = tipc_udp_recv; tuncfg.encap_destroy = NULL; - setup_udp_tunnel_sock(net, ub->ubsock->sk, &tuncfg); + setup_udp_tunnel_sock(net, ub->sk, &tuncfg); err = dst_cache_init(&ub->rcast.dst_cache, GFP_ATOMIC); if (err) @@ -793,7 +796,8 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b, free: dst_cache_destroy(&ub->rcast.dst_cache); - udp_tunnel_sock_release(ub->ubsock->sk); + udp_tunnel_sock_release(ub->sk); + synchronize_rcu(); err: kfree(ub); return err; @@ -812,10 +816,10 @@ static void cleanup_bearer(struct work_struct *work) kfree_rcu(rcast, rcu); } - tn = tipc_net(sock_net(ub->ubsock->sk)); + tn = tipc_net(sock_net(ub->sk)); dst_cache_destroy(&ub->rcast.dst_cache); - udp_tunnel_sock_release(ub->ubsock->sk); + udp_tunnel_sock_release(ub->sk); /* Note: could use a call_rcu() to avoid another synchronize_net() */ synchronize_net(); @@ -833,11 +837,11 @@ static void tipc_udp_disable(struct tipc_bearer *b) pr_err("UDP bearer instance not found\n"); return; } - sock_set_flag(ub->ubsock->sk, SOCK_DEAD); + sock_set_flag(ub->sk, SOCK_DEAD); RCU_INIT_POINTER(ub->bearer, NULL); /* sock_release need to be done outside of rtnl lock */ - atomic_inc(&tipc_net(sock_net(ub->ubsock->sk))->wq_count); + atomic_inc(&tipc_net(sock_net(ub->sk))->wq_count); INIT_WORK(&ub->work, cleanup_bearer); schedule_work(&ub->work); } From 1c8393eefa3cadf4ca0b61119ad1321aa32d3c8c Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 23 Jun 2026 17:30:29 +0000 Subject: [PATCH 1973/2758] tipc: fix UAF in cleanup_bearer() due to premature dst_cache_destroy() [ Upstream commit 7116764ca53ff529335d7ab7c364a69f094b23a5 ] TIPC UDP media bearer teardown calls dst_cache_destroy() on its replicast caches before calling synchronize_net() to wait for concurrent RCU readers (transmitters) to finish: static void cleanup_bearer(struct work_struct *work) { ... list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) { dst_cache_destroy(&rcast->dst_cache); list_del_rcu(&rcast->list); kfree_rcu(rcast, rcu); } ... dst_cache_destroy(&ub->rcast.dst_cache); udp_tunnel_sock_release(ub->sk); synchronize_net(); ... } This is highly buggy because dst_cache_destroy() immediately frees the per-CPU cache memory (free_percpu()) and releases the cached dst entries without any synchronization. If a concurrent transmitter (e.g., tipc_udp_xmit()) is running on another CPU under RCU protection, it can call dst_cache_get() concurrently, leading to: 1. Use-After-Free on the per-CPU cache pointer itself (crash). 2. "rcuref - imbalanced put()" warning if it attempts to release a dst that was concurrently released by dst_cache_destroy(). Furthermore, calling kfree(ub) immediately after synchronize_net() without closing the socket first (or waiting after closing it) leaves a window where a concurrent receiver (tipc_udp_recv()) could start after synchronize_net(), access ub, and suffer a UAF when kfree(ub) runs. To fix this, we must defer dst_cache_destroy() and kfree(ub) until after we have ensured that no more readers can see the bearer/socket and all existing readers have finished: 1. Defer rcast entry destruction (both dst_cache_destroy() and kfree()) to an RCU callback using call_rcu_hurry(). Using call_rcu_hurry() ensures the dst entries are released quickly. 2. Release the bearer socket using udp_tunnel_sock_release() (stops new receive readers). 3. Call synchronize_net() to wait for all outstanding RCU readers (both transmit and receive) to finish. 4. Now that it is safe, call dst_cache_destroy() on the main bearer cache, and free ub. Note: 3) and 4) can be changed later in net-next to also use call_rcu_hurry() and get rid of the synchronize_net() latency. Fixes: e9c1a793210f ("tipc: add dst_cache support for udp media") Reported-by: syzbot+e14bc5d4942756023b77@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6a396a66.52ae72c2.136ac7.0003.GAE@google.com/T/#u Signed-off-by: Eric Dumazet Cc: Jon Maloy Cc: tipc-discussion@lists.sourceforge.net Reviewed-by: Xin Long Link: https://patch.msgid.link/20260623173030.2925059-2-edumazet@google.com Signed-off-by: Jakub Kicinski Stable-dep-of: c1481c94e74c ("tipc: avoid busy looping in tipc_exit_net()") Signed-off-by: Sasha Levin --- net/tipc/udp_media.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c index 988b8a7f953ad..66f3cb87a0aaa 100644 --- a/net/tipc/udp_media.c +++ b/net/tipc/udp_media.c @@ -803,6 +803,14 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b, return err; } +static void rcast_free_rcu(struct rcu_head *rcu) +{ + struct udp_replicast *rcast = container_of(rcu, struct udp_replicast, rcu); + + dst_cache_destroy(&rcast->dst_cache); + kfree(rcast); +} + /* cleanup_bearer - break the socket/bearer association */ static void cleanup_bearer(struct work_struct *work) { @@ -811,18 +819,17 @@ static void cleanup_bearer(struct work_struct *work) struct tipc_net *tn; list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) { - dst_cache_destroy(&rcast->dst_cache); list_del_rcu(&rcast->list); - kfree_rcu(rcast, rcu); + call_rcu_hurry(&rcast->rcu, rcast_free_rcu); } tn = tipc_net(sock_net(ub->sk)); - dst_cache_destroy(&ub->rcast.dst_cache); udp_tunnel_sock_release(ub->sk); - /* Note: could use a call_rcu() to avoid another synchronize_net() */ synchronize_net(); + + dst_cache_destroy(&ub->rcast.dst_cache); atomic_dec(&tn->wq_count); kfree(ub); } From 522d1d950b9e3b68190a6de7534827c8dccedb73 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 23 Jun 2026 17:30:30 +0000 Subject: [PATCH 1974/2758] tipc: avoid busy looping in tipc_exit_net() [ Upstream commit c1481c94e74c955e0448ddf46b8615a44d840c1e ] Blamed commit introduced a busy-wait loop in tipc_exit_net() to wait for pending UDP bearer cleanup works to complete: while (atomic_read(&tn->wq_count)) cond_resched(); This loop can busy-wait for a long time if cond_resched() is a NOP. This typically happens if the netns exit is executed by a high priority task, or under kernels configured without preemption (CONFIG_PREEMPT_NONE). In such cases, it wastes CPU cycles and can lead to soft lockups. Fix this by replacing the busy loop with wait_var_event(), allowing the thread to sleep properly until the work queue count reaches zero. Accordingly, update cleanup_bearer() to use atomic_dec_and_test() and wake_up_var() to wake up the waiter when the count drops to zero. This uses the global wait queue hash table, avoiding the need to bloat struct tipc_net with a wait_queue_head_t. The atomic_dec_and_test() provides the necessary memory barrier to ensure the wakeup is not missed. Fixes: 04c26faa51d1 ("tipc: wait and exit until all work queues are done") Signed-off-by: Eric Dumazet Cc: Jon Maloy Cc: tipc-discussion@lists.sourceforge.net Reviewed-by: Xin Long Link: https://patch.msgid.link/20260623173030.2925059-3-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/tipc/core.c | 4 ++-- net/tipc/udp_media.c | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/net/tipc/core.c b/net/tipc/core.c index 1ddecea1df6e9..315975c3be818 100644 --- a/net/tipc/core.c +++ b/net/tipc/core.c @@ -45,6 +45,7 @@ #include "crypto.h" #include +#include /* configurable TIPC parameters */ unsigned int tipc_net_id __read_mostly; @@ -118,8 +119,7 @@ static void __net_exit tipc_exit_net(struct net *net) #ifdef CONFIG_TIPC_CRYPTO tipc_crypto_stop(&tipc_net(net)->crypto_tx); #endif - while (atomic_read(&tn->wq_count)) - cond_resched(); + wait_var_event(&tn->wq_count, atomic_read(&tn->wq_count) == 0); } static void __net_exit tipc_pernet_pre_exit(struct net *net) diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c index 66f3cb87a0aaa..62ae7f5b58409 100644 --- a/net/tipc/udp_media.c +++ b/net/tipc/udp_media.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -830,7 +831,8 @@ static void cleanup_bearer(struct work_struct *work) synchronize_net(); dst_cache_destroy(&ub->rcast.dst_cache); - atomic_dec(&tn->wq_count); + if (atomic_dec_and_test(&tn->wq_count)) + wake_up_var(&tn->wq_count); kfree(ub); } From 9715ea1ceab786526682d416bacff5876f4a375b Mon Sep 17 00:00:00 2001 From: Nirmoy Das Date: Wed, 24 Jun 2026 06:44:16 -0700 Subject: [PATCH 1975/2758] selftests: tls: size splice_short pipe by page size [ Upstream commit 3e52f56875c6fafee619b5c2b4ded25f2efbd2ec ] splice_short grows its pipe with (MAX_FRAGS + 1) * 0x1000 so it can queue one short vmsplice() buffer for each fragment before draining the pipe. That assumes 4K pipe buffers. On 64K-page kernels the request is rounded to 262144 bytes, which provides only four pipe buffers. The fifth one-byte vmsplice() blocks in pipe_wait_writable and the test times out before it reaches the TLS path. Request enough bytes for the same number of pipe buffers using the runtime page size, and assert that the kernel granted at least that much. If an unprivileged run cannot raise the pipe above the system pipe-max-size limit, skip the test because it cannot exercise the intended path. Fixes: 3667e9b442b9 ("selftests: tls: add test for short splice due to full skmsg") Signed-off-by: Nirmoy Das Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260624134416.3235403-1-nirmoyd@nvidia.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- tools/testing/selftests/net/tls.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c index 30a236b8e9f73..d805a7dfbdd51 100644 --- a/tools/testing/selftests/net/tls.c +++ b/tools/testing/selftests/net/tls.c @@ -997,6 +997,8 @@ TEST_F(tls, splice_short) char sendbuf[0x100]; char sendchar = 'S'; int pipefds[2]; + int pipe_sz; + int ret; int i; sendchar_iov.iov_base = &sendchar; @@ -1005,7 +1007,11 @@ TEST_F(tls, splice_short) memset(sendbuf, 's', sizeof(sendbuf)); ASSERT_GE(pipe2(pipefds, O_NONBLOCK), 0); - ASSERT_GE(fcntl(pipefds[0], F_SETPIPE_SZ, (MAX_FRAGS + 1) * 0x1000), 0); + pipe_sz = (MAX_FRAGS + 1) * getpagesize(); + ret = fcntl(pipefds[0], F_SETPIPE_SZ, pipe_sz); + if (ret < 0 && errno == EPERM) + SKIP(return, "insufficient pipe capacity"); + ASSERT_GE(ret, pipe_sz); for (i = 0; i < MAX_FRAGS; i++) ASSERT_GE(vmsplice(pipefds[1], &sendchar_iov, 1, 0), 0); From ac04c2c833dd872cfae9539536b4d9bcc1ab064b Mon Sep 17 00:00:00 2001 From: Shuaisong Yang Date: Wed, 24 Jun 2026 22:13:16 +0800 Subject: [PATCH 1976/2758] net: hns3: unify copper port ksettings configuration path [ Upstream commit d77e98f8b2b382b06be7f17e482480dd8c4c5046 ] Refactor hns3_set_link_ksettings() and hclge_set_phy_link_ksettings() to unify the configuration path for copper ports. Previously, netdevs with a native kernel phy attached bypassed the main MAC parameter caching logic and returned early via phy_ethtool_ksettings_set(). This prevented the driver from updating hdev->hw.mac.req_xxx variables for kernel PHY setups, leaving them out-of-sync during reset recovery. Clean this up by routing all copper port configurations through ops->set_phy_link_ksettings(), and perform driver-level or kernel-level PHY arbitration inside hclge_set_phy_link_ksettings() via hnae3_dev_phy_imp_supported(). This ensures that the user's intended link profiles (req_speed, req_duplex, req_autoneg) are uniformly recorded across all copper and fiber deployment topologies, laying the groundwork for stable reset recovery. For copper ports where neither IMP firmware nor a kernel PHY is available (e.g. PHY_INEXISTENT), hclge_set_phy_link_ksettings() returns -ENODEV. In hns3_set_link_ksettings(), this is caught so the configuration falls through to the existing MAC-level path (check_ksettings_param -> cfg_mac_speed_dup_h), preserving compatibility with PHY-less copper deployments. Signed-off-by: Shuaisong Yang Signed-off-by: Jijie Shao Link: https://patch.msgid.link/20260624141319.271439-2-shaojijie@huawei.com Signed-off-by: Jakub Kicinski Stable-dep-of: c711f6d1cee9 ("net: hns3: fix permanent link down deadlock after reset") Signed-off-by: Sasha Levin --- .../ethernet/hisilicon/hns3/hns3_ethtool.c | 31 +++++++++---------- .../hisilicon/hns3/hns3pf/hclge_main.c | 28 +++++++++++++++-- 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c index 9cb7ce9fd311c..442f15476af32 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c @@ -811,12 +811,11 @@ static int hns3_get_link_ksettings(struct net_device *netdev, } static int hns3_check_ksettings_param(const struct net_device *netdev, - const struct ethtool_link_ksettings *cmd) + const struct ethtool_link_ksettings *cmd, + u8 media_type) { struct hnae3_handle *handle = hns3_get_handle(netdev); const struct hnae3_ae_ops *ops = hns3_get_ops(handle); - u8 module_type = HNAE3_MODULE_TYPE_UNKNOWN; - u8 media_type = HNAE3_MEDIA_TYPE_UNKNOWN; u32 lane_num; u8 autoneg; u32 speed; @@ -836,9 +835,6 @@ static int hns3_check_ksettings_param(const struct net_device *netdev, return 0; } - if (ops->get_media_type) - ops->get_media_type(handle, &media_type, &module_type); - if (cmd->base.duplex == DUPLEX_HALF && media_type != HNAE3_MEDIA_TYPE_COPPER) { netdev_err(netdev, @@ -863,6 +859,8 @@ static int hns3_set_link_ksettings(struct net_device *netdev, struct hnae3_handle *handle = hns3_get_handle(netdev); struct hnae3_ae_dev *ae_dev = hns3_get_ae_dev(handle); const struct hnae3_ae_ops *ops = hns3_get_ops(handle); + u8 module_type = HNAE3_MODULE_TYPE_UNKNOWN; + u8 media_type = HNAE3_MEDIA_TYPE_UNKNOWN; int ret; /* Chip don't support this mode. */ @@ -878,22 +876,23 @@ static int hns3_set_link_ksettings(struct net_device *netdev, cmd->base.autoneg, cmd->base.speed, cmd->base.duplex, cmd->lanes); - /* Only support ksettings_set for netdev with phy attached for now */ - if (netdev->phydev) { - if (cmd->base.speed == SPEED_1000 && - cmd->base.autoneg == AUTONEG_DISABLE) - return -EINVAL; + if (!ops->get_media_type) + return -EOPNOTSUPP; + ops->get_media_type(handle, &media_type, &module_type); - return phy_ethtool_ksettings_set(netdev->phydev, cmd); - } else if (test_bit(HNAE3_DEV_SUPPORT_PHY_IMP_B, ae_dev->caps) && - ops->set_phy_link_ksettings) { - return ops->set_phy_link_ksettings(handle, cmd); + if (media_type == HNAE3_MEDIA_TYPE_COPPER) { + if (!ops->set_phy_link_ksettings) + return -EOPNOTSUPP; + ret = ops->set_phy_link_ksettings(handle, cmd); + if (ret != -ENODEV) + return ret; + /* PHY_INEXISTENT, use MAC-level configuration */ } if (ae_dev->dev_version < HNAE3_DEVICE_VERSION_V2) return -EOPNOTSUPP; - ret = hns3_check_ksettings_param(netdev, cmd); + ret = hns3_check_ksettings_param(netdev, cmd, media_type); if (ret) return ret; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index dd4045c773d43..9a31939fec4cc 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -3358,8 +3358,8 @@ static int hclge_get_phy_link_ksettings(struct hnae3_handle *handle, } static int -hclge_set_phy_link_ksettings(struct hnae3_handle *handle, - const struct ethtool_link_ksettings *cmd) +hclge_ethtool_ksettings_set(struct hnae3_handle *handle, + const struct ethtool_link_ksettings *cmd) { struct hclge_desc desc[HCLGE_PHY_LINK_SETTING_BD_NUM]; struct hclge_vport *vport = hclge_get_vport(handle); @@ -3400,10 +3400,32 @@ hclge_set_phy_link_ksettings(struct hnae3_handle *handle, return ret; } + linkmode_copy(hdev->hw.mac.advertising, cmd->link_modes.advertising); + return 0; +} + +static int +hclge_set_phy_link_ksettings(struct hnae3_handle *handle, + const struct ethtool_link_ksettings *cmd) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + int ret = -ENODEV; + + if (hnae3_dev_phy_imp_supported(hdev)) { + ret = hclge_ethtool_ksettings_set(handle, cmd); + } else if (handle->netdev->phydev) { + if (cmd->base.speed == SPEED_1000 && + cmd->base.autoneg == AUTONEG_DISABLE) + return -EINVAL; + ret = phy_ethtool_ksettings_set(handle->netdev->phydev, cmd); + } + if (ret) + return ret; + hdev->hw.mac.req_autoneg = cmd->base.autoneg; hdev->hw.mac.req_speed = cmd->base.speed; hdev->hw.mac.req_duplex = cmd->base.duplex; - linkmode_copy(hdev->hw.mac.advertising, cmd->link_modes.advertising); return 0; } From 99f6a07add50feaf141ad28225c19fab7e2ef54f Mon Sep 17 00:00:00 2001 From: Shuaisong Yang Date: Wed, 24 Jun 2026 22:13:17 +0800 Subject: [PATCH 1977/2758] net: hns3: refactor MAC autoneg and speed configuration [ Upstream commit c01f6e6bdc1ccd21b2d07d23f50b82437b8cbf88 ] Extract the MAC autoneg and speed/duplex/lane configuration logic out of hclge_mac_init() and encapsulate it into a new dedicated helper function hclge_set_autoneg_speed_dup(). In the init path (hclge_init_ae_dev), this helper is now called after hclge_update_port_info() so that firmware-reported autoneg values are already populated before applying the link configuration. Introduce a separate req_lane_num field in struct hclge_mac to isolate the user-requested lane count from mac.lane_num, which firmware may overwrite via hclge_get_sfp_info() with stale values from a prior link lifecycle (e.g., lane_num=4 from 100G). During probe, req_lane_num is initialized to 0, which instructs firmware to auto-select the correct lane count for the current speed, rather than reusing the firmware- reported mac.lane_num that may be inconsistent with the target speed. This prevents probe failures from mismatched (speed, lane_num) pairs. In the reset path (hclge_reset_ae_dev), it runs immediately after hclge_mac_init(), using the previously cached req_* values to restore the link without re-querying firmware. Signed-off-by: Shuaisong Yang Signed-off-by: Jijie Shao Link: https://patch.msgid.link/20260624141319.271439-3-shaojijie@huawei.com Signed-off-by: Jakub Kicinski Stable-dep-of: c711f6d1cee9 ("net: hns3: fix permanent link down deadlock after reset") Signed-off-by: Sasha Levin --- .../hisilicon/hns3/hns3pf/hclge_main.c | 55 ++++++++++++++----- .../hisilicon/hns3/hns3pf/hclge_main.h | 1 + 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index 9a31939fec4cc..cd1aed19d4a73 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -1577,6 +1577,11 @@ static int hclge_configure(struct hclge_dev *hdev) hdev->hw.mac.req_autoneg = AUTONEG_ENABLE; hdev->hw.mac.req_duplex = DUPLEX_FULL; + /* When lane_num is 0, the firmware will automatically + * select the appropriate lane_num based on the speed. + */ + hdev->hw.mac.req_lane_num = 0; + hclge_parse_link_mode(hdev, cfg.speed_ability); hdev->hw.mac.max_speed = hclge_get_max_speed(cfg.speed_ability); @@ -2652,6 +2657,7 @@ static int hclge_cfg_mac_speed_dup_h(struct hnae3_handle *handle, int speed, if (ret) return ret; + hdev->hw.mac.req_lane_num = lane_num; hdev->hw.mac.req_speed = (u32)speed; hdev->hw.mac.req_duplex = duplex; @@ -2957,20 +2963,6 @@ static int hclge_mac_init(struct hclge_dev *hdev) if (!test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state)) hdev->hw.mac.duplex = HCLGE_MAC_FULL; - if (hdev->hw.mac.support_autoneg) { - ret = hclge_set_autoneg_en(hdev, hdev->hw.mac.autoneg); - if (ret) - return ret; - } - - if (!hdev->hw.mac.autoneg) { - ret = hclge_cfg_mac_speed_dup_hw(hdev, hdev->hw.mac.req_speed, - hdev->hw.mac.req_duplex, - hdev->hw.mac.lane_num); - if (ret) - return ret; - } - mac->link = 0; if (mac->user_fec_mode & BIT(HNAE3_FEC_USER_DEF)) { @@ -11744,6 +11736,27 @@ static int hclge_set_wol(struct hnae3_handle *handle, return ret; } +static int hclge_set_autoneg_speed_dup(struct hclge_dev *hdev) +{ + int ret; + + if (hdev->hw.mac.support_autoneg) { + ret = hclge_set_autoneg_en(hdev, hdev->hw.mac.autoneg); + if (ret) + return ret; + } + + if (!hdev->hw.mac.autoneg) { + ret = hclge_cfg_mac_speed_dup_hw(hdev, hdev->hw.mac.req_speed, + hdev->hw.mac.req_duplex, + hdev->hw.mac.req_lane_num); + if (ret) + return ret; + } + + return 0; +} + static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev) { struct pci_dev *pdev = ae_dev->pdev; @@ -11905,6 +11918,13 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev) if (ret) goto err_ptp_uninit; + ret = hclge_set_autoneg_speed_dup(hdev); + if (ret) { + dev_err(&pdev->dev, + "failed to set autoneg speed duplex, ret = %d\n", ret); + goto err_ptp_uninit; + } + INIT_KFIFO(hdev->mac_tnl_log); hclge_dcb_ops_set(hdev); @@ -12235,6 +12255,13 @@ static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev) return ret; } + ret = hclge_set_autoneg_speed_dup(hdev); + if (ret) { + dev_err(&pdev->dev, + "failed to set autoneg speed duplex, ret = %d\n", ret); + return ret; + } + ret = hclge_tp_port_init(hdev); if (ret) { dev_err(&pdev->dev, "failed to init tp port, ret = %d\n", diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h index 032b472d23685..4ca6458625a9b 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h @@ -287,6 +287,7 @@ struct hclge_mac { u8 support_autoneg; u8 speed_type; /* 0: sfp speed, 1: active speed */ u8 lane_num; + u8 req_lane_num; u32 speed; u32 req_speed; u32 max_speed; From 783dcef78cb05e16f7961c18a9ae22b1aeed057a Mon Sep 17 00:00:00 2001 From: Shuaisong Yang Date: Wed, 24 Jun 2026 22:13:18 +0800 Subject: [PATCH 1978/2758] net: hns3: fix permanent link down deadlock after reset [ Upstream commit c711f6d1cee955e04d1cd1f76cd8abd024b27a72 ] Fix a critical race condition deadlock where the network interface remains permanently Link Down after a hardware reset under specific ethtool sequences. This issue exclusively manifests in firmware-controlled PHY topologies where the driver relies on the IMP firmware to arbitrate link parameters. Standard devices driven by the kernel's native PHY_LIB are unaffected. The deadlock occurs via the following path: 1. User disables autoneg and forces an unmatched speed, forcing link down: `ethtool -s ethx autoneg off speed 10 duplex full` 2. User re-enables autoneg: `ethtool -s ethx autoneg on`. The netdev stack passes cmd->base.speed as SPEED_UNKNOWN (0xffffffff). 3. Driver saves req_autoneg=1, but before the interface can link up, a hardware reset is triggered. 4. During reset recovery, MAC init reads the un-synchronized runtime state mac.autoneg (which is still 0/OFF), misinterprets it as forced mode, and pushes the cached SPEED_UNKNOWN into the hardware registers, causing the MAC firmware state machine to freeze. Meanwhile, PHY init reads req_autoneg=1 and enables PHY autoneg. Since the MAC is frozen with 0xffffffff and PHY is running autoneg, they mismatch permanently. Fix this by: 1. Intercepting SPEED_UNKNOWN/DUPLEX_UNKNOWN in hclge_set_phy_link_ksettings() and hclge_cfg_mac_speed_dup_h() to prevent it from corrupting the driver's cached valid configuration. 2. Save req_autoneg in hclge_set_autoneg(). 3. Aligning the state judgment in hclge_set_autoneg_speed_dup() to use req_autoneg instead of the un-synchronized runtime mac.autoneg, ensuring both MAC and PHY consistently enter the autoneg branch to eliminate configuration discrepancies during reset recovery. Fixes: 05eb60e9648c ("net: hns3: using user configure after hardware reset") Signed-off-by: Shuaisong Yang Signed-off-by: Jijie Shao Link: https://patch.msgid.link/20260624141319.271439-4-shaojijie@huawei.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- .../hisilicon/hns3/hns3pf/hclge_main.c | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index cd1aed19d4a73..b8563ac34ad19 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -2658,8 +2658,10 @@ static int hclge_cfg_mac_speed_dup_h(struct hnae3_handle *handle, int speed, return ret; hdev->hw.mac.req_lane_num = lane_num; - hdev->hw.mac.req_speed = (u32)speed; - hdev->hw.mac.req_duplex = duplex; + if (speed != SPEED_UNKNOWN) + hdev->hw.mac.req_speed = (u32)speed; + if (duplex != DUPLEX_UNKNOWN) + hdev->hw.mac.req_duplex = duplex; return 0; } @@ -2690,6 +2692,7 @@ static int hclge_set_autoneg(struct hnae3_handle *handle, bool enable) { struct hclge_vport *vport = hclge_get_vport(handle); struct hclge_dev *hdev = vport->back; + int ret; if (!hdev->hw.mac.support_autoneg) { if (enable) { @@ -2701,7 +2704,10 @@ static int hclge_set_autoneg(struct hnae3_handle *handle, bool enable) } } - return hclge_set_autoneg_en(hdev, enable); + ret = hclge_set_autoneg_en(hdev, enable); + if (!ret) + hdev->hw.mac.req_autoneg = enable; + return ret; } static int hclge_get_autoneg(struct hnae3_handle *handle) @@ -3416,8 +3422,10 @@ hclge_set_phy_link_ksettings(struct hnae3_handle *handle, return ret; hdev->hw.mac.req_autoneg = cmd->base.autoneg; - hdev->hw.mac.req_speed = cmd->base.speed; - hdev->hw.mac.req_duplex = cmd->base.duplex; + if (cmd->base.speed != SPEED_UNKNOWN) + hdev->hw.mac.req_speed = cmd->base.speed; + if (cmd->base.duplex != DUPLEX_UNKNOWN) + hdev->hw.mac.req_duplex = cmd->base.duplex; return 0; } @@ -11741,12 +11749,12 @@ static int hclge_set_autoneg_speed_dup(struct hclge_dev *hdev) int ret; if (hdev->hw.mac.support_autoneg) { - ret = hclge_set_autoneg_en(hdev, hdev->hw.mac.autoneg); + ret = hclge_set_autoneg_en(hdev, hdev->hw.mac.req_autoneg); if (ret) return ret; } - if (!hdev->hw.mac.autoneg) { + if (!hdev->hw.mac.req_autoneg) { ret = hclge_cfg_mac_speed_dup_hw(hdev, hdev->hw.mac.req_speed, hdev->hw.mac.req_duplex, hdev->hw.mac.req_lane_num); From b4c34415b82b7d63607bbb991b84705d7f33ed9f Mon Sep 17 00:00:00 2001 From: Shuaisong Yang Date: Wed, 24 Jun 2026 22:13:19 +0800 Subject: [PATCH 1979/2758] net: hns3: differentiate autoneg default values between copper and fiber [ Upstream commit d9d349c4e8a0acd73bac8baa3605443c0df5eb26 ] Fix a link loss issue during driver initialization on optical ports connected to forced-mode (non-autoneg) remote switches. Previously, during driver probe or initialization, hclge_configure() blindly hardcoded hdev->hw.mac.req_autoneg to AUTONEG_ENABLE for all media types. While this is necessary for copper (BASE-T) ports to establish a link, many high-speed optical (fiber) ports in data centers are connected to switches running in forced mode (fixed speed, autoneg disabled). Forcing autoneg on these optical ports during initialization causes a permanent link failure since the remote end refuses to respond to autoneg pulses. Fix this by implementing media-type differentiated initialization in hclge_init_ae_dev(). Copper ports continue to default to AUTONEG_ENABLE, while optical ports strictly inherit the preset autoneg status pre-configured by the firmware (hdev->hw.mac.autoneg), preserving native compatibility with forced-mode network environments. Fixes: 05eb60e9648c ("net: hns3: using user configure after hardware reset") Signed-off-by: Shuaisong Yang Signed-off-by: Jijie Shao Link: https://patch.msgid.link/20260624141319.271439-5-shaojijie@huawei.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index b8563ac34ad19..2a6e4d17452c4 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -11926,6 +11926,13 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev) if (ret) goto err_ptp_uninit; + if (hdev->hw.mac.media_type != HNAE3_MEDIA_TYPE_COPPER) { + hdev->hw.mac.req_autoneg = hdev->hw.mac.autoneg; + if (hdev->hw.mac.autoneg == AUTONEG_DISABLE && + hdev->hw.mac.speed != SPEED_UNKNOWN) + hdev->hw.mac.req_speed = hdev->hw.mac.speed; + } + ret = hclge_set_autoneg_speed_dup(hdev); if (ret) { dev_err(&pdev->dev, From 3ab06151ffcb8c3aeb8f78508658b6c0f05be932 Mon Sep 17 00:00:00 2001 From: Jiaming Zhang Date: Thu, 25 Jun 2026 21:49:33 +0800 Subject: [PATCH 1980/2758] ALSA: FCP: Fix NULL pointer dereference in interface lookup [ Upstream commit e1e31e0ec8a609e17fd2e86b77bc00d9cbb24d7c ] A malformed USB device can provide a vendor-specific interface without any endpoint descriptors. fcp_find_fc_interface() currently selects the first vendor-specific interface and reads endpoint 0 from it, without checking whether the interface actually has any endpoints. When bNumEndpoints is zero, no endpoint array is allocated for the parsed alternate setting, so get_endpoint(..., 0) yields an invalid endpoint descriptor pointer. Dereferencing it through usb_endpoint_num() then triggers a NULL pointer dereference. Skip vendor-specific interfaces that do not have any endpoints. Fixes: 46757a3e7d50 ("ALSA: FCP: Add Focusrite Control Protocol driver") Reported-by: Jiaming Zhang Closes: https://lore.kernel.org/lkml/CANypQFb1EHj0xX8bA1WxSOSK-5xca6ZNKzOQcp12=s=puY7VFw@mail.gmail.com/ Signed-off-by: Jiaming Zhang Link: https://patch.msgid.link/20260625134933.425785-1-r772577952@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/usb/fcp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/usb/fcp.c b/sound/usb/fcp.c index 0fc4d063c48a5..c45dbe4d45328 100644 --- a/sound/usb/fcp.c +++ b/sound/usb/fcp.c @@ -1083,6 +1083,8 @@ static int fcp_find_fc_interface(struct usb_mixer_interface *mixer) if (desc->bInterfaceClass != 255) continue; + if (desc->bNumEndpoints < 1) + continue; epd = get_endpoint(intf->altsetting, 0); private->bInterfaceNumber = desc->bInterfaceNumber; From b2fa801be46d59c62904dbc5b0b128305fdf76eb Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Thu, 25 Jun 2026 08:34:45 +0900 Subject: [PATCH 1981/2758] tracing: probes: fix typo in a log message [ Upstream commit 72c8646956ffc8050bb8be5988a0f28fc37e1ac4 ] Fix a typo ("Invalid $-variable") in a log message. Link: https://lore.kernel.org/all/20260507081041.885781-4-martin@kaiser.cx/ Fixes: ab105a4fb894 ("tracing: Use tracing error_log with probe events") Signed-off-by: Martin Kaiser Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Sasha Levin --- kernel/trace/trace_probe.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h index 262d8707a3df7..df68d40de1610 100644 --- a/kernel/trace/trace_probe.h +++ b/kernel/trace/trace_probe.h @@ -509,7 +509,7 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call, C(NO_RETVAL, "This function returns 'void' type"), \ C(BAD_STACK_NUM, "Invalid stack number"), \ C(BAD_ARG_NUM, "Invalid argument number"), \ - C(BAD_VAR, "Invalid $-valiable specified"), \ + C(BAD_VAR, "Invalid $-variable specified"), \ C(BAD_REG_NAME, "Invalid register name"), \ C(BAD_MEM_ADDR, "Invalid memory address"), \ C(BAD_IMM, "Invalid immediate value"), \ From c637ec6a45925e962c867b717f4f23a2c5c4508d Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 23 Jun 2026 21:58:34 +0800 Subject: [PATCH 1982/2758] spi: sh-msiof: abort transfers when reset times out [ Upstream commit 6dbaa4d288432c697cea47028480481b8b29bd6a ] sh_msiof_spi_reset_regs() asserts TX/RX reset and polls until the reset bits clear, but the poll result is ignored. sh_msiof_transfer_one() can therefore continue programming a transfer after the controller did not leave reset. Return the reset poll result from the helper and abort the transfer on timeout, matching the existing transfer path's error-return style. Fixes: fedd6940682a ("spi: sh-msiof: Add reset of registers before starting transfer") Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260623135834.55442-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-sh-msiof.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index f114b6313f4f6..5e49ce15ac4df 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -114,7 +114,7 @@ static irqreturn_t sh_msiof_spi_irq(int irq, void *data) return IRQ_HANDLED; } -static void sh_msiof_spi_reset_regs(struct sh_msiof_spi_priv *p) +static int sh_msiof_spi_reset_regs(struct sh_msiof_spi_priv *p) { u32 mask = SICTR_TXRST | SICTR_RXRST; u32 data; @@ -123,8 +123,8 @@ static void sh_msiof_spi_reset_regs(struct sh_msiof_spi_priv *p) data |= mask; sh_msiof_write(p, SICTR, data); - readl_poll_timeout_atomic(p->mapbase + SICTR, data, !(data & mask), 1, - 100); + return readl_poll_timeout_atomic(p->mapbase + SICTR, data, + !(data & mask), 1, 100); } static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p, @@ -834,7 +834,9 @@ static int sh_msiof_transfer_one(struct spi_controller *ctlr, int ret; /* reset registers */ - sh_msiof_spi_reset_regs(p); + ret = sh_msiof_spi_reset_regs(p); + if (ret) + return ret; /* setup clocks (clock already enabled in chipselect()) */ if (!spi_controller_is_target(p->ctlr)) From d1a22906727b1a2ce98600bde0171d78e66e114b Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Thu, 25 Jun 2026 17:47:02 +0800 Subject: [PATCH 1983/2758] ACPI: RIMT: Only defer the IOMMU configuration in init stage [ Upstream commit b8f62414fa05144924257db283c5c35f74d21121 ] The IOMMU configuration will be deferred if the IOMMU driver isn't probed by the time. Make this deferral only in the initialization stage with driver_deferred_probe_check_state(). Otherwise the devices depends on IOMMU will be deferred forever in case the IOMMU device probe failed or it doesn't appear in the ACPI namespace. Fixes: 8f7729552582 ("ACPI: RISC-V: Add support for RIMT") Signed-off-by: Yicong Yang Link: https://patch.msgid.link/20260625094702.11558-1-yang.yicong@picoheart.com [pjw@kernel.org: added Fixes line] Signed-off-by: Paul Walmsley Signed-off-by: Sasha Levin --- drivers/acpi/riscv/rimt.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/riscv/rimt.c b/drivers/acpi/riscv/rimt.c index 906282b0e63c0..e4538fa6c2c82 100644 --- a/drivers/acpi/riscv/rimt.c +++ b/drivers/acpi/riscv/rimt.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -257,11 +258,11 @@ static int rimt_iommu_xlate(struct device *dev, struct acpi_rimt_node *node, u32 rimt_fwnode = rimt_get_fwnode(node); /* - * The IOMMU drivers may not be probed yet. - * Defer the IOMMU configuration + * The IOMMU drivers may not be probed yet. Defer the IOMMU + * configuration if it's still in initialization stage. */ if (!rimt_fwnode) - return -EPROBE_DEFER; + return driver_deferred_probe_check_state(dev); /* * EPROBE_DEFER ensures IOMMU is probed before the devices that From 5c907c11615fd07cd65defcb7a0cefbe547c047e Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 24 Jun 2026 19:31:48 +0800 Subject: [PATCH 1984/2758] riscv: Fix 32-bit call_on_irq_stack() frame pointer ABI [ Upstream commit c4c7756a81b5baef286bf9be1ea404f3e4dd7a3c ] call_on_irq_stack() uses struct member offsets to set up its link in the frame record list. On riscv32, struct stackframe is the wrong size to maintain stack pointer alignment, so STACKFRAME_SIZE_ON_STACK includes padding. However, the ABI requires the frame record to be placed immediately below the address stored in s0, so the padding must come before the struct members. Fix the layout by making STACKFRAME_FP and STACKFRAME_RA the negative offsets from s0, instead of the positive offsets from sp. Fixes: 82982fdd5133 ("riscv: Deduplicate IRQ stack switching") Signed-off-by: Samuel Holland Reviewed-by: Matthew Bystrin Signed-off-by: Rui Qi Link: https://lore.kernel.org/all/20240530001733.1407654-2-samuel.holland@sifive.com/ Reviewed-by: Nam Cao Link: https://patch.msgid.link/20260624113148.3723541-1-qirui.001@bytedance.com [pjw@kernel.org: cleaned up the patch tags and added Matthew's Reviewed-by] Signed-off-by: Paul Walmsley Signed-off-by: Sasha Levin --- arch/riscv/kernel/asm-offsets.c | 4 ++-- arch/riscv/kernel/entry.S | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/riscv/kernel/asm-offsets.c b/arch/riscv/kernel/asm-offsets.c index af827448a609e..a75f0cfea1e9f 100644 --- a/arch/riscv/kernel/asm-offsets.c +++ b/arch/riscv/kernel/asm-offsets.c @@ -501,8 +501,8 @@ void asm_offsets(void) OFFSET(SBI_HART_BOOT_STACK_PTR_OFFSET, sbi_hart_boot_data, stack_ptr); DEFINE(STACKFRAME_SIZE_ON_STACK, ALIGN(sizeof(struct stackframe), STACK_ALIGN)); - OFFSET(STACKFRAME_FP, stackframe, fp); - OFFSET(STACKFRAME_RA, stackframe, ra); + DEFINE(STACKFRAME_FP, offsetof(struct stackframe, fp) - sizeof(struct stackframe)); + DEFINE(STACKFRAME_RA, offsetof(struct stackframe, ra) - sizeof(struct stackframe)); #ifdef CONFIG_FUNCTION_TRACER DEFINE(FTRACE_OPS_FUNC, offsetof(struct ftrace_ops, func)); #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index e189c477c0e23..9a06ed983bb06 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -386,8 +386,8 @@ SYM_CODE_END(ret_from_fork_user_asm) SYM_FUNC_START(call_on_irq_stack) /* Create a frame record to save ra and s0 (fp) */ addi sp, sp, -STACKFRAME_SIZE_ON_STACK - REG_S ra, STACKFRAME_RA(sp) - REG_S s0, STACKFRAME_FP(sp) + REG_S ra, (STACKFRAME_SIZE_ON_STACK + STACKFRAME_RA)(sp) + REG_S s0, (STACKFRAME_SIZE_ON_STACK + STACKFRAME_FP)(sp) addi s0, sp, STACKFRAME_SIZE_ON_STACK /* Switch to the per-CPU shadow call stack */ @@ -405,8 +405,8 @@ SYM_FUNC_START(call_on_irq_stack) /* Switch back to the thread stack and restore ra and s0 */ addi sp, s0, -STACKFRAME_SIZE_ON_STACK - REG_L ra, STACKFRAME_RA(sp) - REG_L s0, STACKFRAME_FP(sp) + REG_L ra, (STACKFRAME_SIZE_ON_STACK + STACKFRAME_RA)(sp) + REG_L s0, (STACKFRAME_SIZE_ON_STACK + STACKFRAME_FP)(sp) addi sp, sp, STACKFRAME_SIZE_ON_STACK ret From 1c53d16b174dd9e02243fc0e85089e2aa6a0d21a Mon Sep 17 00:00:00 2001 From: Nuoqi Gui Date: Tue, 23 Jun 2026 18:43:38 +0800 Subject: [PATCH 1985/2758] bpf: Mask pseudo pointer values in verifier logs [ Upstream commit 72a85e9464a5332fb2cd7efd26d9295275ceda2d ] print_bpf_insn() masks ldimm64 immediates for pointer-bearing pseudo sources when pointer leaks are not allowed, but the mask only covers BPF_PSEUDO_MAP_FD and BPF_PSEUDO_MAP_VALUE. BPF_PSEUDO_MAP_IDX, BPF_PSEUDO_MAP_IDX_VALUE, and BPF_PSEUDO_BTF_ID can also be resolved to kernel pointer values before the verifier log prints the instruction. Include them in the existing pointer classification so the log prints 0x0 instead of the rewritten address. Fixes: 4976b718c355 ("bpf: Introduce pseudo_btf_id") Fixes: 387544bfa291 ("bpf: Introduce fd_idx") Signed-off-by: Nuoqi Gui Link: https://lore.kernel.org/r/20260623-f01-13-pseudo-btf-id-cap-bpf-v2-1-a190ebb8f3e2@mails.tsinghua.edu.cn Signed-off-by: Alexei Starovoitov Acked-by: Eduard Zingerman Signed-off-by: Sasha Levin --- kernel/bpf/disasm.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/disasm.c b/kernel/bpf/disasm.c index f8a3c7eb451e4..0391b3bc00734 100644 --- a/kernel/bpf/disasm.c +++ b/kernel/bpf/disasm.c @@ -323,7 +323,10 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs, */ u64 imm = ((u64)(insn + 1)->imm << 32) | (u32)insn->imm; bool is_ptr = insn->src_reg == BPF_PSEUDO_MAP_FD || - insn->src_reg == BPF_PSEUDO_MAP_VALUE; + insn->src_reg == BPF_PSEUDO_MAP_VALUE || + insn->src_reg == BPF_PSEUDO_MAP_IDX || + insn->src_reg == BPF_PSEUDO_MAP_IDX_VALUE || + insn->src_reg == BPF_PSEUDO_BTF_ID; char tmp[64]; if (is_ptr && !allow_ptr_leaks) From d8df91756890de058646597367507b239a6d2025 Mon Sep 17 00:00:00 2001 From: KaFai Wan Date: Wed, 24 Jun 2026 20:35:35 +0800 Subject: [PATCH 1986/2758] bpf: Fix insn_aux_data leak on verifier err_free_env path [ Upstream commit 26490a375cb9be9bac96b5171610fd85ca6c2305 ] When bpf_check() allocates env->insn_aux_data successfully but later fails to allocate env->succ, it jumps directly to err_free_env. The existing vfree(env->insn_aux_data) sits before the err_free_env label, so that direct jump bypasses it and leaks insn_aux_data. Move vfree(env->insn_aux_data) into err_free_env so all early and late exit paths release it consistently. Fixes: 2f69c5685427 ("bpf: make bpf_insn_successors to return a pointer") Signed-off-by: KaFai Wan Reviewed-by: Anton Protopopov Link: https://lore.kernel.org/r/20260624123536.114757-1-kafai.wan@linux.dev Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- kernel/bpf/verifier.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index bda2b2f149468..e3a4fdc8acd92 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -20221,13 +20221,13 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr, __u3 if (!is_priv) mutex_unlock(&bpf_verifier_lock); bpf_clear_insn_aux_data(env, 0, env->prog->len); - vfree(env->insn_aux_data); err_free_env: bpf_stack_liveness_free(env); kvfree(env->cfg.insn_postorder); kvfree(env->scc_info); kvfree(env->succ); kvfree(env->gotox_tmp_buf); + vfree(env->insn_aux_data); kvfree(env); return ret; } From 537e75aeb9cc4c4d93533815434d3d8fe3999c90 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Wed, 24 Jun 2026 21:16:45 +0800 Subject: [PATCH 1987/2758] gpio: mvebu: fail probe if gpiochip registration fails [ Upstream commit 479e91fc92416a4d54d2b3150aa1e4550d9cc759 ] mvebu_gpio_probe() registers the GPIO chip with devm_gpiochip_add_data() but ignores the return value. If registration fails, probe continues and leaves later code operating on a GPIO chip that was never published to gpiolib. Return the registration error so the device fails probe cleanly. Fixes: fefe7b092345 ("gpio: introduce gpio-mvebu driver for Marvell SoCs") Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260624131645.86884-1-pengpeng@iscas.ac.cn Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpio-mvebu.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index c030d1f00abca..689dc6354c2d7 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -1221,7 +1221,10 @@ static int mvebu_gpio_probe(struct platform_device *pdev) BUG(); } - devm_gpiochip_add_data(&pdev->dev, &mvchip->chip, mvchip); + err = devm_gpiochip_add_data(&pdev->dev, &mvchip->chip, mvchip); + if (err) + return dev_err_probe(&pdev->dev, err, + "failed to register gpiochip\n"); /* Some MVEBU SoCs have simple PWM support for GPIO lines */ if (IS_REACHABLE(CONFIG_PWM)) { From 151db2b5474411ac33b1875e7321ce97755af262 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Wed, 24 Jun 2026 21:18:28 +0800 Subject: [PATCH 1988/2758] gpio: htc-egpio: use managed gpiochip registration [ Upstream commit 17326db5f0ab4ec1901e75d052b5ebef486b467f ] egpio_probe() registers each nested gpio_chip with gpiochip_add_data() but ignores the return value. If one registration fails, probe still returns success even though one of the chips was not published to gpiolib. Use devm_gpiochip_add_data() and fail probe if any chip registration fails. This lets devres unwind already registered chips and prevents the driver from publishing a partially initialized device. Fixes: a1635b8fe59d ("[ARM] 4947/1: htc-egpio, a driver for GPIO/IRQ expanders with fixed input/output pins") Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260624131828.94139-1-pengpeng@iscas.ac.cn Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpio-htc-egpio.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-htc-egpio.c b/drivers/gpio/gpio-htc-egpio.c index d15423c718d03..25a4d4494f3c3 100644 --- a/drivers/gpio/gpio-htc-egpio.c +++ b/drivers/gpio/gpio-htc-egpio.c @@ -268,6 +268,7 @@ static int __init egpio_probe(struct platform_device *pdev) struct gpio_chip *chip; unsigned int irq, irq_end; int i; + int ret; /* Initialize ei data structure. */ ei = devm_kzalloc(&pdev->dev, struct_size(ei, chip, pdata->num_chips), GFP_KERNEL); @@ -326,7 +327,10 @@ static int __init egpio_probe(struct platform_device *pdev) chip->base = pdata->chip[i].gpio_base; chip->ngpio = pdata->chip[i].num_gpios; - gpiochip_add_data(chip, &ei->chip[i]); + ret = devm_gpiochip_add_data(&pdev->dev, chip, &ei->chip[i]); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to register gpiochip %d\n", i); } /* Set initial pin values */ From e1fc4b00b96de78cc781b6489bf44563c667a6f7 Mon Sep 17 00:00:00 2001 From: Corey Leavitt Date: Wed, 24 Jun 2026 22:40:16 +0200 Subject: [PATCH 1989/2758] net: pse-pd: scope pse_control regulator handle to kref lifetime [ Upstream commit 16759757c4d28e958fd5a5a1fe0f86828872f28d ] __pse_control_release() drops psec->ps via devm_regulator_put(), which only succeeds if the devres entry added by the matching devm_regulator_get_exclusive() is still present on pcdev->dev at the time the pse_control's kref hits zero. That assumption does not hold when the controller is unbound while a pse_control still has consumers: pcdev->dev's devres list is released LIFO, so every per-attach regulator-GET devres runs (and regulator_put()s the underlying regulator) before pse_controller_unregister() itself is invoked. Any later pse_control_put() from that unbind path then reads psec->ps as a dangling pointer inside devm_regulator_put() and WARNs at drivers/regulator/devres.c:232 (devres_release() fails to find the already-released match). The pse_control's consumer handle is logically scoped to the pse_control's refcount, not to pcdev->dev's devres lifetime. Switch to the plain regulator_get_exclusive() / regulator_put() pair so the regulator put in __pse_control_release() no longer depends on the controller's devres still being present. No change to the regulator-framework-visible refcount or lifetime of the underlying regulator: a single get paired with a single put. The existing devm_regulator_register() for the per-PI rails is unchanged (those ARE correctly scoped to the controller's lifetime). This addresses only the regulator handle. The same unbind-while-held scenario also leaves __pse_control_release() reading psec->pcdev->pi[] and psec->pcdev->owner after pse_controller_unregister() has freed pcdev->pi, because the controller does not drain its outstanding pse_control references on unregister. That wider pse_control vs pcdev lifetime problem pre-dates this change and is addressed by the PSE controller notifier series, which drains phydev->psec on PSE_UNREGISTERED before pcdev->pi is freed. Link: https://lore.kernel.org/netdev/20260620112440.1734404-1-github@szelinsky.de/ Fixes: d83e13761d5b ("net: pse-pd: Use regulator framework within PSE framework") Signed-off-by: Corey Leavitt Acked-by: Kory Maincent Signed-off-by: Carlo Szelinsky Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260624204017.2752934-1-github@szelinsky.de Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/pse-pd/pse_core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c index 69dbdbde9d71a..a5e6d7b26b9fd 100644 --- a/drivers/net/pse-pd/pse_core.c +++ b/drivers/net/pse-pd/pse_core.c @@ -1367,7 +1367,7 @@ static void __pse_control_release(struct kref *kref) if (psec->pcdev->pi[psec->id].admin_state_enabled) regulator_disable(psec->ps); - devm_regulator_put(psec->ps); + regulator_put(psec->ps); module_put(psec->pcdev->owner); @@ -1436,8 +1436,8 @@ pse_control_get_internal(struct pse_controller_dev *pcdev, unsigned int index, goto free_psec; pcdev->pi[index].admin_state_enabled = ret; - psec->ps = devm_regulator_get_exclusive(pcdev->dev, - rdev_get_name(pcdev->pi[index].rdev)); + psec->ps = regulator_get_exclusive(pcdev->dev, + rdev_get_name(pcdev->pi[index].rdev)); if (IS_ERR(psec->ps)) { ret = PTR_ERR(psec->ps); goto put_module; From c9961336aa5ff83092f23e33ee86666a9dbd1b2a Mon Sep 17 00:00:00 2001 From: Nuoqi Gui Date: Tue, 23 Jun 2026 18:32:31 +0800 Subject: [PATCH 1990/2758] seg6: validate SRH length before reading fixed fields [ Upstream commit a75d99f46bf21b45965ce39c5cfb3b8bb5ffb1aa ] seg6_validate_srh() reads fixed SRH fields such as srh->type and srh->hdrlen before checking that the supplied length covers the fixed struct ipv6_sr_hdr fields. The BPF SEG6 encap path reaches this with a BPF program-supplied pointer and length: bpf_lwt_push_encap() and the SEG6 local BPF END_B6 and END_B6_ENCAP actions call bpf_push_seg6_encap(), which forwards the length to seg6_validate_srh() with no minimum-size guard. A 2-byte SEG6 encap header can therefore make the validator read srh->type at offset 2 beyond the caller-supplied buffer. Reject lengths shorter than the fixed SRH at the top of seg6_validate_srh(), before any field is read. This fixes the BPF helper path and keeps the common validator robust. Fixes: fe94cc290f53 ("bpf: Add IPv6 Segment Routing helpers") Signed-off-by: Nuoqi Gui Reviewed-by: Andrea Mayer Link: https://patch.msgid.link/20260623-f01-17-seg6-srh-len-v2-1-2edc40e9e3e1@mails.tsinghua.edu.cn Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/seg6.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv6/seg6.c b/net/ipv6/seg6.c index 1c3ad25700c4c..62a7eb7792026 100644 --- a/net/ipv6/seg6.c +++ b/net/ipv6/seg6.c @@ -29,6 +29,9 @@ bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len, bool reduced) int max_last_entry; int trailing; + if (len < sizeof(*srh)) + return false; + if (srh->type != IPV6_SRCRT_TYPE_4) return false; From 6d46ab395803f162cdc50e8d346e5f1a4e766771 Mon Sep 17 00:00:00 2001 From: Matvey Kovalev Date: Tue, 23 Jun 2026 17:45:54 +0300 Subject: [PATCH 1991/2758] qede: fix out-of-bounds check for cqe->len_list[] [ Upstream commit f9ba47fce5932c15891c89c60e76dfaca919cb8d ] Move index check before element access. Fixes: 896f1a2493b5 ("net: qlogic/qede: fix potential out-of-bounds read in qede_tpa_cont() and qede_tpa_end()") Signed-off-by: Matvey Kovalev Link: https://patch.msgid.link/20260623144602.3521-1-matvey.kovalev@ispras.ru Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/qlogic/qede/qede_fp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede_fp.c b/drivers/net/ethernet/qlogic/qede/qede_fp.c index e338bfc8b7b2f..33e18bb697740 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_fp.c +++ b/drivers/net/ethernet/qlogic/qede/qede_fp.c @@ -961,7 +961,7 @@ static inline void qede_tpa_cont(struct qede_dev *edev, { int i; - for (i = 0; cqe->len_list[i] && i < ARRAY_SIZE(cqe->len_list); i++) + for (i = 0; i < ARRAY_SIZE(cqe->len_list) && cqe->len_list[i]; i++) qede_fill_frag_skb(edev, rxq, cqe->tpa_agg_index, le16_to_cpu(cqe->len_list[i])); @@ -986,7 +986,7 @@ static int qede_tpa_end(struct qede_dev *edev, dma_unmap_page(rxq->dev, tpa_info->buffer.mapping, PAGE_SIZE, rxq->data_direction); - for (i = 0; cqe->len_list[i] && i < ARRAY_SIZE(cqe->len_list); i++) + for (i = 0; i < ARRAY_SIZE(cqe->len_list) && cqe->len_list[i]; i++) qede_fill_frag_skb(edev, rxq, cqe->tpa_agg_index, le16_to_cpu(cqe->len_list[i])); if (unlikely(i > 1)) From d22829101ab675607ad6c3d420fb3ab875f46bbb Mon Sep 17 00:00:00 2001 From: Wei Fang Date: Fri, 26 Jun 2026 15:32:44 +0800 Subject: [PATCH 1992/2758] net: enetc: check the number of BDs needed for xdp_frame [ Upstream commit 555c5475e787802eeae0d2b91c2f66c330db2767 ] The size of xdp_redirect_arr array is ENETC_MAX_SKB_FRAGS. However, the number of fragments contained in xdp_frame may be greater than or equal to ENETC_MAX_SKB_FRAGS, which will cause the access to xdp_redirect_arr to be out of bounds. Fixes: 9d2b68cc108d ("net: enetc: add support for XDP_REDIRECT") Signed-off-by: Wei Fang Link: https://patch.msgid.link/20260626073244.2168214-1-wei.fang@oss.nxp.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/freescale/enetc/enetc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c index aa8a87124b103..8e3f345dd9aac 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc.c +++ b/drivers/net/ethernet/freescale/enetc/enetc.c @@ -1783,6 +1783,7 @@ int enetc_xdp_xmit(struct net_device *ndev, int num_frames, { struct enetc_tx_swbd xdp_redirect_arr[ENETC_MAX_SKB_FRAGS] = {0}; struct enetc_ndev_priv *priv = netdev_priv(ndev); + struct skb_shared_info *shinfo; struct enetc_bdr *tx_ring; int xdp_tx_bd_cnt, i, k; int xdp_tx_frm_cnt = 0; @@ -1798,6 +1799,12 @@ int enetc_xdp_xmit(struct net_device *ndev, int num_frames, prefetchw(ENETC_TXBD(*tx_ring, tx_ring->next_to_use)); for (k = 0; k < num_frames; k++) { + if (xdp_frame_has_frags(frames[k])) { + shinfo = xdp_get_shared_info_from_frame(frames[k]); + if (unlikely((shinfo->nr_frags + 1) > ENETC_MAX_SKB_FRAGS)) + break; + } + xdp_tx_bd_cnt = enetc_xdp_frame_to_xdp_tx_swbd(tx_ring, xdp_redirect_arr, frames[k]); From f7776052bb2325a30e12bed640b1179e7d7e4515 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Thu, 25 Jun 2026 16:23:54 +0200 Subject: [PATCH 1993/2758] sctp: fix SCTP_RESET_STREAMS stream list length limit [ Upstream commit 2b9f5ef534184bd81b8a4772780626c40eed1fd5 ] SCTP_RESET_STREAMS carries a flexible array of u16 stream IDs, but the optlen clamps treat USHRT_MAX as a byte count and then multiply sizeof(__u16) by the fixed header size. That caps the copied and validated option buffer at about 64 KiB, which rejects valid requests containing more than about half of the u16 stream ID range. Use struct_size_t() for the maximum struct sctp_reset_streams layout instead, so the bound matches the flexible array described by srs_number_streams. Fixes: 5960cefab9df ("sctp: add a ceiling to optlen in some sockopts") Acked-by: Xin Long Signed-off-by: Yousef Alhouseen Link: https://patch.msgid.link/20260625142354.2600-1-alhouseenyousef@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sctp/socket.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/net/sctp/socket.c b/net/sctp/socket.c index c8481461f7d8b..c7b9e325ec1cd 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -4111,8 +4111,9 @@ static int sctp_setsockopt_reset_streams(struct sock *sk, if (optlen < sizeof(*params)) return -EINVAL; /* srs_number_streams is u16, so optlen can't be bigger than this. */ - optlen = min_t(unsigned int, optlen, USHRT_MAX + - sizeof(__u16) * sizeof(*params)); + optlen = min_t(unsigned int, optlen, + struct_size_t(struct sctp_reset_streams, srs_stream_list, + USHRT_MAX)); if (params->srs_number_streams * sizeof(__u16) > optlen - sizeof(*params)) @@ -4598,8 +4599,8 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname, if (optlen > 0) { /* Trim it to the biggest size sctp sockopt may need if necessary */ optlen = min_t(unsigned int, optlen, - PAGE_ALIGN(USHRT_MAX + - sizeof(__u16) * sizeof(struct sctp_reset_streams))); + PAGE_ALIGN(struct_size_t(struct sctp_reset_streams, + srs_stream_list, USHRT_MAX))); kopt = memdup_sockptr(optval, optlen); if (IS_ERR(kopt)) return PTR_ERR(kopt); From 062bcbf8d1f1051fdeb20b94920031b0e2cb95a2 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Wed, 24 Jun 2026 18:53:12 -0400 Subject: [PATCH 1994/2758] sctp: add INIT verification after cookie unpacking [ Upstream commit 414c5447fe6a200613dd46d7fdc8454622076cb1 ] In SCTP handshake, the INIT chunk is initially processed by the server and embedded into the cookie carried in INIT-ACK. The client then returns this cookie via COOKIE-ECHO, where the server unpacks it and reconstructs the original INIT chunk. When cookie authentication is enabled, the cookie contents are protected against tampering, so reusing the unpacked INIT without re-verification is safe. However, when cookie authentication is disabled, the reconstructed INIT can no longer be trusted. In this case, the INIT must be explicitly validated after unpacking to avoid processing potentially tampered data. Add sctp_verify_init() checks after cookie unpacking in COOKIE-ECHO processing paths (sctp_sf_do_5_1D_ce() and sctp_sf_do_5_2_4_dupcook()) when cookie_auth_enable is disabled. On failure, the new association is freed and the packet is discarded. Also tighten cookie validation in sctp_unpack_cookie() by verifying the embedded chunk type is SCTP_CID_INIT before treating it as an INIT chunk. Finally, update sctp_verify_init() to validate parameter bounds using the actual embedded INIT length instead of chunk->chunk_end, since the INIT stored in COOKIE-ECHO may not span the entire chunk buffer. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Xin Long Link: https://patch.msgid.link/ebcbbac574815b0850f371b4bdb02f2e602b94d3.1782341592.git.lucien.xin@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sctp/sm_make_chunk.c | 5 ++++- net/sctp/sm_statefuns.c | 36 +++++++++++++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 41958b8e59fd5..8adac9e0cd66b 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1761,6 +1761,8 @@ struct sctp_association *sctp_unpack_cookie( bear_cookie = &cookie->c; ch = (struct sctp_chunkhdr *)(bear_cookie + 1); + if (ch->type != SCTP_CID_INIT) + goto malformed; chlen = ntohs(ch->length); if (chlen < sizeof(struct sctp_init_chunk)) goto malformed; @@ -2298,7 +2300,8 @@ int sctp_verify_init(struct net *net, const struct sctp_endpoint *ep, * VIOLATION error. We build the ERROR chunk here and let the normal * error handling code build and send the packet. */ - if (param.v != (void *)chunk->chunk_end) + if (param.v != (void *)peer_init + + SCTP_PAD4(ntohs(peer_init->chunk_hdr.length))) return sctp_process_inv_paramlength(asoc, param.p, chunk, errp); /* The only missing mandatory param possible today is diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 8e920cef0858c..d23d935e128e9 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -707,11 +707,12 @@ enum sctp_disposition sctp_sf_do_5_1D_ce(struct net *net, struct sctp_cmd_seq *commands) { struct sctp_ulpevent *ev, *ai_ev = NULL, *auth_ev = NULL; + struct sctp_chunk *err_chk_p = NULL; struct sctp_association *new_asoc; struct sctp_init_chunk *peer_init; struct sctp_chunk *chunk = arg; - struct sctp_chunk *err_chk_p; struct sctp_chunk *repl; + enum sctp_cid cid; struct sock *sk; int error = 0; @@ -785,6 +786,19 @@ enum sctp_disposition sctp_sf_do_5_1D_ce(struct net *net, } } + peer_init = (struct sctp_init_chunk *)(chunk->subh.cookie_hdr + 1); + cid = peer_init->chunk_hdr.type; + if (!sctp_sk(sk)->cookie_auth_enable && + !sctp_verify_init(net, ep, asoc, cid, peer_init, chunk, + &err_chk_p)) { + sctp_association_free(new_asoc); + if (err_chk_p) + sctp_chunk_free(err_chk_p); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); + } + if (err_chk_p) + sctp_chunk_free(err_chk_p); + if (security_sctp_assoc_request(new_asoc, chunk->head_skb ?: chunk->skb)) { sctp_association_free(new_asoc); return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); @@ -798,7 +812,6 @@ enum sctp_disposition sctp_sf_do_5_1D_ce(struct net *net, /* This is a brand-new association, so these are not yet side * effects--it is safe to run them here. */ - peer_init = (struct sctp_init_chunk *)(chunk->subh.cookie_hdr + 1); if (!sctp_process_init(new_asoc, chunk, &chunk->subh.cookie_hdr->c.peer_addr, peer_init, GFP_ATOMIC)) @@ -2215,10 +2228,12 @@ enum sctp_disposition sctp_sf_do_5_2_4_dupcook( void *arg, struct sctp_cmd_seq *commands) { + struct sctp_chunk *err_chk_p = NULL; struct sctp_association *new_asoc; + struct sctp_init_chunk *peer_init; struct sctp_chunk *chunk = arg; enum sctp_disposition retval; - struct sctp_chunk *err_chk_p; + enum sctp_cid cid; int error = 0; char action; @@ -2287,6 +2302,21 @@ enum sctp_disposition sctp_sf_do_5_2_4_dupcook( switch (action) { case 'A': /* Association restart. */ case 'B': /* Collision case B. */ + peer_init = (struct sctp_init_chunk *) + (chunk->subh.cookie_hdr + 1); + cid = peer_init->chunk_hdr.type; + if (!sctp_sk(ep->base.sk)->cookie_auth_enable && + !sctp_verify_init(net, ep, asoc, cid, peer_init, chunk, + &err_chk_p)) { + sctp_association_free(new_asoc); + if (err_chk_p) + sctp_chunk_free(err_chk_p); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, + commands); + } + if (err_chk_p) + sctp_chunk_free(err_chk_p); + fallthrough; case 'D': /* Collision case D. */ /* Update socket peer label if first association. */ if (security_sctp_assoc_request((struct sctp_association *)asoc, From 3aca736e177f46e81ece986fa0676809d03a1a71 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Wed, 6 May 2026 23:43:00 +0100 Subject: [PATCH 1995/2758] MIPS: DEC: Ensure RTC platform device deregistration upon failure [ Upstream commit eacaf5ae747f7dead6cc268de17a7382d79031fc ] Switch RTC platform device registration from platform_device_register() to platform_add_devices() so as to make sure any failure will result in automatic device unregistration. Fixes: fae67ad43114 ("arch/mips/dec: switch DECstation systems to rtc-cmos") Signed-off-by: Maciej W. Rozycki Acked-by: Thomas Bogendoerfer Signed-off-by: Thomas Bogendoerfer Signed-off-by: Sasha Levin --- arch/mips/dec/platform.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/mips/dec/platform.c b/arch/mips/dec/platform.c index 723ce16cbfc0c..a005246a0ac5d 100644 --- a/arch/mips/dec/platform.c +++ b/arch/mips/dec/platform.c @@ -38,6 +38,10 @@ static struct platform_device dec_rtc_device = { .num_resources = ARRAY_SIZE(dec_rtc_resources), }; +static struct platform_device *dec_rtc_devices[] __initdata = { + &dec_rtc_device, +}; + static struct resource dec_dz_resources[] = { { .name = "dz", .flags = IORESOURCE_MEM, }, { .name = "dz", .flags = IORESOURCE_IRQ, }, @@ -137,7 +141,7 @@ static int __init dec_add_devices(void) } num_zs = i; - ret1 = platform_device_register(&dec_rtc_device); + ret1 = platform_add_devices(dec_rtc_devices, 1); ret2 = IS_ENABLED(CONFIG_32BIT) ? platform_add_devices(dec_dz_devices, num_dz) : 0; ret3 = platform_add_devices(dec_zs_devices, num_zs); From f3ed7454024415bc626919f7eb1fa799c72fba5c Mon Sep 17 00:00:00 2001 From: Kyle Hendry Date: Sun, 21 Jun 2026 11:47:02 -0700 Subject: [PATCH 1996/2758] MIPS: mm: Add check for highmem before removing memory block [ Upstream commit 6d5fbecd0213489bc4de71a0da194d18e654fd6e ] If a device has less physical memory than the highmem threshold bootmem_init() doesn't set highstart_pfn. This results in highmem_init() wrongly disabling the entire memory range if the cpu doesn't support highmem. Add a check that highstart_pfn is non zero before removing the highmem block. Fixes: f171b55f1441 ("mips: fix HIGHMEM initialization") Signed-off-by: Kyle Hendry Signed-off-by: Thomas Bogendoerfer Signed-off-by: Sasha Levin --- arch/mips/mm/init.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c index 1c07ca84ee214..352718e43f697 100644 --- a/arch/mips/mm/init.c +++ b/arch/mips/mm/init.c @@ -426,10 +426,11 @@ static inline void __init highmem_init(void) unsigned long tmp; /* - * If CPU cannot support HIGHMEM discard the memory above highstart_pfn + * If CPU cannot support HIGHMEM discard any memory above highstart_pfn */ if (cpu_has_dc_aliases) { - memblock_remove(PFN_PHYS(highstart_pfn), -1); + if (highstart_pfn) + memblock_remove(PFN_PHYS(highstart_pfn), -1); return; } From 492d0c8f78d425d23b7e09e0141e0465ffc484a0 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Tue, 26 May 2026 17:03:05 +0200 Subject: [PATCH 1997/2758] ASoC: codecs: lpass-va-macro: Fix LPASS Codec Version for SC7280 [ Upstream commit 0773610eef71c30df3cb4c113c8215625d2a7c23 ] According to both the static definition in downstream... yupik-audio-overlay.dtsi: qcom,bolero-version = <4>; #define BOLERO_VERSION_2_0 0x0004) and the runtime detection: CDC_VA_TOP_CSR_CORE_ID_0=0x1 CDC_VA_TOP_CSR_CORE_ID_1=0xf SC7280 has LPASS Codec Version 2.0 and not, as declared with sm8250_va_data LPASS_CODEC_VERSION_1_0. Create new va_macro_data with .version not set to use the runtime detection and correctly get .version = LPASS_CODEC_VERSION_2_0. Fixes: 77212f300bfd ("ASoC: codecs: lpass-va-macro: set the default codec version for sm8250") Signed-off-by: Luca Weiss Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260526-sc7280-va-macro-2-0-v1-1-2c1b572fa388@fairphone.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/lpass-va-macro.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index 528d5b167ecff..58a5798823d7f 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -244,6 +244,11 @@ static const struct va_macro_data sm8250_va_data = { .version = LPASS_CODEC_VERSION_1_0, }; +static const struct va_macro_data sc7280_va_data = { + .has_swr_master = false, + .has_npl_clk = false, +}; + static const struct va_macro_data sm8450_va_data = { .has_swr_master = true, .has_npl_clk = true, @@ -1755,7 +1760,7 @@ static const struct dev_pm_ops va_macro_pm_ops = { }; static const struct of_device_id va_macro_dt_match[] = { - { .compatible = "qcom,sc7280-lpass-va-macro", .data = &sm8250_va_data }, + { .compatible = "qcom,sc7280-lpass-va-macro", .data = &sc7280_va_data }, { .compatible = "qcom,sm6115-lpass-va-macro", .data = &sm8450_va_data }, { .compatible = "qcom,sm8250-lpass-va-macro", .data = &sm8250_va_data }, { .compatible = "qcom,sm8450-lpass-va-macro", .data = &sm8450_va_data }, From 5bd0d47640395f8a8c34446b62d1781b88869dfa Mon Sep 17 00:00:00 2001 From: Abdurrahman Hussain Date: Sat, 20 Jun 2026 00:50:37 -0700 Subject: [PATCH 1998/2758] hwmon: (pmbus/core) honor vrm_version in pmbus_data2reg_vid() [ Upstream commit 828cd614e2af053ca5e1d6da767bbd8a1b5cabfb ] pmbus_data2reg_vid() hardcoded the VR11 encoding regardless of the vrm_version configured by the driver, while pmbus_reg2data_vid() already switched on it. Any driver that selects a non-VR11 VID mode and exposes a regulator (or hwmon vout setter) sent dangerously wrong codes to PMBUS_VOUT_COMMAND -- e.g. an nvidia195mv part asked for 200 mV got the VR11 clamp to 500 mV encoded as 0xB2, which the chip interprets as 1080 mV. Mirror pmbus_reg2data_vid() so writes round-trip with reads. Signed-off-by: Abdurrahman Hussain Assisted-by: Claude:claude-opus-4-7 [Claude Code] Link: https://lore.kernel.org/r/20260620-pmbus-data2reg-vid-v1-1-5518030432c4@nexthop.ai Fixes: 068c227056b92 ("hwmon: (pmbus) Add support for VR12") Fixes: d4977c083aeb2 ("hwmon: (pmbus) Add support for Intel VID protocol VR13") Fixes: 9d72340b6ade9 ("hwmon: (pmbus/core) Add support for Intel IMVP9 and AMD 6.25mV modes") Fixes: 969a4ec86ca5f ("hwmon: (pmbus/core) Add support for NVIDIA nvidia195mv mode") Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/pmbus/pmbus_core.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index e8fdd799c71c0..8123a568af40d 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -1095,9 +1095,27 @@ static u16 pmbus_data2reg_direct(struct pmbus_data *data, static u16 pmbus_data2reg_vid(struct pmbus_data *data, struct pmbus_sensor *sensor, s64 val) { - val = clamp_val(val, 500, 1600); - - return 2 + DIV_ROUND_CLOSEST_ULL((1600LL - val) * 100LL, 625); + switch (data->info->vrm_version[sensor->page]) { + case vr12: + val = clamp_val(val, 250, 1520); + return 1 + DIV_ROUND_CLOSEST_ULL(val - 250, 5); + case vr13: + val = clamp_val(val, 500, 3040); + return 1 + DIV_ROUND_CLOSEST_ULL(val - 500, 10); + case imvp9: + val = clamp_val(val, 200, 2740); + return 1 + DIV_ROUND_CLOSEST_ULL(val - 200, 10); + case amd625mv: + val = clamp_val(val, 200, 1550); + return DIV_ROUND_CLOSEST_ULL((1550LL - val) * 100LL, 625); + case nvidia195mv: + val = clamp_val(val, 195, 1465); + return 1 + DIV_ROUND_CLOSEST_ULL(val - 195, 5); + case vr11: + default: + val = clamp_val(val, 500, 1600); + return 2 + DIV_ROUND_CLOSEST_ULL((1600LL - val) * 100LL, 625); + } } static u16 pmbus_data2reg(struct pmbus_data *data, From 65e7e2b8d71b418439ebc68e80ed3e5a325375d6 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Wed, 10 Jun 2026 22:51:50 -0700 Subject: [PATCH 1999/2758] accel/amdxdna: Fix iommu domain lifetime race during device removal [ Upstream commit b4a0500fdf6e61a6c5f92ff2e61bc91578075803 ] When force_iova mode is enabled, amdxdna_remove() frees xdna->domain. If amdxdna_gem_obj_free() is called after device removal, it may attempt to access xdna->domain, resulting in a use-after-free. Fix the race by adding freeing xdna->domain as a managed release action, so its lifetime is managed by DRM and remains valid until all managed resources are released. Fixes: ece3e8980907 ("accel/amdxdna: Allow forcing IOVA-based DMA via module parameter") Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260611055150.3070216-3-lizhi.hou@amd.com Signed-off-by: Sasha Levin --- drivers/accel/amdxdna/amdxdna_iommu.c | 43 ++++++++++++++++++--------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/drivers/accel/amdxdna/amdxdna_iommu.c b/drivers/accel/amdxdna/amdxdna_iommu.c index 5a9f06183487f..77443872beb2d 100644 --- a/drivers/accel/amdxdna/amdxdna_iommu.c +++ b/drivers/accel/amdxdna/amdxdna_iommu.c @@ -4,6 +4,7 @@ */ #include +#include #include #include @@ -136,10 +137,30 @@ void amdxdna_iommu_free(struct amdxdna_dev *xdna, size_t size, free_pages((unsigned long)cpu_addr, get_order(size)); } +static void amdxdna_cleanup_force_iova(struct drm_device *dev, void *res) +{ + struct amdxdna_dev *xdna = to_xdna_dev(dev); + + if (xdna->domain) { + iommu_detach_group(xdna->domain, xdna->group); + put_iova_domain(&xdna->iovad); + iova_cache_put(); + iommu_domain_free(xdna->domain); + } + + iommu_group_put(xdna->group); +} + +void amdxdna_iommu_fini(struct amdxdna_dev *xdna) +{ + if (xdna->group && !xdna->domain) + iommu_group_put(xdna->group); +} + int amdxdna_iommu_init(struct amdxdna_dev *xdna) { unsigned long order; - int ret; + int ret = 0; xdna->group = iommu_group_get(xdna->ddev.dev); if (!xdna->group || !force_iova) @@ -165,8 +186,14 @@ int amdxdna_iommu_init(struct amdxdna_dev *xdna) if (ret) goto put_iova; + ret = drmm_add_action(&xdna->ddev, amdxdna_cleanup_force_iova, NULL); + if (ret) + goto detach_group; + return 0; +detach_group: + iommu_detach_group(xdna->domain, xdna->group); put_iova: put_iova_domain(&xdna->iovad); iova_cache_put(); @@ -174,20 +201,8 @@ int amdxdna_iommu_init(struct amdxdna_dev *xdna) iommu_domain_free(xdna->domain); put_group: iommu_group_put(xdna->group); + xdna->group = NULL; xdna->domain = NULL; return ret; } - -void amdxdna_iommu_fini(struct amdxdna_dev *xdna) -{ - if (xdna->domain) { - iommu_detach_group(xdna->domain, xdna->group); - put_iova_domain(&xdna->iovad); - iova_cache_put(); - iommu_domain_free(xdna->domain); - } - - if (xdna->group) - iommu_group_put(xdna->group); -} From 93b96e723bdcba6172a5814262be875abcc5d269 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Fri, 26 Jun 2026 10:22:04 +0300 Subject: [PATCH 2000/2758] hwmon: adm1275: Prevent reading uninitialized stack [ Upstream commit 553f9517813912a5ab661af5504485d96824a61c ] While adding support for the ROHM BD127X0 hot-swap controllers, sashiko reported an error in device-name comparison, which can lead to reading uninitialized stack memory. Quoting Sashiko: This is a pre-existing issue, but I noticed that just before this block in adm1275_probe(), there might be an out-of-bounds stack read: ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, block_buffer); if (ret < 0) { ... } for (mid = adm1275_id; mid->name[0]; mid++) { if (!strncasecmp(mid->name, block_buffer, strlen(mid->name))) break; } Since i2c_smbus_read_block_data() reads up to 32 bytes into the uninitialized stack array block_buffer without appending a null terminator, strncasecmp() could read past the valid bytes returned in ret. For example, if the device returns a shorter string like "adm12", checking it against "adm1275" up to the length of "adm1275" will continue reading into uninitialized stack bounds. Prevent reading uninitialized memory by zeroing the stack array. Signed-off-by: Matti Vaittinen Fixes: 87102808d039 ("hwmon: (pmbus/adm1275) Validate device ID") Link: https://lore.kernel.org/r/c8ad38e0cdb347261c6245de2b7965e747f28d22.1782458224.git.mazziesaccount@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/pmbus/adm1275.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c index bc2a6a07dc3e2..43baa5ded35e5 100644 --- a/drivers/hwmon/pmbus/adm1275.c +++ b/drivers/hwmon/pmbus/adm1275.c @@ -512,7 +512,7 @@ static int adm1275_enable_vout_temp(struct adm1275_data *data, static int adm1275_probe(struct i2c_client *client) { s32 (*config_read_fn)(const struct i2c_client *client, u8 reg); - u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1]; + u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1] = {0}; int config, device_config; int ret; struct pmbus_driver_info *info; From b0ff6b6ae9c5183ef701ece7016698bde5a5bfba Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 29 Jun 2026 16:08:00 -0700 Subject: [PATCH 2001/2758] hwmon: (pmbus) Fix passing events to regulator core [ Upstream commit 9ef7dacd44216bf5ea05c8aef49eba4d145f4047 ] Sashiko reports: Commit 754bd2b4a084 ("hwmon: (pmbus/core) Protect regulator operations with mutex") introduced a worker to batch regulator events over time using atomic_or(). The delayed worker then passes the combined bitmask unmodified to regulator_notifier_call_chain(). The core regulator subsystem's regulator_handle_critical() function evaluates the event parameter using a strict switch statement. If multiple distinct faults occur before the worker runs (e.g., REGULATOR_EVENT_UNDER_VOLTAGE | REGULATOR_EVENT_OVER_CURRENT), the combined bitmask fails to match any case. This leaves the reason as NULL and completely bypasses the critical hw_protection_trigger(). Fix the problem by passing events bit by bit to the regulator event handler. Reported-by: Sashiko Fixes: 754bd2b4a084 ("hwmon: (pmbus/core) Protect regulator operations with mutex") Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/pmbus/pmbus_core.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 8123a568af40d..3143b9e0316c4 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -3347,18 +3347,23 @@ static void pmbus_regulator_notify_worker(struct work_struct *work) int i, j; for (i = 0; i < data->info->pages; i++) { - int event; + unsigned int event; event = atomic_xchg(&data->regulator_events[i], 0); if (!event) continue; for (j = 0; j < data->info->num_regulators; j++) { - if (i == rdev_get_id(data->rdevs[j])) { + if (i != rdev_get_id(data->rdevs[j])) + continue; + while (event) { + unsigned int _event = BIT(__ffs(event)); + regulator_notifier_call_chain(data->rdevs[j], - event, NULL); - break; + _event, NULL); + event &= ~_event; } + break; } } } From fb8a5afe6f1f8aa7f19c6dda23d277dd6ae5d9e1 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 29 Jun 2026 16:18:18 -0700 Subject: [PATCH 2002/2758] hwmon: (aspeed-g6-pwm-tach) Guard fan RPM calculation against divide-by-zero [ Upstream commit fe87b8dc67f1b2c64e76a66e78468c533d3c44ca ] Sashiko reports: In the aspeed-g6-pwm-tacho driver, the aspeed_tach_val_to_rpm() function calculates the fan RPM using the tachometer value. However, it does not check if the tachometer value is zero before performing the division. If the hardware reports a tachometer value of 0 (which can happen due to an extremely fast pulse, a stuck edge, or a hardware glitch), the calculated tach_div evaluates to 0. The subsequent call to do_div() with tach_div as the divisor triggers a divide-by-zero exception, leading to a kernel panic. Check the divisor against zero to fix the problem. Fixes: 7e1449cd15d1 ("hwmon: (aspeed-g6-pwm-tacho): Support for ASPEED g6 PWM/Fan tach") Cc: Billy Tsai Reported-by: Sashiko Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/aspeed-g6-pwm-tach.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/aspeed-g6-pwm-tach.c b/drivers/hwmon/aspeed-g6-pwm-tach.c index 4f6e6d440dd40..5d611a8e52690 100644 --- a/drivers/hwmon/aspeed-g6-pwm-tach.c +++ b/drivers/hwmon/aspeed-g6-pwm-tach.c @@ -293,7 +293,10 @@ static int aspeed_tach_val_to_rpm(struct aspeed_pwm_tach_data *priv, u32 tach_va priv->clk_rate, tach_val, tach_div); rpm = (u64)priv->clk_rate * 60; - do_div(rpm, tach_div); + if (tach_div) + do_div(rpm, tach_div); + else + rpm = 0; return (int)rpm; } From 21f304c2aae46625e050c28d979f4b9d83faa85e Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 25 Jun 2026 09:05:08 -0700 Subject: [PATCH 2003/2758] eth: fbnic: don't cache shinfo across skb realloc [ Upstream commit 62b68b774f06bf52e329f254f0199bc43d350ccf ] fbnic_tx_lso() calls skb_cow_head() which may reallocate the skb including the shared info. We can't use the pointer calculated before the call. BUG: KASAN: slab-use-after-free in fbnic_tx_lso.isra.0+0x668/0x8e0 Read of size 4 at addr ff110000262edd98 by task swapper/5/0 Call Trace: fbnic_tx_lso.isra.0+0x668/0x8e0 fbnic_xmit_frame+0x622/0xba0 dev_hard_start_xmit+0xf4/0x620 Allocated by task 8653: __alloc_skb+0x11e/0x5f0 alloc_skb_with_frags+0xcc/0x6c0 sock_alloc_send_pskb+0x327/0x3f0 __ip_append_data+0x188b/0x47a0 ip_make_skb+0x24a/0x300 udp_sendmsg+0x14d2/0x21e0 Freed by task 0: kfree+0x123/0x5a0 pskb_expand_head+0x36c/0xfa0 fbnic_tx_lso.isra.0+0x500/0x8e0 fbnic_xmit_frame+0x622/0xba0 dev_hard_start_xmit+0xf4/0x620 sch_direct_xmit+0x25b/0x1100 The buggy address belongs to the object at ff110000262edc40 which belongs to the cache skbuff_small_head of size 640 The buggy address is located 344 bytes inside of freed 640-byte region [ff110000262edc40, ff110000262ede Link: https://netdev.bots.linux.dev/logs/vmksft/fbnic-qemu-dbg/results/705762/15-uso-py/stderr Fixes: b0b0f52042ac ("eth: fbnic: support TCP segmentation offload") Reviewed-by: Alexander Duyck Link: https://patch.msgid.link/20260625160508.3327986-1-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/meta/fbnic/fbnic_txrx.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c index 9cd85a0d0c3a7..401f8b8ae1cae 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c @@ -194,16 +194,18 @@ static bool fbnic_tx_tstamp(struct sk_buff *skb) static bool fbnic_tx_lso(struct fbnic_ring *ring, struct sk_buff *skb, - struct skb_shared_info *shinfo, __le64 *meta, - unsigned int *l2len, unsigned int *i3len) + __le64 *meta, unsigned int *l2len, unsigned int *i3len) { unsigned int l3_type, l4_type, l4len, hdrlen; + struct skb_shared_info *shinfo; unsigned char *l4hdr; __be16 payload_len; if (unlikely(skb_cow_head(skb, 0))) return true; + shinfo = skb_shinfo(skb); + if (shinfo->gso_type & SKB_GSO_PARTIAL) { l3_type = FBNIC_TWD_L3_TYPE_OTHER; } else if (!skb->encapsulation) { @@ -258,7 +260,6 @@ fbnic_tx_lso(struct fbnic_ring *ring, struct sk_buff *skb, static bool fbnic_tx_offloads(struct fbnic_ring *ring, struct sk_buff *skb, __le64 *meta) { - struct skb_shared_info *shinfo = skb_shinfo(skb); unsigned int l2len, i3len; if (fbnic_tx_tstamp(skb)) @@ -273,8 +274,8 @@ fbnic_tx_offloads(struct fbnic_ring *ring, struct sk_buff *skb, __le64 *meta) *meta |= cpu_to_le64(FIELD_PREP(FBNIC_TWD_CSUM_OFFSET_MASK, skb->csum_offset / 2)); - if (shinfo->gso_size) { - if (fbnic_tx_lso(ring, skb, shinfo, meta, &l2len, &i3len)) + if (skb_is_gso(skb)) { + if (fbnic_tx_lso(ring, skb, meta, &l2len, &i3len)) return true; } else { *meta |= cpu_to_le64(FBNIC_TWD_FLAG_REQ_CSO); From 110ccbd28c9444866fcc84ba96a2ad64fa6e95ae Mon Sep 17 00:00:00 2001 From: Pengfei Zhang Date: Thu, 25 Jun 2026 15:05:17 +0800 Subject: [PATCH 2004/2758] ipv6: fib6: fix NULL deref in fib6_walk_continue() on multi-batch dump [ Upstream commit 9facb861dc6b9b9ea9793ef5032a9a826f7a4229 ] inet6_dump_fib() saves its progress in cb->args[1] as a positional index within the current hash chain. Between batches, a concurrent fib6_new_table() can insert a new table at the chain head, shifting all existing entries. The saved index then lands on a different table, causing fib6_dump_table() to set w->root to the wrong table while w->node still points into the previous one. fib6_walk_continue() dereferences w->node->parent (NULL) and panics: BUG: kernel NULL pointer dereference, address: 0000000000000008 RIP: 0010:fib6_walk_continue+0x6e/0x170 Call Trace: fib6_dump_table.isra.0+0xc5/0x240 inet6_dump_fib+0xf6/0x420 rtnl_dumpit+0x30/0xa0 netlink_dump+0x15b/0x460 netlink_recvmsg+0x1d6/0x2a0 ____sys_recvmsg+0x17a/0x190 Fix by storing tb->tb6_id in cb->args[1] instead of a positional index. On resume, skip entries until the id matches; a concurrent head-insert can never match the saved id, so the walker always resumes on the correct table. Fixes: 1b43af5480c3 ("[IPV6]: Increase number of possible routing tables to 2^32") Signed-off-by: Pengfei Zhang Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260625070517.965597-1-zhangfeionline@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/ip6_fib.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index b897b3c5023b2..414fc3c567360 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -637,12 +637,12 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) }; const struct nlmsghdr *nlh = cb->nlh; struct net *net = sock_net(skb->sk); - unsigned int e = 0, s_e; struct hlist_head *head; struct fib6_walker *w; struct fib6_table *tb; unsigned int h, s_h; int err = 0; + u32 s_id; rcu_read_lock(); if (cb->strict_check) { @@ -702,23 +702,22 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) } s_h = cb->args[0]; - s_e = cb->args[1]; + s_id = cb->args[1]; - for (h = s_h; h < FIB6_TABLE_HASHSZ; h++, s_e = 0) { - e = 0; + for (h = s_h; h < FIB6_TABLE_HASHSZ; h++, s_id = 0) { head = &net->ipv6.fib_table_hash[h]; hlist_for_each_entry_rcu(tb, head, tb6_hlist) { - if (e < s_e) - goto next; + if (s_id && tb->tb6_id != s_id) + continue; + + s_id = 0; + cb->args[1] = tb->tb6_id; err = fib6_dump_table(tb, skb, cb); if (err != 0) goto out; -next: - e++; } } out: - cb->args[1] = e; cb->args[0] = h; unlock: From 3ef79fa3860e644c8de7834fa7300e1c58f38862 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sat, 27 Jun 2026 13:53:53 -0700 Subject: [PATCH 2005/2758] usbnet: gl620a: fix out-of-bounds read in genelink_rx_fixup() [ Upstream commit 8ff7f2a6da4fccaa5cc9be7251a24e71e29fbd1a ] genelink_rx_fixup() splits an aggregated RX frame into its individual packets, using a per-packet length taken from device-supplied data. That length is only bounded by GL_MAX_PACKET_LEN (1514); it is never compared against how many bytes were actually received. A malicious GeneLink (GL620A) device can therefore send a short URB whose header claims packet_count > 1 and a first packet of up to 1514 bytes. skb_put_data(gl_skb, packet->packet_data, size); then copies past the end of the receive buffer and hands the adjacent slab contents up the network stack, an out-of-bounds read that leaks kernel heap. No privilege is required: the path runs in the usbnet RX softirq as soon as the interface is up. BUG: KASAN: slab-out-of-bounds in genelink_rx_fixup (drivers/net/usb/gl620a.c:112) Read of size 1514 at addr ffff888011309708 by task ksoftirqd/0/14 Call Trace: ... __asan_memcpy (mm/kasan/shadow.c:105) genelink_rx_fixup (include/linux/skbuff.h:2814 drivers/net/usb/gl620a.c:112) usbnet_bh (drivers/net/usb/usbnet.c:572 drivers/net/usb/usbnet.c:1589) process_one_work (kernel/workqueue.c:3322) bh_worker (kernel/workqueue.c:3405) tasklet_action (kernel/softirq.c:965) handle_softirqs (kernel/softirq.c:622) run_ksoftirqd (kernel/softirq.c:1076) ... skb_pull() already verifies that the requested length fits the buffer and returns NULL otherwise. Move it ahead of the copy and check its result, so a packet that overruns the received data is rejected before it is read. Well-formed frames, whose packets are fully present, are unaffected. Fixes: 47ee3051c856 ("[PATCH] USB: usbnet (5/9) module for genesys gl620a cables") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Link: https://patch.msgid.link/20260627205353.4000788-1-xmei5@asu.edu Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/usb/gl620a.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/usb/gl620a.c b/drivers/net/usb/gl620a.c index 0bfa37c140591..09afd137b64e1 100644 --- a/drivers/net/usb/gl620a.c +++ b/drivers/net/usb/gl620a.c @@ -104,6 +104,9 @@ static int genelink_rx_fixup(struct usbnet *dev, struct sk_buff *skb) return 0; } + if (!skb_pull(skb, size + 4)) + return 0; + // allocate the skb for the individual packet gl_skb = alloc_skb(size, GFP_ATOMIC); if (gl_skb) { @@ -116,9 +119,6 @@ static int genelink_rx_fixup(struct usbnet *dev, struct sk_buff *skb) // advance to the next packet packet = (struct gl_packet *)&packet->packet_data[size]; count--; - - // shift the data pointer to the next gl_packet - skb_pull(skb, size + 4); } // skip the packet length field 4 bytes From 2381bf3f484e8e4fd89a225445872ec14e036ab5 Mon Sep 17 00:00:00 2001 From: Petr Wozniak Date: Sat, 27 Jun 2026 19:32:41 +0200 Subject: [PATCH 2006/2758] net: phy: sfp: free mii_bus in sfp_i2c_mdiobus_destroy [ Upstream commit 8f31efff9206f9f0adb853cad6916086aac4d5ef ] sfp_i2c_mdiobus_create() allocates the I2C MDIO bus with mdio_i2c_alloc(), a plain (non-devm) allocation, and registers it. sfp_i2c_mdiobus_destroy() only unregisters the bus and clears sfp->i2c_mii without calling mdiobus_free(). As the only reference to the bus is then cleared, the struct mii_bus is leaked. This is hit whenever a copper/RollBall SFP module that instantiated an MDIO bus is removed: sfp_sm_main() takes the global teardown path and calls sfp_i2c_mdiobus_destroy(). sfp_cleanup(), on driver unbind, frees sfp->i2c_mii directly, which is why the leak only triggered on module hot-removal and not on unbind. Free the bus in sfp_i2c_mdiobus_destroy() to match the allocation done in sfp_i2c_mdiobus_create(). Fixes: e85b1347ace6 ("net: sfp: create/destroy I2C mdiobus before PHY probe/after PHY release") Signed-off-by: Petr Wozniak Reviewed-by: Maxime Chevallier Reviewed-by: Larysa Zaremba Link: https://patch.msgid.link/312bde8176fc429aa89524e3be250137f034ba84.1782581445.git.petr.wozniak@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/phy/sfp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index b94b9c433a219..c47536b456e38 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -852,6 +852,7 @@ static int sfp_i2c_mdiobus_create(struct sfp *sfp) static void sfp_i2c_mdiobus_destroy(struct sfp *sfp) { mdiobus_unregister(sfp->i2c_mii); + mdiobus_free(sfp->i2c_mii); sfp->i2c_mii = NULL; } From 14b4cb78c3323d7a8b9031f47cd45b52855fd0c7 Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Fri, 26 Jun 2026 17:25:30 +0800 Subject: [PATCH 2007/2758] net: libwx: fix VMDQ mask for 1-queue mode [ Upstream commit 6ab752e0b59b825c127d5c86438bee1e8b1641ea ] In wx_set_vmdq_queues(), the VMDQ mask was not set for the devices not supporting WX_FLAG_MULTI_64_FUNC, i.e., NGBE devices. A mask of 0 causes __ALIGN_MASK(1, ~vmdq->mask) to return 0, which incorrectly sets q_per_pool to 0 in wx_write_qde(). Fix the VMDQ 1-queue mask to 0x7F then ensures that __ALIGN_MASK(1, ~0x7F) correctly evaluates to 1. Fixes: c52d4b898901 ("net: libwx: Redesign flow when sriov is enabled") Signed-off-by: Jiawen Wu Reviewed-by: Larysa Zaremba Link: https://patch.msgid.link/161F704D2C983E2C+20260626092530.551028-1-jiawenwu@trustnetic.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/wangxun/libwx/wx_lib.c | 1 + drivers/net/ethernet/wangxun/libwx/wx_type.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c index 746623fa59b40..5c2cd5756f8a9 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c @@ -1802,6 +1802,7 @@ static bool wx_set_vmdq_queues(struct wx *wx) rss_i = 4; } } else { + vmdq_m = WX_VMDQ_1Q_MASK; /* double check we are limited to maximum pools */ vmdq_i = min_t(u16, 8, vmdq_i); diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h index 0da5565ee4ffa..1b3dc2a808f90 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_type.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h @@ -486,6 +486,7 @@ enum WX_MSCA_CMD_value { #define WX_VMDQ_4Q_MASK 0x7C #define WX_VMDQ_2Q_MASK 0x7E +#define WX_VMDQ_1Q_MASK 0x7F /****************** Manageablility Host Interface defines ********************/ #define WX_HI_MAX_BLOCK_BYTE_LENGTH 256 /* Num of bytes in range */ From b637d6b7266124f17445800695f3b8193a8615fb Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Fri, 26 Jun 2026 15:52:28 -0700 Subject: [PATCH 2008/2758] net: gianfar: dispose irq mappings on probe failure and device removal [ Upstream commit dbf803bc4a8b0522c9a12560c20905a5952d1cb9 ] irq_of_parse_and_map() creates irqdomain mappings that should be balanced with irq_dispose_mapping(). The driver never called irq_dispose_mapping(), leaking mappings on probe failure and device removal. Fix by adding irq_dispose_mapping() in free_gfar_dev() and expanding its loop from priv->num_grps to MAXGROUPS so the error path also catches partially-initialized groups. All irqinfo pointers are pre-initialized to NULL in gfar_of_init(), making the NULL-guarded walk in free_gfar_dev() safe for every scenario. gfar_parse_group() itself is left as a simple parse function with no resource management; cleanup is centralized in the caller's error path. Assisted-by: opencode:big-pickle Fixes: b31a1d8b4151 ("gianfar: Convert gianfar to an of_platform_driver") Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260626225228.427392-1-rosenp@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/freescale/gianfar.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index 3271de5844f84..89215e1ddc2db 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -469,10 +469,13 @@ static void free_gfar_dev(struct gfar_private *priv) { int i, j; - for (i = 0; i < priv->num_grps; i++) + for (i = 0; i < MAXGROUPS; i++) for (j = 0; j < GFAR_NUM_IRQS; j++) { - kfree(priv->gfargrp[i].irqinfo[j]); - priv->gfargrp[i].irqinfo[j] = NULL; + if (priv->gfargrp[i].irqinfo[j]) { + irq_dispose_mapping(priv->gfargrp[i].irqinfo[j]->irq); + kfree(priv->gfargrp[i].irqinfo[j]); + priv->gfargrp[i].irqinfo[j] = NULL; + } } free_netdev(priv->ndev); @@ -616,7 +619,7 @@ static phy_interface_t gfar_get_interface(struct net_device *dev) static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev) { const char *model; - int err = 0, i; + int err = 0, i, j; phy_interface_t interface; struct net_device *dev = NULL; struct gfar_private *priv = NULL; @@ -702,8 +705,11 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev) priv->rx_list.count = 0; mutex_init(&priv->rx_queue_access); - for (i = 0; i < MAXGROUPS; i++) + for (i = 0; i < MAXGROUPS; i++) { priv->gfargrp[i].regs = NULL; + for (j = 0; j < GFAR_NUM_IRQS; j++) + priv->gfargrp[i].irqinfo[j] = NULL; + } /* Parse and initialize group specific information */ if (priv->mode == MQ_MG_MODE) { From b26aa9d993537a4c3167d8ceead3b7c69c3a0aac Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Sun, 28 Jun 2026 07:12:29 -0400 Subject: [PATCH 2009/2758] net/sched: sch_teql: Introduce slaves_lock to avoid race condition and UAF [ Upstream commit e5b811fe793166aecc59b085c1b7c31262ef2316 ] The teql master->slaves singly linked list is not protected against multiple writes. It can be mod'ed concurently from teql_master_xmit(), teql_dequeue(), teql_init() and teql_destroy() without holding any list lock or RCU protection. zdi-disclosures@trendmicro.com has demonstrated that the qdisc is freed after an RCU grace period, but teql_master_xmit() running on another CPU can still hold a stale pointer into the list, resulting in a slab-use-after-free: BUG: KASAN: slab-use-after-free in teql_master_xmit+0xf0f/0x16b0 Read of size 8 at addr ffff888013fb0440 by task poc/332 Freed 512-byte region [ffff888013fb0400, ffff888013fb0600) (kmalloc-512) The fix? Add a per-master slaves_lock spinlock that serializes all mutations of master->slaves and the NEXT_SLAVE() links in teql_destroy() and teql_qdisc_init(). teql_master_xmit() also takes the same slaves_lock around those updates. Annotate master->slaves and the per-slave ->next pointer with __rcu and use the appropriate RCU accessors everywhere they are touched: rcu_assign_pointer() on the writer side (under slaves_lock), rcu_dereference_protected() for the writer-side loads (also under slaves_lock), rcu_dereference_bh() for the loads in teql_master_xmit() and rtnl_dereference() for the loads in teql_master_open()/teql_master_mtu(), which run under RTNL. Pair this with rcu_read_lock_bh()/rcu_read_unlock_bh() around the list traversal in teql_master_xmit(), so that readers either observe a fully linked list or are deferred until the in-flight mutation completes. The two early-return paths in teql_master_xmit() are updated to release the RCU-bh read-side critical section before returning, since leaving it held would disable BH on that CPU for good. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: zdi-disclosures@trendmicro.com Tested-by: Victor Nogueira Signed-off-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260628111229.669751-1-jhs@mojatatu.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/sched/sch_teql.c | 125 +++++++++++++++++++++++++++++-------------- 1 file changed, 86 insertions(+), 39 deletions(-) diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index e7bbc9e5174d0..24ba31f8c8285 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -52,7 +52,8 @@ struct teql_master { struct Qdisc_ops qops; struct net_device *dev; - struct Qdisc *slaves; + struct Qdisc __rcu *slaves; + spinlock_t slaves_lock; /* serializes writes to ->slaves */ struct list_head master_list; unsigned long tx_bytes; unsigned long tx_packets; @@ -61,7 +62,7 @@ struct teql_master { }; struct teql_sched_data { - struct Qdisc *next; + struct Qdisc __rcu *next; struct teql_master *m; struct sk_buff_head q; }; @@ -101,7 +102,9 @@ teql_dequeue(struct Qdisc *sch) if (skb == NULL) { struct net_device *m = qdisc_dev(q); if (m) { - dat->m->slaves = sch; + spin_lock_bh(&dat->m->slaves_lock); + rcu_assign_pointer(dat->m->slaves, sch); + spin_unlock_bh(&dat->m->slaves_lock); netif_wake_queue(m); } } else { @@ -132,34 +135,49 @@ teql_destroy(struct Qdisc *sch) struct Qdisc *q, *prev; struct teql_sched_data *dat = qdisc_priv(sch); struct teql_master *master = dat->m; + struct netdev_queue *txq = NULL; + bool reset_master_queue = false; if (!master) return; - prev = master->slaves; + spin_lock_bh(&master->slaves_lock); + prev = rcu_dereference_protected(master->slaves, + lockdep_is_held(&master->slaves_lock)); if (prev) { do { - q = NEXT_SLAVE(prev); - if (q == sch) { - NEXT_SLAVE(prev) = NEXT_SLAVE(q); - if (q == master->slaves) { - master->slaves = NEXT_SLAVE(q); - if (q == master->slaves) { - struct netdev_queue *txq; - - txq = netdev_get_tx_queue(master->dev, 0); - master->slaves = NULL; - - dev_reset_queue(master->dev, - txq, NULL); - } - } - skb_queue_purge(&dat->q); - break; + struct Qdisc *head, *next; + + q = rcu_dereference_protected(NEXT_SLAVE(prev), + lockdep_is_held(&master->slaves_lock)); + if (q != sch) { + prev = q; + continue; } - } while ((prev = q) != master->slaves); + next = rcu_dereference_protected(NEXT_SLAVE(q), + lockdep_is_held(&master->slaves_lock)); + rcu_assign_pointer(NEXT_SLAVE(prev), next); + + head = rcu_dereference_protected(master->slaves, + lockdep_is_held(&master->slaves_lock)); + if (q == head) { + rcu_assign_pointer(master->slaves, next); + if (q == next) { + txq = netdev_get_tx_queue(master->dev, 0); + rcu_assign_pointer(master->slaves, NULL); + reset_master_queue = true; + } + } + skb_queue_purge(&dat->q); + break; + } while (prev != rcu_dereference_protected(master->slaves, + lockdep_is_held(&master->slaves_lock))); } + spin_unlock_bh(&master->slaves_lock); + + if (reset_master_queue) + dev_reset_queue(master->dev, txq, NULL); } static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt, @@ -168,6 +186,7 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt, struct net_device *dev = qdisc_dev(sch); struct teql_master *m = (struct teql_master *)sch->ops; struct teql_sched_data *q = qdisc_priv(sch); + struct Qdisc *first; if (dev->hard_header_len > m->dev->hard_header_len) return -EINVAL; @@ -184,7 +203,9 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt, skb_queue_head_init(&q->q); - if (m->slaves) { + spin_lock_bh(&m->slaves_lock); + first = rcu_dereference_protected(m->slaves, lockdep_is_held(&m->slaves_lock)); + if (first) { if (m->dev->flags & IFF_UP) { if ((m->dev->flags & IFF_POINTOPOINT && !(dev->flags & IFF_POINTOPOINT)) || @@ -192,8 +213,10 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt, !(dev->flags & IFF_BROADCAST)) || (m->dev->flags & IFF_MULTICAST && !(dev->flags & IFF_MULTICAST)) || - dev->mtu < m->dev->mtu) + dev->mtu < m->dev->mtu) { + spin_unlock_bh(&m->slaves_lock); return -EINVAL; + } } else { if (!(dev->flags&IFF_POINTOPOINT)) m->dev->flags &= ~IFF_POINTOPOINT; @@ -204,14 +227,17 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt, if (dev->mtu < m->dev->mtu) m->dev->mtu = dev->mtu; } - q->next = NEXT_SLAVE(m->slaves); - NEXT_SLAVE(m->slaves) = sch; + rcu_assign_pointer(q->next, + rcu_dereference_protected(NEXT_SLAVE(first), + lockdep_is_held(&m->slaves_lock))); + rcu_assign_pointer(NEXT_SLAVE(first), sch); } else { - q->next = sch; - m->slaves = sch; + rcu_assign_pointer(q->next, sch); + rcu_assign_pointer(m->slaves, sch); m->dev->mtu = dev->mtu; m->dev->flags = (m->dev->flags&~FMASK)|(dev->flags&FMASK); } + spin_unlock_bh(&m->slaves_lock); return 0; } @@ -285,7 +311,9 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev) int subq = skb_get_queue_mapping(skb); struct sk_buff *skb_res = NULL; - start = master->slaves; + rcu_read_lock_bh(); + + start = rcu_dereference_bh(master->slaves); restart: nores = 0; @@ -317,10 +345,17 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev) netdev_start_xmit(skb, slave, slave_txq, false) == NETDEV_TX_OK) { __netif_tx_unlock(slave_txq); - master->slaves = NEXT_SLAVE(q); + spin_lock_bh(&master->slaves_lock); + if (rcu_dereference_protected(master->slaves, + lockdep_is_held(&master->slaves_lock)) == q) + rcu_assign_pointer(master->slaves, + rcu_dereference_protected(NEXT_SLAVE(q), + lockdep_is_held(&master->slaves_lock))); + spin_unlock_bh(&master->slaves_lock); netif_wake_queue(dev); master->tx_packets++; master->tx_bytes += length; + rcu_read_unlock_bh(); return NETDEV_TX_OK; } __netif_tx_unlock(slave_txq); @@ -329,14 +364,21 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev) busy = 1; break; case 1: - master->slaves = NEXT_SLAVE(q); + spin_lock_bh(&master->slaves_lock); + if (rcu_dereference_protected(master->slaves, + lockdep_is_held(&master->slaves_lock)) == q) + rcu_assign_pointer(master->slaves, + rcu_dereference_protected(NEXT_SLAVE(q), + lockdep_is_held(&master->slaves_lock))); + spin_unlock_bh(&master->slaves_lock); + rcu_read_unlock_bh(); return NETDEV_TX_OK; default: nores = 1; break; } __skb_pull(skb, skb_network_offset(skb)); - } while ((q = NEXT_SLAVE(q)) != start); + } while ((q = rcu_dereference_bh(NEXT_SLAVE(q))) != start); if (nores && skb_res == NULL) { skb_res = skb; @@ -345,29 +387,32 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev) if (busy) { netif_stop_queue(dev); + rcu_read_unlock_bh(); return NETDEV_TX_BUSY; } master->tx_errors++; drop: master->tx_dropped++; + rcu_read_unlock_bh(); dev_kfree_skb(skb); return NETDEV_TX_OK; } static int teql_master_open(struct net_device *dev) { - struct Qdisc *q; + struct Qdisc *q, *first; struct teql_master *m = netdev_priv(dev); int mtu = 0xFFFE; unsigned int flags = IFF_NOARP | IFF_MULTICAST; - if (m->slaves == NULL) + first = rtnl_dereference(m->slaves); + if (!first) return -EUNATCH; flags = FMASK; - q = m->slaves; + q = first; do { struct net_device *slave = qdisc_dev(q); @@ -389,7 +434,7 @@ static int teql_master_open(struct net_device *dev) flags &= ~IFF_BROADCAST; if (!(slave->flags&IFF_MULTICAST)) flags &= ~IFF_MULTICAST; - } while ((q = NEXT_SLAVE(q)) != m->slaves); + } while ((q = rtnl_dereference(NEXT_SLAVE(q))) != first); m->dev->mtu = mtu; m->dev->flags = (m->dev->flags&~FMASK) | flags; @@ -417,14 +462,15 @@ static void teql_master_stats64(struct net_device *dev, static int teql_master_mtu(struct net_device *dev, int new_mtu) { struct teql_master *m = netdev_priv(dev); - struct Qdisc *q; + struct Qdisc *q, *first; - q = m->slaves; + first = rtnl_dereference(m->slaves); + q = first; if (q) { do { if (new_mtu > qdisc_dev(q)->mtu) return -EINVAL; - } while ((q = NEXT_SLAVE(q)) != m->slaves); + } while ((q = rtnl_dereference(NEXT_SLAVE(q))) != first); } WRITE_ONCE(dev->mtu, new_mtu); @@ -444,6 +490,7 @@ static __init void teql_master_setup(struct net_device *dev) struct teql_master *master = netdev_priv(dev); struct Qdisc_ops *ops = &master->qops; + spin_lock_init(&master->slaves_lock); master->dev = dev; ops->priv_size = sizeof(struct teql_sched_data); From 40cbfa3a28e0919469d1b086629bb3ce38a83593 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 29 Jun 2026 10:21:17 +0300 Subject: [PATCH 2010/2758] bridge: stp: Fix a potential use-after-free when deleting a bridge [ Upstream commit 2a00517db8de4be7df3d483b215c5544fb30a191 ] The three STP timers are not supposed to be armed while the bridge is administratively down. They are synchronously deactivated when the bridge is put administratively down and the various call sites check for 'IFF_UP' before arming them. This check is missing from br_topology_change_detection() and it is possible to engineer a situation in which the topology change timer is armed while the bridge is administratively down, resulting in a use-after-free [1] when the bridge is deleted. Fix by adding the missing check and for good measures synchronously shutdown the three timers when the bridge is deleted. [1] ODEBUG: free active (active state 0) object: ffff88811662b9b0 object type: timer_list hint: br_topology_change_timer_expired (net/bridge/br_stp_timer.c:120) WARNING: lib/debugobjects.c:629 at debug_print_object+0x1bc/0x450, CPU#9: ip/359 Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: Noam Rathaus Reported-by: Neil Young Acked-by: Nikolay Aleksandrov Signed-off-by: Ido Schimmel Reviewed-by: Breno Leitao Link: https://patch.msgid.link/20260629072117.497959-1-idosch@nvidia.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/bridge/br_if.c | 3 +++ net/bridge/br_stp.c | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index d39571e137444..ec773e4ab92ec 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -391,6 +391,9 @@ void br_dev_delete(struct net_device *dev, struct list_head *head) br_fdb_delete_by_port(br, NULL, 0, 1); + timer_shutdown_sync(&br->hello_timer); + timer_shutdown_sync(&br->topology_change_timer); + timer_shutdown_sync(&br->tcn_timer); cancel_delayed_work_sync(&br->gc_work); br_sysfs_delbr(br->dev); diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index 024210f954683..76a2df165ffc8 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -371,7 +371,8 @@ void br_topology_change_detection(struct net_bridge *br) { int isroot = br_is_root_bridge(br); - if (br->stp_enabled != BR_KERNEL_STP) + if (br->stp_enabled != BR_KERNEL_STP || + !(br->dev->flags & IFF_UP)) return; br_info(br, "topology change detected, %s\n", From 8a277a20258d4435973faa9ccd3ca863f2abd76a Mon Sep 17 00:00:00 2001 From: Viacheslav Bocharov Date: Tue, 30 Jun 2026 13:15:44 +0300 Subject: [PATCH 2011/2758] gpio: shared-proxy: always serialize with a sleeping mutex [ Upstream commit efecde8a254d1f207b75c5ebcfba2c51f4c771d9 ] The shared GPIO descriptor used either a mutex or a spinlock, chosen at runtime from the underlying chip's can_sleep: shared_desc->can_sleep = gpiod_cansleep(shared_desc->desc); ... if (can_sleep) mutex_lock(); else spin_lock_irqsave(); can_sleep describes only the value path (->get/->set). Under the same lock, however, the proxy may call gpiod_set_config() and gpiod_direction_*(), which can reach pinctrl paths that take a mutex (e.g. gpiod_set_config() -> gpiochip_generic_config() -> pinctrl_gpio_set_config()), independent of can_sleep. On a controller with non-sleeping MMIO value ops the descriptor lock was a spinlock, so the sleeping pinctrl call ran from atomic context. Reproduced on an Amlogic A113X board with the workaround from commit 28f240683871 ("pinctrl: meson: mark the GPIO controller as sleeping") reverted; the original Khadas VIM3 report hit the same path: BUG: sleeping function called from invalid context __mutex_lock pinctrl_get_device_gpio_range pinctrl_gpio_set_config gpiochip_generic_config gpiod_set_config gpio_shared_proxy_set_config <- voting spinlock held ... mmc_pwrseq_simple_probe The spinlock existed to take the value vote from atomic context, but the vote and the (possibly sleeping) control operations share the same state and lock, so this scheme cannot serialize config under a mutex and still offer atomic value access. Always serialize the shared descriptor with a mutex instead and mark the proxy a sleeping gpiochip, driving the underlying GPIO through the cansleep value accessors: those are valid for both sleeping and non-sleeping chips, so value access keeps working on fast controllers, at the cost of no longer being atomic. With every vote edge now driven through the cansleep value setter, gpio_shared_proxy_set_unlocked() no longer needs a per-call setter: drop its set_func callback and call gpiod_set_value_cansleep() directly. The shared direction_output path reaches it only once the line is already an output, so driving the value there is equivalent to re-issuing gpiod_direction_output(), without the redundant per-edge re-assertion of drive config and bias. This is observable: consumers gating on gpiod_cansleep() take their sleeping branch on a proxied GPIO (mmc-pwrseq-emmc skips its emergency-restart reset handler; its normal reset is unaffected), and consumers that reject sleeping GPIOs (pwm-gpio, ps2-gpio, ...) would fail to probe. Such atomic users do not share a pin through the proxy, whose purpose is voting on shared reset/enable lines. The same narrowing already applies on Amlogic since that workaround, and rockchip addressed the identical splat per-driver in commit 7ca497be0016 ("gpio: rockchip: Stop calling pinctrl for set_direction"); fixing the proxy addresses the locking error once, for every controller. The lock type was added by commit a060b8c511ab ("gpiolib: implement low-level, shared GPIO support"); the sleeping call under it arrived with the proxy driver. Fixes: e992d54c6f97 ("gpio: shared-proxy: implement the shared GPIO proxy driver") Reported-by: Marek Szyprowski Closes: https://lore.kernel.org/all/00107523-7737-4b92-a785-14ce4e93b8cb@samsung.com/ Signed-off-by: Viacheslav Bocharov Link: https://patch.msgid.link/20260630101545.800625-2-v@baodeep.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpio-shared-proxy.c | 76 ++++++++++++-------------------- drivers/gpio/gpiolib-shared.c | 9 +--- drivers/gpio/gpiolib-shared.h | 28 +----------- 3 files changed, 32 insertions(+), 81 deletions(-) diff --git a/drivers/gpio/gpio-shared-proxy.c b/drivers/gpio/gpio-shared-proxy.c index 6941e4be6cf18..10ca2ef77ef34 100644 --- a/drivers/gpio/gpio-shared-proxy.c +++ b/drivers/gpio/gpio-shared-proxy.c @@ -9,8 +9,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -24,15 +26,13 @@ struct gpio_shared_proxy_data { }; static int -gpio_shared_proxy_set_unlocked(struct gpio_shared_proxy_data *proxy, - int (*set_func)(struct gpio_desc *desc, int value), - int value) +gpio_shared_proxy_set_unlocked(struct gpio_shared_proxy_data *proxy, int value) { struct gpio_shared_desc *shared_desc = proxy->shared_desc; struct gpio_desc *desc = shared_desc->desc; int ret = 0; - gpio_shared_lockdep_assert(shared_desc); + lockdep_assert_held(&shared_desc->mutex); if (value) { /* User wants to set value to high. */ @@ -46,7 +46,7 @@ gpio_shared_proxy_set_unlocked(struct gpio_shared_proxy_data *proxy, * Current value is low, need to actually set value * to high. */ - ret = set_func(desc, 1); + ret = gpiod_set_value_cansleep(desc, 1); if (ret) goto out; } @@ -65,7 +65,7 @@ gpio_shared_proxy_set_unlocked(struct gpio_shared_proxy_data *proxy, /* We previously voted for high. */ if (shared_desc->highcnt == 1) { /* This is the last remaining vote for high, set value to low. */ - ret = set_func(desc, 0); + ret = gpiod_set_value_cansleep(desc, 0); if (ret) goto out; } @@ -89,7 +89,7 @@ static int gpio_shared_proxy_request(struct gpio_chip *gc, unsigned int offset) struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc); struct gpio_shared_desc *shared_desc = proxy->shared_desc; - guard(gpio_shared_desc_lock)(shared_desc); + guard(mutex)(&shared_desc->mutex); proxy->shared_desc->usecnt++; @@ -105,11 +105,10 @@ static void gpio_shared_proxy_free(struct gpio_chip *gc, unsigned int offset) struct gpio_shared_desc *shared_desc = proxy->shared_desc; int ret; - guard(gpio_shared_desc_lock)(shared_desc); + guard(mutex)(&shared_desc->mutex); if (proxy->voted_high) { - ret = gpio_shared_proxy_set_unlocked(proxy, - shared_desc->can_sleep ? gpiod_set_value_cansleep : gpiod_set_value, 0); + ret = gpio_shared_proxy_set_unlocked(proxy, 0); if (ret) dev_err(proxy->dev, "Failed to unset the shared GPIO value on release: %d\n", ret); @@ -129,7 +128,7 @@ static int gpio_shared_proxy_set_config(struct gpio_chip *gc, struct gpio_desc *desc = shared_desc->desc; int ret; - guard(gpio_shared_desc_lock)(shared_desc); + guard(mutex)(&shared_desc->mutex); if (shared_desc->usecnt > 1) { if (shared_desc->cfg != cfg) { @@ -157,7 +156,7 @@ static int gpio_shared_proxy_direction_input(struct gpio_chip *gc, struct gpio_desc *desc = shared_desc->desc; int dir; - guard(gpio_shared_desc_lock)(shared_desc); + guard(mutex)(&shared_desc->mutex); if (shared_desc->usecnt == 1) { dev_dbg(proxy->dev, @@ -187,7 +186,7 @@ static int gpio_shared_proxy_direction_output(struct gpio_chip *gc, struct gpio_desc *desc = shared_desc->desc; int ret, dir; - guard(gpio_shared_desc_lock)(shared_desc); + guard(mutex)(&shared_desc->mutex); if (shared_desc->usecnt == 1) { dev_dbg(proxy->dev, @@ -219,14 +218,7 @@ static int gpio_shared_proxy_direction_output(struct gpio_chip *gc, return -EPERM; } - return gpio_shared_proxy_set_unlocked(proxy, gpiod_direction_output, value); -} - -static int gpio_shared_proxy_get(struct gpio_chip *gc, unsigned int offset) -{ - struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc); - - return gpiod_get_value(proxy->shared_desc->desc); + return gpio_shared_proxy_set_unlocked(proxy, value); } static int gpio_shared_proxy_get_cansleep(struct gpio_chip *gc, @@ -237,29 +229,14 @@ static int gpio_shared_proxy_get_cansleep(struct gpio_chip *gc, return gpiod_get_value_cansleep(proxy->shared_desc->desc); } -static int gpio_shared_proxy_do_set(struct gpio_shared_proxy_data *proxy, - int (*set_func)(struct gpio_desc *desc, int value), - int value) -{ - guard(gpio_shared_desc_lock)(proxy->shared_desc); - - return gpio_shared_proxy_set_unlocked(proxy, set_func, value); -} - -static int gpio_shared_proxy_set(struct gpio_chip *gc, unsigned int offset, - int value) -{ - struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc); - - return gpio_shared_proxy_do_set(proxy, gpiod_set_value, value); -} - static int gpio_shared_proxy_set_cansleep(struct gpio_chip *gc, unsigned int offset, int value) { struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc); - return gpio_shared_proxy_do_set(proxy, gpiod_set_value_cansleep, value); + guard(mutex)(&proxy->shared_desc->mutex); + + return gpio_shared_proxy_set_unlocked(proxy, value); } static int gpio_shared_proxy_get_direction(struct gpio_chip *gc, @@ -302,20 +279,25 @@ static int gpio_shared_proxy_probe(struct auxiliary_device *adev, gc->label = dev_name(dev); gc->parent = dev; gc->owner = THIS_MODULE; - gc->can_sleep = shared_desc->can_sleep; + /* + * Under the descriptor mutex the proxy may call + * gpiod_set_config()/gpiod_direction_*(), which can reach pinctrl + * paths that take a mutex (e.g. gpiod_set_config() -> + * gpiochip_generic_config() -> pinctrl_gpio_set_config()), independent + * of the underlying chip's can_sleep. So the descriptor lock must be a + * mutex and the proxy gpiochip is therefore always sleeping; drive the + * underlying GPIO through the cansleep value accessors, which are valid + * for both sleeping and non-sleeping chips. + */ + gc->can_sleep = true; gc->request = gpio_shared_proxy_request; gc->free = gpio_shared_proxy_free; gc->set_config = gpio_shared_proxy_set_config; gc->direction_input = gpio_shared_proxy_direction_input; gc->direction_output = gpio_shared_proxy_direction_output; - if (gc->can_sleep) { - gc->set = gpio_shared_proxy_set_cansleep; - gc->get = gpio_shared_proxy_get_cansleep; - } else { - gc->set = gpio_shared_proxy_set; - gc->get = gpio_shared_proxy_get; - } + gc->set = gpio_shared_proxy_set_cansleep; + gc->get = gpio_shared_proxy_get_cansleep; gc->get_direction = gpio_shared_proxy_get_direction; gc->to_irq = gpio_shared_proxy_to_irq; diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c index de72776fb154f..495bd3d0ddf02 100644 --- a/drivers/gpio/gpiolib-shared.c +++ b/drivers/gpio/gpiolib-shared.c @@ -627,8 +627,7 @@ static void gpio_shared_release(struct kref *kref) shared_desc = entry->shared_desc; gpio_device_put(shared_desc->desc->gdev); - if (shared_desc->can_sleep) - mutex_destroy(&shared_desc->mutex); + mutex_destroy(&shared_desc->mutex); kfree(shared_desc); entry->shared_desc = NULL; } @@ -659,11 +658,7 @@ gpiod_shared_desc_create(struct gpio_shared_entry *entry) } shared_desc->desc = &gdev->descs[entry->offset]; - shared_desc->can_sleep = gpiod_cansleep(shared_desc->desc); - if (shared_desc->can_sleep) - mutex_init(&shared_desc->mutex); - else - spin_lock_init(&shared_desc->spinlock); + mutex_init(&shared_desc->mutex); return shared_desc; } diff --git a/drivers/gpio/gpiolib-shared.h b/drivers/gpio/gpiolib-shared.h index 15e72a8dcdb13..bbdc0ab7b647a 100644 --- a/drivers/gpio/gpiolib-shared.h +++ b/drivers/gpio/gpiolib-shared.h @@ -3,10 +3,7 @@ #ifndef __LINUX_GPIO_SHARED_H #define __LINUX_GPIO_SHARED_H -#include -#include #include -#include struct gpio_device; struct gpio_desc; @@ -42,35 +39,12 @@ static inline int gpio_shared_add_proxy_lookup(struct device *consumer, struct gpio_shared_desc { struct gpio_desc *desc; - bool can_sleep; unsigned long cfg; unsigned int usecnt; unsigned int highcnt; - union { - struct mutex mutex; - spinlock_t spinlock; - }; + struct mutex mutex; /* serializes all proxy operations on this descriptor */ }; struct gpio_shared_desc *devm_gpiod_shared_get(struct device *dev); -DEFINE_LOCK_GUARD_1(gpio_shared_desc_lock, struct gpio_shared_desc, - if (_T->lock->can_sleep) - mutex_lock(&_T->lock->mutex); - else - spin_lock_irqsave(&_T->lock->spinlock, _T->flags), - if (_T->lock->can_sleep) - mutex_unlock(&_T->lock->mutex); - else - spin_unlock_irqrestore(&_T->lock->spinlock, _T->flags), - unsigned long flags) - -static inline void gpio_shared_lockdep_assert(struct gpio_shared_desc *shared_desc) -{ - if (shared_desc->can_sleep) - lockdep_assert_held(&shared_desc->mutex); - else - lockdep_assert_held(&shared_desc->spinlock); -} - #endif /* __LINUX_GPIO_SHARED_H */ From 2946aa6c97ac0e0d777baef8c0e6d944da8724a3 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 25 Jun 2026 14:40:27 +0200 Subject: [PATCH 2012/2758] drm/panthor: Always use the IRQ-safe variant when acquiring the fence lock [ Upstream commit 778c57d624974e64535ef1c9d9b4d8e5066153f4 ] Since dma_fence objects can be shared with other subsystems, they may be accessed from hardirq context in those drivers, and we have to take that into account by also using the IRQ-safe variant when acquiring the lock. While at it, switch to the guard model. Fixes: de8548813824 ("drm/panthor: Add the scheduler logical block") Reported-by: sashiko-bot@kernel.org Closes: https://sashiko.dev/#/patchset/20260625-panthor-signal-from-irq-v5-0-8836a74e0ef9@collabora.com?part=11 Signed-off-by: Boris Brezillon Reviewed-by: Liviu Dudau Signed-off-by: Liviu Dudau Link: https://patch.msgid.link/20260625-panthor-misc-fixes-v1-1-b67ed973fea6@collabora.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/panthor/panthor_sched.c | 81 ++++++++++++------------- 1 file changed, 38 insertions(+), 43 deletions(-) diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c index 2fe04d0f0e3a8..d3e7f70fdd328 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.c +++ b/drivers/gpu/drm/panthor/panthor_sched.c @@ -1151,15 +1151,14 @@ queue_suspend_timeout_locked(struct panthor_queue *queue) static void queue_suspend_timeout(struct panthor_queue *queue) { - spin_lock(&queue->fence_ctx.lock); + guard(spinlock_irqsave)(&queue->fence_ctx.lock); queue_suspend_timeout_locked(queue); - spin_unlock(&queue->fence_ctx.lock); } static void queue_resume_timeout(struct panthor_queue *queue) { - spin_lock(&queue->fence_ctx.lock); + guard(spinlock_irqsave)(&queue->fence_ctx.lock); if (queue_timeout_is_suspended(queue)) { mod_delayed_work(queue->scheduler.timeout_wq, @@ -1168,8 +1167,6 @@ queue_resume_timeout(struct panthor_queue *queue) queue->timeout.remaining = MAX_SCHEDULE_TIMEOUT; } - - spin_unlock(&queue->fence_ctx.lock); } /** @@ -1542,7 +1539,7 @@ cs_slot_process_fault_event_locked(struct panthor_device *ptdev, u64 cs_extract = queue->iface.output->extract; struct panthor_job *job; - spin_lock(&queue->fence_ctx.lock); + guard(spinlock_irqsave)(&queue->fence_ctx.lock); list_for_each_entry(job, &queue->fence_ctx.in_flight_jobs, node) { if (cs_extract >= job->ringbuf.end) continue; @@ -1552,7 +1549,6 @@ cs_slot_process_fault_event_locked(struct panthor_device *ptdev, dma_fence_set_error(job->done_fence, -EINVAL); } - spin_unlock(&queue->fence_ctx.lock); } if (group) { @@ -2181,13 +2177,13 @@ group_term_post_processing(struct panthor_group *group) if (!queue) continue; - spin_lock(&queue->fence_ctx.lock); - list_for_each_entry_safe(job, tmp, &queue->fence_ctx.in_flight_jobs, node) { - list_move_tail(&job->node, &faulty_jobs); - dma_fence_set_error(job->done_fence, err); - dma_fence_signal_locked(job->done_fence); + scoped_guard(spinlock_irqsave, &queue->fence_ctx.lock) { + list_for_each_entry_safe(job, tmp, &queue->fence_ctx.in_flight_jobs, node) { + list_move_tail(&job->node, &faulty_jobs); + dma_fence_set_error(job->done_fence, err); + dma_fence_signal_locked(job->done_fence); + } } - spin_unlock(&queue->fence_ctx.lock); /* Manually update the syncobj seqno to unblock waiters. */ syncobj = group->syncobjs->kmap + (i * sizeof(*syncobj)); @@ -3046,39 +3042,39 @@ static bool queue_check_job_completion(struct panthor_queue *queue) LIST_HEAD(done_jobs); cookie = dma_fence_begin_signalling(); - spin_lock(&queue->fence_ctx.lock); - list_for_each_entry_safe(job, job_tmp, &queue->fence_ctx.in_flight_jobs, node) { - if (!syncobj) { - struct panthor_group *group = job->group; + scoped_guard(spinlock_irqsave, &queue->fence_ctx.lock) { + list_for_each_entry_safe(job, job_tmp, &queue->fence_ctx.in_flight_jobs, node) { + if (!syncobj) { + struct panthor_group *group = job->group; - syncobj = group->syncobjs->kmap + - (job->queue_idx * sizeof(*syncobj)); - } + syncobj = group->syncobjs->kmap + + (job->queue_idx * sizeof(*syncobj)); + } - if (syncobj->seqno < job->done_fence->seqno) - break; + if (syncobj->seqno < job->done_fence->seqno) + break; - list_move_tail(&job->node, &done_jobs); - dma_fence_signal_locked(job->done_fence); - } + list_move_tail(&job->node, &done_jobs); + dma_fence_signal_locked(job->done_fence); + } - if (list_empty(&queue->fence_ctx.in_flight_jobs)) { - /* If we have no job left, we cancel the timer, and reset remaining - * time to its default so it can be restarted next time - * queue_resume_timeout() is called. - */ - queue_suspend_timeout_locked(queue); + if (list_empty(&queue->fence_ctx.in_flight_jobs)) { + /* If we have no job left, we cancel the timer, and reset remaining + * time to its default so it can be restarted next time + * queue_resume_timeout() is called. + */ + queue_suspend_timeout_locked(queue); - /* If there's no job pending, we consider it progress to avoid a - * spurious timeout if the timeout handler and the sync update - * handler raced. - */ - progress = true; - } else if (!list_empty(&done_jobs)) { - queue_reset_timeout_locked(queue); - progress = true; + /* If there's no job pending, we consider it progress to avoid a + * spurious timeout if the timeout handler and the sync update + * handler raced. + */ + progress = true; + } else if (!list_empty(&done_jobs)) { + queue_reset_timeout_locked(queue); + progress = true; + } } - spin_unlock(&queue->fence_ctx.lock); dma_fence_end_signalling(cookie); list_for_each_entry_safe(job, job_tmp, &done_jobs, node) { @@ -3343,9 +3339,8 @@ queue_run_job(struct drm_sched_job *sched_job) job->ringbuf.end = job->ringbuf.start + (instrs.count * sizeof(u64)); panthor_job_get(&job->base); - spin_lock(&queue->fence_ctx.lock); - list_add_tail(&job->node, &queue->fence_ctx.in_flight_jobs); - spin_unlock(&queue->fence_ctx.lock); + scoped_guard(spinlock_irqsave, &queue->fence_ctx.lock) + list_add_tail(&job->node, &queue->fence_ctx.in_flight_jobs); /* Make sure the ring buffer is updated before the INSERT * register. From 752a08cfeeeac10adf961826831c719e7b7e87fb Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 25 Jun 2026 14:40:28 +0200 Subject: [PATCH 2013/2758] drm/panthor: Keep the reset work disabled until everything is initialized [ Upstream commit 1b8d771fb214e1f783d66caf13d35d7eda39a643 ] The reset work will sub-component reset helpers, which might not be ready if the reset happens during initialization, leading to NULL pointer dereferences or worse. Avoid that by keeping the reset work disabled while we're initializing those sub-components. Fixes: 5fe909cae118 ("drm/panthor: Add the device logical block") Reported-by: sashiko-bot@kernel.org Closes: https://sashiko.dev/#/patchset/20260625-panthor-signal-from-irq-v5-0-8836a74e0ef9@collabora.com?part=4 Signed-off-by: Boris Brezillon Reviewed-by: Liviu Dudau Signed-off-by: Liviu Dudau Link: https://patch.msgid.link/20260625-panthor-misc-fixes-v1-2-b67ed973fea6@collabora.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/panthor/panthor_device.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/panthor/panthor_device.c b/drivers/gpu/drm/panthor/panthor_device.c index 54fbb1aa07c52..55fbb201e89f0 100644 --- a/drivers/gpu/drm/panthor/panthor_device.c +++ b/drivers/gpu/drm/panthor/panthor_device.c @@ -231,6 +231,7 @@ int panthor_device_init(struct panthor_device *ptdev) *dummy_page_virt = 1; INIT_WORK(&ptdev->reset.work, panthor_device_reset_work); + disable_work(&ptdev->reset.work); ptdev->reset.wq = alloc_ordered_workqueue("panthor-reset-wq", 0); if (!ptdev->reset.wq) return -ENOMEM; @@ -305,6 +306,9 @@ int panthor_device_init(struct panthor_device *ptdev) panthor_gem_init(ptdev); + /* Now that everything is initialized, we can enable the reset work. */ + enable_work(&ptdev->reset.work); + /* ~3 frames */ pm_runtime_set_autosuspend_delay(ptdev->base.dev, 50); pm_runtime_use_autosuspend(ptdev->base.dev); From 053522ba615888e874adc04e675d9ed2e13f35af Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 25 Jun 2026 14:40:30 +0200 Subject: [PATCH 2014/2758] drm/panthor: Fix potential invalid pointer deref in group_process_tiler_oom() [ Upstream commit b39436d0ba1571dbcda69d20ec567344b3eecfc7 ] If heaps is an ERR_PTR(), panthor_heap_pool_put() will deref an invalid pointer. Make sure we set it to NULL in that case. Fixes: de8548813824 ("drm/panthor: Add the scheduler logical block") Reported-by: sashiko-bot@kernel.org Closes: https://sashiko.dev/#/patchset/20260625-panthor-signal-from-irq-v5-0-8836a74e0ef9@collabora.com?part=2 Signed-off-by: Boris Brezillon Reviewed-by: Liviu Dudau Signed-off-by: Liviu Dudau Link: https://patch.msgid.link/20260625-panthor-misc-fixes-v1-4-b67ed973fea6@collabora.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/panthor/panthor_sched.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c index d3e7f70fdd328..f249eaa0907a4 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.c +++ b/drivers/gpu/drm/panthor/panthor_sched.c @@ -1600,7 +1600,10 @@ static int group_process_tiler_oom(struct panthor_group *group, u32 cs_id) if (unlikely(csg_id < 0)) return 0; - if (IS_ERR(heaps) || frag_end > vt_end || vt_end >= vt_start) { + if (IS_ERR(heaps)) { + ret = -EINVAL; + heaps = NULL; + } else if (frag_end > vt_end || vt_end >= vt_start) { ret = -EINVAL; } else { /* We do the allocation without holding the scheduler lock to avoid From 85c6f80499e679aecca915810168cbe181df5b77 Mon Sep 17 00:00:00 2001 From: Karunika Choo Date: Mon, 27 Apr 2026 16:59:27 +0100 Subject: [PATCH 2015/2758] drm/panthor: Pass an iomem pointer to GPU register access helpers [ Upstream commit a8f5738779a930b4f318104298ce84bc10d43fbc ] Convert the Panthor register access helpers to take an iomem pointer instead of a panthor_device pointer. This makes the helpers usable with block-local registers instead of routing all accesses to go through ptdev->iomem. It is a preparatory change for splitting the register space by components and for moving callers away from cross-component register accesses. No functional change intended. v3: - Pick up R-bs from Liviu and Steve v2: - Pick up Ack from Boris. Reviewed-by: Steven Price Reviewed-by: Liviu Dudau Acked-by: Boris Brezillon Signed-off-by: Karunika Choo Tested-by: Boris Brezillon Signed-off-by: Liviu Dudau Link: https://patch.msgid.link/20260427155934.416502-2-karunika.choo@arm.com Stable-dep-of: fe4c05a59018 ("drm/panthor: Fix theoretical IOMEM access in suspended state") Signed-off-by: Sasha Levin --- drivers/gpu/drm/panthor/panthor_device.c | 2 +- drivers/gpu/drm/panthor/panthor_device.h | 78 ++++++++++++------------ drivers/gpu/drm/panthor/panthor_drv.c | 6 +- drivers/gpu/drm/panthor/panthor_fw.c | 22 +++---- drivers/gpu/drm/panthor/panthor_gpu.c | 42 ++++++------- drivers/gpu/drm/panthor/panthor_hw.c | 47 +++++++------- drivers/gpu/drm/panthor/panthor_mmu.c | 29 +++++---- drivers/gpu/drm/panthor/panthor_pwr.c | 61 +++++++++--------- drivers/gpu/drm/panthor/panthor_sched.c | 2 +- 9 files changed, 146 insertions(+), 143 deletions(-) diff --git a/drivers/gpu/drm/panthor/panthor_device.c b/drivers/gpu/drm/panthor/panthor_device.c index 55fbb201e89f0..00d1111ceaf17 100644 --- a/drivers/gpu/drm/panthor/panthor_device.c +++ b/drivers/gpu/drm/panthor/panthor_device.c @@ -42,7 +42,7 @@ static int panthor_gpu_coherency_init(struct panthor_device *ptdev) /* Check if the ACE-Lite coherency protocol is actually supported by the GPU. * ACE protocol has never been supported for command stream frontend GPUs. */ - if ((gpu_read(ptdev, GPU_COHERENCY_FEATURES) & + if ((gpu_read(ptdev->iomem, GPU_COHERENCY_FEATURES) & GPU_COHERENCY_PROT_BIT(ACE_LITE))) { ptdev->gpu_info.selected_coherency = GPU_COHERENCY_ACE_LITE; return 0; diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h index b6696f73a5367..519c3a4e9515e 100644 --- a/drivers/gpu/drm/panthor/panthor_device.h +++ b/drivers/gpu/drm/panthor/panthor_device.h @@ -432,7 +432,7 @@ static irqreturn_t panthor_ ## __name ## _irq_raw_handler(int irq, void *data) struct panthor_device *ptdev = pirq->ptdev; \ enum panthor_irq_state old_state; \ \ - if (!gpu_read(ptdev, __reg_prefix ## _INT_STAT)) \ + if (!gpu_read(ptdev->iomem, __reg_prefix ## _INT_STAT)) \ return IRQ_NONE; \ \ guard(spinlock_irqsave)(&pirq->mask_lock); \ @@ -442,7 +442,7 @@ static irqreturn_t panthor_ ## __name ## _irq_raw_handler(int irq, void *data) if (old_state != PANTHOR_IRQ_STATE_ACTIVE) \ return IRQ_NONE; \ \ - gpu_write(ptdev, __reg_prefix ## _INT_MASK, 0); \ + gpu_write(ptdev->iomem, __reg_prefix ## _INT_MASK, 0); \ return IRQ_WAKE_THREAD; \ } \ \ @@ -461,7 +461,7 @@ static irqreturn_t panthor_ ## __name ## _irq_threaded_handler(int irq, void *da * right before the HW event kicks in. TLDR; it's all expected races we're \ * covered for. \ */ \ - u32 status = gpu_read(ptdev, __reg_prefix ## _INT_RAWSTAT) & pirq->mask; \ + u32 status = gpu_read(ptdev->iomem, __reg_prefix ## _INT_RAWSTAT) & pirq->mask; \ \ if (!status) \ break; \ @@ -477,7 +477,7 @@ static irqreturn_t panthor_ ## __name ## _irq_threaded_handler(int irq, void *da PANTHOR_IRQ_STATE_PROCESSING, \ PANTHOR_IRQ_STATE_ACTIVE); \ if (old_state == PANTHOR_IRQ_STATE_PROCESSING) \ - gpu_write(ptdev, __reg_prefix ## _INT_MASK, pirq->mask); \ + gpu_write(ptdev->iomem, __reg_prefix ## _INT_MASK, pirq->mask); \ } \ \ return ret; \ @@ -487,7 +487,7 @@ static inline void panthor_ ## __name ## _irq_suspend(struct panthor_irq *pirq) { \ scoped_guard(spinlock_irqsave, &pirq->mask_lock) { \ atomic_set(&pirq->state, PANTHOR_IRQ_STATE_SUSPENDING); \ - gpu_write(pirq->ptdev, __reg_prefix ## _INT_MASK, 0); \ + gpu_write(pirq->ptdev->iomem, __reg_prefix ## _INT_MASK, 0); \ } \ synchronize_irq(pirq->irq); \ atomic_set(&pirq->state, PANTHOR_IRQ_STATE_SUSPENDED); \ @@ -498,8 +498,8 @@ static inline void panthor_ ## __name ## _irq_resume(struct panthor_irq *pirq) guard(spinlock_irqsave)(&pirq->mask_lock); \ \ atomic_set(&pirq->state, PANTHOR_IRQ_STATE_ACTIVE); \ - gpu_write(pirq->ptdev, __reg_prefix ## _INT_CLEAR, pirq->mask); \ - gpu_write(pirq->ptdev, __reg_prefix ## _INT_MASK, pirq->mask); \ + gpu_write(pirq->ptdev->iomem, __reg_prefix ## _INT_CLEAR, pirq->mask); \ + gpu_write(pirq->ptdev->iomem, __reg_prefix ## _INT_MASK, pirq->mask); \ } \ \ static int panthor_request_ ## __name ## _irq(struct panthor_device *ptdev, \ @@ -530,7 +530,7 @@ static inline void panthor_ ## __name ## _irq_enable_events(struct panthor_irq * * If the IRQ is suspended/suspending, the mask is restored at resume time. \ */ \ if (atomic_read(&pirq->state) == PANTHOR_IRQ_STATE_ACTIVE) \ - gpu_write(pirq->ptdev, __reg_prefix ## _INT_MASK, pirq->mask); \ + gpu_write(pirq->ptdev->iomem, __reg_prefix ## _INT_MASK, pirq->mask); \ } \ \ static inline void panthor_ ## __name ## _irq_disable_events(struct panthor_irq *pirq, u32 mask)\ @@ -544,80 +544,80 @@ static inline void panthor_ ## __name ## _irq_disable_events(struct panthor_irq * If the IRQ is suspended/suspending, the mask is restored at resume time. \ */ \ if (atomic_read(&pirq->state) == PANTHOR_IRQ_STATE_ACTIVE) \ - gpu_write(pirq->ptdev, __reg_prefix ## _INT_MASK, pirq->mask); \ + gpu_write(pirq->ptdev->iomem, __reg_prefix ## _INT_MASK, pirq->mask); \ } extern struct workqueue_struct *panthor_cleanup_wq; -static inline void gpu_write(struct panthor_device *ptdev, u32 reg, u32 data) +static inline void gpu_write(void __iomem *iomem, u32 reg, u32 data) { - writel(data, ptdev->iomem + reg); + writel(data, iomem + reg); } -static inline u32 gpu_read(struct panthor_device *ptdev, u32 reg) +static inline u32 gpu_read(void __iomem *iomem, u32 reg) { - return readl(ptdev->iomem + reg); + return readl(iomem + reg); } -static inline u32 gpu_read_relaxed(struct panthor_device *ptdev, u32 reg) +static inline u32 gpu_read_relaxed(void __iomem *iomem, u32 reg) { - return readl_relaxed(ptdev->iomem + reg); + return readl_relaxed(iomem + reg); } -static inline void gpu_write64(struct panthor_device *ptdev, u32 reg, u64 data) +static inline void gpu_write64(void __iomem *iomem, u32 reg, u64 data) { - gpu_write(ptdev, reg, lower_32_bits(data)); - gpu_write(ptdev, reg + 4, upper_32_bits(data)); + gpu_write(iomem, reg, lower_32_bits(data)); + gpu_write(iomem, reg + 4, upper_32_bits(data)); } -static inline u64 gpu_read64(struct panthor_device *ptdev, u32 reg) +static inline u64 gpu_read64(void __iomem *iomem, u32 reg) { - return (gpu_read(ptdev, reg) | ((u64)gpu_read(ptdev, reg + 4) << 32)); + return (gpu_read(iomem, reg) | ((u64)gpu_read(iomem, reg + 4) << 32)); } -static inline u64 gpu_read64_relaxed(struct panthor_device *ptdev, u32 reg) +static inline u64 gpu_read64_relaxed(void __iomem *iomem, u32 reg) { - return (gpu_read_relaxed(ptdev, reg) | - ((u64)gpu_read_relaxed(ptdev, reg + 4) << 32)); + return (gpu_read_relaxed(iomem, reg) | + ((u64)gpu_read_relaxed(iomem, reg + 4) << 32)); } -static inline u64 gpu_read64_counter(struct panthor_device *ptdev, u32 reg) +static inline u64 gpu_read64_counter(void __iomem *iomem, u32 reg) { u32 lo, hi1, hi2; do { - hi1 = gpu_read(ptdev, reg + 4); - lo = gpu_read(ptdev, reg); - hi2 = gpu_read(ptdev, reg + 4); + hi1 = gpu_read(iomem, reg + 4); + lo = gpu_read(iomem, reg); + hi2 = gpu_read(iomem, reg + 4); } while (hi1 != hi2); return lo | ((u64)hi2 << 32); } -#define gpu_read_poll_timeout(dev, reg, val, cond, delay_us, timeout_us) \ +#define gpu_read_poll_timeout(iomem, reg, val, cond, delay_us, timeout_us) \ read_poll_timeout(gpu_read, val, cond, delay_us, timeout_us, false, \ - dev, reg) + iomem, reg) -#define gpu_read_poll_timeout_atomic(dev, reg, val, cond, delay_us, \ +#define gpu_read_poll_timeout_atomic(iomem, reg, val, cond, delay_us, \ timeout_us) \ read_poll_timeout_atomic(gpu_read, val, cond, delay_us, timeout_us, \ - false, dev, reg) + false, iomem, reg) -#define gpu_read64_poll_timeout(dev, reg, val, cond, delay_us, timeout_us) \ +#define gpu_read64_poll_timeout(iomem, reg, val, cond, delay_us, timeout_us) \ read_poll_timeout(gpu_read64, val, cond, delay_us, timeout_us, false, \ - dev, reg) + iomem, reg) -#define gpu_read64_poll_timeout_atomic(dev, reg, val, cond, delay_us, \ +#define gpu_read64_poll_timeout_atomic(iomem, reg, val, cond, delay_us, \ timeout_us) \ read_poll_timeout_atomic(gpu_read64, val, cond, delay_us, timeout_us, \ - false, dev, reg) + false, iomem, reg) -#define gpu_read_relaxed_poll_timeout_atomic(dev, reg, val, cond, delay_us, \ +#define gpu_read_relaxed_poll_timeout_atomic(iomem, reg, val, cond, delay_us, \ timeout_us) \ read_poll_timeout_atomic(gpu_read_relaxed, val, cond, delay_us, \ - timeout_us, false, dev, reg) + timeout_us, false, iomem, reg) -#define gpu_read64_relaxed_poll_timeout(dev, reg, val, cond, delay_us, \ +#define gpu_read64_relaxed_poll_timeout(iomem, reg, val, cond, delay_us, \ timeout_us) \ read_poll_timeout(gpu_read64_relaxed, val, cond, delay_us, timeout_us, \ - false, dev, reg) + false, iomem, reg) #endif diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c index 87d27c3c14569..0433756ce224e 100644 --- a/drivers/gpu/drm/panthor/panthor_drv.c +++ b/drivers/gpu/drm/panthor/panthor_drv.c @@ -838,7 +838,7 @@ static int panthor_query_timestamp_info(struct panthor_device *ptdev, } if (flags & DRM_PANTHOR_TIMESTAMP_GPU_OFFSET) - arg->timestamp_offset = gpu_read64(ptdev, GPU_TIMESTAMP_OFFSET); + arg->timestamp_offset = gpu_read64(ptdev->iomem, GPU_TIMESTAMP_OFFSET); else arg->timestamp_offset = 0; @@ -853,7 +853,7 @@ static int panthor_query_timestamp_info(struct panthor_device *ptdev, query_start_time = 0; if (flags & DRM_PANTHOR_TIMESTAMP_GPU) - arg->current_timestamp = gpu_read64_counter(ptdev, GPU_TIMESTAMP); + arg->current_timestamp = gpu_read64_counter(ptdev->iomem, GPU_TIMESTAMP); else arg->current_timestamp = 0; @@ -869,7 +869,7 @@ static int panthor_query_timestamp_info(struct panthor_device *ptdev, } if (flags & DRM_PANTHOR_TIMESTAMP_GPU_CYCLE_COUNT) - arg->cycle_count = gpu_read64_counter(ptdev, GPU_CYCLE_COUNT); + arg->cycle_count = gpu_read64_counter(ptdev->iomem, GPU_CYCLE_COUNT); else arg->cycle_count = 0; diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c index 8886002e1d316..b53e3286fc2eb 100644 --- a/drivers/gpu/drm/panthor/panthor_fw.c +++ b/drivers/gpu/drm/panthor/panthor_fw.c @@ -1052,7 +1052,7 @@ static void panthor_fw_init_global_iface(struct panthor_device *ptdev) GLB_CFG_POWEROFF_TIMER | GLB_CFG_PROGRESS_TIMER); - gpu_write(ptdev, CSF_DOORBELL(CSF_GLB_DOORBELL_ID), 1); + gpu_write(ptdev->iomem, CSF_DOORBELL(CSF_GLB_DOORBELL_ID), 1); /* Kick the watchdog. */ mod_delayed_work(ptdev->reset.wq, &ptdev->fw->watchdog.ping_work, @@ -1067,7 +1067,7 @@ static void panthor_job_irq_handler(struct panthor_device *ptdev, u32 status) if (tracepoint_enabled(gpu_job_irq)) start = ktime_get_ns(); - gpu_write(ptdev, JOB_INT_CLEAR, status); + gpu_write(ptdev->iomem, JOB_INT_CLEAR, status); if (!ptdev->fw->booted && (status & JOB_INT_GLOBAL_IF)) ptdev->fw->booted = true; @@ -1095,13 +1095,13 @@ static int panthor_fw_start(struct panthor_device *ptdev) ptdev->fw->booted = false; panthor_job_irq_enable_events(&ptdev->fw->irq, ~0); panthor_job_irq_resume(&ptdev->fw->irq); - gpu_write(ptdev, MCU_CONTROL, MCU_CONTROL_AUTO); + gpu_write(ptdev->iomem, MCU_CONTROL, MCU_CONTROL_AUTO); if (!wait_event_timeout(ptdev->fw->req_waitqueue, ptdev->fw->booted, msecs_to_jiffies(1000))) { if (!ptdev->fw->booted && - !(gpu_read(ptdev, JOB_INT_STAT) & JOB_INT_GLOBAL_IF)) + !(gpu_read(ptdev->iomem, JOB_INT_STAT) & JOB_INT_GLOBAL_IF)) timedout = true; } @@ -1112,7 +1112,7 @@ static int panthor_fw_start(struct panthor_device *ptdev) [MCU_STATUS_HALT] = "halt", [MCU_STATUS_FATAL] = "fatal", }; - u32 status = gpu_read(ptdev, MCU_STATUS); + u32 status = gpu_read(ptdev->iomem, MCU_STATUS); drm_err(&ptdev->base, "Failed to boot MCU (status=%s)", status < ARRAY_SIZE(status_str) ? status_str[status] : "unknown"); @@ -1126,8 +1126,8 @@ static void panthor_fw_stop(struct panthor_device *ptdev) { u32 status; - gpu_write(ptdev, MCU_CONTROL, MCU_CONTROL_DISABLE); - if (gpu_read_poll_timeout(ptdev, MCU_STATUS, status, + gpu_write(ptdev->iomem, MCU_CONTROL, MCU_CONTROL_DISABLE); + if (gpu_read_poll_timeout(ptdev->iomem, MCU_STATUS, status, status == MCU_STATUS_DISABLED, 10, 100000)) drm_err(&ptdev->base, "Failed to stop MCU"); } @@ -1137,7 +1137,7 @@ static bool panthor_fw_mcu_halted(struct panthor_device *ptdev) struct panthor_fw_global_iface *glb_iface = panthor_fw_get_glb_iface(ptdev); bool halted; - halted = gpu_read(ptdev, MCU_STATUS) == MCU_STATUS_HALT; + halted = gpu_read(ptdev->iomem, MCU_STATUS) == MCU_STATUS_HALT; if (panthor_fw_has_glb_state(ptdev)) halted &= (GLB_STATE_GET(glb_iface->output->ack) == GLB_STATE_HALT); @@ -1154,7 +1154,7 @@ static void panthor_fw_halt_mcu(struct panthor_device *ptdev) else panthor_fw_update_reqs(glb_iface, req, GLB_HALT, GLB_HALT); - gpu_write(ptdev, CSF_DOORBELL(CSF_GLB_DOORBELL_ID), 1); + gpu_write(ptdev->iomem, CSF_DOORBELL(CSF_GLB_DOORBELL_ID), 1); } static bool panthor_fw_wait_mcu_halted(struct panthor_device *ptdev) @@ -1412,7 +1412,7 @@ void panthor_fw_ring_csg_doorbells(struct panthor_device *ptdev, u32 csg_mask) struct panthor_fw_global_iface *glb_iface = panthor_fw_get_glb_iface(ptdev); panthor_fw_toggle_reqs(glb_iface, doorbell_req, doorbell_ack, csg_mask); - gpu_write(ptdev, CSF_DOORBELL(CSF_GLB_DOORBELL_ID), 1); + gpu_write(ptdev->iomem, CSF_DOORBELL(CSF_GLB_DOORBELL_ID), 1); } static void panthor_fw_ping_work(struct work_struct *work) @@ -1427,7 +1427,7 @@ static void panthor_fw_ping_work(struct work_struct *work) return; panthor_fw_toggle_reqs(glb_iface, req, ack, GLB_PING); - gpu_write(ptdev, CSF_DOORBELL(CSF_GLB_DOORBELL_ID), 1); + gpu_write(ptdev->iomem, CSF_DOORBELL(CSF_GLB_DOORBELL_ID), 1); ret = panthor_fw_glb_wait_acks(ptdev, GLB_PING, &acked, 100); if (ret) { diff --git a/drivers/gpu/drm/panthor/panthor_gpu.c b/drivers/gpu/drm/panthor/panthor_gpu.c index 2ab444ee8c710..bdb72cebccb3f 100644 --- a/drivers/gpu/drm/panthor/panthor_gpu.c +++ b/drivers/gpu/drm/panthor/panthor_gpu.c @@ -56,7 +56,7 @@ struct panthor_gpu { static void panthor_gpu_coherency_set(struct panthor_device *ptdev) { - gpu_write(ptdev, GPU_COHERENCY_PROTOCOL, + gpu_write(ptdev->iomem, GPU_COHERENCY_PROTOCOL, ptdev->gpu_info.selected_coherency); } @@ -75,26 +75,26 @@ static void panthor_gpu_l2_config_set(struct panthor_device *ptdev) } for (i = 0; i < ARRAY_SIZE(data->asn_hash); i++) - gpu_write(ptdev, GPU_ASN_HASH(i), data->asn_hash[i]); + gpu_write(ptdev->iomem, GPU_ASN_HASH(i), data->asn_hash[i]); - l2_config = gpu_read(ptdev, GPU_L2_CONFIG); + l2_config = gpu_read(ptdev->iomem, GPU_L2_CONFIG); l2_config |= GPU_L2_CONFIG_ASN_HASH_ENABLE; - gpu_write(ptdev, GPU_L2_CONFIG, l2_config); + gpu_write(ptdev->iomem, GPU_L2_CONFIG, l2_config); } static void panthor_gpu_irq_handler(struct panthor_device *ptdev, u32 status) { - gpu_write(ptdev, GPU_INT_CLEAR, status); + gpu_write(ptdev->iomem, GPU_INT_CLEAR, status); if (tracepoint_enabled(gpu_power_status) && (status & GPU_POWER_INTERRUPTS_MASK)) trace_gpu_power_status(ptdev->base.dev, - gpu_read64(ptdev, SHADER_READY), - gpu_read64(ptdev, TILER_READY), - gpu_read64(ptdev, L2_READY)); + gpu_read64(ptdev->iomem, SHADER_READY), + gpu_read64(ptdev->iomem, TILER_READY), + gpu_read64(ptdev->iomem, L2_READY)); if (status & GPU_IRQ_FAULT) { - u32 fault_status = gpu_read(ptdev, GPU_FAULT_STATUS); - u64 address = gpu_read64(ptdev, GPU_FAULT_ADDR); + u32 fault_status = gpu_read(ptdev->iomem, GPU_FAULT_STATUS); + u64 address = gpu_read64(ptdev->iomem, GPU_FAULT_ADDR); drm_warn(&ptdev->base, "GPU Fault 0x%08x (%s) at 0x%016llx\n", fault_status, panthor_exception_name(ptdev, fault_status & 0xFF), @@ -204,7 +204,7 @@ int panthor_gpu_block_power_off(struct panthor_device *ptdev, u32 val; int ret; - ret = gpu_read64_relaxed_poll_timeout(ptdev, pwrtrans_reg, val, + ret = gpu_read64_relaxed_poll_timeout(ptdev->iomem, pwrtrans_reg, val, !(mask & val), 100, timeout_us); if (ret) { drm_err(&ptdev->base, @@ -213,9 +213,9 @@ int panthor_gpu_block_power_off(struct panthor_device *ptdev, return ret; } - gpu_write64(ptdev, pwroff_reg, mask); + gpu_write64(ptdev->iomem, pwroff_reg, mask); - ret = gpu_read64_relaxed_poll_timeout(ptdev, pwrtrans_reg, val, + ret = gpu_read64_relaxed_poll_timeout(ptdev->iomem, pwrtrans_reg, val, !(mask & val), 100, timeout_us); if (ret) { drm_err(&ptdev->base, @@ -247,7 +247,7 @@ int panthor_gpu_block_power_on(struct panthor_device *ptdev, u32 val; int ret; - ret = gpu_read64_relaxed_poll_timeout(ptdev, pwrtrans_reg, val, + ret = gpu_read64_relaxed_poll_timeout(ptdev->iomem, pwrtrans_reg, val, !(mask & val), 100, timeout_us); if (ret) { drm_err(&ptdev->base, @@ -256,9 +256,9 @@ int panthor_gpu_block_power_on(struct panthor_device *ptdev, return ret; } - gpu_write64(ptdev, pwron_reg, mask); + gpu_write64(ptdev->iomem, pwron_reg, mask); - ret = gpu_read64_relaxed_poll_timeout(ptdev, rdy_reg, val, + ret = gpu_read64_relaxed_poll_timeout(ptdev->iomem, rdy_reg, val, (mask & val) == val, 100, timeout_us); if (ret) { @@ -326,7 +326,7 @@ int panthor_gpu_flush_caches(struct panthor_device *ptdev, spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags); if (!(ptdev->gpu->pending_reqs & GPU_IRQ_CLEAN_CACHES_COMPLETED)) { ptdev->gpu->pending_reqs |= GPU_IRQ_CLEAN_CACHES_COMPLETED; - gpu_write(ptdev, GPU_CMD, GPU_FLUSH_CACHES(l2, lsc, other)); + gpu_write(ptdev->iomem, GPU_CMD, GPU_FLUSH_CACHES(l2, lsc, other)); } else { ret = -EIO; } @@ -340,7 +340,7 @@ int panthor_gpu_flush_caches(struct panthor_device *ptdev, msecs_to_jiffies(100))) { spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags); if ((ptdev->gpu->pending_reqs & GPU_IRQ_CLEAN_CACHES_COMPLETED) != 0 && - !(gpu_read(ptdev, GPU_INT_RAWSTAT) & GPU_IRQ_CLEAN_CACHES_COMPLETED)) + !(gpu_read(ptdev->iomem, GPU_INT_RAWSTAT) & GPU_IRQ_CLEAN_CACHES_COMPLETED)) ret = -ETIMEDOUT; else ptdev->gpu->pending_reqs &= ~GPU_IRQ_CLEAN_CACHES_COMPLETED; @@ -370,8 +370,8 @@ int panthor_gpu_soft_reset(struct panthor_device *ptdev) if (!drm_WARN_ON(&ptdev->base, ptdev->gpu->pending_reqs & GPU_IRQ_RESET_COMPLETED)) { ptdev->gpu->pending_reqs |= GPU_IRQ_RESET_COMPLETED; - gpu_write(ptdev, GPU_INT_CLEAR, GPU_IRQ_RESET_COMPLETED); - gpu_write(ptdev, GPU_CMD, GPU_SOFT_RESET); + gpu_write(ptdev->iomem, GPU_INT_CLEAR, GPU_IRQ_RESET_COMPLETED); + gpu_write(ptdev->iomem, GPU_CMD, GPU_SOFT_RESET); } spin_unlock_irqrestore(&ptdev->gpu->reqs_lock, flags); @@ -380,7 +380,7 @@ int panthor_gpu_soft_reset(struct panthor_device *ptdev) msecs_to_jiffies(100))) { spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags); if ((ptdev->gpu->pending_reqs & GPU_IRQ_RESET_COMPLETED) != 0 && - !(gpu_read(ptdev, GPU_INT_RAWSTAT) & GPU_IRQ_RESET_COMPLETED)) + !(gpu_read(ptdev->iomem, GPU_INT_RAWSTAT) & GPU_IRQ_RESET_COMPLETED)) timedout = true; else ptdev->gpu->pending_reqs &= ~GPU_IRQ_RESET_COMPLETED; diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c index d135aa6724fa5..9309d09382124 100644 --- a/drivers/gpu/drm/panthor/panthor_hw.c +++ b/drivers/gpu/drm/panthor/panthor_hw.c @@ -194,35 +194,38 @@ static int panthor_gpu_info_init(struct panthor_device *ptdev) { unsigned int i; - ptdev->gpu_info.csf_id = gpu_read(ptdev, GPU_CSF_ID); - ptdev->gpu_info.gpu_rev = gpu_read(ptdev, GPU_REVID); - ptdev->gpu_info.core_features = gpu_read(ptdev, GPU_CORE_FEATURES); - ptdev->gpu_info.l2_features = gpu_read(ptdev, GPU_L2_FEATURES); - ptdev->gpu_info.tiler_features = gpu_read(ptdev, GPU_TILER_FEATURES); - ptdev->gpu_info.mem_features = gpu_read(ptdev, GPU_MEM_FEATURES); - ptdev->gpu_info.mmu_features = gpu_read(ptdev, GPU_MMU_FEATURES); - ptdev->gpu_info.thread_features = gpu_read(ptdev, GPU_THREAD_FEATURES); - ptdev->gpu_info.max_threads = gpu_read(ptdev, GPU_THREAD_MAX_THREADS); - ptdev->gpu_info.thread_max_workgroup_size = gpu_read(ptdev, GPU_THREAD_MAX_WORKGROUP_SIZE); - ptdev->gpu_info.thread_max_barrier_size = gpu_read(ptdev, GPU_THREAD_MAX_BARRIER_SIZE); - ptdev->gpu_info.coherency_features = gpu_read(ptdev, GPU_COHERENCY_FEATURES); + ptdev->gpu_info.csf_id = gpu_read(ptdev->iomem, GPU_CSF_ID); + ptdev->gpu_info.gpu_rev = gpu_read(ptdev->iomem, GPU_REVID); + ptdev->gpu_info.core_features = gpu_read(ptdev->iomem, GPU_CORE_FEATURES); + ptdev->gpu_info.l2_features = gpu_read(ptdev->iomem, GPU_L2_FEATURES); + ptdev->gpu_info.tiler_features = gpu_read(ptdev->iomem, GPU_TILER_FEATURES); + ptdev->gpu_info.mem_features = gpu_read(ptdev->iomem, GPU_MEM_FEATURES); + ptdev->gpu_info.mmu_features = gpu_read(ptdev->iomem, GPU_MMU_FEATURES); + ptdev->gpu_info.thread_features = gpu_read(ptdev->iomem, GPU_THREAD_FEATURES); + ptdev->gpu_info.max_threads = gpu_read(ptdev->iomem, GPU_THREAD_MAX_THREADS); + ptdev->gpu_info.thread_max_workgroup_size = + gpu_read(ptdev->iomem, GPU_THREAD_MAX_WORKGROUP_SIZE); + ptdev->gpu_info.thread_max_barrier_size = + gpu_read(ptdev->iomem, GPU_THREAD_MAX_BARRIER_SIZE); + ptdev->gpu_info.coherency_features = gpu_read(ptdev->iomem, GPU_COHERENCY_FEATURES); for (i = 0; i < 4; i++) - ptdev->gpu_info.texture_features[i] = gpu_read(ptdev, GPU_TEXTURE_FEATURES(i)); + ptdev->gpu_info.texture_features[i] = + gpu_read(ptdev->iomem, GPU_TEXTURE_FEATURES(i)); - ptdev->gpu_info.as_present = gpu_read(ptdev, GPU_AS_PRESENT); + ptdev->gpu_info.as_present = gpu_read(ptdev->iomem, GPU_AS_PRESENT); /* Introduced in arch 11.x */ - ptdev->gpu_info.gpu_features = gpu_read64(ptdev, GPU_FEATURES); + ptdev->gpu_info.gpu_features = gpu_read64(ptdev->iomem, GPU_FEATURES); if (panthor_hw_has_pwr_ctrl(ptdev)) { /* Introduced in arch 14.x */ - ptdev->gpu_info.l2_present = gpu_read64(ptdev, PWR_L2_PRESENT); - ptdev->gpu_info.tiler_present = gpu_read64(ptdev, PWR_TILER_PRESENT); - ptdev->gpu_info.shader_present = gpu_read64(ptdev, PWR_SHADER_PRESENT); + ptdev->gpu_info.l2_present = gpu_read64(ptdev->iomem, PWR_L2_PRESENT); + ptdev->gpu_info.tiler_present = gpu_read64(ptdev->iomem, PWR_TILER_PRESENT); + ptdev->gpu_info.shader_present = gpu_read64(ptdev->iomem, PWR_SHADER_PRESENT); } else { - ptdev->gpu_info.shader_present = gpu_read64(ptdev, GPU_SHADER_PRESENT); - ptdev->gpu_info.tiler_present = gpu_read64(ptdev, GPU_TILER_PRESENT); - ptdev->gpu_info.l2_present = gpu_read64(ptdev, GPU_L2_PRESENT); + ptdev->gpu_info.shader_present = gpu_read64(ptdev->iomem, GPU_SHADER_PRESENT); + ptdev->gpu_info.tiler_present = gpu_read64(ptdev->iomem, GPU_TILER_PRESENT); + ptdev->gpu_info.l2_present = gpu_read64(ptdev->iomem, GPU_L2_PRESENT); } return overload_shader_present(ptdev); @@ -287,7 +290,7 @@ static int panthor_hw_bind_device(struct panthor_device *ptdev) static int panthor_hw_gpu_id_init(struct panthor_device *ptdev) { - ptdev->gpu_info.gpu_id = gpu_read(ptdev, GPU_ID); + ptdev->gpu_info.gpu_id = gpu_read(ptdev->iomem, GPU_ID); if (!ptdev->gpu_info.gpu_id) return -ENXIO; diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c index 75d98dad7b1dd..9c6d1fe785cc1 100644 --- a/drivers/gpu/drm/panthor/panthor_mmu.c +++ b/drivers/gpu/drm/panthor/panthor_mmu.c @@ -507,9 +507,8 @@ static int wait_ready(struct panthor_device *ptdev, u32 as_nr) /* Wait for the MMU status to indicate there is no active command, in * case one is pending. */ - ret = gpu_read_relaxed_poll_timeout_atomic(ptdev, AS_STATUS(as_nr), val, - !(val & AS_STATUS_AS_ACTIVE), - 10, 100000); + ret = gpu_read_relaxed_poll_timeout_atomic(ptdev->iomem, AS_STATUS(as_nr), val, + !(val & AS_STATUS_AS_ACTIVE), 10, 100000); if (ret) { panthor_device_schedule_reset(ptdev); @@ -526,7 +525,7 @@ static int as_send_cmd_and_wait(struct panthor_device *ptdev, u32 as_nr, u32 cmd /* write AS_COMMAND when MMU is ready to accept another command */ status = wait_ready(ptdev, as_nr); if (!status) { - gpu_write(ptdev, AS_COMMAND(as_nr), cmd); + gpu_write(ptdev->iomem, AS_COMMAND(as_nr), cmd); status = wait_ready(ptdev, as_nr); } @@ -577,9 +576,9 @@ static int panthor_mmu_as_enable(struct panthor_device *ptdev, u32 as_nr, panthor_mmu_irq_enable_events(&ptdev->mmu->irq, panthor_mmu_as_fault_mask(ptdev, as_nr)); - gpu_write64(ptdev, AS_TRANSTAB(as_nr), transtab); - gpu_write64(ptdev, AS_MEMATTR(as_nr), memattr); - gpu_write64(ptdev, AS_TRANSCFG(as_nr), transcfg); + gpu_write64(ptdev->iomem, AS_TRANSTAB(as_nr), transtab); + gpu_write64(ptdev->iomem, AS_MEMATTR(as_nr), memattr); + gpu_write64(ptdev->iomem, AS_TRANSCFG(as_nr), transcfg); return as_send_cmd_and_wait(ptdev, as_nr, AS_COMMAND_UPDATE); } @@ -614,9 +613,9 @@ static int panthor_mmu_as_disable(struct panthor_device *ptdev, u32 as_nr, if (recycle_slot) return 0; - gpu_write64(ptdev, AS_TRANSTAB(as_nr), 0); - gpu_write64(ptdev, AS_MEMATTR(as_nr), 0); - gpu_write64(ptdev, AS_TRANSCFG(as_nr), AS_TRANSCFG_ADRMODE_UNMAPPED); + gpu_write64(ptdev->iomem, AS_TRANSTAB(as_nr), 0); + gpu_write64(ptdev->iomem, AS_MEMATTR(as_nr), 0); + gpu_write64(ptdev->iomem, AS_TRANSCFG(as_nr), AS_TRANSCFG_ADRMODE_UNMAPPED); return as_send_cmd_and_wait(ptdev, as_nr, AS_COMMAND_UPDATE); } @@ -759,7 +758,7 @@ int panthor_vm_active(struct panthor_vm *vm) */ fault_mask = panthor_mmu_as_fault_mask(ptdev, as); if (ptdev->mmu->as.faulty_mask & fault_mask) { - gpu_write(ptdev, MMU_INT_CLEAR, fault_mask); + gpu_write(ptdev->iomem, MMU_INT_CLEAR, fault_mask); ptdev->mmu->as.faulty_mask &= ~fault_mask; } @@ -1670,7 +1669,7 @@ static int panthor_vm_lock_region(struct panthor_vm *vm, u64 start, u64 size) mutex_lock(&ptdev->mmu->as.slots_lock); if (vm->as.id >= 0 && size) { /* Lock the region that needs to be updated */ - gpu_write64(ptdev, AS_LOCKADDR(vm->as.id), + gpu_write64(ptdev->iomem, AS_LOCKADDR(vm->as.id), pack_region_range(ptdev, &start, &size)); /* If the lock succeeded, update the locked_region info. */ @@ -1731,8 +1730,8 @@ static void panthor_mmu_irq_handler(struct panthor_device *ptdev, u32 status) u32 access_type; u32 source_id; - fault_status = gpu_read(ptdev, AS_FAULTSTATUS(as)); - addr = gpu_read64(ptdev, AS_FAULTADDRESS(as)); + fault_status = gpu_read(ptdev->iomem, AS_FAULTSTATUS(as)); + addr = gpu_read64(ptdev->iomem, AS_FAULTADDRESS(as)); /* decode the fault status */ exception_type = fault_status & 0xFF; @@ -1763,7 +1762,7 @@ static void panthor_mmu_irq_handler(struct panthor_device *ptdev, u32 status) * Note that COMPLETED irqs are never cleared, but this is fine * because they are always masked. */ - gpu_write(ptdev, MMU_INT_CLEAR, mask); + gpu_write(ptdev->iomem, MMU_INT_CLEAR, mask); if (ptdev->mmu->as.slots[as].vm) ptdev->mmu->as.slots[as].vm->unhandled_fault = true; diff --git a/drivers/gpu/drm/panthor/panthor_pwr.c b/drivers/gpu/drm/panthor/panthor_pwr.c index ed3b2b4479ca3..b77c85ad733af 100644 --- a/drivers/gpu/drm/panthor/panthor_pwr.c +++ b/drivers/gpu/drm/panthor/panthor_pwr.c @@ -55,7 +55,7 @@ struct panthor_pwr { static void panthor_pwr_irq_handler(struct panthor_device *ptdev, u32 status) { spin_lock(&ptdev->pwr->reqs_lock); - gpu_write(ptdev, PWR_INT_CLEAR, status); + gpu_write(ptdev->iomem, PWR_INT_CLEAR, status); if (unlikely(status & PWR_IRQ_COMMAND_NOT_ALLOWED)) drm_err(&ptdev->base, "PWR_IRQ: COMMAND_NOT_ALLOWED"); @@ -74,14 +74,14 @@ PANTHOR_IRQ_HANDLER(pwr, PWR, panthor_pwr_irq_handler); static void panthor_pwr_write_command(struct panthor_device *ptdev, u32 command, u64 args) { if (args) - gpu_write64(ptdev, PWR_CMDARG, args); + gpu_write64(ptdev->iomem, PWR_CMDARG, args); - gpu_write(ptdev, PWR_COMMAND, command); + gpu_write(ptdev->iomem, PWR_COMMAND, command); } static bool reset_irq_raised(struct panthor_device *ptdev) { - return gpu_read(ptdev, PWR_INT_RAWSTAT) & PWR_IRQ_RESET_COMPLETED; + return gpu_read(ptdev->iomem, PWR_INT_RAWSTAT) & PWR_IRQ_RESET_COMPLETED; } static bool reset_pending(struct panthor_device *ptdev) @@ -96,7 +96,7 @@ static int panthor_pwr_reset(struct panthor_device *ptdev, u32 reset_cmd) drm_WARN(&ptdev->base, 1, "Reset already pending"); } else { ptdev->pwr->pending_reqs |= PWR_IRQ_RESET_COMPLETED; - gpu_write(ptdev, PWR_INT_CLEAR, PWR_IRQ_RESET_COMPLETED); + gpu_write(ptdev->iomem, PWR_INT_CLEAR, PWR_IRQ_RESET_COMPLETED); panthor_pwr_write_command(ptdev, reset_cmd, 0); } } @@ -185,7 +185,7 @@ static int panthor_pwr_domain_wait_transition(struct panthor_device *ptdev, u32 u64 val; int ret = 0; - ret = gpu_read64_poll_timeout(ptdev, pwrtrans_reg, val, !(PWR_ALL_CORES_MASK & val), 100, + ret = gpu_read64_poll_timeout(ptdev->iomem, pwrtrans_reg, val, !(PWR_ALL_CORES_MASK & val), 100, timeout_us); if (ret) { drm_err(&ptdev->base, "%s domain power in transition, pwrtrans(0x%llx)", @@ -198,17 +198,17 @@ static int panthor_pwr_domain_wait_transition(struct panthor_device *ptdev, u32 static void panthor_pwr_debug_info_show(struct panthor_device *ptdev) { - drm_info(&ptdev->base, "GPU_FEATURES: 0x%016llx", gpu_read64(ptdev, GPU_FEATURES)); - drm_info(&ptdev->base, "PWR_STATUS: 0x%016llx", gpu_read64(ptdev, PWR_STATUS)); - drm_info(&ptdev->base, "L2_PRESENT: 0x%016llx", gpu_read64(ptdev, PWR_L2_PRESENT)); - drm_info(&ptdev->base, "L2_PWRTRANS: 0x%016llx", gpu_read64(ptdev, PWR_L2_PWRTRANS)); - drm_info(&ptdev->base, "L2_READY: 0x%016llx", gpu_read64(ptdev, PWR_L2_READY)); - drm_info(&ptdev->base, "TILER_PRESENT: 0x%016llx", gpu_read64(ptdev, PWR_TILER_PRESENT)); - drm_info(&ptdev->base, "TILER_PWRTRANS: 0x%016llx", gpu_read64(ptdev, PWR_TILER_PWRTRANS)); - drm_info(&ptdev->base, "TILER_READY: 0x%016llx", gpu_read64(ptdev, PWR_TILER_READY)); - drm_info(&ptdev->base, "SHADER_PRESENT: 0x%016llx", gpu_read64(ptdev, PWR_SHADER_PRESENT)); - drm_info(&ptdev->base, "SHADER_PWRTRANS: 0x%016llx", gpu_read64(ptdev, PWR_SHADER_PWRTRANS)); - drm_info(&ptdev->base, "SHADER_READY: 0x%016llx", gpu_read64(ptdev, PWR_SHADER_READY)); + drm_info(&ptdev->base, "GPU_FEATURES: 0x%016llx", gpu_read64(ptdev->iomem, GPU_FEATURES)); + drm_info(&ptdev->base, "PWR_STATUS: 0x%016llx", gpu_read64(ptdev->iomem, PWR_STATUS)); + drm_info(&ptdev->base, "L2_PRESENT: 0x%016llx", gpu_read64(ptdev->iomem, PWR_L2_PRESENT)); + drm_info(&ptdev->base, "L2_PWRTRANS: 0x%016llx", gpu_read64(ptdev->iomem, PWR_L2_PWRTRANS)); + drm_info(&ptdev->base, "L2_READY: 0x%016llx", gpu_read64(ptdev->iomem, PWR_L2_READY)); + drm_info(&ptdev->base, "TILER_PRESENT: 0x%016llx", gpu_read64(ptdev->iomem, PWR_TILER_PRESENT)); + drm_info(&ptdev->base, "TILER_PWRTRANS: 0x%016llx", gpu_read64(ptdev->iomem, PWR_TILER_PWRTRANS)); + drm_info(&ptdev->base, "TILER_READY: 0x%016llx", gpu_read64(ptdev->iomem, PWR_TILER_READY)); + drm_info(&ptdev->base, "SHADER_PRESENT: 0x%016llx", gpu_read64(ptdev->iomem, PWR_SHADER_PRESENT)); + drm_info(&ptdev->base, "SHADER_PWRTRANS: 0x%016llx", gpu_read64(ptdev->iomem, PWR_SHADER_PWRTRANS)); + drm_info(&ptdev->base, "SHADER_READY: 0x%016llx", gpu_read64(ptdev->iomem, PWR_SHADER_READY)); } static int panthor_pwr_domain_transition(struct panthor_device *ptdev, u32 cmd, u32 domain, @@ -240,13 +240,13 @@ static int panthor_pwr_domain_transition(struct panthor_device *ptdev, u32 cmd, return ret; /* domain already in target state, return early */ - if ((gpu_read64(ptdev, ready_reg) & mask) == expected_val) + if ((gpu_read64(ptdev->iomem, ready_reg) & mask) == expected_val) return 0; panthor_pwr_write_command(ptdev, pwr_cmd, mask); - ret = gpu_read64_poll_timeout(ptdev, ready_reg, val, (mask & val) == expected_val, 100, - timeout_us); + ret = gpu_read64_poll_timeout(ptdev->iomem, ready_reg, val, (mask & val) == expected_val, + 100, timeout_us); if (ret) { drm_err(&ptdev->base, "timeout waiting on %s power domain transition, cmd(0x%x), arg(0x%llx)", @@ -279,7 +279,7 @@ static int panthor_pwr_domain_transition(struct panthor_device *ptdev, u32 cmd, static int retract_domain(struct panthor_device *ptdev, u32 domain) { const u32 pwr_cmd = PWR_COMMAND_DEF(PWR_COMMAND_RETRACT, domain, 0); - const u64 pwr_status = gpu_read64(ptdev, PWR_STATUS); + const u64 pwr_status = gpu_read64(ptdev->iomem, PWR_STATUS); const u64 delegated_mask = PWR_STATUS_DOMAIN_DELEGATED(domain); const u64 allow_mask = PWR_STATUS_DOMAIN_ALLOWED(domain); u64 val; @@ -288,8 +288,9 @@ static int retract_domain(struct panthor_device *ptdev, u32 domain) if (drm_WARN_ON(&ptdev->base, domain == PWR_COMMAND_DOMAIN_L2)) return -EPERM; - ret = gpu_read64_poll_timeout(ptdev, PWR_STATUS, val, !(PWR_STATUS_RETRACT_PENDING & val), - 0, PWR_RETRACT_TIMEOUT_US); + ret = gpu_read64_poll_timeout(ptdev->iomem, PWR_STATUS, val, + !(PWR_STATUS_RETRACT_PENDING & val), 0, + PWR_RETRACT_TIMEOUT_US); if (ret) { drm_err(&ptdev->base, "%s domain retract pending", get_domain_name(domain)); return ret; @@ -306,7 +307,7 @@ static int retract_domain(struct panthor_device *ptdev, u32 domain) * On successful retraction * allow-flag will be set with delegated-flag being cleared. */ - ret = gpu_read64_poll_timeout(ptdev, PWR_STATUS, val, + ret = gpu_read64_poll_timeout(ptdev->iomem, PWR_STATUS, val, ((delegated_mask | allow_mask) & val) == allow_mask, 10, PWR_TRANSITION_TIMEOUT_US); if (ret) { @@ -333,7 +334,7 @@ static int retract_domain(struct panthor_device *ptdev, u32 domain) static int delegate_domain(struct panthor_device *ptdev, u32 domain) { const u32 pwr_cmd = PWR_COMMAND_DEF(PWR_COMMAND_DELEGATE, domain, 0); - const u64 pwr_status = gpu_read64(ptdev, PWR_STATUS); + const u64 pwr_status = gpu_read64(ptdev->iomem, PWR_STATUS); const u64 allow_mask = PWR_STATUS_DOMAIN_ALLOWED(domain); const u64 delegated_mask = PWR_STATUS_DOMAIN_DELEGATED(domain); u64 val; @@ -362,7 +363,7 @@ static int delegate_domain(struct panthor_device *ptdev, u32 domain) * On successful delegation * allow-flag will be cleared with delegated-flag being set. */ - ret = gpu_read64_poll_timeout(ptdev, PWR_STATUS, val, + ret = gpu_read64_poll_timeout(ptdev->iomem, PWR_STATUS, val, ((delegated_mask | allow_mask) & val) == delegated_mask, 10, PWR_TRANSITION_TIMEOUT_US); if (ret) { @@ -410,7 +411,7 @@ static int panthor_pwr_delegate_domains(struct panthor_device *ptdev) */ static int panthor_pwr_domain_force_off(struct panthor_device *ptdev, u32 domain) { - const u64 domain_ready = gpu_read64(ptdev, get_domain_ready_reg(domain)); + const u64 domain_ready = gpu_read64(ptdev->iomem, get_domain_ready_reg(domain)); int ret; /* Domain already powered down, early exit. */ @@ -471,7 +472,7 @@ int panthor_pwr_init(struct panthor_device *ptdev) int panthor_pwr_reset_soft(struct panthor_device *ptdev) { - if (!(gpu_read64(ptdev, PWR_STATUS) & PWR_STATUS_ALLOW_SOFT_RESET)) { + if (!(gpu_read64(ptdev->iomem, PWR_STATUS) & PWR_STATUS_ALLOW_SOFT_RESET)) { drm_err(&ptdev->base, "RESET_SOFT not allowed"); return -EOPNOTSUPP; } @@ -482,7 +483,7 @@ int panthor_pwr_reset_soft(struct panthor_device *ptdev) void panthor_pwr_l2_power_off(struct panthor_device *ptdev) { const u64 l2_allow_mask = PWR_STATUS_DOMAIN_ALLOWED(PWR_COMMAND_DOMAIN_L2); - const u64 pwr_status = gpu_read64(ptdev, PWR_STATUS); + const u64 pwr_status = gpu_read64(ptdev->iomem, PWR_STATUS); /* Abort if L2 power off constraints are not satisfied */ if (!(pwr_status & l2_allow_mask)) { @@ -508,7 +509,7 @@ void panthor_pwr_l2_power_off(struct panthor_device *ptdev) int panthor_pwr_l2_power_on(struct panthor_device *ptdev) { - const u32 pwr_status = gpu_read64(ptdev, PWR_STATUS); + const u32 pwr_status = gpu_read64(ptdev->iomem, PWR_STATUS); const u32 l2_allow_mask = PWR_STATUS_DOMAIN_ALLOWED(PWR_COMMAND_DOMAIN_L2); int ret; diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c index f249eaa0907a4..71317d1921744 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.c +++ b/drivers/gpu/drm/panthor/panthor_sched.c @@ -3368,7 +3368,7 @@ queue_run_job(struct drm_sched_job *sched_job) if (resume_tick) sched_resume_tick(ptdev); - gpu_write(ptdev, CSF_DOORBELL(queue->doorbell_id), 1); + gpu_write(ptdev->iomem, CSF_DOORBELL(queue->doorbell_id), 1); if (!sched->pm.has_ref && !(group->blocked_queues & BIT(job->queue_idx))) { pm_runtime_get(ptdev->base.dev); From 34eb9945a07557f94678bd073527eaa15749142a Mon Sep 17 00:00:00 2001 From: Karunika Choo Date: Mon, 27 Apr 2026 16:59:28 +0100 Subject: [PATCH 2016/2758] drm/panthor: Split register definitions by components [ Upstream commit 1f0a27ba793f3b9b5cf46f16157da41303b4aa88 ] Split the panthor register definitions into per-component headers for the GPU, MMU, firmware, power and generic hardware registers. This makes the register layout easier to follow and prepares the driver for component-local iomem mappings by grouping definitions with the code that owns them. The old monolithic panthor_regs.h header can then be dropped. No functional change intended. v3: - Pick up Ack from Boris and R-bs from Liviu and Steve v2: - Merge GPU_ID definitions into panthor_gpu_regs.h - deleted panthor_hw_regs.h Reviewed-by: Steven Price Reviewed-by: Liviu Dudau Acked-by: Boris Brezillon Signed-off-by: Karunika Choo Tested-by: Boris Brezillon Signed-off-by: Liviu Dudau Link: https://patch.msgid.link/20260427155934.416502-3-karunika.choo@arm.com Stable-dep-of: fe4c05a59018 ("drm/panthor: Fix theoretical IOMEM access in suspended state") Signed-off-by: Sasha Levin --- drivers/gpu/drm/panthor/panthor_device.c | 3 +- drivers/gpu/drm/panthor/panthor_drv.c | 2 +- drivers/gpu/drm/panthor/panthor_fw.c | 2 +- drivers/gpu/drm/panthor/panthor_fw_regs.h | 30 +++ drivers/gpu/drm/panthor/panthor_gpu.c | 2 +- drivers/gpu/drm/panthor/panthor_gpu_regs.h | 120 +++++++++ drivers/gpu/drm/panthor/panthor_heap.c | 2 +- drivers/gpu/drm/panthor/panthor_hw.c | 3 +- drivers/gpu/drm/panthor/panthor_hw.h | 2 +- drivers/gpu/drm/panthor/panthor_mmu.c | 3 +- drivers/gpu/drm/panthor/panthor_mmu_regs.h | 70 +++++ drivers/gpu/drm/panthor/panthor_pwr.c | 3 +- drivers/gpu/drm/panthor/panthor_pwr_regs.h | 83 ++++++ drivers/gpu/drm/panthor/panthor_regs.h | 291 --------------------- drivers/gpu/drm/panthor/panthor_sched.c | 3 +- 15 files changed, 318 insertions(+), 301 deletions(-) create mode 100644 drivers/gpu/drm/panthor/panthor_fw_regs.h create mode 100644 drivers/gpu/drm/panthor/panthor_gpu_regs.h create mode 100644 drivers/gpu/drm/panthor/panthor_mmu_regs.h create mode 100644 drivers/gpu/drm/panthor/panthor_pwr_regs.h delete mode 100644 drivers/gpu/drm/panthor/panthor_regs.h diff --git a/drivers/gpu/drm/panthor/panthor_device.c b/drivers/gpu/drm/panthor/panthor_device.c index 00d1111ceaf17..4f522a912d896 100644 --- a/drivers/gpu/drm/panthor/panthor_device.c +++ b/drivers/gpu/drm/panthor/panthor_device.c @@ -18,12 +18,13 @@ #include "panthor_devfreq.h" #include "panthor_device.h" #include "panthor_fw.h" +#include "panthor_fw_regs.h" #include "panthor_gem.h" #include "panthor_gpu.h" +#include "panthor_gpu_regs.h" #include "panthor_hw.h" #include "panthor_mmu.h" #include "panthor_pwr.h" -#include "panthor_regs.h" #include "panthor_sched.h" static int panthor_gpu_coherency_init(struct panthor_device *ptdev) diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c index 0433756ce224e..3bf0bf3a6a2b2 100644 --- a/drivers/gpu/drm/panthor/panthor_drv.c +++ b/drivers/gpu/drm/panthor/panthor_drv.c @@ -33,9 +33,9 @@ #include "panthor_fw.h" #include "panthor_gem.h" #include "panthor_gpu.h" +#include "panthor_gpu_regs.h" #include "panthor_heap.h" #include "panthor_mmu.h" -#include "panthor_regs.h" #include "panthor_sched.h" /** diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c index b53e3286fc2eb..af7c4ee800d80 100644 --- a/drivers/gpu/drm/panthor/panthor_fw.c +++ b/drivers/gpu/drm/panthor/panthor_fw.c @@ -20,11 +20,11 @@ #include "panthor_device.h" #include "panthor_fw.h" +#include "panthor_fw_regs.h" #include "panthor_gem.h" #include "panthor_gpu.h" #include "panthor_hw.h" #include "panthor_mmu.h" -#include "panthor_regs.h" #include "panthor_sched.h" #include "panthor_trace.h" diff --git a/drivers/gpu/drm/panthor/panthor_fw_regs.h b/drivers/gpu/drm/panthor/panthor_fw_regs.h new file mode 100644 index 0000000000000..d523d41e18ddb --- /dev/null +++ b/drivers/gpu/drm/panthor/panthor_fw_regs.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 or MIT */ +/* Copyright 2026 ARM Limited. All rights reserved. */ + +#ifndef __PANTHOR_FW_REGS_H__ +#define __PANTHOR_FW_REGS_H__ + +#define MCU_CONTROL 0x700 +#define MCU_CONTROL_ENABLE 1 +#define MCU_CONTROL_AUTO 2 +#define MCU_CONTROL_DISABLE 0 + +#define MCU_STATUS 0x704 +#define MCU_STATUS_DISABLED 0 +#define MCU_STATUS_ENABLED 1 +#define MCU_STATUS_HALT 2 +#define MCU_STATUS_FATAL 3 + +#define JOB_INT_RAWSTAT 0x1000 +#define JOB_INT_CLEAR 0x1004 +#define JOB_INT_MASK 0x1008 +#define JOB_INT_STAT 0x100c +#define JOB_INT_GLOBAL_IF BIT(31) +#define JOB_INT_CSG_IF(x) BIT(x) + +#define CSF_GPU_LATEST_FLUSH_ID 0x10000 + +#define CSF_DOORBELL(i) (0x80000 + ((i) * 0x10000)) +#define CSF_GLB_DOORBELL_ID 0 + +#endif /* __PANTHOR_FW_REGS_H__ */ diff --git a/drivers/gpu/drm/panthor/panthor_gpu.c b/drivers/gpu/drm/panthor/panthor_gpu.c index bdb72cebccb3f..fecc30747acff 100644 --- a/drivers/gpu/drm/panthor/panthor_gpu.c +++ b/drivers/gpu/drm/panthor/panthor_gpu.c @@ -19,8 +19,8 @@ #include "panthor_device.h" #include "panthor_gpu.h" +#include "panthor_gpu_regs.h" #include "panthor_hw.h" -#include "panthor_regs.h" #define CREATE_TRACE_POINTS #include "panthor_trace.h" diff --git a/drivers/gpu/drm/panthor/panthor_gpu_regs.h b/drivers/gpu/drm/panthor/panthor_gpu_regs.h new file mode 100644 index 0000000000000..75474b7d73412 --- /dev/null +++ b/drivers/gpu/drm/panthor/panthor_gpu_regs.h @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: GPL-2.0 or MIT */ +/* Copyright 2026 ARM Limited. All rights reserved. */ + +#ifndef __PANTHOR_GPU_REGS_H__ +#define __PANTHOR_GPU_REGS_H__ + +#define GPU_ID 0x0 +#define GPU_ARCH_MAJOR(x) ((x) >> 28) +#define GPU_ARCH_MINOR(x) (((x) & GENMASK(27, 24)) >> 24) +#define GPU_ARCH_REV(x) (((x) & GENMASK(23, 20)) >> 20) +#define GPU_PROD_MAJOR(x) (((x) & GENMASK(19, 16)) >> 16) +#define GPU_VER_MAJOR(x) (((x) & GENMASK(15, 12)) >> 12) +#define GPU_VER_MINOR(x) (((x) & GENMASK(11, 4)) >> 4) +#define GPU_VER_STATUS(x) ((x) & GENMASK(3, 0)) + +#define GPU_L2_FEATURES 0x4 +#define GPU_L2_FEATURES_LINE_SIZE(x) (1 << ((x) & GENMASK(7, 0))) + +#define GPU_CORE_FEATURES 0x8 + +#define GPU_TILER_FEATURES 0xC +#define GPU_MEM_FEATURES 0x10 +#define GROUPS_L2_COHERENT BIT(0) + +#define GPU_MMU_FEATURES 0x14 +#define GPU_MMU_FEATURES_VA_BITS(x) ((x) & GENMASK(7, 0)) +#define GPU_MMU_FEATURES_PA_BITS(x) (((x) >> 8) & GENMASK(7, 0)) +#define GPU_AS_PRESENT 0x18 +#define GPU_CSF_ID 0x1C + +#define GPU_INT_RAWSTAT 0x20 +#define GPU_INT_CLEAR 0x24 +#define GPU_INT_MASK 0x28 +#define GPU_INT_STAT 0x2c +#define GPU_IRQ_FAULT BIT(0) +#define GPU_IRQ_PROTM_FAULT BIT(1) +#define GPU_IRQ_RESET_COMPLETED BIT(8) +#define GPU_IRQ_POWER_CHANGED BIT(9) +#define GPU_IRQ_POWER_CHANGED_ALL BIT(10) +#define GPU_IRQ_CLEAN_CACHES_COMPLETED BIT(17) +#define GPU_IRQ_DOORBELL_MIRROR BIT(18) +#define GPU_IRQ_MCU_STATUS_CHANGED BIT(19) +#define GPU_CMD 0x30 +#define GPU_CMD_DEF(type, payload) ((type) | ((payload) << 8)) +#define GPU_SOFT_RESET GPU_CMD_DEF(1, 1) +#define GPU_HARD_RESET GPU_CMD_DEF(1, 2) +#define CACHE_CLEAN BIT(0) +#define CACHE_INV BIT(1) +#define GPU_FLUSH_CACHES(l2, lsc, oth) \ + GPU_CMD_DEF(4, ((l2) << 0) | ((lsc) << 4) | ((oth) << 8)) + +#define GPU_STATUS 0x34 +#define GPU_STATUS_ACTIVE BIT(0) +#define GPU_STATUS_PWR_ACTIVE BIT(1) +#define GPU_STATUS_PAGE_FAULT BIT(4) +#define GPU_STATUS_PROTM_ACTIVE BIT(7) +#define GPU_STATUS_DBG_ENABLED BIT(8) + +#define GPU_FAULT_STATUS 0x3C +#define GPU_FAULT_ADDR 0x40 +#define GPU_L2_CONFIG 0x48 +#define GPU_L2_CONFIG_ASN_HASH_ENABLE BIT(24) + +#define GPU_PWR_KEY 0x50 +#define GPU_PWR_KEY_UNLOCK 0x2968A819 +#define GPU_PWR_OVERRIDE0 0x54 +#define GPU_PWR_OVERRIDE1 0x58 + +#define GPU_FEATURES 0x60 +#define GPU_FEATURES_RAY_INTERSECTION BIT(2) +#define GPU_FEATURES_RAY_TRAVERSAL BIT(5) + +#define GPU_TIMESTAMP_OFFSET 0x88 +#define GPU_CYCLE_COUNT 0x90 +#define GPU_TIMESTAMP 0x98 + +#define GPU_THREAD_MAX_THREADS 0xA0 +#define GPU_THREAD_MAX_WORKGROUP_SIZE 0xA4 +#define GPU_THREAD_MAX_BARRIER_SIZE 0xA8 +#define GPU_THREAD_FEATURES 0xAC + +#define GPU_TEXTURE_FEATURES(n) (0xB0 + ((n) * 4)) + +#define GPU_SHADER_PRESENT 0x100 +#define GPU_TILER_PRESENT 0x110 +#define GPU_L2_PRESENT 0x120 + +#define SHADER_READY 0x140 +#define TILER_READY 0x150 +#define L2_READY 0x160 + +#define SHADER_PWRON 0x180 +#define TILER_PWRON 0x190 +#define L2_PWRON 0x1A0 + +#define SHADER_PWROFF 0x1C0 +#define TILER_PWROFF 0x1D0 +#define L2_PWROFF 0x1E0 + +#define SHADER_PWRTRANS 0x200 +#define TILER_PWRTRANS 0x210 +#define L2_PWRTRANS 0x220 + +#define SHADER_PWRACTIVE 0x240 +#define TILER_PWRACTIVE 0x250 +#define L2_PWRACTIVE 0x260 + +#define GPU_REVID 0x280 + +#define GPU_ASN_HASH(n) (0x2C0 + ((n) * 4)) + +#define GPU_COHERENCY_FEATURES 0x300 +#define GPU_COHERENCY_PROT_BIT(name) BIT(GPU_COHERENCY_ ## name) + +#define GPU_COHERENCY_PROTOCOL 0x304 +#define GPU_COHERENCY_ACE_LITE 0 +#define GPU_COHERENCY_ACE 1 +#define GPU_COHERENCY_NONE 31 + +#endif /* __PANTHOR_GPU_REGS_H__ */ diff --git a/drivers/gpu/drm/panthor/panthor_heap.c b/drivers/gpu/drm/panthor/panthor_heap.c index 1ee30dc7066f7..99311abdf1e91 100644 --- a/drivers/gpu/drm/panthor/panthor_heap.c +++ b/drivers/gpu/drm/panthor/panthor_heap.c @@ -9,9 +9,9 @@ #include "panthor_device.h" #include "panthor_gem.h" +#include "panthor_gpu_regs.h" #include "panthor_heap.h" #include "panthor_mmu.h" -#include "panthor_regs.h" /* * The GPU heap context is an opaque structure used by the GPU to track the diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c index 9309d09382124..9431f16d950fd 100644 --- a/drivers/gpu/drm/panthor/panthor_hw.c +++ b/drivers/gpu/drm/panthor/panthor_hw.c @@ -8,9 +8,10 @@ #include "panthor_device.h" #include "panthor_gpu.h" +#include "panthor_gpu_regs.h" #include "panthor_hw.h" #include "panthor_pwr.h" -#include "panthor_regs.h" +#include "panthor_pwr_regs.h" #define GPU_PROD_ID_MAKE(arch_major, prod_major) \ (((arch_major) << 24) | (prod_major)) diff --git a/drivers/gpu/drm/panthor/panthor_hw.h b/drivers/gpu/drm/panthor/panthor_hw.h index 2c28aea828418..f797663893b2d 100644 --- a/drivers/gpu/drm/panthor/panthor_hw.h +++ b/drivers/gpu/drm/panthor/panthor_hw.h @@ -5,7 +5,7 @@ #define __PANTHOR_HW_H__ #include "panthor_device.h" -#include "panthor_regs.h" +#include "panthor_gpu_regs.h" /** * struct panthor_hw_ops - HW operations that are specific to a GPU diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c index 9c6d1fe785cc1..901983a1a2742 100644 --- a/drivers/gpu/drm/panthor/panthor_mmu.c +++ b/drivers/gpu/drm/panthor/panthor_mmu.c @@ -31,9 +31,10 @@ #include "panthor_device.h" #include "panthor_gem.h" #include "panthor_gpu.h" +#include "panthor_gpu_regs.h" #include "panthor_heap.h" #include "panthor_mmu.h" -#include "panthor_regs.h" +#include "panthor_mmu_regs.h" #include "panthor_sched.h" #define MAX_AS_SLOTS 32 diff --git a/drivers/gpu/drm/panthor/panthor_mmu_regs.h b/drivers/gpu/drm/panthor/panthor_mmu_regs.h new file mode 100644 index 0000000000000..cc9cf603cec6d --- /dev/null +++ b/drivers/gpu/drm/panthor/panthor_mmu_regs.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0 or MIT */ +/* Copyright 2026 ARM Limited. All rights reserved. */ + +#ifndef __PANTHOR_MMU_REGS_H__ +#define __PANTHOR_MMU_REGS_H__ + +/* MMU regs */ +#define MMU_INT_RAWSTAT 0x2000 +#define MMU_INT_CLEAR 0x2004 +#define MMU_INT_MASK 0x2008 +#define MMU_INT_STAT 0x200c + +/* AS_COMMAND register commands */ + +#define MMU_BASE 0x2400 +#define MMU_AS_SHIFT 6 +#define MMU_AS(as) (MMU_BASE + ((as) << MMU_AS_SHIFT)) + +#define AS_TRANSTAB(as) (MMU_AS(as) + 0x0) +#define AS_MEMATTR(as) (MMU_AS(as) + 0x8) +#define AS_MEMATTR_AARCH64_INNER_ALLOC_IMPL (2 << 2) +#define AS_MEMATTR_AARCH64_INNER_ALLOC_EXPL(w, r) ((3 << 2) | \ + ((w) ? BIT(0) : 0) | \ + ((r) ? BIT(1) : 0)) +#define AS_MEMATTR_AARCH64_SH_MIDGARD_INNER (0 << 4) +#define AS_MEMATTR_AARCH64_SH_CPU_INNER (1 << 4) +#define AS_MEMATTR_AARCH64_SH_CPU_INNER_SHADER_COH (2 << 4) +#define AS_MEMATTR_AARCH64_SHARED (0 << 6) +#define AS_MEMATTR_AARCH64_INNER_OUTER_NC (1 << 6) +#define AS_MEMATTR_AARCH64_INNER_OUTER_WB (2 << 6) +#define AS_MEMATTR_AARCH64_FAULT (3 << 6) +#define AS_LOCKADDR(as) (MMU_AS(as) + 0x10) +#define AS_COMMAND(as) (MMU_AS(as) + 0x18) +#define AS_COMMAND_NOP 0 +#define AS_COMMAND_UPDATE 1 +#define AS_COMMAND_LOCK 2 +#define AS_COMMAND_UNLOCK 3 +#define AS_COMMAND_FLUSH_PT 4 +#define AS_COMMAND_FLUSH_MEM 5 +#define AS_LOCK_REGION_MIN_SIZE (1ULL << 15) +#define AS_FAULTSTATUS(as) (MMU_AS(as) + 0x1C) +#define AS_FAULTSTATUS_ACCESS_TYPE_MASK (0x3 << 8) +#define AS_FAULTSTATUS_ACCESS_TYPE_ATOMIC (0x0 << 8) +#define AS_FAULTSTATUS_ACCESS_TYPE_EX (0x1 << 8) +#define AS_FAULTSTATUS_ACCESS_TYPE_READ (0x2 << 8) +#define AS_FAULTSTATUS_ACCESS_TYPE_WRITE (0x3 << 8) +#define AS_FAULTADDRESS(as) (MMU_AS(as) + 0x20) +#define AS_STATUS(as) (MMU_AS(as) + 0x28) +#define AS_STATUS_AS_ACTIVE BIT(0) +#define AS_TRANSCFG(as) (MMU_AS(as) + 0x30) +#define AS_TRANSCFG_ADRMODE_UNMAPPED (1 << 0) +#define AS_TRANSCFG_ADRMODE_IDENTITY (2 << 0) +#define AS_TRANSCFG_ADRMODE_AARCH64_4K (6 << 0) +#define AS_TRANSCFG_ADRMODE_AARCH64_64K (8 << 0) +#define AS_TRANSCFG_INA_BITS(x) ((x) << 6) +#define AS_TRANSCFG_OUTA_BITS(x) ((x) << 14) +#define AS_TRANSCFG_SL_CONCAT BIT(22) +#define AS_TRANSCFG_PTW_MEMATTR_NC (1 << 24) +#define AS_TRANSCFG_PTW_MEMATTR_WB (2 << 24) +#define AS_TRANSCFG_PTW_SH_NS (0 << 28) +#define AS_TRANSCFG_PTW_SH_OS (2 << 28) +#define AS_TRANSCFG_PTW_SH_IS (3 << 28) +#define AS_TRANSCFG_PTW_RA BIT(30) +#define AS_TRANSCFG_DISABLE_HIER_AP BIT(33) +#define AS_TRANSCFG_DISABLE_AF_FAULT BIT(34) +#define AS_TRANSCFG_WXN BIT(35) +#define AS_TRANSCFG_XREADABLE BIT(36) +#define AS_FAULTEXTRA(as) (MMU_AS(as) + 0x38) + +#endif /* __PANTHOR_MMU_REGS_H__ */ diff --git a/drivers/gpu/drm/panthor/panthor_pwr.c b/drivers/gpu/drm/panthor/panthor_pwr.c index b77c85ad733af..306592ff22272 100644 --- a/drivers/gpu/drm/panthor/panthor_pwr.c +++ b/drivers/gpu/drm/panthor/panthor_pwr.c @@ -11,9 +11,10 @@ #include #include "panthor_device.h" +#include "panthor_gpu_regs.h" #include "panthor_hw.h" #include "panthor_pwr.h" -#include "panthor_regs.h" +#include "panthor_pwr_regs.h" #define PWR_INTERRUPTS_MASK \ (PWR_IRQ_POWER_CHANGED_SINGLE | \ diff --git a/drivers/gpu/drm/panthor/panthor_pwr_regs.h b/drivers/gpu/drm/panthor/panthor_pwr_regs.h new file mode 100644 index 0000000000000..ad3e446971db0 --- /dev/null +++ b/drivers/gpu/drm/panthor/panthor_pwr_regs.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0 or MIT */ +/* Copyright 2026 ARM Limited. All rights reserved. */ + +#ifndef __PANTHOR_PWR_REGS_H__ +#define __PANTHOR_PWR_REGS_H__ + +#define PWR_CONTROL_BASE 0x800 +#define PWR_CTRL_REG(x) (PWR_CONTROL_BASE + (x)) + +#define PWR_INT_RAWSTAT PWR_CTRL_REG(0x0) +#define PWR_INT_CLEAR PWR_CTRL_REG(0x4) +#define PWR_INT_MASK PWR_CTRL_REG(0x8) +#define PWR_INT_STAT PWR_CTRL_REG(0xc) +#define PWR_IRQ_POWER_CHANGED_SINGLE BIT(0) +#define PWR_IRQ_POWER_CHANGED_ALL BIT(1) +#define PWR_IRQ_DELEGATION_CHANGED BIT(2) +#define PWR_IRQ_RESET_COMPLETED BIT(3) +#define PWR_IRQ_RETRACT_COMPLETED BIT(4) +#define PWR_IRQ_INSPECT_COMPLETED BIT(5) +#define PWR_IRQ_COMMAND_NOT_ALLOWED BIT(30) +#define PWR_IRQ_COMMAND_INVALID BIT(31) + +#define PWR_STATUS PWR_CTRL_REG(0x20) +#define PWR_STATUS_ALLOW_L2 BIT_U64(0) +#define PWR_STATUS_ALLOW_TILER BIT_U64(1) +#define PWR_STATUS_ALLOW_SHADER BIT_U64(8) +#define PWR_STATUS_ALLOW_BASE BIT_U64(14) +#define PWR_STATUS_ALLOW_STACK BIT_U64(15) +#define PWR_STATUS_DOMAIN_ALLOWED(x) BIT_U64(x) +#define PWR_STATUS_DELEGATED_L2 BIT_U64(16) +#define PWR_STATUS_DELEGATED_TILER BIT_U64(17) +#define PWR_STATUS_DELEGATED_SHADER BIT_U64(24) +#define PWR_STATUS_DELEGATED_BASE BIT_U64(30) +#define PWR_STATUS_DELEGATED_STACK BIT_U64(31) +#define PWR_STATUS_DELEGATED_SHIFT 16 +#define PWR_STATUS_DOMAIN_DELEGATED(x) BIT_U64((x) + PWR_STATUS_DELEGATED_SHIFT) +#define PWR_STATUS_ALLOW_SOFT_RESET BIT_U64(33) +#define PWR_STATUS_ALLOW_FAST_RESET BIT_U64(34) +#define PWR_STATUS_POWER_PENDING BIT_U64(41) +#define PWR_STATUS_RESET_PENDING BIT_U64(42) +#define PWR_STATUS_RETRACT_PENDING BIT_U64(43) +#define PWR_STATUS_INSPECT_PENDING BIT_U64(44) + +#define PWR_COMMAND PWR_CTRL_REG(0x28) +#define PWR_COMMAND_POWER_UP 0x10 +#define PWR_COMMAND_POWER_DOWN 0x11 +#define PWR_COMMAND_DELEGATE 0x20 +#define PWR_COMMAND_RETRACT 0x21 +#define PWR_COMMAND_RESET_SOFT 0x31 +#define PWR_COMMAND_RESET_FAST 0x32 +#define PWR_COMMAND_INSPECT 0xF0 +#define PWR_COMMAND_DOMAIN_L2 0 +#define PWR_COMMAND_DOMAIN_TILER 1 +#define PWR_COMMAND_DOMAIN_SHADER 8 +#define PWR_COMMAND_DOMAIN_BASE 14 +#define PWR_COMMAND_DOMAIN_STACK 15 +#define PWR_COMMAND_SUBDOMAIN_RTU BIT(0) +#define PWR_COMMAND_DEF(cmd, domain, subdomain) \ + (((subdomain) << 16) | ((domain) << 8) | (cmd)) + +#define PWR_CMDARG PWR_CTRL_REG(0x30) + +#define PWR_L2_PRESENT PWR_CTRL_REG(0x100) +#define PWR_L2_READY PWR_CTRL_REG(0x108) +#define PWR_L2_PWRTRANS PWR_CTRL_REG(0x110) +#define PWR_L2_PWRACTIVE PWR_CTRL_REG(0x118) +#define PWR_TILER_PRESENT PWR_CTRL_REG(0x140) +#define PWR_TILER_READY PWR_CTRL_REG(0x148) +#define PWR_TILER_PWRTRANS PWR_CTRL_REG(0x150) +#define PWR_TILER_PWRACTIVE PWR_CTRL_REG(0x158) +#define PWR_SHADER_PRESENT PWR_CTRL_REG(0x200) +#define PWR_SHADER_READY PWR_CTRL_REG(0x208) +#define PWR_SHADER_PWRTRANS PWR_CTRL_REG(0x210) +#define PWR_SHADER_PWRACTIVE PWR_CTRL_REG(0x218) +#define PWR_BASE_PRESENT PWR_CTRL_REG(0x380) +#define PWR_BASE_READY PWR_CTRL_REG(0x388) +#define PWR_BASE_PWRTRANS PWR_CTRL_REG(0x390) +#define PWR_BASE_PWRACTIVE PWR_CTRL_REG(0x398) +#define PWR_STACK_PRESENT PWR_CTRL_REG(0x3c0) +#define PWR_STACK_READY PWR_CTRL_REG(0x3c8) +#define PWR_STACK_PWRTRANS PWR_CTRL_REG(0x3d0) + +#endif /* __PANTHOR_PWR_REGS_H__ */ diff --git a/drivers/gpu/drm/panthor/panthor_regs.h b/drivers/gpu/drm/panthor/panthor_regs.h deleted file mode 100644 index 08bf06c452d69..0000000000000 --- a/drivers/gpu/drm/panthor/panthor_regs.h +++ /dev/null @@ -1,291 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 or MIT */ -/* Copyright 2018 Marty E. Plummer */ -/* Copyright 2019 Linaro, Ltd, Rob Herring */ -/* Copyright 2023 Collabora ltd. */ -/* - * Register definitions based on mali_kbase_gpu_regmap.h and - * mali_kbase_gpu_regmap_csf.h - * (C) COPYRIGHT 2010-2022 ARM Limited. All rights reserved. - */ -#ifndef __PANTHOR_REGS_H__ -#define __PANTHOR_REGS_H__ - -#define GPU_ID 0x0 -#define GPU_ARCH_MAJOR(x) ((x) >> 28) -#define GPU_ARCH_MINOR(x) (((x) & GENMASK(27, 24)) >> 24) -#define GPU_ARCH_REV(x) (((x) & GENMASK(23, 20)) >> 20) -#define GPU_PROD_MAJOR(x) (((x) & GENMASK(19, 16)) >> 16) -#define GPU_VER_MAJOR(x) (((x) & GENMASK(15, 12)) >> 12) -#define GPU_VER_MINOR(x) (((x) & GENMASK(11, 4)) >> 4) -#define GPU_VER_STATUS(x) ((x) & GENMASK(3, 0)) - -#define GPU_L2_FEATURES 0x4 -#define GPU_L2_FEATURES_LINE_SIZE(x) (1 << ((x) & GENMASK(7, 0))) - -#define GPU_CORE_FEATURES 0x8 - -#define GPU_TILER_FEATURES 0xC -#define GPU_MEM_FEATURES 0x10 -#define GROUPS_L2_COHERENT BIT(0) - -#define GPU_MMU_FEATURES 0x14 -#define GPU_MMU_FEATURES_VA_BITS(x) ((x) & GENMASK(7, 0)) -#define GPU_MMU_FEATURES_PA_BITS(x) (((x) >> 8) & GENMASK(7, 0)) -#define GPU_AS_PRESENT 0x18 -#define GPU_CSF_ID 0x1C - -#define GPU_INT_RAWSTAT 0x20 -#define GPU_INT_CLEAR 0x24 -#define GPU_INT_MASK 0x28 -#define GPU_INT_STAT 0x2c -#define GPU_IRQ_FAULT BIT(0) -#define GPU_IRQ_PROTM_FAULT BIT(1) -#define GPU_IRQ_RESET_COMPLETED BIT(8) -#define GPU_IRQ_POWER_CHANGED BIT(9) -#define GPU_IRQ_POWER_CHANGED_ALL BIT(10) -#define GPU_IRQ_CLEAN_CACHES_COMPLETED BIT(17) -#define GPU_IRQ_DOORBELL_MIRROR BIT(18) -#define GPU_IRQ_MCU_STATUS_CHANGED BIT(19) -#define GPU_CMD 0x30 -#define GPU_CMD_DEF(type, payload) ((type) | ((payload) << 8)) -#define GPU_SOFT_RESET GPU_CMD_DEF(1, 1) -#define GPU_HARD_RESET GPU_CMD_DEF(1, 2) -#define CACHE_CLEAN BIT(0) -#define CACHE_INV BIT(1) -#define GPU_FLUSH_CACHES(l2, lsc, oth) \ - GPU_CMD_DEF(4, ((l2) << 0) | ((lsc) << 4) | ((oth) << 8)) - -#define GPU_STATUS 0x34 -#define GPU_STATUS_ACTIVE BIT(0) -#define GPU_STATUS_PWR_ACTIVE BIT(1) -#define GPU_STATUS_PAGE_FAULT BIT(4) -#define GPU_STATUS_PROTM_ACTIVE BIT(7) -#define GPU_STATUS_DBG_ENABLED BIT(8) - -#define GPU_FAULT_STATUS 0x3C -#define GPU_FAULT_ADDR 0x40 -#define GPU_L2_CONFIG 0x48 -#define GPU_L2_CONFIG_ASN_HASH_ENABLE BIT(24) - -#define GPU_PWR_KEY 0x50 -#define GPU_PWR_KEY_UNLOCK 0x2968A819 -#define GPU_PWR_OVERRIDE0 0x54 -#define GPU_PWR_OVERRIDE1 0x58 - -#define GPU_FEATURES 0x60 -#define GPU_FEATURES_RAY_INTERSECTION BIT(2) -#define GPU_FEATURES_RAY_TRAVERSAL BIT(5) - -#define GPU_TIMESTAMP_OFFSET 0x88 -#define GPU_CYCLE_COUNT 0x90 -#define GPU_TIMESTAMP 0x98 - -#define GPU_THREAD_MAX_THREADS 0xA0 -#define GPU_THREAD_MAX_WORKGROUP_SIZE 0xA4 -#define GPU_THREAD_MAX_BARRIER_SIZE 0xA8 -#define GPU_THREAD_FEATURES 0xAC - -#define GPU_TEXTURE_FEATURES(n) (0xB0 + ((n) * 4)) - -#define GPU_SHADER_PRESENT 0x100 -#define GPU_TILER_PRESENT 0x110 -#define GPU_L2_PRESENT 0x120 - -#define SHADER_READY 0x140 -#define TILER_READY 0x150 -#define L2_READY 0x160 - -#define SHADER_PWRON 0x180 -#define TILER_PWRON 0x190 -#define L2_PWRON 0x1A0 - -#define SHADER_PWROFF 0x1C0 -#define TILER_PWROFF 0x1D0 -#define L2_PWROFF 0x1E0 - -#define SHADER_PWRTRANS 0x200 -#define TILER_PWRTRANS 0x210 -#define L2_PWRTRANS 0x220 - -#define SHADER_PWRACTIVE 0x240 -#define TILER_PWRACTIVE 0x250 -#define L2_PWRACTIVE 0x260 - -#define GPU_REVID 0x280 - -#define GPU_ASN_HASH(n) (0x2C0 + ((n) * 4)) - -#define GPU_COHERENCY_FEATURES 0x300 -#define GPU_COHERENCY_PROT_BIT(name) BIT(GPU_COHERENCY_ ## name) - -#define GPU_COHERENCY_PROTOCOL 0x304 -#define GPU_COHERENCY_ACE_LITE 0 -#define GPU_COHERENCY_ACE 1 -#define GPU_COHERENCY_NONE 31 - -#define MCU_CONTROL 0x700 -#define MCU_CONTROL_ENABLE 1 -#define MCU_CONTROL_AUTO 2 -#define MCU_CONTROL_DISABLE 0 - -#define MCU_STATUS 0x704 -#define MCU_STATUS_DISABLED 0 -#define MCU_STATUS_ENABLED 1 -#define MCU_STATUS_HALT 2 -#define MCU_STATUS_FATAL 3 - -/* Job Control regs */ -#define JOB_INT_RAWSTAT 0x1000 -#define JOB_INT_CLEAR 0x1004 -#define JOB_INT_MASK 0x1008 -#define JOB_INT_STAT 0x100c -#define JOB_INT_GLOBAL_IF BIT(31) -#define JOB_INT_CSG_IF(x) BIT(x) - -/* MMU regs */ -#define MMU_INT_RAWSTAT 0x2000 -#define MMU_INT_CLEAR 0x2004 -#define MMU_INT_MASK 0x2008 -#define MMU_INT_STAT 0x200c - -/* AS_COMMAND register commands */ - -#define MMU_BASE 0x2400 -#define MMU_AS_SHIFT 6 -#define MMU_AS(as) (MMU_BASE + ((as) << MMU_AS_SHIFT)) - -#define AS_TRANSTAB(as) (MMU_AS(as) + 0x0) -#define AS_MEMATTR(as) (MMU_AS(as) + 0x8) -#define AS_MEMATTR_AARCH64_INNER_ALLOC_IMPL (2 << 2) -#define AS_MEMATTR_AARCH64_INNER_ALLOC_EXPL(w, r) ((3 << 2) | \ - ((w) ? BIT(0) : 0) | \ - ((r) ? BIT(1) : 0)) -#define AS_MEMATTR_AARCH64_SH_MIDGARD_INNER (0 << 4) -#define AS_MEMATTR_AARCH64_SH_CPU_INNER (1 << 4) -#define AS_MEMATTR_AARCH64_SH_CPU_INNER_SHADER_COH (2 << 4) -#define AS_MEMATTR_AARCH64_SHARED (0 << 6) -#define AS_MEMATTR_AARCH64_INNER_OUTER_NC (1 << 6) -#define AS_MEMATTR_AARCH64_INNER_OUTER_WB (2 << 6) -#define AS_MEMATTR_AARCH64_FAULT (3 << 6) -#define AS_LOCKADDR(as) (MMU_AS(as) + 0x10) -#define AS_COMMAND(as) (MMU_AS(as) + 0x18) -#define AS_COMMAND_NOP 0 -#define AS_COMMAND_UPDATE 1 -#define AS_COMMAND_LOCK 2 -#define AS_COMMAND_UNLOCK 3 -#define AS_COMMAND_FLUSH_PT 4 -#define AS_COMMAND_FLUSH_MEM 5 -#define AS_LOCK_REGION_MIN_SIZE (1ULL << 15) -#define AS_FAULTSTATUS(as) (MMU_AS(as) + 0x1C) -#define AS_FAULTSTATUS_ACCESS_TYPE_MASK (0x3 << 8) -#define AS_FAULTSTATUS_ACCESS_TYPE_ATOMIC (0x0 << 8) -#define AS_FAULTSTATUS_ACCESS_TYPE_EX (0x1 << 8) -#define AS_FAULTSTATUS_ACCESS_TYPE_READ (0x2 << 8) -#define AS_FAULTSTATUS_ACCESS_TYPE_WRITE (0x3 << 8) -#define AS_FAULTADDRESS(as) (MMU_AS(as) + 0x20) -#define AS_STATUS(as) (MMU_AS(as) + 0x28) -#define AS_STATUS_AS_ACTIVE BIT(0) -#define AS_TRANSCFG(as) (MMU_AS(as) + 0x30) -#define AS_TRANSCFG_ADRMODE_UNMAPPED (1 << 0) -#define AS_TRANSCFG_ADRMODE_IDENTITY (2 << 0) -#define AS_TRANSCFG_ADRMODE_AARCH64_4K (6 << 0) -#define AS_TRANSCFG_ADRMODE_AARCH64_64K (8 << 0) -#define AS_TRANSCFG_INA_BITS(x) ((x) << 6) -#define AS_TRANSCFG_OUTA_BITS(x) ((x) << 14) -#define AS_TRANSCFG_SL_CONCAT BIT(22) -#define AS_TRANSCFG_PTW_MEMATTR_NC (1 << 24) -#define AS_TRANSCFG_PTW_MEMATTR_WB (2 << 24) -#define AS_TRANSCFG_PTW_SH_NS (0 << 28) -#define AS_TRANSCFG_PTW_SH_OS (2 << 28) -#define AS_TRANSCFG_PTW_SH_IS (3 << 28) -#define AS_TRANSCFG_PTW_RA BIT(30) -#define AS_TRANSCFG_DISABLE_HIER_AP BIT(33) -#define AS_TRANSCFG_DISABLE_AF_FAULT BIT(34) -#define AS_TRANSCFG_WXN BIT(35) -#define AS_TRANSCFG_XREADABLE BIT(36) -#define AS_FAULTEXTRA(as) (MMU_AS(as) + 0x38) - -#define CSF_GPU_LATEST_FLUSH_ID 0x10000 - -#define CSF_DOORBELL(i) (0x80000 + ((i) * 0x10000)) -#define CSF_GLB_DOORBELL_ID 0 - -/* PWR Control registers */ - -#define PWR_CONTROL_BASE 0x800 -#define PWR_CTRL_REG(x) (PWR_CONTROL_BASE + (x)) - -#define PWR_INT_RAWSTAT PWR_CTRL_REG(0x0) -#define PWR_INT_CLEAR PWR_CTRL_REG(0x4) -#define PWR_INT_MASK PWR_CTRL_REG(0x8) -#define PWR_INT_STAT PWR_CTRL_REG(0xc) -#define PWR_IRQ_POWER_CHANGED_SINGLE BIT(0) -#define PWR_IRQ_POWER_CHANGED_ALL BIT(1) -#define PWR_IRQ_DELEGATION_CHANGED BIT(2) -#define PWR_IRQ_RESET_COMPLETED BIT(3) -#define PWR_IRQ_RETRACT_COMPLETED BIT(4) -#define PWR_IRQ_INSPECT_COMPLETED BIT(5) -#define PWR_IRQ_COMMAND_NOT_ALLOWED BIT(30) -#define PWR_IRQ_COMMAND_INVALID BIT(31) - -#define PWR_STATUS PWR_CTRL_REG(0x20) -#define PWR_STATUS_ALLOW_L2 BIT_U64(0) -#define PWR_STATUS_ALLOW_TILER BIT_U64(1) -#define PWR_STATUS_ALLOW_SHADER BIT_U64(8) -#define PWR_STATUS_ALLOW_BASE BIT_U64(14) -#define PWR_STATUS_ALLOW_STACK BIT_U64(15) -#define PWR_STATUS_DOMAIN_ALLOWED(x) BIT_U64(x) -#define PWR_STATUS_DELEGATED_L2 BIT_U64(16) -#define PWR_STATUS_DELEGATED_TILER BIT_U64(17) -#define PWR_STATUS_DELEGATED_SHADER BIT_U64(24) -#define PWR_STATUS_DELEGATED_BASE BIT_U64(30) -#define PWR_STATUS_DELEGATED_STACK BIT_U64(31) -#define PWR_STATUS_DELEGATED_SHIFT 16 -#define PWR_STATUS_DOMAIN_DELEGATED(x) BIT_U64((x) + PWR_STATUS_DELEGATED_SHIFT) -#define PWR_STATUS_ALLOW_SOFT_RESET BIT_U64(33) -#define PWR_STATUS_ALLOW_FAST_RESET BIT_U64(34) -#define PWR_STATUS_POWER_PENDING BIT_U64(41) -#define PWR_STATUS_RESET_PENDING BIT_U64(42) -#define PWR_STATUS_RETRACT_PENDING BIT_U64(43) -#define PWR_STATUS_INSPECT_PENDING BIT_U64(44) - -#define PWR_COMMAND PWR_CTRL_REG(0x28) -#define PWR_COMMAND_POWER_UP 0x10 -#define PWR_COMMAND_POWER_DOWN 0x11 -#define PWR_COMMAND_DELEGATE 0x20 -#define PWR_COMMAND_RETRACT 0x21 -#define PWR_COMMAND_RESET_SOFT 0x31 -#define PWR_COMMAND_RESET_FAST 0x32 -#define PWR_COMMAND_INSPECT 0xF0 -#define PWR_COMMAND_DOMAIN_L2 0 -#define PWR_COMMAND_DOMAIN_TILER 1 -#define PWR_COMMAND_DOMAIN_SHADER 8 -#define PWR_COMMAND_DOMAIN_BASE 14 -#define PWR_COMMAND_DOMAIN_STACK 15 -#define PWR_COMMAND_SUBDOMAIN_RTU BIT(0) -#define PWR_COMMAND_DEF(cmd, domain, subdomain) \ - (((subdomain) << 16) | ((domain) << 8) | (cmd)) - -#define PWR_CMDARG PWR_CTRL_REG(0x30) - -#define PWR_L2_PRESENT PWR_CTRL_REG(0x100) -#define PWR_L2_READY PWR_CTRL_REG(0x108) -#define PWR_L2_PWRTRANS PWR_CTRL_REG(0x110) -#define PWR_L2_PWRACTIVE PWR_CTRL_REG(0x118) -#define PWR_TILER_PRESENT PWR_CTRL_REG(0x140) -#define PWR_TILER_READY PWR_CTRL_REG(0x148) -#define PWR_TILER_PWRTRANS PWR_CTRL_REG(0x150) -#define PWR_TILER_PWRACTIVE PWR_CTRL_REG(0x158) -#define PWR_SHADER_PRESENT PWR_CTRL_REG(0x200) -#define PWR_SHADER_READY PWR_CTRL_REG(0x208) -#define PWR_SHADER_PWRTRANS PWR_CTRL_REG(0x210) -#define PWR_SHADER_PWRACTIVE PWR_CTRL_REG(0x218) -#define PWR_BASE_PRESENT PWR_CTRL_REG(0x380) -#define PWR_BASE_READY PWR_CTRL_REG(0x388) -#define PWR_BASE_PWRTRANS PWR_CTRL_REG(0x390) -#define PWR_BASE_PWRACTIVE PWR_CTRL_REG(0x398) -#define PWR_STACK_PRESENT PWR_CTRL_REG(0x3c0) -#define PWR_STACK_READY PWR_CTRL_REG(0x3c8) -#define PWR_STACK_PWRTRANS PWR_CTRL_REG(0x3d0) - -#endif diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c index 71317d1921744..608264031f50a 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.c +++ b/drivers/gpu/drm/panthor/panthor_sched.c @@ -28,11 +28,12 @@ #include "panthor_devfreq.h" #include "panthor_device.h" #include "panthor_fw.h" +#include "panthor_fw_regs.h" #include "panthor_gem.h" #include "panthor_gpu.h" +#include "panthor_gpu_regs.h" #include "panthor_heap.h" #include "panthor_mmu.h" -#include "panthor_regs.h" #include "panthor_sched.h" /** From 1c942c3c51795f7a6c422e6bac10b4f4a01036e7 Mon Sep 17 00:00:00 2001 From: Karunika Choo Date: Mon, 27 Apr 2026 16:59:30 +0100 Subject: [PATCH 2017/2758] drm/panthor: Store IRQ register base iomem pointer in panthor_irq [ Upstream commit 77e5cc60fa172047ad3460f74db0699b3a7746f9 ] Update common IRQ handling code to work from an IRQ-local iomem base instead of referencing block-specific interrupt register offsets. Store the interrupt base address iomem pointer in struct panthor_irq and switch the shared IRQ helpers to use generic INT_* offsets from that local base. This removes the need for each caller to expose absolute IRQ register addresses while keeping the common IRQ flow unchanged. No functional change intended. v3: - Clean up definition of pwr->irq.iomem. v2: - Change IRQ request function to accept an iomem pointer instead of computing it from an offset argument. Signed-off-by: Karunika Choo Reviewed-by: Boris Brezillon Tested-by: Boris Brezillon Reviewed-by: Steven Price Reviewed-by: Liviu Dudau Signed-off-by: Liviu Dudau Link: https://patch.msgid.link/20260427155934.416502-5-karunika.choo@arm.com Stable-dep-of: fe4c05a59018 ("drm/panthor: Fix theoretical IOMEM access in suspended state") Signed-off-by: Sasha Levin --- drivers/gpu/drm/panthor/panthor_device.h | 32 ++++++++++++++-------- drivers/gpu/drm/panthor/panthor_fw.c | 5 ++-- drivers/gpu/drm/panthor/panthor_fw_regs.h | 2 ++ drivers/gpu/drm/panthor/panthor_gpu.c | 6 ++-- drivers/gpu/drm/panthor/panthor_gpu_regs.h | 3 ++ drivers/gpu/drm/panthor/panthor_mmu.c | 5 ++-- drivers/gpu/drm/panthor/panthor_mmu_regs.h | 3 ++ drivers/gpu/drm/panthor/panthor_pwr.c | 6 ++-- 8 files changed, 42 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h index 519c3a4e9515e..dea0ff7def6ca 100644 --- a/drivers/gpu/drm/panthor/panthor_device.h +++ b/drivers/gpu/drm/panthor/panthor_device.h @@ -81,6 +81,9 @@ struct panthor_irq { /** @ptdev: Panthor device */ struct panthor_device *ptdev; + /** @iomem: CPU mapping of IRQ base address */ + void __iomem *iomem; + /** @irq: IRQ number. */ int irq; @@ -415,6 +418,11 @@ panthor_exception_is_fault(u32 exception_code) const char *panthor_exception_name(struct panthor_device *ptdev, u32 exception_code); +#define INT_RAWSTAT 0x0 +#define INT_CLEAR 0x4 +#define INT_MASK 0x8 +#define INT_STAT 0xc + /** * PANTHOR_IRQ_HANDLER() - Define interrupt handlers and the interrupt * registration function. @@ -425,14 +433,13 @@ const char *panthor_exception_name(struct panthor_device *ptdev, * * void (*handler)(struct panthor_device *, u32 status); */ -#define PANTHOR_IRQ_HANDLER(__name, __reg_prefix, __handler) \ +#define PANTHOR_IRQ_HANDLER(__name, __handler) \ static irqreturn_t panthor_ ## __name ## _irq_raw_handler(int irq, void *data) \ { \ struct panthor_irq *pirq = data; \ - struct panthor_device *ptdev = pirq->ptdev; \ enum panthor_irq_state old_state; \ \ - if (!gpu_read(ptdev->iomem, __reg_prefix ## _INT_STAT)) \ + if (!gpu_read(pirq->iomem, INT_STAT)) \ return IRQ_NONE; \ \ guard(spinlock_irqsave)(&pirq->mask_lock); \ @@ -442,7 +449,7 @@ static irqreturn_t panthor_ ## __name ## _irq_raw_handler(int irq, void *data) if (old_state != PANTHOR_IRQ_STATE_ACTIVE) \ return IRQ_NONE; \ \ - gpu_write(ptdev->iomem, __reg_prefix ## _INT_MASK, 0); \ + gpu_write(pirq->iomem, INT_MASK, 0); \ return IRQ_WAKE_THREAD; \ } \ \ @@ -461,7 +468,7 @@ static irqreturn_t panthor_ ## __name ## _irq_threaded_handler(int irq, void *da * right before the HW event kicks in. TLDR; it's all expected races we're \ * covered for. \ */ \ - u32 status = gpu_read(ptdev->iomem, __reg_prefix ## _INT_RAWSTAT) & pirq->mask; \ + u32 status = gpu_read(pirq->iomem, INT_RAWSTAT) & pirq->mask; \ \ if (!status) \ break; \ @@ -477,7 +484,7 @@ static irqreturn_t panthor_ ## __name ## _irq_threaded_handler(int irq, void *da PANTHOR_IRQ_STATE_PROCESSING, \ PANTHOR_IRQ_STATE_ACTIVE); \ if (old_state == PANTHOR_IRQ_STATE_PROCESSING) \ - gpu_write(ptdev->iomem, __reg_prefix ## _INT_MASK, pirq->mask); \ + gpu_write(pirq->iomem, INT_MASK, pirq->mask); \ } \ \ return ret; \ @@ -487,7 +494,7 @@ static inline void panthor_ ## __name ## _irq_suspend(struct panthor_irq *pirq) { \ scoped_guard(spinlock_irqsave, &pirq->mask_lock) { \ atomic_set(&pirq->state, PANTHOR_IRQ_STATE_SUSPENDING); \ - gpu_write(pirq->ptdev->iomem, __reg_prefix ## _INT_MASK, 0); \ + gpu_write(pirq->iomem, INT_MASK, 0); \ } \ synchronize_irq(pirq->irq); \ atomic_set(&pirq->state, PANTHOR_IRQ_STATE_SUSPENDED); \ @@ -498,17 +505,18 @@ static inline void panthor_ ## __name ## _irq_resume(struct panthor_irq *pirq) guard(spinlock_irqsave)(&pirq->mask_lock); \ \ atomic_set(&pirq->state, PANTHOR_IRQ_STATE_ACTIVE); \ - gpu_write(pirq->ptdev->iomem, __reg_prefix ## _INT_CLEAR, pirq->mask); \ - gpu_write(pirq->ptdev->iomem, __reg_prefix ## _INT_MASK, pirq->mask); \ + gpu_write(pirq->iomem, INT_CLEAR, pirq->mask); \ + gpu_write(pirq->iomem, INT_MASK, pirq->mask); \ } \ \ static int panthor_request_ ## __name ## _irq(struct panthor_device *ptdev, \ struct panthor_irq *pirq, \ - int irq, u32 mask) \ + int irq, u32 mask, void __iomem *iomem) \ { \ pirq->ptdev = ptdev; \ pirq->irq = irq; \ pirq->mask = mask; \ + pirq->iomem = iomem; \ spin_lock_init(&pirq->mask_lock); \ panthor_ ## __name ## _irq_resume(pirq); \ \ @@ -530,7 +538,7 @@ static inline void panthor_ ## __name ## _irq_enable_events(struct panthor_irq * * If the IRQ is suspended/suspending, the mask is restored at resume time. \ */ \ if (atomic_read(&pirq->state) == PANTHOR_IRQ_STATE_ACTIVE) \ - gpu_write(pirq->ptdev->iomem, __reg_prefix ## _INT_MASK, pirq->mask); \ + gpu_write(pirq->iomem, INT_MASK, pirq->mask); \ } \ \ static inline void panthor_ ## __name ## _irq_disable_events(struct panthor_irq *pirq, u32 mask)\ @@ -544,7 +552,7 @@ static inline void panthor_ ## __name ## _irq_disable_events(struct panthor_irq * If the IRQ is suspended/suspending, the mask is restored at resume time. \ */ \ if (atomic_read(&pirq->state) == PANTHOR_IRQ_STATE_ACTIVE) \ - gpu_write(pirq->ptdev->iomem, __reg_prefix ## _INT_MASK, pirq->mask); \ + gpu_write(pirq->iomem, INT_MASK, pirq->mask); \ } extern struct workqueue_struct *panthor_cleanup_wq; diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c index af7c4ee800d80..834c48e368fc3 100644 --- a/drivers/gpu/drm/panthor/panthor_fw.c +++ b/drivers/gpu/drm/panthor/panthor_fw.c @@ -1086,7 +1086,7 @@ static void panthor_job_irq_handler(struct panthor_device *ptdev, u32 status) trace_gpu_job_irq(ptdev->base.dev, status, duration); } } -PANTHOR_IRQ_HANDLER(job, JOB, panthor_job_irq_handler); +PANTHOR_IRQ_HANDLER(job, panthor_job_irq_handler); static int panthor_fw_start(struct panthor_device *ptdev) { @@ -1463,7 +1463,8 @@ int panthor_fw_init(struct panthor_device *ptdev) if (irq <= 0) return -ENODEV; - ret = panthor_request_job_irq(ptdev, &fw->irq, irq, 0); + ret = panthor_request_job_irq(ptdev, &fw->irq, irq, 0, + ptdev->iomem + JOB_INT_BASE); if (ret) { drm_err(&ptdev->base, "failed to request job irq"); return ret; diff --git a/drivers/gpu/drm/panthor/panthor_fw_regs.h b/drivers/gpu/drm/panthor/panthor_fw_regs.h index d523d41e18ddb..eeb41aff249bd 100644 --- a/drivers/gpu/drm/panthor/panthor_fw_regs.h +++ b/drivers/gpu/drm/panthor/panthor_fw_regs.h @@ -15,6 +15,8 @@ #define MCU_STATUS_HALT 2 #define MCU_STATUS_FATAL 3 +#define JOB_INT_BASE 0x1000 + #define JOB_INT_RAWSTAT 0x1000 #define JOB_INT_CLEAR 0x1004 #define JOB_INT_MASK 0x1008 diff --git a/drivers/gpu/drm/panthor/panthor_gpu.c b/drivers/gpu/drm/panthor/panthor_gpu.c index fecc30747acff..d8e5283e9a1c8 100644 --- a/drivers/gpu/drm/panthor/panthor_gpu.c +++ b/drivers/gpu/drm/panthor/panthor_gpu.c @@ -110,7 +110,7 @@ static void panthor_gpu_irq_handler(struct panthor_device *ptdev, u32 status) } spin_unlock(&ptdev->gpu->reqs_lock); } -PANTHOR_IRQ_HANDLER(gpu, GPU, panthor_gpu_irq_handler); +PANTHOR_IRQ_HANDLER(gpu, panthor_gpu_irq_handler); /** * panthor_gpu_unplug() - Called when the GPU is unplugged. @@ -162,7 +162,9 @@ int panthor_gpu_init(struct panthor_device *ptdev) if (irq < 0) return irq; - ret = panthor_request_gpu_irq(ptdev, &ptdev->gpu->irq, irq, GPU_INTERRUPTS_MASK); + ret = panthor_request_gpu_irq(ptdev, &ptdev->gpu->irq, irq, + GPU_INTERRUPTS_MASK, + ptdev->iomem + GPU_INT_BASE); if (ret) return ret; diff --git a/drivers/gpu/drm/panthor/panthor_gpu_regs.h b/drivers/gpu/drm/panthor/panthor_gpu_regs.h index 75474b7d73412..3f60c45985a75 100644 --- a/drivers/gpu/drm/panthor/panthor_gpu_regs.h +++ b/drivers/gpu/drm/panthor/panthor_gpu_regs.h @@ -4,6 +4,8 @@ #ifndef __PANTHOR_GPU_REGS_H__ #define __PANTHOR_GPU_REGS_H__ +#define GPU_CONTROL_BASE 0x0 + #define GPU_ID 0x0 #define GPU_ARCH_MAJOR(x) ((x) >> 28) #define GPU_ARCH_MINOR(x) (((x) & GENMASK(27, 24)) >> 24) @@ -28,6 +30,7 @@ #define GPU_AS_PRESENT 0x18 #define GPU_CSF_ID 0x1C +#define GPU_INT_BASE 0x20 #define GPU_INT_RAWSTAT 0x20 #define GPU_INT_CLEAR 0x24 #define GPU_INT_MASK 0x28 diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c index 901983a1a2742..263a22f7b3934 100644 --- a/drivers/gpu/drm/panthor/panthor_mmu.c +++ b/drivers/gpu/drm/panthor/panthor_mmu.c @@ -569,7 +569,7 @@ static u32 panthor_mmu_as_fault_mask(struct panthor_device *ptdev, u32 as) /* Forward declaration to call helpers within as_enable/disable */ static void panthor_mmu_irq_handler(struct panthor_device *ptdev, u32 status); -PANTHOR_IRQ_HANDLER(mmu, MMU, panthor_mmu_irq_handler); +PANTHOR_IRQ_HANDLER(mmu, panthor_mmu_irq_handler); static int panthor_mmu_as_enable(struct panthor_device *ptdev, u32 as_nr, u64 transtab, u64 transcfg, u64 memattr) @@ -2888,7 +2888,8 @@ int panthor_mmu_init(struct panthor_device *ptdev) return -ENODEV; ret = panthor_request_mmu_irq(ptdev, &mmu->irq, irq, - panthor_mmu_fault_mask(ptdev, ~0)); + panthor_mmu_fault_mask(ptdev, ~0), + ptdev->iomem + MMU_INT_BASE); if (ret) return ret; diff --git a/drivers/gpu/drm/panthor/panthor_mmu_regs.h b/drivers/gpu/drm/panthor/panthor_mmu_regs.h index cc9cf603cec6d..de460042651dc 100644 --- a/drivers/gpu/drm/panthor/panthor_mmu_regs.h +++ b/drivers/gpu/drm/panthor/panthor_mmu_regs.h @@ -5,6 +5,9 @@ #define __PANTHOR_MMU_REGS_H__ /* MMU regs */ + +#define MMU_INT_BASE 0x2000 + #define MMU_INT_RAWSTAT 0x2000 #define MMU_INT_CLEAR 0x2004 #define MMU_INT_MASK 0x2008 diff --git a/drivers/gpu/drm/panthor/panthor_pwr.c b/drivers/gpu/drm/panthor/panthor_pwr.c index 306592ff22272..52fc46239e6b6 100644 --- a/drivers/gpu/drm/panthor/panthor_pwr.c +++ b/drivers/gpu/drm/panthor/panthor_pwr.c @@ -70,7 +70,7 @@ static void panthor_pwr_irq_handler(struct panthor_device *ptdev, u32 status) } spin_unlock(&ptdev->pwr->reqs_lock); } -PANTHOR_IRQ_HANDLER(pwr, PWR, panthor_pwr_irq_handler); +PANTHOR_IRQ_HANDLER(pwr, panthor_pwr_irq_handler); static void panthor_pwr_write_command(struct panthor_device *ptdev, u32 command, u64 args) { @@ -464,7 +464,9 @@ int panthor_pwr_init(struct panthor_device *ptdev) if (irq < 0) return irq; - err = panthor_request_pwr_irq(ptdev, &pwr->irq, irq, PWR_INTERRUPTS_MASK); + err = panthor_request_pwr_irq( + ptdev, &pwr->irq, irq, PWR_INTERRUPTS_MASK, + ptdev->iomem + PWR_CONTROL_BASE); if (err) return err; From 361adc5343e98588d149627f77dffb71eb9829d0 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 25 Jun 2026 14:40:31 +0200 Subject: [PATCH 2018/2758] drm/panthor: Fix theoretical IOMEM access in suspended state [ Upstream commit fe4c05a59018964bac7923338706371fff3c09ef ] In theory, our hardirq handler can be called while the device (and thus the panthor_irq) is suspended, because the IRQ line is shared. In practice though, in all the designs we've seen, the line is only shared within the GPU, and because sub-component suspend state is consistent (all-suspended or all-resumed), we shouldn't end up with an interrupt triggered while we're suspended. Fix the problem anyway, if nothing else, for our sanity. Fixes: 0b2d86670a84 ("drm/panthor: Rework panthor_irq::suspended into panthor_irq::state") Reported-by: sashiko-bot@kernel.org Closes: https://sashiko.dev/#/patchset/20260625-panthor-signal-from-irq-v4-0-3d2908912afa@collabora.com?part=1 Signed-off-by: Boris Brezillon Reviewed-by: Liviu Dudau Signed-off-by: Liviu Dudau Link: https://patch.msgid.link/20260625-panthor-misc-fixes-v1-5-b67ed973fea6@collabora.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/panthor/panthor_device.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h index dea0ff7def6ca..724b4aeaf23e1 100644 --- a/drivers/gpu/drm/panthor/panthor_device.h +++ b/drivers/gpu/drm/panthor/panthor_device.h @@ -439,9 +439,6 @@ static irqreturn_t panthor_ ## __name ## _irq_raw_handler(int irq, void *data) struct panthor_irq *pirq = data; \ enum panthor_irq_state old_state; \ \ - if (!gpu_read(pirq->iomem, INT_STAT)) \ - return IRQ_NONE; \ - \ guard(spinlock_irqsave)(&pirq->mask_lock); \ old_state = atomic_cmpxchg(&pirq->state, \ PANTHOR_IRQ_STATE_ACTIVE, \ @@ -449,6 +446,13 @@ static irqreturn_t panthor_ ## __name ## _irq_raw_handler(int irq, void *data) if (old_state != PANTHOR_IRQ_STATE_ACTIVE) \ return IRQ_NONE; \ \ + if (!gpu_read(pirq->iomem, INT_STAT)) { \ + atomic_cmpxchg(&pirq->state, \ + PANTHOR_IRQ_STATE_PROCESSING, \ + PANTHOR_IRQ_STATE_ACTIVE); \ + return IRQ_NONE; \ + } \ + \ gpu_write(pirq->iomem, INT_MASK, 0); \ return IRQ_WAKE_THREAD; \ } \ From 1352cd192e5beff397251bef7a9a60dccb1c8712 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 25 Jun 2026 14:40:32 +0200 Subject: [PATCH 2019/2758] drm/panthor: Don't overrule pending immediate ticks in sched_resume_tick() [ Upstream commit 6fec8b473497b7f32e604a6dd92b32b0889af3e8 ] We schedule immediate ticks when we need to process events on CSGs, but those immediate ticks don't change the resched_target because we want the other groups to stay scheduled for the remaining of the GPU timeslot they were given. Make sure these immediate ticks don't get overruled by a sched_queue_delayed_work() that would delay the tick execution. Fixes: 99820b4b7e50 ("drm/panthor: Make sure we resume the tick when new jobs are submitted") Reported-by: sashiko-bot@kernel.org Closes: https://sashiko.dev/#/patchset/20260625-panthor-signal-from-irq-v4-0-3d2908912afa@collabora.com?part=9 Signed-off-by: Boris Brezillon Reviewed-by: Karunika Choo Signed-off-by: Liviu Dudau Link: https://patch.msgid.link/20260625-panthor-misc-fixes-v1-6-b67ed973fea6@collabora.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/panthor/panthor_sched.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c index 608264031f50a..e347e15349db5 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.c +++ b/drivers/gpu/drm/panthor/panthor_sched.c @@ -2666,7 +2666,14 @@ static void sched_resume_tick(struct panthor_device *ptdev) else delay_jiffies = 0; - sched_queue_delayed_work(sched, tick, delay_jiffies); + /* We schedule immediate ticks when we need to process events on CSGs, + * but those don't change the resched_target because we want the other + * groups to stay scheduled for the remaining of the GPU timeslot they + * were given. Make sure those immediate ticks don't get overruled by + * a sched_queue_delayed_work() that would delay the tick execution. + */ + if (!delayed_work_pending(&sched->tick_work)) + sched_queue_delayed_work(sched, tick, delay_jiffies); } static void group_schedule_locked(struct panthor_group *group, u32 queue_mask) From 50556bfe1d6c6b137882a713a393bea737fbd095 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 25 Jun 2026 14:40:33 +0200 Subject: [PATCH 2020/2758] drm/panthor: Fix panthor_pwr_unplug() [ Upstream commit e62179fd3e23ecfaedf7101e19ec0d3e4f51de76 ] We can't call panthor_pwr_irq_suspend() if the device is suspended, or this leads to a hang when the IOMEM region is accessed while the clks are disabled. Do what other sub-components do and conditionally call panthor_pwr_irq_suspend() if we know the PWR regbank block is accessible. Fixes: c27787f2b77f ("drm/panthor: Introduce panthor_pwr API and power control framework") Signed-off-by: Boris Brezillon Reviewed-by: Liviu Dudau Signed-off-by: Liviu Dudau Link: https://patch.msgid.link/20260625-panthor-misc-fixes-v1-7-b67ed973fea6@collabora.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/panthor/panthor_pwr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panthor/panthor_pwr.c b/drivers/gpu/drm/panthor/panthor_pwr.c index 52fc46239e6b6..dda5159a5271b 100644 --- a/drivers/gpu/drm/panthor/panthor_pwr.c +++ b/drivers/gpu/drm/panthor/panthor_pwr.c @@ -435,7 +435,8 @@ void panthor_pwr_unplug(struct panthor_device *ptdev) return; /* Make sure the IRQ handler is not running after that point. */ - panthor_pwr_irq_suspend(&ptdev->pwr->irq); + if (!IS_ENABLED(CONFIG_PM) || pm_runtime_active(ptdev->base.dev)) + panthor_pwr_irq_suspend(&ptdev->pwr->irq); /* Wake-up all waiters. */ spin_lock_irqsave(&ptdev->pwr->reqs_lock, flags); From 893ed1a7c837a824b2a25543e46fbf19dc41f6b5 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 25 Jun 2026 14:40:35 +0200 Subject: [PATCH 2021/2758] drm/panthor: Fix a leak when a group is evicted before the tiler OOM is serviced [ Upstream commit 6efeb9ddb4fbf5ac30aff03e8f09ffbdf966abd0 ] A group ref is tied to the pending tiler_oom_work, so we need to release it if the cancel was effective. Fixes: de8548813824 ("drm/panthor: Add the scheduler logical block") Reported-by: sashiko-bot@kernel.org Closes: https://sashiko.dev/#/patchset/20260623-panthor-signal-from-irq-v3-0-2ece396f8ee0@collabora.com?part=7 Signed-off-by: Boris Brezillon Reviewed-by: Liviu Dudau Signed-off-by: Liviu Dudau Link: https://patch.msgid.link/20260625-panthor-misc-fixes-v1-9-b67ed973fea6@collabora.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/panthor/panthor_sched.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c index e347e15349db5..b15c15617b70d 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.c +++ b/drivers/gpu/drm/panthor/panthor_sched.c @@ -1058,7 +1058,8 @@ group_unbind_locked(struct panthor_group *group) /* Tiler OOM events will be re-issued next time the group is scheduled. */ atomic_set(&group->tiler_oom, 0); - cancel_work(&group->tiler_oom_work); + if (cancel_work(&group->tiler_oom_work)) + group_put(group); for (u32 i = 0; i < group->queue_count; i++) group->queues[i]->doorbell_id = -1; From 2c88ad0d06c6b043bb4c5b04e78ce80639bf9e33 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 25 Jun 2026 14:40:36 +0200 Subject: [PATCH 2022/2758] drm/panthor: Interrupt group start/resumption if group_bind_locked() fails [ Upstream commit 1f27cef1f41dac0bd254d8741766f189936c9880 ] group_bind_locked() can fail if the MMU block is stuck. This is normally a reset situation, but by the time we reset the GPU, we might have tried to resume a group that's not resident, which will probably trip out the FW. So let's avoid that by bailing out when group_bind_locked() returns an error. We don't even try to start more groups because the GPU will be reset anyway. Fixes: de8548813824 ("drm/panthor: Add the scheduler logical block") Reported-by: sashiko-bot@kernel.org Closes: https://sashiko.dev/#/patchset/20260623-panthor-signal-from-irq-v3-0-2ece396f8ee0@collabora.com?part=7 Signed-off-by: Boris Brezillon Reviewed-by: Liviu Dudau Signed-off-by: Liviu Dudau Link: https://patch.msgid.link/20260625-panthor-misc-fixes-v1-10-b67ed973fea6@collabora.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/panthor/panthor_sched.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c index b15c15617b70d..b09953ac0bd63 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.c +++ b/drivers/gpu/drm/panthor/panthor_sched.c @@ -2367,7 +2367,13 @@ tick_ctx_apply(struct panthor_scheduler *sched, struct panthor_sched_tick_ctx *c csg_iface = panthor_fw_get_csg_iface(ptdev, csg_id); csg_slot = &sched->csg_slots[csg_id]; - group_bind_locked(group, csg_id); + ret = group_bind_locked(group, csg_id); + if (ret) { + panthor_device_schedule_reset(ptdev); + ctx->csg_upd_failed_mask |= BIT(csg_id); + return; + } + csg_slot_prog_locked(ptdev, csg_id, new_csg_prio--); csgs_upd_ctx_queue_reqs(ptdev, &upd_ctx, csg_id, group->state == PANTHOR_CS_GROUP_SUSPENDED ? From 9acf6f34eb4875767e1684b9c469cf5ceb40fc69 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 1 Jun 2026 13:07:46 -0400 Subject: [PATCH 2023/2758] tracing/eprobes: Allow use of BTF names to dereference pointers [ Upstream commit 69efd863a78584b9416ed6be0e1e7349124b4a00 ] Add syntax to the parsing of eprobes to be able to typecast a trace event field that is a pointer to a structure. Currently, a dereference must be a number, where the user has to figure out manually the offset of a member of a structure that they want to dereference. But for event probes that records a field that happens to be a pointer to a structure, it cannot dereference these values with BTF naming, but must use numerical offsets. For example, to find out what device a sk_buff is pointing to in the net_dev_xmit trace event, one must first use gdb to find the offsets of the members of the structures: (gdb) p &((struct sk_buff *)0)->dev $1 = (struct net_device **) 0x10 (gdb) p &((struct net_device *)0)->name $2 = (char (*)[16]) 0x118 And then use the raw numbers to dereference: # echo 'e:xmit net.net_dev_xmit +0x118(+0x10($skbaddr)):string' >> dynamic_events If BTF is in the kernel, then instead, the skbaddr can be typecast to sk_buff and use the normal dereference logic. # echo 'e:xmit net.net_dev_xmit (sk_buff)skbaddr->dev->name:string' >> dynamic_events # echo 1 > events/eprobes/xmit/enable # cat trace [..] sshd-session-1022 [000] b..2. 860.249343: xmit: (net.net_dev_xmit) arg1="enp7s0" sshd-session-1022 [000] b..2. 860.250061: xmit: (net.net_dev_xmit) arg1="enp7s0" sshd-session-1022 [000] b..2. 860.250142: xmit: (net.net_dev_xmit) arg1="enp7s0" sshd-session-1022 [000] b..2. 860.263553: xmit: (net.net_dev_xmit) arg1="enp7s0" sshd-session-1022 [000] b..2. 860.283820: xmit: (net.net_dev_xmit) arg1="enp7s0" sshd-session-1022 [000] b..2. 860.302716: xmit: (net.net_dev_xmit) arg1="enp7s0" sshd-session-1022 [000] b..2. 860.322905: xmit: (net.net_dev_xmit) arg1="enp7s0" sshd-session-1022 [000] b..2. 860.342828: xmit: (net.net_dev_xmit) arg1="enp7s0" sshd-session-1022 [000] b..2. 860.362268: xmit: (net.net_dev_xmit) arg1="enp7s0" sshd-session-1022 [000] b..2. 860.382335: xmit: (net.net_dev_xmit) arg1="enp7s0" sshd-session-1022 [000] b..2. 860.400856: xmit: (net.net_dev_xmit) arg1="enp7s0" sshd-session-1022 [000] b..2. 860.419893: xmit: (net.net_dev_xmit) arg1="enp7s0" The syntax is simply: (STRUCT)(FIELD)->MEMBER[->MEMBER..] Also add comments around the #else and #endif of #ifdef CONFIG_PROBE_EVENTS_BTF_ARGS to know what they are for. Link: https://lore.kernel.org/all/20260601130746.2139d926@gandalf.local.home/ Signed-off-by: Steven Rostedt (Google) Signed-off-by: Masami Hiramatsu (Google) Stable-dep-of: 251a8fe1b9ae ("tracing/probes: Remove WARN_ON_ONCE from parse_btf_arg") Signed-off-by: Sasha Levin --- Documentation/trace/eprobetrace.rst | 4 + kernel/trace/trace_probe.c | 173 +++++++++++++++++++++++----- kernel/trace/trace_probe.h | 5 +- 3 files changed, 154 insertions(+), 28 deletions(-) diff --git a/Documentation/trace/eprobetrace.rst b/Documentation/trace/eprobetrace.rst index 89b5157cfab86..fe3602540569a 100644 --- a/Documentation/trace/eprobetrace.rst +++ b/Documentation/trace/eprobetrace.rst @@ -46,6 +46,10 @@ Synopsis of eprobe_events (x8/x16/x32/x64), VFS layer common type(%pd/%pD), "char", "string", "ustring", "symbol", "symstr" and "bitfield" are supported. + (STRUCT)FIELD->MEMBER[->MEMBER] : If BTF is supported, typecast FIELD to + a pointer to STRUCT and then derference the pointer defined by + ->MEMBER. Note that when this is used, the FIELD name does not + need to be prefixed with a '$'. Types ----- diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index 44c22d4e78818..196eff704bca9 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -332,6 +332,23 @@ static int parse_trace_event_arg(char *arg, struct fetch_insn *code, return -ENOENT; } +static int parse_trace_event(char *arg, struct fetch_insn *code, + struct traceprobe_parse_context *ctx) +{ + int ret; + + if (code->data) + return -EFAULT; + ret = parse_trace_event_arg(arg, code, ctx); + if (!ret) + return 0; + if (strcmp(arg, "comm") == 0 || strcmp(arg, "COMM") == 0) { + code->op = FETCH_OP_COMM; + return 0; + } + return -EINVAL; +} + #ifdef CONFIG_PROBE_EVENTS_BTF_ARGS static u32 btf_type_int(const struct btf_type *t) @@ -376,11 +393,16 @@ static bool btf_type_is_char_array(struct btf *btf, const struct btf_type *type) && BTF_INT_BITS(intdata) == 8; } +static struct btf *ctx_btf(struct traceprobe_parse_context *ctx) +{ + return ctx->struct_btf ? : ctx->btf; +} + static int check_prepare_btf_string_fetch(char *typename, struct fetch_insn **pcode, struct traceprobe_parse_context *ctx) { - struct btf *btf = ctx->btf; + struct btf *btf = ctx_btf(ctx); if (!btf || !ctx->last_type) return 0; @@ -506,6 +528,15 @@ static int query_btf_context(struct traceprobe_parse_context *ctx) return 0; } +static void clear_struct_btf(struct traceprobe_parse_context *ctx) +{ + if (ctx->struct_btf) { + btf_put(ctx->struct_btf); + ctx->struct_btf = NULL; + ctx->last_struct = NULL; + } +} + static void clear_btf_context(struct traceprobe_parse_context *ctx) { if (ctx->btf) { @@ -554,22 +585,29 @@ static int parse_btf_field(char *fieldname, const struct btf_type *type, struct fetch_insn *code = *pcode; const struct btf_member *field; u32 bitoffs, anon_offs; + bool is_struct = ctx->struct_btf != NULL; + struct btf *btf = ctx_btf(ctx); char *next; int is_ptr; s32 tid; do { - /* Outer loop for solving arrow operator ('->') */ - if (BTF_INFO_KIND(type->info) != BTF_KIND_PTR) { - trace_probe_log_err(ctx->offset, NO_PTR_STRCT); - return -EINVAL; - } - /* Convert a struct pointer type to a struct type */ - type = btf_type_skip_modifiers(ctx->btf, type->type, &tid); - if (!type) { - trace_probe_log_err(ctx->offset, BAD_BTF_TID); - return -EINVAL; + if (!is_struct) { + /* Outer loop for solving arrow operator ('->') */ + if (BTF_INFO_KIND(type->info) != BTF_KIND_PTR) { + trace_probe_log_err(ctx->offset, NO_PTR_STRCT); + return -EINVAL; + } + + /* Convert a struct pointer type to a struct type */ + type = btf_type_skip_modifiers(btf, type->type, &tid); + if (!type) { + trace_probe_log_err(ctx->offset, BAD_BTF_TID); + return -EINVAL; + } } + /* Only the first type can skip being a pointer */ + is_struct = false; bitoffs = 0; do { @@ -580,7 +618,7 @@ static int parse_btf_field(char *fieldname, const struct btf_type *type, return is_ptr; anon_offs = 0; - field = btf_find_struct_member(ctx->btf, type, fieldname, + field = btf_find_struct_member(btf, type, fieldname, &anon_offs); if (IS_ERR(field)) { trace_probe_log_err(ctx->offset, BAD_BTF_TID); @@ -602,7 +640,7 @@ static int parse_btf_field(char *fieldname, const struct btf_type *type, ctx->last_bitsize = 0; } - type = btf_type_skip_modifiers(ctx->btf, field->type, &tid); + type = btf_type_skip_modifiers(btf, field->type, &tid); if (!type) { trace_probe_log_err(ctx->offset, BAD_BTF_TID); return -EINVAL; @@ -640,7 +678,7 @@ static int parse_btf_arg(char *varname, int i, is_ptr, ret; u32 tid; - if (WARN_ON_ONCE(!ctx->funcname)) + if (WARN_ON_ONCE(!ctx->funcname && !(ctx->flags & TPARG_FL_TEVENT))) return -EINVAL; is_ptr = split_next_field(varname, &field, ctx); @@ -653,6 +691,19 @@ static int parse_btf_arg(char *varname, return -EOPNOTSUPP; } + if (ctx->flags & TPARG_FL_TEVENT) { + ret = parse_trace_event(varname, code, ctx); + if (ret < 0) { + trace_probe_log_err(ctx->offset, BAD_ATTACH_ARG); + return ret; + } + /* TEVENT is only here via a typecast */ + if (WARN_ON_ONCE(ctx->struct_btf == NULL)) + return -EINVAL; + type = ctx->last_struct; + goto found_type; + } + if (ctx->flags & TPARG_FL_RETURN && !strcmp(varname, "$retval")) { code->op = FETCH_OP_RETVAL; /* Check whether the function return type is not void */ @@ -709,6 +760,7 @@ static int parse_btf_arg(char *varname, found: type = btf_type_skip_modifiers(ctx->btf, tid, &tid); +found_type: if (!type) { trace_probe_log_err(ctx->offset, BAD_BTF_TID); return -EINVAL; @@ -727,7 +779,7 @@ static int parse_btf_arg(char *varname, static const struct fetch_type *find_fetch_type_from_btf_type( struct traceprobe_parse_context *ctx) { - struct btf *btf = ctx->btf; + struct btf *btf = ctx_btf(ctx); const char *typestr = NULL; if (btf && ctx->last_type) @@ -758,7 +810,67 @@ static int parse_btf_bitfield(struct fetch_insn **pcode, return 0; } -#else +static int query_btf_struct(const char *sname, struct traceprobe_parse_context *ctx) +{ + struct btf *btf = NULL; + int id; + + /* A struct_btf should only be used by a single argument */ + if (WARN_ON_ONCE(ctx->struct_btf)) { + btf_put(ctx->struct_btf); + ctx->struct_btf = NULL; + } + + id = bpf_find_btf_id(sname, BTF_KIND_STRUCT, &btf); + if (id < 0) + return id; + ctx->struct_btf = btf; + ctx->last_struct = btf_type_by_id(ctx->struct_btf, id); + return 0; +} + +static int handle_typecast(char *arg, struct fetch_insn **pcode, + struct fetch_insn *end, + struct traceprobe_parse_context *ctx) +{ + char *tmp; + int ret; + + /* Currently this only works for eprobes */ + if (!(ctx->flags & TPARG_FL_TEVENT)) { + trace_probe_log_err(ctx->offset, TYPECAST_NOT_EVENT); + return -EINVAL; + } + + tmp = strchr(arg, ')'); + if (!tmp) { + trace_probe_log_err(ctx->offset + strlen(arg), + DEREF_OPEN_BRACE); + return -EINVAL; + } + *tmp = '\0'; + ret = query_btf_struct(arg + 1, ctx); + *tmp = ')'; + + if (ret < 0) { + trace_probe_log_err(ctx->offset + 1, NO_PTR_STRCT); + return -EINVAL; + } + + tmp++; + + ctx->offset += tmp - arg; + ret = parse_btf_arg(tmp, pcode, end, ctx); + return ret; +} + +#else /* !CONFIG_PROBE_EVENTS_BTF_ARGS */ + +static void clear_struct_btf(struct traceprobe_parse_context *ctx) +{ + ctx->struct_btf = NULL; +} + static void clear_btf_context(struct traceprobe_parse_context *ctx) { ctx->btf = NULL; @@ -794,7 +906,15 @@ static int check_prepare_btf_string_fetch(char *typename, return 0; } -#endif +static int handle_typecast(char *arg, struct fetch_insn **pcode, + struct fetch_insn *end, + struct traceprobe_parse_context *ctx) +{ + trace_probe_log_err(ctx->offset, NOSUP_BTFARG); + return -EOPNOTSUPP; +} + +#endif /* CONFIG_PROBE_EVENTS_BTF_ARGS */ #ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API @@ -953,16 +1073,9 @@ static int parse_probe_vars(char *orig_arg, const struct fetch_type *t, int len; if (ctx->flags & TPARG_FL_TEVENT) { - if (code->data) - return -EFAULT; - ret = parse_trace_event_arg(arg, code, ctx); - if (!ret) - return 0; - if (strcmp(arg, "comm") == 0 || strcmp(arg, "COMM") == 0) { - code->op = FETCH_OP_COMM; - return 0; - } - goto inval; + if (parse_trace_event(arg, code, ctx) < 0) + goto inval; + return 0; } if (str_has_prefix(arg, "retval")) { @@ -1229,6 +1342,9 @@ parse_probe_arg(char *arg, const struct fetch_type *type, code->op = FETCH_OP_IMM; } break; + case '(': + ret = handle_typecast(arg, pcode, end, ctx); + break; default: if (isalpha(arg[0]) || arg[0] == '_') { /* BTF variable */ if (!tparg_is_function_entry(ctx->flags) && @@ -1561,6 +1677,9 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size, } kfree(tmp); + /* struct_btf should not be passed to other arguments */ + clear_struct_btf(ctx); + return ret; } diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h index df68d40de1610..de489f98b8a72 100644 --- a/kernel/trace/trace_probe.h +++ b/kernel/trace/trace_probe.h @@ -422,7 +422,9 @@ struct traceprobe_parse_context { const struct btf_param *params; /* Parameter of the function */ s32 nr_params; /* The number of the parameters */ struct btf *btf; /* The BTF to be used */ + struct btf *struct_btf; /* The BTF to be used for structs */ const struct btf_type *last_type; /* Saved type */ + const struct btf_type *last_struct; /* Saved structure */ u32 last_bitoffs; /* Saved bitoffs */ u32 last_bitsize; /* Saved bitsize */ struct trace_probe *tp; @@ -563,7 +565,8 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call, C(NEED_STRING_TYPE, "$comm and immediate-string only accepts string type"),\ C(TOO_MANY_ARGS, "Too many arguments are specified"), \ C(TOO_MANY_EARGS, "Too many entry arguments specified"), \ - C(EVENT_TOO_BIG, "Event too big (too many fields?)"), + C(EVENT_TOO_BIG, "Event too big (too many fields?)"), \ + C(TYPECAST_NOT_EVENT, "Typecasts are only for eprobe fields"), #undef C #define C(a, b) TP_ERR_##a From 8a662d8c05e2cf16f7a047c81bc1eb5d3b29bacd Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Thu, 25 Jun 2026 08:34:46 +0900 Subject: [PATCH 2024/2758] tracing/probes: Remove WARN_ON_ONCE from parse_btf_arg [ Upstream commit 251a8fe1b9aedccd298b77bc28426d564c5a923f ] Sashiko found that user can cause this WARN_ON_ONCE() easily with adding a kprobe event based on a raw address with BTF parameter. Since this is not an unexpected condition, remove the WARN_ON_ONCE(). Link: https://lore.kernel.org/all/178177265367.2059927.13789953014706792126.stgit@mhiramat.tok.corp.google.com/ Link: https://sashiko.dev/#/patchset/178165816303.269421.7302603996990753309.stgit%40devnote2 Reported-by: Sashiko Fixes: b576e09701c7 ("tracing/probes: Support function parameters if BTF is available") Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Sasha Levin --- kernel/trace/trace_probe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index 196eff704bca9..0b1277f03dfcb 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -678,7 +678,7 @@ static int parse_btf_arg(char *varname, int i, is_ptr, ret; u32 tid; - if (WARN_ON_ONCE(!ctx->funcname && !(ctx->flags & TPARG_FL_TEVENT))) + if (!ctx->funcname && !(ctx->flags & TPARG_FL_TEVENT)) return -EINVAL; is_ptr = split_next_field(varname, &field, ctx); From 10a33029e1cf9edf441bb91ec9f4a161ca97ca3a Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Thu, 25 Jun 2026 08:34:46 +0900 Subject: [PATCH 2025/2758] tracing/events: Fix to check the simple_tsk_fn creation [ Upstream commit cda1fbfc5313bb90daa271d45eea4a8d317a8544 ] Sashiko pointed that this sample code does not correctly handle the failure of thread creation because kthread_run() can return -errno. Check the simple_tsk_fn is correctly initialized (created) or not. Link: https://lore.kernel.org/all/178165817322.269421.3992299509400184196.stgit@devnote2/ Link: https://sashiko.dev/#/patchset/178092865666.163648.10457567771536160909.stgit%40devnote2 Fixes: 9cfe06f8cd5c ("tracing/events: add trace-events-sample") Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Sasha Levin --- samples/trace_events/trace-events-sample.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/samples/trace_events/trace-events-sample.c b/samples/trace_events/trace-events-sample.c index ecc7db237f2ef..0b7a6efdb247a 100644 --- a/samples/trace_events/trace-events-sample.c +++ b/samples/trace_events/trace-events-sample.c @@ -107,6 +107,10 @@ int foo_bar_reg(void) * for consistency sake, we still take the thread_mutex. */ simple_tsk_fn = kthread_run(simple_thread_fn, NULL, "event-sample-fn"); + if (IS_ERR_OR_NULL(simple_tsk_fn)) { + pr_err("Failed to create simple_thread_fn\n"); + simple_tsk_fn = NULL; + } out: mutex_unlock(&thread_mutex); return 0; From f4461db8eb8ed375917c38d93d6def60e3bb06b0 Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Thu, 25 Jun 2026 08:34:46 +0900 Subject: [PATCH 2026/2758] tracing: eprobe: read the complete FILTER_PTR_STRING pointer [ Upstream commit 206b25c09080cc20fd4c2bea12d59df4b7ba2121 ] For a char * element in an event, the FILTER_PTR_STRING filter type is used. When the event occurs, a pointer is stored in the ringbuffer. If an eprobe references such a char * element of a "base event", the stored pointer is truncated when it's read from the ringbuffer. $ cd /sys/kernel/tracing $ echo 'e rcu.rcu_utilization $s:x64 $s:string' > dynamic_events $ echo 1 > tracing_on $ echo 1 > events/eprobes/enable $ sleep 1 $ echo 0 > events/eprobes/enable $ cat trace -0 ...: (rcu.rcu_utilization) arg1=0x4f arg2=(fault) -0 ...: (rcu.rcu_utilization) arg1=0x2 arg2=(fault) The problem is in get_event_field val = (unsigned long)(*(char *)addr); addr points to the position in the ringbuffer where the pointer was stored. The assignment reads only the lowest byte of the pointer. Fix the cast to read the whole pointer. The output of the test above is now -0 ... arg1=0xffffffff81c7d3f3 arg2="Start scheduler-tick" -0 ... arg1=0xffffffff81c57340 arg2="End scheduler-tick" Link: https://lore.kernel.org/all/20260620145339.3234726-1-martin@kaiser.cx/ Fixes: f04dec93466a ("tracing/eprobes: Fix reading of string fields") Signed-off-by: Martin Kaiser Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Sasha Levin --- kernel/trace/trace_eprobe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace_eprobe.c b/kernel/trace/trace_eprobe.c index b66d6196338d0..50518b0714144 100644 --- a/kernel/trace/trace_eprobe.c +++ b/kernel/trace/trace_eprobe.c @@ -315,7 +315,7 @@ get_event_field(struct fetch_insn *code, void *rec) val = (unsigned long)addr; break; case FILTER_PTR_STRING: - val = (unsigned long)(*(char *)addr); + val = *(unsigned long *)addr; break; default: WARN_ON_ONCE(1); From d655cca1c6e67eb081214d37eb231a869ead2f97 Mon Sep 17 00:00:00 2001 From: Sechang Lim Date: Thu, 25 Jun 2026 08:34:48 +0900 Subject: [PATCH 2027/2758] tracing/fprobe: Fix NULL pointer dereference in fprobe_fgraph_entry() [ Upstream commit 367c49d6e283c17b56a31e7a8d964a079244264c ] fprobe_fgraph_entry() sizes a shadow-stack reservation in one walk of the per-ip fprobe list and fills it in a second walk, both under rcu_read_lock() only. A fprobe registered on an already-live ip can become visible between the two walks, so the fill walk processes an exit_handler the sizing walk did not count and used runs past reserved_words. If the sizing walk counted nothing, fgraph_data is NULL and the first write_fprobe_header() faults: Oops: general protection fault, probably for non-canonical address ... KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] RIP: 0010:fprobe_fgraph_entry+0xa38/0xf10 kernel/trace/fprobe.c:167 Call Trace: function_graph_enter_regs+0x44c/0xa10 kernel/trace/fgraph.c:677 ftrace_graph_func+0xc5/0x140 arch/x86/kernel/ftrace.c:671 __kernel_text_address+0x9/0x40 kernel/extable.c:78 arch_stack_walk+0x117/0x170 arch/x86/kernel/stacktrace.c:26 kmem_cache_free+0x188/0x580 mm/slub.c:6378 tcp_data_queue+0x18d/0x6550 net/ipv4/tcp_input.c:5590 [...] The list cannot be frozen across the two walks, so skip a node that does not fit the reservation and count it as missed. Link: https://lore.kernel.org/all/20260619184425.3824774-1-rhkrqnwk98@gmail.com/ Fixes: 4346ba160409 ("fprobe: Rewrite fprobe on function-graph tracer") Signed-off-by: Sechang Lim Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Sasha Levin --- kernel/trace/fprobe.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c index f378613ad1209..f215990b90613 100644 --- a/kernel/trace/fprobe.c +++ b/kernel/trace/fprobe.c @@ -613,6 +613,16 @@ static int fprobe_fgraph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops continue; data_size = fp->entry_data_size; + /* + * The list may have grown since it was sized, so this node + * may not fit. Skip it as missed rather than overrun the + * reservation. + */ + if (fp->exit_handler && + used + FPROBE_HEADER_SIZE_IN_LONG + SIZE_IN_LONG(data_size) > reserved_words) { + fp->nmissed++; + continue; + } if (data_size && fp->exit_handler) data = fgraph_data + used + FPROBE_HEADER_SIZE_IN_LONG; else From 57e1f2cd6a0e890ee2eadbd93de3dcd5e22653a5 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Thu, 25 Jun 2026 08:34:48 +0900 Subject: [PATCH 2028/2758] tracing/probes: Make the $ prefix mandatory for comm access [ Upstream commit a369299c3f785cf556bbef2de2db0aa2d294c4c9 ] Since $comm or $COMM are not event field but special fetcharg variables to access current->comm, It should not be accessed without '$' prefix even with typecast. Link: https://lore.kernel.org/all/178231209724.732967.12049805699091810641.stgit@devnote2/ Fixes: 69efd863a785 ("tracing/eprobes: Allow use of BTF names to dereference pointers") Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Sasha Levin --- kernel/trace/trace_probe.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index 0b1277f03dfcb..3f928ed35180b 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -342,10 +342,6 @@ static int parse_trace_event(char *arg, struct fetch_insn *code, ret = parse_trace_event_arg(arg, code, ctx); if (!ret) return 0; - if (strcmp(arg, "comm") == 0 || strcmp(arg, "COMM") == 0) { - code->op = FETCH_OP_COMM; - return 0; - } return -EINVAL; } @@ -1073,8 +1069,14 @@ static int parse_probe_vars(char *orig_arg, const struct fetch_type *t, int len; if (ctx->flags & TPARG_FL_TEVENT) { - if (parse_trace_event(arg, code, ctx) < 0) + if (parse_trace_event(arg, code, ctx) < 0) { + /* 'comm' should be checked after field parsing. */ + if (strcmp(arg, "comm") == 0 || strcmp(arg, "COMM") == 0) { + code->op = FETCH_OP_COMM; + return 0; + } goto inval; + } return 0; } From 5459f4f32a8eb78908348c8a0fd6a89221247a08 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Sun, 28 Jun 2026 18:07:23 -0400 Subject: [PATCH 2029/2758] irqchip/gic-v3-its: Fix OF node reference leak [ Upstream commit 2e1368a9d61bdf5502ddade004f223a5831c5b8c ] of_get_cpu_node() returns a referenced device node. In its_cpu_init_collection(), the Cavium 23144 workaround only uses the node to compare the CPU NUMA node, but the reference is never dropped. Use the device_node cleanup helper for the CPU node reference so it is released when leaving the workaround block, including the NUMA mismatch return path. Fixes: fbf8f40e1658 ("irqchip/gicv3-its: numa: Enable workaround for Cavium thunderx erratum 23144") Signed-off-by: Yuho Choi Signed-off-by: Thomas Gleixner Reviewed-by: Zenghui Yu (Huawei) Acked-by: Marc Zyngier Signed-off-by: Sasha Levin --- drivers/irqchip/irq-gic-v3-its.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 291d7668cc8da..5f9f58ff758cc 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -3290,11 +3290,9 @@ static void its_cpu_init_collection(struct its_node *its) /* avoid cross node collections and its mapping */ if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_23144) { - struct device_node *cpu_node; + struct device_node *cpu_node __free(device_node) = of_get_cpu_node(cpu, NULL); - cpu_node = of_get_cpu_node(cpu, NULL); - if (its->numa_node != NUMA_NO_NODE && - its->numa_node != of_node_to_nid(cpu_node)) + if (its->numa_node != NUMA_NO_NODE && its->numa_node != of_node_to_nid(cpu_node)) return; } From 9ed0dca2aa05908b33ecf0bb15c7953947529542 Mon Sep 17 00:00:00 2001 From: Qingshuang Fu Date: Tue, 23 Jun 2026 09:52:11 +0800 Subject: [PATCH 2030/2758] irqchip/ts4800: Fix missing chained handler cleanup on remove [ Upstream commit 98bf7e54cec07d514b3575c11896a8b12d50ecc4 ] The driver installs a chained handler for the parent interrupt during probe using irq_set_chained_handler_and_data(), but the remove function does not clear this handler. This leaves a dangling handler that may be called when the parent interrupt fires after the driver has been removed, potentially accessing freed memory and causing a kernel crash. Additionally, the parent_irq obtained via irq_of_parse_and_map() is not stored, making it inaccessible in the remove function. Moreover, interrupt mappings created during probe are not properly disposed. Fix this by: - Saving parent_irq in probe - Clearing the chained handler with NULL in ts4800_ic_remove() - Disposing all IRQ mappings before domain removal to prevent resource leaks Fixes: d01f8633d52e ("irqchip/ts4800: Add TS-4800 interrupt controller") Signed-off-by: Qingshuang Fu Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260623015211.109382-1-fffsqian@163.com Signed-off-by: Sasha Levin --- drivers/irqchip/irq-ts4800.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/irqchip/irq-ts4800.c b/drivers/irqchip/irq-ts4800.c index 2e4013c6834de..c7c0b155e353e 100644 --- a/drivers/irqchip/irq-ts4800.c +++ b/drivers/irqchip/irq-ts4800.c @@ -28,6 +28,7 @@ struct ts4800_irq_data { void __iomem *base; struct platform_device *pdev; struct irq_domain *domain; + unsigned int parent_irq; }; static void ts4800_irq_mask(struct irq_data *d) @@ -134,6 +135,7 @@ static int ts4800_ic_probe(struct platform_device *pdev) irq_set_chained_handler_and_data(parent_irq, ts4800_ic_chained_handle_irq, data); + data->parent_irq = parent_irq; platform_set_drvdata(pdev, data); return 0; @@ -142,6 +144,14 @@ static int ts4800_ic_probe(struct platform_device *pdev) static void ts4800_ic_remove(struct platform_device *pdev) { struct ts4800_irq_data *data = platform_get_drvdata(pdev); + unsigned int hwirq; + + irq_set_chained_handler_and_data(data->parent_irq, NULL, NULL); + + for (hwirq = 0; hwirq < 8; hwirq++) + irq_dispose_mapping(irq_find_mapping(data->domain, hwirq)); + + irq_dispose_mapping(data->parent_irq); irq_domain_remove(data->domain); } From ac39628cb3ef66a305d5cc5aa25ea64404e2032a Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Sat, 27 Jun 2026 00:02:29 +0800 Subject: [PATCH 2031/2758] spi: rzv2h-rspi: Fix DMA transfer error handling for signal interruption [ Upstream commit 7fc2c3dcae28347a30ccd76c8817e5719005f1c3 ] wait_event_interruptible_timeout() can return a negative error code when interrupted by a signal. The original code treated all non-zero return values as success, which would incorrectly synchronize DMA channels and return 0 instead of propagating the interruption error. Fixes: fa08b566860b ("spi: rzv2h-rspi: add support for DMA mode") Signed-off-by: Felix Gu Reviewed-by: Cosmin Tanislav Tested-by: Cosmin Tanislav Reviewed-by: Wolfram Sang Link: https://patch.msgid.link/20260627-rspi-v1-1-170c93ee14da@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-rzv2h-rspi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-rzv2h-rspi.c b/drivers/spi/spi-rzv2h-rspi.c index 6ed3fad873b8c..190d7eb2034af 100644 --- a/drivers/spi/spi-rzv2h-rspi.c +++ b/drivers/spi/spi-rzv2h-rspi.c @@ -366,14 +366,14 @@ static int rzv2h_rspi_transfer_dma(struct rzv2h_rspi_priv *rspi, rzv2h_rspi_clear_all_irqs(rspi); ret = wait_event_interruptible_timeout(rspi->wait, rspi->dma_callbacked, HZ); - if (ret) { + if (ret > 0) { dmaengine_synchronize(rspi->controller->dma_tx); dmaengine_synchronize(rspi->controller->dma_rx); ret = 0; } else { dmaengine_terminate_sync(rspi->controller->dma_tx); dmaengine_terminate_sync(rspi->controller->dma_rx); - ret = -ETIMEDOUT; + ret = ret ?: -ETIMEDOUT; } enable_irq(rspi->irq_rx); From c3e5cac47519d77ad36b9c03a1df1536aaa0c4a1 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Mon, 29 Jun 2026 14:31:14 -0400 Subject: [PATCH 2032/2758] sctp: fix addr_wq_timer race in sctp_free_addr_wq() [ Upstream commit 976c19de0f22a857ba0112f39635f8fd7a257568 ] sctp_free_addr_wq() previously removed addr_wq_timer using timer_delete() while holding addr_wq_lock. However, timer_delete() does not guarantee that a currently running timer handler has completed. This allows a race with sctp_addr_wq_timeout_handler(), where the handler may still run after addr_waitq has been freed, acquire addr_wq_lock, and access freed memory, leading to a use-after-free. Fix this by calling timer_shutdown_sync() before taking addr_wq_lock. This guarantees that any in-flight timer handler has finished and prevents the timer from being re-armed during teardown, making subsequent cleanup safe. Fixes: 4db67e808640 ("sctp: Make the address lists per network namespace") Reported-by: Sashiko Signed-off-by: Xin Long Link: https://patch.msgid.link/5dc95f295bdb5c3f60e880dd9aa5112dc5c071cc.1782757874.git.lucien.xin@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sctp/protocol.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 5c6fa8e8d34d3..e95061b672ef4 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -663,8 +663,9 @@ static void sctp_free_addr_wq(struct net *net) struct sctp_sockaddr_entry *addrw; struct sctp_sockaddr_entry *temp; + timer_shutdown_sync(&net->sctp.addr_wq_timer); + spin_lock_bh(&net->sctp.addr_wq_lock); - timer_delete(&net->sctp.addr_wq_timer); list_for_each_entry_safe(addrw, temp, &net->sctp.addr_waitq, list) { list_del(&addrw->list); kfree(addrw); From 13741bad74d452d21ac6bdcca39804405581f03a Mon Sep 17 00:00:00 2001 From: Longjun Tang Date: Mon, 29 Jun 2026 10:42:30 +0800 Subject: [PATCH 2033/2758] virtio_net: disable cb when NAPI is busy-polled [ Upstream commit 1eb8fc67ca41db71c90866ff76c990d85247daef ] When busy-poll is active, napi_schedule_prep() returns false in virtqueue_napi_schedule(), so virtqueue_disable_cb() is skipped. The device may keep firing irqs until reaches virtqueue_napi_complete(). Under load (received == budget), it will lead to a large number of spurious interrupts. Fix it by disabling the callback at the virtnet_poll() entry. This keeps the callback off while we poll and it is re-enabled by virtqueue_napi_complete() when going idle. Fixes: ceef438d613f ("virtio_net: remove custom busy_poll") Acked-by: Michael S. Tsirkin Signed-off-by: Longjun Tang Link: https://patch.msgid.link/20260629024230.37325-1-lange_tang@163.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/virtio_net.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 26afa6341d161..3e2a5876c6c8c 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -3011,6 +3011,9 @@ static int virtnet_poll(struct napi_struct *napi, int budget) unsigned int xdp_xmit = 0; bool napi_complete; + if (budget) + virtqueue_disable_cb(rq->vq); + virtnet_poll_cleantx(rq, budget); received = virtnet_receive(rq, budget, &xdp_xmit); From 9717091371d71984f1773b4c679690690ff4acf8 Mon Sep 17 00:00:00 2001 From: Gleb Markov Date: Mon, 29 Jun 2026 16:08:54 +0300 Subject: [PATCH 2034/2758] cxgb4: Fix decode strings dump for T6 adapters [ Upstream commit 5d6dc22d62682d93f5f55f145ad792f2891de911 ] Depending on the value of chip_version, the correct decode set is selected. However, the subsequent matching with the t4 encoding type in the if-else block results in a reassignment, which leads to the loss of support for t6_decode as well as reinitializing of values t4_decode and t5_decode. The component history shows that the if-else block previously used for this purpose, as well as the execution order, was not affected by the change. Furthermore, it is suggested by the execution order that the scenario with overwriting and loss of support will be implemented. Delete the if-else block. Fixes: 6df397539cb0 ("cxgb4: Update correct encoding of SGE Ingress DMA States for T6 adapter") Signed-off-by: Gleb Markov Reviewed-by: Potnuri Bharat Teja Link: https://patch.msgid.link/20260629130856.1168-1-markov.gi@npc-ksb.ru Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/chelsio/cxgb4/t4_hw.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 171750fad44f2..6871127427fad 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -6737,14 +6737,6 @@ void t4_sge_decode_idma_state(struct adapter *adapter, int state) return; } - if (is_t4(adapter->params.chip)) { - sge_idma_decode = (const char **)t4_decode; - sge_idma_decode_nstates = ARRAY_SIZE(t4_decode); - } else { - sge_idma_decode = (const char **)t5_decode; - sge_idma_decode_nstates = ARRAY_SIZE(t5_decode); - } - if (state < sge_idma_decode_nstates) CH_WARN(adapter, "idma state %s\n", sge_idma_decode[state]); else From 24e63c47668a7cdb4aa0ae77f22e40e0b7745bca Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 29 Jun 2026 16:39:23 -0700 Subject: [PATCH 2035/2758] selftests: drv-net: tso: don't touch dangerous feature bits [ Upstream commit 2f7f2e311106cb838d3f3fb6ef25effdb3f8e366 ] query_nic_features() detects which offloads depend on tx-gso-partial by enabling everything, turning tx-gso-partial off, and seeing which active features drop out. Enabling all hw features is dangerous: we may end up enabling rx-fcs and loopback for example. For the ice driver we end up getting into problems with feature dependencies so the cleanup isn't successful either, and the test exits with rx-fcs and loopback enabled. Scope the feature probing just to segmentation bits. Fixes: 266b835e5e84 ("selftests: drv-net: tso: enable test cases based on hw_features") Reviewed-by: Pavan Chebbi Reviewed-by: Daniel Zahka Link: https://patch.msgid.link/20260629233923.2151144-1-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- tools/testing/selftests/drivers/net/hw/tso.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/tools/testing/selftests/drivers/net/hw/tso.py b/tools/testing/selftests/drivers/net/hw/tso.py index bb675e3dac88f..abc575e4b3406 100755 --- a/tools/testing/selftests/drivers/net/hw/tso.py +++ b/tools/testing/selftests/drivers/net/hw/tso.py @@ -187,28 +187,24 @@ def query_nic_features(cfg) -> None: cfg.wanted_features.add(f["name"]) cfg.hw_features = set() - hw_all_features_cmd = "" for f in features["hw"]["bits"]["bit"]: if f.get("value", False): - feature = f["name"] - cfg.hw_features.add(feature) - hw_all_features_cmd += f" {feature} on" - try: - ethtool(f"-K {cfg.ifname} {hw_all_features_cmd}") - except Exception as e: - ksft_pr(f"WARNING: failure enabling all hw features: {e}") - ksft_pr("partial gso feature detection may be impacted") + cfg.hw_features.add(f["name"]) # Check which features are supported via GSO partial cfg.partial_features = set() if 'tx-gso-partial' in cfg.hw_features: + seg_features = {f for f in cfg.hw_features if "segmentation" in f} + ethtool(f"-K {cfg.ifname} " + + " ".join(f"{f} on" for f in seg_features)) + ethtool(f"-K {cfg.ifname} tx-gso-partial off") no_partial = set() features = cfg.ethnl.features_get({"header": {"dev-index": cfg.ifindex}}) for f in features["active"]["bits"]["bit"]: no_partial.add(f["name"]) - cfg.partial_features = cfg.hw_features - no_partial + cfg.partial_features = seg_features - no_partial ethtool(f"-K {cfg.ifname} tx-gso-partial on") restore_wanted_features(cfg) From b0933dede95d525bad44d593d35cb8cee75cbe20 Mon Sep 17 00:00:00 2001 From: Sechang Lim Date: Mon, 29 Jun 2026 15:41:06 +0000 Subject: [PATCH 2036/2758] net/sched: act_bpf: use rcu_dereference_bh() to read the filter [ Upstream commit adc49c7ba690c9b33b8392ec27397456b65d0893 ] tcf_bpf_act() can run from the tc egress path, which holds only rcu_read_lock_bh(), but reads prog->filter with rcu_dereference() and trips lockdep: WARNING: suspicious RCU usage net/sched/act_bpf.c:47 suspicious rcu_dereference_check() usage! 1 lock held by syz.2.1588/12756: #0: (rcu_read_lock_bh){....}-{1:3}, at: __dev_queue_xmit net/core/dev.c:4792 tcf_bpf_act+0x6ae/0x940 net/sched/act_bpf.c:47 tcf_classify+0x6e4/0x1080 net/sched/cls_api.c:1860 sch_handle_egress net/core/dev.c:4545 [inline] __dev_queue_xmit+0x2185/0x2c00 net/core/dev.c:4808 packet_sendmsg+0x3dfa/0x5120 net/packet/af_packet.c:3114 The other tc actions and cls_bpf already use rcu_dereference_bh() here. Do the same. Fixes: 1f211a1b929c ("net, sched: add clsact qdisc") Signed-off-by: Sechang Lim Reviewed-by: Amery Hung Link: https://patch.msgid.link/20260629154112.1164986-1-rhkrqnwk98@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/act_bpf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c index c2b5bc19e0911..284800b7d3c55 100644 --- a/net/sched/act_bpf.c +++ b/net/sched/act_bpf.c @@ -44,7 +44,7 @@ TC_INDIRECT_SCOPE int tcf_bpf_act(struct sk_buff *skb, tcf_lastuse_update(&prog->tcf_tm); bstats_update(this_cpu_ptr(prog->common.cpu_bstats), skb); - filter = rcu_dereference(prog->filter); + filter = rcu_dereference_bh(prog->filter); if (at_ingress) { __skb_push(skb, skb->mac_len); filter_res = bpf_prog_run_data_pointers(filter, skb); From 15a9e9b8f7f5d7f380ae54c6f5bcbc0bdcb0f3cd Mon Sep 17 00:00:00 2001 From: Haofeng Li Date: Thu, 25 Jun 2026 14:48:07 +0000 Subject: [PATCH 2037/2758] ksmbd: reject undersized DACLs before parsing ACEs [ Upstream commit 60908f7ebcd9b6cde74ad5711fab0f49c7970949 ] parse_dacl() limits the attacker-controlled ACE count by comparing it with the number of minimal ACEs that fit in the DACL size. The DACL size field is 16 bits, but the expression subtracts sizeof(struct smb_acl). Because sizeof() is unsigned, a DACL size smaller than the ACL header underflows to a large size_t. A malicious client can reach this with: SMB2_SET_INFO (InfoType=SMB2_O_INFO_SECURITY) -> smb2_set_info_sec() -> set_info_sec() -> parse_sec_desc() -> parse_dacl() -> init_acl_state(..., 0xffff) -> init_acl_state(..., 0xffff) -> kmalloc_objs(..., 0xffff) Thus a malformed security descriptor can make num_aces pass the guard and drive large temporary ACL state and pointer-array allocations. Reject DACLs smaller than struct smb_acl before doing the subtraction, so the ACE count check cannot be bypassed by the underflow. Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") Signed-off-by: Haofeng Li Reviewed-by: ChenXiaoSong Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/server/smbacl.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c index 340ea98fa494c..fc9937cedb012 100644 --- a/fs/smb/server/smbacl.c +++ b/fs/smb/server/smbacl.c @@ -374,6 +374,7 @@ static void parse_dacl(struct mnt_idmap *idmap, { int i, ret; u16 num_aces = 0; + u16 dacl_size; unsigned int acl_size; char *acl_base; struct smb_ace **ppace; @@ -403,7 +404,11 @@ static void parse_dacl(struct mnt_idmap *idmap, if (num_aces <= 0) return; - if (num_aces > (le16_to_cpu(pdacl->size) - sizeof(struct smb_acl)) / + dacl_size = le16_to_cpu(pdacl->size); + if (dacl_size < sizeof(struct smb_acl)) + return; + + if (num_aces > (dacl_size - sizeof(struct smb_acl)) / (offsetof(struct smb_ace, sid) + offsetof(struct smb_sid, sub_auth) + sizeof(__le16))) return; From ed98719be41389d416953b8ef9f07a07dfea6b2b Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Fri, 26 Jun 2026 20:38:20 +0300 Subject: [PATCH 2038/2758] ksmbd: fix use-after-free of fp->owner.name in durable handle owner check [ Upstream commit 38637163501fd9e2f684b8cd275d0db5d79f37c6 ] Two concurrent SMB2 durable reconnects (DH2C/DHnC) on the same persistent_id race the fp->owner.name compare-read in ksmbd_vfs_compare_durable_owner() against the kfree() in ksmbd_reopen_durable_fd()'s reopen-success path. fp->owner.name is a standalone kstrdup() buffer whose lifetime is independent of the fp refcount, and the two sites share no lock: the compare reads the buffer while the reopen frees it, so the strcmp() can dereference freed memory. Commit 7ce4fc40018d ("ksmbd: fix durable reconnect double-bind race in ksmbd_reopen_durable_fd") made the fp->conn claim atomic under global_ft.lock (closing the owner.name double-free and the ksmbd_file write-UAF), but the compare-read versus reopen-free pair was left unserialized. BUG: KASAN: slab-use-after-free in strcmp+0x2c/0x80 Read of size 1 by task kworker strcmp ksmbd_vfs_compare_durable_owner smb2_check_durable_oplock smb2_open Freed by task kworker: kfree ksmbd_reopen_durable_fd smb2_open Allocated by task kworker: kstrdup session_fd_check smb2_session_logoff The buggy address belongs to the cache kmalloc-8 Serialize both sides of the race with fp->f_lock. The global durable file-table lock still protects the durable reconnect claim, but fp->owner.name is per-open state and does not need to block unrelated durable table lookups or reconnects. The teardown is left at its existing location after the reopen-success point so that an __open_id() rollback still retains owner.name for a later legitimate reconnect to verify. Fixes: 49110a8ce654 ("ksmbd: validate owner of durable handle on reconnect") Assisted-by: Henry (Claude):claude-opus-4 Signed-off-by: Gil Portnoy Co-developed-by: Namjae Jeon Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/server/vfs_cache.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 39c56942ae440..aa0924c9fdf96 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -1229,16 +1229,21 @@ void ksmbd_stop_durable_scavenger(void) static int ksmbd_vfs_copy_durable_owner(struct ksmbd_file *fp, struct ksmbd_user *user) { + char *name; + if (!user) return -EINVAL; /* Duplicate the user name to ensure identity persistence */ - fp->owner.name = kstrdup(user->name, GFP_KERNEL); - if (!fp->owner.name) + name = kstrdup(user->name, GFP_KERNEL); + if (!name) return -ENOMEM; + spin_lock(&fp->f_lock); fp->owner.uid = user->uid; fp->owner.gid = user->gid; + fp->owner.name = name; + spin_unlock(&fp->f_lock); return 0; } @@ -1256,18 +1261,24 @@ static int ksmbd_vfs_copy_durable_owner(struct ksmbd_file *fp, bool ksmbd_vfs_compare_durable_owner(struct ksmbd_file *fp, struct ksmbd_user *user) { - if (!user || !fp->owner.name) + bool ret = false; + + if (!user) return false; + spin_lock(&fp->f_lock); + if (!fp->owner.name) + goto out; + /* Check if the UID and GID match first (fast path) */ if (fp->owner.uid != user->uid || fp->owner.gid != user->gid) - return false; + goto out; /* Validate the account name to ensure the same SecurityContext */ - if (strcmp(fp->owner.name, user->name)) - return false; - - return true; + ret = (strcmp(fp->owner.name, user->name) == 0); +out: + spin_unlock(&fp->f_lock); + return ret; } static bool session_fd_check(struct ksmbd_tree_connect *tcon, @@ -1460,9 +1471,11 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) } up_write(&ci->m_lock); + spin_lock(&fp->f_lock); fp->owner.uid = fp->owner.gid = 0; kfree(fp->owner.name); fp->owner.name = NULL; + spin_unlock(&fp->f_lock); return 0; } From 2ef42bd9a8b7a99fbbe3e0b825783ca145ba27c0 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Tue, 30 Jun 2026 17:51:48 +0300 Subject: [PATCH 2039/2758] gpio: timberdale: Return -ENOMEM on dynamic memory allocation in probe [ Upstream commit 8d7e62d5e9b2d2ff146f472a9215d7e29c7e2307 ] Out of memory situation on driver's probe is expected to be reported to the driver's framework with a proper -ENOMEM error code. Fixes: 35570ac6039e ("gpio: add GPIO driver for the Timberdale FPGA") Signed-off-by: Vladimir Zapolskiy Link: https://patch.msgid.link/20260630145148.4081967-1-vz@kernel.org Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpio-timberdale.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-timberdale.c b/drivers/gpio/gpio-timberdale.c index f488939dd00a8..b77c9d16fd166 100644 --- a/drivers/gpio/gpio-timberdale.c +++ b/drivers/gpio/gpio-timberdale.c @@ -235,7 +235,7 @@ static int timbgpio_probe(struct platform_device *pdev) tgpio = devm_kzalloc(dev, sizeof(*tgpio), GFP_KERNEL); if (!tgpio) - return -EINVAL; + return -ENOMEM; tgpio->irq_base = pdata->irq_base; From feba85c0eeda85d730fe8bcd6c0be8eb5e2b40d4 Mon Sep 17 00:00:00 2001 From: Viacheslav Bocharov Date: Thu, 25 Jun 2026 14:57:18 +0300 Subject: [PATCH 2040/2758] pinctrl: meson: restore non-sleeping GPIO access [ Upstream commit 9777530157e7b82fd994327ff878c4245dadc931 ] Commit 28f240683871 ("pinctrl: meson: mark the GPIO controller as sleeping") set gpio_chip.can_sleep = true to work around gpio-shared-proxy holding a spinlock across a sleeping pinctrl config path. That locking bug is now fixed in the shared-proxy itself ("gpio: shared-proxy: always serialize with a sleeping mutex"), so the controller-wide workaround is no longer needed; the meson GPIO controller does not sleep. meson_gpio_get/set/direction_* access MMIO through regmap. The regmap_mmio bus uses fast I/O (spinlock) locking, so these value callbacks do not contain sleeping operations. Since gpio_chip.can_sleep describes the get/set value path, restore can_sleep = false. Marking the controller sleeping also broke atomic value consumers such as w1-gpio (1-Wire bitbang): w1_io.c runs its read time slot under local_irq_save() and uses the non-cansleep gpiod_set_value() / gpiod_get_value(), which with can_sleep=true trigger WARN_ON(can_sleep) in gpiolib on every transferred bit (from w1_gpio_write_bit() / w1_gpio_read_bit() via w1_reset_bus() and w1_search()). The printk and stack dump inside the IRQs-off, microsecond-scale time slot destroy the bit timing, so reset/presence detection and ROM search fail: the bus master registers but w1_master_slave_count stays at 0 and no devices are found. Verified on an Amlogic A113X board (DS18B20 on GPIOA_14): with can_sleep restored to false the warnings are gone and the sensor is detected and read again. This must not be applied or backported without the shared-proxy locking fix above; otherwise the original Khadas VIM3 splat returns on boards that genuinely share a meson GPIO. Fixes: 28f240683871 ("pinctrl: meson: mark the GPIO controller as sleeping") Link: https://lore.kernel.org/all/20260105150509.56537-1-bartosz.golaszewski@oss.qualcomm.com/ Signed-off-by: Viacheslav Bocharov Acked-by: Linus Walleij Link: https://patch.msgid.link/20260625115718.1678991-3-v@baodeep.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/pinctrl/meson/pinctrl-meson.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/meson/pinctrl-meson.c b/drivers/pinctrl/meson/pinctrl-meson.c index 4507dc8b5563c..18295b15ecd9d 100644 --- a/drivers/pinctrl/meson/pinctrl-meson.c +++ b/drivers/pinctrl/meson/pinctrl-meson.c @@ -619,7 +619,7 @@ static int meson_gpiolib_register(struct meson_pinctrl *pc) pc->chip.set = meson_gpio_set; pc->chip.base = -1; pc->chip.ngpio = pc->data->num_pins; - pc->chip.can_sleep = true; + pc->chip.can_sleep = false; ret = gpiochip_add_data(&pc->chip, pc); if (ret) { From d1297a9e2fd6ce08678b370d41bc980ca798f809 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 16 Jun 2026 21:41:49 -0400 Subject: [PATCH 2041/2758] xen/pvcalls: bound backend response req_id before indexing rsp[] [ Upstream commit d33846c8dcc06b83b7acdeac1e8bfbb5c0c26cb2 ] pvcalls_front_event_handler() takes req_id directly from the backend-supplied ring response and uses it to index the fixed-size bedata->rsp[] array for a memcpy() and a store, with no range check. A malicious or buggy backend can set req_id past PVCALLS_NR_RSP_PER_RING and drive an out-of-bounds write past the bedata allocation. req_id was also declared int while the wire field rsp->req_id is u32, so a range check on the signed value alone is insufficient: a backend req_id of 0xffffffff becomes -1, passes a >= PVCALLS_NR_RSP_PER_RING test and indexes bedata->rsp[-1]. Declare req_id as u32 so a single bound covers both ends. A backend that sends an out-of-range req_id has violated the wire protocol, so rather than silently dropping the response, log once and stop trusting the backend: set bedata->disabled. The event handler then ignores further responses, and the request paths that wait for a response return -EIO instead of blocking forever. This mirrors the fatal-error handling xen-netback uses (xenvif_fatal_tx_err()). The pvcalls frontend currently trusts its backend, so this is not a classic-Xen security issue, but it matters for hardening PV frontends against malicious backends (confidential and disaggregated deployments). Fixes: 2195046bfd69 ("xen/pvcalls: implement socket command and handle events") Suggested-by: Juergen Gross Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Reviewed-by: Juergen Gross Signed-off-by: Juergen Gross Message-ID: <20260617014149.2647404-1-michael.bommarito@gmail.com> Signed-off-by: Sasha Levin --- drivers/xen/pvcalls-front.c | 88 ++++++++++++++++++++++++++++++++----- 1 file changed, 76 insertions(+), 12 deletions(-) diff --git a/drivers/xen/pvcalls-front.c b/drivers/xen/pvcalls-front.c index 50ce4820f7eeb..3e7aa807c3173 100644 --- a/drivers/xen/pvcalls-front.c +++ b/drivers/xen/pvcalls-front.c @@ -32,6 +32,7 @@ struct pvcalls_bedata { struct xen_pvcalls_front_ring ring; grant_ref_t ref; int irq; + bool disabled; struct list_head socket_mappings; spinlock_t socket_lock; @@ -131,6 +132,20 @@ static inline int get_request(struct pvcalls_bedata *bedata, int *req_id) return 0; } +/* + * Wait for the backend's response to req_id, or for the frontend to be + * disabled because the backend violated the wire protocol. Returns 0 once + * the response has arrived, or -EIO if the frontend was disabled. + */ +static int pvcalls_front_wait_rsp(struct pvcalls_bedata *bedata, u32 req_id) +{ + wait_event(bedata->inflight_req, + READ_ONCE(bedata->rsp[req_id].req_id) == req_id || + READ_ONCE(bedata->disabled)); + + return READ_ONCE(bedata->disabled) ? -EIO : 0; +} + static bool pvcalls_front_write_todo(struct sock_mapping *map) { struct pvcalls_data_intf *intf = map->active.ring; @@ -168,7 +183,8 @@ static irqreturn_t pvcalls_front_event_handler(int irq, void *dev_id) struct pvcalls_bedata *bedata; struct xen_pvcalls_response *rsp; uint8_t *src, *dst; - int req_id = 0, more = 0, done = 0; + u32 req_id = 0; + int more = 0, done = 0; if (dev == NULL) return IRQ_HANDLED; @@ -179,12 +195,31 @@ static irqreturn_t pvcalls_front_event_handler(int irq, void *dev_id) pvcalls_exit(); return IRQ_HANDLED; } + if (READ_ONCE(bedata->disabled)) { + pvcalls_exit(); + return IRQ_HANDLED; + } again: while (RING_HAS_UNCONSUMED_RESPONSES(&bedata->ring)) { rsp = RING_GET_RESPONSE(&bedata->ring, bedata->ring.rsp_cons); req_id = rsp->req_id; + if (req_id >= PVCALLS_NR_RSP_PER_RING) { + /* + * The backend supplied a req_id that would index + * bedata->rsp[] out of bounds: a protocol violation + * from a malicious or buggy backend. Log once, stop + * trusting this backend and disable the frontend rather + * than silently dropping the response and continuing. + */ + pr_err_once("pvcalls: backend sent out-of-range req_id %u, disabling frontend\n", + req_id); + WRITE_ONCE(bedata->disabled, true); + bedata->ring.rsp_cons++; + done = 1; + break; + } if (rsp->cmd == PVCALLS_POLL) { struct sock_mapping *map = (struct sock_mapping *)(uintptr_t) rsp->u.poll.id; @@ -217,7 +252,7 @@ static irqreturn_t pvcalls_front_event_handler(int irq, void *dev_id) } RING_FINAL_CHECK_FOR_RESPONSES(&bedata->ring, more); - if (more) + if (more && !READ_ONCE(bedata->disabled)) goto again; if (done) wake_up(&bedata->inflight_req); @@ -330,8 +365,11 @@ int pvcalls_front_socket(struct socket *sock) if (notify) notify_remote_via_irq(bedata->irq); - wait_event(bedata->inflight_req, - READ_ONCE(bedata->rsp[req_id].req_id) == req_id); + ret = pvcalls_front_wait_rsp(bedata, req_id); + if (ret) { + pvcalls_exit(); + return ret; + } /* read req_id, then the content */ smp_rmb(); @@ -477,8 +515,11 @@ int pvcalls_front_connect(struct socket *sock, struct sockaddr *addr, if (notify) notify_remote_via_irq(bedata->irq); - wait_event(bedata->inflight_req, - READ_ONCE(bedata->rsp[req_id].req_id) == req_id); + ret = pvcalls_front_wait_rsp(bedata, req_id); + if (ret) { + pvcalls_exit_sock(sock); + return ret; + } /* read req_id, then the content */ smp_rmb(); @@ -711,8 +752,11 @@ int pvcalls_front_bind(struct socket *sock, struct sockaddr *addr, int addr_len) if (notify) notify_remote_via_irq(bedata->irq); - wait_event(bedata->inflight_req, - READ_ONCE(bedata->rsp[req_id].req_id) == req_id); + ret = pvcalls_front_wait_rsp(bedata, req_id); + if (ret) { + pvcalls_exit_sock(sock); + return ret; + } /* read req_id, then the content */ smp_rmb(); @@ -761,8 +805,11 @@ int pvcalls_front_listen(struct socket *sock, int backlog) if (notify) notify_remote_via_irq(bedata->irq); - wait_event(bedata->inflight_req, - READ_ONCE(bedata->rsp[req_id].req_id) == req_id); + ret = pvcalls_front_wait_rsp(bedata, req_id); + if (ret) { + pvcalls_exit_sock(sock); + return ret; + } /* read req_id, then the content */ smp_rmb(); @@ -820,6 +867,14 @@ int pvcalls_front_accept(struct socket *sock, struct socket *newsock, } } + if (READ_ONCE(bedata->disabled)) { + clear_bit(PVCALLS_FLAG_ACCEPT_INFLIGHT, + (void *)&map->passive.flags); + wake_up(&map->passive.inflight_accept_req); + pvcalls_exit_sock(sock); + return -EIO; + } + map2 = kzalloc_obj(*map2); if (map2 == NULL) { clear_bit(PVCALLS_FLAG_ACCEPT_INFLIGHT, @@ -880,10 +935,18 @@ int pvcalls_front_accept(struct socket *sock, struct socket *newsock, } if (wait_event_interruptible(bedata->inflight_req, - READ_ONCE(bedata->rsp[req_id].req_id) == req_id)) { + READ_ONCE(bedata->rsp[req_id].req_id) == req_id || + READ_ONCE(bedata->disabled))) { pvcalls_exit_sock(sock); return -EINTR; } + if (READ_ONCE(bedata->disabled)) { + clear_bit(PVCALLS_FLAG_ACCEPT_INFLIGHT, + (void *)&map->passive.flags); + wake_up(&map->passive.inflight_accept_req); + pvcalls_exit_sock(sock); + return -EIO; + } /* read req_id, then the content */ smp_rmb(); @@ -1054,7 +1117,8 @@ int pvcalls_front_release(struct socket *sock) notify_remote_via_irq(bedata->irq); wait_event(bedata->inflight_req, - READ_ONCE(bedata->rsp[req_id].req_id) == req_id); + READ_ONCE(bedata->rsp[req_id].req_id) == req_id || + READ_ONCE(bedata->disabled)); if (map->active_socket) { /* From d25cdea6226cef890a2884ceafa187cb078d69c1 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Sun, 28 Jun 2026 13:48:47 +0000 Subject: [PATCH 2042/2758] net/sched: dualpi2: clear stale classification on filter miss [ Upstream commit bf83ee45874e9f071478bed39f9cf40cc741629f ] DualPI2 leaves previous classification state attached to an skb when filter classification returns no match. The enqueue path can then act on stale state from an earlier classification attempt. A filter miss should fall back to the default class without reusing old per-packet classification data. Initialize the classification result to CLASSIC before running the classifier. Explicit L4S, priority, and successful filter classification can still override that default. Fixes: 8f9516daedd6 ("sched: Add enqueue/dequeue of dualpi2 qdisc") Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/sched/sch_dualpi2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/sched/sch_dualpi2.c b/net/sched/sch_dualpi2.c index 5434df6ca8efe..27088760eff44 100644 --- a/net/sched/sch_dualpi2.c +++ b/net/sched/sch_dualpi2.c @@ -346,6 +346,8 @@ static int dualpi2_skb_classify(struct dualpi2_sched_data *q, struct tcf_proto *fl; int result; + cb->classified = DUALPI2_C_CLASSIC; + dualpi2_read_ect(skb); if (cb->ect & q->ecn_mask) { cb->classified = DUALPI2_C_L4S; @@ -359,10 +361,8 @@ static int dualpi2_skb_classify(struct dualpi2_sched_data *q, } fl = rcu_dereference_bh(q->tcf_filters); - if (!fl) { - cb->classified = DUALPI2_C_CLASSIC; + if (!fl) return NET_XMIT_SUCCESS; - } result = tcf_classify(skb, NULL, fl, &res, false); if (result >= 0) { From fc9332533a58584293e32cb4f8e52b0be4bb1e13 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Mon, 29 Jun 2026 16:44:59 +0000 Subject: [PATCH 2043/2758] net/sched: hhf: clear heavy-hitter state on reset [ Upstream commit a225f8c20712713406ae47024b8df42deacddd4a ] HHF reset does not clear the classifier state used to identify heavy hitters. Packets after reset can therefore be scheduled using flow history from before the reset. The reset operation should return the qdisc to an empty state. Clear the heavy-hitter classifier tables when HHF is reset. Fixes: 10239edf86f1 ("net-qdisc-hhf: Heavy-Hitter Filter (HHF) qdisc") Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/sched/sch_hhf.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/net/sched/sch_hhf.c b/net/sched/sch_hhf.c index 1e25b75daae2e..d85cb0263b675 100644 --- a/net/sched/sch_hhf.c +++ b/net/sched/sch_hhf.c @@ -462,12 +462,39 @@ static struct sk_buff *hhf_dequeue(struct Qdisc *sch) return skb; } +static void hhf_reset_classifier(struct hhf_sched_data *q) +{ + int i; + + if (!q->hh_flows) + return; + + for (i = 0; i < HH_FLOWS_CNT; i++) { + struct hh_flow_state *flow, *next; + struct list_head *head = &q->hh_flows[i]; + + list_for_each_entry_safe(flow, next, head, flowchain) { + list_del(&flow->flowchain); + kfree(flow); + } + } + WRITE_ONCE(q->hh_flows_current_cnt, 0); + + for (i = 0; i < HHF_ARRAYS_CNT; i++) { + if (q->hhf_valid_bits[i]) + bitmap_zero(q->hhf_valid_bits[i], HHF_ARRAYS_LEN); + } + q->hhf_arrays_reset_timestamp = hhf_time_stamp(); +} + static void hhf_reset(struct Qdisc *sch) { + struct hhf_sched_data *q = qdisc_priv(sch); struct sk_buff *skb; while ((skb = hhf_dequeue(sch)) != NULL) rtnl_kfree_skbs(skb, skb); + hhf_reset_classifier(q); } static void hhf_destroy(struct Qdisc *sch) From 47e434da476b5a8bcd1e6e52ab03c5ee7764ee78 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 15 Jun 2026 14:52:18 +0200 Subject: [PATCH 2044/2758] fs: refuse O_TMPFILE creation with an unmapped fsuid or fsgid [ Upstream commit 539dce1144651f7976fa418e618b0b574bf15eeb ] vfs_tmpfile() never checked that the caller's fsuid and fsgid map into the filesystem. On an idmapped mount whose idmapping does not cover the caller's fs{u,g}id, the ->tmpfile() instance initializes the new inode through inode_init_owner(), where mapped_fsuid()/mapped_fsgid() return INVALID_UID/INVALID_GID, and the tmpfile ends up owned by (uid_t)-1. Every other creation path already refuses this: may_o_create() (O_CREAT) and may_create_dentry() (mkdir, mknod, symlink, link) bail out with -EOVERFLOW via fsuidgid_has_mapping() precisely so that an object cannot be created with an owner the filesystem cannot represent. An O_TMPFILE is no exception: it is created I_LINKABLE and linkat(2) can splice it into the namespace afterwards, so the same guarantee must hold. Add the missing fsuidgid_has_mapping() check to vfs_tmpfile(). On a non-idmapped mount the caller's fs{u,g}id always map in the superblock's user namespace, so this is a no-op there and only takes effect on an idmapped mount that does not map the caller. It applies to every filesystem that sets FS_ALLOW_IDMAP and implements ->tmpfile() (tmpfs, ext4, btrfs, xfs, f2fs, ...), and to overlayfs, whose upper-layer tmpfile creation funnels through vfs_tmpfile() via backing_tmpfile_open(). Fixes: 8e5389132ab4 ("fs: introduce fsuidgid_has_mapping() helper") Link: https://patch.msgid.link/20260615-work-idmapped-tmpfile-v1-1-754a94d81f83@kernel.org Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/namei.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/namei.c b/fs/namei.c index 4787244ca4a75..0da20b387e96e 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4735,6 +4735,10 @@ int vfs_tmpfile(struct mnt_idmap *idmap, int error; int open_flag = file->f_flags; + /* A tmpfile is I_LINKABLE, so guard its owner like may_o_create(). */ + if (!fsuidgid_has_mapping(dir->i_sb, idmap)) + return -EOVERFLOW; + /* we want directory to be writable */ error = inode_permission(idmap, dir, MAY_WRITE | MAY_EXEC); if (error) From bdcd80ff12939d172043fac5eb822b92366a1bab Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 22 Jun 2026 10:08:36 +0100 Subject: [PATCH 2045/2758] afs: Fix error code in afs_extract_vl_addrs() [ Upstream commit 4897cb71d4ab1f7e1a214adb1e4b80176702368d ] The error codes on these paths are only set on the first iteration through the loop. Set the correct error code on every iteration. Fixes: 0a5143f2f89c ("afs: Implement VL server rotation") Signed-off-by: Dan Carpenter Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-3-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/vl_list.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/afs/vl_list.c b/fs/afs/vl_list.c index 3e4966915ea4c..003889cf0f18d 100644 --- a/fs/afs/vl_list.c +++ b/fs/afs/vl_list.c @@ -92,7 +92,7 @@ static struct afs_addr_list *afs_extract_vl_addrs(struct afs_net *net, { struct afs_addr_list *alist; const u8 *b = *_b; - int ret = -EINVAL; + int ret; alist = afs_alloc_addrlist(nr_addrs); if (!alist) @@ -110,6 +110,7 @@ static struct afs_addr_list *afs_extract_vl_addrs(struct afs_net *net, case DNS_ADDRESS_IS_IPV4: if (end - b < 4) { _leave(" = -EINVAL [short inet]"); + ret = -EINVAL; goto error; } memcpy(x, b, 4); @@ -122,6 +123,7 @@ static struct afs_addr_list *afs_extract_vl_addrs(struct afs_net *net, case DNS_ADDRESS_IS_IPV6: if (end - b < 16) { _leave(" = -EINVAL [short inet6]"); + ret = -EINVAL; goto error; } memcpy(x, b, 16); From 462eada939f2d73bb814f21d5d34fe72594da2e8 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:38 +0100 Subject: [PATCH 2046/2758] afs: Fix double netfs initialisation in afs_root_iget() [ Upstream commit 733a984a4ee7345325e47efb505eebfe67b299bc ] Fix afs_root_iget() to leave initialisation of the netfs_inode part of the afs_vnode to afs_inode_init_from_status(). Fixes: bc899ee1c898 ("netfs: Add a netfs inode context") Closes: https://sashiko.dev/#/patchset/20260609081738.770127-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-5-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/inode.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 3f48458694baa..a88995629d728 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -566,7 +566,6 @@ struct inode *afs_root_iget(struct super_block *sb, struct key *key) vnode = AFS_FS_I(inode); vnode->cb_v_check = atomic_read(&as->volume->cb_v_break); - afs_set_netfs_context(vnode); op = afs_alloc_operation(key, as->volume); if (IS_ERR(op)) { From 9d6b0f6d437e2f8350e08678e5e1d20c11c364f3 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:39 +0100 Subject: [PATCH 2047/2758] afs: Remove setting of AS_RELEASE_ALWAYS for symlinks and mountpoints [ Upstream commit 81e985b4c3a6cbcc443fcdcd3ebda7fcc845d459 ] Regular AFS files correctly use afs_file_aops which have release_folio set as netfs_release_folio, so AS_RELEASE_ALWAYS is valid for them when fscache is enabled (set via afs_vnode_set_cache()). Symlinks and mountpoints in AFS use afs_dir_aops, which does not provide a release_folio callback. However, afs_apply_status() unconditionally calls mapping_set_release_always() for these. In such case when memory management code attempts to release folios, filemap_release_folio() checks folio_needs_release() which returns true due to AS_RELEASE_ALWAYS being set. Since there is no release_folio callback, it falls through to try_to_free_buffers(), which at present expects buffer_heads to be not null. For symlinks and mountpoints without buffer_heads, this causes pointer dereference. [dh: Added more bits that were missed] Fixes: eae9e78951bb ("afs: Use netfslib for symlinks, allowing them to be cached") Signed-off-by: Deepakkumar Karn Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-6-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/inode.c | 7 +++---- fs/afs/internal.h | 2 -- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/fs/afs/inode.c b/fs/afs/inode.c index a88995629d728..54ac6ec21daf4 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -52,9 +52,9 @@ static noinline void dump_vnode(struct afs_vnode *vnode, struct afs_vnode *paren /* * Set parameters for the netfs library */ -static void afs_set_netfs_context(struct afs_vnode *vnode) +static void afs_set_netfs_context(struct afs_vnode *vnode, bool is_file) { - netfs_inode_init(&vnode->netfs, &afs_req_ops, true); + netfs_inode_init(&vnode->netfs, &afs_req_ops, is_file); } /* @@ -126,7 +126,6 @@ static int afs_inode_init_from_status(struct afs_operation *op, } inode->i_mapping->a_ops = &afs_symlink_aops; inode_nohighmem(inode); - mapping_set_release_always(inode->i_mapping); break; default: dump_vnode(vnode, op->file[0].vnode != vnode ? op->file[0].vnode : NULL); @@ -136,7 +135,7 @@ static int afs_inode_init_from_status(struct afs_operation *op, i_size_write(inode, status->size); inode_set_bytes(inode, status->size); - afs_set_netfs_context(vnode); + afs_set_netfs_context(vnode, status->type == AFS_FTYPE_FILE); vnode->invalid_before = status->data_version; trace_afs_set_dv(vnode, status->data_version); diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 0b72a85662999..785c646856d7f 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -750,8 +750,6 @@ static inline void afs_vnode_set_cache(struct afs_vnode *vnode, { #ifdef CONFIG_AFS_FSCACHE vnode->netfs.cache = cookie; - if (cookie) - mapping_set_release_always(vnode->netfs.inode.i_mapping); #endif } From 6d52ff4c866ea5aeb8d74f4cd5b671878582fb03 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:40 +0100 Subject: [PATCH 2048/2758] afs: Fix directory inode initialisation order [ Upstream commit 35b177ef541ae8eefbfbf679c3476bc3fb1eb83c ] Fix afs_inode_init_from_status() to call afs_set_netfs_context() before the switch to do file type-specific initialisation because local directory changes don't get uploaded to the server, only stored in the cache. This requires that the file size be set before, so move that up too. Without this, NETFS_ICTX_SINGLE_NO_UPLOAD as set on directories gets clobbered. Closes: https://sashiko.dev/#/patchset/20260618074903.2374756-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-7-dhowells@redhat.com Fixes: 6dd80936618c ("afs: Use netfslib for directories") cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/inode.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 54ac6ec21daf4..51c28f1488454 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -93,6 +93,10 @@ static int afs_inode_init_from_status(struct afs_operation *op, inode->i_gid = make_kgid(&init_user_ns, status->group); set_nlink(&vnode->netfs.inode, status->nlink); + i_size_write(inode, status->size); + inode_set_bytes(inode, status->size); + afs_set_netfs_context(vnode, status->type == AFS_FTYPE_FILE); + switch (status->type) { case AFS_FTYPE_FILE: inode->i_mode = S_IFREG | (status->mode & S_IALLUGO); @@ -133,10 +137,6 @@ static int afs_inode_init_from_status(struct afs_operation *op, return afs_protocol_error(NULL, afs_eproto_file_type); } - i_size_write(inode, status->size); - inode_set_bytes(inode, status->size); - afs_set_netfs_context(vnode, status->type == AFS_FTYPE_FILE); - vnode->invalid_before = status->data_version; trace_afs_set_dv(vnode, status->data_version); inode_set_iversion_raw(&vnode->netfs.inode, status->data_version); From 84e4b9232a32f0e5094dec10cf3e8187af289d0f Mon Sep 17 00:00:00 2001 From: Zilin Guan Date: Mon, 22 Jun 2026 10:08:41 +0100 Subject: [PATCH 2049/2758] afs: use kvfree() to free memory allocated by kvcalloc() [ Upstream commit cb39654926f8e7a08ecc1dcb3941628855275940 ] op->more_files is allocated with kvcalloc() but released via afs_put_operation(), which uses kfree() internally. This mismach prevents the resource from being released properly and may lead to undefined behavior. Fix this by using kvfree() to free op->more_files to match its allocation method. Fixes: e49c7b2f6de7 ("afs: Build an abstraction around an "operation" concept") Signed-off-by: Zilin Guan Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-8-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/fs_operation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/afs/fs_operation.c b/fs/afs/fs_operation.c index c0dbbc6d3716e..20801b29521d1 100644 --- a/fs/afs/fs_operation.c +++ b/fs/afs/fs_operation.c @@ -348,7 +348,7 @@ int afs_put_operation(struct afs_operation *op) for (i = 0; i < op->nr_files - 2; i++) if (op->more_files[i].put_vnode) iput(&op->more_files[i].vnode->netfs.inode); - kfree(op->more_files); + kvfree(op->more_files); } if (op->estate) { From ca9f195050777be056f094020814d47fb4257653 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Mon, 22 Jun 2026 10:08:42 +0100 Subject: [PATCH 2050/2758] afs: Remove erroneous seq |= 1 in volume lookup loop [ Upstream commit a58edda50a3ec08e6adac1d04dc3e488494e412d ] The `seq |= 1` operation in the volume lookup loop is incorrect because: seq is already incremented at start, making it odd in next iteration which triggers lock, but The `|= 1` operation causes seq to be even and unintended lockless operation Remove this erroneous operation to maintain proper lock sequencing. Fixes: 32222f09782f ("afs: Apply server breaks to mmap'd files in the call processor") Signed-off-by: Li RongQing Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-9-dhowells@redhat.com Reviewed-by: Oleg Nesterov cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/callback.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/afs/callback.c b/fs/afs/callback.c index 894d2bad6b6ce..833ac3178ddc7 100644 --- a/fs/afs/callback.c +++ b/fs/afs/callback.c @@ -140,7 +140,6 @@ static struct afs_volume *afs_lookup_volume_rcu(struct afs_cell *cell, break; if (!need_seqretry(&cell->volume_lock, seq)) break; - seq |= 1; /* Want a lock next time */ } done_seqretry(&cell->volume_lock, seq); From 552d3c0f184a8cedb36f3840d30744681ae1a150 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:44 +0100 Subject: [PATCH 2051/2758] afs: Fix bulk lookup malfunction due to change in dir_emit() API [ Upstream commit 2f79d1b93c62470fe02dbdc24770f1ae5a9e1be6 ] afs_do_lookup() and afs_do_lookup_one() use the same directory parsing code as afs_readdir() and were supplying alternative dir_context actors to retrieve dirents, but because lookup needs the vnode's uniquifier as part of the reference, but not the DT flags, the uniquifier was being passed in the dt flags argument to the lookup actors. Unfortunately, commit c644bce62b9c, added to fix overlayfs with fuse, broke this by masking off part of the uniquifier. This doesn't matter enough to be directly noticeable, instead causing bulk advance inode lookups to fail (which are retried later) and may cause dir revalidation to malfunction if the uniquifier is changed by masking. Fix this by making the afs directory parsing code take special ->actor values of AFS_LOOKUP or AFS_LOOKUP_ONE instead that tell it to call afs_lookup_filldir() or afs_lookup_one_filldir() directly rather than going through dir_emit(). dir_emit() is still used for readdir. Fixes: c644bce62b9c ("readdir: require opt-in for d_type flags") Reported-by: Marc Dionne Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-11-dhowells@redhat.com cc: Amir Goldstein cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/dir.c | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 498b99ccdf0e2..6df56fe9163fd 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -28,9 +28,11 @@ static int afs_d_revalidate(struct inode *dir, const struct qstr *name, static int afs_d_delete(const struct dentry *dentry); static void afs_d_iput(struct dentry *dentry, struct inode *inode); static bool afs_lookup_one_filldir(struct dir_context *ctx, const char *name, int nlen, - loff_t fpos, u64 ino, unsigned dtype); + u64 ino, u32 uniquifier); +#define AFS_LOOKUP_ONE ((filldir_t)0x123UL) static bool afs_lookup_filldir(struct dir_context *ctx, const char *name, int nlen, - loff_t fpos, u64 ino, unsigned dtype); + u64 ino, u32 uniquifier); +#define AFS_LOOKUP ((filldir_t)0x137UL) static int afs_create(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode, bool excl); static struct dentry *afs_mkdir(struct mnt_idmap *idmap, struct inode *dir, @@ -421,11 +423,18 @@ static int afs_dir_iterate_block(struct afs_vnode *dvnode, } /* found the next entry */ - if (!dir_emit(ctx, dire->u.name, nlen, - ntohl(dire->u.vnode), - (ctx->actor == afs_lookup_filldir || - ctx->actor == afs_lookup_one_filldir)? - ntohl(dire->u.unique) : DT_UNKNOWN)) { + if (ctx->actor == AFS_LOOKUP) { + if (!afs_lookup_filldir(ctx, dire->u.name, nlen, + ntohl(dire->u.vnode), + ntohl(dire->u.unique))) + return 0; + } else if (ctx->actor == AFS_LOOKUP_ONE) { + if (!afs_lookup_one_filldir(ctx, dire->u.name, nlen, + ntohl(dire->u.vnode), + ntohl(dire->u.unique))) + return 0; + } else if (!dir_emit(ctx, dire->u.name, nlen, + ntohl(dire->u.vnode), DT_UNKNOWN)) { _leave(" = 0 [full]"); return 0; } @@ -545,6 +554,7 @@ static int afs_readdir(struct file *file, struct dir_context *ctx) { afs_dataversion_t dir_version; + ctx->dt_flags_mask = UINT_MAX; return afs_dir_iterate(file_inode(file), ctx, file, &dir_version); } @@ -554,14 +564,14 @@ static int afs_readdir(struct file *file, struct dir_context *ctx) * uniquifier through dtype */ static bool afs_lookup_one_filldir(struct dir_context *ctx, const char *name, - int nlen, loff_t fpos, u64 ino, unsigned dtype) + int nlen, u64 ino, u32 uniquifier) { struct afs_lookup_one_cookie *cookie = container_of(ctx, struct afs_lookup_one_cookie, ctx); _enter("{%s,%u},%s,%u,,%llu,%u", cookie->name.name, cookie->name.len, name, nlen, - (unsigned long long) ino, dtype); + (unsigned long long) ino, uniquifier); /* insanity checks first */ BUILD_BUG_ON(sizeof(union afs_xdr_dir_block) != 2048); @@ -574,7 +584,7 @@ static bool afs_lookup_one_filldir(struct dir_context *ctx, const char *name, } cookie->fid.vnode = ino; - cookie->fid.unique = dtype; + cookie->fid.unique = uniquifier; cookie->found = 1; _leave(" = false [found]"); @@ -591,7 +601,7 @@ static int afs_do_lookup_one(struct inode *dir, const struct qstr *name, { struct afs_super_info *as = dir->i_sb->s_fs_info; struct afs_lookup_one_cookie cookie = { - .ctx.actor = afs_lookup_one_filldir, + .ctx.actor = AFS_LOOKUP_ONE, .name = *name, .fid.vid = as->volume->vid }; @@ -622,14 +632,14 @@ static int afs_do_lookup_one(struct inode *dir, const struct qstr *name, * uniquifier through dtype */ static bool afs_lookup_filldir(struct dir_context *ctx, const char *name, - int nlen, loff_t fpos, u64 ino, unsigned dtype) + int nlen, u64 ino, u32 uniquifier) { struct afs_lookup_cookie *cookie = container_of(ctx, struct afs_lookup_cookie, ctx); _enter("{%s,%u},%s,%u,,%llu,%u", cookie->name.name, cookie->name.len, name, nlen, - (unsigned long long) ino, dtype); + (unsigned long long) ino, uniquifier); /* insanity checks first */ BUILD_BUG_ON(sizeof(union afs_xdr_dir_block) != 2048); @@ -637,7 +647,7 @@ static bool afs_lookup_filldir(struct dir_context *ctx, const char *name, if (cookie->nr_fids < 50) { cookie->fids[cookie->nr_fids].vnode = ino; - cookie->fids[cookie->nr_fids].unique = dtype; + cookie->fids[cookie->nr_fids].unique = uniquifier; cookie->nr_fids++; } @@ -778,7 +788,7 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry) for (i = 0; i < ARRAY_SIZE(cookie->fids); i++) cookie->fids[i].vid = dvnode->fid.vid; - cookie->ctx.actor = afs_lookup_filldir; + cookie->ctx.actor = AFS_LOOKUP; cookie->name = dentry->d_name; cookie->nr_fids = 2; /* slot 1 is saved for the fid we actually want * and slot 0 for the directory */ From 654a546c34f3921dd03f9ea74e60139ebffd9e20 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:45 +0100 Subject: [PATCH 2052/2758] afs: Fix misplaced inc of net->cells_outstanding [ Upstream commit c9c3b615a462a4023bd148f02c564e175ed10502 ] Fix net->cells_outstanding being incremented before the check for failure of idr_alloc_cyclic(), leaving the count incremented on error. Fixes: 88c853c3f5c0 ("afs: Fix cell refcounting by splitting the usage counter") Reported-by: Hillf Danton Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-12-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/cell.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/afs/cell.c b/fs/afs/cell.c index 9738684dbdd26..e0fab1609f278 100644 --- a/fs/afs/cell.c +++ b/fs/afs/cell.c @@ -205,11 +205,11 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net, cell->dns_source = vllist->source; cell->dns_status = vllist->status; smp_store_release(&cell->dns_lookup_count, 1); /* vs source/status */ - atomic_inc(&net->cells_outstanding); ret = idr_alloc_cyclic(&net->cells_dyn_ino, cell, 2, INT_MAX / 2, GFP_KERNEL); if (ret < 0) goto error; + atomic_inc(&net->cells_outstanding); cell->dynroot_ino = ret; cell->debug_id = atomic_inc_return(&cell_debug_id); From ebfd13c0367adb43d7c0a72f5cd7e004e60c6b28 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:46 +0100 Subject: [PATCH 2053/2758] afs: Fix reinitialisation of the inode, in particular ->lock_work [ Upstream commit 5597fbd1e7c161914f20315a726e54025b0fdadb ] It seems that initalising afs_vnode::lock_work a single time in the slab's init function isn't sufficient for work_structs. This results in the DEBUG_OBJECTS debugging stuff producing a warning occasionally when running the generic/131 xfstest: ODEBUG: activate not available (active state 0) object: 0000000016d8760f object type: work_struct hint: afs_lock_work+0x0/0x220 WARNING: lib/debugobjects.c:629 at debug_print_object+0x4b/0x90, CPU#3: locktest/7695 ... CPU: 3 UID: 0 PID: 7695 Comm: locktest Tainted: G S 7.1.0-build3+ #2771 PREEMPT ... RIP: 0010:debug_print_object+0x65/0x90 ... Call Trace: ? __pfx_afs_lock_work+0x10/0x10 debug_object_activate+0x122/0x170 insert_work+0x25/0x60 __queue_work+0x2e0/0x340 queue_delayed_work_on+0x48/0x70 afs_fl_release_private+0x57/0x70 locks_release_private+0x5c/0xa0 locks_free_lock+0xe/0x20 posix_lock_inode+0x55f/0x5b0 locks_lock_inode_wait+0x81/0x140 ? file_write_and_wait_range+0x50/0x70 afs_lock+0xcd/0x110 fcntl_setlk+0x10d/0x260 do_fcntl+0x24e/0x5b0 __do_sys_fcntl+0x6a/0x90 do_syscall_64+0x11e/0x310 entry_SYSCALL_64_after_hwframe+0x71/0x79 Fix this by reinitialising ->lock_work after allocating an inode. Also, flush ->lock_work when the inode is being evicted to make sure it's not still running. Fixes: e8d6c554126b ("AFS: implement file locking") Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-13-dhowells@redhat.com cc: Marc Dionne cc: Thomas Gleixner cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/inode.c | 1 + fs/afs/super.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 51c28f1488454..14f39a9bea6cf 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -680,6 +680,7 @@ void afs_evict_inode(struct inode *inode) inode->i_mapping->a_ops->writepages(inode->i_mapping, &wbc); } + flush_delayed_work(&vnode->lock_work); netfs_wait_for_outstanding_io(inode); truncate_inode_pages_final(&inode->i_data); netfs_free_folioq_buffer(vnode->directory); diff --git a/fs/afs/super.c b/fs/afs/super.c index dec091e569c42..82bb713825a0d 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -660,7 +660,6 @@ static void afs_i_init_once(void *_vnode) INIT_LIST_HEAD(&vnode->wb_keys); INIT_LIST_HEAD(&vnode->pending_locks); INIT_LIST_HEAD(&vnode->granted_locks); - INIT_DELAYED_WORK(&vnode->lock_work, afs_lock_work); INIT_LIST_HEAD(&vnode->cb_mmap_link); seqlock_init(&vnode->cb_lock); } @@ -694,6 +693,7 @@ static struct inode *afs_alloc_inode(struct super_block *sb) init_rwsem(&vnode->rmdir_lock); INIT_WORK(&vnode->cb_work, afs_invalidate_mmap_work); + INIT_DELAYED_WORK(&vnode->lock_work, afs_lock_work); _leave(" = %p", &vnode->netfs.inode); return &vnode->netfs.inode; From f14dd036fad3d359f26f1282199cec06b3a9362b Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:47 +0100 Subject: [PATCH 2054/2758] afs: Fix callback service message parsers to pass through -EAGAIN [ Upstream commit 0f36469d7ce98b362934113c550d08bb0c784231 ] The AFS filesystem client uses an rxrpc server to listen for callback notifications. Each callback call type handler has a delivery function that parses the incoming request stream, and this should return -EAGAIN the last packet hasn't yet been seen, but all currently queued received data is consumed. afs_extract_data() does this, but the -EAGAIN return is switched to 0 inadvertantly Fix callback service message parsers to pass through -EAGAIN Fixes: d001648ec7cf ("rxrpc: Don't expose skbs to in-kernel users [ver #2]") Closes: https://sashiko.dev/#/patchset/20260609081738.770127-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-14-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/cmservice.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/afs/cmservice.c b/fs/afs/cmservice.c index 263c60c811a58..db394f101fc66 100644 --- a/fs/afs/cmservice.c +++ b/fs/afs/cmservice.c @@ -334,7 +334,6 @@ static int afs_deliver_cb_init_call_back_state3(struct afs_call *call) ret = afs_extract_data(call, false); switch (ret) { case 0: break; - case -EAGAIN: return 0; default: return ret; } @@ -456,7 +455,6 @@ static int afs_deliver_cb_probe_uuid(struct afs_call *call) ret = afs_extract_data(call, false); switch (ret) { case 0: break; - case -EAGAIN: return 0; default: return ret; } From e3e59ff22a0de01ed0cf3a3e25558811abc3b70a Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:49 +0100 Subject: [PATCH 2055/2758] afs: Fix missing NULL pointer check in afs_break_some_callbacks() [ Upstream commit 794a01110390c1b76f59ece773fb0fbfd89c6f5c ] Fix afs_break_some_callbacks() to check to see if afs_lookup_volume_rcu() returned NULL (e.g. the specified volume is unknown). Fixes: 8230fd8217b7 ("afs: Make callback processing more efficient.") Closes: https://sashiko.dev/#/patchset/20260609081738.770127-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-16-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/callback.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/afs/callback.c b/fs/afs/callback.c index 833ac3178ddc7..0a09a9ca3c6b3 100644 --- a/fs/afs/callback.c +++ b/fs/afs/callback.c @@ -220,7 +220,11 @@ static void afs_break_some_callbacks(struct afs_server *server, rcu_read_lock(); volume = afs_lookup_volume_rcu(server->cell, vid); - if (cbb->fid.vnode == 0 && cbb->fid.unique == 0) { + if (!volume) { + /* Ignore breaks on unknown volumes. */ + rcu_read_unlock(); + *_count = 0; + } else if (cbb->fid.vnode == 0 && cbb->fid.unique == 0) { afs_break_volume_callback(server, volume); *_count -= 1; if (*_count) From 1bdbc50e2d419c34cd6ad5b316a145ed983595dd Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:48 +0100 Subject: [PATCH 2056/2758] afs: Use scoped_seqlock_read() rather than manually doing seqlock stuff [ Upstream commit 3b1601471a88f86082fc1f1c2475645cdf59f7d8 ] This is an addendum to the patch to remove the erroneous seq |= 1 in volume lookup loop. Switch to using scoped_seqlock_read() as suggested by Oleg Nesterov[1]. Signed-off-by: David Howells Link: https://lore.kernel.org/r/aifaeKvz3KemfzaS@redhat.com/ [1] Link: https://patch.msgid.link/20260622090856.2746629-15-dhowells@redhat.com Reviewed-by: Oleg Nesterov cc: Marc Dionne cc: Li RongQing cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Stable-dep-of: d672c276f685 ("afs: Fix leak of ungot volume") Signed-off-by: Sasha Levin --- fs/afs/callback.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/fs/afs/callback.c b/fs/afs/callback.c index 0a09a9ca3c6b3..74853e0d0435a 100644 --- a/fs/afs/callback.c +++ b/fs/afs/callback.c @@ -113,16 +113,12 @@ static struct afs_volume *afs_lookup_volume_rcu(struct afs_cell *cell, { struct afs_volume *volume = NULL; struct rb_node *p; - int seq = 1; - for (;;) { + scoped_seqlock_read(&cell->volume_lock, ss_lock) { /* Unfortunately, rbtree walking doesn't give reliable results * under just the RCU read lock, so we have to check for * changes. */ - seq++; /* 2 on the 1st/lockless path, otherwise odd */ - read_seqbegin_or_lock(&cell->volume_lock, &seq); - p = rcu_dereference_raw(cell->volumes.rb_node); while (p) { volume = rb_entry(p, struct afs_volume, cell_node); @@ -138,11 +134,8 @@ static struct afs_volume *afs_lookup_volume_rcu(struct afs_cell *cell, if (volume && afs_try_get_volume(volume, afs_volume_trace_get_callback)) break; - if (!need_seqretry(&cell->volume_lock, seq)) - break; } - done_seqretry(&cell->volume_lock, seq); return volume; } From 9cabf1c86948793fbad09023bd2ec58c71b9c1d4 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:50 +0100 Subject: [PATCH 2057/2758] afs: Fix leak of ungot volume [ Upstream commit d672c276f685a540ed2b2a8bafaed4650a89022c ] Fix afs_lookup_volume_rcu() so that it doesn't leak a dying volume if afs_try_get_volume() fails. Fixes: 32222f09782f ("afs: Apply server breaks to mmap'd files in the call processor") Closes: https://sashiko.dev/#/patchset/20260609081738.770127-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-17-dhowells@redhat.com cc: Marc Dionne cc: Deepakkumar Karn cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/callback.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/afs/callback.c b/fs/afs/callback.c index 74853e0d0435a..61354003c006c 100644 --- a/fs/afs/callback.c +++ b/fs/afs/callback.c @@ -134,6 +134,7 @@ static struct afs_volume *afs_lookup_volume_rcu(struct afs_cell *cell, if (volume && afs_try_get_volume(volume, afs_volume_trace_get_callback)) break; + volume = NULL; } return volume; From 91d8f8e5fd34d3aed26f0fe6cf52b3f71de66cc6 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:51 +0100 Subject: [PATCH 2058/2758] afs: Fix vllist leak [ Upstream commit fc10c0ecf06f2981af5d04357612b00051e03e9e ] Fix a leak of the new vllist in afs_update_cell() in the event that it is an empty list (nr_servers == 0), in which case the old list isn't displaced unless the old list is also empty. Fixes: d5c32c89b208 ("afs: Fix cell DNS lookup") Closes: https://sashiko.dev/#/patchset/20260609081738.770127-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-18-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/cell.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/afs/cell.c b/fs/afs/cell.c index e0fab1609f278..fbb8a43aa7cd9 100644 --- a/fs/afs/cell.c +++ b/fs/afs/cell.c @@ -547,6 +547,8 @@ static int afs_update_cell(struct afs_cell *cell) rcu_assign_pointer(cell->vl_servers, vllist); cell->dns_source = vllist->source; old = p; + } else { + old = vllist; } write_unlock(&cell->vl_servers_lock); afs_put_vlserverlist(cell->net, old); From e94f92fd56c553a8bf9421c3289e1b85c7c08857 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:52 +0100 Subject: [PATCH 2059/2758] afs: Fix lack of locking around modifications of net->cells_dyn_ino [ Upstream commit 55e841836c6f4646490f7b0347192b7a92d431ba ] Fix the lack of locking around modifications of net->cells_dyn_ino by taking net->cells_lock exclusively. This also requires to cell to be removed from net->cells_dyn_ino in afs_destroy_cell_work() rather than in afs_cell_destroy() as the latter runs in RCU cleanup context and sleeping locks cannot be taken there. Fixes: 1d0b929fc070 ("afs: Change dynroot to create contents on demand") Closes: https://sashiko.dev/#/patchset/20260618074903.2374756-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-19-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/cell.c | 8 +++++++- fs/afs/dynroot.c | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/afs/cell.c b/fs/afs/cell.c index fbb8a43aa7cd9..9d8937ae24e21 100644 --- a/fs/afs/cell.c +++ b/fs/afs/cell.c @@ -205,8 +205,10 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net, cell->dns_source = vllist->source; cell->dns_status = vllist->status; smp_store_release(&cell->dns_lookup_count, 1); /* vs source/status */ + down_write(&net->cells_lock); ret = idr_alloc_cyclic(&net->cells_dyn_ino, cell, 2, INT_MAX / 2, GFP_KERNEL); + up_write(&net->cells_lock); if (ret < 0) goto error; atomic_inc(&net->cells_outstanding); @@ -579,7 +581,6 @@ static void afs_cell_destroy(struct rcu_head *rcu) afs_put_vlserverlist(net, rcu_access_pointer(cell->vl_servers)); afs_unuse_cell(cell->alias_of, afs_cell_trace_unuse_alias); key_put(cell->anonymous_key); - idr_remove(&net->cells_dyn_ino, cell->dynroot_ino); kfree(cell->name - 1); kfree(cell); @@ -594,6 +595,11 @@ static void afs_destroy_cell_work(struct work_struct *work) afs_see_cell(cell, afs_cell_trace_destroy); timer_delete_sync(&cell->management_timer); cancel_work_sync(&cell->manager); + + down_write(&cell->net->cells_lock); + idr_remove(&cell->net->cells_dyn_ino, cell->dynroot_ino); + up_write(&cell->net->cells_lock); + call_rcu(&cell->rcu, afs_cell_destroy); } diff --git a/fs/afs/dynroot.c b/fs/afs/dynroot.c index 1d5e33bc75022..6e3c8c691ba97 100644 --- a/fs/afs/dynroot.c +++ b/fs/afs/dynroot.c @@ -278,7 +278,7 @@ static struct dentry *afs_lookup_atcell(struct inode *dir, struct dentry *dentry } /* - * Transcribe the cell database into readdir content under the RCU read lock. + * Transcribe the cell database into readdir content under net->cells_lock. * Each cell produces two entries, one prefixed with a dot and one not. */ static int afs_dynroot_readdir_cells(struct afs_net *net, struct dir_context *ctx) From c9a0b9e5f3d4ac83b6b549a1dd31fabb970ba3a0 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:53 +0100 Subject: [PATCH 2060/2758] afs: Fix premature cell exposure through /afs [ Upstream commit 26f17ce6fa3f05cb5965790499c1839094260de4 ] AFS cell records are prematurely exposured through the /afs dynamic root by virtue of adding them immediately to the net->cells_dyn_ino IDR when the cell is allocated rather than when it is added to the lookup tree. This allows a candidate record to be accessed, even if it's actually a duplicate or not published yet. Fix this by not adding the cell to cells_dyn_ino until it's confirmed non-duplicate and is being published. A flag is then used to record whether it is added to the IDR to make removal from the IDR conditional. Closes: https://sashiko.dev/#/patchset/20260618155141.2513212-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-20-dhowells@redhat.com Fixes: 1d0b929fc070 ("afs: Change dynroot to create contents on demand") cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/cell.c | 27 +++++++++++++++++---------- fs/afs/internal.h | 1 + 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/fs/afs/cell.c b/fs/afs/cell.c index 9d8937ae24e21..47a2645768d79 100644 --- a/fs/afs/cell.c +++ b/fs/afs/cell.c @@ -205,14 +205,7 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net, cell->dns_source = vllist->source; cell->dns_status = vllist->status; smp_store_release(&cell->dns_lookup_count, 1); /* vs source/status */ - down_write(&net->cells_lock); - ret = idr_alloc_cyclic(&net->cells_dyn_ino, cell, - 2, INT_MAX / 2, GFP_KERNEL); - up_write(&net->cells_lock); - if (ret < 0) - goto error; atomic_inc(&net->cells_outstanding); - cell->dynroot_ino = ret; cell->debug_id = atomic_inc_return(&cell_debug_id); trace_afs_cell(cell->debug_id, 1, 0, afs_cell_trace_alloc); @@ -306,6 +299,13 @@ struct afs_cell *afs_lookup_cell(struct afs_net *net, goto cell_already_exists; } + ret = idr_alloc_cyclic(&net->cells_dyn_ino, candidate, + 2, INT_MAX / 2, GFP_KERNEL); + if (ret < 0) + goto cant_alloc_ino; + candidate->dynroot_ino = ret; + set_bit(AFS_CELL_FL_HAVE_INO, &candidate->flags); + cell = candidate; candidate = NULL; afs_use_cell(cell, trace); @@ -380,6 +380,11 @@ struct afs_cell *afs_lookup_cell(struct afs_net *net, _leave(" = %p [cell]", cell); return cell; +cant_alloc_ino: + up_write(&net->cells_lock); + afs_put_cell(candidate, afs_cell_trace_put_candidate); + goto error_noput; + cell_already_exists: _debug("cell exists"); cell = cursor; @@ -596,9 +601,11 @@ static void afs_destroy_cell_work(struct work_struct *work) timer_delete_sync(&cell->management_timer); cancel_work_sync(&cell->manager); - down_write(&cell->net->cells_lock); - idr_remove(&cell->net->cells_dyn_ino, cell->dynroot_ino); - up_write(&cell->net->cells_lock); + if (test_bit(AFS_CELL_FL_HAVE_INO, &cell->flags)) { + down_write(&cell->net->cells_lock); + idr_remove(&cell->net->cells_dyn_ino, cell->dynroot_ino); + up_write(&cell->net->cells_lock); + } call_rcu(&cell->rcu, afs_cell_destroy); } diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 785c646856d7f..601f01e5c15fc 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -388,6 +388,7 @@ struct afs_cell { #define AFS_CELL_FL_NO_GC 0 /* The cell was added manually, don't auto-gc */ #define AFS_CELL_FL_DO_LOOKUP 1 /* DNS lookup requested */ #define AFS_CELL_FL_CHECK_ALIAS 2 /* Need to check for aliases */ +#define AFS_CELL_FL_HAVE_INO 3 /* Have dynroot_ino */ enum afs_cell_state state; short error; enum dns_record_source dns_source:8; /* Latest source of data from lookup */ From d0c8ad418b47891a03426c5e02ebb0537f8d68f8 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:54 +0100 Subject: [PATCH 2061/2758] afs: Fix the volume AFS_VOLUME_RM_TREE is set on [ Upstream commit 56b4e4b26f84411d880f968a539207b0a8889c8c ] Fix afs_insert_volume_into_cell() to set AFS_VOLUME_RM_TREE on the volume replaced, not the new volume, as it's now removed from the cell's volume tree. This will cause the old volume to be removed from the tree twice and the new volume never to be removed. Fixes: 9a6b294ab496 ("afs: Fix use-after-free due to get/remove race in volume tree") Closes: https://sashiko.dev/#/patchset/20260618074903.2374756-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-21-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/volume.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/afs/volume.c b/fs/afs/volume.c index 9ae5c8ad2e04b..4f79d25ec37f4 100644 --- a/fs/afs/volume.c +++ b/fs/afs/volume.c @@ -40,7 +40,7 @@ static struct afs_volume *afs_insert_volume_into_cell(struct afs_cell *cell, goto found; } - set_bit(AFS_VOLUME_RM_TREE, &volume->flags); + set_bit(AFS_VOLUME_RM_TREE, &p->flags); rb_replace_node_rcu(&p->cell_node, &volume->cell_node, &cell->volumes); } } From 89ec425b454e044149322fd0d6dffd74e96b05d3 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:55 +0100 Subject: [PATCH 2062/2758] afs: Fix unchecked-length string display in debug statement [ Upstream commit 903d37c97228258da71e092f8b4ab260ce81497d ] Fix afs_extract_vlserver_list() to limit the length of the displayed string in a debug statement(). Fixes: 0a5143f2f89c ("afs: Implement VL server rotation") Closes: https://sashiko.dev/#/patchset/20260618074903.2374756-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-22-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/vl_list.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/afs/vl_list.c b/fs/afs/vl_list.c index 003889cf0f18d..0dc1b005f554f 100644 --- a/fs/afs/vl_list.c +++ b/fs/afs/vl_list.c @@ -200,6 +200,8 @@ struct afs_vlserver_list *afs_extract_vlserver_list(struct afs_cell *cell, b += sizeof(*hdr); while (end - b >= sizeof(bs)) { + int nlen; + bs.name_len = afs_extract_le16(&b); bs.priority = afs_extract_le16(&b); bs.weight = afs_extract_le16(&b); @@ -209,10 +211,12 @@ struct afs_vlserver_list *afs_extract_vlserver_list(struct afs_cell *cell, bs.protocol = *b++; bs.nr_addrs = *b++; + nlen = min3(bs.name_len, end - b, 255); + _debug("extract %u %u %u %u %u %u %*.*s", bs.name_len, bs.priority, bs.weight, bs.port, bs.protocol, bs.nr_addrs, - bs.name_len, bs.name_len, b); + bs.name_len, nlen, b); if (end - b < bs.name_len) break; From 27ddd3442fc6f698fb8577c7cfb243ddd81ea8c0 Mon Sep 17 00:00:00 2001 From: Fengnan Chang Date: Fri, 12 Jun 2026 12:40:41 +0800 Subject: [PATCH 2063/2758] iomap: release pages on atomic dio size mismatch [ Upstream commit 681e452683b69a8e1a571cba0f238f8ceacf55d2 ] If bio_iov_iter_get_pages() or the bounce helper succeeds but builds a short bio, the REQ_ATOMIC size check rejects it before submission. The old error path only dropped the bio reference, leaving any pages already attached to the bio unreleased. Release or unbounce the pages before falling through to out_put_bio on this error path. This bug was reported by sashiko: https://sashiko.dev/#/patchset/20260608073134.95964-1-changfengnan%40bytedance.com Fixes: 9e0933c21c12 ("fs: iomap: Atomic write support") Signed-off-by: Fengnan Chang Link: https://patch.msgid.link/20260612044041.10677-1-changfengnan@bytedance.com Reviewed-by: Christoph Hellwig Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/iomap/direct-io.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/iomap/direct-io.c b/fs/iomap/direct-io.c index b36ee619cdcdd..9fedf367c3ebe 100644 --- a/fs/iomap/direct-io.c +++ b/fs/iomap/direct-io.c @@ -369,7 +369,7 @@ static ssize_t iomap_dio_bio_iter_one(struct iomap_iter *iter, */ if ((op & REQ_ATOMIC) && WARN_ON_ONCE(ret != iomap_length(iter))) { ret = -EINVAL; - goto out_put_bio; + goto out_bio_release_pages; } if (iter->iomap.flags & IOMAP_F_INTEGRITY) { @@ -393,6 +393,11 @@ static ssize_t iomap_dio_bio_iter_one(struct iomap_iter *iter, iomap_dio_submit_bio(iter, dio, bio, pos); return ret; +out_bio_release_pages: + if (dio->flags & IOMAP_DIO_BOUNCE) + bio_iov_iter_unbounce(bio, true, false); + else + bio_release_pages(bio, false); out_put_bio: bio_put(bio); return ret; From 8a29e60e2176b02e04f8737c8b32b696230eb0c5 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Thu, 18 Jun 2026 10:39:22 -0400 Subject: [PATCH 2064/2758] minix: avoid overflow in bitmap block count calculation [ Upstream commit fb3e566cafc38fe3ba35e6843a2d529a3748870c ] minix_check_superblock() uses minix_blocks_needed() to verify that the on-disk imap and zmap block counts are large enough for the advertised inode and zone counts. The helper currently performs DIV_ROUND_UP() in unsigned int arithmetic. A Minix v3 image can set s_ninodes or s_zones near UINT_MAX so the addition inside DIV_ROUND_UP() wraps to zero. That makes a zero imap/zmap block count look valid, after which minix_fill_super() can dereference s_imap[0] or s_zmap[0] even though no bitmap buffers were allocated. Impact: mounting a crafted Minix v3 image whose s_ninodes or s_zones is near UINT_MAX makes minix_check_superblock() accept a zero bitmap-block count and minix_fill_super() dereference s_imap[0]/s_zmap[0], panicking the kernel. The divisor is the bitmap capacity in bits, blocksize * 8, which is always a power of two: minix_fill_super() obtains the block size through sb_set_blocksize(), and blk_validate_block_size() rejects any size that is not a power of two. Use DIV_ROUND_UP_POW2(), which divides before adding the round-up term and so cannot overflow for a power-of-two divisor. Fixes: 8c97a6ddc956 ("minix: Add required sanity checking to minix_check_superblock()") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Link: https://patch.msgid.link/20260618143922.3066874-1-michael.bommarito@gmail.com Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/minix/minix.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/minix/minix.h b/fs/minix/minix.h index f2025c9b58252..9e52d4302f0d2 100644 --- a/fs/minix/minix.h +++ b/fs/minix/minix.h @@ -97,7 +97,7 @@ static inline struct minix_inode_info *minix_i(struct inode *inode) static inline unsigned minix_blocks_needed(unsigned bits, unsigned blocksize) { - return DIV_ROUND_UP(bits, blocksize * 8); + return DIV_ROUND_UP_POW2(bits, blocksize * 8); } #if defined(CONFIG_MINIX_FS_NATIVE_ENDIAN) && \ From 26757dac15175f2a42e3537f1ba86e62456d48f1 Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Wed, 17 Jun 2026 16:50:49 +0800 Subject: [PATCH 2065/2758] cachefiles: Fix double unlock in nomem_d_alloc error path [ Upstream commit 8c256fba2b46020004201c500b2a1fbc707a33ef ] When start_creating() fails and returns -ENOMEM, it has already released the parent directory lock in __start_dirop(): static struct dentry *__start_dirop(...) { ... inode_lock_nested(dir, I_MUTEX_PARENT); dentry = lookup_one_qstr_excl(name, parent, lookup_flags); if (IS_ERR(dentry)) inode_unlock(dir); <-- Lock released on error return dentry; } However, the nomem_d_alloc error path in cachefiles_get_directory() unconditionally calls inode_unlock(d_inode(dir)) again, causing a double unlock that corrupts the rwsem state. This is a leftover from commit 7ab96df840e60 which replaced manual locking with start_creating() but failed to update the nomem_d_alloc path (while correctly updating mkdir_error and lookup_error paths). Fixes: 7ab96df840e6 ("VFS/nfsd/cachefiles/ovl: add start_creating() and end_creating()") Signed-off-by: Hongling Zeng Link: https://patch.msgid.link/20260617085049.730789-1-zenghongling@kylinos.cn Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/cachefiles/namei.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c index 2937db690b40b..2c46f0decb02e 100644 --- a/fs/cachefiles/namei.c +++ b/fs/cachefiles/namei.c @@ -209,7 +209,6 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache, return ERR_PTR(ret); nomem_d_alloc: - inode_unlock(d_inode(dir)); _leave(" = -ENOMEM"); return ERR_PTR(-ENOMEM); } From 3c181e6ff1f440f9159a6246e780b400a1133b28 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Tue, 9 Jun 2026 20:46:56 +0200 Subject: [PATCH 2066/2758] ovl: fix comment about locking order [ Upstream commit fd5637a2fe6dd4448392738691d63e5559fafb12 ] Forgot to update the comment when we changed the locking order. Fixes: 162d06444070c ("ovl: reorder ovl_want_write() after ovl_inode_lock()") Signed-off-by: Amir Goldstein Link: https://patch.msgid.link/20260609184656.1916631-1-amir73il@gmail.com Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/overlayfs/inode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 00c69707bda98..bc71231cad532 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -783,8 +783,8 @@ static const struct address_space_operations ovl_aops = { * * This chain is valid: * - inode->i_rwsem (inode_lock[2]) - * - upper_mnt->mnt_sb->s_writers (ovl_want_write[0]) * - OVL_I(inode)->lock (ovl_inode_lock[2]) + * - upper_mnt->mnt_sb->s_writers (ovl_want_write[0]) * - OVL_I(lowerinode)->lock (ovl_inode_lock[1]) * * And this chain is valid: @@ -797,8 +797,8 @@ static const struct address_space_operations ovl_aops = { * held, because it is in reverse order of the non-nested case using the same * upper fs: * - inode->i_rwsem (inode_lock[1]) - * - upper_mnt->mnt_sb->s_writers (ovl_want_write[0]) * - OVL_I(inode)->lock (ovl_inode_lock[1]) + * - upper_mnt->mnt_sb->s_writers (ovl_want_write[0]) */ #define OVL_MAX_NESTING FILESYSTEM_MAX_STACK_DEPTH From 7f7780abb4c0fdc9a2603aea8e985ff14ee900e0 Mon Sep 17 00:00:00 2001 From: Morduan Zang Date: Wed, 24 Jun 2026 14:26:22 +0800 Subject: [PATCH 2067/2758] iomap: guard io_size EOF trim against concurrent truncate underflow [ Upstream commit 55ec50d046c03b3724741957f7b007856e36dbe7 ] iomap: fix zero padding data issue in concurrent append writes changed ioend accounting so that io_size tracks only valid data within EOF. This trims io_size when a writeback range extends past end_pos: ioend->io_size += map_len; if (ioend->io_offset + ioend->io_size > end_pos) ioend->io_size = end_pos - ioend->io_offset; However, if end_pos ends up below ioend->io_offset, the subtraction becomes negative and is stored in size_t io_size, causing an unsigned wrap to a huge value. This can happen when writeback continues past byte-level EOF up to a block-aligned range, or when a concurrent truncate shrinks the file after end_pos was sampled in iomap_writeback_handle_eof(). A wrapped io_size can mislead append detection and corrupt completion-time size handling, since filesystem end_io paths consume io_size for decisions such as on-disk EOF updates and unwritten/COW completion ranges. Fix this by clamping io_size to zero when EOF has moved to or before the ioend start offset. This preserves the original intent of trimming io_size to valid in-EOF data while avoiding the underflow. Fixes: 51d20d1dacbe ("iomap: fix zero padding data issue in concurrent append writes") Suggested-by: Christoph Hellwig Signed-off-by: Morduan Zang Link: https://patch.msgid.link/9E38E2659B47DC2A+20260624062622.337469-1-zhangdandan@uniontech.com Reviewed-by: Christoph Hellwig Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/iomap/ioend.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/iomap/ioend.c b/fs/iomap/ioend.c index acf3cf98b23a9..2d5611f6cc57d 100644 --- a/fs/iomap/ioend.c +++ b/fs/iomap/ioend.c @@ -297,8 +297,12 @@ ssize_t iomap_add_to_ioend(struct iomap_writepage_ctx *wpc, struct folio *folio, * appending writes. */ ioend->io_size += map_len; - if (ioend->io_offset + ioend->io_size > end_pos) - ioend->io_size = end_pos - ioend->io_offset; + if (ioend->io_offset + ioend->io_size > end_pos) { + if (ioend->io_offset >= end_pos) + ioend->io_size = 0; + else + ioend->io_size = end_pos - ioend->io_offset; + } wbc_account_cgroup_owner(wpc->wbc, folio, map_len); return map_len; From 1188a9846fadeacf9d1430b528e5217f749d665b Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 25 Jun 2026 15:06:20 +0100 Subject: [PATCH 2068/2758] netfs: Fix netfs_create_write_req() to handle async cache object creation [ Upstream commit dbd6f56d975b23241b7bbb11bb8f562af548a0aa ] netfs_create_write_req() will skip caching if the fscache cookie is disabled, but this is a problem because async cache object creation might not have got far enough yet that has been enabled - thereby causing the call to fscache_begin_write_operation() to be skipped. Fix this by removing the checks on the cookie and delegating this to fscache_begin_write_operation(). Fixes: 7b589a9b45ae ("netfs: Fix handling of USE_PGPRIV2 and WRITE_TO_CACHE flags") Closes: https://sashiko.dev/#/patchset/20260624115737.2964520-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260625140640.3116900-3-dhowells@redhat.com cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/netfs/write_issue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c index c03c7cc45e471..4f55228f0fd49 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -106,7 +106,7 @@ struct netfs_io_request *netfs_create_write_req(struct address_space *mapping, _enter("R=%x", wreq->debug_id); ictx = netfs_inode(wreq->inode); - if (is_cacheable && netfs_is_cache_enabled(ictx)) + if (is_cacheable) fscache_begin_write_operation(&wreq->cache_resources, netfs_i_cookie(ictx)); if (rolling_buffer_init(&wreq->buffer, wreq->debug_id, ITER_SOURCE) < 0) goto nomem; From 68fb8a93a34bf04554dd1111dad21775f5db8dfc Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 25 Jun 2026 15:06:22 +0100 Subject: [PATCH 2069/2758] cachefiles: Fix file burial to take lock when unsetting S_KERNEL_FILE [ Upstream commit 511a018ed2afd8d415edd307ce7ad2048506f6a1 ] Fix cachefiles_bury_object() to lock the inode of the file being buried whilst it unsets the S_KERNEL_FILE flag. Fixes: 07a90e97400c ("cachefiles: Implement culling daemon commands") Closes: https://sashiko.dev/#/patchset/20260616100821.2062304-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260625140640.3116900-5-dhowells@redhat.com cc: Paulo Alcantara cc: NeilBrown cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/cachefiles/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c index 2c46f0decb02e..90200410dcfd4 100644 --- a/fs/cachefiles/namei.c +++ b/fs/cachefiles/namei.c @@ -374,7 +374,7 @@ int cachefiles_bury_object(struct cachefiles_cache *cache, "Rename failed with error %d", ret); } - __cachefiles_unmark_inode_in_use(object, d_inode(rep)); + cachefiles_do_unmark_inode_in_use(object, d_inode(rep)); end_renaming(&rd); _leave(" = 0"); return 0; From 0348e3fa0dfbf152af687e86ce079742b1860da9 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 25 Jun 2026 15:06:30 +0100 Subject: [PATCH 2070/2758] netfs: Fix writethrough to use collection offload [ Upstream commit ba6a9f6533c77c628eef0c0c5c19cd316e2be1b4 ] Fix writethrough write to set NETFS_RREQ_OFFLOAD_COLLECTION on the request so that collection is processed asynchronously rather than only right at the end - and also so that asynchronous O_SYNC writes get collected at all. Fixes: 288ace2f57c9 ("netfs: New writeback implementation") Closes: https://sashiko.dev/#/patchset/20260616100821.2062304-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260625140640.3116900-13-dhowells@redhat.com cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/netfs/write_issue.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c index 4f55228f0fd49..de7a5684b6091 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -628,6 +628,7 @@ struct netfs_io_request *netfs_begin_writethrough(struct kiocb *iocb, size_t len } wreq->io_streams[0].avail = true; + __set_bit(NETFS_RREQ_OFFLOAD_COLLECTION, &wreq->flags); trace_netfs_write(wreq, netfs_write_trace_writethrough); return wreq; } From 89df9c158a25d0a7c6ca079a9bd9ca7009d75c7b Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 25 Jun 2026 15:06:31 +0100 Subject: [PATCH 2071/2758] netfs: Fix writeback error handling [ Upstream commit ac5f95ac5d6d0f4c567b8b642825705a2bf0d79e ] Fix the error handling in writeback_iter() loop. If an error occurs, writeback_iter() needs to be called again with *error set to the error so that it can clean up iteration state. Further, the current folio needs unlocking and redirtying. Fixes: 288ace2f57c9 ("netfs: New writeback implementation") Link: https://sashiko.dev/#/patchset/20260619140646.2633762-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260625140640.3116900-14-dhowells@redhat.com cc: Paulo Alcantara cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/netfs/write_issue.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c index de7a5684b6091..cd0c8b3297ee0 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -588,8 +588,6 @@ int netfs_writepages(struct address_space *mapping, } error = netfs_write_folio(wreq, wbc, folio); - if (error < 0) - break; } while ((folio = writeback_iter(mapping, wbc, folio, &error))); netfs_end_issue_write(wreq); @@ -602,7 +600,14 @@ int netfs_writepages(struct address_space *mapping, return error; couldnt_start: - netfs_kill_dirty_pages(mapping, wbc, folio); + if (error == -ENOMEM) { + folio_redirty_for_writepage(wbc, folio); + folio_unlock(folio); + folio = writeback_iter(mapping, wbc, folio, &error); + WARN_ON_ONCE(folio != NULL); + } else { + netfs_kill_dirty_pages(mapping, wbc, folio); + } out: mutex_unlock(&ictx->wb_lock); _leave(" = %d", error); From 1a4421c7a5611db554328f721a4f7642440b44bd Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 25 Jun 2026 15:06:32 +0100 Subject: [PATCH 2072/2758] netfs: Fix folio state after ENOMEM whilst under writeback iteration [ Upstream commit b6a713fd34b9498ee2164d5d3e8460732a392efc ] Fix the state of the current folio when ENOMEM occurs during writeback iteration. The folio needs to be redirtied and unlocked before the terminal writeback_iter() is invoked. Fixes: 06fa229ceb36 ("netfs: Abstract out a rolling folio buffer implementation") Link: https://sashiko.dev/#/patchset/20260619140646.2633762-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260625140640.3116900-15-dhowells@redhat.com cc: Paulo Alcantara cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/netfs/write_issue.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c index cd0c8b3297ee0..d0d884731dc5d 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -588,6 +588,10 @@ int netfs_writepages(struct address_space *mapping, } error = netfs_write_folio(wreq, wbc, folio); + if (error == -ENOMEM) { + folio_redirty_for_writepage(wbc, folio); + folio_unlock(folio); + } } while ((folio = writeback_iter(mapping, wbc, folio, &error))); netfs_end_issue_write(wreq); From d94b9922b2ae5ee6e900f8da0bd537b251c6110c Mon Sep 17 00:00:00 2001 From: Francois Dugast Date: Tue, 16 Jun 2026 10:17:56 +0200 Subject: [PATCH 2073/2758] drm/xe/pt: Fix NULL pointer dereference in xe_pt_zap_ptes_entry() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 3feeb667197bd58a17f4edfdbcad249ffcb3c864 ] The page-table walk framework may pass a NULL *child pointer for unpopulated entries. xe_pt_zap_ptes_entry() called container_of(*child) before checking for NULL, then dereferenced the result, causing a crash. Move the container_of() call after a NULL guard, so the function returns early instead of proceeding with an invalid pointer. XE_WARN_ON is kept to help root cause the issue, but we now bail instead of crashing the driver. v2: Comment that triggering XE_WARN_ON is unexpected behavior (Matt Brost) Fixes: dd08ebf6c352 ("drm/xe: Introduce a new DRM driver for Intel GPUs") Cc: Matthew Brost Cc: Thomas Hellström Reviewed-by: Matthew Brost Link: https://lore.kernel.org/r/20260616081756.286918-1-francois.dugast@intel.com Signed-off-by: Francois Dugast (cherry picked from commit b9297d19d9df5d4b6c994648570c5dcd1cac68ff) Signed-off-by: Thomas Hellström Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_pt.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c index 8e5f4f0dea3f9..bfc326acbde42 100644 --- a/drivers/gpu/drm/xe/xe_pt.c +++ b/drivers/gpu/drm/xe/xe_pt.c @@ -884,12 +884,20 @@ static int xe_pt_zap_ptes_entry(struct xe_ptw *parent, pgoff_t offset, { struct xe_pt_zap_ptes_walk *xe_walk = container_of(walk, typeof(*xe_walk), base); - struct xe_pt *xe_child = container_of(*child, typeof(*xe_child), base); + struct xe_pt *xe_child; pgoff_t end_offset; - XE_WARN_ON(!*child); XE_WARN_ON(!level); + /* + * Below would be unexpected behavior that needs to be root caused + * but better warn and bail than crash the driver. + */ + if (XE_WARN_ON(!*child)) + return 0; + + xe_child = container_of(*child, typeof(*xe_child), base); + /* * Note that we're called from an entry callback, and we're dealing * with the child of that entry rather than the parent, so need to From 159f9aa8d2e06eec6a5c06d44f392023efc8a726 Mon Sep 17 00:00:00 2001 From: Brajesh Gupta Date: Wed, 1 Jul 2026 10:49:30 +0530 Subject: [PATCH 2074/2758] drm/imagination: Fix returned size for DRM_IOCTL_PVR_DEV_QUERY [ Upstream commit d431b4012fd22920523dbd2806da663c1048e386 ] For a few subtypes of DRM_IOCTL_PVR_DEV_QUERY, driver was overriding the returned size unconditionally. This would have resulted in increase of reported size beyond the amount of data returned to userspace when args->size < size of query structure. Updated behaviour matches with the description of drm_pvr_ioctl_dev_query_args.size and written byte length. None of the structures of DRM_IOCTL_PVR_DEV_QUERY changed after addition, so change will not break any compatibility with earlier version. Fixes: f99f5f3ea7ef ("drm/imagination: Add GPU ID parsing and firmware loading") Fixes: ff5f643de0bf ("drm/imagination: Add GEM and VM related code") Signed-off-by: Brajesh Gupta Reviewed-by: Alessio Belle Link: https://patch.msgid.link/20260701-b4-b4-query-v2-1-a1b491387875@imgtec.com Signed-off-by: Alessio Belle Signed-off-by: Sasha Levin --- drivers/gpu/drm/imagination/pvr_drv.c | 6 ++++-- drivers/gpu/drm/imagination/pvr_vm.c | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/imagination/pvr_drv.c b/drivers/gpu/drm/imagination/pvr_drv.c index 268900464ab69..cfb67f77bde6c 100644 --- a/drivers/gpu/drm/imagination/pvr_drv.c +++ b/drivers/gpu/drm/imagination/pvr_drv.c @@ -514,7 +514,8 @@ pvr_dev_query_quirks_get(struct pvr_device *pvr_dev, if (err < 0) return err; - args->size = sizeof(query); + if (args->size > sizeof(query)) + args->size = sizeof(query); return 0; } @@ -595,7 +596,8 @@ pvr_dev_query_enhancements_get(struct pvr_device *pvr_dev, if (err < 0) return err; - args->size = sizeof(query); + if (args->size > sizeof(query)) + args->size = sizeof(query); return 0; } diff --git a/drivers/gpu/drm/imagination/pvr_vm.c b/drivers/gpu/drm/imagination/pvr_vm.c index e1ec60f34b6e6..396d349fb6ce4 100644 --- a/drivers/gpu/drm/imagination/pvr_vm.c +++ b/drivers/gpu/drm/imagination/pvr_vm.c @@ -1019,7 +1019,8 @@ pvr_static_data_areas_get(const struct pvr_device *pvr_dev, if (err < 0) return err; - args->size = sizeof(query); + if (args->size > sizeof(query)) + args->size = sizeof(query); return 0; } @@ -1069,7 +1070,8 @@ pvr_heap_info_get(const struct pvr_device *pvr_dev, if (err < 0) return err; - args->size = sizeof(query); + if (args->size > sizeof(query)) + args->size = sizeof(query); return 0; } From ab9ea5c943c7e780124e75b7ffad9f1c752b2579 Mon Sep 17 00:00:00 2001 From: Shuicheng Lin Date: Thu, 25 Jun 2026 21:56:15 +0000 Subject: [PATCH 2075/2758] drm/xe/userptr: Hold notifier_lock for write on inject test path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit dca6e08c923a44d2d66b955e03dd57a3a38c2b94 ] When CONFIG_DRM_XE_USERPTR_INVAL_INJECT=y, xe_pt_svm_userptr_pre_commit() runs vma_check_userptr() with the svm notifier_lock taken for read. The test injection causes vma_check_userptr() to call xe_vma_userptr_force_invalidate(), which feeds into xe_vma_userptr_do_inval() with drm_gpusvm_ctx.in_notifier=true. That flag tells drm_gpusvm_unmap_pages() the caller already holds notifier_lock for write and only asserts the mode. Because the caller actually holds it for read, the assertion fires: WARNING: drivers/gpu/drm/drm_gpusvm.c:1669 at \ drm_gpusvm_unmap_pages+0xd4/0x130 [drm_gpusvm_helper] Call Trace: xe_vma_userptr_do_inval+0x40d/0xfd0 [xe] xe_vma_userptr_invalidate_pass1+0x3e6/0x8d0 [xe] xe_vma_userptr_force_invalidate+0xde/0x290 [xe] vma_check_userptr.constprop.0+0x1c6/0x220 [xe] xe_pt_svm_userptr_pre_commit+0x6a3/0xc60 [xe] ... xe_vm_bind_ioctl+0x3a0a/0x4480 [xe] Acquire notifier_lock for write in pre-commit when the inject Kconfig is enabled, via new helpers xe_pt_svm_userptr_notifier_lock()/_unlock(). Rename xe_svm_assert_held_read() to xe_svm_assert_held_read_or_inject_write() so it asserts the correct mode under each build configuration. Production builds (CONFIG_DRM_XE_USERPTR_INVAL_INJECT=n) keep the existing read-mode behavior bit-for-bit. Fixes: 9e9787414882 ("drm/xe/userptr: replace xe_hmm with gpusvm") Assisted-by: Claude:claude-opus-4.7 Cc: Matthew Auld Cc: Zongyao Bai Reviewed-by: Matthew Brost Link: https://patch.msgid.link/20260625215615.3016892-1-shuicheng.lin@intel.com Signed-off-by: Shuicheng Lin (cherry picked from commit 80ccbd97ffee8ad2e73167d826fe7be548364365) Signed-off-by: Thomas Hellström Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_pt.c | 43 ++++++++++++++++++++++++++++++------- drivers/gpu/drm/xe/xe_svm.h | 15 +++++++++++-- 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c index bfc326acbde42..3e438a2799850 100644 --- a/drivers/gpu/drm/xe/xe_pt.c +++ b/drivers/gpu/drm/xe/xe_pt.c @@ -1085,7 +1085,7 @@ static void xe_pt_commit_locks_assert(struct xe_vma *vma) xe_pt_commit_prepare_locks_assert(vma); if (xe_vma_is_userptr(vma)) - xe_svm_assert_held_read(vm); + xe_svm_assert_held_read_or_inject_write(vm); } static void xe_pt_commit(struct xe_vma *vma, @@ -1405,6 +1405,33 @@ static int xe_pt_pre_commit(struct xe_migrate_pt_update *pt_update) pt_update_ops, rftree); } +/* + * Acquire/release the svm notifier_lock around xe_pt_svm_userptr_pre_commit() + * and the matching late release in xe_pt_update_ops_run(). Read mode by + * default; write mode when CONFIG_DRM_XE_USERPTR_INVAL_INJECT is on, + * because a userptr op in this critical section may invoke the injected + * xe_vma_userptr_force_invalidate() path that calls + * drm_gpusvm_unmap_pages() with ctx->in_notifier=true, which requires the + * lock held for write. + */ +static void xe_pt_svm_userptr_notifier_lock(struct xe_vm *vm) +{ +#if IS_ENABLED(CONFIG_DRM_XE_USERPTR_INVAL_INJECT) + down_write(&vm->svm.gpusvm.notifier_lock); +#else + xe_svm_notifier_lock(vm); +#endif +} + +static void xe_pt_svm_userptr_notifier_unlock(struct xe_vm *vm) +{ +#if IS_ENABLED(CONFIG_DRM_XE_USERPTR_INVAL_INJECT) + up_write(&vm->svm.gpusvm.notifier_lock); +#else + xe_svm_notifier_unlock(vm); +#endif +} + #if IS_ENABLED(CONFIG_DRM_GPUSVM) #ifdef CONFIG_DRM_XE_USERPTR_INVAL_INJECT @@ -1436,7 +1463,7 @@ static int vma_check_userptr(struct xe_vm *vm, struct xe_vma *vma, struct xe_userptr_vma *uvma; unsigned long notifier_seq; - xe_svm_assert_held_read(vm); + xe_svm_assert_held_read_or_inject_write(vm); if (!xe_vma_is_userptr(vma)) return 0; @@ -1466,7 +1493,7 @@ static int op_check_svm_userptr(struct xe_vm *vm, struct xe_vma_op *op, { int err = 0; - xe_svm_assert_held_read(vm); + xe_svm_assert_held_read_or_inject_write(vm); switch (op->base.op) { case DRM_GPUVA_OP_MAP: @@ -1538,12 +1565,12 @@ static int xe_pt_svm_userptr_pre_commit(struct xe_migrate_pt_update *pt_update) if (err) return err; - xe_svm_notifier_lock(vm); + xe_pt_svm_userptr_notifier_lock(vm); list_for_each_entry(op, &vops->list, link) { err = op_check_svm_userptr(vm, op, pt_update_ops); if (err) { - xe_svm_notifier_unlock(vm); + xe_pt_svm_userptr_notifier_unlock(vm); break; } } @@ -2379,7 +2406,7 @@ static void bind_op_commit(struct xe_vm *vm, struct xe_tile *tile, vma->tile_invalidated & ~BIT(tile->id)); vma->tile_staged &= ~BIT(tile->id); if (xe_vma_is_userptr(vma)) { - xe_svm_assert_held_read(vm); + xe_svm_assert_held_read_or_inject_write(vm); to_userptr_vma(vma)->userptr.initial_bind = true; } @@ -2415,7 +2442,7 @@ static void unbind_op_commit(struct xe_vm *vm, struct xe_tile *tile, if (!vma->tile_present) { list_del_init(&vma->combined_links.rebind); if (xe_vma_is_userptr(vma)) { - xe_svm_assert_held_read(vm); + xe_svm_assert_held_read_or_inject_write(vm); spin_lock(&vm->userptr.invalidated_lock); list_del_init(&to_userptr_vma(vma)->userptr.invalidate_link); @@ -2691,7 +2718,7 @@ xe_pt_update_ops_run(struct xe_tile *tile, struct xe_vma_ops *vops) } if (pt_update_ops->needs_svm_lock) - xe_svm_notifier_unlock(vm); + xe_pt_svm_userptr_notifier_unlock(vm); /* * The last fence is only used for zero bind queue idling; migrate diff --git a/drivers/gpu/drm/xe/xe_svm.h b/drivers/gpu/drm/xe/xe_svm.h index b7b8eeacf196b..3ca46a6f98c73 100644 --- a/drivers/gpu/drm/xe/xe_svm.h +++ b/drivers/gpu/drm/xe/xe_svm.h @@ -394,8 +394,19 @@ static inline struct drm_pagemap *xe_drm_pagemap_from_fd(int fd, u32 region_inst #define xe_svm_assert_in_notifier(vm__) \ lockdep_assert_held_write(&(vm__)->svm.gpusvm.notifier_lock) -#define xe_svm_assert_held_read(vm__) \ +/* + * Assert the svm notifier_lock is held. Read mode by default; write mode + * when CONFIG_DRM_XE_USERPTR_INVAL_INJECT is on, because that path forces + * a userptr invalidation that ends in drm_gpusvm_unmap_pages() with + * ctx->in_notifier=true, which requires the lock held for write. + */ +#if IS_ENABLED(CONFIG_DRM_XE_USERPTR_INVAL_INJECT) +#define xe_svm_assert_held_read_or_inject_write(vm__) \ + lockdep_assert_held_write(&(vm__)->svm.gpusvm.notifier_lock) +#else +#define xe_svm_assert_held_read_or_inject_write(vm__) \ lockdep_assert_held_read(&(vm__)->svm.gpusvm.notifier_lock) +#endif #define xe_svm_notifier_lock(vm__) \ drm_gpusvm_notifier_lock(&(vm__)->svm.gpusvm) @@ -409,7 +420,7 @@ static inline struct drm_pagemap *xe_drm_pagemap_from_fd(int fd, u32 region_inst #else #define xe_svm_assert_in_notifier(...) do {} while (0) -static inline void xe_svm_assert_held_read(struct xe_vm *vm) +static inline void xe_svm_assert_held_read_or_inject_write(struct xe_vm *vm) { } From f1a1909f36b7c6eaf48ac5b42d88ee44040bc3d7 Mon Sep 17 00:00:00 2001 From: Shuicheng Lin Date: Thu, 25 Jun 2026 22:44:52 +0000 Subject: [PATCH 2076/2758] drm/xe/userptr: Drop bogus static from finish in force_invalidate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 0c56ea482aab1470b96a525ef53fa3eb8704f9a6 ] The local "finish" pointer in xe_vma_userptr_force_invalidate() is unconditionally written before each read, so the static storage class serves no purpose. Worse, it makes the variable a process-wide shared slot: the function's per-VM asserts do not exclude concurrent callers on different VMs, so two such callers can race on the slot and take the wrong if (finish) branch. The function is gated by CONFIG_DRM_XE_USERPTR_INVAL_INJECT (developer/test option, default n), so production builds are unaffected. Drop the static. Fixes: 18c4e536959e ("drm/xe/userptr: Convert invalidation to two-pass MMU notifier") Assisted-by: Claude:claude-opus-4.7 Cc: Thomas Hellström Cc: Matthew Brost Reviewed-by: Matthew Brost Reviewed-by: Zongyao Bai Link: https://patch.msgid.link/20260625224452.3243231-1-shuicheng.lin@intel.com Signed-off-by: Shuicheng Lin (cherry picked from commit ed382e3b07fae51a09d7290485bff0592f6b168b) Signed-off-by: Thomas Hellström Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_userptr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_userptr.c b/drivers/gpu/drm/xe/xe_userptr.c index 6761005c0b900..6f71bc66b14eb 100644 --- a/drivers/gpu/drm/xe/xe_userptr.c +++ b/drivers/gpu/drm/xe/xe_userptr.c @@ -269,7 +269,7 @@ static const struct mmu_interval_notifier_ops vma_userptr_notifier_ops = { */ void xe_vma_userptr_force_invalidate(struct xe_userptr_vma *uvma) { - static struct mmu_interval_notifier_finish *finish; + struct mmu_interval_notifier_finish *finish; struct xe_vm *vm = xe_vma_vm(&uvma->vma); /* Protect against concurrent userptr pinning */ From 23ee91355e31947595b86ebb9ef4664f20fd926d Mon Sep 17 00:00:00 2001 From: Shuicheng Lin Date: Fri, 26 Jun 2026 21:06:31 +0000 Subject: [PATCH 2077/2758] drm/xe/hw_engine: Fix double-free of managed BO in error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 7ac3cae7a251d28e9079de07a991bd4eb2bb7fd8 ] The error path in hw_engine_init() explicitly frees a BO allocated with xe_managed_bo_create_pin_map() via xe_bo_unpin_map_no_vm(). Since the managed BO already has a devm cleanup action registered, this causes a double-free when devm unwinds during probe failure. Remove the explicit free and let devm handle it, consistent with all other xe_managed_bo_create_pin_map() callers. Fixes: 0e1a47fcabc8 ("drm/xe: Add a helper for DRM device-lifetime BO create") Assisted-by: Claude:claude-opus-4.6 Reviewed-by: Zongyao Bai Link: https://patch.msgid.link/20260626210631.3887291-1-shuicheng.lin@intel.com Signed-off-by: Shuicheng Lin (cherry picked from commit e459a3bdeb117be496d7f229e2ea1f6c9fe4080b) Signed-off-by: Thomas Hellström Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_hw_engine.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_hw_engine.c b/drivers/gpu/drm/xe/xe_hw_engine.c index 6dd05fac65956..a7441a66e94c2 100644 --- a/drivers/gpu/drm/xe/xe_hw_engine.c +++ b/drivers/gpu/drm/xe/xe_hw_engine.c @@ -629,7 +629,7 @@ static int hw_engine_init(struct xe_gt *gt, struct xe_hw_engine *hwe, hwe->exl_port = xe_execlist_port_create(xe, hwe); if (IS_ERR(hwe->exl_port)) { err = PTR_ERR(hwe->exl_port); - goto err_hwsp; + goto err_name; } } else { /* GSCCS has a special interrupt for reset */ @@ -649,8 +649,6 @@ static int hw_engine_init(struct xe_gt *gt, struct xe_hw_engine *hwe, return devm_add_action_or_reset(xe->drm.dev, hw_engine_fini, hwe); -err_hwsp: - xe_bo_unpin_map_no_vm(hwe->hwsp); err_name: hwe->name = NULL; From a4208d8032abd7f591581994f31e23b80b8fe659 Mon Sep 17 00:00:00 2001 From: Michal Wajdeczko Date: Wed, 27 May 2026 20:37:35 +0200 Subject: [PATCH 2078/2758] drm/xe/pf: Don't attempt to process FAST_REQ or EVENT relays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit ed8b0d731892c68b41ecbd27c952af284816dec1 ] Currently defined VF/PF relay actions use regular REQUEST messages only and the PF shouldn't attempt to handle FAST_REQUEST nor EVENT messages as this would result in breaking the VFPF ABI protocol and also might trigger an assert on the PF side. Fixes: 98e62805921c ("drm/xe/pf: Add SR-IOV GuC Relay PF services") Signed-off-by: Michal Wajdeczko Reviewed-by: Michał Winiarski Link: https://patch.msgid.link/20260527183735.22616-1-michal.wajdeczko@intel.com (cherry picked from commit 1714d360fc5ae2e0886a69e979095d9c7ff3568a) Signed-off-by: Thomas Hellström Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_guc_relay.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_guc_relay.c b/drivers/gpu/drm/xe/xe_guc_relay.c index 577a315854afc..eed0a750d2eb2 100644 --- a/drivers/gpu/drm/xe/xe_guc_relay.c +++ b/drivers/gpu/drm/xe/xe_guc_relay.c @@ -689,12 +689,17 @@ static int relay_action_handler(struct xe_guc_relay *relay, u32 origin, return relay_testloop_action_handler(relay, origin, msg, len, response, size); type = FIELD_GET(GUC_HXG_MSG_0_TYPE, msg[0]); + relay_assert(relay, guc_hxg_type_is_action(type)); - if (IS_SRIOV_PF(relay_to_xe(relay))) - ret = xe_gt_sriov_pf_service_process_request(gt, origin, msg, len, response, size); - else + if (IS_SRIOV_PF(relay_to_xe(relay))) { + if (type == GUC_HXG_TYPE_REQUEST) + ret = xe_gt_sriov_pf_service_process_request(gt, origin, msg, len, + response, size); + else + ret = -EOPNOTSUPP; + } else { ret = -EOPNOTSUPP; - + } if (type == GUC_HXG_TYPE_EVENT) relay_assert(relay, ret <= 0); From 5ff2212f0e0779b0b0cbf91fbf970144107c4257 Mon Sep 17 00:00:00 2001 From: Matthew Auld Date: Thu, 25 Jun 2026 16:20:56 +0100 Subject: [PATCH 2079/2758] drm/xe: fix NPD in bo_meminfo() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit b5c55015d4164a0f206bcdcf2985da948b3c7837 ] When a buffer object is purged, its ttm.resource is set to NULL via the TTM pipeline gutting flow. However, the BO remains in the client's object list until userspace explicitly closes the GEM handle. If memory stats are queried during this time, accessing bo->ttm.resource->mem_type will result in a NULL pointer dereference. Fix this by safely skipping purged BOs in bo_meminfo, as they no longer consume any memory. User is getting NPD on device resume, and possible theory is that in bo_move(), if we need to evict something to SYSTEM to save the CCS state, but the BO is marked as dontneed, this won't trigger a move but will nuke the pages, leaving us with a NULL bo resource. And the meminfo() doesn't look ready to handle a NULL resource. v2 (Sashiko): - There could potentially be other cases where we might end up with a NULL resource, so make this a general NULL check for now. Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8419 Fixes: ad9843aac91a ("drm/xe/madvise: Implement purgeable buffer object support") Assisted-by: Copilot:gemini-3.1-pro-preview Reported-by: Matthew Schwartz Signed-off-by: Matthew Auld Cc: Thomas Hellström Cc: Matthew Brost Cc: Arvind Yadav Reviewed-by: Matthew Brost Tested-by: Matthew Schwartz Link: https://patch.msgid.link/20260625152054.450125-6-matthew.auld@intel.com (cherry picked from commit c9a8e7daa0afe3161111e27fd92176e608c7f186) Signed-off-by: Thomas Hellström Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_drm_client.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_drm_client.c b/drivers/gpu/drm/xe/xe_drm_client.c index 84b66147bf494..81020b4b344e0 100644 --- a/drivers/gpu/drm/xe/xe_drm_client.c +++ b/drivers/gpu/drm/xe/xe_drm_client.c @@ -168,10 +168,20 @@ static void bo_meminfo(struct xe_bo *bo, struct drm_memory_stats stats[TTM_NUM_MEM_TYPES]) { u64 sz = xe_bo_size(bo); - u32 mem_type = bo->ttm.resource->mem_type; + u32 mem_type; xe_bo_assert_held(bo); + /* + * The resource can be NULL if the BO has been purged, plus maybe some + * other cases. Either way there shouldn't be any memory to account for, + * or a current resource to account this against, so skip for now. + */ + if (!bo->ttm.resource) + return; + + mem_type = bo->ttm.resource->mem_type; + if (drm_gem_object_is_shared_for_memory_stats(&bo->ttm.base)) stats[mem_type].shared += sz; else From 2b6b3f98d0e93856bee38699b783c71cb0e9d67f Mon Sep 17 00:00:00 2001 From: Matthew Auld Date: Thu, 25 Jun 2026 16:20:58 +0100 Subject: [PATCH 2080/2758] drm/xe/pt: prevent invalid cursor access for purged BOs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 8a0fb57675be578c4db19deb4298ed08a70f0f1a ] During a page table walk for binding, xe_pt_stage_bind() explicitly skips initializing the xe_res_cursor for purged BOs, treating them similarly to NULL VMAs by only setting the cursor size. However, xe_pt_hugepte_possible() and xe_pt_scan_64K() did not check if the BO was purged before attempting to walk the cursor using xe_res_dma() and xe_res_next(). Because the cursor was left uninitialized for purged BOs, this falls through and triggers warnings like: WARNING: drivers/gpu/drm/xe/xe_res_cursor.h:274 at xe_res_next Fix this by explicitly checking if the BO is purged in both xe_pt_hugepte_possible() and xe_pt_scan_64K(), returning early just as we do for NULL VMAs, avoiding the invalid cursor accesses entirely. As a precaution, also zero-initialize the cursor in xe_pt_stage_bind() to ensure we don't pass garbage data into the page table walkers if we ever hit a similar edge case in the future. Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8418 Fixes: ad9843aac91a ("drm/xe/madvise: Implement purgeable buffer object support") Assisted-by: Copilot:gemini-3.1-pro-preview Reported-by: Matthew Schwartz Signed-off-by: Matthew Auld Cc: Thomas Hellström Cc: Matthew Brost Cc: Arvind Yadav Reviewed-by: Matthew Brost Tested-by: Matthew Schwartz Link: https://patch.msgid.link/20260625152054.450125-8-matthew.auld@intel.com (cherry picked from commit 4c7b9c6ece32440e5a435a92076d049450cd2d2e) Signed-off-by: Thomas Hellström Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_pt.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c index 3e438a2799850..4d8f0a8b072e5 100644 --- a/drivers/gpu/drm/xe/xe_pt.c +++ b/drivers/gpu/drm/xe/xe_pt.c @@ -432,6 +432,7 @@ xe_pt_insert_entry(struct xe_pt_stage_bind_walk *xe_walk, struct xe_pt *parent, static bool xe_pt_hugepte_possible(u64 addr, u64 next, unsigned int level, struct xe_pt_stage_bind_walk *xe_walk) { + struct xe_bo *bo = xe_vma_bo(xe_walk->vma); u64 size, dma; if (level > MAX_HUGEPTE_LEVEL) @@ -445,8 +446,8 @@ static bool xe_pt_hugepte_possible(u64 addr, u64 next, unsigned int level, if (next - xe_walk->va_curs_start > xe_walk->curs->size) return false; - /* null VMA's do not have dma addresses */ - if (xe_vma_is_null(xe_walk->vma)) + /* null VMA's and purged BO's do not have dma addresses */ + if (xe_vma_is_null(xe_walk->vma) || (bo && xe_bo_is_purged(bo))) return true; /* if we are clearing page table, no dma addresses*/ @@ -467,6 +468,7 @@ static bool xe_pt_hugepte_possible(u64 addr, u64 next, unsigned int level, static bool xe_pt_scan_64K(u64 addr, u64 next, struct xe_pt_stage_bind_walk *xe_walk) { + struct xe_bo *bo = xe_vma_bo(xe_walk->vma); struct xe_res_cursor curs = *xe_walk->curs; if (!IS_ALIGNED(addr, SZ_64K)) @@ -475,8 +477,8 @@ xe_pt_scan_64K(u64 addr, u64 next, struct xe_pt_stage_bind_walk *xe_walk) if (next > xe_walk->l0_end_addr) return false; - /* null VMA's do not have dma addresses */ - if (xe_vma_is_null(xe_walk->vma)) + /* null VMA's and purged BO's do not have dma addresses */ + if (xe_vma_is_null(xe_walk->vma) || (bo && xe_bo_is_purged(bo))) return true; xe_res_next(&curs, addr - xe_walk->va_curs_start); @@ -707,7 +709,7 @@ xe_pt_stage_bind(struct xe_tile *tile, struct xe_vma *vma, { struct xe_device *xe = tile_to_xe(tile); struct xe_bo *bo = xe_vma_bo(vma); - struct xe_res_cursor curs; + struct xe_res_cursor curs = {}; struct xe_vm *vm = xe_vma_vm(vma); struct xe_pt_stage_bind_walk xe_walk = { .base = { From fa8fd23e3a87ae248738b4c77ae05b5613213b64 Mon Sep 17 00:00:00 2001 From: David Windsor Date: Mon, 29 Jun 2026 20:13:33 -0400 Subject: [PATCH 2081/2758] x86/uprobes: Keep shadow stack in sync for emulated CALLs [ Upstream commit abf08854d224085e2ebb3ba660e7995909f47d6a ] Uprobe CALL emulation updates the normal user stack, but not the CET user shadow stack. The subsequent RET then sees a stale shadow stack entry and raises #CP. Update the relative CALL emulation and XOL CALL fixup paths to keep the shadow stack in sync. Fixes: 488af8ea7131 ("x86/shstk: Wire in shadow stack interface") Signed-off-by: David Windsor Signed-off-by: Peter Zijlstra (Intel) Acked-by: Oleg Nesterov Acked-by: Jiri Olsa Tested-by: Jiri Olsa Link: https://patch.msgid.link/8b5b1c7407b98f31664ad7b6a6faf20d2d4a6cad.1782777969.git.dwindsor@gmail.com Signed-off-by: Sasha Levin --- arch/x86/kernel/uprobes.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c index ebb1baf1eb1d0..d74bb54543b60 100644 --- a/arch/x86/kernel/uprobes.c +++ b/arch/x86/kernel/uprobes.c @@ -1246,9 +1246,15 @@ static int default_post_xol_op(struct arch_uprobe *auprobe, struct pt_regs *regs long correction = utask->vaddr - utask->xol_vaddr; regs->ip += correction; } else if (auprobe->defparam.fixups & UPROBE_FIX_CALL) { + unsigned long retaddr = utask->vaddr + auprobe->defparam.ilen; + int err; + regs->sp += sizeof_long(regs); /* Pop incorrect return address */ - if (emulate_push_stack(regs, utask->vaddr + auprobe->defparam.ilen)) + if (emulate_push_stack(regs, retaddr)) return -ERESTART; + err = shstk_update_last_frame(retaddr); + if (err) + return err; } /* popf; tell the caller to not touch TF */ if (auprobe->defparam.fixups & UPROBE_FIX_SETF) @@ -1338,6 +1344,10 @@ static bool branch_emulate_op(struct arch_uprobe *auprobe, struct pt_regs *regs) */ if (emulate_push_stack(regs, new_ip)) return false; + if (shstk_push(new_ip) == -EFAULT) { + regs->sp += sizeof_long(regs); + return false; + } } else if (!check_jmp_cond(auprobe, regs)) { offs = 0; } From 1acddd3e22dd6912dd5d54f80462405a1f1e6bae Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 1 Jul 2026 13:13:25 +0200 Subject: [PATCH 2082/2758] uprobes/x86: Use proper mm_struct in __in_uprobe_trampoline [ Upstream commit 169328645663bae30e9abad4012d52441e085a71 ] In the unregister path we use __in_uprobe_trampoline check with current->mm for the VMA lookup, which is wrong, because we are in the tracer context, not the traced process. Add mm_struct pointer argument to __in_uprobe_trampoline and changing related callers to pass proper mm_struct pointer. Fixes: ba2bfc97b462 ("uprobes/x86: Add support to optimize uprobes") Reported-by: syzbot+61ce80689253f42e6d80@syzkaller.appspotmail.com Signed-off-by: Jiri Olsa Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Oleg Nesterov Acked-by: Andrii Nakryiko Tested-by: syzbot+61ce80689253f42e6d80@syzkaller.appspotmail.com Link: https://patch.msgid.link/20260701111337.53943-2-jolsa@kernel.org Signed-off-by: Sasha Levin --- arch/x86/kernel/uprobes.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c index d74bb54543b60..3af979fb41d38 100644 --- a/arch/x86/kernel/uprobes.c +++ b/arch/x86/kernel/uprobes.c @@ -761,9 +761,9 @@ void arch_uprobe_clear_state(struct mm_struct *mm) destroy_uprobe_trampoline(tramp); } -static bool __in_uprobe_trampoline(unsigned long ip) +static bool __in_uprobe_trampoline(struct mm_struct *mm, unsigned long ip) { - struct vm_area_struct *vma = vma_lookup(current->mm, ip); + struct vm_area_struct *vma = vma_lookup(mm, ip); return vma && vma_is_special_mapping(vma, &tramp_mapping); } @@ -776,14 +776,14 @@ static bool in_uprobe_trampoline(unsigned long ip) rcu_read_lock(); if (mmap_lock_speculate_try_begin(mm, &seq)) { - found = __in_uprobe_trampoline(ip); + found = __in_uprobe_trampoline(mm, ip); retry = mmap_lock_speculate_retry(mm, seq); } rcu_read_unlock(); if (retry) { mmap_read_lock(mm); - found = __in_uprobe_trampoline(ip); + found = __in_uprobe_trampoline(mm, ip); mmap_read_unlock(mm); } return found; @@ -1044,7 +1044,7 @@ static int copy_from_vaddr(struct mm_struct *mm, unsigned long vaddr, void *dst, return 0; } -static bool __is_optimized(uprobe_opcode_t *insn, unsigned long vaddr) +static bool __is_optimized(struct mm_struct *mm, uprobe_opcode_t *insn, unsigned long vaddr) { struct __packed __arch_relative_insn { u8 op; @@ -1053,7 +1053,7 @@ static bool __is_optimized(uprobe_opcode_t *insn, unsigned long vaddr) if (!is_call_insn(insn)) return false; - return __in_uprobe_trampoline(vaddr + 5 + call->raddr); + return __in_uprobe_trampoline(mm, vaddr + 5 + call->raddr); } static int is_optimized(struct mm_struct *mm, unsigned long vaddr) @@ -1064,7 +1064,7 @@ static int is_optimized(struct mm_struct *mm, unsigned long vaddr) err = copy_from_vaddr(mm, vaddr, &insn, 5); if (err) return err; - return __is_optimized((uprobe_opcode_t *)&insn, vaddr); + return __is_optimized(mm, (uprobe_opcode_t *)&insn, vaddr); } static bool should_optimize(struct arch_uprobe *auprobe) From 3a303f985c6bec8787c2ad4a16b39c14c5bcd765 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 2 Jul 2026 09:25:00 +0100 Subject: [PATCH 2083/2758] cifs: Fix missing credit release on failure in cifs_issue_read() [ Upstream commit c16b8c4cfb4fe2244cc33e469a93c1ab8684146b ] Fix missing release of credits in the failure path in cifs_issue_read() lest retrying the subreq just overwrites the credits value. Fixes: 69c3c023af25 ("cifs: Implement netfslib hooks") Link: https://sashiko.dev/#/patchset/20260608145432.681865-1-dhowells%40redhat.com Signed-off-by: David Howells Acked-by: Paulo Alcantara (Red Hat) cc: linux-cifs@vger.kernel.org cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/client/file.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c index e536e424b9b75..b5a61a4a43b85 100644 --- a/fs/smb/client/file.c +++ b/fs/smb/client/file.c @@ -241,6 +241,7 @@ static void cifs_issue_read(struct netfs_io_subrequest *subreq) return; failed: + add_credits_and_wake_if(rdata->server, &rdata->credits, 0); subreq->error = rc; netfs_read_subreq_terminated(subreq); } From 35cb43b721c06eaea57db54bf0a13f62bed1f3d4 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Fri, 26 Jun 2026 17:58:37 +0900 Subject: [PATCH 2084/2758] ata: sata_gemini: unwind clocks on IDE pinctrl errors [ Upstream commit c0ace4130e813acbabdfaa28d4e94a849c2ffdd7 ] gemini_sata_bridge_init() prepares and enables both SATA PCLKs, then disables them again while keeping the clocks prepared for later bridge start and stop operations. If gemini_setup_ide_pins() fails after that, gemini_sata_probe() returns directly and skips the existing out_unprep_clk unwind path. Route the IDE pinctrl failure through out_unprep_clk so the clocks prepared by gemini_sata_bridge_init() are unprepared before probe fails. Fixes: d872ced29d5f ("ata: sata_gemini: Introduce explicit IDE pin control") Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Reviewed-by: Niklas Cassel Reviewed-by: Linus Walleij Signed-off-by: Damien Le Moal Signed-off-by: Sasha Levin --- drivers/ata/sata_gemini.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/sata_gemini.c b/drivers/ata/sata_gemini.c index 530ee26b30121..56ae2820df588 100644 --- a/drivers/ata/sata_gemini.c +++ b/drivers/ata/sata_gemini.c @@ -353,7 +353,7 @@ static int gemini_sata_probe(struct platform_device *pdev) if (sg->ide_pins) { ret = gemini_setup_ide_pins(dev); if (ret) - return ret; + goto out_unprep_clk; } dev_info(dev, "set up the Gemini IDE/SATA nexus\n"); From 9da2e4275e6432526fda3ce02f315a0eb8c11454 Mon Sep 17 00:00:00 2001 From: Karuna Ramkumar Date: Thu, 2 Jul 2026 02:01:42 +0000 Subject: [PATCH 2085/2758] ata: libata-scsi: limit simulated SCSI command copy to response length [ Upstream commit cd64be0ecd399fa2b1ab60b3aaf2b2b744243467 ] The function ata_scsi_rbuf_fill() is used to copy the response of emulated SCSI commands from ata_scsi_rbuf to the SCSI command's scatterlist. Currently, sg_copy_from_buffer() is called with the size argument set to ATA_SCSI_RBUF_SIZE (2048 bytes). Since ata_scsi_rbuf is zeroed out before the simulation actor is invoked, copying the full buffer size causes the remainder of the SCSI command's transfer buffer (beyond the actual response length 'len') to be overwritten with zeroes. This clobbers any pre-existing sentinel values or data in the caller's buffer tail, even though the correct residual count is reported via scsi_set_resid(). Fix this by passing the actual response length 'len' as the copy size to sg_copy_from_buffer(), ensuring that the tail of the caller's buffer remains untouched. Also, add a defensive check to ensure that the actor does not return a length exceeding the static buffer capacity. If this occurs, trigger a WARN_ON(), fail the command with an aborted command error, and return immediately without copying any data. The fix was tested by invoking an SCSI SG_IO INQUIRY on an ATA disk on vanilla build, and build with the fix. Confirmed that the input buffer's tail end remains unmodified with the fix. Fixes: 5251ae224d8d ("ata: libata-scsi: Return residual for emulated SCSI commands") Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Karuna Ramkumar Signed-off-by: Damien Le Moal Signed-off-by: Sasha Levin --- drivers/ata/libata-scsi.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index d43207c6e4679..a4e0407c7481d 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1931,8 +1931,13 @@ static void ata_scsi_rbuf_fill(struct ata_device *dev, struct scsi_cmnd *cmd, memset(ata_scsi_rbuf, 0, ATA_SCSI_RBUF_SIZE); len = actor(dev, cmd, ata_scsi_rbuf); if (len) { + if (WARN_ON(len > ATA_SCSI_RBUF_SIZE)) { + ata_scsi_set_sense(dev, cmd, ABORTED_COMMAND, 0, 0); + spin_unlock_irqrestore(&ata_scsi_rbuf_lock, flags); + return; + } sg_copy_from_buffer(scsi_sglist(cmd), scsi_sg_count(cmd), - ata_scsi_rbuf, ATA_SCSI_RBUF_SIZE); + ata_scsi_rbuf, len); cmd->result = SAM_STAT_GOOD; if (scsi_bufflen(cmd) > len) scsi_set_resid(cmd, scsi_bufflen(cmd) - len); From be47c047250671c9225c0319ca4695be9b391c59 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 2 Jul 2026 09:23:02 +0100 Subject: [PATCH 2086/2758] netfs: Fix barriering when walking subrequest list [ Upstream commit 5c6ce05e406520290c1d89da97fb3cd70c09137d ] Fix the barriering used when walking the subrequest list in retry as there's a possibility of seeing a subreq that's just been added by the application thread. Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading") Fixes: 288ace2f57c9 ("netfs: New writeback implementation") Link: https://sashiko.dev/#/patchset/20260608145432.681865-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/138807.1782980582@warthog.procyon.org.uk Reviewed-by: Paulo Alcantara (Red Hat) cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/netfs/read_retry.c | 7 ++++++- fs/netfs/write_retry.c | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/fs/netfs/read_retry.c b/fs/netfs/read_retry.c index f59a70f3a086b..2b42758e01ec9 100644 --- a/fs/netfs/read_retry.c +++ b/fs/netfs/read_retry.c @@ -98,7 +98,12 @@ static void netfs_retry_read_subrequests(struct netfs_io_request *rreq) goto abandon; } - list_for_each_continue(next, &stream->subrequests) { + for (;;) { + /* Read pointer to subreq before reading subreq state. */ + next = smp_load_acquire(&next->next); + if (next == &stream->subrequests) + break; + subreq = list_entry(next, struct netfs_io_subrequest, rreq_link); if (subreq->start + subreq->transferred != start + len || test_bit(NETFS_SREQ_BOUNDARY, &subreq->flags) || diff --git a/fs/netfs/write_retry.c b/fs/netfs/write_retry.c index 32735abfa03f0..058bc7a166a59 100644 --- a/fs/netfs/write_retry.c +++ b/fs/netfs/write_retry.c @@ -72,7 +72,12 @@ static void netfs_retry_write_stream(struct netfs_io_request *wreq, !test_bit(NETFS_SREQ_NEED_RETRY, &from->flags)) return; - list_for_each_continue(next, &stream->subrequests) { + for (;;) { + /* Read pointer to subreq before reading subreq state. */ + next = smp_load_acquire(&next->next); + if (next == &stream->subrequests) + break; + subreq = list_entry(next, struct netfs_io_subrequest, rreq_link); if (subreq->start + subreq->transferred != start + len || test_bit(NETFS_SREQ_BOUNDARY, &subreq->flags) || From 9a2e36963a3fc52401586657d34e3f1c5a01ea56 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Thu, 25 Jun 2026 12:45:47 +0800 Subject: [PATCH 2087/2758] ntfs: avoid stale runlist element dereference in MFT writeback [ Upstream commit 81fe702ff1760da32bcd3ef4494b2a33dbeced72 ] ntfs_write_mft_block() maps each $MFT record through the $MFT data runlist. For sub-folio clusters it looks up a struct runlist_element under ni->runlist.lock, drops the lock, and later uses rl->length and rl->vcn when choosing folio_sz. That pointer is only borrowed from ni->runlist.rl. Concurrent $MFT allocation extension can merge a replacement runlist under the same lock, and ntfs_rl_realloc() can free the old backing array. If that happens between the lookup and the later folio_sz decision, writeback can dereference freed runlist storage. The buggy scenario involves two paths, with each column showing the order within that path: MFT writeback path: $MFT allocation extension: 1. Look up rl under 1. Extend the $MFT data allocation. ni->runlist.lock. 2. Publish a replacement runlist. 2. Drop ni->runlist.lock. 3. Free the old runlist array. 3. Read rl->length and rl->vcn to choose folio_sz. Compute the remaining run length while ni->runlist.lock is still held, and use that scalar after unlock. This preserves the existing folio sizing decision without carrying a borrowed runlist_element across the lock boundary. Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in ntfs_mft_writepages+0x1c8d/0x1fb0 Call Trace: dump_stack_lvl+0x66/0xa0 print_report+0xce/0x630 ? ntfs_mft_writepages+0x1c8d/0x1fb0 ? srso_alias_return_thunk+0x5/0xfbef5 ? __virt_addr_valid+0x20d/0x410 ? ntfs_mft_writepages+0x1c8d/0x1fb0 kasan_report+0xe0/0x110 ? ntfs_mft_writepages+0x1c8d/0x1fb0 ntfs_mft_writepages+0x1c8d/0x1fb0 ? __pfx_ntfs_mft_writepages+0x10/0x10 ? __pfx___mutex_unlock_slowpath+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? iput+0x92/0xa80 do_writepages+0x219/0x530 ? __pfx_do_writepages+0x10/0x10 __writeback_single_inode+0x117/0xf50 ? do_raw_spin_lock+0x130/0x270 ? __pfx_do_raw_spin_lock+0x10/0x10 ? __pfx___writeback_single_inode+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 writeback_sb_inodes+0x65b/0x1810 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_acquire+0x2b8/0x2f0 ? __pfx_writeback_sb_inodes+0x10/0x10 ? lock_release+0x1e0/0x280 ? _raw_spin_unlock+0x23/0x40 ? move_expired_inodes+0x2b8/0x850 __writeback_inodes_wb+0xf4/0x270 ? __pfx___writeback_inodes_wb+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? queue_io+0x2e4/0x410 wb_writeback+0x666/0x880 ? srso_alias_return_thunk+0x5/0xfbef5 ? __pfx_wb_writeback+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? srso_alias_return_thunk+0x5/0xfbef5 ? get_nr_dirty_inodes+0x1c/0x170 wb_workfn+0x75e/0xbb0 ? srso_alias_return_thunk+0x5/0xfbef5 ? _raw_spin_unlock_irqrestore+0x27/0x60 ? __pfx_wb_workfn+0x10/0x10 ? __pfx_debug_object_deactivate+0x10/0x10 ? lock_acquire+0x2b8/0x2f0 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_release+0x1e0/0x280 process_one_work+0x8d0/0x1870 ? __pfx_process_one_work+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 worker_thread+0x575/0xf80 ? __pfx_worker_thread+0x10/0x10 kthread+0x2e7/0x3c0 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x576/0x810 ? __pfx_ret_from_fork+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __switch_to+0x57e/0xe10 ? __switch_to_asm+0x33/0x70 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1a/0x30 Allocated by task 970: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0xaa/0xb0 __kvmalloc_node_noprof+0x353/0x920 ntfs_rl_realloc+0x3c/0x80 ntfs_runlists_merge+0x1212/0x3010 ntfs_mft_data_extend_allocation_nolock+0x3e0/0x1f40 ntfs_mft_record_alloc+0x1ab4/0x4f10 __ntfs_create+0x680/0x2e50 ntfs_create+0x1e6/0x3a0 path_openat+0x2b55/0x3c10 do_file_open+0x1f4/0x460 do_sys_openat2+0xde/0x170 __x64_sys_openat+0x122/0x1e0 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 1294: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 kfree+0x307/0x580 ntfs_rl_realloc+0x66/0x80 ntfs_runlists_merge+0x1212/0x3010 ntfs_mft_data_extend_allocation_nolock+0x3e0/0x1f40 ntfs_mft_record_alloc+0x1ab4/0x4f10 __ntfs_create+0x680/0x2e50 ntfs_create+0x1e6/0x3a0 path_openat+0x2b55/0x3c10 do_file_open+0x1f4/0x460 do_sys_openat2+0xde/0x170 __x64_sys_openat+0x122/0x1e0 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Fixes: 115380f9a2f9 ("ntfs: update mft operations") Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Sasha Levin --- fs/ntfs/mft.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/ntfs/mft.c b/fs/ntfs/mft.c index a7d10ee41b344..464f8f706cbc8 100644 --- a/fs/ntfs/mft.c +++ b/fs/ntfs/mft.c @@ -2745,7 +2745,6 @@ static int ntfs_write_mft_block(struct folio *folio, struct writeback_control *w s64 vcn = ntfs_pidx_to_cluster(vol, folio->index); s64 end_vcn = ntfs_bytes_to_cluster(vol, ni->allocated_size); unsigned int folio_sz; - struct runlist_element *rl = NULL; loff_t i_size = i_size_read(vi); ntfs_debug("Entering for inode 0x%llx, attribute type 0x%x, folio index 0x%lx.", @@ -2790,6 +2789,7 @@ static int ntfs_write_mft_block(struct folio *folio, struct writeback_control *w &tni, &ref_inos[nr_ref_inos])) { unsigned int mft_record_off = 0; s64 vcn_off = vcn; + s64 rl_len = 0; /* * Skip $MFT extent mft records and let them being written @@ -2822,8 +2822,12 @@ static int ntfs_write_mft_block(struct folio *folio, struct writeback_control *w } if (vol->cluster_size < folio_size(folio)) { + struct runlist_element *rl; + down_write(&ni->runlist.lock); rl = ntfs_attr_vcn_to_rl(ni, vcn_off, &lcn); + if (!IS_ERR(rl)) + rl_len = rl->length - (vcn_off - rl->vcn); up_write(&ni->runlist.lock); if (IS_ERR(rl) || lcn < 0) { err = -EIO; @@ -2854,7 +2858,7 @@ static int ntfs_write_mft_block(struct folio *folio, struct writeback_control *w if (vol->cluster_size == NTFS_BLOCK_SIZE && (mft_record_off || - (rl && rl->length - (vcn_off - rl->vcn) == 1) || + rl_len == 1 || mft_ofs + NTFS_BLOCK_SIZE >= PAGE_SIZE)) folio_sz = NTFS_BLOCK_SIZE; else From ef649703dce0df1364fcec3cdad9b32d1c522939 Mon Sep 17 00:00:00 2001 From: Georgiy Osokin Date: Sun, 17 May 2026 15:06:39 +0300 Subject: [PATCH 2088/2758] HID: picolcd: prevent NULL pointer dereference in picolcd_send_and_wait() [ Upstream commit 0021eb09041f021c079be1022934a280f7f176c0 ] In picolcd_send_and_wait(), an integer overflow of the signed loop counter 'k' can theoretically lead to a NULL pointer dereference of 'raw_data'. If the loop executes more than INT_MAX times, 'k' becomes negative, making the condition 'k < size' true even when 'size' is 0. Change the type of 'k' to 'unsigned int' to prevent the overflow and eliminate the out-of-bounds access. Found by Linux Verification Center (linuxtesting.org) with the Svace static analysis tool. [jkosina@suse.com: extended hash length] Fixes: fabdbf2fd22fa17 ("HID: picoLCD: split driver code") Signed-off-by: Georgiy Osokin Signed-off-by: Jiri Kosina Signed-off-by: Sasha Levin --- drivers/hid/hid-picolcd_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-picolcd_core.c b/drivers/hid/hid-picolcd_core.c index 2cc01e1bc1a84..d73e97c8b853e 100644 --- a/drivers/hid/hid-picolcd_core.c +++ b/drivers/hid/hid-picolcd_core.c @@ -72,7 +72,8 @@ struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev, struct picolcd_pending *work; struct hid_report *report = picolcd_out_report(report_id, hdev); unsigned long flags; - int i, j, k; + int i, j; + unsigned int k; if (!report || !data) return NULL; From f7e8117e42b20c30d2a5edab82c944a5e381d791 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 16 Jun 2026 11:26:56 +0000 Subject: [PATCH 2089/2758] HID: core: Fix OOB read in hid_get_report for numbered reports [ Upstream commit af1a9b65ebe8a948eda805c14b78d4d0767cb1b5 ] When a caller passes a size of 0 to hid_report_raw_event() for a numbered report, the function originally called hid_get_report() before performing any size validation. Inside hid_get_report(), if the report is numbered (report_enum->numbered is true), it unconditionally dereferences data[0] to extract the report ID. With a size of 0, this results in an out-of-bounds read or kernel panic. Fix this by moving the numbered report size validation check before the call to hid_get_report(), ensuring that size is at least 1 before dereferencing the data pointer. Fixes: 2c85c61d1332 ("HID: pass the buffer size to hid_report_raw_event") Signed-off-by: Lee Jones Signed-off-by: Jiri Kosina Signed-off-by: Sasha Levin --- drivers/hid/hid-core.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 41a79e43c82b3..cf123347a2af7 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2045,6 +2045,13 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 * u8 *cdata = data; int ret = 0; + if (report_enum->numbered && (size < 1 || bufsize < 1)) { + hid_warn_ratelimited(hid, + "Event data for numbered report is too short (%d vs %zu)\n", + size, bufsize); + return -EINVAL; + } + report = hid_get_report(report_enum, data); if (!report) return 0; From 67a863ceb348923936f4819dcbd62dc0f604a766 Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Wed, 10 Jun 2026 11:37:16 +0100 Subject: [PATCH 2090/2758] arm64: Avoid eager DVMSync reclaim batches with C1-Pro SME erratum [ Upstream commit 534eb6940a89ff7ca3f2ab6582f3548ca97674c3 ] The C1-Pro SME DVMSync workaround currently samples mm_cpumask() from arch_tlbbatch_add_pending(). It requires a DSB after every batched TLBI so that the mask read is ordered after the hardware DVMSync, defeating much of the reclaim batching benefit. Introduce the sme_active_cpus mask tracking which CPUs run in user-space with SME enabled and use it for batch flushing instead of accumulating the mm_cpumask() of the unmapped pages. Fixes: 0baba94a9779 ("arm64: errata: Work around early CME DVMSync acknowledgement") Signed-off-by: Catalin Marinas Cc: Will Deacon Tested-by: Joshua Liu Signed-off-by: Will Deacon Signed-off-by: Sasha Levin --- arch/arm64/include/asm/tlbbatch.h | 10 ++----- arch/arm64/include/asm/tlbflush.h | 49 +++++-------------------------- arch/arm64/kernel/fpsimd.c | 10 +++++-- arch/arm64/kernel/process.c | 35 ---------------------- 4 files changed, 17 insertions(+), 87 deletions(-) diff --git a/arch/arm64/include/asm/tlbbatch.h b/arch/arm64/include/asm/tlbbatch.h index 6297631532e59..767f35ea62b39 100644 --- a/arch/arm64/include/asm/tlbbatch.h +++ b/arch/arm64/include/asm/tlbbatch.h @@ -2,17 +2,11 @@ #ifndef _ARCH_ARM64_TLBBATCH_H #define _ARCH_ARM64_TLBBATCH_H -#include - struct arch_tlbflush_unmap_batch { -#ifdef CONFIG_ARM64_ERRATUM_4193714 /* - * Track CPUs that need SME DVMSync on completion of this batch. - * Otherwise, the arm64 HW can do tlb shootdown, so we don't need to - * record cpumask for sending IPI + * For arm64, HW can do TLB shootdown, so we don't need to record a + * cpumask for sending IPIs. */ - cpumask_var_t cpumask; -#endif }; #endif /* _ARCH_ARM64_TLBBATCH_H */ diff --git a/arch/arm64/include/asm/tlbflush.h b/arch/arm64/include/asm/tlbflush.h index c0bf5b3980411..57b4eda6a72bb 100644 --- a/arch/arm64/include/asm/tlbflush.h +++ b/arch/arm64/include/asm/tlbflush.h @@ -82,6 +82,8 @@ static inline unsigned long get_trans_granule(void) #ifdef CONFIG_ARM64_ERRATUM_4193714 +extern cpumask_t sme_active_cpus; + void sme_do_dvmsync(const struct cpumask *mask); static inline void sme_dvmsync(struct mm_struct *mm) @@ -92,42 +94,12 @@ static inline void sme_dvmsync(struct mm_struct *mm) sme_do_dvmsync(mm_cpumask(mm)); } -static inline void sme_dvmsync_add_pending(struct arch_tlbflush_unmap_batch *batch, - struct mm_struct *mm) +static inline void sme_dvmsync_batch(void) { if (!alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714)) return; - /* - * Order the mm_cpumask() read after the hardware DVMSync. - */ - dsb(ish); - if (cpumask_empty(mm_cpumask(mm))) - return; - - /* - * Allocate the batch cpumask on first use. Fall back to an immediate - * IPI for this mm in case of failure. - */ - if (!cpumask_available(batch->cpumask) && - !zalloc_cpumask_var(&batch->cpumask, GFP_ATOMIC)) { - sme_do_dvmsync(mm_cpumask(mm)); - return; - } - - cpumask_or(batch->cpumask, batch->cpumask, mm_cpumask(mm)); -} - -static inline void sme_dvmsync_batch(struct arch_tlbflush_unmap_batch *batch) -{ - if (!alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714)) - return; - - if (!cpumask_available(batch->cpumask)) - return; - - sme_do_dvmsync(batch->cpumask); - cpumask_clear(batch->cpumask); + sme_do_dvmsync(&sme_active_cpus); } #else @@ -135,11 +107,7 @@ static inline void sme_dvmsync_batch(struct arch_tlbflush_unmap_batch *batch) static inline void sme_dvmsync(struct mm_struct *mm) { } -static inline void sme_dvmsync_add_pending(struct arch_tlbflush_unmap_batch *batch, - struct mm_struct *mm) -{ -} -static inline void sme_dvmsync_batch(struct arch_tlbflush_unmap_batch *batch) +static inline void sme_dvmsync_batch(void) { } @@ -285,11 +253,11 @@ static inline void __tlbi_sync_s1ish(struct mm_struct *mm) sme_dvmsync(mm); } -static inline void __tlbi_sync_s1ish_batch(struct arch_tlbflush_unmap_batch *batch) +static inline void __tlbi_sync_s1ish_batch(void) { dsb(ish); __repeat_tlbi_sync(vale1is, 0); - sme_dvmsync_batch(batch); + sme_dvmsync_batch(); } static inline void __tlbi_sync_s1ish_kernel(void) @@ -434,7 +402,7 @@ static inline bool arch_tlbbatch_should_defer(struct mm_struct *mm) */ static inline void arch_tlbbatch_flush(struct arch_tlbflush_unmap_batch *batch) { - __tlbi_sync_s1ish_batch(batch); + __tlbi_sync_s1ish_batch(); } /* @@ -722,7 +690,6 @@ static inline void arch_tlbbatch_add_pending(struct arch_tlbflush_unmap_batch *b __flush_tlb_range(&vma, start, end, PAGE_SIZE, 3, TLBF_NOWALKCACHE | TLBF_NOSYNC); - sme_dvmsync_add_pending(batch, mm); } static inline bool __pte_flags_need_flush(ptdesc_t oldval, ptdesc_t newval) diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c index 60a45d600b460..ab3b63621fd00 100644 --- a/arch/arm64/kernel/fpsimd.c +++ b/arch/arm64/kernel/fpsimd.c @@ -1366,6 +1366,7 @@ void do_sve_acc(unsigned long esr, struct pt_regs *regs) * SME/CME erratum handling. */ static cpumask_t sme_dvmsync_cpus; +cpumask_t sme_active_cpus; /* * These helpers are only called from non-preemptible contexts, so @@ -1379,13 +1380,15 @@ void sme_set_active(void) return; cpumask_set_cpu(cpu, mm_cpumask(current->mm)); + cpumask_set_cpu(cpu, &sme_active_cpus); /* * A subsequent (post ERET) SME access may use a stale address * translation. On C1-Pro, a TLBI+DSB on a different CPU will wait for - * the completion of cpumask_set_cpu() above as it appears in program - * order before the SME access. The post-TLBI+DSB read of mm_cpumask() - * will lead to the IPI being issued. + * the completion of the cpumask_set_cpu() operations above as they + * appear in program order before the SME access. The post-TLBI+DSB + * read of mm_cpumask() or sme_active_cpus will lead to the IPI being + * issued. * * https://lore.kernel.org/r/ablEXwhfKyJW1i7l@J2N7QTR9R3 */ @@ -1403,6 +1406,7 @@ void sme_clear_active(void) * completed on entering EL1. */ cpumask_clear_cpu(cpu, mm_cpumask(current->mm)); + cpumask_clear_cpu(cpu, &sme_active_cpus); } static void sme_dvmsync_ipi(void *unused) diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index 033643cd4e5ed..581f80e9b9b73 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -341,41 +341,8 @@ void flush_thread(void) flush_gcs(); } -#ifdef CONFIG_ARM64_ERRATUM_4193714 - -static void arch_dup_tlbbatch_mask(struct task_struct *dst) -{ - /* - * Clear the inherited cpumask with memset() to cover both cases where - * cpumask_var_t is a pointer or an array. It will be allocated lazily - * in sme_dvmsync_add_pending() if CPUMASK_OFFSTACK=y. - */ - if (alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714)) - memset(&dst->tlb_ubc.arch.cpumask, 0, - sizeof(dst->tlb_ubc.arch.cpumask)); -} - -static void arch_release_tlbbatch_mask(struct task_struct *tsk) -{ - if (alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714)) - free_cpumask_var(tsk->tlb_ubc.arch.cpumask); -} - -#else - -static void arch_dup_tlbbatch_mask(struct task_struct *dst) -{ -} - -static void arch_release_tlbbatch_mask(struct task_struct *tsk) -{ -} - -#endif /* CONFIG_ARM64_ERRATUM_4193714 */ - void arch_release_task_struct(struct task_struct *tsk) { - arch_release_tlbbatch_mask(tsk); fpsimd_release_task(tsk); } @@ -391,8 +358,6 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) *dst = *src; - arch_dup_tlbbatch_mask(dst); - /* * Drop stale reference to src's sve_state and convert dst to * non-streaming FPSIMD mode. From 9a1479b05bd9ee1c888f0f349ae6aabc9c0f3f70 Mon Sep 17 00:00:00 2001 From: Anshuman Khandual Date: Fri, 26 Jun 2026 02:28:45 +0100 Subject: [PATCH 2091/2758] arm64/mm: Optimize TLB flush in unmap_hotplug_[pmd|pud]_range() [ Upstream commit ff4c5a0de1f2ef7737a8688a86e19301e567020d ] Commit 48478b9f7913 ("arm64/mm: Enable batched TLB flush in unmap_hotplug_range") inadvertently introduced redundant TLB invalidation when clearing a block entry, resulting in unnecessary broadcast invalidation on CPUs without support for range-based invalidation. Re-introduce the old behaviour, along with some expanded comments to help people working in this area next time around. Cc: Catalin Marinas Cc: Will Deacon Cc: Ryan Roberts Cc: David Hildenbrand Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Reported-by: Ben Hutchings Closes: https://lore.kernel.org/all/b0d5836032ce3135bfc473f6bff791306d086925.camel@decadent.org.uk/ Fixes: 48478b9f7913 ("arm64/mm: Enable batched TLB flush in unmap_hotplug_range()") Signed-off-by: Anshuman Khandual Reviewed-by: David Hildenbrand (Arm) Reviewed-by: Catalin Marinas [will: Reword comments and commit message] Signed-off-by: Will Deacon Signed-off-by: Sasha Levin --- arch/arm64/mm/mmu.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index 8242f93f05e4f..fd756390fe925 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -1509,7 +1509,13 @@ static void unmap_hotplug_pmd_range(pud_t *pudp, unsigned long addr, if (free_mapped) { /* CONT blocks are not supported in the vmemmap */ WARN_ON(pmd_cont(pmd)); - flush_tlb_kernel_range(addr, addr + PMD_SIZE); + /* + * Invalidating a block entry requires just + * a single overlapping TLB invalidation, + * so limit the range of the flush to a single + * page. + */ + flush_tlb_kernel_range(addr, addr + PAGE_SIZE); free_hotplug_page_range(pmd_page(pmd), PMD_SIZE, altmap); } @@ -1539,7 +1545,8 @@ static void unmap_hotplug_pud_range(p4d_t *p4dp, unsigned long addr, if (pud_leaf(pud)) { pud_clear(pudp); if (free_mapped) { - flush_tlb_kernel_range(addr, addr + PUD_SIZE); + /* See comment in unmap_hotplug_pmd_range(). */ + flush_tlb_kernel_range(addr, addr + PAGE_SIZE); free_hotplug_page_range(pud_page(pud), PUD_SIZE, altmap); } From 6706e332151b7f3eba474691088c47af2a820089 Mon Sep 17 00:00:00 2001 From: Ariana Lazar Date: Thu, 4 Jun 2026 16:46:47 +0300 Subject: [PATCH 2092/2758] iio: dac: mcp47feb02: Fix passing uninitialized vref1_uV for no Vref1 case [ Upstream commit e74c0d0eef7e1fa9fd387b81b2787b4581e0b11c ] Ensure that if a device has Vref1 but reading the regulator returns an error, mcp47feb02_init_ctrl_regs() is not called with an uninitialized vref1_uV value. Also add a device_property_present() check for the Vref1 supply before reading the regulator. Fixes: dd154646d292 ("iio: dac: mcp47feb02: Fix Vref validation [1-999] case") Reported-by: Dan Carpenter Closes: https://lore.kernel.org/all/adiPnla0M5EzvgD-@stanley.mountain/ Signed-off-by: Ariana Lazar Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron Signed-off-by: Sasha Levin --- drivers/iio/dac/mcp47feb02.c | 37 +++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/drivers/iio/dac/mcp47feb02.c b/drivers/iio/dac/mcp47feb02.c index faccb804a5ed5..5f84b46ec6f3d 100644 --- a/drivers/iio/dac/mcp47feb02.c +++ b/drivers/iio/dac/mcp47feb02.c @@ -1136,26 +1136,33 @@ static int mcp47feb02_probe(struct i2c_client *client) vdd_uV = ret; - ret = devm_regulator_get_enable_read_voltage(dev, "vref"); - if (ret > 0) { - vref_uV = ret; + if (device_property_present(dev, "vref-supply")) { + vref_uV = devm_regulator_get_enable_read_voltage(dev, "vref"); + if (vref_uV < 0) + return vref_uV; + + if (vref_uV == 0) + return dev_err_probe(dev, -EINVAL, "Vref is 0 uV.\n"); + data->use_vref = true; } else { vref_uV = 0; - dev_dbg(dev, "using internal band gap as voltage reference.\n"); - dev_dbg(dev, "Vref is unavailable.\n"); + dev_dbg(dev, "Using internal band gap as voltage reference.\n"); } - if (chip_features->have_ext_vref1) { - ret = devm_regulator_get_enable_read_voltage(dev, "vref1"); - if (ret > 0) { - vref1_uV = ret; - data->use_vref1 = true; - } else { - vref1_uV = 0; - dev_dbg(dev, "using internal band gap as voltage reference 1.\n"); - dev_dbg(dev, "Vref1 is unavailable.\n"); - } + if (chip_features->have_ext_vref1 && + device_property_present(dev, "vref1-supply")) { + vref1_uV = devm_regulator_get_enable_read_voltage(dev, "vref1"); + if (vref1_uV < 0) + return vref1_uV; + + if (vref1_uV == 0) + return dev_err_probe(dev, -EINVAL, "Vref1 is 0 uV.\n"); + + data->use_vref1 = true; + } else { + vref1_uV = 0; + dev_dbg(dev, "Using internal band gap as voltage reference 1.\n"); } ret = mcp47feb02_init_ctrl_regs(data); From 3dd3e43f17cda174009a51fe668046ed7afef46a Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sat, 27 Jun 2026 12:27:58 +0800 Subject: [PATCH 2093/2758] ntfs: avoid stale runlist element dereference in fallocate [ Upstream commit 88496c4ac5a6ade75619f4b1015706a8b924d50a ] ntfs_attr_fallocate() allocates holes and delayed allocations inside initialized size by looking up the current runlist element under ni->runlist.lock. The returned struct runlist_element is only a borrowed pointer into ni->runlist.rl. A writer can replace and free that array after the read lock is dropped, so later reads of rl->lcn, rl->length and rl->vcn can touch freed memory. The buggy scenario involves two paths, with each column showing the order within that path: ntfs_attr_fallocate(): 1. Take ni->runlist.lock for read. 2. Get rl from ntfs_attr_find_vcn_nolock(). 3. Drop ni->runlist.lock. 4. Read rl->lcn, rl->length and rl->vcn. mmap page_mkwrite: 1. Enter ntfs_filemap_page_mkwrite(). 2. Reach __ntfs_write_iomap_begin() and ntfs_attr_map_cluster(). 3. Merge allocation state with ntfs_runlists_merge(). 4. Reallocate ni->runlist.rl in ntfs_rl_realloc(), freeing the old array. Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in ntfs_attr_fallocate+0xbb8/0xd00 Call Trace: dump_stack_lvl+0x66/0xa0 print_report+0xce/0x630 ? ntfs_attr_fallocate+0xbb8/0xd00 ? srso_alias_return_thunk+0x5/0xfbef5 ? __virt_addr_valid+0x20d/0x410 ? ntfs_attr_fallocate+0xbb8/0xd00 kasan_report+0xe0/0x110 ? ntfs_attr_fallocate+0xbb8/0xd00 ntfs_attr_fallocate+0xbb8/0xd00 ? lock_acquire+0x2b8/0x2f0 ? __pfx_ntfs_attr_fallocate+0x10/0x10 ? 0xffffffffc0000095 ? down_write+0x10d/0x1e0 ntfs_fallocate+0x5c9/0x1d00 ? __pfx_ntfs_fallocate+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_acquire+0x2b8/0x2f0 ? srso_alias_return_thunk+0x5/0xfbef5 ? selinux_file_permission+0x3a7/0x510 vfs_fallocate+0x29d/0xd30 __x64_sys_fallocate+0xc7/0x150 ? do_syscall_64+0x81/0x6a0 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Allocated by task 410: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0xaa/0xb0 __kvmalloc_node_noprof+0x353/0x920 ntfs_rl_realloc+0x3f/0x110 ntfs_runlists_merge+0xaa3/0x3010 ntfs_attr_map_cluster+0x4e5/0xf80 ntfs_attr_fallocate+0x53f/0xd00 ntfs_fallocate+0x5c9/0x1d00 vfs_fallocate+0x29d/0xd30 __x64_sys_fallocate+0xc7/0x150 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 424: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 kfree+0x307/0x580 ntfs_rl_realloc+0x6f/0x110 ntfs_runlists_merge+0x7b1/0x3010 ntfs_attr_map_cluster+0x4e5/0xf80 __ntfs_write_iomap_begin+0x8cd/0x2280 iomap_iter+0x6de/0x11e0 iomap_page_mkwrite+0x391/0x650 ntfs_filemap_page_mkwrite+0x1ac/0x400 do_page_mkwrite+0x15c/0x280 __handle_mm_fault+0xd6d/0x1ca0 handle_mm_fault+0x19c/0x470 do_user_addr_fault+0x23b/0x9c0 exc_page_fault+0x5c/0xc0 asm_exc_page_fault+0x26/0x30 Fix this by copying the needed runlist fields while the read lock is still held and using only those scalar snapshots after unlocking. After the snapshot, ntfs_attr_map_cluster() can also find that the range is already mapped and return balloc=false. Only call ntfs_dio_zero_range() when new clusters were allocated, matching the write iomap path and preserving the zero-newly-allocated-holes behavior. Fixes: 495e90fa3348 ("ntfs: update attrib operations") Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Sasha Levin --- fs/ntfs/attrib.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index e425c8d074c58..e8dc4b07bd975 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -5435,6 +5435,7 @@ int ntfs_attr_fallocate(struct ntfs_inode *ni, loff_t start, loff_t byte_len, bo s64 old_data_size; s64 vcn_start, vcn_end, vcn_uninit, vcn, try_alloc_cnt; s64 lcn, alloc_cnt; + s64 rl_lcn, rl_length, rl_vcn; int err = 0; struct runlist_element *rl; bool balloc; @@ -5514,19 +5515,23 @@ int ntfs_attr_fallocate(struct ntfs_inode *ni, loff_t start, loff_t byte_len, bo while (vcn < vcn_uninit) { down_read(&ni->runlist.lock); rl = ntfs_attr_find_vcn_nolock(ni, vcn, NULL); - up_read(&ni->runlist.lock); if (IS_ERR(rl)) { + up_read(&ni->runlist.lock); err = PTR_ERR(rl); goto out; } + rl_lcn = rl->lcn; + rl_length = rl->length; + rl_vcn = rl->vcn; + up_read(&ni->runlist.lock); - if (rl->lcn > 0) { - vcn += rl->length - (vcn - rl->vcn); - } else if (rl->lcn == LCN_DELALLOC || rl->lcn == LCN_HOLE) { - try_alloc_cnt = min(rl->length - (vcn - rl->vcn), + if (rl_lcn > 0) { + vcn += rl_length - (vcn - rl_vcn); + } else if (rl_lcn == LCN_DELALLOC || rl_lcn == LCN_HOLE) { + try_alloc_cnt = min(rl_length - (vcn - rl_vcn), vcn_uninit - vcn); - if (rl->lcn == LCN_DELALLOC) { + if (rl_lcn == LCN_DELALLOC) { vcn += try_alloc_cnt; continue; } @@ -5541,11 +5546,14 @@ int ntfs_attr_fallocate(struct ntfs_inode *ni, loff_t start, loff_t byte_len, bo if (err) goto out; - err = ntfs_dio_zero_range(VFS_I(ni), - lcn << vol->cluster_size_bits, - alloc_cnt << vol->cluster_size_bits); - if (err > 0) - goto out; + if (balloc) { + err = ntfs_dio_zero_range(VFS_I(ni), + lcn << vol->cluster_size_bits, + alloc_cnt << + vol->cluster_size_bits); + if (err > 0) + goto out; + } if (signal_pending(current)) goto out; From f81bc5a709dcbaf2a3bbef4ca7167f93900cc39f Mon Sep 17 00:00:00 2001 From: Yiyang Chen Date: Tue, 23 Jun 2026 06:23:13 +0000 Subject: [PATCH 2094/2758] HID: bpf: Fix hid_bpf_get_data() range check [ Upstream commit 2d044049421dd48212b28646a850749d4a2d57fa ] hid_bpf_get_data() returns a pointer into the HID-BPF context data when the caller-provided offset and size fit inside ctx->allocated_size. The current check adds rdwr_buf_size and offset before comparing the result against ctx->allocated_size. Since both values are unsigned, a very large size can wrap the sum below ctx->allocated_size and make the helper return a pointer even though the requested range is not contained in the backing buffer. Use check_add_overflow() to reject wrapped range ends before comparing the requested range end against ctx->allocated_size. Fixes: 658ee5a64fcf ("HID: bpf: allocate data memory for device_event BPF programs") Signed-off-by: Yiyang Chen Signed-off-by: Benjamin Tissoires Signed-off-by: Sasha Levin --- drivers/hid/bpf/hid_bpf_dispatch.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c index d0130658091b0..536f6d01fd14c 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.c +++ b/drivers/hid/bpf/hid_bpf_dispatch.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "hid_bpf_dispatch.h" const struct hid_ops *hid_ops; @@ -296,10 +297,12 @@ __bpf_kfunc __u8 * hid_bpf_get_data(struct hid_bpf_ctx *ctx, unsigned int offset, const size_t rdwr_buf_size) { struct hid_bpf_ctx_kern *ctx_kern; + size_t end; ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx); - if (rdwr_buf_size + offset > ctx->allocated_size) + if (check_add_overflow(rdwr_buf_size, offset, &end) || + end > ctx->allocated_size) return NULL; return ctx_kern->data + offset; From b56f874e49e64f09ceb51e9473432d8c9e977042 Mon Sep 17 00:00:00 2001 From: Yiyang Chen Date: Tue, 23 Jun 2026 06:23:14 +0000 Subject: [PATCH 2095/2758] selftests/hid: Load only requested struct_ops maps [ Upstream commit 5aad55011a37d999cf99d14bafb6a093a1a70466 ] The HID selftest skeleton contains several struct_ops maps, but each test usually wants to load only the programs named by that test. load_programs() disabled auto-attach for all maps, but left struct_ops autocreate enabled. libbpf can enable autoload for programs referenced by autocreated struct_ops maps, so an unrelated program can be loaded and fail even when the current test does not use it. Disable autocreate for all struct_ops maps by default, then re-enable it only for the maps selected by the test before loading the skeleton. Signed-off-by: Yiyang Chen Fixes: f64c1a459339 ("selftests/hid: disable struct_ops auto-attach") Signed-off-by: Benjamin Tissoires Signed-off-by: Sasha Levin --- tools/testing/selftests/hid/hid_bpf.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c index 1e979fb3542ba..269256e1decd8 100644 --- a/tools/testing/selftests/hid/hid_bpf.c +++ b/tools/testing/selftests/hid/hid_bpf.c @@ -86,6 +86,20 @@ static void load_programs(const struct test_program programs[], self->skel = hid__open(); ASSERT_OK_PTR(self->skel) TEARDOWN_LOG("Error while calling hid__open"); + /* + * Disable all struct_ops maps by default so libbpf does not autoload + * programs referenced by maps that are unrelated to the current test. + */ + bpf_object__for_each_map(iter_map, *self->skel->skeleton->obj) { + if (bpf_map__type(iter_map) == BPF_MAP_TYPE_STRUCT_OPS) { + err = bpf_map__set_autocreate(iter_map, false); + ASSERT_OK(err) TH_LOG("can not disable struct_ops map '%s'", + bpf_map__name(iter_map)); + } + + bpf_map__set_autoattach(iter_map, false); + } + for (int i = 0; i < progs_count; i++) { struct bpf_program *prog; struct bpf_map *map; @@ -102,6 +116,10 @@ static void load_programs(const struct test_program programs[], ASSERT_OK_PTR(map) TH_LOG("can not find struct_ops by name '%s'", programs[i].name + 4); + err = bpf_map__set_autocreate(map, true); + ASSERT_OK(err) TH_LOG("can not enable struct_ops map '%s'", + programs[i].name + 4); + /* hid_id is the first field of struct hid_bpf_ops */ ops_hid_id = bpf_map__initial_value(map, NULL); ASSERT_OK_PTR(ops_hid_id) TH_LOG("unable to retrieve struct_ops data"); @@ -109,13 +127,6 @@ static void load_programs(const struct test_program programs[], *ops_hid_id = self->hid.hid_id; } - /* we disable the auto-attach feature of all maps because we - * only want the tested one to be manually attached in the next - * call to bpf_map__attach_struct_ops() - */ - bpf_object__for_each_map(iter_map, *self->skel->skeleton->obj) - bpf_map__set_autoattach(iter_map, false); - err = hid__load(self->skel); ASSERT_OK(err) TH_LOG("hid_skel_load failed: %d", err); From 79b33d9f1d9c1793daadfb9ba281b669f81b73de Mon Sep 17 00:00:00 2001 From: Yiyang Chen Date: Tue, 23 Jun 2026 06:23:15 +0000 Subject: [PATCH 2096/2758] selftests/hid: Cover hid_bpf_get_data() size overflow [ Upstream commit eebbef7c468a5cb58c4772849ee5066441166cf0 ] Add a HID-BPF regression check for hid_bpf_get_data() requests whose size would overflow when added to the offset. The new rdesc fixup callback asks for offset 2 and size ~0ULL, then records whether the helper returns NULL. A vulnerable kernel returns a non-NULL pointer because the runtime check wraps the addition. A fixed kernel rejects the request. The callback records the helper result without dereferencing any returned pointer. The callback reports the helper result through BSS and returns 0 intentionally. hid_rdesc_fixup return values are consumed as report descriptor fixup results, so a positive test-result value would be interpreted as a replacement report descriptor size. Also add KHDR_INCLUDES to the HID selftest build so hid_bpf.c sees the current kernel UAPI HID definitions on systems whose installed headers do not provide enum hid_report_type. Fixes: 658ee5a64fcf ("HID: bpf: allocate data memory for device_event BPF programs") Signed-off-by: Yiyang Chen Signed-off-by: Benjamin Tissoires Signed-off-by: Sasha Levin --- tools/testing/selftests/hid/Makefile | 2 +- tools/testing/selftests/hid/hid_bpf.c | 11 +++++++++++ tools/testing/selftests/hid/progs/hid.c | 15 +++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile index 50ec9e0406aba..357c6eb5ff5ee 100644 --- a/tools/testing/selftests/hid/Makefile +++ b/tools/testing/selftests/hid/Makefile @@ -24,7 +24,7 @@ CXX ?= $(CROSS_COMPILE)g++ HOSTPKG_CONFIG := pkg-config -CFLAGS += -g -O0 -rdynamic -Wall -Werror -I$(OUTPUT) +CFLAGS += -g -O0 -rdynamic -Wall -Werror -I$(OUTPUT) $(KHDR_INCLUDES) CFLAGS += -I$(OUTPUT)/tools/include LDLIBS += -lelf -lz -lrt -lpthread diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c index 269256e1decd8..b851339308c21 100644 --- a/tools/testing/selftests/hid/hid_bpf.c +++ b/tools/testing/selftests/hid/hid_bpf.c @@ -898,6 +898,17 @@ TEST_F(hid_bpf, test_rdesc_fixup) ASSERT_EQ(rpt_desc.value[4], 0x42); } +TEST_F(hid_bpf, test_rdesc_fixup_get_data_overflow) +{ + const struct test_program progs[] = { + { .name = "hid_rdesc_fixup_get_data_overflow" }, + }; + + LOAD_PROGRAMS(progs); + + ASSERT_EQ(self->skel->bss->get_data_overflow_check, 1); +} + static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) { diff --git a/tools/testing/selftests/hid/progs/hid.c b/tools/testing/selftests/hid/progs/hid.c index 5ecc845ef7921..b21fbb13c926f 100644 --- a/tools/testing/selftests/hid/progs/hid.c +++ b/tools/testing/selftests/hid/progs/hid.c @@ -13,6 +13,7 @@ struct attach_prog_args { __u64 callback_check = 52; __u64 callback2_check = 52; +__u64 get_data_overflow_check; SEC("?struct_ops/hid_device_event") int BPF_PROG(hid_first_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) @@ -240,6 +241,20 @@ struct hid_bpf_ops rdesc_fixup = { .hid_rdesc_fixup = (void *)hid_rdesc_fixup, }; +SEC("?struct_ops.s/hid_rdesc_fixup") +int BPF_PROG(hid_rdesc_fixup_get_data_overflow, struct hid_bpf_ctx *hid_ctx) +{ + if (!hid_bpf_get_data(hid_ctx, 2 /* offset */, ~0ULL /* size */)) + get_data_overflow_check = 1; + + return 0; +} + +SEC(".struct_ops.link") +struct hid_bpf_ops rdesc_fixup_get_data_overflow = { + .hid_rdesc_fixup = (void *)hid_rdesc_fixup_get_data_overflow, +}; + SEC("?struct_ops/hid_device_event") int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) { From 9d343a4889e5f5a1ec216d8b7d3f04ae1bc6160d Mon Sep 17 00:00:00 2001 From: Jia He Date: Tue, 30 Jun 2026 22:23:47 +0000 Subject: [PATCH 2097/2758] arm64/sysreg: Fix BWE field encoding in ID_AA64DFR2_EL1 [ Upstream commit 6666336d8fe5f3f0cd3f8da2865ca78110a6e170 ] Commit 93d7356e4b30 ("arm64: sysreg: Describe ID_AA64DFR2_EL1 fields") encodes the FEAT_BWE2 value of the BWE field as '0b0002'. Binary literals only accept the digits 0 and 1, so the intended value is 2, i.e. 0b0010. The macro generated by gen-sysreg.awk currently expands to #define ID_AA64DFR2_EL1_BWE_FEAT_BWE2 UL(0b0002) is not legal C and would fail to compile if any in-tree code referenced it. At present no caller uses this enum value, so the kernel still builds cleanly, but the bug is latent. Fix the typo by using the correct binary literal 0b0010. Cc: Bin Guo Fixes: 93d7356e4b30 ("arm64: sysreg: Describe ID_AA64DFR2_EL1 fields") Signed-off-by: Jia He Reviewed-by: Mark Brown Reviewed-by: Oliver Upton Acked-by: Marc Zyngier Signed-off-by: Will Deacon Signed-off-by: Sasha Levin --- arch/arm64/tools/sysreg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg index 6c3ff14e561e6..88b31ae8450f4 100644 --- a/arch/arm64/tools/sysreg +++ b/arch/arm64/tools/sysreg @@ -1806,7 +1806,7 @@ Res0 15:8 UnsignedEnum 7:4 BWE 0b0000 NI 0b0001 FEAT_BWE - 0b0002 FEAT_BWE2 + 0b0010 FEAT_BWE2 EndEnum UnsignedEnum 3:0 STEP 0b0000 NI From ea866cab12db1a2100b400a8b03569e5bc0ee29a Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Mon, 29 Jun 2026 21:51:21 -0700 Subject: [PATCH 2098/2758] net: usb: net1080: validate packet_len before pad-byte access in rx_fixup [ Upstream commit 03f384bc0cb8d4a1301d4f5b0baef2d980258383 ] For an even packet_len, net1080_rx_fixup() reads the pad byte at skb->data[packet_len] before the skb->len != packet_len check further down, and packet_len is only bounded against NC_MAX_PACKET. A malicious NetChip 1080 device can send a short frame advertising a large even packet_len (e.g. 0x4000), so the pad-byte read lands past the end of the skb: BUG: KASAN: slab-out-of-bounds in net1080_rx_fixup Read of size 1 at addr ffff8880106c83c6 by task ksoftirqd/0/14 ... net1080_rx_fixup (drivers/net/usb/net1080.c:384) usbnet_bh (drivers/net/usb/usbnet.c:1589) process_one_work (kernel/workqueue.c:3322) bh_worker (kernel/workqueue.c:3708) tasklet_action (kernel/softirq.c:965) handle_softirqs (kernel/softirq.c:622) ... Reject the frame when packet_len >= skb->len before reading. Fixes: 904813cd8a0b ("[PATCH] USB: usbnet (4/9) module for net1080 cables") Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Link: https://patch.msgid.link/20260630045121.1565324-1-xmei5@asu.edu Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/usb/net1080.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/usb/net1080.c b/drivers/net/usb/net1080.c index 5d4a1fd2b5244..19f6e1222d931 100644 --- a/drivers/net/usb/net1080.c +++ b/drivers/net/usb/net1080.c @@ -381,7 +381,7 @@ static int net1080_rx_fixup(struct usbnet *dev, struct sk_buff *skb) skb_trim(skb, skb->len - sizeof *trailer); if ((packet_len & 0x01) == 0) { - if (skb->data [packet_len] != PAD_BYTE) { + if (packet_len >= skb->len || skb->data[packet_len] != PAD_BYTE) { dev->net->stats.rx_frame_errors++; netdev_dbg(dev->net, "bad pad\n"); return 0; From f618cbe9b24cd0202004d2db781d5f80ab77037f Mon Sep 17 00:00:00 2001 From: Qihang Date: Wed, 1 Jul 2026 10:26:17 +0800 Subject: [PATCH 2099/2758] gue: validate REMCSUM private option length [ Upstream commit d335dcc6f521571d57117b8deeebc940836e5450 ] GUE private flags can indicate that remote checksum offload metadata is present. The private flags field itself is accounted for by guehdr_flags_len(), but guehdr_priv_flags_len() currently returns 0 even when GUE_PFLAG_REMCSUM is set. This lets a packet with only the private flags field pass validate_gue_flags(), after which gue_remcsum() and gue_gro_remcsum() read the missing REMCSUM start/offset fields from the following bytes. Account for GUE_PLEN_REMCSUM when GUE_PFLAG_REMCSUM is present so that malformed packets are rejected during option validation. Fixes: c1aa8347e73e ("gue: Protocol constants for remote checksum offload") Signed-off-by: Qihang Reviewed-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- include/net/gue.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/gue.h b/include/net/gue.h index dfca298bec9cc..caefd6da86939 100644 --- a/include/net/gue.h +++ b/include/net/gue.h @@ -80,7 +80,7 @@ static inline size_t guehdr_flags_len(__be16 flags) static inline size_t guehdr_priv_flags_len(__be32 flags) { - return 0; + return (flags & GUE_PFLAG_REMCSUM) ? GUE_PLEN_REMCSUM : 0; } /* Validate standard and private flags. Returns non-zero (meaning invalid) From 00d034fe8230dfc5832fe011ff2d81c1a2cc7a29 Mon Sep 17 00:00:00 2001 From: Wyatt Feng Date: Sun, 28 Jun 2026 16:05:54 +0800 Subject: [PATCH 2100/2758] netfilter: xt_u32: reject invalid shift counts [ Upstream commit 64cdf7d30ac18e43df6c48004435febb965809a8 ] u32_match_it() executes rule-supplied shift operands on a 32-bit value. A malformed u32 rule can provide a shift count of 32 or more, triggering an undefined shift out-of-bounds during packet evaluation. Validate XT_U32_LEFTSH and XT_U32_RIGHTSH operands in u32_mt_checkentry() and reject malformed rules before they reach the packet path. Fixes: 1b50b8a371e9 ("[NETFILTER]: Add u32 match") Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Signed-off-by: Wyatt Feng Signed-off-by: Ren Wei Signed-off-by: Florian Westphal Signed-off-by: Sasha Levin --- net/netfilter/xt_u32.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/net/netfilter/xt_u32.c b/net/netfilter/xt_u32.c index 1ae7f926b0df7..dabbaa742874c 100644 --- a/net/netfilter/xt_u32.c +++ b/net/netfilter/xt_u32.c @@ -106,7 +106,7 @@ static int u32_mt_checkentry(const struct xt_mtchk_param *par) { const struct xt_u32 *data = par->matchinfo; const struct xt_u32_test *ct; - unsigned int i; + unsigned int i, j; if (data->ntests > ARRAY_SIZE(data->tests)) return -EINVAL; @@ -117,6 +117,16 @@ static int u32_mt_checkentry(const struct xt_mtchk_param *par) if (ct->nnums > ARRAY_SIZE(ct->location) || ct->nvalues > ARRAY_SIZE(ct->value)) return -EINVAL; + + for (j = 1; j < ct->nnums; ++j) { + switch (ct->location[j].nextop) { + case XT_U32_LEFTSH: + case XT_U32_RIGHTSH: + if (ct->location[j].number >= 32) + return -EINVAL; + break; + } + } } return 0; From 905a927b2e6fec7b174e9e5644d271047b61378f Mon Sep 17 00:00:00 2001 From: Feng Wu Date: Thu, 25 Jun 2026 08:44:25 +0000 Subject: [PATCH 2101/2758] netfilter: xt_rateest: fix u64 truncation in xt_rateest_mt() [ Upstream commit 444853cd438201007da5359821adcc2995655ab1 ] On links faster than ~34 Gbps, where byte rate may exceed 2^32-1 (~ 4.3 GBps), the comparison result becomes incorrect because the truncated value no longer reflects the actual estimator rate. Fix by changing the local variables to u64. Fixes: 1c0d32fde5bd ("net_sched: gen_estimator: complete rewrite of rate estimators") Signed-off-by: Feng Wu Signed-off-by: Florian Westphal Signed-off-by: Sasha Levin --- net/netfilter/xt_rateest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/xt_rateest.c b/net/netfilter/xt_rateest.c index b1d736c15fcbe..7c05b63425784 100644 --- a/net/netfilter/xt_rateest.c +++ b/net/netfilter/xt_rateest.c @@ -16,7 +16,7 @@ xt_rateest_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_rateest_match_info *info = par->matchinfo; struct gnet_stats_rate_est64 sample = {0}; - u_int32_t bps1, bps2, pps1, pps2; + u64 bps1, bps2, pps1, pps2; bool ret = true; gen_estimator_read(&info->est1->rate_est, &sample); From d5e39e5eb6b30bc4a3bb7aba54c293cf36a806c7 Mon Sep 17 00:00:00 2001 From: Zhixing Chen Date: Wed, 1 Jul 2026 18:09:30 +0800 Subject: [PATCH 2102/2758] netfilter: ip6tables: mark malformed IPv6 extension headers for hotdrop [ Upstream commit 43ccc20b5a733226417832cf16ef45322e594990 ] The ah, hbh and rt matches check that the fixed extension header is present, then use the header length field to derive the advertised extension header length for matching. For the ah match, add the missing advertised-length check. For hbh and rt, update the existing advertised-length checks. In all three cases, set hotdrop to true before returning false when the advertised extension header length exceeds the available skb data. Returning false treats the packet as a rule mismatch. Set hotdrop to true and drop malformed packets so they cannot bypass rules intended to drop packets with these IPv6 extension headers. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Zhixing Chen Signed-off-by: Florian Westphal Signed-off-by: Sasha Levin --- net/ipv6/netfilter/ip6t_ah.c | 5 +++++ net/ipv6/netfilter/ip6t_hbh.c | 1 + net/ipv6/netfilter/ip6t_rt.c | 3 ++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/net/ipv6/netfilter/ip6t_ah.c b/net/ipv6/netfilter/ip6t_ah.c index 70da2f2ce064c..1258783ed8762 100644 --- a/net/ipv6/netfilter/ip6t_ah.c +++ b/net/ipv6/netfilter/ip6t_ah.c @@ -56,6 +56,11 @@ static bool ah_mt6(const struct sk_buff *skb, struct xt_action_param *par) } hdrlen = ipv6_authlen(ah); + if (skb->len - ptr < hdrlen) { + /* Packet smaller than its length field */ + par->hotdrop = true; + return false; + } pr_debug("IPv6 AH LEN %u %u ", hdrlen, ah->hdrlen); pr_debug("RES %04X ", ah->reserved); diff --git a/net/ipv6/netfilter/ip6t_hbh.c b/net/ipv6/netfilter/ip6t_hbh.c index 450dd53846a2f..6d1a5d2026a67 100644 --- a/net/ipv6/netfilter/ip6t_hbh.c +++ b/net/ipv6/netfilter/ip6t_hbh.c @@ -75,6 +75,7 @@ hbh_mt6(const struct sk_buff *skb, struct xt_action_param *par) hdrlen = ipv6_optlen(oh); if (skb->len - ptr < hdrlen) { /* Packet smaller than it's length field */ + par->hotdrop = true; return false; } diff --git a/net/ipv6/netfilter/ip6t_rt.c b/net/ipv6/netfilter/ip6t_rt.c index 5561bd9cea818..278b52752f364 100644 --- a/net/ipv6/netfilter/ip6t_rt.c +++ b/net/ipv6/netfilter/ip6t_rt.c @@ -56,7 +56,8 @@ static bool rt_mt6(const struct sk_buff *skb, struct xt_action_param *par) hdrlen = ipv6_optlen(rh); if (skb->len - ptr < hdrlen) { - /* Pcket smaller than its length field */ + /* Packet smaller than its length field */ + par->hotdrop = true; return false; } From 94427ca359435104a8d94fc583cc93e99002ec96 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 2 Jul 2026 14:33:09 +0200 Subject: [PATCH 2103/2758] netfilter: nft_set_rbtree: get command skips end element with open interval [ Upstream commit d63611cbe8af99dd61b118ee6e5b5e3e518250b2 ] The get command on intervals provide partial matches such as subranges for usability reasons. However, an open interval has no closing end element. If the closing element matches within the range of the open internal, ie. its closest match is the start element of the open range, then, return 0 but offer no matching element to userspace through netlink as a special case. Userspace provides at least a matching start element in this case and the closing end element matching the open interal is ignored. Another possibility is to report the matching start element of the open interval for this end interval. However, this results in duplicated matching being listed in userspace because userspace does not expect a start element as response to a end element. Fixes: 2aa34191f06f ("netfilter: nft_set_rbtree: use binary search array in get command") Reported-by: Melbin K Mathew Signed-off-by: Pablo Neira Ayuso Signed-off-by: Florian Westphal Signed-off-by: Sasha Levin --- net/netfilter/nf_tables_api.c | 3 +++ net/netfilter/nft_set_rbtree.c | 8 ++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 87387adbca655..e1db3e678656d 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -6559,6 +6559,9 @@ static int nft_get_set_elem(struct nft_ctx *ctx, const struct nft_set *set, if (err < 0) return err; + if (!elem.priv) + return 0; + err = -ENOMEM; skb = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); if (skb == NULL) diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c index 560fbe6e3f750..b4b67f48ad64e 100644 --- a/net/netfilter/nft_set_rbtree.c +++ b/net/netfilter/nft_set_rbtree.c @@ -184,10 +184,14 @@ nft_rbtree_get(const struct net *net, const struct nft_set *set, if (!interval || nft_set_elem_expired(interval->from)) return ERR_PTR(-ENOENT); - if (flags & NFT_SET_ELEM_INTERVAL_END) + if (flags & NFT_SET_ELEM_INTERVAL_END) { + if (!interval->to) + return NULL; + rbe = container_of(interval->to, struct nft_rbtree_elem, ext); - else + } else { rbe = container_of(interval->from, struct nft_rbtree_elem, ext); + } return &rbe->priv; } From 230173cc6105bdfb2696d37e6e56687b003fbe63 Mon Sep 17 00:00:00 2001 From: Wyatt Feng Date: Fri, 3 Jul 2026 13:04:46 +0800 Subject: [PATCH 2104/2758] netfilter: xt_connmark: reject invalid shift parameters [ Upstream commit 1b47026fb4b35bac850ad6e8a4ad7fc018e09ebc ] Revision 2 of the CONNMARK target accepts user-controlled shift parameters and applies them to 32-bit mark values in connmark_tg_shift(). A shift_bits value of 32 or more triggers an undefined-shift bug when the rule is evaluated. Invalid shift_dir values are also accepted and silently fall back to the left-shift path. Reject invalid revision-2 shift parameters in connmark_tg_check() so malformed rules fail at installation time, before they can reach the packet path. Fixes: 472a73e00757 ("netfilter: xt_conntrack: Support bit-shifting for CONNMARK & MARK targets.") Reported-by: Yuan Tan Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Signed-off-by: Wyatt Feng Reviewed-by: Ren Wei Reviewed-by: Phil Sutter Signed-off-by: Florian Westphal Signed-off-by: Sasha Levin --- net/netfilter/xt_connmark.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/net/netfilter/xt_connmark.c b/net/netfilter/xt_connmark.c index 4277084de2e70..2cf27f7d59b95 100644 --- a/net/netfilter/xt_connmark.c +++ b/net/netfilter/xt_connmark.c @@ -112,6 +112,16 @@ static int connmark_tg_check(const struct xt_tgchk_param *par) return ret; } +static int connmark_tg_check_v2(const struct xt_tgchk_param *par) +{ + const struct xt_connmark_tginfo2 *info = par->targinfo; + + if (info->shift_dir > D_SHIFT_RIGHT || info->shift_bits >= 32) + return -EINVAL; + + return connmark_tg_check(par); +} + static void connmark_tg_destroy(const struct xt_tgdtor_param *par) { nf_ct_netns_put(par->net, par->family); @@ -162,7 +172,7 @@ static struct xt_target connmark_tg_reg[] __read_mostly = { .name = "CONNMARK", .revision = 2, .family = NFPROTO_IPV4, - .checkentry = connmark_tg_check, + .checkentry = connmark_tg_check_v2, .target = connmark_tg_v2, .targetsize = sizeof(struct xt_connmark_tginfo2), .destroy = connmark_tg_destroy, @@ -183,7 +193,7 @@ static struct xt_target connmark_tg_reg[] __read_mostly = { .name = "CONNMARK", .revision = 2, .family = NFPROTO_IPV6, - .checkentry = connmark_tg_check, + .checkentry = connmark_tg_check_v2, .target = connmark_tg_v2, .targetsize = sizeof(struct xt_connmark_tginfo2), .destroy = connmark_tg_destroy, From 6f2cb20d8e286218d51754f06ee219d8ce754b18 Mon Sep 17 00:00:00 2001 From: Mingyou Chen Date: Wed, 1 Jul 2026 20:01:39 +0800 Subject: [PATCH 2105/2758] platform/x86: bitland-mifs-wmi: Fix NULL pointer dereference during suspend/resume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit d3666875c75eb1bc8090343fa0d6fc8fb7924356 ] The driver registers two distinct WMI devices: a control device (BITLAND_WMI_CONTROL) and an event device (BITLAND_WMI_EVENT). During the probe phase, the event device handling path returns early before initializing the platform profile device (data->pp_dev), leaving it NULL. However, the PM sleep operations are registered globally for the WMI driver and are triggered for both devices. When entering suspend, the event device invokes bitland_mifs_wmi_suspend(), which passes the uninitialized data->pp_dev (NULL) into laptop_profile_get(). This leads to a NULL pointer dereference inside dev_get_drvdata(), causing a kernel Oops and halting the suspend sequence. Fix this by adding a validity check for data->pp_dev in both the suspend and resume callbacks, safely skipping profile operations for the event device. Fixes: dc1ec4fa86b2 ("platform/x86: bitland-mifs-wmi: Add new Bitland MIFS WMI driver") Reviewed-by: Armin Wolf Signed-off-by: Mingyou Chen Reviewed-by: Armin Wolf Link: https://patch.msgid.link/20260701120140.430659-1-qby140326@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Sasha Levin --- drivers/platform/x86/bitland-mifs-wmi.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/platform/x86/bitland-mifs-wmi.c b/drivers/platform/x86/bitland-mifs-wmi.c index b0d06a80e89ef..3a373184519da 100644 --- a/drivers/platform/x86/bitland-mifs-wmi.c +++ b/drivers/platform/x86/bitland-mifs-wmi.c @@ -300,6 +300,10 @@ static int bitland_mifs_wmi_suspend(struct device *dev) enum platform_profile_option profile; int ret; + /* Skip event device */ + if (!data->pp_dev) + return 0; + ret = laptop_profile_get(data->pp_dev, &profile); if (ret == 0) data->saved_profile = profile; @@ -311,6 +315,10 @@ static int bitland_mifs_wmi_resume(struct device *dev) { struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev); + /* Skip event device */ + if (!data->pp_dev) + return 0; + dev_dbg(dev, "Resuming, restoring profile %d\n", data->saved_profile); return laptop_profile_set(dev, data->saved_profile); } From 25d4c094830062d0b3889648e6d8f24fa55805ab Mon Sep 17 00:00:00 2001 From: Mark Bloch Date: Sun, 3 May 2026 23:27:20 +0300 Subject: [PATCH 2106/2758] net/mlx5: Lag: refactor representor reload handling [ Upstream commit b501400fa66d9b8045f282ae8de80ceb6e02fe85 ] Representor reload during LAG/MPESW transitions has to be repeated in several flows, and each open-coded loop was easy to get out of sync when adding new flags or tweaking error handling. Move the sequencing into a single helper so that all call sites share the same ordering and checks. Signed-off-by: Mark Bloch Reviewed-by: Shay Drori Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260503202726.266415-2-tariqt@nvidia.com Signed-off-by: Jakub Kicinski Stable-dep-of: 0f0e4ae6975c ("net/mlx5: LAG, Fix off-by-one in single-FDB error rollback") Signed-off-by: Sasha Levin --- .../net/ethernet/mellanox/mlx5/core/lag/lag.c | 45 +++++++++++-------- .../net/ethernet/mellanox/mlx5/core/lag/lag.h | 2 + .../ethernet/mellanox/mlx5/core/lag/mpesw.c | 12 ++--- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c index f8e70ac5a85b7..6c6eba67bab73 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c @@ -1093,6 +1093,27 @@ void mlx5_lag_remove_devices(struct mlx5_lag *ldev) } } +int mlx5_lag_reload_ib_reps(struct mlx5_lag *ldev, u32 flags, bool cont_on_fail) +{ + struct lag_func *pf; + int ret; + int i; + + mlx5_ldev_for_each(i, 0, ldev) { + pf = mlx5_lag_pf(ldev, i); + if (!(pf->dev->priv.flags & flags)) { + struct mlx5_eswitch *esw; + + esw = pf->dev->priv.eswitch; + ret = mlx5_eswitch_reload_ib_reps(esw); + if (ret && !cont_on_fail) + return ret; + } + } + + return 0; +} + void mlx5_disable_lag(struct mlx5_lag *ldev) { bool shared_fdb = test_bit(MLX5_LAG_MODE_FLAG_SHARED_FDB, &ldev->mode_flags); @@ -1130,9 +1151,8 @@ void mlx5_disable_lag(struct mlx5_lag *ldev) mlx5_lag_add_devices(ldev); if (shared_fdb) - mlx5_ldev_for_each(i, 0, ldev) - if (!(mlx5_lag_pf(ldev, i)->dev->priv.flags & MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV)) - mlx5_eswitch_reload_ib_reps(mlx5_lag_pf(ldev, i)->dev->priv.eswitch); + mlx5_lag_reload_ib_reps(ldev, MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV, + true); } bool mlx5_lag_shared_fdb_supported(struct mlx5_lag *ldev) @@ -1393,10 +1413,8 @@ static void mlx5_do_bond(struct mlx5_lag *ldev) if (err) { if (shared_fdb || roce_lag) mlx5_lag_add_devices(ldev); - if (shared_fdb) { - mlx5_ldev_for_each(i, 0, ldev) - mlx5_eswitch_reload_ib_reps(mlx5_lag_pf(ldev, i)->dev->priv.eswitch); - } + if (shared_fdb) + mlx5_lag_reload_ib_reps(ldev, 0, true); return; } @@ -1414,24 +1432,15 @@ static void mlx5_do_bond(struct mlx5_lag *ldev) mlx5_nic_vport_enable_roce(dev); } } else if (shared_fdb) { - int i; - dev0->priv.flags &= ~MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; mlx5_rescan_drivers_locked(dev0); - - mlx5_ldev_for_each(i, 0, ldev) { - err = mlx5_eswitch_reload_ib_reps(mlx5_lag_pf(ldev, i)->dev->priv.eswitch); - if (err) - break; - } - + err = mlx5_lag_reload_ib_reps(ldev, 0, false); if (err) { dev0->priv.flags |= MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; mlx5_rescan_drivers_locked(dev0); mlx5_deactivate_lag(ldev); mlx5_lag_add_devices(ldev); - mlx5_ldev_for_each(i, 0, ldev) - mlx5_eswitch_reload_ib_reps(mlx5_lag_pf(ldev, i)->dev->priv.eswitch); + mlx5_lag_reload_ib_reps(ldev, 0, true); mlx5_core_err(dev0, "Failed to enable lag\n"); return; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h index 6c911374f409b..daca8ebd52567 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h @@ -199,4 +199,6 @@ int mlx5_get_next_ldev_func(struct mlx5_lag *ldev, int start_idx); int mlx5_lag_get_dev_index_by_seq(struct mlx5_lag *ldev, int seq); int mlx5_lag_num_devs(struct mlx5_lag *ldev); int mlx5_lag_num_netdevs(struct mlx5_lag *ldev); +int mlx5_lag_reload_ib_reps(struct mlx5_lag *ldev, u32 flags, + bool cont_on_fail); #endif /* __MLX5_LAG_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c index 5eea12a6887ac..edcd06f3be7a7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c @@ -70,7 +70,6 @@ static int mlx5_lag_enable_mpesw(struct mlx5_lag *ldev) int idx = mlx5_lag_get_dev_index_by_seq(ldev, MLX5_LAG_P1); struct mlx5_core_dev *dev0; int err; - int i; if (ldev->mode == MLX5_LAG_MODE_MPESW) return 0; @@ -103,11 +102,9 @@ static int mlx5_lag_enable_mpesw(struct mlx5_lag *ldev) dev0->priv.flags &= ~MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; mlx5_rescan_drivers_locked(dev0); - mlx5_ldev_for_each(i, 0, ldev) { - err = mlx5_eswitch_reload_ib_reps(mlx5_lag_pf(ldev, i)->dev->priv.eswitch); - if (err) - goto err_rescan_drivers; - } + err = mlx5_lag_reload_ib_reps(ldev, 0, false); + if (err) + goto err_rescan_drivers; mlx5_lag_set_vports_agg_speed(ldev); @@ -119,8 +116,7 @@ static int mlx5_lag_enable_mpesw(struct mlx5_lag *ldev) mlx5_deactivate_lag(ldev); err_add_devices: mlx5_lag_add_devices(ldev); - mlx5_ldev_for_each(i, 0, ldev) - mlx5_eswitch_reload_ib_reps(mlx5_lag_pf(ldev, i)->dev->priv.eswitch); + mlx5_lag_reload_ib_reps(ldev, 0, true); mlx5_mpesw_metadata_cleanup(ldev); return err; } From db9e44e0ed63cc906067b939ab3fd6325a0bc6f7 Mon Sep 17 00:00:00 2001 From: Mark Bloch Date: Sun, 3 May 2026 23:27:22 +0300 Subject: [PATCH 2107/2758] net/mlx5: E-Switch, add representor lifecycle lock [ Upstream commit 9b8468f001e46c4ffcbbeb2050aabf80e1ed7960 ] Add a per-E-Switch mutex for serializing representor lifecycle work and provide small helpers for taking and dropping it. Initialize and destroy the mutex with the E-Switch offloads state. Add the lock and helper API first. Follow-up patches will take the lock in the individual representor lifecycle components. This keeps the functional changes split by component and leaves this patch without intended behavior change, making the series easier to review and bisectable. Signed-off-by: Mark Bloch Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260503202726.266415-4-tariqt@nvidia.com Signed-off-by: Jakub Kicinski Stable-dep-of: 0f0e4ae6975c ("net/mlx5: LAG, Fix off-by-one in single-FDB error rollback") Signed-off-by: Sasha Levin --- drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 6 ++++++ .../ethernet/mellanox/mlx5/core/eswitch_offloads.c | 12 ++++++++++++ 2 files changed, 18 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index e9cf7c592ce9e..0b107c2ddbfa6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -316,6 +316,7 @@ struct mlx5_esw_offload { DECLARE_HASHTABLE(termtbl_tbl, 8); struct mutex termtbl_mutex; /* protects termtbl hash */ struct xarray vhca_map; + struct mutex reps_lock; /* protects representor load/unload/register */ const struct mlx5_eswitch_rep_ops *rep_ops[NUM_REP_TYPES]; u8 inline_mode; atomic64_t num_flows; @@ -949,6 +950,8 @@ mlx5_esw_lag_demux_fg_create(struct mlx5_eswitch *esw, struct mlx5_flow_handle * mlx5_esw_lag_demux_rule_create(struct mlx5_eswitch *esw, u16 vport_num, struct mlx5_flow_table *lag_ft); +void mlx5_esw_reps_block(struct mlx5_eswitch *esw); +void mlx5_esw_reps_unblock(struct mlx5_eswitch *esw); #else /* CONFIG_MLX5_ESWITCH */ /* eswitch API stubs */ static inline int mlx5_eswitch_init(struct mlx5_core_dev *dev) { return 0; } @@ -1026,6 +1029,9 @@ mlx5_esw_host_functions_enabled(const struct mlx5_core_dev *dev) return true; } +static inline void mlx5_esw_reps_block(struct mlx5_eswitch *esw) {} +static inline void mlx5_esw_reps_unblock(struct mlx5_eswitch *esw) {} + static inline bool mlx5_esw_vport_vhca_id(struct mlx5_eswitch *esw, u16 vportn, u16 *vhca_id) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index a078d06f45673..41d65f2bf87dd 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -2410,6 +2410,16 @@ static int esw_create_restore_table(struct mlx5_eswitch *esw) return err; } +void mlx5_esw_reps_block(struct mlx5_eswitch *esw) +{ + mutex_lock(&esw->offloads.reps_lock); +} + +void mlx5_esw_reps_unblock(struct mlx5_eswitch *esw) +{ + mutex_unlock(&esw->offloads.reps_lock); +} + static void esw_mode_change(struct mlx5_eswitch *esw, u16 mode) { mlx5_devcom_comp_lock(esw->dev->priv.hca_devcom_comp); @@ -2642,6 +2652,7 @@ static void esw_offloads_cleanup_reps(struct mlx5_eswitch *esw) mlx5_esw_for_each_rep(esw, i, rep) mlx5_esw_offloads_rep_cleanup(esw, rep); xa_destroy(&esw->offloads.vport_reps); + mutex_destroy(&esw->offloads.reps_lock); } static int esw_offloads_init_reps(struct mlx5_eswitch *esw) @@ -2651,6 +2662,7 @@ static int esw_offloads_init_reps(struct mlx5_eswitch *esw) int err; xa_init(&esw->offloads.vport_reps); + mutex_init(&esw->offloads.reps_lock); mlx5_esw_for_each_vport(esw, i, vport) { err = mlx5_esw_offloads_rep_add(esw, vport); From 7ac37a167cc69ea1ba6efdaf6ddad3849aa3e0be Mon Sep 17 00:00:00 2001 From: Mark Bloch Date: Sun, 3 May 2026 23:27:23 +0300 Subject: [PATCH 2108/2758] net/mlx5: Lag, avoid LAG and representor lock cycles [ Upstream commit 63ec6c69e6134ce4cf5dc9acc61b49bac4916979 ] The LAG shared-FDB and multiport E-Switch transitions rescan auxiliary devices and reload IB representors while holding ldev->lock. Driver bind/unbind paths may register or unregister E-Switch representor ops, and representor load paths may enter LAG code, so holding ldev->lock across those calls creates lock-order cycles with the E-Switch representor lock. Keep the devcom component locked for the transition, but drop ldev->lock before rescanning auxiliary devices or reloading IB representors. Mark the LAG transition as in progress while the lock is dropped and assert the devcom lock where the helper relies on it. This preserves LAG serialization while avoiding ldev->lock nesting under E-Switch representor registration. Signed-off-by: Mark Bloch Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260503202726.266415-5-tariqt@nvidia.com Signed-off-by: Jakub Kicinski Stable-dep-of: 0f0e4ae6975c ("net/mlx5: LAG, Fix off-by-one in single-FDB error rollback") Signed-off-by: Sasha Levin --- .../net/ethernet/mellanox/mlx5/core/lag/lag.c | 142 ++++++++++++++---- .../net/ethernet/mellanox/mlx5/core/lag/lag.h | 7 +- .../ethernet/mellanox/mlx5/core/lag/mpesw.c | 10 +- .../ethernet/mellanox/mlx5/core/lib/devcom.c | 8 + .../ethernet/mellanox/mlx5/core/lib/devcom.h | 1 + 5 files changed, 134 insertions(+), 34 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c index 6c6eba67bab73..22b7efea34b83 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c @@ -1063,37 +1063,99 @@ bool mlx5_lag_check_prereq(struct mlx5_lag *ldev) return true; } -void mlx5_lag_add_devices(struct mlx5_lag *ldev) +static void mlx5_lag_assert_locked_transition(struct mlx5_lag *ldev) { + struct mlx5_devcom_comp_dev *devcom = NULL; struct lag_func *pf; int i; - mlx5_ldev_for_each(i, 0, ldev) { - pf = mlx5_lag_pf(ldev, i); - if (pf->dev->priv.flags & MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV) - continue; + lockdep_assert_held(&ldev->lock); - pf->dev->priv.flags &= ~MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; - mlx5_rescan_drivers_locked(pf->dev); + i = mlx5_get_next_ldev_func(ldev, 0); + if (i < MLX5_MAX_PORTS) { + pf = mlx5_lag_pf(ldev, i); + devcom = pf->dev->priv.hca_devcom_comp; } + mlx5_devcom_comp_assert_locked(devcom); } -void mlx5_lag_remove_devices(struct mlx5_lag *ldev) +static void mlx5_lag_drop_lock_for_reps(struct mlx5_lag *ldev) +{ + mlx5_lag_assert_locked_transition(ldev); + + /* Keep PF membership stable while ldev->lock is dropped. Device add + * and remove paths observe mode_changes_in_progress and retry. + */ + ldev->mode_changes_in_progress++; + mutex_unlock(&ldev->lock); +} + +static void mlx5_lag_retake_lock_after_reps(struct mlx5_lag *ldev) { + mutex_lock(&ldev->lock); + ldev->mode_changes_in_progress--; +} + +void mlx5_lag_rescan_dev_locked(struct mlx5_lag *ldev, + struct mlx5_core_dev *dev, + bool enable) +{ + if (dev->priv.flags & MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV) + return; + + if (enable) + dev->priv.flags &= ~MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; + else + dev->priv.flags |= MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; + + /* Auxiliary bus probe/remove can register or unregister representor + * callbacks and take reps_lock. Drop ldev->lock so the only ordering + * remains reps_lock -> ldev->lock from representor callbacks. + */ + mlx5_lag_drop_lock_for_reps(ldev); + mlx5_rescan_drivers_locked(dev); + mlx5_lag_retake_lock_after_reps(ldev); +} + +static void mlx5_lag_rescan_devices_locked(struct mlx5_lag *ldev, bool enable) +{ + struct mlx5_core_dev *devs[MLX5_MAX_PORTS]; struct lag_func *pf; + int num_devs = 0; int i; + mlx5_lag_assert_locked_transition(ldev); + mlx5_ldev_for_each(i, 0, ldev) { pf = mlx5_lag_pf(ldev, i); if (pf->dev->priv.flags & MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV) continue; - pf->dev->priv.flags |= MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; - mlx5_rescan_drivers_locked(pf->dev); + if (enable) + pf->dev->priv.flags &= ~MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; + else + pf->dev->priv.flags |= MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; + devs[num_devs++] = pf->dev; } + + mlx5_lag_drop_lock_for_reps(ldev); + for (i = 0; i < num_devs; i++) + mlx5_rescan_drivers_locked(devs[i]); + mlx5_lag_retake_lock_after_reps(ldev); } -int mlx5_lag_reload_ib_reps(struct mlx5_lag *ldev, u32 flags, bool cont_on_fail) +void mlx5_lag_add_devices(struct mlx5_lag *ldev) +{ + mlx5_lag_rescan_devices_locked(ldev, true); +} + +void mlx5_lag_remove_devices(struct mlx5_lag *ldev) +{ + mlx5_lag_rescan_devices_locked(ldev, false); +} + +static int mlx5_lag_reload_ib_reps_unlocked(struct mlx5_lag *ldev, u32 flags, + bool cont_on_fail) { struct lag_func *pf; int ret; @@ -1105,7 +1167,9 @@ int mlx5_lag_reload_ib_reps(struct mlx5_lag *ldev, u32 flags, bool cont_on_fail) struct mlx5_eswitch *esw; esw = pf->dev->priv.eswitch; + mlx5_esw_reps_block(esw); ret = mlx5_eswitch_reload_ib_reps(esw); + mlx5_esw_reps_unblock(esw); if (ret && !cont_on_fail) return ret; } @@ -1114,6 +1178,34 @@ int mlx5_lag_reload_ib_reps(struct mlx5_lag *ldev, u32 flags, bool cont_on_fail) return 0; } +static int mlx5_lag_reload_ib_reps(struct mlx5_lag *ldev, u32 flags, + bool cont_on_fail) +{ + int ret; + + /* The HCA devcom component lock serializes LAG mode transitions while + * ldev->lock is dropped here. Dropping ldev->lock is required because + * the reload takes the per-E-Switch reps_lock, and representor + * load/unload callbacks can re-enter LAG netdev add/remove and take + * ldev->lock. Keep the ordering reps_lock -> ldev->lock. + */ + mlx5_lag_drop_lock_for_reps(ldev); + ret = mlx5_lag_reload_ib_reps_unlocked(ldev, flags, cont_on_fail); + mlx5_lag_retake_lock_after_reps(ldev); + + return ret; +} + +int mlx5_lag_reload_ib_reps_from_locked(struct mlx5_lag *ldev, u32 flags, + bool cont_on_fail) +{ + int ret; + + ret = mlx5_lag_reload_ib_reps(ldev, flags, cont_on_fail); + + return ret; +} + void mlx5_disable_lag(struct mlx5_lag *ldev) { bool shared_fdb = test_bit(MLX5_LAG_MODE_FLAG_SHARED_FDB, &ldev->mode_flags); @@ -1132,10 +1224,7 @@ void mlx5_disable_lag(struct mlx5_lag *ldev) if (shared_fdb) { mlx5_lag_remove_devices(ldev); } else if (roce_lag) { - if (!(dev0->priv.flags & MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV)) { - dev0->priv.flags |= MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; - mlx5_rescan_drivers_locked(dev0); - } + mlx5_lag_rescan_dev_locked(ldev, dev0, false); mlx5_ldev_for_each(i, 0, ldev) { if (i == idx) continue; @@ -1151,8 +1240,9 @@ void mlx5_disable_lag(struct mlx5_lag *ldev) mlx5_lag_add_devices(ldev); if (shared_fdb) - mlx5_lag_reload_ib_reps(ldev, MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV, - true); + mlx5_lag_reload_ib_reps_from_locked(ldev, + MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV, + true); } bool mlx5_lag_shared_fdb_supported(struct mlx5_lag *ldev) @@ -1414,7 +1504,8 @@ static void mlx5_do_bond(struct mlx5_lag *ldev) if (shared_fdb || roce_lag) mlx5_lag_add_devices(ldev); if (shared_fdb) - mlx5_lag_reload_ib_reps(ldev, 0, true); + mlx5_lag_reload_ib_reps_from_locked(ldev, 0, + true); return; } @@ -1422,8 +1513,7 @@ static void mlx5_do_bond(struct mlx5_lag *ldev) if (roce_lag) { struct mlx5_core_dev *dev; - dev0->priv.flags &= ~MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; - mlx5_rescan_drivers_locked(dev0); + mlx5_lag_rescan_dev_locked(ldev, dev0, true); mlx5_ldev_for_each(i, 0, ldev) { if (i == idx) continue; @@ -1432,15 +1522,15 @@ static void mlx5_do_bond(struct mlx5_lag *ldev) mlx5_nic_vport_enable_roce(dev); } } else if (shared_fdb) { - dev0->priv.flags &= ~MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; - mlx5_rescan_drivers_locked(dev0); - err = mlx5_lag_reload_ib_reps(ldev, 0, false); + mlx5_lag_rescan_dev_locked(ldev, dev0, true); + err = mlx5_lag_reload_ib_reps_from_locked(ldev, 0, + false); if (err) { - dev0->priv.flags |= MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; - mlx5_rescan_drivers_locked(dev0); + mlx5_lag_rescan_dev_locked(ldev, dev0, false); mlx5_deactivate_lag(ldev); mlx5_lag_add_devices(ldev); - mlx5_lag_reload_ib_reps(ldev, 0, true); + mlx5_lag_reload_ib_reps_from_locked(ldev, 0, + true); mlx5_core_err(dev0, "Failed to enable lag\n"); return; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h index daca8ebd52567..6afe7707d076e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h @@ -164,6 +164,9 @@ void mlx5_disable_lag(struct mlx5_lag *ldev); void mlx5_lag_remove_devices(struct mlx5_lag *ldev); int mlx5_deactivate_lag(struct mlx5_lag *ldev); void mlx5_lag_add_devices(struct mlx5_lag *ldev); +void mlx5_lag_rescan_dev_locked(struct mlx5_lag *ldev, + struct mlx5_core_dev *dev, + bool enable); struct mlx5_devcom_comp_dev *mlx5_lag_get_devcom_comp(struct mlx5_lag *ldev); #ifdef CONFIG_MLX5_ESWITCH @@ -199,6 +202,6 @@ int mlx5_get_next_ldev_func(struct mlx5_lag *ldev, int start_idx); int mlx5_lag_get_dev_index_by_seq(struct mlx5_lag *ldev, int seq); int mlx5_lag_num_devs(struct mlx5_lag *ldev); int mlx5_lag_num_netdevs(struct mlx5_lag *ldev); -int mlx5_lag_reload_ib_reps(struct mlx5_lag *ldev, u32 flags, - bool cont_on_fail); +int mlx5_lag_reload_ib_reps_from_locked(struct mlx5_lag *ldev, u32 flags, + bool cont_on_fail); #endif /* __MLX5_LAG_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c index edcd06f3be7a7..8a349f8fd8233 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c @@ -100,9 +100,8 @@ static int mlx5_lag_enable_mpesw(struct mlx5_lag *ldev) goto err_add_devices; } - dev0->priv.flags &= ~MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; - mlx5_rescan_drivers_locked(dev0); - err = mlx5_lag_reload_ib_reps(ldev, 0, false); + mlx5_lag_rescan_dev_locked(ldev, dev0, true); + err = mlx5_lag_reload_ib_reps_from_locked(ldev, 0, false); if (err) goto err_rescan_drivers; @@ -111,12 +110,11 @@ static int mlx5_lag_enable_mpesw(struct mlx5_lag *ldev) return 0; err_rescan_drivers: - dev0->priv.flags |= MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; - mlx5_rescan_drivers_locked(dev0); + mlx5_lag_rescan_dev_locked(ldev, dev0, false); mlx5_deactivate_lag(ldev); err_add_devices: mlx5_lag_add_devices(ldev); - mlx5_lag_reload_ib_reps(ldev, 0, true); + mlx5_lag_reload_ib_reps_from_locked(ldev, 0, true); mlx5_mpesw_metadata_cleanup(ldev); return err; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c index 4b5ac2db55ce5..d40c53193ea80 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c @@ -3,6 +3,7 @@ #include #include +#include #include "lib/devcom.h" #include "lib/mlx5.h" #include "mlx5_core.h" @@ -438,3 +439,10 @@ int mlx5_devcom_comp_trylock(struct mlx5_devcom_comp_dev *devcom) return 0; return down_write_trylock(&devcom->comp->sem); } + +void mlx5_devcom_comp_assert_locked(struct mlx5_devcom_comp_dev *devcom) +{ + if (!devcom) + return; + lockdep_assert_held_write(&devcom->comp->sem); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.h index 91e5ae529d5c6..316052a85ca5c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.h @@ -75,5 +75,6 @@ void *mlx5_devcom_get_next_peer_data_rcu(struct mlx5_devcom_comp_dev *devcom, void mlx5_devcom_comp_lock(struct mlx5_devcom_comp_dev *devcom); void mlx5_devcom_comp_unlock(struct mlx5_devcom_comp_dev *devcom); int mlx5_devcom_comp_trylock(struct mlx5_devcom_comp_dev *devcom); +void mlx5_devcom_comp_assert_locked(struct mlx5_devcom_comp_dev *devcom); #endif /* __LIB_MLX5_DEVCOM_H__ */ From 3b8b364f97f40420498e3519adea155096cc4f65 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Sun, 31 May 2026 14:39:41 +0300 Subject: [PATCH 2109/2758] net/mlx5: LAG, factor out shared FDB code into dedicated file [ Upstream commit cf6c4c0508a9a3858203b726bd3a3f9557449f67 ] Refactor shared FDB LAG logic into a new lag/shared_fdb.c file to improve code organization and enable reuse. Move shared FDB specific functions from lag.c and introduce consolidated APIs: - mlx5_lag_shared_fdb_create() handles LAG activation with shared FDB - mlx5_lag_shared_fdb_destroy() handles LAG deactivation with shared FDB Update mlx5_do_bond(), mlx5_disable_lag() and mpesw.c to use the new APIs, which simplifies the shared FDB code paths. Signed-off-by: Shay Drory Reviewed-by: Mark Bloch Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260531113954.395443-2-tariqt@nvidia.com Signed-off-by: Jakub Kicinski Stable-dep-of: 0f0e4ae6975c ("net/mlx5: LAG, Fix off-by-one in single-FDB error rollback") Signed-off-by: Sasha Levin --- .../net/ethernet/mellanox/mlx5/core/Makefile | 2 +- .../net/ethernet/mellanox/mlx5/core/lag/lag.c | 156 ++++-------------- .../net/ethernet/mellanox/mlx5/core/lag/lag.h | 26 +++ .../ethernet/mellanox/mlx5/core/lag/mpesw.c | 25 +-- .../mellanox/mlx5/core/lag/shared_fdb.c | 143 ++++++++++++++++ 5 files changed, 210 insertions(+), 142 deletions(-) create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile index d39fe9c4a87cd..19e50f0d55af3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile +++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile @@ -41,7 +41,7 @@ mlx5_core-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o en/port_buffer.o mlx5_core-$(CONFIG_PCI_HYPERV_INTERFACE) += en/hv_vhca_stats.o mlx5_core-$(CONFIG_MLX5_ESWITCH) += lag/mp.o lag/port_sel.o lib/geneve.o lib/port_tun.o \ en_rep.o en/rep/bond.o en/mod_hdr.o \ - en/mapping.o lag/mpesw.o + en/mapping.o lag/mpesw.o lag/shared_fdb.o mlx5_core-$(CONFIG_MLX5_CLS_ACT) += en_tc.o en/rep/tc.o en/rep/neigh.o \ lib/fs_chains.o en/tc_tun.o \ esw/indir_table.o en/tc_tun_encap.o \ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c index 22b7efea34b83..5dfdd799828f1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c @@ -817,43 +817,6 @@ char *mlx5_get_str_port_sel_mode(enum mlx5_lag_mode mode, unsigned long flags) } } -static int mlx5_lag_create_single_fdb(struct mlx5_lag *ldev) -{ - int master_idx = mlx5_lag_get_dev_index_by_seq(ldev, MLX5_LAG_P1); - struct mlx5_eswitch *master_esw; - struct mlx5_core_dev *dev0; - int i, j; - int err; - - if (master_idx < 0) - return -EINVAL; - - dev0 = mlx5_lag_pf(ldev, master_idx)->dev; - master_esw = dev0->priv.eswitch; - mlx5_ldev_for_each(i, 0, ldev) { - struct mlx5_eswitch *slave_esw; - - if (i == master_idx) - continue; - - slave_esw = mlx5_lag_pf(ldev, i)->dev->priv.eswitch; - - err = mlx5_eswitch_offloads_single_fdb_add_one(master_esw, - slave_esw, ldev->ports); - if (err) - goto err; - } - return 0; -err: - mlx5_ldev_for_each_reverse(j, i, 0, ldev) { - if (j == master_idx) - continue; - mlx5_eswitch_offloads_single_fdb_del_one(master_esw, - mlx5_lag_pf(ldev, j)->dev->priv.eswitch); - } - return err; -} - static int mlx5_create_lag(struct mlx5_lag *ldev, struct lag_tracker *tracker, enum mlx5_lag_mode mode, @@ -1218,12 +1181,15 @@ void mlx5_disable_lag(struct mlx5_lag *ldev) if (idx < 0) return; + if (shared_fdb) { + mlx5_lag_shared_fdb_destroy(ldev); + return; + } + dev0 = mlx5_lag_pf(ldev, idx)->dev; roce_lag = __mlx5_lag_is_roce(ldev); - if (shared_fdb) { - mlx5_lag_remove_devices(ldev); - } else if (roce_lag) { + if (roce_lag) { mlx5_lag_rescan_dev_locked(ldev, dev0, false); mlx5_ldev_for_each(i, 0, ldev) { if (i == idx) @@ -1236,49 +1202,8 @@ void mlx5_disable_lag(struct mlx5_lag *ldev) if (err) return; - if (shared_fdb || roce_lag) + if (roce_lag) mlx5_lag_add_devices(ldev); - - if (shared_fdb) - mlx5_lag_reload_ib_reps_from_locked(ldev, - MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV, - true); -} - -bool mlx5_lag_shared_fdb_supported(struct mlx5_lag *ldev) -{ - struct mlx5_core_dev *dev; - bool ret = false; - int idx; - int i; - - idx = mlx5_lag_get_dev_index_by_seq(ldev, MLX5_LAG_P1); - if (idx < 0) - return false; - - mlx5_ldev_for_each(i, 0, ldev) { - if (i == idx) - continue; - dev = mlx5_lag_pf(ldev, i)->dev; - if (is_mdev_switchdev_mode(dev) && - mlx5_eswitch_vport_match_metadata_enabled(dev->priv.eswitch) && - MLX5_CAP_GEN(dev, lag_native_fdb_selection) && - MLX5_CAP_ESW(dev, root_ft_on_other_esw) && - mlx5_eswitch_get_npeers(dev->priv.eswitch) == - MLX5_CAP_GEN(dev, num_lag_ports) - 1) - continue; - return false; - } - - dev = mlx5_lag_pf(ldev, idx)->dev; - if (is_mdev_switchdev_mode(dev) && - mlx5_eswitch_vport_match_metadata_enabled(dev->priv.eswitch) && - mlx5_esw_offloads_devcom_is_ready(dev->priv.eswitch) && - MLX5_CAP_ESW(dev, esw_shared_ingress_acl) && - mlx5_eswitch_get_npeers(dev->priv.eswitch) == MLX5_CAP_GEN(dev, num_lag_ports) - 1) - ret = true; - - return ret; } static bool mlx5_lag_is_roce_lag(struct mlx5_lag *ldev) @@ -1493,47 +1418,37 @@ static void mlx5_do_bond(struct mlx5_lag *ldev) roce_lag = mlx5_lag_is_roce_lag(ldev); - if (shared_fdb || roce_lag) - mlx5_lag_remove_devices(ldev); - - err = mlx5_activate_lag(ldev, &tracker, - roce_lag ? MLX5_LAG_MODE_ROCE : - MLX5_LAG_MODE_SRIOV, - shared_fdb); - if (err) { - if (shared_fdb || roce_lag) - mlx5_lag_add_devices(ldev); - if (shared_fdb) - mlx5_lag_reload_ib_reps_from_locked(ldev, 0, - true); - - return; - } + if (shared_fdb) { + err = mlx5_lag_shared_fdb_create(ldev, &tracker, + MLX5_LAG_MODE_SRIOV); + if (err) + return; + } else { + if (roce_lag) + mlx5_lag_remove_devices(ldev); - if (roce_lag) { - struct mlx5_core_dev *dev; - - mlx5_lag_rescan_dev_locked(ldev, dev0, true); - mlx5_ldev_for_each(i, 0, ldev) { - if (i == idx) - continue; - dev = mlx5_lag_pf(ldev, i)->dev; - if (mlx5_get_roce_state(dev)) - mlx5_nic_vport_enable_roce(dev); - } - } else if (shared_fdb) { - mlx5_lag_rescan_dev_locked(ldev, dev0, true); - err = mlx5_lag_reload_ib_reps_from_locked(ldev, 0, - false); + err = mlx5_activate_lag(ldev, &tracker, + roce_lag ? MLX5_LAG_MODE_ROCE : + MLX5_LAG_MODE_SRIOV, + false); if (err) { - mlx5_lag_rescan_dev_locked(ldev, dev0, false); - mlx5_deactivate_lag(ldev); - mlx5_lag_add_devices(ldev); - mlx5_lag_reload_ib_reps_from_locked(ldev, 0, - true); - mlx5_core_err(dev0, "Failed to enable lag\n"); + if (roce_lag) + mlx5_lag_add_devices(ldev); return; } + + if (roce_lag) { + struct mlx5_core_dev *dev; + + mlx5_lag_rescan_dev_locked(ldev, dev0, true); + mlx5_ldev_for_each(i, 0, ldev) { + if (i == idx) + continue; + dev = mlx5_lag_pf(ldev, i)->dev; + if (mlx5_get_roce_state(dev)) + mlx5_nic_vport_enable_roce(dev); + } + } } if (tracker.tx_type == NETDEV_LAG_TX_TYPE_ACTIVEBACKUP) { ndev = mlx5_lag_active_backup_get_netdev(dev0); @@ -1545,7 +1460,8 @@ static void mlx5_do_bond(struct mlx5_lag *ldev) ndev); dev_put(ndev); } - mlx5_lag_set_vports_agg_speed(ldev); + if (!shared_fdb) + mlx5_lag_set_vports_agg_speed(ldev); } else if (mlx5_lag_should_modify_lag(ldev, do_bond)) { mlx5_modify_lag(ldev, &tracker); mlx5_lag_set_vports_agg_speed(ldev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h index 6afe7707d076e..23c0457ce7993 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h @@ -137,7 +137,33 @@ mlx5_lag_is_ready(struct mlx5_lag *ldev) return test_bit(MLX5_LAG_FLAG_NDEVS_READY, &ldev->state_flags); } +#ifdef CONFIG_MLX5_ESWITCH +int mlx5_lag_shared_fdb_create(struct mlx5_lag *ldev, + struct lag_tracker *tracker, + enum mlx5_lag_mode mode); +void mlx5_lag_shared_fdb_destroy(struct mlx5_lag *ldev); +int mlx5_lag_create_single_fdb(struct mlx5_lag *ldev); bool mlx5_lag_shared_fdb_supported(struct mlx5_lag *ldev); +#else +static inline int mlx5_lag_shared_fdb_create(struct mlx5_lag *ldev, + struct lag_tracker *tracker, + enum mlx5_lag_mode mode) +{ + return -EOPNOTSUPP; +} + +static inline void mlx5_lag_shared_fdb_destroy(struct mlx5_lag *ldev) {} + +static inline int mlx5_lag_create_single_fdb(struct mlx5_lag *ldev) +{ + return -EOPNOTSUPP; +} + +static inline bool mlx5_lag_shared_fdb_supported(struct mlx5_lag *ldev) +{ + return false; +} +#endif bool mlx5_lag_check_prereq(struct mlx5_lag *ldev); int mlx5_lag_demux_init(struct mlx5_core_dev *dev, struct mlx5_flow_table_attr *ft_attr); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c index 8a349f8fd8233..64e2d1dd53087 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c @@ -92,38 +92,21 @@ static int mlx5_lag_enable_mpesw(struct mlx5_lag *ldev) if (err) return err; - mlx5_lag_remove_devices(ldev); - - err = mlx5_activate_lag(ldev, NULL, MLX5_LAG_MODE_MPESW, true); + err = mlx5_lag_shared_fdb_create(ldev, NULL, MLX5_LAG_MODE_MPESW); if (err) { mlx5_core_warn(dev0, "Failed to create LAG in MPESW mode (%d)\n", err); - goto err_add_devices; + mlx5_mpesw_metadata_cleanup(ldev); + return err; } - mlx5_lag_rescan_dev_locked(ldev, dev0, true); - err = mlx5_lag_reload_ib_reps_from_locked(ldev, 0, false); - if (err) - goto err_rescan_drivers; - - mlx5_lag_set_vports_agg_speed(ldev); - return 0; - -err_rescan_drivers: - mlx5_lag_rescan_dev_locked(ldev, dev0, false); - mlx5_deactivate_lag(ldev); -err_add_devices: - mlx5_lag_add_devices(ldev); - mlx5_lag_reload_ib_reps_from_locked(ldev, 0, true); - mlx5_mpesw_metadata_cleanup(ldev); - return err; } void mlx5_lag_disable_mpesw(struct mlx5_lag *ldev) { if (ldev->mode == MLX5_LAG_MODE_MPESW) { mlx5_mpesw_metadata_cleanup(ldev); - mlx5_disable_lag(ldev); + mlx5_lag_shared_fdb_destroy(ldev); } } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c new file mode 100644 index 0000000000000..e5b8e9f1e6fde --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ + +#include +#include +#include +#include "mlx5_core.h" +#include "lag.h" +#include "eswitch.h" + +bool mlx5_lag_shared_fdb_supported(struct mlx5_lag *ldev) +{ + struct mlx5_core_dev *dev; + bool ret = false; + int idx; + int i; + + idx = mlx5_lag_get_dev_index_by_seq(ldev, MLX5_LAG_P1); + if (idx < 0) + return false; + + mlx5_ldev_for_each(i, 0, ldev) { + if (i == idx) + continue; + dev = mlx5_lag_pf(ldev, i)->dev; + if (is_mdev_switchdev_mode(dev) && + mlx5_eswitch_vport_match_metadata_enabled(dev->priv.eswitch) && + MLX5_CAP_GEN(dev, lag_native_fdb_selection) && + MLX5_CAP_ESW(dev, root_ft_on_other_esw) && + mlx5_eswitch_get_npeers(dev->priv.eswitch) == + MLX5_CAP_GEN(dev, num_lag_ports) - 1) + continue; + return false; + } + + dev = mlx5_lag_pf(ldev, idx)->dev; + if (is_mdev_switchdev_mode(dev) && + mlx5_eswitch_vport_match_metadata_enabled(dev->priv.eswitch) && + mlx5_esw_offloads_devcom_is_ready(dev->priv.eswitch) && + MLX5_CAP_ESW(dev, esw_shared_ingress_acl) && + mlx5_eswitch_get_npeers(dev->priv.eswitch) == + MLX5_CAP_GEN(dev, num_lag_ports) - 1) + ret = true; + + return ret; +} + +int mlx5_lag_create_single_fdb(struct mlx5_lag *ldev) +{ + int master_idx = mlx5_lag_get_dev_index_by_seq(ldev, MLX5_LAG_P1); + struct mlx5_eswitch *master_esw; + struct mlx5_core_dev *dev0; + int i, j; + int err; + + if (master_idx < 0) + return -EINVAL; + + dev0 = mlx5_lag_pf(ldev, master_idx)->dev; + master_esw = dev0->priv.eswitch; + mlx5_ldev_for_each(i, 0, ldev) { + struct mlx5_eswitch *slave_esw; + + if (i == master_idx) + continue; + + slave_esw = mlx5_lag_pf(ldev, i)->dev->priv.eswitch; + + err = mlx5_eswitch_offloads_single_fdb_add_one(master_esw, + slave_esw, + ldev->ports); + if (err) + goto err; + } + return 0; +err: + mlx5_ldev_for_each_reverse(j, i, 0, ldev) { + struct mlx5_eswitch *slave_esw; + + if (j == master_idx) + continue; + slave_esw = mlx5_lag_pf(ldev, j)->dev->priv.eswitch; + mlx5_eswitch_offloads_single_fdb_del_one(master_esw, slave_esw); + } + return err; +} + +int mlx5_lag_shared_fdb_create(struct mlx5_lag *ldev, + struct lag_tracker *tracker, + enum mlx5_lag_mode mode) +{ + int idx = mlx5_lag_get_dev_index_by_seq(ldev, MLX5_LAG_P1); + struct mlx5_core_dev *dev0; + int err; + + if (idx < 0) + return -EINVAL; + + dev0 = mlx5_lag_pf(ldev, idx)->dev; + + mlx5_lag_remove_devices(ldev); + + err = mlx5_activate_lag(ldev, tracker, mode, true); + if (err) { + mlx5_core_warn(dev0, "Failed to create LAG in shared FDB mode (%d)\n", + err); + goto err_add_devices; + } + + mlx5_lag_rescan_dev_locked(ldev, dev0, true); + err = mlx5_lag_reload_ib_reps_from_locked(ldev, 0, false); + if (err) { + mlx5_core_err(dev0, "Failed to enable lag\n"); + goto err_rescan_drivers; + } + + mlx5_lag_set_vports_agg_speed(ldev); + return 0; + +err_rescan_drivers: + mlx5_lag_rescan_dev_locked(ldev, dev0, false); + mlx5_deactivate_lag(ldev); +err_add_devices: + mlx5_lag_add_devices(ldev); + mlx5_lag_reload_ib_reps_from_locked(ldev, 0, true); + return err; +} + +void mlx5_lag_shared_fdb_destroy(struct mlx5_lag *ldev) +{ + int err; + + mlx5_lag_remove_devices(ldev); + + err = mlx5_deactivate_lag(ldev); + if (err) + return; + + mlx5_lag_add_devices(ldev); + mlx5_lag_reload_ib_reps_from_locked(ldev, + MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV, + true); +} From 5092213b9a31ecfb64e6534fe74137e646c6779b Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Sun, 31 May 2026 14:39:44 +0300 Subject: [PATCH 2110/2758] net/mlx5: LAG, replace peer count check with direct peer lookup [ Upstream commit 2ca494dad9676301ecde1c9e214bfbf83d4c6326 ] Replace mlx5_eswitch_get_npeers() count-based check with a new mlx5_eswitch_is_peer() function that directly verifies the peer relationship between two eswitches. This change prepares for SD LAG support, which is a virtual LAG that does not have num_lag_ports capability and cannot use the count-based peer validation. Signed-off-by: Shay Drory Reviewed-by: Mark Bloch Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260531113954.395443-5-tariqt@nvidia.com Signed-off-by: Jakub Kicinski Stable-dep-of: 0f0e4ae6975c ("net/mlx5: LAG, Fix off-by-one in single-FDB error rollback") Signed-off-by: Sasha Levin --- .../net/ethernet/mellanox/mlx5/core/eswitch.h | 11 ++--------- .../mellanox/mlx5/core/eswitch_offloads.c | 12 ++++++++++++ .../mellanox/mlx5/core/lag/shared_fdb.c | 17 +++++++---------- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index 0b107c2ddbfa6..f248c66caa848 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -899,6 +899,8 @@ int mlx5_eswitch_offloads_single_fdb_add_one(struct mlx5_eswitch *master_esw, void mlx5_eswitch_offloads_single_fdb_del_one(struct mlx5_eswitch *master_esw, struct mlx5_eswitch *slave_esw); int mlx5_eswitch_reload_ib_reps(struct mlx5_eswitch *esw); +bool mlx5_eswitch_is_peer(struct mlx5_eswitch *esw, + struct mlx5_eswitch *peer_esw); bool mlx5_eswitch_block_encap(struct mlx5_core_dev *dev, bool from_fdb); void mlx5_eswitch_unblock_encap(struct mlx5_core_dev *dev); @@ -914,13 +916,6 @@ static inline int mlx5_eswitch_num_vfs(struct mlx5_eswitch *esw) return 0; } -static inline int mlx5_eswitch_get_npeers(struct mlx5_eswitch *esw) -{ - if (mlx5_esw_allowed(esw)) - return esw->num_peers; - return 0; -} - static inline struct mlx5_flow_table * mlx5_eswitch_get_slow_fdb(struct mlx5_eswitch *esw) { @@ -996,8 +991,6 @@ static inline void mlx5_eswitch_offloads_single_fdb_del_one(struct mlx5_eswitch *master_esw, struct mlx5_eswitch *slave_esw) {} -static inline int mlx5_eswitch_get_npeers(struct mlx5_eswitch *esw) { return 0; } - static inline int mlx5_eswitch_reload_ib_reps(struct mlx5_eswitch *esw) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 41d65f2bf87dd..fbd59225b8637 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -3237,6 +3237,18 @@ static int mlx5_esw_offloads_set_ns_peer(struct mlx5_eswitch *esw, return 0; } +bool mlx5_eswitch_is_peer(struct mlx5_eswitch *esw, + struct mlx5_eswitch *peer_esw) +{ + u16 peer_esw_i; + + if (!mlx5_esw_allowed(esw) || !mlx5_esw_allowed(peer_esw)) + return false; + + peer_esw_i = MLX5_CAP_GEN(peer_esw->dev, vhca_id); + return !!xa_load(&esw->paired, peer_esw_i); +} + static int mlx5_esw_offloads_devcom_event(int event, void *my_data, void *event_data) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c index e5b8e9f1e6fde..b5cbe34097203 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c @@ -10,7 +10,7 @@ bool mlx5_lag_shared_fdb_supported(struct mlx5_lag *ldev) { - struct mlx5_core_dev *dev; + struct mlx5_core_dev *dev0, *dev; bool ret = false; int idx; int i; @@ -19,6 +19,7 @@ bool mlx5_lag_shared_fdb_supported(struct mlx5_lag *ldev) if (idx < 0) return false; + dev0 = mlx5_lag_pf(ldev, idx)->dev; mlx5_ldev_for_each(i, 0, ldev) { if (i == idx) continue; @@ -27,19 +28,15 @@ bool mlx5_lag_shared_fdb_supported(struct mlx5_lag *ldev) mlx5_eswitch_vport_match_metadata_enabled(dev->priv.eswitch) && MLX5_CAP_GEN(dev, lag_native_fdb_selection) && MLX5_CAP_ESW(dev, root_ft_on_other_esw) && - mlx5_eswitch_get_npeers(dev->priv.eswitch) == - MLX5_CAP_GEN(dev, num_lag_ports) - 1) + mlx5_eswitch_is_peer(dev0->priv.eswitch, dev->priv.eswitch)) continue; return false; } - dev = mlx5_lag_pf(ldev, idx)->dev; - if (is_mdev_switchdev_mode(dev) && - mlx5_eswitch_vport_match_metadata_enabled(dev->priv.eswitch) && - mlx5_esw_offloads_devcom_is_ready(dev->priv.eswitch) && - MLX5_CAP_ESW(dev, esw_shared_ingress_acl) && - mlx5_eswitch_get_npeers(dev->priv.eswitch) == - MLX5_CAP_GEN(dev, num_lag_ports) - 1) + if (is_mdev_switchdev_mode(dev0) && + mlx5_eswitch_vport_match_metadata_enabled(dev0->priv.eswitch) && + mlx5_esw_offloads_devcom_is_ready(dev0->priv.eswitch) && + MLX5_CAP_ESW(dev0, esw_shared_ingress_acl)) ret = true; return ret; From d14f2dbf727c004f120c04204b0c60e7cf23ebd4 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Sun, 31 May 2026 14:39:45 +0300 Subject: [PATCH 2111/2758] net/mlx5: LAG, prepare for SD device integration [ Upstream commit 14a47f55c8def9d677a4307bdce88b40255e8505 ] Socket Direct (SD) secondaries devices will participate in LAG, even though they are silent. SD secondary devices share the same physical port as their primary but are separate PCI functions that need to be tracked alongside regular LAG ports. Extend lag_func with a group_id field to identify SD group membership and introduce a unified iterator that can filter by group. Add APIs for registering SD secondary devices in an existing LAG. Signed-off-by: Shay Drory Reviewed-by: Mark Bloch Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260531113954.395443-6-tariqt@nvidia.com Signed-off-by: Jakub Kicinski Stable-dep-of: 0f0e4ae6975c ("net/mlx5: LAG, Fix off-by-one in single-FDB error rollback") Signed-off-by: Sasha Levin --- .../net/ethernet/mellanox/mlx5/core/lag/lag.c | 59 ++++++++++++++----- .../net/ethernet/mellanox/mlx5/core/lag/lag.h | 53 +++++++++++++++-- 2 files changed, 90 insertions(+), 22 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c index 5dfdd799828f1..03cb02c7000d9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c @@ -242,7 +242,7 @@ static void mlx5_ldev_free(struct kref *ref) unregister_netdevice_notifier_net(net, &ldev->nb); } - mlx5_ldev_for_each(i, 0, ldev) { + mlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) { pf = mlx5_lag_pf(ldev, i); if (pf->port_change_nb.nb.notifier_call) { struct mlx5_nb *nb = &pf->port_change_nb; @@ -391,7 +391,7 @@ int mlx5_lag_get_dev_seq(struct mlx5_core_dev *dev) if (pf && pf->dev == dev) return 0; - mlx5_ldev_for_each(i, 0, ldev) { + mlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) { if (i == master_idx) continue; pf = mlx5_lag_pf(ldev, i); @@ -1034,7 +1034,7 @@ static void mlx5_lag_assert_locked_transition(struct mlx5_lag *ldev) lockdep_assert_held(&ldev->lock); - i = mlx5_get_next_ldev_func(ldev, 0); + i = mlx5_get_next_lag_func(ldev, 0, MLX5_LAG_FILTER_PORTS); if (i < MLX5_MAX_PORTS) { pf = mlx5_lag_pf(ldev, i); devcom = pf->dev->priv.hca_devcom_comp; @@ -1482,7 +1482,7 @@ struct mlx5_devcom_comp_dev *mlx5_lag_get_devcom_comp(struct mlx5_lag *ldev) int i; mutex_lock(&ldev->lock); - i = mlx5_get_next_ldev_func(ldev, 0); + i = mlx5_get_next_lag_func(ldev, 0, MLX5_LAG_FILTER_PORTS); if (i < MLX5_MAX_PORTS) { pf = mlx5_lag_pf(ldev, i); devcom = pf->dev->priv.hca_devcom_comp; @@ -1965,8 +1965,9 @@ static void mlx5_ldev_remove_netdev(struct mlx5_lag *ldev, spin_unlock_irqrestore(&lag_lock, flags); } -static int mlx5_ldev_add_mdev(struct mlx5_lag *ldev, - struct mlx5_core_dev *dev) +int mlx5_ldev_add_mdev(struct mlx5_lag *ldev, + struct mlx5_core_dev *dev, + u32 group_id) { struct lag_func *pf; u32 idx; @@ -1985,8 +1986,14 @@ static int mlx5_ldev_add_mdev(struct mlx5_lag *ldev, pf->idx = idx; pf->dev = dev; + pf->group_id = group_id; dev->priv.lag = ldev; + if (group_id) + return 0; + + xa_set_mark(&ldev->pfs, idx, MLX5_LAG_XA_MARK_PORT); + MLX5_NB_INIT(&pf->port_change_nb, mlx5_lag_mpesw_port_change_event, PORT_CHANGE); mlx5_eq_notifier_register(dev, &pf->port_change_nb); @@ -1994,13 +2001,13 @@ static int mlx5_ldev_add_mdev(struct mlx5_lag *ldev, return 0; } -static void mlx5_ldev_remove_mdev(struct mlx5_lag *ldev, - struct mlx5_core_dev *dev) +void mlx5_ldev_remove_mdev(struct mlx5_lag *ldev, + struct mlx5_core_dev *dev) { struct lag_func *pf; int i; - mlx5_ldev_for_each(i, 0, ldev) { + mlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) { pf = mlx5_lag_pf(ldev, i); if (pf->dev == dev) break; @@ -2035,7 +2042,7 @@ static int __mlx5_lag_dev_add_mdev(struct mlx5_core_dev *dev) mlx5_core_err(dev, "Failed to alloc lag dev\n"); return 0; } - err = mlx5_ldev_add_mdev(ldev, dev); + err = mlx5_ldev_add_mdev(ldev, dev, 0); if (err) { mlx5_core_err(dev, "Failed to add mdev to lag dev\n"); mlx5_ldev_put(ldev); @@ -2050,7 +2057,7 @@ static int __mlx5_lag_dev_add_mdev(struct mlx5_core_dev *dev) return -EAGAIN; } mlx5_ldev_get(ldev); - err = mlx5_ldev_add_mdev(ldev, dev); + err = mlx5_ldev_add_mdev(ldev, dev, 0); if (err) { mlx5_ldev_put(ldev); mutex_unlock(&ldev->lock); @@ -2187,27 +2194,47 @@ void mlx5_lag_add_netdev(struct mlx5_core_dev *dev, mlx5_queue_bond_work(ldev, 0); } -int mlx5_get_pre_ldev_func(struct mlx5_lag *ldev, int start_idx, int end_idx) +int mlx5_get_pre_lag_func(struct mlx5_lag *ldev, int start_idx, int end_idx, + u32 filter) { struct lag_func *pf; int i; for (i = start_idx; i >= end_idx; i--) { pf = xa_load(&ldev->pfs, i); - if (pf && pf->dev) + if (!pf || !pf->dev) + continue; + if (filter == MLX5_LAG_FILTER_PORTS) { + if (xa_get_mark(&ldev->pfs, i, MLX5_LAG_XA_MARK_PORT)) + return i; + } else if (filter == MLX5_LAG_FILTER_ALL || + filter == pf->group_id) { return i; + } } return -1; } -int mlx5_get_next_ldev_func(struct mlx5_lag *ldev, int start_idx) +int mlx5_get_next_lag_func(struct mlx5_lag *ldev, int start_idx, u32 filter) { struct lag_func *pf; unsigned long idx; - xa_for_each_start(&ldev->pfs, idx, pf, start_idx) - if (pf->dev) + if (filter == MLX5_LAG_FILTER_PORTS) { + xa_for_each_marked_start(&ldev->pfs, idx, pf, + MLX5_LAG_XA_MARK_PORT, start_idx) + if (pf->dev) + return idx; + return MLX5_MAX_PORTS; + } + + xa_for_each_start(&ldev->pfs, idx, pf, start_idx) { + if (!pf->dev) + continue; + if (filter == MLX5_LAG_FILTER_ALL || + filter == pf->group_id) return idx; + } return MLX5_MAX_PORTS; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h index 23c0457ce7993..9e95d5109e4a6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h @@ -15,6 +15,13 @@ * Note: XA_MARK_0 is reserved by XA_FLAGS_ALLOC for free-slot tracking. */ #define MLX5_LAG_XA_MARK_MASTER XA_MARK_1 +/* XArray mark for port-level entries (excludes SD secondaries) */ +#define MLX5_LAG_XA_MARK_PORT XA_MARK_2 + +/* Like xa_for_each_marked but starting from a given index */ +#define xa_for_each_marked_start(xa, index, entry, filter, start) \ + for (index = start, entry = xa_find(xa, &index, ULONG_MAX, filter); \ + entry; entry = xa_find_after(xa, &index, ULONG_MAX, filter)) #include "mlx5_core.h" #include "mp.h" @@ -50,6 +57,8 @@ struct lag_func { bool has_drop; unsigned int idx; /* xarray index assigned by LAG */ struct mlx5_nb port_change_nb; + u32 group_id; /* SD group ID, 0 = not SD */ + bool sd_fdb_active; /* set on all SD group members */ }; /* Used for collection of netdev event info. */ @@ -125,6 +134,20 @@ mlx5_lag_pf_by_dev_idx(struct mlx5_lag *ldev, int dev_idx) return NULL; } +/* Find lag_func by mlx5_core_dev pointer */ +static inline struct lag_func * +mlx5_lag_pf_by_dev(struct mlx5_lag *ldev, struct mlx5_core_dev *dev) +{ + struct lag_func *pf; + unsigned long idx; + + xa_for_each(&ldev->pfs, idx, pf) { + if (pf->dev == dev) + return pf; + } + return NULL; +} + static inline bool __mlx5_lag_is_active(struct mlx5_lag *ldev) { @@ -214,20 +237,38 @@ static inline bool mlx5_lag_is_supported(struct mlx5_core_dev *dev) return true; } -#define mlx5_ldev_for_each(i, start_index, ldev) \ - for (int tmp = start_index; tmp = mlx5_get_next_ldev_func(ldev, tmp), \ +/* Iterator filter constants for mlx5_lag_for_each() */ +#define MLX5_LAG_FILTER_ALL 0 /* iterate ALL devices */ +#define MLX5_LAG_FILTER_PORTS U32_MAX /* iterate ports only (XA_MARK_PORT) */ +/* any other value = iterate devices with that specific group_id */ + +#define mlx5_lag_for_each(i, start_index, ldev, filter) \ + for (int tmp = start_index; \ + tmp = mlx5_get_next_lag_func(ldev, tmp, filter), \ i = tmp, tmp < MLX5_MAX_PORTS; tmp++) -#define mlx5_ldev_for_each_reverse(i, start_index, end_index, ldev) \ +#define mlx5_lag_for_each_reverse(i, start_index, end_index, ldev, filter) \ for (int tmp = start_index, tmp1 = end_index; \ - tmp = mlx5_get_pre_ldev_func(ldev, tmp, tmp1), \ + tmp = mlx5_get_pre_lag_func(ldev, tmp, tmp1, filter), \ i = tmp, tmp >= tmp1; tmp--) -int mlx5_get_pre_ldev_func(struct mlx5_lag *ldev, int start_idx, int end_idx); -int mlx5_get_next_ldev_func(struct mlx5_lag *ldev, int start_idx); +/* Convenience wrappers - keeps existing behavior */ +#define mlx5_ldev_for_each(i, start_index, ldev) \ + mlx5_lag_for_each(i, start_index, ldev, MLX5_LAG_FILTER_PORTS) + +#define mlx5_ldev_for_each_reverse(i, start_index, end_index, ldev) \ + mlx5_lag_for_each_reverse(i, start_index, end_index, ldev, \ + MLX5_LAG_FILTER_PORTS) + +int mlx5_get_pre_lag_func(struct mlx5_lag *ldev, int start_idx, int end_idx, + u32 filter); +int mlx5_get_next_lag_func(struct mlx5_lag *ldev, int start_idx, u32 filter); int mlx5_lag_get_dev_index_by_seq(struct mlx5_lag *ldev, int seq); int mlx5_lag_num_devs(struct mlx5_lag *ldev); int mlx5_lag_num_netdevs(struct mlx5_lag *ldev); int mlx5_lag_reload_ib_reps_from_locked(struct mlx5_lag *ldev, u32 flags, bool cont_on_fail); +int mlx5_ldev_add_mdev(struct mlx5_lag *ldev, struct mlx5_core_dev *dev, + u32 group_id); +void mlx5_ldev_remove_mdev(struct mlx5_lag *ldev, struct mlx5_core_dev *dev); #endif /* __MLX5_LAG_H__ */ From fd2ef924a56fb70fef26a6f0271a086787125c97 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Sun, 31 May 2026 14:39:46 +0300 Subject: [PATCH 2112/2758] net/mlx5: LAG, extend shared FDB API with group_id filter [ Upstream commit 3cbce590b7f2e9f056ed803a05f9ee09ac4a49a7 ] Add a group_id parameter to mlx5_lag_shared_fdb_create() and mlx5_lag_shared_fdb_destroy() to scope shared FDB operations to a specific SD group. When group_id is U32_MAX, the functions operate on all LAG devices. When group_id is non-zero, they operate only on devices in that SD group without issuing FW LAG commands, since SD LAG is a pure software construct. Signed-off-by: Shay Drory Reviewed-by: Mark Bloch Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260531113954.395443-7-tariqt@nvidia.com Signed-off-by: Jakub Kicinski Stable-dep-of: 0f0e4ae6975c ("net/mlx5: LAG, Fix off-by-one in single-FDB error rollback") Signed-off-by: Sasha Levin --- .../net/ethernet/mellanox/mlx5/core/lag/lag.c | 195 ++++++++++++++---- .../net/ethernet/mellanox/mlx5/core/lag/lag.h | 32 ++- .../ethernet/mellanox/mlx5/core/lag/mpesw.c | 7 +- .../mellanox/mlx5/core/lag/shared_fdb.c | 153 +++++++++++--- .../net/ethernet/mellanox/mlx5/core/lib/sd.c | 10 + .../net/ethernet/mellanox/mlx5/core/lib/sd.h | 10 + 6 files changed, 324 insertions(+), 83 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c index 03cb02c7000d9..3decb49e9f197 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c @@ -370,6 +370,22 @@ int mlx5_lag_get_dev_index_by_seq(struct mlx5_lag *ldev, int seq) return -ENOENT; } +/* Return the appropriate iterator filter for a device in LAG: + * - SD shared FDB active: iterate only the device's SD group + * - SD group exists but shared FDB not active: iterate all devices + * - No SD: iterate ports only + */ +static u32 mlx5_lag_get_filter(struct mlx5_lag *ldev, struct mlx5_core_dev *dev) +{ + struct lag_func *pf = mlx5_lag_pf_by_dev(ldev, dev); + + if (pf && pf->sd_fdb_active) + return pf->group_id; + if (pf && pf->group_id) + return MLX5_LAG_FILTER_ALL; + return MLX5_LAG_FILTER_PORTS; +} + /* Reverse of mlx5_lag_get_dev_index_by_seq: given a device, return its * sequence number in the LAG. Master is always 0, others numbered * sequentially starting from 1. @@ -379,11 +395,13 @@ int mlx5_lag_get_dev_seq(struct mlx5_core_dev *dev) struct mlx5_lag *ldev = mlx5_lag_dev(dev); int master_idx, i, num = 1; struct lag_func *pf; + u32 filter; if (!ldev) return -ENOENT; - master_idx = mlx5_lag_get_master_idx(ldev); + filter = mlx5_lag_get_filter(ldev, dev); + master_idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, 0, filter); if (master_idx < 0) return -ENOENT; @@ -391,7 +409,7 @@ int mlx5_lag_get_dev_seq(struct mlx5_core_dev *dev) if (pf && pf->dev == dev) return 0; - mlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) { + mlx5_lag_for_each(i, 0, ldev, filter) { if (i == master_idx) continue; pf = mlx5_lag_pf(ldev, i); @@ -403,6 +421,69 @@ int mlx5_lag_get_dev_seq(struct mlx5_core_dev *dev) } EXPORT_SYMBOL(mlx5_lag_get_dev_seq); +/* seq 0 = master, then all remaining devices */ +static int mlx5_lag_get_dev_index_by_seq_all(struct mlx5_lag *ldev, int seq) +{ + int master_idx, i, num = 0; + + master_idx = mlx5_lag_get_master_idx(ldev); + + if (master_idx >= 0) { + if (seq == 0) + return master_idx; + num++; + } + + mlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) { + if (i == master_idx) + continue; + if (num == seq) + return i; + num++; + } + return -ENOENT; +} + +/* From group POV, port-marked entry is the lag master */ +static int mlx5_lag_get_dev_index_by_seq_group(struct mlx5_lag *ldev, int seq, + u32 group_id) +{ + int i, num = 0; + + mlx5_lag_for_each(i, 0, ldev, group_id) { + if (xa_get_mark(&ldev->pfs, i, MLX5_LAG_XA_MARK_PORT)) { + if (seq == 0) + return i; + num++; + break; + } + } + + mlx5_lag_for_each(i, 0, ldev, group_id) { + if (xa_get_mark(&ldev->pfs, i, MLX5_LAG_XA_MARK_PORT)) + continue; + if (num == seq) + return i; + num++; + } + return -ENOENT; +} + +int mlx5_lag_get_dev_index_by_seq_filter(struct mlx5_lag *ldev, int seq, + u32 filter) +{ + if (!ldev) + return -ENOENT; + + if (!filter || filter == MLX5_LAG_FILTER_PORTS) + return mlx5_lag_get_dev_index_by_seq(ldev, seq); + + if (filter == MLX5_LAG_FILTER_ALL) + return mlx5_lag_get_dev_index_by_seq_all(ldev, seq); + + return mlx5_lag_get_dev_index_by_seq_group(ldev, seq, filter); +} + /* Devcom events for LAG master marking */ #define LAG_DEVCOM_PAIR (0) #define LAG_DEVCOM_UNPAIR (1) @@ -512,6 +593,14 @@ static bool __mlx5_lag_is_sriov(struct mlx5_lag *ldev) return ldev->mode == MLX5_LAG_MODE_SRIOV; } +static bool __mlx5_lag_is_sd_active(struct mlx5_lag *ldev, + struct mlx5_core_dev *dev) +{ + struct lag_func *pf = mlx5_lag_pf_by_dev(ldev, dev); + + return pf && pf->sd_fdb_active; +} + /* Create a mapping between steering slots and active ports. * As we have ldev->buckets slots per port first assume the native * mapping should be used. @@ -927,27 +1016,19 @@ int mlx5_deactivate_lag(struct mlx5_lag *ldev) u32 in[MLX5_ST_SZ_DW(destroy_lag_in)] = {}; bool roce_lag = __mlx5_lag_is_roce(ldev); unsigned long flags = ldev->mode_flags; - struct mlx5_eswitch *master_esw; struct mlx5_core_dev *dev0; int err; - int i; if (master_idx < 0) return -EINVAL; dev0 = mlx5_lag_pf(ldev, master_idx)->dev; - master_esw = dev0->priv.eswitch; ldev->mode = MLX5_LAG_MODE_NONE; ldev->mode_flags = 0; mlx5_lag_mp_reset(ldev); if (test_bit(MLX5_LAG_MODE_FLAG_SHARED_FDB, &flags)) { - mlx5_ldev_for_each(i, 0, ldev) { - if (i == master_idx) - continue; - mlx5_eswitch_offloads_single_fdb_del_one(master_esw, - mlx5_lag_pf(ldev, i)->dev->priv.eswitch); - } + mlx5_lag_destroy_single_fdb(ldev); clear_bit(MLX5_LAG_MODE_FLAG_SHARED_FDB, &flags); } @@ -1026,7 +1107,7 @@ bool mlx5_lag_check_prereq(struct mlx5_lag *ldev) return true; } -static void mlx5_lag_assert_locked_transition(struct mlx5_lag *ldev) +static void mlx5_lag_assert_locked_transition(struct mlx5_lag *ldev, u32 filter) { struct mlx5_devcom_comp_dev *devcom = NULL; struct lag_func *pf; @@ -1034,17 +1115,21 @@ static void mlx5_lag_assert_locked_transition(struct mlx5_lag *ldev) lockdep_assert_held(&ldev->lock); - i = mlx5_get_next_lag_func(ldev, 0, MLX5_LAG_FILTER_PORTS); + i = mlx5_get_next_lag_func(ldev, 0, filter); if (i < MLX5_MAX_PORTS) { pf = mlx5_lag_pf(ldev, i); - devcom = pf->dev->priv.hca_devcom_comp; + if (filter == MLX5_LAG_FILTER_PORTS || + filter == MLX5_LAG_FILTER_ALL) + devcom = pf->dev->priv.hca_devcom_comp; + else + devcom = mlx5_sd_get_devcom(pf->dev); } mlx5_devcom_comp_assert_locked(devcom); } -static void mlx5_lag_drop_lock_for_reps(struct mlx5_lag *ldev) +static void mlx5_lag_drop_lock_for_reps(struct mlx5_lag *ldev, u32 filter) { - mlx5_lag_assert_locked_transition(ldev); + mlx5_lag_assert_locked_transition(ldev, filter); /* Keep PF membership stable while ldev->lock is dropped. Device add * and remove paths observe mode_changes_in_progress and retry. @@ -1075,21 +1160,22 @@ void mlx5_lag_rescan_dev_locked(struct mlx5_lag *ldev, * callbacks and take reps_lock. Drop ldev->lock so the only ordering * remains reps_lock -> ldev->lock from representor callbacks. */ - mlx5_lag_drop_lock_for_reps(ldev); + mlx5_lag_drop_lock_for_reps(ldev, mlx5_lag_get_filter(ldev, dev)); mlx5_rescan_drivers_locked(dev); mlx5_lag_retake_lock_after_reps(ldev); } -static void mlx5_lag_rescan_devices_locked(struct mlx5_lag *ldev, bool enable) +static void mlx5_lag_rescan_devices_locked_filter(struct mlx5_lag *ldev, + bool enable, u32 filter) { struct mlx5_core_dev *devs[MLX5_MAX_PORTS]; struct lag_func *pf; int num_devs = 0; int i; - mlx5_lag_assert_locked_transition(ldev); + mlx5_lag_assert_locked_transition(ldev, filter); - mlx5_ldev_for_each(i, 0, ldev) { + mlx5_lag_for_each(i, 0, ldev, filter) { pf = mlx5_lag_pf(ldev, i); if (pf->dev->priv.flags & MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV) continue; @@ -1101,30 +1187,40 @@ static void mlx5_lag_rescan_devices_locked(struct mlx5_lag *ldev, bool enable) devs[num_devs++] = pf->dev; } - mlx5_lag_drop_lock_for_reps(ldev); + mlx5_lag_drop_lock_for_reps(ldev, filter); for (i = 0; i < num_devs; i++) mlx5_rescan_drivers_locked(devs[i]); mlx5_lag_retake_lock_after_reps(ldev); } +void mlx5_lag_add_devices_filter(struct mlx5_lag *ldev, u32 filter) +{ + mlx5_lag_rescan_devices_locked_filter(ldev, true, filter); +} + void mlx5_lag_add_devices(struct mlx5_lag *ldev) { - mlx5_lag_rescan_devices_locked(ldev, true); + mlx5_lag_add_devices_filter(ldev, MLX5_LAG_FILTER_PORTS); +} + +void mlx5_lag_remove_devices_filter(struct mlx5_lag *ldev, u32 filter) +{ + mlx5_lag_rescan_devices_locked_filter(ldev, false, filter); } void mlx5_lag_remove_devices(struct mlx5_lag *ldev) { - mlx5_lag_rescan_devices_locked(ldev, false); + mlx5_lag_remove_devices_filter(ldev, MLX5_LAG_FILTER_PORTS); } static int mlx5_lag_reload_ib_reps_unlocked(struct mlx5_lag *ldev, u32 flags, - bool cont_on_fail) + u32 filter, bool cont_on_fail) { struct lag_func *pf; int ret; int i; - mlx5_ldev_for_each(i, 0, ldev) { + mlx5_lag_for_each(i, 0, ldev, filter) { pf = mlx5_lag_pf(ldev, i); if (!(pf->dev->priv.flags & flags)) { struct mlx5_eswitch *esw; @@ -1142,7 +1238,7 @@ static int mlx5_lag_reload_ib_reps_unlocked(struct mlx5_lag *ldev, u32 flags, } static int mlx5_lag_reload_ib_reps(struct mlx5_lag *ldev, u32 flags, - bool cont_on_fail) + u32 filter, bool cont_on_fail) { int ret; @@ -1152,21 +1248,18 @@ static int mlx5_lag_reload_ib_reps(struct mlx5_lag *ldev, u32 flags, * load/unload callbacks can re-enter LAG netdev add/remove and take * ldev->lock. Keep the ordering reps_lock -> ldev->lock. */ - mlx5_lag_drop_lock_for_reps(ldev); - ret = mlx5_lag_reload_ib_reps_unlocked(ldev, flags, cont_on_fail); + mlx5_lag_drop_lock_for_reps(ldev, filter); + ret = mlx5_lag_reload_ib_reps_unlocked(ldev, flags, filter, + cont_on_fail); mlx5_lag_retake_lock_after_reps(ldev); return ret; } int mlx5_lag_reload_ib_reps_from_locked(struct mlx5_lag *ldev, u32 flags, - bool cont_on_fail) + u32 filter, bool cont_on_fail) { - int ret; - - ret = mlx5_lag_reload_ib_reps(ldev, flags, cont_on_fail); - - return ret; + return mlx5_lag_reload_ib_reps(ldev, flags, filter, cont_on_fail); } void mlx5_disable_lag(struct mlx5_lag *ldev) @@ -1182,7 +1275,7 @@ void mlx5_disable_lag(struct mlx5_lag *ldev) return; if (shared_fdb) { - mlx5_lag_shared_fdb_destroy(ldev); + mlx5_lag_shared_fdb_destroy(ldev, 0); return; } @@ -1420,7 +1513,8 @@ static void mlx5_do_bond(struct mlx5_lag *ldev) if (shared_fdb) { err = mlx5_lag_shared_fdb_create(ldev, &tracker, - MLX5_LAG_MODE_SRIOV); + MLX5_LAG_MODE_SRIOV, + 0); if (err) return; } else { @@ -2261,7 +2355,8 @@ bool mlx5_lag_is_active(struct mlx5_core_dev *dev) spin_lock_irqsave(&lag_lock, flags); ldev = mlx5_lag_dev(dev); - res = ldev && __mlx5_lag_is_active(ldev); + res = ldev && (__mlx5_lag_is_active(ldev) || + __mlx5_lag_is_sd_active(ldev, dev)); spin_unlock_irqrestore(&lag_lock, flags); return res; @@ -2294,10 +2389,17 @@ bool mlx5_lag_is_master(struct mlx5_core_dev *dev) spin_lock_irqsave(&lag_lock, flags); ldev = mlx5_lag_dev(dev); - idx = mlx5_lag_get_dev_index_by_seq(ldev, MLX5_LAG_P1); - if (ldev && __mlx5_lag_is_active(ldev) && idx >= 0) { - pf = mlx5_lag_pf(ldev, idx); - res = pf && dev == pf->dev; + if (ldev) { + u32 filter; + + filter = mlx5_lag_get_filter(ldev, dev); + idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, MLX5_LAG_P1, + filter); + if ((__mlx5_lag_is_active(ldev) || + __mlx5_lag_is_sd_active(ldev, dev)) && idx >= 0) { + pf = mlx5_lag_pf(ldev, idx); + res = pf && dev == pf->dev; + } } spin_unlock_irqrestore(&lag_lock, flags); @@ -2324,11 +2426,16 @@ bool mlx5_lag_is_shared_fdb(struct mlx5_core_dev *dev) { struct mlx5_lag *ldev; unsigned long flags; - bool res; + bool res = false; spin_lock_irqsave(&lag_lock, flags); ldev = mlx5_lag_dev(dev); - res = ldev && test_bit(MLX5_LAG_MODE_FLAG_SHARED_FDB, &ldev->mode_flags); + if (ldev) { + res = test_bit(MLX5_LAG_MODE_FLAG_SHARED_FDB, + &ldev->mode_flags); + if (__mlx5_lag_is_sd(ldev, dev) && !__mlx5_lag_is_active(ldev)) + res = __mlx5_lag_is_sd_active(ldev, dev); + } spin_unlock_irqrestore(&lag_lock, flags); return res; @@ -2429,7 +2536,7 @@ struct mlx5_core_dev *mlx5_lag_get_next_peer_mdev(struct mlx5_core_dev *dev, int if (*i == MLX5_MAX_PORTS) goto unlock; - mlx5_ldev_for_each(idx, *i, ldev) { + mlx5_lag_for_each(idx, *i, ldev, mlx5_lag_get_filter(ldev, dev)) { pf = mlx5_lag_pf(ldev, idx); if (pf->dev != dev) break; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h index 9e95d5109e4a6..82b82bdb16dd3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h @@ -148,6 +148,14 @@ mlx5_lag_pf_by_dev(struct mlx5_lag *ldev, struct mlx5_core_dev *dev) return NULL; } +static inline bool +__mlx5_lag_is_sd(struct mlx5_lag *ldev, struct mlx5_core_dev *dev) +{ + struct lag_func *pf = mlx5_lag_pf_by_dev(ldev, dev); + + return pf && pf->group_id != 0; +} + static inline bool __mlx5_lag_is_active(struct mlx5_lag *ldev) { @@ -163,25 +171,31 @@ mlx5_lag_is_ready(struct mlx5_lag *ldev) #ifdef CONFIG_MLX5_ESWITCH int mlx5_lag_shared_fdb_create(struct mlx5_lag *ldev, struct lag_tracker *tracker, - enum mlx5_lag_mode mode); -void mlx5_lag_shared_fdb_destroy(struct mlx5_lag *ldev); + enum mlx5_lag_mode mode, + u32 group_id); +void mlx5_lag_shared_fdb_destroy(struct mlx5_lag *ldev, u32 group_id); int mlx5_lag_create_single_fdb(struct mlx5_lag *ldev); +void mlx5_lag_destroy_single_fdb(struct mlx5_lag *ldev); bool mlx5_lag_shared_fdb_supported(struct mlx5_lag *ldev); +bool mlx5_lag_shared_fdb_supported_filter(struct mlx5_lag *ldev, u32 filter); #else static inline int mlx5_lag_shared_fdb_create(struct mlx5_lag *ldev, struct lag_tracker *tracker, - enum mlx5_lag_mode mode) + enum mlx5_lag_mode mode, + u32 group_id) { return -EOPNOTSUPP; } -static inline void mlx5_lag_shared_fdb_destroy(struct mlx5_lag *ldev) {} +static inline void mlx5_lag_shared_fdb_destroy(struct mlx5_lag *ldev, + u32 group_id) {} static inline int mlx5_lag_create_single_fdb(struct mlx5_lag *ldev) { return -EOPNOTSUPP; } +static inline void mlx5_lag_destroy_single_fdb(struct mlx5_lag *ldev) {} static inline bool mlx5_lag_shared_fdb_supported(struct mlx5_lag *ldev) { return false; @@ -211,11 +225,13 @@ void mlx5_ldev_add_debugfs(struct mlx5_core_dev *dev); void mlx5_ldev_remove_debugfs(struct dentry *dbg); void mlx5_disable_lag(struct mlx5_lag *ldev); void mlx5_lag_remove_devices(struct mlx5_lag *ldev); +void mlx5_lag_remove_devices_filter(struct mlx5_lag *ldev, u32 filter); int mlx5_deactivate_lag(struct mlx5_lag *ldev); void mlx5_lag_add_devices(struct mlx5_lag *ldev); void mlx5_lag_rescan_dev_locked(struct mlx5_lag *ldev, struct mlx5_core_dev *dev, bool enable); +void mlx5_lag_add_devices_filter(struct mlx5_lag *ldev, u32 filter); struct mlx5_devcom_comp_dev *mlx5_lag_get_devcom_comp(struct mlx5_lag *ldev); #ifdef CONFIG_MLX5_ESWITCH @@ -238,8 +254,8 @@ static inline bool mlx5_lag_is_supported(struct mlx5_core_dev *dev) } /* Iterator filter constants for mlx5_lag_for_each() */ -#define MLX5_LAG_FILTER_ALL 0 /* iterate ALL devices */ -#define MLX5_LAG_FILTER_PORTS U32_MAX /* iterate ports only (XA_MARK_PORT) */ +#define MLX5_LAG_FILTER_PORTS 0 /* iterate ports only (XA_MARK_PORT) */ +#define MLX5_LAG_FILTER_ALL U32_MAX /* iterate ALL devices */ /* any other value = iterate devices with that specific group_id */ #define mlx5_lag_for_each(i, start_index, ldev, filter) \ @@ -264,10 +280,12 @@ int mlx5_get_pre_lag_func(struct mlx5_lag *ldev, int start_idx, int end_idx, u32 filter); int mlx5_get_next_lag_func(struct mlx5_lag *ldev, int start_idx, u32 filter); int mlx5_lag_get_dev_index_by_seq(struct mlx5_lag *ldev, int seq); +int mlx5_lag_get_dev_index_by_seq_filter(struct mlx5_lag *ldev, int seq, + u32 filter); int mlx5_lag_num_devs(struct mlx5_lag *ldev); int mlx5_lag_num_netdevs(struct mlx5_lag *ldev); int mlx5_lag_reload_ib_reps_from_locked(struct mlx5_lag *ldev, u32 flags, - bool cont_on_fail); + u32 filter, bool cont_on_fail); int mlx5_ldev_add_mdev(struct mlx5_lag *ldev, struct mlx5_core_dev *dev, u32 group_id); void mlx5_ldev_remove_mdev(struct mlx5_lag *ldev, struct mlx5_core_dev *dev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c index 64e2d1dd53087..2cb44084e239a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c @@ -85,14 +85,15 @@ static int mlx5_lag_enable_mpesw(struct mlx5_lag *ldev) !MLX5_CAP_PORT_SELECTION(dev0, port_select_flow_table) || !MLX5_CAP_GEN(dev0, create_lag_when_not_master_up) || !mlx5_lag_check_prereq(ldev) || - !mlx5_lag_shared_fdb_supported(ldev)) + !mlx5_lag_shared_fdb_supported_filter(ldev, MLX5_LAG_FILTER_ALL)) return -EOPNOTSUPP; err = mlx5_mpesw_metadata_set(ldev); if (err) return err; - err = mlx5_lag_shared_fdb_create(ldev, NULL, MLX5_LAG_MODE_MPESW); + err = mlx5_lag_shared_fdb_create(ldev, NULL, MLX5_LAG_MODE_MPESW, + MLX5_LAG_FILTER_ALL); if (err) { mlx5_core_warn(dev0, "Failed to create LAG in MPESW mode (%d)\n", err); mlx5_mpesw_metadata_cleanup(ldev); @@ -106,7 +107,7 @@ void mlx5_lag_disable_mpesw(struct mlx5_lag *ldev) { if (ldev->mode == MLX5_LAG_MODE_MPESW) { mlx5_mpesw_metadata_cleanup(ldev); - mlx5_lag_shared_fdb_destroy(ldev); + mlx5_lag_shared_fdb_destroy(ldev, MLX5_LAG_FILTER_ALL); } } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c index b5cbe34097203..1371e14c4c131 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c @@ -8,19 +8,19 @@ #include "lag.h" #include "eswitch.h" -bool mlx5_lag_shared_fdb_supported(struct mlx5_lag *ldev) +bool mlx5_lag_shared_fdb_supported_filter(struct mlx5_lag *ldev, u32 filter) { + int idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, MLX5_LAG_P1, + filter); struct mlx5_core_dev *dev0, *dev; bool ret = false; - int idx; int i; - idx = mlx5_lag_get_dev_index_by_seq(ldev, MLX5_LAG_P1); if (idx < 0) return false; dev0 = mlx5_lag_pf(ldev, idx)->dev; - mlx5_ldev_for_each(i, 0, ldev) { + mlx5_lag_for_each(i, 0, ldev, filter) { if (i == idx) continue; dev = mlx5_lag_pf(ldev, i)->dev; @@ -42,9 +42,16 @@ bool mlx5_lag_shared_fdb_supported(struct mlx5_lag *ldev) return ret; } -int mlx5_lag_create_single_fdb(struct mlx5_lag *ldev) +bool mlx5_lag_shared_fdb_supported(struct mlx5_lag *ldev) { - int master_idx = mlx5_lag_get_dev_index_by_seq(ldev, MLX5_LAG_P1); + return mlx5_lag_shared_fdb_supported_filter(ldev, + MLX5_LAG_FILTER_PORTS); +} + +static int mlx5_lag_create_single_fdb_filter(struct mlx5_lag *ldev, u32 filter) +{ + int master_idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, MLX5_LAG_P1, + filter); struct mlx5_eswitch *master_esw; struct mlx5_core_dev *dev0; int i, j; @@ -55,7 +62,7 @@ int mlx5_lag_create_single_fdb(struct mlx5_lag *ldev) dev0 = mlx5_lag_pf(ldev, master_idx)->dev; master_esw = dev0->priv.eswitch; - mlx5_ldev_for_each(i, 0, ldev) { + mlx5_lag_for_each(i, 0, ldev, filter) { struct mlx5_eswitch *slave_esw; if (i == master_idx) @@ -71,7 +78,7 @@ int mlx5_lag_create_single_fdb(struct mlx5_lag *ldev) } return 0; err: - mlx5_ldev_for_each_reverse(j, i, 0, ldev) { + mlx5_lag_for_each_reverse(j, i, 0, ldev, filter) { struct mlx5_eswitch *slave_esw; if (j == master_idx) @@ -82,59 +89,147 @@ int mlx5_lag_create_single_fdb(struct mlx5_lag *ldev) return err; } +static void mlx5_lag_destroy_single_fdb_filter(struct mlx5_lag *ldev, + u32 filter) +{ + int master_idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, MLX5_LAG_P1, + filter); + struct mlx5_eswitch *master_esw; + struct mlx5_eswitch *peer_esw; + int i; + + if (master_idx < 0) + return; + + master_esw = mlx5_lag_pf(ldev, master_idx)->dev->priv.eswitch; + mlx5_lag_for_each(i, 0, ldev, filter) { + if (i == master_idx) + continue; + + peer_esw = mlx5_lag_pf(ldev, i)->dev->priv.eswitch; + mlx5_eswitch_offloads_single_fdb_del_one(master_esw, peer_esw); + } +} + +int mlx5_lag_create_single_fdb(struct mlx5_lag *ldev) +{ + return mlx5_lag_create_single_fdb_filter(ldev, MLX5_LAG_FILTER_ALL); +} + +void mlx5_lag_destroy_single_fdb(struct mlx5_lag *ldev) +{ + mlx5_lag_destroy_single_fdb_filter(ldev, MLX5_LAG_FILTER_ALL); +} + +/** + * mlx5_lag_shared_fdb_create - Create shared FDB LAG + * @ldev: LAG device + * @tracker: LAG tracker (NULL for SD) + * @mode: LAG mode (unused for SD) + * @group_id: SD group ID; 0 (MLX5_LAG_FILTER_PORTS) for ports LAG; + * MLX5_LAG_FILTER_ALL for all-device (mpesw) LAG + * + * When group_id is 0 (MLX5_LAG_FILTER_PORTS) or MLX5_LAG_FILTER_ALL, + * activates a FW LAG with shared FDB. + * When group_id is a specific SD group ID, creates a software-only shared + * FDB scoped to that group (no FW LAG commands). + * + * Return: 0 on success, negative error code on failure. + */ int mlx5_lag_shared_fdb_create(struct mlx5_lag *ldev, struct lag_tracker *tracker, - enum mlx5_lag_mode mode) + enum mlx5_lag_mode mode, + u32 group_id) { - int idx = mlx5_lag_get_dev_index_by_seq(ldev, MLX5_LAG_P1); + u32 filter = group_id ? group_id : MLX5_LAG_FILTER_PORTS; + int idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, MLX5_LAG_P1, + filter); struct mlx5_core_dev *dev0; + struct lag_func *pf; int err; + int i; if (idx < 0) return -EINVAL; dev0 = mlx5_lag_pf(ldev, idx)->dev; - mlx5_lag_remove_devices(ldev); - - err = mlx5_activate_lag(ldev, tracker, mode, true); - if (err) { - mlx5_core_warn(dev0, "Failed to create LAG in shared FDB mode (%d)\n", - err); - goto err_add_devices; + mlx5_lag_remove_devices_filter(ldev, filter); + + if (filter == MLX5_LAG_FILTER_PORTS || filter == MLX5_LAG_FILTER_ALL) { + err = mlx5_activate_lag(ldev, tracker, mode, true); + if (err) { + mlx5_core_warn(dev0, + "Failed to create LAG in shared FDB mode (%d)\n", + err); + goto err_add_devices; + } + } else { + err = mlx5_lag_create_single_fdb_filter(ldev, group_id); + if (err) { + mlx5_core_warn(dev0, + "Failed to create SD shared FDB (%d)\n", + err); + goto err_add_devices; + } + mlx5_lag_for_each(i, 0, ldev, filter) { + pf = mlx5_lag_pf(ldev, i); + pf->sd_fdb_active = true; + } + BLOCKING_INIT_NOTIFIER_HEAD(&dev0->priv.lag_nh); } mlx5_lag_rescan_dev_locked(ldev, dev0, true); - err = mlx5_lag_reload_ib_reps_from_locked(ldev, 0, false); + err = mlx5_lag_reload_ib_reps_from_locked(ldev, 0, filter, false); if (err) { mlx5_core_err(dev0, "Failed to enable lag\n"); goto err_rescan_drivers; } - mlx5_lag_set_vports_agg_speed(ldev); + if (filter == MLX5_LAG_FILTER_PORTS || filter == MLX5_LAG_FILTER_ALL) + mlx5_lag_set_vports_agg_speed(ldev); return 0; err_rescan_drivers: mlx5_lag_rescan_dev_locked(ldev, dev0, false); - mlx5_deactivate_lag(ldev); + if (filter == MLX5_LAG_FILTER_PORTS || filter == MLX5_LAG_FILTER_ALL) { + mlx5_deactivate_lag(ldev); + } else { + mlx5_lag_for_each(i, 0, ldev, filter) { + pf = mlx5_lag_pf(ldev, i); + pf->sd_fdb_active = false; + } + mlx5_lag_destroy_single_fdb_filter(ldev, group_id); + } err_add_devices: - mlx5_lag_add_devices(ldev); - mlx5_lag_reload_ib_reps_from_locked(ldev, 0, true); + mlx5_lag_add_devices_filter(ldev, filter); + mlx5_lag_reload_ib_reps_from_locked(ldev, 0, filter, true); return err; } -void mlx5_lag_shared_fdb_destroy(struct mlx5_lag *ldev) +void mlx5_lag_shared_fdb_destroy(struct mlx5_lag *ldev, u32 group_id) { + u32 filter = group_id ? group_id : MLX5_LAG_FILTER_PORTS; + struct lag_func *pf; int err; + int i; - mlx5_lag_remove_devices(ldev); + mlx5_lag_remove_devices_filter(ldev, filter); - err = mlx5_deactivate_lag(ldev); - if (err) - return; + if (filter == MLX5_LAG_FILTER_PORTS || filter == MLX5_LAG_FILTER_ALL) { + err = mlx5_deactivate_lag(ldev); + if (err) + return; + } else { + mlx5_lag_for_each(i, 0, ldev, filter) { + pf = mlx5_lag_pf(ldev, i); + pf->sd_fdb_active = false; + } + mlx5_lag_destroy_single_fdb_filter(ldev, group_id); + } - mlx5_lag_add_devices(ldev); + mlx5_lag_add_devices_filter(ldev, filter); mlx5_lag_reload_ib_reps_from_locked(ldev, MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV, - true); + filter, true); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c index 6e199161b0083..bbd77ae11e848 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c @@ -57,6 +57,16 @@ static struct mlx5_core_dev *mlx5_sd_get_primary(struct mlx5_core_dev *dev) return sd->primary ? dev : sd->primary_dev; } +struct mlx5_devcom_comp_dev *mlx5_sd_get_devcom(struct mlx5_core_dev *dev) +{ + struct mlx5_sd *sd = mlx5_get_sd(dev); + + if (!sd) + return NULL; + + return sd->devcom; +} + struct mlx5_core_dev * mlx5_sd_primary_get_peer(struct mlx5_core_dev *primary, int idx) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.h index 9bfd5b9756b50..2ab259095d7ef 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.h @@ -21,6 +21,16 @@ void mlx5_sd_put_adev(struct auxiliary_device *actual_adev, int mlx5_sd_init(struct mlx5_core_dev *dev); void mlx5_sd_cleanup(struct mlx5_core_dev *dev); +#ifdef CONFIG_MLX5_CORE_EN +struct mlx5_devcom_comp_dev *mlx5_sd_get_devcom(struct mlx5_core_dev *dev); +#else +static inline struct mlx5_devcom_comp_dev * +mlx5_sd_get_devcom(struct mlx5_core_dev *dev) +{ + return NULL; +} +#endif + #define mlx5_sd_for_each_dev_from_to(i, primary, ix_from, to, pos) \ for (i = ix_from; \ (pos = mlx5_sd_primary_get_peer(primary, i)) && pos != (to); i++) From 40cc06bf71476932e5d139fa326dcd0372766e16 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Tue, 30 Jun 2026 14:29:15 +0300 Subject: [PATCH 2113/2758] net/mlx5: LAG, Fix off-by-one in single-FDB error rollback [ Upstream commit 0f0e4ae6975c773f7854fc48932a267f6c79088f ] On failure at index i, the reverse cleanup loop in mlx5_lag_create_single_fdb() starts from i, so the failed index itself is rolled back. That can operate on uninitialized state or double-tear-down a rule the add_one path already self-rolled-back. Start the rollback from i - 1 so only successfully-installed entries are undone. Fixes: ddbb5ddc43ad ("net/mlx5: LAG, Refactor lag logic") Signed-off-by: Shay Drory Reviewed-by: Mark Bloch Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260630112917.698313-2-tariqt@nvidia.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c index 1371e14c4c131..b72e8659a9686 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c @@ -78,7 +78,7 @@ static int mlx5_lag_create_single_fdb_filter(struct mlx5_lag *ldev, u32 filter) } return 0; err: - mlx5_lag_for_each_reverse(j, i, 0, ldev, filter) { + mlx5_lag_for_each_reverse(j, i - 1, 0, ldev, filter) { struct mlx5_eswitch *slave_esw; if (j == master_idx) From 4d720c6c60e1852253b68aeee3044ebed5243adb Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Tue, 30 Jun 2026 14:29:16 +0300 Subject: [PATCH 2114/2758] net/mlx5: LAG, MPESW, Fix missing complete() on devcom error [ Upstream commit d4b85f9a668b9c44216bb78daf4ec1a915cc92d1 ] mlx5_mpesw_work() returned without calling complete() when mlx5_lag_get_devcom_comp() returned NULL. A caller that queued the work and waited on mpesww->comp would block indefinitely. Funnel the early-return path through a new "complete" label so the waiter is always woken. Fixes: b430c1b4f63b ("net/mlx5: Replace global mlx5_intf_lock with HCA devcom component lock") Signed-off-by: Shay Drory Reviewed-by: Mark Bloch Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260630112917.698313-3-tariqt@nvidia.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c index 2cb44084e239a..97334b563c242 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c @@ -118,8 +118,10 @@ static void mlx5_mpesw_work(struct work_struct *work) struct mlx5_lag *ldev = mpesww->lag; devcom = mlx5_lag_get_devcom_comp(ldev); - if (!devcom) - return; + if (!devcom) { + mpesww->result = -ENODEV; + goto complete; + } mlx5_devcom_comp_lock(devcom); mutex_lock(&ldev->lock); @@ -135,6 +137,7 @@ static void mlx5_mpesw_work(struct work_struct *work) unlock: mutex_unlock(&ldev->lock); mlx5_devcom_comp_unlock(devcom); +complete: complete(&mpesww->comp); } From 5a95aa0198af75047d98fb72950642c89dab770f Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Tue, 30 Jun 2026 14:29:17 +0300 Subject: [PATCH 2115/2758] net/mlx5e: TC, skip peer flow cleanup when LAG seq is unavailable [ Upstream commit 7bed4af0ced82948d660205efecd551ef8bc3912 ] mlx5_lag_get_dev_seq() will return error when the peer isn't in the LAG or when no device is marked as master. Result bad memory access and kernel crash[1]. Hence, skip the peer when lookup fails. Note: In case there are peer flows, they are cleaned before LAG cleared the master mark. [1] RIP: 0010:mlx5e_tc_del_fdb_peers_flow+0x3d/0x350 [mlx5_core] Call Trace: mlx5e_tc_clean_fdb_peer_flows+0xc1/0x130 [mlx5_core] mlx5_esw_offloads_unpair+0x3a/0x400 [mlx5_core] mlx5_esw_offloads_devcom_event+0xee/0x360 [mlx5_core] mlx5_devcom_send_event+0x7a/0x140 [mlx5_core] mlx5_esw_offloads_devcom_cleanup+0x2f/0x90 [mlx5_core] mlx5e_tc_esw_cleanup+0x28/0xf0 [mlx5_core] mlx5e_rep_tc_cleanup+0x19/0x30 [mlx5_core] mlx5e_cleanup_uplink_rep_tx+0x36/0x40 [mlx5_core] mlx5e_cleanup_rep_tx+0x55/0x60 [mlx5_core] mlx5e_detach_netdev+0x96/0xf0 [mlx5_core] mlx5e_netdev_change_profile+0x5b/0x120 [mlx5_core] mlx5e_netdev_attach_nic_profile+0x1b/0x30 [mlx5_core] mlx5e_vport_rep_unload+0xdd/0x110 [mlx5_core] __esw_offloads_unload_rep+0x81/0xb0 [mlx5_core] mlx5_eswitch_unregister_vport_reps+0x1d7/0x220 [mlx5_core] mlx5e_rep_remove+0x22/0x30 [mlx5_core] device_release_driver_internal+0x194/0x1f0 bus_remove_device+0xe8/0x1b0 device_del+0x159/0x3c0 mlx5_rescan_drivers_locked+0xbc/0x2d0 [mlx5_core] mlx5_unregister_device+0x54/0x80 [mlx5_core] mlx5_uninit_one+0x73/0x130 [mlx5_core] remove_one+0x78/0xe0 [mlx5_core] pci_device_remove+0x39/0xa0 Fixes: 971b28accc09 ("net/mlx5: LAG, replace mlx5_get_dev_index with LAG sequence number") Signed-off-by: Shay Drory Reviewed-by: Mark Bloch Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260630112917.698313-4-tariqt@nvidia.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index a9001d1c902ff..8b5a421a22a37 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -5526,6 +5526,9 @@ void mlx5e_tc_clean_fdb_peer_flows(struct mlx5_eswitch *esw) mlx5_devcom_for_each_peer_entry(devcom, peer_esw, pos) { i = mlx5_lag_get_dev_seq(peer_esw->dev); + if (i < 0) + continue; + list_for_each_entry_safe(flow, tmp, &esw->offloads.peer_flows[i], peer[i]) mlx5e_tc_del_fdb_peers_flow(flow); } From 98fc2deffcf1949fbb14c710f21b4db362c8ec4b Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Tue, 23 Jun 2026 10:35:06 +0800 Subject: [PATCH 2116/2758] drm/bridge: analogix_dp: Fix PE/VS value shift mismatch during link training [ Upstream commit 6bb8898f702385d363dc2c513a1efa62807f8068 ] VS/PE values returned by drm_dp_get_adjust_request_voltage() and drm_dp_get_adjust_request_pre_emphasis() are already encoded to their native DPCD register bit positions. However, DPCD_VOLTAGE_SWING_SET / DPCD_PRE_EMPHASIS_SET macros perform an extra internal shift. Feeding the raw offset-bearing values directly leads to overlapping bitfields and invalid lane training configuration, causing link training failures and black screen. Add right shift using DP_TRAIN_*_SHIFT constants to strip the DPCD bit offsets before passing values to the SET macros and subsequent checks. Apply this fix for both clock recovery and adjust training code paths. Reported-by: Vicente Bergas Closes: https://lore.kernel.org/all/CAAMcf8D-d+5n=H44KeKBSqWY42m+o32W+mO-r15VqWNyYhJL7Q@mail.gmail.com/ Fixes: d84b087c7662 ("drm/bridge: analogix_dp: Apply DP helper APIs to get adjusted voltages and pre-emphasises") Signed-off-by: Damon Ding Link: https://lore.kernel.org/all/CAAMcf8D-d+5n=H44KeKBSqWY42m+o32W+mO-r15VqWNyYhJL7Q@mail.gmail.com/ Signed-off-by: Heiko Stuebner Link: https://patch.msgid.link/20260623023506.309858-1-damon.ding@rock-chips.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index 8dee5f2fbde51..00fe1f7893fba 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -306,7 +306,9 @@ static void analogix_dp_get_adjust_training_lane(struct analogix_dp_device *dp, lane_count = dp->link_train.lane_count; for (lane = 0; lane < lane_count; lane++) { voltage_swing = drm_dp_get_adjust_request_voltage(link_status, lane); + voltage_swing >>= DP_TRAIN_VOLTAGE_SWING_SHIFT; pre_emphasis = drm_dp_get_adjust_request_pre_emphasis(link_status, lane); + pre_emphasis >>= DP_TRAIN_PRE_EMPHASIS_SHIFT; training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) | DPCD_PRE_EMPHASIS_SET(pre_emphasis); @@ -352,7 +354,9 @@ static int analogix_dp_process_clock_recovery(struct analogix_dp_device *dp) for (lane = 0; lane < lane_count; lane++) { training_lane = analogix_dp_get_lane_link_training(dp, lane); voltage_swing = drm_dp_get_adjust_request_voltage(link_status, lane); + voltage_swing >>= DP_TRAIN_VOLTAGE_SWING_SHIFT; pre_emphasis = drm_dp_get_adjust_request_pre_emphasis(link_status, lane); + pre_emphasis >>= DP_TRAIN_PRE_EMPHASIS_SHIFT; if (DPCD_VOLTAGE_SWING_GET(training_lane) == voltage_swing && DPCD_PRE_EMPHASIS_GET(training_lane) == pre_emphasis) From abc4c56427f144c96b2827a4db3b90eb5b7349a2 Mon Sep 17 00:00:00 2001 From: Feng Liu Date: Tue, 30 Jun 2026 14:51:49 +0300 Subject: [PATCH 2117/2758] net/mlx5e: Fix HV VHCA stats zero-sized buffer allocation [ Upstream commit 25f6b929c7e379cbea7cb8caa67b49b2d1efae17 ] mlx5e_hv_vhca_stats_create() is called from mlx5e_nic_enable(), before mlx5e_open(). At that point priv->stats_nch is still zero, because it is only ever incremented in mlx5e_channel_stats_alloc(), which is reached only from mlx5e_open_channel(). mlx5e_hv_vhca_stats_buf_size() therefore returns 0, and kvzalloc(0, GFP_KERNEL) returns ZERO_SIZE_PTR ((void *)16) rather than NULL. The "if (!buf)" guard does not catch this, and mlx5e_hv_vhca_stats_create() completes "successfully" with priv->stats_agent.buf set to ZERO_SIZE_PTR. Once channels are opened (priv->stats_nch > 0) and the hypervisor enables stats reporting, mlx5e_hv_vhca_stats_work() recomputes buf_len using the new non-zero stats_nch and calls memset(buf, 0, buf_len) on ZERO_SIZE_PTR, faulting at address 0x10. Allocate the buffer based on priv->max_nch, which is set in mlx5e_priv_init() and is the upper bound on stats_nch: - Add a separate helper mlx5e_hv_vhca_stats_buf_max_size() that returns sizeof(per_ring_stats) * max(max_nch, stats_nch), and use it for the kvzalloc() in mlx5e_hv_vhca_stats_create(). - Keep mlx5e_hv_vhca_stats_buf_size() (which returns based on stats_nch) for the worker's active payload size, so the wire format (block->rings = stats_nch) and the amount of data filled by mlx5e_hv_vhca_fill_stats() are unchanged. The max(max_nch, stats_nch) guard handles the rare case where mlx5e_attach_netdev() recomputes max_nch downward across a detach/resume cycle while priv->stats_nch persists (mlx5e_detach_netdev does not call mlx5e_priv_cleanup, so stats_nch is only reset when the netdev is destroyed). Without the guard, the worker could compute buf_len from stats_nch and overrun the smaller buffer allocated based on the reduced max_nch. Allocating a non-zero buffer also makes the kvzalloc() failure path in mlx5e_hv_vhca_stats_create() reachable for the first time: it returns early without (re)creating the agent. Clear priv->stats_agent.{agent,buf} in mlx5e_hv_vhca_stats_destroy() after freeing them, so that if a later create() bails out on this path, a subsequent teardown does not double-free the stale agent/buffer left from a previous enable/disable cycle. This mirrors the existing mlx5e pattern of preallocating arrays of size max_nch (e.g. priv->channel_stats) and lazily populating entries up to stats_nch on demand. Fixes: fa691d0c9c08 ("net/mlx5e: Allocate per-channel stats dynamically at first usage") Signed-off-by: Feng Liu Reviewed-by: Eran Ben Elisha Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260630115151.729219-2-tariqt@nvidia.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- .../net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c index 195863b2c013e..72f3ca4dd076b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c @@ -54,6 +54,12 @@ static int mlx5e_hv_vhca_stats_buf_size(struct mlx5e_priv *priv) priv->stats_nch); } +static int mlx5e_hv_vhca_stats_buf_max_size(struct mlx5e_priv *priv) +{ + return (sizeof(struct mlx5e_hv_vhca_per_ring_stats) * + max(priv->max_nch, priv->stats_nch)); +} + static void mlx5e_hv_vhca_stats_work(struct work_struct *work) { struct mlx5e_hv_vhca_stats_agent *sagent; @@ -122,7 +128,7 @@ static void mlx5e_hv_vhca_stats_cleanup(struct mlx5_hv_vhca_agent *agent) void mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv) { - int buf_len = mlx5e_hv_vhca_stats_buf_size(priv); + int buf_len = mlx5e_hv_vhca_stats_buf_max_size(priv); struct mlx5_hv_vhca_agent *agent; priv->stats_agent.buf = kvzalloc(buf_len, GFP_KERNEL); @@ -155,5 +161,7 @@ void mlx5e_hv_vhca_stats_destroy(struct mlx5e_priv *priv) return; mlx5_hv_vhca_agent_destroy(priv->stats_agent.agent); + priv->stats_agent.agent = NULL; kvfree(priv->stats_agent.buf); + priv->stats_agent.buf = NULL; } From f5677797b094c3ec5fb350eb8ea7710b88a3d018 Mon Sep 17 00:00:00 2001 From: Feng Liu Date: Tue, 30 Jun 2026 14:51:50 +0300 Subject: [PATCH 2118/2758] net/mlx5e: Fix HV VHCA stats agent registration race [ Upstream commit 89b25b5f46f488ea3b29b3444864c76944c9075b ] mlx5e_hv_vhca_stats_create() registers the stats agent through mlx5_hv_vhca_agent_create(). The helper publishes the agent in hv_vhca->agents[type] under agents_lock and immediately schedules an asynchronous control invalidation on the HV VHCA workqueue before returning to mlx5e. The asynchronous invalidation invokes the control agent's invalidate callback, which reads the hypervisor control block and forwards the command to mlx5e_hv_vhca_stats_control(). That callback may either: - call cancel_delayed_work_sync(&priv->stats_agent.work), or - call queue_delayed_work(priv->wq, &sagent->work, sagent->delay). However, the delayed_work and priv->stats_agent.agent are only initialized after mlx5_hv_vhca_agent_create() returns to mlx5e: agent = mlx5_hv_vhca_agent_create(...); /* publish + invalidate */ ... priv->stats_agent.agent = agent; /* too late */ INIT_DELAYED_WORK(&priv->stats_agent.work, ...); /* too late */ If the asynchronous control path runs before the two assignments above, it can: - Operate on an uninitialized delayed_work whose timer.function is NULL. queue_delayed_work() calls add_timer() unconditionally, so when the timer expires the timer softirq invokes a NULL function pointer. - Re-initialize the timer later through INIT_DELAYED_WORK() while the timer is already enqueued in the timer wheel, corrupting the hlist (entry.pprev cleared while the previous bucket node still points at this entry). - When the worker eventually runs, mlx5e_hv_vhca_stats_work() reads sagent->agent (NULL) and dereferences it inside mlx5_hv_vhca_agent_write(). Fix this by: - Initializing priv->stats_agent.work before invoking mlx5_hv_vhca_agent_create(), so the work is always in a valid state when the control callback observes it. - Adding a struct mlx5_hv_vhca_agent **ctx_update out-parameter to mlx5_hv_vhca_agent_create(). The helper writes the agent pointer to *ctx_update before publishing into hv_vhca->agents[] and triggering the agents_update flow, so any callback subsequently invoked from that flow already sees a valid priv->stats_agent.agent. This avoids having the control callback participate in agent initialization. While at it, access priv->stats_agent.agent with READ_ONCE()/WRITE_ONCE() for the cross-CPU access with the worker, and clear priv->stats_agent.buf on the agent_create() failure path. Fixes: cef35af34d6d ("net/mlx5e: Add mlx5e HV VHCA stats agent") Signed-off-by: Feng Liu Reviewed-by: Eran Ben Elisha Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260630115151.729219-3-tariqt@nvidia.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- .../mellanox/mlx5/core/en/hv_vhca_stats.c | 21 +++++++++++-------- .../ethernet/mellanox/mlx5/core/lib/hv_vhca.c | 8 +++++-- .../ethernet/mellanox/mlx5/core/lib/hv_vhca.h | 6 ++++-- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c index 72f3ca4dd076b..cdaf77650164b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c @@ -73,7 +73,7 @@ static void mlx5e_hv_vhca_stats_work(struct work_struct *work) sagent = container_of(dwork, struct mlx5e_hv_vhca_stats_agent, work); priv = container_of(sagent, struct mlx5e_priv, stats_agent); buf_len = mlx5e_hv_vhca_stats_buf_size(priv); - agent = sagent->agent; + agent = READ_ONCE(sagent->agent); buf = sagent->buf; memset(buf, 0, buf_len); @@ -135,11 +135,14 @@ void mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv) if (!priv->stats_agent.buf) return; + INIT_DELAYED_WORK(&priv->stats_agent.work, mlx5e_hv_vhca_stats_work); + agent = mlx5_hv_vhca_agent_create(priv->mdev->hv_vhca, MLX5_HV_VHCA_AGENT_STATS, mlx5e_hv_vhca_stats_control, NULL, mlx5e_hv_vhca_stats_cleanup, - priv); + priv, + &priv->stats_agent.agent); if (IS_ERR_OR_NULL(agent)) { if (IS_ERR(agent)) @@ -148,20 +151,20 @@ void mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv) agent); kvfree(priv->stats_agent.buf); - return; + priv->stats_agent.buf = NULL; } - - priv->stats_agent.agent = agent; - INIT_DELAYED_WORK(&priv->stats_agent.work, mlx5e_hv_vhca_stats_work); } void mlx5e_hv_vhca_stats_destroy(struct mlx5e_priv *priv) { - if (IS_ERR_OR_NULL(priv->stats_agent.agent)) + struct mlx5_hv_vhca_agent *agent; + + agent = READ_ONCE(priv->stats_agent.agent); + if (IS_ERR_OR_NULL(agent)) return; - mlx5_hv_vhca_agent_destroy(priv->stats_agent.agent); - priv->stats_agent.agent = NULL; + mlx5_hv_vhca_agent_destroy(agent); + WRITE_ONCE(priv->stats_agent.agent, NULL); kvfree(priv->stats_agent.buf); priv->stats_agent.buf = NULL; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c index d6dc7bce855e0..305752dab7bdf 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c @@ -190,7 +190,7 @@ mlx5_hv_vhca_control_agent_create(struct mlx5_hv_vhca *hv_vhca) return mlx5_hv_vhca_agent_create(hv_vhca, MLX5_HV_VHCA_AGENT_CONTROL, NULL, mlx5_hv_vhca_control_agent_invalidate, - NULL, NULL); + NULL, NULL, NULL); } static void mlx5_hv_vhca_control_agent_destroy(struct mlx5_hv_vhca_agent *agent) @@ -256,7 +256,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca, void (*invalidate)(struct mlx5_hv_vhca_agent*, u64 block_mask), void (*cleaup)(struct mlx5_hv_vhca_agent *agent), - void *priv) + void *priv, + struct mlx5_hv_vhca_agent **ctx_update) { struct mlx5_hv_vhca_agent *agent; @@ -284,6 +285,9 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca, agent->invalidate = invalidate; agent->cleanup = cleaup; + if (ctx_update) + WRITE_ONCE(*ctx_update, agent); + mutex_lock(&hv_vhca->agents_lock); hv_vhca->agents[type] = agent; mutex_unlock(&hv_vhca->agents_lock); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h index f240ffe5116c5..8b3974cf0ee49 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h @@ -43,7 +43,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca, void (*invalidate)(struct mlx5_hv_vhca_agent*, u64 block_mask), void (*cleanup)(struct mlx5_hv_vhca_agent *agent), - void *context); + void *context, + struct mlx5_hv_vhca_agent **ctx_update); void mlx5_hv_vhca_agent_destroy(struct mlx5_hv_vhca_agent *agent); int mlx5_hv_vhca_agent_write(struct mlx5_hv_vhca_agent *agent, @@ -84,7 +85,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca, void (*invalidate)(struct mlx5_hv_vhca_agent*, u64 block_mask), void (*cleanup)(struct mlx5_hv_vhca_agent *agent), - void *context) + void *context, + struct mlx5_hv_vhca_agent **ctx_update) { return NULL; } From 815515ec68f527ca755cb1e2c1ff9148f6b3ea56 Mon Sep 17 00:00:00 2001 From: Feng Liu Date: Tue, 30 Jun 2026 14:51:51 +0300 Subject: [PATCH 2119/2758] net/mlx5e: Fix publication race for priv->channel_stats[] [ Upstream commit 5a799714e8ca0bce9ea40694f49914cf1adbbaa9 ] mlx5e_channel_stats_alloc() publishes a new entry to priv->channel_stats[] and then increments priv->stats_nch as a publication token, but neither store carries any memory barrier: priv->channel_stats[ix] = kvzalloc_node(...); if (!priv->channel_stats[ix]) return -ENOMEM; priv->stats_nch++; Concurrent readers compute the loop bound from priv->stats_nch and then dereference priv->channel_stats[i] using plain accesses, e.g. for (i = 0; i < priv->stats_nch; i++) { struct mlx5e_channel_stats *cs = priv->channel_stats[i]; ... cs->rq.packets ... } On weakly-ordered architectures (ARM, PowerPC, RISC-V) the writes to channel_stats[ix] and stats_nch may become visible to other CPUs out of program order. A reader can observe stats_nch == N while still seeing channel_stats[N-1] == NULL, leading to a NULL pointer dereference in the channel_stats loop. This has been observed in production on BlueField-3 DPUs (arm64), where ovs-vswitchd queries netdev statistics over netlink during NIC bringup, racing mlx5e_open_channel() -> mlx5e_channel_stats_alloc() on another CPU: Unable to handle kernel NULL pointer dereference at virtual address 0x840 Hardware name: BlueField-3 DPU pc : mlx5e_fold_sw_stats64+0x30/0x180 [mlx5_core] Call trace: mlx5e_fold_sw_stats64+0x30/0x180 [mlx5_core] dev_get_stats+0x50/0xc0 ovs_vport_get_stats+0x38/0xac [openvswitch] ovs_vport_cmd_fill_info+0x194/0x290 [openvswitch] ovs_vport_cmd_get+0xbc/0x10c [openvswitch] genl_family_rcv_msg_doit+0xd0/0x160 genl_rcv_msg+0xec/0x1f0 netlink_rcv_skb+0x64/0x130 genl_rcv+0x40/0x60 netlink_unicast+0x2fc/0x370 netlink_sendmsg+0x1dc/0x454 ... __arm64_sys_sendmsg+0x2c/0x40 Add mlx5e_stats_nch_write() and mlx5e_stats_nch_read() helpers in en.h that wrap the smp_store_release()/smp_load_acquire() pair on stats_nch. The release/acquire pair establishes the contract: stats_nch == N => channel_stats[0..N-1] are visible and non-NULL. Publish the stats_nch increment via mlx5e_stats_nch_write() in the writer (mlx5e_channel_stats_alloc()), and read stats_nch via mlx5e_stats_nch_read() in all readers: mlx5e RX/TX queue stats, mlx5e_get_base_stats(), ethtool channels stats, IPoIB stats, the sw_stats fold and the HV VHCA stats agent. Fixes: fa691d0c9c08 ("net/mlx5e: Allocate per-channel stats dynamically at first usage") Signed-off-by: Feng Liu Reviewed-by: Eran Ben Elisha Reviewed-by: Cosmin Ratiu Reviewed-by: Nimrod Oren Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260630115151.729219-4-tariqt@nvidia.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 12 ++++++++++++ .../ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c | 10 ++++++---- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 14 ++++++++------ drivers/net/ethernet/mellanox/mlx5/core/en_stats.c | 9 +++++---- .../net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c | 3 ++- 5 files changed, 33 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 2270e2e550dd9..d507289096c20 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -987,6 +987,18 @@ struct mlx5e_priv { struct ethtool_fec_hist_range *fec_ranges; }; +static inline u16 mlx5e_stats_nch_read(const struct mlx5e_priv *priv) +{ + /* Pairs with smp_store_release in mlx5e_stats_nch_write(). */ + return smp_load_acquire(&priv->stats_nch); +} + +static inline void mlx5e_stats_nch_write(struct mlx5e_priv *priv, u16 n) +{ + /* Pairs with smp_load_acquire in mlx5e_stats_nch_read(). */ + smp_store_release(&priv->stats_nch, n); +} + struct mlx5e_dev { struct net_device *netdev; struct devlink_port dl_port; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c index cdaf77650164b..631f802105d5b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c @@ -33,9 +33,10 @@ mlx5e_hv_vhca_fill_ring_stats(struct mlx5e_priv *priv, int ch, static void mlx5e_hv_vhca_fill_stats(struct mlx5e_priv *priv, void *data, int buf_len) { + u16 nch = mlx5e_stats_nch_read(priv); int ch, i = 0; - for (ch = 0; ch < priv->stats_nch; ch++) { + for (ch = 0; ch < nch; ch++) { void *buf = data + i; if (WARN_ON_ONCE(buf + @@ -50,8 +51,9 @@ static void mlx5e_hv_vhca_fill_stats(struct mlx5e_priv *priv, void *data, static int mlx5e_hv_vhca_stats_buf_size(struct mlx5e_priv *priv) { - return (sizeof(struct mlx5e_hv_vhca_per_ring_stats) * - priv->stats_nch); + u16 nch = mlx5e_stats_nch_read(priv); + + return sizeof(struct mlx5e_hv_vhca_per_ring_stats) * nch; } static int mlx5e_hv_vhca_stats_buf_max_size(struct mlx5e_priv *priv) @@ -106,7 +108,7 @@ static void mlx5e_hv_vhca_stats_control(struct mlx5_hv_vhca_agent *agent, sagent = &priv->stats_agent; block->version = MLX5_HV_VHCA_STATS_VERSION; - block->rings = priv->stats_nch; + block->rings = mlx5e_stats_nch_read(priv); if (!block->command) { cancel_delayed_work_sync(&priv->stats_agent.work); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 8f2b3abe00921..94e5352a246cd 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -2773,7 +2773,7 @@ static int mlx5e_channel_stats_alloc(struct mlx5e_priv *priv, int ix, int cpu) GFP_KERNEL, cpu_to_node(cpu)); if (!priv->channel_stats[ix]) return -ENOMEM; - priv->stats_nch++; + mlx5e_stats_nch_write(priv, priv->stats_nch + 1); return 0; } @@ -4043,9 +4043,10 @@ static int mlx5e_setup_tc(struct net_device *dev, enum tc_setup_type type, void mlx5e_fold_sw_stats64(struct mlx5e_priv *priv, struct rtnl_link_stats64 *s) { + u16 nch = mlx5e_stats_nch_read(priv); int i; - for (i = 0; i < priv->stats_nch; i++) { + for (i = 0; i < nch; i++) { struct mlx5e_channel_stats *channel_stats = priv->channel_stats[i]; struct mlx5e_rq_stats *xskrq_stats = &channel_stats->xskrq; struct mlx5e_rq_stats *rq_stats = &channel_stats->rq; @@ -5489,7 +5490,7 @@ static void mlx5e_get_queue_stats_rx(struct net_device *dev, int i, struct mlx5e_rq_stats *xskrq_stats; struct mlx5e_rq_stats *rq_stats; - if (mlx5e_is_uplink_rep(priv) || !priv->stats_nch) + if (mlx5e_is_uplink_rep(priv) || !mlx5e_stats_nch_read(priv)) return; channel_stats = priv->channel_stats[i]; @@ -5508,7 +5509,7 @@ static void mlx5e_get_queue_stats_tx(struct net_device *dev, int i, struct mlx5e_priv *priv = netdev_priv(dev); struct mlx5e_sq_stats *sq_stats; - if (!priv->stats_nch) + if (!mlx5e_stats_nch_read(priv)) return; /* no special case needed for ptp htb etc since txq2sq_stats is kept up @@ -5525,6 +5526,7 @@ static void mlx5e_get_base_stats(struct net_device *dev, struct netdev_queue_stats_tx *tx) { struct mlx5e_priv *priv = netdev_priv(dev); + u16 nch = mlx5e_stats_nch_read(priv); struct mlx5e_ptp *ptp_channel; int i, tc; @@ -5533,7 +5535,7 @@ static void mlx5e_get_base_stats(struct net_device *dev, rx->bytes = 0; rx->alloc_fail = 0; - for (i = priv->channels.params.num_channels; i < priv->stats_nch; i++) { + for (i = priv->channels.params.num_channels; i < nch; i++) { struct netdev_queue_stats_rx rx_i = {0}; mlx5e_get_queue_stats_rx(dev, i, &rx_i); @@ -5558,7 +5560,7 @@ static void mlx5e_get_base_stats(struct net_device *dev, tx->packets = 0; tx->bytes = 0; - for (i = 0; i < priv->stats_nch; i++) { + for (i = 0; i < nch; i++) { struct mlx5e_channel_stats *channel_stats = priv->channel_stats[i]; /* handle two cases: diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c index 1a3ecf073913c..8632b73179cbc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c @@ -516,6 +516,7 @@ static void mlx5e_stats_update_stats_rq_page_pool(struct mlx5e_channel *c) static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(sw) { struct mlx5e_sw_stats *s = &priv->stats.sw; + u16 nch = mlx5e_stats_nch_read(priv); int i; memset(s, 0, sizeof(*s)); @@ -523,7 +524,7 @@ static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(sw) for (i = 0; i < priv->channels.num; i++) /* for active channels only */ mlx5e_stats_update_stats_rq_page_pool(priv->channels.c[i]); - for (i = 0; i < priv->stats_nch; i++) { + for (i = 0; i < nch; i++) { struct mlx5e_channel_stats *channel_stats = priv->channel_stats[i]; @@ -2615,7 +2616,7 @@ static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(ptp) { return; } static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(channels) { - int max_nch = priv->stats_nch; + int max_nch = mlx5e_stats_nch_read(priv); return (NUM_RQ_STATS * max_nch) + (NUM_CH_STATS * max_nch) + @@ -2628,8 +2629,8 @@ static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(channels) static MLX5E_DECLARE_STATS_GRP_OP_FILL_STRS(channels) { + int max_nch = mlx5e_stats_nch_read(priv); bool is_xsk = priv->xsk.ever_used; - int max_nch = priv->stats_nch; int i, j, tc; for (i = 0; i < max_nch; i++) @@ -2661,8 +2662,8 @@ static MLX5E_DECLARE_STATS_GRP_OP_FILL_STRS(channels) static MLX5E_DECLARE_STATS_GRP_OP_FILL_STATS(channels) { + int max_nch = mlx5e_stats_nch_read(priv); bool is_xsk = priv->xsk.ever_used; - int max_nch = priv->stats_nch; int i, j, tc; for (i = 0; i < max_nch; i++) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c index 0a6003fe60e9f..674bed721e637 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c @@ -135,10 +135,11 @@ void mlx5i_cleanup(struct mlx5e_priv *priv) static void mlx5i_grp_sw_update_stats(struct mlx5e_priv *priv) { + u16 nch = mlx5e_stats_nch_read(priv); struct rtnl_link_stats64 s = {}; int i, j; - for (i = 0; i < priv->stats_nch; i++) { + for (i = 0; i < nch; i++) { struct mlx5e_channel_stats *channel_stats; struct mlx5e_rq_stats *rq_stats; From 952928564cc5fdb06f92d7e25c6cd2e1d816362b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Emil=20Schulz=20=C3=98stergaard?= Date: Tue, 30 Jun 2026 14:20:13 +0200 Subject: [PATCH 2120/2758] net: microchip: vcap: fix races on the shared Super VCAP block MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit d7a8d500d7e42837bd8dce40cb52c97c6e8706a9 ] The VCAP instances on a chip are not independent, yet they are locked independently. On sparx5 and lan969x the IS0 and IS2 instances are backed by the same Super VCAP hardware block and share its cache and command registers: every access drives the shared VCAP_SUPER_CTRL register and moves data through the shared cache registers. Accessing one instance therefore races with accessing another. The per-instance admin->lock cannot prevent this, as each instance takes a different lock. The locking issue is mostly disguised by the fact that the core usage of the vcap api runs under rtnl. However, the full rule dump in debugfs decodes rules straight from hardware (a READ command followed by a cache read) and runs outside rtnl, so it races a concurrent tc-flower rule write to another Super VCAP instance. Besides corrupting the dump, the read repopulates the shared cache between the writers cache fill and its write command, so the writer commits the wrong data and corrupts the hardware entry. Introduce vcap_lock() and vcap_unlock() helpers and route every rule lock site in the VCAP API and its debugfs code through them. Replace the per-instance admin->lock with a single mutex in struct vcap_control that serializes access to all instances. The helpers reach it through a new admin->vctrl back-pointer, and the clients initialise and destroy the control lock instead of a per-instance one. No path holds more than one instance lock, so collapsing them onto a single mutex cannot self-deadlock. Fixes: 71c9de995260 ("net: microchip: sparx5: Add VCAP locking to protect rules") Signed-off-by: Jens Emil Schulz Østergaard Link: https://patch.msgid.link/20260630-microchip_fix_vcap_locking-v1-1-f60a4596734d@microchip.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- .../microchip/lan966x/lan966x_vcap_impl.c | 5 +- .../microchip/sparx5/sparx5_vcap_impl.c | 5 +- .../net/ethernet/microchip/vcap/vcap_api.c | 72 +++++++++++-------- .../net/ethernet/microchip/vcap/vcap_api.h | 3 +- .../microchip/vcap/vcap_api_debugfs.c | 8 +-- .../microchip/vcap/vcap_api_debugfs_kunit.c | 3 +- .../ethernet/microchip/vcap/vcap_api_kunit.c | 3 +- .../microchip/vcap/vcap_api_private.h | 3 + 8 files changed, 60 insertions(+), 42 deletions(-) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c index 72e3b189bac5a..eb28df80b2818 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c @@ -601,7 +601,6 @@ static void lan966x_vcap_admin_free(struct vcap_admin *admin) kfree(admin->cache.keystream); kfree(admin->cache.maskstream); kfree(admin->cache.actionstream); - mutex_destroy(&admin->lock); kfree(admin); } @@ -615,7 +614,7 @@ lan966x_vcap_admin_alloc(struct lan966x *lan966x, struct vcap_control *ctrl, if (!admin) return ERR_PTR(-ENOMEM); - mutex_init(&admin->lock); + admin->vctrl = ctrl; INIT_LIST_HEAD(&admin->list); INIT_LIST_HEAD(&admin->rules); INIT_LIST_HEAD(&admin->enabled); @@ -721,6 +720,7 @@ int lan966x_vcap_init(struct lan966x *lan966x) ctrl->ops = &lan966x_vcap_ops; INIT_LIST_HEAD(&ctrl->list); + mutex_init(&ctrl->lock); for (int i = 0; i < ARRAY_SIZE(lan966x_vcap_inst_cfg); ++i) { cfg = &lan966x_vcap_inst_cfg[i]; @@ -780,5 +780,6 @@ void lan966x_vcap_deinit(struct lan966x *lan966x) lan966x_vcap_admin_free(admin); } + mutex_destroy(&ctrl->lock); kfree(ctrl); } diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c index 95b93e46a41dc..cf332de6bf736 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c @@ -1930,7 +1930,6 @@ static void sparx5_vcap_admin_free(struct vcap_admin *admin) { if (!admin) return; - mutex_destroy(&admin->lock); kfree(admin->cache.keystream); kfree(admin->cache.maskstream); kfree(admin->cache.actionstream); @@ -1950,7 +1949,7 @@ sparx5_vcap_admin_alloc(struct sparx5 *sparx5, struct vcap_control *ctrl, INIT_LIST_HEAD(&admin->list); INIT_LIST_HEAD(&admin->rules); INIT_LIST_HEAD(&admin->enabled); - mutex_init(&admin->lock); + admin->vctrl = ctrl; admin->vtype = cfg->vtype; admin->vinst = cfg->vinst; admin->ingress = cfg->ingress; @@ -2059,6 +2058,7 @@ int sparx5_vcap_init(struct sparx5 *sparx5) ctrl->ops = &sparx5_vcap_ops; INIT_LIST_HEAD(&ctrl->list); + mutex_init(&ctrl->lock); for (idx = 0; idx < ARRAY_SIZE(sparx5_vcap_inst_cfg); ++idx) { cfg = &consts->vcaps_cfg[idx]; admin = sparx5_vcap_admin_alloc(sparx5, ctrl, cfg); @@ -2097,5 +2097,6 @@ void sparx5_vcap_deinit(struct sparx5 *sparx5) list_del(&admin->list); sparx5_vcap_admin_free(admin); } + mutex_destroy(&ctrl->lock); kfree(ctrl); } diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index 0fdb5e363badc..ff86cde11a32c 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -934,6 +934,16 @@ static bool vcap_rule_exists(struct vcap_control *vctrl, u32 id) return false; } +void vcap_lock(struct vcap_admin *admin) +{ + mutex_lock(&admin->vctrl->lock); +} + +void vcap_unlock(struct vcap_admin *admin) +{ + mutex_unlock(&admin->vctrl->lock); +} + /* Find a rule with a provided rule id return a locked vcap */ static struct vcap_rule_internal * vcap_get_locked_rule(struct vcap_control *vctrl, u32 id) @@ -943,11 +953,11 @@ vcap_get_locked_rule(struct vcap_control *vctrl, u32 id) /* Look for the rule id in all vcaps */ list_for_each_entry(admin, &vctrl->list, list) { - mutex_lock(&admin->lock); + vcap_lock(admin); list_for_each_entry(ri, &admin->rules, list) if (ri->data.id == id) return ri; - mutex_unlock(&admin->lock); + vcap_unlock(admin); } return NULL; } @@ -961,14 +971,14 @@ int vcap_lookup_rule_by_cookie(struct vcap_control *vctrl, u64 cookie) /* Look for the rule id in all vcaps */ list_for_each_entry(admin, &vctrl->list, list) { - mutex_lock(&admin->lock); + vcap_lock(admin); list_for_each_entry(ri, &admin->rules, list) { if (ri->data.cookie == cookie) { id = ri->data.id; break; } } - mutex_unlock(&admin->lock); + vcap_unlock(admin); if (id) return id; } @@ -985,11 +995,11 @@ int vcap_admin_rule_count(struct vcap_admin *admin, int cid) int count = 0; list_for_each_entry(elem, &admin->rules, list) { - mutex_lock(&admin->lock); + vcap_lock(admin); if (elem->data.vcap_chain_id >= min_cid && elem->data.vcap_chain_id < max_cid) ++count; - mutex_unlock(&admin->lock); + vcap_unlock(admin); } return count; } @@ -2266,7 +2276,7 @@ int vcap_add_rule(struct vcap_rule *rule) if (ret) return ret; /* Insert the new rule in the list of vcap rules */ - mutex_lock(&ri->admin->lock); + vcap_lock(ri->admin); vcap_rule_set_state(ri); ret = vcap_insert_rule(ri, &move); @@ -2302,7 +2312,7 @@ int vcap_add_rule(struct vcap_rule *rule) goto out; } out: - mutex_unlock(&ri->admin->lock); + vcap_unlock(ri->admin); return ret; } EXPORT_SYMBOL_GPL(vcap_add_rule); @@ -2330,7 +2340,7 @@ struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl, if (vctrl->vcaps[admin->vtype].rows == 0) return ERR_PTR(-EINVAL); - mutex_lock(&admin->lock); + vcap_lock(admin); /* Check if a rule with this id already exists */ if (vcap_rule_exists(vctrl, id)) { err = -EINVAL; @@ -2369,13 +2379,13 @@ struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl, goto out_free; } - mutex_unlock(&admin->lock); + vcap_unlock(admin); return (struct vcap_rule *)ri; out_free: kfree(ri); out_unlock: - mutex_unlock(&admin->lock); + vcap_unlock(admin); return ERR_PTR(err); } @@ -2446,7 +2456,7 @@ struct vcap_rule *vcap_get_rule(struct vcap_control *vctrl, u32 id) return ERR_PTR(-ENOENT); rule = vcap_decode_rule(elem); - mutex_unlock(&elem->admin->lock); + vcap_unlock(elem->admin); return rule; } EXPORT_SYMBOL_GPL(vcap_get_rule); @@ -2483,7 +2493,7 @@ int vcap_mod_rule(struct vcap_rule *rule) err = vcap_write_counter(ri, &ctr); out: - mutex_unlock(&ri->admin->lock); + vcap_unlock(ri->admin); return err; } EXPORT_SYMBOL_GPL(vcap_mod_rule); @@ -2570,7 +2580,7 @@ int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id) admin->last_used_addr = elem->addr; } - mutex_unlock(&admin->lock); + vcap_unlock(admin); return err; } EXPORT_SYMBOL_GPL(vcap_del_rule); @@ -2585,7 +2595,7 @@ int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin) if (ret) return ret; - mutex_lock(&admin->lock); + vcap_lock(admin); list_for_each_entry_safe(ri, next_ri, &admin->rules, list) { vctrl->ops->init(ri->ndev, admin, ri->addr, ri->size); list_del(&ri->list); @@ -2598,7 +2608,7 @@ int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin) list_del(&eport->list); kfree(eport); } - mutex_unlock(&admin->lock); + vcap_unlock(admin); return 0; } @@ -3016,7 +3026,7 @@ static int vcap_enable_rules(struct vcap_control *vctrl, continue; /* Found the admin, now find the offloadable rules */ - mutex_lock(&admin->lock); + vcap_lock(admin); list_for_each_entry(ri, &admin->rules, list) { /* Is the rule in the lookup defined by the chain */ if (!(ri->data.vcap_chain_id >= chain && @@ -3034,7 +3044,7 @@ static int vcap_enable_rules(struct vcap_control *vctrl, if (err) break; } - mutex_unlock(&admin->lock); + vcap_unlock(admin); if (err) break; } @@ -3074,7 +3084,7 @@ static int vcap_disable_rules(struct vcap_control *vctrl, continue; /* Found the admin, now find the rules on the chain */ - mutex_lock(&admin->lock); + vcap_lock(admin); list_for_each_entry(ri, &admin->rules, list) { if (ri->data.vcap_chain_id != chain) continue; @@ -3089,7 +3099,7 @@ static int vcap_disable_rules(struct vcap_control *vctrl, if (err) break; } - mutex_unlock(&admin->lock); + vcap_unlock(admin); if (err) break; } @@ -3133,9 +3143,9 @@ static int vcap_enable(struct vcap_control *vctrl, struct net_device *ndev, eport->cookie = cookie; eport->src_cid = src_cid; eport->dst_cid = dst_cid; - mutex_lock(&admin->lock); + vcap_lock(admin); list_add_tail(&eport->list, &admin->enabled); - mutex_unlock(&admin->lock); + vcap_unlock(admin); if (vcap_path_exist(vctrl, ndev, src_cid)) { /* Enable chained lookups */ @@ -3185,9 +3195,9 @@ static int vcap_disable(struct vcap_control *vctrl, struct net_device *ndev, dst_cid = vcap_get_next_chain(vctrl, ndev, dst_cid); } - mutex_lock(&found->lock); + vcap_lock(found); list_del(&eport->list); - mutex_unlock(&found->lock); + vcap_unlock(found); kfree(eport); return 0; } @@ -3270,9 +3280,9 @@ int vcap_rule_set_counter(struct vcap_rule *rule, struct vcap_counter *ctr) return -EINVAL; } - mutex_lock(&ri->admin->lock); + vcap_lock(ri->admin); err = vcap_write_counter(ri, ctr); - mutex_unlock(&ri->admin->lock); + vcap_unlock(ri->admin); return err; } @@ -3291,9 +3301,9 @@ int vcap_rule_get_counter(struct vcap_rule *rule, struct vcap_counter *ctr) return -EINVAL; } - mutex_lock(&ri->admin->lock); + vcap_lock(ri->admin); err = vcap_read_counter(ri, ctr); - mutex_unlock(&ri->admin->lock); + vcap_unlock(ri->admin); return err; } @@ -3395,7 +3405,7 @@ int vcap_get_rule_count_by_cookie(struct vcap_control *vctrl, /* Iterate all rules in each VCAP instance */ list_for_each_entry(admin, &vctrl->list, list) { - mutex_lock(&admin->lock); + vcap_lock(admin); list_for_each_entry(ri, &admin->rules, list) { if (ri->data.cookie != cookie) continue; @@ -3412,12 +3422,12 @@ int vcap_get_rule_count_by_cookie(struct vcap_control *vctrl, if (err) goto unlock; } - mutex_unlock(&admin->lock); + vcap_unlock(admin); } return err; unlock: - mutex_unlock(&admin->lock); + vcap_unlock(admin); return err; } EXPORT_SYMBOL_GPL(vcap_get_rule_count_by_cookie); diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.h b/drivers/net/ethernet/microchip/vcap/vcap_api.h index 6069ad95c27e9..05b4b02e59ef5 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.h @@ -164,7 +164,7 @@ struct vcap_admin { struct list_head list; /* for insertion in vcap_control */ struct list_head rules; /* list of rules */ struct list_head enabled; /* list of enabled ports */ - struct mutex lock; /* control access to rules */ + struct vcap_control *vctrl; /* the control instance owning this vcap */ enum vcap_type vtype; /* type of vcap */ int vinst; /* instance number within the same type */ int first_cid; /* first chain id in this vcap */ @@ -275,6 +275,7 @@ struct vcap_control { const struct vcap_info *vcaps; /* client supplied vcap models */ const struct vcap_statistics *stats; /* client supplied vcap stats */ struct list_head list; /* list of vcap instances */ + struct mutex lock; /* serialize access to all vcap instances */ }; #endif /* __VCAP_API__ */ diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c index 59bfbda29bb3d..e0c65c7ab23e1 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c @@ -410,9 +410,9 @@ static int vcap_debugfs_show(struct seq_file *m, void *unused) }; int ret; - mutex_lock(&info->admin->lock); + vcap_lock(info->admin); ret = vcap_show_admin(info->vctrl, info->admin, &out); - mutex_unlock(&info->admin->lock); + vcap_unlock(info->admin); return ret; } DEFINE_SHOW_ATTRIBUTE(vcap_debugfs); @@ -427,9 +427,9 @@ static int vcap_raw_debugfs_show(struct seq_file *m, void *unused) }; int ret; - mutex_lock(&info->admin->lock); + vcap_lock(info->admin); ret = vcap_show_admin_raw(info->vctrl, info->admin, &out); - mutex_unlock(&info->admin->lock); + vcap_unlock(info->admin); return ret; } DEFINE_SHOW_ATTRIBUTE(vcap_raw_debugfs); diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c index 9c9d380421255..ac2a3b8c4f321 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c @@ -243,10 +243,11 @@ static void vcap_test_api_init(struct vcap_admin *admin) { /* Initialize the shared objects */ INIT_LIST_HEAD(&test_vctrl.list); + mutex_init(&test_vctrl.lock); INIT_LIST_HEAD(&admin->list); INIT_LIST_HEAD(&admin->rules); INIT_LIST_HEAD(&admin->enabled); - mutex_init(&admin->lock); + admin->vctrl = &test_vctrl; list_add_tail(&admin->list, &test_vctrl.list); memset(test_updateaddr, 0, sizeof(test_updateaddr)); test_updateaddridx = 0; diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c index ce26ccbdccdf9..83de384d3e3bd 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c @@ -233,10 +233,11 @@ static void vcap_test_api_init(struct vcap_admin *admin) { /* Initialize the shared objects */ INIT_LIST_HEAD(&test_vctrl.list); + mutex_init(&test_vctrl.lock); INIT_LIST_HEAD(&admin->list); INIT_LIST_HEAD(&admin->rules); INIT_LIST_HEAD(&admin->enabled); - mutex_init(&admin->lock); + admin->vctrl = &test_vctrl; list_add_tail(&admin->list, &test_vctrl.list); memset(test_updateaddr, 0, sizeof(test_updateaddr)); test_updateaddridx = 0; diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_private.h b/drivers/net/ethernet/microchip/vcap/vcap_api_private.h index 844bdf6b5f450..b4057fbe3d188 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_private.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_private.h @@ -50,6 +50,9 @@ struct vcap_stream_iter { /* Check that the control has a valid set of callbacks */ int vcap_api_check(struct vcap_control *ctrl); +/* Serialize access to the vcap instances of a control */ +void vcap_lock(struct vcap_admin *admin); +void vcap_unlock(struct vcap_admin *admin); /* Erase the VCAP cache area used or encoding and decoding */ void vcap_erase_cache(struct vcap_rule_internal *ri); From 982d6d6bc059c5dff37a2201c2f08c14bcfcbd20 Mon Sep 17 00:00:00 2001 From: Shigeru Yoshida Date: Wed, 1 Jul 2026 01:46:20 +0900 Subject: [PATCH 2121/2758] qede: fix off-by-one in BD ring consumption on build_skb failure [ Upstream commit a0a558ca7e75b49e71f8c545c30e8c005e6e4e2f ] qede_rx_build_skb() and qede_tpa_rx_build_skb() do not check for a NULL return from qede_build_skb(). When it returns NULL under memory pressure, the functions still consume a BD from the ring before returning NULL. The callers then recycle additional BDs, resulting in one extra BD being consumed (off-by-one). This desynchronizes the BD ring, which can corrupt DMA page reference counts and lead to SLUB freelist corruption. Commit 4e910dbe3650 ("qede: confirm skb is allocated before using") added a NULL check inside qede_build_skb() to prevent a NULL pointer dereference, but did not address the missing NULL checks in the callers, making this off-by-one reachable. Fix this by adding NULL checks for the return value of qede_build_skb() in both qede_rx_build_skb() and qede_tpa_rx_build_skb(), returning NULL immediately before any BD ring manipulation. Fixes: 8a8633978b84 ("qede: Add build_skb() support.") Signed-off-by: Shigeru Yoshida Reviewed-by: Jamie Bainbridge Link: https://patch.msgid.link/20260630164623.3152625-1-syoshida@redhat.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/qlogic/qede/qede_fp.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/ethernet/qlogic/qede/qede_fp.c b/drivers/net/ethernet/qlogic/qede/qede_fp.c index 33e18bb697740..c11e0d8f98aa5 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_fp.c +++ b/drivers/net/ethernet/qlogic/qede/qede_fp.c @@ -765,6 +765,9 @@ qede_tpa_rx_build_skb(struct qede_dev *edev, struct sk_buff *skb; skb = qede_build_skb(rxq, bd, len, pad); + if (unlikely(!skb)) + return NULL; + bd->page_offset += rxq->rx_buf_seg_size; if (bd->page_offset == PAGE_SIZE) { @@ -812,6 +815,8 @@ qede_rx_build_skb(struct qede_dev *edev, } skb = qede_build_skb(rxq, bd, len, pad); + if (unlikely(!skb)) + return NULL; if (unlikely(qede_realloc_rx_buffer(rxq, bd))) { /* Incr page ref count to reuse on allocation failure so From 231a8a4b76cb1b1827b3b19d7b3603642f5aaaef Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Tue, 30 Jun 2026 10:41:09 -0700 Subject: [PATCH 2122/2758] net: qualcomm: rmnet: validate MAP frame length before ingress parsing [ Upstream commit f0f1887a9e30712a1df03e152dce6fb91344b1f3 ] When ingress deaggregation is disabled, rmnet_map_ingress_handler() passes the skb straight to __rmnet_map_ingress_handler(), skipping the length validation that rmnet_map_deaggregate() performs on the aggregated path. The parser then dereferences the MAP header and csum header/trailer based on the on-wire pkt_len without checking skb->len, so a short frame is read out of bounds: BUG: KASAN: slab-out-of-bounds in rmnet_map_checksum_downlink_packet Read of size 1 at addr ffff88801118ed00 by task exploit/147 Call Trace: ... rmnet_map_checksum_downlink_packet (drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c:413) __rmnet_map_ingress_handler (drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c:96) rmnet_rx_handler (drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c:129) __netif_receive_skb_core.constprop.0 (net/core/dev.c:6089) netif_receive_skb (net/core/dev.c:6460) tun_get_user (drivers/net/tun.c:1955) tun_chr_write_iter (drivers/net/tun.c:2001) vfs_write (fs/read_write.c:688) ksys_write (fs/read_write.c:740) do_syscall_64 (arch/x86/entry/syscall_64.c:94) ... Factor that validation out of rmnet_map_deaggregate() into rmnet_map_validate_packet_len() and run it on the no-aggregation path too. The MAP header is bounds-checked first, since this path can receive a frame shorter than the header. Fixes: ceed73a2cf4a ("drivers: net: ethernet: qualcomm: rmnet: Initial implementation") Reported-by: Weiming Shi Suggested-by: Subash Abhinov Kasiviswanathan Signed-off-by: Xiang Mei Reviewed-by: Subash Abhinov Kasiviswanathan Link: https://patch.msgid.link/20260630174110.2003121-1-xmei5@asu.edu Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- .../ethernet/qualcomm/rmnet/rmnet_handlers.c | 5 +- .../net/ethernet/qualcomm/rmnet/rmnet_map.h | 1 + .../ethernet/qualcomm/rmnet/rmnet_map_data.c | 72 ++++++++++--------- 3 files changed, 45 insertions(+), 33 deletions(-) diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c index 9f3479500f85a..d055a2628d8c9 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c @@ -126,7 +126,10 @@ rmnet_map_ingress_handler(struct sk_buff *skb, consume_skb(skb); } else { - __rmnet_map_ingress_handler(skb, port); + if (rmnet_map_validate_packet_len(skb, port)) + __rmnet_map_ingress_handler(skb, port); + else + kfree_skb(skb); } } diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h index b70284095568c..60ca8b780c88a 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h @@ -59,5 +59,6 @@ void rmnet_map_tx_aggregate_init(struct rmnet_port *port); void rmnet_map_tx_aggregate_exit(struct rmnet_port *port); void rmnet_map_update_ul_agg_config(struct rmnet_port *port, u32 size, u32 count, u32 time); +u32 rmnet_map_validate_packet_len(struct sk_buff *skb, struct rmnet_port *port); #endif /* _RMNET_MAP_H_ */ diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c index 8b4640c5d61eb..305ae15ae8f39 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c @@ -333,54 +333,62 @@ struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb, return map_header; } -/* Deaggregates a single packet - * A whole new buffer is allocated for each portion of an aggregated frame. - * Caller should keep calling deaggregate() on the source skb until 0 is - * returned, indicating that there are no more packets to deaggregate. Caller - * is responsible for freeing the original skb. - */ -struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb, - struct rmnet_port *port) +u32 rmnet_map_validate_packet_len(struct sk_buff *skb, struct rmnet_port *port) { struct rmnet_map_v5_csum_header *next_hdr = NULL; struct rmnet_map_header *maph; void *data = skb->data; - struct sk_buff *skbn; - u8 nexthdr_type; u32 packet_len; - if (skb->len == 0) - return NULL; + if (skb->len < sizeof(*maph)) + return 0; maph = (struct rmnet_map_header *)skb->data; + + /* Some hardware can send us empty frames. Catch them */ + if (!maph->pkt_len) + return 0; + packet_len = ntohs(maph->pkt_len) + sizeof(*maph); if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) { packet_len += sizeof(struct rmnet_map_dl_csum_trailer); - } else if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV5) { - if (!(maph->flags & MAP_CMD_FLAG)) { - packet_len += sizeof(*next_hdr); - if (maph->flags & MAP_NEXT_HEADER_FLAG) - next_hdr = data + sizeof(*maph); - else - /* Mapv5 data pkt without csum hdr is invalid */ - return NULL; - } + } else if ((port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV5) && + !(maph->flags & MAP_CMD_FLAG)) { + /* Mapv5 data pkt without csum hdr is invalid */ + if (!(maph->flags & MAP_NEXT_HEADER_FLAG)) + return 0; + + packet_len += sizeof(*next_hdr); + next_hdr = data + sizeof(*maph); } - if (((int)skb->len - (int)packet_len) < 0) - return NULL; + if (skb->len < packet_len) + return 0; - /* Some hardware can send us empty frames. Catch them */ - if (!maph->pkt_len) - return NULL; + if (next_hdr && + u8_get_bits(next_hdr->header_info, MAPV5_HDRINFO_HDR_TYPE_FMASK) != + RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD) + return 0; - if (next_hdr) { - nexthdr_type = u8_get_bits(next_hdr->header_info, - MAPV5_HDRINFO_HDR_TYPE_FMASK); - if (nexthdr_type != RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD) - return NULL; - } + return packet_len; +} + +/* Deaggregates a single packet + * A whole new buffer is allocated for each portion of an aggregated frame. + * Caller should keep calling deaggregate() on the source skb until 0 is + * returned, indicating that there are no more packets to deaggregate. Caller + * is responsible for freeing the original skb. + */ +struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb, + struct rmnet_port *port) +{ + struct sk_buff *skbn; + u32 packet_len; + + packet_len = rmnet_map_validate_packet_len(skb, port); + if (!packet_len) + return NULL; skbn = alloc_skb(packet_len + RMNET_MAP_DEAGGR_SPACING, GFP_ATOMIC); if (!skbn) From 8e49cd891bda447c68122d672510a604a8bb6b24 Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Wed, 1 Jul 2026 12:19:12 -0400 Subject: [PATCH 2123/2758] net/sched: act_pedit: fix TOCTOU heap OOB write in tc offload [ Upstream commit 8b519cbcabe836a441369fbec1a8a6518a709251 ] There is a TOCTOU race condition in flower lockless approach between sizing a flow_rule buffer and filling it. zdi-disclosures@trendmicro.com reports: The cls_flower classifier operates with TCF_PROTO_OPS_DOIT_UNLOCKED (fl_change runs without RTNL), while RTM_NEWACTION holds RTNL, so the independent locking domains make the race reachable in practice. KASAN confirms: BUG: KASAN: slab-out-of-bounds in tcf_pedit_offload_act_setup+0x81b/0x930 Write of size 4 at addr ffff888001f27520 by task poc-toctou/312 The buggy address is located 0 bytes to the right of allocated 288-byte region [ffff888001f27400, ffff888001f27520) (cache kmalloc-512) Note: The result is a heap OOB write attacker-controlled content into the adjacent slab object (requires CAP_NET_ADMIN). The fix introduces reading tcfp_nkeys under act->tcfa_lock in all places using a new tcf_pedit_nkeys_locked() which replaces the old tcf_pedit_nkeys(). Additionally we close the remaining TOCTOU window between the sizing read and the fill reads by more careful accounting. Rather than silently truncating the key count, which leads to incorrect action semantics offloaded to hardware and secondary OOB writes if the remaining capacity is zero or consumed by prior actions, we enforce remaining capacity checks and return -ENOSPC if the required space exceeds the remaining capacity. Fixes: 71d0ed7079df ("net/act_pedit: Support using offset relative to the conventional network headers") Reported-by: zdi-disclosures@trendmicro.com Tested-by: Victor Nogueira Signed-off-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260701161912.125355-1-jhs@mojatatu.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- include/net/tc_act/tc_pedit.h | 18 ++++++++---------- net/sched/act_api.c | 13 +++++++++---- net/sched/act_pedit.c | 13 +++++++++++-- net/sched/cls_api.c | 22 +++++++++++++++++----- 4 files changed, 45 insertions(+), 21 deletions(-) diff --git a/include/net/tc_act/tc_pedit.h b/include/net/tc_act/tc_pedit.h index cb7b82f2cbc7f..97754ea0a8279 100644 --- a/include/net/tc_act/tc_pedit.h +++ b/include/net/tc_act/tc_pedit.h @@ -37,17 +37,15 @@ static inline bool is_tcf_pedit(const struct tc_action *a) return false; } -static inline int tcf_pedit_nkeys(const struct tc_action *a) +/* Must be called with act->tcfa_lock held to ensure consistency of parallel + * reads of the same action's pedit keys (e.g. flow_offload count vs fill). + * Note, this is only used for pedit offload. + */ +static inline int tcf_pedit_nkeys_locked(const struct tc_action *a) { - struct tcf_pedit_parms *parms; - int nkeys; - - rcu_read_lock(); - parms = to_pedit_parms(a); - nkeys = parms->tcfp_nkeys; - rcu_read_unlock(); - - return nkeys; + lockdep_assert_held(&a->tcfa_lock); + return rcu_dereference_protected(to_pedit(a)->parms, + lockdep_is_held(&a->tcfa_lock))->tcfp_nkeys; } static inline u32 tcf_pedit_htype(const struct tc_action *a, int index) diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 04ea11c90e033..b4a7e1c63cb87 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -148,10 +148,15 @@ static void offload_action_hw_count_dec(struct tc_action *act, static unsigned int tcf_offload_act_num_actions_single(struct tc_action *act) { - if (is_tcf_pedit(act)) - return tcf_pedit_nkeys(act); - else - return 1; + unsigned int count; + + if (is_tcf_pedit(act)) { + spin_lock_bh(&act->tcfa_lock); + count = tcf_pedit_nkeys_locked(act); + spin_unlock_bh(&act->tcfa_lock); + return count; + } + return 1; } static bool tc_act_skip_hw(u32 flags) diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index bd3b1da3cd63b..437a0563786b9 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -563,9 +563,18 @@ static int tcf_pedit_offload_act_setup(struct tc_action *act, void *entry_data, { if (bind) { struct flow_action_entry *entry = entry_data; + int nkeys = tcf_pedit_nkeys_locked(act); int k; - for (k = 0; k < tcf_pedit_nkeys(act); k++) { + /* If the required keys exceed the remaining capacity return + * -ENOSPC to abort the offload and fallback to software. + */ + if (nkeys > *index_inc) { + NL_SET_ERR_MSG_MOD(extack, "Not enough space to offload all pedit keys"); + return -ENOSPC; + } + + for (k = 0; k < nkeys; k++) { switch (tcf_pedit_cmd(act, k)) { case TCA_PEDIT_KEY_EX_CMD_SET: entry->id = FLOW_ACTION_MANGLE; @@ -602,7 +611,7 @@ static int tcf_pedit_offload_act_setup(struct tc_action *act, void *entry_data, return -EOPNOTSUPP; } - for (k = 1; k < tcf_pedit_nkeys(act); k++) { + for (k = 1; k < tcf_pedit_nkeys_locked(act); k++) { if (cmd != tcf_pedit_cmd(act, k)) { NL_SET_ERR_MSG_MOD(extack, "Unsupported pedit command offload"); return -EOPNOTSUPP; diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 3e67600a4a1a1..ffeea6db83374 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -3886,12 +3886,21 @@ int tc_setup_action(struct flow_action *flow_action, entry = &flow_action->entries[j]; spin_lock_bh(&act->tcfa_lock); + + /* Abort the offload if we have exhausted the allocated capacity */ + if (j >= flow_action->num_entries) { + NL_SET_ERR_MSG_MOD(extack, "Flow action buffer overflow"); + err = -ENOSPC; + goto err_out_locked; + } + err = tcf_act_get_user_cookie(entry, act); if (err) goto err_out_locked; - index = 0; - err = tc_setup_offload_act(act, entry, &index, extack); + index = flow_action->num_entries - j; + err = tc_setup_offload_act(act, entry, &index, + extack); if (err) goto err_out_locked; @@ -3945,10 +3954,13 @@ unsigned int tcf_exts_num_actions(struct tcf_exts *exts) int i; tcf_exts_for_each_action(i, act, exts) { - if (is_tcf_pedit(act)) - num_acts += tcf_pedit_nkeys(act); - else + if (is_tcf_pedit(act)) { + spin_lock_bh(&act->tcfa_lock); + num_acts += tcf_pedit_nkeys_locked(act); + spin_unlock_bh(&act->tcfa_lock); + } else { num_acts++; + } } return num_acts; } From ce5aa8084329351086894aa34d77e40301d5bd3d Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Tue, 30 Jun 2026 11:32:27 -0700 Subject: [PATCH 2124/2758] net/smc: fix UAF in smc_cdc_rx_handler() by pinning the socket [ Upstream commit 9d160b35cc34a2ba8229d07651468a7848325135 ] smc_cdc_rx_handler() looks up the connection by token under the link group's conns_lock, drops the lock, and then dereferences conn and the smc_sock derived from it, ending in sock_hold(&smc->sk) inside smc_cdc_msg_recv(). No reference is held across the lock release. The only reference pinning the socket while the connection is discoverable in the link group is taken in smc_lgr_register_conn() (sock_hold) and dropped in __smc_lgr_unregister_conn() (sock_put), both under conns_lock. Once the handler drops conns_lock, a concurrent close() -> smc_release() -> smc_conn_free() -> smc_lgr_unregister_conn() can drop that reference and free the smc_sock, so the handler's later sock_hold() runs on freed memory: WARNING: lib/refcount.c:25 at refcount_warn_saturate Workqueue: rxe_wq do_work refcount_warn_saturate (lib/refcount.c:25) smc_cdc_msg_recv (net/smc/smc_cdc.c:430) smc_cdc_rx_handler (net/smc/smc_cdc.c:502) smc_wr_rx_tasklet_fn (net/smc/smc_wr.c:445) tasklet_action_common (kernel/softirq.c:938) handle_softirqs (kernel/softirq.c:622) Kernel panic - not syncing: panic_on_warn set Only SMC-R is affected. The SMC-D receive tasklet is stopped by tasklet_kill(&conn->rx_tsklet) in smc_conn_free() before the connection is unregistered, so it cannot run concurrently with the free. Take the socket reference while still holding conns_lock, so the registration reference can no longer be the last one, and drop it once the handler is done. Fixes: d7b0e37c1ac1 ("net/smc: restructure CDC message reception") Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Link: https://patch.msgid.link/20260630183227.2044998-1-xmei5@asu.edu Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/smc/smc_cdc.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/net/smc/smc_cdc.c b/net/smc/smc_cdc.c index 619b3bab38248..32d6d03df3214 100644 --- a/net/smc/smc_cdc.c +++ b/net/smc/smc_cdc.c @@ -470,9 +470,9 @@ static void smc_cdc_rx_handler(struct ib_wc *wc, void *buf) { struct smc_link *link = (struct smc_link *)wc->qp->qp_context; struct smc_cdc_msg *cdc = buf; + struct smc_sock *smc = NULL; struct smc_connection *conn; struct smc_link_group *lgr; - struct smc_sock *smc; if (wc->byte_len < offsetof(struct smc_cdc_msg, reserved)) return; /* short message */ @@ -483,21 +483,26 @@ static void smc_cdc_rx_handler(struct ib_wc *wc, void *buf) lgr = smc_get_lgr(link); read_lock_bh(&lgr->conns_lock); conn = smc_lgr_find_conn(ntohl(cdc->token), lgr); - read_unlock_bh(&lgr->conns_lock); - if (!conn || conn->out_of_sync) + if (!conn || conn->out_of_sync) { + read_unlock_bh(&lgr->conns_lock); return; + } smc = container_of(conn, struct smc_sock, conn); + sock_hold(&smc->sk); + read_unlock_bh(&lgr->conns_lock); if (cdc->prod_flags.failover_validation) { smc_cdc_msg_validate(smc, cdc, link); - return; + goto out; } if (smc_cdc_before(ntohs(cdc->seqno), conn->local_rx_ctrl.seqno)) /* received seqno is old */ - return; + goto out; smc_cdc_msg_recv(smc, cdc); +out: + sock_put(&smc->sk); } static struct smc_wr_rx_handler smc_cdc_rx_handlers[] = { From 5a632f2f207e70e46c1b016d8d17bfd7d8ea1a8e Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 1 Jul 2026 12:23:29 +0000 Subject: [PATCH 2125/2758] amt: fix size calculation in amt_get_size() [ Upstream commit 9e05e91a9a847ed57926414bd7c2c5e54d6c56c6 ] amt_get_size() incorrectly used sizeof(struct iphdr) for the sizes of IFLA_AMT_DISCOVERY_IP, IFLA_AMT_REMOTE_IP, and IFLA_AMT_LOCAL_IP. These attributes contain IPv4 addresses (__be32), not full IP headers. Replace sizeof(struct iphdr) with sizeof(__be32) to avoid over-allocating netlink message space. Fixes: b9022b53adad ("amt: add control plane of amt interface") Signed-off-by: Eric Dumazet Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260701122329.3562825-1-edumazet@google.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/amt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/amt.c b/drivers/net/amt.c index c03aa7c207e64..0f4ff41d05352 100644 --- a/drivers/net/amt.c +++ b/drivers/net/amt.c @@ -3301,9 +3301,9 @@ static size_t amt_get_size(const struct net_device *dev) nla_total_size(sizeof(__u16)) + /* IFLA_AMT_GATEWAY_PORT */ nla_total_size(sizeof(__u32)) + /* IFLA_AMT_LINK */ nla_total_size(sizeof(__u32)) + /* IFLA_MAX_TUNNELS */ - nla_total_size(sizeof(struct iphdr)) + /* IFLA_AMT_DISCOVERY_IP */ - nla_total_size(sizeof(struct iphdr)) + /* IFLA_AMT_REMOTE_IP */ - nla_total_size(sizeof(struct iphdr)); /* IFLA_AMT_LOCAL_IP */ + nla_total_size(sizeof(__be32)) + /* IFLA_AMT_DISCOVERY_IP */ + nla_total_size(sizeof(__be32)) + /* IFLA_AMT_REMOTE_IP */ + nla_total_size(sizeof(__be32)); /* IFLA_AMT_LOCAL_IP */ } static int amt_fill_info(struct sk_buff *skb, const struct net_device *dev) From a1f2ada2e4d33d316939dedefbfd2e7d94a0c61f Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 2 Jul 2026 15:49:53 +0900 Subject: [PATCH 2126/2758] ksmbd: fix multichannel binding and enforce channel limit [ Upstream commit 4b706360ffb7e459cb3d3edae30b06a584f6eddd ] A signed multichannel SESSION_SETUP binding request can require multiple authentication rounds. ksmbd excludes SESSION_SETUP from the signed request check and tries to sign every binding response with the channel signing key. The channel does not exist for STATUS_MORE_PROCESSING_REQUIRED, so that response is sent unsigned. Clients reject it with STATUS_ACCESS_DENIED. The final channel signing key also needs the key exported by the binding authentication context. Keep that key in the channel instead of overwriting the established session key, and use the session signing key for intermediate and failed binding responses. Retain the binding session reference until an error response has been signed and sent. Limit a session to 32 channels while holding the channel lock. Return STATUS_INSUFFICIENT_RESOURCES for an additional binding, matching the server limit expected by clients. This fixes smb2.multichannel.generic.num_channels, which previously failed the first binding with STATUS_ACCESS_DENIED and returned the same status instead of STATUS_INSUFFICIENT_RESOURCES for channel 33. Fixes: f5a544e3bab7 ("ksmbd: add support for SMB3 multichannel") Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/server/auth.c | 47 +++++----- fs/smb/server/auth.h | 7 +- fs/smb/server/mgmt/user_session.c | 4 +- fs/smb/server/mgmt/user_session.h | 1 + fs/smb/server/smb2pdu.c | 142 +++++++++++++++++++----------- 5 files changed, 126 insertions(+), 75 deletions(-) diff --git a/fs/smb/server/auth.c b/fs/smb/server/auth.c index 86f521e849d5e..2c7096a782daa 100644 --- a/fs/smb/server/auth.c +++ b/fs/smb/server/auth.c @@ -133,16 +133,17 @@ static int calc_ntlmv2_hash(struct ksmbd_conn *conn, struct ksmbd_session *sess, * @blen: NTLMv2 blob length * @domain_name: domain name * @cryptkey: session crypto key + * @sess_key: derived session key output buffer * * Return: 0 on success, error number on error */ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, struct ntlmv2_resp *ntlmv2, int blen, char *domain_name, - char *cryptkey) + char *cryptkey, char *sess_key) { char ntlmv2_hash[CIFS_ENCPWD_SIZE]; char ntlmv2_rsp[CIFS_HMAC_MD5_HASH_SIZE]; - char sess_key[SMB2_NTLMV2_SESSKEY_SIZE]; + char base_key[SMB2_NTLMV2_SESSKEY_SIZE]; struct hmac_md5_ctx ctx; int rc; @@ -165,7 +166,7 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, /* Generate the session key */ hmac_md5_usingrawkey(ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE, ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE, - sess_key); + base_key); if (crypto_memneq(ntlmv2->ntlmv2_hash, ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE)) { @@ -173,12 +174,12 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, goto out; } - memcpy(sess->sess_key, sess_key, sizeof(sess_key)); + memcpy(sess_key, base_key, sizeof(base_key)); rc = 0; out: memzero_explicit(ntlmv2_hash, sizeof(ntlmv2_hash)); memzero_explicit(ntlmv2_rsp, sizeof(ntlmv2_rsp)); - memzero_explicit(sess_key, sizeof(sess_key)); + memzero_explicit(base_key, sizeof(base_key)); return rc; } @@ -189,12 +190,13 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, * @blob_len: length of the @authblob message * @conn: connection * @sess: session of connection + * @sess_key: derived session key output buffer * * Return: 0 on success, error number on error */ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, int blob_len, struct ksmbd_conn *conn, - struct ksmbd_session *sess) + struct ksmbd_session *sess, char *sess_key) { char *domain_name; unsigned int nt_off, dn_off; @@ -234,7 +236,7 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, ret = ksmbd_auth_ntlmv2(conn, sess, (struct ntlmv2_resp *)((char *)authblob + nt_off), nt_len - CIFS_ENCPWD_SIZE, - domain_name, conn->ntlmssp.cryptkey); + domain_name, conn->ntlmssp.cryptkey, sess_key); kfree(domain_name); if (ret) return ret; @@ -257,8 +259,8 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, if (!ctx_arc4) return -ENOMEM; - arc4_setkey(ctx_arc4, sess->sess_key, SMB2_NTLMV2_SESSKEY_SIZE); - arc4_crypt(ctx_arc4, sess->sess_key, + arc4_setkey(ctx_arc4, sess_key, SMB2_NTLMV2_SESSKEY_SIZE); + arc4_crypt(ctx_arc4, sess_key, (char *)authblob + sess_key_off, sess_key_len); kfree_sensitive(ctx_arc4); } @@ -400,7 +402,8 @@ ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, #ifdef CONFIG_SMB_SERVER_KERBEROS5 int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, - int in_len, char *out_blob, int *out_len) + int in_len, char *out_blob, int *out_len, + char *sess_key) { struct ksmbd_spnego_authen_response *resp; struct ksmbd_login_response_ext *resp_ext = NULL; @@ -455,7 +458,7 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, ksmbd_free_user(user); } - memcpy(sess->sess_key, resp->payload, resp->session_key_len); + memcpy(sess_key, resp->payload, resp->session_key_len); memcpy(out_blob, resp->payload + resp->session_key_len, resp->spnego_blob_len); *out_len = resp->spnego_blob_len; @@ -466,7 +469,8 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, } #else int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, - int in_len, char *out_blob, int *out_len) + int in_len, char *out_blob, int *out_len, + char *sess_key) { return -EOPNOTSUPP; } @@ -525,7 +529,7 @@ struct derivation { bool binding; }; -static void generate_key(struct ksmbd_conn *conn, struct ksmbd_session *sess, +static void generate_key(struct ksmbd_conn *conn, const char *sess_key, struct kvec label, struct kvec context, __u8 *key, unsigned int key_size) { @@ -536,7 +540,7 @@ static void generate_key(struct ksmbd_conn *conn, struct ksmbd_session *sess, unsigned char prfhash[SMB2_HMACSHA256_SIZE]; struct hmac_sha256_ctx ctx; - hmac_sha256_init_usingrawkey(&ctx, sess->sess_key, + hmac_sha256_init_usingrawkey(&ctx, sess_key, SMB2_NTLMV2_SESSKEY_SIZE); hmac_sha256_update(&ctx, i, 4); hmac_sha256_update(&ctx, label.iov_base, label.iov_len); @@ -559,18 +563,21 @@ static int generate_smb3signingkey(struct ksmbd_session *sess, const struct derivation *signing) { struct channel *chann; - char *key; + char *key, *sess_key; chann = lookup_chann_list(sess, conn); if (!chann) return 0; - if (conn->dialect >= SMB30_PROT_ID && signing->binding) + if (conn->dialect >= SMB30_PROT_ID && signing->binding) { key = chann->smb3signingkey; - else + sess_key = chann->sess_key; + } else { key = sess->smb3signingkey; + sess_key = sess->sess_key; + } - generate_key(conn, sess, signing->label, signing->context, key, + generate_key(conn, sess_key, signing->label, signing->context, key, SMB3_SIGN_KEY_SIZE); if (!(conn->dialect >= SMB30_PROT_ID && signing->binding)) @@ -627,11 +634,11 @@ static void generate_smb3encryptionkey(struct ksmbd_conn *conn, struct ksmbd_session *sess, const struct derivation_twin *ptwin) { - generate_key(conn, sess, ptwin->encryption.label, + generate_key(conn, sess->sess_key, ptwin->encryption.label, ptwin->encryption.context, sess->smb3encryptionkey, SMB3_ENC_DEC_KEY_SIZE); - generate_key(conn, sess, ptwin->decryption.label, + generate_key(conn, sess->sess_key, ptwin->decryption.label, ptwin->decryption.context, sess->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE); diff --git a/fs/smb/server/auth.h b/fs/smb/server/auth.h index 5767aabc63c9b..f14b7c0332646 100644 --- a/fs/smb/server/auth.h +++ b/fs/smb/server/auth.h @@ -41,17 +41,18 @@ int ksmbd_crypt_message(struct ksmbd_work *work, struct kvec *iov, void ksmbd_copy_gss_neg_header(void *buf); int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, struct ntlmv2_resp *ntlmv2, int blen, char *domain_name, - char *cryptkey); + char *cryptkey, char *sess_key); int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, int blob_len, struct ksmbd_conn *conn, - struct ksmbd_session *sess); + struct ksmbd_session *sess, char *sess_key); int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob, int blob_len, struct ksmbd_conn *conn); unsigned int ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, struct ksmbd_conn *conn); int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, - int in_len, char *out_blob, int *out_len); + int in_len, char *out_blob, int *out_len, + char *sess_key); void ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, int n_vec, char *sig); void ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c index de58aed76cb42..d6331184ebfcb 100644 --- a/fs/smb/server/mgmt/user_session.c +++ b/fs/smb/server/mgmt/user_session.c @@ -255,7 +255,7 @@ static void free_channel_list(struct ksmbd_session *sess) down_write(&sess->chann_lock); xa_for_each(&sess->ksmbd_chann_list, index, chann) { xa_erase(&sess->ksmbd_chann_list, index); - kfree(chann); + kfree_sensitive(chann); } xa_destroy(&sess->ksmbd_chann_list); @@ -449,7 +449,7 @@ static int ksmbd_chann_del(struct ksmbd_conn *conn, struct ksmbd_session *sess) if (!chann) return -ENOENT; - kfree(chann); + kfree_sensitive(chann); return 0; } diff --git a/fs/smb/server/mgmt/user_session.h b/fs/smb/server/mgmt/user_session.h index 6aebd385be847..8893a9aaede79 100644 --- a/fs/smb/server/mgmt/user_session.h +++ b/fs/smb/server/mgmt/user_session.h @@ -19,6 +19,7 @@ struct ksmbd_file_table; struct channel { + char sess_key[SMB2_NTLMV2_SESSKEY_SIZE]; __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; struct ksmbd_conn *conn; }; diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index e5e2bd7a67265..93fc8ea35baa0 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -89,6 +89,47 @@ struct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_conn return chann; } +#define KSMBD_MAX_CHANNELS 32 + +static int register_session_channel(struct ksmbd_session *sess, + struct ksmbd_conn *conn, + const char *sess_key) +{ + struct channel *chann, *old; + unsigned long index; + unsigned int count = 0; + int rc = 0; + + down_write(&sess->chann_lock); + if (xa_load(&sess->ksmbd_chann_list, (long)conn)) + goto out; + + xa_for_each(&sess->ksmbd_chann_list, index, chann) + count++; + if (count >= KSMBD_MAX_CHANNELS) { + rc = -ENOSPC; + goto out; + } + + chann = kmalloc_obj(struct channel, KSMBD_DEFAULT_GFP); + if (!chann) { + rc = -ENOMEM; + goto out; + } + + chann->conn = conn; + memcpy(chann->sess_key, sess_key, sizeof(chann->sess_key)); + old = xa_store(&sess->ksmbd_chann_list, (long)conn, chann, + KSMBD_DEFAULT_GFP); + if (xa_is_err(old)) { + kfree_sensitive(chann); + rc = xa_err(old); + } +out: + up_write(&sess->chann_lock); + return rc; +} + /** * smb2_get_ksmbd_tcon() - get tree connection information using a tree id. * @work: smb work @@ -1465,9 +1506,11 @@ static int ntlm_authenticate(struct ksmbd_work *work, { struct ksmbd_conn *conn = work->conn; struct ksmbd_session *sess = work->sess; - struct channel *chann = NULL, *old; struct ksmbd_user *user; + char channel_key[SMB2_NTLMV2_SESSKEY_SIZE] = {}; + char *auth_key = conn->binding ? channel_key : sess->sess_key; u64 prev_id; + bool binding = conn->binding; int sz, rc; ksmbd_debug(SMB, "authenticate phase\n"); @@ -1526,11 +1569,13 @@ static int ntlm_authenticate(struct ksmbd_work *work, sz = conn->mechTokenLen; else sz = le16_to_cpu(req->SecurityBufferLength); - rc = ksmbd_decode_ntlmssp_auth_blob(authblob, sz, conn, sess); + rc = ksmbd_decode_ntlmssp_auth_blob(authblob, sz, conn, sess, + auth_key); if (rc) { set_user_flag(sess->user, KSMBD_USER_FLAG_BAD_PASSWORD); ksmbd_debug(SMB, "authentication failed\n"); - return -EPERM; + rc = -EPERM; + goto out; } } @@ -1565,37 +1610,30 @@ static int ntlm_authenticate(struct ksmbd_work *work, binding_session: if (conn->dialect >= SMB30_PROT_ID) { - chann = lookup_chann_list(sess, conn); - if (!chann) { - chann = kmalloc_obj(struct channel, KSMBD_DEFAULT_GFP); - if (!chann) - return -ENOMEM; - - chann->conn = conn; - down_write(&sess->chann_lock); - old = xa_store(&sess->ksmbd_chann_list, (long)conn, chann, - KSMBD_DEFAULT_GFP); - up_write(&sess->chann_lock); - if (xa_is_err(old)) { - kfree(chann); - return xa_err(old); - } - } + rc = register_session_channel(sess, conn, auth_key); + if (rc) + goto out; } if (conn->ops->generate_signingkey) { rc = conn->ops->generate_signingkey(sess, conn); if (rc) { ksmbd_debug(SMB, "SMB3 signing key generation failed\n"); - return -EINVAL; + rc = -EINVAL; + goto out; } } if (!ksmbd_conn_lookup_dialect(conn)) { pr_err("fail to verify the dialect\n"); - return -ENOENT; + rc = -ENOENT; + goto out; } - return 0; + rc = 0; +out: + if (binding) + memzero_explicit(channel_key, sizeof(channel_key)); + return rc; } #ifdef CONFIG_SMB_SERVER_KERBEROS5 @@ -1606,8 +1644,10 @@ static int krb5_authenticate(struct ksmbd_work *work, struct ksmbd_conn *conn = work->conn; struct ksmbd_session *sess = work->sess; char *in_blob, *out_blob; - struct channel *chann = NULL, *old; + char channel_key[SMB2_NTLMV2_SESSKEY_SIZE] = {}; + char *auth_key = conn->binding ? channel_key : sess->sess_key; u64 prev_sess_id; + bool binding = conn->binding; int in_len, out_len; int retval; @@ -1620,10 +1660,11 @@ static int krb5_authenticate(struct ksmbd_work *work, (le16_to_cpu(rsp->SecurityBufferOffset) + 4); retval = ksmbd_krb5_authenticate(sess, in_blob, in_len, - out_blob, &out_len); + out_blob, &out_len, auth_key); if (retval) { ksmbd_debug(SMB, "krb5 authentication failed\n"); - return -EINVAL; + retval = -EINVAL; + goto out; } /* Check previous session */ @@ -1660,37 +1701,30 @@ static int krb5_authenticate(struct ksmbd_work *work, binding_session: if (conn->dialect >= SMB30_PROT_ID) { - chann = lookup_chann_list(sess, conn); - if (!chann) { - chann = kmalloc_obj(struct channel, KSMBD_DEFAULT_GFP); - if (!chann) - return -ENOMEM; - - chann->conn = conn; - down_write(&sess->chann_lock); - old = xa_store(&sess->ksmbd_chann_list, (long)conn, - chann, KSMBD_DEFAULT_GFP); - up_write(&sess->chann_lock); - if (xa_is_err(old)) { - kfree(chann); - return xa_err(old); - } - } + retval = register_session_channel(sess, conn, auth_key); + if (retval) + goto out; } if (conn->ops->generate_signingkey) { retval = conn->ops->generate_signingkey(sess, conn); if (retval) { ksmbd_debug(SMB, "SMB3 signing key generation failed\n"); - return -EINVAL; + retval = -EINVAL; + goto out; } } if (!ksmbd_conn_lookup_dialect(conn)) { pr_err("fail to verify the dialect\n"); - return -ENOENT; + retval = -ENOENT; + goto out; } - return 0; + retval = 0; +out: + if (binding) + memzero_explicit(channel_key, sizeof(channel_key)); + return retval; } #else static int krb5_authenticate(struct ksmbd_work *work, @@ -1912,7 +1946,7 @@ int smb2_sess_setup(struct ksmbd_work *work) rsp->hdr.Status = STATUS_REQUEST_NOT_ACCEPTED; else if (rc == -EFAULT) rsp->hdr.Status = STATUS_NETWORK_SESSION_EXPIRED; - else if (rc == -ENOMEM) + else if (rc == -ENOMEM || rc == -ENOSPC) rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; else if (rc == -EOPNOTSUPP) rsp->hdr.Status = STATUS_NOT_SUPPORTED; @@ -1951,8 +1985,16 @@ int smb2_sess_setup(struct ksmbd_work *work) sess->last_active = jiffies; sess->state = SMB2_SESSION_EXPIRED; } - ksmbd_user_session_put(sess); - work->sess = NULL; + /* + * Keep the binding session reference until the response is + * signed and sent. Error responses for a signed binding + * request are signed with the existing session signing key. + */ + if (!(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING) || + work->sess != sess) { + ksmbd_user_session_put(sess); + work->sess = NULL; + } if (try_delay) { ksmbd_conn_set_need_reconnect(conn); ssleep(5); @@ -9022,7 +9064,6 @@ bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command) if ((rcv_hdr2->Flags & SMB2_FLAGS_SIGNED) && command != SMB2_NEGOTIATE_HE && - command != SMB2_SESSION_SETUP_HE && command != SMB2_OPLOCK_BREAK_HE) return true; @@ -9171,13 +9212,14 @@ void smb3_set_sign_rsp(struct ksmbd_work *work) struct channel *chann; char signature[SMB2_CMACAES_SIZE]; struct kvec *iov; + u16 command = conn->ops->get_cmd_val(work); int n_vec = 1; char *signing_key; hdr = ksmbd_resp_buf_curr(work); - if (conn->binding == false && - le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { + if (command == SMB2_SESSION_SETUP_HE && + (!conn->binding || hdr->Status != STATUS_SUCCESS)) { signing_key = work->sess->smb3signingkey; } else { chann = lookup_chann_list(work->sess, work->conn); From 8d12d1fede47a9001a682a61108e07a5dc482e3f Mon Sep 17 00:00:00 2001 From: Steve French Date: Sun, 5 Jul 2026 16:04:09 -0500 Subject: [PATCH 2127/2758] smb: client: preserve leading slash for POSIX absolute symlink targets [ Upstream commit dce002f685e04e0c94e088cb66cb4021311cc6b5 ] When creating a native SMB symbolic link (CIFS_SYMLINK_TYPE_NATIVE) whose target is an absolute path on a mount that uses POSIX paths, the leading path separator was silently dropped from the stored symlink target. create_native_symlink() converted the target to UTF-16 with cifs_convert_path_to_utf16(). That helper was intended for share-relative SMB paths and therefore unconditionally strips a leading path separator. For an absolute POSIX symlink target the leading '/' is significant, so a target of "/foo/bar" was stored and read back as "foo/bar", even though the reparse point was still flagged as absolute (SYMLINK_FLAG_RELATIVE cleared). On a POSIX paths mount the symlink target is stored verbatim, so convert it directly with cifs_strndup_to_utf16() instead. This preserves the leading separator, avoids the leading-backslash stripping that cifs_convert_path_to_utf16() also performs (a backslash is a valid POSIX filename character), and uses NO_MAP_UNI_RSVD to match the readback path in smb2_parse_native_symlink(), which always converts the target with cifs_strndup_from_utf16() / NO_MAP_UNI_RSVD. This mirrors how the NFS and WSL reparse symlink creators convert their targets. The NT-style absolute symlink handling, which needs the "\??\" prefix and drive-letter colon preserved, continues to use cifs_convert_path_to_utf16() together with the existing masking of those bytes. Fixes: 12b466eb52d9 ("cifs: Fix creating and resolving absolute NT-style symlinks") Reviewed-by: Paulo Alcantara (Red Hat) Acked-by: Ralph Boehme Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/client/reparse.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c index cd1e1eaee67a6..5cc5b0410d489 100644 --- a/fs/smb/client/reparse.c +++ b/fs/smb/client/reparse.c @@ -67,6 +67,7 @@ static int create_native_symlink(const unsigned int xid, struct inode *inode, char *sym = NULL; struct kvec iov; bool directory; + int path_len; int rc = 0; if (strlen(symname) > REPARSE_SYM_PATH_MAX) @@ -168,7 +169,21 @@ static int create_native_symlink(const unsigned int xid, struct inode *inode, if (!(sbflags & CIFS_MOUNT_POSIX_PATHS) && symname[0] == '/') sym[0] = sym[1] = sym[2] = sym[5] = '_'; - path = cifs_convert_path_to_utf16(sym, cifs_sb); + /* + * On a POSIX paths mount the symlink target is stored verbatim, so + * convert it with cifs_strndup_to_utf16(). cifs_convert_path_to_utf16() + * must not be used here: it strips a leading path separator (it is + * meant for share-relative SMB paths), which would corrupt an absolute + * POSIX symlink target such as "/foo/bar". Using NO_MAP_UNI_RSVD also + * matches the readback path in smb2_parse_native_symlink(). + */ + if (sbflags & CIFS_MOUNT_POSIX_PATHS) + path = cifs_strndup_to_utf16(sym, strlen(sym), &path_len, + cifs_sb->local_nls, + NO_MAP_UNI_RSVD); + else + path = cifs_convert_path_to_utf16(sym, cifs_sb); + if (!path) { rc = -ENOMEM; goto out; From 0b98a503ed1ead044dfc81dc26c00cbe32f41dc7 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 30 Jun 2026 16:28:16 +0200 Subject: [PATCH 2128/2758] gpio: shared: make the voting mechanism adaptable [ Upstream commit b30973e8c3920ddfa9255a959b19057515b4e5e8 ] The current voting mechanism in GPIO shared proxy assumes that "low" is always the default value and users can only vote for driving the GPIO "high" in which case it will remain high as long as there's at least one user voting. This makes it impossible to use the automatic sharing management for certain use-cases such as the write-protect GPIOs of EEPROMs which are requested "high" and driven "low" to enable writing. In this case, if the WP GPIO is shared by multiple EEPROMs, and at least one of them wants to enable writing, the pin must be set to "low". Modify the voting heuristic to assume the value set by the first user on request to be the "default" and subseqent calls to gpiod_set_value() will constitute votes for a change of the value to the opposite. In the wp-gpios case it will mean that the nvmem core requests the GPIO as "out-high" for all EEPROMs sharing the pin, and when one of them wants to write, the pin will be driven low, enabling it. Fixes: e992d54c6f97 ("gpio: shared-proxy: implement the shared GPIO proxy driver") Reported-by: Marek Vasut Closes: https://lore.kernel.org/all/20260511163518.51104-1-marex@nabladev.com/ Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260630-gpio-shared-dynamic-voting-v3-1-8ecf0542953b@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpio-shared-proxy.c | 66 ++++++++++++++++---------------- drivers/gpio/gpiolib-shared.h | 3 +- 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/drivers/gpio/gpio-shared-proxy.c b/drivers/gpio/gpio-shared-proxy.c index 10ca2ef77ef34..52a366f0ec4de 100644 --- a/drivers/gpio/gpio-shared-proxy.c +++ b/drivers/gpio/gpio-shared-proxy.c @@ -22,7 +22,7 @@ struct gpio_shared_proxy_data { struct gpio_chip gc; struct gpio_shared_desc *shared_desc; struct device *dev; - bool voted_high; + bool voted_change; }; static int @@ -34,52 +34,54 @@ gpio_shared_proxy_set_unlocked(struct gpio_shared_proxy_data *proxy, int value) lockdep_assert_held(&shared_desc->mutex); - if (value) { - /* User wants to set value to high. */ - if (proxy->voted_high) - /* Already voted for high, nothing to do. */ + if (value != shared_desc->def_val) { + /* User wants to vote for a value change. */ + if (proxy->voted_change) + /* Already voted for a change, nothing to do. */ goto out; - /* Haven't voted for high yet. */ - if (!shared_desc->highcnt) { + /* Haven't voted for a value change yet. */ + if (!shared_desc->votecnt) { /* - * Current value is low, need to actually set value - * to high. + * Current value is default, need to actually set value + * to the opposite. */ - ret = gpiod_set_value_cansleep(desc, 1); + ret = gpiod_set_value_cansleep(desc, value); if (ret) goto out; } - shared_desc->highcnt++; - proxy->voted_high = true; + shared_desc->votecnt++; + proxy->voted_change = true; goto out; } - /* Desired value is low. */ - if (!proxy->voted_high) - /* We didn't vote for high, nothing to do. */ + /* Desired value is the default. */ + if (!proxy->voted_change) + /* We didn't vote for change previously, nothing to do. */ goto out; - /* We previously voted for high. */ - if (shared_desc->highcnt == 1) { - /* This is the last remaining vote for high, set value to low. */ - ret = gpiod_set_value_cansleep(desc, 0); + /* We previously voted for change. */ + if (shared_desc->votecnt == 1) { + /* This is the last remaining vote for change, set value to default. */ + ret = gpiod_set_value_cansleep(desc, shared_desc->def_val); if (ret) goto out; } - shared_desc->highcnt--; - proxy->voted_high = false; + shared_desc->votecnt--; + proxy->voted_change = false; out: - if (shared_desc->highcnt) + if (shared_desc->votecnt) dev_dbg(proxy->dev, - "Voted for value '%s', effective value is 'high', number of votes for 'high': %u\n", - str_high_low(value), shared_desc->highcnt); + "Voted for value '%s', effective value is '%s', number of votes: %u\n", + str_high_low(value), str_high_low(!shared_desc->def_val), + shared_desc->votecnt); else - dev_dbg(proxy->dev, "Voted for value 'low', effective value is 'low'\n"); + dev_dbg(proxy->dev, "Voted for value '%s', effective value is '%s'\n", + str_high_low(value), str_high_low(shared_desc->def_val)); return ret; } @@ -107,8 +109,8 @@ static void gpio_shared_proxy_free(struct gpio_chip *gc, unsigned int offset) guard(mutex)(&shared_desc->mutex); - if (proxy->voted_high) { - ret = gpio_shared_proxy_set_unlocked(proxy, 0); + if (proxy->voted_change) { + ret = gpio_shared_proxy_set_unlocked(proxy, shared_desc->def_val); if (ret) dev_err(proxy->dev, "Failed to unset the shared GPIO value on release: %d\n", ret); @@ -197,13 +199,9 @@ static int gpio_shared_proxy_direction_output(struct gpio_chip *gc, if (ret) return ret; - if (value) { - proxy->voted_high = true; - shared_desc->highcnt = 1; - } else { - proxy->voted_high = false; - shared_desc->highcnt = 0; - } + shared_desc->def_val = value; + shared_desc->votecnt = 0; + proxy->voted_change = false; return 0; } diff --git a/drivers/gpio/gpiolib-shared.h b/drivers/gpio/gpiolib-shared.h index bbdc0ab7b647a..618756f6c6aaf 100644 --- a/drivers/gpio/gpiolib-shared.h +++ b/drivers/gpio/gpiolib-shared.h @@ -41,7 +41,8 @@ struct gpio_shared_desc { struct gpio_desc *desc; unsigned long cfg; unsigned int usecnt; - unsigned int highcnt; + unsigned int votecnt; + int def_val; struct mutex mutex; /* serializes all proxy operations on this descriptor */ }; From b601c031d4fa30b789cbdcc443a85dd992236324 Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Wed, 1 Jul 2026 14:45:03 +0530 Subject: [PATCH 2129/2758] drm/i915/ltphy: Fix SSC Enablement bit in PORT_CLOCK_CTL [ Upstream commit e91da9053006da622b865e672a4a37439a3bb88b ] According to Bspec we only need to write SSC Enable PLL A bit and leave PLL B bit alone in PORT_CLOCK_CTL register. Bspec: 74667, 74492 Fixes: 3383ba2479f7 ("drm/i915/ltphy: Enable SSC during port clock programming") Signed-off-by: Suraj Kandpal Reviewed-by: Ankit Nautiyal Link: https://patch.msgid.link/20260701091503.1302226-3-suraj.kandpal@intel.com (cherry picked from commit 8e27f752037e72ccee9c4a7c4a6202ecf3daf603) Signed-off-by: Rodrigo Vivi Signed-off-by: Sasha Levin --- drivers/gpu/drm/i915/display/intel_lt_phy.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index 657ad5cb0eff9..af5391ba855f1 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -1223,11 +1223,7 @@ intel_lt_phy_program_port_clock_ctl(struct intel_encoder *encoder, else val |= XELPDP_DDI_CLOCK_SELECT_PREP(display, XELPDP_DDI_CLOCK_SELECT_MAXPCLK); - /* DP2.0 10G and 20G rates enable MPLLA*/ - if (port_clock == 1000000 || port_clock == 2000000) - val |= XELPDP_SSC_ENABLE_PLLA; - else - val |= ltpll->ssc_enabled ? XELPDP_SSC_ENABLE_PLLB : 0; + val |= ltpll->ssc_enabled ? XELPDP_SSC_ENABLE_PLLA : 0; intel_de_rmw(display, XELPDP_PORT_CLOCK_CTL(display, encoder->port), XELPDP_LANE1_PHY_CLOCK_SELECT | XELPDP_FORWARD_CLOCK_UNGATE | From feb3fc2c38ed52003142f31e04109719b200c049 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Wed, 24 Jun 2026 00:12:29 +0800 Subject: [PATCH 2130/2758] Bluetooth: 6lowpan: avoid untracked enable work [ Upstream commit 352a59dc1f4a41314b6f827c17e16af7ca88271a ] lowpan_enable_set() allocates a temporary work item and schedules do_enable_set() on system_wq, then returns to debugfs. The debugfs active operation has ended at that point, but the worker still executes module text and manipulates enable_6lowpan and listen_chan. bt_6lowpan_exit() removes the debugfs files and immediately closes and puts listen_chan. It has no pointer to the queued work item, so it cannot cancel or flush it before tearing down the state that the worker uses. The buggy scenario involves two paths, with each column showing the order within that path: debugfs enable write module exit 1. lowpan_enable_set() allocates 1. bt_6lowpan_exit() removes set_enable work the debugfs file 2. schedule_work() queues 2. bt_6lowpan_exit() closes do_enable_set() and puts listen_chan 3. the write operation returns 3. module teardown can continue 4. do_enable_set() later runs against stale state Run the enable state transition synchronously in lowpan_enable_set() instead. The simple debugfs setter can sleep, and this file already handles the 6LoWPAN control write synchronously under the same set_lock. Once the setter returns, debugfs removal covers the whole operation and exit can no longer race with an untracked work item. Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in do_enable_set+0x113/0x2e0 Workqueue: events do_enable_set [bluetooth_6lowpan] The buggy address belongs to the object at ffff888109cb8000 Fixes: 90305829635d ("Bluetooth: 6lowpan: Converting rwlocks to use RCU") Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/6lowpan.c | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 4dbbc44a950d5..88f896401d7e0 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -1061,23 +1061,15 @@ static void disconnect_all_peers(void) } while (nchans); } -struct set_enable { - struct work_struct work; - bool flag; -}; - -static void do_enable_set(struct work_struct *work) +static void do_enable_set(bool flag) { - struct set_enable *set_enable = container_of(work, - struct set_enable, work); - - if (!set_enable->flag || enable_6lowpan != set_enable->flag) + if (!flag || enable_6lowpan != flag) /* Disconnect existing connections if 6lowpan is * disabled */ disconnect_all_peers(); - enable_6lowpan = set_enable->flag; + enable_6lowpan = flag; mutex_lock(&set_lock); if (listen_chan) { @@ -1089,22 +1081,11 @@ static void do_enable_set(struct work_struct *work) listen_chan = bt_6lowpan_listen(); mutex_unlock(&set_lock); - - kfree(set_enable); } static int lowpan_enable_set(void *data, u64 val) { - struct set_enable *set_enable; - - set_enable = kzalloc_obj(*set_enable); - if (!set_enable) - return -ENOMEM; - - set_enable->flag = !!val; - INIT_WORK(&set_enable->work, do_enable_set); - - schedule_work(&set_enable->work); + do_enable_set(!!val); return 0; } From 32c48c7f6cc8c7888e46a8c81622154ccafda5d2 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Wed, 24 Jun 2026 00:12:59 +0800 Subject: [PATCH 2131/2758] Bluetooth: 6lowpan: hold L2CAP conn across debugfs control [ Upstream commit 518aa9505fa10ea5662349e5d2efd8c9e32a820b ] get_l2cap_conn() looks up an LE hci_conn under hdev protection, but then drops that protection before reading hcon->l2cap_data and before lowpan_control_write() later dereferences conn->hcon. A disconnect or device close can tear down the same L2CAP connection in that window. The buggy scenario involves two paths, with each column showing the order within that path: 6LoWPAN control write: HCI disconnect/device close: 1. get_l2cap_conn() finds hcon 1. hci_disconn_cfm() dispatches and hcon->l2cap_data. the L2CAP disconnect callback. 2. get_l2cap_conn() drops hdev 2. l2cap_conn_del() clears protection and returns conn. hcon->l2cap_data and drops the L2CAP connection reference. 3. lowpan_control_write() reads 3. hci_conn_del() removes and drops conn->hcon. the HCI connection. Take a reference to the L2CAP connection with l2cap_conn_hold_unless_zero() while hdev is still locked, and drop that reference after the debugfs command's last use of conn. This mirrors the existing L2CAP ACL receive-side handoff and keeps the connection dereferenceable after leaving hdev protection. Export the existing helper so the bluetooth_6lowpan module can use the same lifetime primitive. Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in lowpan_control_write+0x374/0x520 The buggy address belongs to the object at ffff888111b9d000 which belongs to the cache kmalloc-1k of size 1024 The buggy address is located 0 bytes inside of freed 1024-byte region [ffff888111b9d000, ffff888111b9d400) Read of size 8 Call trace: dump_stack_lvl+0x66/0xa0 print_report+0xce/0x5f0 lowpan_control_write+0x374/0x520 (net/bluetooth/6lowpan.c:1131) srso_alias_return_thunk+0x5/0xfbef5 __virt_addr_valid+0x19f/0x330 kasan_report+0xe0/0x110 __debugfs_file_get+0xf7/0x400 full_proxy_write+0x9e/0xd0 vfs_write+0x1b0/0x810 ksys_write+0xd2/0x170 dnotify_flush+0x32/0x220 do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f Allocated by task stack: kasan_save_stack+0x33/0x60 kasan_save_track+0x17/0x60 __kasan_kmalloc+0xaa/0xb0 l2cap_conn_add+0x45/0x520 l2cap_chan_connect+0xac6/0xd90 l2cap_sock_connect+0x216/0x350 __sys_connect+0x101/0x130 __x64_sys_connect+0x40/0x50 do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task stack: kasan_save_stack+0x33/0x60 kasan_save_track+0x17/0x60 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 kfree+0x313/0x590 hci_conn_hash_flush+0xc0/0x140 hci_dev_close_sync+0x41a/0xb00 hci_dev_close+0x12f/0x160 hci_sock_ioctl+0x157/0x570 sock_do_ioctl+0xf7/0x210 sock_ioctl+0x32f/0x490 __x64_sys_ioctl+0xc7/0x110 do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f kasan_record_aux_stack+0xa7/0xc0 insert_work+0x32/0x100 __queue_work+0x262/0xa60 queue_work_on+0xad/0xb0 l2cap_connect_cfm+0x4ef/0x670 hci_le_remote_feat_complete_evt+0x247/0x430 hci_event_packet+0x360/0x6f0 hci_rx_work+0x2ae/0x7a0 process_one_work+0x4fd/0xbc0 worker_thread+0x2d8/0x570 kthread+0x1ad/0x1f0 ret_from_fork+0x3c9/0x540 ret_from_fork_asm+0x1a/0x30 Fixes: 6b8d4a6a0314 ("Bluetooth: 6LoWPAN: Use connected oriented channel instead of fixed one") Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/6lowpan.c | 21 +++++++++++++++------ net/bluetooth/l2cap_core.c | 1 + 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 88f896401d7e0..e0a47b855928d 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -997,16 +997,19 @@ static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type, hci_dev_lock(hdev); hcon = hci_conn_hash_lookup_le(hdev, addr, le_addr_type); - hci_dev_unlock(hdev); - hci_dev_put(hdev); - - if (!hcon) + if (!hcon) { + hci_dev_unlock(hdev); + hci_dev_put(hdev); return -ENOENT; + } - *conn = (struct l2cap_conn *)hcon->l2cap_data; + *conn = l2cap_conn_hold_unless_zero(hcon->l2cap_data); BT_DBG("conn %p dst %pMR type %u", *conn, &hcon->dst, hcon->dst_type); + hci_dev_unlock(hdev); + hci_dev_put(hdev); + return 0; } @@ -1134,18 +1137,22 @@ static ssize_t lowpan_control_write(struct file *fp, if (conn) { struct lowpan_peer *peer; - if (!is_bt_6lowpan(conn->hcon)) + if (!is_bt_6lowpan(conn->hcon)) { + l2cap_conn_put(conn); return -EINVAL; + } peer = lookup_peer(conn); if (peer) { BT_DBG("6LoWPAN connection already exists"); + l2cap_conn_put(conn); return -EALREADY; } BT_DBG("conn %p dst %pMR type %d user %u", conn, &conn->hcon->dst, conn->hcon->dst_type, addr_type); + l2cap_conn_put(conn); } ret = bt_6lowpan_connect(&addr, addr_type); @@ -1161,6 +1168,8 @@ static ssize_t lowpan_control_write(struct file *fp, return ret; ret = bt_6lowpan_disconnect(conn, addr_type); + if (conn) + l2cap_conn_put(conn); if (ret < 0) return ret; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 32f0c8b73b17f..4b8246b297113 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -7714,6 +7714,7 @@ struct l2cap_conn *l2cap_conn_hold_unless_zero(struct l2cap_conn *c) return c; } +EXPORT_SYMBOL(l2cap_conn_hold_unless_zero); int l2cap_recv_acldata(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags) From dbd935a9e056545721bc4e9ce518c775d787b21e Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Wed, 24 Jun 2026 00:13:28 +0800 Subject: [PATCH 2132/2758] Bluetooth: MGMT: Fix adv monitor add failure cleanup [ Upstream commit 384a4b2fef9ffe5e270ee5558975c0504881c5fb ] hci_add_adv_monitor() publishes a new adv_monitor in hdev->adv_monitors_idr before the powered MSFT setup step. The MSFT offload add path can then fail either locally before the controller add command completes, or in the MSFT add callback. In the current queued management add flow, hci_cmd_sync_work() still invokes mgmt_add_adv_patterns_monitor_complete() with the original pending command after msft_add_monitor_pattern() returns. The buggy scenario involves two paths, with each column showing the order within that path: MSFT add handling MGMT completion 1. insert monitor and handle 1. receive sync error 2. send MSFT add command 2. call add-monitor completion 3. callback sees bad response 3. load cmd->user_data 4. callback frees monitor 4. read monitor->handle Local MSFT setup failures have the other half of the same ownership bug: they return an error after the IDR insertion, but no later code removes the failed monitor from the IDR. Keep ownership with the pending management command until its completion. For normal management adds, the MSFT add callback now records successful controller state and returns errors to its caller. The management completion frees the monitor on non-success after copying the response handle, while resume/reregister callback-error cleanup remains in the MSFT callback. The success path keeps the existing bookkeeping. Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in mgmt_add_adv_patterns_monitor_complete+0xfb/0x260 [bluetooth] Call Trace: dump_stack_lvl+0x66/0xa0 print_report+0xce/0x5f0 ? mgmt_add_adv_patterns_monitor_complete+0xfb/0x260 [bluetooth] ? srso_alias_return_thunk+0x5/0xfbef5 ? __virt_addr_valid+0x19f/0x330 ? mgmt_add_adv_patterns_monitor_complete+0xfb/0x260 [bluetooth] kasan_report+0xe0/0x110 ? mgmt_add_adv_patterns_monitor_complete+0xfb/0x260 [bluetooth] mgmt_add_adv_patterns_monitor_complete+0xfb/0x260 [bluetooth] ? srso_alias_return_thunk+0x5/0xfbef5 ? 0xffffffffc00d00da ? __pfx_mgmt_add_adv_patterns_monitor_complete+0x10/0x10 [bluetooth] ? __pfx_mgmt_add_adv_patterns_monitor_complete+0x10/0x10 [bluetooth] ? hci_cmd_sync_work+0x1ab/0x210 [bluetooth] hci_cmd_sync_work+0x1c0/0x210 [bluetooth] ? __pfx_mgmt_add_adv_patterns_monitor_complete+0x10/0x10 [bluetooth] process_one_work+0x4fd/0xbc0 ? __pfx_process_one_work+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? srso_alias_return_thunk+0x5/0xfbef5 ? __list_add_valid_or_report+0x37/0xf0 ? __pfx_hci_cmd_sync_work+0x10/0x10 [bluetooth] ? srso_alias_return_thunk+0x5/0xfbef5 worker_thread+0x2d8/0x570 ? __pfx_worker_thread+0x10/0x10 kthread+0x1ad/0x1f0 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x3c9/0x540 ? __pfx_ret_from_fork+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __switch_to+0x2e9/0x730 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1a/0x30 Allocated by task 471 on cpu 3 at 285.205389s: kasan_save_stack+0x33/0x60 kasan_save_track+0x17/0x60 __kasan_kmalloc+0xaa/0xb0 add_adv_patterns_monitor_rssi+0xd5/0x230 [bluetooth] hci_sock_sendmsg+0x96b/0xf80 [bluetooth] __sys_sendto+0x2bc/0x2d0 __x64_sys_sendto+0x76/0x90 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 454 on cpu 2 at 285.217112s: kasan_save_stack+0x33/0x60 kasan_save_track+0x17/0x60 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 kfree+0x313/0x590 msft_add_monitor_sync+0x54a/0x570 [bluetooth] hci_add_adv_monitor+0x133/0x180 [bluetooth] hci_cmd_sync_work+0x187/0x210 [bluetooth] process_one_work+0x4fd/0xbc0 worker_thread+0x2d8/0x570 kthread+0x1ad/0x1f0 ret_from_fork+0x3c9/0x540 ret_from_fork_asm+0x1a/0x30 Fixes: a2a4dedf88ab ("Bluetooth: advmon offload MSFT add monitor") Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/mgmt.c | 2 ++ net/bluetooth/msft.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 7c164a17ef59e..e8544194a5242 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5378,6 +5378,8 @@ static void mgmt_add_adv_patterns_monitor_complete(struct hci_dev *hdev, if (monitor->state == ADV_MONITOR_STATE_NOT_REGISTERED) monitor->state = ADV_MONITOR_STATE_REGISTERED; hci_update_passive_scan(hdev); + } else { + hci_free_adv_monitor(hdev, monitor); } mgmt_cmd_complete(cmd->sk, cmd->hdev->id, cmd->opcode, diff --git a/net/bluetooth/msft.c b/net/bluetooth/msft.c index 2f008167cbaa2..d7badce8746ca 100644 --- a/net/bluetooth/msft.c +++ b/net/bluetooth/msft.c @@ -291,7 +291,7 @@ static int msft_le_monitor_advertisement_cb(struct hci_dev *hdev, u16 opcode, monitor->state = ADV_MONITOR_STATE_OFFLOADED; unlock: - if (status) + if (status && msft->resuming) hci_free_adv_monitor(hdev, monitor); hci_dev_unlock(hdev); From a50da115b5888062d32c86a548a29b4e2559d7ec Mon Sep 17 00:00:00 2001 From: Sungwoo Kim Date: Wed, 24 Jun 2026 17:33:04 -0400 Subject: [PATCH 2133/2758] Bluetooth: sco: Fix a race condition in sco_sock_timeout() [ Upstream commit 0f8a5dcc66648b6e1458a9f3ba4c5a0463a228fc ] sco_sock_timeout() runs asynchronously and lock_sock(sk). If the socket is closing while the timer is running, it holds the same lock (lock_sock(sk)) twice, leading to a deadlock. CPU 0 CPU 1 ==================== ====================== sco_sock_close() sco_sock_timeout() lock_sock(sk) // <-- LOCK __sco_sock_close() sco_chan_del() sco_conn_put() sco_conn_free() disable_delayed_work_sync() lock(sk) // <-- SAME LOCK Fix this by moving disable_delayed_work_sync() outside of lock_sock(sk), ensuring that no lock_sock(sk) is held before sco_sock_timeout(). Lockdep splat: WARNING: possible circular locking dependency detected 6.13.0-rc4 #7 Not tainted syz-executor292/9514 is trying to acquire lock: ffff8881115d5070 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}, at: rcu_lock_acquire sect/v6.13-rc4/./include/linux/rcupdate.h:337 [inline] ffff8881115d5070 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}, at: rcu_read_lock sect/v6.13-rc4/./include/linux/rcupdate.h:849 [inline] ffff8881115d5070 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}, at: start_flush_work sect/v6.13-rc4/kernel/workqueue.c:4137 [inline] ffff8881115d5070 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}, at: __flush_work+0xd1/0xc40 sect/v6.13-rc4/kernel/workqueue.c:4195 but task is already holding lock: ffff88807db3a258 (sk_lock-AF_BLUETOOTH-BTPROTO_SCO){+.+.}-{0:0}, at: lock_sock sect/v6.13-rc4/./include/net/sock.h:1623 [inline] ffff88807db3a258 (sk_lock-AF_BLUETOOTH-BTPROTO_SCO){+.+.}-{0:0}, at: sco_sock_close+0x25/0x100 sect/v6.13-rc4/net/bluetooth/sco.c:524 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #1 (sk_lock-AF_BLUETOOTH-BTPROTO_SCO){+.+.}-{0:0}: lock_acquire+0x1c4/0x520 sect/v6.13-rc4/kernel/locking/lockdep.c:5849 lock_sock_nested+0x48/0x130 sect/v6.13-rc4/net/core/sock.c:3622 lock_sock sect/v6.13-rc4/./include/net/sock.h:1623 [inline] sco_sock_timeout+0xbe/0x270 sect/v6.13-rc4/net/bluetooth/sco.c:158 process_one_work sect/v6.13-rc4/kernel/workqueue.c:3229 [inline] process_scheduled_works+0xa99/0x18f0 sect/v6.13-rc4/kernel/workqueue.c:3310 worker_thread+0x8a9/0xd80 sect/v6.13-rc4/kernel/workqueue.c:3391 kthread+0x2c6/0x360 sect/v6.13-rc4/kernel/kthread.c:389 ret_from_fork+0x4e/0x80 sect/v6.13-rc4/arch/x86/kernel/process.c:147 ret_from_fork_asm+0x1a/0x30 sect/v6.13-rc4/arch/x86/entry/entry_64.S:244 -> #0 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}: check_prev_add sect/v6.13-rc4/kernel/locking/lockdep.c:3161 [inline] check_prevs_add sect/v6.13-rc4/kernel/locking/lockdep.c:3280 [inline] validate_chain+0x1888/0x5760 sect/v6.13-rc4/kernel/locking/lockdep.c:3904 __lock_acquire+0x13b4/0x2120 sect/v6.13-rc4/kernel/locking/lockdep.c:5226 lock_acquire+0x1c4/0x520 sect/v6.13-rc4/kernel/locking/lockdep.c:5849 touch_work_lockdep_map sect/v6.13-rc4/kernel/workqueue.c:3909 [inline] start_flush_work sect/v6.13-rc4/kernel/workqueue.c:4163 [inline] __flush_work+0x70f/0xc40 sect/v6.13-rc4/kernel/workqueue.c:4195 __cancel_work_sync sect/v6.13-rc4/kernel/workqueue.c:4351 [inline] disable_delayed_work_sync+0xbb/0xf0 sect/v6.13-rc4/kernel/workqueue.c:4514 sco_conn_free sect/v6.13-rc4/net/bluetooth/sco.c:95 [inline] kref_put sect/v6.13-rc4/./include/linux/kref.h:65 [inline] sco_conn_put+0x18f/0x270 sect/v6.13-rc4/net/bluetooth/sco.c:107 sco_chan_del+0xe2/0x210 sect/v6.13-rc4/net/bluetooth/sco.c:236 sco_sock_close+0x8f/0x100 sect/v6.13-rc4/net/bluetooth/sco.c:526 sco_sock_release+0x62/0x2d0 sect/v6.13-rc4/net/bluetooth/sco.c:1300 __sock_release+0xe1/0x2d0 sect/v6.13-rc4/net/socket.c:640 sock_close+0x1c/0x30 sect/v6.13-rc4/net/socket.c:1408 __fput+0x2bd/0xa80 sect/v6.13-rc4/fs/file_table.c:450 __fput_sync+0x15e/0x1c0 sect/v6.13-rc4/fs/file_table.c:535 __do_sys_close sect/v6.13-rc4/fs/open.c:1554 [inline] __se_sys_close sect/v6.13-rc4/fs/open.c:1539 [inline] __x64_sys_close+0x93/0x120 sect/v6.13-rc4/fs/open.c:1539 do_syscall_x64 sect/v6.13-rc4/arch/x86/entry/common.c:52 [inline] do_syscall_64+0xee/0x210 sect/v6.13-rc4/arch/x86/entry/common.c:83 entry_SYSCALL_64_after_hwframe+0x77/0x7f Fixes: e6720779ae61 ("Bluetooth: SCO: Use kref to track lifetime of sco_conn") Acked-by: Dave Tian Signed-off-by: Sungwoo Kim Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/sco.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 140869e5b2df2..b2c51f3ebc404 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -572,10 +572,23 @@ static void __sco_sock_close(struct sock *sk) /* Must be called on unlocked socket. */ static void sco_sock_close(struct sock *sk) { + struct sco_conn *conn; + + lock_sock(sk); + conn = sco_pi(sk)->conn; + if (conn) + sco_conn_hold(conn); + release_sock(sk); + + if (conn) + disable_delayed_work_sync(&conn->timeout_work); + lock_sock(sk); - sco_sock_clear_timer(sk); __sco_sock_close(sk); release_sock(sk); + + if (conn) + sco_conn_put(conn); } static void sco_sock_init(struct sock *sk, struct sock *parent) From 4ff5778e8ee3b533b2e8c506e2d94f28efd89d44 Mon Sep 17 00:00:00 2001 From: Chandrashekar Devegowda Date: Mon, 13 Apr 2026 09:50:40 +0530 Subject: [PATCH 2134/2758] Bluetooth: btintel_pcie: Support Product level reset [ Upstream commit 2d00975c841fa2e52921353392ce9780843bfee7 ] When driver encounters a TOP exception, ACPI methods will be called for Product level reset since Wifi and BT share the same TOP. BT driver will first reprobe the wifi driver and then reprobe BT. Signed-off-by: Chandrashekar Devegowda Signed-off-by: Venkat Rao Bagalkote Signed-off-by: Luiz Augusto von Dentz Stable-dep-of: 9c36951474d8 ("Bluetooth: btintel_pcie: Refactor FLR to use device_reprobe()") Signed-off-by: Sasha Levin --- drivers/bluetooth/Kconfig | 2 +- drivers/bluetooth/btintel.c | 10 +- drivers/bluetooth/btintel.h | 7 ++ drivers/bluetooth/btintel_pcie.c | 205 ++++++++++++++++++++++++++----- drivers/bluetooth/btintel_pcie.h | 7 ++ 5 files changed, 195 insertions(+), 36 deletions(-) diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index c5d45cf91f882..fc1b37044a9b0 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -502,7 +502,7 @@ config BT_NXPUART config BT_INTEL_PCIE tristate "Intel HCI PCIe driver" - depends on PCI + depends on PCI && ACPI select BT_INTEL select FW_LOADER help diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index dcaaa4ca02b99..5e9cac090bd8f 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -67,9 +67,10 @@ static struct { u32 fw_build_num; } coredump_info; -static const guid_t btintel_guid_dsm = +const guid_t btintel_guid_dsm = GUID_INIT(0xaa10f4e0, 0x81ac, 0x4233, 0xab, 0xf6, 0x3b, 0x2a, 0xc5, 0x0e, 0x28, 0xd9); +EXPORT_SYMBOL_GPL(btintel_guid_dsm); int btintel_check_bdaddr(struct hci_dev *hdev) { @@ -2624,7 +2625,7 @@ static void btintel_set_ppag(struct hci_dev *hdev, struct intel_version_tlv *ver kfree_skb(skb); } -static int btintel_acpi_reset_method(struct hci_dev *hdev) +int btintel_acpi_reset_method(struct hci_dev *hdev) { int ret = 0; acpi_status status; @@ -2632,14 +2633,14 @@ static int btintel_acpi_reset_method(struct hci_dev *hdev) struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; status = acpi_evaluate_object(ACPI_HANDLE(GET_HCIDEV_DEV(hdev)), "_PRR", NULL, &buffer); - if (ACPI_FAILURE(status)) { + if (ACPI_FAILURE(status) || !buffer.pointer) { bt_dev_err(hdev, "Failed to run _PRR method"); ret = -ENODEV; return ret; } p = buffer.pointer; - if (p->package.count != 1 || p->type != ACPI_TYPE_PACKAGE) { + if (p->type != ACPI_TYPE_PACKAGE || p->package.count != 1) { bt_dev_err(hdev, "Invalid arguments"); ret = -EINVAL; goto exit_on_error; @@ -2663,6 +2664,7 @@ static int btintel_acpi_reset_method(struct hci_dev *hdev) kfree(buffer.pointer); return ret; } +EXPORT_SYMBOL_GPL(btintel_acpi_reset_method); static void btintel_set_dsm_reset_method(struct hci_dev *hdev, struct intel_version_tlv *ver_tlv) diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h index 035ed1ea6ce96..182d66637a51d 100644 --- a/drivers/bluetooth/btintel.h +++ b/drivers/bluetooth/btintel.h @@ -80,6 +80,8 @@ struct intel_tlv { #define BTINTEL_HWID_SCP2F 0x21 /* Scorpius Peak2-F - Nova Lake */ #define BTINTEL_HWID_BZRIW 0x22 /* BlazarIW - Wildcat Lake */ +extern const guid_t btintel_guid_dsm; + struct intel_version_tlv { u32 cnvi_top; u32 cnvr_top; @@ -290,6 +292,7 @@ int btintel_bootloader_setup_tlv(struct hci_dev *hdev, int btintel_shutdown_combined(struct hci_dev *hdev); void btintel_hw_error(struct hci_dev *hdev, u8 code); void btintel_print_fseq_info(struct hci_dev *hdev); +int btintel_acpi_reset_method(struct hci_dev *hdev); #else static inline int btintel_check_bdaddr(struct hci_dev *hdev) @@ -423,4 +426,8 @@ static inline void btintel_hw_error(struct hci_dev *hdev, u8 code) static inline void btintel_print_fseq_info(struct hci_dev *hdev) { } +static inline int btintel_acpi_reset_method(struct hci_dev *hdev) +{ + return -ENODEV; +} #endif diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c index d4426ac767fd8..bbd5a2105209b 100644 --- a/drivers/bluetooth/btintel_pcie.c +++ b/drivers/bluetooth/btintel_pcie.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -102,6 +103,22 @@ enum { BTINTEL_PCIE_D3 }; +enum { + BTINTEL_PCIE_DSM_SET_RESET_TIMING = 1, + BTINTEL_PCIE_DSM_GET_RESET_TIMING = 2, + BTINTEL_PCIE_DSM_BT_PLDR_CONFIG = 3, + BTINTEL_PCIE_DSM_GET_RESET_TYPE = 4, + BTINTEL_PCIE_DSM_DYNAMIC_PLDR = 5, + BTINTEL_PCIE_DSM_GET_RESET_METHOD = 6, + BTINTEL_PCIE_DSM_SET_PLDR_DELAY = 7, +}; + +enum btintel_dsm_internal_product_reset_mode { + BTINTEL_PCIE_DSM_PLDR_MODE_EN_PROD_RESET = BIT(0), + BTINTEL_PCIE_DSM_PLDR_MODE_EN_WIFI_FLR = BIT(1), + BTINTEL_PCIE_DSM_PLDR_MODE_EN_BT_OFF_ON = BIT(2), +}; + /* Structure for dbgc fragment buffer * @buf_addr_lsb: LSB of the buffer's physical address * @buf_addr_msb: MSB of the buffer's physical address @@ -128,11 +145,6 @@ struct btintel_pcie_dbgc_ctxt { struct btintel_pcie_dbgc_ctxt_buf bufs[BTINTEL_PCIE_DBGC_BUFFER_COUNT]; }; -struct btintel_pcie_removal { - struct pci_dev *pdev; - struct work_struct work; -}; - static LIST_HEAD(btintel_pcie_recovery_list); static DEFINE_SPINLOCK(btintel_pcie_recovery_lock); @@ -2283,21 +2295,130 @@ static void btintel_pcie_inc_recovery_count(struct pci_dev *pdev, } static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data); +static void btintel_pcie_reset(struct hci_dev *hdev); -static void btintel_pcie_removal_work(struct work_struct *wk) +static int btintel_pcie_acpi_reset_method(struct btintel_pcie_data *data) { - struct btintel_pcie_removal *removal = - container_of(wk, struct btintel_pcie_removal, work); - struct pci_dev *pdev = removal->pdev; - struct btintel_pcie_data *data; + union acpi_object *obj, argv4; + acpi_handle handle; + int ret; + struct pldr_mode { + __le16 cmd_type; + __le16 cmd_payload; + } __packed; + + /* set 1 for _PRR mode + * Product Reset (PLDR Abort flow) + */ + static const struct pldr_mode mode = { + .cmd_type = cpu_to_le16(1), + .cmd_payload = cpu_to_le16(BTINTEL_PCIE_DSM_PLDR_MODE_EN_PROD_RESET | + BTINTEL_PCIE_DSM_PLDR_MODE_EN_WIFI_FLR), + }; + struct hci_dev *hdev = data->hdev; + + handle = ACPI_HANDLE(GET_HCIDEV_DEV(data->hdev)); + if (!handle) { + bt_dev_err(data->hdev, "No support for bluetooth device in ACPI firmware"); + return -EACCES; + } + + if (!acpi_has_method(handle, "_PRR")) { + bt_dev_err(data->hdev, "No support for _PRR ACPI method, cold boot"); + return -ENODEV; + } + + argv4.buffer.type = ACPI_TYPE_BUFFER; + argv4.buffer.length = sizeof(mode); + argv4.buffer.pointer = (void *)&mode; + + obj = acpi_evaluate_dsm(handle, &btintel_guid_dsm, 0, + BTINTEL_PCIE_DSM_DYNAMIC_PLDR, &argv4); + if (!obj) { + bt_dev_err(data->hdev, "Failed to call dsm to set reset method"); + return -EIO; + } + ACPI_FREE(obj); + + pci_dev_lock(data->pdev); + pci_save_state(data->pdev); + ret = btintel_acpi_reset_method(hdev); + if (ret) + bt_dev_err(data->hdev, "ACPI _PRR reset failed (%d), PLDR incomplete", + ret); + pci_restore_state(data->pdev); + pci_dev_unlock(data->pdev); + return ret; +} + +static void btintel_pcie_perform_pldr(struct btintel_pcie_data *data) +{ + struct pci_dev *pdev = data->pdev; + struct pci_dev *wifi = NULL; + struct pci_bus *bus; + int ret; + /* on integrated we have to look up by ID (same bus) */ + static const struct pci_device_id wifi_device_ids[] = { + #define WIFI_DEV(_id) { PCI_DEVICE(PCI_VENDOR_ID_INTEL, _id) } + WIFI_DEV(0xA840), /* LNL */ + WIFI_DEV(0xE440), /* PTL-P */ + WIFI_DEV(0xE340), /* PTL-H */ + WIFI_DEV(0xD340), /* NVL-H */ + WIFI_DEV(0x6E70), /* NVL-S */ + WIFI_DEV(0x4D40), /* WCL */ + {} + }; + struct pci_dev *tmp = NULL; + + bus = pdev->bus; + if (!bus) + return; + + list_for_each_entry(tmp, &bus->devices, bus_list) { + if (pci_match_id(wifi_device_ids, tmp)) { + wifi = pci_dev_get(tmp); + break; + } + } + + if (wifi) + device_release_driver(&wifi->dev); + + /* Wi-Fi is fully unbound before the reset and fully reprobed after + * the normal PCI probe path handles all state setup from scratch. + * BT needs pci_save_state()/pci_restore_state() because the BT driver + * is still partially attached when the _PRR runs (it hasn't been unbound yet). + * The PCI device needs to remain minimally functional so that + * device_reprobe(&pdev->dev) can work afterward + */ + ret = btintel_pcie_acpi_reset_method(data); + + if (wifi) { + if (device_reprobe(&wifi->dev)) + BT_ERR("WiFi reprobe failed for BDF:%s", pci_name(wifi)); + pci_dev_put(wifi); + } + + if (!ret) { + if (device_reprobe(&pdev->dev)) + BT_ERR("BT reprobe failed for BDF:%s", pci_name(pdev)); + } +} + +static void btintel_pcie_reset_work(struct work_struct *wk) +{ + struct btintel_pcie_data *data = + container_of(wk, struct btintel_pcie_data, reset_work); + struct pci_dev *pdev = data->pdev; int err; pci_lock_rescan_remove(); if (!pdev->bus) - goto error; + goto out; - data = pci_get_drvdata(pdev); + if (!data) + goto out; btintel_pcie_disable_interrupts(data); btintel_pcie_synchronize_irqs(data); @@ -2305,12 +2426,21 @@ static void btintel_pcie_removal_work(struct work_struct *wk) flush_work(&data->rx_work); bt_dev_dbg(data->hdev, "Release bluetooth interface"); + if (data->reset_type == BTINTEL_PCIE_IOSF_PRR_PLDR) { + /* This function holds pci_lock_rescan_remove(), which acquires + * pci_rescan_remove_lock. This mutex serializes against PCI device + * addition/removal (hotplug), so no device can be added to or + * removed from the bus list while this code runs. + */ + btintel_pcie_perform_pldr(data); + goto out; + } btintel_pcie_release_hdev(data); err = pci_reset_function(pdev); if (err) { BT_ERR("Failed resetting the pcie device (%d)", err); - goto error; + goto out; } btintel_pcie_enable_interrupts(data); @@ -2320,7 +2450,7 @@ static void btintel_pcie_removal_work(struct work_struct *wk) if (err) { BT_ERR("Failed to enable bluetooth hardware after reset (%d)", err); - goto error; + goto out; } btintel_pcie_reset_ia(data); @@ -2330,17 +2460,15 @@ static void btintel_pcie_removal_work(struct work_struct *wk) err = btintel_pcie_setup_hdev(data); if (err) { BT_ERR("Failed registering hdev (%d)", err); - goto error; + goto out; } -error: +out: pci_dev_put(pdev); pci_unlock_rescan_remove(); - kfree(removal); } static void btintel_pcie_reset(struct hci_dev *hdev) { - struct btintel_pcie_removal *removal; struct btintel_pcie_data *data; data = hci_get_drvdata(hdev); @@ -2351,14 +2479,8 @@ static void btintel_pcie_reset(struct hci_dev *hdev) if (test_and_set_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags)) return; - removal = kzalloc_obj(*removal, GFP_ATOMIC); - if (!removal) - return; - - removal->pdev = data->pdev; - INIT_WORK(&removal->work, btintel_pcie_removal_work); - pci_dev_get(removal->pdev); - schedule_work(&removal->work); + pci_dev_get(data->pdev); + schedule_work(&data->reset_work); } static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code) @@ -2368,15 +2490,19 @@ static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code) struct pci_dev *pdev = dev_data->pdev; time64_t retry_window; - if (code == 0x13) { - bt_dev_err(hdev, "Encountered top exception"); - return; - } + btintel_pcie_dump_debug_registers(hdev); data = btintel_pcie_get_recovery(pdev, &hdev->dev); if (!data) return; + if (code == 0x13) + dev_data->reset_type = BTINTEL_PCIE_IOSF_PRR_PLDR; + else + dev_data->reset_type = BTINTEL_PCIE_IOSF_PRR_FLR; + + bt_dev_err(hdev, "Encountered exception err:0x%x triggering: %s", code, + dev_data->reset_type == BTINTEL_PCIE_IOSF_PRR_PLDR ? "PLDR" : "FLR"); retry_window = ktime_get_boottime_seconds() - data->last_error; if (retry_window < BTINTEL_PCIE_RESET_WINDOW_SECS && @@ -2529,10 +2655,14 @@ static int btintel_pcie_probe(struct pci_dev *pdev, skb_queue_head_init(&data->rx_skb_q); INIT_WORK(&data->rx_work, btintel_pcie_rx_work); + INIT_WORK(&data->reset_work, btintel_pcie_reset_work); data->boot_stage_cache = 0x00; data->img_resp_cache = 0x00; - + /* FLR can be invoked by echoing to debugfs path, so explicitly + * initialized + */ + data->reset_type = BTINTEL_PCIE_IOSF_PRR_FLR; err = btintel_pcie_config_pcie(pdev, data); if (err) goto exit_error; @@ -2580,6 +2710,18 @@ static void btintel_pcie_remove(struct pci_dev *pdev) struct btintel_pcie_data *data; data = pci_get_drvdata(pdev); + if (!data) { + BT_WARN("PCI driver data is NULL, aborting remove"); + return; + } + + /* Cancel pending reset work. Skip only when remove() is called from + * within the reset work itself (PLDR device_reprobe path) to avoid + * deadlock. current_work() returns the work_struct of the caller if + * we are in a workqueue context. + */ + if (current_work() != &data->reset_work) + cancel_work_sync(&data->reset_work); btintel_pcie_disable_interrupts(data); @@ -2730,6 +2872,7 @@ static int btintel_pcie_resume(struct device *dev) if (data->pm_sx_event == PM_EVENT_FREEZE || data->pm_sx_event == PM_EVENT_HIBERNATE) { set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags); + data->reset_type = BTINTEL_PCIE_IOSF_PRR_FLR; btintel_pcie_reset(data->hdev); return 0; } diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h index c7db89af49056..4bc4f34ffd1a5 100644 --- a/drivers/bluetooth/btintel_pcie.h +++ b/drivers/bluetooth/btintel_pcie.h @@ -145,6 +145,11 @@ enum msix_mbox_int_causes { BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX4 = BIT(3), /* cause MBOX4 */ }; +enum btintel_pcie_reset_type { + BTINTEL_PCIE_IOSF_PRR_FLR = 0, + BTINTEL_PCIE_IOSF_PRR_PLDR = 1, +}; + #define BTINTEL_PCIE_MSIX_NON_AUTO_CLEAR_CAUSE BIT(7) /* Minimum and Maximum number of MSI-X Vector @@ -501,6 +506,7 @@ struct btintel_pcie_data { struct workqueue_struct *workqueue; struct sk_buff_head rx_skb_q; struct work_struct rx_work; + struct work_struct reset_work; struct dma_pool *dma_pool; dma_addr_t dma_p_addr; @@ -512,6 +518,7 @@ struct btintel_pcie_data { struct txq txq; struct rxq rxq; u32 alive_intr_ctxt; + enum btintel_pcie_reset_type reset_type; struct btintel_pcie_dbgc dbgc; struct btintel_pcie_dump_header dmp_hdr; u8 pm_sx_event; From fb6fc74cc10f0ee869bc5af9e2fd0d5c787ababf Mon Sep 17 00:00:00 2001 From: Kiran K Date: Wed, 3 Jun 2026 21:24:15 +0530 Subject: [PATCH 2135/2758] Bluetooth: btintel_pcie: Add support for smart trigger dump [ Upstream commit 975a70ff0aec8a2be0d633113f781b301db4a816 ] Based on the debug configuration, firmware can raise MSI-X interrupt with firmware trigger cause bit set on specific events like Disconnection, Connection Timeout, Page Timeout etc. Upon receiving an MSI-X interrupt with the firmware trigger cause bit set, the driver performs the following actions: 1. Reads Device Memory: Retrieves data from the device memory, constructs an HCI diagnostic event, and sends it to the monitor. This event includes details about the trigger, such as connection timeout or page timeout. 2. Dumps Device Coredump: Generates a coredump containing firmware traces for further analysis. The coredump can be retrieved using: $ cat /sys/class/devcoredump/devcd*/data > /tmp/btintel_coredump.bin HCI traces: = Vendor Diagnostic (len 12) a5 a5 a5 a5 01 03 00 23 00 01 00 00 Signed-off-by: Kiran K Signed-off-by: Luiz Augusto von Dentz Stable-dep-of: 9c36951474d8 ("Bluetooth: btintel_pcie: Refactor FLR to use device_reprobe()") Signed-off-by: Sasha Levin --- drivers/bluetooth/btintel.h | 1 + drivers/bluetooth/btintel_pcie.c | 164 +++++++++++++++++++++++++++++++ drivers/bluetooth/btintel_pcie.h | 10 +- 3 files changed, 174 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h index 182d66637a51d..37d93abdd5a37 100644 --- a/drivers/bluetooth/btintel.h +++ b/drivers/bluetooth/btintel.h @@ -53,6 +53,7 @@ struct intel_tlv { } __packed; #define BTINTEL_HCI_OP_RESET 0xfc01 +#define BTINTEL_HCI_OP_DEBUG 0xfcd9 #define BTINTEL_CNVI_BLAZARI 0x900 /* BlazarI - Lunar Lake */ #define BTINTEL_CNVI_BLAZARIW 0x901 /* BlazarIW - Wildcat Lake */ diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c index bbd5a2105209b..bd984751cc1a1 100644 --- a/drivers/bluetooth/btintel_pcie.c +++ b/drivers/bluetooth/btintel_pcie.c @@ -145,6 +145,22 @@ struct btintel_pcie_dbgc_ctxt { struct btintel_pcie_dbgc_ctxt_buf bufs[BTINTEL_PCIE_DBGC_BUFFER_COUNT]; }; +struct btintel_pcie_trigger_evt { + u8 type; + u8 len; + __le32 addr; + __le32 size; +} __packed; + +struct btintel_pcie_fwtrigger_evt { + __le32 reserved; + u8 type; /* Debug Trigger event */ + __le16 len; + u8 event_type; + __le16 event_id; + __le16 reserved2; +} __packed; + static LIST_HEAD(btintel_pcie_recovery_list); static DEFINE_SPINLOCK(btintel_pcie_recovery_lock); @@ -718,6 +734,11 @@ static int btintel_pcie_read_dram_buffers(struct btintel_pcie_data *data) sizeof(*tlv) + strlen(vendor) + sizeof(*tlv) + strlen(driver); + if (data->dmp_hdr.event_type && data->dmp_hdr.event_id) { + data_len += sizeof(*tlv) + sizeof(data->dmp_hdr.event_type); + data_len += sizeof(*tlv) + sizeof(data->dmp_hdr.event_id); + } + /* * sizeof(u32) - signature * sizeof(data_len) - to store tlv data size @@ -761,6 +782,17 @@ static int btintel_pcie_read_dram_buffers(struct btintel_pcie_data *data) p = btintel_pcie_copy_tlv(p, BTINTEL_CNVI_TOP, &data->dmp_hdr.cnvi_top, sizeof(data->dmp_hdr.cnvi_top)); + if (data->dmp_hdr.event_type && data->dmp_hdr.event_id) { + p = btintel_pcie_copy_tlv(p, BTINTEL_EVENT_TYPE, + &data->dmp_hdr.event_type, + sizeof(data->dmp_hdr.event_type)); + p = btintel_pcie_copy_tlv(p, BTINTEL_EVENT_ID, + &data->dmp_hdr.event_id, + sizeof(data->dmp_hdr.event_id)); + data->dmp_hdr.event_type = 0; + data->dmp_hdr.event_id = 0; + } + memcpy(p, dbgc->bufs[0].data, dbgc->count * BTINTEL_PCIE_DBGC_BUFFER_SIZE); dev_coredumpv(&hdev->dev, pdata, dump_size, GFP_KERNEL); return 0; @@ -1348,6 +1380,73 @@ static void btintel_pcie_read_hwexp(struct btintel_pcie_data *data) kfree(buf); } +static int btintel_pcie_dump_fwtrigger_event(struct btintel_pcie_data *data) +{ + struct btintel_pcie_fwtrigger_evt *evt; + struct sk_buff *skb; + unsigned int len; + int err; + u8 *buf; + + if (!data->debug_evt_size || !data->debug_evt_addr) + return -EINVAL; + + len = data->debug_evt_size; + + len = ALIGN_DOWN(len, 4); + + if (len < sizeof(*evt) || len > HCI_MAX_EVENT_SIZE) { + bt_dev_err(data->hdev, "Invalid FW trigger data size (%u bytes)", len); + return -EINVAL; + } + + buf = kzalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + btintel_pcie_mac_init(data); + + err = btintel_pcie_read_device_mem(data, buf, data->debug_evt_addr, + len); + if (err) + goto exit_on_error; + + evt = (void *)buf; + data->dmp_hdr.event_type = evt->event_type; + data->dmp_hdr.event_id = le16_to_cpu(evt->event_id); + + bt_dev_dbg(data->hdev, "event type: 0x%2.2x event id: 0x%4.4x len: %u", + data->dmp_hdr.event_type, data->dmp_hdr.event_id, len); + + skb = bt_skb_alloc(len, GFP_KERNEL); + if (!skb) { + err = -ENOMEM; + goto exit_on_error; + } + skb_put_data(skb, buf, len); + + hci_recv_diag(data->hdev, skb); + err = 0; + +exit_on_error: + kfree(buf); + return err; +} + +static void btintel_pcie_msix_fw_trigger_handler(struct btintel_pcie_data *data) +{ + bt_dev_dbg(data->hdev, "Received firmware smart trigger cause"); + + if (test_and_set_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags)) + return; + + /* Trigger device core dump when there is FW assert */ + if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags)) + data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT; + + queue_work(data->workqueue, &data->rx_work); +} + static void btintel_pcie_msix_hw_exp_handler(struct btintel_pcie_data *data) { bt_dev_err(data->hdev, "Received hw exception interrupt"); @@ -1370,6 +1469,14 @@ static void btintel_pcie_rx_work(struct work_struct *work) struct btintel_pcie_data *data = container_of(work, struct btintel_pcie_data, rx_work); struct sk_buff *skb; + int err; + + if (test_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags)) { + err = btintel_pcie_dump_fwtrigger_event(data); + if (err) + bt_dev_warn(data->hdev, "failed to log fwtrigger event"); + clear_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags); + } if (test_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags)) { btintel_pcie_dump_traces(data->hdev); @@ -1527,6 +1634,9 @@ static irqreturn_t btintel_pcie_irq_msix_handler(int irq, void *dev_id) btintel_pcie_msix_tx_handle(data); } + if (intr_hw & BTINTEL_PCIE_MSIX_HW_INT_CAUSES_FWTRIG) + btintel_pcie_msix_fw_trigger_handler(data); + /* This interrupt is triggered by the firmware after updating * boot_stage register and image_response register */ @@ -1601,6 +1711,7 @@ static struct btintel_pcie_causes_list causes_list[] = { { BTINTEL_PCIE_MSIX_FH_INT_CAUSES_1, BTINTEL_PCIE_CSR_MSIX_FH_INT_MASK, 0x01 }, { BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, 0x20 }, { BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HWEXP, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, 0x23 }, + { BTINTEL_PCIE_MSIX_HW_INT_CAUSES_FWTRIG, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, 0x25 }, }; /* This function configures the interrupt masks for both HW_INT_CAUSES and @@ -2087,6 +2198,55 @@ static void btintel_pcie_synchronize_irqs(struct btintel_pcie_data *data) synchronize_irq(data->msix_entries[i].vector); } +static int btintel_pcie_get_debug_info_addr(struct hci_dev *hdev) +{ + struct btintel_pcie_data *data = hci_get_drvdata(hdev); + struct btintel_pcie_trigger_evt *evt; + u8 param[1] = {0x10}; + struct sk_buff *skb; + int err = 0; + + skb = __hci_cmd_sync(hdev, BTINTEL_HCI_OP_DEBUG, 1, param, + HCI_CMD_TIMEOUT); + if (IS_ERR(skb)) { + bt_dev_err(hdev, "Reading Intel read debug info address command failed (%ld)", + PTR_ERR(skb)); + /* Not all Intel products supports this command */ + if (PTR_ERR(skb) == -EOPNOTSUPP) + return 0; + return PTR_ERR(skb); + } + + if (skb->len < (1 + sizeof(*evt))) { + bt_dev_err(hdev, "Debug info response too short (%u bytes)", skb->len); + err = -EIO; + goto exit_error; + } + + /* Check the status */ + if (skb->data[0]) { + bt_dev_err(hdev, "Reading Intel read debug info command failed (0x%2.2x)", + skb->data[0]); + err = -EIO; + goto exit_error; + } + + /* Consume Command Complete Status field */ + skb_pull(skb, 1); + + evt = (void *)skb->data; + + data->debug_evt_addr = le32_to_cpu(evt->addr); + data->debug_evt_size = le32_to_cpu(evt->size); + + bt_dev_dbg(hdev, "config type: %u config len: %u debug event addr: 0x%8.8x size: 0x%8.8x", + evt->type, evt->len, data->debug_evt_addr, + data->debug_evt_size); +exit_error: + kfree_skb(skb); + return err; +} + static int btintel_pcie_setup_internal(struct hci_dev *hdev) { struct btintel_pcie_data *data = hci_get_drvdata(hdev); @@ -2186,6 +2346,10 @@ static int btintel_pcie_setup_internal(struct hci_dev *hdev) if (ver_tlv.img_type == 0x02 || ver_tlv.img_type == 0x03) data->dmp_hdr.fw_git_sha1 = ver_tlv.git_sha1; + err = btintel_pcie_get_debug_info_addr(hdev); + if (err) + goto exit_error; + btintel_print_fseq_info(hdev); exit_error: kfree_skb(skb); diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h index 4bc4f34ffd1a5..e4a8fa4791888 100644 --- a/drivers/bluetooth/btintel_pcie.h +++ b/drivers/bluetooth/btintel_pcie.h @@ -102,6 +102,7 @@ enum msix_hw_int_causes { BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0 = BIT(0), /* cause 32 */ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP1 = BIT(1), /* cause 33 */ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HWEXP = BIT(3), /* cause 35 */ + BTINTEL_PCIE_MSIX_HW_INT_CAUSES_FWTRIG = BIT(5), /* cause 37 */ }; /* PCIe device states @@ -119,6 +120,7 @@ enum { BTINTEL_PCIE_CORE_HALTED, BTINTEL_PCIE_HWEXP_INPROGRESS, BTINTEL_PCIE_COREDUMP_INPROGRESS, + BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, BTINTEL_PCIE_RECOVERY_IN_PROGRESS, BTINTEL_PCIE_SETUP_DONE }; @@ -134,7 +136,9 @@ enum btintel_pcie_tlv_type { BTINTEL_DUMP_TIME, BTINTEL_FW_BUILD, BTINTEL_VENDOR, - BTINTEL_DRIVER + BTINTEL_DRIVER, + BTINTEL_EVENT_TYPE, + BTINTEL_EVENT_ID }; /* causes for the MBOX interrupts */ @@ -434,6 +438,8 @@ struct btintel_pcie_dump_header { u32 wrap_ctr; u16 trigger_reason; int state; + u8 event_type; + u16 event_id; }; /* struct btintel_pcie_data @@ -522,6 +528,8 @@ struct btintel_pcie_data { struct btintel_pcie_dbgc dbgc; struct btintel_pcie_dump_header dmp_hdr; u8 pm_sx_event; + u32 debug_evt_addr; + u32 debug_evt_size; }; static inline u32 btintel_pcie_rd_reg32(struct btintel_pcie_data *data, From c36895aa1122f8e3f917bf767f9ab59c39280d9d Mon Sep 17 00:00:00 2001 From: Ravindra Date: Wed, 10 Jun 2026 21:55:44 +0530 Subject: [PATCH 2136/2758] Bluetooth: btintel_pcie: Separate coredump work from RX work [ Upstream commit f70f7f2512c6b9113dc78f6a25361166afd1412e ] Sharing a single workqueue between coredump processing and RX delays evacuation of RX events while a coredump is in progress. The firmware's RX buffers can overflow during that window, leading to dropped events. The issue was observed in HID use cases where HID reports arrive in bursts and quickly fill the RX path while a coredump is being collected. Move coredump processing to a dedicated ordered coredump_workqueue with its own coredump_work, so coredumps run independently of RX. All four coredump trigger sources (FW assert, HW exception, user sysfs trigger, and resume-error detection) are switched to this new workqueue. Ordering serialises concurrent triggers without blocking RX. Signed-off-by: Ravindra Signed-off-by: Kiran K Signed-off-by: Luiz Augusto von Dentz Stable-dep-of: 9c36951474d8 ("Bluetooth: btintel_pcie: Refactor FLR to use device_reprobe()") Signed-off-by: Sasha Levin --- drivers/bluetooth/btintel_pcie.c | 102 +++++++++++++++++++++++++------ drivers/bluetooth/btintel_pcie.h | 5 ++ 2 files changed, 87 insertions(+), 20 deletions(-) diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c index bd984751cc1a1..fd1b2045aa2c7 100644 --- a/drivers/bluetooth/btintel_pcie.c +++ b/drivers/bluetooth/btintel_pcie.c @@ -1444,7 +1444,7 @@ static void btintel_pcie_msix_fw_trigger_handler(struct btintel_pcie_data *data) if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags)) data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT; - queue_work(data->workqueue, &data->rx_work); + queue_work(data->coredump_workqueue, &data->coredump_work); } static void btintel_pcie_msix_hw_exp_handler(struct btintel_pcie_data *data) @@ -1461,16 +1461,21 @@ static void btintel_pcie_msix_hw_exp_handler(struct btintel_pcie_data *data) if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags)) data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT; - queue_work(data->workqueue, &data->rx_work); + queue_work(data->coredump_workqueue, &data->coredump_work); } -static void btintel_pcie_rx_work(struct work_struct *work) +static void btintel_pcie_coredump_worker(struct work_struct *work) { struct btintel_pcie_data *data = container_of(work, - struct btintel_pcie_data, rx_work); - struct sk_buff *skb; + struct btintel_pcie_data, coredump_work); int err; + /* hdev is NULL until setup_hdev() succeeds, and is cleared on + * teardown after disable_work_sync() drains us; bail in that case. + */ + if (!data->hdev) + return; + if (test_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags)) { err = btintel_pcie_dump_fwtrigger_event(data); if (err) @@ -1494,6 +1499,13 @@ static void btintel_pcie_rx_work(struct work_struct *work) btintel_pcie_read_hwexp(data); clear_bit(BTINTEL_PCIE_HWEXP_INPROGRESS, &data->flags); } +} + +static void btintel_pcie_rx_work(struct work_struct *work) +{ + struct btintel_pcie_data *data = container_of(work, + struct btintel_pcie_data, rx_work); + struct sk_buff *skb; /* Process the sk_buf in queue and send to the HCI layer */ while ((skb = skb_dequeue(&data->rx_skb_q))) { @@ -2168,9 +2180,11 @@ static int btintel_pcie_send_frame(struct hci_dev *hdev, static void btintel_pcie_release_hdev(struct btintel_pcie_data *data) { - struct hci_dev *hdev; + struct hci_dev *hdev = data->hdev; + + if (!hdev) + return; - hdev = data->hdev; hci_unregister_dev(hdev); hci_free_dev(hdev); data->hdev = NULL; @@ -2588,6 +2602,10 @@ static void btintel_pcie_reset_work(struct work_struct *wk) btintel_pcie_synchronize_irqs(data); flush_work(&data->rx_work); + /* Drain any in-flight coredump and block new ones across reset. + * Safe from self-deadlock: coredump_work runs on a separate wq. + */ + disable_work_sync(&data->coredump_work); bt_dev_dbg(data->hdev, "Release bluetooth interface"); if (data->reset_type == BTINTEL_PCIE_IOSF_PRR_PLDR) { @@ -2595,16 +2613,27 @@ static void btintel_pcie_reset_work(struct work_struct *wk) * pci_rescan_remove_lock. This mutex serializes against PCI device * addition/removal (hotplug), so no device can be added to or * removed from the bus list while this code runs. + * + * device_reprobe() inside btintel_pcie_perform_pldr() destroys + * 'data' via .remove(); a fresh probe re-INIT_WORKs the + * coredump_work with disable count 0, so we must not call + * enable_work() on this path. */ btintel_pcie_perform_pldr(data); goto out; } btintel_pcie_release_hdev(data); - err = pci_reset_function(pdev); + /* Use pci_try_reset_function() rather than pci_reset_function() to + * avoid an ABBA deadlock against btintel_pcie_remove(): the PCI core + * calls .remove() with device_lock held, and remove() then waits for + * this work via cancel_work_sync(); pci_reset_function() would in + * turn try to acquire the same device_lock, deadlocking both paths. + */ + err = pci_try_reset_function(pdev); if (err) { BT_ERR("Failed resetting the pcie device (%d)", err); - goto out; + goto out_enable; } btintel_pcie_enable_interrupts(data); @@ -2614,7 +2643,7 @@ static void btintel_pcie_reset_work(struct work_struct *wk) if (err) { BT_ERR("Failed to enable bluetooth hardware after reset (%d)", err); - goto out; + goto out_enable; } btintel_pcie_reset_ia(data); @@ -2624,8 +2653,15 @@ static void btintel_pcie_reset_work(struct work_struct *wk) err = btintel_pcie_setup_hdev(data); if (err) { BT_ERR("Failed registering hdev (%d)", err); - goto out; + goto out_enable; } + +out_enable: + /* Balance disable_work_sync() above on every exit. Leaving the + * counter incremented on a failed reset would permanently disable + * coredump_work even after a later successful reset. + */ + enable_work(&data->coredump_work); out: pci_dev_put(pdev); pci_unlock_rescan_remove(); @@ -2759,7 +2795,6 @@ static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data) hdev->bus = HCI_PCI; hci_set_drvdata(hdev, data); - data->hdev = hdev; SET_HCIDEV_DEV(hdev, &data->pdev->dev); hdev->manufacturer = 2; @@ -2778,15 +2813,17 @@ static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data) err = hci_register_dev(hdev); if (err < 0) { BT_ERR("Failed to register to hdev (%d)", err); - goto exit_error; + hci_free_dev(hdev); + return err; } + /* Publish hdev only after successful registration; the coredump + * worker bails on !data->hdev, so it never observes a half-set-up + * device. + */ + data->hdev = hdev; data->dmp_hdr.driver_name = KBUILD_MODNAME; return 0; - -exit_error: - hci_free_dev(hdev); - return err; } static int btintel_pcie_probe(struct pci_dev *pdev, @@ -2817,9 +2854,16 @@ static int btintel_pcie_probe(struct pci_dev *pdev, if (!data->workqueue) return -ENOMEM; + data->coredump_workqueue = alloc_ordered_workqueue(KBUILD_MODNAME "_cd", 0); + if (!data->coredump_workqueue) { + destroy_workqueue(data->workqueue); + return -ENOMEM; + } + skb_queue_head_init(&data->rx_skb_q); INIT_WORK(&data->rx_work, btintel_pcie_rx_work); INIT_WORK(&data->reset_work, btintel_pcie_reset_work); + INIT_WORK(&data->coredump_work, btintel_pcie_coredump_worker); data->boot_stage_cache = 0x00; data->img_resp_cache = 0x00; @@ -2862,6 +2906,8 @@ static int btintel_pcie_probe(struct pci_dev *pdev, /* reset device before exit */ btintel_pcie_reset_bt(data); + destroy_workqueue(data->coredump_workqueue); + pci_clear_master(pdev); pci_set_drvdata(pdev, NULL); @@ -2879,13 +2925,20 @@ static void btintel_pcie_remove(struct pci_dev *pdev) return; } + /* Permanently block coredump triggers and drain the worker before + * tearing down. Must run before cancel_work_sync(&reset_work) so + * the disable counter stays >= 1 even after reset_work()'s + * balanced enable_work() (counter 2 -> 1, never reaching 0). + */ + disable_work_sync(&data->coredump_work); + /* Cancel pending reset work. Skip only when remove() is called from * within the reset work itself (PLDR device_reprobe path) to avoid * deadlock. current_work() returns the work_struct of the caller if * we are in a workqueue context. */ if (current_work() != &data->reset_work) - cancel_work_sync(&data->reset_work); + disable_work_sync(&data->reset_work); btintel_pcie_disable_interrupts(data); @@ -2905,6 +2958,7 @@ static void btintel_pcie_remove(struct pci_dev *pdev) btintel_pcie_release_hdev(data); + destroy_workqueue(data->coredump_workqueue); destroy_workqueue(data->workqueue); btintel_pcie_free(data); @@ -2920,11 +2974,19 @@ static void btintel_pcie_coredump(struct device *dev) struct pci_dev *pdev = to_pci_dev(dev); struct btintel_pcie_data *data = pci_get_drvdata(pdev); + if (!data) + return; + if (test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags)) return; data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_USER_TRIGGER; - queue_work(data->workqueue, &data->rx_work); + /* queue_work() returns false if the work is disabled (reset or + * remove in progress); clear the in-progress bit so a later + * trigger can succeed once the work is re-enabled. + */ + if (!queue_work(data->coredump_workqueue, &data->coredump_work)) + clear_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags); } #endif @@ -3065,7 +3127,7 @@ static int btintel_pcie_resume(struct device *dev) &data->flags)) { data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT; - queue_work(data->workqueue, &data->rx_work); + queue_work(data->coredump_workqueue, &data->coredump_work); } set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags); btintel_pcie_reset(data->hdev); diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h index e4a8fa4791888..7caee093e3164 100644 --- a/drivers/bluetooth/btintel_pcie.h +++ b/drivers/bluetooth/btintel_pcie.h @@ -466,6 +466,8 @@ struct btintel_pcie_dump_header { * @workqueue: workqueue for RX work * @rx_skb_q: SKB queue for RX packet * @rx_work: RX work struct to process the RX packet in @rx_skb_q + * @coredump_workqueue: dedicated workqueue for coredump collection + * @coredump_work: work struct for coredump trace collection * @dma_pool: DMA pool for descriptors, index array and ci * @dma_p_addr: DMA address for pool * @dma_v_addr: address of pool @@ -514,6 +516,9 @@ struct btintel_pcie_data { struct work_struct rx_work; struct work_struct reset_work; + struct workqueue_struct *coredump_workqueue; + struct work_struct coredump_work; + struct dma_pool *dma_pool; dma_addr_t dma_p_addr; void *dma_v_addr; From 1f9375f55ead8fb96a8157956900c45423933004 Mon Sep 17 00:00:00 2001 From: Kiran K Date: Tue, 30 Jun 2026 22:29:19 +0530 Subject: [PATCH 2137/2758] Bluetooth: btintel_pcie: Refactor FLR to use device_reprobe() [ Upstream commit 9c36951474d8e1127f4946f39cb874a200f34e9f ] The FLR branch in btintel_pcie_reset_work() open-coded the entire re-init sequence: btintel_pcie_release_hdev() (hci_unregister_dev + hci_free_dev), pci_try_reset_function(), enable_interrupts / config_msix / enable_bt / reset_ia / start_rx, then btintel_pcie_setup_hdev() (hci_alloc_dev_priv + hci_register_dev). Every probe() init step had to be kept in sync with this second copy in the reset path, and any failure mid-sequence left state to unwind by hand. The PLDR path already delegates teardown and re-init to the PCI core via device_reprobe(): .remove() destroys data through devres and unregisters hdev, then .probe() rebuilds everything from scratch. Apply the same model to FLR. Introduce btintel_pcie_perform_flr() mirroring perform_pldr(). It runs pci_try_reset_function() (required to avoid the device_lock ABBA against btintel_pcie_remove(), which calls disable_work_sync(&reset_work) while holding device_lock) followed by device_reprobe(). On success, data is destroyed and a fresh probe re-INIT_WORKs coredump_work with disable count 0, so enable_work() must not be called; on failure, data is still alive and the caller balances the earlier disable_work_sync(). The contract is documented on the helper and reiterated at the reset_work() call site. reset_work() shrinks to interrupt/worker drain, dispatch on reset_type, and the single asymmetry between the two paths. The out_enable label, the manual unregister/register pair, and the forward declaration of btintel_pcie_setup_hdev() are dropped. No intended functional change; FLR and PLDR now share one teardown contract. Fixes: 256ab9520d15 ("Bluetooth: btintel_pcie: Support Function level reset") Assisted-by: GitHub-Copilot:claude-4.7-opus Signed-off-by: Kiran K Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- drivers/bluetooth/btintel_pcie.c | 102 ++++++++++++++++--------------- 1 file changed, 52 insertions(+), 50 deletions(-) diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c index fd1b2045aa2c7..be9348960348f 100644 --- a/drivers/bluetooth/btintel_pcie.c +++ b/drivers/bluetooth/btintel_pcie.c @@ -2114,6 +2114,9 @@ static int btintel_pcie_send_frame(struct hci_dev *hdev, if (test_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags)) return -ENODEV; + if (test_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags)) + return -ENODEV; + /* Due to the fw limitation, the type header of the packet should be * 4 bytes unlike 1 byte for UART. In UART, the firmware can read * the first byte to get the packet type and redirect the rest of data @@ -2472,7 +2475,6 @@ static void btintel_pcie_inc_recovery_count(struct pci_dev *pdev, } } -static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data); static void btintel_pcie_reset(struct hci_dev *hdev); static int btintel_pcie_acpi_reset_method(struct btintel_pcie_data *data) @@ -2583,12 +2585,45 @@ static void btintel_pcie_perform_pldr(struct btintel_pcie_data *data) } } +/* + * Issue a Function Level Reset and hand teardown/re-init off to the PCI + * core via device_reprobe(), mirroring the PLDR path's contract. + * + * Caller must hold pci_lock_rescan_remove() and must have already + * disabled interrupts and drained both rx_work and coredump_work. + */ +static int btintel_pcie_perform_flr(struct btintel_pcie_data *data) +{ + struct pci_dev *pdev = data->pdev; + int err; + + /* pci_try_reset_function() avoids the device_lock ABBA against + * btintel_pcie_remove(): .remove() runs with device_lock held and + * then waits for this work via disable_work_sync(); the blocking + * pci_reset_function() would deadlock by trying to re-acquire + * device_lock here. + */ + err = pci_try_reset_function(pdev); + if (err) { + BT_ERR("Failed resetting the pcie device (%d)", err); + return err; + } + + /* device_reprobe() always detaches the driver first (running + * .remove(), which frees 'data'); any re-probe failure leaves the + * device unbound but 'data' is already gone, so just log it. + */ + if (device_reprobe(&pdev->dev)) + BT_ERR("BT reprobe failed for BDF:%s", pci_name(pdev)); + + return 0; +} + static void btintel_pcie_reset_work(struct work_struct *wk) { struct btintel_pcie_data *data = container_of(wk, struct btintel_pcie_data, reset_work); struct pci_dev *pdev = data->pdev; - int err; pci_lock_rescan_remove(); @@ -2608,60 +2643,27 @@ static void btintel_pcie_reset_work(struct work_struct *wk) disable_work_sync(&data->coredump_work); bt_dev_dbg(data->hdev, "Release bluetooth interface"); + + /* Both reset paths follow the same contract: on success they + * destroy 'data' via device_reprobe() (a fresh probe re-INIT_WORKs + * the coredump_work with disable count 0), so enable_work() must + * NOT be called on the success path. Only the FLR path can fail + * with 'data' still alive, in which case we balance the + * disable_work_sync() above so a later successful reset is not + * permanently blocked. + * + * pci_lock_rescan_remove() (held above) serializes against PCI + * device addition/removal (hotplug), so no device can be added to + * or removed from the bus list while this code runs. + */ if (data->reset_type == BTINTEL_PCIE_IOSF_PRR_PLDR) { - /* This function holds pci_lock_rescan_remove(), which acquires - * pci_rescan_remove_lock. This mutex serializes against PCI device - * addition/removal (hotplug), so no device can be added to or - * removed from the bus list while this code runs. - * - * device_reprobe() inside btintel_pcie_perform_pldr() destroys - * 'data' via .remove(); a fresh probe re-INIT_WORKs the - * coredump_work with disable count 0, so we must not call - * enable_work() on this path. - */ btintel_pcie_perform_pldr(data); goto out; } - btintel_pcie_release_hdev(data); - - /* Use pci_try_reset_function() rather than pci_reset_function() to - * avoid an ABBA deadlock against btintel_pcie_remove(): the PCI core - * calls .remove() with device_lock held, and remove() then waits for - * this work via cancel_work_sync(); pci_reset_function() would in - * turn try to acquire the same device_lock, deadlocking both paths. - */ - err = pci_try_reset_function(pdev); - if (err) { - BT_ERR("Failed resetting the pcie device (%d)", err); - goto out_enable; - } - btintel_pcie_enable_interrupts(data); - btintel_pcie_config_msix(data); - - err = btintel_pcie_enable_bt(data); - if (err) { - BT_ERR("Failed to enable bluetooth hardware after reset (%d)", - err); - goto out_enable; - } - - btintel_pcie_reset_ia(data); - btintel_pcie_start_rx(data); - data->flags = 0; + if (btintel_pcie_perform_flr(data)) + enable_work(&data->coredump_work); - err = btintel_pcie_setup_hdev(data); - if (err) { - BT_ERR("Failed registering hdev (%d)", err); - goto out_enable; - } - -out_enable: - /* Balance disable_work_sync() above on every exit. Leaving the - * counter incremented on a failed reset would permanently disable - * coredump_work even after a later successful reset. - */ - enable_work(&data->coredump_work); out: pci_dev_put(pdev); pci_unlock_rescan_remove(); From 990e65eb9387c4ddfa7f68782b6644c2c35d489f Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Wed, 1 Jul 2026 18:46:38 +0300 Subject: [PATCH 2138/2758] Bluetooth: ISO: fix malformed ISO_END/CONT handling [ Upstream commit e054c1a6ae7310d2815778fddb87da616e11c255 ] Core specification (Part C vol 4 sec 5.4.5) does not exclude empty ISO_CONT, ISO_END packets. We currently reject them if they are last. If controller sends malformed sequence ISO_START -> rx_len = 4, ISO_CONT skb->len 4, ISO_START that ends payload in ISO_CONT, we leak conn->rx_skb. If controller sends too long ISO_END, we panic on skb_put. If controller sends too short ISO_END we accept it. Fix by marking unfinished ISO_START via conn->rx_skb != NULL. Check skb->len properly before skb_put. Combine the ISO_CONT/END code paths as they require the same initial checks. Reject too short ISO_END packets. Fixes: 84c24fb151fc ("Bluetooth: ISO: drop ISO_END frames received without prior ISO_START") Fixes: ccf74f2390d6 ("Bluetooth: Add BTPROTO_ISO socket type") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/iso.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 7186e8d88c757..1d5ce87e6496e 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -2539,7 +2539,7 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags) switch (pb) { case ISO_START: case ISO_SINGLE: - if (conn->rx_len) { + if (conn->rx_skb || conn->rx_len) { BT_ERR("Unexpected start frame (len %d)", skb->len); kfree_skb(conn->rx_skb); conn->rx_skb = NULL; @@ -2620,12 +2620,14 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags) break; case ISO_CONT: - BT_DBG("Cont: frag len %d (expecting %d)", skb->len, + case ISO_END: + BT_DBG("%s: frag len %d (expecting %d)", + (pb == ISO_END) ? "End" : "Cont", skb->len, conn->rx_len); - if (!conn->rx_len) { - BT_ERR("Unexpected continuation frame (len %d)", - skb->len); + if (!conn->rx_skb) { + BT_ERR("Unexpected ISO %s frame (len %d)", + (pb == ISO_END) ? "End" : "Cont", skb->len); goto drop; } @@ -2641,17 +2643,9 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags) skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len), skb->len); conn->rx_len -= skb->len; - break; - case ISO_END: - if (!conn->rx_len) { - BT_ERR("Unexpected end frame (len %d)", skb->len); - goto drop; - } - - skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len), - skb->len); - conn->rx_len -= skb->len; + if (pb == ISO_CONT) + break; if (!conn->rx_len) { struct sk_buff *rx_skb = conn->rx_skb; @@ -2662,6 +2656,13 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags) */ conn->rx_skb = NULL; iso_recv_frame(conn, rx_skb); + } else { + BT_ERR("ISO fragment incomplete (len %d, expected %d)", + skb->len, conn->rx_len); + kfree_skb(conn->rx_skb); + conn->rx_skb = NULL; + conn->rx_len = 0; + goto drop; } break; } From 058d0d087a271671e1bd71823ac0c752e8d6840c Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Wed, 1 Jul 2026 18:46:39 +0300 Subject: [PATCH 2139/2758] Bluetooth: ISO: exclude RFU bits from ISO_SDU_Length [ Upstream commit fd076d8deeab6f9f18ef13400f89e1f550df665b ] slen contains ISO_SDU_Length (12 bits), RFU (2 bits), Packet_Status_Flags (2 bits). Exclude the RFU bits from hci_iso_data_len. Also add masks to the pack macro. Fixes: 4de0fc599eb9 ("Bluetooth: Add definitions for CIS connections") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- include/net/bluetooth/hci.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 572b1c620c5d6..f5cf13b443427 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -3410,8 +3410,9 @@ static inline struct hci_iso_hdr *hci_iso_hdr(const struct sk_buff *skb) #define hci_iso_flags_pack(pb, ts) ((pb & 0x03) | ((ts & 0x01) << 2)) /* ISO data length and flags pack/unpack */ -#define hci_iso_data_len_pack(h, f) ((__u16) ((h) | ((f) << 14))) -#define hci_iso_data_len(h) ((h) & 0x3fff) +#define hci_iso_data_len_pack(h, f) ((__u16) (((h) & 0x0fff) | \ + (((f) & 0x3) << 14))) +#define hci_iso_data_len(h) ((h) & 0x0fff) #define hci_iso_data_flags(h) ((h) >> 14) /* codec transport types */ From a8e169d308775039200bb9c905c7ce420db6e8c5 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Wed, 1 Jul 2026 09:06:14 -0700 Subject: [PATCH 2140/2758] Bluetooth: bpa10x: avoid OOB read of revision string in bpa10x_setup() [ Upstream commit dd068ef044128db655f48323a4acfd5907e04903 ] bpa10x_setup() sends the vendor command 0xfc0e and passes the response to bt_dev_info() and hci_set_fw_info() as a "%s" string starting at skb->data + 1, without checking the length: bt_dev_info(hdev, "%s", (char *)(skb->data + 1)); hci_set_fw_info(hdev, "%s", skb->data + 1); A device that returns a one-byte response (status only) leaves skb->data + 1 past the end of the data, and the %s walk reads adjacent slab memory until it meets a NUL. The same happens when the payload is not NUL-terminated within skb->len. The out-of-bounds bytes end up in the kernel log and the firmware-info debugfs file. Print the revision string with a bounded "%.*s" limited to skb->len - 1 instead. This keeps the string readable for well-behaved devices while never reading past the received data, and does not fail setup, so a device returning a short or unterminated response keeps working. Fixes: ddd68ec8f484 ("Bluetooth: bpa10x: Read revision information in setup stage") Reported-by: Xiang Mei Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Weiming Shi Reported-by: Xiang Mei Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- drivers/bluetooth/bpa10x.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c index 2ae38a321c4b3..e63d1af250ec9 100644 --- a/drivers/bluetooth/bpa10x.c +++ b/drivers/bluetooth/bpa10x.c @@ -255,9 +255,13 @@ static int bpa10x_setup(struct hci_dev *hdev) if (IS_ERR(skb)) return PTR_ERR(skb); - bt_dev_info(hdev, "%s", (char *)(skb->data + 1)); + /* Bounded print: the device controls skb->len. */ + if (skb->len > 1) { + int len = skb->len - 1; - hci_set_fw_info(hdev, "%s", skb->data + 1); + bt_dev_info(hdev, "%.*s", len, (char *)(skb->data + 1)); + hci_set_fw_info(hdev, "%.*s", len, skb->data + 1); + } kfree_skb(skb); return 0; From d0a2b0c81f112540a337cde1c20251bf05ddf1da Mon Sep 17 00:00:00 2001 From: Stig Hornang Date: Fri, 12 Jun 2026 16:38:18 +0200 Subject: [PATCH 2141/2758] Bluetooth: L2CAP: fix tx ident leak for commands without a response [ Upstream commit 6e1930ece855a4c256f1c7e6632d634cfb9888b5 ] Commit 6c3ea155e5ee ("Bluetooth: L2CAP: Fix not tracking outstanding TX ident") changed ident allocation to use an IDA, releasing idents in l2cap_put_ident() when the matching response command is received. But identifiers allocated for commands that have no response defined are never released. In particular L2CAP_LE_CREDITS is sent repeatedly for the lifetime of an LE CoC channel, so a peer streaming data to the host exhausts the 1-255 ident range after 254 credit packets. From then on l2cap_get_ident() fails: kernel: Bluetooth: Unable to allocate ident: -28 and every subsequent L2CAP_LE_CREDITS packet is sent with ident 0, which is invalid (Core Spec, Vol 3, Part A, Section 4: "Signaling identifier 0x00 is an invalid identifier and shall never be used in any command"). Remote stacks that validate the ident drop these commands, never receive new credits, and the channel stalls permanently. With default socket buffers this happens after roughly 0.5 MB of received data (the exact amount depends on the socket receive buffer): < ACL Data TX: Handle 2048 flags 0x00 dlen 12 LE L2CAP: LE Flow Control Credit (0x16) ident 0 len 4 Source CID: 64 Credits: 1 Release the ident immediately after sending L2CAP_LE_CREDITS since no response will ever release it. Use a local variable instead of chan->ident so that an ident that an EXT_FLOWCTL channel may be waiting on (e.g. a pending reconfigure) is not overwritten by a credit packet. Also add the missing L2CAP_LE_CONN_RSP case to l2cap_put_ident() so idents allocated for outgoing L2CAP_LE_CONN_REQ commands are released when the response arrives. Fixes: 6c3ea155e5ee ("Bluetooth: L2CAP: Fix not tracking outstanding TX ident") Link: https://bugzilla.kernel.org/show_bug.cgi?id=221629 Assisted-by: Claude:claude-opus-4.8 Assisted-by: Fable:5 Signed-off-by: Stig Hornang Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/l2cap_core.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 4b8246b297113..93507cb10bc92 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4824,6 +4824,7 @@ static void l2cap_put_ident(struct l2cap_conn *conn, u8 code, u8 id) case L2CAP_ECHO_RSP: case L2CAP_INFO_RSP: case L2CAP_CONN_PARAM_UPDATE_RSP: + case L2CAP_LE_CONN_RSP: case L2CAP_ECRED_CONN_RSP: case L2CAP_ECRED_RECONF_RSP: /* First do a lookup since the remote may send bogus ids that @@ -6721,6 +6722,7 @@ static void l2cap_chan_le_send_credits(struct l2cap_chan *chan) struct l2cap_conn *conn = chan->conn; struct l2cap_le_credits pkt; u16 return_credits = l2cap_le_rx_credits(chan); + int ident; if (chan->mode != L2CAP_MODE_LE_FLOWCTL && chan->mode != L2CAP_MODE_EXT_FLOWCTL) @@ -6738,9 +6740,18 @@ static void l2cap_chan_le_send_credits(struct l2cap_chan *chan) pkt.cid = cpu_to_le16(chan->scid); pkt.credits = cpu_to_le16(return_credits); - chan->ident = l2cap_get_ident(conn); + ident = l2cap_get_ident(conn); - l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CREDITS, sizeof(pkt), &pkt); + l2cap_send_cmd(conn, ident, L2CAP_LE_CREDITS, sizeof(pkt), &pkt); + + /* L2CAP_LE_CREDITS has no response so the ident is never released by + * l2cap_put_ident() - release it right away, otherwise the tx_ida + * range is exhausted after 254 packets and from then on credits are + * sent with the invalid ident 0, which some remote stacks ignore, + * stalling the channel. + */ + if (ident > 0) + ida_free(&conn->tx_ida, ident); } void l2cap_chan_rx_avail(struct l2cap_chan *chan, ssize_t rx_avail) From 3a63a11897c7ba32d1be7a3fdbc48a8b01cf4992 Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Sun, 7 Jun 2026 15:24:30 +0800 Subject: [PATCH 2142/2758] ring-buffer: Fix event length with forced 8-byte alignment [ Upstream commit c37e0a4b79a6bbb96ce5ffe279d7c001e20529e0 ] When RB_FORCE_8BYTE_ALIGNMENT is true, rb_calculate_event_length() reserves the space of event->array[0] for placing the data length and rb_update_event() stores the data length in event->array[0] accordingly. As a result the whole event length will add extra 4 bytes for sizeof(event.array[0]) unconditionally. But ring_buffer_event_length() only subtracts the sizeof(event->array[0]) for events larger than RB_MAX_SMALL_DATA + sizeof(event->array[0]). As a result, small events on architectures with RB_FORCE_8BYTE_ALIGNMENT=true report a data length that is 4 bytes larger than expected. To fix it, add the RB_FORCE_8BYTE_ALIGNMENT as a condition to subtract the size of that length field whenever RB_FORCE_8BYTE_ALIGNMENT is true. This issue is observed in a riscv64 kernel with CONFIG_HAVE_64BIT_ALIGNED_ACCESS set to y, when we run ftrace selftest trace_marker_raw.tc, we get the weird log: for cases where the id is 1..100, the number of data field is 8*N, but once id exceeds 100, the number of data field becomes 8*N+4: # 1 buf: 58 00 00 00 80 5e d1 63 (number of data field is 8*1) ... # a buf: 58 ... (number of data field is 8*2) ... # 64 buf: 58 ... (number of data field is 8*13) # 65 buf: 58 ... (number of data field is 8*13+4) After applying this change, the number of data field keeps being 8*N+4 consistently. Link: https://patch.msgid.link/20260607072431.125633-2-hui.wang@canonical.com Fixes: 2271048d1b3b ("ring-buffer: Do 8 byte alignment for 64 bit that can not handle 4 byte align") Signed-off-by: Hui Wang Reviewed-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt Signed-off-by: Sasha Levin --- kernel/trace/ring_buffer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 7b07d2004cc6f..fcaa3761ef3eb 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -266,7 +266,8 @@ unsigned ring_buffer_event_length(struct ring_buffer_event *event) if (event->type_len > RINGBUF_TYPE_DATA_TYPE_LEN_MAX) return length; length -= RB_EVNT_HDR_SIZE; - if (length > RB_MAX_SMALL_DATA + sizeof(event->array[0])) + if (length > RB_MAX_SMALL_DATA + sizeof(event->array[0]) || + RB_FORCE_8BYTE_ALIGNMENT) length -= sizeof(event->array[0]); return length; } From 2d8eeb0578ae3aadf112fd3e1997e58178f75979 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Tue, 16 Jun 2026 14:24:29 -0700 Subject: [PATCH 2143/2758] accel/amdxdna: Prevent PM resume deadlock in hwctx_sync_debug_bo() [ Upstream commit e35c9cf5512814fb04f369f2eada64f0a7164609 ] amdxdna_hwctx_sync_debug_bo() invokes the hardware hwctx_sync_debug_bo() callback while holding xdna->dev_lock. The callback may call amdxdna_cmd_submit(), which in turn calls amdxdna_pm_resume_get(). If the device is suspended, amdxdna_pm_resume_get() may synchronously execute amdxdna_pm_resume(), which also acquires xdna->dev_lock, resulting in a deadlock. Avoid the deadlock by calling amdxdna_pm_resume_get() before holding xdna->dev_lock in both amdxdna_hwctx_sync_debug_bo() and amdxdna_drm_config_hwctx_ioctl() Fixes: 7ea046838021 ("accel/amdxdna: Support firmware debug buffer") Reviewed-by: Max Zhen Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260616212429.3620645-1-lizhi.hou@amd.com Signed-off-by: Sasha Levin --- drivers/accel/amdxdna/aie2_ctx.c | 2 +- drivers/accel/amdxdna/amdxdna_ctx.c | 26 ++++++++++++++++++++++---- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c index 93535806e72a1..f5d247d65cb06 100644 --- a/drivers/accel/amdxdna/aie2_ctx.c +++ b/drivers/accel/amdxdna/aie2_ctx.c @@ -815,7 +815,7 @@ static int aie2_hwctx_cu_config(struct amdxdna_hwctx *hwctx, void *buf, u32 size if (!hwctx->cus) return -ENOMEM; - ret = amdxdna_pm_resume_get_locked(xdna); + ret = amdxdna_pm_resume_get(xdna); if (ret) goto free_cus; diff --git a/drivers/accel/amdxdna/amdxdna_ctx.c b/drivers/accel/amdxdna/amdxdna_ctx.c index ff6c3e8e5a15b..2ab4443e70946 100644 --- a/drivers/accel/amdxdna/amdxdna_ctx.c +++ b/drivers/accel/amdxdna/amdxdna_ctx.c @@ -356,16 +356,25 @@ int amdxdna_drm_config_hwctx_ioctl(struct drm_device *dev, void *data, struct dr return -EINVAL; } - guard(mutex)(&xdna->dev_lock); + ret = amdxdna_pm_resume_get(xdna); + if (ret) { + XDNA_ERR(xdna, "Resume failed, ret %d", ret); + goto free_buf; + } + + mutex_lock(&xdna->dev_lock); hwctx = xa_load(&client->hwctx_xa, args->handle); if (!hwctx) { XDNA_DBG(xdna, "PID %d failed to get hwctx %d", client->pid, args->handle); ret = -EINVAL; - goto free_buf; + goto unlock; } ret = xdna->dev_info->ops->hwctx_config(hwctx, args->param_type, val, buf, buf_size); +unlock: + mutex_unlock(&xdna->dev_lock); + amdxdna_pm_suspend_put(xdna); free_buf: kfree(buf); return ret; @@ -386,16 +395,25 @@ int amdxdna_hwctx_sync_debug_bo(struct amdxdna_client *client, u32 debug_bo_hdl) if (!gobj) return -EINVAL; + ret = amdxdna_pm_resume_get(xdna); + if (ret) { + XDNA_ERR(xdna, "Resume failed, ret %d", ret); + goto put_obj; + } + abo = to_xdna_obj(gobj); - guard(mutex)(&xdna->dev_lock); + mutex_lock(&xdna->dev_lock); hwctx = xa_load(&client->hwctx_xa, abo->assigned_hwctx); if (!hwctx) { ret = -EINVAL; - goto put_obj; + goto unlock; } ret = xdna->dev_info->ops->hwctx_sync_debug_bo(hwctx, debug_bo_hdl); +unlock: + mutex_unlock(&xdna->dev_lock); + amdxdna_pm_suspend_put(xdna); put_obj: drm_gem_object_put(gobj); return ret; From cf10c506fdbea7f4f05998f85aee65a1237aed1d Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Mon, 15 Jun 2026 23:15:32 -0700 Subject: [PATCH 2144/2758] accel/amdxdna: Use unsigned long for nr_pages in amdxdna_hmm_register() [ Upstream commit 18aaebdf43366954345a6721ed2bff3ac3c9fa61 ] nr_pages is declared as u32 in amdxdna_hmm_register(), which may not be large enough to represent the number of pages for large mappings. Use unsigned long for nr_pages to avoid potential overflow. Fixes: ac49797c1815 ("accel/amdxdna: Add GEM buffer object management") Reviewed-by: Max Zhen Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260616061532.3533469-1-lizhi.hou@amd.com Signed-off-by: Sasha Levin --- drivers/accel/amdxdna/amdxdna_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c index 6e367ddb9e1be..2ac12b49a7003 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.c +++ b/drivers/accel/amdxdna/amdxdna_gem.c @@ -291,7 +291,7 @@ static int amdxdna_hmm_register(struct amdxdna_gem_obj *abo, unsigned long len = vma->vm_end - vma->vm_start; unsigned long addr = vma->vm_start; struct amdxdna_umap *mapp; - u32 nr_pages; + unsigned long nr_pages; int ret; if (!xdna->dev_info->ops->hmm_invalidate) From bea20225c67fed9be3e98619c77af6ee3f43fe07 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Mon, 8 Jun 2026 18:12:42 -0700 Subject: [PATCH 2145/2758] accel/amdxdna: Fix VMA access race [ Upstream commit 1ba02717e821cf14ece642273958647e79698d3d ] aie2_populate_range() and amdxdna_umap_release() access a saved VMA pointer that may have already been freed, leading to a potential use-after-free. Remove the VMA accesses from these functions to avoid the race. Fixes: e486147c912f ("accel/amdxdna: Add BO import and export") Reviewed-by: Max Zhen Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260609011242.2833740-1-lizhi.hou@amd.com Signed-off-by: Sasha Levin --- drivers/accel/amdxdna/aie2_ctx.c | 2 -- drivers/accel/amdxdna/amdxdna_gem.c | 31 +++++++++++++++++++---------- drivers/accel/amdxdna/amdxdna_gem.h | 1 - 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c index f5d247d65cb06..884a64fa3e4fa 100644 --- a/drivers/accel/amdxdna/aie2_ctx.c +++ b/drivers/accel/amdxdna/aie2_ctx.c @@ -977,8 +977,6 @@ static int aie2_populate_range(struct amdxdna_gem_obj *abo) kref_get(&mapp->refcnt); up_write(&xdna->notifier_lock); - XDNA_DBG(xdna, "populate memory range %lx %lx", - mapp->vma->vm_start, mapp->vma->vm_end); mm = mapp->notifier.mm; if (!mmget_not_zero(mm)) { amdxdna_umap_put(mapp); diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c index 2ac12b49a7003..eb440bccd0cbd 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.c +++ b/drivers/accel/amdxdna/amdxdna_gem.c @@ -200,7 +200,7 @@ static bool amdxdna_hmm_invalidate(struct mmu_interval_notifier *mni, xdna = to_xdna_dev(to_gobj(abo)->dev); XDNA_DBG(xdna, "Invalidating range 0x%lx, 0x%lx, type %d", - mapp->vma->vm_start, mapp->vma->vm_end, abo->type); + mapp->range.start, mapp->range.end, abo->type); if (!mmu_notifier_range_blockable(range)) return false; @@ -229,15 +229,23 @@ static const struct mmu_interval_notifier_ops amdxdna_hmm_ops = { .invalidate = amdxdna_hmm_invalidate, }; +static inline bool compare_range(struct amdxdna_umap *mapp, + struct mm_struct *mm, + unsigned long start, unsigned long end) +{ + return (!mapp->unmapped && mapp->notifier.mm == mm && + mapp->range.start == start && mapp->range.end == end); +} + static void amdxdna_hmm_unregister(struct amdxdna_gem_obj *abo, struct vm_area_struct *vma) { struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev); struct amdxdna_umap *mapp; - down_read(&xdna->notifier_lock); + down_write(&xdna->notifier_lock); list_for_each_entry(mapp, &abo->mem.umap_list, node) { - if (!vma || mapp->vma == vma) { + if (!vma || compare_range(mapp, vma->vm_mm, vma->vm_start, vma->vm_end)) { if (!mapp->unmapped) { queue_work(xdna->notifier_wq, &mapp->hmm_unreg_work); mapp->unmapped = true; @@ -246,19 +254,16 @@ static void amdxdna_hmm_unregister(struct amdxdna_gem_obj *abo, break; } } - up_read(&xdna->notifier_lock); + up_write(&xdna->notifier_lock); } static void amdxdna_umap_release(struct kref *ref) { struct amdxdna_umap *mapp = container_of(ref, struct amdxdna_umap, refcnt); struct amdxdna_gem_obj *abo = mapp->abo; - struct vm_area_struct *vma = mapp->vma; struct amdxdna_dev *xdna; mmu_interval_notifier_remove(&mapp->notifier); - if (is_import_bo(abo) && vma->vm_file && vma->vm_file->f_mapping) - mapping_clear_unevictable(vma->vm_file->f_mapping); xdna = to_xdna_dev(to_gobj(mapp->abo)->dev); down_write(&xdna->notifier_lock); @@ -297,6 +302,15 @@ static int amdxdna_hmm_register(struct amdxdna_gem_obj *abo, if (!xdna->dev_info->ops->hmm_invalidate) return 0; + down_read(&xdna->notifier_lock); + list_for_each_entry(mapp, &abo->mem.umap_list, node) { + if (compare_range(mapp, current->mm, addr, addr + len)) { + up_read(&xdna->notifier_lock); + return 0; + } + } + up_read(&xdna->notifier_lock); + mapp = kzalloc_obj(*mapp); if (!mapp) return -ENOMEM; @@ -322,13 +336,10 @@ static int amdxdna_hmm_register(struct amdxdna_gem_obj *abo, mapp->range.start = vma->vm_start; mapp->range.end = vma->vm_end; mapp->range.default_flags = HMM_PFN_REQ_FAULT; - mapp->vma = vma; mapp->abo = abo; kref_init(&mapp->refcnt); INIT_WORK(&mapp->hmm_unreg_work, amdxdna_hmm_unreg_work); - if (is_import_bo(abo) && vma->vm_file && vma->vm_file->f_mapping) - mapping_set_unevictable(vma->vm_file->f_mapping); down_write(&xdna->notifier_lock); if (list_empty(&abo->mem.umap_list)) diff --git a/drivers/accel/amdxdna/amdxdna_gem.h b/drivers/accel/amdxdna/amdxdna_gem.h index 957305ccb485a..10242f4a5ca98 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.h +++ b/drivers/accel/amdxdna/amdxdna_gem.h @@ -12,7 +12,6 @@ #include "amdxdna_pci_drv.h" struct amdxdna_umap { - struct vm_area_struct *vma; struct mmu_interval_notifier notifier; struct hmm_range range; struct work_struct hmm_unreg_work; From ebc295ce343600c2d60c1e1e0c5d192080217457 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 30 Jun 2026 15:15:51 -0400 Subject: [PATCH 2146/2758] net/tls: Consume empty data records in tls_sw_read_sock() [ Upstream commit 3be28e2c9cd0230cb51fd4967df095273afd3848 ] A peer may send a zero-length TLS application_data record; TLS 1.3 explicitly permits these as a traffic-analysis countermeasure (RFC 8446, Section 5.1). After decryption such a record has full_len == 0. tls_sw_read_sock() hands it to the read_actor, which has no payload to consume and returns zero. The loop treats a zero return as backpressure (used <= 0), requeues the skb at the head of rx_list, and stops. rx_list is serviced head-first on the next call, so the empty record is dequeued, fails the same way, and is requeued again; every later record on the connection is blocked behind it. tls_sw_recvmsg() does not stall on this: a zero-length data record copies nothing and falls through to consume_skb(). Mirror that in the read_sock() path by recognizing an empty data record before the actor runs, consuming it, and continuing. Fixes: 662fbcec32f4 ("net/tls: implement ->read_sock()") Signed-off-by: Chuck Lever Reviewed-by: Sabrina Dubroca Link: https://patch.msgid.link/20260630191551.875664-1-cel@kernel.org Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/tls/tls_sw.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index a47f6a1e2c77d..ba95f9d927a35 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -2424,6 +2424,17 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc, goto read_sock_requeue; } + /* An empty data record (legal in TLS 1.3) gives a zero + * read_actor return, indistinguishable from the consumer + * stalling; the used <= 0 path would requeue it at the + * head of rx_list and block all later records. Consume it + * here instead. + */ + if (rxm->full_len == 0) { + consume_skb(skb); + continue; + } + used = read_actor(desc, skb, rxm->offset, rxm->full_len); if (used <= 0) { if (!copied) From 40824fc26ad318dc9e65db1907843afc673b298d Mon Sep 17 00:00:00 2001 From: Dong Yibo Date: Wed, 1 Jul 2026 11:22:08 +0800 Subject: [PATCH 2147/2758] net: rnpgbe: fix mailbox endianness and remove pointer casts [ Upstream commit d9d6d67f4c0877fde783c9d5beee013bcf1b1e85 ] The rnpgbe mailbox exchanges data through 32-bit MMIO registers in little-endian wire format. The original code had two problems: 1. FW structs (with __le16/__le32 fields) were cast to (u32 *) before reaching the mailbox transport, hiding the endian annotations from sparse. 2. No cpu_to_le32()/le32_to_cpu() conversion was done between CPU-endian MMIO values and the little-endian payload, causing data corruption on big-endian systems. Fix by adding the missing byte-order conversions in the transport layer and introducing union wrappers (mbx_fw_cmd_req_u, mbx_fw_cmd_reply_u) that overlay each FW struct with a __le32 dwords[] array. Callers fill named fields using cpu_to_le16/32(), then pass dwords[] to the transport, which now takes explicit __le32 * instead of u32 *. This eliminates all pointer casts on the mailbox data path and lets sparse verify the conversions. Fixes: 4543534c3ef5 ("net: rnpgbe: Add basic mbx ops support") Signed-off-by: Dong Yibo Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20260701032208.1843156-2-dong100@mucse.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- .../net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c | 26 ++++-- .../net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h | 5 +- .../net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c | 82 ++++++++++--------- .../net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h | 14 ++++ 4 files changed, 80 insertions(+), 47 deletions(-) diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c index de5e29230b3c8..c464086982632 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c @@ -166,18 +166,23 @@ static void mucse_mbx_inc_pf_ack(struct mucse_hw *hw) * * Return: 0 on success, negative errno on failure **/ -static int mucse_read_mbx_pf(struct mucse_hw *hw, u32 *msg, u16 size) +static int mucse_read_mbx_pf(struct mucse_hw *hw, __le32 *msg, u16 size) { - const int size_in_words = size / sizeof(u32); + const int size_in_words = size / sizeof(__le32); struct mucse_mbx_info *mbx = &hw->mbx; + int off = MUCSE_MBX_FWPF_SHM; int err; err = mucse_obtain_mbx_lock_pf(hw); if (err) return err; + /* memcpy_fromio() is unsuitable: the mailbox uses 32-bit MMIO + * registers, not byte-addressable RAM. readl() guarantees + * the required 32-bit access width. + */ for (int i = 0; i < size_in_words; i++) - msg[i] = mbx_data_rd32(mbx, MUCSE_MBX_FWPF_SHM + 4 * i); + msg[i] = cpu_to_le32(mbx_data_rd32(mbx, off + 4 * i)); /* Hw needs write data_reg at last */ mbx_data_wr32(mbx, MUCSE_MBX_FWPF_SHM, 0); /* flush reqs as we have read this request data */ @@ -236,7 +241,7 @@ static int mucse_poll_for_msg(struct mucse_hw *hw) * Return: 0 if it successfully received a message notification and * copied it into the receive buffer, negative errno on failure **/ -int mucse_poll_and_read_mbx(struct mucse_hw *hw, u32 *msg, u16 size) +int mucse_poll_and_read_mbx(struct mucse_hw *hw, __le32 *msg, u16 size) { int err; @@ -290,9 +295,9 @@ static void mucse_mbx_inc_pf_req(struct mucse_hw *hw) * Return: 0 if it successfully copied message into the buffer, * negative errno on failure **/ -static int mucse_write_mbx_pf(struct mucse_hw *hw, u32 *msg, u16 size) +static int mucse_write_mbx_pf(struct mucse_hw *hw, const __le32 *msg, u16 size) { - const int size_in_words = size / sizeof(u32); + const int size_in_words = size / sizeof(__le32); struct mucse_mbx_info *mbx = &hw->mbx; int err; @@ -300,8 +305,12 @@ static int mucse_write_mbx_pf(struct mucse_hw *hw, u32 *msg, u16 size) if (err) return err; + /* memcpy_toio() would decompose into arbitrary-width accesses; + * the mailbox requires 32-bit MMIO writes via writel(). + */ for (int i = 0; i < size_in_words; i++) - mbx_data_wr32(mbx, MUCSE_MBX_FWPF_SHM + i * 4, msg[i]); + mbx_data_wr32(mbx, MUCSE_MBX_FWPF_SHM + i * 4, + le32_to_cpu(msg[i])); /* flush acks as we are overwriting the message buffer */ hw->mbx.fw_ack = mucse_mbx_get_fwack(mbx); @@ -360,7 +369,8 @@ static int mucse_poll_for_ack(struct mucse_hw *hw) * Return: 0 if it successfully copied message into the buffer and * received an ack to that message within delay * timeout_cnt period **/ -int mucse_write_and_wait_ack_mbx(struct mucse_hw *hw, u32 *msg, u16 size) +int mucse_write_and_wait_ack_mbx(struct mucse_hw *hw, const __le32 *msg, + u16 size) { int err; diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h index e6fcc8d1d3ca7..75b88b18b04d3 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h @@ -14,7 +14,8 @@ #define MUCSE_MBX_REQ BIT(0) /* Request a req to mailbox */ #define MUCSE_MBX_PFU BIT(3) /* PF owns the mailbox buffer */ -int mucse_write_and_wait_ack_mbx(struct mucse_hw *hw, u32 *msg, u16 size); +int mucse_write_and_wait_ack_mbx(struct mucse_hw *hw, + const __le32 *msg, u16 size); void mucse_init_mbx_params_pf(struct mucse_hw *hw); -int mucse_poll_and_read_mbx(struct mucse_hw *hw, u32 *msg, u16 size); +int mucse_poll_and_read_mbx(struct mucse_hw *hw, __le32 *msg, u16 size); #endif /* _RNPGBE_MBX_H */ diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c index 8c8bd5e8e1db1..5ba74997beacf 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c @@ -20,32 +20,32 @@ * Return: 0 on success, negative errno on failure **/ static int mucse_fw_send_cmd_wait_resp(struct mucse_hw *hw, - struct mbx_fw_cmd_req *req, - struct mbx_fw_cmd_reply *reply) + union mbx_fw_cmd_req_u *req, + union mbx_fw_cmd_reply_u *reply) { - int len = le16_to_cpu(req->datalen); + int len = le16_to_cpu(req->r.datalen); int retry_cnt = 3; int err; mutex_lock(&hw->mbx.lock); - err = mucse_write_and_wait_ack_mbx(hw, (u32 *)req, len); + err = mucse_write_and_wait_ack_mbx(hw, req->dwords, len); if (err) goto out; do { - err = mucse_poll_and_read_mbx(hw, (u32 *)reply, - sizeof(*reply)); + err = mucse_poll_and_read_mbx(hw, reply->dwords, + sizeof(reply->r)); if (err) goto out; /* mucse_write_and_wait_ack_mbx return 0 means fw has * received request, wait for the expect opcode * reply with 'retry_cnt' times. */ - } while (--retry_cnt >= 0 && reply->opcode != req->opcode); + } while (--retry_cnt >= 0 && reply->r.opcode != req->r.opcode); out: mutex_unlock(&hw->mbx.lock); if (!err && retry_cnt < 0) return -ETIMEDOUT; - if (!err && reply->error_code) + if (!err && reply->r.error_code) return -EIO; return err; @@ -61,17 +61,19 @@ static int mucse_fw_send_cmd_wait_resp(struct mucse_hw *hw, **/ static int mucse_mbx_get_info(struct mucse_hw *hw) { - struct mbx_fw_cmd_req req = { - .datalen = cpu_to_le16(MUCSE_MBX_REQ_HDR_LEN), - .opcode = cpu_to_le16(GET_HW_INFO), + union mbx_fw_cmd_req_u req = { + .r = { + .datalen = cpu_to_le16(MUCSE_MBX_REQ_HDR_LEN), + .opcode = cpu_to_le16(GET_HW_INFO), + }, }; - struct mbx_fw_cmd_reply reply = {}; + union mbx_fw_cmd_reply_u reply = {}; int err; err = mucse_fw_send_cmd_wait_resp(hw, &req, &reply); if (!err) hw->pfvfnum = FIELD_GET(GENMASK_U16(7, 0), - le16_to_cpu(reply.hw_info.pfnum)); + le16_to_cpu(reply.r.hw_info.pfnum)); return err; } @@ -111,21 +113,23 @@ int mucse_mbx_sync_fw(struct mucse_hw *hw) **/ int mucse_mbx_powerup(struct mucse_hw *hw, bool is_powerup) { - struct mbx_fw_cmd_req req = { - .datalen = cpu_to_le16(sizeof(req.powerup) + - MUCSE_MBX_REQ_HDR_LEN), - .opcode = cpu_to_le16(POWER_UP), - .powerup = { - /* fw needs this to reply correct cmd */ - .version = cpu_to_le32(GENMASK_U32(31, 0)), - .status = cpu_to_le32(is_powerup ? 1 : 0), + union mbx_fw_cmd_req_u req = { + .r = { + .datalen = cpu_to_le16(sizeof(req.r.powerup) + + MUCSE_MBX_REQ_HDR_LEN), + .opcode = cpu_to_le16(POWER_UP), + .powerup = { + /* fw needs this to reply correct cmd */ + .version = cpu_to_le32(GENMASK_U32(31, 0)), + .status = cpu_to_le32(is_powerup ? 1 : 0), + }, }, }; int len, err; - len = le16_to_cpu(req.datalen); + len = le16_to_cpu(req.r.datalen); mutex_lock(&hw->mbx.lock); - err = mucse_write_and_wait_ack_mbx(hw, (u32 *)&req, len); + err = mucse_write_and_wait_ack_mbx(hw, req.dwords, len); mutex_unlock(&hw->mbx.lock); return err; @@ -142,11 +146,13 @@ int mucse_mbx_powerup(struct mucse_hw *hw, bool is_powerup) **/ int mucse_mbx_reset_hw(struct mucse_hw *hw) { - struct mbx_fw_cmd_req req = { - .datalen = cpu_to_le16(MUCSE_MBX_REQ_HDR_LEN), - .opcode = cpu_to_le16(RESET_HW), + union mbx_fw_cmd_req_u req = { + .r = { + .datalen = cpu_to_le16(MUCSE_MBX_REQ_HDR_LEN), + .opcode = cpu_to_le16(RESET_HW), + }, }; - struct mbx_fw_cmd_reply reply = {}; + union mbx_fw_cmd_reply_u reply = {}; return mucse_fw_send_cmd_wait_resp(hw, &req, &reply); } @@ -166,24 +172,26 @@ int mucse_mbx_get_macaddr(struct mucse_hw *hw, int pfvfnum, u8 *mac_addr, int port) { - struct mbx_fw_cmd_req req = { - .datalen = cpu_to_le16(sizeof(req.get_mac_addr) + - MUCSE_MBX_REQ_HDR_LEN), - .opcode = cpu_to_le16(GET_MAC_ADDRESS), - .get_mac_addr = { - .port_mask = cpu_to_le32(BIT(port)), - .pfvf_num = cpu_to_le32(pfvfnum), + union mbx_fw_cmd_req_u req = { + .r = { + .datalen = cpu_to_le16(sizeof(req.r.get_mac_addr) + + MUCSE_MBX_REQ_HDR_LEN), + .opcode = cpu_to_le16(GET_MAC_ADDRESS), + .get_mac_addr = { + .port_mask = cpu_to_le32(BIT(port)), + .pfvf_num = cpu_to_le32(pfvfnum), + }, }, }; - struct mbx_fw_cmd_reply reply = {}; + union mbx_fw_cmd_reply_u reply = {}; int err; err = mucse_fw_send_cmd_wait_resp(hw, &req, &reply); if (err) return err; - if (le32_to_cpu(reply.mac_addr.ports) & BIT(port)) - memcpy(mac_addr, reply.mac_addr.addrs[port].mac, ETH_ALEN); + if (le32_to_cpu(reply.r.mac_addr.ports) & BIT(port)) + memcpy(mac_addr, reply.r.mac_addr.addrs[port].mac, ETH_ALEN); else return -ENODATA; diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h index fb24fc12b6139..fe996aeffc4de 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h @@ -80,6 +80,20 @@ struct mbx_fw_cmd_reply { }; } __packed; +/* Union wrappers to expose struct as __le32 dword array for mailbox + * transport, eliminating the need for pointer casts. The __packed + * structs have no padding, so dwords[] overlays the fields exactly. + */ +union mbx_fw_cmd_req_u { + struct mbx_fw_cmd_req r; + __le32 dwords[sizeof(struct mbx_fw_cmd_req) / sizeof(__le32)]; +}; + +union mbx_fw_cmd_reply_u { + struct mbx_fw_cmd_reply r; + __le32 dwords[sizeof(struct mbx_fw_cmd_reply) / sizeof(__le32)]; +}; + int mucse_mbx_sync_fw(struct mucse_hw *hw); int mucse_mbx_powerup(struct mucse_hw *hw, bool is_powerup); int mucse_mbx_reset_hw(struct mucse_hw *hw); From 81acef3a247fd523513a2e9f71de1c167bc0f882 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Wed, 1 Jul 2026 00:08:47 -0400 Subject: [PATCH 2148/2758] net/liquidio: drop cached VF pci_dev LUT [ Upstream commit 5c0e3ba4f500fd4314ceb42f07f16bc445156431 ] The PF SR-IOV enable path caches VF pci_dev pointers in dpiring_to_vfpcidev_lut[] by iterating with pci_get_device(). Those entries do not own a reference, because the iterator drops the previous device reference on each step. The cached pointer is then dereferenced later when handling OCTEON_VF_FLR_REQUEST. Replace the cached VF mapping with runtime lookup on the mailbox DPI ring: derive the VF index from q_no, resolve the VF via exported PCI IOV helpers, validate it with the PF pointer and VF ID, then issue pcie_flr() and drop the reference with pci_dev_put(). Remove the unused VF lookup table initialization and cleanup. Fixes: ca6139ffc67ee ("liquidio CN23XX: sysfs VF config support") Fixes: 8c978d059224 ("liquidio CN23XX: Mailbox support") Signed-off-by: Yuho Choi Link: https://patch.msgid.link/20260701040847.1897845-1-dbgh9129@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- .../net/ethernet/cavium/liquidio/lio_main.c | 27 --------------- .../ethernet/cavium/liquidio/octeon_device.h | 3 -- .../ethernet/cavium/liquidio/octeon_mailbox.c | 33 ++++++++++++++++++- 3 files changed, 32 insertions(+), 31 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index c1a3df2252549..32dd9b25760e3 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -3781,9 +3781,7 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) static int octeon_enable_sriov(struct octeon_device *oct) { unsigned int num_vfs_alloced = oct->sriov_info.num_vfs_alloced; - struct pci_dev *vfdev; int err; - u32 u; if (OCTEON_CN23XX_PF(oct) && num_vfs_alloced) { err = pci_enable_sriov(oct->pci_dev, @@ -3796,23 +3794,6 @@ static int octeon_enable_sriov(struct octeon_device *oct) return err; } oct->sriov_info.sriov_enabled = 1; - - /* init lookup table that maps DPI ring number to VF pci_dev - * struct pointer - */ - u = 0; - vfdev = pci_get_device(PCI_VENDOR_ID_CAVIUM, - OCTEON_CN23XX_VF_VID, NULL); - while (vfdev) { - if (vfdev->is_virtfn && - (vfdev->physfn == oct->pci_dev)) { - oct->sriov_info.dpiring_to_vfpcidev_lut[u] = - vfdev; - u += oct->sriov_info.rings_per_vf; - } - vfdev = pci_get_device(PCI_VENDOR_ID_CAVIUM, - OCTEON_CN23XX_VF_VID, vfdev); - } } return num_vfs_alloced; @@ -3820,8 +3801,6 @@ static int octeon_enable_sriov(struct octeon_device *oct) static int lio_pci_sriov_disable(struct octeon_device *oct) { - int u; - if (pci_vfs_assigned(oct->pci_dev)) { dev_err(&oct->pci_dev->dev, "VFs are still assigned to VMs.\n"); return -EPERM; @@ -3829,12 +3808,6 @@ static int lio_pci_sriov_disable(struct octeon_device *oct) pci_disable_sriov(oct->pci_dev); - u = 0; - while (u < MAX_POSSIBLE_VFS) { - oct->sriov_info.dpiring_to_vfpcidev_lut[u] = NULL; - u += oct->sriov_info.rings_per_vf; - } - oct->sriov_info.num_vfs_alloced = 0; dev_info(&oct->pci_dev->dev, "oct->pf_num:%d disabled VFs\n", oct->pf_num); diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.h b/drivers/net/ethernet/cavium/liquidio/octeon_device.h index 19344b21f8fb9..858a0fff2cc0b 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.h @@ -390,9 +390,6 @@ struct octeon_sriov_info { struct lio_trusted_vf trusted_vf; - /*lookup table that maps DPI ring number to VF pci_dev struct pointer*/ - struct pci_dev *dpiring_to_vfpcidev_lut[MAX_POSSIBLE_VFS]; - u64 vf_macaddr[MAX_POSSIBLE_VFS]; u16 vf_vlantci[MAX_POSSIBLE_VFS]; diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c b/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c index ad685f5d0a136..697fcdc41e3cd 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c @@ -26,6 +26,31 @@ #include "octeon_mailbox.h" #include "cn23xx_pf_device.h" +static struct pci_dev *lio_vf_pci_dev_by_qno(struct octeon_device *oct, u32 q_no) +{ + struct pci_dev *vfdev = NULL; + int vfidx; + + if (!oct->sriov_info.rings_per_vf) + return NULL; + + if (q_no % oct->sriov_info.rings_per_vf) + return NULL; + + vfidx = q_no / oct->sriov_info.rings_per_vf; + if (vfidx >= oct->sriov_info.num_vfs_alloced) + return NULL; + + while ((vfdev = pci_get_device(PCI_VENDOR_ID_CAVIUM, + OCTEON_CN23XX_VF_VID, vfdev))) { + if (pci_physfn(vfdev) && pci_physfn(vfdev) == oct->pci_dev && + pci_iov_vf_id(vfdev) == vfidx) + return vfdev; + } + + return NULL; +} + /** * octeon_mbox_read: * @mbox: Pointer mailbox @@ -237,6 +262,7 @@ static int octeon_mbox_process_cmd(struct octeon_mbox *mbox, struct octeon_mbox_cmd *mbox_cmd) { struct octeon_device *oct = mbox->oct_dev; + struct pci_dev *vfdev; switch (mbox_cmd->msg.s.cmd) { case OCTEON_VF_ACTIVE: @@ -260,7 +286,12 @@ static int octeon_mbox_process_cmd(struct octeon_mbox *mbox, dev_info(&oct->pci_dev->dev, "got a request for FLR from VF that owns DPI ring %u\n", mbox->q_no); - pcie_flr(oct->sriov_info.dpiring_to_vfpcidev_lut[mbox->q_no]); + vfdev = lio_vf_pci_dev_by_qno(oct, mbox->q_no); + if (!vfdev) + break; + + pcie_flr(vfdev); + pci_dev_put(vfdev); break; case OCTEON_PF_CHANGED_VF_MACADDR: From 8aede22b6a6948ab608c15c0086139295e8c94b1 Mon Sep 17 00:00:00 2001 From: Enrico Pozzobon Date: Wed, 1 Jul 2026 16:47:23 +0200 Subject: [PATCH 2149/2758] net: usb: lan78xx: disable VLAN filter in promiscuous mode [ Upstream commit 60444706aa17616efc03190d099ac347e28b3d0a ] The hardware VLAN filter (RFE_CTL_VLAN_FILTER_) drops VLAN-tagged frames whose VID has not been registered via lan78xx_vlan_rx_add_vid(). It is left enabled in promiscuous mode, so packet capture (e.g. tcpdump or Wireshark) does not see tagged frames for unregistered VIDs. Clear the filter while the interface is promiscuous and restore it from NETIF_F_HW_VLAN_CTAG_FILTER otherwise. Enforce the same condition in lan78xx_set_features() so netdev_update_features() cannot re-enable the filter while promiscuous. Fixes: 55d7de9de6c3 ("Microchip's LAN7800 family USB 2/3 to 10/100/1000 Ethernet device driver") Signed-off-by: Enrico Pozzobon Reviewed-by: Nicolai Buchwitz Link: https://patch.msgid.link/20260701-lan78xx-vlan-promisc-v3-1-232266d32743@dissecto.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/usb/lan78xx.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index c4cebacabcb53..cb782d81d84f2 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -1499,6 +1499,17 @@ static void lan78xx_deferred_multicast_write(struct work_struct *param) return; } +static void lan78xx_update_vlan_filter(struct lan78xx_priv *pdata, + struct net_device *netdev, + netdev_features_t features) +{ + if ((features & NETIF_F_HW_VLAN_CTAG_FILTER) && + !(netdev->flags & IFF_PROMISC)) + pdata->rfe_ctl |= RFE_CTL_VLAN_FILTER_; + else + pdata->rfe_ctl &= ~RFE_CTL_VLAN_FILTER_; +} + static void lan78xx_set_multicast(struct net_device *netdev) { struct lan78xx_net *dev = netdev_priv(netdev); @@ -1533,6 +1544,8 @@ static void lan78xx_set_multicast(struct net_device *netdev) } } + lan78xx_update_vlan_filter(pdata, dev->net, dev->net->features); + if (netdev_mc_count(dev->net)) { struct netdev_hw_addr *ha; int i; @@ -3074,10 +3087,7 @@ static int lan78xx_set_features(struct net_device *netdev, else pdata->rfe_ctl &= ~RFE_CTL_VLAN_STRIP_; - if (features & NETIF_F_HW_VLAN_CTAG_FILTER) - pdata->rfe_ctl |= RFE_CTL_VLAN_FILTER_; - else - pdata->rfe_ctl &= ~RFE_CTL_VLAN_FILTER_; + lan78xx_update_vlan_filter(pdata, netdev, features); spin_unlock_irqrestore(&pdata->rfe_ctl_lock, flags); From 82202fc724f44cf599f7740cb4cd5ea5171eba67 Mon Sep 17 00:00:00 2001 From: Jia Wang Date: Thu, 2 Jul 2026 17:22:12 +0800 Subject: [PATCH 2150/2758] gpio: dwapb: Defer clock gating until noirq [ Upstream commit d775b9451eb8f52021dea6483ad758fef81dbf1e ] GPIO consumers such as gpio-keys can enable IRQ wake and adjust the wake trigger type from their suspend callbacks. If the DWAPB controller suspends first, masking interrupts and disabling its clocks in the normal suspend phase prevents that late wake configuration from reliably reaching the hardware. Systems with real DWAPB bus clocks then fail to wake from s2idle through GPIO keys. Save the register context in the normal suspend callback, but defer IRQ masking and clock gating until suspend_noirq. At that point all consumers have finished configuring wake IRQs, so keep the clocks enabled when wake lines are armed and only gate them when no wake source is active. Resume_noirq reenables clocks, if they were gated, before the normal resume path restores registers. Propagate wake requests to the parent irqchip while keeping the local wake mask in sync with failures. Fixes: 6437c7ba69c3 ("gpio: dwapb: Add wakeup source support") Signed-off-by: Jia Wang Link: https://patch.msgid.link/20260702-gpio-dwapb-wakeup-v2-1-203f2f33429f@ultrarisc.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpio-dwapb.c | 79 +++++++++++++++++++++++++++++++++------ 1 file changed, 67 insertions(+), 12 deletions(-) diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c index 15cebc8b5d663..0259c65973323 100644 --- a/drivers/gpio/gpio-dwapb.c +++ b/drivers/gpio/gpio-dwapb.c @@ -118,6 +118,7 @@ struct dwapb_gpio { unsigned int flags; struct reset_control *rst; struct clk_bulk_data clks[DWAPB_NR_CLOCKS]; + bool clocks_on_for_wake; struct dwapb_gpio_port ports[] __counted_by(nr_ports); }; @@ -365,11 +366,24 @@ static int dwapb_irq_set_wake(struct irq_data *d, unsigned int enable) struct dwapb_gpio *gpio = to_dwapb_gpio(gc); struct dwapb_context *ctx = gpio->ports[0].ctx; irq_hw_number_t bit = irqd_to_hwirq(d); + u32 wake_en = ctx->wake_en; if (enable) - ctx->wake_en |= BIT(bit); + wake_en |= BIT(bit); else - ctx->wake_en &= ~BIT(bit); + wake_en &= ~BIT(bit); + +#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY + if (d->parent_data && !!ctx->wake_en != !!wake_en) { + int err; + + err = irq_chip_set_wake_parent(d, enable); + if (err) + return err; + } +#endif + + ctx->wake_en = wake_en; return 0; } @@ -749,6 +763,8 @@ static int dwapb_gpio_suspend(struct device *dev) int i; scoped_guard(gpio_generic_lock_irqsave, gen_gc) { + gpio->clocks_on_for_wake = false; + for (i = 0; i < gpio->nr_ports; i++) { unsigned int offset; unsigned int idx = gpio->ports[i].idx; @@ -770,11 +786,38 @@ static int dwapb_gpio_suspend(struct device *dev) ctx->int_pol = dwapb_read(gpio, GPIO_INT_POLARITY); ctx->int_type = dwapb_read(gpio, GPIO_INTTYPE_LEVEL); ctx->int_deb = dwapb_read(gpio, GPIO_PORTA_DEBOUNCE); + } + } + } + + return 0; +} + +static int dwapb_gpio_suspend_noirq(struct device *dev) +{ + struct dwapb_gpio *gpio = dev_get_drvdata(dev); + struct gpio_generic_chip *gen_gc = &gpio->ports[0].chip; + bool wake_enabled = false; + int i; + + scoped_guard(gpio_generic_lock_irqsave, gen_gc) { + for (i = 0; i < gpio->nr_ports; i++) { + unsigned int idx = gpio->ports[i].idx; + struct dwapb_context *ctx = gpio->ports[i].ctx; - /* Mask out interrupts */ + if (idx == 0) { + wake_enabled = ctx->wake_en; dwapb_write(gpio, GPIO_INTMASK, ~ctx->wake_en); + break; } } + + gpio->clocks_on_for_wake = wake_enabled; + } + + if (wake_enabled) { + device_set_wakeup_path(dev); + return 0; } clk_bulk_disable_unprepare(DWAPB_NR_CLOCKS, gpio->clks); @@ -782,18 +825,27 @@ static int dwapb_gpio_suspend(struct device *dev) return 0; } -static int dwapb_gpio_resume(struct device *dev) +static int dwapb_gpio_resume_noirq(struct device *dev) { struct dwapb_gpio *gpio = dev_get_drvdata(dev); - struct gpio_chip *gc = &gpio->ports[0].chip.gc; - struct gpio_generic_chip *gen_gc = to_gpio_generic_chip(gc); - int i, err; + int err; + + if (gpio->clocks_on_for_wake) + return 0; err = clk_bulk_prepare_enable(DWAPB_NR_CLOCKS, gpio->clks); - if (err) { + if (err) dev_err(gpio->dev, "Cannot reenable APB/Debounce clocks\n"); - return err; - } + + return err; +} + +static int dwapb_gpio_resume(struct device *dev) +{ + struct dwapb_gpio *gpio = dev_get_drvdata(dev); + struct gpio_chip *gc = &gpio->ports[0].chip.gc; + struct gpio_generic_chip *gen_gc = to_gpio_generic_chip(gc); + int i; guard(gpio_generic_lock_irqsave)(gen_gc); @@ -827,8 +879,11 @@ static int dwapb_gpio_resume(struct device *dev) return 0; } -static DEFINE_SIMPLE_DEV_PM_OPS(dwapb_gpio_pm_ops, - dwapb_gpio_suspend, dwapb_gpio_resume); +static const struct dev_pm_ops dwapb_gpio_pm_ops = { + SYSTEM_SLEEP_PM_OPS(dwapb_gpio_suspend, dwapb_gpio_resume) + NOIRQ_SYSTEM_SLEEP_PM_OPS(dwapb_gpio_suspend_noirq, + dwapb_gpio_resume_noirq) +}; static struct platform_driver dwapb_gpio_driver = { .driver = { From 170c008d3f49a5774d078d8a9206e2239f2e1bc2 Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Fri, 3 Jul 2026 09:32:30 +0200 Subject: [PATCH 2151/2758] drm: Guard DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE [ Upstream commit ac7c6b4308c5e757eb278d47b9c0761c31ac9d6b ] The client cap is currently advertised unconditionally, even for drivers that do not support plane color pipelines. If clients supporting the latter, like Wayland compositors or tools like drm_info, enable the client cap on such drivers they will be left without both color pipeline and the legacy properties COLOR_ENCODING and COLOR_RANGE, effectively breaking YUV->RGB conversion support. Prevent that by only marking the cap supported if there are actually planes with color pipelines. Note: while the color pipeline replacement for the legacy properties is still under review (1), we can assume that it will work as a drop-in replacement. That means any plane on any hardware currently supporting the legacy properties will be able to offer a functionally equal color pipeline and there will be no technical reason keep using the legacy properties if both the driver and the client support the new API. [1] https://lore.kernel.org/dri-devel/20260623164812.81110-1-harry.wentland@amd.com/ Signed-off-by: Robert Mader Reviewed-by: Chaitanya Kumar Borah Reviewed-by: Melissa Wen Fixes: 179ab8e7d7b3 ("drm/colorop: Introduce DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE") Link: https://patch.msgid.link/20260703073230.19982-1-robert.mader@collabora.com Suggested-by: Maarten Lankhorst Signed-off-by: Maarten Lankhorst Signed-off-by: Sasha Levin --- drivers/gpu/drm/drm_ioctl.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index e2df4becce629..9039a39c43243 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -373,13 +373,25 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv) return -EINVAL; file_priv->supports_virtualized_cursor_plane = req->value; break; - case DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE: + case DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE: { + struct drm_plane *plane; + bool has_plane_with_color_pipeline = false; + if (!file_priv->atomic) return -EINVAL; if (req->value > 1) return -EINVAL; + drm_for_each_plane(plane, dev) { + if (plane->color_pipeline_property) { + has_plane_with_color_pipeline = true; + break; + } + } + if (!has_plane_with_color_pipeline) + return -EOPNOTSUPP; file_priv->plane_color_pipeline = req->value; break; + } default: return -EINVAL; } From 6cb18e712feb1cfaabd087a98a6f4d27a894a6af Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 17 Jun 2026 11:58:22 +0100 Subject: [PATCH 2152/2758] tracing: Make tracepoint_printk static as not exported [ Upstream commit d5c6785f94ead7cd76b38af301ac63cb673485bc ] The tracepoint_printk symbol is not exported, so make it static to remove the following sparse warning: kernel/trace/trace.c:90:5: warning: symbol 'tracepoint_printk' was not declared. Should it be static? Fixes: dd293df6395a2 ("tracing: Move trace sysctls into trace.c") Link: https://patch.msgid.link/20260617105822.904164-1-ben.dooks@codethink.co.uk Signed-off-by: Ben Dooks Signed-off-by: Steven Rostedt Signed-off-by: Sasha Levin --- kernel/trace/trace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 6eb4d3097a4d5..4c3729c8d5e20 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -87,7 +87,7 @@ void __init disable_tracing_selftest(const char *reason) /* Pipe tracepoints to printk */ static struct trace_iterator *tracepoint_print_iter; -int tracepoint_printk; +static int tracepoint_printk; static bool tracepoint_printk_stop_on_boot __initdata; static bool traceoff_after_boot __initdata; static DEFINE_STATIC_KEY_FALSE(tracepoint_printk_key); From 91e8109ecffb925b6202d2737384df285b195bfb Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Mon, 6 Jul 2026 15:12:28 -0700 Subject: [PATCH 2153/2758] accel/amdxdna: Fix potential amdxdna_umap lifetime race [ Upstream commit 14f172eff9c19f8043a9858845f33cd034f3a41e ] amdxdna_umap_release() calls the blocking mmu_interval_notifier_remove() before removing the object from abo->mem.umap_list. If aie2_populate_range() runs concurrently, it may obtain a reference to an amdxdna_umap that is being released, leading to a potential use-after-free. Use kref_get_unless_zero() in aie2_populate_range() when acquiring a reference. If the reference count has already dropped to zero, release is in progress and the entry is skipped. Fixes: e486147c912f ("accel/amdxdna: Add BO import and export") Reviewed-by: Max Zhen Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260706221228.434099-1-lizhi.hou@amd.com Signed-off-by: Sasha Levin --- drivers/accel/amdxdna/aie2_ctx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c index 884a64fa3e4fa..a417b7d9ffac3 100644 --- a/drivers/accel/amdxdna/aie2_ctx.c +++ b/drivers/accel/amdxdna/aie2_ctx.c @@ -963,7 +963,7 @@ static int aie2_populate_range(struct amdxdna_gem_obj *abo) found = false; down_write(&xdna->notifier_lock); list_for_each_entry(mapp, &abo->mem.umap_list, node) { - if (mapp->invalid) { + if (mapp->invalid && kref_get_unless_zero(&mapp->refcnt)) { found = true; break; } @@ -974,7 +974,7 @@ static int aie2_populate_range(struct amdxdna_gem_obj *abo) up_write(&xdna->notifier_lock); return 0; } - kref_get(&mapp->refcnt); + up_write(&xdna->notifier_lock); mm = mapp->notifier.mm; From 5d65dade4d84913d1879f3db6a12ac007e08314b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ADra=20Canal?= Date: Fri, 3 Jul 2026 12:33:35 -0300 Subject: [PATCH 2154/2758] drm/v3d: Reject invalid indirect BO handle in indirect CSD setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 2f8b8593c7832fad655290cef9e99af05b1b52b3 ] v3d_get_cpu_indirect_csd_params() looks up the indirect buffer object from a userspace-supplied handle but never checks the result. A bogus or stale handle makes drm_gem_object_lookup() return NULL, which is then stored in info->indirect and only dereferenced later when the indirect CSD job runs, turning a userspace mistake into a NULL pointer dereference in the kernel. Bail out with -ENOENT as soon as the lookup fails, so the bad handle is rejected at submission time. Fixes: 18b8413b25b7 ("drm/v3d: Create a CPU job extension for a indirect CSD job") Reviewed-by: Iago Toral Quiroga Signed-off-by: Maíra Canal Link: https://patch.msgid.link/20260703-v3d-cpu-job-fixes-v3-2-bc51b1f3eeb5@igalia.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/v3d/v3d_submit.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/v3d/v3d_submit.c b/drivers/gpu/drm/v3d/v3d_submit.c index 3ddd53b6f4376..f6a9bfa3548ce 100644 --- a/drivers/gpu/drm/v3d/v3d_submit.c +++ b/drivers/gpu/drm/v3d/v3d_submit.c @@ -484,6 +484,8 @@ v3d_get_cpu_indirect_csd_params(struct drm_file *file_priv, sizeof(indirect_csd.wg_uniform_offsets)); info->indirect = drm_gem_object_lookup(file_priv, indirect_csd.indirect); + if (!info->indirect) + return -ENOENT; return v3d_setup_csd_jobs_and_bos(file_priv, v3d, &indirect_csd.submit, &info->job, &info->clean_job, From 3861bae3ffe43fa9612244a452d3c340d5fc3c73 Mon Sep 17 00:00:00 2001 From: Cihan Karadag Date: Tue, 7 Jul 2026 17:57:05 -0600 Subject: [PATCH 2155/2758] selftests: gpio: add gpio-cdev-uaf to .gitignore [ Upstream commit 4ad805f0e41acf56ac54855c666d34c59ad66fe7 ] Commit c7f92042d3f3 ("selftests: gpio: Add gpio-cdev-uaf tests") added the gpio-cdev-uaf binary to TEST_GEN_PROGS_EXTENDED but never added it to .gitignore. Building it with: make -C tools/testing/selftests/gpio TARGETS=gpio leaves gpio-cdev-uaf as an untracked file. Fixes: c7f92042d3f3 ("selftests: gpio: Add gpio-cdev-uaf tests") Signed-off-by: Cihan Karadag Reviewed-by: Tzung-Bi Shih Link: https://patch.msgid.link/20260707235707.1349969-1-cihan.cihan@gmail.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- tools/testing/selftests/gpio/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/gpio/.gitignore b/tools/testing/selftests/gpio/.gitignore index ededb077a3a69..16f74de479f17 100644 --- a/tools/testing/selftests/gpio/.gitignore +++ b/tools/testing/selftests/gpio/.gitignore @@ -2,3 +2,4 @@ gpio-mockup-cdev gpio-chip-info gpio-line-name +gpio-cdev-uaf From e3d1ca7882a529a90ccbe6c9e2c7bd8d92caf84f Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 1 Jul 2026 20:26:52 -0700 Subject: [PATCH 2156/2758] net: mdio: select REGMAP_MMIO instead of depending on it [ Upstream commit 1a3267a8c9ecabb8e27f5cbda6d19295d5e41beb ] REGMAP_MMIO is a hidden (non-user-visible) tristate symbol. Using depends on it is incorrect because there is no way for the user to enable it directly. Change to select, which is the convention used by every other driver in the tree that needs REGMAP_MMIO. Fixes: 8057cbb8335c ("net: mdio: mscc-miim: Add depend of REGMAP_MMIO on MDIO_MSCC_MIIM") Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20260702032653.1580616-1-rosenp@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/mdio/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/mdio/Kconfig b/drivers/net/mdio/Kconfig index c71132f33f847..733fa0b1cd3ae 100644 --- a/drivers/net/mdio/Kconfig +++ b/drivers/net/mdio/Kconfig @@ -122,7 +122,8 @@ config MDIO_MVUSB config MDIO_MSCC_MIIM tristate "Microsemi MIIM interface support" - depends on HAS_IOMEM && REGMAP_MMIO + depends on HAS_IOMEM + select REGMAP_MMIO help This driver supports the MIIM (MDIO) interface found in the network switches of the Microsemi SoCs; it is recommended to switch on From f1e7807df5bf2d42a40266430e9f82f37633cdcf Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Thu, 2 Jul 2026 00:07:59 +0000 Subject: [PATCH 2157/2758] net/sched: cake: reject overhead values that underflow length MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit b7f97cae7ec1b6c3c32843c42be218690d310467 ] CAKE accepts signed overhead values and stores them in an s16, but the adjusted packet length calculation uses unsigned arithmetic. A negative effective length can therefore wrap to a large value. Such configurations make rate accounting depend on integer wraparound rather than on the packet size userspace intended to model. A static netlink lower bound is not enough because packets reaching CAKE can be smaller than any reasonable manual-overhead allowance. Fold the signed overhead adjustment into the existing datapath MPU clamp so negative adjusted lengths are clamped before link-layer framing adjustments. Fixes: a729b7f0bd5b ("sch_cake: Add overhead compensation support to the rate shaper") Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius Acked-by: Toke Høiland-Jørgensen Link: https://patch.msgid.link/20260702000758.297407.e5c888d9d99d.cake-overhead-underflow@trailofbits.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/sched/sch_cake.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c index c4915073d9d1a..07ea175657bb8 100644 --- a/net/sched/sch_cake.c +++ b/net/sched/sch_cake.c @@ -1389,10 +1389,7 @@ static u32 cake_calc_overhead(struct cake_sched_data *qd, u32 len, u32 off) if (qd->min_netlen > len) WRITE_ONCE(qd->min_netlen, len); - len += q->rate_overhead; - - if (len < q->rate_mpu) - len = q->rate_mpu; + len = max((s32)len + q->rate_overhead, (s32)q->rate_mpu); if (q->atm_mode == CAKE_ATM_ATM) { len += 47; From 543c66cea0e24a49b3b5468c16655fee56832f77 Mon Sep 17 00:00:00 2001 From: Suman Ghosh Date: Thu, 2 Jul 2026 09:04:51 +0530 Subject: [PATCH 2158/2758] octeontx2-pf: check DMAC extraction support before filtering [ Upstream commit 235acadd310533ba386ae61ad155b72bee381559 ] Currently, configuring a VF MAC address via the PF (e.g., 'ip link set vf 0 mac ') blindly attempts to install a DMAC-based hardware filter. However, the hardware parser profile might not support DMAC extraction. Check if the hardware parsing profile supports DMAC extraction before adding the filter. Additionally, emit a warning message to inform the operator if the MAC filter installation fails due to missing DMAC extraction support. Update config->mac only after hardware programming succeeds in otx2_set_vf_mac(). Fixes: f0c2982aaf98 ("octeontx2-pf: Add support for SR-IOV management functions") Signed-off-by: Suman Ghosh Signed-off-by: Nitin Shetty J Reviewed-by: Harshitha Ramamurthy Link: https://patch.msgid.link/20260702033451.2969880-1-nshettyj@marvell.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- .../ethernet/marvell/octeontx2/nic/otx2_pf.c | 43 ++++++++++++++++--- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index 0c2da974ac6d5..543796b636524 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -2521,10 +2521,42 @@ EXPORT_SYMBOL(otx2_config_hwtstamp_set); static int otx2_do_set_vf_mac(struct otx2_nic *pf, int vf, const u8 *mac) { + struct npc_get_field_status_req *freq; + struct npc_get_field_status_rsp *frsp; struct npc_install_flow_req *req; int err; mutex_lock(&pf->mbox.lock); + + /* Skip installing the DMAC filter if the hardware parser profile + * does not support DMAC extraction. + */ + freq = otx2_mbox_alloc_msg_npc_get_field_status(&pf->mbox); + if (!freq) { + err = -ENOMEM; + goto out; + } + + freq->field = NPC_DMAC; + err = otx2_sync_mbox_msg(&pf->mbox); + if (err) + goto out; + + frsp = (struct npc_get_field_status_rsp *)otx2_mbox_get_rsp + (&pf->mbox.mbox, 0, &freq->hdr); + if (IS_ERR(frsp)) { + err = PTR_ERR(frsp); + goto out; + } + + if (!frsp->enable) { + netdev_warn(pf->netdev, + "VF %d MAC filter not installed: DMAC extraction not supported by parser profile\n", + vf); + err = -EOPNOTSUPP; + goto out; + } + req = otx2_mbox_alloc_msg_npc_install_flow(&pf->mbox); if (!req) { err = -ENOMEM; @@ -2563,13 +2595,12 @@ static int otx2_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) if (!is_valid_ether_addr(mac)) return -EINVAL; - config = &pf->vf_configs[vf]; - ether_addr_copy(config->mac, mac); - ret = otx2_do_set_vf_mac(pf, vf, mac); - if (ret == 0) - dev_info(&pdev->dev, - "Load/Reload VF driver\n"); + if (ret == 0) { + config = &pf->vf_configs[vf]; + ether_addr_copy(config->mac, mac); + dev_info(&pdev->dev, "Load/Reload VF driver\n"); + } return ret; } From 46d0fd8535edce31f15e48d2de1bdee39a4850e5 Mon Sep 17 00:00:00 2001 From: Sandipan Das Date: Wed, 8 Jul 2026 16:02:04 +0530 Subject: [PATCH 2159/2758] perf/x86/amd/core: Avoid enabling BRS from the SVM reload path [ Upstream commit 07c60dda9c059c09f83d42a3ebda2e7cc1cf3bc2 ] Branch Sampling (BRS) and Last Branch Record (LBR) are mutually exclusive hardware features, and users of both are tracked via cpuc->lbr_users. When SVM is toggled on a CPU, the host perf events are reprogrammed to update the HostOnly filter bit (set when virtualization is enabled, cleared when it is disabled). On PerfMonV2-capable processors, this reprogramming is performed by calling amd_pmu_enable_all() to rewrite the event selectors. However, amd_pmu_enable_all() also calls amd_brs_enable_all(), which enables BRS whenever cpuc->lbr_users > 0. Having active LBR events satisfies this gating on processors that have LBR but not BRS. The kernel then tries to set the BRS enable bit in DebugExtnCfg (MSR 0xc000010f). Since that bit is deprecated on such hardware, the write results in a #GP: Call Trace: amd_pmu_enable_all+0x1d/0x90 amd_pmu_disable_virt+0x62/0xb0 kvm_arch_disable_virtualization_cpu+0xa/0x40 [kvm] hardware_disable_nolock+0x1a/0x30 [kvm] __flush_smp_call_function_queue+0x9b/0x410 __sysvec_call_function+0x18/0xc0 sysvec_call_function+0x69/0x90 asm_sysvec_call_function+0x16/0x20 RIP: 0010:cpuidle_enter_state+0xc4/0x450 ? cpuidle_enter_state+0xb7/0x450 cpuidle_enter+0x29/0x40 cpuidle_idle_call+0xf5/0x160 do_idle+0x7b/0xe0 cpu_startup_entry+0x26/0x30 start_secondary+0x115/0x140 secondary_startup_64_no_verify+0x194/0x19b Fix this by ensuring that BRS is not enabled from the event selector reprogramming path even when cpuc->lbr_users > 0. Fixes: bae19fdd7e9e ("perf/x86/amd/core: Fix reloading events for SVM") Signed-off-by: Sandipan Das Signed-off-by: Ingo Molnar Cc: Peter Zijlstra Link: https://patch.msgid.link/702fa204d574b03d14e3664c7d4b201db048bbfd.1783506528.git.sandipan.das@amd.com Signed-off-by: Sasha Levin --- arch/x86/events/amd/core.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/arch/x86/events/amd/core.c b/arch/x86/events/amd/core.c index abc84a92cd590..4b34975dcfc58 100644 --- a/arch/x86/events/amd/core.c +++ b/arch/x86/events/amd/core.c @@ -752,13 +752,11 @@ static void amd_pmu_enable_event(struct perf_event *event) x86_pmu_enable_event(event); } -static void amd_pmu_enable_all(int added) +static void __amd_pmu_enable_all(void) { struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); int idx; - amd_brs_enable_all(); - for_each_set_bit(idx, x86_pmu.cntr_mask, X86_PMC_IDX_MAX) { /* only activate events which are marked as active */ if (!test_bit(idx, cpuc->active_mask)) @@ -773,6 +771,12 @@ static void amd_pmu_enable_all(int added) } } +static void amd_pmu_enable_all(int added) +{ + amd_brs_enable_all(); + __amd_pmu_enable_all(); +} + static void amd_pmu_v2_enable_event(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; @@ -1559,7 +1563,7 @@ static inline void amd_pmu_reload_virt(void) * set global enable bits once again */ amd_pmu_v2_disable_all(); - amd_pmu_enable_all(0); + __amd_pmu_enable_all(); amd_pmu_v2_enable_all(0); return; } From d73e4d790db611da7439e78a6ab6cb32e7885ab8 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Tue, 7 Jul 2026 16:23:58 -0700 Subject: [PATCH 2160/2758] gpio: mvebu: free generic chips on unbind [ Upstream commit b11c513ad943f35cf5e8007d3a56279c79b7ed4b ] irq_alloc_domain_generic_chips() allocates generic chip data that must be freed via irq_domain_remove_generic_chips(). The devres action mvebu_gpio_remove_irq_domain() only called irq_domain_remove(), which only frees the generic chips if IRQ_DOMAIN_FLAG_DESTROY_GC is set. Call irq_domain_remove_generic_chips() explicitly before irq_domain_remove() instead. Fixes: 812d47889a8e ("gpio/mvebu: Use irq_domain_add_linear") Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260707232358.1218077-1-rosenp@gmail.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpio-mvebu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 689dc6354c2d7..a010604e5ff79 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -1110,6 +1110,7 @@ static void mvebu_gpio_remove_irq_domain(void *data) { struct irq_domain *domain = data; + irq_domain_remove_generic_chips(domain); irq_domain_remove(domain); } From 8d4394ffa40508e0de72f464af351f6ca6a6cdc3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 5 Jul 2026 18:17:54 +0000 Subject: [PATCH 2161/2758] ipv4: igmp: Fix potential UAF in igmp_gq_start_timer() [ Upstream commit 7b19c0f81ed1fdaec6bc522569be367199a9edf3 ] A race condition exists between device teardown (inetdev_destroy) and incoming IGMP query processing (igmp_rcv), leading to a Use-After-Free in the IGMP timer callback. During device destruction, inetdev_destroy() drops the primary reference to in_device, which can drop its refcount to 0. The actual freeing of in_device memory is deferred via RCU (using call_rcu()). Concurrently, igmp_rcv() runs under RCU read lock and obtains the in_device pointer. Because the memory is RCU-protected, CPU-0 can safely dereference in_device even if its refcount has hit 0. However, if CPU-0 calls igmp_gq_start_timer() and re-arms the timer, it attempts to acquire a reference using in_dev_hold(). This increments the refcount from 0 to 1, triggering a "refcount_t: addition on 0" warning. Since the in_device memory is still scheduled to be freed after the RCU grace period (as the free callback does not check the refcount again), the device is freed while the timer is still armed. When the timer expires, it accesses the freed memory, causing a kernel panic. Fix this by using refcount_inc_not_zero() (via a new helper in_dev_hold_safe()) to prevent acquiring a reference if the device is already being destroyed. If the refcount is 0, we do not arm the timer. A similar issue in IPv6 MLD is fixed in a subsequent patch. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: Zero Day Initiative Signed-off-by: Eric Dumazet Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260705181756.963063-2-edumazet@google.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- include/linux/inetdevice.h | 5 +++++ net/ipv4/igmp.c | 14 +++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index dccbeb25f7014..6032eea2539a6 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -293,6 +293,11 @@ static inline void in_dev_put(struct in_device *idev) #define __in_dev_put(idev) refcount_dec(&(idev)->refcnt) #define in_dev_hold(idev) refcount_inc(&(idev)->refcnt) +static inline bool in_dev_hold_safe(struct in_device *idev) +{ + return refcount_inc_not_zero(&idev->refcnt); +} + #endif /* __KERNEL__ */ static __inline__ __be32 inet_make_mask(int logmask) diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 523aecb3c9be7..73e9ebb17afaa 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -248,16 +248,20 @@ static void igmp_gq_start_timer(struct in_device *in_dev) return; in_dev->mr_gq_running = 1; - if (!mod_timer(&in_dev->mr_gq_timer, exp)) - in_dev_hold(in_dev); + if (in_dev_hold_safe(in_dev)) { + if (mod_timer(&in_dev->mr_gq_timer, exp)) + in_dev_put(in_dev); + } } static void igmp_ifc_start_timer(struct in_device *in_dev, int delay) { - int tv = get_random_u32_below(delay); + if (in_dev_hold_safe(in_dev)) { + int tv = get_random_u32_below(delay); - if (!mod_timer(&in_dev->mr_ifc_timer, jiffies+tv+2)) - in_dev_hold(in_dev); + if (mod_timer(&in_dev->mr_ifc_timer, jiffies + tv + 2)) + in_dev_put(in_dev); + } } static void igmp_mod_timer(struct ip_mc_list *im, int max_delay) From 0458ba1cda830ba4ccfcd9e19c0891438bcdbe4e Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 5 Jul 2026 18:17:55 +0000 Subject: [PATCH 2162/2758] ipv6: mcast: Fix potential UAF in MLD delayed work [ Upstream commit 9b26518b6896a16b809b1e42986f4ebac7bccc1e ] A race condition exists between device teardown and incoming MLD query processing, leading to a Use-After-Free in the MLD delayed work. During device destruction, the primary reference to inet6_dev is dropped, which can drop its refcount to 0. The actual freeing of inet6_dev memory is deferred via RCU. Concurrently, the packet receive path runs under RCU read lock and obtains the inet6_dev pointer. Because the memory is RCU-protected, CPU-0 can safely dereference inet6_dev even if its refcount has hit 0. However, if CPU-0 calls igmp6_event_query() and schedules delayed work, it attempts to acquire a reference using in6_dev_hold(). This increments the refcount from 0 to 1, triggering a "refcount_t: addition on 0" warning. Since the inet6_dev memory is still scheduled to be freed after the RCU grace period, the device is freed while the work is still scheduled. When the work runs, it accesses the freed memory, causing a kernel panic. Fix this by using refcount_inc_not_zero() (via a new helper in6_dev_hold_safe()) to prevent acquiring a reference if the device is already being destroyed. If the refcount is 0, we do not schedule the work. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Eric Dumazet Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260705181756.963063-3-edumazet@google.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- include/net/addrconf.h | 5 +++++ net/ipv6/mcast.c | 40 ++++++++++++++++++++++++++++------------ 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 539bbbe54b14e..8ced27a8229b6 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -446,6 +446,11 @@ static inline void in6_dev_hold(struct inet6_dev *idev) refcount_inc(&idev->refcnt); } +static inline bool in6_dev_hold_safe(struct inet6_dev *idev) +{ + return refcount_inc_not_zero(&idev->refcnt); +} + /* called with rcu_read_lock held */ static inline bool ip6_ignore_linkdown(const struct net_device *dev) { diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index d9b855d5191bf..2a5e76a38002a 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -1080,8 +1080,10 @@ static void mld_gq_start_work(struct inet6_dev *idev) mc_assert_locked(idev); idev->mc_gq_running = 1; - if (!mod_delayed_work(mld_wq, &idev->mc_gq_work, tv + 2)) - in6_dev_hold(idev); + if (in6_dev_hold_safe(idev)) { + if (mod_delayed_work(mld_wq, &idev->mc_gq_work, tv + 2)) + in6_dev_put(idev); + } } static void mld_gq_stop_work(struct inet6_dev *idev) @@ -1099,8 +1101,10 @@ static void mld_ifc_start_work(struct inet6_dev *idev, unsigned long delay) mc_assert_locked(idev); - if (!mod_delayed_work(mld_wq, &idev->mc_ifc_work, tv + 2)) - in6_dev_hold(idev); + if (in6_dev_hold_safe(idev)) { + if (mod_delayed_work(mld_wq, &idev->mc_ifc_work, tv + 2)) + in6_dev_put(idev); + } } static void mld_ifc_stop_work(struct inet6_dev *idev) @@ -1118,8 +1122,10 @@ static void mld_dad_start_work(struct inet6_dev *idev, unsigned long delay) mc_assert_locked(idev); - if (!mod_delayed_work(mld_wq, &idev->mc_dad_work, tv + 2)) - in6_dev_hold(idev); + if (in6_dev_hold_safe(idev)) { + if (mod_delayed_work(mld_wq, &idev->mc_dad_work, tv + 2)) + in6_dev_put(idev); + } } static void mld_dad_stop_work(struct inet6_dev *idev) @@ -1405,18 +1411,23 @@ static void mld_process_v2(struct inet6_dev *idev, struct mld2_query *mld, void igmp6_event_query(struct sk_buff *skb) { struct inet6_dev *idev = __in6_dev_get(skb->dev); + bool put = false; if (!idev || idev->dead) goto out; spin_lock_bh(&idev->mc_query_lock); - if (skb_queue_len(&idev->mc_query_queue) < MLD_MAX_SKBS) { + if (skb_queue_len(&idev->mc_query_queue) < MLD_MAX_SKBS && + in6_dev_hold_safe(idev)) { __skb_queue_tail(&idev->mc_query_queue, skb); - if (!mod_delayed_work(mld_wq, &idev->mc_query_work, 0)) - in6_dev_hold(idev); + if (mod_delayed_work(mld_wq, &idev->mc_query_work, 0)) + put = true; skb = NULL; } spin_unlock_bh(&idev->mc_query_lock); + + if (put) + in6_dev_put(idev); out: kfree_skb(skb); } @@ -1573,18 +1584,23 @@ static void mld_query_work(struct work_struct *work) void igmp6_event_report(struct sk_buff *skb) { struct inet6_dev *idev = __in6_dev_get(skb->dev); + bool put = false; if (!idev || idev->dead) goto out; spin_lock_bh(&idev->mc_report_lock); - if (skb_queue_len(&idev->mc_report_queue) < MLD_MAX_SKBS) { + if (skb_queue_len(&idev->mc_report_queue) < MLD_MAX_SKBS && + in6_dev_hold_safe(idev)) { __skb_queue_tail(&idev->mc_report_queue, skb); - if (!mod_delayed_work(mld_wq, &idev->mc_report_work, 0)) - in6_dev_hold(idev); + if (mod_delayed_work(mld_wq, &idev->mc_report_work, 0)) + put = true; skb = NULL; } spin_unlock_bh(&idev->mc_report_lock); + + if (put) + in6_dev_put(idev); out: kfree_skb(skb); } From 16e5b2dbea49ad685084f9bb0cbe599563f1dd5f Mon Sep 17 00:00:00 2001 From: Yuyang Huang Date: Fri, 22 May 2026 18:39:06 +0900 Subject: [PATCH 2163/2758] ipv4: igmp: annotate data-races around im->users [ Upstream commit 061c0aa740d5d3847cd600a74c66a165bee1fbe0 ] /proc/net/igmp walks IPv4 multicast memberships under RCU and prints im->users without holding RTNL, while multicast join and leave paths update the field while holding RTNL. Annotate this intentional lockless snapshot with READ_ONCE() and the matching writers with WRITE_ONCE(). Signed-off-by: Yuyang Huang Reviewed-by: Jiayuan Chen Reviewed-by: Eric Dumazet Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260522093906.39764-1-sigefriedhyy@gmail.com Signed-off-by: Jakub Kicinski Stable-dep-of: 3546deaa0c30 ("ipv4: igmp: Fix potential memory leaks in igmp_mod_timer() and igmp_stop_timer()") Signed-off-by: Sasha Levin --- net/ipv4/igmp.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 73e9ebb17afaa..8ae71dd6f76bd 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -1545,7 +1545,7 @@ static void ____ip_mc_inc_group(struct in_device *in_dev, __be32 addr, } if (im) { - im->users++; + WRITE_ONCE(im->users, im->users + 1); ip_mc_add_src(in_dev, &addr, mode, 0, NULL, 0); goto out; } @@ -1554,7 +1554,7 @@ static void ____ip_mc_inc_group(struct in_device *in_dev, __be32 addr, if (!im) goto out; - im->users = 1; + WRITE_ONCE(im->users, 1); im->interface = in_dev; in_dev_hold(in_dev); im->multiaddr = addr; @@ -1788,7 +1788,10 @@ void __ip_mc_dec_group(struct in_device *in_dev, __be32 addr, gfp_t gfp) (i = rtnl_dereference(*ip)) != NULL; ip = &i->next_rcu) { if (i->multiaddr == addr) { - if (--i->users == 0) { + int new_users = i->users - 1; + + WRITE_ONCE(i->users, new_users); + if (new_users == 0) { ip_mc_hash_remove(in_dev, i); *ip = i->next_rcu; in_dev->mc_count--; @@ -2982,7 +2985,7 @@ static int igmp_mc_seq_show(struct seq_file *seq, void *v) delta = im->timer.expires - jiffies; seq_printf(seq, "\t\t\t\t%08X %5d %d:%08lX\t\t%d\n", - im->multiaddr, im->users, + im->multiaddr, READ_ONCE(im->users), im->tm_running, im->tm_running ? jiffies_delta_to_clock_t(delta) : 0, im->reporter); From 1fcabcba272d995759ded8df68145ee05177ee9c Mon Sep 17 00:00:00 2001 From: Yuyang Huang Date: Fri, 5 Jun 2026 10:43:18 +0900 Subject: [PATCH 2164/2758] ipv4: igmp: annotate data-races around timer-related fields [ Upstream commit 3289d17b7a1321e103b8aec4ad82675c03c4764f ] /proc/net/igmp walks the multicast list locklessly under RCU and reads timer-related fields (im->tm_running, im->reporter, im->timer.expires) to print the timer state of multicast memberships. Concurrently, these fields are modified under im->lock spinlock in timer management paths (igmp_stop_timer(), igmp_start_timer(), and igmp_timer_expire()). Fix this intentional lockless snapshot by annotating the lockless reads with READ_ONCE() and the updates with WRITE_ONCE(). Signed-off-by: Yuyang Huang Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260605014318.3890804-3-yuyanghuang@google.com Signed-off-by: Paolo Abeni Stable-dep-of: 3546deaa0c30 ("ipv4: igmp: Fix potential memory leaks in igmp_mod_timer() and igmp_stop_timer()") Signed-off-by: Sasha Levin --- net/ipv4/igmp.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 8ae71dd6f76bd..208328e337c27 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -220,8 +220,8 @@ static void igmp_stop_timer(struct ip_mc_list *im) spin_lock_bh(&im->lock); if (timer_delete(&im->timer)) refcount_dec(&im->refcnt); - im->tm_running = 0; - im->reporter = 0; + WRITE_ONCE(im->tm_running, 0); + WRITE_ONCE(im->reporter, 0); im->unsolicit_count = 0; spin_unlock_bh(&im->lock); } @@ -231,7 +231,7 @@ static void igmp_start_timer(struct ip_mc_list *im, int max_delay) { int tv = get_random_u32_below(max_delay); - im->tm_running = 1; + WRITE_ONCE(im->tm_running, 1); if (refcount_inc_not_zero(&im->refcnt)) { if (mod_timer(&im->timer, jiffies + tv + 2)) ip_ma_put(im); @@ -271,7 +271,7 @@ static void igmp_mod_timer(struct ip_mc_list *im, int max_delay) if (timer_delete(&im->timer)) { if ((long)(im->timer.expires-jiffies) < max_delay) { add_timer(&im->timer); - im->tm_running = 1; + WRITE_ONCE(im->tm_running, 1); spin_unlock_bh(&im->lock); return; } @@ -861,12 +861,12 @@ static void igmp_timer_expire(struct timer_list *t) struct in_device *in_dev = im->interface; spin_lock(&im->lock); - im->tm_running = 0; + WRITE_ONCE(im->tm_running, 0); if (im->unsolicit_count && --im->unsolicit_count) igmp_start_timer(im, unsolicited_report_interval(in_dev)); - im->reporter = 1; + WRITE_ONCE(im->reporter, 1); spin_unlock(&im->lock); if (IGMP_V1_SEEN(in_dev)) @@ -1329,7 +1329,7 @@ static void __igmp_group_dropped(struct ip_mc_list *im, gfp_t gfp) !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) return; - reporter = im->reporter; + reporter = READ_ONCE(im->reporter); igmp_stop_timer(im); if (!in_dev->dead) { @@ -2967,6 +2967,7 @@ static int igmp_mc_seq_show(struct seq_file *seq, void *v) struct ip_mc_list *im = v; struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); char *querier; + int tm_running; long delta; #ifdef CONFIG_IP_MULTICAST @@ -2982,13 +2983,14 @@ static int igmp_mc_seq_show(struct seq_file *seq, void *v) state->dev->ifindex, state->dev->name, state->in_dev->mc_count, querier); } - delta = im->timer.expires - jiffies; + tm_running = READ_ONCE(im->tm_running); + delta = READ_ONCE(im->timer.expires) - jiffies; seq_printf(seq, "\t\t\t\t%08X %5d %d:%08lX\t\t%d\n", im->multiaddr, READ_ONCE(im->users), - im->tm_running, - im->tm_running ? jiffies_delta_to_clock_t(delta) : 0, - im->reporter); + tm_running, + tm_running ? jiffies_delta_to_clock_t(delta) : 0, + READ_ONCE(im->reporter)); } return 0; } From 95128dc74425ec19ed4f2077ccc651e791ff4b75 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 5 Jul 2026 18:17:56 +0000 Subject: [PATCH 2165/2758] ipv4: igmp: Fix potential memory leaks in igmp_mod_timer() and igmp_stop_timer() [ Upstream commit 3546deaa0c30a14c7cdb5dc8f2432cb428f0cd36 ] When a timer is deleted and not re-armed in igmp_mod_timer(), or stopped in igmp_stop_timer(), the code currently decrements the reference counter of the multicast list entry @im using refcount_dec(&im->refcnt). However, both functions can be called from the RCU reader path: - igmp_mod_timer() via igmp_heard_query() -> for_each_pmc_rcu() - igmp_stop_timer() via igmp_rcv() -> igmp_heard_report() If the group im was concurrently removed from the list by ip_mc_dec_group(), its reference count might have already been decremented to 1. In this case, timer_delete() succeeds, and refcount_dec() decrements the refcount from 1 to 0. Since refcount_dec() does not free the object when it hits 0 (unlike ip_ma_put()), the im structure is leaked. Fix this by using ip_ma_put(im) instead of refcount_dec(&im->refcnt), and deferring the put until after the spinlock is released. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Eric Dumazet Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260705181756.963063-4-edumazet@google.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/ipv4/igmp.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 208328e337c27..f22fbc17f15b3 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -217,13 +217,18 @@ static void ip_sf_list_clear_all(struct ip_sf_list *psf) static void igmp_stop_timer(struct ip_mc_list *im) { + bool put = false; + spin_lock_bh(&im->lock); if (timer_delete(&im->timer)) - refcount_dec(&im->refcnt); + put = true; WRITE_ONCE(im->tm_running, 0); WRITE_ONCE(im->reporter, 0); im->unsolicit_count = 0; spin_unlock_bh(&im->lock); + + if (put) + ip_ma_put(im); } /* It must be called with locked im->lock */ @@ -266,6 +271,8 @@ static void igmp_ifc_start_timer(struct in_device *in_dev, int delay) static void igmp_mod_timer(struct ip_mc_list *im, int max_delay) { + bool put = false; + spin_lock_bh(&im->lock); im->unsolicit_count = 0; if (timer_delete(&im->timer)) { @@ -275,10 +282,13 @@ static void igmp_mod_timer(struct ip_mc_list *im, int max_delay) spin_unlock_bh(&im->lock); return; } - refcount_dec(&im->refcnt); + put = true; } igmp_start_timer(im, max_delay); spin_unlock_bh(&im->lock); + + if (put) + ip_ma_put(im); } From ef0c7d4b04a0e6ad175323c24bc84e11470dd79d Mon Sep 17 00:00:00 2001 From: Tamaki Yanagawa Date: Fri, 3 Jul 2026 16:22:57 +0000 Subject: [PATCH 2166/2758] netfilter: nft_lookup: fix catchall element handling with inverted lookups [ Upstream commit e6107a4c74b54cb33e3bce162a63048ae5a6b198 ] nft_lookup_eval() decides whether a lookup matched (`found`) from the direct set lookup and priv->invert before falling back to the catchall element used by interval sets (e.g. nft_set_rbtree) for the open-ended default range. Since `found` is never recomputed after `ext` is replaced by the catchall lookup, inverted lookups (NFT_LOOKUP_F_INV, "!= @set") can wrongly match or wrongly skip the catchall element, producing the wrong verdict. Fold the catchall lookup into `ext` before computing `found`, matching the order already used by nft_objref_map_eval(). Fixes: aaa31047a6d2 ("netfilter: nftables: add catch-all set element support") Signed-off-by: Tamaki Yanagawa Assisted-by: Claude:claude-sonnet-5 Signed-off-by: Florian Westphal Signed-off-by: Sasha Levin --- net/netfilter/nft_lookup.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c index 9fafe5afc490e..8e2c3601133f0 100644 --- a/net/netfilter/nft_lookup.c +++ b/net/netfilter/nft_lookup.c @@ -103,13 +103,13 @@ void nft_lookup_eval(const struct nft_expr *expr, bool found; ext = nft_set_do_lookup(net, set, ®s->data[priv->sreg]); + if (!ext) + ext = nft_set_catchall_lookup(net, set); + found = !!ext ^ priv->invert; if (!found) { - ext = nft_set_catchall_lookup(net, set); - if (!ext) { - regs->verdict.code = NFT_BREAK; - return; - } + regs->verdict.code = NFT_BREAK; + return; } if (ext) { From 568720055fbde64a575df08e0daf972d1c955114 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Mon, 6 Jul 2026 18:16:22 +0800 Subject: [PATCH 2167/2758] ipvs: pass parsed transport offset to state handlers [ Upstream commit bae7ce7bafb59e42dc0e0e2999fdd9d1cffe3866 ] IPVS callers already parse the packet into struct ip_vs_iphdr before updating connection state. For IPv6 this records the real transport-header offset after extension headers in iph.len. Pass this parsed transport offset through ip_vs_set_state() and the protocol state_transition() callback so protocol handlers can use the same packet context as scheduling and NAT handling. This patch only changes the common callback plumbing and adapts the protocol callback signatures; TCP and SCTP start using the value in follow-up patches. Signed-off-by: Yizhou Zhao Acked-by: Julian Anastasov Signed-off-by: Florian Westphal Stable-dep-of: 2500fa3958b1 ("ipvs: use parsed transport offset in TCP state lookup") Signed-off-by: Sasha Levin --- include/net/ip_vs.h | 3 ++- net/netfilter/ipvs/ip_vs_core.c | 10 +++++----- net/netfilter/ipvs/ip_vs_proto_sctp.c | 3 ++- net/netfilter/ipvs/ip_vs_proto_tcp.c | 3 ++- net/netfilter/ipvs/ip_vs_proto_udp.c | 3 ++- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index e517eaaa177b0..62433e48b1f9a 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -744,7 +744,8 @@ struct ip_vs_protocol { void (*state_transition)(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, - struct ip_vs_proto_data *pd); + struct ip_vs_proto_data *pd, + unsigned int iph_len); int (*register_app)(struct netns_ipvs *ipvs, struct ip_vs_app *inc); diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index d40b404c1bf64..bd90f03fe3a4a 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -398,10 +398,10 @@ ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc) static inline void ip_vs_set_state(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, - struct ip_vs_proto_data *pd) + struct ip_vs_proto_data *pd, unsigned int iph_len) { if (likely(pd->pp->state_transition)) - pd->pp->state_transition(cp, direction, skb, pd); + pd->pp->state_transition(cp, direction, skb, pd, iph_len); } static inline int @@ -803,7 +803,7 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, ip_vs_in_stats(cp, skb); /* set state */ - ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd); + ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd, iph->len); /* transmit the first SYN packet */ ret = cp->packet_xmit(skb, cp, pd->pp, iph); @@ -1484,7 +1484,7 @@ handle_response(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, after_nat: ip_vs_out_stats(cp, skb); - ip_vs_set_state(cp, IP_VS_DIR_OUTPUT, skb, pd); + ip_vs_set_state(cp, IP_VS_DIR_OUTPUT, skb, pd, iph->len); skb->ipvs_property = 1; if (!(cp->flags & IP_VS_CONN_F_NFCT)) ip_vs_notrack(skb); @@ -2233,7 +2233,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state IP_VS_DBG_PKT(11, af, pp, skb, iph.off, "Incoming packet"); ip_vs_in_stats(cp, skb); - ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd); + ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd, iph.len); if (cp->packet_xmit) ret = cp->packet_xmit(skb, cp, pp, &iph); /* do not touch skb anymore */ diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index 63c78a1f3918a..394367b7b3887 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -468,7 +468,8 @@ set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, static void sctp_state_transition(struct ip_vs_conn *cp, int direction, - const struct sk_buff *skb, struct ip_vs_proto_data *pd) + const struct sk_buff *skb, struct ip_vs_proto_data *pd, + unsigned int iph_len) { spin_lock_bh(&cp->lock); set_sctp_state(pd, cp, direction, skb); diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index 8cc0a8ce62411..2d3f6aeafe522 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -579,7 +579,8 @@ set_tcp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, static void tcp_state_transition(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, - struct ip_vs_proto_data *pd) + struct ip_vs_proto_data *pd, + unsigned int iph_len) { struct tcphdr _tcph, *th; diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c index f9de632e38cdd..58f9e255927e2 100644 --- a/net/netfilter/ipvs/ip_vs_proto_udp.c +++ b/net/netfilter/ipvs/ip_vs_proto_udp.c @@ -444,7 +444,8 @@ static const char * udp_state_name(int state) static void udp_state_transition(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, - struct ip_vs_proto_data *pd) + struct ip_vs_proto_data *pd, + unsigned int iph_len) { if (unlikely(!pd)) { pr_err("UDP no ns data\n"); From c2ee845e292c278fac75bf28d96bc892607fd5c4 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Mon, 6 Jul 2026 18:16:23 +0800 Subject: [PATCH 2168/2758] ipvs: use parsed transport offset in TCP state lookup [ Upstream commit 2500fa3958b1ba51c2b065e39db1b04dfa7e23a2 ] TCP state handling reparses the skb to find the TCP header. For IPv6 it uses sizeof(struct ipv6hdr), while the surrounding IPVS code already parsed the packet with ip_vs_fill_iph_skb() and has the real transport-header offset in iph.len. This makes TCP state handling look at the wrong bytes when an IPv6 packet carries extension headers. Use the parsed transport offset passed down from ip_vs_set_state() when reading the TCP header. For IPv4 and for IPv6 packets without extension headers, the passed offset matches the previous value. Fixes: 0bbdd42b7efa6 ("IPVS: Extend protocol DNAT/SNAT and state handlers") Link: https://lore.kernel.org/netdev/20260705125659.37744-1-zhaoyz24@mails.tsinghua.edu.cn/ Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Assisted-by: Claude Code:GLM-5.2 Signed-off-by: Yizhou Zhao Acked-by: Julian Anastasov Signed-off-by: Florian Westphal Signed-off-by: Sasha Levin --- net/netfilter/ipvs/ip_vs_proto_tcp.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index 2d3f6aeafe522..f86b763efcc4d 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -584,13 +584,7 @@ tcp_state_transition(struct ip_vs_conn *cp, int direction, { struct tcphdr _tcph, *th; -#ifdef CONFIG_IP_VS_IPV6 - int ihl = cp->af == AF_INET ? ip_hdrlen(skb) : sizeof(struct ipv6hdr); -#else - int ihl = ip_hdrlen(skb); -#endif - - th = skb_header_pointer(skb, ihl, sizeof(_tcph), &_tcph); + th = skb_header_pointer(skb, iph_len, sizeof(_tcph), &_tcph); if (th == NULL) return; From f0f35153de83912da227f4b54d1290e18442efe2 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Thu, 2 Jul 2026 15:34:28 +0800 Subject: [PATCH 2169/2758] ipvs: fix PMTU for GUE/GRE tunnel ICMP errors [ Upstream commit 6b335af0d0d1ff44ac579d106953bf19299e5233 ] When an ICMP Fragmentation Needed error is received for a tunneled IPVS connection, ip_vs_in_icmp() recomputes the MTU that the original packet can use by subtracting the tunnel overhead from the reported next-hop MTU. The current code always subtracts sizeof(struct iphdr), which is only the IPIP overhead. For GUE and GRE tunnels, ipvs_udp_decap() and ipvs_gre_decap() already compute the additional tunnel header length, but that value is scoped to the decapsulation block and is lost before the ICMP_FRAG_NEEDED handling. As a result, the ICMP error sent back to the client advertises an MTU that is too large, so PMTUD can fail to converge for GUE/GRE-tunneled real servers. With a reported next-hop MTU of 1400, a GUE tunnel currently returns 1380 to the client. The correct value is 1368: 1400 - sizeof(struct iphdr) - sizeof(struct udphdr) - sizeof(struct guehdr) Hoist the tunnel header length into the main ip_vs_in_icmp() scope and subtract sizeof(struct iphdr) + ulen in the Fragmentation Needed path. The IPIP path keeps ulen as 0, so its existing 1400 - 20 = 1380 result is unchanged. Fixes: 508f744c0de3 ("ipvs: strip udp tunnel headers from icmp errors") Cc: stable@vger.kernel.org Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Assisted-by: Claude-Code:GLM-5.2 Signed-off-by: Yizhou Zhao Acked-by: Julian Anastasov Signed-off-by: Florian Westphal Stable-dep-of: 3f7a535ff0fa ("ipvs: ensure inner headers in ICMP errors are in headroom") Signed-off-by: Sasha Levin --- net/netfilter/ipvs/ip_vs_core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index bd90f03fe3a4a..f79c098696366 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -1767,6 +1767,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, bool tunnel, new_cp = false; union nf_inet_addr *raddr; char *outer_proto = "IPIP"; + int ulen = 0; *related = 1; @@ -1831,7 +1832,6 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, /* Error for our tunnel must arrive at LOCAL_IN */ (skb_rtable(skb)->rt_flags & RTCF_LOCAL)) { __u8 iproto; - int ulen; /* Non-first fragment has no UDP/GRE header */ if (unlikely(cih->frag_off & htons(IP_OFFSET))) @@ -1936,8 +1936,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, if (dest_dst) mtu = dst_mtu(dest_dst->dst_cache); } - if (mtu > 68 + sizeof(struct iphdr)) - mtu -= sizeof(struct iphdr); + if (mtu > 68 + sizeof(struct iphdr) + ulen) + mtu -= sizeof(struct iphdr) + ulen; info = htonl(mtu); } /* Strip outer IP, ICMP and IPIP/UDP/GRE, go to IP header of From 92185d6f7819bc558939ae83de7b1abe90e3b5c2 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Tue, 7 Jul 2026 21:25:46 +0300 Subject: [PATCH 2170/2758] ipvs: ensure inner headers in ICMP errors are in headroom [ Upstream commit 3f7a535ff0fa627a0132803e4c2f903ceffcbc1c ] Sashiko points out that after stripping the outer headers with pskb_pull() we should ensure the inner IP headers in ICMP errors from tunnels are present in the skb headroom for functions like ipv4_update_pmtu(), icmp_send() and IP_VS_DBG(). Also, add more checks for the length of the inner headers. Fixes: f2edb9f7706d ("ipvs: implement passive PMTUD for IPIP packets") Link: https://sashiko.dev/#/patchset/20260702073430.67680-1-zhaoyz24%40mails.tsinghua.edu.cn Signed-off-by: Julian Anastasov Signed-off-by: Florian Westphal Signed-off-by: Sasha Levin --- net/netfilter/ipvs/ip_vs_core.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index f79c098696366..35cbe821c2596 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -1767,6 +1767,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, bool tunnel, new_cp = false; union nf_inet_addr *raddr; char *outer_proto = "IPIP"; + unsigned int hlen_ipip; int ulen = 0; *related = 1; @@ -1804,9 +1805,10 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, /* Now find the contained IP header */ offset += sizeof(_icmph); cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); - if (cih == NULL) + if (!(cih && cih->version == 4 && cih->ihl >= 5)) return NF_ACCEPT; /* The packet looks wrong, ignore */ raddr = (union nf_inet_addr *)&cih->daddr; + hlen_ipip = cih->ihl * 4; /* Special case for errors for IPIP/UDP/GRE tunnel packets */ tunnel = false; @@ -1822,9 +1824,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, /* Only for known tunnel */ if (!dest || dest->tun_type != IP_VS_CONN_F_TUNNEL_TYPE_IPIP) return NF_ACCEPT; - offset += cih->ihl * 4; + offset += hlen_ipip; cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); - if (cih == NULL) + if (!(cih && cih->version == 4 && cih->ihl >= 5)) return NF_ACCEPT; /* The packet looks wrong, ignore */ tunnel = true; } else if ((cih->protocol == IPPROTO_UDP || /* Can be UDP encap */ @@ -1836,7 +1838,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, /* Non-first fragment has no UDP/GRE header */ if (unlikely(cih->frag_off & htons(IP_OFFSET))) return NF_ACCEPT; - offset2 = offset + cih->ihl * 4; + offset2 = offset + hlen_ipip; if (cih->protocol == IPPROTO_UDP) { ulen = ipvs_udp_decap(ipvs, skb, offset2, AF_INET, raddr, &iproto); @@ -1905,6 +1907,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, } if (tunnel) { + unsigned int hlen_orig = cih->ihl * 4; __be32 info = ic->un.gateway; __u8 type = ic->type; __u8 code = ic->code; @@ -1921,6 +1924,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, goto ignore_tunnel; offset2 -= ihl + sizeof(_icmph); skb_reset_network_header(skb); + /* Ensure the IP header is present in headroom */ + if (!pskb_may_pull(skb, hlen_ipip)) + goto ignore_tunnel; IP_VS_DBG(12, "ICMP for %s %pI4->%pI4: mtu=%u\n", outer_proto, &ip_hdr(skb)->saddr, &ip_hdr(skb)->daddr, mtu); @@ -1936,8 +1942,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, if (dest_dst) mtu = dst_mtu(dest_dst->dst_cache); } - if (mtu > 68 + sizeof(struct iphdr) + ulen) - mtu -= sizeof(struct iphdr) + ulen; + if (mtu > 68 + hlen_ipip + ulen) + mtu -= hlen_ipip + ulen; info = htonl(mtu); } /* Strip outer IP, ICMP and IPIP/UDP/GRE, go to IP header of @@ -1946,6 +1952,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, if (pskb_pull(skb, offset2) == NULL) goto ignore_tunnel; skb_reset_network_header(skb); + /* Ensure the IP header is present in headroom */ + if (!pskb_may_pull(skb, hlen_orig)) + goto ignore_tunnel; IP_VS_DBG(12, "Sending ICMP for %pI4->%pI4: t=%u, c=%u, i=%u\n", &ip_hdr(skb)->saddr, &ip_hdr(skb)->daddr, type, code, ntohl(info)); From b0640acace2543c7c0932602c22f46eff9573717 Mon Sep 17 00:00:00 2001 From: Rongguang Wei Date: Fri, 26 Jun 2026 13:58:20 +0800 Subject: [PATCH 2171/2758] s390/zcrypt: Remove the empty file [ Upstream commit 575f87b9d4882b0d621192fdd754d7e09dbd5789 ] The files has no real user because CEX2 and CEX3 device drivers are removed, also remove these empty files. Fixes: 5ac8c72462cd ("s390/zcrypt: remove CEX2 and CEX3 device drivers") Signed-off-by: Rongguang Wei Reviewed-by: Holger Dengler Signed-off-by: Vasily Gorbik Signed-off-by: Sasha Levin --- drivers/s390/crypto/zcrypt_cex2a.c | 0 drivers/s390/crypto/zcrypt_cex2a.h | 0 drivers/s390/crypto/zcrypt_cex2c.c | 0 drivers/s390/crypto/zcrypt_cex2c.h | 0 4 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 drivers/s390/crypto/zcrypt_cex2a.c delete mode 100644 drivers/s390/crypto/zcrypt_cex2a.h delete mode 100644 drivers/s390/crypto/zcrypt_cex2c.c delete mode 100644 drivers/s390/crypto/zcrypt_cex2c.h diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/drivers/s390/crypto/zcrypt_cex2a.h b/drivers/s390/crypto/zcrypt_cex2a.h deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/drivers/s390/crypto/zcrypt_cex2c.c b/drivers/s390/crypto/zcrypt_cex2c.c deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/drivers/s390/crypto/zcrypt_cex2c.h b/drivers/s390/crypto/zcrypt_cex2c.h deleted file mode 100644 index e69de29bb2d1d..0000000000000 From c37abc99bb3de3d3219ecef253d649f74117fd83 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 8 Jul 2026 19:27:10 +0800 Subject: [PATCH 2172/2758] cifs: validate DFS referral string offsets [ Upstream commit 027a84ac6b50c12ef767c15abfc58aa865820e9e ] parse_dfs_referrals() validates that the response header and referral array fit in the received buffer, but each referral also contains string offsets supplied by the server. Those offsets are used to compute the DfsPath and NetworkAddress string pointers without checking whether they still point inside the response buffer. A malformed referral can therefore make the computed pointer exceed the end of the buffer. The resulting negative max_len is then passed to cifs_strndup_from_utf16(), and the non-Unicode path forwards it to kstrndup() as a size_t, allowing strnlen() to read out of bounds. Validate each string offset before deriving the string pointer. Fixes: 4ecce920e13a ("CIFS: move DFS response parsing out of SMB1 code") Signed-off-by: Guangshuo Li Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/client/misc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c index 0c54b9b79a2ce..ee1728eec8aa0 100644 --- a/fs/smb/client/misc.c +++ b/fs/smb/client/misc.c @@ -752,6 +752,10 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags); /* copy DfsPath */ + if (le16_to_cpu(ref->DfsPathOffset) > data_end - (char *)ref) { + rc = -EINVAL; + goto parse_DFS_referrals_exit; + } temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset); max_len = data_end - temp; node->path_name = cifs_strndup_from_utf16(temp, max_len, @@ -762,6 +766,10 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, } /* copy link target UNC */ + if (le16_to_cpu(ref->NetworkAddressOffset) > data_end - (char *)ref) { + rc = -EINVAL; + goto parse_DFS_referrals_exit; + } temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset); max_len = data_end - temp; node->node_name = cifs_strndup_from_utf16(temp, max_len, From 9359aac4999e99d5eb5d6fe17c4ed1fea9c5946b Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 4 May 2026 06:28:18 -0400 Subject: [PATCH 2173/2758] SUNRPC: release lower rpc_clnt if killed waiting for XPRT_LOCKED [ Upstream commit 29bacb096d43bd04062f6655e50ceeed27fe6d2f ] xs_tcp_tls_setup_socket() creates a temporary "lower" rpc_clnt with rpc_create() to drive the inner TLS handshake, then waits for XPRT_LOCKED on its xprt with TASK_KILLABLE so a stuck handshake can be aborted by signal. When the wait is interrupted, the function jumps to out_unlock without releasing lower_clnt. The success path and the out_close error path both call rpc_shutdown_client(lower_clnt); only the killed-wait path skips it, leaking the clnt and its underlying xprt. Call rpc_shutdown_client() on this path before joining out_unlock. xprt_release_write() is not needed here because XPRT_LOCKED was never acquired. Fixes: 26e8bfa30dac ("SUNRPC/TLS: Lock the lower_xprt during the tls handshake") Signed-off-by: Chuck Lever Tested-by: Michael Nemanov Reviewed-by: Michael Nemanov Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- net/sunrpc/xprtsock.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 2e1fe60133615..3eccd4923e6c6 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -2734,8 +2734,11 @@ static void xs_tcp_tls_setup_socket(struct work_struct *work) lower_xprt = rcu_dereference(lower_clnt->cl_xprt); rcu_read_unlock(); - if (wait_on_bit_lock(&lower_xprt->state, XPRT_LOCKED, TASK_KILLABLE)) + if (wait_on_bit_lock(&lower_xprt->state, XPRT_LOCKED, TASK_KILLABLE)) { + /* XPRT_LOCKED was never acquired. */ + rpc_shutdown_client(lower_clnt); goto out_unlock; + } status = xs_tls_handshake_sync(lower_xprt, &upper_xprt->xprtsec); if (status) { From d49f6d098ed48775b9d27a9f9c5c220fdf76f102 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 4 May 2026 06:28:19 -0400 Subject: [PATCH 2174/2758] SUNRPC: pin upper rpc_clnt across the TLS connect_worker [ Upstream commit 46bc86c833956219bbfd246c1ffd832a479c5199 ] The TLS connect path has a use-after-free: nothing pins the upper rpc_clnt across the delayed connect_worker. xs_connect() stores task->tk_client in sock_xprt::clnt as a raw pointer and queues the worker; for TLS-secured transports that worker is xs_tcp_tls_setup_socket(), which reads several fields out of the saved pointer (cl_timeout, cl_program, cl_prog, cl_vers, cl_cred, cl_stats) to construct the args for the inner handshake rpc_clnt. The xprt does not reference the rpc_clnt; the rpc_clnt references the xprt. xs_destroy() does cancel the connect_worker, but it runs only when the xprt's refcount drops to zero, which cannot happen until the rpc_clnt releases its cl_xprt reference in rpc_free_client_work(). When a TLS handshake fails fatally (for example, an mTLS mount whose client cert does not match the server), the connecting task is woken with -EACCES and exits, the mount caller invokes rpc_shutdown_client(), and the upper rpc_clnt is freed before the queued connect_worker fires. xs_tcp_tls_setup_socket() then dereferences the freed clnt, producing the refcount_t underflow Michael Nemanov reported. Take a reference on the upper rpc_clnt in xs_connect() for TLS transports via a new rpc_hold_client() helper, and drop it in the connect_worker's exit path with rpc_release_client(). The xprt_lock_connect() / xprt_unlock_connect() pairing already serialises xs_connect() with xs_tcp_tls_setup_socket(), so the take and release are balanced one-for-one. The non-TLS connect worker (xs_tcp_setup_socket) never reads sock_xprt::clnt, so leave that path alone and avoid the clnt-holds-xprt-holds-clnt cycle that would otherwise prevent xprt destruction. Reported-by: Michael Nemanov Closes: https://lore.kernel.org/linux-nfs/40e3d522-dfcf-4fc1-9c55-b5e81f1536d5@vastdata.com/ Fixes: 75eb6af7acdf ("SUNRPC: Add a TCP-with-TLS RPC transport class") Signed-off-by: Chuck Lever Tested-by: Michael Nemanov Reviewed-by: Michael Nemanov Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- include/linux/sunrpc/clnt.h | 1 + net/sunrpc/clnt.c | 19 +++++++++++++++++-- net/sunrpc/xprtsock.c | 11 ++++++++++- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index f8b406b0a1af5..3c2b8c355ab3a 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -190,6 +190,7 @@ int rpc_switch_client_transport(struct rpc_clnt *, const struct rpc_timeout *); void rpc_shutdown_client(struct rpc_clnt *); +void rpc_hold_client(struct rpc_clnt *); void rpc_release_client(struct rpc_clnt *); void rpc_task_release_transport(struct rpc_task *); void rpc_task_release_client(struct rpc_task *); diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index bc8ca470718b7..efa26899bc7dc 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -1026,8 +1026,23 @@ rpc_free_auth(struct rpc_clnt *clnt) return NULL; } -/* - * Release reference to the RPC client +/** + * rpc_hold_client - acquire a reference on an rpc_clnt + * @clnt: rpc_clnt to pin + * + * Pairs with rpc_release_client(). + */ +void rpc_hold_client(struct rpc_clnt *clnt) +{ + refcount_inc(&clnt->cl_count); +} + +/** + * rpc_release_client - release a reference on an rpc_clnt + * @clnt: rpc_clnt to release + * + * Pairs with rpc_hold_client(). The rpc_clnt's resources are + * freed once its reference count drops to zero. */ void rpc_release_client(struct rpc_clnt *clnt) diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 3eccd4923e6c6..359407aae03e6 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -2761,6 +2761,7 @@ static void xs_tcp_tls_setup_socket(struct work_struct *work) out_unlock: current_restore_flags(pflags, PF_MEMALLOC); upper_transport->clnt = NULL; + rpc_release_client(upper_clnt); xprt_unlock_connect(upper_xprt, upper_transport); return; @@ -2808,7 +2809,15 @@ static void xs_connect(struct rpc_xprt *xprt, struct rpc_task *task) } else dprintk("RPC: xs_connect scheduled xprt %p\n", xprt); - transport->clnt = task->tk_client; + /* + * Only the TLS connect_worker reads transport->clnt; pinning + * the upper rpc_clnt unconditionally would form a cycle with + * cl_xprt and prevent xprt destruction. + */ + if (xprt->xprtsec.policy != RPC_XPRTSEC_NONE) { + rpc_hold_client(task->tk_client); + transport->clnt = task->tk_client; + } queue_delayed_work(xprtiod_workqueue, &transport->connect_worker, delay); From b69ea153d30ce19ca8997eeeca1e2219fae5a29b Mon Sep 17 00:00:00 2001 From: Cao Guanghui Date: Wed, 17 Jun 2026 14:00:52 +0800 Subject: [PATCH 2175/2758] dm era: fix NULL pointer dereference in metadata_open() [ Upstream commit 9ae672606c17891d90b282e3490b817620549599 ] metadata_open() returns NULL when kzalloc_obj() fails, but the caller era_ctr() only checks IS_ERR(md). Since IS_ERR(NULL) returns false, the NULL pointer is treated as a valid result and later assigned to era->md, leading to a NULL pointer dereference when the metadata is accessed. Fix this by returning ERR_PTR(-ENOMEM) on allocation failure, consistent with dm-cache-metadata.c, dm-thin-metadata.c, and dm-clone-metadata.c which all use ERR_PTR(-ENOMEM) for the same pattern. Fixes: eec40579d848 ("dm: add era target") Signed-off-by: Cao Guanghui Reviewed-by: Su Yue Reviewed-by: Ming-Hung Tsai Signed-off-by: Mikulas Patocka Signed-off-by: Sasha Levin --- drivers/md/dm-era-target.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/md/dm-era-target.c b/drivers/md/dm-era-target.c index 05285c04ff2cd..08ce96e8cf4f6 100644 --- a/drivers/md/dm-era-target.c +++ b/drivers/md/dm-era-target.c @@ -810,8 +810,10 @@ static struct era_metadata *metadata_open(struct block_device *bdev, int r; struct era_metadata *md = kzalloc_obj(*md); - if (!md) - return NULL; + if (!md) { + DMERR("could not allocate metadata struct"); + return ERR_PTR(-ENOMEM); + } md->bdev = bdev; md->block_size = block_size; From f0eac9c3c3711f24efc2aaf12b8ec3e54a38c214 Mon Sep 17 00:00:00 2001 From: Zizhi Wo Date: Sat, 4 Jul 2026 09:53:20 +0800 Subject: [PATCH 2176/2758] smb: client: fix busy dentry warning on unmount after DIO [ Upstream commit 75f5c412fa867efa0bf9b646bffe0d912109e84a ] Commit c68337442f03 ("cifs: Fix busy dentry used after unmounting") fixed the issue in cifs where deferred close of a file led to a dentry reference count not being released in umount, by flushing deferredclose_wq in cifs_kill_sb() to solve it. However, the cifs DIO path suffers from the same busy-dentry problem caused by a delayed dentry reference-count release: [dio] [cifsd] [close + umount] netfs_unbuffered_write_iter_locked ... cifs_demultiplex_thread netfs_unbuffered_write cifs_issue_write netfs_wait_for_in_progress_stream [1] ... netfs_write_subrequest_terminated netfs_subreq_clear_in_progress netfs_wake_collector // wake [1] netfs_put_subrequest netfs_put_request queue_work(system_dfl_wq, xxx) [2] // dio write return cifs_close _cifsFileInfo_put // cfile->count 2->1 --cfile->count [3] // umount cifs_kill_sb kill_anon_super // warning triggered! shrink_dcache_for_umount [4] [system_dfl_wq] [5] netfs_free_request ... _cifsFileInfo_put // cfile->count 1->0 --cfile->count queue_work(fileinfo_put_wq, xxx) [fileinfo_put_wq] [6] cifsFileInfo_put_work cifsFileInfo_put_final dput If the umount path is triggered before [5], it results warning: BUG: Dentry 00000000eab1f070{i=9a917b66ae404fec,n=test} still in use (1) [unmount of cifs cifs] The existing per-inode ictx->io_count wait in cifs_evict_inode() does not help: it lives in the inode eviction path, which runs after shrink_dcache_for_umount() has already warned about the busy dentries. Fix it by adding a per-superblock outstanding-rreq counter that is incremented in cifs_init_request() and decremented in cifs_free_request(). In cifs_kill_sb(), before kill_anon_super(), wait for this counter to reach 0 - which guarantees that all cleanup_work for this sb have run and thus all relevant cfile puts are queued on fileinfo_put_wq or serverclose_wq. Then drain the workqueue so the dentry refs are dropped. This is a targeted wait, not a flush of the system-wide system_dfl_wq. Fixes: 340cea84f691c ("cifs: open files should not hold ref on superblock") Signed-off-by: Zizhi Wo Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/client/cifs_fs_sb.h | 1 + fs/smb/client/cifsfs.c | 12 ++++++++++++ fs/smb/client/connect.c | 1 + fs/smb/client/file.c | 5 +++++ 4 files changed, 19 insertions(+) diff --git a/fs/smb/client/cifs_fs_sb.h b/fs/smb/client/cifs_fs_sb.h index 84e7e366b0ff4..d6494e1d93ccf 100644 --- a/fs/smb/client/cifs_fs_sb.h +++ b/fs/smb/client/cifs_fs_sb.h @@ -56,6 +56,7 @@ struct cifs_sb_info { struct smb3_fs_context *ctx; atomic_t active; atomic_t mnt_cifs_flags; + atomic_t outstanding_rreq; /* nr of rreqs not yet fully deinitialized */ struct delayed_work prune_tlinks; struct rcu_head rcu; diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c index ce23924f01b3a..d6c30f8552e08 100644 --- a/fs/smb/client/cifsfs.c +++ b/fs/smb/client/cifsfs.c @@ -309,6 +309,18 @@ static void cifs_kill_sb(struct super_block *sb) /* Wait for all opened files to release */ flush_workqueue(deferredclose_wq); + /* + * Wait for all in-flight netfs I/O requests to finish their + * cleanup_work so that any cifsFileInfo final puts they queue + * to fileinfo_put_wq/serverclose_wq have been queued, then + * drain the workqueue so the cfile dentry refs are dropped to + * avoid the busy dentry warning. + */ + wait_var_event(&cifs_sb->outstanding_rreq, + !atomic_read(&cifs_sb->outstanding_rreq)); + flush_workqueue(serverclose_wq); + flush_workqueue(fileinfo_put_wq); + /* finally release root dentry */ dput(cifs_sb->root); cifs_sb->root = NULL; diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c index cbeb5637eeb92..9511deef7084f 100644 --- a/fs/smb/client/connect.c +++ b/fs/smb/client/connect.c @@ -3479,6 +3479,7 @@ int cifs_setup_cifs_sb(struct cifs_sb_info *cifs_sb) spin_lock_init(&cifs_sb->tlink_tree_lock); cifs_sb->tlink_tree = RB_ROOT; + atomic_set(&cifs_sb->outstanding_rreq, 0); cifs_dbg(FYI, "file mode: %04ho dir mode: %04ho\n", ctx->file_mode, ctx->dir_mode); diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c index b5a61a4a43b85..a26a464d6242b 100644 --- a/fs/smb/client/file.c +++ b/fs/smb/client/file.c @@ -288,6 +288,7 @@ static int cifs_init_request(struct netfs_io_request *rreq, struct file *file) return smb_EIO1(smb_eio_trace_not_netfs_writeback, rreq->origin); } + atomic_inc(&cifs_sb->outstanding_rreq); return 0; } @@ -309,9 +310,13 @@ static void cifs_rreq_done(struct netfs_io_request *rreq) static void cifs_free_request(struct netfs_io_request *rreq) { struct cifs_io_request *req = container_of(rreq, struct cifs_io_request, rreq); + struct cifs_sb_info *cifs_sb = CIFS_SB(rreq->inode->i_sb); if (req->cfile) cifsFileInfo_put(req->cfile); + + if (atomic_dec_and_test(&cifs_sb->outstanding_rreq)) + wake_up_var(&cifs_sb->outstanding_rreq); } static void cifs_free_subrequest(struct netfs_io_subrequest *subreq) From 153d1b8b5bc30847eb70ad535f62f289aa9217e6 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Wed, 8 Jul 2026 18:57:22 -0500 Subject: [PATCH 2177/2758] regulator: core: regulator_lock_two() should test for EDEADLK not EDEADLOCK [ Upstream commit d38f8bd771c4999b797d7074b348cf201414bd34 ] Compare against -EDEADLK, which is what ww_mutex_lock() actually returns and what every other deadlock check in this file already uses. Function regulator_lock_two() acquires two regulators via regulator_lock_nested() -> ww_mutex_lock(). On contention, ww_mutex_lock() returns -EDEADLK, which is the caller's signal to drop the lock it holds and retry the acquisition in the canonical order. However, regulator_lock_two() tests the return value against -EDEADLOCK rather than -EDEADLK. On most architectures, EDEADLK and EDEADLOCK are the same value, so the comparison happens to be correct and the bug is invisible. But on MIPS, SPARC, and PowerPC, those two errors have different values. The test is wrong: a genuine -EDEADLK backoff no longer matches -EDEADLOCK, so instead of unlocking and retrying, the code falls into WARN_ON(ret) and returns with only one of the two regulators locked. In practice, this is a bug only on MIPS, because the regulator core is not built or used on the other two platforms. In general, EDEADLK is preferred over EDEADLOCK for new code. Fixes: cba6cfdc7c3f ("regulator: core: Avoid lockdep reports when resolving supplies") Signed-off-by: Timur Tabi Link: https://patch.msgid.link/20260708235722.2953579-1-ttabi@nvidia.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/regulator/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index dc5d67767336e..1797929dfe566 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -248,7 +248,7 @@ static void regulator_lock_two(struct regulator_dev *rdev1, ret = regulator_lock_nested(rdev1, ww_ctx); WARN_ON(ret); ret = regulator_lock_nested(rdev2, ww_ctx); - if (ret != -EDEADLOCK) { + if (ret != -EDEADLK) { WARN_ON(ret); goto exit; } @@ -264,7 +264,7 @@ static void regulator_lock_two(struct regulator_dev *rdev1, swap(held, contended); ret = regulator_lock_nested(contended, ww_ctx); - if (ret != -EDEADLOCK) { + if (ret != -EDEADLK) { WARN_ON(ret); break; } From 7d84acf641afb68aa8efe40815ef43779ddb12fd Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 2 Jul 2026 16:50:13 +0200 Subject: [PATCH 2178/2758] drm/fb-helper: Only consider active CRTCs for vblank sync [ Upstream commit 06c2b8d7ea2bcb014dd974fc3bc6d128d65d7477 ] Only synchronize fbdev output to the vblank of an active CRTC. Go over the list of CRTCs and pick the first that matches. Fixes warnings as the one shown below [ 77.201354] WARNING: drivers/gpu/drm/drm_vblank.c:1320 at drm_crtc_wait_one_vblank+0x194/0x1cc [drm], CPU#1: kworker/1:7/1867 [ 77.201354] omapdrm omapdrm.0: [drm] vblank wait timed out on crtc 0 This currently happens if the fbdev output is not on CRTC 0. Atomic and non-atomic drivers require distinct code paths. As for other fbdev operations, implement both and select the correct one at runtime. Not finding an active CRTC is not a bug. Do not wait in this case, but flush the display update as before. v4: - avoid possible deadlocks with locking context (Sashiko) v3: - drop excessive state validation (Jani) - acquire plane and CRTC mutices (Sashiko) v2: - move look-up code into separate helper - support drivers with legacy modesetting v1: - see https://lore.kernel.org/dri-devel/1c9e0e24-9c4a-4259-8700-cf9e5fd60ca3@suse.de/ Co-authored-by: H. Nikolaus Schaller Signed-off-by: Thomas Zimmermann Fixes: d8c4bddcd8bcb ("drm/fb-helper: Synchronize dirty worker with vblank") Tested-by: Icenowy Zheng Tested-by: H. Nikolaus Schaller Closes: https://bugs.debian.org/1138033 Acked-by: Maxime Ripard Link: https://patch.msgid.link/20260702145021.226932-1-tzimmermann@suse.de Signed-off-by: Sasha Levin --- drivers/gpu/drm/drm_fb_helper.c | 92 ++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 1541fc8a9ac2d..515f699e3ecd3 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -225,16 +225,106 @@ static void drm_fb_helper_resume_worker(struct work_struct *work) console_unlock(); } +static int find_crtc_index_atomic(struct drm_fb_helper *helper) +{ + struct drm_device *dev = helper->dev; + int crtc_index = -EINVAL; + struct drm_modeset_acquire_ctx ctx; + struct drm_plane *plane; + int ret = 0; + + drm_modeset_acquire_init(&ctx, 0); + +retry: + drm_for_each_plane(plane, dev) { + const struct drm_plane_state *plane_state; + + if (plane->type != DRM_PLANE_TYPE_PRIMARY) + continue; + + ret = drm_modeset_lock(&plane->mutex, &ctx); + if (ret) + goto err_drm_modeset_lock; + plane_state = plane->state; + + if (plane_state->fb == helper->fb && plane_state->crtc) { + struct drm_crtc *crtc = plane_state->crtc; + + ret = drm_modeset_lock(&crtc->mutex, &ctx); + if (ret) + goto err_drm_modeset_lock; + if (crtc->state->active) + crtc_index = crtc->index; + drm_modeset_unlock(&crtc->mutex); + } + drm_modeset_unlock(&plane->mutex); + + if (crtc_index >= 0) + break; + } + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + + return crtc_index; + +err_drm_modeset_lock: + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry; + } + return ret; +} + +static int find_crtc_index_legacy(struct drm_fb_helper *helper) +{ + struct drm_device *dev = helper->dev; + struct drm_crtc *crtc; + + drm_for_each_crtc(crtc, dev) { + struct drm_plane *plane = crtc->primary; + + if (!crtc->enabled) + continue; + if (!plane || plane->fb != helper->fb) + continue; /* CRTC doesn't display fbdev emulation */ + + return crtc->index; + } + + return -EINVAL; +} + +static int drm_fb_helper_find_crtc_index(struct drm_fb_helper *helper) +{ + struct drm_device *dev = helper->dev; + int crtc_index; + + mutex_lock(&dev->mode_config.mutex); + + if (drm_drv_uses_atomic_modeset(dev)) + crtc_index = find_crtc_index_atomic(helper); + else + crtc_index = find_crtc_index_legacy(helper); + + mutex_unlock(&dev->mode_config.mutex); + + return crtc_index; +} + static void drm_fb_helper_fb_dirty(struct drm_fb_helper *helper) { struct drm_device *dev = helper->dev; struct drm_clip_rect *clip = &helper->damage_clip; struct drm_clip_rect clip_copy; + int crtc_index; unsigned long flags; int ret; mutex_lock(&helper->lock); - drm_client_modeset_wait_for_vblank(&helper->client, 0); + crtc_index = drm_fb_helper_find_crtc_index(helper); + if (crtc_index >= 0) + drm_client_modeset_wait_for_vblank(&helper->client, crtc_index); mutex_unlock(&helper->lock); if (drm_WARN_ON_ONCE(dev, !helper->funcs->fb_dirty)) From ddd5ab921fdd2b04ec24cc5ff06055296d227445 Mon Sep 17 00:00:00 2001 From: Wang Yan Date: Thu, 2 Jul 2026 10:59:49 +0800 Subject: [PATCH 2179/2758] selftests/net: fix EVP_MD_CTX leak in tcp_mmap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit f4ef35efbb49527293309f668ea73ec5de9b8e7a ] In tcp_mmap.c, both child_thread() and main() allocate an EVP_MD_CTX via EVP_MD_CTX_new() when integrity checking is enabled, but neither function releases the context. child_thread() misses the free in its common cleanup block, and main() returns without freeing the context. This results in a SHA256 context leak on every run that uses the ‑i (integrity) option. Add the missing EVP_MD_CTX_free() calls to the appropriate cleanup paths to fix the leak. Fixes: 5c5945dc695c ("selftests/net: Add SHA256 computation over data sent in tcp_mmap") Signed-off-by: Wang Yan Link: https://patch.msgid.link/20260702025949.442523-1-wangyan01@kylinos.cn Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- tools/testing/selftests/net/tcp_mmap.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/testing/selftests/net/tcp_mmap.c b/tools/testing/selftests/net/tcp_mmap.c index 4fcce5150850d..2544ae35d07a8 100644 --- a/tools/testing/selftests/net/tcp_mmap.c +++ b/tools/testing/selftests/net/tcp_mmap.c @@ -313,6 +313,8 @@ void *child_thread(void *arg) tcp_info_get_rcv_mss(fd)); } error: + if (ctx) + EVP_MD_CTX_free(ctx); munmap(buffer, buffer_sz); close(fd); if (zflg) @@ -606,6 +608,8 @@ int main(int argc, char *argv[]) EVP_DigestFinal_ex(ctx, digest, &digest_len); send(fd, digest, (size_t)SHA256_DIGEST_LENGTH, 0); } + if (ctx) + EVP_MD_CTX_free(ctx); close(fd); munmap(buffer, buffer_sz); return 0; From 7b2fbdafc6de4701ccac80311141b1c603118a04 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Fri, 3 Jul 2026 22:14:23 +0800 Subject: [PATCH 2180/2758] net/mlx5: Fix L3 tunnel entropy refcount leak [ Upstream commit c914307e1d41c2cb7bcdcbfde4cd2f214f6aa027 ] mlx5_tun_entropy_refcount_inc() counts both VXLAN and L2-to-L3 tunnel reformat entries as entropy-enabling users. The matching decrement path only handled VXLAN, leaving L2-to-L3 tunnel entries counted after release. Handle MLX5_REFORMAT_TYPE_L2_TO_L3_TUNNEL in mlx5_tun_entropy_refcount_dec() as well so the enabling entry refcount remains balanced. Fixes: f828ca6a2fb6 ("net/mlx5e: Add support for hw encapsulation of MPLS over UDP") Signed-off-by: Li RongQing Reviewed-by: Simon Horman Reviewed-by: Tariq Toukan Link: https://patch.msgid.link/20260703141423.1723-1-lirongqing@baidu.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c index 4571c56ec3c9b..97f6097d4c705 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c @@ -176,7 +176,8 @@ void mlx5_tun_entropy_refcount_dec(struct mlx5_tun_entropy *tun_entropy, int reformat_type) { mutex_lock(&tun_entropy->lock); - if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_VXLAN) + if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_VXLAN || + reformat_type == MLX5_REFORMAT_TYPE_L2_TO_L3_TUNNEL) tun_entropy->num_enabling_entries--; else if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_NVGRE && --tun_entropy->num_disabling_entries == 0) From 2ae146bcfcc15eed0e2cb8168405bfcbb40f59cd Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Mon, 6 Jul 2026 08:50:17 +0300 Subject: [PATCH 2181/2758] ethtool: rss: Fix hfunc and input_xfrm parsing on big endian [ Upstream commit b62869a81a7ce388d1fbb0fac5fa8300ea614d81 ] ETHTOOL_A_RSS_HFUNC and ETHTOOL_A_RSS_INPUT_XFRM are NLA_U32 attributes, but ethnl_rss_set() and ethnl_rss_create_doit() parse them with ethnl_update_u8(), which reads a single byte. On little endian this happens to read the least significant byte and works as long as the value fits in a byte. On big endian it reads the most significant byte, so the requested value is parsed incorrectly. The destination fields in struct ethtool_rxfh_param are u8, so the attribute can't be read directly with ethnl_update_u32(). Cap the hfunc policy at U8_MAX so an out of range value is rejected instead of being silently truncated into the u8 field, and add ethnl_update_u8_u32() to read the full u32 and narrow it into the u8 destination. Fixes: 82ae67cbc423 ("ethtool: rss: support setting hfunc via Netlink") Fixes: d3e2c7bab124 ("ethtool: rss: support setting input-xfrm via Netlink") Fixes: a166ab7816c5 ("ethtool: rss: support creating contexts via Netlink") Reviewed-by: Dragos Tatulea Reviewed-by: Nimrod Oren Signed-off-by: Gal Pressman Link: https://patch.msgid.link/20260706055017.3355806-1-gal@nvidia.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/ethtool/netlink.h | 28 ++++++++++++++++++++++++++++ net/ethtool/rss.c | 14 ++++++++------ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index fd2198e45d2bb..733fb3c3a63a5 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -115,6 +115,34 @@ static inline void ethnl_update_u8(u8 *dst, const struct nlattr *attr, *mod = true; } +/** + * ethnl_update_u8_u32() - update u8 value from an NLA_U32 attribute + * @dst: value to update + * @attr: netlink attribute with new value or null + * @mod: pointer to bool for modification tracking + * + * Some attributes are NLA_U32 on the wire but are stored in a u8. Read the + * full 32-bit value from NLA_U32 netlink attribute @attr and narrow it into + * the u8 pointed to by @dst; do nothing if @attr is null. + * Bool pointed to by @mod is set to true if this function changed the value + * of *dst, otherwise it is left as is. + */ +static inline void ethnl_update_u8_u32(u8 *dst, const struct nlattr *attr, + bool *mod) +{ + u32 val; + + if (!attr) + return; + val = nla_get_u32(attr); + DEBUG_NET_WARN_ON_ONCE(val > U8_MAX); + if (*dst == val) + return; + + *dst = val; + *mod = true; +} + /** * ethnl_update_bool32() - update u32 used as bool from NLA_U8 attribute * @dst: value to update diff --git a/net/ethtool/rss.c b/net/ethtool/rss.c index 53792f53f9229..ca6ed152c2be3 100644 --- a/net/ethtool/rss.c +++ b/net/ethtool/rss.c @@ -574,7 +574,7 @@ static const struct nla_policy ethnl_rss_flows_policy[] = { const struct nla_policy ethnl_rss_set_policy[ETHTOOL_A_RSS_FLOW_HASH + 1] = { [ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy), [ETHTOOL_A_RSS_CONTEXT] = { .type = NLA_U32, }, - [ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_MIN(NLA_U32, 1), + [ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_RANGE(NLA_U32, 1, U8_MAX), [ETHTOOL_A_RSS_INDIR] = { .type = NLA_BINARY, }, [ETHTOOL_A_RSS_HKEY] = NLA_POLICY_MIN(NLA_BINARY, 1), [ETHTOOL_A_RSS_INPUT_XFRM] = @@ -855,7 +855,7 @@ ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info) indir_mod = !!tb[ETHTOOL_A_RSS_INDIR]; rxfh.hfunc = data.hfunc; - ethnl_update_u8(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod); + ethnl_update_u8_u32(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod); if (rxfh.hfunc == data.hfunc) rxfh.hfunc = ETH_RSS_HASH_NO_CHANGE; @@ -864,7 +864,8 @@ ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info) goto exit_free_indir; rxfh.input_xfrm = data.input_xfrm; - ethnl_update_u8(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM], &mod); + ethnl_update_u8_u32(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM], + &mod); xfrm_sym = rxfh.input_xfrm || data.input_xfrm; if (rxfh.input_xfrm == data.input_xfrm) rxfh.input_xfrm = RXH_XFRM_NO_CHANGE; @@ -938,7 +939,7 @@ const struct ethnl_request_ops ethnl_rss_request_ops = { const struct nla_policy ethnl_rss_create_policy[ETHTOOL_A_RSS_INPUT_XFRM + 1] = { [ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy), [ETHTOOL_A_RSS_CONTEXT] = NLA_POLICY_MIN(NLA_U32, 1), - [ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_MIN(NLA_U32, 1), + [ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_RANGE(NLA_U32, 1, U8_MAX), [ETHTOOL_A_RSS_INDIR] = NLA_POLICY_MIN(NLA_BINARY, 1), [ETHTOOL_A_RSS_HKEY] = NLA_POLICY_MIN(NLA_BINARY, 1), [ETHTOOL_A_RSS_INPUT_XFRM] = @@ -1054,14 +1055,15 @@ int ethnl_rss_create_doit(struct sk_buff *skb, struct genl_info *info) goto exit_clean_data; indir_user_size = ret; - ethnl_update_u8(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod); + ethnl_update_u8_u32(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod); ret = rss_set_prep_hkey(dev, info, &data, &rxfh, &mod); if (ret) goto exit_free_indir; rxfh.input_xfrm = RXH_XFRM_NO_CHANGE; - ethnl_update_u8(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM], &mod); + ethnl_update_u8_u32(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM], + &mod); ctx = ethtool_rxfh_ctx_alloc(ops, data.indir_size, data.hkey_size); if (!ctx) { From 3cf83432e0561aef6d7ec2664909d12d0ff0ffc1 Mon Sep 17 00:00:00 2001 From: Harman Kalra Date: Thu, 2 Jul 2026 10:26:16 +0530 Subject: [PATCH 2182/2758] octeontx2-af: fix VF bringup affecting PF promiscuous state [ Upstream commit fabb881df322da25442f98d23f5fa371e3c78ec4 ] Mbox handling of nix_set_rx_mode for a VF with promiscuous and all_multi flags set to false causes deletion of the PF's promiscuous and allmulti MCAM rules. This occurs because the APIs that enable/disable these rules operate only on the PF, even when the mbox request is made via a VF interface. Guard both rvu_npc_enable_allmulti_entry() and rvu_npc_enable_promisc_entry() disable paths with an is_vf() check so that a VF bringing up or tearing down its interface cannot inadvertently clear the PF's MCAM rules. Fixes: 967db3529eca ("octeontx2-af: add support for multicast/promisc packet replication feature") Signed-off-by: Harman Kalra Signed-off-by: Nitin Shetty J Link: https://patch.msgid.link/20260702045616.3002773-2-nshettyj@marvell.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c index f977734ae712c..f4c066aff3716 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -4548,7 +4548,7 @@ int rvu_mbox_handler_nix_set_rx_mode(struct rvu *rvu, struct nix_rx_mode *req, rvu_npc_install_allmulti_entry(rvu, pcifunc, nixlf, pfvf->rx_chan_base); } else { - if (!nix_rx_multicast) + if (!nix_rx_multicast && !is_vf(pcifunc)) rvu_npc_enable_allmulti_entry(rvu, pcifunc, nixlf, false); } @@ -4558,7 +4558,7 @@ int rvu_mbox_handler_nix_set_rx_mode(struct rvu *rvu, struct nix_rx_mode *req, pfvf->rx_chan_base, pfvf->rx_chan_cnt); else - if (!nix_rx_multicast) + if (!nix_rx_multicast && !is_vf(pcifunc)) rvu_npc_enable_promisc_entry(rvu, pcifunc, nixlf, false); return 0; From c69369057b3080a21d337a5636b074223bf155eb Mon Sep 17 00:00:00 2001 From: Anas Khan Date: Thu, 2 Jul 2026 16:58:20 +0530 Subject: [PATCH 2183/2758] drm/xe: remove duplicate include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 91426ce50d14a49bde53b3ad1e48393556ba92cd ] xe_pci.c includes twice, separated only by the include. Drop the redundant second include; this is a non-functional cleanup flagged by scripts/checkincludes.pl. Fixes: 6cad22853cb8 ("drm/xe/kunit: Add stub to read_gmdid") Signed-off-by: Anas Khan Link: https://patch.msgid.link/20260702112820.34675-1-anxkhn28@gmail.com Signed-off-by: Rodrigo Vivi (cherry picked from commit 84ed5b0a925721aaf069d36e18a99db966ff4e80) Signed-off-by: Thomas Hellström Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/tests/xe_pci.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/xe/tests/xe_pci.c b/drivers/gpu/drm/xe/tests/xe_pci.c index f3179b31f13e3..8a32fb469147d 100644 --- a/drivers/gpu/drm/xe/tests/xe_pci.c +++ b/drivers/gpu/drm/xe/tests/xe_pci.c @@ -9,7 +9,6 @@ #include #include -#include #include #define PLATFORM_CASE(platform__, graphics_step__) \ From bf126747e7bff55e5ba0b1ec10fc5fce99feaed9 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 8 Jul 2026 15:34:22 +0800 Subject: [PATCH 2184/2758] drm/xe: free madvise VMA array on L2 flush failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 14abbed336a2d1bbd726c25d148d2ec0ff928073 ] xe_vm_madvise_ioctl() allocates madvise_range.vmas in get_vmas(). After get_vmas() succeeds with at least one VMA, error paths must go through free_vmas so the array is released before the madvise details are destroyed. The L2 flush validation path added for PAT madvise rejects some SVM/userptr ranges after get_vmas() has succeeded, but jumps directly to madv_fini. This skips kfree(madvise_range.vmas), leaking the VMA array on each failed ioctl. Jump to free_vmas instead, matching the other validation failure paths after get_vmas() has succeeded. Fixes: 4f39a194d41e ("drm/xe/xe3p_lpg: Restrict UAPI to enable L2 flush optimization") Signed-off-by: Guangshuo Li Reviewed-by: Rodrigo Vivi Link: https://patch.msgid.link/20260708073422.725186-1-lgs201920130244@gmail.com Signed-off-by: Rodrigo Vivi (cherry picked from commit c3a1c3579b1250060da73507a4acef712974c78a) Signed-off-by: Thomas Hellström Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_vm_madvise.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_vm_madvise.c b/drivers/gpu/drm/xe/xe_vm_madvise.c index c4fb290041956..246fe18431428 100644 --- a/drivers/gpu/drm/xe/xe_vm_madvise.c +++ b/drivers/gpu/drm/xe/xe_vm_madvise.c @@ -643,7 +643,7 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil xe_device_is_l2_flush_optimized(xe) && (pat_index != 19 && coh_mode != XE_COH_2WAY))) { err = -EINVAL; - goto madv_fini; + goto free_vmas; } } From 1a638c55f2db6cb2296e5e3138015dd8fd9d4aa9 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 8 Jul 2026 20:35:20 +0800 Subject: [PATCH 2185/2758] smb: client: fix overflow in passthrough ioctl bounds check [ Upstream commit a4f27ad055392fa164f5649e89a3637b033c5fcc ] smb2_ioctl_query_info() validates the PASSTHRU_FSCTL response payload before copying it to userspace. The payload offset and length both come from 32-bit fields. The bounds check currently adds OutputOffset and qi.input_buffer_length directly, so the addition can wrap in 32-bit arithmetic before the result is compared against the response buffer length. A malicious server can use a large OutputOffset and a small OutputCount to make the wrapped sum pass the bounds check. The later copy_to_user() then reads from io_rsp + OutputOffset, outside the response buffer. Use size_add() for the offset plus length check so overflow is treated as out of bounds. Fixes: 2b1116bbe898 ("CIFS: Use common error handling code in smb2_ioctl_query_info()") Signed-off-by: Guangshuo Li Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/client/smb2ops.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 10664380f351a..15cfb513238f6 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -1772,8 +1772,8 @@ smb2_ioctl_query_info(const unsigned int xid, if (le32_to_cpu(io_rsp->OutputCount) < qi.input_buffer_length) qi.input_buffer_length = le32_to_cpu(io_rsp->OutputCount); if (qi.input_buffer_length > 0 && - le32_to_cpu(io_rsp->OutputOffset) + qi.input_buffer_length - > rsp_iov[1].iov_len) { + size_add(le32_to_cpu(io_rsp->OutputOffset), + qi.input_buffer_length) > rsp_iov[1].iov_len) { rc = -EFAULT; goto out; } From c25212f274a5cc34862160e39ba139ea889e3cac Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 3 Jul 2026 17:23:38 +0100 Subject: [PATCH 2186/2758] drm/imagination: make pvr_fw_trace_init_mask_ops static [ Upstream commit c804aadc4ce3dcb34d6f2ecc9a159c148210805a ] The pvr_fw_trace_init_mask_ops is not used outside pvr_fw_trace.c so make it static to avoid the following sparse warning: drivers/gpu/drm/imagination/pvr_fw_trace.c:74:31: warning: symbol 'pvr_fw_trace_init_mask_ops' was not declared. Should it be static? Fixes: c6978643ea1c ("drm/imagination: Validate fw trace group_mask") Reviewed-by: Alessio Belle Signed-off-by: Ben Dooks Link: https://patch.msgid.link/20260703162338.2848039-1-ben.dooks@codethink.co.uk Signed-off-by: Alessio Belle Signed-off-by: Sasha Levin --- drivers/gpu/drm/imagination/pvr_fw_trace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/imagination/pvr_fw_trace.c b/drivers/gpu/drm/imagination/pvr_fw_trace.c index 6193811ef7beb..5b2218a7cd663 100644 --- a/drivers/gpu/drm/imagination/pvr_fw_trace.c +++ b/drivers/gpu/drm/imagination/pvr_fw_trace.c @@ -71,7 +71,7 @@ pvr_fw_trace_init_mask_set(const char *val, const struct kernel_param *kp) return 0; } -const struct kernel_param_ops pvr_fw_trace_init_mask_ops = { +static const struct kernel_param_ops pvr_fw_trace_init_mask_ops = { .set = pvr_fw_trace_init_mask_set, .get = param_get_hexint, }; From 81a7b7ddb07efdd8cf30eac6def18b9289b8de1e Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Thu, 9 Jul 2026 17:00:15 +0100 Subject: [PATCH 2187/2758] tracing/remotes: Fix leak in trace_remote_alloc_buffer() error path [ Upstream commit ec082d0b978b5fb4c11205ccce63587ac94c74e1 ] If page allocation fails in trace_remote_alloc_buffer(), desc->nr_cpus is not yet incremented for the current CPU. As a consequence, on error, half-allocated rb_desc will not be freed in trace_remote_free_buffer(). Increment desc->nr_cpus as soon as the first allocation for the current CPU has succeeded. Link: https://patch.msgid.link/20260709160017.1729517-2-vdonnefort@google.com Fixes: 96e43537af54 ("tracing: Introduce trace remotes") Reported-by: Sashiko Signed-off-by: Vincent Donnefort Signed-off-by: Steven Rostedt Signed-off-by: Sasha Levin --- kernel/trace/trace_remote.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/trace/trace_remote.c b/kernel/trace/trace_remote.c index d6c3f94d67cd9..4895241bf8ff0 100644 --- a/kernel/trace/trace_remote.c +++ b/kernel/trace/trace_remote.c @@ -1006,6 +1006,8 @@ int trace_remote_alloc_buffer(struct trace_buffer_desc *desc, size_t desc_size, if (!rb_desc->meta_va) goto err; + desc->nr_cpus++; + for (id = 0; id < nr_pages; id++) { rb_desc->page_va[id] = (unsigned long)__get_free_page(GFP_KERNEL); if (!rb_desc->page_va[id]) @@ -1013,7 +1015,6 @@ int trace_remote_alloc_buffer(struct trace_buffer_desc *desc, size_t desc_size, rb_desc->nr_page_va++; } - desc->nr_cpus++; desc->struct_len += offsetof(struct ring_buffer_desc, page_va); desc->struct_len += struct_size(rb_desc, page_va, rb_desc->nr_page_va); rb_desc = __next_ring_buffer_desc(rb_desc); From 57e566db78fc48da7f278cb6f4e7ede99d374f55 Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Thu, 9 Jul 2026 17:00:16 +0100 Subject: [PATCH 2188/2758] tracing/remotes: Fix struct_len in trace_remote_alloc_buffer() [ Upstream commit d471d4f86e8689ad15309e49da8e5e4e9bb9ce87 ] Pre-calculate desc->struct_len up-front in trace_remote_alloc_buffer() with trace_buffer_desc_size() to fix double-counting. While at it, use the accessor __first_ring_buffer_desc(). Link: https://patch.msgid.link/20260709160017.1729517-3-vdonnefort@google.com Fixes: 96e43537af54 ("tracing: Introduce trace remotes") Reported-by: Sashiko Signed-off-by: Vincent Donnefort Signed-off-by: Steven Rostedt Signed-off-by: Sasha Levin --- kernel/trace/trace_remote.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/kernel/trace/trace_remote.c b/kernel/trace/trace_remote.c index 4895241bf8ff0..6dde6bdcbde70 100644 --- a/kernel/trace/trace_remote.c +++ b/kernel/trace/trace_remote.c @@ -979,27 +979,22 @@ EXPORT_SYMBOL_GPL(trace_remote_free_buffer); int trace_remote_alloc_buffer(struct trace_buffer_desc *desc, size_t desc_size, size_t buffer_size, const struct cpumask *cpumask) { + size_t min_desc_size = trace_buffer_desc_size(buffer_size, cpumask_weight(cpumask)); unsigned int nr_pages = max(DIV_ROUND_UP(buffer_size, PAGE_SIZE), 2UL) + 1; - void *desc_end = desc + desc_size; struct ring_buffer_desc *rb_desc; int cpu, ret = -ENOMEM; - if (desc_size < struct_size(desc, __data, 0)) + if (desc_size < min_desc_size) return -EINVAL; desc->nr_cpus = 0; - desc->struct_len = struct_size(desc, __data, 0); + desc->struct_len = min_desc_size; - rb_desc = (struct ring_buffer_desc *)&desc->__data[0]; + rb_desc = __first_ring_buffer_desc(desc); for_each_cpu(cpu, cpumask) { unsigned int id; - if ((void *)rb_desc + struct_size(rb_desc, page_va, nr_pages) > desc_end) { - ret = -EINVAL; - goto err; - } - rb_desc->cpu = cpu; rb_desc->nr_page_va = 0; rb_desc->meta_va = (unsigned long)__get_free_page(GFP_KERNEL); @@ -1015,8 +1010,6 @@ int trace_remote_alloc_buffer(struct trace_buffer_desc *desc, size_t desc_size, rb_desc->nr_page_va++; } - desc->struct_len += offsetof(struct ring_buffer_desc, page_va); - desc->struct_len += struct_size(rb_desc, page_va, rb_desc->nr_page_va); rb_desc = __next_ring_buffer_desc(rb_desc); } From 21710f27d55e918c8ff4ce239427bb11291a8bd2 Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Thu, 9 Jul 2026 17:00:17 +0100 Subject: [PATCH 2189/2758] ring-buffer: Allow sparse CPU masks in ring_buffer_desc() [ Upstream commit 601ddaceb861be7eb557278109966320a6f3478c ] No user currently relies on sparse CPU masks, but the descriptor logic already supports them via linear fallback. Remove the arbitrary limitation. Link: https://patch.msgid.link/20260709160017.1729517-4-vdonnefort@google.com Fixes: 2e67fabd8b77 ("ring-buffer: Introduce ring-buffer remotes") Reported-by: Sashiko Signed-off-by: Vincent Donnefort Signed-off-by: Steven Rostedt Signed-off-by: Sasha Levin --- kernel/trace/ring_buffer.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index fcaa3761ef3eb..da9339466bddd 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -2220,10 +2220,7 @@ static struct ring_buffer_desc *ring_buffer_desc(struct trace_buffer_desc *trace size_t len; int i; - if (!trace_desc) - return NULL; - - if (cpu >= trace_desc->nr_cpus) + if (!trace_desc || !trace_desc->nr_cpus) return NULL; end = (struct ring_buffer_desc *)((void *)trace_desc + trace_desc->struct_len); From 708df5274ceec9d9747895a31c733c11b12b264c Mon Sep 17 00:00:00 2001 From: Przemyslaw Korba Date: Mon, 25 May 2026 10:38:03 +0200 Subject: [PATCH 2190/2758] idpf: add padding to PTP virtchnl structures commit d1e8f9fd6b98307bc8d2863c7baa465d8a5a43be upstream. Add padding to virtchnl2 PTP structures to match the Control Plane expected message sizes: * virtchnl2_ptp_get_dev_clk_time: 8 -> 16 bytes * virtchnl2_ptp_set_dev_clk_time: 8 -> 16 bytes * virtchnl2_ptp_get_cross_time: 16 -> 24 bytes The FW expects the above sizes and PTP negotiation fails due to the mismatch. Previously neither the FW nor the driver checked message/reply sizes strictly, so the problem appeared only after recent validation improvements. reproduction steps: ptp4l -i -m Observe: failed to open /dev/ptp0: Permission denied Fixes: bf27283ba594 ("virtchnl: add PTP virtchnl definitions") Cc: stable@vger.kernel.org Reviewed-by: Aleksandr Loktionov Reviewed-by: Alexander Lobakin Signed-off-by: Przemyslaw Korba Tested-by: Samuel Salin Signed-off-by: Tony Nguyen Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/idpf/virtchnl2.h | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/intel/idpf/virtchnl2.h b/drivers/net/ethernet/intel/idpf/virtchnl2.h index 02ae447cc24ac..39fea65c075c7 100644 --- a/drivers/net/ethernet/intel/idpf/virtchnl2.h +++ b/drivers/net/ethernet/intel/idpf/virtchnl2.h @@ -1572,13 +1572,15 @@ VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_get_vport_tx_tstamp_latches); * struct virtchnl2_ptp_get_dev_clk_time - Associated with message * VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME. * @dev_time_ns: Device clock time value in nanoseconds + * @pad: Padding for future extensions * * PF/VF sends this message to receive the time from the main timer. */ struct virtchnl2_ptp_get_dev_clk_time { __le64 dev_time_ns; + u8 pad[8]; }; -VIRTCHNL2_CHECK_STRUCT_LEN(8, virtchnl2_ptp_get_dev_clk_time); +VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_get_dev_clk_time); /** * struct virtchnl2_ptp_get_cross_time: Associated with message @@ -1586,26 +1588,30 @@ VIRTCHNL2_CHECK_STRUCT_LEN(8, virtchnl2_ptp_get_dev_clk_time); * @sys_time_ns: System counter value expressed in nanoseconds, read * synchronously with device time * @dev_time_ns: Device clock time value expressed in nanoseconds + * @pad: Padding for future extensions * * PF/VF sends this message to receive the cross time. */ struct virtchnl2_ptp_get_cross_time { __le64 sys_time_ns; __le64 dev_time_ns; + u8 pad[8]; }; -VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_get_cross_time); +VIRTCHNL2_CHECK_STRUCT_LEN(24, virtchnl2_ptp_get_cross_time); /** * struct virtchnl2_ptp_set_dev_clk_time: Associated with message * VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME. * @dev_time_ns: Device time value expressed in nanoseconds to set + * @pad: Padding for future extensions * * PF/VF sends this message to set the time of the main timer. */ struct virtchnl2_ptp_set_dev_clk_time { __le64 dev_time_ns; + u8 pad[8]; }; -VIRTCHNL2_CHECK_STRUCT_LEN(8, virtchnl2_ptp_set_dev_clk_time); +VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_set_dev_clk_time); /** * struct virtchnl2_ptp_adj_dev_clk_fine: Associated with message From cab468c3c03f4bcd7530ce2783a4140da14efb7b Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Tue, 9 Jun 2026 08:37:09 +0000 Subject: [PATCH 2191/2758] mlxsw: fix refcount leak in mlxsw_sp_port_lag_join() commit 41c8c1d65b32beacd8d916a22457b4f6e47f45af upstream. When mlxsw_sp_port_lag_index_get() fails, mlxsw_sp_port_lag_join() returns an error without releasing the lag reference obtained by the earlier mlxsw_sp_lag_get(). All other error paths in the function jump to the cleanup label that ends with mlxsw_sp_lag_put(), so this is a single missed release. Fix the leak by replacing the bare 'return err' with a goto to the existing error cleanup label, which will drop the reference safely. Cc: stable@vger.kernel.org Fixes: 0d65fc13042f ("mlxsw: spectrum: Implement LAG port join/leave") Signed-off-by: Wentao Liang Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260609083709.209743-1-vulab@iscas.ac.cn Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 3a65420fa1ad1..fed708c173328 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -4360,7 +4360,7 @@ static int mlxsw_sp_port_lag_join(struct mlxsw_sp_port *mlxsw_sp_port, lag_id = lag->lag_id; err = mlxsw_sp_port_lag_index_get(mlxsw_sp, lag_id, &port_index); if (err) - return err; + goto err_lag_uppers_bridge_join; err = mlxsw_sp_lag_uppers_bridge_join(mlxsw_sp_port, lag_dev, extack); From 8adebf07b46df79a0e49a6d4ae384f0db7c91db6 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Tue, 9 Jun 2026 08:47:30 +0000 Subject: [PATCH 2192/2758] mlxsw: fix refcount leak in mlxsw_sp_vrs_lpm_tree_replace() commit 21cf8dc478a49e8de039c2739b1646a774cb1944 upstream. When mlxsw_sp_vrs_lpm_tree_replace() fails after replacing some VRs, the error rollback loop does not correctly revert the preceding replacements. The loop decrements the index but fails to update the vr pointer, which still points to the VR that caused the failure. As a result, the condition and the rollback call always operate on the same VR, potentially calling mlxsw_sp_vr_lpm_tree_replace() multiple times on it while never rolling back the earlier VRs. Those VRs continue to hold a reference to new_tree acquired via mlxsw_sp_lpm_tree_hold(), leaking the reference count of new_tree. Fix by reinitializing vr inside the error loop with the updated index: vr = &mlxsw_sp->router->vrs[i]; so that the loop correctly iterates over all VRs that were actually replaced. Cc: stable@vger.kernel.org Fixes: fc922bb0dd94 ("mlxsw: spectrum_router: Use one LPM tree for all virtual routers") Signed-off-by: Wentao Liang Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260609084730.215732-1-vulab@iscas.ac.cn Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 7bd87d0547d8a..3d6fdbab05e01 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -1018,6 +1018,7 @@ static int mlxsw_sp_vrs_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp, err_tree_replace: for (i--; i >= 0; i--) { + vr = &mlxsw_sp->router->vrs[i]; if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, new_id)) continue; mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp, From 8062ff9d366c4bc4ae775e14eac9a769f20c60dd Mon Sep 17 00:00:00 2001 From: Zhang Tianci Date: Thu, 26 Feb 2026 19:55:50 +0800 Subject: [PATCH 2193/2758] vduse: Fix race in vduse_dev_msg_sync and vduse_dev_read_iter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit ae9c13b6fd79087cc5a216ee1649b6f012c2a238 upstream. There is one race case in vduse_dev_msg_sync and vduse_dev_read_iter: vduse_dev_read_iter(): lock(msg_lock); dequeue_msg(send_list); unlock(msg_lock); vduse_dev_msg_sync(): wait_timeout() finish lock(msg_lock); check msg->complete is false list_del(msg); <- double list_del() crash! To fix this case, we shall ensure vduse_msg is on send_list or recv_list outside the msg_lock critical section. Fixes: c8a6153b6c59 ("vduse: Introduce VDUSE - vDPA Device in Userspace") Cc: stable@vger.kernel.org Signed-off-by: Zhang Tianci Reviewed-by: Xie Yongji Acked-by: Jason Wang Acked-by: Eugenio Pérez Acked-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Message-ID: <20260226115550.1814-3-zhangtianci.1997@bytedance.com> Signed-off-by: Greg Kroah-Hartman --- drivers/vdpa/vdpa_user/vduse_dev.c | 37 ++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/drivers/vdpa/vdpa_user/vduse_dev.c b/drivers/vdpa/vdpa_user/vduse_dev.c index 1367d14e5105e..0fb14e4861ada 100644 --- a/drivers/vdpa/vdpa_user/vduse_dev.c +++ b/drivers/vdpa/vdpa_user/vduse_dev.c @@ -364,6 +364,7 @@ static ssize_t vduse_dev_read_iter(struct kiocb *iocb, struct iov_iter *to) struct file *file = iocb->ki_filp; struct vduse_dev *dev = file->private_data; struct vduse_dev_msg *msg; + struct vduse_dev_request req; int size = sizeof(struct vduse_dev_request); ssize_t ret; @@ -375,12 +376,11 @@ static ssize_t vduse_dev_read_iter(struct kiocb *iocb, struct iov_iter *to) msg = vduse_dequeue_msg(&dev->send_list); if (msg) break; + spin_unlock(&dev->msg_lock); - ret = -EAGAIN; if (file->f_flags & O_NONBLOCK) - goto unlock; + return -EAGAIN; - spin_unlock(&dev->msg_lock); ret = wait_event_interruptible_exclusive(dev->waitq, !list_empty(&dev->send_list)); if (ret) @@ -388,17 +388,34 @@ static ssize_t vduse_dev_read_iter(struct kiocb *iocb, struct iov_iter *to) spin_lock(&dev->msg_lock); } + + memcpy(&req, &msg->req, sizeof(req)); + /* + * We must ensure vduse_msg is on send_list or recv_list before unlock + * dev->msg_lock. Because vduse_dev_msg_sync() may be timeout when we + * copy data to userspace, and will call list_del() for this msg. + */ + vduse_enqueue_msg(&dev->recv_list, msg); spin_unlock(&dev->msg_lock); - ret = copy_to_iter(&msg->req, size, to); - spin_lock(&dev->msg_lock); + + ret = copy_to_iter(&req, size, to); if (ret != size) { + /* + * Roll back: move msg back to send_list if still pending. + * + * NOTE: + * vduse_find_msg() must use req.request_id instead of `msg`. + * A malicious userspace may reply to this request, and wake up + * the caller, after which `msg` will have already been freed. + * And here vduse_find_msg() will return NULL then do nothing. + */ + spin_lock(&dev->msg_lock); + msg = vduse_find_msg(&dev->recv_list, req.request_id); + if (msg) + vduse_enqueue_msg_head(&dev->send_list, msg); + spin_unlock(&dev->msg_lock); ret = -EFAULT; - vduse_enqueue_msg_head(&dev->send_list, msg); - goto unlock; } - vduse_enqueue_msg(&dev->recv_list, msg); -unlock: - spin_unlock(&dev->msg_lock); return ret; } From 00335df9da2011e095f846d645cc2e9fd2907659 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Fri, 30 Jan 2026 13:07:50 +0800 Subject: [PATCH 2194/2758] VDUSE: avoid leaking information to userspace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 9c1523803445ee0348f62b77793266dd981596e0 upstream. The bounceing is not necessarily page aligned, so current VDUSE can leak kernel information through mapping bounce pages to userspace. Allocate bounce pages with __GFP_ZERO to avoid leaking information to userspace. Fixes: 8c773d53fb7b ("vduse: Implement an MMU-based software IOTLB") Cc: stable@vger.kernel.org Signed-off-by: Jason Wang Reviewed-by: Xie Yongji Reviewed-by: Eugenio Pérez Signed-off-by: Michael S. Tsirkin Message-ID: <20260130050750.4050-1-jasowang@redhat.com> Signed-off-by: Greg Kroah-Hartman --- drivers/vdpa/vdpa_user/iova_domain.c | 2 +- drivers/vdpa/vdpa_user/vduse_dev.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/vdpa/vdpa_user/iova_domain.c b/drivers/vdpa/vdpa_user/iova_domain.c index 806cec32c4bc5..4dc76c0d0d13d 100644 --- a/drivers/vdpa/vdpa_user/iova_domain.c +++ b/drivers/vdpa/vdpa_user/iova_domain.c @@ -124,7 +124,7 @@ static int vduse_domain_map_bounce_page(struct vduse_iova_domain *domain, if (!map->bounce_page) { head_map = &domain->bounce_maps[(iova & PAGE_MASK) >> BOUNCE_MAP_SHIFT]; if (!head_map->bounce_page) { - tmp_page = alloc_page(GFP_ATOMIC); + tmp_page = alloc_page(GFP_ATOMIC | __GFP_ZERO); if (!tmp_page) return -ENOMEM; if (cmpxchg(&head_map->bounce_page, NULL, tmp_page)) diff --git a/drivers/vdpa/vdpa_user/vduse_dev.c b/drivers/vdpa/vdpa_user/vduse_dev.c index 0fb14e4861ada..f15ad425e01fa 100644 --- a/drivers/vdpa/vdpa_user/vduse_dev.c +++ b/drivers/vdpa/vdpa_user/vduse_dev.c @@ -999,7 +999,7 @@ static void *vduse_dev_alloc_coherent(union virtio_map token, size_t size, if (!token.group) return NULL; - addr = alloc_pages_exact(size, flag); + addr = alloc_pages_exact(size, flag | __GFP_ZERO); if (!addr) return NULL; From 038406abde0d0883419ec89425ea941ec8bbef95 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 9 Jun 2026 11:34:53 +0300 Subject: [PATCH 2195/2758] ASoC: SOF: ipc4-control: Fix TOCTOU in sof_ipc4_bytes_put commit 3ad673e7139cf214afd24321a829aad6575f4163 upstream. In sof_ipc4_bytes_put(), the copy size is derived from the old data->size in the buffer rather than the incoming new data's size field from ucontrol. If the new data has a different size, the copy uses the wrong length: it may truncate valid data or copy stale bytes. Fix by validating and using the incoming data's sof_abi_hdr.size from ucontrol before copying. Fixes: a062c8899fed ("ASoC: SOF: ipc4-control: Add support for bytes control get and put") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Reviewed-by: Liam Girdwood Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260609083458.31193-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/sof/ipc4-control.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index 596c3d77a34e1..3631138d530ee 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -562,6 +562,8 @@ static int sof_ipc4_bytes_put(struct snd_sof_control *scontrol, struct snd_soc_component *scomp = scontrol->scomp; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_abi_hdr *data = cdata->data; + const struct sof_abi_hdr *new_hdr = + (const struct sof_abi_hdr *)ucontrol->value.bytes.data; size_t size; int ret; @@ -572,15 +574,16 @@ static int sof_ipc4_bytes_put(struct snd_sof_control *scontrol, return -EINVAL; } - /* scontrol->max_size has been verified to be >= sizeof(struct sof_abi_hdr) */ - if (data->size > scontrol->max_size - sizeof(*data)) { + /* Validate the new data's size, not the old one */ + if (new_hdr->size > scontrol->max_size - sizeof(*new_hdr)) { dev_err_ratelimited(scomp->dev, "data size too big %u bytes max is %zu\n", - data->size, scontrol->max_size - sizeof(*data)); + new_hdr->size, + scontrol->max_size - sizeof(*new_hdr)); return -EINVAL; } - size = data->size + sizeof(*data); + size = new_hdr->size + sizeof(*new_hdr); /* copy from kcontrol */ memcpy(data, ucontrol->value.bytes.data, size); From c29f5b4498894aebb2f87b1a29bb320e2f9348f9 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 9 Jun 2026 11:34:54 +0300 Subject: [PATCH 2196/2758] ASoC: SOF: ipc4-control: Validate notification payload size commit 5bdfeccb7fbf6e000fc783cd8412732e67c1ad0c upstream. Validate MODULE_NOTIFICATION payload length before reading bytes/channel data in control update handling. Fixes: 2a28b5240f2b ("ASoC: SOF: ipc4-control: Add support for generic bytes control") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Reviewed-by: Liam Girdwood Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260609083458.31193-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/sof/ipc4-control.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index 3631138d530ee..b603a54492210 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -883,6 +883,16 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) */ if (type == SND_SOC_TPLG_TYPE_BYTES) { struct sof_abi_hdr *data = cdata->data; + size_t source_size = struct_size(msg_data, data, msg_data->num_elems); + + if (source_size > ndata->event_data_size) { + dev_warn(sdev->dev, + "%s: invalid bytes notification size for %s (%zu, %u)\n", + __func__, scontrol->name, source_size, + ndata->event_data_size); + scontrol->comp_data_dirty = true; + goto notify; + } if (msg_data->num_elems > scontrol->max_size - sizeof(*data)) { dev_warn(sdev->dev, @@ -895,6 +905,17 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) scontrol->size = sizeof(*cdata) + sizeof(*data) + data->size; } } else { + size_t source_size = struct_size(msg_data, chanv, msg_data->num_elems); + + if (source_size > ndata->event_data_size) { + dev_warn(sdev->dev, + "%s: invalid channel notification size for %s (%zu, %u)\n", + __func__, scontrol->name, source_size, + ndata->event_data_size); + scontrol->comp_data_dirty = true; + goto notify; + } + for (i = 0; i < msg_data->num_elems; i++) { u32 channel = msg_data->chanv[i].channel; @@ -922,6 +943,8 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) scontrol->comp_data_dirty = true; } +notify: + /* * Look up the ALSA kcontrol of the scontrol to be able to send a * notification to user space From 312c7d2ebe696da3f885eee77d52297664e57c53 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 9 Jun 2026 11:34:55 +0300 Subject: [PATCH 2197/2758] ASoC: SOF: ipc3-control: Use overflow checks in control_update size calc commit 8791977d7289f6e9d2b014f60a5455f053a7bc04 upstream. In sof_ipc3_control_update(), the expected_size calculation uses firmware-provided cdata->num_elems in arithmetic that could overflow on 32-bit platforms, wrapping to a small value. This would allow the cdata->rhdr.hdr.size comparison to pass with mismatched sizes, potentially leading to out-of-bounds access in snd_sof_update_control. Use check_mul_overflow() and check_add_overflow() to detect and reject overflowed size calculations. Fixes: 10f461d79c2d ("ASoC: SOF: Add IPC3 topology control ops") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Reviewed-by: Liam Girdwood Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260609083458.31193-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/sof/ipc3-control.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index 2b1befad6d5c0..23b0ae3ad4146 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -626,16 +626,28 @@ static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_ return; } - expected_size = sizeof(struct sof_ipc_ctrl_data); switch (cdata->type) { case SOF_CTRL_TYPE_VALUE_CHAN_GET: case SOF_CTRL_TYPE_VALUE_CHAN_SET: - expected_size += cdata->num_elems * - sizeof(struct sof_ipc_ctrl_value_chan); + if (check_mul_overflow((size_t)cdata->num_elems, + sizeof(struct sof_ipc_ctrl_value_chan), + &expected_size)) + return; + if (check_add_overflow(expected_size, + sizeof(struct sof_ipc_ctrl_data), + &expected_size)) + return; break; case SOF_CTRL_TYPE_DATA_GET: case SOF_CTRL_TYPE_DATA_SET: - expected_size += cdata->num_elems + sizeof(struct sof_abi_hdr); + if (check_add_overflow((size_t)cdata->num_elems, + sizeof(struct sof_abi_hdr), + &expected_size)) + return; + if (check_add_overflow(expected_size, + sizeof(struct sof_ipc_ctrl_data), + &expected_size)) + return; break; default: return; From 92f90917413bdd6078fefff6f6c83a07bf870b04 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 9 Jun 2026 11:34:57 +0300 Subject: [PATCH 2198/2758] ASoC: SOF: ipc3-control: Fix TOCTOU in bytes_put and bytes_get commit 1f97760417b5faa60e9642fd0ed61eb17d0b1b39 upstream. In sof_ipc3_bytes_put(), the size used for the memcpy is derived from the old data->size already in the buffer, not the incoming new data's size field. If the new data has a different size, the copy length is wrong: it may truncate valid data or copy stale bytes. Similarly, sof_ipc3_bytes_get() checks data->size against max_size without accounting for the sizeof(struct sof_ipc_ctrl_data) offset of the flex array within the allocation. Fix bytes_put to validate and use the incoming data's sof_abi_hdr.size from ucontrol before copying. Fix bytes_get to subtract sizeof(*cdata) from the bounds check to match the actual available space. Fixes: 544ac8858f24 ("ASoC: SOF: Add bytes_get/put control IPC ops for IPC3") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Reviewed-by: Liam Girdwood Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260609083458.31193-6-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/sof/ipc3-control.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index 23b0ae3ad4146..e8654edab90bc 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -315,10 +315,13 @@ static int sof_ipc3_bytes_get(struct snd_sof_control *scontrol, } /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ - if (data->size > scontrol->max_size - sizeof(*data)) { + if (data->size > scontrol->max_size - sizeof(*cdata) - + sizeof(*data)) { dev_err_ratelimited(scomp->dev, "%u bytes of control data is invalid, max is %zu\n", - data->size, scontrol->max_size - sizeof(*data)); + data->size, + scontrol->max_size - sizeof(*cdata) - + sizeof(*data)); return -EINVAL; } @@ -336,6 +339,8 @@ static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol, struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; struct snd_soc_component *scomp = scontrol->scomp; struct sof_abi_hdr *data = cdata->data; + const struct sof_abi_hdr *new_hdr = + (const struct sof_abi_hdr *)ucontrol->value.bytes.data; size_t size; if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) { @@ -344,14 +349,18 @@ static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol, return -EINVAL; } - /* scontrol->max_size has been verified to be >= sizeof(struct sof_abi_hdr) */ - if (data->size > scontrol->max_size - sizeof(*data)) { - dev_err_ratelimited(scomp->dev, "data size too big %u bytes max is %zu\n", - data->size, scontrol->max_size - sizeof(*data)); + /* Validate the new data's size, not the old one */ + if (new_hdr->size > scontrol->max_size - sizeof(*cdata) - + sizeof(*new_hdr)) { + dev_err_ratelimited(scomp->dev, + "data size too big %u bytes max is %zu\n", + new_hdr->size, + scontrol->max_size - sizeof(*cdata) - + sizeof(*new_hdr)); return -EINVAL; } - size = data->size + sizeof(*data); + size = new_hdr->size + sizeof(*new_hdr); /* copy from kcontrol */ memcpy(data, ucontrol->value.bytes.data, size); From 201b60c4d15538fcc3c0c2ea9b75dd7d0f58022c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Wed, 3 Jun 2026 14:57:54 -0300 Subject: [PATCH 2199/2758] ASoC: SOF: topology: validate vendor array size before parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 8468dd79cfb2ffbdeaf7c353f63d64941cb8ba05 upstream. sof_parse_token_sets() reads array->size while iterating over topology private data. The loop condition only checks that some data remains, so a malformed topology with a truncated trailing vendor array can make the parser read the size field before a full vendor-array header is available. Validate that the remaining private data contains a complete snd_soc_tplg_vendor_array header before reading array->size. The declared array size check also needs to remain signed. asize is an int, but sizeof(*array) has type size_t, so comparing them directly promotes negative asize values to unsigned and lets them pass the check, as reported in the stable review thread reference below. Cast sizeof(*array) to int when validating the declared array size. This rejects negative, zero and otherwise too-small sizes before the parser dispatches to the tuple-specific code. Link: https://lore.kernel.org/stable/CANiDSCsjR5NHqu_Ui5cOqWdJgFqmYsQ9WR8O7m0WOhngaYXFpw@mail.gmail.com/t/#m9b3be379221e79327cc13fd71009287368ef4f23 Fixes: 215e5fe75881 ("ASoC: SOF: topology: reject invalid vendor array size in token parser") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260603-sof-topology-array-size-signed-v1-1-84f97879a4ef@gmail.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/sof/topology.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 63d582c658915..7336ff18a9402 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -733,10 +733,13 @@ static int sof_parse_token_sets(struct snd_soc_component *scomp, int ret; while (array_size > 0 && total < count * token_instance_num) { + if (array_size < (int)sizeof(*array)) + return -EINVAL; + asize = le32_to_cpu(array->size); /* validate asize */ - if (asize < sizeof(*array)) { + if (asize < (int)sizeof(*array)) { dev_err(scomp->dev, "error: invalid array size 0x%x\n", asize); return -EINVAL; From 6acbbe54215d5f4251593000cff2bf51d6748713 Mon Sep 17 00:00:00 2001 From: Li Xiasong Date: Thu, 11 Jun 2026 21:56:47 +0800 Subject: [PATCH 2200/2758] tipc: restrict socket queue dumps in enqueue tracepoints commit acd7df8d955480a6f6e5bb809da67b1500cc3cf4 upstream. tipc_sk_enqueue() runs with sk->sk_lock.slock held while the socket is owned by user context. The spinlock protects the backlog queue in this path, but it does not serialize against the socket owner consuming or purging sk_receive_queue. KASAN reported: CPU: 14 UID: 0 PID: 1050 Comm: tipc3 Not tainted 7.1.0-rc6+ #126 PREEMPT(lazy) Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014 Call Trace: dump_stack_lvl+0x76/0xa0 lib/dump_stack.c:123 print_report+0xce/0x5b0 mm/kasan/report.c:482 kasan_report+0xc6/0x100 mm/kasan/report.c:597 __asan_report_load4_noabort+0x14/0x30 mm/kasan/report_generic.c:380 tipc_skb_dump+0x1327/0x16f0 net/tipc/trace.c:73 tipc_list_dump+0x208/0x2e0 net/tipc/trace.c:187 tipc_sk_dump+0xaf6/0xd60 net/tipc/socket.c:3996 trace_event_raw_event_tipc_sk_class+0x312/0x5a0 net/tipc/trace.h:188 tipc_sk_rcv+0xb1d/0x1d50 net/tipc/socket.c:2497 tipc_node_xmit+0x1c3/0x1440 net/tipc/node.c:1689 __tipc_sendmsg+0x97a/0x1440 net/tipc/socket.c:1512 tipc_sendmsg+0x52/0x80 net/tipc/socket.c:1400 sock_sendmsg+0x2f6/0x3e0 net/socket.c:825 splice_to_socket+0x7f9/0x1010 fs/splice.c:884 do_splice+0xe21/0x2330 fs/splice.c:936 __do_splice+0x153/0x260 fs/splice.c:1431 __x64_sys_splice+0x150/0x230 fs/splice.c:1616 x64_sys_call+0xeb5/0x2790 arch/x86/entry/syscall_64.c:41 do_syscall_64+0xf3/0x620 arch/x86/entry/syscall_64.c:63 entry_SYSCALL_64_after_hwframe+0x76/0x7e arch/x86/entry/entry_64.S:130 RIP: 0033:0x71624e8aafe2 Code: 08 0f 85 71 3a ff ff 49 89 fb 48 89 f0 48 89 d7 48 89 ce 4c 89 c2 4d 89 ca 4c 8b 44 24 08 4c 8b 4c 24 10 4c 89 5c 24 08 0f 05 66 2e 0f 1f 84 00 00 00 00 00 66 2e 0f 1f 84 00 00 00 00 00 66 RSP: 002b:0000716157ffed68 EFLAGS: 00000246 ORIG_RAX: 0000000000000113 RAX: ffffffffffffffda RBX: 0000716157fff6c0 RCX: 000071624e8aafe2 RDX: 000000000000005f RSI: 0000000000000000 RDI: 0000000000000066 RBP: 0000716157ffed90 R08: 0000000000008000 R09: 0000000000000001 R10: 0000000000000000 R11: 0000000000000246 R12: ffffffffffffff00 R13: 0000000000000021 R14: 0000000000000000 R15: 00007fff89799c40 The TIPC_DUMP_ALL tracepoints in tipc_sk_enqueue() also dump sk_receive_queue and can therefore dereference skbs that the socket owner has already dequeued or freed. Restrict these dumps to TIPC_DUMP_SK_BKLGQ, which matches the queue protected by the held spinlock. Keep the change limited to the enqueue path, where the unsafe queue dump is reachable while the socket is owned by user context. Fixes: 01e661ebfbad ("tipc: add trace_events for tipc socket") Cc: stable@vger.kernel.org Signed-off-by: Li Xiasong Reviewed-by: Tung Nguyen Link: https://patch.msgid.link/20260611135647.3666727-1-lixiasong1@huawei.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/tipc/socket.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index f64f7a35b5c91..490f30899b5a9 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -2455,17 +2455,17 @@ static void tipc_sk_enqueue(struct sk_buff_head *inputq, struct sock *sk, atomic_set(dcnt, 0); lim = rcvbuf_limit(sk, skb) + atomic_read(dcnt); if (likely(!sk_add_backlog(sk, skb, lim))) { - trace_tipc_sk_overlimit1(sk, skb, TIPC_DUMP_ALL, + trace_tipc_sk_overlimit1(sk, skb, TIPC_DUMP_SK_BKLGQ, "bklg & rcvq >90% allocated!"); continue; } - trace_tipc_sk_dump(sk, skb, TIPC_DUMP_ALL, "err_overload!"); + trace_tipc_sk_dump(sk, skb, TIPC_DUMP_SK_BKLGQ, "err_overload!"); /* Overload => reject message back to sender */ onode = tipc_own_addr(sock_net(sk)); sk_drops_inc(sk); if (tipc_msg_reverse(onode, &skb, TIPC_ERR_OVERLOAD)) { - trace_tipc_sk_rej_msg(sk, skb, TIPC_DUMP_ALL, + trace_tipc_sk_rej_msg(sk, skb, TIPC_DUMP_SK_BKLGQ, "@sk_enqueue!"); __skb_queue_tail(xmitq, skb); } From 7f72c285f6d3bf63968a0344beee8ab1b370198b Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Thu, 11 Jun 2026 08:54:55 -0400 Subject: [PATCH 2201/2758] net: qrtr: fix 32-bit integer overflow in qrtr_endpoint_post() commit 20054869770c7df060c5ecee3e8bbf9029c47191 upstream. qrtr_endpoint_post() validates an incoming packet with if (!size || len != ALIGN(size, 4) + hdrlen) goto err; where size comes from the wire. On 32-bit, size_t is 32 bits and ALIGN(size, 4) wraps to 0 for size >= 0xfffffffd, so the check passes and skb_put_data(skb, data + hdrlen, size) writes past the hdrlen-sized skb and oopses the kernel. 64-bit is unaffected. This is the 32-bit residual of ad9d24c9429e2 ("net: qrtr: fix OOB Read in qrtr_endpoint_post"), which fixed only the 64-bit case. Reject any size that cannot fit the buffer before the ALIGN. Fixes: ad9d24c9429e2 ("net: qrtr: fix OOB Read in qrtr_endpoint_post") Cc: stable@vger.kernel.org Signed-off-by: Michael Bommarito Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260611125455.2352279-1-michael.bommarito@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/qrtr/af_qrtr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c index db823177e6361..2288159f5b1bb 100644 --- a/net/qrtr/af_qrtr.c +++ b/net/qrtr/af_qrtr.c @@ -496,7 +496,7 @@ int qrtr_endpoint_post(struct qrtr_endpoint *ep, const void *data, size_t len) if (cb->dst_port == QRTR_PORT_CTRL_LEGACY) cb->dst_port = QRTR_PORT_CTRL; - if (!size || len != ALIGN(size, 4) + hdrlen) + if (!size || size > len || len != ALIGN(size, 4) + hdrlen) goto err; if ((cb->type == QRTR_TYPE_NEW_SERVER || From 806b7b6edc8446e8b94b706807a7090a14d47b5c Mon Sep 17 00:00:00 2001 From: Zhengchuan Liang Date: Tue, 9 Jun 2026 16:34:37 +0800 Subject: [PATCH 2202/2758] net: atm: reject out-of-range traffic classes in QoS validation commit cdf19f380e46192e7084be559638aab1f6ed86a2 upstream. Reject ATM traffic classes above ATM_ANYCLASS in check_tp(). SO_ATMQOS stores the supplied QoS after check_qos() succeeds, so accepting larger values leaves invalid traffic_class values in vcc->qos. That bad state later reaches pvc_info(), which indexes class_name[] with vcc->qos.{rx,tp}.traffic_class. Values above ATM_ANYCLASS cause an out-of-bounds read when /proc/net/atm/pvc is read. Tighten the existing QoS validation so invalid traffic_class values are rejected at the point where user supplied QoS is accepted. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Xin Liu Signed-off-by: Zhengchuan Liang Signed-off-by: Ren Wei Reviewed-by: Simon Horman Link: https://patch.msgid.link/58f02c6f73d9818fd5d2022e1116759fdde6116b.1780965530.git.zcliangcn@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/atm/common.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/atm/common.c b/net/atm/common.c index fe77f51f6ce18..6eb78c34c2843 100644 --- a/net/atm/common.c +++ b/net/atm/common.c @@ -720,6 +720,8 @@ static int atm_change_qos(struct atm_vcc *vcc, struct atm_qos *qos) static int check_tp(const struct atm_trafprm *tp) { /* @@@ Should be merged with adjust_tp */ + if (tp->traffic_class > ATM_ANYCLASS) + return -EINVAL; if (!tp->traffic_class || tp->traffic_class == ATM_ANYCLASS) return 0; if (tp->traffic_class != ATM_UBR && !tp->min_pcr && !tp->pcr && From 1ffc164c474473a162a0c20e21673823324a035f Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Fri, 12 Jun 2026 00:00:13 +0800 Subject: [PATCH 2203/2758] octeontx2-pf: clear stale mailbox IRQ state before request_irq() commit f918554fb7246e89b98ef90abe80801f038258b3 upstream. otx2_register_mbox_intr() currently installs the PF mailbox IRQ handler before clearing stale mailbox interrupt state. The function itself then comments that the local interrupt bits must be cleared first to avoid spurious interrupts, but that clear happens only after request_irq() has already exposed the handler to irq delivery. A running system can reach this during PF mailbox interrupt registration while stale or latched RVU_PF_INT state is still present. If delivery happens in the request_irq()-to-clear window, otx2_pfaf_mbox_intr_handler() can run before local quiesce and touch the same pf->mbox and pf->mbox_wq carrier that probe and teardown later reuse or destroy. Move the stale mailbox interrupt clear ahead of request_irq(), but keep interrupt enabling after the handler is installed. This closes the pre-clear early-IRQ window without creating a new enable-before-handler window. Fixes: 5a6d7c9daef3 ("octeontx2-pf: Mailbox communication with AF") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Reviewed-by: Simon Horman Reviewed-by: Ratheesh Kannoth Link: https://patch.msgid.link/20260611160014.3202224-2-runyu.xiao@seu.edu.cn Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- .../ethernet/marvell/octeontx2/nic/otx2_pf.c | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index 543796b636524..e2505631d215c 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -1119,9 +1119,16 @@ int otx2_register_mbox_intr(struct otx2_nic *pf, bool probe_af) { struct otx2_hw *hw = &pf->hw; struct msg_req *req; + u64 mbox_int_mask; char *irq_name; int err; + mbox_int_mask = !is_cn20k(pf->pdev) ? BIT_ULL(0) : + BIT_ULL(0) | BIT_ULL(1); + + /* Clear stale mailbox interrupt state before installing the handler. */ + otx2_write64(pf, RVU_PF_INT, mbox_int_mask); + /* Register mailbox interrupt handler */ if (!is_cn20k(pf->pdev)) { irq_name = &hw->irq_name[RVU_PF_INT_VEC_AFPF_MBOX * NAME_SIZE]; @@ -1147,17 +1154,8 @@ int otx2_register_mbox_intr(struct otx2_nic *pf, bool probe_af) return err; } - /* Enable mailbox interrupt for msgs coming from AF. - * First clear to avoid spurious interrupts, if any. - */ - if (!is_cn20k(pf->pdev)) { - otx2_write64(pf, RVU_PF_INT, BIT_ULL(0)); - otx2_write64(pf, RVU_PF_INT_ENA_W1S, BIT_ULL(0)); - } else { - otx2_write64(pf, RVU_PF_INT, BIT_ULL(0) | BIT_ULL(1)); - otx2_write64(pf, RVU_PF_INT_ENA_W1S, BIT_ULL(0) | - BIT_ULL(1)); - } + /* Enable mailbox interrupt for msgs coming from AF. */ + otx2_write64(pf, RVU_PF_INT_ENA_W1S, mbox_int_mask); if (!probe_af) return 0; From 463d417a905dfc671d8a1b59e1f45780384334ea Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Fri, 12 Jun 2026 00:00:14 +0800 Subject: [PATCH 2204/2758] octeontx2-vf: clear stale mailbox IRQ state before request_irq() commit 0b352f04b9be2c83c0240aa6dae7257fefa90464 upstream. otx2vf_register_mbox_intr() currently installs the VF mailbox IRQ handler before clearing stale mailbox interrupt state. The code then says that local interrupt bits should be cleared first to avoid spurious interrupts, but that clear still happens only after request_irq() has already made the handler reachable. A running system can reach this during VF mailbox interrupt registration while stale or latched RVU_VF_INT state is still present. If delivery happens in the request_irq()-to-clear window, otx2vf_vfaf_mbox_intr_handler() can run before local quiesce and touch the same vf->mbox and vf->mbox_wq carrier that probe and teardown later reuse or destroy. Move the stale mailbox interrupt clear ahead of request_irq(), but keep interrupt enabling after the handler is installed. This closes the pre-clear early-IRQ window without creating a new enable-before-handler window. Fixes: 3184fb5ba96e ("octeontx2-vf: Virtual function driver support") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Reviewed-by: Simon Horman Reviewed-by: Ratheesh Kannoth Link: https://patch.msgid.link/20260611160014.3202224-3-runyu.xiao@seu.edu.cn Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- .../ethernet/marvell/octeontx2/nic/otx2_vf.c | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c index f4fdbfba86676..b022f52c68450 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c @@ -251,9 +251,17 @@ static int otx2vf_register_mbox_intr(struct otx2_nic *vf, bool probe_pf) { struct otx2_hw *hw = &vf->hw; struct msg_req *req; + u64 mbox_int_mask; char *irq_name; int err; + mbox_int_mask = !is_cn20k(vf->pdev) ? BIT_ULL(0) : + BIT_ULL(0) | BIT_ULL(1) | + BIT_ULL(2) | BIT_ULL(3); + + /* Clear stale mailbox interrupt state before installing the handler. */ + otx2_write64(vf, RVU_VF_INT, mbox_int_mask); + /* Register mailbox interrupt handler */ irq_name = &hw->irq_name[RVU_VF_INT_VEC_MBOX * NAME_SIZE]; snprintf(irq_name, NAME_SIZE, "RVUVF%d AFVF Mbox", ((vf->pcifunc & @@ -274,18 +282,8 @@ static int otx2vf_register_mbox_intr(struct otx2_nic *vf, bool probe_pf) return err; } - /* Enable mailbox interrupt for msgs coming from PF. - * First clear to avoid spurious interrupts, if any. - */ - if (!is_cn20k(vf->pdev)) { - otx2_write64(vf, RVU_VF_INT, BIT_ULL(0)); - otx2_write64(vf, RVU_VF_INT_ENA_W1S, BIT_ULL(0)); - } else { - otx2_write64(vf, RVU_VF_INT, BIT_ULL(0) | BIT_ULL(1) | - BIT_ULL(2) | BIT_ULL(3)); - otx2_write64(vf, RVU_VF_INT_ENA_W1S, BIT_ULL(0) | - BIT_ULL(1) | BIT_ULL(2) | BIT_ULL(3)); - } + /* Enable mailbox interrupt for msgs coming from PF. */ + otx2_write64(vf, RVU_VF_INT_ENA_W1S, mbox_int_mask); if (!probe_pf) return 0; From b69ad768cd4a2ef4e07c18492ae85438ed17c7cb Mon Sep 17 00:00:00 2001 From: Yong Wang Date: Thu, 11 Jun 2026 02:37:43 +0800 Subject: [PATCH 2205/2758] net: ife: require ETH_HLEN to be pullable in ife_decode() commit 9406f6012b7343661efb516a11c62d4db2b62f75 upstream. ife decode may return after making only the outer IFE header and metadata pullable. The caller then passes the decapsulated packet to eth_type_trans(), which expects the inner Ethernet header to be accessible from the linear data area. With a malformed IFE frame, the inner Ethernet header may still be shorter than ETH_HLEN in the linear area, which can lead to a crash in the original code. Fix this by extending the pull check in ife_decode() so that the inner Ethernet header is also guaranteed to be pullable before returning. Fixes: ef6980b6becb ("introduce IFE action") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Xin Liu Signed-off-by: Yong Wang Signed-off-by: Ren Wei Link: https://patch.msgid.link/20260610183814.1648888-2-n05ec@lzu.edu.cn Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ife/ife.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ife/ife.c b/net/ife/ife.c index be05b690b9ef2..7a75947a31e31 100644 --- a/net/ife/ife.c +++ b/net/ife/ife.c @@ -79,7 +79,7 @@ void *ife_decode(struct sk_buff *skb, u16 *metalen) if (unlikely(ifehdrln < 2)) return NULL; - if (unlikely(!pskb_may_pull(skb, total_pull))) + if (unlikely(!pskb_may_pull(skb, total_pull + ETH_HLEN))) return NULL; ifehdr = (struct ifeheadr *)(skb->data + skb->dev->hard_header_len); From 9b6a94b187f4d5e97c1e8f7c8648964e43d9ee86 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Wed, 3 Jun 2026 12:06:11 +0100 Subject: [PATCH 2206/2758] arm64: fpsimd: Fix type mismatch in sve_{save,load}_state() commit ae24f6b06e90681ec36b9c21c3f5c09618350f5a upstream. The sve_save_state() and sve_load_state() functions take a 32-bit int argument that describes whether to save/restore the FFR. Their assembly implementations consume the entire 64-bit register containing this 32-bit value, and will attempt to save/restore the FFR if any bit of that 64-bit register is non-zero. Per the AAPCS64 parameter passing rules, the callee is responsible for any necessary widening, and the upper 32-bits are permitted to contain arbitrary values. If the upper 32 bits are non-zero, this could result in an unexpected attempt to save/restore the FFR, and consequently could lead to unexpected traps/undefs/faults. In practice compilers are very unlikely to generate code where the upper 32-bits would be non-zero, but they are permitted to do so. Fix this by only consuming the low 32 bits of the register, and update comments accordingly. The hyp code __sve_save_state() and __sve_restore_state() functions don't have the same latent bug as they override the full 64-bit register containing the argument. Fixes: 9f5848665788 ("arm64/sve: Make access to FFR optional") Signed-off-by: Mark Rutland Cc: Catalin Marinas Cc: Fuad Tabba Cc: James Morse Cc: Marc Zyngier Cc: Mark Brown Cc: Oliver Upton Cc: Vladimir Murzin Cc: Will Deacon Cc: stable@vger.kernel.org Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kernel/entry-fpsimd.S | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm64/kernel/entry-fpsimd.S b/arch/arm64/kernel/entry-fpsimd.S index fb441702495fa..28af4bcd97a50 100644 --- a/arch/arm64/kernel/entry-fpsimd.S +++ b/arch/arm64/kernel/entry-fpsimd.S @@ -38,10 +38,10 @@ SYM_FUNC_END(fpsimd_load_state) * * x0 - pointer to buffer for state * x1 - pointer to storage for FPSR - * x2 - Save FFR if non-zero + * w2 - Save FFR if non-zero */ SYM_FUNC_START(sve_save_state) - sve_save 0, x1, x2, 3 + sve_save 0, x1, w2, 3 ret SYM_FUNC_END(sve_save_state) @@ -50,10 +50,10 @@ SYM_FUNC_END(sve_save_state) * * x0 - pointer to buffer for state * x1 - pointer to storage for FPSR - * x2 - Restore FFR if non-zero + * w2 - Restore FFR if non-zero */ SYM_FUNC_START(sve_load_state) - sve_load 0, x1, x2, 4 + sve_load 0, x1, w2, 4 ret SYM_FUNC_END(sve_load_state) From 7b71b69719eb164c58172258f126f7d272ac81a8 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 28 May 2026 14:03:24 +0200 Subject: [PATCH 2207/2758] arm64: dts: s32g3: Fix SWT8 watchdog address commit 65210e81f7837a871a17237d15e4b1191d5e8771 upstream. Add missing hex annotation to fix the SWT8 watchdog address in 'reg' property, as reported by dtc W=1: s32g3.dtsi:863.27-869.5: Warning (simple_bus_reg): /soc@0/watchdog@40500000: simple-bus unit address format error, expected "269fb20" Lack of hex '0x' meant address would be interpreted as decimal thus completely different value used as this device MMIO. If device was enabled this could lead to corruption of other device address space and broken boot. Cc: stable@vger.kernel.org Fixes: 6db84f042745 ("arm64: dts: s32g3: Add the Software Timer Watchdog (SWT) nodes") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Daniel Lezcano Signed-off-by: Frank Li Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/freescale/s32g3.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/freescale/s32g3.dtsi b/arch/arm64/boot/dts/freescale/s32g3.dtsi index e314f3c7d61d0..7e28dff53a865 100644 --- a/arch/arm64/boot/dts/freescale/s32g3.dtsi +++ b/arch/arm64/boot/dts/freescale/s32g3.dtsi @@ -862,7 +862,7 @@ swt8: watchdog@40500000 { compatible = "nxp,s32g3-swt", "nxp,s32g2-swt"; - reg = <40500000 0x1000>; + reg = <0x40500000 0x1000>; clocks = <&clks 0x3a>, <&clks 0x3b>, <&clks 0x3b>; clock-names = "counter", "module", "register"; status = "disabled"; From 7cc51bb053f6b4bb17db4e10afcef3147566cb47 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 10 Jul 2026 18:04:22 +0200 Subject: [PATCH 2208/2758] arm64: dts: renesas: ironhide: Describe inline ECC carveouts commit 6fa6ee724d8dadf392139e242ac936b5da730c4b upstream. The DBSC5 DRAM controller protects DRAM content using inline ECC. The inline ECC utilizes areas of DRAM for its operation, which are in the DRAM address range, but must not be accessed or modified. Describe the inline ECC carveout areas used by the DBSC5 controller on this hardware as reserved-memory, which must not be accessed. Include DRAM areas which are unprotected by ECC as well, those are parts of the DRAM which directly precede the ECC carveout. In case of high DRAM utilization, unless the inline ECC carveouts are properly reserved, Linux may use and corrupt the memory used by the DBSC5 DRAM controller for inline ECC, which would lead to the system becoming unstable. Fixes: ad142a4ef710 ("arm64: dts: renesas: r8a78000: Add initial Ironhide board support") Cc: stable@vger.kernel.org Signed-off-by: Marek Vasut Tested-by: Geert Uytterhoeven Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260710160450.64967-1-marek.vasut+renesas@mailbox.org Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- .../boot/dts/renesas/r8a78000-ironhide.dts | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts b/arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts index ed027a6c356ed..803ba00809b25 100644 --- a/arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts +++ b/arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts @@ -93,6 +93,47 @@ reg = <0x0 0x8c400000 0x0 0x02000000>; no-map; }; + + /* DRAM controller inline ECC areas */ + ecc@10cccc0000 { + reg = <0x10 0xcccc0000 0x0 0x33340000>; + no-map; + }; + + ecc@12cccc0000 { + reg = <0x12 0xcccc0000 0x0 0x33340000>; + no-map; + }; + + ecc@14cccc0000 { + reg = <0x14 0xcccc0000 0x0 0x33340000>; + no-map; + }; + + ecc@16cccc0000 { + reg = <0x16 0xcccc0000 0x0 0x33340000>; + no-map; + }; + + ecc@18cccc0000 { + reg = <0x18 0xcccc0000 0x0 0x33340000>; + no-map; + }; + + ecc@1a66660000 { + reg = <0x1a 0x66660000 0x0 0x999a0000>; + no-map; + }; + + ecc@1c66660000 { + reg = <0x1c 0x66660000 0x0 0x999a0000>; + no-map; + }; + + ecc@1e66660000 { + reg = <0x1e 0x66660000 0x0 0x999a0000>; + no-map; + }; }; }; From 17b7ab1d26b3e52567eca2c45f829a1769d8b4bd Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Thu, 5 Mar 2026 13:06:16 -0500 Subject: [PATCH 2209/2758] ARM: dts: imx6ul-var-som: fix warning for non-existent dc-supply property commit 0372cc5776e7fd5570884aed0c1e9a8a546cad66 upstream. The dc-supply property is non-existent in Linux now, nor when this DTS file was created when importing it from Variscite own kernel. Therefore remove it to fix this warning: imx6ul-var-som-concerto.dtb: cpu@0 (arm,cortex-a7): Unevaluated properties are not allowed ('dc-supply' was unexpected) from schema $id: http://devicetree.org/schemas/arm/cpus.yaml Fixes: 9d6a67d9c7a9 ("ARM: dts: imx6ul: Add Variscite VAR-SOM-MX6UL SoM support") Cc: stable@kernel.org Signed-off-by: Hugo Villeneuve Signed-off-by: Frank Li Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/nxp/imx/imx6ul-var-som.dtsi | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/arm/boot/dts/nxp/imx/imx6ul-var-som.dtsi b/arch/arm/boot/dts/nxp/imx/imx6ul-var-som.dtsi index 4e536e0252def..3fb5e2fb68777 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ul-var-som.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6ul-var-som.dtsi @@ -45,10 +45,6 @@ assigned-clock-rates = <786432000>; }; -&cpu0 { - dc-supply = <®_gpio_dvfs>; -}; - &fec1 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_enet1>, <&pinctrl_enet1_gpio>, <&pinctrl_enet1_mdio>; From b5ab9ada87e83a36f036e1bb7d791c9736f28ef0 Mon Sep 17 00:00:00 2001 From: Nickolay Goppen Date: Wed, 29 Apr 2026 12:30:11 +0300 Subject: [PATCH 2210/2758] arm64: dts: qcom: sdm630: describe adsp_mem region properly commit ce414263e9ebe5080381a50cbdf9065c29816202 upstream. Downstream [1] this region is marked as shared, reusable and dynamic so describe it that way. [1]: https://github.com/xiaomi-sdm660/android_kernel_xiaomi_sdm660/blob/11-EAS/arch/arm/boot/dts/qcom/sdm660.dtsi#L448 Fixes: b190fb010664 ("arm64: dts: qcom: sdm630: Add sdm630 dts file") Cc: stable@vger.kernel.org Reviewed-by: Ekansh Gupta Reviewed-by: Dmitry Baryshkov Signed-off-by: Nickolay Goppen Link: https://lore.kernel.org/r/20260429-qcom-sdm660-cdsp-adsp-fastrpc-dts-fix-v5-4-16bc82e622ad@mainlining.org Signed-off-by: Bjorn Andersson Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/qcom/sdm630.dtsi | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/sdm630.dtsi b/arch/arm64/boot/dts/qcom/sdm630.dtsi index 4b47efdb57b21..252c301f0156c 100644 --- a/arch/arm64/boot/dts/qcom/sdm630.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm630.dtsi @@ -494,9 +494,12 @@ no-map; }; - adsp_mem: adsp-region@f6000000 { - reg = <0x0 0xf6000000 0x0 0x800000>; - no-map; + adsp_mem: adsp-region { + compatible = "shared-dma-pool"; + alloc-ranges = <0x0 0x80000000 0x0 0x80000000>; + alignment = <0x0 0x400000>; + size = <0x0 0x800000>; + reusable; }; qseecom_mem: qseecom-region@f6800000 { From 12cabe872172ed3a295351878b6abc34931c32a8 Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Tue, 21 Apr 2026 11:45:06 +0200 Subject: [PATCH 2211/2758] arm64: dts: rockchip: fix Ethernet PHY not found on PX30 Ringneck commit ae653cb854f36d1555681ce70ca3d80d0ec73516 upstream. When not passing the PHY ID with an ethernet-phy-idX.Y compatible property, the MDIO bus will attempt to auto-detect the PHY by reading its registers and then probing the appropriate driver. For this to work, the PHY needs to be in a working state. Unfortunately, the net subsystem doesn't control the PHY reset GPIO when attempting to auto-detect the PHY. This means the PHY needs to be in a working state when entering the Linux kernel. This historically has been the case for this device, but only because the bootloader was taking care of initializing the Ethernet controller even when not using it. We're attempting to support the removal of the network stack in the bootloader, which means the Linux kernel will be entered with the PHY still in reset and now Ethernet doesn't work anymore. The devices in the field only ever had a TI DP83825, so let's simply bypass the auto-detection mechanism entirely by passing the appropriate PHY IDs via the compatible. Note that this is only an issue since commit e463625af7f9 ("arm64: dts: rockchip: move reset to dedicated eth-phy node on ringneck") as before that commit the reset was done by the MAC controller before starting the MDIO auto-detection mechanism, via the snps,reset-* properties. Cc: stable@vger.kernel.org Fixes: e463625af7f9 ("arm64: dts: rockchip: move reset to dedicated eth-phy node on ringneck") Signed-off-by: Quentin Schulz Link: https://patch.msgid.link/20260421-px30-eth-phy-v2-2-68c375b120fd@cherry.de Signed-off-by: Heiko Stuebner Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi b/arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi index 4203b335a2633..973b4c5880e24 100644 --- a/arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi +++ b/arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi @@ -344,7 +344,7 @@ &mdio { dp83825: ethernet-phy@0 { - compatible = "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id2000.a140"; reg = <0x0>; pinctrl-names = "default"; pinctrl-0 = <&phy_rst>; From d09c701a531c198809f35a4fdf23749d9e686835 Mon Sep 17 00:00:00 2001 From: David Jander Date: Wed, 18 Mar 2026 11:51:18 +0100 Subject: [PATCH 2212/2758] ARM: dts: stm32: stm32mp15x-mecio1-io: Fix ADC sampling times commit 8407e611faf80ce790a393addf7b44cc595742af upstream. Increase the minimum ADC sample times for all configured channels on ADC1 and ADC2 to ensure measurement accuracy meets specifications. The default 5us sample time is insufficient for the internal sampling capacitor to fully charge. Increase the default time to 20us to relax the input impedance requirements. Additionally, the phint0_ain and phint1_ain channels require a much longer sampling period due to their specific circuit design. Increase their sample times to 200us. Remove stale comments regarding clock cycles that no longer match the updated timings. Fixes: 8267753c891c ("ARM: dts: stm32: Add MECIO1 and MECT1S board variants") Co-developed-by: Oleksij Rempel Signed-off-by: David Jander Signed-off-by: Oleksij Rempel Cc: Link: https://lore.kernel.org/r/20260318105123.819807-3-o.rempel@pengutronix.de Signed-off-by: Alexandre Torgue Signed-off-by: Greg Kroah-Hartman --- .../arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi b/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi index 915ba2526f451..fe7880a34e684 100644 --- a/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi +++ b/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi @@ -104,80 +104,79 @@ channel@0 { reg = <0>; - /* 16.5 ck_cycles sampling time */ - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <20000>; label = "p24v_stp"; }; channel@1 { reg = <1>; - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <20000>; label = "p24v_hpdcm"; }; channel@2 { reg = <2>; - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <20000>; label = "ain0"; }; channel@3 { reg = <3>; - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <20000>; label = "hpdcm1_i2"; }; channel@5 { reg = <5>; - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <20000>; label = "hpout1_i"; }; channel@6 { reg = <6>; - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <20000>; label = "ain1"; }; channel@9 { reg = <9>; - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <20000>; label = "hpout0_i"; }; channel@10 { reg = <10>; - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <200000>; label = "phint0_ain"; }; channel@13 { reg = <13>; - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <200000>; label = "phint1_ain"; }; channel@15 { reg = <15>; - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <20000>; label = "hpdcm0_i1"; }; channel@16 { reg = <16>; - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <20000>; label = "lsin"; }; channel@18 { reg = <18>; - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <20000>; label = "hpdcm0_i2"; }; channel@19 { reg = <19>; - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <20000>; label = "hpdcm1_i1"; }; }; @@ -187,14 +186,13 @@ channel@2 { reg = <2>; - /* 16.5 ck_cycles sampling time */ - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <20000>; label = "ain2"; }; channel@6 { reg = <6>; - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <20000>; label = "ain3"; }; }; From ba13b141ddb58cc1eaefff856d767334f3dfc47c Mon Sep 17 00:00:00 2001 From: David Jander Date: Wed, 18 Mar 2026 11:51:19 +0100 Subject: [PATCH 2213/2758] ARM: dts: stm32: stm32mp15x-mecio1-io: Move divergent mecio1 ADC channels to board files commit 70f1d8fcbd121a40f51b6c846d41e8cbb38ba210 upstream. Move the divergent adc1 channel definitions out of the common mecio1-io.dtsi file and into the specific Revision 0 and Revision 1 board files. The original common file contained incorrect schematic labels for the Revision 0 hardware (e.g., labeling ana0 as p24v_hpdcm instead of ain_aux0) and failed to account for physical signal routing changes between the board revisions. Retain only the strictly shared channels in the common include file. Map the correct channels and schematic labels directly within stm32mp151c-mecio1r0.dts and stm32mp153c-mecio1r1.dts. Crucially, ensure that the required 200us sample time follows the phint1_ain signal to its new physical location on channel 3 for the Revision 1 hardware. Fixes: 8267753c891c ("ARM: dts: stm32: Add MECIO1 and MECT1S board variants") Co-developed-by: Oleksij Rempel Signed-off-by: David Jander Signed-off-by: Oleksij Rempel Cc: Link: https://lore.kernel.org/r/20260318105123.819807-4-o.rempel@pengutronix.de Signed-off-by: Alexandre Torgue Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts | 50 +++++++++++++++++++ arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts | 50 +++++++++++++++++++ .../arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi | 50 +------------------ 3 files changed, 101 insertions(+), 49 deletions(-) diff --git a/arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts b/arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts index a5ea1431c3991..4e795ad42928b 100644 --- a/arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts +++ b/arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts @@ -36,6 +36,56 @@ }; }; +&adc1 { + channel@0 { + reg = <0>; + st,min-sample-time-ns = <20000>; + label = "ain_aux0"; + }; + + channel@1 { + reg = <1>; + st,min-sample-time-ns = <20000>; + label = "ain_aux1"; + }; + + channel@3 { + reg = <3>; + st,min-sample-time-ns = <20000>; + label = "hpdcm1_i2"; + }; + + channel@5 { + reg = <5>; + st,min-sample-time-ns = <20000>; + label = "pout1_i"; + }; + + channel@9 { + reg = <9>; + st,min-sample-time-ns = <20000>; + label = "pout0_i"; + }; + + channel@13 { + reg = <13>; + st,min-sample-time-ns = <200000>; + label = "phint1_ain"; + }; + + channel@15 { + reg = <15>; + st,min-sample-time-ns = <20000>; + label = "hpdcm0_i1"; + }; + + channel@18 { + reg = <18>; + st,min-sample-time-ns = <20000>; + label = "hpdcm0_i2"; + }; +}; + &clk_hse { clock-frequency = <25000000>; }; diff --git a/arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts b/arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts index 16b814c19350c..d32816093e47d 100644 --- a/arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts +++ b/arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts @@ -36,6 +36,56 @@ }; }; +&adc1 { + channel@0 { + reg = <0>; + st,min-sample-time-ns = <20000>; + label = "p24v_hpdcm"; + }; + + channel@1 { + reg = <1>; + st,min-sample-time-ns = <20000>; + label = "p24v_stp"; + }; + + channel@3 { + reg = <3>; + st,min-sample-time-ns = <200000>; + label = "phint1_ain"; + }; + + channel@5 { + reg = <5>; + st,min-sample-time-ns = <20000>; + label = "hpout1_i"; + }; + + channel@9 { + reg = <9>; + st,min-sample-time-ns = <20000>; + label = "hpout0_i"; + }; + + channel@13 { + reg = <13>; + st,min-sample-time-ns = <20000>; + label = "hpdcm0_i2"; + }; + + channel@15 { + reg = <15>; + st,min-sample-time-ns = <20000>; + label = "hpdcm1_i2"; + }; + + channel@18 { + reg = <18>; + st,min-sample-time-ns = <20000>; + label = "hpdcm0_i1"; + }; +}; + &clk_hse { clock-frequency = <24000000>; }; diff --git a/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi b/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi index fe7880a34e684..f1d8e4324a49c 100644 --- a/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi +++ b/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi @@ -90,7 +90,7 @@ }; &adc { - /* ANA0, ANA1 are dedicated pins and don't need pinctrl: only in6. */ + /* ANA0, ANA1 are dedicated pins and don't need pinctrl. */ pinctrl-0 = <&adc12_pins_mecsbc>; pinctrl-names = "default"; vdd-supply = <&v3v3>; @@ -102,78 +102,30 @@ &adc1 { status = "okay"; - channel@0 { - reg = <0>; - st,min-sample-time-ns = <20000>; - label = "p24v_stp"; - }; - - channel@1 { - reg = <1>; - st,min-sample-time-ns = <20000>; - label = "p24v_hpdcm"; - }; - channel@2 { reg = <2>; st,min-sample-time-ns = <20000>; label = "ain0"; }; - channel@3 { - reg = <3>; - st,min-sample-time-ns = <20000>; - label = "hpdcm1_i2"; - }; - - channel@5 { - reg = <5>; - st,min-sample-time-ns = <20000>; - label = "hpout1_i"; - }; - channel@6 { reg = <6>; st,min-sample-time-ns = <20000>; label = "ain1"; }; - channel@9 { - reg = <9>; - st,min-sample-time-ns = <20000>; - label = "hpout0_i"; - }; - channel@10 { reg = <10>; st,min-sample-time-ns = <200000>; label = "phint0_ain"; }; - channel@13 { - reg = <13>; - st,min-sample-time-ns = <200000>; - label = "phint1_ain"; - }; - - channel@15 { - reg = <15>; - st,min-sample-time-ns = <20000>; - label = "hpdcm0_i1"; - }; - channel@16 { reg = <16>; st,min-sample-time-ns = <20000>; label = "lsin"; }; - channel@18 { - reg = <18>; - st,min-sample-time-ns = <20000>; - label = "hpdcm0_i2"; - }; - channel@19 { reg = <19>; st,min-sample-time-ns = <20000>; From 0623e082e99ab1e7eba999255a183476180fe4c1 Mon Sep 17 00:00:00 2001 From: Judith Mendez Date: Tue, 24 Mar 2026 09:02:47 -0500 Subject: [PATCH 2214/2758] arm64: dts: ti: k3-am62a7-sk: Add bootph-all tag to vqmmc commit d8fe8442366ffd8306575028bda92389d0dfb674 upstream. Add bootph-all property to vqmmc voltage regulator node and its corresponding pinmux node to make it available during all boot phases. This allows to run tuning early in SPL stages of boot. Fixes: 8f023012eb4a ("arm64: dts: ti: k3-am62a: Enable UHS mode support for SD cards") Cc: stable@vger.kernel.org Signed-off-by: Judith Mendez Link: https://patch.msgid.link/20260324140247.1200631-1-jm@ti.com Signed-off-by: Nishanth Menon Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/ti/k3-am62a7-sk.dts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts b/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts index c1e9067b3bdd5..821a9705bb7d4 100644 --- a/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts +++ b/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts @@ -154,6 +154,7 @@ gpios = <&main_gpio0 31 GPIO_ACTIVE_HIGH>; states = <1800000 0x0>, <3300000 0x1>; + bootph-all; }; leds { @@ -407,6 +408,7 @@ pinctrl-single,pins = < AM62AX_IOPAD(0x07c, PIN_OUTPUT, 7) /* (N22) GPMC0_CLK.GPIO0_31 */ >; + bootph-all; }; main_ecap0_pins_default: main-ecap0-default-pins { From 520de5e79ddadaded3cd0a04c6cb19161f0e5259 Mon Sep 17 00:00:00 2001 From: David Jander Date: Wed, 18 Mar 2026 11:51:17 +0100 Subject: [PATCH 2215/2758] ARM: dts: stm32: stm32mp15x-mecio1-io: Enable internal ADC reference commit c84f22405085d91cd5f0c5b967318371c07904ba upstream. Switch the ADC reference supply from the general 3.3V rail to the internal 2.5V VREFBUF regulator. The ADC circuits on this board are designed for the internal 2.5V reference. Without this change, all ADC measurement values are incorrect. Fixes: 8267753c891c ("ARM: dts: stm32: Add MECIO1 and MECT1S board variants") Co-developed-by: Oleksij Rempel Signed-off-by: David Jander Signed-off-by: Oleksij Rempel Cc: Link: https://lore.kernel.org/r/20260318105123.819807-2-o.rempel@pengutronix.de Signed-off-by: Alexandre Torgue Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi b/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi index f1d8e4324a49c..f91b3d1f037b5 100644 --- a/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi +++ b/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi @@ -95,7 +95,7 @@ pinctrl-names = "default"; vdd-supply = <&v3v3>; vdda-supply = <&v3v3>; - vref-supply = <&v3v3>; + vref-supply = <&vrefbuf>; status = "okay"; }; @@ -362,6 +362,13 @@ phy-supply = <&v3v3>; }; +&vrefbuf { + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <2500000>; + vdda-supply = <&v3v3>; + status = "okay"; +}; + &pinctrl { adc12_pins_mecsbc: adc12-ain-mecsbc-0 { pins { From 73e14c8bf53cb853480421b8f6a153395cbae1df Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 13 Apr 2026 11:07:24 +0200 Subject: [PATCH 2216/2758] arm64: dts: imx8ulp-evk: Correct Type-C int GPIO flags commit b4f5c46163b3fe3ec7ed8a76dff8b7e80a776a1f upstream. IRQ_TYPE_xxx flags are not correct in the context of GPIO flags. These are simple defines so they could be used in DTS but they will not have the same meaning: IRQ_TYPE_EDGE_FALLING = 2 = GPIO_SINGLE_ENDED. Correct the Type-C int-gpios to use proper flags, assuming the author of the code wanted similar logical behavior: IRQ_TYPE_EDGE_FALLING => GPIO_ACTIVE_LOW Fixes: c4b4593ecb0b ("arm64: dts: imx8ulp-evk: enable usb nodes and add ptn5150 nodes") Cc: stable@vger.kernel.org Signed-off-by: Krzysztof Kozlowski Signed-off-by: Frank Li Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/freescale/imx8ulp-evk.dts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx8ulp-evk.dts b/arch/arm64/boot/dts/freescale/imx8ulp-evk.dts index 290a49bea2f76..5dea66c1e7aa0 100644 --- a/arch/arm64/boot/dts/freescale/imx8ulp-evk.dts +++ b/arch/arm64/boot/dts/freescale/imx8ulp-evk.dts @@ -166,7 +166,7 @@ ptn5150_1: typec@1d { compatible = "nxp,ptn5150"; reg = <0x1d>; - int-gpios = <&gpiof 3 IRQ_TYPE_EDGE_FALLING>; + int-gpios = <&gpiof 3 GPIO_ACTIVE_LOW>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_typec1>; status = "disabled"; @@ -182,7 +182,7 @@ ptn5150_2: typec@3d { compatible = "nxp,ptn5150"; reg = <0x3d>; - int-gpios = <&gpiof 5 IRQ_TYPE_EDGE_FALLING>; + int-gpios = <&gpiof 5 GPIO_ACTIVE_LOW>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_typec2>; status = "disabled"; From fc58177cca3bce9c77ee3784da65786cb023226c Mon Sep 17 00:00:00 2001 From: David Jander Date: Wed, 18 Mar 2026 11:51:20 +0100 Subject: [PATCH 2217/2758] ARM: dts: stm32: stm32mp15x-mecio1-io: Fix GPIO names typo commit b04ccecb714de913e360f0866c66f38e1606e89b upstream. The reset pins for the LPOUT lines were incorrectly prefixed with "GPOUT" instead of "LPOUT" in the gpio-line-names array. Fix these typos so the pin names consistently match the LPOUT0-4 signals they belong to. Fixes: 8267753c891c ("ARM: dts: stm32: Add MECIO1 and MECT1S board variants") Co-developed-by: Oleksij Rempel Signed-off-by: David Jander Signed-off-by: Oleksij Rempel Cc: Link: https://lore.kernel.org/r/20260318105123.819807-5-o.rempel@pengutronix.de Signed-off-by: Alexandre Torgue Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi b/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi index f91b3d1f037b5..e50e9ae085e81 100644 --- a/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi +++ b/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi @@ -185,14 +185,14 @@ &gpioe { gpio-line-names = "HPOUT0_RESETN", "HPOUT1", "HPOUT1_ALERTN", "", "", "", "HPOUT1_RESETN", - "LPOUT0", "LPOUT0_ALERTN", "GPOUT0_RESETN", - "LPOUT1", "LPOUT1_ALERTN", "GPOUT1_RESETN", - "LPOUT2", "LPOUT2_ALERTN", "GPOUT2_RESETN"; + "LPOUT0", "LPOUT0_ALERTN", "LPOUT0_RESETN", + "LPOUT1", "LPOUT1_ALERTN", "LPOUT1_RESETN", + "LPOUT2", "LPOUT2_ALERTN", "LPOUT2_RESETN"; }; &gpiof { - gpio-line-names = "LPOUT3", "LPOUT3_ALERTN", "GPOUT3_RESETN", - "LPOUT4", "LPOUT4_ALERTN", "GPOUT4_RESETN", + gpio-line-names = "LPOUT3", "LPOUT3_ALERTN", "LPOUT3_RESETN", + "LPOUT4", "LPOUT4_ALERTN", "LPOUT4_RESETN", "", "", "", "", "", "", "", "", "", ""; From e61543c0aa5a9d3904374fd4c718ae6919ddc86d Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Mon, 23 Mar 2026 12:01:12 +0200 Subject: [PATCH 2218/2758] arm64: dts: qcom: hamoa: Fix OPP tables for all DisplayPort controllers commit c17e220946675232d383620ed9cff6685735ec48 upstream. According to internal documentation, the corners specific for each rate from the DP link clock are: - LOWSVS_D1 -> 19.2 MHz - LOWSVS -> 270 MHz - SVS -> 540 MHz (594 MHz in case of DP3) - SVS_L1 -> 594 MHz - NOM -> 810 MHz - NOM_L1 -> 810 MHz - TURBO -> 810 MHz So fix all tables for each of the four controllers according to the documentation, but since DP0 through DP2 have the same entries in their tables, lets drop the DP1 and DP2 and have all of them share the DP0 table instead. However keep a separate table for the DP3 as it is different for the SVS, compared to the rest of the controllers. The 19.2 MHz @ LOWSVS_D1 isn't needed as it's not an actual working frequency and the controller will never select it. So remove it. Cc: stable@vger.kernel.org # v6.9+ Fixes: 1940c25eaa63 ("arm64: dts: qcom: x1e80100: Add display nodes") Suggested-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Signed-off-by: Abel Vesa Link: https://lore.kernel.org/r/20260323-hamoa-fix-dp3-opp-table-v3-1-a823776bd1b0@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/qcom/hamoa.dtsi | 77 +++++------------------------ 1 file changed, 12 insertions(+), 65 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/hamoa.dtsi b/arch/arm64/boot/dts/qcom/hamoa.dtsi index 051dee0764167..4ba751a65142b 100644 --- a/arch/arm64/boot/dts/qcom/hamoa.dtsi +++ b/arch/arm64/boot/dts/qcom/hamoa.dtsi @@ -5744,18 +5744,18 @@ mdss_dp0_opp_table: opp-table { compatible = "operating-points-v2"; - opp-162000000 { - opp-hz = /bits/ 64 <162000000>; - required-opps = <&rpmhpd_opp_low_svs>; - }; - opp-270000000 { opp-hz = /bits/ 64 <270000000>; - required-opps = <&rpmhpd_opp_svs>; + required-opps = <&rpmhpd_opp_low_svs>; }; opp-540000000 { opp-hz = /bits/ 64 <540000000>; + required-opps = <&rpmhpd_opp_svs>; + }; + + opp-594000000 { + opp-hz = /bits/ 64 <594000000>; required-opps = <&rpmhpd_opp_svs_l1>; }; @@ -5796,7 +5796,7 @@ <&usb_1_ss1_qmpphy QMP_USB43DP_DP_VCO_DIV_CLK>, <&usb_1_ss1_qmpphy QMP_USB43DP_DP_VCO_DIV_CLK>; - operating-points-v2 = <&mdss_dp1_opp_table>; + operating-points-v2 = <&mdss_dp0_opp_table>; power-domains = <&rpmhpd RPMHPD_MMCX>; @@ -5829,30 +5829,6 @@ }; }; }; - - mdss_dp1_opp_table: opp-table { - compatible = "operating-points-v2"; - - opp-162000000 { - opp-hz = /bits/ 64 <162000000>; - required-opps = <&rpmhpd_opp_low_svs>; - }; - - opp-270000000 { - opp-hz = /bits/ 64 <270000000>; - required-opps = <&rpmhpd_opp_svs>; - }; - - opp-540000000 { - opp-hz = /bits/ 64 <540000000>; - required-opps = <&rpmhpd_opp_svs_l1>; - }; - - opp-810000000 { - opp-hz = /bits/ 64 <810000000>; - required-opps = <&rpmhpd_opp_nom>; - }; - }; }; mdss_dp2: displayport-controller@ae9a000 { @@ -5885,7 +5861,7 @@ <&usb_1_ss2_qmpphy QMP_USB43DP_DP_VCO_DIV_CLK>, <&usb_1_ss2_qmpphy QMP_USB43DP_DP_VCO_DIV_CLK>; - operating-points-v2 = <&mdss_dp2_opp_table>; + operating-points-v2 = <&mdss_dp0_opp_table>; power-domains = <&rpmhpd RPMHPD_MMCX>; @@ -5917,30 +5893,6 @@ }; }; }; - - mdss_dp2_opp_table: opp-table { - compatible = "operating-points-v2"; - - opp-162000000 { - opp-hz = /bits/ 64 <162000000>; - required-opps = <&rpmhpd_opp_low_svs>; - }; - - opp-270000000 { - opp-hz = /bits/ 64 <270000000>; - required-opps = <&rpmhpd_opp_svs>; - }; - - opp-540000000 { - opp-hz = /bits/ 64 <540000000>; - required-opps = <&rpmhpd_opp_svs_l1>; - }; - - opp-810000000 { - opp-hz = /bits/ 64 <810000000>; - required-opps = <&rpmhpd_opp_nom>; - }; - }; }; mdss_dp3: displayport-controller@aea0000 { @@ -6004,19 +5956,14 @@ mdss_dp3_opp_table: opp-table { compatible = "operating-points-v2"; - opp-162000000 { - opp-hz = /bits/ 64 <162000000>; - required-opps = <&rpmhpd_opp_low_svs>; - }; - opp-270000000 { opp-hz = /bits/ 64 <270000000>; - required-opps = <&rpmhpd_opp_svs>; + required-opps = <&rpmhpd_opp_low_svs>; }; - opp-540000000 { - opp-hz = /bits/ 64 <540000000>; - required-opps = <&rpmhpd_opp_svs_l1>; + opp-594000000 { + opp-hz = /bits/ 64 <594000000>; + required-opps = <&rpmhpd_opp_svs>; }; opp-810000000 { From d735c64a1462d0a270b67ad95f136b529eec94ab Mon Sep 17 00:00:00 2001 From: David Jander Date: Wed, 18 Mar 2026 11:51:21 +0100 Subject: [PATCH 2219/2758] ARM: dts: stm32: stm32mp15x-mecio1-io: Move gpio-line-names to board files commit 4f5069609ac99894c0632d8b8c4c016f85199de9 upstream. Move the gpio-line-names properties out of the common mecio1-io.dtsi file and into the specific board dts files. The pinout originally defined in the common include file belonged to the mecio1r0 (Revision 0) hardware. This is moved 1:1 into the stm32mp151c-mecio1r0.dts file without any modifications. A large number of GPIO pins are swapped on the mecio1r1 (Revision 1) hardware, so a new, board-specific gpio-line-names mapping is added to stm32mp153c-mecio1r1.dts to reflect those hardware changes. Fixes: 8267753c891c ("ARM: dts: stm32: Add MECIO1 and MECT1S board variants") Co-developed-by: Oleksij Rempel Signed-off-by: David Jander Signed-off-by: Oleksij Rempel Cc: Link: https://lore.kernel.org/r/20260318105123.819807-6-o.rempel@pengutronix.de Signed-off-by: Alexandre Torgue Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts | 64 +++++++++++++++ arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts | 80 +++++++++++++++++++ .../arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi | 63 --------------- 3 files changed, 144 insertions(+), 63 deletions(-) diff --git a/arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts b/arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts index 4e795ad42928b..06ab774658162 100644 --- a/arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts +++ b/arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts @@ -96,3 +96,67 @@ assigned-clock-rates = <125000000>; /* Clock PLL3 to 625Mhz in tf-a. */ st,eth-clk-sel; }; + +&gpiod { + gpio-line-names = "", "", "", "", + "", "", "", "", + "", "", "", "", + "STP_RESETN", "STP_ENABLEN", "HPOUT0", "HPOUT0_ALERTN"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog_d_mecsbc>; +}; + +&gpioe { + gpio-line-names = "HPOUT0_RESETN", "HPOUT1", "HPOUT1_ALERTN", "", + "", "", "HPOUT1_RESETN", + "LPOUT0", "LPOUT0_ALERTN", "LPOUT0_RESETN", + "LPOUT1", "LPOUT1_ALERTN", "LPOUT1_RESETN", + "LPOUT2", "LPOUT2_ALERTN", "LPOUT2_RESETN"; +}; + +&gpiof { + gpio-line-names = "LPOUT3", "LPOUT3_ALERTN", "LPOUT3_RESETN", + "LPOUT4", "LPOUT4_ALERTN", "LPOUT4_RESETN", + "", "", + "", "", "", "", + "", "", "", ""; +}; + +&gpiog { + gpio-line-names = "LPOUT5", "LPOUT5_ALERTN", "", "LPOUT5_RESETN", + "", "", "", "", + "", "", "", "", + "", "", "", ""; +}; + +&gpioh { + gpio-line-names = "", "", "", "", + "", "", "", "", + "GPIO0_RESETN", "", "", "", + "", "", "", ""; +}; + +&gpioi { + gpio-line-names = "", "", "", "", + "", "", "", "", + "HPDCM0_SLEEPN", "HPDCM1_SLEEPN", "GPIO1_RESETN", "", + "", "", "", ""; +}; + +&gpioj { + gpio-line-names = "HSIN10", "HSIN11", "HSIN12", "HSIN13", + "HSIN14", "HSIN15", "", "", + "", "", "", "", + "", "RTD_RESETN", "", ""; +}; + +&gpiok { + gpio-line-names = "", "", "HSIN0", "HSIN1", + "HSIN2", "HSIN3", "HSIN4", "HSIN5"; +}; + +&gpioz { + gpio-line-names = "", "", "", "HSIN6", + "HSIN7", "HSIN8", "HSIN9", ""; +}; + diff --git a/arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts b/arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts index d32816093e47d..2b3989303cd15 100644 --- a/arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts +++ b/arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts @@ -90,6 +90,86 @@ clock-frequency = <24000000>; }; +&gpioa { + gpio-line-names = "", "", "", "", + "", "", "", "", + "", "", "GPIO1_RESETN", "", + "", "", "", "LPOUT5"; +}; + +&gpiob { + gpio-line-names = "", "", "", "", + "LPOUT4_RESETN", "", "", "", + "", "LPOUT4_ALERTN", "", "", + "", "", "", ""; +}; + +&gpioc { + gpio-line-names = "", "", "", "", + "", "", "", "", + "", "LPOUT4", "", "", + "", "", "", ""; +}; + +&gpiod { + gpio-line-names = "LPOUT2", "", "LPOUT3_RESETN", "", + "LPOUT2_ALERTN", "", "MECIO_ADDR0", "", + "HPOUT1_ALERTN", "HPOUT1_RESETN", "", "", + "", "", "HPOUT0", "HPOUT1"; +}; + +&gpioe { + gpio-line-names = "LPOUT0_RESETN", "", "", "", + "", "LPOUT3", "LPOUT5_ALERTN", "", + "", "", "", "", + "", "", "", "HSIN_RESETN"; +}; + +&gpiof { + gpio-line-names = "LPOUT5_RESETN", "", "", "HPOUT0_ALERTN", + "", "LPOUT1", "", "", + "", "", "", "", + "", "", "", ""; +}; + +&gpiog { + gpio-line-names = "", "", "", "HPOUT0_RESETN", + "", "", "LPOUT3_ALERTN", "", + "", "", "GPIO0_RESETN", "", + "", "", "", "LPOUT2_RESETN"; +}; + +&gpioh { + gpio-line-names = "", "", "", "", + "", "", "", "", + "", "LPOUT0", "", "", + "", "LPOUT0_ALERTN", "STP_ENABLEN", "STP_RESETN"; +}; + +&gpioi { + gpio-line-names = "", "", "", "", + "", "", "", "", + "", "", "SPE_RESETN", "", + "HPDCM0_SLEEPN", "", "", ""; +}; + +&gpioj { + gpio-line-names = "", "", "", "", + "", "", "", "MECIO_ADDR1", + "", "", "", "", + "", "", "", "LPOUT1_RESETN"; +}; + +&gpiok { + gpio-line-names = "", "", "RTD_RESETN", "", + "", "LPOUT1_ALERTN", "", ""; +}; + +&gpioz { + gpio-line-names = "", "", "", "", + "HPDCM1_SLEEPN", "", "", ""; +}; + &m_can1 { pinctrl-names = "default", "sleep"; pinctrl-0 = <&m_can1_pins_b>; diff --git a/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi b/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi index e50e9ae085e81..69a502ec36d4d 100644 --- a/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi +++ b/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi @@ -173,69 +173,6 @@ }; }; -&gpiod { - gpio-line-names = "", "", "", "", - "", "", "", "", - "", "", "", "", - "STP_RESETN", "STP_ENABLEN", "HPOUT0", "HPOUT0_ALERTN"; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_hog_d_mecsbc>; -}; - -&gpioe { - gpio-line-names = "HPOUT0_RESETN", "HPOUT1", "HPOUT1_ALERTN", "", - "", "", "HPOUT1_RESETN", - "LPOUT0", "LPOUT0_ALERTN", "LPOUT0_RESETN", - "LPOUT1", "LPOUT1_ALERTN", "LPOUT1_RESETN", - "LPOUT2", "LPOUT2_ALERTN", "LPOUT2_RESETN"; -}; - -&gpiof { - gpio-line-names = "LPOUT3", "LPOUT3_ALERTN", "LPOUT3_RESETN", - "LPOUT4", "LPOUT4_ALERTN", "LPOUT4_RESETN", - "", "", - "", "", "", "", - "", "", "", ""; -}; - -&gpiog { - gpio-line-names = "LPOUT5", "LPOUT5_ALERTN", "", "LPOUT5_RESETN", - "", "", "", "", - "", "", "", "", - "", "", "", ""; -}; - -&gpioh { - gpio-line-names = "", "", "", "", - "", "", "", "", - "GPIO0_RESETN", "", "", "", - "", "", "", ""; -}; - -&gpioi { - gpio-line-names = "", "", "", "", - "", "", "", "", - "HPDCM0_SLEEPN", "HPDCM1_SLEEPN", "GPIO1_RESETN", "", - "", "", "", ""; -}; - -&gpioj { - gpio-line-names = "HSIN10", "HSIN11", "HSIN12", "HSIN13", - "HSIN14", "HSIN15", "", "", - "", "", "", "", - "", "RTD_RESETN", "", ""; -}; - -&gpiok { - gpio-line-names = "", "", "HSIN0", "HSIN1", - "HSIN2", "HSIN3", "HSIN4", "HSIN5"; -}; - -&gpioz { - gpio-line-names = "", "", "", "HSIN6", - "HSIN7", "HSIN8", "HSIN9", ""; -}; - &i2c2 { pinctrl-names = "default"; pinctrl-0 = <&i2c2_pins_a>; From 7b5e3c15eee1d5bf42cd4cee3fa93415f4ae148e Mon Sep 17 00:00:00 2001 From: David Jander Date: Wed, 18 Mar 2026 11:51:22 +0100 Subject: [PATCH 2220/2758] ARM: dts: stm32: stm32mp15x-mecio1-io: Fix expander gpio line typo commit dfb93c4acce8ad9c4f573128b2cf7ddb936e0de7 upstream. Fix a copy-paste error in the GPIO line names for the TCA6416 expander (gpio@20). The common mecio1-io include file was originally defined using the mecio1r1 (Revision 1) hardware layout, but incorrectly labeled pin 13 as "HSIN9_BIAS" instead of the actual "HSIN7_BIAS" present in the schematics. Fixes: 8267753c891c ("ARM: dts: stm32: Add MECIO1 and MECT1S board variants") Co-developed-by: Oleksij Rempel Signed-off-by: David Jander Signed-off-by: Oleksij Rempel Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20260318105123.819807-7-o.rempel@pengutronix.de Signed-off-by: Alexandre Torgue Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi b/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi index 69a502ec36d4d..1808289f8193b 100644 --- a/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi +++ b/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi @@ -186,7 +186,7 @@ #gpio-cells = <2>; gpio-line-names = "HSIN0_BIAS", "HSIN1_BIAS", "HSIN2_BIAS", "HSIN3_BIAS", "", "", "HSIN_VREF0_LVL", "HSIN_VREF1_LVL", - "HSIN4_BIAS", "HSIN5_BIAS", "HSIN6_BIAS", "HSIN9_BIAS", + "HSIN4_BIAS", "HSIN5_BIAS", "HSIN6_BIAS", "HSIN7_BIAS", "", "", "", ""; }; From 81f5b85a5fb0ae20b84a89b05852375123a11def Mon Sep 17 00:00:00 2001 From: David Jander Date: Wed, 18 Mar 2026 11:51:23 +0100 Subject: [PATCH 2221/2758] ARM: dts: stm32: stm32mp15x-mecio1-io: Move expander gpio-line-names to board files commit a0d6c2a06fffff47bcca4d5bfdab4cc428a315fc upstream. Move the gpio-line-names properties for the I2C GPIO expanders (gpio0 and gpio1) out of the common mecio1-io.dtsi file and into the specific board dts files. The layout originally defined in the common include file belonged to the mecio1r1 (Revision 1) hardware. This layout is moved 1:1 into the stm32mp153c-mecio1r1.dts file. The mecio1r0 (Revision 0) hardware utilizes a completely different pinout for these expanders. A new, accurate mapping reflecting the Revision 0 schematics is added to stm32mp151c-mecio1r0.dts. Fixes: 8267753c891c ("ARM: dts: stm32: Add MECIO1 and MECT1S board variants") Co-developed-by: Oleksij Rempel Signed-off-by: David Jander Signed-off-by: Oleksij Rempel Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20260318105123.819807-8-o.rempel@pengutronix.de Signed-off-by: Alexandre Torgue Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts | 14 ++++++++++++++ arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts | 14 ++++++++++++++ arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi | 8 -------- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts b/arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts index 06ab774658162..862782d20d10e 100644 --- a/arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts +++ b/arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts @@ -97,6 +97,20 @@ st,eth-clk-sel; }; +&gpio0 { + gpio-line-names = "HSIN0_BIAS", "HSIN1_BIAS", "HSIN2_BIAS", "HSIN3_BIAS", + "HSIN4_BIAS", "", "STP_VREF0_LVL", "HSIN_VREF0_LVL", + "STP0_FB_BIAS", "STP1_FB_BIAS", "STP2_FB_BIAS", "STP3_FB_BIAS", + "", "", "", ""; +}; + +&gpio1 { + gpio-line-names = "HSIN5_BIAS", "HSIN6_BIAS", "HSIN7_BIAS", "HSIN8_BIAS", + "HSIN9_BIAS", "", "STP_VREF1_LVL", "HSIN_VREF1_LVL", + "STP4_FB_BIAS", "STP5_FB_BIAS", "STP6_FB_BIAS", "", + "", "", "LSIN8_BIAS", "LSIN9_BIAS"; +}; + &gpiod { gpio-line-names = "", "", "", "", "", "", "", "", diff --git a/arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts b/arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts index 2b3989303cd15..739cc18c3d3af 100644 --- a/arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts +++ b/arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts @@ -90,6 +90,20 @@ clock-frequency = <24000000>; }; +&gpio0 { + gpio-line-names = "HSIN0_BIAS", "HSIN1_BIAS", "HSIN2_BIAS", "HSIN3_BIAS", + "", "", "HSIN_VREF0_LVL", "HSIN_VREF1_LVL", + "HSIN4_BIAS", "HSIN5_BIAS", "HSIN6_BIAS", "HSIN7_BIAS", + "", "", "", ""; +}; + +&gpio1 { + gpio-line-names = "HSIN8_BIAS", "HSIN9_BIAS", "HSIN10_BIAS", "HSIN11_BIAS", + "", "", "HSIN_VREF2_LVL", "HSIN_VREF3_LVL", + "HSIN12_BIAS", "HSIN13_BIAS", "HSIN14_BIAS", "HSIN15_BIAS", + "", "", "LSIN8_BIAS", "LSIN9_BIAS"; +}; + &gpioa { gpio-line-names = "", "", "", "", "", "", "", "", diff --git a/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi b/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi index 1808289f8193b..1a4f5a523eb33 100644 --- a/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi +++ b/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi @@ -184,10 +184,6 @@ reg = <0x20>; gpio-controller; #gpio-cells = <2>; - gpio-line-names = "HSIN0_BIAS", "HSIN1_BIAS", "HSIN2_BIAS", "HSIN3_BIAS", - "", "", "HSIN_VREF0_LVL", "HSIN_VREF1_LVL", - "HSIN4_BIAS", "HSIN5_BIAS", "HSIN6_BIAS", "HSIN7_BIAS", - "", "", "", ""; }; gpio1: gpio@21 { @@ -195,10 +191,6 @@ reg = <0x21>; gpio-controller; #gpio-cells = <2>; - gpio-line-names = "HSIN8_BIAS", "HSIN9_BIAS", "HSIN10_BIAS", "HSIN11_BIAS", - "", "", "HSIN_VREF2_LVL", "HSIN_VREF3_LVL", - "HSIN12_BIAS", "HSIN13_BIAS", "HSIN14_BIAS", "HSIN15_BIAS", - "", "", "LSIN8_BIAS", "LSIN9_BIAS"; }; }; From 8b3e188d19e4ffe916780aeb7ddad9d6457b8bcc Mon Sep 17 00:00:00 2001 From: Yanfei Xu Date: Thu, 11 Jun 2026 20:46:43 +0800 Subject: [PATCH 2222/2758] LoongArch: KVM: Validate irqchip index in irqfd routing commit 3474037904c20ff915e3ebab0ab5c1e41bbe549e upstream. Sashiko reported that the irqchip index is not validated for LoongArch. Add validation and reject out-of-range irqchip indexes to avoid indexing past the routing table's chip array. Cc: stable@vger.kernel.org Fixes: 1928254c5ccb ("LoongArch: KVM: Add irqfd support") Closes: https://lore.kernel.org/kvm/20260525051714.485D51F000E9@smtp.kernel.org/ Reported-by: Sashiko Reviewed-by: Bibo Mao Signed-off-by: Yanfei Xu Signed-off-by: Huacai Chen Signed-off-by: Greg Kroah-Hartman --- arch/loongarch/kvm/irqfd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/loongarch/kvm/irqfd.c b/arch/loongarch/kvm/irqfd.c index f4f953b224192..40ed1081c4b62 100644 --- a/arch/loongarch/kvm/irqfd.c +++ b/arch/loongarch/kvm/irqfd.c @@ -51,7 +51,8 @@ int kvm_set_routing_entry(struct kvm *kvm, e->irqchip.irqchip = ue->u.irqchip.irqchip; e->irqchip.pin = ue->u.irqchip.pin; - if (e->irqchip.pin >= KVM_IRQCHIP_NUM_PINS) + if (e->irqchip.pin >= KVM_IRQCHIP_NUM_PINS || + e->irqchip.irqchip >= KVM_NR_IRQCHIPS) return -EINVAL; return 0; From d4574547e04a47ad498149576f65b84475ea6f4c Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 11 Jun 2026 20:46:40 +0800 Subject: [PATCH 2223/2758] LoongArch: KVM: Check irq validity in kvm_vcpu_ioctl_interrupt() commit 09b318ab77b7a4fc9987fd98d1525fc55ddc2617 upstream. Function kvm_vcpu_ioctl_interrupt() can be called from userspace, here add irq validility cheking in kvm_vcpu_ioctl_interrupt(). Cc: stable@vger.kernel.org Fixes: f45ad5b8aa93 ("LoongArch: KVM: Implement vcpu interrupt operations") Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen Signed-off-by: Greg Kroah-Hartman --- arch/loongarch/kvm/vcpu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index e28084c49e682..4c6e2c05daafa 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -1487,6 +1487,10 @@ void kvm_lose_fpu(struct kvm_vcpu *vcpu) int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu, struct kvm_interrupt *irq) { int intr = (int)irq->irq; + unsigned int vector = abs(intr); + + if (vector >= EXCCODE_INT_NUM) + return -EINVAL; if (intr > 0) kvm_queue_irq(vcpu, intr); From 5c827b66a626b41ad9a4cdff1c12fc287c38ba87 Mon Sep 17 00:00:00 2001 From: Qiang Ma Date: Thu, 11 Jun 2026 20:46:43 +0800 Subject: [PATCH 2224/2758] LoongArch: KVM: Check the return values for put_user() commit fb89e0fe2dc4246c86ad0eb0fa2b7cb2f8ba9728 upstream. put_user() may return -EFAULT, so, when the user space address is invalid, the caller should return -EFAULT. Cc: stable@vger.kernel.org Reviewed-by: Bibo Mao Signed-off-by: Qiang Ma Signed-off-by: Huacai Chen Signed-off-by: Greg Kroah-Hartman --- arch/loongarch/kvm/vcpu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index 4c6e2c05daafa..8101ecad02cf6 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -1108,7 +1108,8 @@ static int kvm_loongarch_cpucfg_get_attr(struct kvm_vcpu *vcpu, return -ENXIO; } - put_user(val, uaddr); + if (put_user(val, uaddr)) + return -EFAULT; return ret; } From 31e99851ee9973670d4a2cda8a056851e23895cf Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 11 Jun 2026 20:46:40 +0800 Subject: [PATCH 2225/2758] LoongArch: KVM: Fix FPU register width with user access API commit f4caaac76379daf4a617d9134b6087fb2636fb31 upstream. At the beginning, only 64 bit FPU is supported. With FPU register get interface, 64 bit FPU data is copied to user space, the same with FPU register set API. However with LSX and LASX supported in later, there should be FPU data copied with bigger width. So here fixes this issue, copy the whole 256 bit FPU data from/to user space. Cc: stable@vger.kernel.org Fixes: db1ecca22edf ("LoongArch: KVM: Add LSX (128bit SIMD) support") Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen Signed-off-by: Greg Kroah-Hartman --- arch/loongarch/kvm/vcpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index 8101ecad02cf6..7f7f2b71f03ae 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -1313,7 +1313,7 @@ int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) fpu->fcc = vcpu->arch.fpu.fcc; fpu->fcsr = vcpu->arch.fpu.fcsr; for (i = 0; i < NUM_FPU_REGS; i++) - memcpy(&fpu->fpr[i], &vcpu->arch.fpu.fpr[i], FPU_REG_WIDTH / 64); + memcpy(&fpu->fpr[i], &vcpu->arch.fpu.fpr[i], sizeof(union fpureg)); return 0; } @@ -1325,7 +1325,7 @@ int kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) vcpu->arch.fpu.fcc = fpu->fcc; vcpu->arch.fpu.fcsr = fpu->fcsr; for (i = 0; i < NUM_FPU_REGS; i++) - memcpy(&vcpu->arch.fpu.fpr[i], &fpu->fpr[i], FPU_REG_WIDTH / 64); + memcpy(&vcpu->arch.fpu.fpr[i], &fpu->fpr[i], sizeof(union fpureg)); return 0; } From ac33662452217f6ea66c30dc129c70b3148ba459 Mon Sep 17 00:00:00 2001 From: Qiang Ma Date: Thu, 11 Jun 2026 20:46:43 +0800 Subject: [PATCH 2226/2758] LoongArch: KVM: Return full old CSR value from kvm_emu_xchg_csr() commit ebd50de14f1a06b7e0206083904bcc62b9ba65be upstream. The LoongArch CSRXCHG instruction returns the full old CSR value in rd after applying the masked update. kvm_emu_xchg_csr() currently masks the saved value before returning it to the guest, so rd receives only the bits selected by the write mask. That breaks the architectural behavior and makes a zero mask return 0 instead of the previous CSR value. So, keep the masked CSR update, but return the unmodified old CSR value. Cc: stable@vger.kernel.org Fixes: da50f5a693ff ("LoongArch: KVM: Implement handle csr exception") Reviewed-by: Bibo Mao Signed-off-by: Qiang Ma Signed-off-by: Huacai Chen Signed-off-by: Greg Kroah-Hartman --- arch/loongarch/kvm/exit.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/loongarch/kvm/exit.c b/arch/loongarch/kvm/exit.c index 3b95cd0f989b0..264813d45cbe0 100644 --- a/arch/loongarch/kvm/exit.c +++ b/arch/loongarch/kvm/exit.c @@ -103,7 +103,6 @@ static unsigned long kvm_emu_xchg_csr(struct kvm_vcpu *vcpu, int csrid, old = kvm_read_sw_gcsr(csr, csrid); val = (old & ~csr_mask) | (val & csr_mask); kvm_write_sw_gcsr(csr, csrid, val); - old = old & csr_mask; } else pr_warn_once("Unsupported csrxchg 0x%x with pc %lx\n", csrid, vcpu->arch.pc); From bcc6b684fcf6366fe08f61d841b924c0cb36d491 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Thu, 11 Jun 2026 12:48:48 +0200 Subject: [PATCH 2227/2758] KVM: s390: vsie: Fix allocation of struct vsie_rmap commit 505fcce0c64957f475f9b11fb1403821b7f33e7e upstream. The allocation size for struct vsie_rmap in kvm_s390_mmu_cache_topup() was wrong due to a copy-paste error. Fix it by using the type name. Fixes: 12f2f61a9e1a ("KVM: s390: KVM page table management functions: allocation") CC: stable@vger.kernel.org # 7.1 Signed-off-by: Claudio Imbrenda Message-ID: <20260611104850.110313-4-imbrenda@linux.ibm.com> Signed-off-by: Greg Kroah-Hartman --- arch/s390/kvm/dat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/kvm/dat.c b/arch/s390/kvm/dat.c index 4a41c0247ffa2..a4fe664f65ee1 100644 --- a/arch/s390/kvm/dat.c +++ b/arch/s390/kvm/dat.c @@ -45,7 +45,7 @@ int kvm_s390_mmu_cache_topup(struct kvm_s390_mmu_cache *mc) mc->pts[mc->n_pts] = o; } for ( ; mc->n_rmaps < KVM_S390_MMU_CACHE_N_RMAPS; mc->n_rmaps++) { - o = kzalloc_obj(*mc->rmaps[0], GFP_KERNEL_ACCOUNT); + o = kzalloc_obj(struct vsie_rmap, GFP_KERNEL_ACCOUNT); if (!o) return -ENOMEM; mc->rmaps[mc->n_rmaps] = o; From 0c3d4ca328e661cb4eda3bb344ea17e4b6a63171 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Thu, 11 Jun 2026 12:48:49 +0200 Subject: [PATCH 2228/2758] KVM: s390: vsie: Add missing radix_tree_preload() in _gaccess_shadow_fault() commit 668e70cc545e2659f3c4adad20a0883533042473 upstream. Add missing radix_tree_preload() in _gaccess_shadow_fault() to guarantee forward progress. The core of _gaccess_shadow_fault() has been split into ___gaccess_shadow_fault() in order to simplify locking. Fixes: e38c884df921 ("KVM: s390: Switch to new gmap") CC: stable@vger.kernel.org # 7.1 Signed-off-by: Claudio Imbrenda Message-ID: <20260611104850.110313-5-imbrenda@linux.ibm.com> Signed-off-by: Greg Kroah-Hartman --- arch/s390/kvm/gaccess.c | 57 +++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/arch/s390/kvm/gaccess.c b/arch/s390/kvm/gaccess.c index 20e28b183c1ac..0584fc91606fa 100644 --- a/arch/s390/kvm/gaccess.c +++ b/arch/s390/kvm/gaccess.c @@ -1582,35 +1582,48 @@ static int _gaccess_do_shadow(struct kvm_s390_mmu_cache *mc, struct gmap *sg, return _do_shadow_crste(sg, saddr, host, table, entries + LEVEL_MEM, w->p); } -static inline int _gaccess_shadow_fault(struct kvm_vcpu *vcpu, struct gmap *sg, gpa_t saddr, - unsigned long seq, struct pgtwalk *walk) +static inline int ___gaccess_shadow_fault(struct kvm_vcpu *vcpu, struct gmap *sg, gpa_t saddr, + unsigned long seq, struct pgtwalk *walk) { struct gmap *parent; int rc; - if (kvm_s390_array_needs_retry_unsafe(vcpu->kvm, seq, walk->raw_entries)) + if (kvm_s390_array_needs_retry_safe(vcpu->kvm, seq, walk->raw_entries)) return -EAGAIN; -again: - rc = kvm_s390_mmu_cache_topup(vcpu->arch.mc); - if (rc) - return rc; - scoped_guard(read_lock, &vcpu->kvm->mmu_lock) { - if (kvm_s390_array_needs_retry_safe(vcpu->kvm, seq, walk->raw_entries)) - return -EAGAIN; - parent = READ_ONCE(sg->parent); - if (!parent) + parent = READ_ONCE(sg->parent); + if (!parent) + return -EAGAIN; + scoped_guard(spinlock, &parent->children_lock) { + if (READ_ONCE(sg->parent) != parent) return -EAGAIN; - scoped_guard(spinlock, &parent->children_lock) { - if (READ_ONCE(sg->parent) != parent) - return -EAGAIN; - sg->invalidated = false; - rc = _gaccess_do_shadow(vcpu->arch.mc, sg, saddr, walk); - } - if (rc == -ENOMEM) - goto again; - if (!rc) - kvm_s390_release_faultin_array(vcpu->kvm, walk->raw_entries, false); + sg->invalidated = false; + rc = _gaccess_do_shadow(vcpu->arch.mc, sg, saddr, walk); } + if (!rc) + kvm_s390_release_faultin_array(vcpu->kvm, walk->raw_entries, false); + return rc; +} + +static inline int _gaccess_shadow_fault(struct kvm_vcpu *vcpu, struct gmap *sg, gpa_t saddr, + unsigned long seq, struct pgtwalk *walk) +{ + int rc; + + if (kvm_s390_array_needs_retry_unsafe(vcpu->kvm, seq, walk->raw_entries)) + return -EAGAIN; + + do { + rc = kvm_s390_mmu_cache_topup(vcpu->arch.mc); + if (rc) + return rc; + rc = radix_tree_preload(GFP_KERNEL); + if (rc) + return rc; + scoped_guard(read_lock, &vcpu->kvm->mmu_lock) + rc = ___gaccess_shadow_fault(vcpu, sg, saddr, seq, walk); + radix_tree_preload_end(); + } while (rc == -ENOMEM); + return rc; } From eeeb9bc718311b5478b8caae840b94348e89ad09 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Thu, 11 Jun 2026 12:48:46 +0200 Subject: [PATCH 2229/2758] KVM: s390: Silence potential warnings in _gmap_crstep_xchg_atomic() commit f79413b2dde10829a1b526543e725dd5c1847e9a upstream. While dat_crstep_xchg_atomic() is marked as __must_check, in this particular case the return value should be ignored. Silence potential compiler warnings with a pointless check, and add a comment to explain the situation. Fixes: d1adc098ce08 ("KVM: s390: Fix _gmap_crstep_xchg_atomic()") CC: stable@vger.kernel.org # 7.1 Signed-off-by: Claudio Imbrenda Message-ID: <20260611104850.110313-2-imbrenda@linux.ibm.com> Signed-off-by: Greg Kroah-Hartman --- arch/s390/kvm/gmap.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/arch/s390/kvm/gmap.h b/arch/s390/kvm/gmap.h index 5374f21aaf8df..20881e3ce9d86 100644 --- a/arch/s390/kvm/gmap.h +++ b/arch/s390/kvm/gmap.h @@ -279,7 +279,16 @@ static inline bool __must_check _gmap_crstep_xchg_atomic(struct gmap *gmap, unio gmap_handle_vsie_unshadow_event(gmap, gfn); else _gmap_handle_vsie_unshadow_event(gmap, gfn); - dat_crstep_xchg_atomic(crstep, oldcrste, newcrste, gfn, gmap->asce); + if (!dat_crstep_xchg_atomic(crstep, oldcrste, newcrste, gfn, gmap->asce)) + return false; + /* + * Return false even if the swap was successful, as it only + * indicates that the best effort clearing of the vsie_notif + * bit was successful. The caller will have to try again + * regardless, since the desired value has not been set. + * This pointless check is needed to silence a potential + * __must_check warning. + */ return false; } if (!oldcrste.s.fc1.d && newcrste.s.fc1.d && !newcrste.s.fc1.s) From 5fc9690db3bf8c372efb6a31f9a0da48e7189750 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Thu, 11 Jun 2026 12:48:50 +0200 Subject: [PATCH 2230/2758] KVM: s390: vsie: Use mmu cache to allocate rmap commit abeb7eb57f1671d9185ddf11236c784f07bdb928 upstream. Use kvm_s390_mmu_cache_alloc_rmap() to allocate the rmap in gmap_insert_rmap(), instead of a normal kzalloc_obj() with GFP_ATOMIC. This guarantees forward progress. Fixes: a2c17f9270cc ("KVM: s390: New gmap code") CC: stable@vger.kernel.org # 7.1 Signed-off-by: Claudio Imbrenda Message-ID: <20260611104850.110313-6-imbrenda@linux.ibm.com> Signed-off-by: Greg Kroah-Hartman --- arch/s390/kvm/gaccess.c | 16 ++++++++-------- arch/s390/kvm/gmap.c | 7 ++++--- arch/s390/kvm/gmap.h | 3 ++- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/arch/s390/kvm/gaccess.c b/arch/s390/kvm/gaccess.c index 0584fc91606fa..36102b2727fbf 100644 --- a/arch/s390/kvm/gaccess.c +++ b/arch/s390/kvm/gaccess.c @@ -1419,8 +1419,8 @@ static int walk_guest_tables(struct gmap *sg, unsigned long saddr, struct pgtwal return kvm_s390_get_guest_page(kvm, entries + LEVEL_MEM, table.pte.pfra, wr); } -static int _do_shadow_pte(struct gmap *sg, gpa_t raddr, union pte *ptep_h, union pte *ptep, - struct guest_fault *f, bool p) +static int _do_shadow_pte(struct kvm_s390_mmu_cache *mc, struct gmap *sg, gpa_t raddr, + union pte *ptep_h, union pte *ptep, struct guest_fault *f, bool p) { union pgste pgste; union pte newpte; @@ -1430,7 +1430,7 @@ static int _do_shadow_pte(struct gmap *sg, gpa_t raddr, union pte *ptep_h, union lockdep_assert_held(&sg->parent->children_lock); scoped_guard(spinlock, &sg->host_to_rmap_lock) - rc = gmap_insert_rmap(sg, f->gfn, gpa_to_gfn(raddr), TABLE_TYPE_PAGE_TABLE); + rc = gmap_insert_rmap(mc, sg, f->gfn, gpa_to_gfn(raddr), TABLE_TYPE_PAGE_TABLE); if (rc) return rc; @@ -1462,8 +1462,8 @@ static int _do_shadow_pte(struct gmap *sg, gpa_t raddr, union pte *ptep_h, union return 0; } -static int _do_shadow_crste(struct gmap *sg, gpa_t raddr, union crste *host, union crste *table, - struct guest_fault *f, bool p) +static int _do_shadow_crste(struct kvm_s390_mmu_cache *mc, struct gmap *sg, gpa_t raddr, + union crste *host, union crste *table, struct guest_fault *f, bool p) { union crste newcrste, oldcrste; unsigned long mask; @@ -1476,7 +1476,7 @@ static int _do_shadow_crste(struct gmap *sg, gpa_t raddr, union crste *host, uni mask = is_pmd(*table) ? _SEGMENT_FR_MASK : _REGION3_FR_MASK; r_gfn = gpa_to_gfn(raddr) & mask; scoped_guard(spinlock, &sg->host_to_rmap_lock) - rc = gmap_insert_rmap(sg, f->gfn & mask, r_gfn, host->h.tt); + rc = gmap_insert_rmap(mc, sg, f->gfn & mask, r_gfn, host->h.tt); if (rc) return rc; @@ -1578,8 +1578,8 @@ static int _gaccess_do_shadow(struct kvm_s390_mmu_cache *mc, struct gmap *sg, if (KVM_BUG_ON(l > TABLE_TYPE_REGION3, sg->kvm)) return -EFAULT; if (l == TABLE_TYPE_PAGE_TABLE) - return _do_shadow_pte(sg, saddr, ptep_h, ptep, entries + LEVEL_MEM, w->p); - return _do_shadow_crste(sg, saddr, host, table, entries + LEVEL_MEM, w->p); + return _do_shadow_pte(mc, sg, saddr, ptep_h, ptep, entries + LEVEL_MEM, w->p); + return _do_shadow_crste(mc, sg, saddr, host, table, entries + LEVEL_MEM, w->p); } static inline int ___gaccess_shadow_fault(struct kvm_vcpu *vcpu, struct gmap *sg, gpa_t saddr, diff --git a/arch/s390/kvm/gmap.c b/arch/s390/kvm/gmap.c index 52d55ddea8d4f..1d289f8fa3b20 100644 --- a/arch/s390/kvm/gmap.c +++ b/arch/s390/kvm/gmap.c @@ -1000,7 +1000,8 @@ int gmap_pv_destroy_range(struct gmap *gmap, gfn_t start, gfn_t end, bool interr return 0; } -int gmap_insert_rmap(struct gmap *sg, gfn_t p_gfn, gfn_t r_gfn, int level) +int gmap_insert_rmap(struct kvm_s390_mmu_cache *mc, struct gmap *sg, gfn_t p_gfn, + gfn_t r_gfn, int level) { struct vsie_rmap *rmap __free(kvfree) = NULL; struct vsie_rmap *temp; @@ -1010,7 +1011,7 @@ int gmap_insert_rmap(struct gmap *sg, gfn_t p_gfn, gfn_t r_gfn, int level) KVM_BUG_ON(!is_shadow(sg), sg->kvm); lockdep_assert_held(&sg->host_to_rmap_lock); - rmap = kzalloc_obj(*rmap, GFP_ATOMIC); + rmap = kvm_s390_mmu_cache_alloc_rmap(mc); if (!rmap) return -ENOMEM; @@ -1057,7 +1058,7 @@ int gmap_protect_rmap(struct kvm_s390_mmu_cache *mc, struct gmap *sg, gfn_t p_gf if (level <= TABLE_TYPE_REGION1) { bitmask = -1UL << (8 + 11 * level); scoped_guard(spinlock, &sg->host_to_rmap_lock) - rc = gmap_insert_rmap(sg, p_gfn, r_gfn & bitmask, level); + rc = gmap_insert_rmap(mc, sg, p_gfn, r_gfn & bitmask, level); } if (rc) return rc; diff --git a/arch/s390/kvm/gmap.h b/arch/s390/kvm/gmap.h index 20881e3ce9d86..1c040472f56d7 100644 --- a/arch/s390/kvm/gmap.h +++ b/arch/s390/kvm/gmap.h @@ -100,7 +100,8 @@ int gmap_ucas_map(struct gmap *gmap, gfn_t p_gfn, gfn_t c_gfn, unsigned long cou void gmap_ucas_unmap(struct gmap *gmap, gfn_t c_gfn, unsigned long count); int gmap_enable_skeys(struct gmap *gmap); int gmap_pv_destroy_range(struct gmap *gmap, gfn_t start, gfn_t end, bool interruptible); -int gmap_insert_rmap(struct gmap *sg, gfn_t p_gfn, gfn_t r_gfn, int level); +int gmap_insert_rmap(struct kvm_s390_mmu_cache *mc, struct gmap *sg, gfn_t p_gfn, + gfn_t r_gfn, int level); int gmap_protect_rmap(struct kvm_s390_mmu_cache *mc, struct gmap *sg, gfn_t p_gfn, gfn_t r_gfn, kvm_pfn_t pfn, int level, bool wr); void gmap_set_cmma_all_dirty(struct gmap *gmap); From a4a19941ccb2164edc3491faa5ac5df82e94b363 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Thu, 11 Jun 2026 12:50:36 +0200 Subject: [PATCH 2231/2758] KVM: s390: Initialize KVM_S390_GET_CMMA_BITS memory commit c7dda3d0f869dc97223448a06c9a2e5235928e48 upstream. kvm_s390_get_cmma_bits() allocates its output buffer with vmalloc(), which does not zero the returned pages: values = vmalloc(args->count); In the non-peek (migration) path, dat_get_cmma() reports a byte count spanning from the first to the last dirty page, but __dat_get_cmma_pte() writes values[gfn - start] only for pages whose CMMA dirty bit is set. The walk uses DAT_WALK_IGN_HOLES, so clean and unmapped pages that lie between two dirty pages within the reported span are visited but never store their byte. Those gaps (up to KVM_S390_MAX_BIT_DISTANCE pages each) stay uninitialized yet fall inside [0, count) and are copied out by copy_to_user(), disclosing stale kernel memory to user space. Before the switch to the new gmap implementation the buffer was fully populated for every gfn in the span, so no uninitialized bytes were exposed; the dirty-only walk introduced the leak. Use vzalloc() so the gaps read back as zero. Fixes: e38c884df921 ("KVM: s390: Switch to new gmap") Cc: stable@vger.kernel.org Signed-off-by: Christian Borntraeger Reviewed-by: Claudio Imbrenda Signed-off-by: Claudio Imbrenda Message-ID: <20260611105036.11491-1-borntraeger@linux.ibm.com> Signed-off-by: Greg Kroah-Hartman --- arch/s390/kvm/kvm-s390.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index ffb20a64d328f..6afd45b5f7dc0 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -2252,7 +2252,7 @@ static int kvm_s390_get_cmma_bits(struct kvm *kvm, return 0; } - values = vmalloc(args->count); + values = vzalloc(args->count); if (!values) return -ENOMEM; From ce587046baacdeb774b7756ab29b3f594e429004 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Thu, 11 Jun 2026 12:48:47 +0200 Subject: [PATCH 2232/2758] KVM: s390: Fix unlikely race in try_get_locked_pte() commit 5670b7f927f8d98685f3f5873dbf9f8d7a5a63f3 upstream. Fix an unlikely race in try_get_locked_pte(), which could have happened if puds or pmds get unmapped between the p?dp_get() and p?d_offset() functions. Fixes: 89fa757931dc ("KVM: s390: Avoid potentially sleeping while atomic when zapping pages") CC: stable@vger.kernel.org # 7.1 Signed-off-by: Claudio Imbrenda Message-ID: <20260611104850.110313-3-imbrenda@linux.ibm.com> Signed-off-by: Greg Kroah-Hartman --- arch/s390/mm/gmap_helpers.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/s390/mm/gmap_helpers.c b/arch/s390/mm/gmap_helpers.c index 60023b6fdcb13..4bf7c9012feb6 100644 --- a/arch/s390/mm/gmap_helpers.c +++ b/arch/s390/mm/gmap_helpers.c @@ -51,15 +51,15 @@ pte_t *try_get_locked_pte(struct mm_struct *mm, unsigned long vmaddr, spinlock_t pgd = pgdp_get(pgdp); if (pgd_none(pgd) || !pgd_present(pgd)) return NULL; - p4dp = p4d_offset(pgdp, vmaddr); + p4dp = p4d_offset_lockless(pgdp, pgd, vmaddr); p4d = p4dp_get(p4dp); if (p4d_none(p4d) || !p4d_present(p4d)) return NULL; - pudp = pud_offset(p4dp, vmaddr); + pudp = pud_offset_lockless(p4dp, p4d, vmaddr); pud = pudp_get(pudp); if (pud_none(pud) || pud_leaf(pud) || !pud_present(pud)) return NULL; - pmdp = pmd_offset(pudp, vmaddr); + pmdp = pmd_offset_lockless(pudp, pud, vmaddr); pmd = pmdp_get_lockless(pmdp); if (pmd_none(pmd) || pmd_leaf(pmd) || !pmd_present(pmd)) return NULL; From a2e7bbc91cf621a033a37800b86366f35b0e4303 Mon Sep 17 00:00:00 2001 From: Gautam Menghani Date: Mon, 15 Jun 2026 14:41:19 +0530 Subject: [PATCH 2233/2758] powerpc/pseries/Kconfig: Enable CONFIG_VPA_PMU to be used with KVM commit fe179677b6dcb4b658586038a811f87265e97777 upstream. Currently, CONFIG_VPA_PMU is not enabled by default, and consequently cannot be used for KVM guests at all, unless explicitly enabled on host kernel. Mark CONFIG_VPA_PMU as "default m" to ensure it is available when KVM is being used. Cc: stable@vger.kernel.org # v6.13+ Suggested-by: Sean Christopherson Reviewed-by: Amit Machhiwal Reviewed-by: Harsh Prateek Bora Reviewed-by: Ritesh Harjani (IBM) Signed-off-by: Gautam Menghani [Maddy: Changed tag order] Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260615091120.84169-1-gautam@linux.ibm.com Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/platforms/pseries/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig index f7052b131a4c5..74910ce3a541c 100644 --- a/arch/powerpc/platforms/pseries/Kconfig +++ b/arch/powerpc/platforms/pseries/Kconfig @@ -154,6 +154,7 @@ config HV_PERF_CTRS config VPA_PMU tristate "VPA PMU events" depends on KVM_BOOK3S_64_HV && HV_PERF_CTRS + default m help Enable access to the VPA PMU counters via perf. This enables code that support measurement for KVM on PowerVM(KoP) feature. From adce12bb0e0dc82d1d6f0821c9faee3145e62a6f Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Wed, 24 Jun 2026 14:19:10 +0800 Subject: [PATCH 2234/2758] KVM: s390: pci: Fix GISC refcount leak on AIF enable failure commit 7b69729046a4c58f4cb457184e5ac4aaa179bff4 upstream. kvm_s390_gisc_register() registers the guest ISC before pinning the guest interrupt forwarding pages and allocating the AISB bit. If any of the later setup steps fails, the function unwinds the pinned pages and other local state, but does not unregister the GISC reference. Add the missing kvm_s390_gisc_unregister() to the error unwind path. Fixes: 3c5a1b6f0a18 ("KVM: s390: pci: provide routines for enabling/disabling interrupt forwarding") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Reviewed-by: Matthew Rosato Tested-by: Matthew Rosato Acked-by: Claudio Imbrenda Reviewed-by: Christian Borntraeger Signed-off-by: Claudio Imbrenda Message-ID: <20260624061910.2794734-1-haoxiang_li2024@163.com> Signed-off-by: Christian Borntraeger Signed-off-by: Greg Kroah-Hartman --- arch/s390/kvm/pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/s390/kvm/pci.c b/arch/s390/kvm/pci.c index 5b075c38998e3..686113be05309 100644 --- a/arch/s390/kvm/pci.c +++ b/arch/s390/kvm/pci.c @@ -328,6 +328,7 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, unpin1: unpin_user_page(aibv_page); out: + kvm_s390_gisc_unregister(kvm, fib->fmt0.isc); return rc; } From 0658b09cba7fe866c6cd70cd2dcdfdcabe80328f Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Fri, 5 Jun 2026 05:59:15 +0900 Subject: [PATCH 2235/2758] KVM: arm64: vgic: Check the interrupt is still ours before migrating it commit 0074b82cdfcb5fd13710a0ac308ade68ac6f6fbe upstream. vgic_prune_ap_list() drops both ap_list_lock and irq_lock while migrating an interrupt to another vCPU. After reacquiring the locks it only checks that the affinity is unchanged (target_vcpu == vgic_target_oracle(irq)) before moving the interrupt, which assumes that an interrupt whose affinity is preserved is still queued on this vCPU's ap_list. That assumption no longer holds if the interrupt is taken off the ap_list while the locks are dropped. vgic_flush_pending_lpis() removes the interrupt from the list and sets irq->vcpu to NULL, but leaves enabled/pending/target_vcpu untouched. As the interrupt is still enabled and pending, vgic_target_oracle() returns the same target_vcpu, so the affinity check passes and list_del() is run a second time on an entry that has already been removed. Also check that the interrupt is still assigned to this vCPU (irq->vcpu == vcpu) before moving it. Fixes: 0919e84c0fc1 ("KVM: arm/arm64: vgic-new: Add IRQ sync/flush framework") Signed-off-by: Hyunwoo Kim Link: https://patch.msgid.link/aiHnI1mu6SGQrgnz@v4bel Signed-off-by: Marc Zyngier Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/vgic/vgic.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c index 1e9fe8764584d..18b280de9a299 100644 --- a/arch/arm64/kvm/vgic/vgic.c +++ b/arch/arm64/kvm/vgic/vgic.c @@ -818,15 +818,16 @@ static void vgic_prune_ap_list(struct kvm_vcpu *vcpu) raw_spin_lock(&irq->irq_lock); /* - * If the affinity has been preserved, move the - * interrupt around. Otherwise, it means things have - * changed while the interrupt was unlocked, and we - * need to replay this. + * If the interrupt is still ours and its affinity has + * been preserved, move it around. Otherwise, it means + * things have changed while the interrupt was unlocked + * (it may even have been taken off the list with its + * affinity left untouched), and we need to replay this. * * In all cases, we cannot trust the list not to have * changed, so we restart from the beginning. */ - if (target_vcpu == vgic_target_oracle(irq)) { + if (irq->vcpu == vcpu && target_vcpu == vgic_target_oracle(irq)) { struct vgic_cpu *new_cpu = &target_vcpu->arch.vgic_cpu; list_del(&irq->ap_list); From b1a89d12d35a8256d2b170ced0b1c86851f3def2 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 15 Jun 2026 19:16:25 +0100 Subject: [PATCH 2236/2758] KVM: arm64: vgic: Handle race between interrupt affinity change and LPI disabling commit 7258770e5814f15e8308ebda82ac9acf6964ba8e upstream. Hyunwoo Kim reports some really bad races should the following situation occur: - LPI-I is pending in vcpu-B's AP list - vcpu-A writes to vcpu-B's RD to disable its LPIs - vcpu-C moves I from B to C If the last two race nicely enough, vgic_prune_ap_list() can drop the irq and AP list locks, reacquire them, and in the interval the irq has been freed. UAF follows. The fix is two-fold: - Before dropping the irq and ap_list locks, take a reference on the irq - Do not try to handle migration of the pending bit: there is no expectation that this state is retained, as per the architecture With that, we're sure that the interrupt is still around, and we safely remove it from the AP list as it has no target at this stage (unless another interrupt fires, but that's another story). Reported-by: Hyunwoo Kim Tested-by: Hyunwoo Kim Link: https://lore.kernel.org/r/ailsCnyoS82r_QRz@v4bel Link: https://patch.msgid.link/20260615181625.3029352-1-maz@kernel.org Fixes: 5dd4b924e390a ("KVM: arm/arm64: vgic: Add refcounting for IRQs") Signed-off-by: Marc Zyngier Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/vgic/vgic.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c index 18b280de9a299..4cd818ce90933 100644 --- a/arch/arm64/kvm/vgic/vgic.c +++ b/arch/arm64/kvm/vgic/vgic.c @@ -204,6 +204,7 @@ void vgic_flush_pending_lpis(struct kvm_vcpu *vcpu) list_for_each_entry_safe(irq, tmp, &vgic_cpu->ap_list_head, ap_list) { if (irq_is_lpi(vcpu->kvm, irq->intid)) { raw_spin_lock(&irq->irq_lock); + irq->pending_latch = false; list_del(&irq->ap_list); irq->vcpu = NULL; raw_spin_unlock(&irq->irq_lock); @@ -795,7 +796,11 @@ static void vgic_prune_ap_list(struct kvm_vcpu *vcpu) continue; } - /* This interrupt looks like it has to be migrated. */ + /* + * This interrupt looks like it has to be migrated, + * make sure it is kept alive while locks are dropped. + */ + vgic_get_irq_ref(irq); raw_spin_unlock(&irq->irq_lock); raw_spin_unlock(&vgic_cpu->ap_list_lock); @@ -840,6 +845,8 @@ static void vgic_prune_ap_list(struct kvm_vcpu *vcpu) raw_spin_unlock(&vcpuB->arch.vgic_cpu.ap_list_lock); raw_spin_unlock(&vcpuA->arch.vgic_cpu.ap_list_lock); + deleted_lpis |= vgic_put_irq_norelease(vcpu->kvm, irq); + if (target_vcpu_needs_kick) { kvm_make_request(KVM_REQ_IRQ_PENDING, target_vcpu); kvm_vcpu_kick(target_vcpu); From 7d066368f72e6192af7e21c5817626f6da666991 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 12 Jun 2026 07:56:41 -0700 Subject: [PATCH 2237/2758] KVM: nVMX: Move vTPR vs. TPR Threshold consistency check into "normal" checks commit ebdac7554abb347ca4197be241116842161acd9b upstream. Move the off-by-default consistency check for vmcs12.tpr_threshold vs. the virtual APIC vTPR into the "normal" controls checks, as waiting until KVM has loaded some amount of state is unnecessary and actively dangerous. Specifically, failure to unwind vmcs01.GUEST_CR3 to KVM's value when EPT is disabled results in KVM running L1 with an L1-controlled CR3, not with KVM's CR3! Alternatively, KVM could simply reset the MMU to force a reload of vmcs01.GUEST_CR3, but the _only_ reason the check was shoved into a "late" flow was to wait until the vmcs12 pages were retrieved. Rather than build up more crusty code, simply access vTPR using a regular guest memory access (performance isn't a concern). To circumvent the restrictions that led to KVM deferring nested_get_vmcs12_pages(), (a) use a VM-scoped API to read guest memory so that it always hits non-SMM memslots (for RSM), and (b) skip the check (since its off-by-default anyways) when the vCPU doesn't want to run, i.e. when userspace is restoring/stuffing state. If reading guest memory fails, simply skip the consistency check, as KVM's de facto ABI is that VMX instruction accesses to non-existent memory get PCI Bus Error semantics, where reads return 0xFFs. And if vTPR=0xFF, then the vTPR is guaranteed to be greater than or equal to TPR_THRESHOLD. Fixes: 1100e4910ad2 ("KVM: nVMX: Add an off-by-default module param to WARN on missed consistency checks") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260612145642.452392-2-seanjc@google.com Signed-off-by: Sean Christopherson Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/vmx/nested.c | 66 +++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 37 deletions(-) diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 3fe88f29be7a9..ea58e27bc7c7d 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -553,6 +553,9 @@ static int nested_vmx_check_msr_bitmap_controls(struct kvm_vcpu *vcpu, static int nested_vmx_check_tpr_shadow_controls(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12) { + gpa_t vtpr_gpa = vmcs12->virtual_apic_page_addr + APIC_TASKPRI; + u32 vtpr; + if (!nested_cpu_has(vmcs12, CPU_BASED_TPR_SHADOW)) return 0; @@ -562,6 +565,32 @@ static int nested_vmx_check_tpr_shadow_controls(struct kvm_vcpu *vcpu, if (CC(!nested_cpu_has_vid(vmcs12) && vmcs12->tpr_threshold >> 4)) return -EINVAL; + /* + * Do the illegal vTPR vs. TPR Threshold consistency check if and only + * if KVM is configured to WARN on missed consistency checks, otherwise + * it's a waste of time. KVM needs to rely on hardware to fully detect + * an illegal combination due to the vTPR being writable by L1 at all + * times (it's an in-memory value, not a VMCS field). I.e. even if the + * check passes now, it might fail at the actual VM-Enter. + * + * If reading guest memory fails, skip the check as KVM's de facto ABI + * for VMX instruction accesses to non-existent memory is to provide + * PCI Bus Error semantics (reads return 0xFFs), in which case the vTPR + * is guaranteed to greater than or equal to the threshold. + * + * Note! Deliberately use the VM-scoped API when reading guest memory, + * to ensure the read doesn't hit SMRAM when restoring L2 state on RSM, + * and only perform the check when in KVM_RUN, to avoid a false failure + * if userspace hasn't yet configured memslots during state restore. + */ + if (warn_on_missed_cc && vcpu->wants_to_run && + nested_cpu_has(vmcs12, CPU_BASED_TPR_SHADOW) && + !nested_cpu_has_vid(vmcs12) && + !nested_cpu_has2(vmcs12, SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES) && + !kvm_read_guest(vcpu->kvm, vtpr_gpa, &vtpr, sizeof(vtpr)) && + CC((vmcs12->tpr_threshold & GENMASK(3, 0)) > ((vtpr >> 4) & GENMASK(3, 0)))) + return -EINVAL; + return 0; } @@ -3085,38 +3114,6 @@ static int nested_vmx_check_controls(struct kvm_vcpu *vcpu, return 0; } -static int nested_vmx_check_controls_late(struct kvm_vcpu *vcpu, - struct vmcs12 *vmcs12) -{ - void *vapic = to_vmx(vcpu)->nested.virtual_apic_map.hva; - u32 vtpr = vapic ? (*(u32 *)(vapic + APIC_TASKPRI)) >> 4 : 0; - - /* - * Don't bother with the consistency checks if KVM isn't configured to - * WARN on missed consistency checks, as KVM needs to rely on hardware - * to fully detect an illegal vTPR vs. TRP Threshold combination due to - * the vTPR being writable by L1 at all times (it's an in-memory value, - * not a VMCS field). I.e. even if the check passes now, it might fail - * at the actual VM-Enter. - * - * Keying off the module param also allows treating an invalid vAPIC - * mapping as a consistency check failure without increasing the risk - * of breaking a "real" VM. - */ - if (!warn_on_missed_cc) - return 0; - - if ((exec_controls_get(to_vmx(vcpu)) & CPU_BASED_TPR_SHADOW) && - nested_cpu_has(vmcs12, CPU_BASED_TPR_SHADOW) && - !nested_cpu_has_vid(vmcs12) && - !nested_cpu_has2(vmcs12, SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES) && - (CC(!vapic) || - CC((vmcs12->tpr_threshold & GENMASK(3, 0)) > (vtpr & GENMASK(3, 0))))) - return -EINVAL; - - return 0; -} - static int nested_vmx_check_address_space_size(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12) { @@ -3666,11 +3663,6 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu, return NVMX_VMENTRY_KVM_INTERNAL_ERROR; } - if (nested_vmx_check_controls_late(vcpu, vmcs12)) { - vmx_switch_vmcs(vcpu, &vmx->vmcs01); - return NVMX_VMENTRY_VMFAIL; - } - if (nested_vmx_check_guest_state(vcpu, vmcs12, &entry_failure_code)) { exit_reason.basic = EXIT_REASON_INVALID_STATE; From 124a3769c43713a11a93a821b313e61ad5110cb8 Mon Sep 17 00:00:00 2001 From: Matthew Rosato Date: Thu, 9 Jul 2026 09:54:04 -0400 Subject: [PATCH 2238/2758] KVM: s390: pci: Fix handling of AIF enable without AISB commit 3e3aa6da87d30a0064a17b836685cd43c90a3572 upstream. When a guest seeks to register IRQs without a summary bit specified, ensure that the associated GAITE then stores 0 for the guest AISB location instead of virt_to_phys(page_address(NULL)). Fixes: 3c5a1b6f0a18 ("KVM: s390: pci: provide routines for enabling/disabling interrupt forwarding") Cc: stable@vger.kernel.org Reviewed-by: Farhan Ali Signed-off-by: Matthew Rosato Signed-off-by: Christian Borntraeger Signed-off-by: Greg Kroah-Hartman --- arch/s390/kvm/pci.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/arch/s390/kvm/pci.c b/arch/s390/kvm/pci.c index 686113be05309..720bb58cabe2e 100644 --- a/arch/s390/kvm/pci.c +++ b/arch/s390/kvm/pci.c @@ -300,9 +300,14 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, gaite->gisc = fib->fmt0.isc; gaite->count++; - gaite->aisbo = fib->fmt0.aisbo; - gaite->aisb = virt_to_phys(page_address(aisb_page) + (fib->fmt0.aisb & - ~PAGE_MASK)); + if (fib->fmt0.sum == 1) { + gaite->aisbo = fib->fmt0.aisbo; + gaite->aisb = virt_to_phys(page_address(aisb_page) + + (fib->fmt0.aisb & ~PAGE_MASK)); + } else { + gaite->aisbo = 0; + gaite->aisb = 0; + } aift->kzdev[zdev->aisb] = zdev->kzdev; spin_unlock_irq(&aift->gait_lock); From d2f9df3b615ca0cd45899c090366945528186052 Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Tue, 2 Jun 2026 15:36:32 -0700 Subject: [PATCH 2239/2758] KVM: SEV: Do not allow intra-host migration/mirroring of SNP VMs commit 6ee4140788234a6fabf59e6a50e38cdb936008cd upstream. The intra-host migration/mirroring feature is not fully implemented for SEV-SNP VMs. The proper migration requires additional SNP-specific state such as guest_req_mutex, guest_req_buf, and guest_resp_buf to be transferred or initialized on the destination. The SNP VM mirroring requires vmsa features to be copied as well otherwise ASID would be bound to SNP range while VM is detected as a SEV VM. Reject SNP source VMs in migration/mirroring until proper SNP state transfer is implemented. Fixes: 1dfe571c12cf ("KVM: SEV: Add initial SEV-SNP support") Reported-by: Chris Mason Reported-by: Sashiko Assisted-by: Claude:claude-opus-4-6 Reviewed-by: Tom Lendacky Signed-off-by: Atish Patra Link: https://patch.msgid.link/20260602-sev_snp_fixes-v3-1-24bfd3ae047c@meta.com Cc: stable@vger.kernel.org [sean: let lines poke past 80 chars, tag for stable] Signed-off-by: Sean Christopherson Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/svm/sev.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index d90f5a41e3404..996f398a577ff 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -2142,8 +2142,9 @@ int sev_vm_move_enc_context_from(struct kvm *kvm, unsigned int source_fd) if (ret) return ret; + /* Do not allow SNP VM migration until additional state transfer is implemented */ if (kvm->arch.vm_type != source_kvm->arch.vm_type || - sev_guest(kvm) || !sev_guest(source_kvm)) { + sev_guest(kvm) || !sev_guest(source_kvm) || sev_snp_guest(source_kvm)) { ret = -EINVAL; goto out_unlock; } @@ -2865,8 +2866,9 @@ int sev_vm_copy_enc_context_from(struct kvm *kvm, unsigned int source_fd) * disallow out-of-band SEV/SEV-ES init if the target is already an * SEV guest, or if vCPUs have been created. KVM relies on vCPUs being * created after SEV/SEV-ES initialization, e.g. to init intercepts. + * Also do not allow SNP VM mirroring until additional state transfer is implemented. */ - if (sev_guest(kvm) || !sev_guest(source_kvm) || + if (sev_guest(kvm) || !sev_guest(source_kvm) || sev_snp_guest(source_kvm) || is_mirroring_enc_context(source_kvm) || kvm->created_vcpus) { ret = -EINVAL; goto e_unlock; From d6b5aba65e99531c97b146622a406c75653819d5 Mon Sep 17 00:00:00 2001 From: Binbin Wu Date: Fri, 10 Jul 2026 11:53:23 +0800 Subject: [PATCH 2240/2758] KVM: TDX: Reject concurrent change to CPUID entry count commit cfbebb55e5127dc162e73fa8956000055a78606c upstream. Reject KVM_TDX_INIT_VM if userspace changes cpuid.nent between the initial read and the subsequent copy of the initialization data. tdx_td_init() first reads user_data->cpuid.nent to size the flexible kvm_tdx_init_vm copy. The copied structure also contains cpuid.nent, and that field can differ from the value used to size the allocation if userspace modifies the input concurrently. setup_tdparams_cpuids() later passes init_vm->cpuid.nent to kvm_find_cpuid_entry2(), which uses it as the array bound for the copied entries. Require the copied count to match the value used to size the allocation so that CPUID parsing cannot access beyond the entries actually copied. Fixes: 0bd0a4a1428b ("KVM: TDX: Replace kmalloc + copy_from_user with memdup_user in tdx_td_init()") Reported-by: Sashiko:gemini-3.1-pro-preview Cc: Signed-off-by: Binbin Wu Reviewed-by: Xiaoyao Li Reviewed-by: Thorsten Blum Link: https://patch.msgid.link/20260710035324.3170534-1-binbin.wu@linux.intel.com Signed-off-by: Sean Christopherson Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/vmx/tdx.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index ec29a2db1adce..556bf9fe1f5d0 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -2725,7 +2725,11 @@ static int tdx_td_init(struct kvm *kvm, struct kvm_tdx_cmd *cmd) goto out; } - if (init_vm->cpuid.padding) { + /* + * Reject the request if userspace changes cpuid.nent between the + * initial read and the subsequent copy. + */ + if (init_vm->cpuid.padding || init_vm->cpuid.nent != nr_user_entries) { ret = -EINVAL; goto out; } From 32bdca80aa81c2cb906f50a88b220ce1ecdc5e6e Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 24 Jun 2026 15:05:16 -0700 Subject: [PATCH 2241/2758] KVM: x86: Ignore pending PV EOI if the vCPU has since disabled PV EOIs commit 9285e4070df2c40585c3d7ec9571faa7a2b97e17 upstream. Ignore KVM's internal "service pending PV EOI" request if the vCPU has disabled PV EOIs since the request was made. Asserting that PV EOIs are enabled can fail if reading guest memory in pv_eoi_get_user() fails, i.e. if pv_eoi_test_and_clr_pending() bails early, *and* the vCPU also disables PV EOIs. kernel BUG at arch/x86/kvm/lapic.c:3338! Oops: invalid opcode: 0000 [#1] SMP CPU: 4 UID: 1000 PID: 890 Comm: pv_eoi_test Not tainted 7.0.0-d585aa5894d8-vm #337 PREEMPT Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015 RIP: 0010:kvm_lapic_sync_from_vapic+0x12b/0x140 [kvm] Call Trace: kvm_arch_vcpu_ioctl_run+0x1075/0x1c30 [kvm] kvm_vcpu_ioctl+0x2d5/0x980 [kvm] __x64_sys_ioctl+0x8a/0xd0 do_syscall_64+0xb5/0xb40 entry_SYSCALL_64_after_hwframe+0x4b/0x53 Modules linked in: kvm_intel kvm irqbypass ---[ end trace 0000000000000000 ]--- Fixes: ae7a2a3fb6f8 ("KVM: host side for eoi optimization") Cc: stable@vger.kernel.org Reviewed-by: Kai Huang Link: https://patch.msgid.link/20260624220516.3033391-1-seanjc@google.com Signed-off-by: Sean Christopherson Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/lapic.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 92a1e69c59626..d806abaeda3de 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -3371,6 +3371,12 @@ static void apic_sync_pv_eoi_from_guest(struct kvm_vcpu *vcpu, struct kvm_lapic *apic) { int vector; + + if (unlikely(!pv_eoi_enabled(vcpu))) { + __clear_bit(KVM_APIC_PV_EOI_PENDING, &vcpu->arch.apic_attention); + return; + } + /* * PV EOI state is derived from KVM_APIC_PV_EOI_PENDING in host * and KVM_PV_EOI_ENABLED in guest memory as follows: @@ -3382,8 +3388,6 @@ static void apic_sync_pv_eoi_from_guest(struct kvm_vcpu *vcpu, * KVM_APIC_PV_EOI_PENDING is set, KVM_PV_EOI_ENABLED is unset: * -> host enabled PV EOI, guest executed EOI. */ - BUG_ON(!pv_eoi_enabled(vcpu)); - if (pv_eoi_test_and_clr_pending(vcpu)) return; vector = apic_set_eoi(apic); From d5560b6569cd05ba72c6b33427fbabc6ec46b8cf Mon Sep 17 00:00:00 2001 From: leixiang Date: Mon, 22 Jun 2026 15:51:01 +0800 Subject: [PATCH 2242/2758] KVM: x86: Nullify irqfd->producer if updating IRTE for bypass fails commit ed446e8aa894883c08892cfee69782fdf8f6c3ca upstream. Nullify irqfd->producer if updating the IRTE for bypass fails, as leaving a dangling pointer will result in a use-after-free if the irqfd is reachable through KVM's routing, but the producer is freed separately. E.g. for VFIO PCI, the producer is embedded in struct "vfio_pci_irq_ctx" and freed when the vector is disabled, which can happen independent of routing updates. Fixes: 77e1b8332d1d ("KVM: x86: Decouple device assignment from IRQ bypass") Cc: stable@vger.kernel.org Signed-off-by: leixiang Link: https://patch.msgid.link/1782119051448443.14545.seg@mailgw.kylinos.cn [sean: drop PPC change, massage changelog] Signed-off-by: Sean Christopherson Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/irq.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/irq.c b/arch/x86/kvm/irq.c index 9519fec09ee6f..c5c2f778c6692 100644 --- a/arch/x86/kvm/irq.c +++ b/arch/x86/kvm/irq.c @@ -488,8 +488,10 @@ int kvm_arch_irq_bypass_add_producer(struct irq_bypass_consumer *cons, if (irqfd->irq_entry.type == KVM_IRQ_ROUTING_MSI) { ret = kvm_pi_update_irte(irqfd, &irqfd->irq_entry); - if (ret) + if (ret) { kvm->arch.nr_possible_bypass_irqs--; + irqfd->producer = NULL; + } } spin_unlock_irq(&kvm->irqfds.lock); From 2c87a087c20632d68920272173b5d7c47f9bcf70 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Tue, 14 Jul 2026 09:03:06 -0700 Subject: [PATCH 2243/2758] KVM: nVMX: Put vmcs12 pages if nested VM-Enter fails due to invalid guest state commit 2f2312c422fd2695da772cecb30c69994b795964 upstream. Put all vmcs12 pages if KVM synthesizes a nested VM-Exit due to invalid guest while emulating VMLAUNCH or VMRESUME. The invalid guest state path doesn't use nested_vmx_vmexit() as that API is intended to be used if and only if L2 is active, and the open coded equivalent neglects to put the vmcs12 pages. Failure to put the vmcs12 pages leaks any pinned pages (and/or mappings) if L1 retries VMLAUNCH/VMRESUME. Note, the !from_vmenter scenario doesn't suffer the same problem, as vmx_get_nested_state_pages() only gets/pins/maps the vmcs12 pages if L2 is active, i.e. if a "full" VM-Exit is guaranteed before KVM will retry getting vmcs12 pages. Fixes: 96c66e87deee ("KVM/nVMX: Use kvm_vcpu_map when mapping the virtual APIC page") Fixes: 3278e0492554 ("KVM/nVMX: Use kvm_vcpu_map when mapping the posted interrupt descriptor table") Fixes: fe1911aa443e ("KVM: nVMX: Use kvm_vcpu_map() to get/pin vmcs12's APIC-access page") Reported-by: Minh Nguyen Cc: stable@vger.kernel.org Signed-off-by: Sean Christopherson Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/vmx/nested.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index ea58e27bc7c7d..52ab52b0e1ccf 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -3747,6 +3747,8 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu, if (!from_vmentry) return NVMX_VMENTRY_VMEXIT; + nested_put_vmcs12_pages(vcpu); + load_vmcs12_host_state(vcpu, vmcs12); vmcs12->vm_exit_reason = exit_reason.full; if (enable_shadow_vmcs || nested_vmx_is_evmptr12_valid(vmx)) From 0cbae0e296d27ce4c4cce83e34d40c2bfd8133aa Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Sat, 27 Jun 2026 11:51:05 +0100 Subject: [PATCH 2244/2758] KVM: Move kvm_io_bus_get_dev() locking responsibilities to callers commit 3a07249981629ace483ebbef81ef6b34c2d2afec upstream. kvm_io_bus_get_dev() returns a device that is only matched by the address, and nothing else. This can cause a lifetime issue if the matched device is not the expected type, as by the time the caller can introspect the object, it might be gone (the srcu lock having been dropped). Given that there is only a single user of this helper, the simplest option is to move the locking responsibility to the caller, which can keep the srcu lock held for as long as it wants. Note that this aligns with other kvm_io_bus*() helpers, which already require the srcu lock to be held by the callers. Reported-by: Will Deacon Fixes: 8a39d00670f07 ("KVM: kvm_io_bus: Add kvm_io_bus_get_dev() call") Link: https://lore.kernel.org/all/20260626111344.802555-1-maz@kernel.org Cc: stable@vger.kernel.org Reviewed-by: Oliver Upton Link: https://patch.msgid.link/20260627105105.1005990-1-maz@kernel.org Signed-off-by: Marc Zyngier Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/vgic/vgic-its.c | 2 ++ virt/kvm/kvm_main.c | 16 +++++----------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c index 1e3706ac3b8e9..7abb685c65465 100644 --- a/arch/arm64/kvm/vgic/vgic-its.c +++ b/arch/arm64/kvm/vgic/vgic-its.c @@ -507,6 +507,8 @@ static struct vgic_its *__vgic_doorbell_to_its(struct kvm *kvm, gpa_t db) struct kvm_io_device *kvm_io_dev; struct vgic_io_device *iodev; + guard(srcu)(&kvm->srcu); + kvm_io_dev = kvm_io_bus_get_dev(kvm, KVM_MMIO_BUS, db); if (!kvm_io_dev) return ERR_PTR(-EINVAL); diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 881f92d7a469e..1a529098eec98 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -6069,25 +6069,19 @@ struct kvm_io_device *kvm_io_bus_get_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr) { struct kvm_io_bus *bus; - int dev_idx, srcu_idx; - struct kvm_io_device *iodev = NULL; + int dev_idx; - srcu_idx = srcu_read_lock(&kvm->srcu); + lockdep_assert_held(&kvm->srcu); bus = kvm_get_bus_srcu(kvm, bus_idx); if (!bus) - goto out_unlock; + return NULL; dev_idx = kvm_io_bus_get_first_dev(bus, addr, 1); if (dev_idx < 0) - goto out_unlock; - - iodev = bus->range[dev_idx].dev; - -out_unlock: - srcu_read_unlock(&kvm->srcu, srcu_idx); + return NULL; - return iodev; + return bus->range[dev_idx].dev; } EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_io_bus_get_dev); From 34d8d7242c52c174add6b413d56420aa60f8054c Mon Sep 17 00:00:00 2001 From: Bradley Morgan Date: Sun, 21 Jun 2026 21:31:55 +0000 Subject: [PATCH 2245/2758] KVM: arm64: account pKVM reclaim against the VM mm commit d098bb75d14fde2f12155f1a95ec0168160867ce upstream. Protected guest faults charge long term pins to the VM's mm. Teardown can run later from file release, where current->mm may be unrelated. Drop the charge from kvm->mm instead. Fixes: 4e6e03f9eadd ("KVM: arm64: Hook up reclaim hypercall to pkvm_pgtable_stage2_destroy()") Signed-off-by: Bradley Morgan Reviewed-by: Fuad Tabba Tested-by: Fuad Tabba Link: https://patch.msgid.link/20260621213155.6019-1-include@grrlz.net Signed-off-by: Marc Zyngier Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/pkvm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c index 053e4f733e4be..428723b1b0f5c 100644 --- a/arch/arm64/kvm/pkvm.c +++ b/arch/arm64/kvm/pkvm.c @@ -352,7 +352,7 @@ static int __pkvm_pgtable_stage2_reclaim(struct kvm_pgtable *pgt, u64 start, u64 page = pfn_to_page(mapping->pfn); WARN_ON_ONCE(mapping->nr_pages != 1); unpin_user_pages_dirty_lock(&page, 1, true); - account_locked_vm(current->mm, 1, false); + account_locked_vm(kvm->mm, 1, false); pkvm_mapping_remove(mapping, &pgt->pkvm_mappings); kfree(mapping); } From a805ab1914ea02796c9ff1ebfdb0eaa850b51b26 Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Wed, 1 Jul 2026 16:16:19 -0700 Subject: [PATCH 2246/2758] KVM: arm64: Ensure level is always initialized when relaxing perms commit 100baf0184896f859290a684f864b8200d8ac872 upstream. stage2_update_leaf_attrs() returns early before writing to @level if the table walker returned an error. At the same time, kvm_pgtable_stage2_relax_perms() uses the level as a TLBI TTL hint when the error was EAGAIN, indicating the vCPU raced with a table update and the TLB entry it hit is now stale. Fall back to an unknown TTL if none was provided by the walk. Cc: stable@vger.kernel.org Fixes: be097997a273 ("KVM: arm64: Always invalidate TLB for stage-2 permission faults") Signed-off-by: Oliver Upton Reviewed-by: Wei-Lin Chang Link: https://patch.msgid.link/20260701231620.3300204-2-oupton@kernel.org Signed-off-by: Marc Zyngier Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/hyp/pgtable.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c index 91a7dfad66866..31aaca35693a9 100644 --- a/arch/arm64/kvm/hyp/pgtable.c +++ b/arch/arm64/kvm/hyp/pgtable.c @@ -1358,7 +1358,7 @@ int kvm_pgtable_stage2_relax_perms(struct kvm_pgtable *pgt, u64 addr, enum kvm_pgtable_prot prot, enum kvm_pgtable_walk_flags flags) { kvm_pte_t xn = 0, set = 0, clr = 0; - s8 level; + s8 level = TLBI_TTL_UNKNOWN; int ret; if (prot & KVM_PTE_LEAF_ATTR_HI_SW) From 7deadbc5dab5b8e2316364603bc6281129c8461c Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Sun, 14 Jun 2026 22:13:24 -0700 Subject: [PATCH 2247/2758] KVM: arm64: nv: Drop bogus WARN for write to ZCR_EL2 commit 9f1667098c6ae7ec81a9a56859cfdacb822aa0d0 upstream. It is entirely possible for a guest to write to the ZCR_EL2 sysreg alias while in a nested context, as it is expected if FEAT_NV2 is advertised to the L1 hypervisor. Get rid of the bogus WARN which, since the hyp vectors were installed at this point, has the effect of a hyp_panic... Cc: stable@vger.kernel.org Fixes: 0cfc85b8f5cf ("KVM: arm64: nv: Load guest FP state for ZCR_EL2 trap") Signed-off-by: Oliver Upton Link: https://patch.msgid.link/20260615051324.830045-1-oupton@kernel.org Signed-off-by: Marc Zyngier Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/hyp/include/hyp/switch.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h index e9b36a3b27bbc..addad92376f60 100644 --- a/arch/arm64/kvm/hyp/include/hyp/switch.h +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h @@ -601,8 +601,6 @@ static inline bool kvm_hyp_handle_fpsimd(struct kvm_vcpu *vcpu, u64 *exit_code) return false; break; case ESR_ELx_EC_SYS64: - if (WARN_ON_ONCE(!is_hyp_ctxt(vcpu))) - return false; fallthrough; case ESR_ELx_EC_SVE: if (!sve_guest) From 29227821e2320ff6a174c91742b4ce221fe8d563 Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Mon, 15 Jun 2026 14:11:16 +0100 Subject: [PATCH 2248/2758] KVM: arm64: nv: Write ESR_EL2 for injected nested SError exceptions commit e2cb1f4578625e71f461d5c1ce70984193389cbb upstream. kvm_inject_el2_exception() writes ESR_EL2 for synchronous exceptions but not for SError. enter_exception64() does not write ESR_ELx for any exception type, so the constructed syndrome is dropped. A guest L2 hypervisor taking a nested SError observes stale ESR_EL2. This affects both kvm_inject_nested_serror() and the EASE path in kvm_inject_nested_sea(). Write ESR_EL2 for except_type_serror, matching except_type_sync. Fixes: 77ee70a07357 ("KVM: arm64: nv: Honor SError exception routing / masking") Reported-by: sashiko Signed-off-by: Fuad Tabba Link: https://patch.msgid.link/20260615131116.390977-1-tabba@google.com Signed-off-by: Marc Zyngier Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/emulate-nested.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c index dba7ced74ca5e..7989c79ce6dbc 100644 --- a/arch/arm64/kvm/emulate-nested.c +++ b/arch/arm64/kvm/emulate-nested.c @@ -2818,6 +2818,7 @@ static void kvm_inject_el2_exception(struct kvm_vcpu *vcpu, u64 esr_el2, break; case except_type_serror: kvm_pend_exception(vcpu, EXCEPT_AA64_EL2_SERR); + vcpu_write_sys_reg(vcpu, esr_el2, ESR_EL2); break; default: WARN_ONCE(1, "Unsupported EL2 exception injection %d\n", type); From dd3b237eb7780d65eae296d3d3a70012b6e7a02f Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Wed, 17 Jun 2026 12:08:21 +0800 Subject: [PATCH 2249/2758] KVM: arm64: nv: Fix SPSR_EL2 restore in kvm_hyp_handle_mops() commit ff1022c3de46753eb7eba2f6efd990569e66ff95 upstream. kvm_hyp_handle_mops() resets the single-step state machine as part of rewinding state for a MOPS exception by modifying vcpu_cpsr() and writing the result directly into hardware. In the case of nested virtualization, vcpu_cpsr() is a synthetic value such that the rest of KVM can deal with vEL2 cleanly. That means the value requires translation before being written into hardware, which is unfortunately missing from the MOPS handler. Fix it by directly modifying SPSR_EL2 and avoiding the synthetic state altogether, which will be resynchronized on the next 'full' exit back to KVM. Fixes: 2de451a329cf ("KVM: arm64: Add handler for MOPS exceptions") Reported-by: Zhong Wang Reported-by: Xuanqing Shi Link: https://lore.kernel.org/all/ajE4lHQevXNHpl1M@Air.local/ Cc: stable@vger.kernel.org Signed-off-by: Weiming Shi Link: https://patch.msgid.link/20260617040820.2194831-2-bestswngs@gmail.com Signed-off-by: Marc Zyngier Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/hyp/include/hyp/switch.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h index addad92376f60..4bd4a2b5cba43 100644 --- a/arch/arm64/kvm/hyp/include/hyp/switch.h +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h @@ -448,16 +448,19 @@ static inline bool __populate_fault_info(struct kvm_vcpu *vcpu) static inline bool kvm_hyp_handle_mops(struct kvm_vcpu *vcpu, u64 *exit_code) { + u64 spsr; + *vcpu_pc(vcpu) = read_sysreg_el2(SYS_ELR); arm64_mops_reset_regs(vcpu_gp_regs(vcpu), vcpu->arch.fault.esr_el2); write_sysreg_el2(*vcpu_pc(vcpu), SYS_ELR); /* * Finish potential single step before executing the prologue - * instruction. + * instruction. Modify the hardware SPSR_EL2 directly, as vcpu_cpsr() + * may hold a synthetic (vEL2) value for a guest hypervisor. */ - *vcpu_cpsr(vcpu) &= ~DBG_SPSR_SS; - write_sysreg_el2(*vcpu_cpsr(vcpu), SYS_SPSR); + spsr = read_sysreg_el2(SYS_SPSR); + write_sysreg_el2(spsr & ~DBG_SPSR_SS, SYS_SPSR); return true; } From d35defbdfcb15296ebe28968ad7452c1a8c11cea Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Thu, 18 Jun 2026 16:42:02 -0700 Subject: [PATCH 2250/2758] KVM: arm64: nv: Respect read-only PFN when mapping L1 VNCR commit 2684e02bac41c5220f6c1ab2bdcc957b71812977 upstream. KVM currently maps the L1 VNCR into the host stage-1 by relying entirely on the permissions of the guest stage-1. At the same time, it is entirely possible that the backing PFN is read-only (e.g. RO memslot), meaning that the L1 VNCR should use at most a read-only mapping. Cache the writability of the PFN in the VNCR TLB and use it to constrain the resulting fixmap permissions. Promote VNCR permission faults to an SEA in the case where the guest attempts to write to a read-only endpoint. Conveniently, this also plugs a page leak found by Sashiko [*] resulting from the early return for a read-only PFN. Cc: stable@vger.kernel.org Fixes: 2a359e072596 ("KVM: arm64: nv: Handle mapping of VNCR_EL2 at EL2") Link: https://lore.kernel.org/kvm/20260608082603.16AEC1F00893@smtp.kernel.org/ Signed-off-by: Oliver Upton Link: https://patch.msgid.link/20260618234207.1063941-2-oupton@kernel.org Signed-off-by: Marc Zyngier Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/nested.c | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 682e1c8437fb1..9a80e69ca9b1a 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -24,6 +24,7 @@ struct vncr_tlb { struct s1_walk_result wr; u64 hpa; + bool hpa_writable; /* -1 when not mapped on a CPU */ int cpu; @@ -1315,7 +1316,7 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem) if (!*is_gmem) { pfn = __kvm_faultin_pfn(memslot, gfn, write_fault ? FOLL_WRITE : 0, &writable, &page); - if (is_error_noslot_pfn(pfn) || (write_fault && !writable)) + if (is_error_noslot_pfn(pfn)) return -EFAULT; } else { ret = kvm_gmem_get_pfn(vcpu->kvm, memslot, gfn, &pfn, &page, NULL); @@ -1324,6 +1325,8 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem) write_fault, false, false); return ret; } + + writable = !(memslot->flags & KVM_MEM_READONLY); } scoped_guard(write_lock, &vcpu->kvm->mmu_lock) { @@ -1334,28 +1337,41 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem) vt->gva = va; vt->hpa = pfn << PAGE_SHIFT; + vt->hpa_writable = writable; vt->valid = true; vt->cpu = -1; kvm_make_request(KVM_REQ_MAP_L1_VNCR_EL2, vcpu); - kvm_release_faultin_page(vcpu->kvm, page, false, vt->wr.pw); + kvm_release_faultin_page(vcpu->kvm, page, false, vt->wr.pw && vt->hpa_writable); } - if (vt->wr.pw) + if (vt->wr.pw && vt->hpa_writable) mark_page_dirty(vcpu->kvm, gfn); return 0; } -static void inject_vncr_perm(struct kvm_vcpu *vcpu) +static void handle_vncr_perm(struct kvm_vcpu *vcpu) { struct vncr_tlb *vt = vcpu->arch.vncr_tlb; u64 esr = kvm_vcpu_get_esr(vcpu); + u64 fsc; + + /* + * Promote to an external abort if the stage-1 permits writes but the + * HPA is read-only (e.g. RO memslot). + */ + if (kvm_is_write_fault(vcpu) && vt->wr.pw && !vt->hpa_writable) + fsc = ESR_ELx_FSC_EXTABT; + /* + * Otherwise, inject a permission fault using the guest's translation + * level rather than the host's. + */ + else + fsc = ESR_ELx_FSC_PERM_L(vt->wr.level); - /* Adjust the fault level to reflect that of the guest's */ esr &= ~ESR_ELx_FSC; - esr |= FIELD_PREP(ESR_ELx_FSC, - ESR_ELx_FSC_PERM_L(vt->wr.level)); + esr |= FIELD_PREP(ESR_ELx_FSC, fsc); kvm_inject_nested_sync(vcpu, esr); } @@ -1389,7 +1405,7 @@ int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu) return kvm_handle_guest_sea(vcpu); if (esr_fsc_is_permission_fault(esr)) { - inject_vncr_perm(vcpu); + handle_vncr_perm(vcpu); } else if (esr_fsc_is_translation_fault(esr)) { bool valid, is_gmem = false; int ret; @@ -1437,7 +1453,7 @@ int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu) break; case -EPERM: /* Hack to deal with POE until we get kernel support */ - inject_vncr_perm(vcpu); + handle_vncr_perm(vcpu); break; case 0: break; @@ -1481,7 +1497,7 @@ static void kvm_map_l1_vncr(struct kvm_vcpu *vcpu) vt->cpu = smp_processor_id(); - if (vt->wr.pw && vt->wr.pr) + if (vt->hpa_writable && vt->wr.pw && vt->wr.pr) prot = PAGE_KERNEL; else if (vt->wr.pr) prot = PAGE_KERNEL_RO; From 53804b6839573c9c6fff5f4cf075d6746267345e Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Thu, 18 Jun 2026 16:42:03 -0700 Subject: [PATCH 2251/2758] KVM: arm64: nv: Inject SEA if kvm_translate_vncr() can't resolve PFN commit 9f3e83345a56280efffe235c65593c7e544c0fcc upstream. kvm_handle_vncr_abort() assumes that s1_walk_result conveys an abort when kvm_translate_vncr() returns -EFAULT. This is not always the case as it's possible to encounter 'late' failures on the output of S1 translation, e.g. a GFN outside of the memslots. Fix it by preparing an external abort before returning from kvm_translate_vncr(). Get rid of the BUG_ON() in the fault injection path while at it. Cc: stable@vger.kernel.org Fixes: 2a359e072596 ("KVM: arm64: nv: Handle mapping of VNCR_EL2 at EL2") Signed-off-by: Oliver Upton Link: https://patch.msgid.link/20260618234207.1063941-3-oupton@kernel.org Signed-off-by: Marc Zyngier Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/kvm_nested.h | 8 ++++++++ arch/arm64/kvm/at.c | 8 -------- arch/arm64/kvm/nested.c | 10 ++++++---- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/arch/arm64/include/asm/kvm_nested.h b/arch/arm64/include/asm/kvm_nested.h index cdf3e8422ea12..012d711034d17 100644 --- a/arch/arm64/include/asm/kvm_nested.h +++ b/arch/arm64/include/asm/kvm_nested.h @@ -388,6 +388,14 @@ struct s1_walk_result { bool failed; }; +static inline void fail_s1_walk(struct s1_walk_result *wr, u8 fst, bool s1ptw) +{ + wr->fst = fst; + wr->ptw = s1ptw; + wr->s2 = s1ptw; + wr->failed = true; +} + int __kvm_translate_va(struct kvm_vcpu *vcpu, struct s1_walk_info *wi, struct s1_walk_result *wr, u64 va); int __kvm_find_s1_desc_level(struct kvm_vcpu *vcpu, u64 va, u64 ipa, diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c index 889c2c15d7bd0..4c58e18ec6936 100644 --- a/arch/arm64/kvm/at.c +++ b/arch/arm64/kvm/at.c @@ -11,14 +11,6 @@ #include #include -static void fail_s1_walk(struct s1_walk_result *wr, u8 fst, bool s1ptw) -{ - wr->fst = fst; - wr->ptw = s1ptw; - wr->s2 = s1ptw; - wr->failed = true; -} - #define S1_MMU_DISABLED (-127) static int get_ia_size(struct s1_walk_info *wi) diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 9a80e69ca9b1a..65c5da802f319 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -1309,15 +1309,19 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem) gfn = vt->wr.pa >> PAGE_SHIFT; memslot = gfn_to_memslot(vcpu->kvm, gfn); - if (!memslot) + if (!memslot) { + fail_s1_walk(&vt->wr, ESR_ELx_FSC_EXTABT, false); return -EFAULT; + } *is_gmem = kvm_slot_has_gmem(memslot); if (!*is_gmem) { pfn = __kvm_faultin_pfn(memslot, gfn, write_fault ? FOLL_WRITE : 0, &writable, &page); - if (is_error_noslot_pfn(pfn)) + if (is_error_noslot_pfn(pfn)) { + fail_s1_walk(&vt->wr, ESR_ELx_FSC_EXTABT, false); return -EFAULT; + } } else { ret = kvm_gmem_get_pfn(vcpu->kvm, memslot, gfn, &pfn, &page, NULL); if (ret) { @@ -1444,8 +1448,6 @@ int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu) * Translation failed, inject the corresponding * exception back to EL2. */ - BUG_ON(!vt->wr.failed); - esr &= ~ESR_ELx_FSC; esr |= FIELD_PREP(ESR_ELx_FSC, vt->wr.fst); From 0a5dd8cf4d58ea28da132c2097cd1c525302ac48 Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Thu, 18 Jun 2026 16:42:04 -0700 Subject: [PATCH 2252/2758] KVM: arm64: nv: Re-translate VNCR before injecting abort commit bb645aa0a4caeaf7f9cd32e9a948594d434c1a8f upstream. KVM faults in the VNCR page with FOLL_WRITE whenever the guest aborts for a write, similar to how a regular stage-2 mapping is handled. It is entirely possible that the guest reads from the VNCR before writing to it, in which case the PFN could only be read-only. Invalidate the VNCR TLB and re-fetch the translation upon taking a VNCR abort, allowing the host mapping to be faulted in for write the second time around. Interestingly enough, this also satisfies the ordering requirements of FEAT_ETS2/3 between descriptor updates and MMU faults. Cc: stable@vger.kernel.org Fixes: 2a359e072596 ("KVM: arm64: nv: Handle mapping of VNCR_EL2 at EL2") Reported-by: Sashiko Signed-off-by: Oliver Upton Link: https://patch.msgid.link/20260618234207.1063941-4-oupton@kernel.org Signed-off-by: Marc Zyngier Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/nested.c | 111 +++++++++++++++------------------------- 1 file changed, 42 insertions(+), 69 deletions(-) diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 65c5da802f319..57bbab4f1194e 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -1380,88 +1380,61 @@ static void handle_vncr_perm(struct kvm_vcpu *vcpu) kvm_inject_nested_sync(vcpu, esr); } -static bool kvm_vncr_tlb_lookup(struct kvm_vcpu *vcpu) -{ - struct vncr_tlb *vt = vcpu->arch.vncr_tlb; - - lockdep_assert_held_read(&vcpu->kvm->mmu_lock); - - if (!vt->valid) - return false; - - if (read_vncr_el2(vcpu) != vt->gva) - return false; - - if (vt->wr.nG) - return get_asid_by_regime(vcpu, TR_EL20) == vt->wr.asid; - - return true; -} - int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu) { struct vncr_tlb *vt = vcpu->arch.vncr_tlb; u64 esr = kvm_vcpu_get_esr(vcpu); + bool is_gmem = false; + bool perm; + int ret; WARN_ON_ONCE(!(esr & ESR_ELx_VNCR)); if (kvm_vcpu_abt_issea(vcpu)) return kvm_handle_guest_sea(vcpu); - if (esr_fsc_is_permission_fault(esr)) { - handle_vncr_perm(vcpu); - } else if (esr_fsc_is_translation_fault(esr)) { - bool valid, is_gmem = false; - int ret; - - scoped_guard(read_lock, &vcpu->kvm->mmu_lock) - valid = kvm_vncr_tlb_lookup(vcpu); - - if (!valid) - ret = kvm_translate_vncr(vcpu, &is_gmem); - else - ret = -EPERM; + if (!esr_fsc_is_translation_fault(esr) && !esr_fsc_is_permission_fault(esr)) { + WARN_ONCE(1, "Unhandled VNCR abort, ESR=%llx\n", esr); + return 1; + } - switch (ret) { - case -EAGAIN: - /* Let's try again... */ - break; - case -ENOMEM: - /* - * For guest_memfd, this indicates that it failed to - * create a folio to back the memory. Inform userspace. - */ - if (is_gmem) - return 0; - /* Otherwise, let's try again... */ - break; - case -EFAULT: - case -EIO: - case -EHWPOISON: - if (is_gmem) - return 0; - fallthrough; - case -EINVAL: - case -ENOENT: - case -EACCES: - /* - * Translation failed, inject the corresponding - * exception back to EL2. - */ - esr &= ~ESR_ELx_FSC; - esr |= FIELD_PREP(ESR_ELx_FSC, vt->wr.fst); + ret = kvm_translate_vncr(vcpu, &is_gmem); + switch (ret) { + case -EAGAIN: + /* Let's try again... */ + return 1; + case -ENOMEM: + /* + * For guest_memfd, this indicates that it failed to + * create a folio to back the memory. Inform userspace. + */ + if (is_gmem) + return 0; + /* Otherwise, let's try again... */ + break; + case -EFAULT: + case -EIO: + case -EHWPOISON: + if (is_gmem) + return 0; + fallthrough; + case -EINVAL: + case -ENOENT: + case -EACCES: + /* + * Translation failed, inject the corresponding + * exception back to EL2. + */ + esr &= ~ESR_ELx_FSC; + esr |= FIELD_PREP(ESR_ELx_FSC, vt->wr.fst); - kvm_inject_nested_sync(vcpu, esr); - break; - case -EPERM: - /* Hack to deal with POE until we get kernel support */ + kvm_inject_nested_sync(vcpu, esr); + break; + case 0: + perm = kvm_is_write_fault(vcpu) ? vt->wr.pw && vt->hpa_writable : vt->wr.pr; + if (!perm) handle_vncr_perm(vcpu); - break; - case 0: - break; - } - } else { - WARN_ONCE(1, "Unhandled VNCR abort, ESR=%llx\n", esr); + break; } return 1; From bc00e0e376ee3572f5d26c174473abef1e35decc Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Thu, 18 Jun 2026 16:42:05 -0700 Subject: [PATCH 2253/2758] KVM: arm64: nv: Inject SEA if guest VNCR isn't normal memory commit 4bd7dbe0b2243e6aa735cae4d5e1ff988b30b2a6 upstream. When constructing an L1 VNCR mapping, KVM unconditionally uses cacheable memory attributes, even if the underlying PFN isn't memory. This gets particularly hairy if the endpoint doesn't support cacheable memory attributes, potentially throwing an SError on writeback... While KVM does permit cacheable memory attributes on certain PFNMAP VMAs, kvm_translate_vncr() isn't currently grabbing the VMA. So do the simpler thing for now and just reject everything that isn't memory. Cc: stable@vger.kernel.org Fixes: 2a359e072596 ("KVM: arm64: nv: Handle mapping of VNCR_EL2 at EL2") Signed-off-by: Oliver Upton Link: https://patch.msgid.link/20260618234207.1063941-5-oupton@kernel.org Signed-off-by: Marc Zyngier Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/nested.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 57bbab4f1194e..41b124f443b36 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -1333,6 +1333,17 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem) writable = !(memslot->flags & KVM_MEM_READONLY); } + /* + * FIXME: This check is too restrictive as KVM allows cacheable memory + * attributes for PFNMAP VMAs that have cacheable attributes in host + * stage-1. + */ + if (!pfn_is_map_memory(pfn)) { + kvm_release_faultin_page(vcpu->kvm, page, true, false); + fail_s1_walk(&vt->wr, ESR_ELx_FSC_EXTABT, false); + return -EINVAL; + } + scoped_guard(write_lock, &vcpu->kvm->mmu_lock) { if (mmu_invalidate_retry(vcpu->kvm, mmu_seq)) { kvm_release_faultin_page(vcpu->kvm, page, true, false); From a889978ec44fe33edb3fb7140dcbbb8e6465c1cb Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Thu, 14 May 2026 13:54:32 +0530 Subject: [PATCH 2254/2758] fbdev: metronomefb: fix potential memory leak in metronomefb_probe() commit 894632b862a39b3fe1cb5de06fbae86225ea64de upstream. The memory allocated for pagerefs in fb_deferred_io_init() is not freed on the error path. Fix it by calling fb_deferred_io_cleanup(). Fixes: 56c134f7f1b5 ("fbdev: Track deferred-I/O pages in pageref struct") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Reviewed-by: Thomas Zimmermann Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/metronomefb.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/video/fbdev/metronomefb.c b/drivers/video/fbdev/metronomefb.c index 6f0942c6e5f1c..83c614963a0a6 100644 --- a/drivers/video/fbdev/metronomefb.c +++ b/drivers/video/fbdev/metronomefb.c @@ -645,12 +645,14 @@ static int metronomefb_probe(struct platform_device *dev) info->flags = FBINFO_VIRTFB; info->fbdefio = &metronomefb_defio; - fb_deferred_io_init(info); + retval = fb_deferred_io_init(info); + if (retval) + goto err_free_irq; retval = fb_alloc_cmap(&info->cmap, 8, 0); if (retval < 0) { dev_err(&dev->dev, "Failed to allocate colormap\n"); - goto err_free_irq; + goto err_fbdefio; } /* set cmap */ @@ -673,6 +675,8 @@ static int metronomefb_probe(struct platform_device *dev) err_cmap: fb_dealloc_cmap(&info->cmap); +err_fbdefio: + fb_deferred_io_cleanup(info); err_free_irq: board->cleanup(par); err_csum_table: From e818c397548cde68d64c6abf8be0a5f3e3eef7d1 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Thu, 14 May 2026 13:54:31 +0530 Subject: [PATCH 2255/2758] fbdev: broadsheetfb: fix potential memory leak in broadsheetfb_probe() commit 9d18a4e4234fd3ee0d0eed8ccbbb50cb76b2232c upstream. The memory allocated for pagerefs in fb_deferred_io_init() is not freed on the error path. Fix it by calling fb_deferred_io_cleanup(). Fixes: 56c134f7f1b5 ("fbdev: Track deferred-I/O pages in pageref struct") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Reviewed-by: Thomas Zimmermann Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/broadsheetfb.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/video/fbdev/broadsheetfb.c b/drivers/video/fbdev/broadsheetfb.c index c8ba098a8c42f..582f1ee4c9b6d 100644 --- a/drivers/video/fbdev/broadsheetfb.c +++ b/drivers/video/fbdev/broadsheetfb.c @@ -1072,12 +1072,14 @@ static int broadsheetfb_probe(struct platform_device *dev) info->flags = FBINFO_VIRTFB; info->fbdefio = &broadsheetfb_defio; - fb_deferred_io_init(info); + retval = fb_deferred_io_init(info); + if (retval) + goto err_vfree; retval = fb_alloc_cmap(&info->cmap, 16, 0); if (retval < 0) { dev_err(&dev->dev, "Failed to allocate colormap\n"); - goto err_vfree; + goto err_fbdefio; } /* set cmap */ @@ -1121,6 +1123,8 @@ static int broadsheetfb_probe(struct platform_device *dev) board->cleanup(par); err_cmap: fb_dealloc_cmap(&info->cmap); +err_fbdefio: + fb_deferred_io_cleanup(info); err_vfree: vfree(videomemory); err_fb_rel: From 9a94b85531852eb86283eca36ab041782c6b3518 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Thu, 14 May 2026 13:54:30 +0530 Subject: [PATCH 2256/2758] fbdev: hecubafb: fix potential memory leak in hecubafb_probe() commit cbef2a305a8a72969b86f96b7c07b86edde61aff upstream. The memory allocated for pagerefs in fb_deferred_io_init() is not freed on the error path. Fix it by calling fb_deferred_io_cleanup(). Fixes: 56c134f7f1b5 ("fbdev: Track deferred-I/O pages in pageref struct") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Reviewed-by: Thomas Zimmermann Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/hecubafb.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/video/fbdev/hecubafb.c b/drivers/video/fbdev/hecubafb.c index 3547d58a29cf2..dd2af980f3d8b 100644 --- a/drivers/video/fbdev/hecubafb.c +++ b/drivers/video/fbdev/hecubafb.c @@ -192,7 +192,9 @@ static int hecubafb_probe(struct platform_device *dev) info->flags = FBINFO_VIRTFB; info->fbdefio = &hecubafb_defio; - fb_deferred_io_init(info); + retval = fb_deferred_io_init(info); + if (retval) + goto err_fbdefio; retval = register_framebuffer(info); if (retval < 0) @@ -209,6 +211,8 @@ static int hecubafb_probe(struct platform_device *dev) return 0; err_fbreg: + fb_deferred_io_cleanup(info); +err_fbdefio: framebuffer_release(info); err_fballoc: vfree(videomemory); From f6a1ac55e6ca70777b6f0989860b924238bafb7e Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Thu, 14 May 2026 21:02:02 -0400 Subject: [PATCH 2257/2758] fbdev: sm712: Fix operator precedence in big_swap macro commit 5dbe5b65df0b0c0ec77492427c274b7b5011890e upstream. The big_swap(p) macro was intended to swap bytes within 16-bit halves of a 32-bit value. However, because the bitwise shift operators (<<, >>) have higher precedence than the bitwise AND operator (&), the original code failed to perform any shifting on the masked bits. For example, 'p & 0xff00ff00 >> 8' was evaluated as 'p & (0xff00ff00 >> 8)', effectively neutralizing the intended swap. Fix this by adding parentheses to ensure the bitwise AND is performed before the shift, correctly implementing the byte swap logic. Fixes: 1461d66728648 ("staging: sm7xxfb: merge sm712fb with fbdev") Cc: stable@vger.kernel.org Signed-off-by: Li RongQing Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/sm712.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/fbdev/sm712.h b/drivers/video/fbdev/sm712.h index c7ebf03b8d537..83fe25fc61f2c 100644 --- a/drivers/video/fbdev/sm712.h +++ b/drivers/video/fbdev/sm712.h @@ -101,7 +101,7 @@ struct modeinit { #define mmio_addr 0x00800000 #define seqw17() smtc_seqw(0x17, 0x30) #define big_pixel_depth(p, d) {if (p == 24) {p = 32; d = 32; } } -#define big_swap(p) ((p & 0xff00ff00 >> 8) | (p & 0x00ff00ff << 8)) +#define big_swap(p) (((p & 0xff00ff00) >> 8) | ((p & 0x00ff00ff) << 8)) #else #define pal_rgb(r, g, b, val) val #define big_addr 0 From 5276e3f75ddb51f980a8903f4b39e73cf42415e5 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Thu, 14 May 2026 13:54:41 +0530 Subject: [PATCH 2258/2758] fbdev: efifb: fix memory leak in efifb_probe() commit 9b6eaf101656958397a6012bf43f6e2e42c9e5cb upstream. Since commit 73ce73c30ba9 ("fbdev: Transfer video= option strings to caller; clarify ownership") the string returned from fb_get_options() is expected to be freed by the caller, but the string is not freed in efifb_probe(). Fix that by freeing the option string after setup. Fixes: 73ce73c30ba9 ("fbdev: Transfer video= option strings to caller; clarify ownership") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Reviewed-by: Thomas Zimmermann Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/efifb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/video/fbdev/efifb.c b/drivers/video/fbdev/efifb.c index 47ebc0107209e..ad8dec7807c3a 100644 --- a/drivers/video/fbdev/efifb.c +++ b/drivers/video/fbdev/efifb.c @@ -377,6 +377,7 @@ static int efifb_probe(struct platform_device *dev) if (fb_get_options("efifb", &option)) return -ENODEV; efifb_setup(si, option); + kfree(option); /* We don't get linelength from UGA Draw Protocol, only from * EFI Graphics Protocol. So if it's not in DMI, and it's not From 1b1b43342fbf11eaf2a8926b9ed4805579d772ac Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Thu, 14 May 2026 13:54:33 +0530 Subject: [PATCH 2259/2758] fbdev: radeon: fix potential memory leak in radeonfb_pci_register() commit df8c1101c9a08859da612b5d0a08d55d475522c6 upstream. The function radeonfb_pci_register() allocates memory for modelist (by calling radeon_check_modes() which calls fb_add_videomode()). The memory is appended to info->modelist, but is not freed in subsequent error paths. Fix this by calling fb_destroy_modelist(). Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/aty/radeon_base.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/video/fbdev/aty/radeon_base.c b/drivers/video/fbdev/aty/radeon_base.c index cb006484831b6..2bed904e71e3b 100644 --- a/drivers/video/fbdev/aty/radeon_base.c +++ b/drivers/video/fbdev/aty/radeon_base.c @@ -2476,6 +2476,7 @@ static int radeonfb_pci_register(struct pci_dev *pdev, return 0; err_unmap_fb: iounmap(rinfo->fb_base); + fb_destroy_modelist(&info->modelist); err_unmap_rom: kfree(rinfo->mon1_EDID); kfree(rinfo->mon2_EDID); From 2ede8fa708235c81ab18de14077f1a458bec7e22 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Thu, 14 May 2026 13:54:35 +0530 Subject: [PATCH 2260/2758] fbdev: i740fb: fix potential memory leak in i740fb_probe() commit 5936063409af230a2c88b8700c47b89a19fd70b5 upstream. In i740fb_probe(), the memory allocated in fb_videomode_to_modelist() for modelist is not freed in the error paths. Fix that by calling fb_destroy_modelist(). Fixes: 5350c65f4f15 ("Resurrect Intel740 driver: i740fb") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/i740fb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/video/fbdev/i740fb.c b/drivers/video/fbdev/i740fb.c index 9b74dae71472c..c14a19382769a 100644 --- a/drivers/video/fbdev/i740fb.c +++ b/drivers/video/fbdev/i740fb.c @@ -1152,6 +1152,7 @@ static int i740fb_probe(struct pci_dev *dev, const struct pci_device_id *ent) fb_dealloc_cmap(&info->cmap); err_alloc_cmap: err_find_mode: + fb_destroy_modelist(&info->modelist); if (par->ddc_registered) i2c_del_adapter(&par->ddc_adapter); pci_iounmap(dev, par->regs); From 56964e8039150a14462005458b25f19d6cad8f4a Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Thu, 14 May 2026 13:54:37 +0530 Subject: [PATCH 2261/2758] fbdev: s3fb: fix potential memory leak in s3_pci_probe() commit 3b0ed04bc852887a9164e1bbf521652e8ef3eb92 upstream. In s3_pci_probe(), the memory allocated for modelist using fb_videomode_to_modelist() is not freed in subsequent error paths. Fix that by calling fb_destroy_modelist() Fixes: 86c0f043a737 ("s3fb: add DDC support") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/s3fb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/video/fbdev/s3fb.c b/drivers/video/fbdev/s3fb.c index ba30e5568cabc..d7ee2ff07c7c9 100644 --- a/drivers/video/fbdev/s3fb.c +++ b/drivers/video/fbdev/s3fb.c @@ -1446,6 +1446,7 @@ static int s3_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) err_alloc_cmap: err_find_mode: #ifdef CONFIG_FB_S3_DDC + fb_destroy_modelist(&info->modelist); if (par->ddc_registered) i2c_del_adapter(&par->ddc_adapter); if (par->mmio) From aa387a3e51808f580b51593e3c2f3d4703d91c1a Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Thu, 14 May 2026 13:54:40 +0530 Subject: [PATCH 2262/2758] fbdev: uvesafb: fix potential memory leak in uvesafb_probe() commit 033e56fed09047ee63072e9f58789f40c1c7079d upstream. Due to an incorrect goto label, memory allocated for modedb and modelist in uvesafb_vbe_init() is not freed in some error paths. Fix this by updating the goto label. Fixes: 8bdb3a2d7df4 ("uvesafb: the driver core") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/uvesafb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/video/fbdev/uvesafb.c b/drivers/video/fbdev/uvesafb.c index 88667fccc27b9..9d82326c744f2 100644 --- a/drivers/video/fbdev/uvesafb.c +++ b/drivers/video/fbdev/uvesafb.c @@ -1694,14 +1694,14 @@ static int uvesafb_probe(struct platform_device *dev) i = uvesafb_vbe_init_mode(info); if (i < 0) { err = -EINVAL; - goto out; + goto out_mode; } else { mode = &par->vbe_modes[i]; } if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) { err = -ENXIO; - goto out; + goto out_mode; } uvesafb_init_info(info, mode); From 2fd16a94bea5e0c3e93791436cf8a800b175762a Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Thu, 14 May 2026 13:54:38 +0530 Subject: [PATCH 2263/2758] fbdev: tdfxfb: fix potential memory leak in tdfxfb_probe() commit bb019d755366cc3e777a12d4bf457ff289837370 upstream. In tdfxfb_probe(), the memory allocated for modelist using fb_videomode_to_modelist() when CONFIG_FB_3DFX_I2C is defined, is not freed in the subsequent error paths. Fix that by calling fb_destroy_modelist(). Fixes: 215059d2421f ("tdfxfb: make use of DDC information about connected monitor") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/tdfxfb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/video/fbdev/tdfxfb.c b/drivers/video/fbdev/tdfxfb.c index 4c4e53aaea3ad..ac74ccda4108c 100644 --- a/drivers/video/fbdev/tdfxfb.c +++ b/drivers/video/fbdev/tdfxfb.c @@ -1551,6 +1551,7 @@ static int tdfxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id) out_err_iobase: #ifdef CONFIG_FB_3DFX_I2C + fb_destroy_modelist(&info->modelist); tdfxfb_delete_i2c_busses(default_par); #endif arch_phys_wc_del(default_par->wc_cookie); From dae8f6ddc35cb1673dba32d2fd8f1f85cd377adf Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Thu, 14 May 2026 13:54:34 +0530 Subject: [PATCH 2264/2758] fbdev: carminefb: fix potential memory leak in alloc_carmine_fb() commit 6fcca16a2b19c37f60693c56cbc0c923364ff3ef upstream. The memory allocated for modelist in fb_videomode_to_modelist() is not freed in the subsequent error path. Fix that by calling fb_destroy_modelist() Fixes: 2ece5f43b041 ("fbdev: add the carmine FB driver") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/carminefb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/video/fbdev/carminefb.c b/drivers/video/fbdev/carminefb.c index bd4bff6a24844..18f02dd881e1c 100644 --- a/drivers/video/fbdev/carminefb.c +++ b/drivers/video/fbdev/carminefb.c @@ -589,6 +589,7 @@ static int alloc_carmine_fb(void __iomem *regs, void __iomem *smem_base, return 0; err_dealloc_cmap: + fb_destroy_modelist(&info->modelist); fb_dealloc_cmap(&info->cmap); err_free_fb: framebuffer_release(info); From 7b96ce9f8e47538c3c6eebbb217c94d696975cac Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Thu, 14 May 2026 13:54:42 +0530 Subject: [PATCH 2265/2758] fbdev: vesafb: fix memory leak in vesafb_probe() commit b15d708995c01bbffe7dcd634a31959f6805bed3 upstream. Since commit 73ce73c30ba9 ("fbdev: Transfer video= option strings to caller; clarify ownership") the string returned from fb_get_options() is expected to be freed by the caller. But the string is not freed in vesafb_probe(). Fix that by freeing the option string after setup. Fixes: 73ce73c30ba9 ("fbdev: Transfer video= option strings to caller; clarify ownership") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Reviewed-by: Thomas Zimmermann Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/vesafb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/video/fbdev/vesafb.c b/drivers/video/fbdev/vesafb.c index f84f4db244bfe..f1902056bd733 100644 --- a/drivers/video/fbdev/vesafb.c +++ b/drivers/video/fbdev/vesafb.c @@ -269,6 +269,7 @@ static int vesafb_probe(struct platform_device *dev) /* ignore error return of fb_get_options */ fb_get_options("vesafb", &option); vesafb_setup(option); + kfree(option); if (si->orig_video_isVGA != VIDEO_TYPE_VLFB) return -ENODEV; From ed3b3eb212441fb5f287dfd24ebafe11d634f009 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Thu, 14 May 2026 13:54:36 +0530 Subject: [PATCH 2266/2758] fbdev: nvidia: fix potential memory leak in nvidiafb_probe() commit 85f5e38c162bdf9dbbe197275d416402712f3707 upstream. In nvidiafb_probe(), the memory allocated for modelist in nvidia_set_fbinfo() is not freed in the subsequent error paths. Fix that by calling fb_destroy_modelist(). Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/nvidia/nvidia.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/video/fbdev/nvidia/nvidia.c b/drivers/video/fbdev/nvidia/nvidia.c index 72b85f475605d..c9dcc9e899487 100644 --- a/drivers/video/fbdev/nvidia/nvidia.c +++ b/drivers/video/fbdev/nvidia/nvidia.c @@ -1421,6 +1421,7 @@ static int nvidiafb_probe(struct pci_dev *pd, const struct pci_device_id *ent) err_out_iounmap_fb: iounmap(info->screen_base); + fb_destroy_modelist(&info->modelist); err_out_free_base1: fb_destroy_modedb(info->monspecs.modedb); nvidia_delete_i2c_busses(par); From 2efd9797331a559b93ab888f451838f87bd6dfa3 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Thu, 14 May 2026 13:54:39 +0530 Subject: [PATCH 2267/2758] fbdev: tridentfb: fix potential memory leak in trident_pci_probe() commit 7a35ec619d9af8ee128320975c1252b8ad65f1e8 upstream. In trident_pci_probe(), the memory allocated for modelist using fb_videomode_to_modelist() is not freed in subsequent error paths. Fix that by calling fb_destroy_modelist(). Fixes: 6a5e3bd0c8bc ("tridentfb: Add DDC support") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/tridentfb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/video/fbdev/tridentfb.c b/drivers/video/fbdev/tridentfb.c index 17b7253b8fbec..5c7ccf76348e4 100644 --- a/drivers/video/fbdev/tridentfb.c +++ b/drivers/video/fbdev/tridentfb.c @@ -1706,6 +1706,7 @@ static int trident_pci_probe(struct pci_dev *dev, return 0; out_unmap2: + fb_destroy_modelist(&info->modelist); if (default_par->ddc_registered) i2c_del_adapter(&default_par->ddc_adapter); kfree(info->pixmap.addr); From 6ed7787c43ecf4ae27a3e700cab53a1ed646c7f8 Mon Sep 17 00:00:00 2001 From: Zhao Dongdong Date: Wed, 10 Jun 2026 15:20:43 +0800 Subject: [PATCH 2268/2758] ASoC: SOF: topology: fix memory leak in snd_sof_load_topology commit d46f9f23897261da53ffbeb89d48a13982ba7d28 upstream. When the topology filename contains "dummy" and tplg_cnt is 0, the function returns -EINVAL directly without freeing the tplg_files allocated by kcalloc() at line 2497. This leaks memory on every such topology load attempt. Fix this by setting ret = -EINVAL and jumping to the out: label, which already handles the kfree(tplg_files) cleanup. Fixes: 99c159279c6d ("ASoC: SOF: don't check the existence of dummy topology") Cc: stable@vger.kernel.org Signed-off-by: Zhao Dongdong Acked-by: Peter Ujfalusi Link: https://patch.msgid.link/tencent_3EED6D778DC52C3703A2D1EE8119372E8E08@qq.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/sof/topology.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 7336ff18a9402..8f6f89b983eb0 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2537,6 +2537,8 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) if (strstr(file, "dummy")) { dev_err(scomp->dev, "Function topology is required, please upgrade sof-firmware\n"); + + kfree(tplg_files); return -EINVAL; } tplg_files[0] = file; From f4933e1d11b97b6a0951648b7c3e53850e1b33a9 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 9 Jun 2026 11:34:58 +0300 Subject: [PATCH 2269/2758] ASoC: SOF: ipc3-control: Fix heap overflow in bytes_ext put/get commit fd46668d538993218eea19c6925c868ac0f2630c upstream. The ipc_control_data buffer is allocated as kzalloc(max_size), where max_size covers the entire struct sof_ipc_ctrl_data including its flexible array payload. However, the bounds checks in bytes_ext_put and _bytes_ext_get compared user data lengths against max_size directly, ignoring that cdata->data sits at an offset of sizeof(struct sof_ipc_ctrl_data) bytes into the allocation. This allowed writing up to sizeof(struct sof_ipc_ctrl_data) bytes past the end of the heap buffer from unprivileged userspace via the ALSA TLV kcontrol interface, and similarly allowed over-reading adjacent heap data on the get path. Fix all bounds checks to subtract sizeof(*cdata) from max_size so they reflect the actual space available at the cdata->data offset. Also fix the error-path restore in bytes_ext_put which wrote to cdata->data instead of cdata, causing the same overflow. Fixes: 67ec2a091630 ("ASoC: SOF: Add bytes_ext control IPC ops for IPC3") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Reviewed-by: Liam Girdwood Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260609083458.31193-7-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/sof/ipc3-control.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index e8654edab90bc..17c12abeb5d02 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -398,9 +398,17 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol, } /* be->max is coming from topology */ - if (header.length > scontrol->max_size) { - dev_err_ratelimited(scomp->dev, "Bytes data size %d exceeds max %zu\n", - header.length, scontrol->max_size); + if (header.length > scontrol->max_size - sizeof(*cdata)) { + dev_err_ratelimited(scomp->dev, "Bytes data size %u exceeds max %zu\n", + header.length, scontrol->max_size - sizeof(*cdata)); + return -EINVAL; + } + + /* Ensure the data is large enough to contain the ABI header */ + if (header.length < sizeof(struct sof_abi_hdr)) { + dev_err_ratelimited(scomp->dev, + "Bytes data size %u less than ABI header %zu\n", + header.length, sizeof(struct sof_abi_hdr)); return -EINVAL; } @@ -436,7 +444,7 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol, } /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ - if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) { + if (cdata->data->size > scontrol->max_size - sizeof(*cdata) - sizeof(struct sof_abi_hdr)) { dev_err_ratelimited(scomp->dev, "Mismatch in ABI data size (truncated?)\n"); goto err_restore; } @@ -452,7 +460,7 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol, err_restore: /* If we have an issue, we restore the old, valid bytes control data */ if (scontrol->old_ipc_control_data) { - memcpy(cdata->data, scontrol->old_ipc_control_data, scontrol->max_size); + memcpy(cdata, scontrol->old_ipc_control_data, scontrol->max_size); kfree(scontrol->old_ipc_control_data); scontrol->old_ipc_control_data = NULL; } @@ -491,10 +499,13 @@ static int _sof_ipc3_bytes_ext_get(struct snd_sof_control *scontrol, } /* check data size doesn't exceed max coming from topology */ - if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) { - dev_err_ratelimited(scomp->dev, "User data size %d exceeds max size %zu\n", + if (cdata->data->size > scontrol->max_size - sizeof(*cdata) - + sizeof(struct sof_abi_hdr)) { + dev_err_ratelimited(scomp->dev, + "User data size %u exceeds max size %zu\n", cdata->data->size, - scontrol->max_size - sizeof(struct sof_abi_hdr)); + scontrol->max_size - sizeof(*cdata) - + sizeof(struct sof_abi_hdr)); return -EINVAL; } From 2a591bf6fd41fd14bdae689aafac4a9ee702c23c Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 9 Jun 2026 11:34:56 +0300 Subject: [PATCH 2270/2758] ASoC: SOF: ipc3-control: Validate size in snd_sof_update_control commit 390aa4c9339bb0ec0bc8d554e830faf93ca9d49e upstream. In snd_sof_update_control(), firmware-provided cdata->num_elems is checked against local_cdata->data->size but never against the actual allocation size. If local_cdata->data->size was previously set to an inconsistent value, the memcpy could write past the allocated buffer. Add a bounds check to ensure num_elems fits within the available space in the ipc_control_data allocation before copying. Fixes: 10f461d79c2d ("ASoC: SOF: Add IPC3 topology control ops") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Reviewed-by: Liam Girdwood Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260609083458.31193-5-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/sof/ipc3-control.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index 17c12abeb5d02..d1697401b1da0 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -555,6 +555,15 @@ static void snd_sof_update_control(struct snd_sof_control *scontrol, return; } + /* Verify the size fits within the allocation */ + if (cdata->num_elems > scontrol->max_size - sizeof(*local_cdata) - + sizeof(*local_cdata->data)) { + dev_err(scomp->dev, + "cdata binary size %u exceeds buffer\n", + cdata->num_elems); + return; + } + /* copy the new binary data */ memcpy(local_cdata->data, cdata->data, cdata->num_elems); } else if (cdata->num_elems != scontrol->num_channels) { From f6e424835cc05d215c57b6370b2c1e353dd02915 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Wed, 27 May 2026 10:55:47 -0300 Subject: [PATCH 2271/2758] ASoC: mediatek: mt8192: Check runtime resume during probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit e24d5dde56a50946020b134fa8448869093db76a upstream. The MT8192 AFE probe enables runtime PM temporarily while reinitializing the regmap cache from hardware, but it uses pm_runtime_get_sync() without checking the return value. If runtime resume fails, probe keeps going without the device necessarily being accessible, and pm_runtime_get_sync() may leave the PM usage count incremented. The regmap_reinit_cache() failure path also returns before dropping the temporary PM reference and before clearing pm_runtime_bypass_reg_ctl. Use pm_runtime_resume_and_get() so resume failures do not leak a usage count, and clear the temporary bypass flag after dropping the probe PM reference on all regmap_reinit_cache() outcomes. Fixes: 125ab5d588b0 ("ASoC: mediatek: mt8192: add platform driver") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260527-asoc-mt8192-probe-cleanup-v1-2-1bb834d05b72@gmail.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/mediatek/mt8192/mt8192-afe-pcm.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c index 3d32fe46118ec..dfae512c470ba 100644 --- a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c +++ b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c @@ -2218,15 +2218,19 @@ static int mt8192_afe_pcm_dev_probe(struct platform_device *pdev) /* enable clock for regcache get default value from hw */ afe_priv->pm_runtime_bypass_reg_ctl = true; - pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); + if (ret) { + afe_priv->pm_runtime_bypass_reg_ctl = false; + return dev_err_probe(dev, ret, "failed to resume device\n"); + } ret = regmap_reinit_cache(afe->regmap, &mt8192_afe_regmap_config); - if (ret) - return dev_err_probe(dev, ret, "regmap_reinit_cache fail\n"); - pm_runtime_put_sync(dev); afe_priv->pm_runtime_bypass_reg_ctl = false; + if (ret) + return dev_err_probe(dev, ret, "regmap_reinit_cache fail\n"); + regcache_cache_only(afe->regmap, true); regcache_mark_dirty(afe->regmap); From 4c9df23e121f1095f02cc7e1531ce3a6d74ff697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Wed, 27 May 2026 10:55:46 -0300 Subject: [PATCH 2272/2758] ASoC: mediatek: mt8192: Release reserved memory on cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 965e17ae6751c5d3302430c8ebd650e72d45a85f upstream. The MT8192 AFE probe calls of_reserved_mem_device_init() and falls back to preallocated buffers when no reserved memory region is available. When the reserved memory assignment succeeds, however, the driver never releases it. Register a devm cleanup action after a successful reserved-memory assignment so the assignment is released on probe failure and driver unbind. Fixes: ec4a10ca4a68 ("ASoC: mediatek: use reserved memory or enable buffer pre-allocation") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260527-asoc-mt8192-probe-cleanup-v1-1-1bb834d05b72@gmail.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/mediatek/mt8192/mt8192-afe-pcm.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c index dfae512c470ba..db0ae44a86afe 100644 --- a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c +++ b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c @@ -2155,6 +2155,11 @@ static const dai_register_cb dai_register_cbs[] = { mt8192_dai_memif_register, }; +static void mt8192_afe_release_reserved_mem(void *data) +{ + of_reserved_mem_device_release(data); +} + static int mt8192_afe_pcm_dev_probe(struct platform_device *pdev) { struct mtk_base_afe *afe; @@ -2184,6 +2189,10 @@ static int mt8192_afe_pcm_dev_probe(struct platform_device *pdev) if (ret) { dev_info(dev, "no reserved memory found, pre-allocating buffers instead\n"); afe->preallocate_buffers = true; + } else { + ret = devm_add_action_or_reset(dev, mt8192_afe_release_reserved_mem, dev); + if (ret) + return ret; } /* init audio related clock */ From 80506fcac597770066cc95f6cd446d9f71ba8ce9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Wed, 27 May 2026 10:41:49 -0300 Subject: [PATCH 2273/2758] ASoC: mediatek: mt8183: Check runtime resume during probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit f0334fbfd107682d0c95f3f71e25f6127038e2b9 upstream. The MT8183 AFE probe uses pm_runtime_get_sync() before reading hardware defaults into the regmap cache, but does not check whether runtime resume failed. If regmap_reinit_cache() then fails, the temporary runtime PM usage count is also not released. Use pm_runtime_resume_and_get() so resume failures abort probe without leaking a usage count, and release the temporary reference before handling the regmap cache result. Fixes: a94aec035a12 ("ASoC: mediatek: mt8183: add platform driver") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260527-asoc-mt8183-probe-cleanup-v1-2-4f4f5593c8d1@gmail.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/mediatek/mt8183/mt8183-afe-pcm.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c index a7fef772760a6..7463018ea3811 100644 --- a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c +++ b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c @@ -833,17 +833,21 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev) /* enable clock for regcache get default value from hw */ afe_priv->pm_runtime_bypass_reg_ctl = true; - pm_runtime_get_sync(dev); - - ret = regmap_reinit_cache(afe->regmap, &mt8183_afe_regmap_config); + ret = pm_runtime_resume_and_get(dev); if (ret) { - dev_err(dev, "regmap_reinit_cache fail, ret %d\n", ret); + afe_priv->pm_runtime_bypass_reg_ctl = false; goto err_pm_disable; } + ret = regmap_reinit_cache(afe->regmap, &mt8183_afe_regmap_config); pm_runtime_put_sync(dev); afe_priv->pm_runtime_bypass_reg_ctl = false; + if (ret) { + dev_err(dev, "regmap_reinit_cache fail, ret %d\n", ret); + goto err_pm_disable; + } + regcache_cache_only(afe->regmap, true); regcache_mark_dirty(afe->regmap); From 03009465312ca35137f84cf84c95686137608deb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Wed, 27 May 2026 10:41:48 -0300 Subject: [PATCH 2274/2758] ASoC: mediatek: mt8183: Release reserved memory on cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit bee65e00c0924ebecf97718d95dcf4a05ee36471 upstream. The MT8183 AFE probe can assign reserved memory with of_reserved_mem_device_init(), but the assignment is never released on driver removal or later probe failures. Register a devm cleanup action so the reserved memory assignment is released consistently, matching newer Mediatek AFE drivers. Fixes: ec4a10ca4a68 ("ASoC: mediatek: use reserved memory or enable buffer pre-allocation") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260527-asoc-mt8183-probe-cleanup-v1-1-4f4f5593c8d1@gmail.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/mediatek/mt8183/mt8183-afe-pcm.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c index 7463018ea3811..2634699534db7 100644 --- a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c +++ b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c @@ -766,6 +766,11 @@ static const dai_register_cb dai_register_cbs[] = { mt8183_dai_memif_register, }; +static void mt8183_afe_release_reserved_mem(void *data) +{ + of_reserved_mem_device_release(data); +} + static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev) { struct mtk_base_afe *afe; @@ -794,6 +799,12 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev) if (ret) { dev_info(dev, "no reserved memory found, pre-allocating buffers instead\n"); afe->preallocate_buffers = true; + } else { + ret = devm_add_action_or_reset(dev, + mt8183_afe_release_reserved_mem, + dev); + if (ret) + return ret; } /* initial audio related clock */ From 214af790e3a33ab73587de4c925c60a550eae9c6 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 16 Jun 2026 18:02:57 +0100 Subject: [PATCH 2275/2758] ASoC: qcom: q6apm: fix NULL pointer dereference in graph_callback commit 2e9261761b35f0b67b7487688cd1365f535be0b3 upstream. When q6apm_free_fragments() is called it frees rx_data.buf/tx_data.buf and sets them to NULL under graph->lock. A late DSP buffer-done response can race with this: graph_callback() passes the !graph->ar_graph guard (not yet NULL), acquires the lock, but then dereferences a now-NULL buf pointer to read buf[token].phys, crashing at virtual address 0x10. Add a NULL check for buf inside the mutex-protected section in both the write-done (DATA_CMD_RSP_WR_SH_MEM_EP_DATA_BUFFER_DONE_V2) and read-done (DATA_CMD_RSP_RD_SH_MEM_EP_DATA_BUFFER_V2) handlers and bail out cleanly if buffers have already been freed. This problem is only shown up recently while apr bus was updated to process the commands per service rather from single global queue. Fixes: 5477518b8a0e ("ASoC: qdsp6: audioreach: add q6apm support") Cc: Stable@vger.kernel.org Assisted-by: Claude:claude-4-6-sonnet Reported-by: Val Packett Closes: https://lore.kernel.org/all/133ced18-1aa9-475d-80d8-6120678bdde4@packett.cool/ Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260616170257.9381-1-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/qcom/qdsp6/q6apm.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 2ab378fb50326..2873e831a8deb 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -549,6 +549,10 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op) token = hdr->token & APM_WRITE_TOKEN_MASK; done = data->payload; + if (!graph->rx_data.buf) { + mutex_unlock(&graph->lock); + break; + } phys = graph->rx_data.buf[token].phys; mutex_unlock(&graph->lock); /* token numbering starts at 0 */ @@ -571,6 +575,10 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op) client_event = APM_CLIENT_EVENT_DATA_READ_DONE; mutex_lock(&graph->lock); rd_done = data->payload; + if (!graph->tx_data.buf) { + mutex_unlock(&graph->lock); + break; + } phys = graph->tx_data.buf[hdr->token].phys; mutex_unlock(&graph->lock); /* token numbering starts at 0 */ From eeef3b81f449560653662df2dde6f6fe247c5365 Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Tue, 19 May 2026 17:23:28 -0400 Subject: [PATCH 2276/2758] netfilter: nf_conntrack_irc: fix parse_dcc() off-by-one OOB read commit ef6400ca25a13fd6dedbe8ef4a1d0979bbbfe88a upstream. parse_dcc() treats data_end as an inclusive end pointer, but its only caller passes data_limit = ib_ptr + datalen, which points one past the last valid byte. The newline search loop iterates while tmp <= data_end, so when no newline is present, *tmp is read at tmp == data_end, one byte beyond the region filled by skb_header_pointer(). irc_buffer is kmalloc'd as MAX_SEARCH_SIZE + 1 bytes and datalen is capped at MAX_SEARCH_SIZE, so the stray read does not fault. The byte is uninitialized or stale; if it contains an ASCII digit, simple_strtoul will consume it and produce a wrong DCC IP or port in the conntrack expectation. The extra allocation byte is also a fragile guard: if the cap or allocation size changes, this becomes a real out-of-bounds read. Change the loop and its post-loop check to use strict less-than, consistent with the caller's exclusive-end convention. Update the function comment accordingly. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Muhammad Bilal Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_conntrack_irc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nf_conntrack_irc.c b/net/netfilter/nf_conntrack_irc.c index 6a72848bd3513..0c117b8492e99 100644 --- a/net/netfilter/nf_conntrack_irc.c +++ b/net/netfilter/nf_conntrack_irc.c @@ -59,7 +59,7 @@ static const char *const dccprotos[] = { /* tries to get the ip_addr and port out of a dcc command * return value: -1 on failure, 0 on success * data pointer to first byte of DCC command data - * data_end pointer to last byte of dcc command data + * data_end one past end of data * ip returns parsed ip of dcc command * port returns parsed port of dcc command * ad_beg_p returns pointer to first byte of addr data @@ -77,10 +77,10 @@ static int parse_dcc(char *data, const char *data_end, __be32 *ip, /* Make sure we have a newline character within the packet boundaries * because simple_strtoul parses until the first invalid character. */ - for (tmp = data; tmp <= data_end; tmp++) + for (tmp = data; tmp < data_end; tmp++) if (*tmp == '\n') break; - if (tmp > data_end || *tmp != '\n') + if (tmp >= data_end || *tmp != '\n') return -1; *ad_beg_p = data; From 29e06c8f616c67784ddd5afe7887dc6ba53cd68e Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 11 Apr 2026 19:57:21 +0100 Subject: [PATCH 2277/2758] netfilter: nfnl_cthelper: apply per-class values when updating policies commit d738feccb98cb224ebabecb703e98f5008276bff upstream. When a userspace conntrack helper with multiple expectation classes is updated via nfnetlink, every class ends up with the first class's max_expected and timeout values. nfnl_cthelper_update_policy_all() validates each new policy into the corresponding slot of the temporary new_policy array, but the second loop that commits the values into the live helper dereferences new_policy as a pointer instead of indexing it, so every iteration reads new_policy[0] regardless of i. An update that changes per-class values is silently collapsed onto class 0's values with no error returned to userspace. Index the temporary array by i in the commit loop so each class gets its own validated values. Fixes: 2c422257550f ("netfilter: nfnl_cthelper: fix runtime expectation policy updates") Cc: stable@vger.kernel.org Signed-off-by: David Carlier Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nfnetlink_cthelper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index 96fc65414502c..866f46ecfae91 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -354,8 +354,8 @@ static int nfnl_cthelper_update_policy_all(struct nlattr *tb[], for (i = 0; i < helper->expect_class_max + 1; i++) { policy = (struct nf_conntrack_expect_policy *) &helper->expect_policy[i]; - policy->max_expected = new_policy->max_expected; - policy->timeout = new_policy->timeout; + policy->max_expected = new_policy[i].max_expected; + policy->timeout = new_policy[i].timeout; } err: From 5b2d4f0010018a7aa3495aa1dbf1b7a34011e7aa Mon Sep 17 00:00:00 2001 From: Wyatt Feng Date: Thu, 11 Jun 2026 15:21:42 +0800 Subject: [PATCH 2278/2758] netfilter: xt_cluster: reject template conntracks in hash match commit 5feba91006ec92da57acc1cc2e34df623b98541e upstream. xt_cluster_mt() treats any non-NULL nf_ct_get() result as a fully initialized conntrack and passes it to xt_cluster_hash(). This causes a state confusion bug when the raw table CT target attaches a template conntrack to skb->_nfct before normal conntrack processing. Templates carry IPS_TEMPLATE status but do not have a valid tuple for hashing yet, so xt_cluster_hash() can hit its WARN_ON() path on the zeroed l3num field. Reject template conntracks before hashing them. This matches existing netfilter handling for template objects and avoids hashing incomplete conntrack state. Fixes: 0269ea493734 ("netfilter: xtables: add cluster match") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Signed-off-by: Wyatt Feng Signed-off-by: Ren Wei Signed-off-by: Pablo Neira Ayuso Signed-off-by: Greg Kroah-Hartman --- net/netfilter/xt_cluster.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/xt_cluster.c b/net/netfilter/xt_cluster.c index 908fd5f2c3c84..eaf2511d63f0e 100644 --- a/net/netfilter/xt_cluster.c +++ b/net/netfilter/xt_cluster.c @@ -107,7 +107,7 @@ xt_cluster_mt(const struct sk_buff *skb, struct xt_action_param *par) } ct = nf_ct_get(skb, &ctinfo); - if (ct == NULL) + if (!ct || nf_ct_is_template(ct)) return false; if (ct->master) From 47b3af24de5fbed4bf2952de0f5294ef1a338a26 Mon Sep 17 00:00:00 2001 From: Haoze Xie Date: Mon, 8 Jun 2026 13:43:44 +0800 Subject: [PATCH 2279/2758] netfilter: nf_queue: pin bridge device while NFQUEUE holds fake dst commit c9c9b37f8c5505224e8d206184df3bb668ee00cf upstream. The br_netfilter fake rtable is embedded in struct net_bridge and is attached to bridged packets with skb_dst_set_noref(). If such a packet is queued to NFQUEUE, __nf_queue() upgrades that fake dst with skb_dst_force(). At that point the queued skb can hold a real dst reference after bridge teardown has started. The problem is not that every bridged packet needs its own dst reference. The problem is that NFQUEUE can keep the bridge private fake dst alive after unregister begins. Fix this by keeping the bridge fake dst model unchanged and pinning the bridge master device only while the packet sits in NFQUEUE. Record the bridge device in nf_queue_entry when the queued skb carries a bridge fake dst, take a device reference for the queue lifetime, and drop it when the queue entry is freed. Also make sure queued entries are reaped when that bridge device goes down, and drop the redundant nf_bridge_info_exists() test from the fake dst detection. This keeps netdev_priv(br->dev) alive until verdict completion, so the embedded fake rtable and its metrics backing storage cannot be freed out from under dst_release(). It also avoids the constant refcount bump and avoids using ipv4-specific dst helpers for IPv6 bridge traffic. Fixes: 34666d467cbf ("netfilter: bridge: move br_netfilter out of the core") Cc: stable@kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Signed-off-by: Haoze Xie Signed-off-by: Ren Wei Signed-off-by: Pablo Neira Ayuso Signed-off-by: Greg Kroah-Hartman --- include/net/netfilter/nf_queue.h | 1 + net/netfilter/nf_queue.c | 14 ++++++++++++++ net/netfilter/nfnetlink_queue.c | 3 +++ 3 files changed, 18 insertions(+) diff --git a/include/net/netfilter/nf_queue.h b/include/net/netfilter/nf_queue.h index 3978c3174cdbe..fc3e81c07364f 100644 --- a/include/net/netfilter/nf_queue.h +++ b/include/net/netfilter/nf_queue.h @@ -18,6 +18,7 @@ struct nf_queue_entry { unsigned int id; unsigned int hook_index; /* index in hook_entries->hook[] */ #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) + struct net_device *bridge_dev; struct net_device *physin; struct net_device *physout; #endif diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index 57b450024a99e..73363ceedebe3 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c @@ -68,6 +68,7 @@ static void nf_queue_entry_release_refs(struct nf_queue_entry *entry) nf_queue_sock_put(state->sk); #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) + dev_put(entry->bridge_dev); dev_put(entry->physin); dev_put(entry->physout); #endif @@ -84,6 +85,8 @@ static void __nf_queue_entry_init_physdevs(struct nf_queue_entry *entry) { #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) const struct sk_buff *skb = entry->skb; + struct dst_entry *dst = skb_dst(skb); + struct net_device *dev = NULL; if (nf_bridge_info_exists(skb)) { entry->physin = nf_bridge_get_physindev(skb, entry->state.net); @@ -92,6 +95,16 @@ static void __nf_queue_entry_init_physdevs(struct nf_queue_entry *entry) entry->physin = NULL; entry->physout = NULL; } + + if (entry->state.pf == NFPROTO_BRIDGE && + dst && (dst->flags & DST_FAKE_RTABLE)) + dev = dst_dev_rcu(dst); + + /* Must hold a reference on the bridge device: dst_hold() protects + * the dst itself, but the fake rtable is embedded in bridge-private + * storage that netdevice teardown can free independently. + */ + entry->bridge_dev = dev; #endif } @@ -108,6 +121,7 @@ bool nf_queue_entry_get_refs(struct nf_queue_entry *entry) dev_hold(state->out); #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) + dev_hold(entry->bridge_dev); dev_hold(entry->physin); dev_hold(entry->physout); #endif diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index e0cf9977887f6..d4f5003280d70 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -1270,6 +1270,9 @@ dev_cmp(struct nf_queue_entry *entry, unsigned long ifindex) if (physinif == ifindex || physoutif == ifindex) return 1; + + if (entry->bridge_dev && entry->bridge_dev->ifindex == ifindex) + return 1; #endif if (entry->skb_dev && entry->skb_dev->ifindex == ifindex) return 1; From f68305267ebda7e839b5e8f77e8d77535a3d5a0f Mon Sep 17 00:00:00 2001 From: Theodor Arsenij Larionov-Trichkine Date: Mon, 29 Jun 2026 12:53:11 +0200 Subject: [PATCH 2280/2758] netfilter: nft_fib: reject fib expression on the netdev egress hook commit d07955dd34ecae17d35d8c7d0a273a3fba653a8c upstream. A fib expression in a netdev egress base chain dereferences nft_in(pkt), NULL on the transmit path, causing a NULL pointer dereference at eval. nft_fib_validate() masks the hook with NF_INET_* values, but netdev hook numbers are a separate enum that aliases them (NF_NETDEV_EGRESS == NF_INET_LOCAL_IN), so an egress chain passes validation and then faults. Add nft_fib_netdev_validate() that limits each result/flag to the netdev hook where the device it reads exists: the input-device cases (OIF, OIFNAME, ADDRTYPE with F_IIF) to ingress, the output-device case (ADDRTYPE with F_OIF) to egress, ADDRTYPE with no device flag to both. Also restrict nft_fib_validate() to NFPROTO_IPV4/IPV6/INET so its NF_INET_* masks are not applied to another family's hooks. Fixes: 42df6e1d221d ("netfilter: Introduce egress hook") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/netfilter-devel/ajxsjcDOnwllMfoR@strlen.de/ Signed-off-by: Theodor Arsenij Larionov-Trichkine Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nft_fib.c | 9 +++++++++ net/netfilter/nft_fib_netdev.c | 29 ++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/net/netfilter/nft_fib.c b/net/netfilter/nft_fib.c index a1632e308f182..7529f58a6d36f 100644 --- a/net/netfilter/nft_fib.c +++ b/net/netfilter/nft_fib.c @@ -31,6 +31,15 @@ int nft_fib_validate(const struct nft_ctx *ctx, const struct nft_expr *expr) const struct nft_fib *priv = nft_expr_priv(expr); unsigned int hooks; + switch (ctx->family) { + case NFPROTO_IPV4: + case NFPROTO_IPV6: + case NFPROTO_INET: + break; + default: + return -EOPNOTSUPP; + } + switch (priv->result) { case NFT_FIB_RESULT_OIF: case NFT_FIB_RESULT_OIFNAME: diff --git a/net/netfilter/nft_fib_netdev.c b/net/netfilter/nft_fib_netdev.c index 3f3478abd8454..5774a75440278 100644 --- a/net/netfilter/nft_fib_netdev.c +++ b/net/netfilter/nft_fib_netdev.c @@ -50,6 +50,33 @@ static void nft_fib_netdev_eval(const struct nft_expr *expr, regs->verdict.code = NFT_BREAK; } +static int nft_fib_netdev_validate(const struct nft_ctx *ctx, + const struct nft_expr *expr) +{ + const struct nft_fib *priv = nft_expr_priv(expr); + unsigned int hooks; + + switch (priv->result) { + case NFT_FIB_RESULT_OIF: + case NFT_FIB_RESULT_OIFNAME: + hooks = (1 << NF_NETDEV_INGRESS); + break; + case NFT_FIB_RESULT_ADDRTYPE: + if (priv->flags & NFTA_FIB_F_IIF) + hooks = (1 << NF_NETDEV_INGRESS); + else if (priv->flags & NFTA_FIB_F_OIF) + hooks = (1 << NF_NETDEV_EGRESS); + else + hooks = (1 << NF_NETDEV_INGRESS) | + (1 << NF_NETDEV_EGRESS); + break; + default: + return -EINVAL; + } + + return nft_chain_validate_hooks(ctx->chain, hooks); +} + static struct nft_expr_type nft_fib_netdev_type; static const struct nft_expr_ops nft_fib_netdev_ops = { .type = &nft_fib_netdev_type, @@ -57,7 +84,7 @@ static const struct nft_expr_ops nft_fib_netdev_ops = { .eval = nft_fib_netdev_eval, .init = nft_fib_init, .dump = nft_fib_dump, - .validate = nft_fib_validate, + .validate = nft_fib_netdev_validate, }; static struct nft_expr_type nft_fib_netdev_type __read_mostly = { From b843a96252f672332837ea2ecb7c8db0acf68e20 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 26 Jun 2026 13:24:49 +0200 Subject: [PATCH 2281/2758] netfilter: nf_conntrack_sip: validate skb_dst() before accessing it commit e5e24a365a5e024efef63cc49abb345fbd4852c5 upstream. tc ingress and openvswitch do not guarantee routing information to be available. These subsystems use the conntrack helper infrastructure, and the SIP helper relies on the skb_dst() to be present if sip_external_media is set to 1 (which is disabled by default as a module parameter). This effectively disables the sip_external_media toggle for these subsystems without resulting in a crash. Fixes: cae3a2627520 ("openvswitch: Allow attaching helpers to ct action") Fixes: b57dc7c13ea9 ("net/sched: Introduce action ct") Cc: stable@vger.kernel.org Reported-by: Ren Wei Signed-off-by: Pablo Neira Ayuso Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_conntrack_sip.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index 5ec3a4a4bbd70..f3f90a8663389 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -956,7 +956,6 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, return NF_ACCEPT; saddr = &ct->tuplehash[!dir].tuple.src.u3; } else if (sip_external_media) { - struct net_device *dev = skb_dst(skb)->dev; struct dst_entry *dst = NULL; struct flowi fl; @@ -978,7 +977,11 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, * through the same interface as the signalling peer. */ if (dst) { - bool external_media = (dst->dev == dev); + const struct dst_entry *this_dst = skb_dst(skb); + bool external_media = false; + + if (this_dst && dst->dev == this_dst->dev) + external_media = true; dst_release(dst); if (external_media) From e74f9680e1b64872a51cc7b5bda1edaaa08aa51f Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 16 Jun 2026 13:26:26 +0200 Subject: [PATCH 2282/2758] netfilter: nft_set_pipapo: don't leak bad clone into future transaction commit 47e65eff50691f0a5b79d325e28d83ec1da43bcf upstream. On memory allocation failure the cloned nft_pipapo_match can enter a bad state: - some fields can have their lookup tables resized while others did not - bits might have been toggled - scratch map can be undersized which also means m->bsize_max can be lower than what is required This means that the next insertion in the same batch can trigger out-of-bounds writes. Furthermore, a failure in the first can result in the bad clone to leak into the next transaction because the abort callback is never executed in this case (the upper layer saw an error and no attempt to allocate a transactional request was made). Record a state for the nft_pipapo_match structure: - NEW (pristine clone) - MOD (modified clone with good state) - ERR (potentially bogus content) Then make it so that deletes and insertions fail when the clone entered ERR state. In case the very first insert attempt results in an error, free the clone right away. Fixes: 3c4287f62044 ("nf_tables: Add set type for arbitrary concatenation of ranges") Cc: stable@vger.kernel.org Reported-and-tested-by: Seesee Reviewed-by: Stefano Brivio Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nft_set_pipapo.c | 34 +++++++++++++++++++++++++++++----- net/netfilter/nft_set_pipapo.h | 8 ++++++++ 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/net/netfilter/nft_set_pipapo.c b/net/netfilter/nft_set_pipapo.c index 50d4a4f04309a..61b8601ee3770 100644 --- a/net/netfilter/nft_set_pipapo.c +++ b/net/netfilter/nft_set_pipapo.c @@ -342,6 +342,8 @@ #include "nft_set_pipapo_avx2.h" #include "nft_set_pipapo.h" +static void nft_pipapo_abort(const struct nft_set *set); + /** * pipapo_refill() - For each set bit, set bits from selected mapping table item * @map: Bitmap to be scanned for set bits @@ -1296,7 +1298,7 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set, const u8 *start_p, *end_p; int i, bsize_max, err = 0; - if (!m) + if (!m || m->state == NFT_PIPAPO_CLONE_ERR) return -ENOMEM; if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END)) @@ -1367,8 +1369,10 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set, else ret = pipapo_expand(f, start, end, f->groups * f->bb); - if (ret < 0) - return ret; + if (ret < 0) { + err = ret; + goto abort; + } if (f->bsize > bsize_max) bsize_max = f->bsize; @@ -1384,7 +1388,7 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set, err = pipapo_realloc_scratch(m, bsize_max); if (err) - return err; + goto abort; m->bsize_max = bsize_max; } else { @@ -1396,7 +1400,26 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set, pipapo_map(m, rulemap, e); + m->state = NFT_PIPAPO_CLONE_MOD; return 0; +abort: + DEBUG_NET_WARN_ON_ONCE(m->state == NFT_PIPAPO_CLONE_ERR); + + /* Two rollback cases: + * 1) no previous changes. nft_pipapo_abort is not + * guaranteed to be invoked (there might be no further + * add/delete requests coming after this). + * + * 2) we had previous changes: there are transaction + * records pointing to this set. Leave the rollback to + * the transaction handling. + */ + if (m->state == NFT_PIPAPO_CLONE_NEW) + nft_pipapo_abort(set); /* releases m */ + else + m->state = NFT_PIPAPO_CLONE_ERR; + + return err; } /** @@ -1473,6 +1496,7 @@ static struct nft_pipapo_match *pipapo_clone(struct nft_pipapo_match *old) dst++; } + new->state = NFT_PIPAPO_CLONE_NEW; return new; out_mt: @@ -1896,7 +1920,7 @@ nft_pipapo_deactivate(const struct net *net, const struct nft_set *set, /* removal must occur on priv->clone, if we are low on memory * we have no choice and must fail the removal request. */ - if (!m) + if (!m || m->state == NFT_PIPAPO_CLONE_ERR) return NULL; e = pipapo_get(m, (const u8 *)elem->key.val.data, diff --git a/net/netfilter/nft_set_pipapo.h b/net/netfilter/nft_set_pipapo.h index b82abb03576ed..a19e980d06ef4 100644 --- a/net/netfilter/nft_set_pipapo.h +++ b/net/netfilter/nft_set_pipapo.h @@ -131,9 +131,16 @@ struct nft_pipapo_scratch { unsigned long __map[]; }; +enum nft_pipapo_clone_state { + NFT_PIPAPO_CLONE_NEW, + NFT_PIPAPO_CLONE_MOD, + NFT_PIPAPO_CLONE_ERR, +}; + /** * struct nft_pipapo_match - Data used for lookup and matching * @field_count: Amount of fields in set + * @state: add/delete state; used from control plane * @bsize_max: Maximum lookup table bucket size of all fields, in longs * @scratch: Preallocated per-CPU maps for partial matching results * @rcu: Matching data is swapped on commits @@ -141,6 +148,7 @@ struct nft_pipapo_scratch { */ struct nft_pipapo_match { u8 field_count; + enum nft_pipapo_clone_state state:8; unsigned int bsize_max; struct nft_pipapo_scratch * __percpu *scratch; struct rcu_head rcu; From 0e76e3e886cc9ee027337d5ad39cb96f57b7bdc7 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 1 Jul 2026 07:44:17 +0200 Subject: [PATCH 2283/2758] netfilter: nf_nat_sip: reload possible stale data pointer commit 77e43bcb7ec177e293a5c3f1b91a2c5aebfb6c68 upstream. quoting sashiko: ------------------------------------------------------------------------ [..] noticed a potential memory bug and header corruption involving the SIP NAT helper. In net/netfilter/nf_nat_sip.c:nf_nat_sip(): if (skb_ensure_writable(skb, skb->len)) { nf_ct_helper_log(skb, ct, "cannot mangle packet"); return NF_DROP; } uh = (void *)skb->data + protoff; uh->dest = ct_sip_info->forced_dport; if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo, protoff, 0, 0, NULL, 0)) { If a cloned or fragmented SKB is reallocated by skb_ensure_writable(), the old data buffer is freed. However, nf_nat_sip() fails to update *dptr to point to the new buffer. It also appears to use nf_nat_mangle_udp_packet() on what could be a TCP packet, which would overwrite the sequence number with a checksum update. ------------------------------------------------------------------------ nf_conntrack_sip linerizes skbs, hence no fragmented skb can be seen. But clones are possible, so rebuild dptr. Disable nf_nat_mangle_udp_packet() branch for TCP streams. It doesn't look like this can ever happen, else we should have received bug reports about this, so just check the conntrack is UDP and drop otherwise. The calling conntrack_sip set ->forced_dport for SIP_HDR_VIA_UDP messages, so I don't think this is ever expected to be true for a TCP stream. Fixes: 7266507d8999 ("netfilter: nf_ct_sip: support Cisco 7941/7945 IP phones") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_nat_sip.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c index 67c04d8143ab9..aea02f6aff092 100644 --- a/net/netfilter/nf_nat_sip.c +++ b/net/netfilter/nf_nat_sip.c @@ -289,13 +289,24 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, /* Mangle destination port for Cisco phones, then fix up checksums */ if (dir == IP_CT_DIR_REPLY && ct_sip_info->forced_dport) { + int doff = *dptr - (const char *)skb->data; struct udphdr *uh; + if (doff <= 0) { + DEBUG_NET_WARN_ON_ONCE(1); + return NF_DROP; + } + + /* ct_sip_info->forced_dport only expected with UDP */ + if (nf_ct_protonum(ct) != IPPROTO_UDP) + return NF_DROP; + if (skb_ensure_writable(skb, skb->len)) { nf_ct_helper_log(skb, ct, "cannot mangle packet"); return NF_DROP; } + *dptr = skb->data + doff; uh = (void *)skb->data + protoff; uh->dest = ct_sip_info->forced_dport; From 00bdce2fda7e430d24cfbc96764a1b96deb31f82 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sun, 5 Jul 2026 16:36:29 -0700 Subject: [PATCH 2284/2758] netfilter: nf_conntrack_reasm: guard mac_header adjustment after IPv6 defrag commit 3b08fed5b7e0d5e3a25d73ef3ba09cd33ade16c9 upstream. nf_ct_frag6_reasm() slides the packet head forward to drop the IPv6 fragment header and then unconditionally advances skb->mac_header: skb->mac_header += sizeof(struct frag_hdr); On the NF_INET_LOCAL_OUT defrag path the skb has no link-layer header yet, so skb->mac_header is still the "not set" sentinel (u16)~0U. Adding sizeof(struct frag_hdr) wraps it to a small value (0xffff + 8 == 7), after which skb_mac_header_was_set() wrongly reports a MAC header is present and skb_mac_header() points into the headroom. The reassembler has done this unconditional add since it was introduced; it was harmless while mac_header was a bare pointer, but wrong once mac_header became a u16 offset whose unset state is the ~0U sentinel tested by skb_mac_header_was_set(). The sibling net/ipv6/reassembly.c does the same relocation and does guard the adjustment; mirror the guard here. Fixes: 9fb9cbb1082d ("[NETFILTER]: Add nf_conntrack subsystem.") Cc: stable@vger.kernel.org Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/ipv6/netfilter/nf_conntrack_reasm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 7edca05dbfc93..599c49bf0a0af 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -348,7 +348,8 @@ static int nf_ct_frag6_reasm(struct frag_queue *fq, struct sk_buff *skb, skb_network_header(skb)[fq->nhoffset] = skb_transport_header(skb)[0]; memmove(skb->head + sizeof(struct frag_hdr), skb->head, (skb->data - skb->head) - sizeof(struct frag_hdr)); - skb->mac_header += sizeof(struct frag_hdr); + if (skb_mac_header_was_set(skb)) + skb->mac_header += sizeof(struct frag_hdr); skb->network_header += sizeof(struct frag_hdr); skb_reset_transport_header(skb); From ecb78fbb03d3f86e2e93767875e273308086a424 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 30 Jun 2026 11:40:54 +0200 Subject: [PATCH 2285/2758] netfilter: flowtable: use dst in this direction when pushing IPIP header commit c328b90c17fc5fa7786503695152880b2afb9326 upstream. When pushing the IPIP header, the route of the other direction is used to calculate the headroom, use the route in this direction. Accessing the other tuple to set the IP source and destination is fine because this tuple does not provide such information to avoid storing redundant information. However, this tuple already provides the dst for this direction, this went unnoticed because this bug affects headroom and iph->frag_off only at this stage. Fixes: d30301ba4b07 ("netfilter: flowtable: Add IPIP tx sw acceleration") Fixes: 93cf357fa797 ("netfilter: flowtable: Add IP6IP6 tx sw acceleration") Cc: stable@vger.kernel.org Acked-by: Lorenzo Bianconi Signed-off-by: Pablo Neira Ayuso Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_flow_table_ip.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c index 29e93ac1e2e40..089f2bc19972f 100644 --- a/net/netfilter/nf_flow_table_ip.c +++ b/net/netfilter/nf_flow_table_ip.c @@ -590,10 +590,10 @@ static int nf_flow_pppoe_push(struct sk_buff *skb, u16 id, static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb, struct flow_offload_tuple *tuple, - __be32 *ip_daddr) + struct dst_entry *dst, __be32 *ip_daddr) { struct iphdr *iph = (struct iphdr *)skb_network_header(skb); - struct rtable *rt = dst_rtable(tuple->dst_cache); + struct rtable *rt = dst_rtable(dst); u8 tos = iph->tos, ttl = iph->ttl; __be16 frag_off = iph->frag_off; u32 headroom = sizeof(*iph); @@ -636,21 +636,22 @@ static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb, static int nf_flow_tunnel_v4_push(struct net *net, struct sk_buff *skb, struct flow_offload_tuple *tuple, - __be32 *ip_daddr) + struct dst_entry *dst, __be32 *ip_daddr) { if (tuple->tun_num) - return nf_flow_tunnel_ipip_push(net, skb, tuple, ip_daddr); + return nf_flow_tunnel_ipip_push(net, skb, tuple, dst, ip_daddr); return 0; } static int nf_flow_tunnel_ip6ip6_push(struct net *net, struct sk_buff *skb, struct flow_offload_tuple *tuple, + struct dst_entry *dst, struct in6_addr **ip6_daddr) { struct ipv6hdr *ip6h = (struct ipv6hdr *)skb_network_header(skb); - struct rtable *rt = dst_rtable(tuple->dst_cache); __u8 dsfield = ipv6_get_dsfield(ip6h); + struct rtable *rt = dst_rtable(dst); struct flowi6 fl6 = { .daddr = tuple->tun.src_v6, .saddr = tuple->tun.dst_v6, @@ -696,10 +697,11 @@ static int nf_flow_tunnel_ip6ip6_push(struct net *net, struct sk_buff *skb, static int nf_flow_tunnel_v6_push(struct net *net, struct sk_buff *skb, struct flow_offload_tuple *tuple, + struct dst_entry *dst, struct in6_addr **ip6_daddr) { if (tuple->tun_num) - return nf_flow_tunnel_ip6ip6_push(net, skb, tuple, ip6_daddr); + return nf_flow_tunnel_ip6ip6_push(net, skb, tuple, dst, ip6_daddr); return 0; } @@ -842,7 +844,8 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb, other_tuple = &flow->tuplehash[!dir].tuple; ip_daddr = other_tuple->src_v4.s_addr; - if (nf_flow_tunnel_v4_push(state->net, skb, other_tuple, &ip_daddr) < 0) + if (nf_flow_tunnel_v4_push(state->net, skb, other_tuple, + tuplehash->tuple.dst_cache, &ip_daddr) < 0) return NF_DROP; switch (tuplehash->tuple.xmit_type) { @@ -1158,6 +1161,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb, ip6_daddr = &other_tuple->src_v6; if (nf_flow_tunnel_v6_push(state->net, skb, other_tuple, + tuplehash->tuple.dst_cache, &ip6_daddr) < 0) return NF_DROP; From 0880c4ed122d0cddc9f29a2b28f055d1f24f0fca Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 30 Jun 2026 11:40:56 +0200 Subject: [PATCH 2286/2758] netfilter: flowtable: support IPIP tunnel with direct xmit commit fa7395c02d95e51bad2952325d2d6503bfbad437 upstream. The combination of IPIP tunnel with direct xmit, eg. bridge device, breaks because no dst_entry is provided to check the skb headroom and to set the iph->frag_off field. This leads to invalid dst usage and can trigger a crash in the tunnel transmit path. Fix this by moving dst_cache and dst_cookie out of the runtime union so that they can be shared by neighbour, xfrm, and direct tunnel flows. For FLOW_OFFLOAD_XMIT_DIRECT tuples carrying tunnel metadata, preserve route state in these shared fields and release it through the common dst release path. Since dst_entry is now available to the three supported xmit modes and dst_release() already deals with NULL dst, remove the xmit type check in nft_flow_dst_release(). Moreover, skip the check if the dst entry is NULL in nf_flow_dst_check() which is now the case for the direct xmit case. Based on patch from Rein Wei . Fixes: d30301ba4b07 ("netfilter: flowtable: Add IPIP tx sw acceleration") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Xin Liu Reported-by: Zhengyang Chen Reported-by: Ren Wei Signed-off-by: Pablo Neira Ayuso Acked-by: Lorenzo Bianconi Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- include/net/netfilter/nf_flow_table.h | 5 +++-- net/netfilter/nf_flow_table_core.c | 12 ++++++++---- net/netfilter/nf_flow_table_ip.c | 3 +-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h index dc5c9b48e65a9..ce414118962f5 100644 --- a/include/net/netfilter/nf_flow_table.h +++ b/include/net/netfilter/nf_flow_table.h @@ -155,11 +155,12 @@ struct flow_offload_tuple { tun_num:2, in_vlan_ingress:2; u16 mtu; + u32 dst_cookie; + struct dst_entry *dst_cache; + union { struct { - struct dst_entry *dst_cache; u32 ifidx; - u32 dst_cookie; }; struct { u32 ifidx; diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c index d06ce0848b68e..2a829b5e8240a 100644 --- a/net/netfilter/nf_flow_table_core.c +++ b/net/netfilter/nf_flow_table_core.c @@ -127,12 +127,18 @@ static int flow_offload_fill_route(struct flow_offload *flow, switch (route->tuple[dir].xmit_type) { case FLOW_OFFLOAD_XMIT_DIRECT: + if (flow_tuple->tun_num) { + flow_tuple->dst_cache = dst; + flow_tuple->dst_cookie = + flow_offload_dst_cookie(flow_tuple); + } memcpy(flow_tuple->out.h_dest, route->tuple[dir].out.h_dest, ETH_ALEN); memcpy(flow_tuple->out.h_source, route->tuple[dir].out.h_source, ETH_ALEN); flow_tuple->out.ifidx = route->tuple[dir].out.ifindex; - dst_release(dst); + if (!flow_tuple->tun_num) + dst_release(dst); break; case FLOW_OFFLOAD_XMIT_XFRM: case FLOW_OFFLOAD_XMIT_NEIGH: @@ -152,9 +158,7 @@ static int flow_offload_fill_route(struct flow_offload *flow, static void nft_flow_dst_release(struct flow_offload *flow, enum flow_offload_tuple_dir dir) { - if (flow->tuplehash[dir].tuple.xmit_type == FLOW_OFFLOAD_XMIT_NEIGH || - flow->tuplehash[dir].tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM) - dst_release(flow->tuplehash[dir].tuple.dst_cache); + dst_release(flow->tuplehash[dir].tuple.dst_cache); } void flow_offload_route_init(struct flow_offload *flow, diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c index 089f2bc19972f..0b78decce8a9b 100644 --- a/net/netfilter/nf_flow_table_ip.c +++ b/net/netfilter/nf_flow_table_ip.c @@ -299,8 +299,7 @@ static bool nf_flow_exceeds_mtu(const struct sk_buff *skb, unsigned int mtu) static inline bool nf_flow_dst_check(struct flow_offload_tuple *tuple) { - if (tuple->xmit_type != FLOW_OFFLOAD_XMIT_NEIGH && - tuple->xmit_type != FLOW_OFFLOAD_XMIT_XFRM) + if (!tuple->dst_cache) return true; return dst_check(tuple->dst_cache, tuple->dst_cookie); From 6ff07ac5405bea4d4ead3559fc123f987576424a Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Wed, 8 Jul 2026 13:27:28 +0800 Subject: [PATCH 2287/2758] netfilter: nf_conncount: fix zone comparison in tuple dedup commit f62c41b4910e65da396ec9a8c40c1fe7fe82e449 upstream. The "already exists" dedup logic in __nf_conncount_add() decides whether a connection has already been counted and can be skipped instead of incrementing the connlimit count. It compares the conntrack zone of a list entry with the zone of the connection being added using nf_ct_zone_id() and nf_ct_zone_equal(), passing conn->zone.dir or zone->dir as the direction argument. Those helpers take enum ip_conntrack_dir values: IP_CT_DIR_ORIGINAL is 0 and IP_CT_DIR_REPLY is 1. However, zone->dir is a u8 bitmask: NF_CT_ZONE_DIR_ORIG is 1, NF_CT_ZONE_DIR_REPL is 2 and NF_CT_DEFAULT_ZONE_DIR is 3. Passing that bitmask as the enum direction shifts the meaning of every non-zero value. An ORIG-only zone passes 1 and is tested as REPLY, while REPL-only and default zones pass 2 or 3 and test bits beyond the valid direction range. In those cases nf_ct_zone_id() can fall back to NF_CT_DEFAULT_ZONE_ID instead of using the real zone id, so different zones can be treated as equal and dedup collapses to tuple equality alone. nf_conncount stores and compares the original-direction tuple for a connection. If an skb already has an attached conntrack entry, get_ct_or_tuple_from_skb() explicitly copies ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, regardless of the packet's ctinfo. Therefore the zone comparison in the tuple dedup path must use IP_CT_DIR_ORIGINAL as well; the zone direction bitmask describes where a zone id applies, not which direction this conncount tuple represents. Fix the two dedup comparisons by passing IP_CT_DIR_ORIGINAL directly. Do not special-case NF_CT_DEFAULT_ZONE_DIR and do not compare raw zone ids: using the existing helpers with IP_CT_DIR_ORIGINAL preserves the direction-aware NF_CT_DEFAULT_ZONE_ID fallback. A default bidirectional zone contains the ORIG bit, so it naturally returns the real zone id; reply-only zones continue to fall back for original-direction tuple comparisons. Fixes: 21ba8847f857 ("netfilter: nf_conncount: Fix garbage collection with zones") Fixes: b36e4523d4d5 ("netfilter: nf_conncount: fix garbage collection confirm race") Cc: stable@vger.kernel.org Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Assisted-by: Claude-Code:GLM-5.2 Signed-off-by: Yizhou Zhao Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_conncount.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nf_conncount.c b/net/netfilter/nf_conncount.c index c6d3f1c06b330..495c4a779d22f 100644 --- a/net/netfilter/nf_conncount.c +++ b/net/netfilter/nf_conncount.c @@ -207,8 +207,8 @@ static int __nf_conncount_add(struct net *net, /* Not found, but might be about to be confirmed */ if (PTR_ERR(found) == -EAGAIN) { if (nf_ct_tuple_equal(&conn->tuple, &tuple) && - nf_ct_zone_id(&conn->zone, conn->zone.dir) == - nf_ct_zone_id(zone, zone->dir)) + nf_ct_zone_id(&conn->zone, IP_CT_DIR_ORIGINAL) == + nf_ct_zone_id(zone, IP_CT_DIR_ORIGINAL)) goto out_put; /* already exists */ } else { collect++; @@ -219,7 +219,7 @@ static int __nf_conncount_add(struct net *net, found_ct = nf_ct_tuplehash_to_ctrack(found); if (nf_ct_tuple_equal(&conn->tuple, &tuple) && - nf_ct_zone_equal(found_ct, zone, zone->dir)) { + nf_ct_zone_equal(found_ct, zone, IP_CT_DIR_ORIGINAL)) { /* * We should not see tuples twice unless someone hooks * this into a table without "-p tcp --syn". From b7a1626c28badec4559e8a97665baf3b6e015fc0 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Fri, 10 Jul 2026 12:44:41 +0200 Subject: [PATCH 2288/2758] netfilter: xt_physdev: masks are not c-strings commit f468c48d488d0ea2df3422b3e1dfafae1611e853 upstream. ... and must not be subjected to the 'nul terminated' constraint. If the interface name is 15 characters long, the mask is 16-bytes '0xff' (to cover for \0) and the valid device name is rejected. Fixes: 8df772afc9d0 ("netfilter: x_physdev: reject empty or not-nul terminated device names") Cc: stable@vger.kernel.org Closes: https://bugs.launchpad.net/neutron/+bug/2159935 Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/xt_physdev.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/net/netfilter/xt_physdev.c b/net/netfilter/xt_physdev.c index dd98f758176c2..a388881c68d42 100644 --- a/net/netfilter/xt_physdev.c +++ b/net/netfilter/xt_physdev.c @@ -130,11 +130,6 @@ static int physdev_mt_check(const struct xt_mtchk_param *par) if (X(physoutdev)) return -ENAMETOOLONG; } - - if (X(in_mask)) - return -ENAMETOOLONG; - if (X(out_mask)) - return -ENAMETOOLONG; #undef X if (!brnf_probed) { From 4301ae9ce3d4cc8f99043b20b6685691b28a65ca Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Tue, 7 Jul 2026 19:00:14 +0800 Subject: [PATCH 2289/2758] netfilter: ecache: fix inverted time_after() check commit b06163ce52ec0561f202fbfb9b08090cb61f512e upstream. ecache_work_evict_list() redelivers DESTROY events for conntracks that were moved to the per-netns dying_list after event delivery failed. It sets a 10ms deadline: stop = jiffies + ECACHE_MAX_JIFFIES but then tests: time_after(stop, jiffies) This condition is true while the deadline is still in the future, so the worker returns STATE_RESTART after the first successful redelivery in the usual case. ecache_work() maps STATE_RESTART to delay 0, which turns the redelivery path into one dying conntrack per workqueue dispatch and makes the sent > 16 batching/cond_resched() path effectively unreachable. A conntrack netlink listener whose receive queue is congested can make DESTROY event delivery fail with -ENOBUFS. With sustained conntrack churn, entries then accumulate on the dying_list and are only drained at the degraded one-entry-per-dispatch rate once delivery succeeds again, wasting CPU on back-to-back workqueue reschedules and prolonging conntrack memory/resource pressure. In a KASAN QEMU test with CONFIG_NF_CONNTRACK_EVENTS=y and nf_conntrack.enable_hooks=1, a congested DESTROY listener caused 8192 nf_ct_delete() calls to return false and move entries to the dying_list. After closing the listener, the unfixed kernel needed 7670 ecache_work() entries to destroy 7669 conntracks. With this change, the same 8192 entries were destroyed by 2 ecache_work() entries. Swap the comparison so the worker restarts only after the deadline has expired. Fixes: 2ed3bf188b33 ("netfilter: ecache: use dedicated list for event redelivery") Cc: stable@vger.kernel.org Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Assisted-by: Claude-Code:GLM-5.2 Signed-off-by: Yizhou Zhao Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_conntrack_ecache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c index 9df159448b897..cc8d8e85169fb 100644 --- a/net/netfilter/nf_conntrack_ecache.c +++ b/net/netfilter/nf_conntrack_ecache.c @@ -77,7 +77,7 @@ static enum retry_state ecache_work_evict_list(struct nf_conntrack_net *cnet) hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode); hlist_nulls_add_head(&ct->tuplehash[IP_CT_DIR_REPLY].hnnode, &evicted_list); - if (time_after(stop, jiffies)) { + if (time_after(jiffies, stop)) { ret = STATE_RESTART; break; } From ec88fa71c82072e9189983b05b499d3507550271 Mon Sep 17 00:00:00 2001 From: Wyatt Feng Date: Sat, 13 Jun 2026 18:27:15 +0800 Subject: [PATCH 2290/2758] netfilter: xt_nat: reject unsupported target families commit 5d1a2240935ea47e2673d0ea17fdb058e4dc91dd upstream. xt_nat SNAT and DNAT target handlers assume IP-family conntrack state is present and can dereference a NULL pointer when instantiated from an unsupported family through nft_compat. A bridge-family compat rule can therefore trigger a NULL-dereference in nf_nat_setup_info(). Reject non-IP families in xt_nat_checkentry() so unsupported targets cannot be installed. Keep NFPROTO_INET allowed for valid inet NAT compat users and leave the runtime fast path unchanged. [ The crash was fixed via 9dbba7e694ec ("netfilter: nft_compat: ebtables emulation must reject non-bridge targets"), so this patch is no longer critical. Nevertheless, NAT is only relevant for ipv4/ipv6, so this extra family check is a good idea in any case. ] Fixes: c7232c9979cb ("netfilter: add protocol independent NAT core") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Signed-off-by: Wyatt Feng Signed-off-by: Ren Wei Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/xt_nat.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/net/netfilter/xt_nat.c b/net/netfilter/xt_nat.c index b4f7bbc3f3caf..51c7f7ce88d9a 100644 --- a/net/netfilter/xt_nat.c +++ b/net/netfilter/xt_nat.c @@ -26,6 +26,15 @@ static int xt_nat_checkentry_v0(const struct xt_tgchk_param *par) static int xt_nat_checkentry(const struct xt_tgchk_param *par) { + switch (par->family) { + case NFPROTO_IPV4: + case NFPROTO_IPV6: + case NFPROTO_INET: + break; + default: + return -EINVAL; + } + return nf_ct_netns_get(par->net, par->family); } From 4ac981a8b7ce7aec99a52d08f8a8953e8e120067 Mon Sep 17 00:00:00 2001 From: "Xiang Mei (Microsoft)" Date: Wed, 8 Jul 2026 18:11:50 +0000 Subject: [PATCH 2291/2758] netfilter: bridge: fix stale prevhdr pointer in br_ip6_fragment() commit 86f3ce81dd2b4b0aa2c3016c989a943e4b1b643d upstream. br_ip6_fragment() gets prevhdr, a pointer into the skb head, from ip6_find_1stfragopt(), then calls skb_checksum_help(). For a cloned skb skb_checksum_help() reallocates the head via pskb_expand_head(), leaving prevhdr dangling. It is later dereferenced in ip6_frag_next(), causing a use-after-free write. Save prevhdr's offset before skb_checksum_help() and recompute it after, like commit ef0efcd3bd3f ("ipv6: Fix dangling pointer when ipv6 fragment"). BUG: KASAN: slab-use-after-free in ip6_frag_next (net/ipv6/ip6_output.c:857) Write of size 1 at addr ffff888013ff5016 by task exploit/141 Call Trace: ... kasan_report (mm/kasan/report.c:595) ip6_frag_next (net/ipv6/ip6_output.c:857) br_ip6_fragment (net/ipv6/netfilter.c:212) nf_ct_bridge_post (net/bridge/netfilter/nf_conntrack_bridge.c:407) nf_hook_slow (net/netfilter/core.c:619) br_forward_finish (net/bridge/br_forward.c:66) __br_forward (net/bridge/br_forward.c:115) maybe_deliver (net/bridge/br_forward.c:191) br_flood (net/bridge/br_forward.c:245) br_handle_frame_finish (net/bridge/br_input.c:229) br_handle_frame (net/bridge/br_input.c:442) ... packet_sendmsg (net/packet/af_packet.c:3114) ... 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 Fixes: 764dd163ac92 ("netfilter: nf_conntrack_bridge: add support for IPv6") Cc: stable@vger.kernel.org Reported-by: AutonomousCodeSecurity@microsoft.com Signed-off-by: Xiang Mei (Microsoft) Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/ipv6/netfilter.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c index 6d80f85e55fa7..a7025ec870359 100644 --- a/net/ipv6/netfilter.c +++ b/net/ipv6/netfilter.c @@ -120,7 +120,7 @@ int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, ktime_t tstamp = skb->tstamp; struct ip6_frag_state state; u8 *prevhdr, nexthdr = 0; - unsigned int mtu, hlen; + unsigned int mtu, hlen, nexthdr_offset; int hroom, err = 0; __be32 frag_id; @@ -129,6 +129,7 @@ int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, goto blackhole; hlen = err; nexthdr = *prevhdr; + nexthdr_offset = prevhdr - skb_network_header(skb); mtu = skb->dev->mtu; if (frag_max_size > mtu || @@ -147,6 +148,7 @@ int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, (err = skb_checksum_help(skb))) goto blackhole; + prevhdr = skb_network_header(skb) + nexthdr_offset; hroom = LL_RESERVED_SPACE(skb->dev); if (skb_has_frag_list(skb)) { unsigned int first_len = skb_pagelen(skb); From 392d033fd372268015bc5a54ba7c593ba44940d1 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 9 Jul 2026 13:40:25 +0200 Subject: [PATCH 2292/2758] netfilter: flowtable: use correct direction to set up tunnel route commit 90941d9c925d66a482c9121919ec3546a6988c16 upstream. The layer 2 encapsulation and layer 3 tunnel information in the xmit path is taken from the other tuple, because the tunnel information that is included in the tuple for hashtable lookups is also used to perform the egress encapsulation in the transmit path. This patch uses the correct direction when setting up the tunnel, the original proposed patch to address this fix uses the reversed direction. While at it, remove the redundant check to call dst_release() to drop the reference on the dst that was obtained from the forward path, which is not useful in the direct xmit path unless tunneling is performed. Fixes: fa7395c02d95 ("netfilter: flowtable: support IPIP tunnel with direct xmit") Cc: stable@vger.kernel.org Signed-off-by: Pablo Neira Ayuso Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_flow_table_core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c index 2a829b5e8240a..b66e654393411 100644 --- a/net/netfilter/nf_flow_table_core.c +++ b/net/netfilter/nf_flow_table_core.c @@ -127,18 +127,18 @@ static int flow_offload_fill_route(struct flow_offload *flow, switch (route->tuple[dir].xmit_type) { case FLOW_OFFLOAD_XMIT_DIRECT: - if (flow_tuple->tun_num) { + if (route->tuple[!dir].in.num_tuns) { flow_tuple->dst_cache = dst; flow_tuple->dst_cookie = flow_offload_dst_cookie(flow_tuple); + } else { + dst_release(dst); } memcpy(flow_tuple->out.h_dest, route->tuple[dir].out.h_dest, ETH_ALEN); memcpy(flow_tuple->out.h_source, route->tuple[dir].out.h_source, ETH_ALEN); flow_tuple->out.ifidx = route->tuple[dir].out.ifindex; - if (!flow_tuple->tun_num) - dst_release(dst); break; case FLOW_OFFLOAD_XMIT_XFRM: case FLOW_OFFLOAD_XMIT_NEIGH: From 8559b1501f77a5b5d003790bec170ca449e0c674 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Mon, 13 Apr 2026 22:15:26 +0800 Subject: [PATCH 2293/2758] gpu: host1x: Fix device reference leak in host1x_device_parse_dt() error path commit e75717f9aec04355777be41070890c6a815c76df upstream. After device_initialize(), the embedded struct device in struct host1x_device should be released through the device core with put_device(). In host1x_device_add(), if host1x_device_parse_dt() fails, the current error path frees the object directly with kfree(device). That bypasses the normal device lifetime handling and leaks the reference held on the embedded struct device. The issue was identified by a static analysis tool I developed and confirmed by manual review. Fix this by using put_device() in the host1x_device_parse_dt() failure path. Fixes: f4c5cf88fbd50 ("gpu: host1x: Provide a proper struct bus_type") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Acked-by: Mikko Perttunen Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260413141526.2961841-1-lgs201920130244@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/host1x/bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c index 772e05a7b45b3..c273a4476f237 100644 --- a/drivers/gpu/host1x/bus.c +++ b/drivers/gpu/host1x/bus.c @@ -485,7 +485,7 @@ static int host1x_device_add(struct host1x *host1x, err = host1x_device_parse_dt(device, driver); if (err < 0) { - kfree(device); + put_device(&device->dev); return err; } From 419d7d9306491f3e0e417cf794844c73cabee090 Mon Sep 17 00:00:00 2001 From: Arunpravin Paneer Selvam Date: Thu, 9 Jul 2026 18:40:50 +0530 Subject: [PATCH 2294/2758] gpu/buddy: bail out of try_harder when alignment cannot be honoured MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 56bc6384314fb9ae98975fb2af8b143097ede3dc upstream. The try_harder contiguous fallback could return a range whose start offset did not match the caller's min_block_size. When a candidate's start is misaligned, realign it: free the misaligned run and reallocate exactly @size at the next lower min_block_size boundary. This keeps the returned size unchanged with no surplus to trim, and rejects the request only when no aligned candidate fits. v2: align misaligned candidates down to min_block_size instead of bailing out, for both the RHS and LHS paths (Matthew). Fixes: 0a1844bf0b53 ("drm/buddy: Improve contiguous memory allocation") Suggested-by: Christian König Cc: Matthew Auld Cc: Christian König Cc: Timur Kristóf Cc: stable@vger.kernel.org Reviewed-by: Matthew Auld Tested-by: John Olender Signed-off-by: Arunpravin Paneer Selvam Link: https://patch.msgid.link/20260709131050.1022759-1-Arunpravin.PaneerSelvam@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/buddy.c | 63 +++++++++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/buddy.c b/drivers/gpu/buddy.c index 52686672e99f5..5490dcd27f22c 100644 --- a/drivers/gpu/buddy.c +++ b/drivers/gpu/buddy.c @@ -1078,22 +1078,30 @@ static int __gpu_buddy_alloc_range(struct gpu_buddy *mm, blocks, total_allocated_on_err); } +static int __alloc_contig_aligned_retry(struct gpu_buddy *mm, + u64 unaligned_offset, + u64 size, + u64 min_block_size, + struct list_head *blocks) +{ + u64 aligned_offset = round_down(unaligned_offset, min_block_size); + + return __gpu_buddy_alloc_range(mm, aligned_offset, size, NULL, blocks); +} + static int __alloc_contig_try_harder(struct gpu_buddy *mm, u64 size, u64 min_block_size, struct list_head *blocks) { - u64 rhs_offset, lhs_offset, lhs_size, filled; + u64 rhs_offset, lhs_offset, filled; struct gpu_buddy_block *block; unsigned int tree, order; - LIST_HEAD(blocks_lhs); - unsigned long pages; u64 modify_size; int err; modify_size = rounddown_pow_of_two(size); - pages = modify_size >> ilog2(mm->chunk_size); - order = fls(pages) - 1; + order = ilog2(modify_size) - ilog2(mm->chunk_size); if (order == 0) return -ENOSPC; @@ -1109,31 +1117,48 @@ static int __alloc_contig_try_harder(struct gpu_buddy *mm, while (iter) { block = rbtree_get_free_block(iter); - /* Allocate blocks traversing RHS */ rhs_offset = gpu_buddy_block_offset(block); + + /* Allocate blocks traversing RHS */ err = __gpu_buddy_alloc_range(mm, rhs_offset, size, &filled, blocks); - if (!err || err != -ENOSPC) + if (err && err != -ENOSPC) return err; + if (!err && IS_ALIGNED(rhs_offset, min_block_size)) + return 0; + if (!err) { + /* Allocate the unaligned RHS offset using round_down */ + gpu_buddy_free_list_internal(mm, blocks); + err = __alloc_contig_aligned_retry(mm, rhs_offset, + size, + min_block_size, + blocks); + if (!err) + return 0; + if (err != -ENOSPC) { + gpu_buddy_free_list_internal(mm, blocks); + return err; + } + goto next; + } - lhs_size = max((size - filled), min_block_size); - if (!IS_ALIGNED(lhs_size, min_block_size)) - lhs_size = round_up(lhs_size, min_block_size); + if (size - filled > rhs_offset) + goto next; - /* Allocate blocks traversing LHS */ - lhs_offset = gpu_buddy_block_offset(block) - lhs_size; - err = __gpu_buddy_alloc_range(mm, lhs_offset, lhs_size, - NULL, &blocks_lhs); - if (!err) { - list_splice(&blocks_lhs, blocks); + lhs_offset = rhs_offset - (size - filled); + + /* Allocate the unaligned LHS offset using round_down */ + gpu_buddy_free_list_internal(mm, blocks); + err = __alloc_contig_aligned_retry(mm, lhs_offset, size, + min_block_size, blocks); + if (!err) return 0; - } else if (err != -ENOSPC) { + if (err != -ENOSPC) { gpu_buddy_free_list_internal(mm, blocks); return err; } - /* Free blocks for the next iteration */ +next: gpu_buddy_free_list_internal(mm, blocks); - iter = rb_prev(iter); } } From 9b3325f5a9fb6e3702daa417778202a1163f1ed5 Mon Sep 17 00:00:00 2001 From: Siddharth Vadapalli Date: Fri, 1 May 2026 18:10:54 +0530 Subject: [PATCH 2295/2758] soc: ti: k3-ringacc: Fix access mode for k3_ringacc_ring_pop_tail_io/proxy commit b920352cfd2b0fcd1249ff006618c939b64fc8f7 upstream. k3_ringacc_ring_pop_tail_io() and k3_ringacc_ring_pop_tail_proxy() incorrectly use K3_RINGACC_ACCESS_MODE_POP_HEAD instead of K3_RINGACC_ACCESS_MODE_POP_TAIL. This will result in ring elements being popped in the reverse order of that which the caller expects. Fix this. Fixes: 3277e8aa2504 ("soc: ti: k3: add navss ringacc driver") Cc: stable@vger.kernel.org Signed-off-by: Siddharth Vadapalli Reviewed-by: Hari Prasath Gujulan Elango Link: https://patch.msgid.link/20260501124129.362192-1-s-vadapalli@ti.com Signed-off-by: Nishanth Menon Signed-off-by: Greg Kroah-Hartman --- drivers/soc/ti/k3-ringacc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/soc/ti/k3-ringacc.c b/drivers/soc/ti/k3-ringacc.c index 7602b8a909b05..e2ca380812d29 100644 --- a/drivers/soc/ti/k3-ringacc.c +++ b/drivers/soc/ti/k3-ringacc.c @@ -1012,7 +1012,7 @@ static int k3_ringacc_ring_pop_head_proxy(struct k3_ring *ring, void *elem) static int k3_ringacc_ring_pop_tail_proxy(struct k3_ring *ring, void *elem) { return k3_ringacc_ring_access_proxy(ring, elem, - K3_RINGACC_ACCESS_MODE_POP_HEAD); + K3_RINGACC_ACCESS_MODE_POP_TAIL); } static int k3_ringacc_ring_access_io(struct k3_ring *ring, void *elem, @@ -1083,7 +1083,7 @@ static int k3_ringacc_ring_pop_io(struct k3_ring *ring, void *elem) static int k3_ringacc_ring_pop_tail_io(struct k3_ring *ring, void *elem) { return k3_ringacc_ring_access_io(ring, elem, - K3_RINGACC_ACCESS_MODE_POP_HEAD); + K3_RINGACC_ACCESS_MODE_POP_TAIL); } /* From fe11d6ce19b2b2d01e2e324f375280d1aa38b75d Mon Sep 17 00:00:00 2001 From: Wang Jun <1742789905@qq.com> Date: Fri, 27 Mar 2026 08:12:25 +0800 Subject: [PATCH 2296/2758] soc: fsl: qe: panic on ioremap() failure in qe_reset() commit a0fe29d20e7822182e12324905af5115c1b3aed3 upstream. When ioremap() fails in qe_reset(), the global pointer qe_immr remains NULL, leading to a subsequent NULL pointer dereference when the pointer is accessed. Since this happens early in the boot process, a failure to map a few bytes of I/O memory indicates a fatal error from which the system cannot recover. Follow the same pattern as qe_sdma_init() and panic immediately when ioremap() fails. This avoids a silent NULL pointer dereference later and makes the error explicit. Fixes: 986585385131 ("[POWERPC] Add QUICC Engine (QE) infrastructure") Cc: stable@vger.kernel.org Signed-off-by: Wang Jun <1742789905@qq.com> Link: https://lore.kernel.org/r/tencent_FED49CF5331CC0C7910618883332A08E2606@qq.com [chleroy: Rearranged change to reduce churn] Signed-off-by: Christophe Leroy (CS GROUP) Signed-off-by: Greg Kroah-Hartman --- drivers/soc/fsl/qe/qe.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/soc/fsl/qe/qe.c b/drivers/soc/fsl/qe/qe.c index 70b6eddb867b9..3ecc4ce9cfa25 100644 --- a/drivers/soc/fsl/qe/qe.c +++ b/drivers/soc/fsl/qe/qe.c @@ -89,6 +89,9 @@ void qe_reset(void) if (qe_immr == NULL) qe_immr = ioremap(get_qe_base(), QE_IMMAP_SIZE); + if (!qe_immr) + panic("QE:ioremap failed!"); + qe_snums_init(); qe_issue_cmd(QE_RESET, QE_CR_SUBBLOCK_INVALID, From 646ebbc5f2ff9147d084e1213143f091026a611c Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Thu, 18 Jun 2026 13:55:14 -0400 Subject: [PATCH 2297/2758] selinux: check connect-related permissions on TCP Fast Open commit 44c74d27d1b9aaa99fa8a83640c1223575262b80 upstream. Similar to Landlock, SELinux was not updated when TCP Fast Open support was introduced to ensure connect-related permissions are checked when using TCP Fast Open. Update its socket_sendmsg() hook to call selinux_socket_connect() when MSG_FASTOPEN is passed. Cc: stable@vger.kernel.org Link: https://lore.kernel.org/linux-security-module/20260616201615.275032-1-hexlabsecurity@proton.me/ Link: https://lore.kernel.org/linux-security-module/20260617180526.15627-2-matthieu@buffet.re/ Reported-by: Bryam Vargas Reported-by: Matthieu Buffet Reported-by: Mikhail Ivanov Signed-off-by: Stephen Smalley Tested-by: Bryam Vargas Signed-off-by: Paul Moore Signed-off-by: Greg Kroah-Hartman --- security/selinux/hooks.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 0f704380a8c81..fe04def35557f 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -5266,7 +5266,24 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size) { - return sock_has_perm(sock->sk, SOCKET__WRITE); + int rc; + struct sockaddr *const addr = msg->msg_name; + const int addrlen = msg->msg_namelen; + + rc = sock_has_perm(sock->sk, SOCKET__WRITE); + if (rc) + return rc; + + if (addr && (msg->msg_flags & MSG_FASTOPEN) && + (sk_is_tcp(sock->sk) || + (sk_is_inet(sock->sk) && sock->sk->sk_type == SOCK_STREAM && + sock->sk->sk_protocol == IPPROTO_MPTCP))) { + rc = selinux_socket_connect(sock, addr, addrlen); + if (rc) + return rc; + } + + return 0; } static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg, From 37d642b37ccdc31e1947c2ebc8dc38f03d4a0ceb Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Thu, 25 Jun 2026 23:53:36 +0000 Subject: [PATCH 2298/2758] selinux: avoid sk_socket dereference in selinux_sctp_bind_connect() commit 56acfeb10019e200ab6787d01f8d7cbe0f01526f upstream. selinux_sctp_bind_connect() dereferences sk->sk_socket to pass a struct socket * to selinux_socket_bind() and selinux_socket_connect_helper(). However, when the hook is invoked from the ASCONF softirq path (sctp_process_asconf), there is no file reference guaranteeing that sk->sk_socket is non-NULL. The setsockopt callers (bindx, connectx, set_primary, sendmsg connect) hold a file reference and are not affected. Both selinux_socket_bind() and selinux_socket_connect_helper() immediately resolve sock->sk, never using the struct socket * for anything else. Refactor the inner logic into helpers that take a struct sock * directly so that selinux_sctp_bind_connect() never needs to touch sk->sk_socket at all. Cc: stable@vger.kernel.org Fixes: d452930fd3b9 ("selinux: Add SCTP support") Suggested-by: Stephen Smalley Signed-off-by: Tristan Madani Reviewed-by: Stephen Smalley Tested-by: Stephen Smalley Signed-off-by: Paul Moore Signed-off-by: Greg Kroah-Hartman --- security/selinux/hooks.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index fe04def35557f..f5eeef921f88f 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4998,9 +4998,8 @@ static int selinux_socket_socketpair(struct socket *socka, Need to determine whether we should perform a name_bind permission check between the socket and the port number. */ -static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) +static int __selinux_socket_bind(struct sock *sk, struct sockaddr *address, int addrlen) { - struct sock *sk = sock->sk; struct sk_security_struct *sksec = selinux_sock(sk); u16 family; int err; @@ -5130,13 +5129,17 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in return -EAFNOSUPPORT; } +static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) +{ + return __selinux_socket_bind(sock->sk, address, addrlen); +} + /* This supports connect(2) and SCTP connect services such as sctp_connectx(3) * and sctp_sendmsg(3) as described in Documentation/security/SCTP.rst */ -static int selinux_socket_connect_helper(struct socket *sock, +static int selinux_socket_connect_helper(struct sock *sk, struct sockaddr *address, int addrlen) { - struct sock *sk = sock->sk; struct sk_security_struct *sksec = selinux_sock(sk); int err; @@ -5225,7 +5228,7 @@ static int selinux_socket_connect(struct socket *sock, int err; struct sock *sk = sock->sk; - err = selinux_socket_connect_helper(sock, address, addrlen); + err = selinux_socket_connect_helper(sk, address, addrlen); if (err) return err; @@ -5727,13 +5730,11 @@ static int selinux_sctp_bind_connect(struct sock *sk, int optname, int len, err = 0, walk_size = 0; void *addr_buf; struct sockaddr *addr; - struct socket *sock; if (!selinux_policycap_extsockclass()) return 0; /* Process one or more addresses that may be IPv4 or IPv6 */ - sock = sk->sk_socket; addr_buf = address; while (walk_size < addrlen) { @@ -5762,14 +5763,14 @@ static int selinux_sctp_bind_connect(struct sock *sk, int optname, case SCTP_PRIMARY_ADDR: case SCTP_SET_PEER_PRIMARY_ADDR: case SCTP_SOCKOPT_BINDX_ADD: - err = selinux_socket_bind(sock, addr, len); + err = __selinux_socket_bind(sk, addr, len); break; /* Connect checks */ case SCTP_SOCKOPT_CONNECTX: case SCTP_PARAM_SET_PRIMARY: case SCTP_PARAM_ADD_IP: case SCTP_SENDMSG_CONNECT: - err = selinux_socket_connect_helper(sock, addr, len); + err = selinux_socket_connect_helper(sk, addr, len); if (err) return err; From c9a691350e28e41c2b15c0ec43b9dbadb974a925 Mon Sep 17 00:00:00 2001 From: Ondrej Mosnacek Date: Tue, 14 Jul 2026 14:57:59 +0200 Subject: [PATCH 2299/2758] selinux: fix incorrect execmem checks on overlayfs commit 9fe595fad54d4ac6a402edb3f60bec859d52cea6 upstream. The commit fixing the overlayfs mmap() and mprotect() access checks failed to skip the execmem check in __file_map_prot_check() for the case where the "mounter check" is being performed. This check should be performed only against the credentials of the task that is calling mmap()/mprotect(), since it doesn't pertain to the file itself, but rather just gates the ability of the calling task to get an executable memory mapping in general. The purpose of the "mounter check" is to guard against using an overlayfs mount to gain file access that would otherwise be denied to the mounter. For execmem this is not relevant, as there is no further file access granted based on it (notice that the file's context is not used as the target in the check), so checking it also against the mounter credentials would be incorrect. Fix this by passing a boolean to [__]file_map_prot_check() and selinux_mmap_file_common() that indicates if we are doing the "mounter check" and skiping the execmem check in that case. Since this boolean also indicates if we use current_cred() or the mounter cred as the subject, also remove the "cred" argument from these functions and determine it based on the boolean and the file struct. Cc: stable@vger.kernel.org Fixes: 82544d36b172 ("selinux: fix overlayfs mmap() and mprotect() access checks") Signed-off-by: Ondrej Mosnacek Reviewed-by: Stephen Smalley Signed-off-by: Paul Moore Signed-off-by: Greg Kroah-Hartman --- security/selinux/hooks.c | 42 +++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index f5eeef921f88f..e810f39291677 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3973,9 +3973,9 @@ static int selinux_file_ioctl_compat(struct file *file, unsigned int cmd, static int default_noexec __ro_after_init; -static int __file_map_prot_check(const struct cred *cred, - const struct file *file, unsigned long prot, - bool shared, bool bf_user_file) +static int __file_map_prot_check(const struct file *file, unsigned long prot, + bool shared, bool mounter_check, + bool bf_user_file) { struct inode *inode = NULL; bool prot_exec = prot & PROT_EXEC; @@ -3988,10 +3988,10 @@ static int __file_map_prot_check(const struct cred *cred, inode = file_inode(file); } - if (default_noexec && prot_exec && + if (!mounter_check && default_noexec && prot_exec && (!file || IS_PRIVATE(inode) || (!shared && prot_write))) { int rc; - u32 sid = cred_sid(cred); + u32 sid = current_sid(); /* * We are making executable an anonymous mapping or a private @@ -4004,6 +4004,8 @@ static int __file_map_prot_check(const struct cred *cred, } if (file) { + const struct cred *cred = mounter_check ? + file->f_cred : current_cred(); /* "read" always possible, "write" only if shared */ u32 av = FILE__READ; if (shared && prot_write) @@ -4017,11 +4019,11 @@ static int __file_map_prot_check(const struct cred *cred, return 0; } -static inline int file_map_prot_check(const struct cred *cred, - const struct file *file, - unsigned long prot, bool shared) +static inline int file_map_prot_check(const struct file *file, + unsigned long prot, bool shared, + bool mounter_check) { - return __file_map_prot_check(cred, file, prot, shared, false); + return __file_map_prot_check(file, prot, shared, mounter_check, false); } static int selinux_mmap_addr(unsigned long addr) @@ -4037,12 +4039,14 @@ static int selinux_mmap_addr(unsigned long addr) return rc; } -static int selinux_mmap_file_common(const struct cred *cred, struct file *file, - unsigned long prot, bool shared) +static int selinux_mmap_file_common(struct file *file, unsigned long prot, + bool shared, bool mounter_check) { if (file) { int rc; struct common_audit_data ad; + const struct cred *cred = mounter_check ? + file->f_cred : current_cred(); ad.type = LSM_AUDIT_DATA_FILE; ad.u.file = file; @@ -4051,15 +4055,16 @@ static int selinux_mmap_file_common(const struct cred *cred, struct file *file, return rc; } - return file_map_prot_check(cred, file, prot, shared); + return file_map_prot_check(file, prot, shared, mounter_check); } static int selinux_mmap_file(struct file *file, unsigned long reqprot __always_unused, unsigned long prot, unsigned long flags) { - return selinux_mmap_file_common(current_cred(), file, prot, - (flags & MAP_TYPE) == MAP_SHARED); + return selinux_mmap_file_common(file, prot, + (flags & MAP_TYPE) == MAP_SHARED, + false); } /** @@ -4091,8 +4096,9 @@ static int selinux_mmap_backing_file(struct vm_area_struct *vma, if (vma->vm_flags & VM_EXEC) prot |= PROT_EXEC; - return selinux_mmap_file_common(backing_file->f_cred, backing_file, - prot, vma->vm_flags & VM_SHARED); + return selinux_mmap_file_common(backing_file, prot, + vma->vm_flags & VM_SHARED, + true); } static int selinux_file_mprotect(struct vm_area_struct *vma, @@ -4153,11 +4159,11 @@ static int selinux_file_mprotect(struct vm_area_struct *vma, } } - rc = __file_map_prot_check(cred, file, prot, shared, backing_file); + rc = __file_map_prot_check(file, prot, shared, false, backing_file); if (rc) return rc; if (backing_file) { - rc = file_map_prot_check(file->f_cred, file, prot, shared); + rc = file_map_prot_check(file, prot, shared, true); if (rc) return rc; } From 263ccdd627caeda6e980ed3c6e32bb4fd6b380b1 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 25 May 2026 01:55:53 +0200 Subject: [PATCH 2300/2758] leds: uleds: Fix potential buffer overread commit c19fe864f667afc49d1391d764e20b66555bcf7a upstream. The name string supplied by userspace is not guaranteed to be null-terminated, so using strchr() on it might result in a buffer overread. The same thing will happen when said string is used by the LED class device. Fix this by using strnchr() instead and explicitly check that the name string is properly null-terminated. Cc: stable@vger.kernel.org Fixes: e381322b0190 ("leds: Introduce userspace LED class driver") Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260524235553.189134-1-W_Armin@gmx.de Signed-off-by: Lee Jones Signed-off-by: Greg Kroah-Hartman --- drivers/leds/uleds.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/leds/uleds.c b/drivers/leds/uleds.c index ace71ffc0591f..41942cfca7051 100644 --- a/drivers/leds/uleds.c +++ b/drivers/leds/uleds.c @@ -102,7 +102,8 @@ static ssize_t uleds_write(struct file *file, const char __user *buffer, name = udev->user_dev.name; if (!name[0] || !strcmp(name, ".") || !strcmp(name, "..") || - strchr(name, '/')) { + strnchr(name, sizeof(udev->user_dev.name), '/') || + !strnchr(name, sizeof(udev->user_dev.name), '\0')) { ret = -EINVAL; goto out; } From 2ff8156fd500d54c61430b6834137fd0a07047ce Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Thu, 16 Apr 2026 00:26:27 +0800 Subject: [PATCH 2301/2758] mfd: sm501: Fix reference leak on failed device registration commit 8c2f0b42fc252e1bf1c7746447091a468e784ca1 upstream. When platform_device_register() fails in sm501_register_device(), the embedded struct device in pdev has already been initialized by device_initialize(), but the failure path only reports the error and returns without dropping the device reference for the current platform device: sm501_register_device() -> platform_device_register(pdev) -> device_initialize(&pdev->dev) -> setup_pdev_dma_masks(pdev) -> platform_device_add(pdev) This leads to a reference leak when platform_device_register() fails. Fix this by calling platform_device_put() before returning the error. The issue was identified by a static analysis tool I developed and confirmed by manual review. Fixes: b6d6454fdb66f ("[PATCH] mfd: SM501 core driver") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Link: https://patch.msgid.link/20260415162627.3558789-1-lgs201920130244@gmail.com Signed-off-by: Lee Jones Signed-off-by: Greg Kroah-Hartman --- drivers/mfd/sm501.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index 0ee6d8940e690..8276456b142fe 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -704,9 +704,11 @@ static int sm501_register_device(struct sm501_devdata *sm, if (ret >= 0) { dev_dbg(sm->dev, "registered %s\n", pdev->name); list_add_tail(&smdev->list, &sm->devices); - } else + } else { dev_err(sm->dev, "error registering %s (%d)\n", pdev->name, ret); + platform_device_put(pdev); + } return ret; } From e8adac69d1bdf035ef97cc914e845acd4ef08e28 Mon Sep 17 00:00:00 2001 From: Ali Ahmet MEMIS Date: Sun, 26 Apr 2026 08:09:28 -0700 Subject: [PATCH 2302/2758] tools/power/x86/intel-speed-select: Harden daemon pidfile open commit 607af438e6430893a822964c841a1994b33acccc upstream. Avoid symlink-based pidfile clobbering by opening the pidfile with O_NOFOLLOW and validating it with fstat() before locking/writing. The daemon currently uses a fixed pidfile path under /tmp. A local unprivileged user can pre-create a symlink at that path and cause a root-run daemon instance to write into an attacker-chosen file. Fixes: 7fd786dfbd2c ("tools/power/x86/intel-speed-select: OOB daemon mode") Signed-off-by: Ali Ahmet MEMIS Signed-off-by: Srinivas Pandruvada Cc: stable@kernel.org Signed-off-by: Greg Kroah-Hartman --- tools/power/x86/intel-speed-select/isst-daemon.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/power/x86/intel-speed-select/isst-daemon.c b/tools/power/x86/intel-speed-select/isst-daemon.c index 66df21b2b5737..acedb74328494 100644 --- a/tools/power/x86/intel-speed-select/isst-daemon.c +++ b/tools/power/x86/intel-speed-select/isst-daemon.c @@ -148,6 +148,7 @@ static void daemonize(char *rundir, char *pidfile) { int pid, sid, i; char str[10]; + struct stat st; struct sigaction sig_actions; sigset_t sig_set; int ret; @@ -200,11 +201,17 @@ static void daemonize(char *rundir, char *pidfile) if (ret == -1) exit(EXIT_FAILURE); - pid_file_handle = open(pidfile, O_RDWR | O_CREAT, 0600); + pid_file_handle = open(pidfile, O_RDWR | O_CREAT | O_NOFOLLOW, 0600); if (pid_file_handle == -1) { /* Couldn't open lock file */ exit(1); } + + if (fstat(pid_file_handle, &st) == -1) + exit(1); + + if (!S_ISREG(st.st_mode)) + exit(1); /* Try to lock file */ #ifdef LOCKF_SUPPORT if (lockf(pid_file_handle, F_TLOCK, 0) == -1) { From 8a2a0b911cd6ebf61217b552d758b0c2a22cad82 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 23 Jun 2026 07:15:05 -0700 Subject: [PATCH 2303/2758] x86/video: Only fall back to vga_default_device() without screen info commit 596b3678326d3d1aed7c19423b6746f1ce09688a upstream. Some multi GPU systems may have a VGA compatible device, but that might not be used for display. If due to enumeration order this device is found before the one actually used for display then multiple devices may show the boot_display attribute, confusing userspace. When screen info is valid, use it exclusively to find the primary device so that only the device backing the framebuffer is reported. Only when no framebuffer has been set up does it make sense to fall back to the default VGA device. This ensures at most one primary graphics device, preferably the one with the framebuffer. Fixes: ad90860bd10ee ("fbcon: Use screen info to find primary device") Closes: https://lore.kernel.org/linux-pci/20260618081803.2790848-1-aaron.ma@canonical.com/#t Reported-by: Aaron Ma Suggested-by: Thomas Zimmermann Signed-off-by: Mario Limonciello Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Thomas Zimmermann Tested-by: Aaron Ma Cc: Link: https://patch.msgid.link/20260623141505.1816786-1-mario.limonciello@amd.com Signed-off-by: Greg Kroah-Hartman --- arch/x86/video/video-common.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/arch/x86/video/video-common.c b/arch/x86/video/video-common.c index 152789f00fcda..8ed82fff7638e 100644 --- a/arch/x86/video/video-common.c +++ b/arch/x86/video/video-common.c @@ -43,21 +43,26 @@ bool video_is_primary_device(struct device *dev) if (!pci_is_display(pdev)) return false; - if (pdev == vga_default_device()) - return true; - #ifdef CONFIG_SCREEN_INFO numres = screen_info_resources(si, res, ARRAY_SIZE(res)); - for (i = 0; i < numres; ++i) { - if (!(res[i].flags & IORESOURCE_MEM)) - continue; + if (numres > 0) { + for (i = 0; i < numres; ++i) { + if (!(res[i].flags & IORESOURCE_MEM)) + continue; + + if (pci_find_resource(pdev, &res[i])) + return true; + } - if (pci_find_resource(pdev, &res[i])) - return true; + return false; } #endif - return false; + /* + * No framebuffer was set up by the firmware/bootloader, so fall back + * to the default VGA device. + */ + return pdev == vga_default_device(); } EXPORT_SYMBOL(video_is_primary_device); From e5158ff53fdff9c229bf13aa5f75eb17cdcfcd2d Mon Sep 17 00:00:00 2001 From: "Tycho Andersen (AMD)" Date: Tue, 7 Jul 2026 09:00:33 -0600 Subject: [PATCH 2304/2758] x86/virt/sev: Revert "Drop WBINVD before setting MSR_AMD64_SYSCFG_SNP_EN" commit 4c2509f3b79756679a02bea649c6a7501b58f52c upstream. Revert 99cf1fb58e68 ("x86/virt/sev: Drop WBINVD before setting MSR_AMD64_SYSCFG_SNP_EN"). Section 8.8 of the SNP spec says: Before invoking SNP_INIT_EX with INIT_RMP set to 1, software must ensure that no CPUs contain dirty cache lines for the memory containing the RMP. Cachelines can be moved from cache to cache in a dirty state. The wbinvd_on_all_cpus() before SNP_INIT_EX flushes the caches for each CPU, but if the IPIs for WBINVD race with this dirty cacheline movement, it is possible that they may not get flushed, violating the firmware requirement. Doing wbinvd_on_all_cpus() before setting SNPEn is safer since the RMP table is not yet in use. [ Heroically bisected by Srikanth. ] [ bp: Massage commit message. ] Fixes: 99cf1fb58e68 ("x86/virt/sev: Drop WBINVD before setting MSR_AMD64_SYSCFG_SNP_EN") Reported-by: Srikanth Aithal Signed-off-by: Tycho Andersen (AMD) Signed-off-by: Borislav Petkov (AMD) Tested-by: Srikanth Aithal Reviewed-by: Tom Lendacky Cc: Link: https://patch.msgid.link/20260707150033.2364758-1-tycho@kernel.org Signed-off-by: Greg Kroah-Hartman --- arch/x86/virt/svm/sev.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/x86/virt/svm/sev.c b/arch/x86/virt/svm/sev.c index 41f76f15caa13..b210f09eba464 100644 --- a/arch/x86/virt/svm/sev.c +++ b/arch/x86/virt/svm/sev.c @@ -527,6 +527,8 @@ void snp_prepare(void) cpus_read_lock(); + wbinvd_on_all_cpus(); + /* * MtrrFixDramModEn is not shared between threads on a core, * therefore it must be set on all CPUs prior to enabling SNP. From 4dad7e870c7e5178f71b5e1e4f67934b9e8cc207 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 13 Jul 2026 21:49:25 +0200 Subject: [PATCH 2305/2758] x86/boot: Validate console=uart8250 baud rate to fix early boot hang commit ffa0aa5b625fe0bed7463ac613f8b06676ff4542 upstream. When the baud rate is empty, 0, invalid, or overflows to 0 when stored as an int, the system will hang during early boot because of a division by zero in early_serial_init(). Fall back to DEFAULT_BAUD when the resulting baud rate is 0 to prevent an early system hang. Fixes: ce0aa5dd20e4 ("x86, setup: Make the setup code also accept console=uart8250") Signed-off-by: Thorsten Blum Signed-off-by: Ingo Molnar Cc: "H. Peter Anvin" Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260713194924.126472-3-thorsten.blum@linux.dev Signed-off-by: Greg Kroah-Hartman --- arch/x86/boot/early_serial_console.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/arch/x86/boot/early_serial_console.c b/arch/x86/boot/early_serial_console.c index 023bf1c3de8b7..5b83beab89e15 100644 --- a/arch/x86/boot/early_serial_console.c +++ b/arch/x86/boot/early_serial_console.c @@ -117,7 +117,7 @@ static unsigned int probe_baud(int port) static void parse_console_uart8250(void) { char optstr[64], *options; - int baud = DEFAULT_BAUD; + int baud; int port = 0; /* @@ -136,10 +136,13 @@ static void parse_console_uart8250(void) else return; - if (options && (options[0] == ',')) - baud = simple_strtoull(options + 1, &options, 0); - else + if (options && (options[0] == ',')) { + baud = simple_strtoull(options + 1, NULL, 0); + if (!baud) + baud = DEFAULT_BAUD; + } else { baud = probe_baud(port); + } if (port) early_serial_init(port, baud); From 64193ed819db31b9548074daa6b3948c317f59c6 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sun, 21 Jun 2026 19:00:10 +0200 Subject: [PATCH 2306/2758] x86/boot: Reject too long acpi_rsdp= values commit d130041a7b96f79cd4c7079a6c2431a6db4c9619 upstream. cmdline_find_option() returns the full length of the parsed acpi_rsdp= value. get_cmdline_acpi_rsdp() then silently truncates values which do not fit in the val[] buffer. Prevent boot_kstrtoul() from parsing a truncated value and then the kernel from silently using the wrong RSDP address, see discussion in Link:. Issue a warning so that the user is aware that s/he supplied a malformed value and can get feedback instead of silent crashes. [ bp: Make commit message more precise. ] Fixes: 3c98e71b42a7 ("x86/boot: Add "acpi_rsdp=" early parsing") Signed-off-by: Thorsten Blum Signed-off-by: Borislav Petkov (AMD) Cc: stable@vger.kernel.org Link: https://lore.kernel.org/all/20260617130417.36651-4-thorsten.blum@linux.dev Signed-off-by: Greg Kroah-Hartman --- arch/x86/boot/compressed/acpi.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/x86/boot/compressed/acpi.c b/arch/x86/boot/compressed/acpi.c index f196b1d1ddf86..aed27604c11f0 100644 --- a/arch/x86/boot/compressed/acpi.c +++ b/arch/x86/boot/compressed/acpi.c @@ -184,10 +184,15 @@ static unsigned long get_cmdline_acpi_rsdp(void) char val[MAX_ADDR_LEN] = { }; int ret; - ret = cmdline_find_option("acpi_rsdp", val, MAX_ADDR_LEN); + ret = cmdline_find_option("acpi_rsdp", val, sizeof(val)); if (ret < 0) return 0; + if (ret >= sizeof(val)) { + warn("acpi_rsdp= value too long; ignoring"); + return 0; + } + if (boot_kstrtoul(val, 16, &addr)) return 0; #endif From 2e706be56f418718bb3ae66c0aa94f9b61150e6d Mon Sep 17 00:00:00 2001 From: Sandipan Das Date: Fri, 10 Jul 2026 22:04:49 +0530 Subject: [PATCH 2307/2758] perf/x86/amd/brs: Fix kernel address leakage commit 47915e855fb38b42133e31ba917d99565f862154 upstream. A user-only branch stack can contain branches that originate from the kernel. As a result, kernel addresses are exposed to user space even when PERF_SAMPLE_BRANCH_USER is requested. On AMD processors supporting X86_FEATURE_BRS (Zen 3 only), perf can still report entries such as SYSRET/interrupt returns for which the branch-from addresses are in the kernel. E.g. $ perf record -j any,u -c 4000 -e branch-brs -o - -- \ perf bench syscall basic --loop 1000 | \ perf script -i - -F brstack|tr ' ' '\n'| \ grep -E '0x[89a-f][0-9a-f]{15}' ... 0xffffffff810001c4/0x72e2e32955eb/-/-/-/0//- 0xffffffff810001c4/0x72e2d94a9821/-/-/-/0//- 0xffffffff810001c4/0x72e2d94ffa1b/-/-/-/0//- ... BRS provides no hardware branch filtering, so privilege level filtering is performed entirely in software. However, amd_brs_match_plm() only validates the branch-to address against the requested privilege levels. For branches from the kernel to user space, the branch-from address is left unchecked and is leaked. Extend the software filter to also validate the branch-from address, so that any branch record whose branch-from address is in the kernel is dropped when PERF_SAMPLE_BRANCH_USER is requested. Fixes: 8910075d61a3 ("perf/x86/amd: Enable branch sampling priv level filtering") Reported-by: Sashiko Signed-off-by: Sandipan Das Signed-off-by: Ingo Molnar Cc: stable@vger.kernel.org Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://patch.msgid.link/f05931c4f89a146c364bd5dc6b8170b1ac611c65.1783701239.git.sandipan.das@amd.com Closes: https://lore.kernel.org/all/20260710110235.F3FD81F000E9@smtp.kernel.org/ Signed-off-by: Greg Kroah-Hartman --- arch/x86/events/amd/brs.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/x86/events/amd/brs.c b/arch/x86/events/amd/brs.c index 06f35a6b58a5b..dc564688f3d73 100644 --- a/arch/x86/events/amd/brs.c +++ b/arch/x86/events/amd/brs.c @@ -259,13 +259,13 @@ void amd_brs_disable_all(void) amd_brs_disable(); } -static bool amd_brs_match_plm(struct perf_event *event, u64 to) +static bool amd_brs_match_plm(struct perf_event *event, u64 from, u64 to) { int type = event->attr.branch_sample_type; int plm_k = PERF_SAMPLE_BRANCH_KERNEL | PERF_SAMPLE_BRANCH_HV; int plm_u = PERF_SAMPLE_BRANCH_USER; - if (!(type & plm_k) && kernel_ip(to)) + if (!(type & plm_k) && (kernel_ip(to) || kernel_ip(from))) return 0; if (!(type & plm_u) && !kernel_ip(to)) @@ -338,11 +338,11 @@ void amd_brs_drain(void) */ to = (u64)(((s64)to << shift) >> shift); - if (!amd_brs_match_plm(event, to)) - continue; - rdmsrq(brs_from(brs_idx), from); + if (!amd_brs_match_plm(event, from, to)) + continue; + perf_clear_branch_entry_bitfields(br+nr); br[nr].from = from; From fb3b76b5ad2ebad63dd76f8b65b624eaf638b73f Mon Sep 17 00:00:00 2001 From: Sandipan Das Date: Fri, 10 Jul 2026 16:15:27 +0530 Subject: [PATCH 2308/2758] perf/x86/amd/lbr: Fix kernel address leakage commit 2a892294b83f541115c94b0bb637f39bef187657 upstream. A user-only branch stack can contain branches that originate from the kernel. As a result, kernel addresses are exposed to user space even when PERF_SAMPLE_BRANCH_USER is requested. On AMD processors supporting X86_FEATURE_AMD_LBR_V2, perf can still report SYSRET/ERET entries for which the branch-from addresses are in the kernel. E.g. $ perf record -e cycles -o - -j any,save_type,u -- \ perf bench syscall basic --loop 1000 | \ perf script -i - -F brstack|tr ' ' '\n'| \ grep -E '0x[89a-f][0-9a-f]{15}' ... 0xffffffff81001268/0x717a90a38f1a/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH 0xffffffff81001268/0x717a90a39157/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH 0xffffffff81001268/0x717a90a2c628/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH 0xffffffff81001268/0x717a90a41b60/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH 0xffffffff81001268/0x717a90a260db/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH 0xffffffff81001268/0x717a90a260db/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH 0xffffffff81001268/0x717a8bef1c30/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH 0xffffffff81001268/0x717a8e4d3c90/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH ... The reason is that the hardware filter only considers the privilege level applicable to the branch target. Extend software filtering to also validate the branch-from addresses against br_sel, so that any branch record whose branch-from address is in the kernel is dropped when PERF_SAMPLE_BRANCH_USER is requested. Fixes: f4f925dae741 ("perf/x86/amd/lbr: Add LbrExtV2 hardware branch filter support") Reported-by: Ian Rogers Signed-off-by: Sandipan Das Signed-off-by: Ingo Molnar Cc: stable@vger.kernel.org Cc: Peter Zijlstra Link: https://patch.msgid.link/a898a29725f6b2f30518354cdc2e432db66c43cf.1783680119.git.sandipan.das@amd.com Signed-off-by: Greg Kroah-Hartman --- arch/x86/events/amd/lbr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/x86/events/amd/lbr.c b/arch/x86/events/amd/lbr.c index d24da377df776..b9722e60d8bc7 100644 --- a/arch/x86/events/amd/lbr.c +++ b/arch/x86/events/amd/lbr.c @@ -125,7 +125,8 @@ static void amd_pmu_lbr_filter(void) } /* If type does not correspond, then discard */ - if (type == X86_BR_NONE || (br_sel & type) != type) { + if (type == X86_BR_NONE || (br_sel & type) != type || + (!(br_sel & X86_BR_KERNEL) && kernel_ip(cpuc->lbr_entries[i].from))) { cpuc->lbr_entries[i].from = 0; /* mark invalid */ compress = true; } From a18afd69408cf53035b4f1483f8c0d7de1aa813b Mon Sep 17 00:00:00 2001 From: Zhongqiu Han Date: Tue, 16 Jun 2026 23:47:33 +0800 Subject: [PATCH 2309/2758] cpufreq: schedutil: Fix uncleared need_freq_update on the .adjust_perf() path commit 9ef450ca74e43dacf9a2a15db7a851052c78dcf0 upstream. The need_freq_update flag makes sugov_should_update_freq() return true regardless of the rate_limit_us throttling, and is cleared in sugov_update_next_freq(). sugov_update_single_freq() and sugov_update_shared() go through that helper, so the flag does not persist there. However, sugov_update_single_perf(), used by drivers implementing the .adjust_perf() callback (e.g. intel_pstate or amd-pstate in passive mode) calls cpufreq_driver_adjust_perf() directly and never goes through sugov_update_next_freq(), so the need_freq_update flag is not cleared in that path. Before commit 75da043d8f88 ("cpufreq/sched: Set need_freq_update in ignore_dl_rate_limit()"), this was effectively harmless because sugov_should_update_freq() still honored the rate limit even when need_freq_update was set. After that change, the flag forces sugov_should_update_freq() to always return true, so once set, it stays effective indefinitely on the .adjust_perf() path. As a result, cpufreq_driver_adjust_perf() gets called on every scheduler utilization update (with the runqueue lock held) rather than being throttled by rate_limit_us, even if the driver itself may skip redundant hardware updates. Clear need_freq_update at the end of the adjust_perf path as well. Fixes: 75da043d8f88 ("cpufreq/sched: Set need_freq_update in ignore_dl_rate_limit()") Signed-off-by: Zhongqiu Han Reviewed-by: Hongyan Xia Reviewed-by: Christian Loehle Cc: All applicable [ rjw: Subject and changelog edits ] Link: https://patch.msgid.link/20260616154733.2405236-1-zhongqiu.han@oss.qualcomm.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- kernel/sched/cpufreq_schedutil.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index ae9fd211cec1f..a4e689eefdfbd 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -486,6 +486,7 @@ static void sugov_update_single_perf(struct update_util_data *hook, u64 time, cpufreq_driver_adjust_perf(sg_policy->policy, sg_cpu->bw_min, sg_cpu->util, max_cap); + sg_policy->need_freq_update = false; sg_policy->last_freq_update_time = time; } From cabcfbc069d804e4fb4215cf1167c531ed2a6394 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 7 Jul 2026 19:19:55 +0200 Subject: [PATCH 2310/2758] cpufreq: intel_pstate: Set non-turbo capacity to HWP_GUARANTEED_PERF() commit 27d80e0f8b8dff97503fc0061754b1d3800cb961 upstream. Setting cpu->capacity_perf to cpu->pstate.max_pstate_physical in the "no turbo" case is inconsistent with what happens elsewhere in the driver and causes arch_scale_cpu_capacity() to be incorrect. It also skews arch_scale_freq_capacity() which ends up differing from 1024 for the guaranteed P-state. Address that by setting capacity_perf to HWP_GUARANTEED_PERF() in the "no turbo" case. Fixes: 929ebc93ccaa ("cpufreq: intel_pstate: Set asymmetric CPU capacity on hybrid systems") Signed-off-by: Rafael J. Wysocki Tested-by: Ricardo Neri Cc: All applicable Link: https://patch.msgid.link/12928972.O9o76ZdvQC@rafael.j.wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/cpufreq/intel_pstate.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 0fbbdbd5765cf..3a44d2da08d98 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -1058,12 +1058,14 @@ static void hybrid_clear_cpu_capacity(unsigned int cpunum) static void hybrid_get_capacity_perf(struct cpudata *cpu) { + u64 hwp_cap = READ_ONCE(cpu->hwp_cap_cached); + if (READ_ONCE(global.no_turbo)) { - cpu->capacity_perf = cpu->pstate.max_pstate_physical; + cpu->capacity_perf = HWP_GUARANTEED_PERF(hwp_cap); return; } - cpu->capacity_perf = HWP_HIGHEST_PERF(READ_ONCE(cpu->hwp_cap_cached)); + cpu->capacity_perf = HWP_HIGHEST_PERF(hwp_cap); } static void hybrid_set_capacity_of_cpus(void) From f79dff8c721bbb1f3fc312ea55e0551c2cc28801 Mon Sep 17 00:00:00 2001 From: Sumanth Korikkar Date: Mon, 6 Jul 2026 12:46:31 +0200 Subject: [PATCH 2311/2758] s390/perf_cpum_cf: Add missing array_index_nospec() to __hw_perf_event_init() commit 49145bce539117db4b6e9e83c0e5ef528e361050 upstream. ev variable is userspace controlled via event->attr.config and used as an array index after bounds checking, but without speculation barriers. Add the missing array_index_nospec() call to prevent speculative execution. Cc: stable@vger.kernel.org Fixes: 212188a596d1 ("[S390] perf: add support for s390x CPU counters") Signed-off-by: Sumanth Korikkar Reviewed-by: Ilya Leoshkevich Acked-by: Thomas Richter Signed-off-by: Vasily Gorbik Signed-off-by: Greg Kroah-Hartman --- arch/s390/kernel/perf_cpum_cf.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/s390/kernel/perf_cpum_cf.c b/arch/s390/kernel/perf_cpum_cf.c index 7aa655664eccb..2076ac22e2c49 100644 --- a/arch/s390/kernel/perf_cpum_cf.c +++ b/arch/s390/kernel/perf_cpum_cf.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -768,6 +769,7 @@ static int __hw_perf_event_init(struct perf_event *event, unsigned int type) if (!is_userspace_event(ev)) { if (ev >= ARRAY_SIZE(cpumf_generic_events_user)) return -EOPNOTSUPP; + ev = array_index_nospec(ev, ARRAY_SIZE(cpumf_generic_events_user)); ev = cpumf_generic_events_user[ev]; } } else if (!attr->exclude_kernel && attr->exclude_user) { @@ -778,6 +780,7 @@ static int __hw_perf_event_init(struct perf_event *event, unsigned int type) if (!is_userspace_event(ev)) { if (ev >= ARRAY_SIZE(cpumf_generic_events_basic)) return -EOPNOTSUPP; + ev = array_index_nospec(ev, ARRAY_SIZE(cpumf_generic_events_basic)); ev = cpumf_generic_events_basic[ev]; } } From 916dac5f2944f63f07f7b501acbea6737dbbed0e Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sun, 28 Jun 2026 08:45:41 +0200 Subject: [PATCH 2312/2758] batman-adv: gw: acquire ethernet header only after skb realloc commit 77880a3be88d378d60cc1e8f8ec70430e2ed0518 upstream. The pskb_may_pull() called by batadv_get_vid() could reallocate the buffer behind the skb. Variables which were pointing to the old buffer need to be reassigned to avoid an use-after-free. Cc: stable@vger.kernel.org Fixes: 6c413b1c22a2 ("batman-adv: send every DHCP packet as bat-unicast") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/gateway_client.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index a9d0346e8332e..36137acb6eb22 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c @@ -683,12 +683,13 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, struct batadv_gw_node *gw_node = NULL; struct batadv_gw_node *curr_gw = NULL; struct batadv_neigh_ifinfo *curr_ifinfo, *old_ifinfo; - struct ethhdr *ethhdr = (struct ethhdr *)skb->data; + struct ethhdr *ethhdr; bool out_of_range = false; u8 curr_tq_avg; unsigned short vid; vid = batadv_get_vid(skb, 0); + ethhdr = (struct ethhdr *)skb->data; if (is_multicast_ether_addr(ethhdr->h_dest)) goto out; From b031fc97e1993d29d6c3a0e86a99140528cf31e8 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sun, 28 Jun 2026 06:44:13 +0200 Subject: [PATCH 2313/2758] batman-adv: retrieve ethhdr after potential skb realloc on RX commit 035e1fed892d3d06002a73ff73668f618a514644 upstream. pskb_may_pull() in batadv_interface_rx() could reallocate the buffer behind the skb. Variables which were pointing to the old buffer need to be reassigned to avoid an use-after-free. This was done correctly for the VLAN header but missed for the ethernet header which is later used for the TT and AP isolation handling. Cc: stable@vger.kernel.org Reported-by: Sashiko Fixes: c6c8fea29769 ("net: Add batman-adv meshing protocol") Fixes: c78296665c3d ("batman-adv: Check skb size before using encapsulated ETH+VLAN header") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/mesh-interface.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c index e7aa45bc6b7ad..d2bb7f5e29087 100644 --- a/net/batman-adv/mesh-interface.c +++ b/net/batman-adv/mesh-interface.c @@ -432,6 +432,7 @@ void batadv_interface_rx(struct net_device *mesh_iface, if (!pskb_may_pull(skb, VLAN_ETH_HLEN)) goto dropped; + ethhdr = eth_hdr(skb); vhdr = skb_vlan_eth_hdr(skb); /* drop batman-in-batman packets to prevent loops */ From 9a7b7248798123efbd5fafe58461d57c7cc718af Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sun, 28 Jun 2026 08:35:35 +0200 Subject: [PATCH 2314/2758] batman-adv: access unicast_ttvn skb->data only after skb realloc commit 7141990add3f75436f2933cb310654cad3b1e3e9 upstream. The pskb_may_pull() called by batadv_get_vid() could reallocate the buffer behind the skb. Variables which were pointing to the old buffer need to be reassigned to avoid an use-after-free. This was done correctly for the ethernet header but missed for the unicast_packet pointer. Cc: stable@vger.kernel.org Reported-by: Sashiko Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/routing.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 41951c7a1c50b..44e1f5746ee54 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -855,8 +855,8 @@ static bool batadv_check_unicast_ttvn(struct batadv_priv *bat_priv, if (skb_cow(skb, sizeof(*unicast_packet)) < 0) return false; - unicast_packet = (struct batadv_unicast_packet *)skb->data; vid = batadv_get_vid(skb, hdr_len); + unicast_packet = (struct batadv_unicast_packet *)skb->data; ethhdr = (struct ethhdr *)(skb->data + hdr_len); /* do not reroute multicast frames in a unicast header */ From 059a70e1d12d6d99310e0599d37b0323557569a8 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sun, 28 Jun 2026 08:45:41 +0200 Subject: [PATCH 2315/2758] batman-adv: dat: acquire ARP hw source only after skb realloc commit 48067b2ae4504500a7093d9e1e16b42e70330480 upstream. The pskb_may_pull() called by batadv_get_vid() could reallocate the buffer behind the skb. Variables which were pointing to the old buffer need to be reassigned to avoid an use-after-free. Cc: stable@vger.kernel.org Fixes: b61ec31c8575 ("batman-adv: Snoop DHCPACKs for DAT") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/distributed-arp-table.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index 86fb5de5022ac..e15feeb105df2 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -1746,6 +1746,7 @@ void batadv_dat_snoop_incoming_dhcp_ack(struct batadv_priv *bat_priv, struct ethhdr *ethhdr; __be32 ip_src, yiaddr; unsigned short vid; + int hdr_size_tmp; __be16 proto; u8 *hw_src; @@ -1762,8 +1763,10 @@ void batadv_dat_snoop_incoming_dhcp_ack(struct batadv_priv *bat_priv, if (!batadv_dat_check_dhcp_ack(skb, proto, &ip_src, chaddr, &yiaddr)) return; + hdr_size_tmp = hdr_size; + vid = batadv_dat_get_vid(skb, &hdr_size_tmp); + ethhdr = (struct ethhdr *)(skb->data + hdr_size); hw_src = ethhdr->h_source; - vid = batadv_dat_get_vid(skb, &hdr_size); batadv_dat_entry_add(bat_priv, yiaddr, chaddr, vid); batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid); From f4fb97ecf677cd9c3aa4f3bfc6fbf5b0e4bdbbbf Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sun, 28 Jun 2026 08:45:41 +0200 Subject: [PATCH 2316/2758] batman-adv: bla: reacquire gw address after skb realloc commit cdf3b5af2bc4431e58629e8ad2086b1e9185c761 upstream. The pskb_may_pull() called by batadv_bla_is_backbone_gw() could reallocate the buffer behind the skb. Variables which were pointing to the old buffer need to be reassigned to avoid an use-after-free. Cc: stable@vger.kernel.org Fixes: 9e794b6bf4a2 ("batman-adv: drop unicast packets from other backbone gw") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/routing.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 44e1f5746ee54..9de5bc552bfdc 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -1029,6 +1029,7 @@ int batadv_recv_unicast_packet(struct sk_buff *skb, hdr_size); batadv_orig_node_put(orig_node_gw); if (is_gw) { + orig_addr_gw = eth_hdr(skb)->h_source; batadv_dbg(BATADV_DBG_BLA, bat_priv, "%s(): Dropped unicast pkt received from another backbone gw %pM.\n", __func__, orig_addr_gw); From 4407ff3af469356f9641c4a6e7072309bae86bea Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sun, 28 Jun 2026 10:37:07 +0200 Subject: [PATCH 2317/2758] batman-adv: dat: ensure accessible eth_hdr proto field commit 26560c4a03dc4d607331600c187f59ab2df5f341 upstream. When batadv_get_vid() accesses the proto field of the ethernet header, it is not checking if the data itself is accessible. The caller is responsible for it. But in contrast to other call sites, batadv_dat_get_vid() and its caller didn't make sure this is true. This could have caused an out-of-bounds access. Cc: stable@vger.kernel.org Reported-by: Sashiko Fixes: be1db4f6615b ("batman-adv: make the Distributed ARP Table vlan aware") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/distributed-arp-table.c | 23 +++++++++++++++++++++++ net/batman-adv/main.c | 3 +++ 2 files changed, 26 insertions(+) diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index e15feeb105df2..1edafffe40a06 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -1065,6 +1065,9 @@ static u16 batadv_arp_get_type(struct batadv_priv *bat_priv, * @skb: the buffer containing the packet to extract the VID from * @hdr_size: the size of the batman-adv header encapsulating the packet * + * The caller must ensure that at least @hdr_size + ETH_HLEN bytes are + * accessible after skb->data. + * * Return: If the packet embedded in the skb is vlan tagged this function * returns the VID with the BATADV_VLAN_HAS_TAG flag. Otherwise BATADV_NO_FLAGS * is returned. @@ -1147,6 +1150,10 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv, if (!atomic_read(&bat_priv->distributed_arp_table)) goto out; + /* first, find out the vid. */ + if (!pskb_may_pull(skb, hdr_size + ETH_HLEN)) + goto out; + vid = batadv_dat_get_vid(skb, &hdr_size); type = batadv_arp_get_type(bat_priv, skb, hdr_size); @@ -1242,6 +1249,10 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv, if (!atomic_read(&bat_priv->distributed_arp_table)) goto out; + /* first, find out the vid. */ + if (!pskb_may_pull(skb, hdr_size + ETH_HLEN)) + goto out; + vid = batadv_dat_get_vid(skb, &hdr_size); type = batadv_arp_get_type(bat_priv, skb, hdr_size); @@ -1304,6 +1315,10 @@ void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv, if (!atomic_read(&bat_priv->distributed_arp_table)) return; + /* first, find out the vid. */ + if (!pskb_may_pull(skb, hdr_size + ETH_HLEN)) + return; + vid = batadv_dat_get_vid(skb, &hdr_size); type = batadv_arp_get_type(bat_priv, skb, hdr_size); @@ -1352,6 +1367,10 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv, if (!atomic_read(&bat_priv->distributed_arp_table)) goto out; + /* first, find out the vid. */ + if (!pskb_may_pull(skb, hdr_size + ETH_HLEN)) + goto out; + vid = batadv_dat_get_vid(skb, &hdr_size); type = batadv_arp_get_type(bat_priv, skb, hdr_size); @@ -1806,6 +1825,10 @@ bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv, if (batadv_forw_packet_is_rebroadcast(forw_packet)) goto out; + /* first, find out the vid. */ + if (!pskb_may_pull(forw_packet->skb, hdr_size + ETH_HLEN)) + goto out; + vid = batadv_dat_get_vid(forw_packet->skb, &hdr_size); type = batadv_arp_get_type(bat_priv, forw_packet->skb, hdr_size); diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index a4d33ee0fda59..36ce95059bdc7 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -566,6 +566,9 @@ void batadv_recv_handler_unregister(u8 packet_type) * @skb: the buffer containing the packet * @header_len: length of the batman header preceding the ethernet header * + * The caller must ensure that at least @header_len + ETH_HLEN bytes are + * accessible after skb->data. + * * Return: VID with the BATADV_VLAN_HAS_TAG flag when the packet embedded in the * skb is vlan tagged. Otherwise BATADV_NO_FLAGS. */ From dbeb4145d9778f922f459935da9a027750765a69 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 2 Jul 2026 11:46:21 +0200 Subject: [PATCH 2318/2758] batman-adv: ensure minimal ethernet header on TX commit 49df66b7993c80b80c7eb9a84ba5b3410c8296a0 upstream. As documented in commit 8bd67ebb50c0 ("net: bridge: xmit: make sure we have at least eth header len bytes"), it is possible by for a local user with eBPF TC hook access to attach a tc filter which truncates the packet and redirects to an batadv interface. But the code assumes that at least ETH_HLEN bytes are available and thus might read outside of the available buffer. The batadv_interface_tx() must therefore always check itself if enough data is available for the ethernet header and don't rely on min_header_len. Cc: stable@vger.kernel.org Fixes: c6c8fea29769 ("net: Add batman-adv meshing protocol") Reported-by: Sashiko Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/mesh-interface.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c index d2bb7f5e29087..7259bbe069971 100644 --- a/net/batman-adv/mesh-interface.c +++ b/net/batman-adv/mesh-interface.c @@ -193,6 +193,9 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb, if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) goto dropped; + if (!pskb_may_pull(skb, ETH_HLEN)) + goto dropped; + /* reset control block to avoid left overs from previous users */ memset(skb->cb, 0, sizeof(struct batadv_skb_cb)); From 64fd0b0dbb5210bd071b82fce30d65218f5953b5 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 2 Jul 2026 19:32:40 +0200 Subject: [PATCH 2319/2758] batman-adv: dat: fix tie-break for candidate selection commit 98052bdaf6ac1639a63ffc10244eeeab1f62ed2b upstream. The original version of the candidate selection for DAT attempted to compare both candidate and max_orig_node to identify which has the smaller MAC address. This comparison is required as tie-break when a hash collision happened. But the used function returned 0 when the function was not equal and a non-zero value when it was equal. As result, the actually selected node was dependent on the order of entries in the orig_hash and not actually on the mac addresses. The last originator in the hash collision would always win. To have a proper ordering, it must diff the actual MAC address bytes and reject the candidate when the diff is not smaller than 0. Cc: stable@vger.kernel.org Fixes: 785ea1144182 ("batman-adv: Distributed ARP Table - create DHT helper functions") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/distributed-arp-table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index 1edafffe40a06..bcfbb39889a1c 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -545,7 +545,7 @@ static bool batadv_is_orig_node_eligible(struct batadv_dat_candidate *res, * the one with the lowest address */ if (tmp_max == max && max_orig_node && - batadv_compare_eth(candidate->orig, max_orig_node->orig)) + memcmp(candidate->orig, max_orig_node->orig, ETH_ALEN) >= 0) goto out; ret = true; From aba1cf21954e64c36afb966b754adad2b0b8aa48 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 2 Jul 2026 21:06:23 +0200 Subject: [PATCH 2320/2758] batman-adv: tt: avoid request storms during pending request commit 27c7d40008231ae4140d35501b60087a9de2d2c3 upstream. batadv_send_tt_request() allocates a tt_req_node when none exists for the destination originator node. This should prevent that a multiple TT requests are send at the same time to an originator. But if allocation of the send buffer failed, this request must be cleaned up again. But indicator for such a failure is "ret == false". But the actual implementation is checking for "ret == true". The check must be inverted to not loose the information about the TT request directly after it was attempted to be sent out. This should avoid potential request storms. Cc: stable@vger.kernel.org Fixes: 335fbe0f5d25 ("batman-adv: tvlv - convert tt query packet to use tvlv unicast packets") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/translation-table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 83dfd804a143a..79bcce09ca05f 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -2963,7 +2963,7 @@ static bool batadv_send_tt_request(struct batadv_priv *bat_priv, out: batadv_hardif_put(primary_if); - if (ret && tt_req_node) { + if (!ret && tt_req_node) { spin_lock_bh(&bat_priv->tt.req_list_lock); if (!hlist_unhashed(&tt_req_node->list)) { hlist_del_init(&tt_req_node->list); From ae8355b24abec43328c8bf8187645a5310e90a43 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 2 Jul 2026 20:45:24 +0200 Subject: [PATCH 2321/2758] batman-adv: fix VLAN priority offset commit fdb3be00ba4dafa313e699d6b5b90d13f22f3f25 upstream. The batadv_skb_set_priority() receives an SKB with the inner ethernet header at position "offset". When it tries to extract the IPv4 and IPv6 header, it needs to skip the ethernet header to get access to the IP header. But for VLAN header, it performs the access with the struct vlan_ethhdr. This struct contains both both the ethernet header and the VLAN header. It is therefore incorrect to skip over the whole vlan_ethhdr size to get access to the vlan_ethhdr. Cc: stable@vger.kernel.org Fixes: c54f38c9aa22 ("batman-adv: set skb priority according to content") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index 36ce95059bdc7..b95fcd17be692 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -354,7 +354,7 @@ void batadv_skb_set_priority(struct sk_buff *skb, int offset) switch (ethhdr->h_proto) { case htons(ETH_P_8021Q): - vhdr = skb_header_pointer(skb, offset + sizeof(*vhdr), + vhdr = skb_header_pointer(skb, offset, sizeof(*vhdr), &vhdr_tmp); if (!vhdr) return; From 08047838817561cef33ada9774a2a4663d499ecb Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 3 Jul 2026 20:28:31 +0200 Subject: [PATCH 2322/2758] batman-adv: frag: free unfragmentable packet commit 6b628425aed49a1c7a4ffc997583840fc582d32b upstream. The caller of batadv_frag_send_packet() assume that the skb provided to the function are always consumed. But the pre-check for an empty payload or the zero fragment size returned an error without any further actions. A failed pre-check must use the same error handling code as the rest of the function. Cc: stable@vger.kernel.org Fixes: ee75ed88879a ("batman-adv: Fragment and send skbs larger than mtu") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/fragmentation.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c index 9a5927ecc4746..be206f1070d3b 100644 --- a/net/batman-adv/fragmentation.c +++ b/net/batman-adv/fragmentation.c @@ -516,8 +516,10 @@ int batadv_frag_send_packet(struct sk_buff *skb, mtu = min_t(unsigned int, mtu, BATADV_FRAG_MAX_FRAG_SIZE); max_fragment_size = mtu - header_size; - if (skb->len == 0 || max_fragment_size == 0) - return -EINVAL; + if (skb->len == 0 || max_fragment_size == 0) { + ret = -EINVAL; + goto free_skb; + } num_fragments = (skb->len - 1) / max_fragment_size + 1; max_fragment_size = (skb->len - 1) / num_fragments + 1; From 5a82c558098889cc8bfff85cc62f563833205d91 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 4 Jul 2026 09:46:09 +0200 Subject: [PATCH 2323/2758] batman-adv: clean untagged VLAN on netdev registration failure commit 8669a550c752d86baebc5fdc83b8ff35c4372c0e upstream. When an mesh interface is registered, it creates an untagged struct batadv_meshif_vlan on top of it via the NETDEV_REGISTER notifier. But in this process, another receiver of this notification can veto the registration. The netdev registration will be aborted because of this veto. The register_netdevice() call will try to clean up the net_device using unregister_netdevice_queue() - which only uses the .priv_destructor to free private resources. In this situation, .dellink will not be called. The cleanup of the untagged batadv_meshif_vlan must thefore be done in the destructor to avoid a leak of this object. Cc: stable@vger.kernel.org Fixes: 5d2c05b21337 ("batman-adv: add per VLAN interface attribute framework") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/main.c | 8 ++++++++ net/batman-adv/mesh-interface.c | 13 ++----------- net/batman-adv/mesh-interface.h | 2 ++ 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index b95fcd17be692..b9b0bef448030 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -245,6 +245,7 @@ int batadv_mesh_init(struct net_device *mesh_iface) void batadv_mesh_free(struct net_device *mesh_iface) { struct batadv_priv *bat_priv = netdev_priv(mesh_iface); + struct batadv_meshif_vlan *vlan; atomic_set(&bat_priv->mesh_state, BATADV_MESH_DEACTIVATING); @@ -259,6 +260,13 @@ void batadv_mesh_free(struct net_device *mesh_iface) batadv_mcast_free(bat_priv); + /* destroy the "untagged" VLAN */ + vlan = batadv_meshif_vlan_get(bat_priv, BATADV_NO_FLAGS); + if (vlan) { + batadv_meshif_destroy_vlan(bat_priv, vlan); + batadv_meshif_vlan_put(vlan); + } + /* Free the TT and the originator tables only after having terminated * all the other depending components which may use these structures for * their purposes. diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c index 7259bbe069971..6f4c8da4fa5d3 100644 --- a/net/batman-adv/mesh-interface.c +++ b/net/batman-adv/mesh-interface.c @@ -593,8 +593,8 @@ int batadv_meshif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid) * @bat_priv: the bat priv with all the mesh interface information * @vlan: the object to remove */ -static void batadv_meshif_destroy_vlan(struct batadv_priv *bat_priv, - struct batadv_meshif_vlan *vlan) +void batadv_meshif_destroy_vlan(struct batadv_priv *bat_priv, + struct batadv_meshif_vlan *vlan) { /* explicitly remove the associated TT local entry because it is marked * with the NOPURGE flag @@ -1089,22 +1089,13 @@ static int batadv_meshif_newlink(struct net_device *dev, static void batadv_meshif_destroy_netlink(struct net_device *mesh_iface, struct list_head *head) { - struct batadv_priv *bat_priv = netdev_priv(mesh_iface); struct batadv_hard_iface *hard_iface; - struct batadv_meshif_vlan *vlan; while (!list_empty(&mesh_iface->adj_list.lower)) { hard_iface = netdev_adjacent_get_private(mesh_iface->adj_list.lower.next); batadv_hardif_disable_interface(hard_iface); } - /* destroy the "untagged" VLAN */ - vlan = batadv_meshif_vlan_get(bat_priv, BATADV_NO_FLAGS); - if (vlan) { - batadv_meshif_destroy_vlan(bat_priv, vlan); - batadv_meshif_vlan_put(vlan); - } - unregister_netdevice_queue(mesh_iface, head); } diff --git a/net/batman-adv/mesh-interface.h b/net/batman-adv/mesh-interface.h index 53756c5a45e04..5e1e83e04ffbc 100644 --- a/net/batman-adv/mesh-interface.h +++ b/net/batman-adv/mesh-interface.h @@ -21,6 +21,8 @@ void batadv_interface_rx(struct net_device *mesh_iface, bool batadv_meshif_is_valid(const struct net_device *net_dev); extern struct rtnl_link_ops batadv_link_ops; int batadv_meshif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid); +void batadv_meshif_destroy_vlan(struct batadv_priv *bat_priv, + struct batadv_meshif_vlan *vlan); void batadv_meshif_vlan_release(struct kref *ref); struct batadv_meshif_vlan *batadv_meshif_vlan_get(struct batadv_priv *bat_priv, unsigned short vid); From 777a88256d6f70672116e53400659cc417e1feaa Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 3 Jul 2026 21:04:03 +0200 Subject: [PATCH 2324/2758] batman-adv: frag: fix primary_if leak on failed linearization commit 353d2c1d5492e53ae34f490a84494124dc3d3531 upstream. If the skb has a frag_list, it must be linearized before it can be split using skb_split(). But when this step failed, it must not only free the skb but also take care of the reference to the already found primary_if. Cc: stable@vger.kernel.org Reported-by: Sashiko Fixes: a063f2fba3fa ("batman-adv: Don't skb_split skbuffs with frag_list") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/fragmentation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c index be206f1070d3b..9a8db648f957e 100644 --- a/net/batman-adv/fragmentation.c +++ b/net/batman-adv/fragmentation.c @@ -545,7 +545,7 @@ int batadv_frag_send_packet(struct sk_buff *skb, */ if (skb_has_frag_list(skb) && __skb_linearize(skb)) { ret = -ENOMEM; - goto free_skb; + goto put_primary_if; } /* Create one header to be copied to all fragments */ From 7d1a877670bc2e901241073f022ca8d1b2f85f1c Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 3 Jul 2026 20:47:45 +0200 Subject: [PATCH 2325/2758] batman-adv: mcast: avoid OOB read of num_dests header commit 38eaed28e250895d56f4b7989bd65479a511c5c3 upstream. Before the access to struct batadv_tvlv_mcast_tracker's num_dests, it is attempted to check whether enough space is actually in the network header. But instead of using offsetofend() to check for the whole size (2) which must be accessible, offsetof() of is called. The latter is always returning 0. The comparison with the network header length will always return that enough data is available - even when only 1 or 0 bytes are accessible. Instead of using offsetofend(), use the more common check for the whole header. Cc: stable@vger.kernel.org Fixes: 07afe1ba288c ("batman-adv: mcast: implement multicast packet reception and forwarding") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/multicast_forw.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/net/batman-adv/multicast_forw.c b/net/batman-adv/multicast_forw.c index b8668a80b94a1..1404a3b7adfb1 100644 --- a/net/batman-adv/multicast_forw.c +++ b/net/batman-adv/multicast_forw.c @@ -927,11 +927,11 @@ static int batadv_mcast_forw_packet(struct batadv_priv *bat_priv, { struct batadv_tvlv_mcast_tracker *mcast_tracker; struct batadv_neigh_node *neigh_node; - unsigned long offset, num_dests_off; struct sk_buff *nexthop_skb; unsigned char *skb_net_hdr; bool local_recv = false; unsigned int tvlv_len; + unsigned long offset; bool xmitted = false; u8 *dest, *next_dest; u16 num_dests; @@ -940,9 +940,8 @@ static int batadv_mcast_forw_packet(struct batadv_priv *bat_priv, /* (at least) TVLV part needs to be linearized */ SKB_LINEAR_ASSERT(skb); - /* check if num_dests is within skb length */ - num_dests_off = offsetof(struct batadv_tvlv_mcast_tracker, num_dests); - if (num_dests_off > skb_network_header_len(skb)) + /* check if batadv_tvlv_mcast_tracker header is within skb length */ + if (sizeof(*mcast_tracker) > skb_network_header_len(skb)) return -EINVAL; skb_net_hdr = skb_network_header(skb); From 6222b443686525cb5a9b6a9cecf23b2e2ab23e2a Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 3 Jul 2026 22:27:13 +0200 Subject: [PATCH 2326/2758] batman-adv: tt: prevent TVLV OOB check overflow commit 7a581d9aaba8c82bd6177fa36b2588eea77f6e2b upstream. A TT unicast TVLV contains the number of VLANs stored in it. This number is an u16 and gets multiplied by the size of the struct batadv_tvlv_tt_vlan_data (8 bytes). The size can therefore overflow the u16 used to store the tt_vlan_len. All additional safety checks to prevent out-of-bounds access of the TVLV buffer are invalid due to this overflow. Using size_t prevents this overflow and ensures that the safety checks compare against the actual buffer requirements. Cc: stable@vger.kernel.org Fixes: 7ea7b4a14275 ("batman-adv: make the TT CRC logic VLAN specific") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/translation-table.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 79bcce09ca05f..877475c688252 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -4025,7 +4025,8 @@ static int batadv_tt_tvlv_unicast_handler_v1(struct batadv_priv *bat_priv, u16 tvlv_value_len) { struct batadv_tvlv_tt_data *tt_data; - u16 tt_vlan_len, tt_num_entries; + u16 tt_num_entries; + size_t tt_vlan_len; char tt_flag; bool ret; From da743704c647226a45ba0082982e5363f54d3c96 Mon Sep 17 00:00:00 2001 From: Shyam Prasad N Date: Thu, 14 May 2026 23:38:07 +0530 Subject: [PATCH 2327/2758] cifs: invalidate cfid on unlink/rename/rmdir commit f71d68d2b473e47db260c27c98212829590d8bae upstream. Today we do not invalidate the cached_dirent or the entire parent cfid when a dentry in a dir has been removed/moved. This change invalidates the parent cfid so that we don't serve directory contents from the cache. Cc: Signed-off-by: Shyam Prasad N Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/inode.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index 3b5bac93812d6..826d36ed13ec9 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -28,6 +28,23 @@ #include "cached_dir.h" #include "reparse.h" +static void cifs_invalidate_cached_dir(struct cifs_tcon *tcon, + struct dentry *parent) +{ + struct cached_fid *parent_cfid = NULL; + + if (!tcon || !parent) + return; + + if (!open_cached_dir_by_dentry(tcon, parent, &parent_cfid)) { + mutex_lock(&parent_cfid->dirents.de_mutex); + parent_cfid->dirents.is_valid = false; + parent_cfid->dirents.is_failed = true; + mutex_unlock(&parent_cfid->dirents.de_mutex); + close_cached_dir(parent_cfid); + } +} + /* * Set parameters for the netfs library */ @@ -2067,6 +2084,9 @@ static int __cifs_unlink(struct inode *dir, struct dentry *dentry, bool sillyren cifs_set_file_info(inode, attrs, xid, full_path, origattr); out_reval: + if (!rc && dentry->d_parent) + cifs_invalidate_cached_dir(tcon, dentry->d_parent); + if (inode) { cifs_inode = CIFS_I(inode); cifs_inode->time = 0; /* will force revalidate to get info @@ -2378,7 +2398,6 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry) } rc = server->ops->rmdir(xid, tcon, full_path, cifs_sb); - cifs_put_tlink(tlink); cifsInode = CIFS_I(d_inode(direntry)); @@ -2388,6 +2407,8 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry) i_size_write(d_inode(direntry), 0); clear_nlink(d_inode(direntry)); spin_unlock(&d_inode(direntry)->i_lock); + if (direntry->d_parent) + cifs_invalidate_cached_dir(tcon, direntry->d_parent); } /* force revalidate to go get info when needed */ @@ -2402,6 +2423,7 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry) inode_set_ctime_current(d_inode(direntry)); inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); + cifs_put_tlink(tlink); rmdir_exit: free_dentry_path(page); @@ -2668,6 +2690,12 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir, } /* force revalidate to go get info when needed */ + if (!rc) { + cifs_invalidate_cached_dir(tcon, source_dentry->d_parent); + if (target_dentry->d_parent != source_dentry->d_parent) + cifs_invalidate_cached_dir(tcon, target_dentry->d_parent); + } + CIFS_I(source_dir)->time = CIFS_I(target_dir)->time = 0; cifs_rename_exit: From 089ea1e2faf4968d698087b4325f952ce769a50f Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 21 May 2026 10:36:24 +0200 Subject: [PATCH 2328/2758] mfd: tps6586x: Fix OF node refcount commit 3001ed2b4e06da2276c42ace6551617065a5b1f9 upstream. Platform devices created with platform_device_alloc() call platform_device_release() when the last reference to the device's kobject is dropped. This function calls of_node_put() unconditionally. This works fine for devices created with platform_device_register_full() but users of the split approach (platform_device_alloc() + platform_device_add()) must bump the reference of the of_node they assign manually. Add the missing call to of_node_get(). Cc: stable@vger.kernel.org Fixes: 62f6b0879304 ("tps6586x: Add device tree support") Signed-off-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260521-pdev-fwnode-ref-v1-1-88c324a1b8d2@oss.qualcomm.com Signed-off-by: Lee Jones Signed-off-by: Greg Kroah-Hartman --- drivers/mfd/tps6586x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c index 8d5fe2b60bfa5..f5f8054466033 100644 --- a/drivers/mfd/tps6586x.c +++ b/drivers/mfd/tps6586x.c @@ -397,7 +397,7 @@ static int tps6586x_add_subdevs(struct tps6586x *tps6586x, pdev->dev.parent = tps6586x->dev; pdev->dev.platform_data = subdev->platform_data; - pdev->dev.of_node = subdev->of_node; + pdev->dev.of_node = of_node_get(subdev->of_node); ret = platform_device_add(pdev); if (ret) { From 0ec5c7f03ecf66f8d31d595a2bbf439e32d0eb94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Duje=20Mihanovi=C4=87?= Date: Sat, 28 Mar 2026 21:42:16 +0100 Subject: [PATCH 2329/2758] backlight: ktd2801: Enable BL_CORE_SUSPENDRESUME MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit f37f5a2ac9d3737617c08f0dc7270b42e9cad907 upstream. Boards using this backlight chip do not power the backlight off on suspend. Enable BL_CORE_SUSPENDRESUME so the chip gets powered off by the backlight core on suspend. Tested on samsung,coreprimevelte. Cc: stable@vger.kernel.org # v6.19 Signed-off-by: Duje Mihanović Reviewed-by: Daniel Thompson (RISCstar) Link: https://patch.msgid.link/20260328-ktd2801-pm-fix-v1-1-007cb103faeb@dujemihanovic.xyz Signed-off-by: Lee Jones Signed-off-by: Greg Kroah-Hartman --- drivers/video/backlight/ktd2801-backlight.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/video/backlight/ktd2801-backlight.c b/drivers/video/backlight/ktd2801-backlight.c index 17eac1b3bce4a..1b1307e03b204 100644 --- a/drivers/video/backlight/ktd2801-backlight.c +++ b/drivers/video/backlight/ktd2801-backlight.c @@ -53,6 +53,7 @@ static int ktd2801_update_status(struct backlight_device *bd) } static const struct backlight_ops ktd2801_backlight_ops = { + .options = BL_CORE_SUSPENDRESUME, .update_status = ktd2801_update_status, }; From fc5eb0962a5e50d64e711817cc24d67df2d90528 Mon Sep 17 00:00:00 2001 From: Terry Bowman Date: Fri, 5 Jun 2026 13:06:10 -0500 Subject: [PATCH 2330/2758] cxl: Fix CXL_HEADERLOG_SIZE to match RAS Capability size commit c268f949e219f9e179558e836f457f6c5fbec416 upstream. The CXL r4.0 8.2.4.17.7 RAS Capability Structure has total length 0x58 bytes (CXL_RAS_CAPABILITY_LENGTH); the Header Log occupies the trailing 64 bytes at offset 0x18. CXL_HEADERLOG_SIZE was defined as SZ_512, eight times the actual on-device size. header_log_copy() reads CXL_HEADERLOG_SIZE_U32 (128) dwords from the RAS capability iomap, overrunning the 88-byte mapping by 448 bytes. The cxl_aer_uncorrectable_error trace event memcpy()s CXL_HEADERLOG_SIZE (512) bytes from its source. For the CPER caller the source is struct cxl_ras_capability_regs::header_log[16] (64 bytes) embedded in a stack-local cxl_cper_prot_err_work_data, so the memcpy reads 448 bytes of kernel stack into the trace event ring buffer where userspace can read it via tracefs. Set CXL_HEADERLOG_SIZE to 64 and derive CXL_HEADERLOG_SIZE_U32 from it, bringing all iomap readers into agreement on 16 dwords. Userspace tools such as rasdaemon have grown a dependency on the buggy 512-byte (128 u32) header_log layout in the cxl_aer_uncorrectable_error trace event. Add CXL_HEADERLOG_TRACE_SIZE_U32 = 128 and use it for the trace event __array and its memcpy to preserve that ABI. Both callers now pass a zero-filled u32[CXL_HEADERLOG_TRACE_SIZE_U32] staging buffer with only the first CXL_HEADERLOG_SIZE_U32 (16) entries populated from hardware; the remaining 112 u32s are zero-padded, keeping the 512-byte trace ring buffer layout intact. [ dj: Replaced 64 with SZ_64 per RichardC ] Fixes: 36f257e3b0ba ("acpi/ghes, cxl/pci: Process CXL CPER Protocol Errors") Fixes: 2905cb5236cb ("cxl/pci: Add (hopeful) error handling support") Cc: stable@vger.kernel.org Reported-by: Sashiko Signed-off-by: Terry Bowman Reviewed-by: Alison Schofield Reviewed-by: Dave Jiang Reviewed-by: Ben Cheatham Reviewed-by: Richard Cheng Link: https://patch.msgid.link/20260605180610.2249458-1-terry.bowman@amd.com Signed-off-by: Dave Jiang Signed-off-by: Greg Kroah-Hartman --- drivers/cxl/core/ras.c | 27 ++++++++++++++++++++------- drivers/cxl/core/trace.h | 24 ++++++++++++++++-------- drivers/cxl/cxl.h | 14 ++++++++++++-- 3 files changed, 48 insertions(+), 17 deletions(-) diff --git a/drivers/cxl/core/ras.c b/drivers/cxl/core/ras.c index 006c6ffc2f567..99fb00949c2fa 100644 --- a/drivers/cxl/core/ras.c +++ b/drivers/cxl/core/ras.c @@ -8,6 +8,10 @@ #include #include "trace.h" +/* Check that UCE header definition is maintained to keep ABI intact */ +static_assert(CXL_HEADERLOG_TRACE_SIZE_U32 == 128, + "rasdaemon ABI requires exactly 128 u32s"); + static void cxl_cper_trace_corr_port_prot_err(struct pci_dev *pdev, struct cxl_ras_capability_regs ras_cap) { @@ -19,6 +23,7 @@ static void cxl_cper_trace_corr_port_prot_err(struct pci_dev *pdev, static void cxl_cper_trace_uncorr_port_prot_err(struct pci_dev *pdev, struct cxl_ras_capability_regs ras_cap) { + u32 hl[CXL_HEADERLOG_TRACE_SIZE_U32] = {}; u32 status = ras_cap.uncor_status & ~ras_cap.uncor_mask; u32 fe; @@ -28,8 +33,8 @@ static void cxl_cper_trace_uncorr_port_prot_err(struct pci_dev *pdev, else fe = status; - trace_cxl_port_aer_uncorrectable_error(&pdev->dev, status, fe, - ras_cap.header_log); + memcpy(hl, ras_cap.header_log, CXL_HEADERLOG_SIZE); + trace_cxl_port_aer_uncorrectable_error(&pdev->dev, status, fe, hl); } static void cxl_cper_trace_corr_prot_err(struct cxl_memdev *cxlmd, @@ -44,6 +49,7 @@ static void cxl_cper_trace_uncorr_prot_err(struct cxl_memdev *cxlmd, struct cxl_ras_capability_regs ras_cap) { + u32 hl[CXL_HEADERLOG_TRACE_SIZE_U32] = {}; u32 status = ras_cap.uncor_status & ~ras_cap.uncor_mask; u32 fe; @@ -53,8 +59,15 @@ cxl_cper_trace_uncorr_prot_err(struct cxl_memdev *cxlmd, else fe = status; - trace_cxl_aer_uncorrectable_error(cxlmd, status, fe, - ras_cap.header_log); + /* + * ras_cap.header_log[] holds CXL_HEADERLOG_SIZE_U32 (16) hardware + * dwords. Copy them into the front of a zero-filled + * CXL_HEADERLOG_TRACE_SIZE_U32 (128) u32 staging buffer so the trace + * event memcpy sees a full 512-byte source and the userspace ABI + * (rasdaemon) is preserved. + */ + memcpy(hl, ras_cap.header_log, CXL_HEADERLOG_SIZE); + trace_cxl_aer_uncorrectable_error(cxlmd, status, fe, hl); } static int match_memdev_by_parent(struct device *dev, const void *uport) @@ -204,12 +217,12 @@ static void header_log_copy(void __iomem *ras_base, u32 *log) { void __iomem *addr; u32 *log_addr; - int i, log_u32_size = CXL_HEADERLOG_SIZE / sizeof(u32); + int i; addr = ras_base + CXL_RAS_HEADER_LOG_OFFSET; log_addr = log; - for (i = 0; i < log_u32_size; i++) { + for (i = 0; i < CXL_HEADERLOG_SIZE_U32; i++) { *log_addr = readl(addr); log_addr++; addr += sizeof(u32); @@ -222,7 +235,7 @@ static void header_log_copy(void __iomem *ras_base, u32 *log) */ bool cxl_handle_ras(struct device *dev, void __iomem *ras_base) { - u32 hl[CXL_HEADERLOG_SIZE_U32]; + u32 hl[CXL_HEADERLOG_TRACE_SIZE_U32] = {}; void __iomem *addr; u32 status; u32 fe; diff --git a/drivers/cxl/core/trace.h b/drivers/cxl/core/trace.h index a972e4ef19368..d37876096dd7c 100644 --- a/drivers/cxl/core/trace.h +++ b/drivers/cxl/core/trace.h @@ -56,7 +56,7 @@ TRACE_EVENT(cxl_port_aer_uncorrectable_error, __string(host, dev_name(dev->parent)) __field(u32, status) __field(u32, first_error) - __array(u32, header_log, CXL_HEADERLOG_SIZE_U32) + __array(u32, header_log, CXL_HEADERLOG_TRACE_SIZE_U32) ), TP_fast_assign( __assign_str(device); @@ -64,10 +64,14 @@ TRACE_EVENT(cxl_port_aer_uncorrectable_error, __entry->status = status; __entry->first_error = fe; /* - * Embed the 512B headerlog data for user app retrieval and - * parsing, but no need to print this in the trace buffer. + * Embed headerlog data for user app retrieval and parsing, + * but no need to print in the trace buffer. Only + * CXL_HEADERLOG_SIZE_U32 (16) dwords are hardware data; + * the remaining entries preserve the 512-byte ABI layout + * rasdaemon depends on and are zero-filled by the caller. */ - memcpy(__entry->header_log, hl, CXL_HEADERLOG_SIZE); + memcpy(__entry->header_log, hl, + CXL_HEADERLOG_TRACE_SIZE_U32 * sizeof(u32)); ), TP_printk("device=%s host=%s status: '%s' first_error: '%s'", __get_str(device), __get_str(host), @@ -85,7 +89,7 @@ TRACE_EVENT(cxl_aer_uncorrectable_error, __field(u64, serial) __field(u32, status) __field(u32, first_error) - __array(u32, header_log, CXL_HEADERLOG_SIZE_U32) + __array(u32, header_log, CXL_HEADERLOG_TRACE_SIZE_U32) ), TP_fast_assign( __assign_str(memdev); @@ -94,10 +98,14 @@ TRACE_EVENT(cxl_aer_uncorrectable_error, __entry->status = status; __entry->first_error = fe; /* - * Embed the 512B headerlog data for user app retrieval and - * parsing, but no need to print this in the trace buffer. + * Embed headerlog data for user app retrieval and parsing, + * but no need to print in the trace buffer. Only + * CXL_HEADERLOG_SIZE_U32 (16) dwords are hardware data; + * the remaining entries preserve the 512-byte ABI layout + * rasdaemon depends on and are zero-filled by the caller. */ - memcpy(__entry->header_log, hl, CXL_HEADERLOG_SIZE); + memcpy(__entry->header_log, hl, + CXL_HEADERLOG_TRACE_SIZE_U32 * sizeof(u32)); ), TP_printk("memdev=%s host=%s serial=%lld: status: '%s' first_error: '%s'", __get_str(memdev), __get_str(host), __entry->serial, diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h index f43abd1903ce9..765419af2c6b7 100644 --- a/drivers/cxl/cxl.h +++ b/drivers/cxl/cxl.h @@ -158,8 +158,18 @@ static inline int ways_to_eiw(unsigned int ways, u8 *eiw) #define CXL_RAS_CAP_CONTROL_FE_MASK GENMASK(5, 0) #define CXL_RAS_HEADER_LOG_OFFSET 0x18 #define CXL_RAS_CAPABILITY_LENGTH 0x58 -#define CXL_HEADERLOG_SIZE SZ_512 -#define CXL_HEADERLOG_SIZE_U32 SZ_512 / sizeof(u32) +#define CXL_HEADERLOG_SIZE SZ_64 +#define CXL_HEADERLOG_SIZE_U32 (CXL_HEADERLOG_SIZE / sizeof(u32)) + +/* + * The RAS UCE trace event header array was originally sized at SZ_512/sizeof(u32) + * = 128 u32s due to a bug. Userspace tools (rasdaemon) have grown a dependency + * on that 512-byte layout. Keep the trace array at 128 u32s to preserve the + * ABI; only CXL_HEADERLOG_SIZE_U32 (16) dwords are valid hardware data, the + * remainder are zero-filled. + */ +#define CXL_HEADERLOG_TRACE_SIZE SZ_512 +#define CXL_HEADERLOG_TRACE_SIZE_U32 (CXL_HEADERLOG_TRACE_SIZE / sizeof(u32)) /* CXL 2.0 8.2.8.1 Device Capabilities Array Register */ #define CXLDEV_CAP_ARRAY_OFFSET 0x0 From a58fc10adf503969fec2007b5afe8987258046c4 Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Wed, 13 May 2026 17:28:40 +0800 Subject: [PATCH 2331/2758] jbd2: fix integer underflow in jbd2_journal_initialize_fast_commit() commit 289a2ca0c9b7eae74f93fc213b0b971669b8683d upstream. jbd2_journal_initialize_fast_commit() validates journal capacity by checking (journal->j_last - num_fc_blks < JBD2_MIN_JOURNAL_BLOCKS). Both j_last and num_fc_blks are unsigned, so when num_fc_blks exceeds j_last the subtraction wraps to a large value, bypassing the bounds check. The resulting underflow corrupts j_last, j_fc_first, and j_free, leading to journal abort. Fix by checking num_fc_blks against j_last before the subtraction, returning -EFSCORRUPTED. Fixes: 6866d7b3f2bb ("ext4 / jbd2: add fast commit initialization") Reported-by: Yuhao Jiang Cc: stable@vger.kernel.org Signed-off-by: Junrui Luo Fixes: e029c5f27987 ("ext4: make num of fast commit blocks configurable") Reviewed-by: Baokun Li Fixes: e029c5f279872 ("ext4: make num of fast commit blocks configurable") Reviewed-by: Zhang Yi Reviewed-by: Jan Kara Link: https://patch.msgid.link/SYBPR01MB7881663C927DE9D7BBF4D1DFAF062@SYBPR01MB7881.ausprd01.prod.outlook.com Signed-off-by: Theodore Ts'o Signed-off-by: Greg Kroah-Hartman --- fs/jbd2/journal.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 4f397fcdb13c5..e3b2e38e1a1b7 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -2263,6 +2263,8 @@ jbd2_journal_initialize_fast_commit(journal_t *journal) unsigned long long num_fc_blks; num_fc_blks = jbd2_journal_get_num_fc_blks(sb); + if (num_fc_blks > journal->j_last) + return -EFSCORRUPTED; if (journal->j_last - num_fc_blks < JBD2_MIN_JOURNAL_BLOCKS) return -ENOSPC; From 7e49684d90faa34ff6d5586be658e986d9d475ac Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Tue, 19 May 2026 11:20:13 +0530 Subject: [PATCH 2332/2758] nvdimm/btt: Free arenas on btt_init() error paths commit 1a6b6442a982d0ca5fb6a1a39b6f6dfd760eda57 upstream. The arenas allocated by discover_arenas() or create_arenas() are not freed on some error paths in btt_init(). This leaks memory when BTT initialization fails. Call free_arenas() from the affected error paths to release the allocations. [ as: commit message and log edits ] Fixes: 5212e11fde4d ("nd_btt: atomic sector updates") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260519-nvdimmleaks-v1-2-592300fb7a43@cse.iitm.ac.in Signed-off-by: Alison Schofield Signed-off-by: Greg Kroah-Hartman --- drivers/nvdimm/btt.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c index fdcb080a4314f..81c634e5d0064 100644 --- a/drivers/nvdimm/btt.c +++ b/drivers/nvdimm/btt.c @@ -1589,7 +1589,7 @@ static struct btt *btt_init(struct nd_btt *nd_btt, unsigned long long rawsize, if (btt->init_state != INIT_READY && nd_region->ro) { dev_warn(dev, "%s is read-only, unable to init btt metadata\n", dev_name(&nd_region->dev)); - return NULL; + goto err; } else if (btt->init_state != INIT_READY) { btt->num_arenas = (rawsize / ARENA_MAX_SIZE) + ((rawsize % ARENA_MAX_SIZE) ? 1 : 0); @@ -1599,25 +1599,28 @@ static struct btt *btt_init(struct nd_btt *nd_btt, unsigned long long rawsize, ret = create_arenas(btt); if (ret) { dev_info(dev, "init: create_arenas: %d\n", ret); - return NULL; + goto err; } ret = btt_meta_init(btt); if (ret) { dev_err(dev, "init: error in meta_init: %d\n", ret); - return NULL; + goto err; } } ret = btt_blk_init(btt); if (ret) { dev_err(dev, "init: error in blk_init: %d\n", ret); - return NULL; + goto err; } btt_debugfs_init(btt); return btt; +err: + free_arenas(btt); + return NULL; } /** From ba59b96d8d21dc8729fca44a022ca5919c1848c9 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Tue, 19 May 2026 11:20:12 +0530 Subject: [PATCH 2333/2758] nvdimm/btt: Free arena sub-allocations on discover_arenas() error path commit 13fe4cd9ddd0aacb7777812328be525a11ea3fea upstream. Memory allocated by btt_freelist_init(), btt_rtt_init(), and btt_maplocks_init() is not freed on some discover_arenas() error paths. This leaks memory when arena discovery fails. Add the missing kfree() calls to release the allocations before returning an error. [ as: commit message and log edits ] Fixes: 5212e11fde4d ("nd_btt: atomic sector updates") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260519-nvdimmleaks-v1-1-592300fb7a43@cse.iitm.ac.in Signed-off-by: Alison Schofield Signed-off-by: Greg Kroah-Hartman --- drivers/nvdimm/btt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c index 81c634e5d0064..7e1112960d7f8 100644 --- a/drivers/nvdimm/btt.c +++ b/drivers/nvdimm/btt.c @@ -919,6 +919,9 @@ static int discover_arenas(struct btt *btt) return ret; out: + kfree(arena->freelist); + kfree(arena->rtt); + kfree(arena->map_locks); kfree(arena); free_arenas(btt); return ret; From c49df5f1e1937fcbc93f1e9a903e5db1d2269389 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 15 May 2026 15:40:07 +0300 Subject: [PATCH 2334/2758] pinctrl: renesas: rzg2l: Use -ENOTSUPP instead of -EOPNOTSUPP commit c1492da3939c89372929e062d731f328f7693f1e upstream. The pinctrl and GPIO core code make exceptions for the -ENOTSUPP error code. One such example is gpio_set_config_with_argument_optional(), which returns success when gpio_set_config_with_argument() returns -ENOTSUPP, but reports failure for all other error codes. Returning -EOPNOTSUPP from the pinctrl driver on the unsupported pinctrl operation may lead to boot failures when pinctrl drivers implements struct gpio_chip::set_config, the system uses GPIO hogs, and the struct gpio_chip::set_config implementation returns -EOPNOTSUPP for the unsupported operations. Return -ENOTSUPP for the unsupported pinctrl operation. Fixes: 560c633d378a ("pinctrl: renesas: rzg2l: Drop oen_read and oen_write callbacks") Fixes: c4c4637eb57f ("pinctrl: renesas: Add RZ/G2L pin and gpio controller driver") Cc: stable@vger.kernel.org Signed-off-by: Claudiu Beznea Reviewed-by: Bartosz Golaszewski Reviewed-by: Geert Uytterhoeven Tested-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260515124008.2947838-2-claudiu.beznea@kernel.org Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/pinctrl/renesas/pinctrl-rzg2l.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index 1c6b115e65d85..8e7c503f9bb32 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -1103,7 +1103,7 @@ static int rzg2l_read_oen(struct rzg2l_pinctrl *pctrl, unsigned int _pin) int bit; if (!pctrl->data->pin_to_oen_bit) - return -EOPNOTSUPP; + return -ENOTSUPP; bit = pctrl->data->pin_to_oen_bit(pctrl, _pin); if (bit < 0) @@ -1145,7 +1145,7 @@ static int rzg2l_write_oen(struct rzg2l_pinctrl *pctrl, unsigned int _pin, u8 oe u8 val; if (!pctrl->data->pin_to_oen_bit) - return -EOPNOTSUPP; + return -ENOTSUPP; bit = pctrl->data->pin_to_oen_bit(pctrl, _pin); if (bit < 0) @@ -1576,7 +1576,7 @@ static int rzg2l_pinctrl_pinconf_set(struct pinctrl_dev *pctldev, break; default: - return -EOPNOTSUPP; + return -ENOTSUPP; } } @@ -1658,7 +1658,7 @@ static int rzg2l_pinctrl_pinconf_group_get(struct pinctrl_dev *pctldev, /* Check config matching between to pin */ if (i && prev_config != *config) - return -EOPNOTSUPP; + return -ENOTSUPP; prev_config = *config; } From 083e9c2ec7e8bb13b79c9fd7b337abdd758ecc5f Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Fri, 22 May 2026 09:39:06 -0400 Subject: [PATCH 2335/2758] sunrpc: pin svc_xprt across the asynchronous TLS handshake callback commit 4f988f3a2808fb659f3880c282041ff067acad78 upstream. svc_tcp_handshake() stores the raw svc_xprt pointer in tls_handshake_args.ta_data and submits the request through tls_server_hello_x509(). The handshake core takes only sock_hold(req->hr_sk); nothing references the embedding struct svc_sock that svc_tcp_handshake_done() reaches via container_of(). Two close races leave the in-flight callback writing through a freed svc_sock. svc_sock_free() calls tls_handshake_cancel() and discards its return value: a false return means handshake_complete() has already set HANDSHAKE_F_REQ_COMPLETED but hp_done() may not have finished, yet svc_sock_free() proceeds to kfree(svsk). The cancel-loser fall-through inside svc_tcp_handshake() itself produces the same window: when wait_for_completion_interruptible_timeout() returns <= 0 (timeout or signal) and tls_handshake_cancel() returns false, the function does not drain, returns, and svc_handle_xprt() calls svc_xprt_received(), which clears XPT_BUSY and can drop the last reference. A concurrent close then runs svc_sock_free() while svc_tcp_handshake_done() is still updating xpt_flags and walking svsk->sk_handshake_done. The corruption surfaces as set_bit/clear_bit RMW into the freed xpt_flags slab slot and as complete_all() walking and writing the freed wait_queue_head_t list embedded in sk_handshake_done -- a slab-corruption primitive, not a benign read. The path is reachable on any TLS-enabled NFS server whenever a connection close overlaps the tlshd downcall delivery window; the interruptible wait means signal delivery suffices, not just SVC_HANDSHAKE_TO expiry. Take svc_xprt_get(xprt) immediately before tls_server_hello_x509() so the in-flight callback owns its own reference. Release it on the two edges where the callback is guaranteed not to fire -- submission failure from tls_server_hello_x509() and a successful tls_handshake_cancel() -- and at the tail of svc_tcp_handshake_done() after complete_all(). Fixes: b3cbf98e2fdf ("SUNRPC: Support TLS handshake in the server-side TCP socket code") Cc: stable@vger.kernel.org Signed-off-by: Chris Mason Assisted-by: kres (claude-opus-4-7) [cel: rewrote commit message to describe the actual change] Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- net/sunrpc/svcsock.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 7be3de1a1aed9..c8e194fce6228 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -471,6 +471,7 @@ static void svc_tcp_handshake_done(void *data, int status, key_serial_t peerid) } clear_bit(XPT_HANDSHAKE, &xprt->xpt_flags); complete_all(&svsk->sk_handshake_done); + svc_xprt_put(xprt); } /** @@ -494,9 +495,13 @@ static void svc_tcp_handshake(struct svc_xprt *xprt) clear_bit(XPT_TLS_SESSION, &xprt->xpt_flags); init_completion(&svsk->sk_handshake_done); + /* Pin the transport across the asynchronous handshake callback. */ + svc_xprt_get(xprt); + ret = tls_server_hello_x509(&args, GFP_KERNEL); if (ret) { trace_svc_tls_not_started(xprt); + svc_xprt_put(xprt); goto out_failed; } @@ -505,6 +510,7 @@ static void svc_tcp_handshake(struct svc_xprt *xprt) if (ret <= 0) { if (tls_handshake_cancel(sk)) { trace_svc_tls_timed_out(xprt); + svc_xprt_put(xprt); goto out_close; } } From a4f878e8ecd729ccf2e50993444e217583adeace Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 22 May 2026 09:39:07 -0400 Subject: [PATCH 2336/2758] sunrpc: wait for in-flight TLS handshake callback when cancel loses race commit d00e32f84ca1a77cb67a3fbf59f58dada95f5a21 upstream. When wait_for_completion_interruptible_timeout() in svc_tcp_handshake() returns 0 (timeout) or -ERESTARTSYS (signal) and tls_handshake_cancel() then returns false, handshake_complete() has won the cancellation race: it has set HANDSHAKE_F_REQ_COMPLETED and is about to invoke svc_tcp_handshake_done(), but the callback's side effects on xpt_flags and on svsk->sk_handshake_done have not yet committed. The current code reads xpt_flags immediately to decide whether the session succeeded. Two races result. If the callback has executed set_bit(XPT_TLS_SESSION) but not yet clear_bit(XPT_HANDSHAKE), svc_tcp_handshake() sees a session, enqueues the transport, and returns. svc_xprt_received() then clears XPT_BUSY, a worker thread picks the transport up, the dispatcher in svc_handle_xprt() observes XPT_HANDSHAKE still set, and xpo_handshake is invoked a second time. That svc_tcp_handshake() calls init_completion(&svsk->sk_handshake_done) while the original callback concurrently calls complete_all() on it, corrupting the embedded swait_queue. If the callback has set HANDSHAKE_F_REQ_COMPLETED but not yet entered svc_tcp_handshake_done(), svc_tcp_handshake() reads XPT_TLS_SESSION as clear and tears the connection down even though the handshake is about to succeed. Wait for the callback to commit before inspecting xpt_flags. The completion is guaranteed to fire because handshake_complete() invokes svc_tcp_handshake_done() unconditionally once it has set HANDSHAKE_F_REQ_COMPLETED. Fixes: b3cbf98e2fdf ("SUNRPC: Support TLS handshake in the server-side TCP socket code") Cc: stable@vger.kernel.org Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- net/sunrpc/svcsock.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index c8e194fce6228..eb747493db829 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -513,6 +513,10 @@ static void svc_tcp_handshake(struct svc_xprt *xprt) svc_xprt_put(xprt); goto out_close; } + /* Cancellation lost to handshake_complete(): the + * callback is in flight and should finish quickly. + */ + wait_for_completion(&svsk->sk_handshake_done); } if (!test_bit(XPT_TLS_SESSION, &xprt->xpt_flags)) { From 31ba490c02d476a5e4f90b8845932ac9db8aa71b Mon Sep 17 00:00:00 2001 From: Luxiao Xu Date: Thu, 21 May 2026 15:06:32 +0800 Subject: [PATCH 2337/2758] sunrpc: harden rq_procinfo lifecycle to prevent double-free commit 18d216788bef06332ff8901670ecf1ed8f6eb614 upstream. The svc_release_rqst() function executes the callback inside rqstp->rq_procinfo->pc_release. However, if a worker thread begins processing a new request and encounters an early error path (e.g., unsupported protocol, short frame, or bad auth) before a valid rq_procinfo is installed, a stale release hook can be re-triggered against reused state from the previous RPC, resulting in a double-free or use-after-free vulnerability. Harden the lifecycle of rq_procinfo by: 1. Ensuring svc_release_rqst() always clears rq_procinfo after the optional pc_release() call, regardless of whether the hook exists. 2. Explicitly clearing rq_procinfo at request entry in svc_process() before any early decode or drop paths. 3. Ensuring svc_process_bc() does the same at backchannel entry. This guarantees that error flows will not encounter a non-NULL stale rq_procinfo pointer when there is nothing to release. Fixes: d9adbb6e10bf ("sunrpc: delay pc_release callback until after the reply is sent") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Suggested-by: Chuck Lever Reviewed-by: Jeff Layton Signed-off-by: Luxiao Xu Signed-off-by: Ren Wei Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- net/sunrpc/svc.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 576fa42e7abf7..ae9ec4bf34f72 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -1598,6 +1598,12 @@ static void svc_release_rqst(struct svc_rqst *rqstp) if (procp && procp->pc_release) procp->pc_release(rqstp); + + /* + * A subsequent svc_release_rqst() on this rqstp must not + * re-invoke pc_release against released state. + */ + rqstp->rq_procinfo = NULL; } /** @@ -1616,6 +1622,9 @@ void svc_process(struct svc_rqst *rqstp) svc_xprt_deferred_close(rqstp->rq_xprt); #endif + /* Discard a stale release hook from a previous RPC. */ + rqstp->rq_procinfo = NULL; + /* * Setup response xdr_buf. * Initially it has just one page @@ -1672,6 +1681,7 @@ void svc_process_bc(struct rpc_rqst *req, struct svc_rqst *rqstp) int proc_error; /* Build the svc_rqst used by the common processing routine */ + rqstp->rq_procinfo = NULL; rqstp->rq_xid = req->rq_xid; rqstp->rq_prot = req->rq_xprt->prot; rqstp->rq_bc_net = req->rq_xprt->xprt_net; From 3f2dc01b9cb516d4727a3b9263ee58c71ca00ba9 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 14 May 2026 16:56:04 -0400 Subject: [PATCH 2338/2758] lockd: Plug nlm_file leak when nlm_do_fopen() fails commit f16a1513452edb532fec81e591c64c320866719c upstream. A client can repeatedly drive nlm_do_fopen() failures by presenting file handles that the underlying export rejects. After kzalloc_obj() succeeds in nlm_lookup_file(), the freshly allocated nlm_file is not yet inserted into nlm_files[]. The nlm_do_fopen() failure path jumps to out_unlock, which releases nlm_file_mutex and returns without freeing the allocation, so each failure leaks one nlm_file. Route the failure through out_free so kfree() runs before the function returns. Fixes: 7f024fcd5c97 ("Keep read and write fds with each nlm_file") Cc: stable@vger.kernel.org Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- fs/lockd/svcsubs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index 95a499940daf2..4bef042ef5cbc 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -165,7 +165,7 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, nfserr = nlm_do_fopen(rqstp, file, mode); if (nfserr) - goto out_unlock; + goto out_free; hlist_add_head(&file->f_list, &nlm_files[hash]); From 3a5c55a19cad62f2973be25fe96a1a9e7f618e8a Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 14 May 2026 16:56:06 -0400 Subject: [PATCH 2339/2758] lockd: Plug nlm_file refcount leak on cached nlm_do_fopen() failure commit 70a38f87bed7f0694fd07988b47b2db1e10d8df3 upstream. The cached-file path in nlm_lookup_file() reaches the found: label unconditionally, even when nlm_do_fopen() fails. At that label *result and file->f_count are updated before the error is returned. The wrappers nlm3svc_lookup_file() and nlm4svc_lookup_file() then bail out of their switch without copying *result back to their caller, so the proc handler's local nlm_file pointer remains NULL and the cleanup path skips nlm_release_file(). The f_count increment is never released, and nlm_traverse_files() can no longer reap the file because its refcount never returns to zero between requests. Short-circuit the cached path so neither *result nor f_count is touched when nlm_do_fopen() fails on a hashed nlm_file. Fixes: 7f024fcd5c97 ("Keep read and write fds with each nlm_file") Cc: stable@vger.kernel.org Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- fs/lockd/svcsubs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index 4bef042ef5cbc..c7945282d479f 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -149,6 +149,8 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, mutex_lock(&file->f_mutex); nfserr = nlm_do_fopen(rqstp, file, mode); mutex_unlock(&file->f_mutex); + if (nfserr) + goto out_unlock; goto found; } nlm_debug_print_fh("creating file for", &lock->fh); From 98414b42530af65cb984ffc12685096a3b5e179a Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 19 May 2026 09:34:21 -0400 Subject: [PATCH 2340/2758] SUNRPC: Bound-check xdr_buf_to_bvec() stores before writing commit 42f5b80dda6b86e424054baf1475df686c403d5c upstream. xdr_buf_to_bvec() writes a bio_vec into the caller's array before testing whether that slot is in range, and the head branch performs the store with no check at all. When the caller's budget is exactly used up, the next store lands one element past the end of the array. The overflow label returns count - 1, which masks the surplus store but cannot undo it. rq_bvec, the array passed by nfsd_vfs_write(), is allocated to exactly rq_maxpages entries with no slack. The OOB store can land in adjacent slab memory; the bv_len and bv_offset fields written there are derived from client-supplied RPC payload sizes. Move the in-range check ahead of the store in the head, page-loop, and tail branches. With the check at the top of each sequence, count is incremented only after a successful store, so the overflow label can return count directly. Reported-by: Chris Mason Fixes: 2eb2b9358181 ("SUNRPC: Convert svc_tcp_sendmsg to use bio_vecs directly") Cc: stable@vger.kernel.org Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- net/sunrpc/xdr.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index e83d5d0be78b7..4c41e83b7ca9d 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -152,6 +152,8 @@ unsigned int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size, unsigned int count = 0; if (head->iov_len) { + if (unlikely(count >= bvec_size)) + goto bvec_overflow; bvec_set_virt(bvec++, head->iov_base, head->iov_len); ++count; } @@ -165,25 +167,27 @@ unsigned int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size, while (remaining > 0) { len = min_t(unsigned int, remaining, PAGE_SIZE - offset); + if (unlikely(count >= bvec_size)) + goto bvec_overflow; bvec_set_page(bvec++, *pages++, len, offset); remaining -= len; offset = 0; - if (unlikely(++count > bvec_size)) - goto bvec_overflow; + ++count; } } if (tail->iov_len) { - bvec_set_virt(bvec, tail->iov_base, tail->iov_len); - if (unlikely(++count > bvec_size)) + if (unlikely(count >= bvec_size)) goto bvec_overflow; + bvec_set_virt(bvec, tail->iov_base, tail->iov_len); + ++count; } return count; bvec_overflow: pr_warn_once("%s: bio_vec array overflow\n", __func__); - return count - 1; + return count; } EXPORT_SYMBOL_GPL(xdr_buf_to_bvec); From e5b1aaa74118e91f0c0f18b22b6f853199873db4 Mon Sep 17 00:00:00 2001 From: Wasim Nazir Date: Wed, 18 Mar 2026 17:19:16 +0530 Subject: [PATCH 2341/2758] remoteproc: qcom: Fix leak when custom dump_segments addition fails commit ecf9fc18e62c58eae1ceb65dab2bccb8a724de2d upstream. Free allocated minidump_region 'name' in qcom_add_minidump_segments() when failing before adding the region to 'dump_segments'. Otherwise, the 'name' is not tracked and is never freed by qcom_minidump_cleanup(). Return error when adding to 'dump_segments' fails. Cc: stable@vger.kernel.org # v5.11 Fixes: 8ed8485c4f05 ("remoteproc: qcom: Add capability to collect minidumps") Reviewed-by: Mukesh Ojha Signed-off-by: Wasim Nazir Link: https://lore.kernel.org/r/20260318-rproc-memleak-v2-1-ade70ab858f2@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Greg Kroah-Hartman --- drivers/remoteproc/qcom_common.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/remoteproc/qcom_common.c b/drivers/remoteproc/qcom_common.c index fd2b6824ad265..e1a955476c9b3 100644 --- a/drivers/remoteproc/qcom_common.c +++ b/drivers/remoteproc/qcom_common.c @@ -109,6 +109,7 @@ static int qcom_add_minidump_segments(struct rproc *rproc, struct minidump_subsy struct minidump_region __iomem *ptr; struct minidump_region region; int seg_cnt, i; + int ret = 0; dma_addr_t da; size_t size; char *name; @@ -129,17 +130,22 @@ static int qcom_add_minidump_segments(struct rproc *rproc, struct minidump_subsy if (le32_to_cpu(region.valid) == MINIDUMP_REGION_VALID) { name = kstrndup(region.name, MAX_REGION_NAME_LENGTH - 1, GFP_KERNEL); if (!name) { - iounmap(ptr); - return -ENOMEM; + ret = -ENOMEM; + break; } da = le64_to_cpu(region.address); size = le64_to_cpu(region.size); - rproc_coredump_add_custom_segment(rproc, da, size, rproc_dumpfn_t, name); + ret = rproc_coredump_add_custom_segment(rproc, da, size, rproc_dumpfn_t, + name); + if (ret) { + kfree(name); + break; + } } } iounmap(ptr); - return 0; + return ret; } void qcom_minidump(struct rproc *rproc, unsigned int minidump_id, From f80fafe24f7221bfd1108b752108c1fcc6528fca Mon Sep 17 00:00:00 2001 From: Tanmay Shah Date: Tue, 28 Apr 2026 15:18:56 -0700 Subject: [PATCH 2342/2758] remoteproc: xlnx: Check remote core state commit a48df51d23138388900995add2854cda4aa68e55 upstream. The remote state is set to RPROC_DETACHED if the resource table is found in the memory. However, this can be wrong if the remote is not started, but firmware is still loaded in the memory. Use PM_GET_NODE_STATUS call to the firmware to request the state of the RPU node. If the RPU is actually out of reset and running, only then move the remote state to RPROC_DETACHED, otherwise keep the remote state to RPROC_OFFLINE. Signed-off-by: Tanmay Shah Fixes: bca4b02ef92e ("remoteproc: xlnx: Add attach detach support") Reviewed-by: Beleswar Padhi Acked-by: Michal Simek Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20260428221855.313752-1-tanmay.shah@amd.com Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/xilinx/zynqmp.c | 28 +++++++++++++++ drivers/remoteproc/xlnx_r5_remoteproc.c | 46 +++++++++++++++++++------ include/linux/firmware/xlnx-zynqmp.h | 21 +++++++++++ 3 files changed, 85 insertions(+), 10 deletions(-) diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index fbe8510f49271..af838b2dc3279 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -1450,6 +1450,34 @@ int zynqmp_pm_get_node_status(const u32 node, u32 *const status, } EXPORT_SYMBOL_GPL(zynqmp_pm_get_node_status); +/** + * zynqmp_pm_get_rpu_node_status - PM call to request a RPU node's current power state + * @node: ID of the RPU component or sub-system in question + * @status: Current operating state of the requested RPU node. + * @requirements: Current requirements asserted on the RPU node. + * @usage: Usage information, used for RPU slave nodes only: + * PM_USAGE_NO_MASTER - No master is currently using + * the node + * PM_USAGE_CURRENT_MASTER - Only requesting master is + * currently using the node + * PM_USAGE_OTHER_MASTER - Only other masters are + * currently using the node + * PM_USAGE_BOTH_MASTERS - Both the current and at least + * one other master is currently + * using the node + * + * Return: Returns status, either success or error+reason + */ +int zynqmp_pm_get_rpu_node_status(const u32 node, u32 *const status, + u32 *const requirements, u32 *const usage) +{ + if (zynqmp_pm_feature(PM_GET_NODE_STATUS) < PM_API_VERSION_2) + return -EOPNOTSUPP; + + return zynqmp_pm_get_node_status(node, status, requirements, usage); +} +EXPORT_SYMBOL_GPL(zynqmp_pm_get_rpu_node_status); + /** * zynqmp_pm_force_pwrdwn - PM call to request for another PU or subsystem to * be powered down forcefully diff --git a/drivers/remoteproc/xlnx_r5_remoteproc.c b/drivers/remoteproc/xlnx_r5_remoteproc.c index 50a9974f3202e..45a62cb980725 100644 --- a/drivers/remoteproc/xlnx_r5_remoteproc.c +++ b/drivers/remoteproc/xlnx_r5_remoteproc.c @@ -948,16 +948,6 @@ static struct zynqmp_r5_core *zynqmp_r5_add_rproc_core(struct device *cdev) goto free_rproc; } - /* - * If firmware is already available in the memory then move rproc state - * to DETACHED. Firmware can be preloaded via debugger or by any other - * agent (processors) in the system. - * If firmware isn't available in the memory and resource table isn't - * found, then rproc state remains OFFLINE. - */ - if (!zynqmp_r5_get_rsc_table_va(r5_core)) - r5_rproc->state = RPROC_DETACHED; - r5_core->rproc = r5_rproc; return r5_core; @@ -1210,6 +1200,7 @@ static int zynqmp_r5_core_init(struct zynqmp_r5_cluster *cluster, { struct device *dev = cluster->dev; struct zynqmp_r5_core *r5_core; + u32 req, usage, status; int ret = -EINVAL, i; r5_core = cluster->r5_cores[0]; @@ -1255,6 +1246,41 @@ static int zynqmp_r5_core_init(struct zynqmp_r5_cluster *cluster, ret = zynqmp_r5_get_sram_banks(r5_core); if (ret) return ret; + + /* + * It is possible that firmware is loaded into the memory, but + * RPU (remote) is not running. In such case, RPU state will be + * moved to RPROC_DETACHED wrongfully. To avoid it first make + * sure RPU is power-on and out of reset before parsing for the + * resource table. + */ + ret = zynqmp_pm_get_rpu_node_status(r5_core->pm_domain_id, + &status, &req, &usage); + if (ret) { + dev_warn(r5_core->dev, + "failed to get rpu node status, err %d\n", ret); + continue; + } + + /* + * If RPU state is power on and out of reset i.e. running, then + * assign RPROC_DETACHED state. If the RPU is not out of reset + * then do not attempt to attach to the remote processor. + */ + if (status == PM_NODE_RUNNING) { + /* + * Not all the firmware that is running on the remote + * core is expected to have the resource table. The + * firmware might not use RPMsg at all, and in that case + * resource table becomes irrelevant. However, we still + * need to make sure that running core is not reported + * as offline. so do not decide remote core state based + * on the resource table availability + */ + if (zynqmp_r5_get_rsc_table_va(r5_core)) + dev_dbg(r5_core->dev, "rsc tbl not found\n"); + r5_core->rproc->state = RPROC_DETACHED; + } } return 0; diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index d70dcd462b441..7e27b0f7bf7ed 100644 --- a/include/linux/firmware/xlnx-zynqmp.h +++ b/include/linux/firmware/xlnx-zynqmp.h @@ -542,6 +542,18 @@ enum pm_gem_config_type { GEM_CONFIG_FIXED = 2, }; +/** + * enum pm_node_status - Device node status provided by xilpm fw + * @PM_NODE_UNUSED: Device is not used + * @PM_NODE_RUNNING: Device is power-on and out of reset + * @PM_NODE_HALT: Device is power-on but in the reset state + */ +enum pm_node_status { + PM_NODE_UNUSED = 0, + PM_NODE_RUNNING = 1, + PM_NODE_HALT = 12, +}; + /** * struct zynqmp_pm_query_data - PM query data * @qid: query ID @@ -630,6 +642,8 @@ int zynqmp_pm_set_rpu_mode(u32 node_id, enum rpu_oper_mode rpu_mode); int zynqmp_pm_set_tcm_config(u32 node_id, enum rpu_tcm_comb tcm_mode); int zynqmp_pm_get_node_status(const u32 node, u32 *const status, u32 *const requirements, u32 *const usage); +int zynqmp_pm_get_rpu_node_status(const u32 node, u32 *const status, + u32 *const requirements, u32 *const usage); int zynqmp_pm_set_sd_config(u32 node, enum pm_sd_config_type config, u32 value); int zynqmp_pm_set_gem_config(u32 node, enum pm_gem_config_type config, u32 value); @@ -939,6 +953,13 @@ static inline int zynqmp_pm_get_node_status(const u32 node, u32 *const status, return -ENODEV; } +static inline int zynqmp_pm_get_rpu_node_status(const u32 node, u32 *const status, + u32 *const requirements, + u32 *const usage) +{ + return -ENODEV; +} + static inline int zynqmp_pm_set_sd_config(u32 node, enum pm_sd_config_type config, u32 value) From 25bec992181dfc1eba5d8784e8ab17bef8a41f5d Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Tue, 28 Apr 2026 16:18:50 +0800 Subject: [PATCH 2343/2758] mm/sparse-vmemmap: fix vmemmap accounting underflow commit c373f7f98e6ad591c85d40548cf8b6443be69311 upstream. Patch series "mm: Fix vmemmap optimization accounting and initialization", v8. The series fixes several bugs in vmemmap optimization, mainly around incorrect page accounting and memmap initialization in DAX and memory hotplug paths. It also fixes pageblock migratetype initialization and struct page initialization for ZONE_DEVICE compound pages. Patches 1-4 fix vmemmap accounting issues. Patch 1 fixes an accounting underflow in the section activation failure path by moving vmemmap page accounting into the lower-level allocation and freeing helpers. Patch 2 fixes incorrect altmap passing in the memory hotplug error path. Patch 3 passes pgmap through memory deactivation paths so the teardown side can determine whether vmemmap optimization was in effect. Patch 4 uses that information to account the optimized DAX vmemmap size correctly. Patches 5-6 fix initialization issues in mm/mm_init. One makes sure all pageblocks in ZONE_DEVICE compound pages get their migratetype initialized. The other fixes a case where DAX memory hotplug reuses an unoptimized early-section memmap while compound_nr_pages() still assumes vmemmap optimization, leaving tail struct pages uninitialized. This patch (of 6): In section_activate(), if populate_section_memmap() fails, the error handling path calls section_deactivate() to roll back the state. This causes a vmemmap accounting imbalance. Since commit c3576889d87b ("mm: fix accounting of memmap pages"), memmap pages are accounted for only after populate_section_memmap() succeeds. However, the failure path unconditionally calls section_deactivate(), which decreases the vmemmap count. Consequently, a failure in populate_section_memmap() leads to an accounting underflow, incorrectly reducing the system's tracked vmemmap usage. Fix this more thoroughly by moving all accounting calls into the lower level functions that actually perform the vmemmap allocation and freeing: - populate_section_memmap() accounts for newly allocated vmemmap pages - depopulate_section_memmap() unaccounts when vmemmap is freed This ensures proper accounting in all code paths, including error handling and early section cases. Link: https://lore.kernel.org/20260428081855.1249045-1-songmuchun@bytedance.com Link: https://lore.kernel.org/20260428081855.1249045-2-songmuchun@bytedance.com Fixes: c3576889d87b ("mm: fix accounting of memmap pages") Signed-off-by: Muchun Song Acked-by: Mike Rapoport (Microsoft) Acked-by: Oscar Salvador Acked-by: David Hildenbrand (Arm) Acked-by: Liam R. Howlett Cc: "Aneesh Kumar K.V" Cc: Joao Martins Cc: Lorenzo Stoakes Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Michal Hocko Cc: Nicholas Piggin Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/sparse-vmemmap.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c index 6eadb9d116e43..a7b11248b989c 100644 --- a/mm/sparse-vmemmap.c +++ b/mm/sparse-vmemmap.c @@ -656,7 +656,12 @@ static struct page * __meminit populate_section_memmap(unsigned long pfn, unsigned long nr_pages, int nid, struct vmem_altmap *altmap, struct dev_pagemap *pgmap) { - return __populate_section_memmap(pfn, nr_pages, nid, altmap, pgmap); + struct page *page = __populate_section_memmap(pfn, nr_pages, nid, altmap, + pgmap); + + memmap_pages_add(DIV_ROUND_UP(nr_pages * sizeof(struct page), PAGE_SIZE)); + + return page; } static void depopulate_section_memmap(unsigned long pfn, unsigned long nr_pages, @@ -665,13 +670,17 @@ static void depopulate_section_memmap(unsigned long pfn, unsigned long nr_pages, unsigned long start = (unsigned long) pfn_to_page(pfn); unsigned long end = start + nr_pages * sizeof(struct page); + memmap_pages_add(-1L * (DIV_ROUND_UP(nr_pages * sizeof(struct page), PAGE_SIZE))); vmemmap_free(start, end, altmap); } + static void free_map_bootmem(struct page *memmap) { unsigned long start = (unsigned long)memmap; unsigned long end = (unsigned long)(memmap + PAGES_PER_SECTION); + memmap_boot_pages_add(-1L * (DIV_ROUND_UP(PAGES_PER_SECTION * sizeof(struct page), + PAGE_SIZE))); vmemmap_free(start, end, NULL); } @@ -774,14 +783,10 @@ static void section_deactivate(unsigned long pfn, unsigned long nr_pages, * The memmap of early sections is always fully populated. See * section_activate() and pfn_valid() . */ - if (!section_is_early) { - memmap_pages_add(-1L * (DIV_ROUND_UP(nr_pages * sizeof(struct page), PAGE_SIZE))); + if (!section_is_early) depopulate_section_memmap(pfn, nr_pages, altmap); - } else if (memmap) { - memmap_boot_pages_add(-1L * (DIV_ROUND_UP(nr_pages * sizeof(struct page), - PAGE_SIZE))); + else if (memmap) free_map_bootmem(memmap); - } if (empty) ms->section_mem_map = (unsigned long)NULL; @@ -826,7 +831,6 @@ static struct page * __meminit section_activate(int nid, unsigned long pfn, section_deactivate(pfn, nr_pages, altmap); return ERR_PTR(-ENOMEM); } - memmap_pages_add(DIV_ROUND_UP(nr_pages * sizeof(struct page), PAGE_SIZE)); return memmap; } From a018c9b8805c53bd92a69e42ca8ea24d61e2f164 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 24 Apr 2026 12:28:46 +0200 Subject: [PATCH 2344/2758] MIPS: ip22-gio: fix gio device memory leak commit 7de9a1b45f5a95b58145653c525c8fb80292d9ab upstream. The gio device release callback was never wired up so gio devices are not freed when the last reference is dropped. Fixes: e84de0c61905 ("MIPS: GIO bus support for SGI IP22/28") Cc: stable@vger.kernel.org # 3.3 Cc: Thomas Bogendoerfer Signed-off-by: Johan Hovold Signed-off-by: Thomas Bogendoerfer Signed-off-by: Greg Kroah-Hartman --- arch/mips/sgi-ip22/ip22-gio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/mips/sgi-ip22/ip22-gio.c b/arch/mips/sgi-ip22/ip22-gio.c index 9eec8842ffb70..fe6cdc61d1eeb 100644 --- a/arch/mips/sgi-ip22/ip22-gio.c +++ b/arch/mips/sgi-ip22/ip22-gio.c @@ -101,6 +101,8 @@ int gio_device_register(struct gio_device *giodev) { giodev->dev.bus = &gio_bus_type; giodev->dev.parent = &gio_bus; + giodev->dev.release = gio_release_dev; + return device_register(&giodev->dev); } EXPORT_SYMBOL_GPL(gio_device_register); From b04bbb89ca3a459330920d90dcda7676511e6911 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 24 Apr 2026 12:28:45 +0200 Subject: [PATCH 2345/2758] MIPS: ip22-gio: fix kfree() of static object commit c62cdd3e919bdf84c37ec46810f87cdb1736e822 upstream. The gio bus root device is a statically allocated object which must not be freed by kfree() on failure to register the device or bus. Fixes: 82242d28ff8b ("MIPS: IP22: Add missing put_device call") Cc: stable@vger.kernel.org # 3.17 Cc: Levente Kurusa Signed-off-by: Johan Hovold Signed-off-by: Thomas Bogendoerfer Signed-off-by: Greg Kroah-Hartman --- arch/mips/sgi-ip22/ip22-gio.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/mips/sgi-ip22/ip22-gio.c b/arch/mips/sgi-ip22/ip22-gio.c index fe6cdc61d1eeb..2f5c6c6f82540 100644 --- a/arch/mips/sgi-ip22/ip22-gio.c +++ b/arch/mips/sgi-ip22/ip22-gio.c @@ -30,7 +30,6 @@ static struct { static void gio_bus_release(struct device *dev) { - kfree(dev); } static struct device gio_bus = { From 35521e4ec762a8f357ff4bbad5f344b92c135755 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 24 Apr 2026 12:28:47 +0200 Subject: [PATCH 2346/2758] MIPS: ip22-gio: fix device reference leak in probe commit b82930a4c5dbc5c4df39c0f93d968c239f2c6885 upstream. The gio probe function needlessly takes a device reference which is never released and therefore prevents unbound gio devices from being freed. Fixes: e84de0c61905 ("MIPS: GIO bus support for SGI IP22/28") Cc: stable@vger.kernel.org # 3.3 Cc: Thomas Bogendoerfer Signed-off-by: Johan Hovold Signed-off-by: Thomas Bogendoerfer Signed-off-by: Greg Kroah-Hartman --- arch/mips/sgi-ip22/ip22-gio.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/mips/sgi-ip22/ip22-gio.c b/arch/mips/sgi-ip22/ip22-gio.c index 2f5c6c6f82540..7b7572d112501 100644 --- a/arch/mips/sgi-ip22/ip22-gio.c +++ b/arch/mips/sgi-ip22/ip22-gio.c @@ -133,13 +133,9 @@ static int gio_device_probe(struct device *dev) if (!drv->probe) return error; - gio_dev_get(gio_dev); - match = gio_match_device(drv->id_table, gio_dev); if (match) error = drv->probe(gio_dev, match); - if (error) - gio_dev_put(gio_dev); return error; } From 3ae86630b94f72bf4012c6322f81f622dc4fdf24 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Wed, 6 May 2026 23:42:23 +0100 Subject: [PATCH 2347/2758] MIPS: DEC: Ensure 32-bit stack location for o32 prom_printf() commit 5ff79e8bdc75db51e30298a75939e2308e7658e0 upstream. In 64-bit configurations calling any firmware entry points from a kernel thread other than the initial one will result in a situation where the stack has been placed in the XKPHYS 64-bit memory segment. Consequently the stack pointer is no longer a 32-bit value and when the 32-bit firmware code called uses 32-bit ALU operations to manipulate the stack pointer, the calculated result is incorrect (in fact in the 64-bit MIPS ISA almost all 32-bit ALU operations will produce an unpredictable result when executed on 64-bit data) and control goes astray. This may happen when no final console driver has been enabled in the configuration and consequently the initial console continues being used late into bootstrap, or with an upcoming change that will switch the zs driver to use a platform device, which in turn will make the console handover happen only after other kernel threads have already been started, and the kernel will hang at: pid_max: default: 32768 minimum: 301 or somewhat later, but always before: cblist_init_generic: Setting adjustable number of callback queues. has been printed. It seems that only the prom_printf() entry point is affected. Of all the other entry points wired only rex_slot_address() and rex_gettcinfo() are called from a kernel thread other than the initial one, specifically kernel_init(), and they are leaf functions that do no business with the stack, having worked with no issue ever since 64-bit support was added for the platform back in 2002. To address this issue then, arrange for the stack to be switched in the o32 wrapper as required for prom_printf() only, by supplying call_o32() with a pointer to a chunk of initdata space, which is placed in the CKSEG0 32-bit compatibility segment, observing that prom_printf() is only called from console output handler and therefore with the console lock held, implying no need for this code to be reentrant. Other firmware entry points may be called with interrupts enabled and no lock held, and may therefore require that call_o32() be reentrant. They trigger no issue at this point and "if it ain't broke, don't fix it," so just leave them alone. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Maciej W. Rozycki Cc: stable@vger.kernel.org # v2.6.12+ Signed-off-by: Thomas Bogendoerfer Signed-off-by: Greg Kroah-Hartman --- arch/mips/dec/prom/init.c | 6 +++++- arch/mips/include/asm/dec/prom.h | 15 +++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/arch/mips/dec/prom/init.c b/arch/mips/dec/prom/init.c index 8d74d7d6c05b4..e46c7acceffff 100644 --- a/arch/mips/dec/prom/init.c +++ b/arch/mips/dec/prom/init.c @@ -3,7 +3,7 @@ * init.c: PROM library initialisation code. * * Copyright (C) 1998 Harald Koerfgen - * Copyright (C) 2002, 2004 Maciej W. Rozycki + * Copyright (C) 2002, 2004, 2026 Maciej W. Rozycki */ #include #include @@ -20,6 +20,10 @@ #include +#ifdef CONFIG_64BIT +unsigned long o32_stk[O32_STK_SIZE] __initdata = { 0 }; +#endif + int (*__rex_bootinit)(void); int (*__rex_bootread)(void); int (*__rex_getbitmap)(memmap *); diff --git a/arch/mips/include/asm/dec/prom.h b/arch/mips/include/asm/dec/prom.h index 8fcad6984389c..af068e634f1e3 100644 --- a/arch/mips/include/asm/dec/prom.h +++ b/arch/mips/include/asm/dec/prom.h @@ -4,7 +4,7 @@ * * DECstation PROM interface. * - * Copyright (C) 2002 Maciej W. Rozycki + * Copyright (C) 2002, 2026 Maciej W. Rozycki * * Based on arch/mips/dec/prom/prom.h by the Anonymous. */ @@ -97,6 +97,17 @@ extern int (*__pmax_close)(int); #ifdef CONFIG_64BIT +#define O32_STK_SIZE 512 +extern unsigned long o32_stk[]; + +/* Switch the stack if outside the 32-bit address space. */ +static inline unsigned long *o32_get_stk(void) +{ + long fp = (long)__builtin_frame_address(0); + + return fp != (int)fp ? o32_stk + O32_STK_SIZE : NULL; +} + /* * On MIPS64 we have to call PROM functions via a helper * dispatcher to accommodate ABI incompatibilities. @@ -128,7 +139,7 @@ int __DEC_PROM_O32(_prom_printf, (int (*)(char *, ...), void *, char *, ...)); #define prom_getchar() _prom_getchar(__prom_getchar, NULL) #define prom_getenv(x) _prom_getenv(__prom_getenv, NULL, x) -#define prom_printf(x...) _prom_printf(__prom_printf, NULL, x) +#define prom_printf(x...) _prom_printf(__prom_printf, o32_get_stk(), x) #else /* !CONFIG_64BIT */ From b03e62112c9d1acb8120df9679b77fcc4ee7891a Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Tue, 28 Apr 2026 16:18:54 +0800 Subject: [PATCH 2348/2758] mm/mm_init: fix pageblock migratetype for ZONE_DEVICE compound pages commit 94405c6136839f7c462249c8b4b957bcb9527a9d upstream. The memmap_init_zone_device() function only initializes the migratetype of the first pageblock of a compound page. If the compound page size exceeds pageblock_nr_pages (e.g., 1GB hugepages with 2MB pageblocks), subsequent pageblocks in the compound page remain uninitialized. Move the migratetype initialization out of __init_zone_device_page() and into a separate pageblock_migratetype_init_range() function. This iterates over the entire PFN range of the memory, ensuring that all pageblocks are correctly initialized. Also remove the stale confusing comment about MEMINIT_HOTPLUG above the migratetype setting since it is an obsolete relic from commit 966cf44f637e ("mm: defer ZONE_DEVICE page initialization to the point where we init pgmap") and no longer makes sense here. Link: https://lore.kernel.org/20260428081855.1249045-6-songmuchun@bytedance.com Fixes: c4386bd8ee3a ("mm/memremap: add ZONE_DEVICE support for compound pages") Signed-off-by: Muchun Song Reviewed-by: Mike Rapoport (Microsoft) Reviewed-by: Oscar Salvador Acked-by: David Hildenbrand (Arm) Acked-by: Liam R. Howlett Cc: "Aneesh Kumar K.V" Cc: Joao Martins Cc: Lorenzo Stoakes Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Michal Hocko Cc: Nicholas Piggin Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/mm_init.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/mm/mm_init.c b/mm/mm_init.c index f9f8e1af921cd..cfc76953e249c 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -674,6 +674,20 @@ static inline void fixup_hashdist(void) static inline void fixup_hashdist(void) {} #endif /* CONFIG_NUMA */ +#ifdef CONFIG_ZONE_DEVICE +static __meminit void pageblock_migratetype_init_range(unsigned long pfn, + unsigned long nr_pages, int migratetype) +{ + const unsigned long end = pfn + nr_pages; + + for (pfn = pageblock_align(pfn); pfn < end; pfn += pageblock_nr_pages) { + init_pageblock_migratetype(pfn_to_page(pfn), migratetype, false); + if (IS_ALIGNED(pfn, PAGES_PER_SECTION)) + cond_resched(); + } +} +#endif + /* * Initialize a reserved page unconditionally, finding its zone first. */ @@ -1011,21 +1025,6 @@ static void __ref __init_zone_device_page(struct page *page, unsigned long pfn, page_folio(page)->pgmap = pgmap; page->zone_device_data = NULL; - /* - * Mark the block movable so that blocks are reserved for - * movable at startup. This will force kernel allocations - * to reserve their blocks rather than leaking throughout - * the address space during boot when many long-lived - * kernel allocations are made. - * - * Please note that MEMINIT_HOTPLUG path doesn't clear memmap - * because this is done early in section_activate() - */ - if (pageblock_aligned(pfn)) { - init_pageblock_migratetype(page, MIGRATE_MOVABLE, false); - cond_resched(); - } - /* * ZONE_DEVICE pages other than MEMORY_TYPE_GENERIC are released * directly to the driver page allocator which will set the page count @@ -1122,6 +1121,9 @@ void __ref memmap_init_zone_device(struct zone *zone, __init_zone_device_page(page, pfn, zone_idx, nid, pgmap); + if (IS_ALIGNED(pfn, PAGES_PER_SECTION)) + cond_resched(); + if (pfns_per_compound == 1) continue; @@ -1129,6 +1131,8 @@ void __ref memmap_init_zone_device(struct zone *zone, compound_nr_pages(altmap, pgmap)); } + pageblock_migratetype_init_range(start_pfn, nr_pages, MIGRATE_MOVABLE); + pr_debug("%s initialised %lu pages in %ums\n", __func__, nr_pages, jiffies_to_msecs(jiffies - start)); } From b56a5cbf8f1f1a5740f1137c89b14d0373309d8d Mon Sep 17 00:00:00 2001 From: Ma Ke Date: Fri, 24 Apr 2026 09:10:13 +0800 Subject: [PATCH 2349/2758] power: supply: cpcap-battery: Fix missing nvmem_device_put() causing reference leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit a2c14ff63e0e02e3c832385e523e9cc81301171c upstream. In cpcap_battery_detect_battery_type(), the reference to an nvmem device obtained via nvmem_device_find() is not released with nvmem_device_put() on the success or read-failure paths, causing a permanent reference leak. The driver’s retry logic on subsequent battery property reads can compound this leak, preventing the nvmem device from ever being freed. Found by code review. Signed-off-by: Ma Ke Cc: stable@vger.kernel.org Fixes: fd46821e85de ("power: supply: cpcap-battery: Add battery type auto detection for mapphone devices") Link: https://patch.msgid.link/20260424011013.879639-1-make24@iscas.ac.cn Signed-off-by: Sebastian Reichel Signed-off-by: Greg Kroah-Hartman --- drivers/power/supply/cpcap-battery.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 7b7bdce3162fc..59c741993ef8d 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -439,10 +439,13 @@ static void cpcap_battery_detect_battery_type(struct cpcap_battery_ddata *ddata) if (IS_ERR_OR_NULL(nvmem)) { ddata->check_nvmem = true; dev_info_once(ddata->dev, "Can not find battery nvmem device. Assuming generic lipo battery\n"); - } else if (nvmem_device_read(nvmem, 2, 1, &battery_id) < 0) { - battery_id = 0; - ddata->check_nvmem = true; - dev_warn(ddata->dev, "Can not read battery nvmem device. Assuming generic lipo battery\n"); + } else { + if (nvmem_device_read(nvmem, 2, 1, &battery_id) < 0) { + battery_id = 0; + ddata->check_nvmem = true; + dev_warn(ddata->dev, "Can not read battery nvmem device. Assuming generic lipo battery\n"); + } + nvmem_device_put(nvmem); } switch (battery_id) { From fc3f0eef426f1cdea387ce886c86e119f6a898e7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 7 Apr 2026 14:33:38 +0200 Subject: [PATCH 2350/2758] power: supply: max17042: fix OF node reference imbalance commit 68d234144b7dffd1f50b07ba74d0d6e833ef43a4 upstream. The driver reuses the OF node of the parent multi-function device but fails to take another reference to balance the one dropped by the platform bus code when unbinding the MFD and deregistering the child devices. Fix this by using the intended helper for reusing OF nodes. Fixes: 0cd4f1f77ad4 ("power: supply: max17042: add platform driver variant") Cc: stable@vger.kernel.org # 6.14 Cc: Dzmitry Sankouski Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260407123338.2677375-1-johan@kernel.org Signed-off-by: Sebastian Reichel Signed-off-by: Greg Kroah-Hartman --- drivers/power/supply/max17042_battery.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index 167fb3fb37320..75ed9374a51fb 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -1254,7 +1254,8 @@ static int max17042_platform_probe(struct platform_device *pdev) if (!i2c) return -EINVAL; - dev->of_node = dev->parent->of_node; + device_set_of_node_from_dev(dev, dev->parent); + id = platform_get_device_id(pdev); irq = platform_get_irq(pdev, 0); From b785f2bd9496facedc0a031be09cddcd1d3c84d3 Mon Sep 17 00:00:00 2001 From: Deepanshu Kartikey Date: Sat, 28 Mar 2026 12:25:34 +0530 Subject: [PATCH 2351/2758] mm/hugetlb: fix hugetlb cgroup rsvd charge/uncharge mismatch commit 15807d0ddde37407af72859426b654f3d1972b00 upstream. In alloc_hugetlb_folio(), a single h_cg pointer is used for both the rsvd and non-rsvd hugetlb cgroup charges. When map_chg is set, hugetlb_cgroup_charge_cgroup_rsvd() stores the charged cgroup in h_cg, but the immediately following hugetlb_cgroup_charge_cgroup() overwrites h_cg with the non-rsvd cgroup pointer. As a result, hugetlb_cgroup_commit_charge_rsvd() stores the wrong (non-rsvd) cgroup pointer into the folio's rsvd slot. When the folio is later freed, free_huge_folio() unconditionally calls both hugetlb_cgroup_uncharge_folio() and hugetlb_cgroup_uncharge_folio_rsvd(). The rsvd uncharge reads back the wrong cgroup from the folio and decrements a counter that was never charged for that cgroup, causing a page_counter underflow: page_counter underflow: -512 nr_pages=512 WARNING: mm/page_counter.c:61 at page_counter_cancel Fix this by introducing a separate h_cg_rsvd pointer exclusively for the rsvd charge path, keeping the rsvd and non-rsvd charges fully independent through their charge, commit, and error uncharge paths. Link: https://lore.kernel.org/20260328065534.346053-1-kartikey406@gmail.com Fixes: 08cf9faf7558 ("hugetlb_cgroup: support noreserve mappings") Reported-by: syzbot+226c1f947186f8fef796@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=226c1f947186f8fef796 Signed-off-by: Deepanshu Kartikey Reviewed-by: Muchun Song Cc: David Hildenbrand Cc: Oscar Salvador Cc: Mina Almasry Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/hugetlb.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index c921287489de3..571212b80835e 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -2862,6 +2862,7 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma, map_chg_state map_chg; int ret, idx; struct hugetlb_cgroup *h_cg = NULL; + struct hugetlb_cgroup *h_cg_rsvd = NULL; gfp_t gfp = htlb_alloc_mask(h) | __GFP_RETRY_MAYFAIL; idx = hstate_index(h); @@ -2912,7 +2913,7 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma, */ if (map_chg) { ret = hugetlb_cgroup_charge_cgroup_rsvd( - idx, pages_per_huge_page(h), &h_cg); + idx, pages_per_huge_page(h), &h_cg_rsvd); if (ret) goto out_subpool_put; } @@ -2954,7 +2955,7 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma, */ if (map_chg) { hugetlb_cgroup_commit_charge_rsvd(idx, pages_per_huge_page(h), - h_cg, folio); + h_cg_rsvd, folio); } spin_unlock_irq(&hugetlb_lock); @@ -3006,7 +3007,7 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma, out_uncharge_cgroup_reservation: if (map_chg) hugetlb_cgroup_uncharge_cgroup_rsvd(idx, pages_per_huge_page(h), - h_cg); + h_cg_rsvd); out_subpool_put: /* * put page to subpool iff the quota of subpool's rsv_hpages is used From 2f9e3ec17c3d2093c664c00a027588a446c39894 Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Tue, 28 Apr 2026 16:18:51 +0800 Subject: [PATCH 2352/2758] mm/memory_hotplug: fix incorrect altmap passing in error path commit 2fac4afa0e2e68841334c78c1821e49f74fbc66a upstream. In create_altmaps_and_memory_blocks(), when arch_add_memory() succeeds with memmap_on_memory enabled, the vmemmap pages are allocated from params.altmap. If create_memory_block_devices() subsequently fails, the error path calls arch_remove_memory() with a NULL altmap instead of params.altmap. This is a bug that could lead to memory corruption. Since altmap is NULL, vmemmap_free() falls back to freeing the vmemmap pages into the system buddy allocator via free_pages() instead of the altmap. arch_remove_memory() then immediately destroys the physical linear mapping for this memory. This injects unowned pages into the buddy allocator, causing machine checks or memory corruption if the system later attempts to allocate and use those freed pages. Fix this by passing params.altmap to arch_remove_memory() in the error path. Link: https://lore.kernel.org/20260428081855.1249045-3-songmuchun@bytedance.com Fixes: 6b8f0798b85a ("mm/memory_hotplug: split memmap_on_memory requests across memblocks") Signed-off-by: Muchun Song Acked-by: David Hildenbrand (Arm) Acked-by: Liam R. Howlett Reviewed-by: Georgi Djakov Cc: "Aneesh Kumar K.V" Cc: Joao Martins Cc: Lorenzo Stoakes Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Michal Hocko Cc: Mike Rapoport (Microsoft) Cc: Nicholas Piggin Cc: Oscar Salvador Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/memory_hotplug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 40c7915dabe05..cf4f77108c43f 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -1470,7 +1470,7 @@ static int create_altmaps_and_memory_blocks(int nid, struct memory_group *group, ret = create_memory_block_devices(cur_start, memblock_size, nid, params.altmap, group); if (ret) { - arch_remove_memory(cur_start, memblock_size, NULL); + arch_remove_memory(cur_start, memblock_size, params.altmap); kfree(params.altmap); goto out; } From 57094929cf094e563f0fc2f083b192c28767ef78 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 27 Apr 2026 21:29:40 -0700 Subject: [PATCH 2353/2758] mm/damon/core: make charge_addr_from aware of end-address exclusivity commit 5a2d162e22bf33eb89d53e802d0fc1ec422e19b6 upstream. DAMON region end address is exclusive one, but charge_addr_from is assigned assuming the end address is inclusive. As a result, DAMOS action to next up to min_region_sz memory can be skipped. This is quite negligible user impact. But, the bug is a bug that can be very simply fixed. Fix the wrong assignment to respect the exclusiveness of the address. The issue was discovered [1] by Sashiko. Link: https://lore.kernel.org/20260428042942.118230-1-sj@kernel.org Link: https://lore.kernel.org/20260428032324.115663-1-sj@kernel.org [1] Fixes: 50585192bc2e ("mm/damon/schemes: skip already charged targets and regions") Signed-off-by: SeongJae Park Cc: # 5.16.x Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/damon/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/damon/core.c b/mm/damon/core.c index 3dbbbfdeff719..901ffdaefb7f0 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -2106,7 +2106,7 @@ static void damos_apply_scheme(struct damon_ctx *c, struct damon_target *t, if (damos_quota_is_set(quota) && quota->charged_sz >= quota->esz) { quota->charge_target_from = t; - quota->charge_addr_from = r->ar.end + 1; + quota->charge_addr_from = r->ar.end; } } if (s->action != DAMOS_STAT) From 38d444271604afc6381ddb5a181e391915c35fae Mon Sep 17 00:00:00 2001 From: DaeMyung Kang Date: Tue, 9 Jun 2026 00:49:15 +0900 Subject: [PATCH 2354/2758] ntfs: grow index root value before reparent header update commit 0bb508fb3b97e4802ec727fd2af4d608f65dd190 upstream. ntfs_ir_reparent() moves the resident index root entries into an index block and leaves a small root stub containing the child VCN. That root stub can be larger than the existing resident value. For example, an empty root with value_length 48 has an index area of 32 bytes, while the large-index root stub needs index_length and allocated_size of 40 bytes. The current code publishes the larger index.index_length and index.allocated_size before resizing the resident value. If the resize returns -ENOSPC, the recovery path can call ntfs_inode_add_attrlist(), which looks attributes up again while the root header says allocated_size 40 but the resident value still only provides 32 bytes of index area. Lookup-time $INDEX_ROOT validation then correctly rejects that transient layout as corrupt. This reproduces as a generic/013 failure under qemu. In the failing run, the transient root had value_len=48, index_size=32, index_length=40, and allocated_size=40, and ntfsprogs-plus ntfsck reported "Corrupt index root in MFT record 1177". When the root stub grows, resize the resident value before publishing the larger root header. If the resize fails, the old root remains valid for recovery lookups. Keep the existing header-before-resize ordering for shrink or same-size cases so the resident value never temporarily exposes an allocated_size beyond its bounds. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: DaeMyung Kang Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/index.c | 78 ++++++++++++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 30 deletions(-) diff --git a/fs/ntfs/index.c b/fs/ntfs/index.c index 4200a8138efad..b79e667390517 100644 --- a/fs/ntfs/index.c +++ b/fs/ntfs/index.c @@ -1176,6 +1176,8 @@ static int ntfs_ir_reparent(struct ntfs_index_context *icx) struct index_entry *ie; struct index_block *ib = NULL; s64 new_ib_vcn; + u32 index_length; + u32 old_value_length; int ix_root_size; int ret = 0; @@ -1223,6 +1225,21 @@ static int ntfs_ir_reparent(struct ntfs_index_context *icx) goto clear_bmp; } + old_value_length = le32_to_cpu(ctx->attr->data.resident.value_length); + index_length = le32_to_cpu(ir->index.entries_offset) + + sizeof(struct index_entry_header) + sizeof(s64); + ix_root_size = offsetof(struct index_root, index) + index_length; + /* Grow the resident value before publishing the larger root header. */ + if (ix_root_size > old_value_length) { + ret = ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, ix_root_size); + if (ret) + goto resize_failed; + + icx->idx_ni->data_size = ix_root_size; + icx->idx_ni->initialized_size = ix_root_size; + icx->idx_ni->allocated_size = (ix_root_size + 7) & ~7; + } + ntfs_ir_nill(ir); ie = ntfs_ie_get_first(&ir->index); @@ -1231,48 +1248,49 @@ static int ntfs_ir_reparent(struct ntfs_index_context *icx) ir->index.flags = LARGE_INDEX; NInoSetIndexAllocPresent(icx->idx_ni); - ir->index.index_length = cpu_to_le32(le32_to_cpu(ir->index.entries_offset) + - le16_to_cpu(ie->length)); + ir->index.index_length = cpu_to_le32(index_length); ir->index.allocated_size = ir->index.index_length; - ix_root_size = sizeof(struct index_root) - sizeof(struct index_header) + - le32_to_cpu(ir->index.allocated_size); - ret = ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, ix_root_size); - if (ret) { - /* - * When there is no space to build a non-resident - * index, we may have to move the root to an extent - */ - if ((ret == -ENOSPC) && (ctx->al_entry || !ntfs_inode_add_attrlist(icx->idx_ni))) { + if (ix_root_size <= old_value_length) { + ret = ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, ix_root_size); + if (ret) + goto resize_failed; + + icx->idx_ni->data_size = ix_root_size; + icx->idx_ni->initialized_size = ix_root_size; + icx->idx_ni->allocated_size = (ix_root_size + 7) & ~7; + } + ntfs_ie_set_vcn(ie, new_ib_vcn); + goto err_out; + +resize_failed: + /* + * When there is no space to build a non-resident + * index, we may have to move the root to an extent + */ + if ((ret == -ENOSPC) && (ctx->al_entry || !ntfs_inode_add_attrlist(icx->idx_ni))) { + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + ir = ntfs_ir_lookup(icx->idx_ni, icx->name, icx->name_len, &ctx); + if (ir && !ntfs_attr_record_move_away(ctx, ix_root_size - + le32_to_cpu(ctx->attr->data.resident.value_length))) { + if (ntfs_attrlist_update(ctx->base_ntfs_ino ? + ctx->base_ntfs_ino : ctx->ntfs_ino)) + goto clear_bmp; ntfs_attr_put_search_ctx(ctx); ctx = NULL; - ir = ntfs_ir_lookup(icx->idx_ni, icx->name, icx->name_len, &ctx); - if (ir && !ntfs_attr_record_move_away(ctx, ix_root_size - - le32_to_cpu(ctx->attr->data.resident.value_length))) { - if (ntfs_attrlist_update(ctx->base_ntfs_ino ? - ctx->base_ntfs_ino : ctx->ntfs_ino)) - goto clear_bmp; - ntfs_attr_put_search_ctx(ctx); - ctx = NULL; - goto retry; - } + goto retry; } - goto clear_bmp; - } else { - icx->idx_ni->data_size = icx->idx_ni->initialized_size = ix_root_size; - icx->idx_ni->allocated_size = (ix_root_size + 7) & ~7; } - ntfs_ie_set_vcn(ie, new_ib_vcn); - +clear_bmp: + ntfs_ibm_clear(icx, new_ib_vcn); + goto err_out; err_out: kvfree(ib); if (ctx) ntfs_attr_put_search_ctx(ctx); out: return ret; -clear_bmp: - ntfs_ibm_clear(icx, new_ib_vcn); - goto err_out; } /* From 7d702aee1589e23f72b5914da36f5f74fb7dffb4 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Mon, 15 Jun 2026 08:49:52 +0900 Subject: [PATCH 2355/2758] ntfs: fix incorrect size of symbolic link commit 05a5ff86a7f12c861e3516d3dc4d092ce620742d upstream. This patch fixes the issue where a symbolic link size is displayed as 0. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/inode.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index c4fa6aa11c9e8..279b2791c6a10 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -1201,6 +1201,9 @@ static int ntfs_read_locked_inode(struct inode *vi) else vi->i_blocks = ni->allocated_size >> 9; + if (S_ISLNK(vi->i_mode) && ni->target) + vi->i_size = strlen(ni->target); + ntfs_debug("Done."); return 0; unm_err_out: From bfe835e535fe0aa5767fdd8116f62e835ba50b55 Mon Sep 17 00:00:00 2001 From: Ron de Bruijn Date: Sat, 30 May 2026 09:19:18 +0900 Subject: [PATCH 2356/2758] ntfs: fix off-by-one in mapping pairs decoding bounds checks commit 18760a74ef7c28df93726445b5595162e62ed341 upstream. In ntfs_mapping_pairs_decompress(), attr_end points one byte past the end of the attribute record: attr_end = (u8 *)attr + le32_to_cpu(attr->length); The two bounds checks validating that mapping pair data bytes fit within the attribute use strict greater-than (>), which allows a one-byte out-of-bounds read when the data extends exactly to attr_end: b = *buf & 0xf; if (b) { if (unlikely(buf + b > attr_end)) // off-by-one goto io_error; for (deltaxcn = (s8)buf[b--]; b; b--) deltaxcn = (deltaxcn << 8) + buf[b]; } When buf + b == attr_end, the check evaluates to false and buf[b] reads one byte past the valid attribute boundary. The same pattern appears in the LCN delta bytes check. Fix both checks to use >= so that buf[b] at exactly attr_end is correctly rejected as out of bounds. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: Ron de Bruijn Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/runlist.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/ntfs/runlist.c b/fs/ntfs/runlist.c index e7de3d01257e7..2d68101fbca26 100644 --- a/fs/ntfs/runlist.c +++ b/fs/ntfs/runlist.c @@ -763,7 +763,7 @@ struct runlist_element *ntfs_mapping_pairs_decompress(const struct ntfs_volume * buf = (u8 *)attr + le16_to_cpu(attr->data.non_resident.mapping_pairs_offset); attr_end = (u8 *)attr + le32_to_cpu(attr->length); - if (unlikely(buf < (u8 *)attr || buf > attr_end)) { + if (unlikely(buf < (u8 *)attr || buf >= attr_end)) { ntfs_error(vol->sb, "Corrupt attribute."); return ERR_PTR(-EIO); } @@ -811,7 +811,7 @@ struct runlist_element *ntfs_mapping_pairs_decompress(const struct ntfs_volume * */ b = *buf & 0xf; if (b) { - if (unlikely(buf + b > attr_end)) + if (unlikely(buf + b >= attr_end)) goto io_error; for (deltaxcn = (s8)buf[b--]; b; b--) deltaxcn = (deltaxcn << 8) + buf[b]; @@ -855,7 +855,7 @@ struct runlist_element *ntfs_mapping_pairs_decompress(const struct ntfs_volume * u8 b2 = *buf & 0xf; b = b2 + ((*buf >> 4) & 0xf); - if (buf + b > attr_end) + if (buf + b >= attr_end) goto io_error; for (deltaxcn = (s8)buf[b--]; b > b2; b--) deltaxcn = (deltaxcn << 8) + buf[b]; From 353a79fb76bf2befd42f7d54e08b9d2aa3d7b25e Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Thu, 14 May 2026 15:54:08 +0200 Subject: [PATCH 2357/2758] ntfs: Add WQ_PERCPU to alloc_workqueue users MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 38e8db370843b518ff9bee4af46c6b800684cc78 upstream. This continues the effort to refactor workqueue APIs, which began with the introduction of new workqueues and a new alloc_workqueue flag in: commit 128ea9f6ccfb ("workqueue: Add system_percpu_wq and system_dfl_wq") commit 930c2ea566af ("workqueue: Add new WQ_PERCPU flag") The refactoring is going to alter the default behavior of alloc_workqueue() to be unbound by default. With the introduction of the WQ_PERCPU flag (equivalent to !WQ_UNBOUND), any alloc_workqueue() caller that doesn’t explicitly specify WQ_UNBOUND must now use WQ_PERCPU. For more details see the Link tag below. In order to keep alloc_workqueue() behavior identical, explicitly request WQ_PERCPU. Cc: stable@vger.kernel.org # v7.1 Link: https://lore.kernel.org/all/20250221112003.1dSuoGyc@linutronix.de/ Suggested-by: Tejun Heo Signed-off-by: Marco Crivellari Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index cb880cb6e3886..66fc528c7e394 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -2656,7 +2656,7 @@ MODULE_ALIAS_FS("ntfs"); static int ntfs_workqueue_init(void) { - ntfs_wq = alloc_workqueue("ntfs-bg-io", 0, 0); + ntfs_wq = alloc_workqueue("ntfs-bg-io", WQ_PERCPU, 0); if (!ntfs_wq) return -ENOMEM; return 0; From e4c36dfac57a7261e9aeb0f3a7f30944a8aefb56 Mon Sep 17 00:00:00 2001 From: DaeMyung Kang Date: Sat, 30 May 2026 23:35:09 +0900 Subject: [PATCH 2358/2758] ntfs: validate attribute values on lookup commit d5803e3345dae9c6470bb61869885236276b9a35 upstream. ntfs_attr_find() and ntfs_external_attr_find() check that generic resident attribute values fit in their attribute records and that fixed-size resident values are large enough. For variable-length resident formats, however, the fixed part is not enough: embedded length fields can still point callers past the resident value. A crafted image can set a small resident $FILE_NAME value_length while leaving file_name_length large. Callers then trust file_name_length and read past the resident value when converting or comparing the name. This was reproduced with a crafted image under KASAN as a slab-out-of-bounds read from the kmalloc-1k MFT record copy. The stack included ntfs_lookup(), ntfs_iget(), ntfs_read_locked_inode(), ntfs_attr_name_get(), ntfs_ucstonls(), and utf16s_to_utf8s(). Add a shared attribute value validator and use it before a lookup path can return an attribute, including the AT_UNUSED enumeration case where callers inspect returned attributes directly. The helper validates resident value bounds, minimum resident value sizes, variable-length $FILE_NAME fields, and non-resident mapping-pairs metadata that was previously checked separately in both lookup paths. This also preserves the intended resident @val matching semantics in the external attribute lookup path. The old duplicated validation block overwrote the actual resident value length with the type-specific minimum length before comparing @val, so variable-length resident values could fail to match even when the bytes were identical. Keep the comparison on the actual value length, and make ntfs_attrlist_entry_add() compare resident attributes with lowest_vcn zero instead of reading the non-resident union member after a successful resident match. Reject non-resident $FILE_NAME records too: the format requires $FILE_NAME to be resident and callers treat returned records as resident. Cc: stable@vger.kernel.org # v7.1 Fixes: 6ceb4cc81ef3 ("ntfs: add bound checking to ntfs_attr_find") Signed-off-by: DaeMyung Kang Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/attrib.c | 160 ++++++++++++++++++++++++++++----------------- fs/ntfs/attrlist.c | 11 +++- 2 files changed, 107 insertions(+), 64 deletions(-) diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index e8dc4b07bd975..114c965d10947 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -595,6 +595,97 @@ static u32 ntfs_resident_attr_min_value_length(const __le32 type) } } +static bool ntfs_file_name_attr_value_is_valid(const u8 *value, const u32 value_length) +{ + const struct file_name_attr *fn; + u32 file_name_size; + + fn = (const struct file_name_attr *)value; + file_name_size = fn->file_name_length * sizeof(__le16); + + return file_name_size <= + value_length - offsetof(struct file_name_attr, file_name); +} + +struct ntfs_resident_attr_value { + const u8 *data; + u32 len; +}; + +static bool ntfs_resident_attr_value_get(const struct attr_record *a, + struct ntfs_resident_attr_value *value) +{ + u32 attr_len; + u16 value_offset; + + attr_len = le32_to_cpu(a->length); + if (attr_len < offsetof(struct attr_record, data.resident.reserved) + + sizeof(a->data.resident.reserved)) + return false; + + value->len = le32_to_cpu(a->data.resident.value_length); + value_offset = le16_to_cpu(a->data.resident.value_offset); + + if (value->len > attr_len || value_offset > attr_len - value->len) + return false; + + value->data = (const u8 *)a + value_offset; + return true; +} + +static bool ntfs_non_resident_attr_value_is_valid(const struct attr_record *a) +{ + u32 attr_len; + u32 min_len; + u16 mp_offset; + + attr_len = le32_to_cpu(a->length); + min_len = offsetof(struct attr_record, data.non_resident.initialized_size) + + sizeof(a->data.non_resident.initialized_size); + if (attr_len < min_len) + return false; + + mp_offset = le16_to_cpu(a->data.non_resident.mapping_pairs_offset); + return mp_offset >= min_len && mp_offset <= attr_len; +} + +static bool ntfs_attr_value_is_valid(struct ntfs_volume *vol, + const struct attr_record *a, + const u64 mft_no) +{ + struct ntfs_resident_attr_value value; + u32 min_len; + + if (a->non_resident) { + if (a->type == AT_FILE_NAME) + goto corrupt; + if (!ntfs_non_resident_attr_value_is_valid(a)) + goto corrupt; + return true; + } + + if (!ntfs_resident_attr_value_get(a, &value)) + goto corrupt; + + min_len = ntfs_resident_attr_min_value_length(a->type); + if (min_len && value.len < min_len) + goto corrupt; + + switch (a->type) { + case AT_FILE_NAME: + if (!ntfs_file_name_attr_value_is_valid(value.data, value.len)) + goto corrupt; + break; + } + return true; + +corrupt: + ntfs_error(vol->sb, + "Corrupt %#x attribute in MFT record %llu\n", + le32_to_cpu(a->type), mft_no); + return false; +} + /* * ntfs_attr_find - find (next) attribute in mft record * @type: attribute type to find @@ -705,8 +796,11 @@ static int ntfs_attr_find(const __le32 type, const __le16 *name, } } - if (type == AT_UNUSED) + if (type == AT_UNUSED) { + if (!ntfs_attr_value_is_valid(vol, a, ctx->ntfs_ino->mft_no)) + break; return 0; + } if (a->type != type) continue; /* @@ -747,37 +841,8 @@ static int ntfs_attr_find(const __le32 type, const __le16 *name, } } - /* Validate attribute's value offset/length */ - if (!a->non_resident) { - u32 min_len; - u32 value_length = le32_to_cpu(a->data.resident.value_length); - u16 value_offset = le16_to_cpu(a->data.resident.value_offset); - - if (value_length > le32_to_cpu(a->length) || - value_offset > le32_to_cpu(a->length) - value_length) - break; - - min_len = ntfs_resident_attr_min_value_length(a->type); - if (min_len && value_length < min_len) { - ntfs_error(vol->sb, - "Too small %#x resident attribute value in MFT record %lld\n", - le32_to_cpu(a->type), (long long)ctx->ntfs_ino->mft_no); - break; - } - } else { - u32 min_len; - u16 mp_offset; - - min_len = offsetof(struct attr_record, data.non_resident.initialized_size) + - sizeof(a->data.non_resident.initialized_size); - if (le32_to_cpu(a->length) < min_len) - break; - - mp_offset = le16_to_cpu(a->data.non_resident.mapping_pairs_offset); - if (mp_offset < min_len || - mp_offset > le32_to_cpu(a->length)) - break; - } + if (!ntfs_attr_value_is_valid(vol, a, ctx->ntfs_ino->mft_no)) + break; /* * The names match or @name not present and attribute is @@ -1299,22 +1364,8 @@ static int ntfs_external_attr_find(const __le32 type, ctx->attr = a; - if (a->non_resident) { - u32 min_len; - u16 mp_offset; - - min_len = offsetof(struct attr_record, - data.non_resident.initialized_size) + - sizeof(a->data.non_resident.initialized_size); - - if (le32_to_cpu(a->length) < min_len) - break; - - mp_offset = - le16_to_cpu(a->data.non_resident.mapping_pairs_offset); - if (mp_offset < min_len || mp_offset > attr_len) - break; - } + if (!ntfs_attr_value_is_valid(vol, a, ctx->ntfs_ino->mft_no)) + break; /* * If no @val specified or @val specified and it matches, we @@ -1326,19 +1377,6 @@ static int ntfs_external_attr_find(const __le32 type, u32 value_length = le32_to_cpu(a->data.resident.value_length); u16 value_offset = le16_to_cpu(a->data.resident.value_offset); - if (attr_len < offsetof(struct attr_record, data.resident.reserved) + - sizeof(a->data.resident.reserved)) - break; - if (value_length > attr_len || value_offset > attr_len - value_length) - break; - - value_length = ntfs_resident_attr_min_value_length(a->type); - if (value_length && le32_to_cpu(a->data.resident.value_length) < - value_length) { - pr_err("Too small resident attribute value in MFT record %lld, type %#x\n", - (long long)ctx->ntfs_ino->mft_no, a->type); - break; - } if (value_length == val_len && !memcmp((u8 *)a + value_offset, val, val_len)) { attr_found: diff --git a/fs/ntfs/attrlist.c b/fs/ntfs/attrlist.c index c2594d4c83b06..afb13038ba425 100644 --- a/fs/ntfs/attrlist.c +++ b/fs/ntfs/attrlist.c @@ -118,6 +118,7 @@ int ntfs_attrlist_entry_add(struct ntfs_inode *ni, struct attr_record *attr) int entry_len, entry_offset, err; struct mft_record *ni_mrec; u8 *old_al; + __le64 lowest_vcn; if (!ni || !attr) { ntfs_debug("Invalid arguments.\n"); @@ -158,17 +159,21 @@ int ntfs_attrlist_entry_add(struct ntfs_inode *ni, struct attr_record *attr) ntfs_error(ni->vol->sb, "Failed to get search context"); goto err_out; } + if (attr->non_resident) + lowest_vcn = attr->data.non_resident.lowest_vcn; + else + lowest_vcn = 0; err = ntfs_attr_lookup(attr->type, (attr->name_length) ? (__le16 *) ((u8 *)attr + le16_to_cpu(attr->name_offset)) : AT_UNNAMED, attr->name_length, CASE_SENSITIVE, - (attr->non_resident) ? le64_to_cpu(attr->data.non_resident.lowest_vcn) : - 0, (attr->non_resident) ? NULL : ((u8 *)attr + + le64_to_cpu(lowest_vcn), + (attr->non_resident) ? NULL : ((u8 *)attr + le16_to_cpu(attr->data.resident.value_offset)), (attr->non_resident) ? 0 : le32_to_cpu(attr->data.resident.value_length), ctx); if (!err) { /* Found some extent, check it to be before new extent. */ - if (ctx->al_entry->lowest_vcn == attr->data.non_resident.lowest_vcn) { + if (ctx->al_entry->lowest_vcn == lowest_vcn) { err = -EEXIST; ntfs_debug("Such attribute already present in the attribute list.\n"); ntfs_attr_put_search_ctx(ctx); From d9d9925de1d8f233cc60d3dc356e12f232f97c15 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Sat, 23 May 2026 13:14:23 +0900 Subject: [PATCH 2359/2758] ntfs: add bounds check before accessing EA entries commit 937282f7d15b593d0be765fa2ced164130ec87f7 upstream. in ntfs_ea_lookup and ntfs_listxattr, this verifies that there is enough space in the EA entry before accessing the next_entry_offset field of the EA entry. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/ea.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/fs/ntfs/ea.c b/fs/ntfs/ea.c index c4a4a3e3e5996..0cd192752b7cd 100644 --- a/fs/ntfs/ea.c +++ b/fs/ntfs/ea.c @@ -53,11 +53,11 @@ static int ntfs_ea_lookup(char *ea_buf, s64 ea_buf_size, const char *name, loff_t offset, p_ea_size; unsigned int next; - if (ea_buf_size < sizeof(struct ea_attr)) - goto out; - offset = 0; do { + if (ea_buf_size - offset < sizeof(struct ea_attr)) + break; + p_ea = (const struct ea_attr *)&ea_buf[offset]; next = le32_to_cpu(p_ea->next_entry_offset); p_ea_size = next ? next : (ea_buf_size - offset); @@ -479,13 +479,13 @@ ssize_t ntfs_listxattr(struct dentry *dentry, char *buffer, size_t size) if (ea_info_qsize > ea_buf_size || ea_info_qsize == 0) goto out; - if (ea_info_qsize < sizeof(struct ea_attr)) { - err = -EIO; - goto out; - } - offset = 0; do { + if (ea_info_qsize - offset < sizeof(struct ea_attr)) { + err = -EIO; + goto out; + } + p_ea = (const struct ea_attr *)&ea_buf[offset]; next = le32_to_cpu(p_ea->next_entry_offset); ea_size = next ? next : (ea_info_qsize - offset); From ceb49c37250125d418410988f2376ed4d57a6706 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Thu, 28 May 2026 11:15:35 +0900 Subject: [PATCH 2360/2758] ntfs: not change 0-byte $DATA attribute to non-resident commit 0aad21570197973af4a1b25b3fb8ed3aeb9e7670 upstream. When ntfs_resident_attr_resize() cannot grow a resident attribute in place, it retries after converting other resident attributes to non-resident to free space in the MFT recrord. Do not select zero-length resident $DATA attributes for this conversion. fsck treats 0-byte non-resident $DATA attribute as corruptions. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/attrib.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index 114c965d10947..6ab8898d13f97 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -4621,10 +4621,12 @@ static int ntfs_resident_attr_resize(struct ntfs_inode *attr_ni, const s64 newsi while (!(err = ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx))) { struct inode *tvi; struct attr_record *a; + u32 value_len; a = ctx->attr; if (a->non_resident || a->type == AT_ATTRIBUTE_LIST) continue; + value_len = le32_to_cpu(a->data.resident.value_length); if (ntfs_attr_can_be_non_resident(vol, a->type)) continue; @@ -4636,6 +4638,8 @@ static int ntfs_resident_attr_resize(struct ntfs_inode *attr_ni, const s64 newsi if (le32_to_cpu(a->length) <= (sizeof(struct attr_record) - sizeof(s64)) + ((a->name_length * sizeof(__le16) + 7) & ~7) + 8) continue; + if (a->type == AT_DATA && !value_len) + continue; if (a->type == AT_DATA) tvi = ntfs_iget(sb, base_ni->mft_no); @@ -4648,8 +4652,7 @@ static int ntfs_resident_attr_resize(struct ntfs_inode *attr_ni, const s64 newsi continue; } - if (ntfs_attr_make_non_resident(NTFS_I(tvi), - le32_to_cpu(ctx->attr->data.resident.value_length))) { + if (ntfs_attr_make_non_resident(NTFS_I(tvi), value_len)) { iput(tvi); continue; } From 34a49b3e94a50f45b62c6e6f574f676a079ba23e Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Sat, 23 May 2026 13:14:20 +0900 Subject: [PATCH 2361/2758] ntfs: validate index block header more strictly commit 14bc34fe948523dc2b0174691f9af9e74eb4f3fd upstream. Modify ntfs_index_block_inconsisent() to perform stricter validation of INDEX_HEADER geometry in INDX blocks, and update ntfs_lookup_inode_by_name() to use that function to validate INDX blocks. Cc: stable@vger.kernel.org # v7.1 Tested-by: woot000 Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/dir.c | 38 ++++-------------- fs/ntfs/index.c | 101 ++++++++++++++++++++++++++++++++++-------------- fs/ntfs/index.h | 3 ++ 3 files changed, 81 insertions(+), 61 deletions(-) diff --git a/fs/ntfs/dir.c b/fs/ntfs/dir.c index 20f5c7074bdd1..6745a0e6e3e76 100644 --- a/fs/ntfs/dir.c +++ b/fs/ntfs/dir.c @@ -342,43 +342,19 @@ u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, const __le16 *uname, dir_ni->mft_no); goto unm_err_out; } - /* Catch multi sector transfer fixup errors. */ - if (unlikely(!ntfs_is_indx_record(ia->magic))) { - ntfs_error(sb, - "Directory index record with vcn 0x%llx is corrupt. Corrupt inode 0x%llx. Run chkdsk.", - vcn, dir_ni->mft_no); - goto unm_err_out; - } - if (le64_to_cpu(ia->index_block_vcn) != vcn) { - ntfs_error(sb, - "Actual VCN (0x%llx) of index buffer is different from expected VCN (0x%llx). Directory inode 0x%llx is corrupt or driver bug.", - le64_to_cpu(ia->index_block_vcn), - vcn, dir_ni->mft_no); - goto unm_err_out; - } - if (le32_to_cpu(ia->index.allocated_size) + 0x18 != - dir_ni->itype.index.block_size) { - ntfs_error(sb, - "Index buffer (VCN 0x%llx) of directory inode 0x%llx has a size (%u) differing from the directory specified size (%u). Directory inode is corrupt or driver bug.", - vcn, dir_ni->mft_no, - le32_to_cpu(ia->index.allocated_size) + 0x18, - dir_ni->itype.index.block_size); - goto unm_err_out; - } index_end = (u8 *)ia + dir_ni->itype.index.block_size; if (index_end > kaddr + PAGE_SIZE) { ntfs_error(sb, - "Index buffer (VCN 0x%llx) of directory inode 0x%llx crosses page boundary. Impossible! Cannot access! This is probably a bug in the driver.", - vcn, dir_ni->mft_no); + "Index buffer (VCN 0x%llx) of directory inode 0x%llx crosses page boundary. Impossible! Cannot access! This is probably a bug in the driver.", + vcn, dir_ni->mft_no); goto unm_err_out; } - index_end = (u8 *)&ia->index + le32_to_cpu(ia->index.index_length); - if (index_end > (u8 *)ia + dir_ni->itype.index.block_size) { - ntfs_error(sb, - "Size of index buffer (VCN 0x%llx) of directory inode 0x%llx exceeds maximum size.", - vcn, dir_ni->mft_no); + err = ntfs_index_block_inconsistent(vol, ia, + dir_ni->itype.index.block_size, + vcn, dir_ni->mft_no); + if (err) goto unm_err_out; - } + index_end = (u8 *)&ia->index + le32_to_cpu(ia->index.index_length); /* The first index entry. */ ie = (struct index_entry *)((u8 *)&ia->index + le32_to_cpu(ia->index.entries_offset)); diff --git a/fs/ntfs/index.c b/fs/ntfs/index.c index b79e667390517..b00550cb866b6 100644 --- a/fs/ntfs/index.c +++ b/fs/ntfs/index.c @@ -306,6 +306,55 @@ static int ntfs_ie_end(struct index_entry *ie) return ie->flags & INDEX_ENTRY_END || !ie->length; } +static int ntfs_index_header_inconsistent(struct ntfs_volume *vol, + const struct index_header *ih, + u32 bytes_available, u64 inum) +{ + u32 entries_offset, index_length, allocated_size; + + if (bytes_available < sizeof(struct index_header)) { + ntfs_error(vol->sb, + "index block in inode %llu is smaller than an index header.", + (unsigned long long)inum); + return -EIO; + } + + entries_offset = le32_to_cpu(ih->entries_offset); + index_length = le32_to_cpu(ih->index_length); + allocated_size = le32_to_cpu(ih->allocated_size); + + if (entries_offset < sizeof(struct index_header) || + entries_offset > bytes_available) { + ntfs_error(vol->sb, + "Invalid index entry offset in inode %llu.", + (unsigned long long)inum); + return -EIO; + } + + if (index_length <= entries_offset) { + ntfs_error(vol->sb, + "No space for index entries in inode %llu.", + (unsigned long long)inum); + return -EIO; + } + + if (allocated_size < index_length) { + ntfs_error(vol->sb, + "Index entries overflow in inode %llu.", + (unsigned long long)inum); + return -EIO; + } + + if (allocated_size > bytes_available || index_length > bytes_available) { + ntfs_error(vol->sb, + "Index entries in inode %llu exceed the available buffer.", + (unsigned long long)inum); + return -EIO; + } + + return 0; +} + /* * Find the last entry in the index block */ @@ -440,7 +489,7 @@ static struct index_entry *ntfs_ie_dup_novcn(struct index_entry *ie) * The size of block is assumed to have been checked to be what is * defined in the index root. * - * Returns 0 if no error was found -1 otherwise (with errno unchanged) + * Returns 0 if no error was found, -EIO otherwise * * |<--->| offsetof(struct index_block, index) * | |<--->| sizeof(struct index_header) @@ -455,21 +504,20 @@ static struct index_entry *ntfs_ie_dup_novcn(struct index_entry *ie) * * size(struct index_header) <= ent_offset < ind_length <= alloc_size < bk_size */ -static int ntfs_index_block_inconsistent(struct ntfs_index_context *icx, - struct index_block *ib, s64 vcn) +int ntfs_index_block_inconsistent(struct ntfs_volume *vol, + const struct index_block *ib, + u32 block_size, s64 vcn, u64 inum) { u32 ib_size = (unsigned int)le32_to_cpu(ib->index.allocated_size) + offsetof(struct index_block, index); - struct super_block *sb = icx->idx_ni->vol->sb; - unsigned long long inum = icx->idx_ni->mft_no; + struct super_block *sb = vol->sb; ntfs_debug("Entering\n"); if (!ntfs_is_indx_record(ib->magic)) { - ntfs_error(sb, "Corrupt index block signature: vcn %lld inode %llu\n", - vcn, (unsigned long long)icx->idx_ni->mft_no); - return -1; + vcn, (unsigned long long)inum); + return -EIO; } if (le64_to_cpu(ib->index_block_vcn) != vcn) { @@ -477,30 +525,21 @@ static int ntfs_index_block_inconsistent(struct ntfs_index_context *icx, "Corrupt index block: s64 (%lld) is different from expected s64 (%lld) in inode %llu\n", (long long)le64_to_cpu(ib->index_block_vcn), vcn, inum); - return -1; + return -EIO; } - if (ib_size != icx->block_size) { + if (ib_size != block_size) { ntfs_error(sb, - "Corrupt index block : s64 (%lld) of inode %llu has a size (%u) differing from the index specified size (%u)\n", - vcn, inum, ib_size, icx->block_size); - return -1; + "Corrupt index block : s64 (%lld) of inode %llu has a size (%u) differing from the index specified size (%u)\n", + vcn, inum, ib_size, block_size); + return -EIO; } - if (le32_to_cpu(ib->index.entries_offset) < sizeof(struct index_header)) { - ntfs_error(sb, "Invalid index entry offset in inode %lld\n", inum); - return -1; - } - if (le32_to_cpu(ib->index.index_length) <= - le32_to_cpu(ib->index.entries_offset)) { - ntfs_error(sb, "No space for index entries in inode %lld\n", inum); - return -1; - } - if (le32_to_cpu(ib->index.allocated_size) < - le32_to_cpu(ib->index.index_length)) { - ntfs_error(sb, "Index entries overflow in inode %lld\n", inum); - return -1; - } + if (ntfs_index_header_inconsistent(vol, &ib->index, + block_size - + offsetof(struct index_block, index), + inum)) + return -EIO; return 0; } @@ -668,12 +707,14 @@ static int ntfs_ib_read(struct ntfs_index_context *icx, s64 vcn, struct index_bl else ntfs_error(icx->idx_ni->vol->sb, "Failed to read full index block at %lld\n", pos); - return -1; + return -EIO; } post_read_mst_fixup((struct ntfs_record *)((u8 *)dst), icx->block_size); - if (ntfs_index_block_inconsistent(icx, dst, vcn)) - return -1; + if (ntfs_index_block_inconsistent(icx->idx_ni->vol, dst, + icx->block_size, vcn, + icx->idx_ni->mft_no)) + return -EIO; return 0; } diff --git a/fs/ntfs/index.h b/fs/ntfs/index.h index e68d6fabaf9f1..3451ec8a1c4eb 100644 --- a/fs/ntfs/index.h +++ b/fs/ntfs/index.h @@ -89,6 +89,9 @@ struct ntfs_index_context { bool sync_write; }; +int ntfs_index_block_inconsistent(struct ntfs_volume *vol, + const struct index_block *ib, + u32 block_size, s64 vcn, u64 inum); int ntfs_index_entry_inconsistent(struct ntfs_index_context *icx, struct ntfs_volume *vol, const struct index_entry *ie, __le32 collation_rule, u64 inum); struct ntfs_index_context *ntfs_index_ctx_get(struct ntfs_inode *ni, __le16 *name, From aca3d383a23cb7f3a2849c09fda3974f1838d941 Mon Sep 17 00:00:00 2001 From: DaeMyung Kang Date: Thu, 21 May 2026 19:17:49 +0900 Subject: [PATCH 2362/2758] ntfs: free volume-wide resources on fill_super failure commit a9523a7d3b24b3a6b25ec1eb668ee6618cacf05e upstream. ntfs_fill_super()'s err_out_now path frees only the volume struct via kfree(vol), leaving several vol-owned allocations behind on every mount failure: - vol->nls_map, loaded by ntfs_init_fs_context() via load_nls_default() (or replaced by an explicit nls= option in ntfs_parse_param()), is never unload_nls()'d. - vol->volume_label, allocated by load_system_files() through ntfs_ucstonls() once the $Volume name attribute has been parsed, is not released by load_system_files()'s own error labels nor by the fill_super() inline cleanup that only runs on d_make_root() failure. Any later failure inside load_system_files() leaks it. - vol->lcn_empty_bits_per_page was kvfree()'d in unl_upcase_iput_tmp_ino_err_out_now without clearing the pointer, so it could not be folded into a single common cleanup. Because the failure paths never call ntfs_volume_free() and never reach the d_make_root() inline cleanup block (it sits above the label and is jumped over by the load_system_files() / kvmalloc failure gotos), these resources accumulate per failed mount attempt with no chance of recovery short of unloading the module. This is a silent leak: the inodes loaded prior to failure remain hashed but generic_shutdown_super() skips evict_inodes() when sb->s_root is unset, so no CHECK_DATA_CORRUPTION warning is emitted either. Move the per-volume frees down to err_out_now and drop the lcn_empty_bits_per_page kvfree() from the upper label so the cleanup is performed exactly once on every failure path. Using unconditional kvfree() / kfree() / unload_nls() is safe because they all accept NULL and the upper labels that previously freed nls_map (the d_make_root() inline cleanup) already clear the pointer. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: DaeMyung Kang Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/super.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 66fc528c7e394..807bcc26def50 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -2536,8 +2536,6 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) } /* Error exit code path. */ unl_upcase_iput_tmp_ino_err_out_now: - if (vol->lcn_empty_bits_per_page) - kvfree(vol->lcn_empty_bits_per_page); /* * Decrease the number of upcase users and destroy the global default * upcase table if necessary. @@ -2557,6 +2555,9 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) /* Errors at this stage are irrelevant. */ err_out_now: sb->s_fs_info = NULL; + kvfree(vol->lcn_empty_bits_per_page); + kfree(vol->volume_label); + unload_nls(vol->nls_map); kfree(vol); ntfs_debug("Failed, returning -EINVAL."); lockdep_on(); From 0527a81e85eeb71a34e15aff305f52cf345d5898 Mon Sep 17 00:00:00 2001 From: DaeMyung Kang Date: Tue, 9 Jun 2026 00:49:16 +0900 Subject: [PATCH 2363/2758] ntfs: update index root allocated size before shrink commit e782ca90ceb798bb1811b214bf814216f11aae6a upstream. ntfs_ir_truncate() currently shrinks the resident $INDEX_ROOT value first and only updates index.allocated_size after re-looking up the attribute. During that relookup, the resident value_length can already be smaller while index.allocated_size still contains the old larger size. That leaves a transiently inconsistent $INDEX_ROOT layout and prevents lookup-time $INDEX_ROOT validation from being enabled: validation can correctly reject allocated_size extending past the newly shrunk resident value. When shrinking, lower index.allocated_size before shrinking value_length. If the truncate fails, restore the old allocated_size. Keep the existing grow ordering because the old allocated_size remains within the enlarged resident value until it is updated after the relookup. The shrink path is safe because the new value_length still covers struct index_root, so the index.allocated_size field remains present while it is updated first. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: DaeMyung Kang Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/index.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/fs/ntfs/index.c b/fs/ntfs/index.c index b00550cb866b6..7d74dbf255afd 100644 --- a/fs/ntfs/index.c +++ b/fs/ntfs/index.c @@ -1342,9 +1342,16 @@ static int ntfs_ir_reparent(struct ntfs_index_context *icx) static int ntfs_ir_truncate(struct ntfs_index_context *icx, int data_size) { int ret; + u32 old_allocated_size; + bool shrink; ntfs_debug("Entering\n"); + old_allocated_size = le32_to_cpu(icx->ir->index.allocated_size); + shrink = data_size < old_allocated_size; + if (shrink) + icx->ir->index.allocated_size = cpu_to_le32(data_size); + /* * INDEX_ROOT must be resident and its entries can be moved to * struct index_block, so ENOSPC isn't a real error. @@ -1356,9 +1363,14 @@ static int ntfs_ir_truncate(struct ntfs_index_context *icx, int data_size) if (!icx->ir) return -ENOENT; - icx->ir->index.allocated_size = cpu_to_le32(data_size); - } else if (ret != -ENOSPC) - ntfs_error(icx->idx_ni->vol->sb, "Failed to truncate INDEX_ROOT"); + if (!shrink) + icx->ir->index.allocated_size = cpu_to_le32(data_size); + } else { + if (shrink) + icx->ir->index.allocated_size = cpu_to_le32(old_allocated_size); + if (ret != -ENOSPC) + ntfs_error(icx->idx_ni->vol->sb, "Failed to truncate INDEX_ROOT"); + } return ret; } From b06730c6af58d3569883f8dd7c36a90aba5ecc0a Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Sat, 23 May 2026 13:14:21 +0900 Subject: [PATCH 2364/2758] ntfs: centalize $INDEX_ROOT header validation commit 8b97b302f553a480fb76d2afd53cd6c0635a9dcd upstream. Add a dedicated helper to perform stricter validation of $INDEX_ROOT and use it for both directory inodes and named index inodes. This keeps the root size and header geometry checks consistent across both read paths. Cc: stable@vger.kernel.org # v7.1 Tested-by: woot000 Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/index.c | 18 ++++++++++++++++++ fs/ntfs/index.h | 3 +++ fs/ntfs/inode.c | 11 ++--------- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/fs/ntfs/index.c b/fs/ntfs/index.c index 7d74dbf255afd..682246afe5737 100644 --- a/fs/ntfs/index.c +++ b/fs/ntfs/index.c @@ -544,6 +544,24 @@ int ntfs_index_block_inconsistent(struct ntfs_volume *vol, return 0; } +int ntfs_index_root_inconsistent(struct ntfs_volume *vol, + const struct attr_record *a, + const struct index_root *ir, u64 inum) +{ + u32 value_length = le32_to_cpu(a->data.resident.value_length); + + if (value_length < offsetof(struct index_root, index)) { + ntfs_error(vol->sb, "$INDEX_ROOT in inode %llu is too small.", + (unsigned long long)inum); + return -EIO; + } + + return ntfs_index_header_inconsistent(vol, &ir->index, + value_length - + offsetof(struct index_root, index), + inum); +} + static struct index_root *ntfs_ir_lookup(struct ntfs_inode *ni, __le16 *name, u32 name_len, struct ntfs_attr_search_ctx **ctx) { diff --git a/fs/ntfs/index.h b/fs/ntfs/index.h index 3451ec8a1c4eb..cad78568d8b35 100644 --- a/fs/ntfs/index.h +++ b/fs/ntfs/index.h @@ -89,6 +89,9 @@ struct ntfs_index_context { bool sync_write; }; +int ntfs_index_root_inconsistent(struct ntfs_volume *vol, + const struct attr_record *a, + const struct index_root *ir, u64 inum); int ntfs_index_block_inconsistent(struct ntfs_volume *vol, const struct index_block *ib, u32 block_size, s64 vcn, u64 inum); diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index 279b2791c6a10..8106593af6102 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -896,7 +896,6 @@ static int ntfs_read_locked_inode(struct inode *vi) */ if (S_ISDIR(vi->i_mode)) { struct index_root *ir; - u8 *ir_end, *index_end; view_index_meta: /* It is a directory, find index root attribute. */ @@ -946,10 +945,7 @@ static int ntfs_read_locked_inode(struct inode *vi) } ir = (struct index_root *)((u8 *)a + le16_to_cpu(a->data.resident.value_offset)); - ir_end = (u8 *)ir + le32_to_cpu(a->data.resident.value_length); - index_end = (u8 *)&ir->index + - le32_to_cpu(ir->index.index_length); - if (index_end > ir_end) { + if (ntfs_index_root_inconsistent(ni->vol, a, ir, ni->mft_no)) { ntfs_error(vi->i_sb, "Directory index is corrupt."); goto unm_err_out; } @@ -1492,7 +1488,6 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi) struct attr_record *a; struct ntfs_attr_search_ctx *ctx; struct index_root *ir; - u8 *ir_end, *index_end; int err = 0; ntfs_debug("Entering for i_ino 0x%llx.", ni->mft_no); @@ -1543,9 +1538,7 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi) } ir = (struct index_root *)((u8 *)a + le16_to_cpu(a->data.resident.value_offset)); - ir_end = (u8 *)ir + le32_to_cpu(a->data.resident.value_length); - index_end = (u8 *)&ir->index + le32_to_cpu(ir->index.index_length); - if (index_end > ir_end) { + if (ntfs_index_root_inconsistent(vol, a, ir, ni->mft_no)) { ntfs_error(vi->i_sb, "Index is corrupt."); goto unm_err_out; } From f831ab09d521898bf1dd99bf5adfd630ea1428e3 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Thu, 21 May 2026 14:37:03 +0900 Subject: [PATCH 2365/2758] ntfs: skip extent mft records in writeback to prevent deadlock commit 76bc14c7097ff678b2b5dbfd4fa33b46897d87ce upstream. This patch fixes the ABBA deadlock between extent_lock and extent mrec_lock triggered by xfstests generic/113, that occurs since the commit 6994acf33bae ("ntfs: use base mft_no when looking up base inode for extent record"). Path A (inode writeback): VFS writeback -> ntfs_write_inode() -> __ntfs_write_inode() -> mutex_lock(&ni->extent_lock) -> mutex_lock(&tni->mrec_lock) Path B (MFT folio writeback): VFS writeback of $MFT dirty folios -> ntfs_mft_writepages() -> ntfs_write_mft_block() -> ntfs_may_write_mft_record() -> holds one extent mrec_lock from a previous iteration -> tries to acquire another base inode extent_lock By removing all extent_lock and extent mrec_lock acquisition from the MFT folio writeback path, the ABBA lock ordering is eliminated: Path A: __ntfs_write_inode(): extent_lock -> mrec_lock Path B (removed): ntfs_write_mft_block(): mrec_lock -> extent_lock Path B is always redundant for extent records because: 1. mark_mft_record_dirty(ext_ni) does NOT dirty the MFT folio. It only sets NInoDirty(ext_ni) and marks the base VFS inode dirty via __mark_inode_dirty(I_DIRTY_DATASYNC), which triggers Path A. Therefore, normal extent modifications never create a situation where the MFT folio is dirty and Path B is not scheduled. 2. The MFT folio only gets dirtied via ntfs_mft_mark_dirty() inside ntfs_mft_record_alloc(). But all identified callers in attrib.c (ntfs_attr_add, ntfs_attr_record_move_away, ntfs_attr_make_non_resident, ntfs_attr_record_resize) follow through with mark_mft_record_dirty(), which triggers Path A to write the complete record. 3. ntfs_evict_big_inode() calls ntfs_commit_inode() before freeing extent inodes, ensuring all dirty extents are flushed via Path A before the base inode leaves the icache. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/mft.c | 129 ++------------------------------------------------ 1 file changed, 4 insertions(+), 125 deletions(-) diff --git a/fs/ntfs/mft.c b/fs/ntfs/mft.c index 464f8f706cbc8..fd20d7abd6f5e 100644 --- a/fs/ntfs/mft.c +++ b/fs/ntfs/mft.c @@ -743,23 +743,6 @@ static int ntfs_test_inode_wb(struct inode *vi, u64 ino, void *data) * * If the mft record is not a FILE record or it is a base mft record, we can * safely write it and return 'true'. - * - * We now know the mft record is an extent mft record. We check if the inode - * corresponding to its base mft record is in icache. If it is not, we cannot - * safely determine the state of the extent inode, so we return 'false'. - * - * We now have the base inode for the extent mft record. We check if it has an - * ntfs inode for the extent mft record attached. If not, it is safe to write - * the extent mft record and we return 'true'. - * - * If the extent inode is attached, we check if it is dirty. If so, we return - * 'false' (letting the standard write_inode path handle it). - * - * If it is not dirty, we attempt to lock the extent mft record. If the lock - * was already taken, it is not safe to write and we return 'false'. - * - * If we manage to obtain the lock we have exclusive access to the extent mft - * record. We set @locked_ni to the now locked ntfs inode and return 'true'. */ static bool ntfs_may_write_mft_record(struct ntfs_volume *vol, const u64 mft_no, const struct mft_record *m, struct ntfs_inode **locked_ni, @@ -768,8 +751,7 @@ static bool ntfs_may_write_mft_record(struct ntfs_volume *vol, const u64 mft_no, struct super_block *sb = vol->sb; struct inode *mft_vi = vol->mft_ino; struct inode *vi; - struct ntfs_inode *ni, *eni, **extent_nis; - int i; + struct ntfs_inode *ni; struct ntfs_attr na = {0}; ntfs_debug("Entering for inode 0x%llx.", mft_no); @@ -849,100 +831,10 @@ static bool ntfs_may_write_mft_record(struct ntfs_volume *vol, const u64 mft_no, mft_no); return true; } - /* - * This is an extent mft record. Check if the inode corresponding to - * its base mft record is in icache and obtain a reference to it if it - * is. - */ - na.mft_no = MREF_LE(m->base_mft_record); - na.state = 0; - ntfs_debug("Mft record 0x%llx is an extent record. Looking for base inode 0x%llx in icache.", - mft_no, na.mft_no); - if (!na.mft_no) { - /* Balance the below iput(). */ - vi = igrab(mft_vi); - WARN_ON(vi != mft_vi); - } else { - vi = find_inode_nowait(sb, na.mft_no, ntfs_test_inode_wb, &na); - if (na.state == NI_BeingDeleted || na.state == NI_BeingCreated) - return false; - } - if (!vi) - return false; - ntfs_debug("Base inode 0x%llx is in icache.", na.mft_no); - /* - * The base inode is in icache. Check if it has the extent inode - * corresponding to this extent mft record attached. - */ - ni = NTFS_I(vi); - mutex_lock(&ni->extent_lock); - if (ni->nr_extents <= 0) { - /* - * The base inode has no attached extent inodes, write this - * extent mft record. - */ - mutex_unlock(&ni->extent_lock); - *ref_vi = vi; - ntfs_debug("Base inode 0x%llx has no attached extent inodes, write the extent record.", - na.mft_no); - return true; - } - /* Iterate over the attached extent inodes. */ - extent_nis = ni->ext.extent_ntfs_inos; - for (eni = NULL, i = 0; i < ni->nr_extents; ++i) { - if (mft_no == extent_nis[i]->mft_no) { - /* - * Found the extent inode corresponding to this extent - * mft record. - */ - eni = extent_nis[i]; - break; - } - } - /* - * If the extent inode was not attached to the base inode, write this - * extent mft record. - */ - if (!eni) { - mutex_unlock(&ni->extent_lock); - *ref_vi = vi; - ntfs_debug("Extent inode 0x%llx is not attached to its base inode 0x%llx, write the extent record.", - mft_no, na.mft_no); - return true; - } - ntfs_debug("Extent inode 0x%llx is attached to its base inode 0x%llx.", - mft_no, na.mft_no); - /* Take a reference to the extent ntfs inode. */ - atomic_inc(&eni->count); - mutex_unlock(&ni->extent_lock); - - /* if extent inode is dirty, write_inode will write it */ - if (NInoDirty(eni)) { - atomic_dec(&eni->count); - *ref_vi = vi; - return false; - } - - /* - * Found the extent inode coresponding to this extent mft record. - * Try to take the mft record lock. - */ - if (unlikely(!mutex_trylock(&eni->mrec_lock))) { - atomic_dec(&eni->count); - *ref_vi = vi; - ntfs_debug("Extent mft record 0x%llx is already locked, do not write it.", - mft_no); - return false; - } - ntfs_debug("Managed to lock extent mft record 0x%llx, write it.", - mft_no); - /* - * The write has to occur while we hold the mft record lock so return - * the locked extent ntfs inode. - */ - *locked_ni = eni; - return true; + ntfs_debug("Mft record 0x%llx is an extent record, skip it.", + mft_no); + return false; } static const char *es = " Leaving inconsistent metadata. Unmount and run chkdsk."; @@ -2791,19 +2683,6 @@ static int ntfs_write_mft_block(struct folio *folio, struct writeback_control *w s64 vcn_off = vcn; s64 rl_len = 0; - /* - * Skip $MFT extent mft records and let them being written - * by writeback to avioid deadlocks. the $MFT runlist - * lock must be taken before $MFT extent mrec_lock is taken. - */ - if (tni && tni->nr_extents < 0 && - tni->ext.base_ntfs_ino == NTFS_I(vol->mft_ino)) { - mutex_unlock(&tni->mrec_lock); - atomic_dec(&tni->count); - iput(vol->mft_ino); - continue; - } - /* * The record should be written. If a locked ntfs * inode was returned, add it to the array of locked From d7773b7af1d29d668c67e492575d13ae319c1b20 Mon Sep 17 00:00:00 2001 From: DaeMyung Kang Date: Sat, 30 May 2026 23:35:11 +0900 Subject: [PATCH 2366/2758] ntfs: reinit search context before volume information lookup commit 45dd046ced0f5982a6d64ca449de3a61f5f15669 upstream. On mount the volume inode is searched for $VOLUME_NAME and then, reusing the same search context, for $VOLUME_INFORMATION. The $VOLUME_NAME lookup is optional and its result is otherwise ignored. Once lookup-time validation can reject a corrupt $VOLUME_NAME with -EIO, the search context is left in an undefined state: ntfs_attr_find() documents that on an actual error @ctx->attr is undefined. Continuing the $VOLUME_INFORMATION search from that context is not contractually valid. Reinitialize the search context before the $VOLUME_INFORMATION lookup so it always starts from a well-defined state regardless of the $VOLUME_NAME lookup outcome. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: DaeMyung Kang Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/super.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 807bcc26def50..4a27079f84086 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -1537,6 +1537,7 @@ static bool load_system_files(struct ntfs_volume *vol) vol->volume_label = NULL; } + ntfs_attr_reinit_search_ctx(ctx); if (ntfs_attr_lookup(AT_VOLUME_INFORMATION, NULL, 0, 0, 0, NULL, 0, ctx) || ctx->attr->non_resident || ctx->attr->flags) { ntfs_attr_put_search_ctx(ctx); From a9cafa8c780f3b9bec3493db4de418e7d6dda2d8 Mon Sep 17 00:00:00 2001 From: DaeMyung Kang Date: Thu, 21 May 2026 19:17:51 +0900 Subject: [PATCH 2367/2758] ntfs: only alias volume $UpCase to default on exact match commit 8f4b6e8bda121ae3d4d8e332b789664604d3e9d2 upstream. load_and_init_upcase() currently aliases vol->upcase to the global default upcase whenever the shared prefix matches, and then truncates vol->upcase_len to that shorter prefix. The result is correct only by accident: upcase[] accesses in name collation are gated by upcase_len, so the prefix-equality alias produces the same fold output as keeping the volume's own shorter table. Still, prefix equality is not equality: the volume table is logically distinct from the default and should not be replaced by it unless they are byte-for-byte identical. Use memcmp() to compare the complete table in one expression and drop the now-redundant upcase_len rewrite. No user-visible change is expected for compliant volumes whose $UpCase has exactly default_upcase_len entries; shorter volume tables are no longer aliased to the default. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: DaeMyung Kang Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/super.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 4a27079f84086..b3efd3f323f2b 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -1329,7 +1329,6 @@ static bool load_and_init_upcase(struct ntfs_volume *vol) u8 *addr; pgoff_t index, max_index; unsigned int size; - int i, max; ntfs_debug("Entering."); /* Read upcase table and setup vol->upcase and vol->upcase_len. */ @@ -1380,16 +1379,11 @@ static bool load_and_init_upcase(struct ntfs_volume *vol) mutex_unlock(&ntfs_lock); return true; } - max = default_upcase_len; - if (max > vol->upcase_len) - max = vol->upcase_len; - for (i = 0; i < max; i++) - if (vol->upcase[i] != default_upcase[i]) - break; - if (i == max) { + if (default_upcase_len == vol->upcase_len && + !memcmp(vol->upcase, default_upcase, + default_upcase_len * sizeof(*default_upcase))) { kvfree(vol->upcase); vol->upcase = default_upcase; - vol->upcase_len = max; ntfs_nr_upcase_users++; mutex_unlock(&ntfs_lock); ntfs_debug("Volume specified $UpCase matches default. Using default."); From 40ee64e633e5e413f2255bb48c063977d8c86f34 Mon Sep 17 00:00:00 2001 From: DaeMyung Kang Date: Fri, 22 May 2026 23:20:48 +0900 Subject: [PATCH 2368/2758] ntfs: avoid heap allocation for free-cluster readahead state commit c05132077df57a384919f61d7f8a8e76d748a6d4 upstream. get_nr_free_clusters() allocates a temporary file_ra_state before it publishes the precomputed free cluster count, sets NVolFreeClusterKnown(), and wakes vol->free_waitq. If that allocation fails, the worker returns without setting the flag or waking waiters, so callers waiting for the free count can block indefinitely. The readahead state is only used synchronously while scanning the bitmap. Keep it on the stack and pass it by address to the readahead helper. This eliminates the early allocation failure path instead of adding a special case that publishes a conservative count and wakes the waitqueue. Zero-initialize the on-stack state because file_ra_state_init() only sets ra_pages and prev_pos. Apply the same treatment to __get_nr_free_mft_records(), which scans the MFT bitmap with the same short-lived readahead state. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: DaeMyung Kang Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/super.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index b3efd3f323f2b..f09bbf330f229 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -1955,7 +1955,7 @@ s64 get_nr_free_clusters(struct ntfs_volume *vol) struct address_space *mapping = vol->lcnbmp_ino->i_mapping; struct folio *folio; pgoff_t index, max_index; - struct file_ra_state *ra; + struct file_ra_state ra = { 0 }; ntfs_debug("Entering."); /* Serialize accesses to the cluster bitmap. */ @@ -1963,11 +1963,7 @@ s64 get_nr_free_clusters(struct ntfs_volume *vol) if (NVolFreeClusterKnown(vol)) return atomic64_read(&vol->free_clusters); - ra = kzalloc(sizeof(*ra), GFP_NOFS); - if (!ra) - return 0; - - file_ra_state_init(ra, mapping); + file_ra_state_init(&ra, mapping); /* * Convert the number of bits into bytes rounded up, then convert into @@ -1986,7 +1982,7 @@ s64 get_nr_free_clusters(struct ntfs_volume *vol) * Get folio from page cache, getting it from backing store * if necessary, and increment the use count. */ - folio = ntfs_get_locked_folio(mapping, index, max_index, ra); + folio = ntfs_get_locked_folio(mapping, index, max_index, &ra); /* Ignore pages which errored synchronously. */ if (IS_ERR(folio)) { @@ -2025,7 +2021,6 @@ s64 get_nr_free_clusters(struct ntfs_volume *vol) else atomic64_set(&vol->free_clusters, nr_free); - kfree(ra); NVolSetFreeClusterKnown(vol); wake_up_all(&vol->free_waitq); ntfs_debug("Exiting."); @@ -2080,15 +2075,11 @@ static unsigned long __get_nr_free_mft_records(struct ntfs_volume *vol, struct address_space *mapping = vol->mftbmp_ino->i_mapping; struct folio *folio; pgoff_t index; - struct file_ra_state *ra; + struct file_ra_state ra = { 0 }; ntfs_debug("Entering."); - ra = kzalloc(sizeof(*ra), GFP_NOFS); - if (!ra) - return 0; - - file_ra_state_init(ra, mapping); + file_ra_state_init(&ra, mapping); /* Use multiples of 4 bytes, thus max_size is PAGE_SIZE / 4. */ ntfs_debug("Reading $MFT/$BITMAP, max_index = 0x%lx, max_size = 0x%lx.", @@ -2100,7 +2091,7 @@ static unsigned long __get_nr_free_mft_records(struct ntfs_volume *vol, * Get folio from page cache, getting it from backing store * if necessary, and increment the use count. */ - folio = ntfs_get_locked_folio(mapping, index, max_index, ra); + folio = ntfs_get_locked_folio(mapping, index, max_index, &ra); /* Ignore pages which errored synchronously. */ if (IS_ERR(folio)) { @@ -2132,7 +2123,6 @@ static unsigned long __get_nr_free_mft_records(struct ntfs_volume *vol, else atomic64_set(&vol->free_mft_records, nr_free); - kfree(ra); ntfs_debug("Exiting."); return nr_free; } From e2b95d3adb558ddd5685f9e072ec8661d57ee3a9 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Sat, 23 May 2026 13:14:22 +0900 Subject: [PATCH 2369/2758] ntfs: validate index entries on reading commit 2221b691d7b2e17f08153f95848dacaa5d87e21d upstream. Validate index entries immediately after reading an index root or index block from disk. This eliminates repeated checks in lookup and readdir, and reduce the risk of missing checks in those paths. Cc: stable@vger.kernel.org # v7.1 Tested-by: woot000 Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/dir.c | 28 ++--------------- fs/ntfs/index.c | 84 +++++++++++++++++++++++++++---------------------- fs/ntfs/index.h | 8 +++-- fs/ntfs/inode.c | 8 +++-- 4 files changed, 60 insertions(+), 68 deletions(-) diff --git a/fs/ntfs/dir.c b/fs/ntfs/dir.c index 6745a0e6e3e76..4b6bd5f30c65e 100644 --- a/fs/ntfs/dir.c +++ b/fs/ntfs/dir.c @@ -135,10 +135,6 @@ u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, const __le16 *uname, /* Key length should not be zero if it is not last entry. */ if (!ie->key_length) goto dir_err_out; - /* Check the consistency of an index entry */ - if (ntfs_index_entry_inconsistent(NULL, vol, ie, COLLATION_FILE_NAME, - dir_ni->mft_no)) - goto dir_err_out; /* * We perform a case sensitive comparison and if that matches * we are done and return the mft reference of the inode (i.e. @@ -351,7 +347,8 @@ u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, const __le16 *uname, } err = ntfs_index_block_inconsistent(vol, ia, dir_ni->itype.index.block_size, - vcn, dir_ni->mft_no); + vcn, COLLATION_FILE_NAME, + dir_ni->mft_no); if (err) goto unm_err_out; index_end = (u8 *)&ia->index + le32_to_cpu(ia->index.index_length); @@ -364,15 +361,6 @@ u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, const __le16 *uname, * reach the last entry. */ for (;; ie = (struct index_entry *)((u8 *)ie + le16_to_cpu(ie->length))) { - /* Bounds checks. */ - if ((u8 *)ie < (u8 *)ia || - (u8 *)ie + sizeof(struct index_entry_header) > index_end || - (u8 *)ie + sizeof(struct index_entry_header) + le16_to_cpu(ie->key_length) > - index_end || (u8 *)ie + le16_to_cpu(ie->length) > index_end) { - ntfs_error(sb, "Index entry out of bounds in directory inode 0x%llx.", - dir_ni->mft_no); - goto unm_err_out; - } /* * The last entry cannot contain a name. It can however contain * a pointer to a child node in the B+tree so we just break out. @@ -382,10 +370,6 @@ u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, const __le16 *uname, /* Key length should not be zero if it is not last entry. */ if (!ie->key_length) goto unm_err_out; - /* Check the consistency of an index entry */ - if (ntfs_index_entry_inconsistent(NULL, vol, ie, COLLATION_FILE_NAME, - dir_ni->mft_no)) - goto unm_err_out; /* * We perform a case sensitive comparison and if that matches * we are done and return the mft reference of the inode (i.e. @@ -868,6 +852,7 @@ static int ntfs_readdir(struct file *file, struct dir_context *actor) ictx->vcn_size_bits = vol->cluster_size_bits; else ictx->vcn_size_bits = NTFS_BLOCK_SIZE_BITS; + ictx->cr = ir->collation_rule; /* The first index entry. */ next = (struct index_entry *)((u8 *)&ir->index + @@ -905,13 +890,6 @@ static int ntfs_readdir(struct file *file, struct dir_context *actor) if (!next) break; nextdir: - /* Check the consistency of an index entry */ - if (ntfs_index_entry_inconsistent(ictx, vol, next, COLLATION_FILE_NAME, - ndir->mft_no)) { - err = -EIO; - goto out; - } - if (ie_pos < actor->pos) { ie_pos += le16_to_cpu(next->length); continue; diff --git a/fs/ntfs/index.c b/fs/ntfs/index.c index 682246afe5737..9495b39bd3ceb 100644 --- a/fs/ntfs/index.c +++ b/fs/ntfs/index.c @@ -28,41 +28,10 @@ * length must have been checked beforehand to not overflow from the * index record. */ -int ntfs_index_entry_inconsistent(struct ntfs_index_context *icx, - struct ntfs_volume *vol, const struct index_entry *ie, - __le32 collation_rule, u64 inum) +static int ntfs_index_entry_inconsistent(const struct ntfs_volume *vol, + const struct index_entry *ie, + __le32 collation_rule, u64 inum) { - if (icx) { - struct index_header *ih; - u8 *ie_start, *ie_end; - - if (icx->is_in_root) - ih = &icx->ir->index; - else - ih = &icx->ib->index; - - if ((le32_to_cpu(ih->index_length) > le32_to_cpu(ih->allocated_size)) || - (le32_to_cpu(ih->index_length) > icx->block_size)) { - ntfs_error(vol->sb, "%s Index entry(0x%p)'s length is too big.", - icx->is_in_root ? "Index root" : "Index block", - (u8 *)icx->entry); - return -EINVAL; - } - - ie_start = (u8 *)ih + le32_to_cpu(ih->entries_offset); - ie_end = (u8 *)ih + le32_to_cpu(ih->index_length); - - if (ie_start > (u8 *)ie || - ie_end <= (u8 *)ie + le16_to_cpu(ie->length) || - le16_to_cpu(ie->length) > le32_to_cpu(ih->allocated_size) || - le16_to_cpu(ie->length) > icx->block_size) { - ntfs_error(vol->sb, "Index entry(0x%p) is out of range from %s", - (u8 *)icx->entry, - icx->is_in_root ? "index root" : "index block"); - return -EIO; - } - } - if (ie->key_length && ((le16_to_cpu(ie->key_length) + offsetof(struct index_entry, key)) > le16_to_cpu(ie->length))) { @@ -355,6 +324,44 @@ static int ntfs_index_header_inconsistent(struct ntfs_volume *vol, return 0; } +int ntfs_index_entries_inconsistent(const struct ntfs_volume *vol, + const struct index_header *ih, + __le32 collation_rule, u64 inum) +{ + struct index_entry *ie; + u8 *index_end = (u8 *)ih + le32_to_cpu(ih->index_length); + + for (ie = ntfs_ie_get_first((struct index_header *)ih); + ; ie = ntfs_ie_get_next(ie)) { + if ((u8 *)ie + sizeof(struct index_entry_header) > index_end || + (u8 *)ie + le16_to_cpu(ie->length) > index_end) { + ntfs_error(vol->sb, + "Index entry out of bounds in inode %llu.", + (unsigned long long)inum); + return -EIO; + } + + if (le16_to_cpu(ie->length) < sizeof(struct index_entry_header)) { + ntfs_error(vol->sb, + "Index etnry too small in inode %llu.", + inum); + return -EIO; + } + + if (ntfs_ie_end(ie)) + break; + + if (!ie->key_length) + return -EIO; + + if (ntfs_index_entry_inconsistent(vol, ie, + collation_rule, inum)) + return -EIO; + } + + return 0; +} + /* * Find the last entry in the index block */ @@ -506,7 +513,8 @@ static struct index_entry *ntfs_ie_dup_novcn(struct index_entry *ie) */ int ntfs_index_block_inconsistent(struct ntfs_volume *vol, const struct index_block *ib, - u32 block_size, s64 vcn, u64 inum) + u32 block_size, s64 vcn, __le32 cr, + u64 inum) { u32 ib_size = (unsigned int)le32_to_cpu(ib->index.allocated_size) + offsetof(struct index_block, index); @@ -540,7 +548,8 @@ int ntfs_index_block_inconsistent(struct ntfs_volume *vol, offsetof(struct index_block, index), inum)) return -EIO; - + if (ntfs_index_entries_inconsistent(vol, &ib->index, cr, inum)) + return -EIO; return 0; } @@ -730,10 +739,9 @@ static int ntfs_ib_read(struct ntfs_index_context *icx, s64 vcn, struct index_bl post_read_mst_fixup((struct ntfs_record *)((u8 *)dst), icx->block_size); if (ntfs_index_block_inconsistent(icx->idx_ni->vol, dst, - icx->block_size, vcn, + icx->block_size, vcn, icx->cr, icx->idx_ni->mft_no)) return -EIO; - return 0; } diff --git a/fs/ntfs/index.h b/fs/ntfs/index.h index cad78568d8b35..9a03f53bba479 100644 --- a/fs/ntfs/index.h +++ b/fs/ntfs/index.h @@ -94,9 +94,11 @@ int ntfs_index_root_inconsistent(struct ntfs_volume *vol, const struct index_root *ir, u64 inum); int ntfs_index_block_inconsistent(struct ntfs_volume *vol, const struct index_block *ib, - u32 block_size, s64 vcn, u64 inum); -int ntfs_index_entry_inconsistent(struct ntfs_index_context *icx, struct ntfs_volume *vol, - const struct index_entry *ie, __le32 collation_rule, u64 inum); + u32 block_size, s64 vcn, + __le32 cr, u64 inum); +int ntfs_index_entries_inconsistent(const struct ntfs_volume *vol, + const struct index_header *ih, + __le32 collation_rule, u64 inum); struct ntfs_index_context *ntfs_index_ctx_get(struct ntfs_inode *ni, __le16 *name, u32 name_len); void ntfs_index_ctx_put(struct ntfs_index_context *ictx); diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index 8106593af6102..efb34a5e94d94 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -945,7 +945,9 @@ static int ntfs_read_locked_inode(struct inode *vi) } ir = (struct index_root *)((u8 *)a + le16_to_cpu(a->data.resident.value_offset)); - if (ntfs_index_root_inconsistent(ni->vol, a, ir, ni->mft_no)) { + if (ntfs_index_root_inconsistent(ni->vol, a, ir, ni->mft_no) || + ntfs_index_entries_inconsistent(ni->vol, &ir->index, + ir->collation_rule, ni->mft_no)) { ntfs_error(vi->i_sb, "Directory index is corrupt."); goto unm_err_out; } @@ -1538,7 +1540,9 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi) } ir = (struct index_root *)((u8 *)a + le16_to_cpu(a->data.resident.value_offset)); - if (ntfs_index_root_inconsistent(vol, a, ir, ni->mft_no)) { + if (ntfs_index_root_inconsistent(vol, a, ir, ni->mft_no) || + ntfs_index_entries_inconsistent(vol, &ir->index, + ir->collation_rule, ni->mft_no)) { ntfs_error(vi->i_sb, "Index is corrupt."); goto unm_err_out; } From 7fb64788812d137b37f6d8724e1e41c624c1e814 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Wed, 3 Jun 2026 17:41:09 +0000 Subject: [PATCH 2370/2758] ntfs: detect mapping-pairs LCN accumulator overflow commit ec4f061f2219e0f0c6465d56d0380bf749235a53 upstream. The NTFS mapping-pairs parser accumulates relative LCN deltas in a signed integer. A corrupted attribute can drive that addition past the representable range. One corrupt runlist shape sets the accumulated LCN to S64_MAX and then adds a delta of 1 in the next mapping-pairs entry. Signed overflow is undefined and can turn an invalid runlist into a different set of physical clusters. Check the LCN addition for overflow before storing the next run. Cc: stable@vger.kernel.org # v7.1 Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/runlist.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/ntfs/runlist.c b/fs/ntfs/runlist.c index 2d68101fbca26..15f1ae530ae13 100644 --- a/fs/ntfs/runlist.c +++ b/fs/ntfs/runlist.c @@ -860,7 +860,11 @@ struct runlist_element *ntfs_mapping_pairs_decompress(const struct ntfs_volume * for (deltaxcn = (s8)buf[b--]; b > b2; b--) deltaxcn = (deltaxcn << 8) + buf[b]; /* Change the current lcn to its new value. */ - lcn += deltaxcn; + if (unlikely(check_add_overflow(lcn, deltaxcn, &lcn))) { + ntfs_error(vol->sb, + "LCN overflow in mapping pairs array."); + goto err_out; + } #ifdef DEBUG /* * On NTFS 1.2-, apparently can have lcn == -1 to From 82510cb5c658b5bb424dbde9ad96b7a8b1bb332f Mon Sep 17 00:00:00 2001 From: DaeMyung Kang Date: Sat, 30 May 2026 23:35:10 +0900 Subject: [PATCH 2371/2758] ntfs: do not replace volume name after lookup errors commit 40d88020d0797f96a93edd2e8edc413c2e2d8f84 upstream. ntfs_write_volume_label() removes an existing $VOLUME_NAME attribute and then adds the replacement. The old code only distinguished lookup success from all other results, so any lookup error was treated like an absent label and the add path still ran. That is unsafe once lookup-time validation rejects corrupt $VOLUME_NAME records with -EIO: the corrupt record would remain in place and a second $VOLUME_NAME record could be appended next to it. Only add the replacement after the old label was removed successfully or after lookup returned -ENOENT. Propagate all other lookup errors, and also stop if removing the old attribute fails. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: DaeMyung Kang Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/super.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index f09bbf330f229..fc9e2724039b9 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -452,10 +452,15 @@ int ntfs_write_volume_label(struct ntfs_volume *vol, char *label) goto out; } - if (!ntfs_attr_lookup(AT_VOLUME_NAME, NULL, 0, 0, 0, NULL, 0, - ctx)) - ntfs_attr_record_rm(ctx); + ret = ntfs_attr_lookup(AT_VOLUME_NAME, NULL, 0, 0, 0, NULL, 0, + ctx); + if (!ret) + ret = ntfs_attr_record_rm(ctx); + else if (ret == -ENOENT) + ret = 0; ntfs_attr_put_search_ctx(ctx); + if (ret) + goto out; ret = ntfs_resident_attr_record_add(vol_ni, AT_VOLUME_NAME, AT_UNNAMED, 0, (u8 *)uname, uname_len * sizeof(__le16), 0); From 18fe978d265bf971757d3631eae3ea3d68b7b528 Mon Sep 17 00:00:00 2001 From: DaeMyung Kang Date: Sat, 30 May 2026 23:35:12 +0900 Subject: [PATCH 2372/2758] ntfs: validate resident volume name values on lookup commit b3f6cd1d54aa279cc4f47aa27939ebe517a2c390 upstream. The shared lookup-time attribute validator now has a safe caller path for $VOLUME_NAME corruption: ntfs_write_volume_label() no longer treats lookup errors as an absent label, and the mount path reinitializes its search context before continuing to $VOLUME_INFORMATION. Add $VOLUME_NAME-specific resident value validation. A volume name is stored as a UTF-16LE string, so reject odd byte lengths, and reject values longer than the NTFS volume label limit. Empty labels remain valid. Also reject non-resident $VOLUME_NAME records. $VOLUME_NAME is required to be resident, like $FILE_NAME; a crafted non-resident record would otherwise pass lookup and ntfs_write_volume_label() would remove it as if it were a normal resident attribute. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: DaeMyung Kang Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/attrib.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index 6ab8898d13f97..3e673cea17cef 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -607,6 +607,14 @@ static bool ntfs_file_name_attr_value_is_valid(const u8 *value, const u32 value_ value_length - offsetof(struct file_name_attr, file_name); } +static bool ntfs_volume_name_attr_value_is_valid(const u32 value_length) +{ + if (value_length & 1) + return false; + + return value_length <= NTFS_MAX_LABEL_LEN * sizeof(__le16); +} + struct ntfs_resident_attr_value { const u8 *data; u32 len; @@ -657,7 +665,7 @@ static bool ntfs_attr_value_is_valid(struct ntfs_volume *vol, u32 min_len; if (a->non_resident) { - if (a->type == AT_FILE_NAME) + if (a->type == AT_FILE_NAME || a->type == AT_VOLUME_NAME) goto corrupt; if (!ntfs_non_resident_attr_value_is_valid(a)) goto corrupt; @@ -676,6 +684,10 @@ static bool ntfs_attr_value_is_valid(struct ntfs_volume *vol, if (!ntfs_file_name_attr_value_is_valid(value.data, value.len)) goto corrupt; break; + case AT_VOLUME_NAME: + if (!ntfs_volume_name_attr_value_is_valid(value.len)) + goto corrupt; + break; } return true; From bfb01dd319b6b4c3e79756de7b75ccf0b9a0a247 Mon Sep 17 00:00:00 2001 From: DaeMyung Kang Date: Tue, 9 Jun 2026 00:49:17 +0900 Subject: [PATCH 2373/2758] ntfs: validate resident index root values on lookup commit fcf5bf0e8570798970e3ae8c95d04765ba2c5b97 upstream. Resident $INDEX_ROOT values carry index header fields that callers consume after lookup. Some callers already validate parts of the layout before walking entries, but those checks are scattered and do not cover all root header invariants, such as entries_offset alignment and lower bound, index_length, and allocated_size consistency. The resident root resize paths now keep these header fields consistent while the value size changes: ntfs_ir_truncate() lowers index.allocated_size before shrinking the resident value, and ntfs_ir_reparent() grows the resident value before publishing a larger root header. Lookup-time validation can therefore cover these invariants without tripping over the driver's own resize paths. Add $INDEX_ROOT to the minimum resident value size table and validate the resident index header fields before returning the attribute from lookup. Require 8-byte aligned index header fields, a sane entries_offset, an index_length within allocated_size, allocated_size within the resident value, and enough entry space for at least an index entry header. The shared validator already rejects non-resident records for resident-only attribute types, including $INDEX_ROOT. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: DaeMyung Kang Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/attrib.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index 3e673cea17cef..651acc3cf0519 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -588,6 +588,8 @@ static u32 ntfs_resident_attr_min_value_length(const __le32 type) sizeof(__le16) * 1; case AT_VOLUME_INFORMATION: return sizeof(struct volume_information); + case AT_INDEX_ROOT: + return sizeof(struct index_root); case AT_EA_INFORMATION: return sizeof(struct ea_information); default: @@ -615,6 +617,31 @@ static bool ntfs_volume_name_attr_value_is_valid(const u32 value_length) return value_length <= NTFS_MAX_LABEL_LEN * sizeof(__le16); } +static bool ntfs_index_root_attr_value_is_valid(const u8 *value, const u32 value_length) +{ + const struct index_root *ir; + u32 index_size; + u32 entries_offset; + u32 index_length; + u32 allocated_size; + + ir = (const struct index_root *)value; + index_size = value_length - offsetof(struct index_root, index); + entries_offset = le32_to_cpu(ir->index.entries_offset); + index_length = le32_to_cpu(ir->index.index_length); + allocated_size = le32_to_cpu(ir->index.allocated_size); + + if ((entries_offset | index_length | allocated_size) & 7 || + entries_offset < sizeof(struct index_header) || + entries_offset > index_length || + index_length > allocated_size || + allocated_size > index_size || + index_length - entries_offset < sizeof(struct index_entry_header)) + return false; + + return true; +} + struct ntfs_resident_attr_value { const u8 *data; u32 len; @@ -688,6 +715,10 @@ static bool ntfs_attr_value_is_valid(struct ntfs_volume *vol, if (!ntfs_volume_name_attr_value_is_valid(value.len)) goto corrupt; break; + case AT_INDEX_ROOT: + if (!ntfs_index_root_attr_value_is_valid(value.data, value.len)) + goto corrupt; + break; } return true; From b54c9beb90e570bae17a9c18442aeeaf17165ccb Mon Sep 17 00:00:00 2001 From: DaeMyung Kang Date: Tue, 9 Jun 2026 00:49:14 +0900 Subject: [PATCH 2374/2758] ntfs: reject non-resident records for resident-only attributes commit 097cdfd0a55df5af82c9753833f39a8bfadbcfcb upstream. The shared lookup-time attribute validator rejects non-resident $FILE_NAME and $VOLUME_NAME records because their formats require resident values and callers handle returned records as resident attributes. Other resident-only attribute types still pass through the generic non-resident mapping-pairs checks. That leaves real resident/non-resident union confusion paths. Inode load looks up $STANDARD_INFORMATION and then reads data.resident.value_offset without checking a->non_resident. ntfs_inode_sync_standard_information() does the same when updating the standard information value. ntfs_write_volume_flags() also looks up $VOLUME_INFORMATION and reads data.resident.value_offset directly. $INDEX_ROOT callers in dir.c and index.c depend on the same lookup contract before consuming the resident index root value. Reject non-resident records for all resident-only attribute types in the shared validator. Keep the existing $FILE_NAME and $VOLUME_NAME behavior, but factor it through a helper and extend it to $STANDARD_INFORMATION, $OBJECT_ID, $VOLUME_INFORMATION, $INDEX_ROOT, and $EA_INFORMATION. For $OBJECT_ID and $EA_INFORMATION this is contract hardening for resident-only formats; this patch only rejects the non-resident form and does not add new resident value validation for those types. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: DaeMyung Kang Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/attrib.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index 651acc3cf0519..a10e0e3f764d4 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -597,6 +597,22 @@ static u32 ntfs_resident_attr_min_value_length(const __le32 type) } } +static bool ntfs_attr_type_is_resident_only(const __le32 type) +{ + switch (type) { + case AT_STANDARD_INFORMATION: + case AT_FILE_NAME: + case AT_OBJECT_ID: + case AT_VOLUME_NAME: + case AT_VOLUME_INFORMATION: + case AT_INDEX_ROOT: + case AT_EA_INFORMATION: + return true; + default: + return false; + } +} + static bool ntfs_file_name_attr_value_is_valid(const u8 *value, const u32 value_length) { const struct file_name_attr *fn; @@ -692,7 +708,7 @@ static bool ntfs_attr_value_is_valid(struct ntfs_volume *vol, u32 min_len; if (a->non_resident) { - if (a->type == AT_FILE_NAME || a->type == AT_VOLUME_NAME) + if (ntfs_attr_type_is_resident_only(a->type)) goto corrupt; if (!ntfs_non_resident_attr_value_is_valid(a)) goto corrupt; From 0079773250213bb1ad2a49ee0b0a8eeaba9f1e2b Mon Sep 17 00:00:00 2001 From: Zhan Xusheng Date: Wed, 6 May 2026 15:55:54 +0800 Subject: [PATCH 2375/2758] fs/ntfs3: fix syncing wrong inode on DIRSYNC cross-directory rename commit 932fa0c1496286e39e14e27194c1ee7c2181629f upstream. In ntfs3_rename(), when IS_DIRSYNC(new_dir) is true, the code syncs the renamed file inode instead of the target directory new_dir: if (IS_DIRSYNC(new_dir)) ntfs_sync_inode(inode); /* should be new_dir */ DIRSYNC requires that directory metadata changes are written to disk synchronously. Since new_dir was modified (a new directory entry was added), it is new_dir that must be synced to satisfy the guarantee, not the renamed file itself. This bug has existed since the initial ntfs3 implementation and was carried through the refactoring in commit 78ab59fee07f ("fs/ntfs3: Rework file operations"). Fix by syncing new_dir instead of inode. Fixes: 4342306f0f0d ("fs/ntfs3: Add file operations and implementation") Cc: stable@vger.kernel.org Signed-off-by: Zhan Xusheng Signed-off-by: Konstantin Komarov Signed-off-by: Greg Kroah-Hartman --- fs/ntfs3/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ntfs3/namei.c b/fs/ntfs3/namei.c index b2af8f695e60f..64cde1a856f44 100644 --- a/fs/ntfs3/namei.c +++ b/fs/ntfs3/namei.c @@ -340,7 +340,7 @@ static int ntfs_rename(struct mnt_idmap *idmap, struct inode *dir, ntfs_sync_inode(dir); if (IS_DIRSYNC(new_dir)) - ntfs_sync_inode(inode); + ntfs_sync_inode(new_dir); } if (dir_ni != new_dir_ni) From 554700c65d398276cb00a8ef95f1d5e00b9eff93 Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Tue, 2 Jun 2026 15:21:03 +0200 Subject: [PATCH 2376/2758] fs/ntfs3: bound DeleteIndexEntryAllocation memmove length commit fc4626bb3656362de8b0ecd56605d47a19ec3518 upstream. In do_action()'s DeleteIndexEntryAllocation case, e->size comes from an on-disk INDEX_BUFFER entry. When e->size makes e + e->size point past hdr + hdr->used, PtrOffset(e1, Add2Ptr(hdr, used)) returns a negative ptrdiff_t that is silently cast to a quasi-infinite size_t when passed to memmove(). The memmove then walks past the destination buffer. The sibling DeleteIndexEntryRoot case at fslog.c:3540-3543 already carries the corresponding guard: if (PtrOffset(e1, Add2Ptr(hdr, used)) < esize || Add2Ptr(e, esize) > Add2Ptr(lrh, rec_len) || used + esize > le32_to_cpu(hdr->total)) { goto dirty_vol; } Apply the same shape to the allocation-path case. Also reject esize == 0: memmove(e, e, ...) is a no-op and leaves hdr->used unchanged, hiding a malformed entry from the existing check_index_header() walk. Reproduced under UML+KASAN on mainline 8d90b09e6741 by mounting a crafted NTFS image: the unguarded memmove takes a length of 0xffffffffffffff00 and the kernel oopses in memmove+0x81/0x1a0 on the do_action+0x36a2 frame. Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito [almaz.alexandrovich@paragon-software.com: clang-formatted the changes] Signed-off-by: Konstantin Komarov Signed-off-by: Greg Kroah-Hartman --- fs/ntfs3/fslog.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index a76d4c3cd3946..7938c840fafb8 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -3573,9 +3573,23 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, } e1 = Add2Ptr(e, esize); - nsize = esize; used = le32_to_cpu(hdr->used); + /* + * Reject crafted entries whose e->size makes e + esize + * point past the INDEX_HDR's used boundary. Without this, + * PtrOffset(e1, hdr + used) underflows to a quasi-infinite + * size_t when fed to the memmove() below. + * + * Also reject esize == 0: memmove(e, e, ...) is a no-op and + * leaves hdr->used unchanged, masking the crafted entry. + */ + if (!esize || Add2Ptr(e, esize) > Add2Ptr(hdr, used) || + PtrOffset(e1, Add2Ptr(hdr, used)) < esize) + goto dirty_vol; + + nsize = esize; + memmove(e, e1, PtrOffset(e1, Add2Ptr(hdr, used))); hdr->used = cpu_to_le32(used - nsize); From 49c86dae0c0ccb8d98ddcdc46987259389c816dd Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Fri, 15 May 2026 12:34:05 -0400 Subject: [PATCH 2377/2758] fs/ntfs3: bound copy_lcns dp->page_lcns[] index in analysis pass commit 5e7b598660cfa8e5af172cf4c65cffc126333307 upstream. In log_replay()'s analysis pass, after find_dp() returns a valid DIR_PAGE_ENTRY for the (target_attr, target_vcn) tuple, the copy_lcns block walks lrh->lcns_follow further entries: t16 = le16_to_cpu(lrh->lcns_follow); for (i = 0; i < t16; i++) { size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) - le64_to_cpu(dp->vcn)); dp->page_lcns[j + i] = lrh->page_lcns[i]; } find_dp() only validates that target_vcn falls within [dp->vcn, dp->vcn + dp->lcns_follow), i.e., that the FIRST cluster is covered. The walk through the further entries is not bounded against dp->lcns_follow. For a malformed LRH where target_vcn = dp->vcn + dp->lcns_follow - 1 and lrh->lcns_follow > 1, the i > 0 writes overflow the dp's allocated page_lcns[] array. Add the missing j + lrh->lcns_follow <= dp->lcns_follow guard. Reproduced under UML+KASAN on mainline 8d90b09e6741 as a slab-out-of-bounds write of size 8 from log_replay+0x68d4 on the mount path. This is distinct from Pavitra Jha's 2026-05-02 patch ("fs/ntfs3: validate lcns_follow in log_replay conversion", <20260502154252.164586-1-jhapavitra98@gmail.com>) which addresses the separate version-0 dirty-page-table conversion path's memmove(&dp->vcn, ...) call. The two fixes are complementary; both should land. Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito [almaz.alexandrovich@paragon-software.com: clang-formatted the changes, fixed conflicts] Signed-off-by: Konstantin Komarov Signed-off-by: Greg Kroah-Hartman --- fs/ntfs3/fslog.c | 46 +++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index 7938c840fafb8..cfe52d055dd6e 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -3369,8 +3369,8 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, if (run_get_highest_vcn(le64_to_cpu(attr->nres.svcn), attr_run(attr), - le32_to_cpu(attr->size) - - le16_to_cpu(attr->nres.run_off), + le32_to_cpu(attr->size) - + le16_to_cpu(attr->nres.run_off), &t64)) { goto dirty_vol; } @@ -4579,22 +4579,34 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) * whole routine a loop, case Lcns do not fit below. */ t16 = le16_to_cpu(lrh->lcns_follow); - t32 = le32_to_cpu(dp->lcns_follow); - if (le64_to_cpu(lrh->target_vcn) < le64_to_cpu(dp->vcn)) { - err = -EINVAL; - goto out; - } - - for (i = 0; i < t16; i++) { - size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) - - le64_to_cpu(dp->vcn)); - if (j >= t32 || i >= t32 - j) { - err = -EINVAL; - goto out; - } - dp->page_lcns[j + i] = lrh->page_lcns[i]; - } + t32 = le32_to_cpu(dp->lcns_follow); + if (le64_to_cpu(lrh->target_vcn) < le64_to_cpu(dp->vcn)) { + err = -EINVAL; + goto out; + } + + /* + * find_dp() only validates that target_vcn is the first + * cluster covered by dp. The walk through lrh->lcns_follow + * further entries must stay within the allocated + * dp->page_lcns[] array, which is sized by dp->lcns_follow. + */ + if (le64_to_cpu(lrh->target_vcn) - le64_to_cpu(dp->vcn) + t16 > + le32_to_cpu(dp->lcns_follow)) { + err = -EINVAL; + log->set_dirty = true; + goto out; + } + for (i = 0; i < t16; i++) { + size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) - + le64_to_cpu(dp->vcn)); + if (j >= t32 || i >= t32 - j) { + err = -EINVAL; + goto out; + } + dp->page_lcns[j + i] = lrh->page_lcns[i]; + } goto next_log_record_analyze; case DeleteDirtyClusters: { From a89c66674283a0293c0f266dc57087a6114371a3 Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Tue, 2 Jun 2026 15:15:47 +0200 Subject: [PATCH 2378/2758] fs/ntfs3: bound attr_off in UpdateResidentValue against data_off commit d1570c48f49a693974d000251030370ee2e83539 upstream. In do_action()'s UpdateResidentValue case (fslog.c:3307), lrh->attr_off and lrh->redo_len come from the on-disk LRH. When they satisfy aoff + dlen < attr->res.data_off, the assignment attr->res.data_size = cpu_to_le32(aoff + dlen - data_off); underflows to ~4 GiB (e.g. 0xFFFFFFF9 when aoff=0x10, dlen=1, data_off=0x18). Subsequent code that reads attr->res.data_size to walk the resident attribute payload would then read up to 4 GiB past the 1024-byte MFT record allocation. The existing mi_enum_attr() defense in fs/ntfs3/record.c:287 catches the corrupted data_size on the next attribute walk and fails the mount, but only on the path that walks all attributes. A read site that picks an attribute by name and reads its data_size without re-validating is not covered. Validate aoff against data_off and asize at the source. Reproduced under UML+KASAN on mainline 8d90b09e6741 via pr_warn-only probe: with aoff=0x10 and data_off=0x18, the post-assignment data_size is 0xfffffff9 (mount then fails at -22 from mi_enum_attr). Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito [almaz.alexandrovich@paragon-software.com: clang-formatted the changes] Signed-off-by: Konstantin Komarov Signed-off-by: Greg Kroah-Hartman --- fs/ntfs3/fslog.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index cfe52d055dd6e..b31062f6e3cd8 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -3325,6 +3325,17 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, nsize = ALIGN(nsize, 8); data_off = le16_to_cpu(attr->res.data_off); + /* + * aoff comes from the on-disk lrh->attr_off. Forbid + * writes that begin below the resident attribute's + * data_off (which would overwrite the resident header), + * and forbid aoff + dlen < data_off, which would make + * the data_size assignment below underflow to ~4 GiB. + */ + if (aoff < data_off || aoff + dlen < data_off || + aoff + dlen > asize) + goto dirty_vol; + if (nsize < asize) { memmove(Add2Ptr(attr, aoff), data, dlen); data = NULL; // To skip below memmove(). From 32b9f8733feb241627fa5f564b1a99b5cae974c5 Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Mon, 1 Jun 2026 10:57:56 +0200 Subject: [PATCH 2379/2758] fs/ntfs3: validate lcns_follow in log_replay conversion commit 6a4c53a2e26a865565bd6a460961e8d6fcb32329 upstream. log_replay() converts DIR_PAGE_ENTRY_32 records into DIR_PAGE_ENTRY records when replaying version 0 restart tables. During this conversion, the memmove() length is derived directly from the on-disk lcns_follow field: memmove(&dp->vcn, &dp0->vcn_low, 2 * sizeof(u64) + le32_to_cpu(dp->lcns_follow) * sizeof(u64)); check_rstbl() validates restart table structure, but does not constrain per-entry lcns_follow values relative to the entry size. A malformed filesystem image can provide an oversized lcns_follow value, causing the conversion memmove() to access memory beyond the bounds of the allocated restart table buffer. The same field is later used to bound iteration over page_lcns[], so validating lcns_follow during conversion also prevents downstream out-of-bounds access from the same malformed metadata. Compute the maximum valid lcns_follow from the already-validated restart table entry size and reject entries that exceed this bound. Reuse the existing t16/t32 scratch variables already declared in log_replay() to avoid introducing new declarations. Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Cc: stable@vger.kernel.org Signed-off-by: Pavitra Jha [almaz.alexandrovich@paragon-software.com: fixed the conflicts] Signed-off-by: Konstantin Komarov Signed-off-by: Greg Kroah-Hartman --- fs/ntfs3/fslog.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index b31062f6e3cd8..906c9de8942c1 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -4262,13 +4262,26 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) if (rst->major_ver) goto end_conv_1; /* reduce tab pressure. */ + t16 = le16_to_cpu(dptbl->size); + if (t16 < sizeof(struct DIR_PAGE_ENTRY)) { + log->set_dirty = true; + goto out; + } + + t32 = (t16 - sizeof(struct DIR_PAGE_ENTRY)) / sizeof(u64); + dp = NULL; while ((dp = enum_rstbl(dptbl, dp))) { struct DIR_PAGE_ENTRY_32 *dp0 = (struct DIR_PAGE_ENTRY_32 *)dp; - // NOTE: Danger. Check for of boundary. + u32 lcns = le32_to_cpu(dp->lcns_follow); + + if (lcns > t32) { + log->set_dirty = true; + goto out; + } + memmove(&dp->vcn, &dp0->vcn_low, - 2 * sizeof(u64) + - le32_to_cpu(dp->lcns_follow) * sizeof(u64)); + 2 * sizeof(u64) + lcns * sizeof(u64)); } end_conv_1: From fdf50c788e0991e42a187ff75479a0df7fb752f1 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Mon, 13 Apr 2026 09:31:17 -0400 Subject: [PATCH 2380/2758] fs/ntfs3: add depth limit to indx_find_buffer to prevent stack overflow commit 1ebd684b8f627f75bc3e03f8b2ad8400fd1f02cd upstream. indx_find_buffer() recursively descends the B+ tree index with no depth limit. A crafted NTFS image with circular index node references causes unbounded recursion, overflowing the kernel stack and panicking the system. This is reachable by mounting a malicious NTFS filesystem (e.g. from a USB drive via desktop automount) and deleting a file whose index entry triggers the rebalancing fallback path in indx_delete_entry(). Add a depth parameter and bail out with -EINVAL when it reaches the fnd->nodes array bound, matching the constraint already enforced by fnd_push() in indx_find(). The related function indx_find() was previously patched for a similar infinite-loop issue (commit 1732053c8a6b), but indx_find_buffer() was missed. Fixes: 82cae269cfa9 ("fs/ntfs3: Add initialization of super block") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-6 Assisted-by: Codex:gpt-5-4 Signed-off-by: Michael Bommarito Signed-off-by: Konstantin Komarov Signed-off-by: Greg Kroah-Hartman --- fs/ntfs3/index.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c index 5344b29b0577f..dbf5ec00b2376 100644 --- a/fs/ntfs3/index.c +++ b/fs/ntfs3/index.c @@ -2022,13 +2022,21 @@ int indx_insert_entry(struct ntfs_index *indx, struct ntfs_inode *ni, static struct indx_node *indx_find_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, const struct INDEX_ROOT *root, - __le64 vbn, struct indx_node *n) + __le64 vbn, struct indx_node *n, + int depth) { int err; const struct NTFS_DE *e; struct indx_node *r; const struct INDEX_HDR *hdr = n ? &n->index->ihdr : &root->ihdr; + /* + * Limit recursion depth to prevent stack overflow from crafted + * images. Use the same bound as the fnd->nodes array (20). + */ + if (depth > ARRAY_SIZE(((struct ntfs_fnd *)NULL)->nodes)) + return ERR_PTR(-EINVAL); + /* Step 1: Scan one level. */ for (e = hdr_first_de(hdr);; e = hdr_next_de(hdr, e)) { if (!e) @@ -2049,7 +2057,8 @@ static struct indx_node *indx_find_buffer(struct ntfs_index *indx, if (err) return ERR_PTR(err); - r = indx_find_buffer(indx, ni, root, vbn, n); + r = indx_find_buffer(indx, ni, root, vbn, n, + depth + 1); if (r) return r; } @@ -2462,7 +2471,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, fnd_clear(fnd); - in = indx_find_buffer(indx, ni, root, sub_vbn, NULL); + in = indx_find_buffer(indx, ni, root, sub_vbn, NULL, 0); if (IS_ERR(in)) { err = PTR_ERR(in); goto out; From 36feda687afebae24c472202694448738809c411 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 19 May 2026 05:51:35 -0400 Subject: [PATCH 2381/2758] fs/ntfs3: bound NTFS_DE view.data_off in UpdateRecordData{Root,Allocation} commit 3e127829e57f5190f612412ece4541cb96d5ec7a upstream. In do_action()'s UpdateRecordDataRoot (fslog.c:3489) and UpdateRecordDataAllocation (fslog.c:3697) cases, the memmove destination is `Add2Ptr(e, le16_to_cpu(e->view.data_off))`, where e->view.data_off comes from an on-disk NTFS_DE inside an INDEX_ROOT or INDEX_BUFFER. Neither case validates view.data_off + dlen against e->size; the existing check_if_index_root / check_if_alloc_index helpers walk the entry chain and validate the entry's offset, but not its internal view fields. The neighbouring read sites (e.g., fs/ntfs3/index.c when iterating view entries) check view.data_off + view.data_size <= e->size. Apply the same bound at the two memmove sites. Reproduced under UML+KASAN on mainline 8d90b09e6741 via pr_warn-only probe instrumentation: with view.data_off forced to 0xFFFC, the memmove writes 32 bytes past the end of the NTFS_DE. This is similar in shape to Pavitra Jha's 2026-05-02 patch "fs/ntfs3: prevent oob in case UpdateRecordDataRoot" (<20260502105008.21827-1-jhapavitra98@gmail.com>) which proposes calling ntfs3_bad_de_range(); that helper does not exist in mainline. This patch uses inline checks. Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Cc: stable@vger.kernel.org Reported-by: Pavitra Jha Closes: https://lore.kernel.org/ntfs3/20260502105008.21827-1-jhapavitra98@gmail.com/ Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Signed-off-by: Konstantin Komarov Signed-off-by: Greg Kroah-Hartman --- fs/ntfs3/fslog.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index 906c9de8942c1..af110c1daf052 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -3511,6 +3511,18 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, e = Add2Ptr(attr, le16_to_cpu(lrh->attr_off)); + /* + * e->view.data_off and dlen come from the on-disk + * INDEX_ROOT entry / LRH. The neighbouring read sites + * (e.g. fs/ntfs3/index.c) check that + * view.data_off + view.data_size <= e->size; mirror that + * bound here so the memmove cannot reach past the entry. + */ + if (le16_to_cpu(e->view.data_off) > le16_to_cpu(e->size) || + le16_to_cpu(e->view.data_off) + dlen > + le16_to_cpu(e->size)) + goto dirty_vol; + memmove(Add2Ptr(e, le16_to_cpu(e->view.data_off)), data, dlen); mi->dirty = true; @@ -3717,6 +3729,12 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, goto dirty_vol; } + /* See UpdateRecordDataRoot for the rationale. */ + if (le16_to_cpu(e->view.data_off) > le16_to_cpu(e->size) || + le16_to_cpu(e->view.data_off) + dlen > + le16_to_cpu(e->size)) + goto dirty_vol; + memmove(Add2Ptr(e, le16_to_cpu(e->view.data_off)), data, dlen); a_dirty = true; From d313416280d41bea272f02a6034dfa88008692a0 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 17 May 2026 19:41:40 -0400 Subject: [PATCH 2382/2758] ntfs3: cap RESTART_TABLE free-chain walker at rt->used commit 9611f644302c07d21bc8af97e3e06a3d30064253 upstream. A crafted NTFS3 disk image triggers an in-kernel infinite loop at mount time, hanging the mounting thread and firing the soft-lockup watchdog within ~22s on multi-CPU hosts (panic with kernel.softlockup_panic=1). The bug is reachable from desktop USB auto-mount on distributions where udisks2 routes the NTFS signature to the in-tree ntfs3 driver (Arch family and an increasing fraction of Fedora / openSUSE / RHEL deployments); CAP_SYS_ADMIN-class manual mount elsewhere. check_rstbl()'s second walker iterates the free-entry singly-linked list headed by rt->first_free with no upper bound on iteration count: for (off = ff; off;) { if (off == RESTART_ENTRY_ALLOCATED) return false; off = le32_to_cpu(*(__le32 *)Add2Ptr(rt, off)); if (off > ts - sizeof(__le32)) return false; } The existing guards cover three exits: end-of-list (off == 0), the in-use marker (off == RESTART_ENTRY_ALLOCATED), and out-of-bounds (off > ts - sizeof(__le32)). None of the three prevents an in-bounds cycle. A crafted on-disk RESTART_TABLE whose free chain contains a self-loop or A->B->A cycle whose offsets satisfy: - in range [sizeof(struct RESTART_TABLE), ts - sizeof(__le32)] - (off - sizeof(struct RESTART_TABLE)) % rsize == 0 passes all existing guards and spins the mount-time thread forever. Reproduced in UML by hand-forging a 2 MB NTFS3 image whose journal RESTART_TABLE first_free = 0x18 and whose entry at offset 0x18 stores 0x18 as its next pointer; mount of the forged image with the in-tree ntfs3 driver never returns. Bound the walker by rt->used. Each entry on a legitimate free chain is unique, and the total slot count is ne = le16_to_cpu (rt->used). A traversal that visits more than ne slots is by construction malformed; reject it as a corrupt RESTART_TABLE. After this patch, mount of the forged image returns with -EINVAL and a log_replay failure message, and mkntfs-produced legitimate images mount cleanly (verified in the same UML harness). Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Cc: stable@vger.kernel.org Signed-off-by: Michael Bommarito Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Konstantin Komarov Signed-off-by: Greg Kroah-Hartman --- fs/ntfs3/fslog.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index af110c1daf052..7a75aeef5e5dc 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -764,8 +764,19 @@ static bool check_rstbl(const struct RESTART_TABLE *rt, size_t bytes) /* * Walk through the list headed by the first entry to make * sure none of the entries are currently being used. + * + * Bound traversal by ne (rt->used) to defeat a crafted on-disk + * cycle in the free chain. Each entry in a legitimate free + * list is unique, so a chain that visits more than ne slots + * is malformed. Without this guard, an attacker-controlled + * RESTART_TABLE with a self-loop or A->B->A cycle whose + * offsets satisfy the existing alignment + in-bounds guards + * spins forever at mount time. */ - for (off = ff; off;) { + for (off = ff, i = 0; off; i++) { + if (i > ne) + return false; + if (off == RESTART_ENTRY_ALLOCATED) return false; From d240f5f9d036b8180224954d9873f172b6be4dd8 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Fri, 17 Apr 2026 19:33:05 -0400 Subject: [PATCH 2383/2758] ntfs3: bound to_move in indx_insert_into_root before hdr_insert_head commit 9b6926ac9c970ae0b2c2fe6289b16e9aa10b6a67 upstream. indx_insert_into_root() promotes a full resident $INDEX_ROOT into $INDEX_ALLOCATION and copies all non-last resident root entries into a newly allocated INDEX_BUFFER via hdr_insert_head(). The source byte count 'to_move' is summed from the on-disk resident entry sizes and is independent of the destination buffer size, which comes from root->index_block_size (via indx->index_bits). A crafted NTFS image that keeps a valid, full resident root but shrinks root->index_block_size down to 512 after the root has been populated makes hdr_insert_head() memcpy attacker-controlled resident entry bytes past the end of the kmalloc(1u << indx->index_bits) allocation returned by indx_new(). For a 512-byte destination and a resident root whose non-last entries total 560 bytes, the memcpy overruns by 120 bytes and a following memmove extends the highest written offset to 136 bytes past the allocation. The overflow bytes are a direct copy of on-disk entries (via kmemdup), so they are fully attacker-controlled. The write is reachable from unprivileged open(O_CREAT) on a mounted crafted NTFS image: a single sufficiently long create in a directory whose resident root is already full forces root promotion and triggers the copy. This is a controlled out-of-bounds write of 120-136 bytes past a kmalloc(index_block_size) allocation, with attacker-controlled content. It is a bounded adjacent-heap corruption primitive; it is not an arbitrary-address write. Successful exploitation into a named victim object depends on the surrounding slab layout. Reject the copy at the sink. The destination's INDEX_HDR already reports hdr_total (the payload capacity of the new buffer) and hdr_used (the bytes already consumed by the terminal END entry installed by indx_new()); require that to_move fits in the remaining payload before calling hdr_insert_head(). On mismatch, fail with -EINVAL and mark the filesystem as having a detected on-disk inconsistency, which is the same behaviour as the surrounding validation in this function. Fixes: 82cae269cfa9 ("fs/ntfs3: Add initialization of super block") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Signed-off-by: Konstantin Komarov Signed-off-by: Greg Kroah-Hartman --- fs/ntfs3/index.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c index dbf5ec00b2376..5868964e129c1 100644 --- a/fs/ntfs3/index.c +++ b/fs/ntfs3/index.c @@ -1742,6 +1742,22 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, hdr_used = le32_to_cpu(hdr->used); hdr_total = le32_to_cpu(hdr->total); + /* + * The destination INDEX_BUFFER has 'hdr_total' bytes of payload + * available after the header, of which 'hdr_used' are already + * consumed by the single terminal END entry installed by + * indx_new(). A crafted image can present a resident root whose + * non-last entries (summing to 'to_move') exceed what fits in + * this buffer; copying them unchecked would overrun the + * kmalloc(1u << indx->index_bits) allocation backing the new + * buffer. Reject the copy in that case. + */ + if (to_move > hdr_total - hdr_used) { + err = -EINVAL; + ntfs_set_state(sbi, NTFS_DIRTY_ERROR); + goto out_put_n; + } + /* Copy root entries into new buffer. */ hdr_insert_head(hdr, re, to_move); From 1758a564b6ebe7f4a82f23c9851d1cae15549457 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Fri, 17 Apr 2026 18:57:12 -0400 Subject: [PATCH 2384/2758] ntfs3: validate split-point offset in indx_insert_into_buffer commit f1df9d771df47aa40de6d70949c28720ae1e430d upstream. indx_insert_into_buffer() computes used = used1 - to_copy - sp_size; memmove(de_t, Add2Ptr(sp, sp_size), used - le32_to_cpu(hdr1->de_off)); where sp and sp_size come from hdr_find_split(). hdr_find_split() walks entries by le16_to_cpu(e->size) without validating that each step stays within hdr->used or that the size field is at least sizeof(struct NTFS_DE). index_hdr_check(), the on-load gatekeeper, only validates header-level fields (used, total, de_off) and does not walk per-entry sizes. A crafted NTFS image whose leaf INDEX_HDR reports used == total but contains one interior NTFS_DE with size = 0xFFF0 therefore passes validation, descends to indx_insert_into_buffer() through the ntfs_create() -> indx_insert_entry() path, and makes hdr_find_split() return an sp whose sp_size (0xFFF0) greatly exceeds the remaining bytes in the buffer. The u32 subtraction underflows and the memmove count becomes a near-4-GiB value, producing an out-of-bounds kernel write that corrupts adjacent allocations and panics the kernel. Reproduced on 7.0.0-rc7 with UML + KASAN via a crafted image and a single 'touch' inside the mounted directory; crash site resolves to fs/ntfs3/index.c at the memmove. Trigger requires only local mount of an attacker-supplied filesystem image (USB, loopback, or removable media auto-mount). Reject the split whenever the chosen sp plus its declared size already extends past hdr1->used. This is the minimal fix; it preserves the existing hdr_find_split() contract and relies on the same out: cleanup path as the pre-existing error returns. A prior OOB read in the very same indx_insert_into_buffer() memmove was fixed in commit b8c44949044e ("fs/ntfs3: Fix OOB read in indx_insert_into_buffer") by tightening hdr_find_e(), but that fix does not cover the split-point size field path addressed here: sp is returned by hdr_find_split(), not hdr_find_e(), and the underflow is driven by sp->size rather than hdr->used exceeding hdr->total. Fixes: 82cae269cfa9 ("fs/ntfs3: Add initialization of super block") Cc: stable@vger.kernel.org Reported-by: Michael Bommarito Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Signed-off-by: Konstantin Komarov Signed-off-by: Greg Kroah-Hartman --- fs/ntfs3/index.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c index 5868964e129c1..ade2762259995 100644 --- a/fs/ntfs3/index.c +++ b/fs/ntfs3/index.c @@ -1862,6 +1862,20 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, memcpy(up_e, sp, sp_size); used1 = le32_to_cpu(hdr1->used); + + /* + * hdr_find_split does not validate per-entry sizes, so a crafted + * NTFS_DE whose le16 size field is out of range can place sp such + * that (PtrOffset(hdr1, sp) + sp_size) exceeds used1. Without this + * guard the u32 'used = used1 - to_copy - sp_size' underflows and + * the subsequent memmove count becomes a near-4-GiB value, + * triggering an out-of-bounds kernel write. + */ + if (PtrOffset(hdr1, sp) + sp_size > used1) { + err = -EINVAL; + goto out; + } + hdr1_saved = kmemdup(hdr1, used1, GFP_NOFS); if (!hdr1_saved) { err = -ENOMEM; From a93980141253c932aa6ae5d4422d90e0162dc774 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Sat, 18 Apr 2026 13:11:18 +0000 Subject: [PATCH 2385/2758] ntfs3: fix out-of-bounds read in decompress_lznt commit 7160a57192fb16d7a6fa9b7f5c7ac341d2444a89 upstream. decompress_lznt() does not validate array index bounds before accessing the decompression table. A corrupted NTFS3 image with invalid compressed data can trigger an out-of-bounds read. Add index bounds checking to prevent the OOB access. Reported-by: syzbot+39b2fb0f2638669008ec@syzkaller.appspotmail.com Cc: stable@vger.kernel.org Signed-off-by: Tristan Madani Signed-off-by: Konstantin Komarov Signed-off-by: Greg Kroah-Hartman --- fs/ntfs3/lznt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ntfs3/lznt.c b/fs/ntfs3/lznt.c index fdc9b2ebf3410..f818d97850049 100644 --- a/fs/ntfs3/lznt.c +++ b/fs/ntfs3/lznt.c @@ -240,7 +240,7 @@ static inline ssize_t decompress_chunk(u8 *unc, u8 *unc_end, const u8 *cmpr, if (up - unc > LZNT_CHUNK_SIZE) return -EINVAL; /* Correct index */ - while (unc + s_max_off[index] < up) + while (index < ARRAY_SIZE(s_max_off) - 1 && unc + s_max_off[index] < up) index += 1; /* Check the current flag for zero. */ From b3d09502b80dfe0bab9090ca532710da389f6c7f Mon Sep 17 00:00:00 2001 From: Peiyang He Date: Tue, 30 Jun 2026 11:08:56 +0800 Subject: [PATCH 2386/2758] ntfs: fix mrec_lock ABBA deadlock in rename commit eb94f5a41a193a425e09a63cb75dffd151d8f42e upstream. ntfs_file_fsync(), ntfs_dir_fsync() and __ntfs_write_inode() lock an inode's mrec_lock before taking the mrec_lock of its parent directory. ntfs_rename() takes old_ni->mrec_lock and old_dir_ni->mrec_lock before taking new_ni->mrec_lock for an existing target, or new_dir_ni->mrec_lock for a cross-directory rename. This can deadlock when ntfs_file_fsync() or __ntfs_write_inode() holds the target inode, or when ntfs_dir_fsync() holds a child target directory, while rename() holds the parent directory and waits for the target. Fix this by locking the existing target inode before taking any parent directory mrec_lock. For cross-directory renames where the target parent is a descendant of the source parent, lock the target parent before the source parent so the directory order matches the child-to-parent order used by ntfs_file_fsync(), ntfs_dir_fsync(), and __ntfs_write_inode(). Reported-by: Peiyang He Closes: https://lore.kernel.org/all/C4D296F0E9F3D66C+9397ffbc-eb55-44bb-9b3f-5da4809e7955@smail.nju.edu.cn/ Fixes: af0db57d4293 ("ntfs: update inode operations") Cc: stable@vger.kernel.org Signed-off-by: Peiyang He Assisted-by: Codex:gpt-5.5 Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/namei.c | 62 ++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c index 9c1c36acfad24..a20ef06087368 100644 --- a/fs/ntfs/namei.c +++ b/fs/ntfs/namei.c @@ -1264,6 +1264,7 @@ static int ntfs_rename(struct mnt_idmap *idmap, struct inode *old_dir, struct ntfs_volume *vol = NTFS_SB(sb); struct ntfs_inode *old_ni, *new_ni = NULL; struct ntfs_inode *old_dir_ni = NTFS_I(old_dir), *new_dir_ni = NTFS_I(new_dir); + bool new_dir_first = false; if (NVolShutdown(old_dir_ni->vol)) return -EIO; @@ -1299,36 +1300,39 @@ static int ntfs_rename(struct mnt_idmap *idmap, struct inode *old_dir, old_inode = old_dentry->d_inode; new_inode = new_dentry->d_inode; old_ni = NTFS_I(old_inode); + if (new_inode) + new_ni = NTFS_I(new_inode); + if (old_dir != new_dir) + new_dir_first = is_subdir(new_dentry->d_parent, + old_dentry->d_parent); if (!(vol->vol_flags & VOLUME_IS_DIRTY)) ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY); mutex_lock_nested(&old_ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL); - mutex_lock_nested(&old_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT); + if (new_ni) + mutex_lock_nested(&new_ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL_2); + + if (old_dir == new_dir) { + mutex_lock_nested(&old_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT); + } else if (new_dir_first) { + mutex_lock_nested(&new_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT); + mutex_lock_nested(&old_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT_2); + } else { + mutex_lock_nested(&old_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT); + mutex_lock_nested(&new_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT_2); + } - if (NInoBeingDeleted(old_ni) || NInoBeingDeleted(old_dir_ni)) { + if (NInoBeingDeleted(old_ni) || NInoBeingDeleted(old_dir_ni) || + (new_ni && NInoBeingDeleted(new_ni)) || + (old_dir != new_dir && NInoBeingDeleted(new_dir_ni))) { err = -ENOENT; - goto unlock_old; + goto err_out; } is_dir = S_ISDIR(old_inode->i_mode); if (new_inode) { - new_ni = NTFS_I(new_inode); - mutex_lock_nested(&new_ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL_2); - if (old_dir != new_dir) { - mutex_lock_nested(&new_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT_2); - if (NInoBeingDeleted(new_dir_ni)) { - err = -ENOENT; - goto err_out; - } - } - - if (NInoBeingDeleted(new_ni)) { - err = -ENOENT; - goto err_out; - } - if (is_dir) { struct mft_record *ni_mrec; @@ -1346,14 +1350,6 @@ static int ntfs_rename(struct mnt_idmap *idmap, struct inode *old_dir, err = ntfs_delete(new_ni, new_dir_ni, uname_new, new_name_len, false); if (err) goto err_out; - } else { - if (old_dir != new_dir) { - mutex_lock_nested(&new_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT_2); - if (NInoBeingDeleted(new_dir_ni)) { - err = -ENOENT; - goto err_out; - } - } } err = __ntfs_link(old_ni, new_dir_ni, uname_new, new_name_len); @@ -1384,13 +1380,17 @@ static int ntfs_rename(struct mnt_idmap *idmap, struct inode *old_dir, inode_inc_iversion(new_dir); err_out: - if (old_dir != new_dir) + if (old_dir == new_dir) { + mutex_unlock(&old_dir_ni->mrec_lock); + } else if (new_dir_first) { + mutex_unlock(&old_dir_ni->mrec_lock); mutex_unlock(&new_dir_ni->mrec_lock); - if (new_inode) + } else { + mutex_unlock(&new_dir_ni->mrec_lock); + mutex_unlock(&old_dir_ni->mrec_lock); + } + if (new_ni) mutex_unlock(&new_ni->mrec_lock); - -unlock_old: - mutex_unlock(&old_dir_ni->mrec_lock); mutex_unlock(&old_ni->mrec_lock); if (uname_new) kmem_cache_free(ntfs_name_cache, uname_new); From d5379035355c0dcb1e92a2544d40f48441e1d637 Mon Sep 17 00:00:00 2001 From: Peiyang He Date: Mon, 6 Jul 2026 12:00:15 +0800 Subject: [PATCH 2387/2758] ntfs: fail attrlist updates when the superblock is inactive commit 0ebe8f625ab0520217a425d7cd366e4670484941 upstream. generic_shutdown_super() clears SB_ACTIVE before evicting cached inodes. If eviction selects the fake inode for a base inode's unnamed $ATTRIBUTE_LIST attribute, ntfs_evict_big_inode() drops the fake inode's reference on the base inode while the fake inode is still hashed and marked I_FREEING. That iput can synchronously write back the base inode. The writeback path may update mapping pairs and call ntfs_attrlist_update(), which unconditionally calls ntfs_attr_iget() for the same $ATTRIBUTE_LIST fake inode. VFS then finds the I_FREEING inode and waits for eviction to finish, but the current task is still inside that eviction path, causing a self-deadlock in find_inode(). Fix this by mirroring the teardown guard used by __ntfs_write_inode(): once SB_ACTIVE has been cleared, do not try to iget the attribute-list fake inode. Return -EIO so teardown aborts the update instead of waiting on the inode it is evicting. Reported-by: Peiyang He Closes: https://lore.kernel.org/all/AB8D5E603E6EA856+ae5f622a-dd3a-4e38-bdd2-42276ae0e1a8@smail.nju.edu.cn/ Fixes: 495e90fa3348 ("ntfs: update attrib operations") Cc: stable@vger.kernel.org Signed-off-by: Peiyang He Assisted-by: Codex:gpt-5.5 Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/attrlist.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/ntfs/attrlist.c b/fs/ntfs/attrlist.c index afb13038ba425..be3086d343381 100644 --- a/fs/ntfs/attrlist.c +++ b/fs/ntfs/attrlist.c @@ -57,6 +57,15 @@ int ntfs_attrlist_update(struct ntfs_inode *base_ni) struct ntfs_inode *attr_ni; int err; + /* + * generic_shutdown_super() clears SB_ACTIVE before evicting cached + * inodes. Do not look up the attribute-list inode after SB_ACTIVE has + * been cleared; it may already be I_FREEING, and waiting on it can + * self-deadlock. + */ + if (!(VFS_I(base_ni)->i_sb->s_flags & SB_ACTIVE)) + return -EIO; + attr_vi = ntfs_attr_iget(VFS_I(base_ni), AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); if (IS_ERR(attr_vi)) { err = PTR_ERR(attr_vi); From 83f396d881c4fd312c7fd5fff2c157fc21104464 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Mon, 6 Jul 2026 12:00:00 +0900 Subject: [PATCH 2388/2758] ntfs: sanitize MFT references returned from ntfs_lookup_inode_by_name() commit d97a36bae86a9a4021562ded2987f904e6bcb1d7 upstream. ntfs_lookup_inode_by_name() returns MFT references read from directory index entries on disk. These values are untrusted, but the function can currently return an error-marked MFT reference to its callers without validating it. Callers later decode lookup failures with MREF_ERR(). A crafted NTFS image can set the MREF error bit while leaving the low bits as an arbitrary value, causing callers to consume a bogus pseudo-errno instead of treating the lookup result as corrupted on-disk metadata. Fix this at the source by normalizing every error-marked MFT reference returned from ntfs_lookup_inode_by_name() to ERR_MREF(-EIO). Apply this to all four directory lookup return paths so every caller gets a validated result without needing additional checks or an API change. This keeps the sanitization in the common lookup helper, which is cleaner than duplicating validation in each caller. Fixes: 1e9ea7e04472 ("Revert "fs: Remove NTFS classic"") Cc: stable@vger.kernel.org Reported-by: Hongling Zeng Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/dir.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/fs/ntfs/dir.c b/fs/ntfs/dir.c index 4b6bd5f30c65e..6fa9ae3377cb8 100644 --- a/fs/ntfs/dir.c +++ b/fs/ntfs/dir.c @@ -23,6 +23,13 @@ __le16 I30[5] = { cpu_to_le16('$'), cpu_to_le16('I'), cpu_to_le16('3'), cpu_to_le16('0'), 0 }; +static inline u64 ntfs_check_mref(u64 mref) +{ + if (IS_ERR_MREF(mref)) + return ERR_MREF(-EIO); + return mref; +} + /* * ntfs_lookup_inode_by_name - find an inode in a directory given its name * @dir_ni: ntfs inode of the directory in which to search for the name @@ -178,7 +185,7 @@ u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, const __le16 *uname, mref = le64_to_cpu(ie->data.dir.indexed_file); ntfs_attr_put_search_ctx(ctx); unmap_mft_record(dir_ni); - return mref; + return ntfs_check_mref(mref); } /* * For a case insensitive mount, we also perform a case @@ -273,7 +280,7 @@ u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, const __le16 *uname, if (name) { ntfs_attr_put_search_ctx(ctx); unmap_mft_record(dir_ni); - return name->mref; + return ntfs_check_mref(name->mref); } ntfs_debug("Entry not found."); err = -ENOENT; @@ -413,7 +420,7 @@ u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, const __le16 *uname, mref = le64_to_cpu(ie->data.dir.indexed_file); kfree(kaddr); iput(ia_vi); - return mref; + return ntfs_check_mref(mref); } /* * For a case insensitive mount, we also perform a case @@ -538,7 +545,7 @@ u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, const __le16 *uname, if (name) { kfree(kaddr); iput(ia_vi); - return name->mref; + return ntfs_check_mref(name->mref); } ntfs_debug("Entry not found."); err = -ENOENT; From 5a5f877c5df7605e9bae524a25ec0df9b9cb8ea8 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Thu, 2 Jul 2026 14:28:16 +0900 Subject: [PATCH 2389/2758] ntfs: avoid self-deadlock during inode eviction commit 77dc384207d5fa63ba97c3bf3285fe1215a1cbf6 upstream. An attribute-list update performed while allocating clusters can drop the last reference to the temporary attribute inode. Evicting that inode drops its reference to the base inode and can invoke ntfs_drop_big_inode() for the base inode from within the base inode's own writeback path. If the base inode is unlinked, ntfs_drop_big_inode() calls truncate_setsize(), which waits for the inode's folio writeback to complete. The same writeback worker is responsible for completing that writeback, so it waits for itself indefinitely. Prevent this self-deadlock by grabbing a reference to the base inode at the beginning of ntfs_writepages() and releasing it at the end of the function. This defers eviction until all bios have been submitted, allowing the wait for folio writeback to complete safely. Fixes: b041ca562526 ("ntfs: update iomap and address space operations") Cc: stable@vger.kernel.org Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/aops.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c index 1fbf832ad1654..88e5b2def66ca 100644 --- a/fs/ntfs/aops.c +++ b/fs/ntfs/aops.c @@ -251,6 +251,8 @@ static int ntfs_writepages(struct address_space *mapping, .wbc = wbc, .ops = &ntfs_writeback_ops, }; + bool need_iput = false; + int ret; if (NVolShutdown(ni->vol)) return -EIO; @@ -267,7 +269,20 @@ static int ntfs_writepages(struct address_space *mapping, return -EOPNOTSUPP; } - return iomap_writepages(&wpc); + /* + * Prevent eviction in writeback to avoid deadlock in + * ntfs_drop_big_inode(). + */ + if ((ni->type == AT_DATA || ni->type == AT_INDEX_ALLOCATION) && + igrab(inode)) + need_iput = true; + + ret = iomap_writepages(&wpc); + + if (need_iput) + iput(inode); + + return ret; } static int ntfs_swap_activate(struct swap_info_struct *sis, From 8f313e92522ac41d273ea137db13ea8a8df2beed Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 2 Jul 2026 20:36:59 +0900 Subject: [PATCH 2390/2758] ntfs: make system files immutable to prevent corruption commit f72df3a4c33b64de3418ec74d1ad4f028e09d161 upstream. When a system file such as $Bitmap is exposed via show_sys_files and written from userspace, the volume is corrupted and, because the cluster allocator scans $Bitmap through the same inode's page cache, a write to $Bitmap also deadlocks writeback against the folio it already holds locked. These files are maintained by the driver itself and have no valid reason to be written through the file interface. Mark base metadata files (mft_no < FILE_first_user) as immutable during inode read so the VFS rejects write, mmap, truncate and unlink with -EPERM. Directories are skipped so the root and $Extend remain usable. Internal metadata updates do not go through the VFS write path and are unaffected. Fixes: af0db57d4293 ("ntfs: update inode operations") Cc: stable@vger.kernel.org Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/inode.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index efb34a5e94d94..f577f7abed544 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -1183,6 +1183,15 @@ static int ntfs_read_locked_inode(struct inode *vi) !S_ISFIFO(vi->i_mode) && !S_ISSOCK(vi->i_mode) && !S_ISLNK(vi->i_mode)) vi->i_flags |= S_IMMUTABLE; + /* + * System files such as $Bitmap and $MFT are maintained by the driver + * itself, and writing them from userspace corrupts the volume. + * Always make them immutable regardless of the sys_immutable option. + * Directories are skipped so the root and $Extend stay usable. + */ + if (ni->mft_no < FILE_first_user && S_ISREG(vi->i_mode)) + vi->i_flags |= S_IMMUTABLE; + /* * The number of 512-byte blocks used on disk (for stat). This is in so * far inaccurate as it doesn't account for any named streams or other From b397b1238a217264bb02f963a1a1eadf71906375 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 2 Jul 2026 10:31:44 +0900 Subject: [PATCH 2391/2758] ntfs: fix WARN_ON for resident attribute in ntfs_map_runlist_nolock() commit b8d6c528e9d57d263fee1a648409f84a68b2561d upstream. When ntfs_map_runlist_nolock() needs to look up the attribute extent containing a target VCN (ctx_needs_reset == true), it calls ntfs_attr_lookup() and then expects the result to be a non-resident attribute, since only non-resident attributes have a mapping pairs array to decompress. A crafted NTFS image can place a resident attribute where a non-resident one is expected, causing ntfs_attr_lookup() to succeed but return a resident attribute record. Previously this was caught only by a WARN_ON(), which does not stop execution. The code then falls through to read a->data.non_resident.highest_vcn from what is actually a resident attribute, accessing the wrong union member and corrupting the VCN range check. The caller path triggering this warning during mount is: ntfs_map_runlist_nolock ntfs_empty_logfile load_system_files ntfs_fill_super In this path ctx is NULL, so ntfs_map_runlist_nolock() allocates a temporary search context internally and sets ctx_needs_reset = true. The existing resident-attribute guard in the ctx != NULL branch already returns -EIO silently for the same condition; make the ctx_needs_reset path consistent by replacing the WARN_ON() with the same -EIO error return. This causes the crafted image to be rejected with a mount error instead of triggering a kernel warning. Fixes: 495e90fa3348 ("ntfs: update attrib operations") Cc: stable@vger.kernel.org Reported-by: Sangho Lee Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/attrib.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index a10e0e3f764d4..8a82dc0a23aba 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -174,7 +174,10 @@ int ntfs_map_runlist_nolock(struct ntfs_inode *ni, s64 vcn, struct ntfs_attr_sea err = -EIO; goto err_out; } - WARN_ON(!ctx->attr->non_resident); + if (unlikely(!ctx->attr->non_resident)) { + err = -EIO; + goto err_out; + } } a = ctx->attr; /* From 7d7f72cb21a829682f6d8968af4fbe413789d1fd Mon Sep 17 00:00:00 2001 From: Peiyang He Date: Sun, 5 Jul 2026 19:14:09 +0800 Subject: [PATCH 2392/2758] ntfs: fix hole runlist memory leak in insert range error path commit 06769b8f23b4b645b270c438649fff79768fb6fe upstream. ntfs_non_resident_attr_insert_range() allocates hole_rl before mapping the whole runlist. If ntfs_attr_map_whole_runlist() fails, the error path drops ni->runlist.lock and returns without freeing hole_rl. This leaks memory of sizeof(*hole_rl) * 2 bytes. Fix this memory leak by freeing hole_rl before returning from that error path, matching the later error paths in the same function. Fixes: 495e90fa3348 ("ntfs: update attrib operations") Cc: stable@vger.kernel.org Signed-off-by: Peiyang He Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/ntfs/attrib.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index 8a82dc0a23aba..0f1d0b54cfb52 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -5327,6 +5327,7 @@ int ntfs_non_resident_attr_insert_range(struct ntfs_inode *ni, s64 start_vcn, s6 ret = ntfs_attr_map_whole_runlist(ni); if (ret) { up_write(&ni->runlist.lock); + kfree(hole_rl); return ret; } From 04916f7dc6d37cd478b06c86398c34a6963ac8c9 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Thu, 4 Jun 2026 23:16:56 +0000 Subject: [PATCH 2393/2758] landlock: Fix LANDLOCK_SCOPE_SIGNAL bypass on the SIGIO path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 4b80320ca7ed03d6e683f95b6066565dc97b9f92 upstream. LANDLOCK_SCOPE_SIGNAL must prevent a sandboxed process from signaling processes outside its Landlock domain. It can be bypassed through the asynchronous SIGIO delivery path. A sandboxed process that owns any file or socket can arm it with fcntl(fd, F_SETOWN, -pgid), fcntl(fd, F_SETSIG, SIGKILL) and O_ASYNC, so that an I/O event makes the kernel deliver the chosen signal to the whole process group. As the head of its process group's task list (the default position right after fork()) that group can also hold the non-sandboxed process that launched it, e.g. a supervisor or a security monitor. The sandbox can thus kill or signal the processes LANDLOCK_SCOPE_SIGNAL is meant to protect from it. The scope is enforced in hook_file_send_sigiotask() against the Landlock domain recorded at F_SETOWN time, not the live domain of the sender. control_current_fowner() decides whether to record that domain and skips recording it when the fowner target is in the caller's thread group, which is safe only for a single-task target (PIDTYPE_PID, PIDTYPE_TGID). For a process group (PIDTYPE_PGID) pid_task() returns only one member; recording is skipped whenever that member shares the caller's thread group, and hook_file_send_sigiotask() then lets the signal fan out to the whole group unchecked. Record the domain for every non single-process target so the scope is enforced against each group member at delivery time. That recording is necessary but not sufficient on its own: the kernel signals a process group through its members' thread-group leaders, and the leader of the registrant's own process can carry a different Landlock domain than the sibling thread that armed the owner. domain_is_scoped() would then deny that leader, even though commit 18eb75f3af40 ("landlock: Always allow signals between threads of the same process") requires same-process delivery to be allowed. hook_task_kill() avoids this by evaluating same_thread_group() live, per recipient; the SIGIO path instead delegates the whole decision to a single registration-time check, which a process-group fan-out cannot honor. So also record the registrant's thread group next to its domain and exempt it at delivery: hook_file_send_sigiotask() allows the signal whenever the recipient belongs to the registrant's own process, restoring the same-process guarantee while keeping out-of-domain group members blocked. The direct kill() path (hook_task_kill) already evaluates the live domain and is unaffected. Fixes: 18eb75f3af40 ("landlock: Always allow signals between threads of the same process") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Reviewed-by: Günther Noack Link: https://patch.msgid.link/56bffc24f3d0d08b45a686a48e99766b0a0821fa.1780614610.git.hexlabsecurity@proton.me [mic: Check pid_type earlier and improve comment, fix commit message, fix comment formatting] Signed-off-by: Mickaël Salaün Signed-off-by: Greg Kroah-Hartman --- security/landlock/fs.c | 14 ++++++++++++++ security/landlock/fs.h | 10 ++++++++++ security/landlock/task.c | 11 +++++++++++ 3 files changed, 35 insertions(+) diff --git a/security/landlock/fs.c b/security/landlock/fs.c index c1ecfe2390326..664962a416d7a 100644 --- a/security/landlock/fs.c +++ b/security/landlock/fs.c @@ -1900,6 +1900,14 @@ static bool control_current_fowner(struct fown_struct *const fown) */ lockdep_assert_held(&fown->lock); + /* + * A process-group or session owner (PIDTYPE_PGID/PIDTYPE_SID) fans the + * signal out to every member at delivery time, so record the domain and + * let hook_file_send_sigiotask() check the live scope per recipient. + */ + if (fown->pid_type != PIDTYPE_PID && fown->pid_type != PIDTYPE_TGID) + return true; + /* * Some callers (e.g. fcntl_dirnotify) may not be in an RCU read-side * critical section. @@ -1916,6 +1924,7 @@ static void hook_file_set_fowner(struct file *file) { struct landlock_ruleset *prev_dom; struct landlock_cred_security fown_subject = {}; + struct pid *prev_tg, *fown_tg = NULL; size_t fown_layer = 0; if (control_current_fowner(file_f_owner(file))) { @@ -1928,21 +1937,26 @@ static void hook_file_set_fowner(struct file *file) if (new_subject) { landlock_get_ruleset(new_subject->domain); fown_subject = *new_subject; + fown_tg = get_pid(task_tgid(current)); } } prev_dom = landlock_file(file)->fown_subject.domain; + prev_tg = landlock_file(file)->fown_tg; landlock_file(file)->fown_subject = fown_subject; + landlock_file(file)->fown_tg = fown_tg; #ifdef CONFIG_AUDIT landlock_file(file)->fown_layer = fown_layer; #endif /* CONFIG_AUDIT*/ /* May be called in an RCU read-side critical section. */ landlock_put_ruleset_deferred(prev_dom); + put_pid(prev_tg); } static void hook_file_free_security(struct file *file) { + put_pid(landlock_file(file)->fown_tg); landlock_put_ruleset_deferred(landlock_file(file)->fown_subject.domain); } diff --git a/security/landlock/fs.h b/security/landlock/fs.h index bf9948941f2fb..911b83669e207 100644 --- a/security/landlock/fs.h +++ b/security/landlock/fs.h @@ -78,6 +78,16 @@ struct landlock_file_security { * euid. */ struct landlock_cred_security fown_subject; + /** + * @fown_tg: Thread group of the task that set the file owner, pinned + * while @fown_subject holds a domain. It lets + * hook_file_send_sigiotask() always allow a SIGIO delivered to the + * owner's own process -- e.g. the thread-group leader reached through a + * process-group owner -- matching the same-process exemption of + * hook_task_kill(). NULL when no domain is recorded. Protected by + * file->f_owner->lock, like @fown_subject. + */ + struct pid *fown_tg; }; #ifdef CONFIG_AUDIT diff --git a/security/landlock/task.c b/security/landlock/task.c index 6d46042132ce1..7ddf211f75c37 100644 --- a/security/landlock/task.c +++ b/security/landlock/task.c @@ -411,6 +411,17 @@ static int hook_file_send_sigiotask(struct task_struct *tsk, if (!subject->domain) return 0; + /* + * Always allow delivery to the file owner's own process, including a + * thread-group leader reached through a process-group owner. This + * mirrors hook_task_kill()'s same-process exemption and preserves the + * guarantee of commit 18eb75f3af40 ("landlock: Always allow signals + * between threads of the same process"), which the registration-time + * check cannot honor for a process-group target. + */ + if (task_tgid(tsk) == landlock_file(fown->file)->fown_tg) + return 0; + scoped_guard(rcu) { is_scoped = domain_is_scoped(subject->domain, From e8631b883338ea4c3b5d8ddfbe16407af9f0b4b9 Mon Sep 17 00:00:00 2001 From: WenTao Liang Date: Thu, 11 Jun 2026 08:53:21 +0800 Subject: [PATCH 2394/2758] power: supply: charger-manager: fix refcount leak in is_full_charged() commit 4373cfa38ead58f980362c841b0d0bdf8c4d956c upstream. In is_full_charged(), power_supply_get_by_name() is called to obtain a reference to the fuel_gauge power supply. If the voltage check (uV >= desc->fullbatt_uV) succeeds, the function returns true directly without releasing the reference, leaking the refcount. Fix this by setting a flag and jumping to the out label where power_supply_put() properly drops the reference. Cc: stable@vger.kernel.org Fixes: e132fc6bb89b ("power: supply: charger-manager: Make decisions focussed on battery status") Signed-off-by: WenTao Liang Link: https://patch.msgid.link/20260611005322.53096-1-vulab@iscas.ac.cn Signed-off-by: Sebastian Reichel Signed-off-by: Greg Kroah-Hartman --- drivers/power/supply/charger-manager.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index c49e0e4d02f77..c3644018b6bb4 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -303,8 +303,10 @@ static bool is_full_charged(struct charger_manager *cm) if (cm->battery_status == POWER_SUPPLY_STATUS_FULL && desc->fullbatt_vchkdrop_uV) uV += desc->fullbatt_vchkdrop_uV; - if (uV >= desc->fullbatt_uV) - return true; + if (uV >= desc->fullbatt_uV) { + is_full = true; + goto out; + } } } From 4d46e07b23d8e2a2e5ac61816a9fc0d9726b5a9d Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Thu, 4 Jun 2026 23:17:05 +0000 Subject: [PATCH 2395/2758] selftests/landlock: Test SCOPE_SIGNAL on the SIGIO/fowner pgid path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 76579d09beedaffe7fe76e9c05644f73983e1ceb upstream. Add regression tests for the LANDLOCK_SCOPE_SIGNAL handling of the asynchronous SIGIO delivery path (fcntl(F_SETOWN)) with a process-group owner. sigio_to_pgid_members covers the bypass: a sandboxed process at the head of its process group's PGID hlist (the default after fork()) arms F_SETOWN(-pgrp) + O_ASYNC and triggers the fan-out; the in-domain owner must be signaled (proving the trigger fired) while the non-sandboxed member of the group, outside the domain, must not. sigio_to_pgid_self covers the same-process guarantee: the owner is registered from a sandboxed non-leader thread, whose domain differs from the thread-group leader the kernel signals for a process-group owner. That leader belongs to the owner's own process and must still be signaled. Without the fix the first test sees the out-of-domain member signaled and the second sees the owner's own leader denied. Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Reviewed-by: Günther Noack Link: https://patch.msgid.link/43370e89f7a896a583bf33d1cd171d02630e61bf.1780614610.git.hexlabsecurity@proton.me [mic: Fix comment] Signed-off-by: Mickaël Salaün Signed-off-by: Greg Kroah-Hartman --- .../selftests/landlock/scoped_signal_test.c | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/tools/testing/selftests/landlock/scoped_signal_test.c b/tools/testing/selftests/landlock/scoped_signal_test.c index d8bf33417619f..f24f2c28f62e5 100644 --- a/tools/testing/selftests/landlock/scoped_signal_test.c +++ b/tools/testing/selftests/landlock/scoped_signal_test.c @@ -559,4 +559,186 @@ TEST_F(fown, sigurg_socket) _metadata->exit_code = KSFT_FAIL; } +/* + * Checks that LANDLOCK_SCOPE_SIGNAL is enforced on the asynchronous SIGIO + * delivery path (fcntl(F_SETOWN)) when the file owner is a process group. + * + * A sandboxed process sitting at the head of its process group's PID hlist (the + * default position right after fork()) used to escape the fcntl(F_SETOWN, + * -pgrp) domain recording: pid_task(pgrp, PIDTYPE_PGID) resolved to the process + * itself, so the same-thread-group exemption skipped recording its Landlock + * domain. At SIGIO time that domain was then unset and the signal fanned out + * to every group member, including non-sandboxed processes outside the domain. + */ +TEST(sigio_to_pgid_members) +{ + int trigger[2], sync_child[2]; + char buf; + pid_t child; + int status, i; + + drop_caps(_metadata); + + /* + * Isolates the test in its own process group so the SIGIO fan-out stays + * bounded to this parent and the child forked below. + */ + ASSERT_EQ(0, setpgid(0, 0)); + + /* The non-sandboxed parent is the protected (out-of-domain) target. */ + ASSERT_EQ(0, setup_signal_handler(SIGURG)); + signal_received = 0; + + ASSERT_EQ(0, pipe2(trigger, O_CLOEXEC)); + ASSERT_EQ(0, pipe2(sync_child, O_CLOEXEC)); + + child = fork(); + ASSERT_LE(0, child); + if (child == 0) { + /* + * The child inherits the parent's new process group and, just + * attached with hlist_add_head_rcu(), is now the head of the + * pgid hlist: this is the case that used to skip the recording. + */ + EXPECT_EQ(0, close(sync_child[0])); + + /* In-domain positive control: the child must be signaled. */ + ASSERT_EQ(0, setup_signal_handler(SIGURG)); + signal_received = 0; + + create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL); + + /* Owns the SIGIO source for the whole process group. */ + ASSERT_EQ(0, fcntl(trigger[0], F_SETSIG, SIGURG)); + ASSERT_EQ(0, fcntl(trigger[0], F_SETOWN, -getpgrp())); + ASSERT_EQ(0, fcntl(trigger[0], F_SETFL, O_ASYNC)); + + /* Fans SIGURG out to every member of the process group. */ + ASSERT_EQ(1, write(trigger[1], ".", 1)); + + /* + * The sandboxed child is in its own domain and must always be + * signaled: this proves the SIGIO actually fired. + */ + for (i = 0; i < 1000 && !signal_received; i++) + usleep(1000); + EXPECT_EQ(1, signal_received); + + ASSERT_EQ(1, write(sync_child[1], ".", 1)); + EXPECT_EQ(0, close(sync_child[1])); + + _exit(_metadata->exit_code); + return; + } + EXPECT_EQ(0, close(sync_child[1])); + EXPECT_EQ(0, close(trigger[0])); + EXPECT_EQ(0, close(trigger[1])); + + /* Waits for the child to generate the SIGIO. */ + ASSERT_EQ(1, read(sync_child[0], &buf, 1)); + EXPECT_EQ(0, close(sync_child[0])); + + /* Lets a delivered-but-pending signal run our handler, if any. */ + for (i = 0; i < 100 && !signal_received; i++) + usleep(1000); + + /* + * SCOPE_SIGNAL must block the fan-out to this non-sandboxed parent, + * which is outside the child's Landlock domain. Before the fix the + * parent was signaled here. + */ + EXPECT_EQ(0, signal_received); + + ASSERT_EQ(child, waitpid(child, &status, 0)); + if (WIFSIGNALED(status) || !WIFEXITED(status) || + WEXITSTATUS(status) != EXIT_SUCCESS) + _metadata->exit_code = KSFT_FAIL; +} + +static void *thread_setown_scoped(void *arg) +{ + const int fd = *(int *)arg; + int ruleset_fd; + const struct landlock_ruleset_attr ruleset_attr = { + .scoped = LANDLOCK_SCOPE_SIGNAL, + }; + + /* Sandboxes only this non-leader thread (no thread syncing). */ + ruleset_fd = + landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); + if (ruleset_fd < 0) + return (void *)THREAD_ERROR; + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) || + landlock_restrict_self(ruleset_fd, 0)) { + close(ruleset_fd); + return (void *)THREAD_ERROR; + } + close(ruleset_fd); + + /* Makes this process group own the SIGIO source. */ + if (fcntl(fd, F_SETSIG, SIGURG) || fcntl(fd, F_SETOWN, -getpgrp()) || + fcntl(fd, F_SETFL, O_ASYNC)) + return (void *)THREAD_ERROR; + + return (void *)THREAD_SUCCESS; +} + +/* + * Checks that the SIGIO fan-out is still delivered to the file owner's own + * process when fcntl(F_SETOWN, -pgrp) was issued from a sandboxed non-leader + * thread. + * + * The Landlock domain is recorded for a process-group owner (so out-of-domain + * members stay blocked, see sigio_to_pgid_members), but the kernel signals a + * process group through its members' thread-group leaders. Here the leader is + * not sandboxed and thus has a different domain than the registering thread, so + * the registration-time check cannot tell that it belongs to the owner's own + * process. hook_file_send_sigiotask() must recognize it through the recorded + * thread group and allow the delivery, matching the same-process guarantee of + * commit 18eb75f3af40. Without that exemption the leader is wrongly denied and + * never signaled. + */ +TEST(sigio_to_pgid_self) +{ + int trigger[2]; + pthread_t thread; + enum thread_return ret = THREAD_INVALID; + int i; + + drop_caps(_metadata); + + /* Bounds the SIGIO fan-out to this process. */ + ASSERT_EQ(0, setpgid(0, 0)); + + /* The non-sandboxed thread-group leader is the SIGIO target. */ + ASSERT_EQ(0, setup_signal_handler(SIGURG)); + signal_received = 0; + + ASSERT_EQ(0, pipe2(trigger, O_CLOEXEC)); + + /* + * Registers the process-group fowner from a sibling thread that + * sandboxes only itself, so its domain differs from the leader's. + */ + ASSERT_EQ(0, pthread_create(&thread, NULL, thread_setown_scoped, + &trigger[0])); + ASSERT_EQ(0, pthread_join(thread, (void **)&ret)); + ASSERT_EQ(THREAD_SUCCESS, ret); + + /* Fans SIGURG out to the process group. */ + ASSERT_EQ(1, write(trigger[1], ".", 1)); + + for (i = 0; i < 1000 && !signal_received; i++) + usleep(1000); + + /* + * Same-process delivery must always be allowed, even though the owner + * was registered from a sandboxed sibling thread. + */ + EXPECT_EQ(1, signal_received); + + EXPECT_EQ(0, close(trigger[0])); + EXPECT_EQ(0, close(trigger[1])); +} + TEST_HARNESS_MAIN From a1dd41d00c57efb1fbc6f361c5f48c9d00cca51c Mon Sep 17 00:00:00 2001 From: Aaron Tomlin Date: Tue, 26 May 2026 10:16:51 -0400 Subject: [PATCH 2396/2758] mips: sched: Fix CPUMASK_OFFSTACK memory corruption commit 98e37db4a34d3af3fb2f4648295c25b5e40b20e3 upstream. This patch addresses a critical memory management flaw. When CONFIG_CPUMASK_OFFSTACK is enabled, cpumask_var_t is a pointer. Consequently, sizeof(new_mask) evaluates to the pointer size, causing copy_from_user() to clobber the mask pointer. Furthermore, the old logic performed copy_from_user() before allocating the mask. Fix this by allocating new_mask first. To handle variable-sized user masks correctly, use cpumask_size() to truncate overly large user masks or pad undersized masks with zeros before copying the data directly into the allocated buffer. Fixes: 295cbf6d63165 ("[MIPS] Move FPU affinity code into separate file.") Cc: stable@vger.kernel.org Signed-off-by: Aaron Tomlin Signed-off-by: Thomas Bogendoerfer Signed-off-by: Greg Kroah-Hartman --- arch/mips/kernel/mips-mt-fpaff.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/arch/mips/kernel/mips-mt-fpaff.c b/arch/mips/kernel/mips-mt-fpaff.c index 10172fc4f627b..4fead87d2f433 100644 --- a/arch/mips/kernel/mips-mt-fpaff.c +++ b/arch/mips/kernel/mips-mt-fpaff.c @@ -71,11 +71,16 @@ asmlinkage long mipsmt_sys_sched_setaffinity(pid_t pid, unsigned int len, struct task_struct *p; int retval; - if (len < sizeof(new_mask)) - return -EINVAL; - - if (copy_from_user(&new_mask, user_mask_ptr, sizeof(new_mask))) - return -EFAULT; + if (!alloc_cpumask_var(&new_mask, GFP_KERNEL)) + return -ENOMEM; + if (len < cpumask_size()) + cpumask_clear(new_mask); + else if (len > cpumask_size()) + len = cpumask_size(); + if (copy_from_user(new_mask, user_mask_ptr, len)) { + retval = -EFAULT; + goto out_free_new_mask; + } cpus_read_lock(); rcu_read_lock(); @@ -84,7 +89,8 @@ asmlinkage long mipsmt_sys_sched_setaffinity(pid_t pid, unsigned int len, if (!p) { rcu_read_unlock(); cpus_read_unlock(); - return -ESRCH; + retval = -ESRCH; + goto out_free_new_mask; } /* Prevent p going away */ @@ -95,13 +101,9 @@ asmlinkage long mipsmt_sys_sched_setaffinity(pid_t pid, unsigned int len, retval = -ENOMEM; goto out_put_task; } - if (!alloc_cpumask_var(&new_mask, GFP_KERNEL)) { - retval = -ENOMEM; - goto out_free_cpus_allowed; - } if (!alloc_cpumask_var(&effective_mask, GFP_KERNEL)) { retval = -ENOMEM; - goto out_free_new_mask; + goto out_free_cpus_allowed; } if (!check_same_owner(p) && !capable(CAP_SYS_NICE)) { retval = -EPERM; @@ -142,13 +144,13 @@ asmlinkage long mipsmt_sys_sched_setaffinity(pid_t pid, unsigned int len, } out_unlock: free_cpumask_var(effective_mask); -out_free_new_mask: - free_cpumask_var(new_mask); out_free_cpus_allowed: free_cpumask_var(cpus_allowed); out_put_task: put_task_struct(p); cpus_read_unlock(); +out_free_new_mask: + free_cpumask_var(new_mask); return retval; } From 2611f7521c6c124b1bef1be4fc319e0ca144502f Mon Sep 17 00:00:00 2001 From: "Kiryl Shutsemau (Meta)" Date: Fri, 29 May 2026 18:23:28 +0100 Subject: [PATCH 2397/2758] mm/huge_memory: preserve pmd_swp_uffd_wp on device-private PMD downgrade commit f7e2c21bd1f57cd5350eecdfdb5d6025ca6afbab upstream. change_non_present_huge_pmd() rewrites a writable device-private PMD swap entry into a readable one without carrying pmd_swp_uffd_wp() across. The PTE-level change_softleaf_pte() does this correctly; mirror that here, matching what copy_huge_pmd() does for the fork path. Without the carry, a plain mprotect() over a UFFD_WP-marked device-private THP strips the bit and the trap is bypassed on swap-in. Link: https://lore.kernel.org/20260529172331.356655-5-kas@kernel.org Fixes: 368076f52ebe ("mm/huge_memory: add device-private THP support to PMD operations") Signed-off-by: Kiryl Shutsemau Reported-by: Sashiko AI review Reviewed-by: Balbir Singh Cc: David Hildenbrand Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Peter Xu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/huge_memory.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index d29e85495091d..3f0466729c75b 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -2638,6 +2638,8 @@ static void change_non_present_huge_pmd(struct mm_struct *mm, } else if (softleaf_is_device_private_write(entry)) { entry = make_readable_device_private_entry(swp_offset(entry)); newpmd = swp_entry_to_pmd(entry); + if (pmd_swp_uffd_wp(*pmd)) + newpmd = pmd_swp_mkuffd_wp(newpmd); } else { newpmd = *pmd; } From 36e4843fe39ea2f17f4de6d59fba26271916c184 Mon Sep 17 00:00:00 2001 From: Zishun Yi Date: Sat, 6 Jun 2026 20:17:58 -0600 Subject: [PATCH 2398/2758] riscv: cacheinfo: Fix node reference leak in populate_cache_leaves commit bf4a195f063b0a0805c1417f6aad1dd32ea48f0f upstream. Currently, the while loop drops the reference to prev in each iteration. If the loop terminates early due to a break, the final of_node_put(np) correctly drops the reference to the current node. However, if the loop terminates naturally because np == NULL, calling of_node_put(np) is a no-op. This leaves the last valid node stored in prev without its reference dropped, resulting in a node reference leak. Fix this by changing the final `of_node_put(np)` to `of_node_put(prev)`. Fixes: 94f9bf118f1e ("RISC-V: Fix of_node_* refcount") Cc: stable@vger.kernel.org Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Zishun Yi Link: https://patch.msgid.link/20260509074040.1747800-1-vulab@iscas.ac.cn Signed-off-by: Paul Walmsley Signed-off-by: Greg Kroah-Hartman --- arch/riscv/kernel/cacheinfo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/kernel/cacheinfo.c b/arch/riscv/kernel/cacheinfo.c index 26b085dbdd073..6c9a1ef2d45af 100644 --- a/arch/riscv/kernel/cacheinfo.c +++ b/arch/riscv/kernel/cacheinfo.c @@ -133,7 +133,7 @@ int populate_cache_leaves(unsigned int cpu) ci_leaf_init(this_leaf++, CACHE_TYPE_DATA, level); levels = level; } - of_node_put(np); + of_node_put(prev); return 0; } From 837f619f1d98e967bd63e51ecd1e77bfa468992d Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Thu, 4 Jun 2026 18:38:48 -0700 Subject: [PATCH 2399/2758] mm/damon/core: always put unsuccessfully committed target pids commit 6a66c557a2ab2609575bafd15e093669c05f9711 upstream. damon_commit_target() puts and gets the destination and the source target pids. It puts the destination target pid because it will be overwritten by the source target pid. It gets the source pid because the caller is supposed to eventually put the pids. In more detail, the caller will call damon_destroy_ctx() after damon_commit_ctx() to destroy the entire source context. And in this case, [f]vaddr operation set's cleanup_target() callback will put the pids. The commit operation is made at the context level. The operation can fail in multiple places including in the middle and after the targets commit operations. For any such failures, immediately the error is returned to the damon_commit_ctx() caller. If some or all of the source target pids were committed to the destination during the unsuccessful context commit attempt, those pids should be put twice. The source context will do the put operations using the above explained routine. However, let's suppose the destination context was not originally using [f]vaddr operation set and the commit failed before the ops of the source context is committed. The destination does not have the cleanup_target() ops callback, so it cannot put the pids via the damon_destroy_ctx(). As a result, the pids are leaked. The issue in the real world would be not very common. The commit feature is for changing parameters of running DAMON context while inheriting internal status like the monitoring results. The monitoring results of a physical address range ain't have things that are beneficial to be inherited to a virtual address ranges monitoring. So the problem-causing DAMON control would be not very common in the real world. That said, it is a supported feature. And damon_commit_target() failure due to memory allocation is relatively realistic [1] if there are a huge number of target regions. Fix by putting the pids in the commit operation in case of the failures. The issue was discovered [2] by Sashiko. Link: https://lore.kernel.org/20260605013849.83750-1-sj@kernel.org Link: https://lore.kernel.org/20260603112306.58490-1-akinobu.mita@gmail.com [1] Link: https://lore.kernel.org/20260320020056.835-1-sj@kernel.org [2] Fixes: 83dc7bbaecae ("mm/damon/sysfs: use damon_commit_ctx()") Signed-off-by: SeongJae Park Cc: # 6.11.x Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/damon/core.c | 55 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/mm/damon/core.c b/mm/damon/core.c index 901ffdaefb7f0..eeea7d5c45e23 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -1257,10 +1257,36 @@ static int damon_commit_target( return 0; } +/* + * damon_revert_target_commits() - revert unsuccessful target commits. + * @dst: Commit destination context + * @failed: Commit failed destination target + * @src: Commit source context + * + * Revert target states that changed by damon_commit_target(), and cannot be + * cleaned up by the destination context's ops.cleanup_target(). + */ +static void damon_revert_target_commits(struct damon_ctx *dst, + struct damon_target *failed, struct damon_ctx *src) +{ + struct damon_target *target; + + if (!damon_target_has_pid(src)) + return; + if (dst->ops.cleanup_target) + return; + damon_for_each_target(target, dst) { + if (target == failed) + return; + put_pid(target->pid); + } +} + static int damon_commit_targets( struct damon_ctx *dst, struct damon_ctx *src) { struct damon_target *dst_target, *next, *src_target, *new_target; + struct damon_target *failed; int i = 0, j = 0, err; damon_for_each_target_safe(dst_target, next, dst) { @@ -1274,8 +1300,10 @@ static int damon_commit_targets( dst_target, damon_target_has_pid(dst), src_target, damon_target_has_pid(src), src->min_region_sz); - if (err) - return err; + if (err) { + failed = dst_target; + goto out; + } } else { struct damos *s; @@ -1289,25 +1317,34 @@ static int damon_commit_targets( } } + failed = NULL; damon_for_each_target_safe(src_target, next, src) { if (j++ < i) continue; /* target to remove has no matching dst */ - if (src_target->obsolete) - return -EINVAL; + if (src_target->obsolete) { + err = -EINVAL; + goto out; + } new_target = damon_new_target(); - if (!new_target) - return -ENOMEM; + if (!new_target) { + err = -ENOMEM; + goto out; + } err = damon_commit_target(new_target, false, src_target, damon_target_has_pid(src), src->min_region_sz); if (err) { damon_destroy_target(new_target, NULL); - return err; + goto out; } damon_add_target(dst, new_target); } return 0; + +out: + damon_revert_target_commits(dst, failed, src); + return err; } /** @@ -1346,8 +1383,10 @@ int damon_commit_ctx(struct damon_ctx *dst, struct damon_ctx *src) */ if (!damon_attrs_equals(&dst->attrs, &src->attrs)) { err = damon_set_attrs(dst, &src->attrs); - if (err) + if (err) { + damon_revert_target_commits(dst, NULL, src); return err; + } } dst->ops = src->ops; dst->addr_unit = src->addr_unit; From ee59df7a886a35113f309e3eb791060c46f79bb1 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Wed, 17 Jun 2026 17:56:47 -0700 Subject: [PATCH 2400/2758] mm/damon/sysfs-schemes: fix dir put orders in access_pattern_add_dirs() commit d58fdbe37a829fd2e5803dd4e5a72992dd8c5368 upstream. Patch series "mm/damon/sysfs-schemes: fix wrong directories put orders in error paths". Error paths of damon_sysfs_access_pattern_add_dirs() and damon_sysfs_scheme_add_dirs() functions put references to directories in wrong orders. As a result, uninitialized memory dereference and/or memory leak can happen. Fix those. This patch (of 2): In access_pattern_add_dirs(), error handling path puts references starting from setup failed directories. If the failure happpened from the initial allication in the setup functions, uninitialized memory dereference happen. The allocation failures will not commonly happen, but the consequence is quite bad. Fix the wrong reference put orders. The issue was discovered [1] by Sashiko. Link: https://lore.kernel.org/20260618005650.83868-2-sj@kernel.org Link: https://lore.kernel.org/20260617060005.86852-1-sj@kernel.org [1] Fixes: 7e84b1f8212a ("mm/damon/sysfs: support DAMON-based Operation Schemes") Signed-off-by: SeongJae Park Cc: # 5.18.x Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/damon/sysfs-schemes.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index a8014780edae9..faccf295d6a75 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -1767,22 +1767,19 @@ static int damon_sysfs_access_pattern_add_dirs( err = damon_sysfs_access_pattern_add_range_dir(access_pattern, &access_pattern->sz, "sz"); if (err) - goto put_sz_out; + return err; err = damon_sysfs_access_pattern_add_range_dir(access_pattern, &access_pattern->nr_accesses, "nr_accesses"); if (err) - goto put_nr_accesses_sz_out; + goto put_sz_out; err = damon_sysfs_access_pattern_add_range_dir(access_pattern, &access_pattern->age, "age"); if (err) - goto put_age_nr_accesses_sz_out; + goto put_nr_accesses_sz_out; return 0; -put_age_nr_accesses_sz_out: - kobject_put(&access_pattern->age->kobj); - access_pattern->age = NULL; put_nr_accesses_sz_out: kobject_put(&access_pattern->nr_accesses->kobj); access_pattern->nr_accesses = NULL; From 6b6b5d7c2c957136b92c00b77b7175259f13082b Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Wed, 17 Jun 2026 17:56:48 -0700 Subject: [PATCH 2401/2758] mm/damon/sysfs-schemes: put stats for scheme_add_dirs() internal error commit 05ea83ee88ca70f8932906d9f2617ff996f45b50 upstream. damon_sysfs_scheme_add_dirs() setup the tried_regions directory after the stats directory setup is completed. When the tried_regions directory setup is failed, the setup function ensures the reference for the tried regions directory is released. Hence the error path should put references on setup succeeded directory objects, starting from the stats directory. However, the error path is putting the tried_regions directory instead of the stats directory. As a direct result, the stats directory object is leaked. Worse yet, if the tried_regions directory setup failed from the initial allocation, the scheme->tried_regions field remains uninitialized. The following kobject_put(&scheme->tried_regions->kobj) call in the error path will dereference the uninitialized memory. The setup failures should not be common. But once it happens, the consequence is quite bad. Fix this issue by correctly putting the stats directory instead of the tried_regions directory. The issue was discovered [1] by Sashiko. Link: https://lore.kernel.org/20260618005650.83868-3-sj@kernel.org Link: https://lore.kernel.org/20260617005223.96813-1-sj@kernel.org [1] Fixes: 5181b75f438d ("mm/damon/sysfs-schemes: implement schemes/tried_regions directory") Signed-off-by: SeongJae Park Cc: # 6.2.x Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/damon/sysfs-schemes.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index faccf295d6a75..f847d32a0ee33 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -2283,12 +2283,12 @@ static int damon_sysfs_scheme_add_dirs(struct damon_sysfs_scheme *scheme) goto put_filters_watermarks_quotas_access_pattern_out; err = damon_sysfs_scheme_set_tried_regions(scheme); if (err) - goto put_tried_regions_out; + goto put_stats_out; return 0; -put_tried_regions_out: - kobject_put(&scheme->tried_regions->kobj); - scheme->tried_regions = NULL; +put_stats_out: + kobject_put(&scheme->stats->kobj); + scheme->stats = NULL; put_filters_watermarks_quotas_access_pattern_out: kobject_put(&scheme->ops_filters->kobj); scheme->ops_filters = NULL; From 6b7f774b8882445d9174681747d37c42548686a4 Mon Sep 17 00:00:00 2001 From: "Kiryl Shutsemau (Meta)" Date: Fri, 29 May 2026 18:23:25 +0100 Subject: [PATCH 2402/2758] fs/proc/task_mmu: fix make_uffd_wp_huge_pte() prot-update race commit 04718f7c9290f95385f0dd328758753dc1c36dec upstream. Patch series "userfaultfd/pagemap: pre-existing fixes". These are pre-existing bug fixes that were carried at the front of the userfaultfd RWP working-set-tracking series up to v5 [1]. Per review feedback that fixes should not sit in the middle of a feature series, they are split out and sent on their own; the RWP series is reposted rebased on top of this. All six were flagged by the Sashiko AI review of the RWP series and carry Reported-by: Sashiko AI review . They are independent of RWP, apply to mm-new directly, and carry Cc: stable@. 1: fs/proc/task_mmu: a missing huge_ptep_modify_prot_start() in make_uffd_wp_huge_pte() can lose hardware Dirty/Accessed updates when PAGEMAP_SCAN write-protects a hugetlb PTE. 2: fs/proc/task_mmu: pagemap_scan_hugetlb_entry() compares the range against HPAGE_SIZE rather than the hstate page size, so it never write-protects gigantic hugetlb pages. 3: fs/proc/task_mmu: PAGEMAP_SCAN with PM_SCAN_WP_MATCHING over an unpopulated hugetlb range self-deadlocks -- pagemap_scan_pte_hole() calls uffd_wp_range() while walk_hugetlb_range() holds the hugetlb vma lock for read, and hugetlb_change_protection() then takes it for write. Install the marker inline instead. 4: mm/huge_memory: change_non_present_huge_pmd() drops pmd_swp_uffd_wp on a device-private PMD permission downgrade, silently losing the uffd-wp marker. 5: userfaultfd: must_wait() applies pte_write() to a locklessly read PTE without checking pte_present(), so swap/migration entries decode random offset bits and a thread can stay parked on a stale fault. 6: userfaultfd: __VMA_UFFD_FLAGS feeds VMA_UFFD_MINOR_BIT (41) to mk_vma_flags() unconditionally, an out-of-bounds write into the single-word vma_flags_t on 32-bit. Build the mask from config-gated per-mode masks so an unavailable bit is never materialised. This patch (of 6): make_uffd_wp_huge_pte() arms the UFFD_WP bit on a present HugeTLB PTE by calling huge_ptep_modify_prot_commit() with a ptent snapshot that was fetched without the corresponding huge_ptep_modify_prot_start(). The start helper is what atomically clears the entry so the kernel-owned snapshot stays consistent until the commit; without it, the hardware may set Dirty or Accessed in the live PTE between the original read and the commit, and huge_ptep_modify_prot_commit() (whose generic implementation just calls set_huge_pte_at()) then writes the stale snapshot back over the live hardware bits, losing the update. The non-hugetlb sibling make_uffd_wp_pte() does this correctly via ptep_modify_prot_start() / ptep_modify_prot_commit(). Mirror that pattern for the present-PTE branch. The migration case stays as-is -- migration entries are non-present, so there's no hardware update to race against. Link: https://lore.kernel.org/20260529172331.356655-1-kas@kernel.org Link: https://lore.kernel.org/20260529172331.356655-2-kas@kernel.org Link: https://lore.kernel.org/all/20260526130509.2748441-1-kirill@shutemov.name/ [1] Fixes: 52526ca7fdb9 ("fs/proc/task_mmu: implement IOCTL to get and optionally clear info about PTEs") Signed-off-by: Kiryl Shutsemau Reported-by: Sashiko AI review Reviewed-by: Lorenzo Stoakes Reviewed-by: Dev Jain Cc: David Hildenbrand Cc: Michal Hocko Cc: Mike Rapoport Cc: Peter Xu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Balbir Singh Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/proc/task_mmu.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 751b9ba160fbf..597122394c5d4 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -2523,12 +2523,16 @@ static void make_uffd_wp_huge_pte(struct vm_area_struct *vma, if (softleaf_is_hwpoison(entry) || softleaf_is_marker(entry)) return; - if (softleaf_is_migration(entry)) + if (softleaf_is_migration(entry)) { set_huge_pte_at(vma->vm_mm, addr, ptep, pte_swp_mkuffd_wp(ptent), psize); - else - huge_ptep_modify_prot_commit(vma, addr, ptep, ptent, - huge_pte_mkuffd_wp(ptent)); + } else { + pte_t old_pte, new_pte; + + old_pte = huge_ptep_modify_prot_start(vma, addr, ptep); + new_pte = huge_pte_mkuffd_wp(old_pte); + huge_ptep_modify_prot_commit(vma, addr, ptep, old_pte, new_pte); + } } #endif /* CONFIG_HUGETLB_PAGE */ From 18b8a9700610299819d21fd0ea85d24726d17f65 Mon Sep 17 00:00:00 2001 From: "Kiryl Shutsemau (Meta)" Date: Fri, 29 May 2026 18:23:27 +0100 Subject: [PATCH 2403/2758] fs/proc/task_mmu: fix hugetlb self-deadlock in pagemap_scan_pte_hole() commit e92d92bbafb264dc0518d52b846a3c07ed8d523f upstream. A PAGEMAP_SCAN ioctl requesting PM_SCAN_WP_MATCHING on a hugetlb VMA hangs the calling thread, unkillably, as soon as the scan reaches an unpopulated part of the range: do_pagemap_scan() walk_page_range() walk_hugetlb_range() hugetlb_vma_lock_read() # take the vma lock for read ... pagemap_scan_pte_hole() # ... ->pte_hole() for a hole uffd_wp_range() change_protection() hugetlb_change_protection() hugetlb_vma_lock_write() # ... and block taking it for write walk_hugetlb_range() holds the hugetlb vma lock for read across the whole walk. A present entry goes to ->hugetlb_entry(); an unpopulated one goes to ->pte_hole(), i.e. pagemap_scan_pte_hole(). To write-protect the hole that handler calls uffd_wp_range(), which on a hugetlb VMA reaches hugetlb_change_protection() and takes the same vma lock for write. The thread then blocks in down_write() waiting for the read lock it is itself holding. The populated path avoids this: pagemap_scan_hugetlb_entry() write-protects the entry inline under the page-table lock and never enters hugetlb_change_protection(). Do the same for holes. Fault in the page table and install the uffd-wp marker directly with make_uffd_wp_huge_pte() under the page-table lock, rather than routing through uffd_wp_range(). That is the same sequence hugetlb_change_protection() runs for an unpopulated entry, minus the vma write lock -- which is safe to skip because PMD sharing is disabled on uffd-wp VMAs (hugetlb_unshare_all_pmds() runs at registration), leaving nothing for that lock to serialise against. Link: https://lore.kernel.org/20260529172331.356655-4-kas@kernel.org Fixes: 52526ca7fdb9 ("fs/proc/task_mmu: implement IOCTL to get and optionally clear info about PTEs") Signed-off-by: Kiryl Shutsemau Reported-by: Sashiko AI review Assisted-by: Claude:claude-opus-4-8 Cc: David Hildenbrand Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Peter Xu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Balbir Singh Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/proc/task_mmu.c | 59 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 597122394c5d4..534e722d44998 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -2890,8 +2890,62 @@ static int pagemap_scan_hugetlb_entry(pte_t *ptep, unsigned long hmask, return ret; } + +/* + * Write-protect the unpopulated hugetlb entries covering [addr, end) by + * installing uffd-wp markers inline, exactly as pagemap_scan_hugetlb_entry() + * does for populated entries. + * + * walk_hugetlb_range() currently calls ->pte_hole() once per huge page, so the + * loop normally runs a single iteration; it is written to cover the full range + * in case the walker ever coalesces adjacent holes. + * + * The obvious route -- uffd_wp_range() -> hugetlb_change_protection() -- + * cannot be used here: it takes hugetlb_vma_lock_write(), but the page-table + * walker (walk_hugetlb_range()) already holds hugetlb_vma_lock_read() on the + * same VMA, so the scanning thread would deadlock against itself. PMD sharing + * is disabled on uffd-wp VMAs (hugetlb_unshare_all_pmds() at registration), so + * the vma lock guards nothing that matters for these entries anyway. + */ +static int pagemap_scan_hugetlb_hole_wp(struct vm_area_struct *vma, + unsigned long addr, unsigned long end) +{ + struct hstate *h = hstate_vma(vma); + unsigned long psize = huge_page_size(h); + struct mm_struct *mm = vma->vm_mm; + spinlock_t *ptl; + pte_t *ptep; + pte_t pte; + + for (addr = ALIGN_DOWN(addr, psize); addr < end; addr += psize) { + ptep = huge_pte_alloc(mm, vma, addr, psize); + if (!ptep) + return -ENOMEM; + + i_mmap_lock_write(vma->vm_file->f_mapping); + ptl = huge_pte_lock(h, mm, ptep); + pte = huge_ptep_get(mm, addr, ptep); + make_uffd_wp_huge_pte(vma, addr, ptep, pte); + /* + * A none entry has no cached translation, so installing the + * marker needs no TLB flush. Flush only if a fault populated + * the entry between huge_pte_alloc() and the page table lock. + */ + if (!huge_pte_none(pte)) + flush_hugetlb_tlb_range(vma, addr, addr + psize); + spin_unlock(ptl); + i_mmap_unlock_write(vma->vm_file->f_mapping); + } + + return 0; +} #else #define pagemap_scan_hugetlb_entry NULL +static int pagemap_scan_hugetlb_hole_wp(struct vm_area_struct *vma, + unsigned long addr, unsigned long end) +{ + return 0; +} #endif static int pagemap_scan_pte_hole(unsigned long addr, unsigned long end, @@ -2911,7 +2965,10 @@ static int pagemap_scan_pte_hole(unsigned long addr, unsigned long end, if (~p->arg.flags & PM_SCAN_WP_MATCHING) return ret; - err = uffd_wp_range(vma, addr, end - addr, true); + if (is_vm_hugetlb_page(vma)) + err = pagemap_scan_hugetlb_hole_wp(vma, addr, end); + else + err = uffd_wp_range(vma, addr, end - addr, true); if (err < 0) ret = err; From 5ac8f1c56ba16d94dd22a94480d30a40bb61a04d Mon Sep 17 00:00:00 2001 From: "Kiryl Shutsemau (Meta)" Date: Fri, 29 May 2026 18:23:26 +0100 Subject: [PATCH 2404/2758] fs/proc/task_mmu: use huge_page_size() in pagemap_scan_hugetlb_entry() commit 1b074e3270e1c061c829150c742eb83bad4dddd1 upstream. The partial-page check compares against HPAGE_SIZE (PMD_SIZE), which is wrong for gigantic hugetlb hstates (e.g. 1G). The walker hands the callback a huge_page_size()-sized range, never start + HPAGE_SIZE, so the comparison always declares it partial and aborts the WP. Compare against the actual hstate's page size. Link: https://lore.kernel.org/20260529172331.356655-3-kas@kernel.org Fixes: 52526ca7fdb9 ("fs/proc/task_mmu: implement IOCTL to get and optionally clear info about PTEs") Signed-off-by: Kiryl Shutsemau Reported-by: Sashiko AI review Reviewed-by: Lorenzo Stoakes Reviewed-by: Dev Jain Cc: David Hildenbrand Cc: Michal Hocko Cc: Mike Rapoport Cc: Peter Xu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Balbir Singh Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/proc/task_mmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 534e722d44998..2d182126815cc 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -2873,7 +2873,7 @@ static int pagemap_scan_hugetlb_entry(pte_t *ptep, unsigned long hmask, if (~categories & PAGE_IS_WRITTEN) goto out_unlock; - if (end != start + HPAGE_SIZE) { + if (end != start + huge_page_size(hstate_vma(vma))) { /* Partial HugeTLB page WP isn't possible. */ pagemap_scan_backout_range(p, start, end); p->arg.walk_end = start; From 319caaca072adc564ac9c5357ed013909f23d5ab Mon Sep 17 00:00:00 2001 From: Dev Jain Date: Thu, 4 Jun 2026 05:53:05 +0000 Subject: [PATCH 2405/2758] fs/proc/task_mmu: do not warn on seeing non-migration pmd entry commit cd1fc0e3c1f67c0c31dfc215e5d9b771133dedc0 upstream. Patch series "mm/hmm: A fix and a selftest", v3. Patch 1 fixes a stale warning present from the time when only migration softleaf entries were supported at the PMD level. Patch 2 adds some code into hmm-tests.c which exercises the pagemap path for PMD device-private entries. This patch (of 2): pagemap_pmd_range_thp() warns if a non-present PMD is not a migration entry. This became false once device-private entries at the PMD level were added. Therefore, remove the stale migration-only assertion. Link: https://lore.kernel.org/20260604055308.1947679-1-dev.jain@arm.com Link: https://lore.kernel.org/20260604055308.1947679-2-dev.jain@arm.com Fixes: a30b48bf1b24 ("mm/migrate_device: implement THP migration of zone device pages") Signed-off-by: Dev Jain Reviewed-by: Balbir Singh Reviewed-by: Lorenzo Stoakes Tested-by: Lorenzo Stoakes Acked-by: David Hildenbrand (Arm) Reviewed-by: Oscar Salvador (SUSE) Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/proc/task_mmu.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 2d182126815cc..29f4bdf6e4ab9 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -2042,7 +2042,6 @@ static int pagemap_pmd_range_thp(pmd_t *pmdp, unsigned long addr, flags |= PM_SOFT_DIRTY; if (pmd_swp_uffd_wp(pmd)) flags |= PM_UFFD_WP; - VM_WARN_ON_ONCE(!pmd_is_migration_entry(pmd)); page = softleaf_to_page(entry); } From b09d5ad003388e3c3501e05955cf241901c9a846 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Sat, 13 Jun 2026 21:10:05 +0000 Subject: [PATCH 2406/2758] proc: only bump parent nlink when registering directories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 16b02eb4b9b272c221255c20d34ccd5db53a3ed3 upstream. proc_register() increments the parent directory's link count for every entry it registers, while remove_proc_entry() and remove_proc_subtree() decrement it only when the removed entry is a directory. Regular files thus inflate the parent's count while they exist, and leak one link permanently on every create and remove cycle. For example, /proc/bus/pci/00 with twenty-two device files and no subdirectories reports nlink 24 instead of 2, and SR-IOV VF enable and disable cycles, each creating and removing the VF config space entries under /proc/bus/pci/, inflate the link count of that directory without bound. Before commit e06689bf5701 ("proc: change ->nlink under proc_subdir_lock"), the increment lived in proc_mkdir_data() and proc_create_mount_point(), and was therefore applied only to directories. Moving it into proc_register() to bring it under proc_subdir_lock dropped the S_ISDIR check. Thus, move the nlink accounting into pde_subdir_insert() and pde_erase(), only updating it for directories in both, so the link count is always changed together with the directory entry itself. Fixes: e06689bf5701 ("proc: change ->nlink under proc_subdir_lock") Cc: stable@vger.kernel.org # v5.5+ Signed-off-by: Krzysztof Wilczyński Link: https://patch.msgid.link/20260613211005.921692-1-kwilczynski@kernel.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/proc/generic.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 8bb81e58c9d8c..32b6b0f979679 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -112,6 +112,8 @@ static bool pde_subdir_insert(struct proc_dir_entry *dir, /* Add new node and rebalance tree. */ rb_link_node(&de->subdir_node, parent, new); rb_insert_color(&de->subdir_node, root); + if (S_ISDIR(de->mode)) + dir->nlink++; return true; } @@ -404,7 +406,6 @@ struct proc_dir_entry *proc_register(struct proc_dir_entry *dir, write_unlock(&proc_subdir_lock); goto out_free_inum; } - dir->nlink++; write_unlock(&proc_subdir_lock); return dp; @@ -702,6 +703,8 @@ static void pde_erase(struct proc_dir_entry *pde, struct proc_dir_entry *parent) { rb_erase(&pde->subdir_node, &parent->subdir); RB_CLEAR_NODE(&pde->subdir_node); + if (S_ISDIR(pde->mode)) + parent->nlink--; } /* @@ -727,8 +730,6 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent) de = NULL; } else { pde_erase(de, parent); - if (S_ISDIR(de->mode)) - parent->nlink--; } } write_unlock(&proc_subdir_lock); @@ -787,8 +788,6 @@ int remove_proc_subtree(const char *name, struct proc_dir_entry *parent) continue; } next = de->parent; - if (S_ISDIR(de->mode)) - next->nlink--; write_unlock(&proc_subdir_lock); proc_entry_rundown(de); From 8e7709aaed667b62b5e03526bf737f24cdb7ce7f Mon Sep 17 00:00:00 2001 From: Jinjiang Tu Date: Fri, 26 Jun 2026 09:32:52 +0800 Subject: [PATCH 2407/2758] fs/proc: fix KPF_KSM reported for all anonymous pages commit 81401cebfc1598306b0a981b5f9ee5b58c1aac52 upstream. Reading /proc/kpageflags for any anonymous page returns KPF_KSM set, even when KSM is not in use. As a result, tools misclassify all anonymous pages as KSM merged. In stable_page_flags(), if the page is anonymous, then use (mapping & FOLIO_MAPPING_KSM) check to identify if the anonymous page is KSM page. However, FOLIO_MAPPING_KSM is FOLIO_MAPPING_ANON | FOLIO_MAPPING_ANON_KSM, (mapping & FOLIO_MAPPING_KSM) check returns true for all anonymous pages. To fix it, use FOLIO_MAPPING_ANON_KSM instead. Link: https://lore.kernel.org/20260629033122.774318-1-tujinjiang@huawei.com Link: https://lore.kernel.org/20260626013252.2846774-1-tujinjiang@huawei.com Fixes: dee3d0bef2b0 ("proc: rewrite stable_page_flags()") Signed-off-by: Jinjiang Tu Acked-by: David Hildenbrand (Arm) Acked-by: Zi Yan Reviewed-by: Xu Xin Cc: Chengming Zhou Cc: Kefeng Wang Cc: Luiz Capitulino Cc: Matthew Wilcox (Oracle) Cc: Miaohe Lin Cc: Nanyong Sun Cc: Svetly Todorov Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/proc/page.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/proc/page.c b/fs/proc/page.c index f9b2c2c906cd7..7d93871434357 100644 --- a/fs/proc/page.c +++ b/fs/proc/page.c @@ -173,7 +173,7 @@ u64 stable_page_flags(const struct page *page) u |= 1 << KPF_MMAP; if (is_anon) { u |= 1 << KPF_ANON; - if (mapping & FOLIO_MAPPING_KSM) + if ((mapping & FOLIO_MAPPING_FLAGS) == FOLIO_MAPPING_KSM) u |= 1 << KPF_KSM; } From 1712a7fa133924d91a7211dde206ca1892bf0a40 Mon Sep 17 00:00:00 2001 From: Amit Machhiwal Date: Sun, 14 Jun 2026 23:04:37 +0530 Subject: [PATCH 2408/2758] powerpc/dt_cpu_ftrs: Set CPU_FTR_P11_PVR for Power11 and later processors commit e4de1b9cb3b5c981e4fe9bca253a7fb9161f5acd upstream. When using device tree CPU features (dt-cpu-ftrs), the kernel bypasses the traditional cputable-based CPU identification and instead derives CPU features from the device tree's "ibm,powerpc-cpu-features" node provided by firmware. However, CPU_FTR_P11_PVR is a kernel-internal feature flag used to identify Power11 and later processors, and is not represented in the device tree's ISA feature set. While ISA v3.1 support (indicated by CPU_FTR_ARCH_31) is present on both Power10 and Power11, the CPU_FTR_P11_PVR flag is specifically needed by code that must distinguish between Power10 and Power11 processors. Without this flag set, code that checks for Power11 using cpu_has_feature(CPU_FTR_P11_PVR) will incorrectly return false on Power11+ systems using dt-cpu-ftrs, leading to incorrect behavior. This issue manifests specifically in powernv environments (bare-metal or QEMU TCG with powernv machine type), where skiboot/OPAL firmware provides the "ibm,powerpc-cpu-features" node, causing the kernel to use dt-cpu-ftrs. The issue does not affect pseries guests, where SLOF firmware does not provide this node, causing the kernel to fall back to the traditional cputable path (identify_cpu) which correctly sets CPU_FTR_P11_PVR during PVR-based CPU identification. In powernv TCG guests, the missing flag causes KVM code to trigger warnings when attempting to create KVM guests, as cpu_features shows 0x000c00eb8f4fb187 (missing bit 53) instead of the correct 0x002c00eb8f4fb187 (with bit 53 set). Fix this by setting CPU_FTR_P11_PVR for all processors with PVR >= PVR_POWER11 when ISA v3.1 support is detected in cpufeatures_setup_start(). This approach ensures forward compatibility with future processor generations. Fixes: 96e266e3bcd6 ("KVM: PPC: Book3S HV: Add Power11 capability support for Nested PAPR guests") Cc: stable@vger.kernel.org # v6.13+ Signed-off-by: Amit Machhiwal Reviewed-by: Mukesh Kumar Chaurasiya (IBM) Reviewed-by: Christophe Leroy (CS GROUP) Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260614173437.26352-1-amachhiw@linux.ibm.com Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/kernel/dt_cpu_ftrs.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/powerpc/kernel/dt_cpu_ftrs.c b/arch/powerpc/kernel/dt_cpu_ftrs.c index 3af6c06af02f3..e5853daa6a486 100644 --- a/arch/powerpc/kernel/dt_cpu_ftrs.c +++ b/arch/powerpc/kernel/dt_cpu_ftrs.c @@ -704,6 +704,15 @@ static void __init cpufeatures_setup_start(u32 isa) if (isa >= ISA_V3_1) { cur_cpu_spec->cpu_features |= CPU_FTR_ARCH_31; cur_cpu_spec->cpu_user_features2 |= PPC_FEATURE2_ARCH_3_1; + + /* + * CPU_FTR_P11_PVR is a kernel-internal flag to identify + * Power11 and later processors. While ISA v3.1 is supported + * by Power10+, this flag specifically indicates Power11+ + * for code that needs to distinguish between P10 and P11. + */ + if (PVR_VER(mfspr(SPRN_PVR)) >= PVR_POWER11) + cur_cpu_spec->cpu_features |= CPU_FTR_P11_PVR; } } From b91e27bce37cab9f35de0059278ebe457ca9878b Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Tue, 28 Apr 2026 16:18:55 +0800 Subject: [PATCH 2409/2758] mm/mm_init: fix uninitialized struct pages for ZONE_DEVICE commit cd681403a87085562499d60325b7b45d3be11217 upstream. If DAX memory is hotplugged into an unoccupied subsection of an early section, section_activate() reuses the unoptimized boot memmap. However, compound_nr_pages() still assumes that vmemmap optimization is in effect and initializes only the reduced number of struct pages. As a result, the remaining tail struct pages are left uninitialized, which can later lead to unexpected behavior or crashes. Fix this by treating early sections as unoptimized when calculating how many struct pages to initialize. Link: https://lore.kernel.org/20260428081855.1249045-7-songmuchun@bytedance.com Fixes: 6fd3620b3428 ("mm/page_alloc: reuse tail struct pages for compound devmaps") Signed-off-by: Muchun Song Acked-by: David Hildenbrand (Arm) Acked-by: Mike Rapoport (Microsoft) Acked-by: Liam R. Howlett Cc: "Aneesh Kumar K.V" Cc: Joao Martins Cc: Lorenzo Stoakes Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Michal Hocko Cc: Nicholas Piggin Cc: Oscar Salvador Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/mm_init.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/mm/mm_init.c b/mm/mm_init.c index cfc76953e249c..bd466a3c10c8e 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -1055,10 +1055,17 @@ static void __ref __init_zone_device_page(struct page *page, unsigned long pfn, * of how the sparse_vmemmap internals handle compound pages in the lack * of an altmap. See vmemmap_populate_compound_pages(). */ -static inline unsigned long compound_nr_pages(struct vmem_altmap *altmap, +static inline unsigned long compound_nr_pages(unsigned long pfn, + struct vmem_altmap *altmap, struct dev_pagemap *pgmap) { - if (!vmemmap_can_optimize(altmap, pgmap)) + /* + * If DAX memory is hot-plugged into an unoccupied subsection + * of an early section, the unoptimized boot memmap is reused. + * See section_activate(). + */ + if (early_section(__pfn_to_section(pfn)) || + !vmemmap_can_optimize(altmap, pgmap)) return pgmap_vmemmap_nr(pgmap); return VMEMMAP_RESERVE_NR * (PAGE_SIZE / sizeof(struct page)); @@ -1128,7 +1135,7 @@ void __ref memmap_init_zone_device(struct zone *zone, continue; memmap_init_compound(page, pfn, zone_idx, nid, pgmap, - compound_nr_pages(altmap, pgmap)); + compound_nr_pages(pfn, altmap, pgmap)); } pageblock_migratetype_init_range(start_pfn, nr_pages, MIGRATE_MOVABLE); From 3afd3929fbc794961b87ef826134dfdfae808033 Mon Sep 17 00:00:00 2001 From: Karl Mehltretter Date: Tue, 26 May 2026 13:47:15 +0200 Subject: [PATCH 2410/2758] kcov: use WRITE_ONCE() for selftest mode stores commit 9a79524d1420e6b79a6868208c264f4518d1318e upstream. The KCOV selftest enables coverage by setting current->kcov_mode to KCOV_MODE_TRACE_PC without installing a coverage area. If an interrupt records coverage in that window, the access should fault and expose the bug. When building for QEMU raspi0 (Raspberry Pi Zero, ARMv6, CONFIG_CPU_V6K=y, CONFIG_CURRENT_POINTER_IN_TPIDRURO=y) with GCC 13.3.0, the store that enables the mode is removed. The generated kcov_init() code only stores zero after the wait loop: mrc 15, 0, r3, cr13, cr0, {3} str r4, [r3, #2028] where r4 is zero. There is no store of KCOV_MODE_TRACE_PC before the loop, so the selftest reports success without exercising coverage. Use WRITE_ONCE() for the temporary mode stores. With the same compiler and config, kcov_init() contains the intended mode store: mov r3, #2 mrc 15, 0, r2, cr13, cr0, {3} str r3, [r2, #2028] Now that the KCOV selftest is actually executed, it may expose KCOV instrumentation issues depending on the kernel config. That is expected for a selftest that was intended to catch coverage from interrupt paths. Link: https://lore.kernel.org/20260526114715.38280-1-kmehltretter@gmail.com Fixes: 6cd0dd934b03 ("kcov: Add interrupt handling self test") Assisted-by: Codex:gpt-5 Signed-off-by: Karl Mehltretter Reviewed-by: Alexander Potapenko Cc: Andrey Konovalov Cc: Dmitry Vyukov Cc: Kees Cook Cc: Marco Elver Cc: Peter Zijlstra Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- kernel/kcov.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/kcov.c b/kernel/kcov.c index 0b369e88c7c9b..f3a5ae960ce51 100644 --- a/kernel/kcov.c +++ b/kernel/kcov.c @@ -1109,10 +1109,10 @@ static void __init selftest(void) * potentially traced functions in this region. */ start = jiffies; - current->kcov_mode = KCOV_MODE_TRACE_PC; + WRITE_ONCE(current->kcov_mode, KCOV_MODE_TRACE_PC); while ((jiffies - start) * MSEC_PER_SEC / HZ < 300) ; - current->kcov_mode = 0; + WRITE_ONCE(current->kcov_mode, 0); pr_err("done running self test\n"); } #endif From bdcdfc2464659789032edfad15ff5f7a166f5d7b Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Tue, 9 Jun 2026 16:45:27 +0800 Subject: [PATCH 2411/2758] mtd: slram: remove failed entries from the device list commit 36f1648644d769c496a8e47e53603e863e358d73 upstream. register_device() links a new slram_mtdlist entry before allocating all of the state needed by the entry. If a later allocation, memremap(), or mtd_device_register() fails, the partially initialized entry remains on the global list. A later cleanup can then dereference or free invalid state from that failed entry. Unwind the partially initialized entry and clear the list tail on each failure path after the entry has been linked. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Ruoyu Wang Signed-off-by: Miquel Raynal Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/devices/slram.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c index 69cb63d99f573..48c2bc6b65eec 100644 --- a/drivers/mtd/devices/slram.c +++ b/drivers/mtd/devices/slram.c @@ -129,6 +129,7 @@ static int slram_write(struct mtd_info *mtd, loff_t to, size_t len, static int register_device(char *name, unsigned long start, unsigned long length) { slram_mtd_list_t **curmtd; + int ret = -ENOMEM; curmtd = &slram_mtdlist; while (*curmtd) { @@ -155,14 +156,15 @@ static int register_device(char *name, unsigned long start, unsigned long length if (!(*curmtd)->mtdinfo) { E("slram: Cannot allocate new MTD device.\n"); - return(-ENOMEM); + goto err_free_list; } if (!(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start = memremap(start, length, MEMREMAP_WB | MEMREMAP_WT | MEMREMAP_WC))) { E("slram: memremap failed\n"); - return -EIO; + ret = -EIO; + goto err_free_priv; } ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->end = ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start + length; @@ -183,10 +185,8 @@ static int register_device(char *name, unsigned long start, unsigned long length if (mtd_device_register((*curmtd)->mtdinfo, NULL, 0)) { E("slram: Failed to register new device\n"); - memunmap(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start); - kfree((*curmtd)->mtdinfo->priv); - kfree((*curmtd)->mtdinfo); - return(-EAGAIN); + ret = -EAGAIN; + goto err_unmap; } T("slram: Registered device %s from %luKiB to %luKiB\n", name, (start / 1024), ((start + length) / 1024)); @@ -194,6 +194,16 @@ static int register_device(char *name, unsigned long start, unsigned long length ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start, ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->end); return(0); + +err_unmap: + memunmap(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start); +err_free_priv: + kfree((*curmtd)->mtdinfo->priv); +err_free_list: + kfree((*curmtd)->mtdinfo); + kfree(*curmtd); + *curmtd = NULL; + return ret; } static void unregister_devices(void) From 8faccac11e1369adddf5d80f4a45af93f13b2e1a Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Tue, 21 Apr 2026 02:41:09 -0700 Subject: [PATCH 2412/2758] 9p: skip nlink update in cacheless mode to fix WARN_ON commit 574aa0b4799470ac814479f1138d19efe6262255 upstream. v9fs_dec_count() unconditionally calls drop_nlink() on regular files, even when the inode's nlink is already zero. In cacheless mode the client refetches inode metadata from the server (the source of truth) on every operation, so by the time v9fs_remove() returns, the locally cached nlink may already reflect the post-unlink value: 1. Client initiates unlink, server processes it and sets nlink to 0 2. Client refetches inode metadata (nlink=0) before unlink returns 3. Client's v9fs_remove() completes successfully 4. Client calls v9fs_dec_count() which calls drop_nlink() on nlink=0 This race is easily triggered under heavy unlink workloads, such as stress-ng's unlink stressor, producing the following warning: WARNING: fs/inode.c:417 at drop_nlink+0x4c/0xc8 Call trace: drop_nlink+0x4c/0xc8 v9fs_remove+0x1e0/0x250 [9p] v9fs_vfs_unlink+0x20/0x38 [9p] vfs_unlink+0x13c/0x258 ... In cacheless mode the server is authoritative and the inode is on its way out, so locally adjusting nlink buys nothing. Skip v9fs_dec_count() entirely when neither CACHE_META nor CACHE_LOOSE is set, which both avoids the warning and removes a class of nlink races (two concurrent unlinkers observing nlink > 0 and both calling drop_nlink()) that an nlink == 0 guard alone would only narrow rather than close. Fixes: ac89b2ef9b55 ("9p: don't maintain dir i_nlink if the exported fs doesn't either") Cc: stable@vger.kernel.org Suggested-by: Dominique Martinet Signed-off-by: Breno Leitao Message-ID: <20260421-9p-v2-1-48762d294fad@debian.org> Signed-off-by: Dominique Martinet Signed-off-by: Greg Kroah-Hartman --- fs/9p/vfs_inode.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index cdaa259af16de..952792ce2dee9 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -488,10 +488,19 @@ static int v9fs_at_to_dotl_flags(int flags) * - ext4 (with dir_nlink feature enabled) sets nlink to 1 if a dir has more * than EXT4_LINK_MAX (65000) links. * + * In cacheless mode the server is the source of truth for nlink and the + * inode is going away immediately, so locally adjusting i_nlink buys + * nothing and races with concurrent metadata fetches that may already + * have observed the post-unlink value (nlink == 0). + * * @inode: inode whose nlink is being dropped */ static void v9fs_dec_count(struct inode *inode) { + struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode); + + if (!(v9ses->cache & (CACHE_META | CACHE_LOOSE))) + return; if (!S_ISDIR(inode->i_mode) || inode->i_nlink > 2) drop_nlink(inode); } From 8c1b23d830087287b3cef2eb899ec6282f53c378 Mon Sep 17 00:00:00 2001 From: Alexey Charkov Date: Wed, 3 Jun 2026 00:10:50 +0400 Subject: [PATCH 2413/2758] power: supply: bq257xx: Fix VSYSMIN clamping logic commit b6c6b9260a92dacef6edef8e93bc2767b86b1dfe upstream. The minimal system voltage (VSYSMIN) is meant to protect the battery from dangerous over-discharge. When the device tree provides a value for the minimum design voltage of the battery, the user should not be allowed to set a lower VSYSMIN, as that would defeat the purpose of this protection. Flip the clamping logic when setting VSYSMIN to ensure that battery design voltage is respected. Cc: stable@vger.kernel.org Fixes: 1cc017b7f9c7 ("power: supply: bq257xx: Add support for BQ257XX charger") Tested-by: Chris Morgan Signed-off-by: Alexey Charkov Link: https://patch.msgid.link/20260603-bq25792-v7-2-d487bed276d0@flipper.net Signed-off-by: Sebastian Reichel Signed-off-by: Greg Kroah-Hartman --- drivers/power/supply/bq257xx_charger.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/power/supply/bq257xx_charger.c b/drivers/power/supply/bq257xx_charger.c index 02c7d8b61e82b..7ca4ae6109027 100644 --- a/drivers/power/supply/bq257xx_charger.c +++ b/drivers/power/supply/bq257xx_charger.c @@ -128,9 +128,8 @@ static int bq25703_get_min_vsys(struct bq257xx_chg *pdata, int *intval) * @vsys: voltage value to set in uV. * * This function takes a requested minimum system voltage value, clamps - * it between the minimum supported value by the charger and a user - * defined minimum system value, and then writes the value to the - * appropriate register. + * it between the user defined minimum system value and the maximum supported + * value by the charger, and then writes the value to the appropriate register. * * Return: Returns 0 on success or error if an error occurs. */ @@ -139,7 +138,7 @@ static int bq25703_set_min_vsys(struct bq257xx_chg *pdata, int vsys) unsigned int reg; int vsys_min = pdata->vsys_min; - vsys = clamp(vsys, BQ25703_MINVSYS_MIN_UV, vsys_min); + vsys = clamp(vsys, vsys_min, BQ25703_MINVSYS_MAX_UV); reg = ((vsys - BQ25703_MINVSYS_MIN_UV) / BQ25703_MINVSYS_STEP_UV); reg = FIELD_PREP(BQ25703_MINVSYS_MASK, reg); From d52d4c9ac716a671cf2a1f85a52a1548aebedab3 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 13 May 2026 19:42:35 +0200 Subject: [PATCH 2414/2758] scsi: smartpqi: Use shost_to_hba() in pqi_scan_finished() commit 57db1307afb1f83d45f5ff53b93f8d040100d13e upstream. shost_to_hba() is used everywhere except to obtain pqi_ctrl_info from shosti, except in pqi_scan_finished(), where shost_priv() is used. This causes one pointer dereference to be missed, as shost->hostdata is a pointer in smartpqi. Fix it. Fixes: 6c223761eb54 ("smartpqi: initial commit of Microsemi smartpqi driver") Signed-off-by: Martin Wilck Reviewed-by: Don Brace Cc: Don Brace Cc: storagedev@microchip.com Cc: stable@vger.kernel.org Reviewed-by: Hannes Reinecke Reviewed-by: Hannes Reinecke Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260513174236.430465-2-mwilck@suse.com Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/smartpqi/smartpqi_init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index 2026ac645d6ab..5ec583dc2e7df 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -2642,7 +2642,7 @@ static int pqi_scan_finished(struct Scsi_Host *shost, { struct pqi_ctrl_info *ctrl_info; - ctrl_info = shost_priv(shost); + ctrl_info = shost_to_hba(shost); return !mutex_is_locked(&ctrl_info->scan_mutex); } From 9fefab759f59bf891bef7be15aee4bc7caec7ec3 Mon Sep 17 00:00:00 2001 From: "Pratyush Yadav (Google)" Date: Tue, 19 May 2026 18:05:49 +0200 Subject: [PATCH 2415/2758] kho: make sure scratch size is always aligned by CMA_MIN_ALIGNMENT_BYTES commit 0e39380a7316122e1b00012b3f3cd3e318b3e7d3 upstream. When using scratch_scale, the scratch sizes are rounded up to CMA_MIN_ALIGNMENT_BYTES since they will be released as MIGRATE_CMA. This is not done when using fixed scratch sizes via command line. This can result in user specifying a size which is not aligned, and thus kernel releasing a pageblock that is only partially scratch. Do the rounding up for both cases in scratch_size_update(). Fixes: 3dc92c311498 ("kexec: add Kexec HandOver (KHO) generation helpers") Cc: stable@kernel.org Signed-off-by: Pratyush Yadav (Google) Link: https://patch.msgid.link/20260519160554.2713361-1-pratyush@kernel.org Signed-off-by: Pasha Tatashin Signed-off-by: Mike Rapoport (Microsoft) Signed-off-by: Greg Kroah-Hartman --- kernel/liveupdate/kexec_handover.c | 32 ++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c index 1b592d86dc489..48d98fa9621e3 100644 --- a/kernel/liveupdate/kexec_handover.c +++ b/kernel/liveupdate/kexec_handover.c @@ -593,20 +593,30 @@ early_param("kho_scratch", kho_parse_scratch_size); static void __init scratch_size_update(void) { - phys_addr_t size; + /* + * If fixed sizes are not provided via command line, calculate them + * now. + */ + if (scratch_scale) { + phys_addr_t size; - if (!scratch_scale) - return; + size = memblock_reserved_kern_size(ARCH_LOW_ADDRESS_LIMIT, + NUMA_NO_NODE); + size = size * scratch_scale / 100; + scratch_size_lowmem = size; - size = memblock_reserved_kern_size(ARCH_LOW_ADDRESS_LIMIT, - NUMA_NO_NODE); - size = size * scratch_scale / 100; - scratch_size_lowmem = round_up(size, CMA_MIN_ALIGNMENT_BYTES); + size = memblock_reserved_kern_size(MEMBLOCK_ALLOC_ANYWHERE, + NUMA_NO_NODE); + size = size * scratch_scale / 100 - scratch_size_lowmem; + scratch_size_global = size; + } - size = memblock_reserved_kern_size(MEMBLOCK_ALLOC_ANYWHERE, - NUMA_NO_NODE); - size = size * scratch_scale / 100 - scratch_size_lowmem; - scratch_size_global = round_up(size, CMA_MIN_ALIGNMENT_BYTES); + /* + * Scratch areas are released as MIGRATE_CMA. Round them up to the right + * size. + */ + scratch_size_lowmem = round_up(scratch_size_lowmem, CMA_MIN_ALIGNMENT_BYTES); + scratch_size_global = round_up(scratch_size_global, CMA_MIN_ALIGNMENT_BYTES); } static phys_addr_t __init scratch_size_node(int nid) From c8f8e61332ba72d50e2dda9d09abe1e89bc2eed3 Mon Sep 17 00:00:00 2001 From: Ionut Nechita Date: Tue, 19 May 2026 16:52:33 +0300 Subject: [PATCH 2416/2758] scsi: sas: Skip opt_sectors when DMA reports no real optimization hint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit be8fcd4a8217a916344c88a4b1b84f5736dda17e upstream. sas_host_setup() unconditionally sets shost->opt_sectors from dma_opt_mapping_size(). When the IOMMU is disabled or in passthrough mode and no DMA ops provide an opt_mapping_size callback, dma_opt_mapping_size() returns min(dma_max_mapping_size(), SIZE_MAX) which equals dma_max_mapping_size() — a hard upper bound, not an optimization hint. On a Dell PowerEdge R750 with mpt3sas (Broadcom SAS3816, FW 33.15.00.00) and intel_iommu=off the following values are observed: dma_opt_mapping_size() = dma_max_mapping_size() (no real hint) shost->max_sectors = 32767 opt_sectors = min(32767, huge >> 9) = 32767 optimal_io_size = 32767 << 9 = 16776704 → round_down(16776704, 4096) = 16773120 The SAS disk (SAMSUNG MZILT800HBHQ0D3) does not report an Optimal Transfer Length in VPD page B0, so sdkp->opt_xfer_blocks remains 0. sd_revalidate_disk() then uses min_not_zero(0, opt_sectors) = opt_sectors, propagating the bogus value into the block device's optimal_io_size (visible as OPT-IO = 16773120 in lsblk --topology). mkfs.xfs picks up optimal_io_size and minimum_io_size and computes: swidth = 16773120 / 4096 = 4095 sunit = 8192 / 4096 = 2 Since 4095 % 2 != 0, XFS rejects the geometry: SB stripe unit sanity check failed This makes it impossible to create XFS filesystems (e.g. for /var/lib/docker) during system bootstrap. Fix this by introducing a sas_dma_setup_opt_sectors() helper that sets opt_sectors only when dma_opt_mapping_size() is strictly less than dma_max_mapping_size(), indicating a genuine DMA optimization constraint. The helper computes min(opt_sectors, max_sectors) first, then rounds down to a power of two so that filesystem geometry calculations always produce clean results. When the two DMA values are equal, no backend provided a real hint, so opt_sectors stays at 0 ("no preference"). [mkp: implemented hch's suggestion] Fixes: 4cbfca5f7750 ("scsi: scsi_transport_sas: cap shost opt_sectors according to DMA optimal limit") Cc: stable@vger.kernel.org Reviewed-by: John Garry Signed-off-by: Ionut Nechita Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260519135238.373784-2-ionut.nechita@windriver.com Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/scsi_transport_sas.c | 41 +++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index d8f2377b017fe..d689b9ed08a6c 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -220,12 +221,45 @@ static int sas_bsg_initialize(struct Scsi_Host *shost, struct sas_rphy *rphy) * SAS host attributes */ +/* + * Set shost->opt_sectors from the DMA optimal mapping size, but only + * when dma_opt_mapping_size() is strictly less than dma_max_mapping_size(), + * indicating a genuine optimization hint from an IOMMU or DMA backend. + * When the two are equal (e.g. IOMMU disabled / passthrough), no real + * hint exists, so leave opt_sectors at 0 to avoid bogus optimal_io_size + * values that break filesystem geometry (e.g. mkfs.xfs stripe alignment). + */ +static void sas_dma_setup_opt_sectors(struct Scsi_Host *shost) +{ + struct device *dma_dev = shost->dma_dev; + size_t opt = dma_opt_mapping_size(dma_dev); + size_t max = dma_max_mapping_size(dma_dev); + unsigned int opt_sectors; + + /* opt >= max means no real hint was provided by the DMA layer */ + if (opt >= max) + return; + + /* Clamp to max_sectors to avoid overflow in sector arithmetic */ + opt_sectors = min_t(unsigned int, opt >> SECTOR_SHIFT, + shost->max_sectors); + + /* Guard against zero before rounddown_pow_of_two() */ + if (!opt_sectors) + return; + + /* + * Round down to power-of-two so filesystem geometry calculations + * (e.g. XFS stripe width/unit) always produce clean divisors. + */ + shost->opt_sectors = rounddown_pow_of_two(opt_sectors); +} + static int sas_host_setup(struct transport_container *tc, struct device *dev, struct device *cdev) { struct Scsi_Host *shost = dev_to_shost(dev); struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); - struct device *dma_dev = shost->dma_dev; INIT_LIST_HEAD(&sas_host->rphy_list); mutex_init(&sas_host->lock); @@ -237,10 +271,7 @@ static int sas_host_setup(struct transport_container *tc, struct device *dev, dev_printk(KERN_ERR, dev, "fail to a bsg device %d\n", shost->host_no); - if (dma_dev->dma_mask) { - shost->opt_sectors = min_t(unsigned int, shost->max_sectors, - dma_opt_mapping_size(dma_dev) >> SECTOR_SHIFT); - } + sas_dma_setup_opt_sectors(shost); return 0; } From 9f457beb601d1bfaa0fe57f53543a00fed92d6a0 Mon Sep 17 00:00:00 2001 From: Stafford Horne Date: Fri, 22 May 2026 16:56:03 +0100 Subject: [PATCH 2417/2758] openrisc: Add full instruction cache invalidate functions commit 1be031de802ba486a7605b52eda825f128b026e2 upstream. Add functions to invalidate all cache lines which we will use for static_key patching. On OpenRISC there is no instruction to invalidate an entire cache so we loop and invalidate cache lines one by one. This is not extremely expensive on OpenRISC as we usually have only a few hundred cache lines. I considered using the invalidate cache page or range functions. However, tracking which ranges need invalidation would have been more expensive than flushing all pages. Cc: stable@vger.kernel.org Signed-off-by: Stafford Horne Signed-off-by: Greg Kroah-Hartman --- arch/openrisc/include/asm/cacheflush.h | 4 ++++ arch/openrisc/kernel/smp.c | 21 +++++++++++++++++++++ arch/openrisc/mm/cache.c | 16 ++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/arch/openrisc/include/asm/cacheflush.h b/arch/openrisc/include/asm/cacheflush.h index cd8f971c0fecc..7b8c043a831df 100644 --- a/arch/openrisc/include/asm/cacheflush.h +++ b/arch/openrisc/include/asm/cacheflush.h @@ -26,6 +26,7 @@ extern void local_icache_page_inv(struct page *page); extern void local_dcache_range_flush(unsigned long start, unsigned long end); extern void local_dcache_range_inv(unsigned long start, unsigned long end); extern void local_icache_range_inv(unsigned long start, unsigned long end); +extern void local_icache_all_inv(void); /* * Data cache flushing always happen on the local cpu. Instruction cache @@ -35,10 +36,13 @@ extern void local_icache_range_inv(unsigned long start, unsigned long end); #ifndef CONFIG_SMP #define dcache_page_flush(page) local_dcache_page_flush(page) #define icache_page_inv(page) local_icache_page_inv(page) +#define icache_all_inv() local_icache_all_inv() #else /* CONFIG_SMP */ #define dcache_page_flush(page) local_dcache_page_flush(page) #define icache_page_inv(page) smp_icache_page_inv(page) +#define icache_all_inv() smp_icache_all_inv() extern void smp_icache_page_inv(struct page *page); +extern void smp_icache_all_inv(void); #endif /* CONFIG_SMP */ /* diff --git a/arch/openrisc/kernel/smp.c b/arch/openrisc/kernel/smp.c index 040ca201b6927..65599252f3d41 100644 --- a/arch/openrisc/kernel/smp.c +++ b/arch/openrisc/kernel/smp.c @@ -346,3 +346,24 @@ void smp_icache_page_inv(struct page *page) on_each_cpu(ipi_icache_page_inv, page, 1); } EXPORT_SYMBOL(smp_icache_page_inv); + +static void ipi_icache_all_inv(void *arg) +{ + local_icache_all_inv(); +} + +void smp_icache_all_inv(void) +{ + if (num_online_cpus() < 2) { + local_icache_all_inv(); + return; + } + + /* + * Ensure stores complete before we request remote icaches + * to invalidate. + */ + mb(); + + on_each_cpu(ipi_icache_all_inv, NULL, 1); +} diff --git a/arch/openrisc/mm/cache.c b/arch/openrisc/mm/cache.c index f33df46dae4e5..2667d90691b59 100644 --- a/arch/openrisc/mm/cache.c +++ b/arch/openrisc/mm/cache.c @@ -63,6 +63,22 @@ void local_icache_page_inv(struct page *page) } EXPORT_SYMBOL(local_icache_page_inv); +void local_icache_all_inv(void) +{ + if (cpu_cache_is_present(SPR_UPR_ICP)) { + unsigned long iccfgr = mfspr(SPR_ICCFGR); + unsigned long sets = 1 << ((iccfgr & SPR_ICCFGR_NCS) >> 3); + unsigned long block_size = 16 << ((iccfgr & SPR_ICCFGR_CBS) >> 7); + unsigned long paddr = 0; + unsigned long end = sets * block_size; + + while (paddr < end) { + mtspr(SPR_ICBIR, paddr); + paddr += block_size; + } + } +} + void local_dcache_range_flush(unsigned long start, unsigned long end) { cache_loop(start, end, SPR_DCBFR, SPR_UPR_DCP); From 455519f6b70f46ac6cbf41a75ac76ec5e59040f2 Mon Sep 17 00:00:00 2001 From: Florian Fuchs Date: Mon, 18 May 2026 13:45:20 +0200 Subject: [PATCH 2418/2758] mtd: maps: vmu-flash: fix fault in unaligned fixup commit 79d1661502c6e4b6f626185cef72cf2fa78116e1 upstream. Use kzalloc_obj() / kzalloc_objs() to allocate the memcard structs, instead of kmalloc_obj() / kmalloc_objs() to prevent access to uninitialized data. Fixes runtime error: Fault in unaligned fixup: 0000 [#1] at mtd_get_fact_prot_info. Fixes: 47a72688fae7 ("mtd: flash mapping support for Dreamcast VMU.") Cc: stable@vger.kernel.org Signed-off-by: Florian Fuchs Signed-off-by: Miquel Raynal Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/maps/vmu-flash.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/maps/vmu-flash.c b/drivers/mtd/maps/vmu-flash.c index 75e06d249ce99..b76d0609d1b76 100644 --- a/drivers/mtd/maps/vmu-flash.c +++ b/drivers/mtd/maps/vmu-flash.c @@ -609,7 +609,7 @@ static int vmu_connect(struct maple_device *mdev) basic_flash_data = be32_to_cpu(mdev->devinfo.function_data[c - 1]); - card = kmalloc_obj(struct memcard); + card = kzalloc_obj(struct memcard); if (!card) { error = -ENOMEM; goto fail_nomem; @@ -627,13 +627,13 @@ static int vmu_connect(struct maple_device *mdev) * Not sure there are actually any multi-partition devices in the * real world, but the hardware supports them, so, so will we */ - card->parts = kmalloc_objs(struct vmupart, card->partitions); + card->parts = kzalloc_objs(struct vmupart, card->partitions); if (!card->parts) { error = -ENOMEM; goto fail_partitions; } - card->mtd = kmalloc_objs(struct mtd_info, card->partitions); + card->mtd = kzalloc_objs(struct mtd_info, card->partitions); if (!card->mtd) { error = -ENOMEM; goto fail_mtd_info; From 20869525a283c2cd5d2f467bca6d0009b6e42f15 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Sat, 18 Apr 2026 13:10:48 +0000 Subject: [PATCH 2419/2758] ocfs2: use kzalloc for quota recovery bitmap allocation commit 93c8c6ea90be9e9df8fe14048ad4e3caad0770a6 upstream. ocfs2 quota recovery allocates a bitmap buffer with kmalloc and does not fully initialize it. This can lead to use of uninitialized bits during quota recovery from a corrupted filesystem image. Use kzalloc instead to ensure the bitmap is zero-initialized. Link: https://lore.kernel.org/20260418131048.1052507-1-tristmd@gmail.com Reported-by: syzbot+7ea0b96c4ddb49fd1a70@syzkaller.appspotmail.com Signed-off-by: Tristan Madani Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/ocfs2/quota_local.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c index 12cbb4fccda0d..f55810c59b1b1 100644 --- a/fs/ocfs2/quota_local.c +++ b/fs/ocfs2/quota_local.c @@ -302,7 +302,7 @@ static int ocfs2_add_recovery_chunk(struct super_block *sb, if (!rc) return -ENOMEM; rc->rc_chunk = chunk; - rc->rc_bitmap = kmalloc(sb->s_blocksize, GFP_NOFS); + rc->rc_bitmap = kzalloc(sb->s_blocksize, GFP_NOFS); if (!rc->rc_bitmap) { kfree(rc); return -ENOMEM; From 3c44f6c62f652d3ac9d03beb8a199e6388256cdd Mon Sep 17 00:00:00 2001 From: Bastien Curutchet Date: Tue, 26 May 2026 09:10:00 +0200 Subject: [PATCH 2420/2758] mtd: rawnand: pl353: fix probe resource allocation commit 19ed11aee966d91beebdef9d32ce926474872f79 upstream. During probe(), the devm_ioremap() is called with the parent device instead of the current one. So when the module is unloaded, the register area isn't released. Target the pl35x device in the devm_ioremap() instead of its parent. Cc: stable@vger.kernel.org Fixes: 08d8c62164a3 ("mtd: rawnand: pl353: Add support for the ARM PL353 SMC NAND controller") Signed-off-by: Bastien Curutchet Signed-off-by: Miquel Raynal Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/nand/raw/pl35x-nand-controller.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/pl35x-nand-controller.c b/drivers/mtd/nand/raw/pl35x-nand-controller.c index f2c65eb7a8d9a..06f8f1e14b9c2 100644 --- a/drivers/mtd/nand/raw/pl35x-nand-controller.c +++ b/drivers/mtd/nand/raw/pl35x-nand-controller.c @@ -1155,7 +1155,7 @@ static int pl35x_nand_probe(struct platform_device *pdev) nfc->controller.ops = &pl35x_nandc_ops; INIT_LIST_HEAD(&nfc->chips); - nfc->conf_regs = devm_ioremap_resource(&smc_amba->dev, &smc_amba->res); + nfc->conf_regs = devm_ioremap_resource(nfc->dev, &smc_amba->res); if (IS_ERR(nfc->conf_regs)) return PTR_ERR(nfc->conf_regs); From 823886a1b089b49bcd349bc8bd3417b7910cd1ac Mon Sep 17 00:00:00 2001 From: Vasiliy Kovalev Date: Wed, 15 Apr 2026 18:52:37 +0300 Subject: [PATCH 2421/2758] net/9p: fix infinite loop in p9_client_rpc on fatal signal commit 6b4f48728faa8bb514368f7eacda05565dea8696 upstream. When p9_client_rpc() is called with type P9_TFLUSH and the transport has no peer (e.g. fd transport backed by pipes with no 9p server), a fatal signal causes an infinite loop: again: err = io_wait_event_killable(req->wq, ...) /* SIGKILL wakes the task, returns -ERESTARTSYS */ if (err == -ERESTARTSYS && c->status == Connected && type == P9_TFLUSH) { sigpending = 1; clear_thread_flag(TIF_SIGPENDING); goto again; } clear_thread_flag() clears TIF_SIGPENDING before jumping back to io_wait_event_killable(). signal_pending_state() checks TIF_SIGPENDING, finds it zero, and the task goes to sleep again. The task can only wake on the next signal delivery that calls signal_wake_up() and sets TIF_SIGPENDING again. When that happens the loop repeats, clears TIF_SIGPENDING, and sleeps again indefinitely. This is triggered in practice by coredump_wait(): when a thread in a multi-threaded process causes a coredump (e.g. via SIGSYS from Syscall User Dispatch), coredump_wait() sends SIGKILL to all other threads and waits for them to call mm_release(). If one of those threads is blocked in p9_client_rpc() over an fd transport with no peer, it enters the P9_TFLUSH loop and never calls mm_release(), so coredump_wait() stalls forever: INFO: task syz.0.18:676 blocked for more than 143 seconds. Not tainted 6.12.77+ #1 task:syz.0.18 state:D stack:27600 pid:676 tgid:673 ppid:630 flags:0x00000004 Call Trace: context_switch kernel/sched/core.c:5344 [inline] __schedule+0xcb4/0x5d50 kernel/sched/core.c:6724 __schedule_loop kernel/sched/core.c:6801 [inline] schedule+0xe5/0x350 kernel/sched/core.c:6816 schedule_timeout+0x253/0x290 kernel/time/timer.c:2593 do_wait_for_common kernel/sched/completion.c:95 [inline] __wait_for_common+0x409/0x600 kernel/sched/completion.c:116 wait_for_common kernel/sched/completion.c:127 [inline] wait_for_completion_state+0x1d/0x40 kernel/sched/completion.c:264 coredump_wait fs/coredump.c:448 [inline] do_coredump+0x854/0x4350 fs/coredump.c:629 get_signal+0x1425/0x2730 kernel/signal.c:2903 arch_do_signal_or_restart+0x81/0x880 arch/x86/kernel/signal.c:337 exit_to_user_mode_loop kernel/entry/common.c:111 [inline] exit_to_user_mode_prepare include/linux/entry-common.h:328 [inline] __syscall_exit_to_user_mode_work kernel/entry/common.c:207 [inline] syscall_exit_to_user_mode+0xf9/0x160 kernel/entry/common.c:218 do_syscall_64+0x102/0x220 arch/x86/entry/common.c:84 entry_SYSCALL_64_after_hwframe+0x77/0x7f Fix: check fatal_signal_pending() before clearing TIF_SIGPENDING in the P9_TFLUSH retry loop. At that point TIF_SIGPENDING is still set, so fatal_signal_pending() works correctly. If a fatal signal is pending, jump to recalc_sigpending to restore TIF_SIGPENDING and return -ERESTARTSYS to the caller. The same defect is present in stable kernels back to 5.4. On those kernels the infinite loop is broken earlier by a second SIGKILL from the parent process (e.g. kill_and_wait() retrying after a timeout), resulting in a zombie process and a shutdown delay rather than a permanent D-state hang, but the underlying flaw is the same. Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: 91b8534fa8f5 ("9p: make rpc code common and rework flush code") Closes: https://syzkaller.appspot.com/bug?extid=3ce7863f8fc836a427e7 Cc: stable@vger.kernel.org Signed-off-by: Vasiliy Kovalev Message-ID: <20260415155237.182891-1-kovalev@altlinux.org> Signed-off-by: Dominique Martinet Signed-off-by: Greg Kroah-Hartman --- net/9p/client.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/9p/client.c b/net/9p/client.c index 4b942d02c29cf..7d1535edfce7f 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -600,6 +600,8 @@ p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...) if (err == -ERESTARTSYS && c->status == Connected && type == P9_TFLUSH) { + if (fatal_signal_pending(current)) + goto recalc_sigpending; sigpending = 1; clear_thread_flag(TIF_SIGPENDING); goto again; From 8f575fc17360827ca1d1940a84f3c7ee40407a10 Mon Sep 17 00:00:00 2001 From: Arseniy Krasnov Date: Tue, 5 May 2026 11:30:30 +0300 Subject: [PATCH 2422/2758] mtd: rawnand: fix condition in 'nand_select_target()' commit 8507c2cc9e4fa402401819f44d1e8a5ef4d11d8b upstream. 'cs' here must be in range [0:nanddev_ntargets[. Cc: stable@vger.kernel.org Fixes: 32813e288414 ("mtd: rawnand: Get rid of chip->numchips") Signed-off-by: Arseniy Krasnov Signed-off-by: Miquel Raynal Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/nand/raw/nand_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index d6d3e17ab4079..e4e1383bd5e1a 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -175,7 +175,7 @@ void nand_select_target(struct nand_chip *chip, unsigned int cs) * cs should always lie between 0 and nanddev_ntargets(), when that's * not the case it's a bug and the caller should be fixed. */ - if (WARN_ON(cs > nanddev_ntargets(&chip->base))) + if (WARN_ON(cs >= nanddev_ntargets(&chip->base))) return; chip->cur_cs = cs; From 4d1953d3aeb4a7f6623083e1839068ee1c157db2 Mon Sep 17 00:00:00 2001 From: Kyle Zeng Date: Thu, 11 Jun 2026 14:35:10 -0700 Subject: [PATCH 2423/2758] ocfs2: avoid moving extents to occupied clusters commit 22920541c35a9f23f219038ba5874c843a7c4419 upstream. For non-auto OCFS2_IOC_MOVE_EXT operations, userspace supplies a physical me_goal. ocfs2_move_extent() initializes new_phys_cpos from that goal and expects ocfs2_probe_alloc_group() to replace it with a free run in the target block group. The probe currently leaves *phys_cpos unchanged if the scan reaches the end of the group without finding a free run. An occupied goal at the last bit can therefore survive the probe and be passed to __ocfs2_move_extent(), which copies file data into a cluster still owned by another inode before the bitmap is updated. When the probe does find a free run, it also subtracts move_len from the ending bit. The start of an N-bit run ending at i is i - N + 1, so the current calculation can report the bit immediately before the free run. Clear *phys_cpos before scanning and use the correct free-run start. Callers already treat a zero result as -ENOSPC, so failed probes no longer continue with an occupied caller-controlled goal. Link: https://lore.kernel.org/20260611213510.16956-1-kylebot@openai.com Fixes: e6b5859cccfa ("Ocfs2/move_extents: helper to probe a proper region to move in an alloc group.") Fixes: 236b9254f8d1 ("ocfs2: fix non-auto defrag path not working issue") Assisted-by: Codex:gpt-5.5 Signed-off-by: Kyle Zeng Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/ocfs2/move_extents.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/ocfs2/move_extents.c b/fs/ocfs2/move_extents.c index c53de4439d933..ad1678ee7cc46 100644 --- a/fs/ocfs2/move_extents.c +++ b/fs/ocfs2/move_extents.c @@ -534,6 +534,8 @@ static void ocfs2_probe_alloc_group(struct inode *inode, struct buffer_head *bh, u32 base_cpos = ocfs2_blocks_to_clusters(inode->i_sb, le64_to_cpu(gd->bg_blkno)); + *phys_cpos = 0; + for (i = base_bit; i < le16_to_cpu(gd->bg_bits); i++) { used = ocfs2_test_bit(i, (unsigned long *)gd->bg_bitmap); @@ -555,7 +557,7 @@ static void ocfs2_probe_alloc_group(struct inode *inode, struct buffer_head *bh, last_free_bits++; if (last_free_bits == move_len) { - i -= move_len; + i = i - move_len + 1; *goal_bit = i; *phys_cpos = base_cpos + i; break; From 253ed993e0b36997ec7b04c1ff76103b38241de2 Mon Sep 17 00:00:00 2001 From: Ian Bridges Date: Thu, 11 Jun 2026 09:46:38 -0500 Subject: [PATCH 2424/2758] ocfs2: fix NULL h_transaction deref in ocfs2_assure_trans_credits commit f9ab30c96b0f00c20c6dac93681bdae3a033d229 upstream. [BUG] A direct write over unwritten extents can panic the kernel in ocfs2_assure_trans_credits() when the journal aborts during DIO completion. The crash is a general protection fault from a NULL pointer dereference. [CAUSE] ocfs2_dio_end_io_write() loops over a direct write's unwritten extents, marking each written under a single journal handle. If the journal aborts (for example after an I/O error) while the extent tree is being updated, the handle is left aborted with its transaction pointer cleared. The extent merge treats that failure as not critical and reports success, so the loop keeps using the handle. ocfs2_assure_trans_credits() reads the handle's remaining credits without first checking whether the handle is aborted, and that read dereferences the cleared transaction pointer. [FIX] A journal abort is recorded in the handle itself, so callers are expected to test the handle rather than rely on a returned error. Make ocfs2_assure_trans_credits() do that, as the other ocfs2 journal helpers already do, and return -EROFS when the handle is aborted. Link: https://lore.kernel.org/airKTsM1fRVN-Wj7@dev Fixes: be346c1a6eeb ("ocfs2: fix DIO failure due to insufficient transaction credits") Signed-off-by: Ian Bridges Reported-by: syzbot+e9c15ff790cea6a0cfae@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=e9c15ff790cea6a0cfae Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/ocfs2/journal.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index fc54cc798ce35..d8afbc1a76bb8 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -473,8 +473,12 @@ int ocfs2_extend_trans(handle_t *handle, int nblocks) */ int ocfs2_assure_trans_credits(handle_t *handle, int nblocks) { - int old_nblks = jbd2_handle_buffer_credits(handle); + int old_nblks; + if (is_handle_aborted(handle)) + return -EROFS; + + old_nblks = jbd2_handle_buffer_credits(handle); trace_ocfs2_assure_trans_credits(old_nblks); if (old_nblks >= nblocks) return 0; From 858aa4965ffa8c0d4bb5dd835ac4f1c9a1dcab85 Mon Sep 17 00:00:00 2001 From: Ian Bridges Date: Wed, 10 Jun 2026 19:23:11 -0500 Subject: [PATCH 2425/2758] ocfs2: fix UBSAN array-index-out-of-bounds in ocfs2_sum_rightmost_rec commit 452a8467be8143747292218212671deeb186d2ae upstream. [BUG] On-disk corruption setting l_next_free_rec to 0 in an inode's embedded extent list triggers a UBSAN panic on the next write to that file. [CAUSE] ocfs2_sum_rightmost_rec() computes i = le16_to_cpu(el->l_next_free_rec) - 1 and accesses el->l_recs[i] without validating i. When l_next_free_rec is 0, i becomes -1; when l_next_free_rec exceeds l_count, i falls past the end of the array. Either case violates the __counted_by_le(l_count) annotation on l_recs[] and triggers UBSAN. [FIX] Validate the inode's embedded extent list when the inode is read, in ocfs2_validate_inode_block(): l_count must be non-zero and no larger than the inode block can hold, and l_next_free_rec must not exceed l_count. A corrupt list is rejected at read time, before the b-tree code can index l_recs[] out of bounds. Link: https://lore.kernel.org/ain_780qc0P4ypNd@dev Signed-off-by: Ian Bridges Reported-by: syzbot+be16e33db01e6644db7a@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=be16e33db01e6644db7a Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/ocfs2/inode.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index 994d6e46472f5..540d0b07cb73a 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -1582,6 +1582,38 @@ int ocfs2_validate_inode_block(struct super_block *sb, goto bail; } + if (ocfs2_dinode_has_extents(di)) { + struct ocfs2_extent_list *el = &di->id2.i_list; + u16 count = le16_to_cpu(el->l_count); + u16 next_free = le16_to_cpu(el->l_next_free_rec); + + if (count == 0) { + rc = ocfs2_error(sb, + "Invalid dinode %llu: extent list l_count is zero\n", + (unsigned long long)bh->b_blocknr); + goto bail; + } + /* + * The exact capacity depends on i_xattr_inline_size, another + * unvalidated on-disk field. Inline xattrs only shrink the + * list, so the no-xattr maximum is a safe upper bound that a + * valid l_count never exceeds. + */ + if (count > ocfs2_extent_recs_per_inode(sb)) { + rc = ocfs2_error(sb, + "Invalid dinode %llu: extent list l_count %u exceeds max %u\n", + (unsigned long long)bh->b_blocknr, count, + ocfs2_extent_recs_per_inode(sb)); + goto bail; + } + if (next_free > count) { + rc = ocfs2_error(sb, + "Invalid dinode %llu: extent list l_next_free_rec %u exceeds l_count %u\n", + (unsigned long long)bh->b_blocknr, next_free, count); + goto bail; + } + } + rc = 0; bail: From 63921f790234b221e4b15c56c72888b13250f81d Mon Sep 17 00:00:00 2001 From: Joseph Qi Date: Sun, 31 May 2026 21:16:45 +0800 Subject: [PATCH 2426/2758] ocfs2: add journal NULL check in ocfs2_checkpoint_inode() commit a291c77c034b7a81849ce9b71cc9ecda9e587d89 upstream. During unmount, ocfs2_journal_shutdown() frees the journal and sets osb->journal to NULL. Later, when VFS evicts remaining cached inodes, ocfs2_evict_inode() -> ocfs2_clear_inode() -> ocfs2_checkpoint_inode() -> ocfs2_ci_fully_checkpointed() dereferences osb->journal, causing a NULL pointer dereference. Fix this by adding a NULL check for osb->journal in ocfs2_checkpoint_inode(). If the journal is NULL, it has already been fully flushed and destroyed during shutdown, so there is nothing to checkpoint. Link: https://lore.kernel.org/20260531131645.3650299-1-joseph.qi@linux.alibaba.com Reported-by: Farhad Alemi Fixes: da5e7c87827e ("ocfs2: cleanup journal init and shutdown") Signed-off-by: Joseph Qi Tested-by: Farhad Alemi Reviewed-by: Heming Zhao Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/ocfs2/journal.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h index 6397170f302f2..f8b3b2a3d6309 100644 --- a/fs/ocfs2/journal.h +++ b/fs/ocfs2/journal.h @@ -196,6 +196,9 @@ static inline void ocfs2_checkpoint_inode(struct inode *inode) if (ocfs2_mount_local(osb)) return; + if (!osb->journal) + return; + if (!ocfs2_ci_fully_checkpointed(INODE_CACHE(inode))) { /* WARNING: This only kicks off a single * checkpoint. If someone races you and adds more From b858f2d57cfc9d57ce61b86051d603dc0ebccd40 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 19 May 2026 07:04:02 -0400 Subject: [PATCH 2427/2758] ocfs2: reject dinodes with non-canonical i_mode type commit 5366a017099c6a3c443be908a05f26fd72af12a1 upstream. Patch series "ocfs2: harden inode validators against forged metadata", v2. This series adds three structural checks to OCFS2 dinode validation so malformed on-disk fields are rejected before ocfs2_populate_inode() copies them into the in-core inode. The checks cover: - i_mode values whose type bits do not name a canonical POSIX file type; - non-device dinodes whose id1.dev1.i_rdev field is non-zero; and - non-inline dinodes that claim non-zero i_size while i_clusters is zero, covering directories unconditionally and regular files on non-sparse volumes. The normal read path reports these through ocfs2_error(), matching the existing suballoc-slot, inline-data, chain-list, and refcount checks. The online filecheck path uses the same structural predicates but keeps its own reporting contract, returning OCFS2_FILECHECK_ERR_INVALIDINO instead of calling ocfs2_error(). This patch (of 3): ocfs2_validate_inode_block() currently accepts any non-zero i_mode value. ocfs2_populate_inode() then copies that mode verbatim into inode->i_mode and dispatches on i_mode & S_IFMT to the file/dir/symlink/special_file iops; an unrecognised type falls through to ocfs2_special_file_iops and init_special_inode(). Reject dinodes whose type bits do not name one of the seven canonical POSIX file types. Use fs_umode_to_ftype(), the same generic file-type conversion helper OCFS2 already uses for directory entries, so the accepted inode type set matches the kernel file-type vocabulary instead of open-coding a local switch. Apply the same structural check to the online filecheck read path. filecheck keeps its own error namespace, so it reports malformed i_mode through the filecheck logger and OCFS2_FILECHECK_ERR_INVALIDINO instead of calling ocfs2_error(), but it must not allow a malformed dinode to proceed into ocfs2_populate_inode(). Link: https://lore.kernel.org/20260519110404.1803902-1-michael.bommarito@gmail.com Link: https://lore.kernel.org/20260519110404.1803902-2-michael.bommarito@gmail.com Fixes: b657c95c1108 ("ocfs2: Wrap inode block reads in a dedicated function.") Signed-off-by: Michael Bommarito Link: https://sashiko.dev/#/patchset/20260517111015.3187935-1-michael.bommarito%40gmail.com Assisted-by: Claude:claude-opus-4-7 Reviewed-by: Joseph Qi Cc: Changwei Ge Cc: Heming Zhao Cc: Joel Becker Cc: Jun Piao Cc: Junxiao Bi Cc: Mark Fasheh Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/ocfs2/inode.c | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index 540d0b07cb73a..2682a31ea1422 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -64,7 +65,12 @@ static int ocfs2_filecheck_read_inode_block_full(struct inode *inode, static int ocfs2_filecheck_validate_inode_block(struct super_block *sb, struct buffer_head *bh); static int ocfs2_filecheck_repair_inode_block(struct super_block *sb, - struct buffer_head *bh); + struct buffer_head *bh); + +static bool ocfs2_valid_inode_mode(umode_t mode) +{ + return fs_umode_to_ftype(mode) != FT_UNKNOWN; +} void ocfs2_set_inode_flags(struct inode *inode) { @@ -1494,6 +1500,24 @@ int ocfs2_validate_inode_block(struct super_block *sb, goto bail; } + /* + * Reject dinodes whose i_mode does not name one of the seven + * canonical POSIX file types. ocfs2_populate_inode() copies + * i_mode verbatim into inode->i_mode and then dispatches via + * switch (mode & S_IFMT) to file/dir/symlink/special_file iops; + * an unrecognised type falls into ocfs2_special_file_iops with + * init_special_inode(), which interprets i_rdev. Constrain the + * type here so the dispatch only ever sees a value mkfs.ocfs2 / + * VFS can produce. + */ + if (!ocfs2_valid_inode_mode(le16_to_cpu(di->i_mode))) { + rc = ocfs2_error(sb, + "Invalid dinode #%llu: mode 0%o has unknown file type\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(di->i_mode)); + goto bail; + } + if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL) { struct ocfs2_inline_data *data = &di->id2.i_data; @@ -1679,6 +1703,15 @@ static int ocfs2_filecheck_validate_inode_block(struct super_block *sb, (unsigned long long)bh->b_blocknr, le32_to_cpu(di->i_fs_generation)); rc = -OCFS2_FILECHECK_ERR_GENERATION; + goto bail; + } + + if (!ocfs2_valid_inode_mode(le16_to_cpu(di->i_mode))) { + mlog(ML_ERROR, + "Filecheck: invalid dinode #%llu: mode 0%o has unknown file type\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(di->i_mode)); + rc = -OCFS2_FILECHECK_ERR_INVALIDINO; } bail: @@ -1867,4 +1900,3 @@ const struct ocfs2_caching_operations ocfs2_inode_caching_ops = { .co_io_lock = ocfs2_inode_cache_io_lock, .co_io_unlock = ocfs2_inode_cache_io_unlock, }; - From 60ceecda550e3d9683fb4b7604b36a014ae044cd Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 19 May 2026 07:04:03 -0400 Subject: [PATCH 2428/2758] ocfs2: reject dinodes whose i_rdev disagrees with the file type commit 51407c2d249987a466416890a5c931a2354a46aa upstream. id1.dev1.i_rdev is the device-number arm of the ocfs2_dinode id1 union. It is only meaningful for character and block device inodes. For any other user-visible file type the on-disk value must be zero. ocfs2_populate_inode() currently copies id1.dev1.i_rdev into inode->i_rdev before the S_IFMT switch decides whether the inode is a special file. A non-device inode with a non-zero i_rdev can therefore publish stale or attacker-controlled device state into the in-core inode. System inodes legitimately use other arms of the same union, so keep the cross-check restricted to non-system inodes. Factor that predicate into a helper and use it in both the normal validator and online filecheck path; filecheck reports the malformed dinode through OCFS2_FILECHECK_ERR_INVALIDINO instead of ocfs2_error(). Link: https://lore.kernel.org/20260519110404.1803902-3-michael.bommarito@gmail.com Fixes: b657c95c1108 ("ocfs2: Wrap inode block reads in a dedicated function.") Signed-off-by: Michael Bommarito Assisted-by: Claude:claude-opus-4-7 Reviewed-by: Joseph Qi Cc: Changwei Ge Cc: Heming Zhao Cc: Joel Becker Cc: Jun Piao Cc: Junxiao Bi Cc: Mark Fasheh Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/ocfs2/inode.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index 2682a31ea1422..45bdcf4794c51 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -72,6 +72,16 @@ static bool ocfs2_valid_inode_mode(umode_t mode) return fs_umode_to_ftype(mode) != FT_UNKNOWN; } +static bool ocfs2_dinode_has_unexpected_rdev(struct ocfs2_dinode *di) +{ + umode_t mode = le16_to_cpu(di->i_mode); + + if (le32_to_cpu(di->i_flags) & OCFS2_SYSTEM_FL) + return false; + + return !S_ISCHR(mode) && !S_ISBLK(mode) && di->id1.dev1.i_rdev != 0; +} + void ocfs2_set_inode_flags(struct inode *inode) { unsigned int flags = OCFS2_I(inode)->ip_attr; @@ -1518,6 +1528,41 @@ int ocfs2_validate_inode_block(struct super_block *sb, goto bail; } + /* + * id1.dev1.i_rdev is the device-number arm of the id1 union and + * is only meaningful for character and block device inodes. For + * any other regular user-visible file type the on-disk value + * must be zero. ocfs2_populate_inode() currently runs + * + * inode->i_rdev = huge_decode_dev(le64_to_cpu(fe->id1.dev1.i_rdev)); + * + * unconditionally, before the S_IFMT switch decides whether the + * inode is a special file. As a result, an i_rdev value present + * on a non-device inode is silently published into the in-core + * inode; a subsequent forced re-read or in-core mode mutation + * (cluster peer with raw write access to the shared LUN, + * on-disk corruption, or a separately forged dinode) can then + * expose the attacker-controlled device number to + * init_special_inode() without ever showing an unusual i_mode + * at validation time. + * + * System inodes (OCFS2_SYSTEM_FL) legitimately use the bitmap1 + * and journal1 arms of the same union (allocator i_used / + * i_total counters and the journal ij_flags / + * ij_recovery_generation pair); those bytes are not an i_rdev + * and must not be checked here. Restrict the cross-check to + * non-system inodes, which is the full attacker-controllable + * surface. + */ + if (ocfs2_dinode_has_unexpected_rdev(di)) { + rc = ocfs2_error(sb, + "Invalid dinode #%llu: non-device mode 0%o with i_rdev %llu\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(di->i_mode), + (unsigned long long)le64_to_cpu(di->id1.dev1.i_rdev)); + goto bail; + } + if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL) { struct ocfs2_inline_data *data = &di->id2.i_data; @@ -1712,6 +1757,16 @@ static int ocfs2_filecheck_validate_inode_block(struct super_block *sb, (unsigned long long)bh->b_blocknr, le16_to_cpu(di->i_mode)); rc = -OCFS2_FILECHECK_ERR_INVALIDINO; + goto bail; + } + + if (ocfs2_dinode_has_unexpected_rdev(di)) { + mlog(ML_ERROR, + "Filecheck: invalid dinode #%llu: non-device mode 0%o with i_rdev %llu\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(di->i_mode), + (unsigned long long)le64_to_cpu(di->id1.dev1.i_rdev)); + rc = -OCFS2_FILECHECK_ERR_INVALIDINO; } bail: From 3bfeb436d4be6a2beb56cab344770e93e6b07260 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 19 May 2026 07:04:04 -0400 Subject: [PATCH 2429/2758] ocfs2: reject non-inline dinodes with i_size and zero i_clusters commit 7ebc672fab7a76e1e47e0f2fc1ee48118d27fde4 upstream. On a volume mounted without OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC, a non-inline regular file with non-zero i_size and zero i_clusters is structurally malformed: the extent map declares no allocated clusters yet the size header claims content exists. Keep rejecting that shape, but express it through a shared predicate so the same invariant is available to normal inode reads and online filecheck. The same zero-cluster shape is also malformed for non-inline directories. ocfs2 directory growth allocates backing storage before advancing i_size, and ocfs2_dir_foreach_blk_el() later walks until ctx->pos reaches i_size_read(inode). A forged directory dinode with a huge i_size and no clusters would repeatedly fail on holes while advancing through the claimed size. Sparse regular files remain exempt: on sparse-alloc volumes, truncate can legitimately grow i_size without allocating clusters. System inodes and inline-data dinodes also retain their separate storage rules. Mirror the check in ocfs2_filecheck_validate_inode_block() as well. filecheck reports through its own error namespace, so malformed size/cluster state is logged as a filecheck invalid-inode result rather than via ocfs2_error(), but it must not proceed into ocfs2_populate_inode(). Link: https://lore.kernel.org/20260519110404.1803902-4-michael.bommarito@gmail.com Fixes: b657c95c1108 ("ocfs2: Wrap inode block reads in a dedicated function.") Signed-off-by: Michael Bommarito Link: https://sashiko.dev/#/patchset/20260517111015.3187935-1-michael.bommarito%40gmail.com Assisted-by: Claude:claude-opus-4-7 Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/ocfs2/inode.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index 45bdcf4794c51..662dbc845b8bd 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -82,6 +82,24 @@ static bool ocfs2_dinode_has_unexpected_rdev(struct ocfs2_dinode *di) return !S_ISCHR(mode) && !S_ISBLK(mode) && di->id1.dev1.i_rdev != 0; } +static bool ocfs2_dinode_has_size_without_clusters(struct super_block *sb, + struct ocfs2_dinode *di) +{ + umode_t mode = le16_to_cpu(di->i_mode); + + if (le32_to_cpu(di->i_flags) & OCFS2_SYSTEM_FL) + return false; + if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL) + return false; + if (!le64_to_cpu(di->i_size) || le32_to_cpu(di->i_clusters)) + return false; + + if (S_ISDIR(mode)) + return true; + + return !ocfs2_sparse_alloc(OCFS2_SB(sb)) && S_ISREG(mode); +} + void ocfs2_set_inode_flags(struct inode *inode) { unsigned int flags = OCFS2_I(inode)->ip_attr; @@ -1563,6 +1581,33 @@ int ocfs2_validate_inode_block(struct super_block *sb, goto bail; } + /* + * Non-inline directories must not have i_size without allocated + * clusters: directory growth adds storage before advancing i_size, + * and readdir walks i_size block-by-block. A forged directory + * with zero clusters and a huge i_size would repeatedly fault on + * holes while advancing through the claimed size. + * + * Non-inline regular files have the same invariant on non-sparse + * volumes. Sparse regular files are different: truncate can + * legitimately grow i_size without allocating clusters, so keep + * the sparse-alloc carveout for S_IFREG only. System inodes and + * inline-data dinodes have their own storage rules. + */ + if (ocfs2_dinode_has_size_without_clusters(sb, di)) { + if (S_ISDIR(le16_to_cpu(di->i_mode))) + rc = ocfs2_error(sb, + "Invalid dinode #%llu: directory i_size %llu with i_clusters 0 and no inline-data flag\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)le64_to_cpu(di->i_size)); + else + rc = ocfs2_error(sb, + "Invalid dinode #%llu: regular file i_size %llu with i_clusters 0 and no inline-data flag on non-sparse volume\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)le64_to_cpu(di->i_size)); + goto bail; + } + if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL) { struct ocfs2_inline_data *data = &di->id2.i_data; @@ -1767,6 +1812,21 @@ static int ocfs2_filecheck_validate_inode_block(struct super_block *sb, le16_to_cpu(di->i_mode), (unsigned long long)le64_to_cpu(di->id1.dev1.i_rdev)); rc = -OCFS2_FILECHECK_ERR_INVALIDINO; + goto bail; + } + + if (ocfs2_dinode_has_size_without_clusters(sb, di)) { + if (S_ISDIR(le16_to_cpu(di->i_mode))) + mlog(ML_ERROR, + "Filecheck: invalid dinode #%llu: directory i_size %llu with i_clusters 0 and no inline-data flag\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)le64_to_cpu(di->i_size)); + else + mlog(ML_ERROR, + "Filecheck: invalid dinode #%llu: regular file i_size %llu with i_clusters 0 and no inline-data flag on non-sparse volume\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)le64_to_cpu(di->i_size)); + rc = -OCFS2_FILECHECK_ERR_INVALIDINO; } bail: From f3df5386e3bb54626c117bd85e0158d45fb4aea9 Mon Sep 17 00:00:00 2001 From: Sebastian Alba Vives Date: Mon, 18 May 2026 13:07:40 -0600 Subject: [PATCH 2430/2758] fpga: dfl: add bounds check in dfh_get_param_size() commit 9e8bc49f91f3f81d957c4f1c1f09fe94e2f88f6a upstream. dfh_get_param_size() can return a parameter size larger than the feature region because the loop bounds check is evaluated before incrementing size. If the EOP (End of Parameters) bit is set in the same iteration, the inflated size is returned without re-validation against max. This can cause create_feature_instance() to call memcpy_fromio() with a size exceeding the ioremap'd region when a malicious FPGA device provides crafted DFHv1 parameter headers. Add a bounds check after the size increment to ensure the accumulated size never exceeds the feature boundary. Fixes: 4747ab89b4a6 ("fpga: dfl: add basic support for DFHv1") Cc: stable@vger.kernel.org Signed-off-by: Sebastian Alba Vives Reviewed-by: Xu Yilun Link: https://lore.kernel.org/r/20260518190742.61426-2-sebasjosue84@gmail.com Signed-off-by: Xu Yilun Signed-off-by: Greg Kroah-Hartman --- drivers/fpga/dfl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c index 4087a36a0571a..4c63c7c8579bb 100644 --- a/drivers/fpga/dfl.c +++ b/drivers/fpga/dfl.c @@ -1132,6 +1132,8 @@ static int dfh_get_param_size(void __iomem *dfh_base, resource_size_t max) return -EINVAL; size += next * sizeof(u64); + if (size > max) + return -EINVAL; if (FIELD_GET(DFHv1_PARAM_HDR_NEXT_EOP, v)) return size; From e0578493c950e155149560afc69e7048a8da67ce Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Wed, 27 May 2026 10:46:13 +0200 Subject: [PATCH 2431/2758] bus: mhi: host: pci_generic: Fix the physical function check commit 32845111e8ea6ef5e837b6d25080e69e99bd4561 upstream. Commit b4d01c5b9a9d ("bus: mhi: host: pci_generic: Read SUBSYSTEM_VENDOR_ID for VF's to check status") added the check for physical function by checking for 'pdev->is_physfn. But 'pdev->is_physfn' is only set for the physical function of a SR-IOV capable device. But for the non-SR-IOV device this variable will be 0. So this check ended up breaking the health check functionality for all non-SR-IOV devices. Fix it by checking for '!pdev->is_virtfn' to make sure that the check is only skipped for virtual functions. Cc: stable@vger.kernel.org # 6.18 Reported-by: Slark Xiao Tested-by: Slark Xiao Fixes: b4d01c5b9a9d ("bus: mhi: host: pci_generic: Read SUBSYSTEM_VENDOR_ID for VF's to check status") Signed-off-by: Manivannan Sadhasivam Signed-off-by: Greg Kroah-Hartman --- drivers/bus/mhi/host/pci_generic.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c index 750da3dbb4c6e..a738333bfc758 100644 --- a/drivers/bus/mhi/host/pci_generic.c +++ b/drivers/bus/mhi/host/pci_generic.c @@ -1247,7 +1247,7 @@ static void mhi_pci_recovery_work(struct work_struct *work) dev_warn(&pdev->dev, "device recovery started\n"); - if (pdev->is_physfn) + if (!pdev->is_virtfn) timer_delete(&mhi_pdev->health_check_timer); pm_runtime_forbid(&pdev->dev); @@ -1277,7 +1277,7 @@ static void mhi_pci_recovery_work(struct work_struct *work) set_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status); - if (pdev->is_physfn) + if (!pdev->is_virtfn) mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD); return; @@ -1368,7 +1368,7 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) mhi_cntrl_config = info->config; /* Initialize health check monitor only for Physical functions */ - if (pdev->is_physfn) + if (!pdev->is_virtfn) timer_setup(&mhi_pdev->health_check_timer, health_check, 0); mhi_cntrl = &mhi_pdev->mhi_cntrl; @@ -1390,7 +1390,7 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) mhi_cntrl->mru = info->mru_default; mhi_cntrl->name = info->name; - if (pdev->is_physfn) + if (!pdev->is_virtfn) mhi_pdev->reset_on_remove = info->reset_on_remove; if (info->edl_trigger) @@ -1439,7 +1439,7 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) set_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status); /* start health check */ - if (pdev->is_physfn) + if (!pdev->is_virtfn) mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD); /* Allow runtime suspend only if both PME from D3Hot and M3 are supported */ @@ -1468,7 +1468,7 @@ static void mhi_pci_remove(struct pci_dev *pdev) pm_runtime_forbid(&pdev->dev); pci_disable_sriov(pdev); - if (pdev->is_physfn) + if (!pdev->is_virtfn) timer_delete_sync(&mhi_pdev->health_check_timer); cancel_work_sync(&mhi_pdev->recovery_work); @@ -1500,7 +1500,7 @@ static void mhi_pci_reset_prepare(struct pci_dev *pdev) dev_info(&pdev->dev, "reset\n"); - if (pdev->is_physfn) + if (!pdev->is_virtfn) timer_delete(&mhi_pdev->health_check_timer); /* Clean up MHI state */ @@ -1546,7 +1546,7 @@ static void mhi_pci_reset_done(struct pci_dev *pdev) } set_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status); - if (pdev->is_physfn) + if (!pdev->is_virtfn) mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD); } @@ -1612,7 +1612,7 @@ static int __maybe_unused mhi_pci_runtime_suspend(struct device *dev) if (test_and_set_bit(MHI_PCI_DEV_SUSPENDED, &mhi_pdev->status)) return 0; - if (pdev->is_physfn) + if (!pdev->is_virtfn) timer_delete(&mhi_pdev->health_check_timer); cancel_work_sync(&mhi_pdev->recovery_work); @@ -1665,7 +1665,7 @@ static int __maybe_unused mhi_pci_runtime_resume(struct device *dev) } /* Resume health check */ - if (pdev->is_physfn) + if (!pdev->is_virtfn) mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD); /* It can be a remote wakeup (no mhi runtime_get), update access time */ From 4f6542b1428893a64b7477e64d9c518c6c8f0003 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 2 Mar 2026 14:26:12 +0530 Subject: [PATCH 2432/2758] bus: mhi: ep: Protect mhi_ep_handle_syserr() in the error path commit 519ddf194b158b91439319f6b977b8a465fda0fb upstream. All the callers of mhi_ep_handle_syserr() except mhi_ep_process_cmd_ring() are holding the 'state_lock' to avoid the race in setting the MHI state. So do the same in mhi_ep_process_cmd_ring() for sanity. Fixes: e827569062a8 ("bus: mhi: ep: Add support for processing command rings") Cc: stable@vger.kernel.org # 5.18 Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260302085612.18725-1-manivannan.sadhasivam@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/bus/mhi/ep/main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c index 5330b03dc6369..9db2a2a2c9134 100644 --- a/drivers/bus/mhi/ep/main.c +++ b/drivers/bus/mhi/ep/main.c @@ -232,7 +232,9 @@ static int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ring_ele ret = mhi_ep_create_device(mhi_cntrl, ch_id); if (ret) { dev_err(dev, "Error creating device for channel (%u)\n", ch_id); + mutex_lock(&mhi_cntrl->state_lock); mhi_ep_handle_syserr(mhi_cntrl); + mutex_unlock(&mhi_cntrl->state_lock); return ret; } } From fe6b606fbf0c3beb94ccf17fcf31d8c2138264e3 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Wed, 17 Jun 2026 01:38:41 +0800 Subject: [PATCH 2433/2758] net: thunderbolt: Fix frags[] overflow by bounding frame_count commit 55d9895f89970501fe126d1026b586b04a224c27 upstream. tbnet_poll() assembles a multi-frame ThunderboltIP packet into one skb. The first frame goes into the skb linear area and every further frame is added as a page fragment. skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, hdr_size, frame_size, TBNET_RX_PAGE_SIZE - hdr_size); A packet of frame_count frames therefore ends up with frame_count - 1 fragments. tbnet_check_frame() only bounds the peer supplied frame_count to TBNET_RING_SIZE / 4 (64), which is far above MAX_SKB_FRAGS (17 by default). A peer that sends a packet of 19 or more small frames pushes nr_frags past MAX_SKB_FRAGS, so skb_add_rx_frag() writes past skb_shinfo()->frags[] and corrupts memory after the shared info. Tighten the start of packet bound to MAX_SKB_FRAGS + 1 so a packet can never produce more fragments than frags[] can hold. This matches the recent skb frags overflow fixes in other receive paths, for example f0813bcd2d9d ("net: wwan: t7xx: fix potential skb->frags overflow in RX path") and 600dc40554dc ("net: usb: cdc-phonet: fix skb frags[] overflow in rx_complete()"). Fixes: e69b6c02b4c3 ("net: Add support for networking over Thunderbolt cable") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Acked-by: Mika Westerberg Link: https://patch.msgid.link/178163152194.2486768.14724194232649760778@maoyixie.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/thunderbolt/main.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/thunderbolt/main.c b/drivers/net/thunderbolt/main.c index 7aae5d915a1eb..ac016890646cf 100644 --- a/drivers/net/thunderbolt/main.c +++ b/drivers/net/thunderbolt/main.c @@ -787,8 +787,12 @@ static bool tbnet_check_frame(struct tbnet *net, const struct tbnet_frame *tf, return true; } - /* Start of packet, validate the frame header */ - if (frame_count == 0 || frame_count > TBNET_RING_SIZE / 4) { + /* Start of packet, validate the frame header. tbnet_poll() puts the + * first frame in the skb linear area and every further frame in a page + * fragment, so a packet may not span more than MAX_SKB_FRAGS + 1 frames + * without overflowing skb_shinfo()->frags[]. + */ + if (frame_count == 0 || frame_count > MAX_SKB_FRAGS + 1) { net->stats.rx_length_errors++; return false; } From 3da8eaf5469eda2353b72038d644fabddb848ce1 Mon Sep 17 00:00:00 2001 From: Sebastian Alba Vives Date: Mon, 18 May 2026 13:07:42 -0600 Subject: [PATCH 2434/2758] fpga: microchip-spi: fix zero header_size OOB read in mpf_ops_parse_header() commit 43a1974da6bc7ce8f4d1dc1d03d56997428c29c3 upstream. mpf_ops_parse_header() reads header_size from the bitstream at MPF_HEADER_SIZE_OFFSET (24). When header_size is zero, the expression *(buf + header_size - 1) reads one byte before the buffer start. Since initial_header_size is set to 71 in mpf_ops, the fpga-mgr core guarantees the buffer is large enough to reach MPF_HEADER_SIZE_OFFSET. The only real gap is the zero header_size case, which cannot be resolved by providing a larger buffer, so return -EINVAL. Fixes: 5f8d4a900830 ("fpga: microchip-spi: add Microchip MPF FPGA manager") Cc: stable@vger.kernel.org Signed-off-by: Sebastian Alba Vives Reviewed-by: Xu Yilun Link: https://lore.kernel.org/r/20260518190742.61426-4-sebasjosue84@gmail.com Signed-off-by: Xu Yilun Signed-off-by: Greg Kroah-Hartman --- drivers/fpga/microchip-spi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/fpga/microchip-spi.c b/drivers/fpga/microchip-spi.c index 6134cea86ac85..cc8f6d7bb9781 100644 --- a/drivers/fpga/microchip-spi.c +++ b/drivers/fpga/microchip-spi.c @@ -116,6 +116,9 @@ static int mpf_ops_parse_header(struct fpga_manager *mgr, } header_size = *(buf + MPF_HEADER_SIZE_OFFSET); + if (!header_size) + return -EINVAL; + if (header_size > count) { info->header_size = header_size; return -EAGAIN; From 7e7e03848c918aa0acad5ffd75d929e3afec1554 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Mon, 15 Jun 2026 17:39:12 +0200 Subject: [PATCH 2435/2758] s390/pkey: Check length in PKEY_VERIFYPROTK ioctl commit b3d4ab2d7df9426f7f1d3671d7e2108f2ca6e970 upstream. Explicitly check the buffer length request structure provided by user-space and fail, if it exceeds the buffer size. Cc: stable@vger.kernel.org Fixes: 8fcc231ce3be ("s390/pkey: Introduce pkey base with handler registry and handler modules") Reported-by: Christian Borntraeger Reviewed-by: Harald Freudenberger Reviewed-by: Ingo Franzki Signed-off-by: Holger Dengler Signed-off-by: Alexander Gordeev Signed-off-by: Greg Kroah-Hartman --- drivers/s390/crypto/pkey_api.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/s390/crypto/pkey_api.c b/drivers/s390/crypto/pkey_api.c index d6b595eb33709..28e1007005f21 100644 --- a/drivers/s390/crypto/pkey_api.c +++ b/drivers/s390/crypto/pkey_api.c @@ -334,6 +334,13 @@ static int pkey_ioctl_verifyprotk(struct pkey_verifyprotk __user *uvp) if (copy_from_user(&kvp, uvp, sizeof(kvp))) return -EFAULT; + if (kvp.protkey.len > sizeof(kvp.protkey.protkey)) { + PKEY_DBF_ERR("%s protkey length %u exceeds protkey buffer size\n", + __func__, kvp.protkey.len); + memzero_explicit(&kvp, sizeof(kvp)); + return -EINVAL; + } + keytype = pkey_aes_bitsize_to_keytype(8 * kvp.protkey.len); if (!keytype) { PKEY_DBF_ERR("%s unknown/unsupported protkey length %u\n", From 614aa0491c7a190556c2345dddee0b6f5ed90989 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 17 Jun 2026 19:06:39 +0200 Subject: [PATCH 2436/2758] s390/pkey: Check length in pkey_pckmo handler implementation commit 1ac287e2af9a9112fe271427ef45eceb26bce8b4 upstream. Explicitly check the length of the target buffer in the pkey_pckmo implementation of the key_to_protkey() handler function. The handler function fails, if the generated output data exceeds the length of the provided target buffer. Cc: stable@vger.kernel.org Fixes: 8fcc231ce3be ("s390/pkey: Introduce pkey base with handler registry and handler modules") Reported-by: Christian Borntraeger Reviewed-by: Harald Freudenberger Signed-off-by: Holger Dengler Signed-off-by: Alexander Gordeev Signed-off-by: Greg Kroah-Hartman --- drivers/s390/crypto/pkey_pckmo.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/s390/crypto/pkey_pckmo.c b/drivers/s390/crypto/pkey_pckmo.c index ea774ab891806..0cc015cc9f6bd 100644 --- a/drivers/s390/crypto/pkey_pckmo.c +++ b/drivers/s390/crypto/pkey_pckmo.c @@ -257,6 +257,10 @@ static int pckmo_key2protkey(const u8 *key, u32 keylen, goto out; break; } + if (t->len > *protkeylen) { + rc = -EINVAL; + goto out; + } memcpy(protkey, t->protkey, t->len); *protkeylen = t->len; *protkeytype = t->keytype; From 12d4d69221159e6d0e72400ec81195d691169b53 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:26 +0200 Subject: [PATCH 2437/2758] mtd: spi-nor: swp: Improve locking user experience commit e1d456b26bf23e30db305a6184e8abd9ab68bbf2 upstream. In the case of the first block being locked (or the few first blocks), if the user want to fully unlock the device it has two possibilities: - either it asks to unlock the entire device, and this works; - or it asks to unlock just the block(s) that are currently locked, which fails. It fails because the conditions "can_be_top" and "can_be_bottom" are true. Indeed, in this case, we unlock everything, so the TB bit does not matter. However in the current implementation, use_top would be true (as this is the favourite option) and lock_len, which in practice should be reduced down to 0, is set to "nor->params->size - (ofs + len)" which is a positive number. This is wrong. An easy way is to simply add an extra condition. In the unlock() path, if we can achieve the same result from both sides, it means we unlock everything and lock_len must simply be 0. A comment is added to clarify that logic. Fixes: 3dd8012a8eeb ("mtd: spi-nor: add TB (Top/Bottom) protect support") Cc: stable@kernel.org Signed-off-by: Miquel Raynal Reviewed-by: Michael Walle Signed-off-by: Pratyush Yadav Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/spi-nor/swp.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/spi-nor/swp.c b/drivers/mtd/spi-nor/swp.c index e67a81dbb6bf6..d5f4bf555cfc6 100644 --- a/drivers/mtd/spi-nor/swp.c +++ b/drivers/mtd/spi-nor/swp.c @@ -282,8 +282,15 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len) /* Prefer top, if both are valid */ use_top = can_be_top; - /* lock_len: length of region that should remain locked */ - if (use_top) + /* + * lock_len: length of region that should remain locked. + * + * When can_be_top and can_be_bottom booleans are true, both adjacent + * regions are unlocked, thus the entire flash can be unlocked. + */ + if (can_be_top && can_be_bottom) + lock_len = 0; + else if (use_top) lock_len = nor->params->size - (ofs + len); else lock_len = ofs; From 007e28b2916d07b2a9a608d94c421d944d7b76aa Mon Sep 17 00:00:00 2001 From: Takahiro Kuwano Date: Wed, 27 May 2026 18:05:39 +0900 Subject: [PATCH 2438/2758] mtd: spi-nor: spansion: use die erase for multi-die devices only commit 306e443156b82a2a14a3f33da908c303be45529c upstream. Die erase opcode is supported in multi-die devices only. For single die devices, default chip erase opcode must be used. In s25hx_t_late_init(), die erase opcode is set only when the device is multi-die. Fixes: 461d0babb544 ("mtd: spi-nor: spansion: enable die erase for multi die flashes") Cc: stable@kernel.org Reviewed-by: Pratyush Yadav Reviewed-by: Tudor Ambarus Reviewed-by: Miquel Raynal Signed-off-by: Takahiro Kuwano Reviewed-by: Michael Walle Signed-off-by: Pratyush Yadav Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/spi-nor/spansion.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index 8498c7003d888..b6023076903a3 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -674,7 +674,9 @@ static int s25hx_t_late_init(struct spi_nor *nor) params->ready = cypress_nor_sr_ready_and_clear; cypress_nor_ecc_init(nor); - params->die_erase_opcode = SPINOR_OP_CYPRESS_DIE_ERASE; + if (params->n_dice > 1) + params->die_erase_opcode = SPINOR_OP_CYPRESS_DIE_ERASE; + return 0; } From a145b47e22fdff38e475c5e3159de3ed086dece8 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 22 May 2026 11:17:39 +0200 Subject: [PATCH 2439/2758] mtd: rawnand: Pause continuous reads at block boundaries commit 8e4531667d718e2e9b193928cf9b2497fa0d01ef upstream. Some chips do not support sequential cached reads past block boundaries, like Winbond. In practice when using UBI, this should very rarely happen, but let's make sure it never happens. Cc: stable@vger.kernel.org Fixes: 003fe4b9545b ("mtd: rawnand: Support for sequential cache reads") Signed-off-by: Miquel Raynal Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/nand/raw/nand_base.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index e4e1383bd5e1a..a5b278ab9384f 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -1216,32 +1216,32 @@ static int nand_lp_exec_read_page_op(struct nand_chip *chip, unsigned int page, return nand_exec_op(chip, &op); } -static unsigned int rawnand_last_page_of_lun(unsigned int pages_per_lun, unsigned int lun) +static unsigned int rawnand_last_page_of_block(unsigned int ppb, unsigned int block) { - /* lun is expected to be very small */ - return (lun * pages_per_lun) + pages_per_lun - 1; + /* block is expected to be very small */ + return (block * ppb) + ppb - 1; } static void rawnand_cap_cont_reads(struct nand_chip *chip) { struct nand_memory_organization *memorg; - unsigned int ppl, first_lun, last_lun; + unsigned int ppb, first_block, last_block; memorg = nanddev_get_memorg(&chip->base); - ppl = memorg->pages_per_eraseblock * memorg->eraseblocks_per_lun; - first_lun = chip->cont_read.first_page / ppl; - last_lun = chip->cont_read.last_page / ppl; + ppb = memorg->pages_per_eraseblock; + first_block = chip->cont_read.first_page / ppb; + last_block = chip->cont_read.last_page / ppb; - /* Prevent sequential cache reads across LUN boundaries */ - if (first_lun != last_lun) - chip->cont_read.pause_page = rawnand_last_page_of_lun(ppl, first_lun); + /* Prevent sequential cache reads across block boundaries */ + if (first_block != last_block) + chip->cont_read.pause_page = rawnand_last_page_of_block(ppb, first_block); else chip->cont_read.pause_page = chip->cont_read.last_page; if (chip->cont_read.first_page == chip->cont_read.pause_page) { chip->cont_read.first_page++; chip->cont_read.pause_page = min(chip->cont_read.last_page, - rawnand_last_page_of_lun(ppl, first_lun + 1)); + rawnand_last_page_of_block(ppb, first_block + 1)); } if (chip->cont_read.first_page >= chip->cont_read.last_page) From 57740658042daf591c57d6e700d9a304d5972552 Mon Sep 17 00:00:00 2001 From: Stafford Horne Date: Fri, 22 May 2026 17:28:31 +0100 Subject: [PATCH 2440/2758] openrisc: Fix jump_label smp syncing commit aca063c9024522e4e5b9a9d1927433f6a01785a3 upstream. The original commit 8c30b0018f9d ("openrisc: Add jump label support") copies from arm64 and does not properly consider how icache invalidation on remote cores works in OpenRISC. On OpenRISC remote icaches need to be invalidated otherwise static key's may remain state after updating. Fix SMP cache syncing by: 1. Properly invalidate remote core icaches on SMP systems by using icache_all_inv. The old code uses kick_all_cpus_sync() which runs a no-op IPI function call on remote CPU's which does execute a lot of code and flushes many cache lines in the process, but does not flush all and it's not correct on OpenRISC. 2. For architectures that do not have WRITETHROUGH caches be sure to flush the dcache after patching. To test this I first reproduced the issue using a custom test module [0]. The test confirmed that some icache lines maintained stale static_key code sequences after calling static_branch_enable(). After this patch there are no longer jump_label coherency issues. [0] https://github.com/stffrdhrn/or1k-utils/tree/master/tests/smp_static_key_test Cc: stable@vger.kernel.org # depends on openrisc: Add icache_all_inv Fixes: 8c30b0018f9d ("openrisc: Add jump label support") Signed-off-by: Stafford Horne Signed-off-by: Greg Kroah-Hartman --- arch/openrisc/kernel/jump_label.c | 2 +- arch/openrisc/kernel/patching.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/openrisc/kernel/jump_label.c b/arch/openrisc/kernel/jump_label.c index ab7137c23b46c..9cb63f2d2e2b7 100644 --- a/arch/openrisc/kernel/jump_label.c +++ b/arch/openrisc/kernel/jump_label.c @@ -47,5 +47,5 @@ bool arch_jump_label_transform_queue(struct jump_entry *entry, void arch_jump_label_transform_apply(void) { - kick_all_cpus_sync(); + icache_all_inv(); } diff --git a/arch/openrisc/kernel/patching.c b/arch/openrisc/kernel/patching.c index d186172beb337..5db027b78bc40 100644 --- a/arch/openrisc/kernel/patching.c +++ b/arch/openrisc/kernel/patching.c @@ -49,6 +49,9 @@ static int __patch_insn_write(void *addr, u32 insn) waddr = patch_map(addr, FIX_TEXT_POKE0); ret = copy_to_kernel_nofault(waddr, &insn, OPENRISC_INSN_SIZE); + if (!IS_ENABLED(CONFIG_DCACHE_WRITETHROUGH)) + local_dcache_range_flush((unsigned long)waddr, + (unsigned long)waddr + OPENRISC_INSN_SIZE); local_icache_range_inv((unsigned long)waddr, (unsigned long)waddr + OPENRISC_INSN_SIZE); From f4599793240dd60a0a708ef1fe116223f47aa105 Mon Sep 17 00:00:00 2001 From: Florian Fuchs Date: Mon, 18 May 2026 13:45:21 +0200 Subject: [PATCH 2441/2758] mtd: maps: vmu-flash: fix NULL pointer dereference in initialization commit 357e3b8e3a8769ba36eb8ec5e053e4825f1a9329 upstream. The mtd_info contains a struct device, which must be linked to its parent. Without this, the initialization of the MTD fails with a NULL pointer dereference. Fixes: 47a72688fae7 ("mtd: flash mapping support for Dreamcast VMU.") Cc: stable@vger.kernel.org Signed-off-by: Florian Fuchs Signed-off-by: Miquel Raynal Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/maps/vmu-flash.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/maps/vmu-flash.c b/drivers/mtd/maps/vmu-flash.c index b76d0609d1b76..10244e6731d07 100644 --- a/drivers/mtd/maps/vmu-flash.c +++ b/drivers/mtd/maps/vmu-flash.c @@ -547,6 +547,7 @@ static void vmu_queryblocks(struct mapleq *mq) mpart->partition = card->partition; mtd_cur->priv = mpart; mtd_cur->owner = THIS_MODULE; + mtd_cur->dev.parent = &mdev->dev; pcache = kzalloc_obj(struct vmu_cache); if (!pcache) From c267911b4226a3f6e7987fd9ee2d38719bb065bf Mon Sep 17 00:00:00 2001 From: Yiyang Chen Date: Mon, 13 Apr 2026 23:45:44 +0800 Subject: [PATCH 2442/2758] taskstats: retain dead thread stats in TGID queries commit b3e4fbb04220efc3bc022bcf31b5689d39c6b111 upstream. Patch series "taskstats: fix TGID dead-thread stat retention", v3. This series fixes a taskstats TGID aggregation bug where fields added in the TGID query path were not preserved after thread exit, and adds a kselftest covering the regression. The first patch keeps the cached TGID aggregate used for dead threads in step with the fields already accumulated for live threads, and also fixes the final TGID exit notification emitted when group_dead is true. The second patch adds a kselftest that verifies TGID CPU stats do not regress after a worker thread exits and has been reaped. This patch (of 2): fill_stats_for_tgid() builds TGID stats from two sources: the cached aggregate in signal->stats and a scan of the live threads in the group. However, fill_tgid_exit() only accumulates delay accounting into signal->stats. This means that once a thread exits, TGID queries lose the fields that fill_stats_for_tgid() adds for live threads. This gap was introduced incrementally by two earlier changes that extended fill_stats_for_tgid() but did not make the corresponding update to fill_tgid_exit(): - commit 8c733420bdd5 ("taskstats: add e/u/stime for TGID command") added ac_etime, ac_utime, and ac_stime to the TGID query path. - commit b663a79c1915 ("taskstats: add context-switch counters") added nvcsw and nivcsw to the TGID query path. As a result, those fields were accounted for live threads in TGID queries, but were dropped from the cached TGID aggregate after thread exit. The final TGID exit notification emitted when group_dead is true also copies that cached aggregate, so it loses the same fields. Factor the per-task TGID accumulation into tgid_stats_add_task() and use it in both fill_stats_for_tgid() and fill_tgid_exit(). This keeps the cached aggregate used for dead threads aligned with the live-thread accumulation used by TGID queries. Link: https://lore.kernel.org/cover.1776094300.git.cyyzero16@gmail.com Link: https://lore.kernel.org/abd2a15d33343636ab5ba43d540bcfe508bd66c7.1776094300.git.cyyzero16@gmail.com Fixes: 8c733420bdd5 ("taskstats: add e/u/stime for TGID command") Fixes: b663a79c1915 ("taskstats: add context-switch counters") Signed-off-by: Yiyang Chen Acked-by: Balbir Singh Cc: Dr. Thomas Orgis Cc: Oleg Nesterov Cc: Wang Yaxin Cc: Yang Yang Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- kernel/taskstats.c | 62 ++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/kernel/taskstats.c b/kernel/taskstats.c index 73bd6a6a78935..2cd0172d05160 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c @@ -210,13 +210,39 @@ static int fill_stats_for_pid(pid_t pid, struct taskstats *stats) return 0; } +static void tgid_stats_add_task(struct taskstats *stats, + struct task_struct *tsk, u64 now_ns) +{ + u64 delta, utime, stime; + + /* + * Each accounting subsystem calls its functions here to + * accumulate its per-task stats for tsk, into the per-tgid structure + * + * per-task-foo(stats, tsk); + */ + delayacct_add_tsk(stats, tsk); + + /* calculate task elapsed time in nsec */ + delta = now_ns - tsk->start_time; + /* Convert to micro seconds */ + do_div(delta, NSEC_PER_USEC); + stats->ac_etime += delta; + + task_cputime(tsk, &utime, &stime); + stats->ac_utime += div_u64(utime, NSEC_PER_USEC); + stats->ac_stime += div_u64(stime, NSEC_PER_USEC); + + stats->nvcsw += tsk->nvcsw; + stats->nivcsw += tsk->nivcsw; +} + static int fill_stats_for_tgid(pid_t tgid, struct taskstats *stats) { struct task_struct *tsk, *first; unsigned long flags; int rc = -ESRCH; - u64 delta, utime, stime; - u64 start_time; + u64 now_ns; /* * Add additional stats from live tasks except zombie thread group @@ -233,30 +259,12 @@ static int fill_stats_for_tgid(pid_t tgid, struct taskstats *stats) else memset(stats, 0, sizeof(*stats)); - start_time = ktime_get_ns(); + now_ns = ktime_get_ns(); for_each_thread(first, tsk) { if (tsk->exit_state) continue; - /* - * Accounting subsystem can call its functions here to - * fill in relevant parts of struct taskstsats as follows - * - * per-task-foo(stats, tsk); - */ - delayacct_add_tsk(stats, tsk); - - /* calculate task elapsed time in nsec */ - delta = start_time - tsk->start_time; - /* Convert to micro seconds */ - do_div(delta, NSEC_PER_USEC); - stats->ac_etime += delta; - task_cputime(tsk, &utime, &stime); - stats->ac_utime += div_u64(utime, NSEC_PER_USEC); - stats->ac_stime += div_u64(stime, NSEC_PER_USEC); - - stats->nvcsw += tsk->nvcsw; - stats->nivcsw += tsk->nivcsw; + tgid_stats_add_task(stats, tsk, now_ns); } unlock_task_sighand(first, &flags); @@ -275,18 +283,14 @@ static int fill_stats_for_tgid(pid_t tgid, struct taskstats *stats) static void fill_tgid_exit(struct task_struct *tsk) { unsigned long flags; + u64 now_ns; spin_lock_irqsave(&tsk->sighand->siglock, flags); if (!tsk->signal->stats) goto ret; - /* - * Each accounting subsystem calls its functions here to - * accumalate its per-task stats for tsk, into the per-tgid structure - * - * per-task-foo(tsk->signal->stats, tsk); - */ - delayacct_add_tsk(tsk->signal->stats, tsk); + now_ns = ktime_get_ns(); + tgid_stats_add_task(tsk->signal->stats, tsk, now_ns); ret: spin_unlock_irqrestore(&tsk->sighand->siglock, flags); return; From e5be5d452d5f1efbb3342da2b00029be6c99034e Mon Sep 17 00:00:00 2001 From: Bhargav Joshi Date: Sat, 20 Jun 2026 17:39:16 +0530 Subject: [PATCH 2443/2758] irqchip/crossbar: Use correct index in crossbar_domain_free() commit 043db005a8d6932dc7d217c86307e9af0bc10ddc upstream. crossbar_domain_free() resets the domain data and then uses the nulled out irq_data->hwirq member as index to reset the irq_map[] entry and to write the relevant crossbar register with a safe entry. That means it never frees the correct index and keeps the crossbar register connection to the source interrupt active. If it would not reset the domain data, then this would be even worse as irq_data->hwirq holds the source interrupt number, but both the map and register index need the corresponding GIC SPI number and not the source interrupt number. This might even result in an out of bounds access as the source interrupt number can be higher than the maximal index space. Fix this by using the GIC SPI index from the parent domain's irq_data. Fixes: 783d31863fb82 ("irqchip: crossbar: Convert dra7 crossbar to stacked domains") Signed-off-by: Bhargav Joshi Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260620-irq-crossbar-fix-v2-1-b8e8499f468a@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/irqchip/irq-crossbar.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index 3035f8ecfac85..033b087821195 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -158,9 +158,14 @@ static void crossbar_domain_free(struct irq_domain *domain, unsigned int virq, for (i = 0; i < nr_irqs; i++) { struct irq_data *d = irq_domain_get_irq_data(domain, virq + i); + /* + * irq_map[] is indexed by GIC SPI number. The parent domain's + * hwirq contains the GIC interrupt number (GIC SPI + + * GIC_IRQ_START). + */ + cb->irq_map[d->parent_data->hwirq - GIC_IRQ_START] = IRQ_FREE; + cb->write(d->parent_data->hwirq - GIC_IRQ_START, cb->safe_map); irq_domain_reset_irq_data(d); - cb->irq_map[d->hwirq] = IRQ_FREE; - cb->write(d->hwirq, cb->safe_map); } raw_spin_unlock(&cb->lock); irq_domain_free_irqs_parent(domain, virq, nr_irqs); From 711cbcb464d288254c958094ffd71ed17c089dc7 Mon Sep 17 00:00:00 2001 From: Baoli Zhang Date: Tue, 21 Apr 2026 08:50:20 +0800 Subject: [PATCH 2444/2758] tpm: restore timeout for key creation commands commit de59d78e64039baa5fed455ddb905ba8263e7ede upstream. Commit 207696b17f38 ("tpm: use a map for tpm2_calc_ordinal_duration()") inadvertently reduced the timeout for TPM2 key creation commands (`CREATE_PRIMARY`, `CREATE`, `CREATE_LOADED`) from 300 seconds to 30 seconds. This causes intermittent timeout failures, with several failures observed across hundreds of test runs on some Intel platforms using Infineon SLB9670 and SLB9672 TPM modules. Restore the timeout to 300 seconds to avoid spurious failures. Cc: stable@vger.kernel.org # v6.18+ Fixes: 207696b17f38 ("tpm: use a map for tpm2_calc_ordinal_duration()") Co-developed-by: Lili Li Signed-off-by: Lili Li Signed-off-by: Baoli Zhang Link: https://lore.kernel.org/r/20260421005021.13765-1-baoli.zhang@linux.intel.com Signed-off-by: Jarkko Sakkinen Signed-off-by: Greg Kroah-Hartman --- drivers/char/tpm/tpm2-cmd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index b11e6fa8b740e..52ee350da8671 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -71,9 +71,9 @@ static const struct { {TPM2_CC_HIERARCHY_CHANGE_AUTH, 2000}, {TPM2_CC_GET_CAPABILITY, 750}, {TPM2_CC_NV_READ, 2000}, - {TPM2_CC_CREATE_PRIMARY, 30000}, - {TPM2_CC_CREATE, 30000}, - {TPM2_CC_CREATE_LOADED, 30000}, + {TPM2_CC_CREATE_PRIMARY, 300000}, + {TPM2_CC_CREATE, 300000}, + {TPM2_CC_CREATE_LOADED, 300000}, }; /** From 4bb3e1bc142dc9c3240ceed1b3ab031aa9cb1723 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Sat, 9 May 2026 21:51:07 +0300 Subject: [PATCH 2445/2758] tpm: tpm_tis_spi: Use wait_woken() in wait_for_tmp_stat() commit c0c9cfb3b75def8bf200a2d4db09015806acfeaf upstream. wait_event_interruptible_timeout() evaluates its condition after setting the current task state to TASK_INTERRUPTIBLE. With CONFIG_DEBUG_ATOMIC_SLEEP this triggers a warning when the IRQ wait path is used: tpm_tis_status() tpm_tis_spi_read_bytes() tpm_tis_spi_transfer_full() spi_bus_lock() mutex_lock() Address this with the following measures: 1. Call wait_tpm_stat_cond() only while tasking is running. 2. Use wait_woken() to wait for changes. Cc: stable@vger.kernel.org # v4.19+ Cc: Linus Walleij Reported-by: Stefan Wahren Closes: https://lore.kernel.org/linux-integrity/6964bec7-3dbb-453b-89ef-9b990217a8b9@gmx.net/ Fixes: 1a339b658d9d ("tpm_tis_spi: Pass the SPI IRQ down to the driver") Reviewed-by: Linus Walleij Tested-by: Stefan Wahren Signed-off-by: Jarkko Sakkinen Signed-off-by: Greg Kroah-Hartman --- drivers/char/tpm/tpm_tis_core.c | 35 ++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index 21d79ad3b164e..153a57c792400 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -66,8 +66,8 @@ static int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask, bool check_cancel) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + DEFINE_WAIT_FUNC(wait, woken_wake_function); unsigned long stop; - long rc; u8 status; bool canceled = false; u8 sts_mask; @@ -87,23 +87,30 @@ static int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask, /* process status changes with irq support */ if (sts_mask) { ret = -ETIME; + add_wait_queue(queue, &wait); again: + if (wait_for_tpm_stat_cond(chip, sts_mask, check_cancel, + &canceled)) { + ret = canceled ? -ECANCELED : 0; + goto out; + } + timeout = stop - jiffies; if ((long)timeout <= 0) - return -ETIME; - rc = wait_event_interruptible_timeout(*queue, - wait_for_tpm_stat_cond(chip, sts_mask, check_cancel, - &canceled), - timeout); - if (rc > 0) { - if (canceled) - return -ECANCELED; - ret = 0; - } - if (rc == -ERESTARTSYS && freezing(current)) { - clear_thread_flag(TIF_SIGPENDING); - goto again; + goto out; + + if (signal_pending(current)) { + if (freezing(current)) { + clear_thread_flag(TIF_SIGPENDING); + goto again; + } + goto out; } + + wait_woken(&wait, TASK_INTERRUPTIBLE, timeout); + goto again; +out: + remove_wait_queue(queue, &wait); } if (ret) From 493333f167926c7adab8e7563e21ad71d8af84fa Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 31 May 2026 08:44:28 -0400 Subject: [PATCH 2446/2758] tpm: tpm2-sessions: wait for async KPP completion in tpm_buf_append_salt commit 73851a7c43dfa52d2ed9415889b33daf85da0ed9 upstream. tpm_buf_append_salt() in drivers/char/tpm/tpm2-sessions.c calls crypto_kpp_generate_public_key() and crypto_kpp_compute_shared_secret() without installing a completion callback, discards both return values, and immediately frees the kpp_request via kpp_request_free(). When the resolved ecdh-nist-p256 KPP backend is asynchronous (atmel-ecc, HPRE, keembay-ocs), either operation returns -EINPROGRESS and the deferred completion worker dereferences the freed request. The path fires automatically from the hwrng_fillfn kernel thread via tpm_get_random -> tpm2_get_random -> tpm2_start_auth_session -> tpm_buf_append_salt on every entropy poll, without any userland action. Install crypto_req_done as the completion callback, wrap both KPP operations in crypto_wait_req(), and propagate errors to the caller. The wait is a no-op for synchronous backends. Fixes: 1085b8276bb4 ("tpm: Add the rest of the session HMAC API") Cc: stable@vger.kernel.org # v6.10+ Signed-off-by: Michael Bommarito Assisted-by: Claude:claude-opus-4-7 Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen Signed-off-by: Greg Kroah-Hartman --- drivers/char/tpm/tpm2-sessions.c | 45 ++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c index c4da6fde748f4..f44646b26b192 100644 --- a/drivers/char/tpm/tpm2-sessions.c +++ b/drivers/char/tpm/tpm2-sessions.c @@ -489,15 +489,17 @@ static void tpm2_KDFe(u8 z[EC_PT_SZ], const char *str, u8 *pt_u, u8 *pt_v, sha256_final(&sctx, out); } -static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip, - struct tpm2_auth *auth) +static int tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip, + struct tpm2_auth *auth) { struct crypto_kpp *kpp; struct kpp_request *req; + DECLARE_CRYPTO_WAIT(wait); struct scatterlist s[2], d[1]; struct ecdh p = {0}; u8 encoded_key[EC_PT_SZ], *x, *y; unsigned int buf_len; + int rc; /* secret is two sized points */ tpm_buf_append_u16(buf, (EC_PT_SZ + 2)*2); @@ -520,14 +522,15 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip, kpp = crypto_alloc_kpp("ecdh-nist-p256", CRYPTO_ALG_INTERNAL, 0); if (IS_ERR(kpp)) { dev_err(&chip->dev, "crypto ecdh allocation failed\n"); - return; + return PTR_ERR(kpp); } buf_len = crypto_ecdh_key_len(&p); if (sizeof(encoded_key) < buf_len) { dev_err(&chip->dev, "salt buffer too small needs %d\n", buf_len); - goto out; + rc = -EINVAL; + goto err_free_kpp; } crypto_ecdh_encode_key(encoded_key, buf_len, &p); /* this generates a random private key */ @@ -535,11 +538,17 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip, /* salt is now the public point of this private key */ req = kpp_request_alloc(kpp, GFP_KERNEL); - if (!req) - goto out; + if (!req) { + rc = -ENOMEM; + goto err_free_kpp; + } + kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + crypto_req_done, &wait); kpp_request_set_input(req, NULL, 0); kpp_request_set_output(req, s, EC_PT_SZ*2); - crypto_kpp_generate_public_key(req); + rc = crypto_wait_req(crypto_kpp_generate_public_key(req), &wait); + if (rc) + goto err_free_req; /* * we're not done: now we have to compute the shared secret * which is our private key multiplied by the tpm_key public @@ -551,8 +560,9 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip, kpp_request_set_input(req, s, EC_PT_SZ*2); sg_init_one(d, auth->salt, EC_PT_SZ); kpp_request_set_output(req, d, EC_PT_SZ); - crypto_kpp_compute_shared_secret(req); - kpp_request_free(req); + rc = crypto_wait_req(crypto_kpp_compute_shared_secret(req), &wait); + if (rc) + goto err_free_req; /* * pass the shared secret through KDFe for salt. Note salt @@ -562,8 +572,16 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip, */ tpm2_KDFe(auth->salt, "SECRET", x, chip->null_ec_key_x, auth->salt); - out: + kpp_request_free(req); crypto_free_kpp(kpp); + return 0; + +err_free_req: + kpp_request_free(req); + +err_free_kpp: + crypto_free_kpp(kpp); + return rc; } /** @@ -1018,7 +1036,12 @@ int tpm2_start_auth_session(struct tpm_chip *chip) tpm_buf_append(&buf, auth->our_nonce, sizeof(auth->our_nonce)); /* append encrypted salt and squirrel away unencrypted in auth */ - tpm_buf_append_salt(&buf, chip, auth); + rc = tpm_buf_append_salt(&buf, chip, auth); + if (rc) { + tpm2_flush_context(chip, null_key); + tpm_buf_destroy(&buf); + goto out; + } /* session type (HMAC, audit or policy) */ tpm_buf_append_u8(&buf, TPM2_SE_HMAC); From 7d3ce3bd23c062a7c1c6d372d1b08e48b9e17cc3 Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Wed, 3 Jun 2026 09:36:52 +0800 Subject: [PATCH 2447/2758] sunrpc: fix uninitialized xprt_create_args structure commit 17d90b68c3a3d7d7e95b49e1fe9381a723f637a8 upstream. The xprt_create_args structure is allocated on the stack without initialization in rpc_sysfs_xprt_switch_add_xprt_store(). While some fields are manually populated, critical fields like srcaddr, bc_xps, and flags contain uninitialized stack garbage. This can lead to: 1. Kernel panic when xs_setup_xprt() dereferences garbage srcaddr 2. Information leak if srcaddr points to sensitive stack data 3. Unpredictable behavior if flags has random bits set The fix is to zero-initialize the structure to ensure all unused fields are NULL/0, preventing the transport setup code from acting on garbage data. Cc: stable@vger.kernel.org Suggested-by: Jeff Layton Reviewed-by: Jeff Layton Signed-off-by: Hongling Zeng Signed-off-by: Anna Schumaker Signed-off-by: Greg Kroah-Hartman --- net/sunrpc/sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sunrpc/sysfs.c b/net/sunrpc/sysfs.c index 49686bf740e69..e638b92b7ad1f 100644 --- a/net/sunrpc/sysfs.c +++ b/net/sunrpc/sysfs.c @@ -327,7 +327,7 @@ static ssize_t rpc_sysfs_xprt_switch_add_xprt_store(struct kobject *kobj, { struct rpc_xprt_switch *xprt_switch = rpc_sysfs_xprt_switch_kobj_get_xprt(kobj); - struct xprt_create xprt_create_args; + struct xprt_create xprt_create_args = {}; struct rpc_xprt *xprt, *new; if (!xprt_switch) From 6e37e9e230c7e848bd8e8cd4db15bb18bcf11ad1 Mon Sep 17 00:00:00 2001 From: Kartik Rajput Date: Wed, 22 Apr 2026 12:11:34 +0530 Subject: [PATCH 2448/2758] dmaengine: tegra: Fix burst size calculation commit 4651df83b6c796daead3447e8fd874322918ee4f upstream. Currently, the Tegra GPC DMA hardware requires the transfer length to be a multiple of the max burst size configured for the channel. When a client requests a transfer where the length is not evenly divisible by the configured max burst size, the DMA hangs with partial burst at the end. Fix this by reducing the burst size to the largest power-of-2 value that evenly divides the transfer length. For example, a 40-byte transfer with a 16-byte max burst will now use an 8-byte burst (40 / 8 = 5 complete bursts) instead of causing a hang. This issue was observed with the PL011 UART driver where TX DMA transfers of arbitrary lengths were stuck. Fixes: ee17028009d4 ("dmaengine: tegra: Add tegra gpcdma driver") Cc: stable@vger.kernel.org Signed-off-by: Kartik Rajput Reviewed-by: Frank Li Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20260422064134.1323610-1-kkartik@nvidia.com Signed-off-by: Vinod Koul Signed-off-by: Greg Kroah-Hartman --- drivers/dma/tegra186-gpc-dma.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c index 5948fbf32c21e..0aa3a02b2277f 100644 --- a/drivers/dma/tegra186-gpc-dma.c +++ b/drivers/dma/tegra186-gpc-dma.c @@ -825,6 +825,13 @@ static unsigned int get_burst_size(struct tegra_dma_channel *tdc, * len to calculate the optimum burst size */ burst_byte = burst_size ? burst_size * slave_bw : len; + + /* + * Find the largest burst size that evenly divides the transfer length. + * The hardware requires the transfer length to be a multiple of the + * burst size - partial bursts are not supported. + */ + burst_byte = min(burst_byte, 1U << __ffs(len)); burst_mmio_width = burst_byte / 4; if (burst_mmio_width < TEGRA_GPCDMA_MMIOSEQ_BURST_MIN) From 1553ca96e9df158d8f37137cf4bf5fb0dc981d94 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Thu, 21 May 2026 23:21:53 +0900 Subject: [PATCH 2449/2758] dmaengine: dw-edma: Add spinlock to protect DONE_INT_MASK and ABORT_INT_MASK commit 8ffba0171c6bbce5f093c6dba5a02c0805b31203 upstream. The DONE_INT_MASK and ABORT_INT_MASK registers are shared by all DMA channels, and modifying them requires a read-modify-write sequence. Because this operation is not atomic, concurrent calls to dw_edma_v0_core_start() can introduce race conditions if two channels update these registers simultaneously. Add a spinlock to serialize access to these registers and prevent race conditions. Fixes: 7e4b8a4fbe2c ("dmaengine: Add Synopsys eDMA IP version 0 support") Cc: stable@vger.kernel.org Signed-off-by: Frank Li [den: update dw_edma.lock comment] Link: https://lore.kernel.org/dmaengine/20260109-edma_ll-v2-1-5c0b27b2c664@nxp.com/ Signed-off-by: Koichiro Den Link: https://patch.msgid.link/20260521142153.2957432-5-den@valinux.co.jp Signed-off-by: Vinod Koul Signed-off-by: Greg Kroah-Hartman --- drivers/dma/dw-edma/dw-edma-core.h | 2 +- drivers/dma/dw-edma/dw-edma-v0-core.c | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h index 902574b1ba867..6474cacf71953 100644 --- a/drivers/dma/dw-edma/dw-edma-core.h +++ b/drivers/dma/dw-edma/dw-edma-core.h @@ -109,7 +109,7 @@ struct dw_edma { struct dw_edma_chan *chan; - raw_spinlock_t lock; /* Only for legacy */ + raw_spinlock_t lock; /* Protect v0 shared registers */ struct dw_edma_chip *chip; diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c index 69e8279adec81..cfdd6463252e6 100644 --- a/drivers/dma/dw-edma/dw-edma-v0-core.c +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c @@ -364,6 +364,7 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) { struct dw_edma_chan *chan = chunk->chan; struct dw_edma *dw = chan->dw; + unsigned long flags; u32 tmp; dw_edma_v0_core_write_chunk(chunk); @@ -408,6 +409,8 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) } } /* Interrupt unmask - done, abort */ + raw_spin_lock_irqsave(&dw->lock, flags); + tmp = GET_RW_32(dw, chan->dir, int_mask); tmp &= ~FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id)); tmp &= ~FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id)); @@ -416,6 +419,9 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) tmp = GET_RW_32(dw, chan->dir, linked_list_err_en); tmp |= FIELD_PREP(EDMA_V0_LINKED_LIST_ERR_MASK, BIT(chan->id)); SET_RW_32(dw, chan->dir, linked_list_err_en, tmp); + + raw_spin_unlock_irqrestore(&dw->lock, flags); + /* Channel control */ SET_CH_32(dw, chan->dir, chan->id, ch_control1, (DW_EDMA_V0_CCS | DW_EDMA_V0_LLE)); From 070b92cbb82acb22e748e438d1de6b4ec8749fb4 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 26 May 2026 11:46:54 +0300 Subject: [PATCH 2450/2758] dmaengine: sh: rz-dmac: Fix incorrect NULL check for list_first_entry() commit 5fbf3a2a3b96ef5810e6e0fbc601f82067629bc5 upstream. list_first_entry() does not return NULL when the list is empty, making the existing NULL check invalid. Use list_first_entry_or_null() instead. Fixes: 21323b118c16 ("dmaengine: sh: rz-dmac: Add device_tx_status() callback") Cc: stable@vger.kernel.org Tested-by: John Madieu Signed-off-by: Claudiu Beznea Tested-by: Tommaso Merciai Link: https://patch.msgid.link/20260526084710.3491480-3-claudiu.beznea@kernel.org Signed-off-by: Vinod Koul Signed-off-by: Greg Kroah-Hartman --- drivers/dma/sh/rz-dmac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c index 625ff29024de0..3dd76615881ff 100644 --- a/drivers/dma/sh/rz-dmac.c +++ b/drivers/dma/sh/rz-dmac.c @@ -723,8 +723,8 @@ static u32 rz_dmac_chan_get_residue(struct rz_dmac_chan *channel, u32 crla, crtb, i; /* Get current processing virtual descriptor */ - current_desc = list_first_entry(&channel->ld_active, - struct rz_dmac_desc, node); + current_desc = list_first_entry_or_null(&channel->ld_active, + struct rz_dmac_desc, node); if (!current_desc) return 0; From 044f7b3252d4fb2143d4999eec2800c08f1001ed Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Thu, 21 May 2026 23:21:51 +0900 Subject: [PATCH 2451/2758] dmaengine: dw-edma-pcie: Reject devices without driver data commit 11d7cfe0c119691b2dafbb699bbca90258c678aa upstream. dw_edma_pcie_probe() treats the PCI device ID driver_data as the template for the controller layout and copies it unconditionally. A device bound dynamically via sysfs can match the driver without that data, which leads to a NULL pointer dereference. Reject such matches before enabling the device. Fixes: 41aaff2a2ac0 ("dmaengine: Add Synopsys eDMA IP PCIe glue-logic") Cc: stable@vger.kernel.org Signed-off-by: Koichiro Den Reviewed-by: Frank Li Link: https://patch.msgid.link/20260521142153.2957432-3-den@valinux.co.jp Signed-off-by: Vinod Koul Signed-off-by: Greg Kroah-Hartman --- drivers/dma/dw-edma/dw-edma-pcie.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c b/drivers/dma/dw-edma/dw-edma-pcie.c index 0b30ce1385036..6639efffca584 100644 --- a/drivers/dma/dw-edma/dw-edma-pcie.c +++ b/drivers/dma/dw-edma/dw-edma-pcie.c @@ -314,6 +314,9 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, int i, mask; bool non_ll = false; + if (!pdata) + return -ENODEV; + struct dw_edma_pcie_data *vsec_data __free(kfree) = kmalloc_obj(*vsec_data); if (!vsec_data) From 07ae600bd353b22f31a8f1007269744fafc7f123 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 26 May 2026 11:46:53 +0300 Subject: [PATCH 2452/2758] dmaengine: sh: rz-dmac: Move interrupt request after everything is set up commit 731712403ddb39d1a76a11abf339a0615bc85de7 upstream. Once the interrupt is requested, the interrupt handler may run immediately. Since the IRQ handler can access channel->ch_base, which is initialized only after requesting the IRQ, this may lead to invalid memory access. Likewise, the IRQ thread may access uninitialized data (the ld_free, ld_queue, and ld_active lists), which may also lead to issues. Request the interrupts only after everything is set up. To keep the error path simpler, use dmam_alloc_coherent() instead of dma_alloc_coherent(). Fixes: 5000d37042a6 ("dmaengine: sh: Add DMAC driver for RZ/G2L SoC") Cc: stable@vger.kernel.org Reviewed-by: Frank Li Tested-by: John Madieu Signed-off-by: Claudiu Beznea Tested-by: Tommaso Merciai Link: https://patch.msgid.link/20260526084710.3491480-2-claudiu.beznea@kernel.org Signed-off-by: Vinod Koul Signed-off-by: Greg Kroah-Hartman --- drivers/dma/sh/rz-dmac.c | 88 +++++++++++++++------------------------- 1 file changed, 33 insertions(+), 55 deletions(-) diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c index 3dd76615881ff..6d80cb668957f 100644 --- a/drivers/dma/sh/rz-dmac.c +++ b/drivers/dma/sh/rz-dmac.c @@ -981,25 +981,6 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac, channel->index = index; channel->mid_rid = -EINVAL; - /* Request the channel interrupt. */ - scnprintf(pdev_irqname, sizeof(pdev_irqname), "ch%u", index); - irq = platform_get_irq_byname(pdev, pdev_irqname); - if (irq < 0) - return irq; - - irqname = devm_kasprintf(dmac->dev, GFP_KERNEL, "%s:%u", - dev_name(dmac->dev), index); - if (!irqname) - return -ENOMEM; - - ret = devm_request_threaded_irq(dmac->dev, irq, rz_dmac_irq_handler, - rz_dmac_irq_handler_thread, 0, - irqname, channel); - if (ret) { - dev_err(dmac->dev, "failed to request IRQ %u (%d)\n", irq, ret); - return ret; - } - /* Set io base address for each channel */ if (index < 8) { channel->ch_base = dmac->base + CHANNEL_0_7_OFFSET + @@ -1012,9 +993,9 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac, } /* Allocate descriptors */ - lmdesc = dma_alloc_coherent(&pdev->dev, - sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC, - &channel->lmdesc.base_dma, GFP_KERNEL); + lmdesc = dmam_alloc_coherent(&pdev->dev, + sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC, + &channel->lmdesc.base_dma, GFP_KERNEL); if (!lmdesc) { dev_err(&pdev->dev, "Can't allocate memory (lmdesc)\n"); return -ENOMEM; @@ -1030,7 +1011,24 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac, INIT_LIST_HEAD(&channel->ld_free); INIT_LIST_HEAD(&channel->ld_active); - return 0; + /* Request the channel interrupt. */ + scnprintf(pdev_irqname, sizeof(pdev_irqname), "ch%u", index); + irq = platform_get_irq_byname(pdev, pdev_irqname); + if (irq < 0) + return irq; + + irqname = devm_kasprintf(dmac->dev, GFP_KERNEL, "%s:%u", + dev_name(dmac->dev), index); + if (!irqname) + return -ENOMEM; + + ret = devm_request_threaded_irq(dmac->dev, irq, rz_dmac_irq_handler, + rz_dmac_irq_handler_thread, 0, + irqname, channel); + if (ret) + dev_err(dmac->dev, "failed to request IRQ %u (%d)\n", irq, ret); + + return ret; } static void rz_dmac_put_device(void *_dev) @@ -1099,7 +1097,6 @@ static int rz_dmac_probe(struct platform_device *pdev) const char *irqname = "error"; struct dma_device *engine; struct rz_dmac *dmac; - int channel_num; int ret; int irq; u8 i; @@ -1132,18 +1129,6 @@ static int rz_dmac_probe(struct platform_device *pdev) return PTR_ERR(dmac->ext_base); } - /* Register interrupt handler for error */ - irq = platform_get_irq_byname_optional(pdev, irqname); - if (irq > 0) { - ret = devm_request_irq(&pdev->dev, irq, rz_dmac_irq_handler, 0, - irqname, NULL); - if (ret) { - dev_err(&pdev->dev, "failed to request IRQ %u (%d)\n", - irq, ret); - return ret; - } - } - /* Initialize the channels. */ INIT_LIST_HEAD(&dmac->engine.channels); @@ -1169,6 +1154,18 @@ static int rz_dmac_probe(struct platform_device *pdev) goto err; } + /* Register interrupt handler for error */ + irq = platform_get_irq_byname_optional(pdev, irqname); + if (irq > 0) { + ret = devm_request_irq(&pdev->dev, irq, rz_dmac_irq_handler, 0, + irqname, NULL); + if (ret) { + dev_err(&pdev->dev, "failed to request IRQ %u (%d)\n", + irq, ret); + goto err; + } + } + /* Register the DMAC as a DMA provider for DT. */ ret = of_dma_controller_register(pdev->dev.of_node, rz_dmac_of_xlate, NULL); @@ -1210,16 +1207,6 @@ static int rz_dmac_probe(struct platform_device *pdev) dma_register_err: of_dma_controller_free(pdev->dev.of_node); err: - channel_num = i ? i - 1 : 0; - for (i = 0; i < channel_num; i++) { - struct rz_dmac_chan *channel = &dmac->channels[i]; - - dma_free_coherent(&pdev->dev, - sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC, - channel->lmdesc.base, - channel->lmdesc.base_dma); - } - reset_control_assert(dmac->rstc); err_pm_runtime_put: pm_runtime_put(&pdev->dev); @@ -1232,18 +1219,9 @@ static int rz_dmac_probe(struct platform_device *pdev) static void rz_dmac_remove(struct platform_device *pdev) { struct rz_dmac *dmac = platform_get_drvdata(pdev); - unsigned int i; dma_async_device_unregister(&dmac->engine); of_dma_controller_free(pdev->dev.of_node); - for (i = 0; i < dmac->n_channels; i++) { - struct rz_dmac_chan *channel = &dmac->channels[i]; - - dma_free_coherent(&pdev->dev, - sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC, - channel->lmdesc.base, - channel->lmdesc.base_dma); - } reset_control_assert(dmac->rstc); pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); From 2137f21542900233a42bf00578817f9b8dfecadc Mon Sep 17 00:00:00 2001 From: ZhaoJinming Date: Thu, 21 May 2026 21:08:47 +0800 Subject: [PATCH 2453/2758] platform/x86/intel/tpmi: use cleanup helpers in mem_write() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit a221557958e3a82d8565729d445a7385963f30b6 upstream. In mem_write(), the temporary array returned by parse_int_array_user() must be released on all exit paths. Convert the array variable to use cleanup.h scope-based cleanup so it is freed automatically on return. This also moves the array declaration next to parse_int_array_user() as required by cleanup.h usage guidelines. Fixes: 8e0a2fc68ec3 ("platform/x86/intel/tpmi: Use 32 bit aligned address for debugfs mem write") Cc: stable@vger.kernel.org Signed-off-by: ZhaoJinming Link: https://patch.msgid.link/20260521130848.2860219-1-zhaojinming@uniontech.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/intel/vsec_tpmi.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/drivers/platform/x86/intel/vsec_tpmi.c b/drivers/platform/x86/intel/vsec_tpmi.c index 16fd7aa41f20e..88f14d0ad4102 100644 --- a/drivers/platform/x86/intel/vsec_tpmi.c +++ b/drivers/platform/x86/intel/vsec_tpmi.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -473,7 +474,7 @@ static ssize_t mem_write(struct file *file, const char __user *userbuf, size_t l struct seq_file *m = file->private_data; struct intel_tpmi_pm_feature *pfs = m->private; u32 addr, value, punit, size; - u32 num_elems, *array; + u32 num_elems; void __iomem *mem; int ret; @@ -481,15 +482,14 @@ static ssize_t mem_write(struct file *file, const char __user *userbuf, size_t l if (!size) return -EIO; + u32 *array __free(kfree) = NULL; ret = parse_int_array_user(userbuf, len, (int **)&array); if (ret < 0) return ret; num_elems = *array; - if (num_elems != 3) { - ret = -EINVAL; - goto exit_write; - } + if (num_elems != 3) + return -EINVAL; punit = array[1]; addr = array[2]; @@ -498,15 +498,11 @@ static ssize_t mem_write(struct file *file, const char __user *userbuf, size_t l if (!IS_ALIGNED(addr, sizeof(u32))) return -EINVAL; - if (punit >= pfs->pfs_header.num_entries) { - ret = -EINVAL; - goto exit_write; - } + if (punit >= pfs->pfs_header.num_entries) + return -EINVAL; - if (addr >= size) { - ret = -EINVAL; - goto exit_write; - } + if (addr >= size) + return -EINVAL; mutex_lock(&tpmi_dev_lock); @@ -525,9 +521,6 @@ static ssize_t mem_write(struct file *file, const char __user *userbuf, size_t l unlock_mem_write: mutex_unlock(&tpmi_dev_lock); -exit_write: - kfree(array); - return ret; } From b351e082711d12f075a36a6cd67709693689315f Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Tue, 9 Jun 2026 16:14:19 +0800 Subject: [PATCH 2454/2758] platform/x86: dell-laptop: fix missing cleanups in init error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 6e9cab2247e5b243ae2d907ce7c948a8a9c8d61a upstream. dell_init() initializes several resources after dell_setup_rfkill(), including the optional touchpad LED, keyboard backlight LED, battery hook, debugfs directory and dell-laptop notifier. If a later LED or backlight registration fails, the error path only tears down the battery hook and rfkill resources. This leaves the notifier, debugfs directory, keyboard backlight LED and optional touchpad LED registered after dell_init() returns an error. Add the missing cleanup calls before tearing down rfkill. Fixes: 9c656b07997f ("platform/x86: dell-*: Call new led hw_changed API on kbd brightness change") Fixes: 037accfa14b2 ("dell-laptop: Add debugfs support") Fixes: 2d8b90be4f1c ("dell-laptop: support Synaptics/Alps touchpad led") Fixes: 6cff8d60aa0a ("platform: x86: dell-laptop: Add support for keyboard backlight") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Link: https://patch.msgid.link/20260609081419.1995169-1-lihaoxiang@isrc.iscas.ac.cn Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/dell/dell-laptop.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/platform/x86/dell/dell-laptop.c b/drivers/platform/x86/dell/dell-laptop.c index 57748c3ea24fc..053f40572bf6e 100644 --- a/drivers/platform/x86/dell/dell-laptop.c +++ b/drivers/platform/x86/dell/dell-laptop.c @@ -2551,7 +2551,12 @@ static int __init dell_init(void) if (mute_led_registered) led_classdev_unregister(&mute_led_cdev); fail_led: + dell_laptop_unregister_notifier(&dell_laptop_notifier); + debugfs_remove_recursive(dell_laptop_dir); dell_battery_exit(); + kbd_led_exit(); + if (quirks && quirks->touchpad_led) + touchpad_led_exit(); dell_cleanup_rfkill(); fail_rfkill: platform_device_del(platform_device); From 4676e81d55abb84a40d4ef9cd9e2eda7f6c1c719 Mon Sep 17 00:00:00 2001 From: Krishna Chomal Date: Mon, 25 May 2026 15:52:26 +0530 Subject: [PATCH 2455/2758] platform/x86: hp-wmi: Add support for Omen 16-ap0xxx (8D26) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 0aab31d47c2e857ca05028d718d1e0d239e683ad upstream. The HP Omen 16-ap0xxx (board ID: 8D26) has the same WMI interface as other Victus S boards, but requires quirks for correctly switching thermal profile. Add the DMI board name to victus_s_thermal_profile_boards[] table and map it to omen_v1_legacy_thermal_params. Testing on board 8D26 confirmed that platform profile is registered successfully and fan RPMs are readable and controllable. Tested-by: Alberto Escaño Reported-by: Alberto Escaño Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221514 Cc: stable@vger.kernel.org # v6.18+ Signed-off-by: Krishna Chomal Link: https://patch.msgid.link/20260525102226.56300-1-krishna.chomal108@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/hp/hp-wmi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index f63bc00d9a9b3..eedbdf932a763 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -249,6 +249,10 @@ static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst .matches = { DMI_MATCH(DMI_BOARD_NAME, "8C9C") }, .driver_data = (void *)&victus_s_thermal_params, }, + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "8D26") }, + .driver_data = (void *)&omen_v1_legacy_thermal_params, + }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8D41") }, .driver_data = (void *)&omen_v1_no_ec_thermal_params, From 14812174d720d83a79ed44ad0295a14cf40182a6 Mon Sep 17 00:00:00 2001 From: Krishna Chomal Date: Mon, 8 Jun 2026 19:12:55 +0530 Subject: [PATCH 2456/2758] platform/x86: hp-wmi: Add support for Omen 16-ap0xxx (8E35) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 56b7981c6f21670c0a1a62e6d2f9afb380e2596d upstream. The HP Omen 16-ap0xxx (board ID: 8E35) has the same WMI interface as other Victus S boards, but requires quirks for correctly switching thermal profile. Add the DMI board name to victus_s_thermal_profile_boards[] table and map it to omen_v1_legacy_thermal_params. Testing on board 8E35 confirmed that platform profile is registered successfully and fan RPMs are readable and controllable. Tested-by: Ahmet Öztürk Reported-by: Ahmet Öztürk Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221523 Cc: stable@vger.kernel.org # v6.18+ Signed-off-by: Krishna Chomal Link: https://patch.msgid.link/20260608134255.36280-1-krishna.chomal108@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/hp/hp-wmi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index eedbdf932a763..37937d8a059bf 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -261,6 +261,10 @@ static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst .matches = { DMI_MATCH(DMI_BOARD_NAME, "8D87") }, .driver_data = (void *)&omen_v1_no_ec_thermal_params, }, + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "8E35") }, + .driver_data = (void *)&omen_v1_legacy_thermal_params, + }, {}, }; From 2a42f651cce91e9cbe65fe933622c522cbb1868b Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Thu, 28 May 2026 13:45:21 -0700 Subject: [PATCH 2457/2758] platform/x86: ISST: Restore SST-PP control to all domains MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 2565a28cdcdcb035e151d285efcba26bccb3726e upstream. The SST-PP control offset is only restored to power domain 0 after resume. During suspend, control values are read and stored for all power domains. Use pd_info->sst_base instead of power_domain_info->sst_base, which only points to power domain 0 base address. Fixes: dc7901b5a156 ("platform/x86: ISST: Store and restore all domains data") Reported-by: Yi Lai Signed-off-by: Srinivas Pandruvada Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260528204521.3531456-1-srinivas.pandruvada@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c index b804cb753f942..24334ae70d825 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c @@ -1804,7 +1804,7 @@ void tpmi_sst_dev_resume(struct auxiliary_device *auxdev) if (!(pd_info->sst_header.cap_mask & SST_PP_CAP_PP_ENABLE)) continue; - writeq(pd_info->saved_pp_control, power_domain_info->sst_base + + writeq(pd_info->saved_pp_control, pd_info->sst_base + pd_info->sst_header.pp_offset + SST_PP_CONTROL_OFFSET); } } From 3002e2dda6216a238fde5f5b14e891827e0e00bf Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Thu, 11 Jun 2026 17:04:23 +0200 Subject: [PATCH 2458/2758] platform/x86/amd/pmc: Check for intermediate wakeup in function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 3bdd6fc11fbfa8249483f4b716ead51e43e3a0cd upstream. Refactor code introduced by commit 9f5595d5f03f ("pmc: Require at least 2.5 seconds between HW sleep cycles") to allow adding different conditions for that delay in an upcoming change. Signed-off-by: Daniel Gibson Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260611150426.3683372-2-daniel@gibson.sh Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/amd/pmc/pmc.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index cae3fcafd4d7b..2b9e5730170a5 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -598,6 +598,19 @@ static int amd_pmc_verify_czn_rtc(struct amd_pmc_dev *pdev, u32 *arg) return rc; } +static bool amd_pmc_intermediate_wakeup_need_delay(struct amd_pmc_dev *pdev) +{ + /* + * Starting a new HW sleep cycle right after waking from one + * can cause electrical problems triggering the over voltage protection. + * That is avoided by delaying the next suspend a bit, see also + * https://lore.kernel.org/all/20250414162446.3853194-1-superm1@kernel.org/ + */ + struct smu_metrics table; + + return get_metrics_table(pdev, &table) == 0 && table.s0i3_last_entry_status; +} + static void amd_pmc_s2idle_prepare(void) { struct amd_pmc_dev *pdev = &pmc; @@ -632,11 +645,9 @@ static void amd_pmc_s2idle_prepare(void) static void amd_pmc_s2idle_check(void) { struct amd_pmc_dev *pdev = &pmc; - struct smu_metrics table; int rc; - /* Avoid triggering OVP */ - if (!get_metrics_table(pdev, &table) && table.s0i3_last_entry_status) + if (amd_pmc_intermediate_wakeup_need_delay(pdev)) msleep(2500); /* Dump the IdleMask before we add to the STB */ From 675592e86e8122b3dc39ec79df282a9946025fc6 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Thu, 11 Jun 2026 17:04:24 +0200 Subject: [PATCH 2459/2758] platform/x86/amd/pmc: Delay suspend for some Lenovo Laptops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 9b9e60dd31da054a37d601e9fcabdfd8a2bff354 upstream. Some IdeaPad Slim 3 devices and similar with AMD CPUs have a nonfunctional keyboard and lid switch after s2idle. It helps to delay suspend by 2.5 seconds so the EC has some time to do whatever it needs to get done before suspend - unfortunately at least on my 16ABR8 waking it with a timer (wakealarm) still triggers the issue, but at least normal resume via keypress or lid works fine. On the 14ARP10 wakealarm has been reported to also work fine with this patch. This issue has been reported for many different devices, this patch has been tested with the Zen3-based IdeaPad Slim 3 16ABR8 (82XR) and the Zen3+-based IdeaPad Slim 3 14ARP10 (83K6) and IdeaPad Slim 3 15ARP10 (83MM). Reported-by: Sindre Henriksen Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221383 Tested-by: Sindre Henriksen Suggested-by: Mario Limonciello (AMD) Reviewed-by: Mario Limonciello (AMD) Reviewed-by: Ilpo Järvinen Reviewed-by: Hans de Goede Signed-off-by: Daniel Gibson Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260611150426.3683372-3-daniel@gibson.sh Signed-off-by: Ilpo Järvinen Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/amd/pmc/pmc-quirks.c | 39 +++++++++++++++++++++++ drivers/platform/x86/amd/pmc/pmc.c | 24 +++++++++++++- drivers/platform/x86/amd/pmc/pmc.h | 1 + 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/amd/pmc/pmc-quirks.c b/drivers/platform/x86/amd/pmc/pmc-quirks.c index 24506e3429430..74ddf1d8289a9 100644 --- a/drivers/platform/x86/amd/pmc/pmc-quirks.c +++ b/drivers/platform/x86/amd/pmc/pmc-quirks.c @@ -18,6 +18,7 @@ struct quirk_entry { u32 s2idle_bug_mmio; bool spurious_8042; + bool need_suspend_delay; }; static struct quirk_entry quirk_s2idle_bug = { @@ -33,6 +34,10 @@ static struct quirk_entry quirk_s2idle_spurious_8042 = { .spurious_8042 = true, }; +static struct quirk_entry quirk_s2idle_need_suspend_delay = { + .need_suspend_delay = true, +}; + static const struct dmi_system_id fwbug_list[] = { { .ident = "L14 Gen2 AMD", @@ -203,6 +208,35 @@ static const struct dmi_system_id fwbug_list[] = { DMI_MATCH(DMI_PRODUCT_NAME, "82XQ"), } }, + /* https://bugzilla.kernel.org/show_bug.cgi?id=221383 */ + { + .ident = "Zen3-based IdeaPad Slim and similar", + .driver_data = &quirk_s2idle_need_suspend_delay, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + /* + * Note: there are also some Zen2-based 82X* devices that + * need different quirks, they're already handled above + */ + DMI_MATCH(DMI_PRODUCT_NAME, "82X"), + } + }, + { + .ident = "Zen3+-based IdeaPad Slim and similar", + .driver_data = &quirk_s2idle_need_suspend_delay, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83K"), + } + }, + { + .ident = "IdeaPad Slim 3 15ARP10 (83MM)", + .driver_data = &quirk_s2idle_need_suspend_delay, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83MM"), + } + }, /* https://bugzilla.kernel.org/show_bug.cgi?id=221273 */ { .ident = "Thinkpad L14 Gen3", @@ -356,6 +390,11 @@ void amd_pmc_process_restore_quirks(struct amd_pmc_dev *dev) amd_pmc_skip_nvme_smi_handler(dev->quirks->s2idle_bug_mmio); } +bool amd_pmc_quirk_need_suspend_delay(struct amd_pmc_dev *dev) +{ + return dev->quirks && dev->quirks->need_suspend_delay; +} + void amd_pmc_quirks_init(struct amd_pmc_dev *dev) { const struct dmi_system_id *dmi_id; diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index 2b9e5730170a5..6bafd8661d686 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -611,6 +611,27 @@ static bool amd_pmc_intermediate_wakeup_need_delay(struct amd_pmc_dev *pdev) return get_metrics_table(pdev, &table) == 0 && table.s0i3_last_entry_status; } +static bool amd_pmc_want_suspend_delay(struct amd_pmc_dev *pdev) +{ + /* + * Some Lenovo Laptops (like different IdeaPad 3 Slims) need some + * me-time before sleeping or they get uncooperative after waking + * up and don't send events for keyboard and lid switch anymore. + * + * Unfortunately this doesn't entirely fix the problem: It can still + * happen when resuming with a timer (wakealarm), but at least the + * more common usecases (wakeup by opening lid or pressing a key) + * work fine with this workaround. + * + * See https://bugzilla.kernel.org/show_bug.cgi?id=221383 + */ + if (!disable_workarounds && amd_pmc_quirk_need_suspend_delay(pdev)) { + dev_info(pdev->dev, "Delaying suspend by 2.5s to avoid platform bug\n"); + return true; + } + return false; +} + static void amd_pmc_s2idle_prepare(void) { struct amd_pmc_dev *pdev = &pmc; @@ -647,7 +668,8 @@ static void amd_pmc_s2idle_check(void) struct amd_pmc_dev *pdev = &pmc; int rc; - if (amd_pmc_intermediate_wakeup_need_delay(pdev)) + if (amd_pmc_intermediate_wakeup_need_delay(pdev) || + amd_pmc_want_suspend_delay(pdev)) msleep(2500); /* Dump the IdleMask before we add to the STB */ diff --git a/drivers/platform/x86/amd/pmc/pmc.h b/drivers/platform/x86/amd/pmc/pmc.h index fe3f53eb59558..f5257e47b8c47 100644 --- a/drivers/platform/x86/amd/pmc/pmc.h +++ b/drivers/platform/x86/amd/pmc/pmc.h @@ -147,6 +147,7 @@ enum amd_pmc_def { }; void amd_pmc_process_restore_quirks(struct amd_pmc_dev *dev); +bool amd_pmc_quirk_need_suspend_delay(struct amd_pmc_dev *dev); void amd_pmc_quirks_init(struct amd_pmc_dev *dev); void amd_mp2_stb_init(struct amd_pmc_dev *dev); void amd_mp2_stb_deinit(struct amd_pmc_dev *dev); From d53314ae31fd28e042aa4921c9057a7a018f7bea Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Thu, 11 Jun 2026 17:04:25 +0200 Subject: [PATCH 2460/2758] platform/x86/amd/pmc: Add delay_suspend module parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 428b9fd2dce50b4dc5cd9ade10b92efcf57ce7aa upstream. Enabling the new delay_suspend module parameter delays suspend for 2.5 seconds which is known to help for some AMD-based Lenovo Laptops that otherwise failed to send/receive events for key presses or the lid switch after s2idle. Apparently the EC needs to do some things in the background before suspend or it gets into a bad state. There are many reports of AMD-based laptops (mostly but not exclusively IdeaPads) about similar issues on the web; this parameter gives affected users an easy way to try out if their issues have the same root cause and to work around them until their specific device is added to the quirks list. The parameter description has a note encouraging users to report their device so it can be added to the quirks list, inspired by a similar request in parameter descriptions of the ideapad-laptop module. The module parameter can be set to "1" to explicitly enable it, "0" to disable it even on devices that are assumed to be affected, or -1 (the default) to enable it if the device is assumed to be affected (according to fwbug_list[]) Link: https://bugzilla.kernel.org/show_bug.cgi?id=221383 Reviewed-by: Hans de Goede Signed-off-by: Daniel Gibson Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260611150426.3683372-4-daniel@gibson.sh Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/amd/pmc/pmc.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index 6bafd8661d686..2d3d180c15d23 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -89,6 +90,11 @@ static bool disable_workarounds; module_param(disable_workarounds, bool, 0644); MODULE_PARM_DESC(disable_workarounds, "Disable workarounds for platform bugs"); +static int delay_suspend = -1; +module_param(delay_suspend, int, 0644); +MODULE_PARM_DESC(delay_suspend, + "Delays s2idle by 2.5 seconds to work around buggy ECs, often causing keyboard issues after suspend. 0: don't delay, 1: do delay, -1 (default): let amd_pmc decide. If you need this please report this to: platform-driver-x86@vger.kernel.org"); + static struct amd_pmc_dev pmc; static inline u32 amd_pmc_reg_read(struct amd_pmc_dev *dev, int reg_offset) @@ -625,8 +631,23 @@ static bool amd_pmc_want_suspend_delay(struct amd_pmc_dev *pdev) * * See https://bugzilla.kernel.org/show_bug.cgi?id=221383 */ - if (!disable_workarounds && amd_pmc_quirk_need_suspend_delay(pdev)) { - dev_info(pdev->dev, "Delaying suspend by 2.5s to avoid platform bug\n"); + if (amd_pmc_quirk_need_suspend_delay(pdev)) { + /* + * delay_suspend=1 force-enables this, otherwise it can be + * disabled with disable_workarounds or delay_suspend=0 + */ + if (delay_suspend == 1 || (delay_suspend == -1 && !disable_workarounds)) { + dev_info(pdev->dev, "Delaying suspend by 2.5s to avoid platform bug\n"); + return true; + } + dev_info(pdev->dev, "Not delaying suspend because of module parameter, even though your device is assumed to need it!\n"); + } else if (delay_suspend == 1) { + dev_info(pdev->dev, "Delaying suspend by 2.5s because delay_suspend=1. If this solves problems on your machine, please report this whole line to: platform-driver-x86@vger.kernel.org so it can be automatically detected as affected in the future. System Vendor: \"%s\" Product Name: \"%s\" Product Family: \"%s\" Board Vendor: \"%s\" Board Name: \"%s\"\n", + dmi_get_system_info(DMI_SYS_VENDOR), + dmi_get_system_info(DMI_PRODUCT_NAME), + dmi_get_system_info(DMI_PRODUCT_FAMILY), + dmi_get_system_info(DMI_BOARD_VENDOR), + dmi_get_system_info(DMI_BOARD_NAME)); return true; } return false; From 301bb780d1b9e61075331ec9f2636bed25be3e8d Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Thu, 11 Jun 2026 17:04:26 +0200 Subject: [PATCH 2461/2758] platform/x86/amd/pmc: Don't log during intermediate wakeups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 037f0b03c663a247366673a807834389107995b7 upstream. The ECs in the IdeaPads that need the delay_suspend quirk send lots of messages when charging, which not only causes intermediate wakeups when suspended, but also prevents the device from reaching the deepest suspend state. Because of this amd_pmc_intermediate_wakeup_need_delay() returns false during intermediate wakeups and amd_pmc_want_suspend_delay() is called. So far it always logged its "Delaying suspend by 2.5s ..." messages then, which spams dmesg. This commit makes sure that those messages are only logged once per suspend. Link: https://bugzilla.kernel.org/show_bug.cgi?id=221383 Reviewed-by: Hans de Goede Signed-off-by: Daniel Gibson Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260611150426.3683372-5-daniel@gibson.sh Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/amd/pmc/pmc.c | 39 ++++++++++++++++++++++++------ drivers/platform/x86/amd/pmc/pmc.h | 1 + 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index 2d3d180c15d23..7d772ccd17a67 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -619,6 +619,20 @@ static bool amd_pmc_intermediate_wakeup_need_delay(struct amd_pmc_dev *pdev) static bool amd_pmc_want_suspend_delay(struct amd_pmc_dev *pdev) { + /* + * intermediate_wakeup implies that the machine didn't get to deepest sleep + * state before - otherwise this function isn't called in amd_pmc_s2idle_check() + * because amd_pmc_intermediate_wakeup_need_delay() returns true first. + * On some IdeaPads that happens when charging, because the EC seems + * to send lots of messages then that wake the machine. + * + * But even in that case, the sleep here is necessary (on those IdeaPads), + * otherwise they wake up completely (resume) after a few seconds. + * So this variable is only used to avoid spamming dmesg on each + * intermediate wakeup. + */ + bool intermediate_wakeup = !pdev->is_first_check_after_suspend; + /* * Some Lenovo Laptops (like different IdeaPad 3 Slims) need some * me-time before sleeping or they get uncooperative after waking @@ -637,17 +651,20 @@ static bool amd_pmc_want_suspend_delay(struct amd_pmc_dev *pdev) * disabled with disable_workarounds or delay_suspend=0 */ if (delay_suspend == 1 || (delay_suspend == -1 && !disable_workarounds)) { - dev_info(pdev->dev, "Delaying suspend by 2.5s to avoid platform bug\n"); + if (!intermediate_wakeup) + dev_info(pdev->dev, "Delaying suspend by 2.5s to avoid platform bug\n"); return true; } - dev_info(pdev->dev, "Not delaying suspend because of module parameter, even though your device is assumed to need it!\n"); + if (!intermediate_wakeup) + dev_info(pdev->dev, "Not delaying suspend because of module parameter, even though your device is assumed to need it!\n"); } else if (delay_suspend == 1) { - dev_info(pdev->dev, "Delaying suspend by 2.5s because delay_suspend=1. If this solves problems on your machine, please report this whole line to: platform-driver-x86@vger.kernel.org so it can be automatically detected as affected in the future. System Vendor: \"%s\" Product Name: \"%s\" Product Family: \"%s\" Board Vendor: \"%s\" Board Name: \"%s\"\n", - dmi_get_system_info(DMI_SYS_VENDOR), - dmi_get_system_info(DMI_PRODUCT_NAME), - dmi_get_system_info(DMI_PRODUCT_FAMILY), - dmi_get_system_info(DMI_BOARD_VENDOR), - dmi_get_system_info(DMI_BOARD_NAME)); + if (!intermediate_wakeup) + dev_info(pdev->dev, "Delaying suspend by 2.5s because delay_suspend=1. If this solves problems on your machine, please report this whole line to: platform-driver-x86@vger.kernel.org so it can be automatically detected as affected in the future. System Vendor: \"%s\" Product Name: \"%s\" Product Family: \"%s\" Board Vendor: \"%s\" Board Name: \"%s\"\n", + dmi_get_system_info(DMI_SYS_VENDOR), + dmi_get_system_info(DMI_PRODUCT_NAME), + dmi_get_system_info(DMI_PRODUCT_FAMILY), + dmi_get_system_info(DMI_BOARD_VENDOR), + dmi_get_system_info(DMI_BOARD_NAME)); return true; } return false; @@ -660,6 +677,9 @@ static void amd_pmc_s2idle_prepare(void) u8 msg; u32 arg = 1; + /* Reset this variable because this is a fresh suspend */ + pdev->is_first_check_after_suspend = true; + /* Reset and Start SMU logging - to monitor the s0i3 stats */ amd_pmc_setup_smu_logging(pdev); @@ -699,6 +719,9 @@ static void amd_pmc_s2idle_check(void) rc = amd_stb_write(pdev, AMD_PMC_STB_S2IDLE_CHECK); if (rc) dev_err(pdev->dev, "error writing to STB: %d\n", rc); + + /* remember that first check after suspend is done (until next prepare) */ + pdev->is_first_check_after_suspend = false; } static int amd_pmc_dump_data(struct amd_pmc_dev *pdev) diff --git a/drivers/platform/x86/amd/pmc/pmc.h b/drivers/platform/x86/amd/pmc/pmc.h index f5257e47b8c47..8aa7073ed09f2 100644 --- a/drivers/platform/x86/amd/pmc/pmc.h +++ b/drivers/platform/x86/amd/pmc/pmc.h @@ -114,6 +114,7 @@ struct amd_pmc_dev { struct dentry *dbgfs_dir; struct quirk_entry *quirks; bool disable_8042_wakeup; + bool is_first_check_after_suspend; struct amd_mp2_dev *mp2; struct stb_arg stb_arg; }; From 0bf6482919b9ce1fc7cf32521263ff1841495794 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 23 Jun 2026 16:20:31 +0200 Subject: [PATCH 2462/2758] pkey: Move keytype check from pkey api to handler commit 754e9e49b76fd5be339172aa98544182ed3ca75e upstream. The PKEY_VERIFYPROTK ioctl takes data from user-space and verifies the contained protected key. While checking the integrity of the ioctl request structure is the responsibility of the generic pkey_api code, the verification of the contained protected key is the responsibility of the pkey handler. The keytype verification (based on the calculated bitsize of the key) is part of the protected key verification and therefore the responsibility of the pkey handler (which already verifies it). Therefore the keytype verification is removed from the generic pkey_api code. As the calculation of the key bitsize is currently wrong, the removal of the keytype check in pkey_api also removes this wrong calculation. For this reason, the commit is flagged with the Fixes: tag. Cc: stable@kernel.org # 6.12+ Fixes: 8fcc231ce3be ("s390/pkey: Introduce pkey base with handler registry and handler modules") Reviewed-by: Ingo Franzki Reviewed-by: Harald Freudenberger Signed-off-by: Holger Dengler Signed-off-by: Alexander Gordeev Signed-off-by: Vasily Gorbik Signed-off-by: Greg Kroah-Hartman --- drivers/s390/crypto/pkey_api.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/s390/crypto/pkey_api.c b/drivers/s390/crypto/pkey_api.c index 28e1007005f21..5d8f63f390a88 100644 --- a/drivers/s390/crypto/pkey_api.c +++ b/drivers/s390/crypto/pkey_api.c @@ -327,7 +327,6 @@ static int pkey_ioctl_verifyprotk(struct pkey_verifyprotk __user *uvp) { struct pkey_verifyprotk kvp; struct protaeskeytoken *t; - u32 keytype; u8 *tmpbuf; int rc; @@ -341,14 +340,6 @@ static int pkey_ioctl_verifyprotk(struct pkey_verifyprotk __user *uvp) return -EINVAL; } - keytype = pkey_aes_bitsize_to_keytype(8 * kvp.protkey.len); - if (!keytype) { - PKEY_DBF_ERR("%s unknown/unsupported protkey length %u\n", - __func__, kvp.protkey.len); - memzero_explicit(&kvp, sizeof(kvp)); - return -EINVAL; - } - /* build a 'protected key token' from the raw protected key */ tmpbuf = kzalloc(sizeof(*t), GFP_KERNEL); if (!tmpbuf) { @@ -358,7 +349,7 @@ static int pkey_ioctl_verifyprotk(struct pkey_verifyprotk __user *uvp) t = (struct protaeskeytoken *)tmpbuf; t->type = TOKTYPE_NON_CCA; t->version = TOKVER_PROTECTED_KEY; - t->keytype = keytype; + t->keytype = kvp.protkey.type; t->len = kvp.protkey.len; memcpy(t->protkey, kvp.protkey.protkey, kvp.protkey.len); From c32f565f32919d9f5de3f2b27c5324358098edeb Mon Sep 17 00:00:00 2001 From: Fredric Cover Date: Sat, 11 Jul 2026 19:54:02 -0700 Subject: [PATCH 2463/2758] smb: client: use kvzalloc() for megabyte buffer in simple fallocate commit 806c00c23e3ce8eae397a40ced536ef88ae4e012 upstream. Currently in smb3_simple_fallocate_range(), a 1 MB buffer is allocated using kzalloc(). Under heavy memory fragmentation, a contiguous 1 MB block of physical memory (an order-8 allocation) may not be available, causing the allocation to fail. This failure was observed during xfstests generic/013 on a 4GB RAM test machine running fsstress: fsstress: page allocation failure: order:8, mode:0x40dc0(GFP_KERNEL|__GFP_ZERO|__GFP_COMP), nodemask=(null),cpuset=/,mems_allowed=0 Call Trace: dump_stack_lvl+0x5d/0x80 warn_alloc+0x163/0x190 __alloc_pages_slowpath.constprop.0+0x71b/0x12f0 __alloc_frozen_pages_noprof+0x2f6/0x340 alloc_pages_mpol+0xb6/0x170 ___kmalloc_large_node+0xb3/0xd0 __kmalloc_large_noprof+0x1e/0xc0 smb3_simple_falloc.isra.0+0x62b/0x960 cifs_fallocate+0xed/0x180 vfs_fallocate+0x165/0x3c0 __x64_sys_fallocate+0x48/0xa0 do_syscall_64+0xe1/0x640 entry_SYSCALL_64_after_hwframe+0x76/0x7e Node 0 Normal: 3375*4kB ... 7*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB Since this scratch buffer does not require physically contiguous memory, switch the allocation to kvzalloc(). This retains the performance benefits of kmalloc() under normal conditions, while gracefully falling back to virtually contiguous memory when physical allocation fails. Fixes: 966a3cb7c7db ("cifs: improve fallocate emulation") Cc: stable@vger.kernel.org Signed-off-by: Fredric Cover Tested-by: Fredric Cover Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2ops.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 15cfb513238f6..02c2f83353e27 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -3595,7 +3595,7 @@ static int smb3_simple_fallocate_range(unsigned int xid, if (rc) goto out; - buf = kzalloc(1024 * 1024, GFP_KERNEL); + buf = kvzalloc(1024 * 1024, GFP_KERNEL); if (buf == NULL) { rc = -ENOMEM; goto out; @@ -3652,7 +3652,7 @@ static int smb3_simple_fallocate_range(unsigned int xid, out: kfree(out_data); - kfree(buf); + kvfree(buf); return rc; } From a0d8e415ebf35e7ac28ea18c77ed00a9daef4925 Mon Sep 17 00:00:00 2001 From: Ibrahim Hashimov Date: Thu, 9 Jul 2026 17:05:30 +0200 Subject: [PATCH 2464/2758] ksmbd: fix integer overflow in set_file_allocation_info() commit 1c0ae3df692ea2a4ce992f786346154e75a3f0d5 upstream. set_file_allocation_info() converts the client-supplied FILE_ALLOCATION_INFORMATION::AllocationSize into a 512-byte block count with: alloc_blks = (le64_to_cpu(file_alloc_info->AllocationSize) + 511) >> 9; AllocationSize is a fully client-controlled __le64 field; the only validation performed by the caller (smb2_set_info_file(), case FILE_ALLOCATION_INFORMATION) is that the fixed buffer is at least sizeof(struct smb2_file_alloc_info) == 8 bytes. The value itself is never range-checked before this arithmetic. When AllocationSize is close to U64_MAX (e.g. 0xffffffffffffffff), "AllocationSize + 511" wraps around mod 2^64 to a small number (0xffffffffffffffff + 511 = 510), so alloc_blks becomes 0. Since any existing regular file has stat.blocks > 0, the function then takes the "shrink" branch and calls: ksmbd_vfs_truncate(work, fp, alloc_blks * 512); /* == 0 */ silently truncating the file to size 0, even though the client asked to grow the allocation to (what looks like) the maximum possible size. The trailing "if (size < alloc_blks * 512) i_size_write(inode, size);" restore is guarded by a comparison that is never true once alloc_blks == 0, so the truncation is not undone. This lets an authenticated SMB client that already holds an open handle with FILE_WRITE_DATA on a file silently truncate that same file to size 0 via a single crafted SET_INFO(FILE_ALLOCATION_INFORMATION) request advertising a near-U64_MAX AllocationSize, even though the request asks to grow the file's allocation rather than shrink it. This is a functional/data-loss bug, not a privilege-boundary violation: the same client could already truncate the file via FILE_END_OF_FILE_INFORMATION or a plain write. Fix it by validating AllocationSize against MAX_LFS_FILESIZE, the same upper bound the VFS itself uses to reject unrepresentable file sizes, before doing the "+511" rounding, and rejecting oversized values with -EINVAL. Bounding AllocationSize to MAX_LFS_FILESIZE - 511 guarantees the "+511" addition cannot wrap, and that the subsequent "alloc_blks * 512" values passed to vfs_fallocate() and ksmbd_vfs_truncate() stay within a representable loff_t as well. No legitimate SMB client asks for an allocation size anywhere near 2^64 bytes, so this only rejects a value that was previously silently misinterpreted as zero. Runtime-verified on a v6.19 KASAN test stand: sending SET_INFO (FILE_ALLOCATION_INFORMATION) with AllocationSize = 0xffffffffffffffff against ksmbd now returns -EINVAL and leaves the target file's size unchanged, where the unpatched kernel truncated it from 4096 to 0 bytes. Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") Cc: stable@vger.kernel.org Signed-off-by: Ibrahim Hashimov Assisted-by: AuditCode-AI:2026.07 Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smb2pdu.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 93fc8ea35baa0..e920a283a3116 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -6382,6 +6382,7 @@ static int set_file_allocation_info(struct ksmbd_work *work, */ loff_t alloc_blks; + u64 alloc_size; struct inode *inode; struct kstat stat; int rc; @@ -6397,7 +6398,19 @@ static int set_file_allocation_info(struct ksmbd_work *work, if (rc) return rc; - alloc_blks = (le64_to_cpu(file_alloc_info->AllocationSize) + 511) >> 9; + /* + * AllocationSize is fully client-controlled (the caller only + * validates the fixed 8-byte buffer length). Reject values that + * would overflow the "round up to 512-byte blocks" conversion + * below instead of silently wrapping it to a tiny block count, + * which would truncate the file to a size the client never + * asked for. + */ + alloc_size = le64_to_cpu(file_alloc_info->AllocationSize); + if (alloc_size > MAX_LFS_FILESIZE - 511) + return -EINVAL; + + alloc_blks = (alloc_size + 511) >> 9; inode = file_inode(fp->filp); if (alloc_blks > stat.blocks) { From b9f07a4ec6e39ff2321046d4dcd245f88aef4fd9 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Mon, 29 Jun 2026 21:17:40 +0200 Subject: [PATCH 2465/2758] hwmon: (ltc2992) add missing 'select REGMAP_I2C' to Kconfig commit a35a6f1b20100057c66b7be5a8f6864661c3945c upstream. The Kconfig entry for the LTC2992 sensor doesn't contain a `select REGMAP_I2C` parameter, causing build failures if regmap isn't selected previously during the build process. Fixes: b0bd407e94b0 ("hwmon: (ltc2992) Add support") Cc: stable@vger.kernel.org Signed-off-by: Joshua Crofts Link: https://lore.kernel.org/r/20260629-add-kconfig-deps-v1-2-8104df929b1a@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 14e4cea48acc4..c02ad06eee08b 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1076,6 +1076,7 @@ config SENSORS_LTC2992 tristate "Linear Technology LTC2992" depends on I2C depends on GPIOLIB + select REGMAP_I2C help If you say yes here you get support for Linear Technology LTC2992 I2C System Monitor. The LTC2992 measures current, voltage, and From c9a0f2bff2cb622c0f2a7534afaa5934a96c60dd Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Mon, 29 Jun 2026 21:17:41 +0200 Subject: [PATCH 2466/2758] hwmon: (max6697) add missing 'select REGMAP_I2C' to Kconfig commit ed576f2f4eef8cbe2c110da503825a8dc4717030 upstream. The Kconfig entry for the MAX6697 sensor doesn't contain a `select REGMAP_I2C` parameter, causing build failures if regmap isn't selected previously during the build process. Fixes: 3a2a8cc3fe24 ("hwmon: (max6697) Convert to use regmap") Cc: stable@vger.kernel.org Signed-off-by: Joshua Crofts Link: https://lore.kernel.org/r/20260629-add-kconfig-deps-v1-3-8104df929b1a@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index c02ad06eee08b..b9a225076765a 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1333,6 +1333,7 @@ config SENSORS_MAX6650 config SENSORS_MAX6697 tristate "Maxim MAX6697 and compatibles" depends on I2C + select REGMAP_I2C help If you say yes here you get support for MAX6581, MAX6602, MAX6622, MAX6636, MAX6689, MAX6693, MAX6694, MAX6697, MAX6698, and MAX6699 From 60ed00d46616a9232e42ea7a3e3c0273d7cf7543 Mon Sep 17 00:00:00 2001 From: Vincent Jardin Date: Mon, 13 Jul 2026 20:11:59 +0200 Subject: [PATCH 2467/2758] i2c: imx: fix locked bus on SMBus block-read of 0 (atomic) commit cb2fc37857693b55909fb77dc2c87cfbc1cdc476 upstream. SMBus 3.1 6.5.7 allows a Block Read byte count of 0, but the atomic (polling) path rejects it as -EPROTO. Worse, it returns without a NACK+STOP: the next receive cycle has already started, so the target keeps holding SDA and the bus stays stuck until a power cycle for this i2c controller. Reading I2DR to obtain the count likewise arms the next byte on the count > I2C_SMBUS_BLOCK_MAX path, which also returned -EPROTO directly and left the bus held. Handle both: NACK the in-flight dummy byte (TXAK) and extend msgs->len so the existing last-byte handling emits STOP; the dummy byte is discarded. A count of 0 is a valid empty block read; a count above I2C_SMBUS_BLOCK_MAX is still reported as -EPROTO, but only after the bus has been released. The interrupt-driven path has the same flaw from a later commit and is fixed separately, as it carries a different Fixes: tag and stable range. Fixes: 8e8782c71595 ("i2c: imx: add SMBus block read support") Signed-off-by: Vincent Jardin Cc: # v3.16+ Acked-by: Oleksij Rempel Acked-by: Carlos Song Reviewed-by: Stefan Eichenberger Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260713-for-upstream-i2c-lx2160-fix-v1-v3-1-073ac9e103a5@free.fr Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-imx.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 28313d0fad37e..cfd1e63359e75 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -1415,6 +1415,7 @@ static int i2c_imx_atomic_read(struct imx_i2c_struct *i2c_imx, int i, result; unsigned int temp; int block_data = msgs->flags & I2C_M_RECV_LEN; + int block_err = 0; result = i2c_imx_prepare_read(i2c_imx, msgs, false); if (result) @@ -1436,8 +1437,20 @@ static int i2c_imx_atomic_read(struct imx_i2c_struct *i2c_imx, */ if ((!i) && block_data) { len = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); - if ((len == 0) || (len > I2C_SMBUS_BLOCK_MAX)) - return -EPROTO; + if ((len == 0) || (len > I2C_SMBUS_BLOCK_MAX)) { + /* + * SMBus 3.1 6.5.7: support count byte of 0. + * I2C_SMBUS_BLOCK_MAX case should not hold the SDA either. + */ + if (len > I2C_SMBUS_BLOCK_MAX) + block_err = -EPROTO; + temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + temp |= I2CR_TXAK; + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); + msgs->buf[0] = 0; + msgs->len = 2; + continue; + } dev_dbg(&i2c_imx->adapter.dev, "<%s> read length: 0x%X\n", __func__, len); @@ -1485,7 +1498,7 @@ static int i2c_imx_atomic_read(struct imx_i2c_struct *i2c_imx, "<%s> read byte: B%d=0x%X\n", __func__, i, msgs->buf[i]); } - return 0; + return block_err; } static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, From 56ddfc18ea8f7d77747658892873eb632b2ed530 Mon Sep 17 00:00:00 2001 From: Vincent Jardin Date: Mon, 13 Jul 2026 20:12:00 +0200 Subject: [PATCH 2468/2758] i2c: imx: fix locked bus on SMBus block-read of 0 (IRQ) commit 07fd9385f0d87dff4b34f355f68adf701080cb24 upstream. SMBus 3.1 6.5.7 allows a Block Read byte count of 0, but the interrupt-driven block-read state machine rejects it as -EPROTO. Worse, it returns without a NACK+STOP: the next receive cycle has already started, so the target keeps holding SDA and the bus stays stuck until a power cycle of this i2c controller. Accept count=0: NACK the in-flight dummy byte (TXAK) and set msg->len to 2 so i2c_imx_isr_read_continue() emits STOP via its normal last-byte path. The dummy byte is discarded; block-read callers only consume buf[0..count-1]. Reading I2DR has likewise already armed the next byte on the count > I2C_SMBUS_BLOCK_MAX error path, so NACK it (TXAK) before aborting with -EPROTO; otherwise the failing transfer's STOP cannot complete and the bus stays held. The atomic path regressed earlier (v3.16) and is fixed separately; this patch covers only the v6.13 state-machine rework. Fixes: 5f5c2d4579ca ("i2c: imx: prevent rescheduling in non dma mode") Signed-off-by: Vincent Jardin Cc: # v6.13+ Acked-by: Oleksij Rempel Acked-by: Carlos Song Reviewed-by: Stefan Eichenberger Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260713-for-upstream-i2c-lx2160-fix-v1-v3-2-073ac9e103a5@free.fr Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-imx.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index cfd1e63359e75..d5e6e2eca3b34 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -1061,11 +1061,28 @@ static inline enum imx_i2c_state i2c_imx_isr_read_continue(struct imx_i2c_struct static inline void i2c_imx_isr_read_block_data_len(struct imx_i2c_struct *i2c_imx) { u8 len = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); + unsigned int temp; if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) { + /* + * SMBus 3.1 6.5.7: support count byte of 0. + * I2C_SMBUS_BLOCK_MAX case should not hold the SDA either. + * So NACK it (TXAK) to not hold the bus. + */ + temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + temp |= I2CR_TXAK; + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); + + if (len == 0) { + i2c_imx->msg->buf[i2c_imx->msg_buf_idx++] = 0; + i2c_imx->msg->len = 2; + return; + } + i2c_imx->isr_result = -EPROTO; i2c_imx->state = IMX_I2C_STATE_FAILED; wake_up(&i2c_imx->queue); + return; } i2c_imx->msg->len += len; i2c_imx->msg->buf[i2c_imx->msg_buf_idx++] = len; From bbc08be46f00222474ddf4f902c624a7830d7de7 Mon Sep 17 00:00:00 2001 From: Roman Vivchar Date: Thu, 9 Jul 2026 16:31:29 +0300 Subject: [PATCH 2469/2758] i2c: mediatek: fix WRRD for SoCs without auto_restart option commit deb35336b5bfed5db9231b5348bc1514db930797 upstream. MediaTek mt65xx family SoCs have no auto restart, however, they still support the WRRD mode in the hardware. Because auto_restart is set to 0, the WRRD mode will be never enabled, leading to read errors. Fix this by removing auto_restart check from the WRRD enable path. Fixes: b49218365280 ("i2c: mediatek: fix potential incorrect use of I2C_MASTER_WRRD") Signed-off-by: Roman Vivchar Cc: # v6.18+ Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260709-6572-6595-i2c-v2-1-b2fb8510d1d3@protonmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-mt65xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index 126040ca05f14..307925fb78e36 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -1258,7 +1258,7 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap, i2c->auto_restart = i2c->dev_comp->auto_restart; /* checking if we can skip restart and optimize using WRRD mode */ - if (i2c->auto_restart && num == 2) { + if (num == 2) { if (!(msgs[0].flags & I2C_M_RD) && (msgs[1].flags & I2C_M_RD) && msgs[0].addr == msgs[1].addr) { i2c->auto_restart = 0; From fb267770bf822064f510c9b46743f533d8fa8a5f Mon Sep 17 00:00:00 2001 From: Xuanqiang Luo Date: Tue, 14 Jul 2026 23:08:08 +0800 Subject: [PATCH 2470/2758] i2c: mlxbf: Fix use-after-free in mlxbf_i2c_init_resource() commit 71356737a7a55c76fee847563e3d33f8e6dc6b6d upstream. If devm_platform_get_and_ioremap_resource() returns an error, mlxbf_i2c_init_resource() frees tmp_res before reading tmp_res->io to get the error code. This results in a use-after-free. Save the error code before freeing tmp_res. Fixes: b5b5b32081cd ("i2c: mlxbf: I2C SMBus driver for Mellanox BlueField SoC") Signed-off-by: Xuanqiang Luo Cc: # v5.10+ Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260714150808.85045-1-xuanqiang.luo@linux.dev Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-mlxbf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-mlxbf.c b/drivers/i2c/busses/i2c-mlxbf.c index 6c1cfe9ec8acc..e33512b253531 100644 --- a/drivers/i2c/busses/i2c-mlxbf.c +++ b/drivers/i2c/busses/i2c-mlxbf.c @@ -1051,8 +1051,10 @@ static int mlxbf_i2c_init_resource(struct platform_device *pdev, tmp_res->io = devm_platform_get_and_ioremap_resource(pdev, type, &tmp_res->params); if (IS_ERR(tmp_res->io)) { + int ret = PTR_ERR(tmp_res->io); + devm_kfree(dev, tmp_res); - return PTR_ERR(tmp_res->io); + return ret; } tmp_res->type = type; From 35176f104612e4f93a1ebefc65a9c5e8e23a0341 Mon Sep 17 00:00:00 2001 From: Pei Xiao Date: Fri, 10 Jul 2026 15:21:13 +0800 Subject: [PATCH 2471/2758] i2c: spacemit: fix spurious IRQ handling returning IRQ_HANDLED commit 9db20d23aac7916ff49be409a4bfd48fe7cbbfb4 upstream. When the interrupt status register reads zero, the handler should return IRQ_NONE instead of IRQ_HANDLED. What the return value actually feeds into is the spurious interrupt accounting in note_interrupt(): falsely claiming IRQ_HANDLED defeats the "irq XX: nobody cared" detection, so a stuck interrupt source would never be caught. Fixes: 5ea558473fa3 ("i2c: spacemit: add support for SpacemiT K1 SoC") Signed-off-by: Pei Xiao Cc: # v6.15+ Reviewed-by: Troy Mitchell Reviewed-by: Mukesh Savaliya Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/ef8b623f45d4e430721e46572c2598d882044aed.1783667875.git.xiaopei01@kylinos.cn Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-k1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-k1.c b/drivers/i2c/busses/i2c-k1.c index 9152cf436bea0..51a0c3d80fc92 100644 --- a/drivers/i2c/busses/i2c-k1.c +++ b/drivers/i2c/busses/i2c-k1.c @@ -596,7 +596,7 @@ static irqreturn_t spacemit_i2c_irq_handler(int irq, void *devid) status = readl(i2c->base + SPACEMIT_ISR); if (!status) - return IRQ_HANDLED; + return IRQ_NONE; i2c->status = status; From b7ef06d010c9ba77d0ffcbae9917b2f4c607f8ad Mon Sep 17 00:00:00 2001 From: Paul Greenwalt Date: Wed, 8 Apr 2026 16:11:05 +0200 Subject: [PATCH 2472/2758] ice: fix ice_init_link() error return preventing probe commit eb509638686b0f8a98a0dd9c809f6a8db4d73a45 upstream. ice_init_link() can return an error status from ice_update_link_info() or ice_init_phy_user_cfg(), causing probe to fail. An incorrect NVM update procedure can result in link/PHY errors, and the recommended resolution is to update the NVM using the correct procedure. If the driver fails probe due to link errors, the user cannot update the NVM to recover. The link/PHY errors logged are non-fatal: they are already annotated as 'not a fatal error if this fails'. Since none of the errors inside ice_init_link() should prevent probe from completing, convert it to void and remove the error check in the caller. All failures are already logged; callers have no meaningful recovery path for link init errors. Fixes: 5b246e533d01 ("ice: split probe into smaller functions") Cc: stable@vger.kernel.org Signed-off-by: Paul Greenwalt Signed-off-by: Aleksandr Loktionov Reviewed-by: Simon Horman Tested-by: Alexander Nowlin Signed-off-by: Tony Nguyen Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/ice/ice_main.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index e2fbe111f8496..e2fd2dab03e36 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -4789,16 +4789,14 @@ static void ice_init_wakeup(struct ice_pf *pf) device_set_wakeup_enable(ice_pf_to_dev(pf), false); } -static int ice_init_link(struct ice_pf *pf) +static void ice_init_link(struct ice_pf *pf) { struct device *dev = ice_pf_to_dev(pf); int err; err = ice_init_link_events(pf->hw.port_info); - if (err) { + if (err) dev_err(dev, "ice_init_link_events failed: %d\n", err); - return err; - } /* not a fatal error if this fails */ err = ice_init_nvm_phy_type(pf->hw.port_info); @@ -4838,8 +4836,6 @@ static int ice_init_link(struct ice_pf *pf) } else { set_bit(ICE_FLAG_NO_MEDIA, pf->flags); } - - return err; } static int ice_init_pf_sw(struct ice_pf *pf) @@ -4982,13 +4978,11 @@ static int ice_init(struct ice_pf *pf) ice_init_wakeup(pf); - err = ice_init_link(pf); - if (err) - goto err_init_link; + ice_init_link(pf); err = ice_send_version(pf); if (err) - goto err_init_link; + goto err_deinit_pf_sw; ice_verify_cacheline_size(pf); @@ -5007,7 +5001,7 @@ static int ice_init(struct ice_pf *pf) return 0; -err_init_link: +err_deinit_pf_sw: ice_deinit_pf_sw(pf); err_init_pf_sw: ice_dealloc_vsis(pf); From da48b9bf1eb95a9cfd09d615ca58cfc2b03de369 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Thu, 25 Jun 2026 19:21:40 +0100 Subject: [PATCH 2473/2758] tcp: defer md5sig_info kfree past RCU grace period in tcp_connect commit b74cd55038905d5e74c1de109ab78a30b2ea0e1f upstream. The md5+ao reconciliation in tcp_connect() (net/ipv4/tcp_output.c) has two symmetric branches: if (needs_md5) { tcp_ao_destroy_sock(sk, false); } else if (needs_ao) { tcp_clear_md5_list(sk); kfree(rcu_replace_pointer(tp->md5sig_info, NULL, ...)); } Both branches free a per-socket auth-info object while the socket is in TCP_SYN_SENT and is already on the inet ehash (inserted by inet_hash_connect() in tcp_v4_connect()). Both branches are reachable by softirq RX-path readers that load the corresponding info pointer via implicit RCU before bh_lock_sock_nested() is taken. The needs_md5 branch is fixed in the prior patch by re-introducing the call_rcu() free in tcp_ao_destroy_sock(): the equivalent per-key loop runs inside tcp_ao_info_free_rcu(), the RCU callback, so by the time it frees each tcp_ao_key all softirq readers that captured the container have already completed rcu_read_unlock(). The needs_ao branch is not symmetric in the same way. The container free can be deferred via kfree_rcu(md5sig, rcu) -- struct tcp_md5sig_info already has the required rcu member (include/net/tcp.h:1999-2002), and the rest of the tree already does this in the tcp_md5sig_info_add() rollback paths (net/ipv4/tcp_ipv4.c:1410, 1436). But the per-key teardown is done by tcp_clear_md5_list() in process context BEFORE the container's RCU grace period: it walks &md5sig->head and frees each tcp_md5sig_key with bare hlist_del + kfree. A concurrent softirq reader in __tcp_md5_do_lookup() / __tcp_md5_do_lookup_exact() (tcp_ipv4.c:1253, 1298) walks the same list via hlist_for_each_entry_rcu() and races with that bare kfree on the keys themselves -- a per-key slab use-after-free of the same class as the TCP-AO bug, on the same race window. Fix this in two halves: 1. Convert the bare kfree() in tcp_connect() to kfree_rcu() so the md5sig_info container joins the rest of the md5sig lifecycle. The local-variable lift is mechanical and required because kfree_rcu() is a macro that expects an lvalue. 2. Make tcp_clear_md5_list() RCU-safe by replacing hlist_del + kfree(key) with hlist_del_rcu + kfree_rcu(key, rcu). struct tcp_md5sig_key already carries the rcu member (include/net/tcp.h:1995) and tcp_md5_do_del() (net/ipv4/tcp_ipv4.c:1456) already uses kfree_rcu, so this restores the lifecycle invariant the rest of the file follows rather than introducing a one-off. The other caller of tcp_clear_md5_list() is tcp_md5_destruct_sock() (net/ipv4/tcp.c:412), which runs from the sock destructor when the socket is already unhashed and unreachable; the extra grace period there is unnecessary but harmless. Making the helper unconditionally RCU-safe is the cleaner contract. The needs_ao branch is not reachable by the userns reproducer used to demonstrate the AO-side splat (the repro installs both keys but ends up in the needs_md5 branch because the connect peer matches the MD5 key, not the AO key); however the symmetric race exists and a maintainer touching this code should not have to think about which branch escapes RCU and which one does not. Fixes: 51e547e8c89c ("tcp: Free TCP-AO/TCP-MD5 info/keys without RCU") Cc: stable@vger.kernel.org # v6.18+ Suggested-by: Eric Dumazet Signed-off-by: Michael Bommarito Reviewed-by: Dmitry Safonov Reviewed-by: Eric Dumazet [also credits to Qihang, who found that this races with tcp-diag] Reported-by: Qihang Signed-off-by: Dmitry Safonov <0x7f454c46@gmail.com> Link: https://patch.msgid.link/20260625-tcp-md5-connect-v3-2-1fd313d6c1e0@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv4/tcp_ipv4.c | 4 ++-- net/ipv4/tcp_output.c | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index fdc81150ff6cf..bfdabad01fa76 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1467,9 +1467,9 @@ void tcp_clear_md5_list(struct sock *sk) md5sig = rcu_dereference_protected(tp->md5sig_info, 1); hlist_for_each_entry_safe(key, n, &md5sig->head, node) { - hlist_del(&key->node); + hlist_del_rcu(&key->node); atomic_sub(sizeof(*key), &sk->sk_omem_alloc); - kfree(key); + kfree_rcu(key, rcu); } } diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 193637a58dcc1..48fe2fb7c7ade 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -4328,9 +4328,13 @@ int tcp_connect(struct sock *sk) if (needs_md5) { tcp_ao_destroy_sock(sk, false); } else if (needs_ao) { + struct tcp_md5sig_info *md5sig; + tcp_clear_md5_list(sk); - kfree(rcu_replace_pointer(tp->md5sig_info, NULL, - lockdep_sock_is_held(sk))); + md5sig = rcu_replace_pointer(tp->md5sig_info, NULL, + lockdep_sock_is_held(sk)); + if (md5sig) + kfree_rcu(md5sig, rcu); } } #endif From f48d3ae9d320862d88c046daa9ddeb1e73d9d72b Mon Sep 17 00:00:00 2001 From: Dmitry Safonov <0x7f454c46@gmail.com> Date: Thu, 25 Jun 2026 19:21:41 +0100 Subject: [PATCH 2474/2758] tcp: Decrement tcp_md5_needed static branch commit 6f6e860e370c9e4e919b92118a25e9e1f82e9180 upstream. In case of early freeing an unwanted TCP-MD5 key on TCP-AO connect(), md5sig_info is freed right away (and set to NULL). Later, at the moment of socket destruction, the static branch counter is not getting decremented. Add a missing decrement for TCP-MD5 static branch. Reported-by: Qihang Fixes: 0aadc73995d0 ("net/tcp: Prevent TCP-MD5 with TCP-AO being set") Cc: stable@vger.kernel.org Signed-off-by: Dmitry Safonov <0x7f454c46@gmail.com> Link: https://patch.msgid.link/20260625-tcp-md5-connect-v3-3-1fd313d6c1e0@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv4/tcp_output.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 48fe2fb7c7ade..a88c7eb72b965 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -4333,8 +4333,8 @@ int tcp_connect(struct sock *sk) tcp_clear_md5_list(sk); md5sig = rcu_replace_pointer(tp->md5sig_info, NULL, lockdep_sock_is_held(sk)); - if (md5sig) - kfree_rcu(md5sig, rcu); + kfree_rcu(md5sig, rcu); + static_branch_slow_dec_deferred(&tcp_md5_needed); } } #endif From 2510434307a224078302019e52ab3c863fbe87fb Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 30 Jun 2026 18:54:12 -0400 Subject: [PATCH 2475/2758] ufs: core: tracing: Do not dereference pointers in TP_printk() commit 535fcf4b8a261fbb8cc4f91e4597343c135a90f2 upstream. The trace events in drivers/ufs/core/ufs_trace.h were converted to take a pointer to the hba structure as an argument for the tracepoint and then in TP_printk() the printing of the dev_name from the ring buffer was converted to using the dev dereferenced pointer from the hba saved pointer. This is not allowed as the TP_printk() is executed at the time the trace event is read from /sys/kernel/tracing/trace file. That can happen literally, seconds, minutes, hours, weeks, days, or even months later! There is no guarantee that the hba pointer will still exist by the time it is dereferenced when the "trace" file is read. Instead, save the device name from the hba pointer at the time the tracepoint is called and place it into the ring buffer event. Then the TP_printk() can read the name directly from the ring buffer and remove the possibility that it will read a freed pointer and crash the kernel. This was detected when testing the trace event code that looks for TP_printk() parameters doing illegal derferences[1] [1] https://lore.kernel.org/all/20260630184836.74d477b6@gandalf.local.home/ Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260630185412.283c26c5@gandalf.local.home Fixes: 583e518e71003 ("scsi: ufs: core: Add hba parameter to trace events") Reviewed-by: Peter Wang Reviewed-by: Bart Van Assche Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- drivers/ufs/core/ufs_trace.h | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/drivers/ufs/core/ufs_trace.h b/drivers/ufs/core/ufs_trace.h index 309ae51b49063..377a3c54b9f56 100644 --- a/drivers/ufs/core/ufs_trace.h +++ b/drivers/ufs/core/ufs_trace.h @@ -89,16 +89,18 @@ TRACE_EVENT(ufshcd_clk_gating, TP_STRUCT__entry( __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __field(int, state) ), TP_fast_assign( + __assign_str(dev_name); __entry->hba = hba; __entry->state = state; ), TP_printk("%s: gating state changed to %s", - dev_name(__entry->hba->dev), + __get_str(dev_name), __print_symbolic(__entry->state, UFSCHD_CLK_GATING_STATES)) ); @@ -111,6 +113,7 @@ TRACE_EVENT(ufshcd_clk_scaling, TP_STRUCT__entry( __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __string(state, state) __string(clk, clk) __field(u32, prev_state) @@ -119,6 +122,7 @@ TRACE_EVENT(ufshcd_clk_scaling, TP_fast_assign( __entry->hba = hba; + __assign_str(dev_name); __assign_str(state); __assign_str(clk); __entry->prev_state = prev_state; @@ -126,7 +130,7 @@ TRACE_EVENT(ufshcd_clk_scaling, ), TP_printk("%s: %s %s from %u to %u Hz", - dev_name(__entry->hba->dev), __get_str(state), __get_str(clk), + __get_str(dev_name), __get_str(state), __get_str(clk), __entry->prev_state, __entry->curr_state) ); @@ -138,16 +142,18 @@ TRACE_EVENT(ufshcd_auto_bkops_state, TP_STRUCT__entry( __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __string(state, state) ), TP_fast_assign( __entry->hba = hba; + __assign_str(dev_name); __assign_str(state); ), TP_printk("%s: auto bkops - %s", - dev_name(__entry->hba->dev), __get_str(state)) + __get_str(dev_name), __get_str(state)) ); DECLARE_EVENT_CLASS(ufshcd_profiling_template, @@ -158,6 +164,7 @@ DECLARE_EVENT_CLASS(ufshcd_profiling_template, TP_STRUCT__entry( __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __string(profile_info, profile_info) __field(s64, time_us) __field(int, err) @@ -165,13 +172,14 @@ DECLARE_EVENT_CLASS(ufshcd_profiling_template, TP_fast_assign( __entry->hba = hba; + __assign_str(dev_name); __assign_str(profile_info); __entry->time_us = time_us; __entry->err = err; ), TP_printk("%s: %s: took %lld usecs, err %d", - dev_name(__entry->hba->dev), __get_str(profile_info), + __get_str(dev_name), __get_str(profile_info), __entry->time_us, __entry->err) ); @@ -200,6 +208,7 @@ DECLARE_EVENT_CLASS(ufshcd_template, __field(s64, usecs) __field(int, err) __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __field(int, dev_state) __field(int, link_state) ), @@ -208,13 +217,14 @@ DECLARE_EVENT_CLASS(ufshcd_template, __entry->usecs = usecs; __entry->err = err; __entry->hba = hba; + __assign_str(dev_name); __entry->dev_state = dev_state; __entry->link_state = link_state; ), TP_printk( "%s: took %lld usecs, dev_state: %s, link_state: %s, err %d", - dev_name(__entry->hba->dev), + __get_str(dev_name), __entry->usecs, __print_symbolic(__entry->dev_state, UFS_PWR_MODES), __print_symbolic(__entry->link_state, UFS_LINK_STATES), @@ -279,6 +289,7 @@ TRACE_EVENT(ufshcd_command, TP_STRUCT__entry( __field(struct scsi_device *, sdev) __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(&sdev->sdev_dev)) __field(enum ufs_trace_str_t, str_t) __field(unsigned int, tag) __field(u32, doorbell) @@ -291,6 +302,7 @@ TRACE_EVENT(ufshcd_command, ), TP_fast_assign( + __assign_str(dev_name); __entry->sdev = sdev; __entry->hba = hba; __entry->str_t = str_t; @@ -307,7 +319,7 @@ TRACE_EVENT(ufshcd_command, TP_printk( "%s: %s: tag: %u, DB: 0x%x, size: %d, IS: %u, LBA: %llu, opcode: 0x%x (%s), group_id: 0x%x, hwq_id: %d", show_ufs_cmd_trace_str(__entry->str_t), - dev_name(&__entry->sdev->sdev_dev), __entry->tag, + __get_str(dev_name), __entry->tag, __entry->doorbell, __entry->transfer_len, __entry->intr, __entry->lba, (u32)__entry->opcode, str_opcode(__entry->opcode), (u32)__entry->group_id, __entry->hwq_id @@ -322,6 +334,7 @@ TRACE_EVENT(ufshcd_uic_command, TP_STRUCT__entry( __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __field(enum ufs_trace_str_t, str_t) __field(u32, cmd) __field(u32, arg1) @@ -331,6 +344,7 @@ TRACE_EVENT(ufshcd_uic_command, TP_fast_assign( __entry->hba = hba; + __assign_str(dev_name); __entry->str_t = str_t; __entry->cmd = cmd; __entry->arg1 = arg1; @@ -340,7 +354,7 @@ TRACE_EVENT(ufshcd_uic_command, TP_printk( "%s: %s: cmd: 0x%x, arg1: 0x%x, arg2: 0x%x, arg3: 0x%x", - show_ufs_cmd_trace_str(__entry->str_t), dev_name(__entry->hba->dev), + show_ufs_cmd_trace_str(__entry->str_t), __get_str(dev_name), __entry->cmd, __entry->arg1, __entry->arg2, __entry->arg3 ) ); @@ -353,6 +367,7 @@ TRACE_EVENT(ufshcd_upiu, TP_STRUCT__entry( __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __field(enum ufs_trace_str_t, str_t) __array(unsigned char, hdr, 12) __array(unsigned char, tsf, 16) @@ -361,6 +376,7 @@ TRACE_EVENT(ufshcd_upiu, TP_fast_assign( __entry->hba = hba; + __assign_str(dev_name); __entry->str_t = str_t; memcpy(__entry->hdr, hdr, sizeof(__entry->hdr)); memcpy(__entry->tsf, tsf, sizeof(__entry->tsf)); @@ -369,7 +385,7 @@ TRACE_EVENT(ufshcd_upiu, TP_printk( "%s: %s: HDR:%s, %s:%s", - show_ufs_cmd_trace_str(__entry->str_t), dev_name(__entry->hba->dev), + show_ufs_cmd_trace_str(__entry->str_t), __get_str(dev_name), __print_hex(__entry->hdr, sizeof(__entry->hdr)), show_ufs_cmd_trace_tsf(__entry->tsf_t), __print_hex(__entry->tsf, sizeof(__entry->tsf)) @@ -384,16 +400,18 @@ TRACE_EVENT(ufshcd_exception_event, TP_STRUCT__entry( __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __field(u16, status) ), TP_fast_assign( __entry->hba = hba; + __assign_str(dev_name); __entry->status = status; ), TP_printk("%s: status 0x%x", - dev_name(__entry->hba->dev), __entry->status + __get_str(dev_name), __entry->status ) ); From 6883269a323609f68f6faa903f8f8ff3d191cec8 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Mon, 22 Jun 2026 19:25:41 +0800 Subject: [PATCH 2476/2758] xen/gntdev: fix error handling in ioctl commit 45ca1afe2fd14c04e37227e79d3f8455831d8408 upstream. When gntdev_ioctl_map_grant_ref() fails to copy the operation result back to userspace after successfully adding the mapping to the list, the error path returns -EFAULT without releasing the reference acquired by gntdev_alloc_map(). The mapping remains in priv->maps with a refcount of 1, causing a memory leak and a dangling list entry. Additionally, gntdev_add_map() may modify map->index to avoid overlap with existing mappings. Therefore, the index returned to userspace must be obtained after gntdev_add_map() completes. Fix this by holding the mutex across gntdev_add_map(), retrieving the correct index, and copy_to_user(). If copy_to_user() fails, remove the mapping from the list and release the reference while still holding the lock. Cc: stable@vger.kernel.org Fix these issues by properly handling all error cases. Fixes: 1401c00e59ea ("xen/gntdev: convert priv->lock to a mutex") Fixes: 68b025c813c2 ("xen-gntdev: Add reference counting to maps") Signed-off-by: Wentao Liang Reviewed-by: Juergen Gross Signed-off-by: Juergen Gross Message-ID: <20260622112541.38194-1-vulab@iscas.ac.cn> Signed-off-by: Greg Kroah-Hartman --- drivers/xen/gntdev.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c index 61ea855c45087..1dcc4675580ed 100644 --- a/drivers/xen/gntdev.c +++ b/drivers/xen/gntdev.c @@ -670,11 +670,15 @@ static long gntdev_ioctl_map_grant_ref(struct gntdev_priv *priv, mutex_lock(&priv->lock); gntdev_add_map(priv, map); op.index = map->index << PAGE_SHIFT; - mutex_unlock(&priv->lock); - if (copy_to_user(u, &op, sizeof(op)) != 0) + if (copy_to_user(u, &op, sizeof(op)) != 0) { + list_del(&map->next); + mutex_unlock(&priv->lock); + gntdev_put_map(priv, map); return -EFAULT; + } + mutex_unlock(&priv->lock); return 0; } From a8a7e6a9ff8a4c1f067694ddbd44be67fdf36693 Mon Sep 17 00:00:00 2001 From: Qianyu Luo Date: Thu, 25 Jun 2026 13:55:08 +0800 Subject: [PATCH 2477/2758] xfrm: nat_keepalive: avoid double free on send error commit 226f4a490d1a938fc838d8f8c46a4eca864c0d78 upstream. nat_keepalive_send() frees the keepalive skb whenever the IPv4 or IPv6 send helper reports an error. That cleanup is only correct before the skb is handed to the output path. Once ip_build_and_send_pkt() or ip6_xmit() takes ownership, the networking stack may already have consumed the skb before returning an error, so freeing it again is unsafe. Handle the pre-handoff failure cases inside nat_keepalive_send_ipv4() and nat_keepalive_send_ipv6(), where the caller still owns the skb, and keep nat_keepalive_send() responsible only for family dispatch and the unsupported-family cleanup path. Fixes: f531d13bdfe3 ("xfrm: support sending NAT keepalives in ESP in UDP states") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Xin Liu Signed-off-by: Qianyu Luo Signed-off-by: Ren Wei Reviewed-by: Eyal Birger Signed-off-by: Steffen Klassert Signed-off-by: Greg Kroah-Hartman --- net/xfrm/xfrm_nat_keepalive.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/net/xfrm/xfrm_nat_keepalive.c b/net/xfrm/xfrm_nat_keepalive.c index 458931062a04e..eb1b6f67739e1 100644 --- a/net/xfrm/xfrm_nat_keepalive.c +++ b/net/xfrm/xfrm_nat_keepalive.c @@ -55,8 +55,10 @@ static int nat_keepalive_send_ipv4(struct sk_buff *skb, ka->encap_sport, sock_net_uid(net, NULL)); rt = ip_route_output_key(net, &fl4); - if (IS_ERR(rt)) + if (IS_ERR(rt)) { + kfree_skb(skb); return PTR_ERR(rt); + } skb_dst_set(skb, &rt->dst); @@ -101,6 +103,7 @@ static int nat_keepalive_send_ipv6(struct sk_buff *skb, dst = ip6_dst_lookup_flow(net, sk, &fl6, NULL); if (IS_ERR(dst)) { local_unlock_nested_bh(&nat_keepalive_sk_ipv6.bh_lock); + kfree_skb(skb); return PTR_ERR(dst); } @@ -118,7 +121,6 @@ static void nat_keepalive_send(struct nat_keepalive *ka) sizeof(struct ipv6hdr)) + sizeof(struct udphdr); const u8 nat_ka_payload = 0xFF; - int err = -EAFNOSUPPORT; struct sk_buff *skb; struct udphdr *uh; @@ -140,16 +142,17 @@ static void nat_keepalive_send(struct nat_keepalive *ka) switch (ka->family) { case AF_INET: - err = nat_keepalive_send_ipv4(skb, ka); + nat_keepalive_send_ipv4(skb, ka); break; #if IS_ENABLED(CONFIG_IPV6) case AF_INET6: - err = nat_keepalive_send_ipv6(skb, ka, uh); + nat_keepalive_send_ipv6(skb, ka, uh); break; #endif - } - if (err) + default: kfree_skb(skb); + break; + } } struct nat_keepalive_work_ctx { From b4e9dcf4143ec27e52a017687ed75715358cc209 Mon Sep 17 00:00:00 2001 From: Sanman Pradhan Date: Sun, 7 Jun 2026 16:47:34 +0000 Subject: [PATCH 2478/2758] xfrm: use compat translator only for u64 alignment mismatch commit 355fbcbdc2539cca7890b0d0914d4ce0f985ad74 upstream. The XFRM compat layer (CONFIG_XFRM_USER_COMPAT) translates 32-bit xfrm netlink and setsockopt messages into the native 64-bit layout. It is only needed on architectures where the 32-bit and 64-bit ABIs disagree on u64 alignment, which the kernel encodes as COMPAT_FOR_U64_ALIGNMENT. That symbol is defined only by arch/x86. XFRM_USER_COMPAT depends on it, so the translator can never be built on any other architecture, including arm64, which still provides a 32-bit compat ABI (CONFIG_COMPAT) for AArch32 EL0 userspace. On arm64 the AArch32 EABI already aligns u64 to 8 bytes, identical to the AArch64 ABI, so no translation is required and the native code path is correct for 32-bit tasks. However, xfrm_user_rcv_msg() and xfrm_user_policy() gate on in_compat_syscall() alone and then call xfrm_get_translator(), which returns NULL when no translator is registered. On arm64 that is always the case, so every xfrm netlink message and the XFRM_POLICY setsockopt issued by a 32-bit task returns -EOPNOTSUPP. A 32-bit userspace process on arm64 (and on any other arch with CONFIG_COMPAT but without COMPAT_FOR_U64_ALIGNMENT) therefore cannot configure XFRM state or policy through the XFRM_USER netlink API, and cannot use the XFRM_POLICY setsockopt path, because both fail before reaching the native parser. The translator series replaced the blanket compat rejection with a translator lookup. That made the path usable on x86 when the translator is available, but left architectures that cannot build the translator permanently rejected even when their compat layout already matches the native layout. Let those architectures use the native parser instead. Gate the translator requirement on COMPAT_FOR_U64_ALIGNMENT instead of on in_compat_syscall() alone. Gating on the ABI property rather than on CONFIG_XFRM_USER_COMPAT is deliberate: on x86 with IA32_EMULATION=y but XFRM_USER_COMPAT=n, a 32-bit task must still be rejected rather than routed through the native parser, which would misread genuinely 4-byte-aligned x86-32 messages. COMPAT_FOR_U64_ALIGNMENT is the ABI property that makes the XFRM translator mandatory. Only the receive/input direction needs the guard. The send, dump and notification paths already call the translator as "if (xtr) { ... }" with no error on NULL, so on arches without a translator they no-op and the kernel emits native 64-bit-layout messages, which is what an AArch32 task expects. Tested on Juniper SRX hardware: with the fix, 32-bit IPsec userspace netlink and XFRM_POLICY setsockopt operations that previously failed with -EOPNOTSUPP now succeed; x86 behaviour is unchanged by inspection. Fixes: 5106f4a8acff ("xfrm/compat: Add 32=>64-bit messages translator") Fixes: 96392ee5a13b ("xfrm/compat: Translate 32-bit user_policy from sockptr") Cc: stable@vger.kernel.org Signed-off-by: Sanman Pradhan Signed-off-by: Steffen Klassert Signed-off-by: Greg Kroah-Hartman --- net/xfrm/xfrm_state.c | 2 +- net/xfrm/xfrm_user.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index ae4fdc60c2a62..ec9ecf72c76ba 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -2984,7 +2984,7 @@ int xfrm_user_policy(struct sock *sk, int optname, sockptr_t optval, int optlen) if (IS_ERR(data)) return PTR_ERR(data); - if (in_compat_syscall()) { + if (IS_ENABLED(CONFIG_COMPAT_FOR_U64_ALIGNMENT) && in_compat_syscall()) { struct xfrm_translator *xtr = xfrm_get_translator(); if (!xtr) { diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 8de2ebb0f9d79..61eb5de33b87e 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -3472,7 +3472,7 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, if (!netlink_net_capable(skb, CAP_NET_ADMIN)) return -EPERM; - if (in_compat_syscall()) { + if (IS_ENABLED(CONFIG_COMPAT_FOR_U64_ALIGNMENT) && in_compat_syscall()) { struct xfrm_translator *xtr = xfrm_get_translator(); if (!xtr) From 3ba2b2ef7d6a63b190f15cfc2b4ba0fba59928ea Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Fri, 12 Jun 2026 16:59:41 +0800 Subject: [PATCH 2479/2758] xfrm: xfrm_interface: require CAP_NET_ADMIN in the device netns for changelink commit 095515d89b19b6cc19dfcdc846f97403ed1ebce3 upstream. xfrmi_changelink() operates on at most two netns, dev_net(dev) and the interface link netns xi->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in xi->net can rewrite an interface that lives in xi->net. Gate xfrmi_changelink() on rtnl_dev_link_net_capable() at its top, before any attribute is parsed. Reported-by: Xiao Liang Closes: https://lore.kernel.org/netdev/CABAhCOSzP1vaThGV35_VnsRCb=87_CPjPVsTHbq905k8A+BuUg@mail.gmail.com/ Fixes: f203b76d7809 ("xfrm: Add virtual xfrm interfaces") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260612085941.3158249-8-maoyixie.tju@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/xfrm/xfrm_interface_core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/xfrm/xfrm_interface_core.c b/net/xfrm/xfrm_interface_core.c index 330a05286a56f..688306bf62c5a 100644 --- a/net/xfrm/xfrm_interface_core.c +++ b/net/xfrm/xfrm_interface_core.c @@ -869,6 +869,9 @@ static int xfrmi_changelink(struct net_device *dev, struct nlattr *tb[], struct net *net = xi->net; struct xfrm_if_parms p = {}; + if (!rtnl_dev_link_net_capable(dev, net)) + return -EPERM; + xfrmi_netlink_parms(data, &p); if (!p.if_id) { NL_SET_ERR_MSG(extack, "if_id must be non zero"); From 98fa6e42fd51f9d1a9fdc4f46dcb705d758b3dc0 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 15 Jun 2026 15:02:05 +0300 Subject: [PATCH 2480/2758] tpm: fix event_size output in tpm1_binary_bios_measurements_show commit 1a58f6115bfb34eabcc7de8a3a9745b219179781 upstream. Commit 186d124f07da ("tpm_eventlog.c: fix binary_bios_measurements") split the output to write the endian-converted event header first and then the variable-length event data. However, the split was at sizeof(struct tcpa_event) - 1, even though event_data was a zero-length array, and later a flexible array member, both of which already excluded the event data. Therefore, the current code writes the first three bytes of event_size from the endian-converted header and then the last byte from the raw header, which can emit a corrupted event_size on PPC64, where do_endian_conversion() maps to be32_to_cpu(). Split one byte later to write the full endian-converted header first, followed by the variable-length event->event_data. Fixes: 186d124f07da ("tpm_eventlog.c: fix binary_bios_measurements") Cc: stable@vger.kernel.org # v5.10+ Signed-off-by: Thorsten Blum Signed-off-by: Jarkko Sakkinen Signed-off-by: Greg Kroah-Hartman --- drivers/char/tpm/eventlog/tpm1.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/char/tpm/eventlog/tpm1.c b/drivers/char/tpm/eventlog/tpm1.c index e7913b2853d53..0397e3361020a 100644 --- a/drivers/char/tpm/eventlog/tpm1.c +++ b/drivers/char/tpm/eventlog/tpm1.c @@ -236,12 +236,12 @@ static int tpm1_binary_bios_measurements_show(struct seq_file *m, void *v) temp_ptr = (char *) &temp_event; - for (i = 0; i < (sizeof(struct tcpa_event) - 1) ; i++) + for (i = 0; i < sizeof(struct tcpa_event); i++) seq_putc(m, temp_ptr[i]); temp_ptr = (char *) v; - for (i = (sizeof(struct tcpa_event) - 1); + for (i = sizeof(struct tcpa_event); i < (sizeof(struct tcpa_event) + temp_event.event_size); i++) seq_putc(m, temp_ptr[i]); From 947b773caaa548672184df025271b29bdc80b0f1 Mon Sep 17 00:00:00 2001 From: Jaewon Yang Date: Mon, 13 Jul 2026 02:11:47 +0900 Subject: [PATCH 2481/2758] tpm: Make the TPM character devices non-seekable commit f20d61c22bcaf172d6790b6500e3838e532e71c8 upstream. The TPM character devices expose a sequential command/response interface, but their open handlers leave FMODE_PREAD and FMODE_PWRITE enabled. After a command leaves a response pending, pread(fd, buf, 16, 0x1400) passes 0x1400 as *off to tpm_common_read(). The transfer length is bounded by response_length, but the offset is used unchecked when forming data_buffer + *off. A sufficiently large offset therefore causes an out-of-bounds heap read through copy_to_user() and, if the copy succeeds, an out-of-bounds zero-write through the following memset(). Positional I/O does not provide coherent semantics for this interface. An arbitrary pread offset cannot represent how much of a response has been consumed sequentially. The write callback always stores a command at the start of data_buffer, while pwrite() does not update file->f_pos and can leave the sequential read cursor stale. Call nonseekable_open() from both open handlers. This removes FMODE_PREAD and FMODE_PWRITE, causing positional reads and writes to fail with -ESPIPE before reaching the TPM callbacks, and explicitly marks the files non-seekable. Normal read() and write() continue to use the existing sequential f_pos cursor, leaving the response state machine unchanged. Tested on Linux 6.12 with KASAN and a swtpm TPM2 device: - sequential partial reads returned the complete response - pread() and preadv() with offset 0x1400 returned -ESPIPE - pwrite() and pwritev() with offset zero returned -ESPIPE - the pending response remained intact after the rejected operations - a subsequent normal command/response cycle completed normally - no KASAN report was produced. Fixes: 9488585b21be ("tpm: add support for partial reads") Link: https://lore.kernel.org/all/20260710090217.191289-1-yong010301@gmail.com/ Cc: stable@vger.kernel.org Signed-off-by: Jaewon Yang Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/char/tpm/tpm-dev.c | 2 +- drivers/char/tpm/tpmrm-dev.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c index 2779a8738c59e..74488f0a7b789 100644 --- a/drivers/char/tpm/tpm-dev.c +++ b/drivers/char/tpm/tpm-dev.c @@ -36,7 +36,7 @@ static int tpm_open(struct inode *inode, struct file *file) tpm_common_open(file, chip, priv, NULL); - return 0; + return nonseekable_open(inode, file); out: clear_bit(0, &chip->is_open); diff --git a/drivers/char/tpm/tpmrm-dev.c b/drivers/char/tpm/tpmrm-dev.c index f48d4d9e179cd..19e8f2779265f 100644 --- a/drivers/char/tpm/tpmrm-dev.c +++ b/drivers/char/tpm/tpmrm-dev.c @@ -29,7 +29,7 @@ static int tpmrm_open(struct inode *inode, struct file *file) tpm_common_open(file, chip, &priv->priv, &priv->space); - return 0; + return nonseekable_open(inode, file); } static int tpmrm_release(struct inode *inode, struct file *file) From 91376c61a5fd77c989230bcd33fb51d21b40d55c Mon Sep 17 00:00:00 2001 From: Wang Yan Date: Mon, 22 Jun 2026 18:33:48 +0800 Subject: [PATCH 2482/2758] time: Fix off-by-one in compat settimeofday() usec validation commit 269f2b43fae692d1f3988c9f888a6301aa537b82 upstream. The compat version of settimeofday() uses '>' instead of '>=' when validating tv_usec against USEC_PER_SEC, allowing the value 1000000 to pass the check. After the subsequent conversion to nanoseconds (tv_nsec *= NSEC_PER_USEC), this results in tv_nsec == NSEC_PER_SEC, which violates the timespec invariant that tv_nsec must be strictly less than NSEC_PER_SEC. The native settimeofday() was already fixed in commit ce4abda5e126 ("time: Fix off-by-one in settimeofday() usec validation"), but the compat counterpart was missed. Fix it by using '>=' to reject tv_usec values outside the valid range [0, USEC_PER_SEC - 1]. Fixes: 5e0fb1b57bea ("y2038: time: avoid timespec usage in settimeofday()") Signed-off-by: Wang Yan Signed-off-by: Thomas Gleixner Acked-by: Arnd Bergmann Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260622103348.120255-1-wangyan01@kylinos.cn Signed-off-by: Greg Kroah-Hartman --- kernel/time/time.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/time/time.c b/kernel/time/time.c index 771cef87ad3b0..0dd63a91e7c58 100644 --- a/kernel/time/time.c +++ b/kernel/time/time.c @@ -251,7 +251,7 @@ COMPAT_SYSCALL_DEFINE2(settimeofday, struct old_timeval32 __user *, tv, get_user(new_ts.tv_nsec, &tv->tv_usec)) return -EFAULT; - if (new_ts.tv_nsec > USEC_PER_SEC || new_ts.tv_nsec < 0) + if (new_ts.tv_nsec >= USEC_PER_SEC || new_ts.tv_nsec < 0) return -EINVAL; new_ts.tv_nsec *= NSEC_PER_USEC; From 40dee2d3e9994aed9efe7bed40eb0e4d38d5a25c Mon Sep 17 00:00:00 2001 From: Javier Fernandez Pastrana Date: Wed, 24 Jun 2026 17:19:58 +0200 Subject: [PATCH 2483/2758] spi: imx: reconfigure for PIO when DMA cannot be started commit 245404c26563aafb36aafb01298f148db1851be3 upstream. When spi_imx_can_dma() selects DMA, the ECSPI is configured for DMA: spi_imx_setupxfer() sets CTRL.SMC and clears dynamic_burst, and spi_imx_dma_transfer() programs the dynamic-burst BURST_LENGTH and the SDMA watermarks. If the DMA descriptor cannot be prepared (dmaengine_prep_slave_single() returns NULL), the transfer is failed with SPI_TRANS_FAIL_NO_START and falls back to PIO. The dynamic-burst DMA path uses its own bounce buffers instead of the SPI core's mapping, so xfer->{tx,rx}_sg_mapped are not set and the core's DMA->PIO retry is skipped; the driver falls back to PIO internally. But none of the DMA-mode configuration is undone, so the PIO transfer runs with CTRL.SMC set, the wrong burst length and dynamic_burst cleared, and the transferred data is corrupted. This is easily hit on i.MX8MP boards that describe ECSPI DMA in the device tree but run SDMA on ROM firmware (no external sdma-imx7d.bin): every ECSPI DMA prepare fails. An Infineon SLB9670 TPM on ECSPI1 then returns shifted TPM2_GetCapability data, is flagged "field failure mode", /dev/tpmrm0 is never created. Set controller->fallback before re-running spi_imx_setupxfer() so the ECSPI is reconfigured exactly like a normal PIO transfer. With controller->fallback set, spi_imx_setupxfer() sees spi_imx_can_dma() return false, so it clears spi_imx->usedma and reprograms the controller (clears CTRL.SMC, restores dynamic_burst and the PIO burst length). No explicit spi_imx->usedma = false is needed: setupxfer() already updates it from the can_dma() result. Fixes: faa8e404ad8e ("spi: imx: support dynamic burst length for ECSPI DMA mode") Cc: stable@vger.kernel.org Signed-off-by: Javier Fernandez Pastrana Acked-by: Carlos Song Reviewed-by: Frank Li Link: https://patch.msgid.link/20260624151958.18626-1-javier.pastrana@linutronix.de Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi-imx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index ae9912905c67a..79a6c1a60b0a0 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -2152,7 +2152,8 @@ static int spi_imx_transfer_one(struct spi_controller *controller, if (spi_imx->usedma) { ret = spi_imx_dma_transfer(spi_imx, transfer); if (transfer->error & SPI_TRANS_FAIL_NO_START) { - spi_imx->usedma = false; + controller->fallback = true; + spi_imx_setupxfer(spi, transfer); if (spi_imx->target_mode) return spi_imx_pio_transfer_target(spi, transfer); else From 077a7bc1c32d3da9670c5e282ea3e5ac8a94be59 Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Tue, 16 Jun 2026 10:12:23 +0900 Subject: [PATCH 2484/2758] spi: uniphier: Fix completion initialization order before devm_request_irq() commit f3ad1c87d8201e54b66bd6072442f0b5d5a308ee upstream. The driver calls devm_request_irq() before initializing the completion used by the interrupt handler. Because the interrupt may occur immediately after devm_request_irq(), the handler may execute before init_completion(). This may result in calling complete() on an uninitialized completion, causing undefined behavior. This has been observed with KASAN. Fix this by initializing the completion before registering the IRQ. Reported-by: Sangyun Kim Reported-by: Kyungwook Boo Fixes: 5ba155a4d4cc ("spi: add SPI controller driver for UniPhier SoC") Cc: stable@vger.kernel.org Cc: Masami Hiramatsu Signed-off-by: Kunihiko Hayashi Reviewed-by: Masami Hiramatsu (Google) Link: https://patch.msgid.link/20260616011223.201357-1-hayashi.kunihiko@socionext.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi-uniphier.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-uniphier.c b/drivers/spi/spi-uniphier.c index eac6c3e8908bd..f2859e96003a2 100644 --- a/drivers/spi/spi-uniphier.c +++ b/drivers/spi/spi-uniphier.c @@ -659,6 +659,8 @@ static int uniphier_spi_probe(struct platform_device *pdev) priv->host = host; priv->is_save_param = false; + init_completion(&priv->xfer_done); + priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(priv->base)) { ret = PTR_ERR(priv->base); @@ -686,8 +688,6 @@ static int uniphier_spi_probe(struct platform_device *pdev) goto out_host_put; } - init_completion(&priv->xfer_done); - clk_rate = clk_get_rate(priv->clk); host->max_speed_hz = DIV_ROUND_UP(clk_rate, SSI_MIN_CLK_DIVIDER); From bbd6b2ea966cf57b6ae095cf5a8dbc993cd197a0 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Fri, 3 Jul 2026 20:35:46 -0700 Subject: [PATCH 2485/2758] sctp: validate STALE_COOKIE cause length before reading staleness commit 1cd23ca80784223fa2204e16203f754da4e821f8 upstream. When an ERROR chunk with a STALE_COOKIE cause is received in the COOKIE_ECHOED state, sctp_sf_do_5_2_6_stale() reads the 4-byte Measure of Staleness that follows the cause header: err = (struct sctp_errhdr *)(chunk->skb->data); stale = ntohl(*(__be32 *)((u8 *)err + sizeof(*err))); err is the first cause in the chunk, not the STALE_COOKIE cause that caused the dispatch, and nothing guarantees the staleness field is present. sctp_walk_errors() only requires a cause to be as long as the 4-byte header, so for a STALE_COOKIE cause of length 4 the read runs past the cause, and for a minimal ERROR chunk past skb->tail. The value is echoed to the peer in the Cookie Preservative of the reply INIT, leaking uninitialized memory. sctp_sf_cookie_echoed_err() already walks to the STALE_COOKIE cause, so check its length there and pass it to sctp_sf_do_5_2_6_stale(), which reads that cause instead of the first one. A STALE_COOKIE cause too short to hold the staleness field is discarded. The read is reachable by any peer that can drive an association into COOKIE_ECHOED, including an unprivileged process using a raw SCTP socket in a user and network namespace. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: Xiang Mei Assisted-by: Claude:claude-opus-4-8 Cc: stable@vger.kernel.org Signed-off-by: Weiming Shi Acked-by: Xin Long Link: https://patch.msgid.link/20260704033545.2438373-2-bestswngs@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- net/sctp/sm_statefuns.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index d23d935e128e9..3893b44448b38 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -74,7 +74,8 @@ static enum sctp_disposition sctp_sf_do_5_2_6_stale( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - struct sctp_cmd_seq *commands); + struct sctp_cmd_seq *commands, + struct sctp_errhdr *err); static enum sctp_disposition sctp_sf_shut_8_4_5( struct net *net, const struct sctp_endpoint *ep, @@ -2529,9 +2530,15 @@ enum sctp_disposition sctp_sf_cookie_echoed_err( * errors. */ sctp_walk_errors(err, chunk->chunk_hdr) { - if (SCTP_ERROR_STALE_COOKIE == err->cause) - return sctp_sf_do_5_2_6_stale(net, ep, asoc, type, - arg, commands); + if (err->cause != SCTP_ERROR_STALE_COOKIE) + continue; + /* The staleness is only meaningful if the cause is long + * enough to hold it; a shorter one is malformed. + */ + if (ntohs(err->length) < sizeof(*err) + sizeof(__be32)) + break; + return sctp_sf_do_5_2_6_stale(net, ep, asoc, type, + arg, commands, err); } /* It is possible to have malformed error causes, and that @@ -2573,13 +2580,13 @@ static enum sctp_disposition sctp_sf_do_5_2_6_stale( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - struct sctp_cmd_seq *commands) + struct sctp_cmd_seq *commands, + struct sctp_errhdr *err) { int attempts = asoc->init_err_counter + 1; - struct sctp_chunk *chunk = arg, *reply; struct sctp_cookie_preserve_param bht; struct sctp_bind_addr *bp; - struct sctp_errhdr *err; + struct sctp_chunk *reply; u32 stale; if (attempts > asoc->max_init_attempts) { @@ -2590,8 +2597,6 @@ static enum sctp_disposition sctp_sf_do_5_2_6_stale( return SCTP_DISPOSITION_DELETE_TCB; } - err = (struct sctp_errhdr *)(chunk->skb->data); - /* When calculating the time extension, an implementation * SHOULD use the RTT information measured based on the * previous COOKIE ECHO / ERROR exchange, and should add no From a192b6c149c6ea10cc88869accb78165eb454456 Mon Sep 17 00:00:00 2001 From: Benjamin Coddington Date: Tue, 7 Jul 2026 11:07:16 -0400 Subject: [PATCH 2486/2758] NFS: Charge unstable writes by request size, not folio size commit 27934d02cbeb8a957dd11c985a579e58d30c5270 upstream. nfs_folio_mark_unstable() and nfs_folio_clear_commit() charge and uncharge NR_WRITEBACK/WB_WRITEBACK by folio_nr_pages(folio) once per *request* added to or removed from a commit list. This is correct only when a folio has a single associated request. When pg_test splits a folio into N sub-folio requests (e.g. pNFS flexfiles striping with a stripe unit smaller than the folio size, or plain wsize-limited splitting), each of the N requests independently charges the whole folio's page count, inflating the accounting by a factor of N per folio. With large folios and small stripe units this reaches multiple orders of magnitude: a 2 MiB folio split into 512 4 KiB requests can charge up to 512x its real size, pushing global dirty+writeback accounting past the system's dirty threshold and forcing every buffered writer on the host into the hard-throttle path, including unrelated in-kernel NFS server threads sharing the box. Charge each request only for the pages it actually covers. Fixes: 0c493b5cf16e ("NFS: Convert buffered writes to use folios") Cc: stable@vger.kernel.org Signed-off-by: Benjamin Coddington Assisted-By: Claude Sonnet 5 Signed-off-by: Anna Schumaker Signed-off-by: Greg Kroah-Hartman --- fs/nfs/internal.h | 12 +++++++----- fs/nfs/pnfs_nfs.c | 2 +- fs/nfs/write.c | 14 ++++++++------ 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 18d46b0e71ddc..1d5d62f88ddef 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -847,17 +847,19 @@ void nfs_super_set_maxbytes(struct super_block *sb, __u64 maxfilesize) } /* - * Record the page as unstable (an extra writeback period) and mark its - * inode as dirty. + * Record the request's range as unstable (an extra writeback period) and + * mark its inode as dirty. */ -static inline void nfs_folio_mark_unstable(struct folio *folio, +static inline void nfs_folio_mark_unstable(struct nfs_page *req, struct nfs_commit_info *cinfo) { + struct folio *folio = nfs_page_to_folio(req); + if (folio && !cinfo->dreq) { struct inode *inode = folio->mapping->host; - long nr = folio_nr_pages(folio); + long nr = DIV_ROUND_UP(req->wb_bytes, PAGE_SIZE); - /* This page is really still in write-back - just that the + /* This range is really still in write-back - just that the * writeback is happening on the server now. */ node_stat_mod_folio(folio, NR_WRITEBACK, nr); diff --git a/fs/nfs/pnfs_nfs.c b/fs/nfs/pnfs_nfs.c index 0ff43dbcb7cd7..648c95b78eeab 100644 --- a/fs/nfs/pnfs_nfs.c +++ b/fs/nfs/pnfs_nfs.c @@ -1199,7 +1199,7 @@ pnfs_layout_mark_request_commit(struct nfs_page *req, nfs_request_add_commit_list_locked(req, list, cinfo); mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex); - nfs_folio_mark_unstable(nfs_page_to_folio(req), cinfo); + nfs_folio_mark_unstable(req, cinfo); return; out_resched: mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex); diff --git a/fs/nfs/write.c b/fs/nfs/write.c index d7c399763ad94..f7a5fb8140c47 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -807,7 +807,7 @@ nfs_request_add_commit_list(struct nfs_page *req, struct nfs_commit_info *cinfo) mutex_lock(&NFS_I(cinfo->inode)->commit_mutex); nfs_request_add_commit_list_locked(req, &cinfo->mds->list, cinfo); mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex); - nfs_folio_mark_unstable(nfs_page_to_folio(req), cinfo); + nfs_folio_mark_unstable(req, cinfo); } EXPORT_SYMBOL_GPL(nfs_request_add_commit_list); @@ -866,10 +866,12 @@ nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg, nfs_request_add_commit_list(req, cinfo); } -static void nfs_folio_clear_commit(struct folio *folio) +static void nfs_folio_clear_commit(struct nfs_page *req) { + struct folio *folio = nfs_page_to_folio(req); + if (folio) { - long nr = folio_nr_pages(folio); + long nr = DIV_ROUND_UP(req->wb_bytes, PAGE_SIZE); node_stat_mod_folio(folio, NR_WRITEBACK, -nr); bdi_wb_stat_mod(folio->mapping->host, WB_WRITEBACK, -nr); @@ -889,7 +891,7 @@ static void nfs_clear_request_commit(struct nfs_commit_info *cinfo, nfs_request_remove_commit_list(req, cinfo); } mutex_unlock(&NFS_I(inode)->commit_mutex); - nfs_folio_clear_commit(nfs_page_to_folio(req)); + nfs_folio_clear_commit(req); } } @@ -1741,7 +1743,7 @@ void nfs_retry_commit(struct list_head *page_list, req = nfs_list_entry(page_list->next); nfs_list_remove_request(req); nfs_mark_request_commit(req, lseg, cinfo, ds_commit_idx); - nfs_folio_clear_commit(nfs_page_to_folio(req)); + nfs_folio_clear_commit(req); nfs_unlock_and_release_request(req); } } @@ -1813,7 +1815,7 @@ static void nfs_commit_release_pages(struct nfs_commit_data *data) req = nfs_list_entry(data->pages.next); nfs_list_remove_request(req); folio = nfs_page_to_folio(req); - nfs_folio_clear_commit(folio); + nfs_folio_clear_commit(req); dprintk("NFS: commit (%s/%llu %d@%lld)", nfs_req_openctx(req)->dentry->d_sb->s_id, From b7d9aaedf024bb6c0bb6a205848861d888eb1afa Mon Sep 17 00:00:00 2001 From: Nick Chan Date: Sun, 7 Jun 2026 14:10:58 +0800 Subject: [PATCH 2487/2758] nvme-apple: Prevent shared tags across queues on Apple A11 commit 6fe0687245e8406bf26143bd45eb16441bbe5280 upstream. On Apple A11, tags of pending commands must be unique across the admin and IO queues, else the firmware crashes with "duplicate tag error for tag N", with N being the tag. Apply the existing workaround for M1 of reserving two tags for the admin queue to A11. Cc: stable@vger.kernel.org Fixes: 04d8ecf37b5e ("nvme: apple: Add Apple A11 support") Reviewed-by: Sven Peter Signed-off-by: Nick Chan Signed-off-by: Keith Busch Signed-off-by: Greg Kroah-Hartman --- drivers/nvme/host/apple.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/nvme/host/apple.c b/drivers/nvme/host/apple.c index c692fc73babfe..da6e983e20058 100644 --- a/drivers/nvme/host/apple.c +++ b/drivers/nvme/host/apple.c @@ -225,7 +225,7 @@ static unsigned int apple_nvme_queue_depth(struct apple_nvme_queue *q) { struct apple_nvme *anv = queue_to_apple_nvme(q); - if (q->is_adminq && anv->hw->has_lsq_nvmmu) + if (q->is_adminq) return APPLE_NVME_AQ_DEPTH; return anv->hw->max_queue_depth; @@ -303,7 +303,7 @@ static void apple_nvme_submit_cmd_t8015(struct apple_nvme_queue *q, memcpy((void *)q->sqes + (q->sq_tail << APPLE_NVME_IOSQES), cmd, sizeof(*cmd)); - if (++q->sq_tail == anv->hw->max_queue_depth) + if (++q->sq_tail == apple_nvme_queue_depth(q)) q->sq_tail = 0; writel(q->sq_tail, q->sq_db); @@ -1139,10 +1139,7 @@ static void apple_nvme_reset_work(struct work_struct *work) } /* Setup the admin queue */ - if (anv->hw->has_lsq_nvmmu) - aqa = APPLE_NVME_AQ_DEPTH - 1; - else - aqa = anv->hw->max_queue_depth - 1; + aqa = APPLE_NVME_AQ_DEPTH - 1; aqa |= aqa << 16; writel(aqa, anv->mmio_nvme + NVME_REG_AQA); writeq(anv->adminq.sq_dma_addr, anv->mmio_nvme + NVME_REG_ASQ); @@ -1325,8 +1322,7 @@ static int apple_nvme_alloc_tagsets(struct apple_nvme *anv) * both queues. The admin queue gets the first APPLE_NVME_AQ_DEPTH which * must be marked as reserved in the IO queue. */ - if (anv->hw->has_lsq_nvmmu) - anv->tagset.reserved_tags = APPLE_NVME_AQ_DEPTH; + anv->tagset.reserved_tags = APPLE_NVME_AQ_DEPTH; anv->tagset.queue_depth = anv->hw->max_queue_depth - 1; anv->tagset.timeout = NVME_IO_TIMEOUT; anv->tagset.numa_node = NUMA_NO_NODE; From bc111698b46e43eddd8664cceaa621cd559e99a0 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 9 Jun 2026 14:24:31 -0400 Subject: [PATCH 2488/2758] nvmet-auth: reject short AUTH_RECEIVE buffers commit 779575bc35c687697ba69e904f2cd22e60112534 upstream. nvmet_execute_auth_receive() trusts the AUTH_RECEIVE allocation length after checking only that it is nonzero and matches the transfer length. In the SUCCESS1 and FAILURE1/default states, that lets a remote NVMe-oF initiator reach the fixed-size DH-HMAC-CHAP response builders with a kmalloc() buffer shorter than the response, so nvmet_auth_success1() and nvmet_auth_failure1() write past the allocation; both only WARN_ON the short length and then format the message anyway. Impact: A remote NVMe-oF initiator with access to an auth-enabled target can trigger a 16-byte heap out-of-bounds write via a one-byte AUTH_RECEIVE allocation length. Compute the minimum response length for the current DH-HMAC-CHAP step in nvmet_auth_receive_data_len() and report a zero data length when the host-supplied allocation length is shorter, so the existing zero-length check in nvmet_execute_auth_receive() rejects the command before any builder runs. The SUCCESS1 minimum is sizeof(struct nvmf_auth_dhchap_success1_data) plus the HMAC hash length, because the response hash is written into the rval[] flexible-array tail, so the minimum is state dependent rather than a flat sizeof. CHALLENGE keeps its existing variable-length guard in nvmet_auth_challenge(). This is reachable only when in-band DH-HMAC-CHAP authentication is configured on the target. Fixes: db1312dd9548 ("nvmet: implement basic In-Band Authentication") Cc: stable@vger.kernel.org Assisted-by: Codex:gpt-5-5-xhigh Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Hannes Reinecke Signed-off-by: Michael Bommarito Signed-off-by: Keith Busch Signed-off-by: Greg Kroah-Hartman --- drivers/nvme/target/fabrics-cmd-auth.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/nvme/target/fabrics-cmd-auth.c b/drivers/nvme/target/fabrics-cmd-auth.c index 0a85acf1e5c71..45820a12750de 100644 --- a/drivers/nvme/target/fabrics-cmd-auth.c +++ b/drivers/nvme/target/fabrics-cmd-auth.c @@ -493,7 +493,31 @@ static void nvmet_auth_failure1(struct nvmet_req *req, void *d, int al) u32 nvmet_auth_receive_data_len(struct nvmet_req *req) { - return le32_to_cpu(req->cmd->auth_receive.al); + struct nvmet_ctrl *ctrl = req->sq->ctrl; + u32 al = le32_to_cpu(req->cmd->auth_receive.al); + u32 min_len; + + /* + * Reject too-short al before kmalloc(al), since the SUCCESS1 and + * FAILURE1/default builders write fixed response headers into it. + */ + switch (req->sq->dhchap_step) { + case NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE: + return al; + case NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1: + min_len = sizeof(struct nvmf_auth_dhchap_success1_data); + if (req->sq->dhchap_c2) + min_len += nvme_auth_hmac_hash_len(ctrl->shash_id); + break; + default: + min_len = sizeof(struct nvmf_auth_dhchap_failure_data); + break; + } + + if (al < min_len) + return 0; + + return al; } void nvmet_execute_auth_receive(struct nvmet_req *req) From 98bcdfa619150b2f41fa15bac140dbaf2584ad05 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Thu, 4 Jun 2026 19:36:54 +0000 Subject: [PATCH 2489/2758] nvmet-rdma: handle inline data with a nonzero offset commit 48c0162f647bb47e6084ffbc71b8f213f5e2f4f8 upstream. nvmet_rdma_use_inline_sg() maps the host-controlled inline data offset into the per-command inline scatterlist. The bounds check admits any offset with off + len <= inline_data_size, but the mapping still assumes the data begins in the first inline page: sg->offset = off; sg->length = min_t(int, len, PAGE_SIZE - off); When a port is configured with inline_data_size > PAGE_SIZE (settable up to max(SZ_16K, PAGE_SIZE)), an offset in (PAGE_SIZE, inline_data_size] makes "PAGE_SIZE - off" underflow, so sg->length is set to ~4 GiB and the block backend reads far past the first inline page. num_pages(len) also ignores the offset, so an in-bounds offset whose [off, off+len) span crosses a page boundary under-counts the scatterlist. Map the offset properly: split it into a page index and an in-page offset, start the scatterlist at that page, and size the page count from page_off + len. Because the request scatterlist may now start at inline_sg[page_idx] rather than inline_sg[0], generalize the inline-SGL identity test in nvmet_rdma_release_rsp() to a range test; otherwise the persistent inline scatterlist is mistaken for an allocated one and nvmet_req_free_sgls() frees an inline page (and warns in free_large_kmalloc()). Fixes: 0d5ee2b2ab4f ("nvmet-rdma: support max(16KB, PAGE_SIZE) inline data") Cc: stable@vger.kernel.org Suggested-by: Keith Busch Reported-by: Bryam Vargas Signed-off-by: Bryam Vargas Signed-off-by: Keith Busch Signed-off-by: Greg Kroah-Hartman --- drivers/nvme/target/rdma.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/nvme/target/rdma.c b/drivers/nvme/target/rdma.c index ac26f4f774c4d..ea1185b8267ef 100644 --- a/drivers/nvme/target/rdma.c +++ b/drivers/nvme/target/rdma.c @@ -666,7 +666,8 @@ static void nvmet_rdma_release_rsp(struct nvmet_rdma_rsp *rsp) if (rsp->n_rdma) nvmet_rdma_rw_ctx_destroy(rsp); - if (rsp->req.sg != rsp->cmd->inline_sg) + if (rsp->req.sg < rsp->cmd->inline_sg || + rsp->req.sg >= rsp->cmd->inline_sg + queue->dev->inline_page_count) nvmet_req_free_sgls(&rsp->req); if (unlikely(!list_empty_careful(&queue->rsp_wr_wait_list))) @@ -821,24 +822,25 @@ static void nvmet_rdma_write_data_done(struct ib_cq *cq, struct ib_wc *wc) static void nvmet_rdma_use_inline_sg(struct nvmet_rdma_rsp *rsp, u32 len, u64 off) { - int sg_count = num_pages(len); + u64 page_off = off % PAGE_SIZE; + u64 page_idx = off / PAGE_SIZE; + int sg_count = num_pages(page_off + len); struct scatterlist *sg; int i; - sg = rsp->cmd->inline_sg; + sg = &rsp->cmd->inline_sg[page_idx]; for (i = 0; i < sg_count; i++, sg++) { if (i < sg_count - 1) sg_unmark_end(sg); else sg_mark_end(sg); - sg->offset = off; - sg->length = min_t(int, len, PAGE_SIZE - off); + sg->offset = page_off; + sg->length = min_t(u64, len, PAGE_SIZE - page_off); len -= sg->length; - if (!i) - off = 0; + page_off = 0; } - rsp->req.sg = rsp->cmd->inline_sg; + rsp->req.sg = &rsp->cmd->inline_sg[page_idx]; rsp->req.sg_cnt = sg_count; } From fcef60ed5f714a24104eb021d6397a67955ebeff Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Tue, 9 Jun 2026 09:55:05 +0000 Subject: [PATCH 2490/2758] nvmet: fix refcount leak in nvmet_sq_create() commit 34b9a83c50660148bde01cde16451dbe78369749 upstream. In nvmet_sq_create(), a reference on the ctrl is taken via kref_get_unless_zero() before calling nvmet_check_sqid(). If nvmet_check_sqid() fails, the function returns the error directly without releasing the reference, leading to a leak. Fix this by jumping to the "ctrl_put" label, which already performs the necessary nvmet_ctrl_put(ctrl). This ensures the reference is properly released on this error path. Cc: stable@vger.kernel.org Fixes: 1eb380caf527 ("nvmet: Introduce nvmet_sq_create() and nvmet_cq_create()") Signed-off-by: Wentao Liang Signed-off-by: Keith Busch Signed-off-by: Greg Kroah-Hartman --- drivers/nvme/target/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c index 62dd59b9aa4f1..4477c4d6b1eed 100644 --- a/drivers/nvme/target/core.c +++ b/drivers/nvme/target/core.c @@ -944,7 +944,7 @@ u16 nvmet_sq_create(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq, status = nvmet_check_sqid(ctrl, sqid, true); if (status != NVME_SC_SUCCESS) - return status; + goto ctrl_put; ret = nvmet_sq_init(sq, cq); if (ret) { From fd750b694f1f9e1ecb8ca19314e4e21edbb15f42 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Tue, 16 Jun 2026 01:17:36 +0800 Subject: [PATCH 2491/2758] netdev-genl: report NAPI thread PID in the caller's pid namespace commit 1f24c0d01db214c9e661915e9972404c96ca73c0 upstream. netdev_nl_napi_fill_one() reports the NAPI kthread PID in NETDEV_A_NAPI_PID using task_pid_nr(), which returns the PID in the initial pid namespace. NETDEV_CMD_NAPI_GET does not have GENL_ADMIN_PERM and the netdev genl family is netnsok, so a caller in a child pid namespace can issue it. That caller then sees the kthread's global PID, even though the kthread is not visible in its pid namespace, where the value should be 0. Translate the PID through the caller's pid namespace, the same way commit 3799c2570982 ("io_uring/fdinfo: translate SqThread PID through caller's pid_ns") did for the io_uring SQPOLL thread. The doit and dumpit paths both run synchronously in the caller's context, so task_active_pid_ns(current) is the caller's pid namespace. Fixes: db4704f4e4df ("netdev-genl: Add PID for the NAPI thread") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Joe Damato Reviewed-by: Samiullah Khawaja Link: https://patch.msgid.link/20260615171736.1709318-1-maoyixie.tju@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/core/netdev-genl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/core/netdev-genl.c b/net/core/netdev-genl.c index 119eaa6501d5e..ed76544b2d45c 100644 --- a/net/core/netdev-genl.c +++ b/net/core/netdev-genl.c @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -189,7 +190,8 @@ netdev_nl_napi_fill_one(struct sk_buff *rsp, struct napi_struct *napi, goto nla_put_failure; if (napi->thread) { - pid = task_pid_nr(napi->thread); + pid = task_pid_nr_ns(napi->thread, + task_active_pid_ns(current)); if (nla_put_u32(rsp, NETDEV_A_NAPI_PID, pid)) goto nla_put_failure; } From 046380f3e1112525715816a3f25bfb8bfa0a5e92 Mon Sep 17 00:00:00 2001 From: Souvik Banerjee Date: Fri, 1 May 2026 23:27:35 +0000 Subject: [PATCH 2492/2758] ovl: use linked upper dentry in copy-up tmpfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit e348eecd4d8fa8d18a5157ff59f7be1dc59c5928 upstream. ovl_copy_up_tmpfile() stores the disconnected O_TMPFILE dentry as the overlay's upper dentry reference via ovl_inode_update(). vfs_tmpfile() allocated this dentry via d_alloc(parentpath->dentry, &slash_name), so d_name is "/" and d_parent is c->workdir. Local upper filesystems (ext4, btrfs, xfs, ...) immediately rename it to "#" via d_mark_tmpfile() inside their ->tmpfile() op; FUSE and virtiofs do not, so both fields stay that way. Neither identifies the destination directory and filename where ovl_do_link() actually linked the file. When the upper filesystem implements ->d_revalidate() (e.g. FUSE or virtiofs), ovl_revalidate_real() calls it with the dentry's parent inode and a snapshot of d_name. The server tries to look up "/" inside c->workdir, fails, and overlayfs reports -ESTALE. This causes persistent ESTALE errors for any file that was copied up via the tmpfile path, breaking dpkg, apt, and other tools that do rename-over-existing on overlayfs with a FUSE/virtiofs upper. Before commit 6b52243f633e ("ovl: fold copy-up helpers into callers"), the tmpfile copy-up path used a dedicated helper ovl_link_tmpfile() that captured the linked destination dentry returned by ovl_do_link(): err = ovl_do_link(temp, udir, upper); ... if (!err) *newdentry = dget(upper); and published it via ovl_inode_update(d_inode(c->dentry), newdentry). The fold inlined ovl_do_link() into ovl_copy_up_tmpfile() but dropped the dget(upper) capture, and rewrote the publish line as ovl_inode_update(d_inode(c->dentry), dget(temp)) — where temp is the disconnected O_TMPFILE dentry. Fix by keeping a reference to the linked destination dentry after ovl_do_link() succeeds, and publishing that dentry at the existing ovl_inode_update() call site. The non-tmpfile/workdir path continues to publish the renamed temporary dentry. Reproducer: - Mount overlayfs with virtiofs (or a FUSE fs whose server advertises FUSE_TMPFILE) as upper - Run: dpkg -i - Observe: "error installing new file '...': Stale file handle" Fixes: 6b52243f633e ("ovl: fold copy-up helpers into callers") Cc: stable@vger.kernel.org # v4.20+ Signed-off-by: Souvik Banerjee Link: https://patch.msgid.link/20260501232735.2610824-1-souvik@amlalabs.com Reviewed-by: Amir Goldstein Reviewed-by: Miklos Szeredi Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/overlayfs/copy_up.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 13cb60b52bd6e..e963701b4c877 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -853,7 +853,7 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c) { struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb); struct inode *udir = d_inode(c->destdir); - struct dentry *temp, *upper; + struct dentry *temp, *upper, *newdentry = NULL; struct file *tmpfile; int err; @@ -889,6 +889,14 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c) err = PTR_ERR(upper); if (!IS_ERR(upper)) { err = ovl_do_link(ofs, temp, udir, upper); + if (!err) { + /* + * Record the linked dentry -- not the disconnected + * O_TMPFILE dentry -- so that ->d_revalidate() on + * the upper fs sees the real parent/name. + */ + newdentry = dget(upper); + } end_creating(upper); } @@ -903,7 +911,7 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c) if (!c->metacopy) ovl_set_upperdata(d_inode(c->dentry)); - ovl_inode_update(d_inode(c->dentry), dget(temp)); + ovl_inode_update(d_inode(c->dentry), newdentry); out: ovl_end_write(c->dentry); From 5832c55b3c824ba2fe9c36ac3c411baddcce053e Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Thu, 9 Jul 2026 16:41:59 +0000 Subject: [PATCH 2493/2758] can: esd_usb: kill anchored URBs before freeing netdevs commit c43122fef328a70045fe7621c06de6b2b8e19264 upstream. esd_usb_disconnect() frees each CAN netdev with free_candev() inside its per-netdev loop and only calls unlink_all_urbs(dev) afterwards. The per-netdev private data (struct esd_usb_net_priv) is embedded in the net_device allocation returned by alloc_candev(), so once free_candev() has run, dev->nets[i] points to freed memory. unlink_all_urbs() then dereferences the freed dev->nets[i] to kill the per-netdev TX anchor (usb_kill_anchored_urbs(&priv->tx_submitted)), clear active_tx_jobs, and reset priv->tx_contexts[]. Reorder the teardown so the anchored URBs are killed before the netdevs are freed, matching other CAN/USB drivers in the same directory such as ems_usb, usb_8dev and mcba_usb, which unregister, then unlink, then free: unregister the netdevs first (which stops their TX queues), call unlink_all_urbs(dev) once, then free the netdevs. This issue was found by an in-house static analysis tool. Fixes: 96d8e90382dc ("can: Add driver for esd CAN-USB/2 device") Cc: stable@vger.kernel.org Assisted-by: Codex:gpt-5.5 Signed-off-by: Fan Wu Link: https://patch.msgid.link/20260709164159.497640-1-fanwu01@zju.edu.cn Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/usb/esd_usb.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/can/usb/esd_usb.c b/drivers/net/can/usb/esd_usb.c index d257440fa01ff..f41d4a0d140f7 100644 --- a/drivers/net/can/usb/esd_usb.c +++ b/drivers/net/can/usb/esd_usb.c @@ -1390,10 +1390,13 @@ static void esd_usb_disconnect(struct usb_interface *intf) netdev = dev->nets[i]->netdev; netdev_info(netdev, "unregister\n"); unregister_netdev(netdev); - free_candev(netdev); } } unlink_all_urbs(dev); + for (i = 0; i < dev->net_count; i++) { + if (dev->nets[i]) + free_candev(dev->nets[i]->netdev); + } kfree(dev); } } From b8278ff605187ef3fa0f2705e93251cce4c4f8ee Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Sun, 12 Jul 2026 19:59:41 +0200 Subject: [PATCH 2494/2758] can: isotp: use unconditional synchronize_rcu() in isotp_release() commit 9b1a02e0d980ac6b0e36a90378f847062f81d7e4 upstream. isotp_notify() unregisters the (RCU) CAN filters via can_rx_unregister() and clears so->bound without waiting for a grace period. isotp_release() uses so->bound to decide whether it needs to call synchronize_rcu() before cancelling so->rxtimer, so when NETDEV_UNREGISTER runs first it skips that synchronize_rcu() and can cancel the timer while an in-flight isotp_rcv() is still executing and about to re-arm it via isotp_send_fc(), leading to a use-after-free timer callback on the freed socket. sakisho-bot remarked a problem with rtnl_lock held in isotp_notify(), therefore make isotp_release() always call synchronize_rcu() before cancelling the timers, regardless of so->bound. This still closes the original race (isotp_notify() clearing so->bound without waiting for in-flight isotp_rcv() callers before isotp_release() cancels the RX timer) without adding any RCU wait to the netdevice notifier path. Fixes: 14a4696bc311 ("can: isotp: isotp_release(): omit unintended hrtimer restart on socket release") Closes: https://lore.kernel.org/linux-can/20260707085210.6B6C01F000E9@smtp.kernel.org/ Reported-by: Nico Yip Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260712-isotp-fixes-v10-1-793a1b1ce17f@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- net/can/isotp.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/net/can/isotp.c b/net/can/isotp.c index c48b4a818297e..d30937345bcdc 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -1237,11 +1237,18 @@ static int isotp_release(struct socket *sock) SINGLE_MASK(so->txid), isotp_rcv_echo, sk); dev_put(dev); - synchronize_rcu(); } } } + /* Always wait for a grace period before touching the timers below. + * A concurrent NETDEV_UNREGISTER may have already unregistered our + * filters and cleared so->bound in isotp_notify() without waiting + * for in-flight isotp_rcv() callers to finish, so this call must not + * be skipped just because so->bound is already 0 here. + */ + synchronize_rcu(); + hrtimer_cancel(&so->txfrtimer); hrtimer_cancel(&so->txtimer); hrtimer_cancel(&so->rxtimer); From e442b62ba5a7756c17e05a77b32cdd085a2b6138 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Sun, 12 Jul 2026 19:59:42 +0200 Subject: [PATCH 2495/2758] can: isotp: fix use-after-free race with concurrent NETDEV_UNREGISTER commit 20bab8b88baac140ca3701116e1d486c7f51e311 upstream. isotp_release() looked up the bound network device via dev_get_by_index() using the stored ifindex. During device unregistration the device is unlisted from the ifindex hash before the NETDEV_UNREGISTER notifier chain runs, so a concurrent isotp_release() could find no device, skip can_rx_unregister() entirely, and still proceed to free the socket. Since isotp_release() had already removed itself from the isotp notifier list at that point, isotp_notify() would never get a chance to clean up either, leaving a stale CAN filter that keeps pointing at the freed socket. Fix this the same way raw.c already does: hold a tracked reference to the bound net_device in the socket (so->dev/so->dev_tracker) from bind() onward instead of re-resolving it from the ifindex, and serialize bind()/release() with rtnl_lock() so that so->dev is always consistent with what the NETDEV_UNREGISTER notifier sees. so->dev stays valid regardless of ifindex-hash unlisting, and is only ever cleared by whichever of isotp_release()/isotp_notify() gets there first, so the filter is always removed exactly once. isotp_bind() now rejects a (re)bind with -EAGAIN while so->[tx|rx].state isn't ISOTP_IDLE yet, so a timer left running by a prior NETDEV_UNREGISTER can't act on a newly bound so->ifindex. Both checks share the same lock_sock() section, so there is no window in which a concurrent isotp_notify() clearing so->bound could be missed. Fixes: e057dd3fc20f ("can: add ISO 15765-2:2016 transport protocol") Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/linux-can/20260707101420.47F261F000E9@smtp.kernel.org/ Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260712-isotp-fixes-v10-2-793a1b1ce17f@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- net/can/isotp.c | 87 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 59 insertions(+), 28 deletions(-) diff --git a/net/can/isotp.c b/net/can/isotp.c index d30937345bcdc..44c044eb83e12 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -152,6 +152,8 @@ struct isotp_sock { struct sock sk; int bound; int ifindex; + struct net_device *dev; + netdevice_tracker dev_tracker; canid_t txid; canid_t rxid; ktime_t tx_gap; @@ -978,6 +980,14 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) goto err_event_drop; } + /* so->bound is only checked once above - a wakeup may have + * unbound/rebound the socket meanwhile, so re-validate it + */ + if (!so->bound) { + err = -EADDRNOTAVAIL; + goto err_out_drop; + } + /* PDU size > default => try max_pdu_size */ if (size > so->tx.buflen && so->tx.buflen < max_pdu_size) { u8 *newbuf = kmalloc(max_pdu_size, GFP_KERNEL); @@ -1219,28 +1229,30 @@ static int isotp_release(struct socket *sock) list_del(&so->notifier); spin_unlock(&isotp_notifier_lock); + rtnl_lock(); lock_sock(sk); - /* remove current filters & unregister */ - if (so->bound) { - if (so->ifindex) { - struct net_device *dev; - - dev = dev_get_by_index(net, so->ifindex); - if (dev) { - if (isotp_register_rxid(so)) - can_rx_unregister(net, dev, so->rxid, - SINGLE_MASK(so->rxid), - isotp_rcv, sk); - - can_rx_unregister(net, dev, so->txid, - SINGLE_MASK(so->txid), - isotp_rcv_echo, sk); - dev_put(dev); - } - } + /* remove current filters & unregister + * tracked reference so->dev is taken at bind() time with rtnl_lock + */ + if (so->bound && so->dev) { + if (isotp_register_rxid(so)) + can_rx_unregister(net, so->dev, so->rxid, + SINGLE_MASK(so->rxid), + isotp_rcv, sk); + + can_rx_unregister(net, so->dev, so->txid, + SINGLE_MASK(so->txid), + isotp_rcv_echo, sk); + netdev_put(so->dev, &so->dev_tracker); } + so->ifindex = 0; + so->bound = 0; + so->dev = NULL; + + rtnl_unlock(); + /* Always wait for a grace period before touching the timers below. * A concurrent NETDEV_UNREGISTER may have already unregistered our * filters and cleared so->bound in isotp_notify() without waiting @@ -1253,9 +1265,6 @@ static int isotp_release(struct socket *sock) hrtimer_cancel(&so->txtimer); hrtimer_cancel(&so->rxtimer); - so->ifindex = 0; - so->bound = 0; - sock_orphan(sk); sock->sk = NULL; @@ -1310,6 +1319,7 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l if (!addr->can_ifindex) return -ENODEV; + rtnl_lock(); lock_sock(sk); if (so->bound) { @@ -1317,6 +1327,17 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l goto out; } + /* A transmission or reception that outlived a previous binding + * (unbound by NETDEV_UNREGISTER) may still be draining; the FC/echo + * and RX watchdog timers bound how long this takes. Checked together + * with so->bound in the same lock_sock() section above, so there is + * no window in which a concurrent isotp_notify() could be missed. + */ + if (so->tx.state != ISOTP_IDLE || so->rx.state != ISOTP_IDLE) { + err = -EAGAIN; + goto out; + } + /* ensure different CAN IDs when the rx_id is to be registered */ if (isotp_register_rxid(so) && rx_id == tx_id) { err = -EADDRNOTAVAIL; @@ -1329,14 +1350,12 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l goto out; } if (dev->type != ARPHRD_CAN) { - dev_put(dev); err = -ENODEV; - goto out; + goto out_put_dev; } if (READ_ONCE(dev->mtu) < so->ll.mtu) { - dev_put(dev); err = -EINVAL; - goto out; + goto out_put_dev; } if (!(dev->flags & IFF_UP)) notify_enetdown = 1; @@ -1354,16 +1373,25 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l can_rx_register(net, dev, tx_id, SINGLE_MASK(tx_id), isotp_rcv_echo, sk, "isotpe", sk); - dev_put(dev); - /* switch to new settings */ so->ifindex = ifindex; so->rxid = rx_id; so->txid = tx_id; so->bound = 1; + /* bind() ok -> hold a reference for so->dev so that isotp_release() + * can safely reach the device later, even if a concurrent + * NETDEV_UNREGISTER has already unlisted it by ifindex. + */ + so->dev = dev; + netdev_hold(so->dev, &so->dev_tracker, GFP_KERNEL); + +out_put_dev: + /* remove potential reference from dev_get_by_index() */ + dev_put(dev); out: release_sock(sk); + rtnl_unlock(); if (notify_enetdown) { sk->sk_err = ENETDOWN; @@ -1566,7 +1594,7 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg, if (!net_eq(dev_net(dev), sock_net(sk))) return; - if (so->ifindex != dev->ifindex) + if (so->dev != dev) return; switch (msg) { @@ -1582,10 +1610,12 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg, can_rx_unregister(dev_net(dev), dev, so->txid, SINGLE_MASK(so->txid), isotp_rcv_echo, sk); + netdev_put(so->dev, &so->dev_tracker); } so->ifindex = 0; so->bound = 0; + so->dev = NULL; release_sock(sk); sk->sk_err = ENODEV; @@ -1645,6 +1675,7 @@ static int isotp_init(struct sock *sk) so->ifindex = 0; so->bound = 0; + so->dev = NULL; so->opt.flags = CAN_ISOTP_DEFAULT_FLAGS; so->opt.ext_address = CAN_ISOTP_DEFAULT_EXT_ADDRESS; From 4f1fdf1a1c317bcac0c6b6c8e12642c9983de1ca Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Sun, 12 Jul 2026 19:59:43 +0200 Subject: [PATCH 2496/2758] can: isotp: serialize TX state transitions under so->rx_lock commit cf070fe33bfbd1a4c21236078fadb35dd223a157 upstream. The TX state machine (so->tx.state) is driven from three contexts: sendmsg() claiming and progressing a transfer, the RX path consuming Flow Control/echo frames, and two hrtimers timing out a stalled transfer. Mixing a lock-free cmpxchg() claim in sendmsg() with hrtimer_cancel() calls made under so->rx_lock elsewhere left windows where a frame or timer callback could act on a state that had already moved on, corrupting an unrelated transfer. so->rx_lock now covers the full lifecycle of a TX claim: sendmsg() takes it to check so->tx.state is ISOTP_IDLE, switch it to ISOTP_SENDING, bump so->tx_gen and drain the previous transfer's timers - all as one critical section. isotp_rcv_fc()/isotp_rcv_cf() already run under this lock via isotp_rcv(), and isotp_rcv_echo() now takes it itself, so none of them can ever observe a transfer mid-claim. This also means a transfer can no longer be handed to sendmsg()'s cleanup paths (signal or send error) while another thread is concurrently claiming or finishing it, so those paths can cancel timers and reset the state unconditionally. isotp_release() claims the socket the same way, so a racing sendmsg() sees a consistent ISOTP_SHUTDOWN and skips arming its timer or sending. Only the hrtimer callbacks stay outside so->rx_lock, since they run under so->rx_lock's cancellation elsewhere and taking it themselves would deadlock. so->tx_gen lets them recognize whether the transfer they timed out is still the one currently active, so they don't report an error against a transfer that has since completed or been superseded. Fixes: e057dd3fc20f ("can: add ISO 15765-2:2016 transport protocol") Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/linux-can/20260710142146.BDAE61F000E9@smtp.kernel.org/ Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260712-isotp-fixes-v10-3-793a1b1ce17f@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- net/can/isotp.c | 202 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 160 insertions(+), 42 deletions(-) diff --git a/net/can/isotp.c b/net/can/isotp.c index 44c044eb83e12..54becaf6898f1 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -158,7 +158,7 @@ struct isotp_sock { canid_t rxid; ktime_t tx_gap; ktime_t lastrxcf_tstamp; - struct hrtimer rxtimer, txtimer, txfrtimer; + struct hrtimer rxtimer, txtimer, txfrtimer, echotimer; struct can_isotp_options opt; struct can_isotp_fc_options rxfc, txfc; struct can_isotp_ll_options ll; @@ -166,6 +166,7 @@ struct isotp_sock { u32 force_tx_stmin; u32 force_rx_stmin; u32 cfecho; /* consecutive frame echo tag */ + u32 tx_gen; /* generation, bumped per new tx transfer */ struct tpcon rx, tx; struct list_head notifier; wait_queue_head_t wait; @@ -378,6 +379,15 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae) hrtimer_cancel(&so->txtimer); + /* isotp_tx_timeout() may have given up on this job while + * hrtimer_cancel() above waited for it to finish; so->rx_lock + * (held by our caller isotp_rcv()) rules out a concurrent claim, + * so a plain recheck is enough here. + */ + if (so->tx.state != ISOTP_WAIT_FC && + so->tx.state != ISOTP_WAIT_FIRST_FC) + return 1; + if ((cf->len < ae + FC_CONTENT_SZ) || ((so->opt.flags & ISOTP_CHECK_PADDING) && check_pad(so, cf, ae + FC_CONTENT_SZ, so->opt.rxpad_content))) { @@ -424,7 +434,7 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae) so->tx.bs = 0; so->tx.state = ISOTP_SENDING; /* send CF frame and enable echo timeout handling */ - hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0), + hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0), HRTIMER_MODE_REL_SOFT); isotp_send_cframe(so); break; @@ -577,6 +587,14 @@ static int isotp_rcv_cf(struct sock *sk, struct canfd_frame *cf, int ae, hrtimer_cancel(&so->rxtimer); + /* isotp_rx_timer_handler() may have raced us for so->rx.state + * while hrtimer_cancel() above waited for it to finish, already + * reporting ETIMEDOUT and resetting the reception; don't process + * this CF into a reassembly that has already been given up on. + */ + if (so->rx.state != ISOTP_WAIT_DATA) + return 1; + /* CFs are never longer than the FF */ if (cf->len > so->rx.ll_dl) return 1; @@ -872,20 +890,36 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data) struct canfd_frame *cf = (struct canfd_frame *)skb->data; /* only handle my own local echo CF/SF skb's (no FF!) */ - if (skb->sk != sk || so->cfecho != *(u32 *)cf->data) + if (skb->sk != sk) return; + /* unlike isotp_rcv_fc()/isotp_rcv_cf(), not already under so->rx_lock + * (no isotp_rcv() caller here), so take it ourselves + */ + spin_lock(&so->rx_lock); + + /* so->cfecho may since belong to a new transfer; recheck under lock */ + if (so->cfecho != *(u32 *)cf->data) + goto out_unlock; + /* cancel local echo timeout */ - hrtimer_cancel(&so->txtimer); + hrtimer_cancel(&so->echotimer); /* local echo skb with consecutive frame has been consumed */ so->cfecho = 0; + /* claiming a transfer also takes so->rx_lock, so a plain recheck + * is enough: so->tx.state can't have flipped to ISOTP_SENDING for + * a new claim while we're still in here + */ + if (so->tx.state != ISOTP_SENDING) + goto out_unlock; + if (so->tx.idx >= so->tx.len) { /* we are done */ so->tx.state = ISOTP_IDLE; wake_up_interruptible(&so->wait); - return; + goto out_unlock; } if (so->txfc.bs && so->tx.bs >= so->txfc.bs) { @@ -893,53 +927,83 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data) so->tx.state = ISOTP_WAIT_FC; hrtimer_start(&so->txtimer, ktime_set(ISOTP_FC_TIMEOUT, 0), HRTIMER_MODE_REL_SOFT); - return; + goto out_unlock; } /* no gap between data frames needed => use burst mode */ if (!so->tx_gap) { /* enable echo timeout handling */ - hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0), + hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0), HRTIMER_MODE_REL_SOFT); isotp_send_cframe(so); - return; + goto out_unlock; } /* start timer to send next consecutive frame with correct delay */ hrtimer_start(&so->txfrtimer, so->tx_gap, HRTIMER_MODE_REL_SOFT); + +out_unlock: + spin_unlock(&so->rx_lock); } -static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer) +/* shared by so->txtimer's and so->echotimer's callbacks. Both timers get + * cancelled under so->rx_lock elsewhere, so this must stay lock-free to + * avoid deadlocking with that; uses so->tx_gen instead to avoid tainting + * a new transfer with an error from the one that just timed out. + */ +static enum hrtimer_restart isotp_tx_timeout(struct isotp_sock *so) { - struct isotp_sock *so = container_of(hrtimer, struct isotp_sock, - txtimer); struct sock *sk = &so->sk; + u32 gen = READ_ONCE(so->tx_gen); + u32 old_state = READ_ONCE(so->tx.state); /* don't handle timeouts in IDLE or SHUTDOWN state */ - if (so->tx.state == ISOTP_IDLE || so->tx.state == ISOTP_SHUTDOWN) + if (old_state == ISOTP_IDLE || old_state == ISOTP_SHUTDOWN) + return HRTIMER_NORESTART; + + /* only claim the timeout if the state is still unchanged */ + if (cmpxchg(&so->tx.state, old_state, ISOTP_IDLE) != old_state) return HRTIMER_NORESTART; /* we did not get any flow control or echo frame in time */ - /* report 'communication error on send' */ - sk->sk_err = ECOMM; - if (!sock_flag(sk, SOCK_DEAD)) - sk_error_report(sk); + if (READ_ONCE(so->tx_gen) == gen) { + /* report 'communication error on send' */ + sk->sk_err = ECOMM; + if (!sock_flag(sk, SOCK_DEAD)) + sk_error_report(sk); + } - /* reset tx state */ - so->tx.state = ISOTP_IDLE; wake_up_interruptible(&so->wait); return HRTIMER_NORESTART; } +/* so->txtimer: fires when a Flow Control frame does not arrive in time */ +static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer) +{ + struct isotp_sock *so = container_of(hrtimer, struct isotp_sock, + txtimer); + + return isotp_tx_timeout(so); +} + +/* so->echotimer: fires when a sent CF/SF's local echo does not arrive */ +static enum hrtimer_restart isotp_echo_timer_handler(struct hrtimer *hrtimer) +{ + struct isotp_sock *so = container_of(hrtimer, struct isotp_sock, + echotimer); + + return isotp_tx_timeout(so); +} + static enum hrtimer_restart isotp_txfr_timer_handler(struct hrtimer *hrtimer) { struct isotp_sock *so = container_of(hrtimer, struct isotp_sock, txfrtimer); /* start echo timeout handling and cover below protocol error */ - hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0), + hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0), HRTIMER_MODE_REL_SOFT); /* cfecho should be consumed by isotp_rcv_echo() here */ @@ -960,13 +1024,24 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) int ae = (so->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0; int wait_tx_done = (so->opt.flags & CAN_ISOTP_WAIT_TX_DONE) ? 1 : 0; s64 hrtimer_sec = ISOTP_ECHO_TIMEOUT; + struct hrtimer *tx_hrt = &so->echotimer; + u32 new_state = ISOTP_SENDING; int off; int err; if (!so->bound || so->tx.state == ISOTP_SHUTDOWN) return -EADDRNOTAVAIL; - while (cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SENDING) != ISOTP_IDLE) { + /* claim the socket under so->rx_lock: this serializes the claim + * with the RX path and with sendmsg()'s own error paths below, so + * none of them can ever see a transfer mid-claim + */ + for (;;) { + spin_lock_bh(&so->rx_lock); + if (READ_ONCE(so->tx.state) == ISOTP_IDLE) + break; + spin_unlock_bh(&so->rx_lock); + /* we do not support multiple buffers - for now */ if (msg->msg_flags & MSG_DONTWAIT) return -EAGAIN; @@ -975,11 +1050,23 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) return -EADDRNOTAVAIL; /* wait for complete transmission of current pdu */ - err = wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE); + err = wait_event_interruptible(so->wait, + so->tx.state == ISOTP_IDLE); if (err) - goto err_event_drop; + return err; } + /* new transfer: bump so->tx_gen and drain the old one's timers, + * still under the so->rx_lock we just claimed the socket with + */ + WRITE_ONCE(so->tx.state, ISOTP_SENDING); + WRITE_ONCE(so->tx_gen, READ_ONCE(so->tx_gen) + 1); + hrtimer_cancel(&so->txtimer); + hrtimer_cancel(&so->echotimer); + hrtimer_cancel(&so->txfrtimer); + so->cfecho = 0; + spin_unlock_bh(&so->rx_lock); + /* so->bound is only checked once above - a wakeup may have * unbound/rebound the socket meanwhile, so re-validate it */ @@ -1096,18 +1183,33 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) so->cfecho = *(u32 *)cf->data; } else { /* standard flow control check */ - so->tx.state = ISOTP_WAIT_FIRST_FC; + new_state = ISOTP_WAIT_FIRST_FC; /* start timeout for FC */ hrtimer_sec = ISOTP_FC_TIMEOUT; + tx_hrt = &so->txtimer; /* no CF echo tag for isotp_rcv_echo() (FF-mode) */ so->cfecho = 0; } } - hrtimer_start(&so->txtimer, ktime_set(hrtimer_sec, 0), + spin_lock_bh(&so->rx_lock); + if (so->tx.state == ISOTP_SHUTDOWN) { + /* isotp_release() has since taken over and already drained + * our timers - don't send into a socket that's going away + */ + spin_unlock_bh(&so->rx_lock); + kfree_skb(skb); + dev_put(dev); + wake_up_interruptible(&so->wait); + return -EADDRNOTAVAIL; + } + /* WAIT_FIRST_FC for standard FF, else stays ISOTP_SENDING */ + so->tx.state = new_state; + hrtimer_start(tx_hrt, ktime_set(hrtimer_sec, 0), HRTIMER_MODE_REL_SOFT); + spin_unlock_bh(&so->rx_lock); /* send the first or only CAN frame */ cf->flags = so->ll.tx_flags; @@ -1120,13 +1222,10 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) pr_notice_once("can-isotp: %s: can_send_ret %pe\n", __func__, ERR_PTR(err)); + spin_lock_bh(&so->rx_lock); /* no transmission -> no timeout monitoring */ - hrtimer_cancel(&so->txtimer); - - /* reset consecutive frame echo tag */ - so->cfecho = 0; - - goto err_out_drop; + hrtimer_cancel(tx_hrt); + goto err_out_drop_locked; } if (wait_tx_done) { @@ -1142,14 +1241,21 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) return size; +err_out_drop: + /* claimed but nothing sent yet - no timer to cancel */ + spin_lock_bh(&so->rx_lock); + goto err_out_drop_locked; err_event_drop: - /* got signal: force tx state machine to be idle */ - so->tx.state = ISOTP_IDLE; + /* interrupted waiting on our own transfer - drain its timers */ + spin_lock_bh(&so->rx_lock); hrtimer_cancel(&so->txfrtimer); hrtimer_cancel(&so->txtimer); -err_out_drop: - /* drop this PDU and unlock a potential wait queue */ + hrtimer_cancel(&so->echotimer); +err_out_drop_locked: + /* release the claim; so->rx_lock still held from above */ + so->cfecho = 0; so->tx.state = ISOTP_IDLE; + spin_unlock_bh(&so->rx_lock); wake_up_interruptible(&so->wait); return err; @@ -1211,13 +1317,20 @@ static int isotp_release(struct socket *sock) so = isotp_sk(sk); net = sock_net(sk); - /* wait for complete transmission of current pdu */ - while (wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE) == 0 && - cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SHUTDOWN) != ISOTP_IDLE) + /* best-effort: wait for a running pdu to finish, but don't block on + * it forever - give up after the first signal + */ + while (so->tx.state != ISOTP_IDLE && + wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE) == 0) ; - /* force state machines to be idle also when a signal occurred */ + /* claim the socket under so->rx_lock like sendmsg() does, so its + * claim can't race the forced ISOTP_SHUTDOWN below; force it + * unconditionally, even when a signal cut the wait above short + */ + spin_lock_bh(&so->rx_lock); so->tx.state = ISOTP_SHUTDOWN; + spin_unlock_bh(&so->rx_lock); so->rx.state = ISOTP_IDLE; spin_lock(&isotp_notifier_lock); @@ -1263,6 +1376,7 @@ static int isotp_release(struct socket *sock) hrtimer_cancel(&so->txfrtimer); hrtimer_cancel(&so->txtimer); + hrtimer_cancel(&so->echotimer); hrtimer_cancel(&so->rxtimer); sock_orphan(sk); @@ -1702,10 +1816,14 @@ static int isotp_init(struct sock *sk) so->rx.buflen = ARRAY_SIZE(so->rx.sbuf); so->tx.buflen = ARRAY_SIZE(so->tx.sbuf); - hrtimer_setup(&so->rxtimer, isotp_rx_timer_handler, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); - hrtimer_setup(&so->txtimer, isotp_tx_timer_handler, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); - hrtimer_setup(&so->txfrtimer, isotp_txfr_timer_handler, CLOCK_MONOTONIC, - HRTIMER_MODE_REL_SOFT); + hrtimer_setup(&so->rxtimer, isotp_rx_timer_handler, + CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); + hrtimer_setup(&so->txtimer, isotp_tx_timer_handler, + CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); + hrtimer_setup(&so->echotimer, isotp_echo_timer_handler, + CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); + hrtimer_setup(&so->txfrtimer, isotp_txfr_timer_handler, + CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); init_waitqueue_head(&so->wait); spin_lock_init(&so->rx_lock); From ce2d4b121fb7545e1ed588e860c8e5fd5ad45224 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 14 Jul 2026 18:55:23 +0200 Subject: [PATCH 2497/2758] can: bcm: defer rx_op deallocation to workqueue to fix thrtimer UAF commit 68973f9db76144825e4f35dfdc80fb8279eb2d57 upstream. Commit f1b4e32aca08 ("can: bcm: use call_rcu() instead of costly synchronize_rcu()") replaced synchronize_rcu() in bcm_delete_rx_op() with call_rcu() and introduced the RX_NO_AUTOTIMER flag. However, this flag check was omitted for thrtimer in the packet rx fast-path. During BCM RX operation teardown, a concurrent RCU reader (bcm_rx_handler) can race and re-arm thrtimer via bcm_rx_update_and_send() after call_rcu() has been scheduled. Once the RCU grace period elapses, bcm_op is freed. The subsequently firing thrtimer then dereferences the deallocated op, causing a UAF. Adding flag checks to the rx fast-path (bcm_rx_update_and_send) does not fully close the TOCTOU race and introduces latency for every CAN frame. Conversely, calling hrtimer_cancel() directly inside the RCU callback (softirq context) is fatal as hrtimer_cancel() can sleep, triggering a "scheduling while atomic" panic. Resolve this by deferring the timer cancellation and memory free to a dedicated unbound workqueue (bcm_wq). The RCU callback now queues a work item to bcm_wq, which safely cancels both timers and deallocates memory in sleepable process context. A dedicated workqueue is used to prevent system-wide WQ saturation and is cleanly flushed/destroyed on module unload to avoid rmmod page faults. Since the deferred work can now outlive the calling context by an unbounded amount, also take a reference on op->sk when it is assigned and drop it only once the deferred work has cancelled both timers, so a socket can no longer be freed out from under a still-armed timer whose callback (bcm_send_to_user()) dereferences op->sk. Fixes: f1b4e32aca08 ("can: bcm: use call_rcu() instead of costly synchronize_rcu()") Tested-by: Feng Xue Tested-by: Oliver Hartkopp Signed-off-by: Lee Jones Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-1-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- net/can/bcm.c | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index a4bef2c48a559..bdf53241bd7b3 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -92,6 +93,8 @@ MODULE_ALIAS("can-proto-2"); #define BCM_MIN_NAMELEN CAN_REQUIRED_SIZE(struct sockaddr_can, can_ifindex) +static struct workqueue_struct *bcm_wq; + /* * easy access to the first 64 bit of can(fd)_frame payload. cp->data is * 64 bit aligned so the offset has to be multiples of 8 which is ensured @@ -105,6 +108,7 @@ static inline u64 get_u64(const struct canfd_frame *cp, int offset) struct bcm_op { struct list_head list; struct rcu_head rcu; + struct work_struct work; int ifindex; canid_t can_id; u32 flags; @@ -793,9 +797,12 @@ static struct bcm_op *bcm_find_op(struct list_head *ops, return NULL; } -static void bcm_free_op_rcu(struct rcu_head *rcu_head) +static void bcm_free_op_work(struct work_struct *work) { - struct bcm_op *op = container_of(rcu_head, struct bcm_op, rcu); + struct bcm_op *op = container_of(work, struct bcm_op, work); + + hrtimer_cancel(&op->timer); + hrtimer_cancel(&op->thrtimer); if ((op->frames) && (op->frames != &op->sframe)) kfree(op->frames); @@ -803,9 +810,23 @@ static void bcm_free_op_rcu(struct rcu_head *rcu_head) if ((op->last_frames) && (op->last_frames != &op->last_sframe)) kfree(op->last_frames); + /* the last possible access to op->timer/op->thrtimer has now + * happened above via hrtimer_cancel() - op->sk is no longer + * needed by any pending timer callback, so drop our reference + */ + sock_put(op->sk); + kfree(op); } +static void bcm_free_op_rcu(struct rcu_head *rcu_head) +{ + struct bcm_op *op = container_of(rcu_head, struct bcm_op, rcu); + + INIT_WORK(&op->work, bcm_free_op_work); + queue_work(bcm_wq, &op->work); +} + static void bcm_remove_op(struct bcm_op *op) { hrtimer_cancel(&op->timer); @@ -1060,6 +1081,7 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* bcm_can_tx / bcm_tx_timeout_handler needs this */ op->sk = sk; + sock_hold(sk); op->ifindex = ifindex; /* initialize uninitialized (kzalloc) structure */ @@ -1221,6 +1243,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* bcm_can_tx / bcm_tx_timeout_handler needs this */ op->sk = sk; + sock_hold(sk); op->ifindex = ifindex; /* ifindex for timeout events w/o previous frame reception */ @@ -1839,11 +1862,15 @@ static int __init bcm_module_init(void) { int err; + bcm_wq = alloc_workqueue("can-bcm-wq", WQ_UNBOUND, 0); + if (!bcm_wq) + return -ENOMEM; + pr_info("can: broadcast manager protocol\n"); err = register_pernet_subsys(&canbcm_pernet_ops); if (err) - return err; + goto register_pernet_failed; err = register_netdevice_notifier(&canbcm_notifier); if (err) @@ -1861,6 +1888,8 @@ static int __init bcm_module_init(void) unregister_netdevice_notifier(&canbcm_notifier); register_notifier_failed: unregister_pernet_subsys(&canbcm_pernet_ops); +register_pernet_failed: + destroy_workqueue(bcm_wq); return err; } @@ -1869,6 +1898,8 @@ static void __exit bcm_module_exit(void) can_proto_unregister(&bcm_can_proto); unregister_netdevice_notifier(&canbcm_notifier); unregister_pernet_subsys(&canbcm_pernet_ops); + rcu_barrier(); + destroy_workqueue(bcm_wq); } module_init(bcm_module_init); From b9c6ac6fb4e01b34575816066e5d3890a57b3c86 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:24 +0200 Subject: [PATCH 2498/2758] can: bcm: fix lockless bound/ifindex race and silent RX_SETUP failure commit d9b091d9d22fee81ec53fb55d2032951993ceadb upstream. bcm_sendmsg() reads bo->ifindex and checks bo->bound before taking lock_sock(), while bcm_notify(), bcm_connect() and bcm_release() all mutate both fields under that same lock. Because the lockless reads and the locked writes are unordered with respect to each other, a racing bcm_notify() (device unregister) or bcm_connect() (concurrent bind on another thread sharing the socket) can make bcm_sendmsg() observe an inconsistent combination, e.g. a stale bound=1 together with the now-cleared ifindex=0, silently turning a socket bound to a specific CAN interface into one that also matches "any" interface. Keep the lockless bo->bound check purely as a fast-path reject, and move the ifindex read (and a bo->bound re-check) into the locked section, where every writer already serializes. This removes the possibility of observing the two fields torn against each other, rather than trying to fix it with more READ_ONCE()/WRITE_ONCE() pairs on two independently updated fields. Annotate the now-purely-lockless bo->bound accesses consistently across all its write sites. Also fix bcm_rx_setup() silently returning success when the target device disappears concurrently instead of reporting -ENODEV, so a broken RX op is no longer left registered as if it had succeeded. Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol") Reported-by: Ginger Closes: https://lore.kernel.org/linux-can/CAGp+u1aBK8QVjsvAxM2Ldzep4rEbsP9x_pV3At4g=h1kVEtyhA@mail.gmail.com/ Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-2-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- net/can/bcm.c | 65 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 14 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index bdf53241bd7b3..b612135b017d1 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -1323,6 +1323,11 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, op->rx_reg_dev = dev; dev_put(dev); + } else { + /* the requested device is gone - do not + * silently succeed without registering + */ + err = -ENODEV; } } else @@ -1396,12 +1401,13 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) { struct sock *sk = sock->sk; struct bcm_sock *bo = bcm_sk(sk); - int ifindex = bo->ifindex; /* default ifindex for this bcm_op */ + int ifindex; struct bcm_msg_head msg_head; int cfsiz; int ret; /* read bytes or error codes as return value */ - if (!bo->bound) + /* Lockless fast-path check for bound socket */ + if (!READ_ONCE(bo->bound)) return -ENOTCONN; /* check for valid message length from userspace */ @@ -1417,17 +1423,38 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) if ((size - MHSIZ) % cfsiz) return -EINVAL; + lock_sock(sk); + + /* Re-validate under the socket lock: a concurrent bcm_notify() + * may have unbound this socket (device removal) after the + * lockless fast-path check above. bo->ifindex is only ever + * mutated under lock_sock(), so reading it here - instead of + * before taking the lock - guarantees it can't be observed + * torn against bo->bound. + */ + if (!bo->bound) { + ret = -ENOTCONN; + goto out_release; + } + + /* default ifindex for this bcm_op */ + ifindex = bo->ifindex; + /* check for alternative ifindex for this bcm_op */ if (!ifindex && msg->msg_name) { /* no bound device as default => check msg_name */ DECLARE_SOCKADDR(struct sockaddr_can *, addr, msg->msg_name); - if (msg->msg_namelen < BCM_MIN_NAMELEN) - return -EINVAL; + if (msg->msg_namelen < BCM_MIN_NAMELEN) { + ret = -EINVAL; + goto out_release; + } - if (addr->can_family != AF_CAN) - return -EINVAL; + if (addr->can_family != AF_CAN) { + ret = -EINVAL; + goto out_release; + } /* ifindex from sendto() */ ifindex = addr->can_ifindex; @@ -1436,20 +1463,21 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) struct net_device *dev; dev = dev_get_by_index(sock_net(sk), ifindex); - if (!dev) - return -ENODEV; + if (!dev) { + ret = -ENODEV; + goto out_release; + } if (dev->type != ARPHRD_CAN) { dev_put(dev); - return -ENODEV; + ret = -ENODEV; + goto out_release; } dev_put(dev); } } - lock_sock(sk); - switch (msg_head.opcode) { case TX_SETUP: @@ -1499,6 +1527,7 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) break; } +out_release: release_sock(sk); return ret; @@ -1535,7 +1564,12 @@ static void bcm_notify(struct bcm_sock *bo, unsigned long msg, bo->bcm_proc_read = NULL; } #endif - bo->bound = 0; + /* Paired with the lockless fast-path check in + * bcm_sendmsg(); bo->ifindex itself is only ever + * accessed under lock_sock() so it needs no + * annotation. + */ + WRITE_ONCE(bo->bound, 0); bo->ifindex = 0; notify_enodev = 1; } @@ -1676,7 +1710,7 @@ static int bcm_release(struct socket *sock) /* remove device reference */ if (bo->bound) { - bo->bound = 0; + WRITE_ONCE(bo->bound, 0); bo->ifindex = 0; } @@ -1746,7 +1780,10 @@ static int bcm_connect(struct socket *sock, struct sockaddr_unsized *uaddr, int } #endif /* CONFIG_PROC_FS */ - bo->bound = 1; + /* bo->ifindex above is fully assigned before this point; pairs + * with the lockless fast-path check in bcm_sendmsg() + */ + WRITE_ONCE(bo->bound, 1); fail: release_sock(sk); From fc9f5ee1b073bd233d9c604e338af4ebb42cbc33 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:25 +0200 Subject: [PATCH 2499/2758] can: bcm: add locking when updating filter and timer values commit 749179c2e25b95d22499ed29096b3e02d6dfd2b4 upstream. KCSAN detected a simultaneous access to timer values that can be overwritten in bcm_rx_setup() when updating timer and filter content while bcm_rx_handler(), bcm_rx_timeout_handler() or bcm_rx_thr_handler() run concurrently on incoming CAN traffic. Protect the timer (ival1/ival2/kt_ival1/kt_ival2/kt_lastmsg) and filter (nframes/flags/frames/last_frames) updates in bcm_rx_setup() with a new per-op bcm_rx_update_lock, taken with the matching scope in the RX handlers. memcpy_from_msg() is staged into a temporary buffer before the lock is taken, since it can sleep and must not run under a spinlock. hrtimer_cancel() is always called without bcm_rx_update_lock held, since bcm_rx_timeout_handler()/bcm_rx_thr_handler() take the same lock and a running callback would otherwise deadlock against the canceller. Also close a related race: bcm_rx_setup() cleared the RTR flag in the stored reply frame's can_id as a separate, unprotected step after the frame content was already installed, so a concurrent bcm_rx_handler() could transmit a stale reply with CAN_RTR_FLAG still set. Fold that normalization into the initial frame preparation instead (on the staged buffer for updates, directly on op->frames pre-registration for new ops), so the installed frame is always atomically self-consistent. bcm_rx_handler()'s RX_RTR_FRAME check now takes a lock-protected snapshot of op->flags before deciding whether to call bcm_can_tx(), but does not hold the lock across that call. Also take a lock-protected snapshot of the currframe in bcm_can_tx() to avoid partly overwrites by content updates in bcm_tx_setup(). Finally check if a TX_RESET_MULTI_IDX/SETTIMER might have reset op->currframe between the two locked sections in bcm_can_tx(). Omit calling hrtimer_forward() with zero interval in bcm_rx_thr_handler(). kt_ival2 may have been concurrently cleared by bcm_rx_setup() before it cancels this timer, so check kt_ival2 inside the bcm_rx_update_lock. Fixes: c2aba69d0c36 ("can: bcm: add locking for bcm_op runtime updates") Reported-by: syzbot+75e5e4ae00c3b4bb544e@syzkaller.appspotmail.com Closes: https://lore.kernel.org/linux-can/6975d5cf.a00a0220.33ccc7.0022.GAE@google.com/ Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-3-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- net/can/bcm.c | 176 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 133 insertions(+), 43 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index b612135b017d1..1e5f8d65d351b 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -129,6 +129,7 @@ struct bcm_op { struct sock *sk; struct net_device *rx_reg_dev; spinlock_t bcm_tx_lock; /* protect currframe/count in runtime updates */ + spinlock_t bcm_rx_update_lock; /* protect filter/timer data updates */ }; struct bcm_sock { @@ -293,22 +294,28 @@ static int bcm_proc_show(struct seq_file *m, void *v) * bcm_can_tx - send the (next) CAN frame to the appropriate CAN interface * of the given bcm tx op */ -static void bcm_can_tx(struct bcm_op *op) +static void bcm_can_tx(struct bcm_op *op, struct canfd_frame *cf) { struct sk_buff *skb; struct can_skb_ext *csx; struct net_device *dev; - struct canfd_frame *cf; + struct canfd_frame cframe; + bool cyclic = !cf; + unsigned int idx = 0; int err; /* no target device? => exit */ if (!op->ifindex) return; - /* read currframe under lock protection */ - spin_lock_bh(&op->bcm_tx_lock); - cf = op->frames + op->cfsiz * op->currframe; - spin_unlock_bh(&op->bcm_tx_lock); + if (cyclic) { + /* read currframe under lock protection */ + spin_lock_bh(&op->bcm_tx_lock); + idx = op->currframe; + memcpy(&cframe, op->frames + op->cfsiz * idx, op->cfsiz); + cf = &cframe; + spin_unlock_bh(&op->bcm_tx_lock); + } dev = dev_get_by_index(sock_net(op->sk), op->ifindex); if (!dev) { @@ -341,14 +348,20 @@ static void bcm_can_tx(struct bcm_op *op) if (!err) op->frames_abs++; - op->currframe++; + /* only advance the cyclic sequence if nothing reset currframe while + * we were sending - a concurrent TX_RESET_MULTI_IDX means this + * frame's bookkeeping belongs to a sequence that no longer exists + */ + if (!cyclic || op->currframe == idx) { + op->currframe++; - /* reached last frame? */ - if (op->currframe >= op->nframes) - op->currframe = 0; + /* reached last frame? */ + if (op->currframe >= op->nframes) + op->currframe = 0; - if (op->count > 0) - op->count--; + if (op->count > 0) + op->count--; + } spin_unlock_bh(&op->bcm_tx_lock); out: @@ -461,7 +474,7 @@ static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer) struct bcm_msg_head msg_head; if (op->kt_ival1 && (op->count > 0)) { - bcm_can_tx(op); + bcm_can_tx(op, NULL); if (!op->count && (op->flags & TX_COUNTEVT)) { /* create notification to user */ @@ -478,7 +491,7 @@ static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer) } } else if (op->kt_ival2) { - bcm_can_tx(op); + bcm_can_tx(op, NULL); } return bcm_tx_set_expiry(op, &op->timer) ? @@ -622,6 +635,8 @@ static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer) struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer); struct bcm_msg_head msg_head; + spin_lock_bh(&op->bcm_rx_update_lock); + /* if user wants to be informed, when cyclic CAN-Messages come back */ if ((op->flags & RX_ANNOUNCE_RESUME) && op->last_frames) { /* clear received CAN frames to indicate 'nothing received' */ @@ -638,6 +653,8 @@ static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer) msg_head.can_id = op->can_id; msg_head.nframes = 0; + spin_unlock_bh(&op->bcm_rx_update_lock); + bcm_send_to_user(op, &msg_head, NULL, 0); return HRTIMER_NORESTART; @@ -686,15 +703,26 @@ static int bcm_rx_thr_flush(struct bcm_op *op) static enum hrtimer_restart bcm_rx_thr_handler(struct hrtimer *hrtimer) { struct bcm_op *op = container_of(hrtimer, struct bcm_op, thrtimer); + enum hrtimer_restart ret; - if (bcm_rx_thr_flush(op)) { + spin_lock_bh(&op->bcm_rx_update_lock); + + /* kt_ival2 may have been concurrently cleared by bcm_rx_setup() + * before it cancels this timer - never forward with a zero + * interval in that case. + */ + if (bcm_rx_thr_flush(op) && op->kt_ival2) { hrtimer_forward_now(hrtimer, op->kt_ival2); - return HRTIMER_RESTART; + ret = HRTIMER_RESTART; } else { /* rearm throttle handling */ op->kt_lastmsg = 0; - return HRTIMER_NORESTART; + ret = HRTIMER_NORESTART; } + + spin_unlock_bh(&op->bcm_rx_update_lock); + + return ret; } /* @@ -704,8 +732,10 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) { struct bcm_op *op = (struct bcm_op *)data; const struct canfd_frame *rxframe = (struct canfd_frame *)skb->data; + struct canfd_frame rtrframe; unsigned int i; unsigned char traffic_flags; + bool rtr_frame; if (op->can_id != rxframe->can_id) return; @@ -729,9 +759,18 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) /* update statistics */ op->frames_abs++; - if (op->flags & RX_RTR_FRAME) { + /* snapshot the flag under lock: op->flags/op->frames may be updated + * concurrently by bcm_rx_setup(). + */ + spin_lock_bh(&op->bcm_rx_update_lock); + rtr_frame = op->flags & RX_RTR_FRAME; + if (rtr_frame) + memcpy(&rtrframe, op->frames, op->cfsiz); + spin_unlock_bh(&op->bcm_rx_update_lock); + + if (rtr_frame) { /* send reply for RTR-request (placed in op->frames[0]) */ - bcm_can_tx(op); + bcm_can_tx(op, &rtrframe); return; } @@ -743,6 +782,8 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) traffic_flags |= RX_OWN; } + spin_lock_bh(&op->bcm_rx_update_lock); + if (op->flags & RX_FILTER_ID) { /* the easiest case */ bcm_rx_update_and_send(op, op->last_frames, rxframe, @@ -778,6 +819,8 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) rx_starttimer: bcm_rx_starttimer(op); + + spin_unlock_bh(&op->bcm_rx_update_lock); } /* @@ -1116,7 +1159,7 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, } if (op->flags & TX_ANNOUNCE) - bcm_can_tx(op); + bcm_can_tx(op, NULL); if (op->flags & STARTTIMER) bcm_tx_start_timer(op); @@ -1130,6 +1173,24 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, return err; } +static void bcm_rx_setup_rtr_check(struct bcm_msg_head *msg_head, + struct bcm_op *op, void *new_frames) +{ + /* funny feature in RX(!)_SETUP only for RTR-mode: + * copy can_id into frame BUT without RTR-flag to + * prevent a full-load-loopback-test ... ;-] + * normalize this on the staged buffer, before it is + * ever installed into op->frames. + */ + if (msg_head->flags & RX_RTR_FRAME) { + struct canfd_frame *frame0 = new_frames; + + if ((msg_head->flags & TX_CP_CAN_ID) || + frame0->can_id == op->can_id) + frame0->can_id = op->can_id & ~CAN_RTR_FLAG; + } +} + /* * bcm_rx_setup - create or update a bcm rx op (for bcm_sendmsg) */ @@ -1164,6 +1225,8 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* check the given can_id */ op = bcm_find_op(&bo->rx_ops, msg_head, ifindex); if (op) { + void *new_frames = NULL; + /* update existing BCM operation */ /* @@ -1175,19 +1238,48 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, return -E2BIG; if (msg_head->nframes) { - /* update CAN frames content */ - err = memcpy_from_msg(op->frames, msg, + /* get new CAN frames content before locking */ + new_frames = kmalloc(msg_head->nframes * op->cfsiz, + GFP_KERNEL); + if (!new_frames) + return -ENOMEM; + + err = memcpy_from_msg(new_frames, msg, msg_head->nframes * op->cfsiz); - if (err < 0) + if (err < 0) { + kfree(new_frames); return err; + } - /* clear last_frames to indicate 'nothing received' */ - memset(op->last_frames, 0, msg_head->nframes * op->cfsiz); + bcm_rx_setup_rtr_check(msg_head, op, new_frames); } + spin_lock_bh(&op->bcm_rx_update_lock); op->nframes = msg_head->nframes; op->flags = msg_head->flags; + if (msg_head->nframes) { + /* update CAN frames content */ + memcpy(op->frames, new_frames, + msg_head->nframes * op->cfsiz); + + /* clear last_frames to indicate 'nothing received' */ + memset(op->last_frames, 0, + msg_head->nframes * op->cfsiz); + } + + if (msg_head->flags & SETTIMER) { + op->ival1 = msg_head->ival1; + op->ival2 = msg_head->ival2; + op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1); + op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2); + op->kt_lastmsg = 0; + } + spin_unlock_bh(&op->bcm_rx_update_lock); + + /* free temporary frames / kfree(NULL) is safe */ + kfree(new_frames); + /* Only an update -> do not call can_rx_register() */ do_rx_register = 0; @@ -1198,6 +1290,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, return -ENOMEM; spin_lock_init(&op->bcm_tx_lock); + spin_lock_init(&op->bcm_rx_update_lock); op->can_id = msg_head->can_id; op->nframes = msg_head->nframes; op->cfsiz = CFSIZ(msg_head->flags); @@ -1239,6 +1332,8 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, kfree(op); return err; } + + bcm_rx_setup_rtr_check(msg_head, op, op->frames); } /* bcm_can_tx / bcm_tx_timeout_handler needs this */ @@ -1266,29 +1361,22 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* check flags */ if (op->flags & RX_RTR_FRAME) { - struct canfd_frame *frame0 = op->frames; - /* no timers in RTR-mode */ hrtimer_cancel(&op->thrtimer); hrtimer_cancel(&op->timer); - - /* - * funny feature in RX(!)_SETUP only for RTR-mode: - * copy can_id into frame BUT without RTR-flag to - * prevent a full-load-loopback-test ... ;-] - */ - if ((op->flags & TX_CP_CAN_ID) || - (frame0->can_id == op->can_id)) - frame0->can_id = op->can_id & ~CAN_RTR_FLAG; - } else { if (op->flags & SETTIMER) { - /* set timer value */ - op->ival1 = msg_head->ival1; - op->ival2 = msg_head->ival2; - op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1); - op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2); + /* set timers (locked) for newly created op */ + if (do_rx_register) { + spin_lock_bh(&op->bcm_rx_update_lock); + op->ival1 = msg_head->ival1; + op->ival2 = msg_head->ival2; + op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1); + op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2); + op->kt_lastmsg = 0; + spin_unlock_bh(&op->bcm_rx_update_lock); + } /* disable an active timer due to zero value? */ if (!op->kt_ival1) @@ -1298,9 +1386,11 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, * In any case cancel the throttle timer, flush * potentially blocked msgs and reset throttle handling */ - op->kt_lastmsg = 0; hrtimer_cancel(&op->thrtimer); + + spin_lock_bh(&op->bcm_rx_update_lock); bcm_rx_thr_flush(op); + spin_unlock_bh(&op->bcm_rx_update_lock); } if ((op->flags & STARTTIMER) && op->kt_ival1) From 30f7bb922cb7e7f072a56c7cb7a5efccd2ceca1d Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:27 +0200 Subject: [PATCH 2500/2758] can: bcm: add missing rcu list annotations and operations commit 7b2c3eabc4dafc062a25e10711154f2107526a78 upstream. sashiko-bot remarked the missing use of list_add_rcu() in bcm_[rx|tx]_setup() to have a proper initialized bcm_op structure when bcm_proc_show() traverses the bcm_op's under rcu_read_lock(). To cover all initial settings of the bcm_op's the list_add_rcu() calls are moved to the end of the setup code. While at it, also fix the mirroring removal side: bcm_release() called bcm_remove_op() - which frees the op via call_rcu() - on ops that were still linked in bo->tx_ops/bo->rx_ops, without list_del_rcu() first. Unlink each op with list_del_rcu() before handing it to bcm_remove_op(), matching the existing pattern in bcm_delete_tx_op()/bcm_delete_rx_op(). Reported-by: sashiko-reviews@lists.linux.dev Closes: https://lore.kernel.org/linux-can/20260610094654.A1FFE1F00893@smtp.kernel.org/ Fixes: dac5e6249159 ("can: bcm: add missing rcu read protection for procfs content") Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-5-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- net/can/bcm.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index 1e5f8d65d351b..bc169c040cd02 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -262,7 +262,7 @@ static int bcm_proc_show(struct seq_file *m, void *v) (reduction == 100) ? "near " : "", reduction); } - list_for_each_entry(op, &bo->tx_ops, list) { + list_for_each_entry_rcu(op, &bo->tx_ops, list) { seq_printf(m, "tx_op: %03X %s ", op->can_id, bcm_proc_getifname(net, ifname, op->ifindex)); @@ -994,6 +994,7 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, struct bcm_sock *bo = bcm_sk(sk); struct bcm_op *op; struct canfd_frame *cf; + bool add_op_to_list = false; unsigned int i; int err; @@ -1135,8 +1136,7 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, hrtimer_setup(&op->thrtimer, hrtimer_dummy_timeout, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); - /* add this bcm_op to the list of the tx_ops */ - list_add(&op->list, &bo->tx_ops); + add_op_to_list = true; } /* if ((op = bcm_find_op(&bo->tx_ops, msg_head->can_id, ifindex))) */ @@ -1158,6 +1158,10 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, op->flags |= TX_ANNOUNCE; } + /* add this bcm_op to the list of the tx_ops? */ + if (add_op_to_list) + list_add_rcu(&op->list, &bo->tx_ops); + if (op->flags & TX_ANNOUNCE) bcm_can_tx(op, NULL); @@ -1350,9 +1354,6 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, hrtimer_setup(&op->thrtimer, bcm_rx_thr_handler, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); - /* add this bcm_op to the list of the rx_ops */ - list_add(&op->list, &bo->rx_ops); - /* call can_rx_register() */ do_rx_register = 1; @@ -1426,10 +1427,12 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, bcm_rx_handler, op, "bcm", sk); if (err) { /* this bcm rx op is broken -> remove it */ - list_del_rcu(&op->list); bcm_remove_op(op); return err; } + + /* add this bcm_op to the list of the rx_ops */ + list_add_rcu(&op->list, &bo->rx_ops); } return msg_head->nframes * op->cfsiz + MHSIZ; @@ -1763,8 +1766,10 @@ static int bcm_release(struct socket *sock) remove_proc_entry(bo->procname, net->can.bcmproc_dir); #endif /* CONFIG_PROC_FS */ - list_for_each_entry_safe(op, next, &bo->tx_ops, list) + list_for_each_entry_safe(op, next, &bo->tx_ops, list) { + list_del_rcu(&op->list); bcm_remove_op(op); + } list_for_each_entry_safe(op, next, &bo->rx_ops, list) { /* @@ -1795,8 +1800,10 @@ static int bcm_release(struct socket *sock) synchronize_rcu(); - list_for_each_entry_safe(op, next, &bo->rx_ops, list) + list_for_each_entry_safe(op, next, &bo->rx_ops, list) { + list_del_rcu(&op->list); bcm_remove_op(op); + } /* remove device reference */ if (bo->bound) { From 337f966c00662d81ad82cf5a4bbb150b2e32c0d4 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:28 +0200 Subject: [PATCH 2501/2758] can: bcm: extend bcm_tx_lock usage for data and timer updates commit 12ce799f7ab1e05bd8fbf79e46f403bfe5597ebc upstream. Stage new CAN frame content for an existing tx op into a kmalloc()'d buffer and validate it there, mirroring the approach already used in bcm_rx_setup(). Only copy the validated data into op->frames while holding op->bcm_tx_lock, so bcm_can_tx() and bcm_tx_timeout_handler() can no longer observe a partially updated or unvalidated frame. Add a missing error path for memcpy_from_msg() when copying CAN frame data from userspace. Also move the kt_ival1/kt_ival2/ival1/ival2 updates in bcm_tx_setup() under op->bcm_tx_lock, and read kt_ival1/kt_ival2/count under the same lock in bcm_tx_set_expiry() and bcm_tx_timeout_handler(), closing the torn 64-bit ktime_t read on 32-bit platforms. Fixes: c2aba69d0c36 ("can: bcm: add locking for bcm_op runtime updates") Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-6-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- net/can/bcm.c | 104 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 75 insertions(+), 29 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index bc169c040cd02..42cda7bcf94ef 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -128,7 +128,7 @@ struct bcm_op { struct canfd_frame last_sframe; struct sock *sk; struct net_device *rx_reg_dev; - spinlock_t bcm_tx_lock; /* protect currframe/count in runtime updates */ + spinlock_t bcm_tx_lock; /* protect tx data and timer updates */ spinlock_t bcm_rx_update_lock; /* protect filter/timer data updates */ }; @@ -450,12 +450,18 @@ static bool bcm_tx_set_expiry(struct bcm_op *op, struct hrtimer *hrt) { ktime_t ival; + spin_lock_bh(&op->bcm_tx_lock); + if (op->kt_ival1 && op->count) ival = op->kt_ival1; - else if (op->kt_ival2) + else if (op->kt_ival2) { ival = op->kt_ival2; - else + } else { + spin_unlock_bh(&op->bcm_tx_lock); return false; + } + + spin_unlock_bh(&op->bcm_tx_lock); hrtimer_set_expires(hrt, ktime_add(ktime_get(), ival)); return true; @@ -472,25 +478,47 @@ static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer) { struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer); struct bcm_msg_head msg_head; + bool tx_ival1, tx_ival2; + + /* snapshot kt_ival1/kt_ival2/count under lock to avoid torn + * ktime_t reads racing with concurrent bcm_tx_setup() updates + */ + spin_lock_bh(&op->bcm_tx_lock); + tx_ival1 = op->kt_ival1 && (op->count > 0); + tx_ival2 = !!op->kt_ival2; + spin_unlock_bh(&op->bcm_tx_lock); + + if (tx_ival1) { + u32 flags, count; + struct bcm_timeval ival1, ival2; - if (op->kt_ival1 && (op->count > 0)) { bcm_can_tx(op, NULL); - if (!op->count && (op->flags & TX_COUNTEVT)) { + /* snapshot variables under lock to avoid torn reads racing + * with concurrent bcm_tx_setup() updates + */ + spin_lock_bh(&op->bcm_tx_lock); + flags = op->flags; + count = op->count; + ival1 = op->ival1; + ival2 = op->ival2; + spin_unlock_bh(&op->bcm_tx_lock); + + if (!count && (flags & TX_COUNTEVT)) { /* create notification to user */ memset(&msg_head, 0, sizeof(msg_head)); msg_head.opcode = TX_EXPIRED; - msg_head.flags = op->flags; - msg_head.count = op->count; - msg_head.ival1 = op->ival1; - msg_head.ival2 = op->ival2; + msg_head.flags = flags; + msg_head.count = count; + msg_head.ival1 = ival1; + msg_head.ival2 = ival2; msg_head.can_id = op->can_id; msg_head.nframes = 0; bcm_send_to_user(op, &msg_head, NULL, 0); } - } else if (op->kt_ival2) { + } else if (tx_ival2) { bcm_can_tx(op, NULL); } @@ -1013,6 +1041,8 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* check the given can_id */ op = bcm_find_op(&bo->tx_ops, msg_head, ifindex); if (op) { + void *new_frames; + /* update existing BCM operation */ /* @@ -1023,11 +1053,23 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, if (msg_head->nframes > op->nframes) return -E2BIG; - /* update CAN frames content */ + /* get new CAN frames content into a staging buffer before + * locking: validate and normalize the frames there so that + * bcm_can_tx() / bcm_tx_timeout_handler() never observe a + * partially updated or unvalidated frame in op->frames + */ + new_frames = kmalloc(msg_head->nframes * op->cfsiz, GFP_KERNEL); + if (!new_frames) + return -ENOMEM; + for (i = 0; i < msg_head->nframes; i++) { - cf = op->frames + op->cfsiz * i; + cf = new_frames + op->cfsiz * i; err = memcpy_from_msg((u8 *)cf, msg, op->cfsiz); + if (err < 0) { + kfree(new_frames); + return err; + } if (op->flags & CAN_FD_FRAME) { if (cf->len > 64) @@ -1037,36 +1079,38 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, err = -EINVAL; } - if (err < 0) + if (err < 0) { + kfree(new_frames); return err; + } if (msg_head->flags & TX_CP_CAN_ID) { /* copy can_id into frame */ cf->can_id = msg_head->can_id; } } + + spin_lock_bh(&op->bcm_tx_lock); + + /* update CAN frames content */ + memcpy(op->frames, new_frames, msg_head->nframes * op->cfsiz); + op->flags = msg_head->flags; - /* only lock for unlikely count/nframes/currframe changes */ if (op->nframes != msg_head->nframes || - op->flags & TX_RESET_MULTI_IDX || - op->flags & SETTIMER) { - - spin_lock_bh(&op->bcm_tx_lock); + op->flags & TX_RESET_MULTI_IDX) { + /* potentially update changed nframes */ + op->nframes = msg_head->nframes; + /* restart multiple frame transmission */ + op->currframe = 0; + } - if (op->nframes != msg_head->nframes || - op->flags & TX_RESET_MULTI_IDX) { - /* potentially update changed nframes */ - op->nframes = msg_head->nframes; - /* restart multiple frame transmission */ - op->currframe = 0; - } + if (op->flags & SETTIMER) + op->count = msg_head->count; - if (op->flags & SETTIMER) - op->count = msg_head->count; + spin_unlock_bh(&op->bcm_tx_lock); - spin_unlock_bh(&op->bcm_tx_lock); - } + kfree(new_frames); } else { /* insert new BCM operation for the given can_id */ @@ -1142,10 +1186,12 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, if (op->flags & SETTIMER) { /* set timer values */ + spin_lock_bh(&op->bcm_tx_lock); op->ival1 = msg_head->ival1; op->ival2 = msg_head->ival2; op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1); op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2); + spin_unlock_bh(&op->bcm_tx_lock); /* disable an active timer due to zero values? */ if (!op->kt_ival1 && !op->kt_ival2) From df47f07cdc801a6afe05a486b5a343c3e532a93c Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:26 +0200 Subject: [PATCH 2502/2758] can: bcm: fix CAN frame rx/tx statistics commit e6c24ba95fc3f1b5e1dcd28b1c6e59ef61a9daa5 upstream. KCSAN detected a data race within the bcm_rx_handler() when two CAN frames have been simultaneously received and processed in a single rx op by two different CPUs. Use atomic operations with (signed) long data types to access the statistics in the hot path to fix the KCSAN complaint. Additionally simplify the update and check of statistics overflow by using the atomic operations in separate bcm_update_[rx|tx]_stats() functions. The rx variant runs under bcm_rx_update_lock to prevent races when resetting the two rx counters; the tx variant runs under bcm_tx_lock and only needs to guard its own counter's overflow. As the rx path resets its values already at LONG_MAX / 100, there is no conflict between the two locking domains (bcm_rx_update_lock vs. bcm_tx_lock) even for ops that use both paths. The rx statistics update and the frames_filtered update in bcm_rx_changed() were previously performed in two separate bcm_rx_update_lock sections. For an rx op subscribed on all interfaces (ifindex == 0), bcm_rx_handler() can run concurrently on different CPUs, so a counter reset by one CPU between these two sections could leave frames_filtered larger than frames_abs on another CPU, producing a bogus (even negative) reduction percentage in procfs. Update the statistics in the same critical section as bcm_rx_changed() to close this gap, which also removes the now unneeded extra lock/unlock pair around the traffic_flags calculation. Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol") Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-4-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- net/can/bcm.c | 69 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 23 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index 42cda7bcf94ef..68a62f605432f 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -112,7 +112,7 @@ struct bcm_op { int ifindex; canid_t can_id; u32 flags; - unsigned long frames_abs, frames_filtered; + atomic_long_t frames_abs, frames_filtered; struct bcm_timeval ival1, ival2; struct hrtimer timer, thrtimer; ktime_t rx_stamp, kt_ival1, kt_ival2, kt_lastmsg; @@ -229,10 +229,13 @@ static int bcm_proc_show(struct seq_file *m, void *v) list_for_each_entry_rcu(op, &bo->rx_ops, list) { - unsigned long reduction; + long reduction, frames_filtered, frames_abs; + + frames_filtered = atomic_long_read(&op->frames_filtered); + frames_abs = atomic_long_read(&op->frames_abs); /* print only active entries & prevent division by zero */ - if (!op->frames_abs) + if (!frames_abs) continue; seq_printf(m, "rx_op: %03X %-5s ", op->can_id, @@ -254,9 +257,9 @@ static int bcm_proc_show(struct seq_file *m, void *v) (long long)ktime_to_us(op->kt_ival2)); seq_printf(m, "# recv %ld (%ld) => reduction: ", - op->frames_filtered, op->frames_abs); + frames_filtered, frames_abs); - reduction = 100 - (op->frames_filtered * 100) / op->frames_abs; + reduction = 100 - (frames_filtered * 100) / frames_abs; seq_printf(m, "%s%ld%%\n", (reduction == 100) ? "near " : "", reduction); @@ -280,7 +283,8 @@ static int bcm_proc_show(struct seq_file *m, void *v) seq_printf(m, "t2=%lld ", (long long)ktime_to_us(op->kt_ival2)); - seq_printf(m, "# sent %ld\n", op->frames_abs); + seq_printf(m, "# sent %ld\n", + atomic_long_read(&op->frames_abs)); } seq_putc(m, '\n'); @@ -290,6 +294,24 @@ static int bcm_proc_show(struct seq_file *m, void *v) } #endif /* CONFIG_PROC_FS */ +static void bcm_update_rx_stats(struct bcm_op *op) +{ + /* prevent overflow of the reduction% calculation in bcm_proc_show() */ + if (atomic_long_inc_return(&op->frames_abs) > LONG_MAX / 100) { + atomic_long_set(&op->frames_filtered, 0); + atomic_long_set(&op->frames_abs, 0); + } +} + +static void bcm_update_tx_stats(struct bcm_op *op) +{ + /* tx_op has no reduction% calculation - use the full range and + * just keep the displayed counter non-negative on overflow + */ + if (atomic_long_inc_return(&op->frames_abs) == LONG_MAX) + atomic_long_set(&op->frames_abs, 0); +} + /* * bcm_can_tx - send the (next) CAN frame to the appropriate CAN interface * of the given bcm tx op @@ -346,7 +368,7 @@ static void bcm_can_tx(struct bcm_op *op, struct canfd_frame *cf) spin_lock_bh(&op->bcm_tx_lock); if (!err) - op->frames_abs++; + bcm_update_tx_stats(op); /* only advance the cyclic sequence if nothing reset currframe while * we were sending - a concurrent TX_RESET_MULTI_IDX means this @@ -533,12 +555,9 @@ static void bcm_rx_changed(struct bcm_op *op, struct canfd_frame *data) { struct bcm_msg_head head; - /* update statistics */ - op->frames_filtered++; - - /* prevent statistics overflow */ - if (op->frames_filtered > ULONG_MAX/100) - op->frames_filtered = op->frames_abs = 0; + /* update statistics (frames_filtered <= frames_abs) */ + if (atomic_long_read(&op->frames_abs)) + atomic_long_inc(&op->frames_filtered); /* this element is not throttled anymore */ data->flags &= ~RX_THR; @@ -784,24 +803,30 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) op->rx_stamp = skb->tstamp; /* save originator for recvfrom() */ op->rx_ifindex = skb->dev->ifindex; - /* update statistics */ - op->frames_abs++; - /* snapshot the flag under lock: op->flags/op->frames may be updated - * concurrently by bcm_rx_setup(). - */ + /* op->flags/op->frames may be updated concurrently by bcm_rx_setup() */ spin_lock_bh(&op->bcm_rx_update_lock); + rtr_frame = op->flags & RX_RTR_FRAME; - if (rtr_frame) + if (rtr_frame) { + bcm_update_rx_stats(op); + /* snapshot RTR content under lock */ memcpy(&rtrframe, op->frames, op->cfsiz); - spin_unlock_bh(&op->bcm_rx_update_lock); + spin_unlock_bh(&op->bcm_rx_update_lock); - if (rtr_frame) { /* send reply for RTR-request (placed in op->frames[0]) */ bcm_can_tx(op, &rtrframe); return; } + /* update statistics in the same critical section as bcm_rx_changed() + * below: frames_filtered must never be checked/incremented against a + * frames_abs snapshot from a concurrent bcm_rx_handler() call on + * another CPU for the same (wildcard) op, or frames_filtered can end + * up larger than frames_abs. + */ + bcm_update_rx_stats(op); + /* compute flags to distinguish between own/local/remote CAN traffic */ traffic_flags = 0; if (skb->sk) { @@ -810,8 +835,6 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) traffic_flags |= RX_OWN; } - spin_lock_bh(&op->bcm_rx_update_lock); - if (op->flags & RX_FILTER_ID) { /* the easiest case */ bcm_rx_update_and_send(op, op->last_frames, rxframe, From c312b750bb5ac3348cfc85dab25e90937bd4d251 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:32 +0200 Subject: [PATCH 2503/2758] can: bcm: fix data race on rx_stamp/rx_ifindex in bcm_rx_handler() commit 58fd6cbc8541216af1d7ed272ea7ac2b66d50fd8 upstream. For an rx op subscribed on all interfaces (ifindex == 0), the same op is registered once in the shared per-netns wildcard filter list, so bcm_rx_handler() can run concurrently on different CPUs for frames arriving on different net devices. op->rx_stamp and op->rx_ifindex were written before bcm_rx_update_lock was taken, allowing concurrent writers to race each other - including a torn store of the 64-bit rx_stamp on 32-bit platforms. Beyond a torn store bcm_send_to_user() must report the timestamp/ifindex of the very same frame whose content it is delivering. So the assignment is placed in the same unbroken bcm_rx_update_lock section as the content comparison. As a side effect, the RTR-request frame feature (which never reach bcm_send_to_user()) no longer updates rx_stamp/rx_ifindex, since only the notification path needs them. Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol") Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/linux-can/20260707145135.5BC831F00A3A@smtp.kernel.org/ Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-10-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- net/can/bcm.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index 68a62f605432f..16caa7241b901 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -799,11 +799,6 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) /* disable timeout */ hrtimer_cancel(&op->timer); - /* save rx timestamp */ - op->rx_stamp = skb->tstamp; - /* save originator for recvfrom() */ - op->rx_ifindex = skb->dev->ifindex; - /* op->flags/op->frames may be updated concurrently by bcm_rx_setup() */ spin_lock_bh(&op->bcm_rx_update_lock); @@ -835,6 +830,14 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) traffic_flags |= RX_OWN; } + /* save rx timestamp and originator for recvfrom() under lock. + * For an op subscribed on all interfaces (ifindex == 0) + * bcm_rx_handler() can run concurrently on different CPUs so + * the CAN content and the meta data must be bundled correctly. + */ + op->rx_stamp = skb->tstamp; + op->rx_ifindex = skb->dev->ifindex; + if (op->flags & RX_FILTER_ID) { /* the easiest case */ bcm_rx_update_and_send(op, op->last_frames, rxframe, From b31d0933509c5a35c0be5736a2ce8df0d1bf112c Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:31 +0200 Subject: [PATCH 2504/2758] can: bcm: fix stale rx/tx ops after device removal commit 3b762c0d950383ab7a002686c9136b9aa55d2d70 upstream. RX: an RX_SETUP update(!) for an existing op skipped can_rx_register() unconditionally, even when a concurrent NETDEV_UNREGISTER had already torn down its registration (op->rx_reg_dev == NULL). This silently did not re-enable frame delivery for that updated filter. bcm_rx_setup() now re-registers in that case, while leaving rx_ops with ifindex = 0 (all CAN devices) which never carry a tracked rx_reg_dev registered as-is. TX: bcm_notify() only handled bo->rx_ops on NETDEV_UNREGISTER, leaving tx_ops with an active cyclic transmission re-arming its hrtimer indefinitely to execute bcm_tx_timeout_handler(). Cancelling the hrtimer prevents the runaway timer and any injection into a later reused ifindex, since nothing else calls bcm_can_tx() for the op until an explicit TX_SETUP update re-arms it. Unlike bcm_rx_unreg(), which clears the tracked rx_reg_dev for rx_ops, the ifindex is intentionally left unchanged for tx_ops. bcm_tx_setup() always rejects ifindex 0, so clearing it would strand the op: neither a later TX_SETUP (bcm_find_op()) nor TX_DELETE (bcm_delete_tx_op()) could ever find it again, since both require an exact ifindex match. Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/linux-can/20260708094536.DDF821F00A3A@smtp.kernel.org/ Closes: https://lore.kernel.org/linux-can/20260708154039.347ED1F000E9@smtp.kernel.org/ Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol") Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-9-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- net/can/bcm.c | 54 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index 16caa7241b901..be484cd00741a 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -1276,6 +1276,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, struct bcm_sock *bo = bcm_sk(sk); struct bcm_op *op; int do_rx_register; + int new_op = 0; int err = 0; if ((msg_head->flags & RX_FILTER_ID) || (!(msg_head->nframes))) { @@ -1356,8 +1357,15 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* free temporary frames / kfree(NULL) is safe */ kfree(new_frames); - /* Only an update -> do not call can_rx_register() */ - do_rx_register = 0; + /* Don't register a new CAN filter for the rx_op update unless + * a concurrent NETDEV_UNREGISTER notifier already tore down + * the previous registration. In this case the receiver needs + * to be re-registered here so that this update doesn't + * silently stop delivering frames for the given ifindex. + * Ops with ifindex = 0 (all CAN interfaces) never carry a + * tracked rx_reg_dev and stay registered as-is. + */ + do_rx_register = (ifindex && !op->rx_reg_dev) ? 1 : 0; } else { /* insert new BCM operation for the given can_id */ @@ -1428,6 +1436,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* call can_rx_register() */ do_rx_register = 1; + new_op = 1; } /* if ((op = bcm_find_op(&bo->rx_ops, msg_head->can_id, ifindex))) */ @@ -1441,7 +1450,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, if (op->flags & SETTIMER) { /* set timers (locked) for newly created op */ - if (do_rx_register) { + if (new_op) { spin_lock_bh(&op->bcm_rx_update_lock); op->ival1 = msg_head->ival1; op->ival2 = msg_head->ival2; @@ -1471,7 +1480,10 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, HRTIMER_MODE_REL_SOFT); } - /* now we can register for can_ids, if we added a new bcm_op */ + /* now we can register for can_ids, if we added a new bcm_op + * or need to re-register after a NETDEV_UNREGISTER tore down + * the previous registration of an existing op + */ if (do_rx_register) { if (ifindex) { struct net_device *dev; @@ -1493,18 +1505,32 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, err = -ENODEV; } - } else + } else { err = can_rx_register(sock_net(sk), NULL, op->can_id, REGMASK(op->can_id), bcm_rx_handler, op, "bcm", sk); + } + if (err) { - /* this bcm rx op is broken -> remove it */ - bcm_remove_op(op); + /* newly created bcm rx op is broken -> remove it */ + if (new_op) { + bcm_remove_op(op); + return err; + } + + /* an existing op just stays unregistered. + * Cancel op->timer and (defensively) op->thrtimer. + * Other settings can't be reached until the next + * successful RX_SETUP. + */ + hrtimer_cancel(&op->timer); + hrtimer_cancel(&op->thrtimer); return err; } - /* add this bcm_op to the list of the rx_ops */ - list_add_rcu(&op->list, &bo->rx_ops); + /* add a new bcm_op to the list of the rx_ops */ + if (new_op) + list_add_rcu(&op->list, &bo->rx_ops); } return msg_head->nframes * op->cfsiz + MHSIZ; @@ -1716,11 +1742,19 @@ static void bcm_notify(struct bcm_sock *bo, unsigned long msg, case NETDEV_UNREGISTER: lock_sock(sk); - /* remove device specific receive entries */ + /* rx_ops: remove device specific receive entries */ list_for_each_entry(op, &bo->rx_ops, list) if (op->rx_reg_dev == dev) bcm_rx_unreg(dev, op); + /* tx_ops: stop device specific cyclic transmissions on the + * vanishing ifindex. Cancelling the timer is enough to stop + * cyclic bcm_can_tx() calls as there is no re-arming. + */ + list_for_each_entry(op, &bo->tx_ops, list) + if (op->ifindex == dev->ifindex) + hrtimer_cancel(&op->timer); + /* remove device reference, if this is our bound device */ if (bo->bound && bo->ifindex == dev->ifindex) { #if IS_ENABLED(CONFIG_PROC_FS) From b6317022b685a430a3ae420456716e3c0c02ef4b Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:33 +0200 Subject: [PATCH 2505/2758] can: bcm: track a single source interface for ANYDEV timeout/throttle ops commit 2f5976f54a04e9f18b25283036ac3136be453b17 upstream. An ANYDEV rx op (ifindex == 0) with an active RX timeout and/or throttle timer has no defined semantics when matching frames arrive from several interfaces: bcm_rx_handler() can run concurrently for the same op on different CPUs, racing hrtimer_cancel()/ bcm_rx_starttimer() against bcm_rx_timeout_handler() and causing spurious RX_TIMEOUT notifications and last_frames corruption. The same concurrency lets throttled multiplex frames from different interfaces clobber the single rx_ifindex/rx_stamp fields shared by the op. Add op->if_detected to track the first interface that delivers a matching frame while a timeout/throttle timer is configured, and reject frames from any other interface for that op. The claim is decided in bcm_rx_handler() before hrtimer_cancel() touches op->timer, so a rejected frame can never disturb the claimed interface's watchdog. RTR-mode ops are excluded via RX_RTR_FRAME, independent of kt_ival1/kt_ival2, since those may briefly hold a stale value from an earlier non-RTR configuration. The claim is released in bcm_notify() on NETDEV_UNREGISTER and in bcm_rx_setup() when SETTIMER reconfigures the timer values. A (re-)claim is only possible on CAN devices in NETREG_REGISTERED dev->reg_state to cover the release in bcm_notify() where reg_state becomes NETREG_UNREGISTERING until synchronize_net(). Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol") Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/linux-can/20260709105031.1A39C1F000E9@smtp.kernel.org/ Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-11-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- net/can/bcm.c | 49 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index be484cd00741a..491da4189c3c0 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -117,6 +117,7 @@ struct bcm_op { struct hrtimer timer, thrtimer; ktime_t rx_stamp, kt_ival1, kt_ival2, kt_lastmsg; int rx_ifindex; + int if_detected; /* first received ifindex in ANYDEV rx_op mode */ int cfsiz; u32 count; u32 nframes; @@ -796,6 +797,33 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) return; } + /* An ANYDEV op with an active RX timeout and/or throttle timer + * tracks a single source interface: claim the first interface that + * delivers a matching frame and reject frames from any other one, + * before hrtimer_cancel() below can touch op->timer - this avoids + * racing bcm_rx_timeout_handler() across concurrent interfaces. + * RX_RTR_FRAME ops are excluded, as kt_ival1/kt_ival2 may briefly + * hold a stale value from an earlier non-RTR configuration. + */ + if (!op->ifindex) { + spin_lock_bh(&op->bcm_rx_update_lock); + + if (!(op->flags & RX_RTR_FRAME) && + (op->kt_ival1 || op->kt_ival2)) { + /* don't claim to vanishing interface */ + if (!op->if_detected && + READ_ONCE(skb->dev->reg_state) == NETREG_REGISTERED) + op->if_detected = skb->dev->ifindex; + + if (op->if_detected != skb->dev->ifindex) { + spin_unlock_bh(&op->bcm_rx_update_lock); + return; + } + } + + spin_unlock_bh(&op->bcm_rx_update_lock); + } + /* disable timeout */ hrtimer_cancel(&op->timer); @@ -830,10 +858,9 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) traffic_flags |= RX_OWN; } - /* save rx timestamp and originator for recvfrom() under lock. - * For an op subscribed on all interfaces (ifindex == 0) - * bcm_rx_handler() can run concurrently on different CPUs so - * the CAN content and the meta data must be bundled correctly. + /* save rx timestamp and originator for recvfrom() under lock: an + * ANYDEV op without an active timer can still run concurrently on + * different CPUs, so content and meta data must be bundled here. */ op->rx_stamp = skb->tstamp; op->rx_ifindex = skb->dev->ifindex; @@ -1351,6 +1378,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1); op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2); op->kt_lastmsg = 0; + op->if_detected = 0; /* reclaim ifindex in ANYDEV mode */ } spin_unlock_bh(&op->bcm_rx_update_lock); @@ -1743,10 +1771,21 @@ static void bcm_notify(struct bcm_sock *bo, unsigned long msg, lock_sock(sk); /* rx_ops: remove device specific receive entries */ - list_for_each_entry(op, &bo->rx_ops, list) + list_for_each_entry(op, &bo->rx_ops, list) { if (op->rx_reg_dev == dev) bcm_rx_unreg(dev, op); + /* release an ANYDEV op's claim (see bcm_rx_handler()) + * on this now confirmed-gone interface. + */ + if (!op->ifindex) { + spin_lock_bh(&op->bcm_rx_update_lock); + if (op->if_detected == dev->ifindex) + op->if_detected = 0; + spin_unlock_bh(&op->bcm_rx_update_lock); + } + } + /* tx_ops: stop device specific cyclic transmissions on the * vanishing ifindex. Cancelling the timer is enough to stop * cyclic bcm_can_tx() calls as there is no re-arming. From 59bfddea64159594feb62ef11b7d7a33c8ee3783 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:29 +0200 Subject: [PATCH 2506/2758] can: bcm: validate frame length in bcm_rx_setup() for RTR replies commit 62ec41f364648be79d54d94d0d240ee326948afd upstream. bcm_tx_setup() validates cf->len against the CAN/CAN FD DLC limits before installing frames for TX_SETUP, but bcm_rx_setup() never did the same for the RTR-reply frame configured via RX_SETUP with RX_RTR_FRAME. Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol") Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-7-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- net/can/bcm.c | 59 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index 491da4189c3c0..81ac87588e1a7 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -1276,22 +1276,37 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, return err; } -static void bcm_rx_setup_rtr_check(struct bcm_msg_head *msg_head, - struct bcm_op *op, void *new_frames) +static int bcm_rx_setup_rtr_check(struct bcm_msg_head *msg_head, + struct bcm_op *op, void *new_frames) { + struct canfd_frame *frame0 = new_frames; + + if (!(msg_head->flags & RX_RTR_FRAME)) + return 0; + + /* this frame is sent out as-is by bcm_can_tx() whenever a matching + * remote request is received, so validate its length the same way + * bcm_tx_setup() validates TX_SETUP frames before installing it + */ + if (msg_head->flags & CAN_FD_FRAME) { + if (frame0->len > 64) + return -EINVAL; + } else { + if (frame0->len > 8) + return -EINVAL; + } + /* funny feature in RX(!)_SETUP only for RTR-mode: * copy can_id into frame BUT without RTR-flag to * prevent a full-load-loopback-test ... ;-] * normalize this on the staged buffer, before it is * ever installed into op->frames. */ - if (msg_head->flags & RX_RTR_FRAME) { - struct canfd_frame *frame0 = new_frames; + if ((msg_head->flags & TX_CP_CAN_ID) || + frame0->can_id == op->can_id) + frame0->can_id = op->can_id & ~CAN_RTR_FLAG; - if ((msg_head->flags & TX_CP_CAN_ID) || - frame0->can_id == op->can_id) - frame0->can_id = op->can_id & ~CAN_RTR_FLAG; - } + return 0; } /* @@ -1355,7 +1370,11 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, return err; } - bcm_rx_setup_rtr_check(msg_head, op, new_frames); + err = bcm_rx_setup_rtr_check(msg_head, op, new_frames); + if (err < 0) { + kfree(new_frames); + return err; + } } spin_lock_bh(&op->bcm_rx_update_lock); @@ -1436,16 +1455,12 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, if (msg_head->nframes) { err = memcpy_from_msg(op->frames, msg, msg_head->nframes * op->cfsiz); - if (err < 0) { - if (op->frames != &op->sframe) - kfree(op->frames); - if (op->last_frames != &op->last_sframe) - kfree(op->last_frames); - kfree(op); - return err; - } + if (err < 0) + goto free_op; - bcm_rx_setup_rtr_check(msg_head, op, op->frames); + err = bcm_rx_setup_rtr_check(msg_head, op, op->frames); + if (err < 0) + goto free_op; } /* bcm_can_tx / bcm_tx_timeout_handler needs this */ @@ -1562,6 +1577,14 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, } return msg_head->nframes * op->cfsiz + MHSIZ; + +free_op: + if (op->frames != &op->sframe) + kfree(op->frames); + if (op->last_frames != &op->last_sframe) + kfree(op->last_frames); + kfree(op); + return err; } /* From 04d23061bbf18d5d81022eb21e9d32e99d24468d Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:30 +0200 Subject: [PATCH 2507/2758] can: bcm: add missing device refcount for CAN filter removal commit d59948293ea34b6337ce2b5febab8510de70048c upstream. sashiko-bot remarked a problem with a concurrent device unregistration in isotp.c which also is present in the bcm.c code. A former fix for raw.c commit c275a176e4b6 ("can: raw: add missing refcount for memory leak fix") introduced a netdevice_tracker which solves the issue for bcm.c too. bcm_release(), bcm_delete_rx_op() and bcm_notifier() relied on dev_get_by_index(ifindex) to re-find the device for an rx_op before unregistering its filter. If a concurrent NETDEV_UNREGISTER has already unlisted the device from the ifindex table, that lookup fails and can_rx_unregister() is silently skipped, leaving a stale CAN filter pointing at the soon-to-be-freed bcm_op/socket. Hold a netdev_hold()/netdev_put() tracked reference on op->rx_reg_dev from the moment the rx filter is registered in bcm_rx_setup() until it is unregistered in bcm_rx_unreg(), and use that reference directly in bcm_release() and bcm_delete_rx_op() instead of re-looking the device up by ifindex. Reported-by: sashiko-bot@kernel.org Closes: https://sashiko.dev/#/patchset/20260707094716.63578-1-socketcan@hartkopp.net Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol") Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-8-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- net/can/bcm.c | 47 +++++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index 81ac87588e1a7..3d637a1e0ac1a 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -129,6 +129,7 @@ struct bcm_op { struct canfd_frame last_sframe; struct sock *sk; struct net_device *rx_reg_dev; + netdevice_tracker rx_reg_dev_tracker; spinlock_t bcm_tx_lock; /* protect tx data and timer updates */ spinlock_t bcm_rx_update_lock; /* protect filter/timer data updates */ }; @@ -967,6 +968,7 @@ static void bcm_rx_unreg(struct net_device *dev, struct bcm_op *op) /* mark as removed subscription */ op->rx_reg_dev = NULL; + netdev_put(dev, &op->rx_reg_dev_tracker); } else printk(KERN_ERR "can-bcm: bcm_rx_unreg: registered device " "mismatch %p %p\n", op->rx_reg_dev, dev); @@ -997,17 +999,14 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh, * Only remove subscriptions that had not * been removed due to NETDEV_UNREGISTER * in bcm_notifier() + * + * op->rx_reg_dev is a tracked reference taken + * when the subscription was registered, so it + * stays valid here even if a concurrent + * NETDEV_UNREGISTER already unlisted the dev. */ - if (op->rx_reg_dev) { - struct net_device *dev; - - dev = dev_get_by_index(sock_net(op->sk), - op->ifindex); - if (dev) { - bcm_rx_unreg(dev, op); - dev_put(dev); - } - } + if (op->rx_reg_dev) + bcm_rx_unreg(op->rx_reg_dev, op); } else can_rx_unregister(sock_net(op->sk), NULL, op->can_id, @@ -1539,7 +1538,17 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, bcm_rx_handler, op, "bcm", sk); - op->rx_reg_dev = dev; + /* keep a tracked reference so that a later + * unregister can safely reach the device even + * if a concurrent NETDEV_UNREGISTER has + * already unlisted it by ifindex + */ + if (!err) { + op->rx_reg_dev = dev; + netdev_hold(dev, + &op->rx_reg_dev_tracker, + GFP_KERNEL); + } dev_put(dev); } else { /* the requested device is gone - do not @@ -1949,16 +1958,14 @@ static int bcm_release(struct socket *sock) * Only remove subscriptions that had not * been removed due to NETDEV_UNREGISTER * in bcm_notifier() + * + * op->rx_reg_dev is a tracked reference taken + * when the subscription was registered, so it + * stays valid here even if a concurrent + * NETDEV_UNREGISTER already unlisted the device. */ - if (op->rx_reg_dev) { - struct net_device *dev; - - dev = dev_get_by_index(net, op->ifindex); - if (dev) { - bcm_rx_unreg(dev, op); - dev_put(dev); - } - } + if (op->rx_reg_dev) + bcm_rx_unreg(op->rx_reg_dev, op); } else can_rx_unregister(net, NULL, op->can_id, REGMASK(op->can_id), From 0639ea767fe04c288a8d6cb826100fe3d95d4936 Mon Sep 17 00:00:00 2001 From: Woraphat Khiaodaeng Date: Fri, 17 Jul 2026 22:45:37 +0700 Subject: [PATCH 2508/2758] io_uring/bpf-ops: reject re-registration of an already-bound ops commit 3afc64c61ce906a04f073ca350b46de10e8302f9 upstream. io_install_bpf() only rejects a second registration on the ctx side (ctx->bpf_ops) and sets the per-map back-pointer ops->priv unconditionally. The struct_ops link path never advances a map past BPF_STRUCT_OPS_STATE_READY, so the same io_uring_bpf_ops map can be registered more than once, and bpf_io_reg() re-resolves the target ring via fget(ops->ring_fd) on every call. A caller can therefore point the same ring_fd at a different io_ring_ctx between two BPF_LINK_CREATE calls. The second registration passes the ctx->bpf_ops check (the new ctx has none) and overwrites ops->priv, orphaning the first ctx. Teardown (io_eject_bpf()/bpf_io_unreg()) only reaches a ctx through ops->priv, so the orphaned ctx is never torn down: its ctx->loop_step keeps pointing into the struct_ops trampoline, which is freed once the map is gone. A later io_uring_enter() on the orphaned ring then calls the dangling ctx->loop_step from io_run_loop() -- a use-after-free of freed executable memory, reachable by a task with CAP_BPF + CAP_PERFMON. Reject registration when ops->priv is already set, as hid_bpf_reg() does for its struct_ops. Cc: stable@vger.kernel.org Fixes: 98f37634b12b ("io_uring/bpf-ops: implement bpf ops registration") Signed-off-by: Woraphat Khiaodaeng Reviewed-by: Gabriel Krisman Bertazi Reviewed-by: Pavel Begunkov Link: https://patch.msgid.link/20260717154537.129736-1-worapat.kd2@gmail.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- io_uring/bpf-ops.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/io_uring/bpf-ops.c b/io_uring/bpf-ops.c index 937e48bef40bc..bc27584609236 100644 --- a/io_uring/bpf-ops.c +++ b/io_uring/bpf-ops.c @@ -165,6 +165,8 @@ static int io_install_bpf(struct io_ring_ctx *ctx, struct io_uring_bpf_ops *ops) if (ctx->bpf_ops) return -EBUSY; + if (ops->priv) + return -EBUSY; if (WARN_ON_ONCE(!ops->loop_step)) return -EINVAL; From 5a55f9aecc08990940e70f0c7048a80850c5a16a Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Mon, 22 Jun 2026 23:01:22 +0000 Subject: [PATCH 2509/2758] bpf: Reset register bounds before narrowing retval range in check_mem_access() commit 5e0b273e0a62cc04ec338c7b502797c66c2ed42a upstream. When the BPF verifier processes a context load of an LSM hook return value, it calls __mark_reg_s32_range() to narrow the register to the hook's valid range. However, __mark_reg_s32_range() intersects the new range with the register's existing bounds using max_t()/min_t() rather than replacing them. If the destination register carries stale bounds from a prior instruction (e.g. BPF_MOV64_IMM), the intersection can produce a range narrower than reality. The verifier then believes it knows the register's exact value, while at runtime the actual hook return value is loaded, creating a verifier/runtime mismatch that can be used to bypass BPF memory safety checks. The else branch already calls mark_reg_unknown() to reset register state before any narrowing. Apply the same reset in the is_retval path so stale bounds are cleared before __mark_reg_s32_range() intersects. Fixes: 5d99e198be27 ("bpf, lsm: Add check for BPF LSM return value") Cc: stable@vger.kernel.org Signed-off-by: Tristan Madani Acked-by: Eduard Zingerman Link: https://lore.kernel.org/r/20260622230123.3695446-2-tristmd@gmail.com Signed-off-by: Alexei Starovoitov Signed-off-by: Greg Kroah-Hartman --- kernel/bpf/verifier.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index e3a4fdc8acd92..52e405a64afe2 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -6454,6 +6454,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn */ if (info.reg_type == SCALAR_VALUE) { if (info.is_retval && get_func_retval_range(env->prog, &range)) { + mark_reg_unknown(env, regs, value_regno); err = __mark_reg_s32_range(env, regs, value_regno, range.minval, range.maxval); if (err) From 43f0005f81b8ce3be962d653cde8db9022f1e9b0 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Fri, 26 Jun 2026 17:52:52 +0200 Subject: [PATCH 2510/2758] bpf,fork: wipe ->bpf_storage before bailouts that access it commit 9b51a6155d14389876916726430da30eabb1d4ed upstream. Currently, copy_process() can bail out to free_task() before p->bpf_storage has been initialized, with this call graph (shown here for the !CONFIG_MEMCG case): copy_process dup_task_struct arch_dup_task_struct [copies the entire task_struct, including ->bpf_storage member] [RLIMIT_NPROC check fails] delayed_free_task free_task bpf_task_storage_free rcu_dereference(task->bpf_storage) bpf_local_storage_destroy In this case, the nascent task's ->bpf_storage member that bpf_local_storage_destroy() operates on is a plain copy of the parent's ->bpf_storage pointer, not a real initialized pointer. This leads to badness (kernel hangs, UAF). This is reachable as long as the process calling fork() has been inserted into a task storage map. Cc: stable@kernel.org Fixes: a10787e6d58c ("bpf: Enable task local storage for tracing programs") Signed-off-by: Jann Horn Signed-off-by: Andrii Nakryiko Signed-off-by: Greg Kroah-Hartman --- kernel/fork.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/kernel/fork.c b/kernel/fork.c index 145bcc4c8e125..5a4623b058a20 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1004,6 +1004,11 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node) tsk->mm_cid.active = 0; INIT_HLIST_NODE(&tsk->mm_cid.node); #endif + +#ifdef CONFIG_BPF_SYSCALL + RCU_INIT_POINTER(tsk->bpf_storage, NULL); + tsk->bpf_ctx = NULL; +#endif return tsk; free_stack: @@ -2223,10 +2228,6 @@ __latent_entropy struct task_struct *copy_process( p->sequential_io = 0; p->sequential_io_avg = 0; #endif -#ifdef CONFIG_BPF_SYSCALL - RCU_INIT_POINTER(p->bpf_storage, NULL); - p->bpf_ctx = NULL; -#endif unwind_task_init(p); From 28ce7bcf8a29aa395b60764df4c97be745de89d0 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 16 Jun 2026 10:30:56 +0200 Subject: [PATCH 2511/2758] bpf: Add missing access_ok call to copy_user_syms commit d5dc200c3a3f217de072af269dd90adddf90e48d upstream. As reported by sashiko we use __get_user without prior access_ok call on the user space pointer. Adding the missing call for the whole pointer array. Plus removing the err check in the error path, because it's not needed and also we can return -ENOMEM directly from the first kvmalloc_array fail path. Cc: stable@vger.kernel.org [1] https://lore.kernel.org/bpf/20260611115503.AC16D1F00893@smtp.kernel.org/ Fixes: 0236fec57a15 ("bpf: Resolve symbols with ftrace_lookup_symbols for kprobe multi link") Reported-by: Sashiko Closes: https://lore.kernel.org/bpf/20260611115503.AC16D1F00893@smtp.kernel.org/ Signed-off-by: Jiri Olsa Reviewed-by: Emil Tsalapatis Link: https://lore.kernel.org/r/20260616083056.405652-1-jolsa@kernel.org Signed-off-by: Alexei Starovoitov Signed-off-by: Greg Kroah-Hartman --- kernel/trace/bpf_trace.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index a4de502950c4b..91309df691840 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -2331,9 +2331,12 @@ static int copy_user_syms(struct user_syms *us, unsigned long __user *usyms, u32 int err = -ENOMEM; unsigned int i; + if (!access_ok(usyms, cnt * sizeof(*usyms))) + return -EFAULT; + syms = kvmalloc_array(cnt, sizeof(*syms), GFP_KERNEL); if (!syms) - goto error; + return -ENOMEM; buf = kvmalloc_array(cnt, KSYM_NAME_LEN, GFP_KERNEL); if (!buf) @@ -2358,10 +2361,8 @@ static int copy_user_syms(struct user_syms *us, unsigned long __user *usyms, u32 return 0; error: - if (err) { - kvfree(syms); - kvfree(buf); - } + kvfree(syms); + kvfree(buf); return err; } From fe1d9121b4b75154f28e1940abf28323c85afc41 Mon Sep 17 00:00:00 2001 From: Sun Jian Date: Tue, 14 Jul 2026 02:38:46 -0700 Subject: [PATCH 2512/2758] selftests/bpf: Cover negative buffer pointer offsets commit 6f59deb32efa4673fc3b1fef9f3a0da48e9d8494 upstream. Add verifier coverage for constant negative offsets on PTR_TO_TP_BUFFER and PTR_TO_BUF pointers. Both programs adjust the buffer pointer by -8 and access it at offset zero, so the negative effective start must be rejected at load time. Switch the raw tracepoint writable attach checks from nbd_send_request to bpf_testmod_test_writable_bare_tp, avoiding a dependency on the NBD tracepoint. Keep the existing past-end case and add a case with a negative var_off compensated by a positive instruction offset. The effective start remains non-negative, so the program loads, but its access end exceeds the writable context size and bpf_raw_tracepoint_open() must return -EINVAL. Cc: stable@vger.kernel.org # 5.2.0 Signed-off-by: Sun Jian Acked-by: Shung-Hsi Yu Link: https://patch.msgid.link/20260714093846.18159-3-sun.jian.kdev@gmail.com Signed-off-by: Eduard Zingerman Signed-off-by: Greg Kroah-Hartman --- .../raw_tp_writable_reject_bad_access.c | 57 +++++++++++++++++++ .../raw_tp_writable_reject_nbd_invalid.c | 43 -------------- .../selftests/bpf/prog_tests/verifier.c | 2 + .../selftests/bpf/progs/verifier_ptr_to_buf.c | 27 +++++++++ .../bpf/progs/verifier_raw_tp_writable.c | 16 ++++++ 5 files changed, 102 insertions(+), 43 deletions(-) create mode 100644 tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_bad_access.c delete mode 100644 tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c create mode 100644 tools/testing/selftests/bpf/progs/verifier_ptr_to_buf.c diff --git a/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_bad_access.c b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_bad_access.c new file mode 100644 index 0000000000000..b8538fc4fc3f6 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_bad_access.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include "test_kmods/bpf_testmod.h" +#include "bpf_util.h" + +static void check_attach_reject(const struct bpf_insn *program, size_t prog_len) +{ + LIBBPF_OPTS(bpf_prog_load_opts, opts); + char error[4096]; + int bpf_fd, tp_fd; + + opts.log_level = 2; + opts.log_buf = error; + opts.log_size = sizeof(error); + + bpf_fd = bpf_prog_load(BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, NULL, "GPL v2", + program, prog_len, &opts); + if (!ASSERT_GE(bpf_fd, 0, "prog_load")) + return; + + tp_fd = bpf_raw_tracepoint_open("bpf_testmod_test_writable_bare_tp", bpf_fd); + ASSERT_EQ(tp_fd, -EINVAL, "bpf_raw_tracepoint_open"); + if (tp_fd >= 0) + close(tp_fd); + + close(bpf_fd); +} + +void test_raw_tp_writable_reject_bad_access(void) +{ + const struct bpf_insn program[] = { + /* r6 is our tp buffer */ + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), + /* one byte beyond the end of the writable context */ + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_6, + sizeof(struct bpf_testmod_test_writable_ctx)), + BPF_EXIT_INSN(), + }; + + const struct bpf_insn negative_var_off_program[] = { + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), + /* make var_off negative, but keep the effective access offset non-negative */ + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), + /* one byte beyond the end of the writable context */ + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_6, + sizeof(struct bpf_testmod_test_writable_ctx) + 8), + BPF_EXIT_INSN(), + }; + + if (test__start_subtest("past_end")) + check_attach_reject(program, ARRAY_SIZE(program)); + + if (test__start_subtest("negative_var_off_past_end")) + check_attach_reject(negative_var_off_program, + ARRAY_SIZE(negative_var_off_program)); +} diff --git a/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c deleted file mode 100644 index 216b0dfac0fe6..0000000000000 --- a/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include -#include -#include "bpf_util.h" - -void test_raw_tp_writable_reject_nbd_invalid(void) -{ - __u32 duration = 0; - char error[4096]; - int bpf_fd = -1, tp_fd = -1; - - const struct bpf_insn program[] = { - /* r6 is our tp buffer */ - BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), - /* one byte beyond the end of the nbd_request struct */ - BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_6, - sizeof(struct nbd_request)), - BPF_EXIT_INSN(), - }; - - LIBBPF_OPTS(bpf_prog_load_opts, opts, - .log_level = 2, - .log_buf = error, - .log_size = sizeof(error), - ); - - bpf_fd = bpf_prog_load(BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, NULL, "GPL v2", - program, ARRAY_SIZE(program), - &opts); - if (CHECK(bpf_fd < 0, "bpf_raw_tracepoint_writable load", - "failed: %d errno %d\n", bpf_fd, errno)) - return; - - tp_fd = bpf_raw_tracepoint_open("nbd_send_request", bpf_fd); - if (CHECK(tp_fd >= 0, "bpf_raw_tracepoint_writable open", - "erroneously succeeded\n")) - goto out_bpffd; - - close(tp_fd); -out_bpffd: - close(bpf_fd); -} diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c index 06cd24e37b3f8..e67140c24b742 100644 --- a/tools/testing/selftests/bpf/prog_tests/verifier.c +++ b/tools/testing/selftests/bpf/prog_tests/verifier.c @@ -77,6 +77,7 @@ #include "verifier_precision.skel.h" #include "verifier_prevent_map_lookup.skel.h" #include "verifier_private_stack.skel.h" +#include "verifier_ptr_to_buf.skel.h" #include "verifier_raw_stack.skel.h" #include "verifier_raw_tp_writable.skel.h" #include "verifier_reg_equal.skel.h" @@ -225,6 +226,7 @@ void test_verifier_or_jmp32_k(void) { RUN(verifier_or_jmp32_k); } void test_verifier_precision(void) { RUN(verifier_precision); } void test_verifier_prevent_map_lookup(void) { RUN(verifier_prevent_map_lookup); } void test_verifier_private_stack(void) { RUN(verifier_private_stack); } +void test_verifier_ptr_to_buf(void) { RUN(verifier_ptr_to_buf); } void test_verifier_raw_stack(void) { RUN(verifier_raw_stack); } void test_verifier_raw_tp_writable(void) { RUN(verifier_raw_tp_writable); } void test_verifier_reg_equal(void) { RUN(verifier_reg_equal); } diff --git a/tools/testing/selftests/bpf/progs/verifier_ptr_to_buf.c b/tools/testing/selftests/bpf/progs/verifier_ptr_to_buf.c new file mode 100644 index 0000000000000..12cf24db46a0a --- /dev/null +++ b/tools/testing/selftests/bpf/progs/verifier_ptr_to_buf.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include "bpf_misc.h" + +SEC("iter/bpf_map_elem") +__description("PTR_TO_BUF: reject negative const offset") +__failure +__msg("invalid negative rdwr buffer offset") +__naked void ptr_to_buf_reject_negative_const_offset(void) +{ + asm volatile ("r0 = 0; \ + r2 = *(u64 *)(r1 + %[value_off]); \ + if r2 == 0 goto l0_%=; \ + r2 += -8; \ + r0 = *(u64 *)(r2 + 0); \ +l0_%=: \ + exit; \ + " + : + : __imm_const(value_off, + offsetof(struct bpf_iter__bpf_map_elem, value)) + : __clobber_all); +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/verifier_raw_tp_writable.c b/tools/testing/selftests/bpf/progs/verifier_raw_tp_writable.c index 14a0172e2141e..4055a6443bc20 100644 --- a/tools/testing/selftests/bpf/progs/verifier_raw_tp_writable.c +++ b/tools/testing/selftests/bpf/progs/verifier_raw_tp_writable.c @@ -47,4 +47,20 @@ l0_%=: /* shift the buffer pointer to a variable location */\ : __clobber_all); } +SEC("raw_tracepoint.w") +__description("raw_tracepoint_writable: reject negative const offset") +__failure +__msg("invalid negative tracepoint buffer offset") +__naked void tracepoint_writable_reject_negative_const_offset(void) +{ + asm volatile (" \ + r6 = *(u64 *)(r1 + 0); \ + r6 += -8; \ + r0 = *(u64 *)(r6 + 0); \ + exit; \ +" : + : + : __clobber_all); +} + char _license[] SEC("license") = "GPL"; From 02f8ad12545cb05656a5a20bc52f41f0367ab3e6 Mon Sep 17 00:00:00 2001 From: Connor Williamson Date: Mon, 15 Jun 2026 13:07:15 +0000 Subject: [PATCH 2513/2758] block: remove redundant GD_NEED_PART_SCAN in add_disk_final() commit 181bb9c9eae4f69fe510a62a42c2932d0314a800 upstream. add_disk_final() sets GD_NEED_PART_SCAN before calling bdev_add(), then calls disk_scan_partitions() which sets the flag itself. The early set is redundant and introduces a race. Between bdev_add() and disk_scan_partitions(), concurrent openers (multipathd, blkid, LVM) see the flag in blkdev_get_whole() and trigger bdev_disk_changed(). When disk_scan_partitions() then runs, it calls bdev_disk_changed() again, dropping the partitions the concurrent opener already created before re-adding them, which can result in transient partition disappearances. The race is observable by inserting an msleep() between bdev_add() and disk_scan_partitions() while running concurrent open() calls during device bind. Without artificial delay, it manifests under scheduling pressure during boot on systems with aggressive device scanners (multipathd, systemd-udevd). Therefore, do not set GD_NEED_PART_SCAN in add_disk_final(). Other GD_NEED_PART_SCAN consumers (blkdev_get_whole(), sd_need_revalidate()) should not be affected as the flag is set internally by disk_scan_partitions(). The retry-on-next-open intention from commit e5cfefa97bcc ("block: fix scan partition for exclusively open device again") should also not be affected as the early return paths in disk_scan_partitions() should be unreachable at device registration time (bd_holder is NULL and open_partitions is zero). Fixes: e5cfefa97bcc ("block: fix scan partition for exclusively open device again") Cc: stable@vger.kernel.org Signed-off-by: Connor Williamson Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260615130715.53693-1-connordw@amazon.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/genhd.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/block/genhd.c b/block/genhd.c index 7d6854fd28e95..0da6cdf3d5fb0 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -407,10 +407,6 @@ static void add_disk_final(struct gendisk *disk) struct device *ddev = disk_to_dev(disk); if (!(disk->flags & GENHD_FL_HIDDEN)) { - /* Make sure the first partition scan will be proceed */ - if (get_capacity(disk) && disk_has_partscan(disk)) - set_bit(GD_NEED_PART_SCAN, &disk->state); - bdev_add(disk->part0, ddev->devt); if (get_capacity(disk)) disk_scan_partitions(disk, BLK_OPEN_READ); From c0f10f43ffa5d5feaddc62df1de553bb50d01e27 Mon Sep 17 00:00:00 2001 From: Mike Waychison Date: Wed, 15 Jul 2026 15:29:50 -0400 Subject: [PATCH 2514/2758] block: fix race in blk_time_get_ns() returning 0 commit 691b052139c94ee6640ac39e0b764dd3867897c0 upstream. blk_time_get_ns() populates the per-plug cached timestamp and then returns it by re-reading the field: if (!plug->cur_ktime) { plug->cur_ktime = ktime_get_ns(); current->flags |= PF_BLOCK_TS; } return plug->cur_ktime; This is problematic when the compiler emits the final "return plug->cur_ktime" as a reload from memory, after PF_BLOCK_TS has already been set. Since the cached timestamp is now invalidated from finish_task_switch() (fad156c2af22 "block: invalidate cached plug timestamp after task switch"), a task preempted between setting PF_BLOCK_TS and that reload has plug->cur_ktime zeroed by blk_plug_invalidate_ts() when it is scheduled back in. The reload then returns 0. A 0 handed back here is stored as a start timestamp -- e.g. blk_account_io_start() writes it to rq->start_time_ns -- and later subtracted from "now". blk_account_io_done() then adds (now - 0), i.e. roughly the system uptime, to the per-group nsecs[] counters. On an otherwise idle, healthy device this appears as sudden ~uptime-sized jumps in the diskstats time fields (write_ticks/discard_ticks/time_in_queue). The solution is to be explicit in our reads and writes to this field that is preemption volatile. We also add a barrier() to ensure that any setting of PF_BLOCK_TS is ordered to happen after the cur_ktime update. This issue was discovered using AI-assisted kprobes looking for paths that were leaking zeroed timestamps in a live system, based on the observation that we were sometimes seeing uptime-sized jumps in kernel exported counters. This was flagged by NodeDiskIOSaturation prometheus alerts that started firing on all hosts post 7.1.3 kernel upgrade, due to node-exporter now exporting a nonsensical node_disk_io_time_weighted_seconds_total. Fixes: fad156c2af22 ("block: invalidate cached plug timestamp after task switch") Cc: stable@vger.kernel.org Signed-off-by: Mike Waychison Assisted-by: Claude:claude-opus-4.8 Link: https://patch.msgid.link/20260715192950.2488921-1-mike@waychison.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/blk.h | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/block/blk.h b/block/blk.h index b998a7761faf3..17e03656ba9ac 100644 --- a/block/blk.h +++ b/block/blk.h @@ -689,6 +689,7 @@ static inline int req_ref_read(struct request *req) static inline u64 blk_time_get_ns(void) { struct blk_plug *plug = current->plug; + u64 now; if (!plug || !in_task()) return ktime_get_ns(); @@ -697,12 +698,18 @@ static inline u64 blk_time_get_ns(void) * 0 could very well be a valid time, but rather than flag "this is * a valid timestamp" separately, just accept that we'll do an extra * ktime_get_ns() if we just happen to get 0 as the current time. + * + * cur_ktime can be zeroed by pre-emption the moment PF_BLOCK_TS is set. */ - if (!plug->cur_ktime) { - plug->cur_ktime = ktime_get_ns(); + now = READ_ONCE(plug->cur_ktime); + if (!now) { + now = ktime_get_ns(); + WRITE_ONCE(plug->cur_ktime, now); + /* Ensure PF_BLOCK_TS is set after cur_ktime. */ + barrier(); current->flags |= PF_BLOCK_TS; } - return plug->cur_ktime; + return now; } static inline ktime_t blk_time_get(void) From d4cc255f35d5f47dfd878f2270cb605443a087da Mon Sep 17 00:00:00 2001 From: Yitang Yang Date: Tue, 16 Jun 2026 23:51:29 +0800 Subject: [PATCH 2515/2758] block: fix IORING_URING_CMD_REISSUE flags check in blkdev_uring_cmd commit 4f919141be38ea2b1314e3a531b7b998eb64e8bc upstream. blkdev_uring_cmd() checks IORING_URING_CMD_REISSUE to determine whether this is the first issue. However, this flag lives in cmd->flags instead of issue_flags. Coincidentally, IO_URING_F_NONBLOCK shares bit 31 with IORING_URING_CMD_REISSUE. As a result, the SQE read was never performed, bic->len remained zero, and every BLOCK_URING_CMD_DISCARD failed with -EINVAL. Fix it by checking cmd->flags as intended. Cc: stable@vger.kernel.org Fixes: 212ec34e4e72 ("block: only read from sqe on initial invocation of blkdev_uring_cmd") Signed-off-by: Yitang Yang Link: https://patch.msgid.link/20260616155129.406057-1-yi1tang.yang@gmail.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/ioctl.c b/block/ioctl.c index ab2c9ed799468..3d4ea1537457d 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -951,7 +951,7 @@ int blkdev_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags) u32 cmd_op = cmd->cmd_op; /* Read what we need from the SQE on the first issue */ - if (!(issue_flags & IORING_URING_CMD_REISSUE)) { + if (!(cmd->flags & IORING_URING_CMD_REISSUE)) { const struct io_uring_sqe *sqe = cmd->sqe; if (unlikely(sqe->ioprio || sqe->__pad1 || sqe->len || From 17f113e7b622dc850992ade540181717de6a8561 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Tue, 23 Jun 2026 19:57:14 +0800 Subject: [PATCH 2516/2758] net: sparx5: unregister blocking notifier on init failure commit 483be61b4a9a6df3b7cb277e8f189e082dee4cb8 upstream. sparx5_register_notifier_blocks() registers the switchdev blocking notifier before allocating the ordered workqueue. If the workqueue allocation fails, the error path unregisters the switchdev and netdevice notifiers, but leaves the blocking notifier registered. Add a separate error label for the workqueue allocation failure path and unregister the switchdev blocking notifier there. Fixes: d6fce5141929 ("net: sparx5: add switching support") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260623115714.2192074-1-haoxiang_li2024@163.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c index 644458108dd20..dac4dd833127a 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c @@ -765,11 +765,13 @@ int sparx5_register_notifier_blocks(struct sparx5 *s5) sparx5_owq = alloc_ordered_workqueue("sparx5_order", 0); if (!sparx5_owq) { err = -ENOMEM; - goto err_switchdev_blocking_nb; + goto err_alloc_workqueue; } return 0; +err_alloc_workqueue: + unregister_switchdev_blocking_notifier(&s5->switchdev_blocking_nb); err_switchdev_blocking_nb: unregister_switchdev_notifier(&s5->switchdev_nb); err_switchdev_nb: From 0562bd39d361eaefeaf8bed2926a348bfbe4d83f Mon Sep 17 00:00:00 2001 From: Genjian Zhang Date: Sat, 11 Jul 2026 18:05:26 +0800 Subject: [PATCH 2517/2758] dm thin metadata: fix superblock refcount leak on snapshot shadow failure commit 4b22d0801fadfcae2e106e6ba32e49439c7c7ebf upstream. __reserve_metadata_snap() increments THIN_SUPERBLOCK_LOCATION in the metadata space map before shadowing it. When dm_tm_shadow_block() fails, a reference is leaked in the metadata space map. Fix by adding the missing dm_sm_dec_block(). Signed-off-by: Genjian Zhang Signed-off-by: Mikulas Patocka Fixes: cc8394d86f04 ("dm thin: provide userspace access to pool metadata") Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-thin-metadata.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c index b6a2d2081a24b..b194fb57d1586 100644 --- a/drivers/md/dm-thin-metadata.c +++ b/drivers/md/dm-thin-metadata.c @@ -1353,8 +1353,10 @@ static int __reserve_metadata_snap(struct dm_pool_metadata *pmd) dm_sm_inc_block(pmd->metadata_sm, THIN_SUPERBLOCK_LOCATION); r = dm_tm_shadow_block(pmd->tm, THIN_SUPERBLOCK_LOCATION, &sb_validator, ©, &inc); - if (r) + if (r) { + dm_sm_dec_block(pmd->metadata_sm, THIN_SUPERBLOCK_LOCATION); return r; + } BUG_ON(!inc); From 9f1a0d27586ceab055e6b050e3731ce3c6b2c4f0 Mon Sep 17 00:00:00 2001 From: Ming-Hung Tsai Date: Tue, 30 Jun 2026 20:17:44 +0800 Subject: [PATCH 2518/2758] dm thin metadata: fix metadata snapshot consistency on commit failure commit 5bcd4d3058ebaf46ad2e163829d87dd4870c7a45 upstream. __reserve_metadata_snap() and __release_metadata_snap() modify the superblock's held_root directly in the block_manager's buffer. If the subsequent metadata commit fails, the held_root gets flushed to disk through the abort_transaction path, resulting in inconsistent metadata. Reproducer 1: __reserve_metadata_snap() 1. Create a 2 MiB metadata device and make the region after the 14th block inaccessible, to trigger metadata commit failure in the subsequent reserve_metadata_snap operation. The 14th block will be the shadow destination for the index block. dmsetup create tmeta --table "0 112 linear /dev/sdc 0 112 3984 error" 2. Create a 16 MiB thin-pool dmsetup create tdata --table "0 32768 zero" dd if=/dev/zero of=/dev/mapper/tmeta bs=4k count=1 dmsetup create tpool --table "0 32768 thin-pool /dev/mapper/tmeta \ /dev/mapper/tdata 128 0 1 skip_block_zeroing" 3. Take a metadata snapshot to trigger metadata commit failure and transaction abort. However, the held_root is written to disk, breaking metadata consistency. dmsetup message tpool 0 "reserve_metadata_snap" thin_check v1.2.2 result: Bad reference count for metadata block 6. Expected 2, but space map contains 1. Bad reference count for metadata block 7. Expected 2, but space map contains 1. Bad reference count for metadata block 13. Expected 1, but space map contains 0. Reproducer 2: __release_metadata_snap() 1. Create a 2 MiB metadata device and make the region after the 16th block inaccessible, to trigger metadata commit failure in the subsequent release_metadata_snap operation. The 16th block will be the shadow destination for the index block. dmsetup create tmeta --table "0 128 linear /dev/sdc 0 128 3968 error" 2. Create a 16 MiB thin-pool dmsetup create tdata --table "0 32768 zero" dd if=/dev/zero of=/dev/mapper/tmeta bs=4k count=1 dmsetup create tpool --table "0 32768 thin-pool /dev/mapper/tmeta \ /dev/mapper/tdata 128 0 1 skip_block_zeroing" 3. Reserve then release the metadata snapshot, to trigger metadata commit failure and transaction abort. The held_root gets removed from the on-disk superblock, causing inconsistent metadata. dmsetup message tpool 0 "reserve_metadata_snap" dmsetup message tpool 0 "release_metadata_snap" thin_check v1.2.2 result: Bad reference count for metadata block 6. Expected 1, but space map contains 2. Bad reference count for metadata block 7. Expected 1, but space map contains 2. 1 metadata blocks have leaked. Fix by deferring the held_root update to commit time. Additionally, move the existing-snapshot check in __reserve_metadata_snap before the shadow operation to avoid unnecessary work. In __release_metadata_snap, clear pmd->held_root before btree deletion so partial failure leaks blocks rather than leaving a stale reference, and unlock the snapshot block before decrementing its refcount. Fixes: 991d9fa02da0 ("dm: add thin provisioning target") Cc: stable@vger.kernel.org Signed-off-by: Ming-Hung Tsai Signed-off-by: Mikulas Patocka Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-thin-metadata.c | 63 ++++++++++------------------------- 1 file changed, 18 insertions(+), 45 deletions(-) diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c index b194fb57d1586..e60e1326376a0 100644 --- a/drivers/md/dm-thin-metadata.c +++ b/drivers/md/dm-thin-metadata.c @@ -186,6 +186,7 @@ struct dm_pool_metadata { uint32_t time; dm_block_t root; dm_block_t details_root; + dm_block_t held_root; struct list_head thin_devices; uint64_t trans_id; unsigned long flags; @@ -748,6 +749,7 @@ static int __open_metadata(struct dm_pool_metadata *pmd) */ pmd->root = le64_to_cpu(disk_super->data_mapping_root); pmd->details_root = le64_to_cpu(disk_super->device_details_root); + pmd->held_root = le64_to_cpu(disk_super->held_root); __setup_btree_details(pmd); dm_bm_unlock(sblock); @@ -838,6 +840,7 @@ static int __begin_transaction(struct dm_pool_metadata *pmd) pmd->time = le32_to_cpu(disk_super->time); pmd->root = le64_to_cpu(disk_super->data_mapping_root); pmd->details_root = le64_to_cpu(disk_super->device_details_root); + pmd->held_root = le64_to_cpu(disk_super->held_root); pmd->trans_id = le64_to_cpu(disk_super->trans_id); pmd->flags = le32_to_cpu(disk_super->flags); pmd->data_block_size = le32_to_cpu(disk_super->data_block_size); @@ -928,6 +931,7 @@ static int __commit_transaction(struct dm_pool_metadata *pmd) disk_super->time = cpu_to_le32(pmd->time); disk_super->data_mapping_root = cpu_to_le64(pmd->root); disk_super->device_details_root = cpu_to_le64(pmd->details_root); + disk_super->held_root = cpu_to_le64(pmd->held_root); disk_super->trans_id = cpu_to_le64(pmd->trans_id); disk_super->flags = cpu_to_le32(pmd->flags); @@ -1333,9 +1337,14 @@ static int __reserve_metadata_snap(struct dm_pool_metadata *pmd) { int r, inc; struct thin_disk_superblock *disk_super; - struct dm_block *copy, *sblock; + struct dm_block *copy; dm_block_t held_root; + if (pmd->held_root) { + DMWARN("Pool metadata snapshot already exists: release this before taking another."); + return -EBUSY; + } + /* * We commit to ensure the btree roots which we increment in a * moment are up to date. @@ -1363,14 +1372,6 @@ static int __reserve_metadata_snap(struct dm_pool_metadata *pmd) held_root = dm_block_location(copy); disk_super = dm_block_data(copy); - if (le64_to_cpu(disk_super->held_root)) { - DMWARN("Pool metadata snapshot already exists: release this before taking another."); - - dm_tm_dec(pmd->tm, held_root); - dm_tm_unlock(pmd->tm, copy); - return -EBUSY; - } - /* * Wipe the spacemap since we're not publishing this. */ @@ -1386,18 +1387,8 @@ static int __reserve_metadata_snap(struct dm_pool_metadata *pmd) dm_tm_inc(pmd->tm, le64_to_cpu(disk_super->device_details_root)); dm_tm_unlock(pmd->tm, copy); - /* - * Write the held root into the superblock. - */ - r = superblock_lock(pmd, &sblock); - if (r) { - dm_tm_dec(pmd->tm, held_root); - return r; - } + pmd->held_root = held_root; - disk_super = dm_block_data(sblock); - disk_super->held_root = cpu_to_le64(held_root); - dm_bm_unlock(sblock); return 0; } @@ -1417,18 +1408,10 @@ static int __release_metadata_snap(struct dm_pool_metadata *pmd) { int r; struct thin_disk_superblock *disk_super; - struct dm_block *sblock, *copy; + struct dm_block *copy; dm_block_t held_root; - r = superblock_lock(pmd, &sblock); - if (r) - return r; - - disk_super = dm_block_data(sblock); - held_root = le64_to_cpu(disk_super->held_root); - disk_super->held_root = cpu_to_le64(0); - - dm_bm_unlock(sblock); + held_root = pmd->held_root; if (!held_root) { DMWARN("No pool metadata snapshot found: nothing to release."); @@ -1439,13 +1422,15 @@ static int __release_metadata_snap(struct dm_pool_metadata *pmd) if (r) return r; + pmd->held_root = 0; + disk_super = dm_block_data(copy); dm_btree_del(&pmd->info, le64_to_cpu(disk_super->data_mapping_root)); dm_btree_del(&pmd->details_info, le64_to_cpu(disk_super->device_details_root)); - dm_sm_dec_block(pmd->metadata_sm, held_root); - dm_tm_unlock(pmd->tm, copy); + dm_sm_dec_block(pmd->metadata_sm, held_root); + return 0; } @@ -1464,19 +1449,7 @@ int dm_pool_release_metadata_snap(struct dm_pool_metadata *pmd) static int __get_metadata_snap(struct dm_pool_metadata *pmd, dm_block_t *result) { - int r; - struct thin_disk_superblock *disk_super; - struct dm_block *sblock; - - r = dm_bm_read_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION, - &sb_validator, &sblock); - if (r) - return r; - - disk_super = dm_block_data(sblock); - *result = le64_to_cpu(disk_super->held_root); - - dm_bm_unlock(sblock); + *result = pmd->held_root; return 0; } From bafe3e720cdac38cd7ea4eb7852a8f2dbe1bbfe6 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Thu, 2 Jul 2026 00:27:35 +0000 Subject: [PATCH 2519/2758] dm era: fix out-of-bounds memory access for non-zero start sector commit a868196f03c2b19418ae3d2b69e195d668a271e5 upstream. dm-era tracks writes in target-relative blocks, but era_map() calculates the writeset block before applying the target offset. Tables with a non-zero start sector can therefore pass an absolute mapped-device block to metadata_current_marked(). If the absolute block is beyond the current writeset size, writeset_marked() tests past the end of the in-core bitset. KASAN reports this as a vmalloc-out-of-bounds access. Apply the target offset before calculating the era block so writeset lookups use the target-relative block number. Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius Reviewed-by: Ming-Hung Tsai Signed-off-by: Mikulas Patocka Cc: stable@vger.kernel.org Fixes: eec40579d848 ("dm: add era target") Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-era-target.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/md/dm-era-target.c b/drivers/md/dm-era-target.c index 08ce96e8cf4f6..cef2881948043 100644 --- a/drivers/md/dm-era-target.c +++ b/drivers/md/dm-era-target.c @@ -1231,6 +1231,7 @@ static dm_block_t get_block(struct era *era, struct bio *bio) static void remap_to_origin(struct era *era, struct bio *bio) { bio_set_dev(bio, era->origin_dev->bdev); + bio->bi_iter.bi_sector = dm_target_offset(era->ti, bio->bi_iter.bi_sector); } /* @@ -1562,7 +1563,7 @@ static void era_dtr(struct dm_target *ti) static int era_map(struct dm_target *ti, struct bio *bio) { struct era *era = ti->private; - dm_block_t block = get_block(era, bio); + dm_block_t block; /* * All bios get remapped to the origin device. We do this now, but @@ -1570,6 +1571,7 @@ static int era_map(struct dm_target *ti, struct bio *bio) * block is marked in this era. */ remap_to_origin(era, bio); + block = get_block(era, bio); /* * REQ_PREFLUSH bios carry no data, so we're not interested in them. From 53477ce5ef9061a73650355572788db0bd8c26f6 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Fri, 10 Jul 2026 18:32:49 +0200 Subject: [PATCH 2520/2758] dm-bufio: fix wrong count calculation in dm_bufio_issue_discard commit 422f1d4f141eaa3a6e4199ceec86cc6b9bf26570 upstream. block_to_sector converts a block number to a sector number and adds c->start to the result. It is inappropriate to use this function for converting the number of blocks to a number to sectors because c->start would be incorrectly added to the result. Luckily, the only target that uses dm_bufio_issue_discard is dm-ebs, which sets c->start to 0, so this bug is latent. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4-6 Fixes: 6fbeb0048e6b ("dm bufio: implement discard") Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-bufio.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c index 26fedf5883eff..a458b9fd2fcd6 100644 --- a/drivers/md/dm-bufio.c +++ b/drivers/md/dm-bufio.c @@ -2238,7 +2238,9 @@ int dm_bufio_issue_discard(struct dm_bufio_client *c, sector_t block, sector_t c struct dm_io_region io_reg = { .bdev = c->bdev, .sector = block_to_sector(c, block), - .count = block_to_sector(c, count), + .count = likely(c->sectors_per_block_bits >= 0) ? + count << c->sectors_per_block_bits : + count * (c->block_size >> SECTOR_SHIFT), }; if (WARN_ON_ONCE(dm_bufio_in_request())) From df50c24c6447c18886ed126d3d81cc7e155ea8b6 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:31:47 +0200 Subject: [PATCH 2521/2758] dm-ioctl: fix a possible overflow in list_version_get_info commit 76c6f845dc0c614304a6e6ee619b552f97cf24b3 upstream. sizeof(tt->version) is 12 bytes, but the code writes 16 bytes into the output buffer - info->vers->version[0], info->vers->version[1], info->vers->version[2] and info->vers->next. This can cause buffer overflow. Fix this buffer overflow by replacing "sizeof(tt->version)" with "sizeof(struct dm_target_versions)". Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4.6 Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index c23c03f0cb6e4..9ab75c7a007f8 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -698,7 +698,7 @@ static void list_version_get_info(struct target_type *tt, void *param) struct vers_iter *info = param; /* Check space - it might have changed since the first iteration */ - if ((char *)info->vers + sizeof(tt->version) + strlen(tt->name) + 1 > info->end) { + if ((char *)info->vers + sizeof(struct dm_target_versions) + strlen(tt->name) + 1 > info->end) { info->flags = DM_BUFFER_FULL_FLAG; return; } From 79feb87ab2396d49b9b65e4bb815d33cc71cba47 Mon Sep 17 00:00:00 2001 From: Benjamin Marzinski Date: Thu, 2 Jul 2026 21:43:39 -0400 Subject: [PATCH 2522/2758] dm-log: fix a bitset_size overflow on 32bit machines commit 9743132a41f4d9d0e54c5f2adcb821b04796bab1 upstream. Commit c20e36b7631d ("dm log: fix out-of-bounds write due to region_count overflow") made sure that region_count could fit in an unsigned int. But the bitmap memory isn't allocated based on region_count. It uses bitset_size (a size_t variable). The first step of calculating bitset_size is to set it to region_count, rounded up to a multiple of BITS_PER_LONG. If region_size is less than BITS_PER_LONG smaller than UINT_MAX, it will get rounded up to 2^32. On a 32bit architecture, this will make bitset_size wrap around to 0 and fail, despite region_count being valid. Since bitset_size gets divided by 8, it can hold any valid region_count. It just needs a special case to handle the rollover. If it is 0, the value rolled over, and bitset size should be set to the number of bytes needed to hold 2^32 bits. Signed-off-by: Benjamin Marzinski Signed-off-by: Mikulas Patocka Fixes: c20e36b7631d ("dm log: fix out-of-bounds write due to region_count overflow") Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-log.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c index d316757a328be..2ddeb4250c592 100644 --- a/drivers/md/dm-log.c +++ b/drivers/md/dm-log.c @@ -425,6 +425,9 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti, */ bitset_size = dm_round_up(region_count, BITS_PER_LONG); bitset_size >>= BYTE_SHIFT; + /* Handle dm_round_up rollover on 32-bit systems */ + if (!bitset_size) + bitset_size = 1UL << (BITS_PER_LONG - BYTE_SHIFT); lc->bitset_uint32_count = bitset_size / sizeof(*lc->clean_bits); From 0cbe13fe540330d31e5273bbf8cbd66bc7c9cb5a Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Mon, 29 Jun 2026 15:47:40 +0000 Subject: [PATCH 2523/2758] dm-pcache: reject option groups without values commit d9c631e3fbd44246a2be781d26cfacbb9b8ec127 upstream. The pcache target parses optional arguments as name/value pairs. A table that advertises one optional argument and supplies only a recognized option name, for example "cache_mode", reaches parse_cache_opts() with argc == 1. The parser consumes the name, decrements argc to zero, then calls dm_shift_arg() again for the value. dm_shift_arg() returns NULL when no arguments remain, and the following strcmp() dereferences that NULL pointer. Check that each recognized option has a value before consuming it. This keeps valid "cache_mode writeback" and "data_crc true/false" tables unchanged while making malformed tables fail during target construction with a precise missing-value error. Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius Reviewed-by: Zheng Gu Signed-off-by: Mikulas Patocka Fixes: 1d57628ff95b ("dm-pcache: add persistent cache target in device-mapper") Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-pcache/dm_pcache.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/md/dm-pcache/dm_pcache.c b/drivers/md/dm-pcache/dm_pcache.c index 81c795c0400e9..d5cfd162c0633 100644 --- a/drivers/md/dm-pcache/dm_pcache.c +++ b/drivers/md/dm-pcache/dm_pcache.c @@ -168,6 +168,10 @@ static int parse_cache_opts(struct dm_pcache *pcache, struct dm_arg_set *as, argc--; if (!strcmp(arg, "cache_mode")) { + if (!argc) { + *error = "Missing value for cache_mode"; + return -EINVAL; + } arg = dm_shift_arg(as); if (!strcmp(arg, "writeback")) { opts->cache_mode = PCACHE_CACHE_MODE_WRITEBACK; @@ -177,6 +181,10 @@ static int parse_cache_opts(struct dm_pcache *pcache, struct dm_arg_set *as, } argc--; } else if (!strcmp(arg, "data_crc")) { + if (!argc) { + *error = "Missing value for data_crc"; + return -EINVAL; + } arg = dm_shift_arg(as); if (!strcmp(arg, "true")) { opts->data_crc = true; From f3441b3bf5199f815240edbf24278d10a1a5c5e9 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Fri, 10 Jul 2026 18:37:15 +0200 Subject: [PATCH 2524/2758] dm-stats: fix dm_jiffies_to_msec64 commit 386df1a57b631c456d14f857cb0c0c2e11c16bef upstream. There were wrong calculations in dm_jiffies_to_msec64 that produced incorrect output when HZ was different from 1000. This commit fixes them. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4-6 Fixes: fd2ed4d25270 ("dm: add statistics support") Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-stats.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/md/dm-stats.c b/drivers/md/dm-stats.c index c53cf07ab7b0b..87842d815f4bb 100644 --- a/drivers/md/dm-stats.c +++ b/drivers/md/dm-stats.c @@ -842,10 +842,10 @@ static unsigned long long dm_jiffies_to_msec64(struct dm_stat *s, unsigned long result = jiffies_to_msecs(j & 0x3fffff); if (j >= 1 << 22) { mult = jiffies_to_msecs(1 << 22); - result += (unsigned long long)mult * (unsigned long long)jiffies_to_msecs((j >> 22) & 0x3fffff); + result += (unsigned long long)mult * ((j >> 22) & 0x3fffff); } if (j >= 1ULL << 44) - result += (unsigned long long)mult * (unsigned long long)mult * (unsigned long long)jiffies_to_msecs(j >> 44); + result += (unsigned long long)mult * (unsigned long long)(1 << 22) * (j >> 44); return result; } From 750b23d4935bb720806558da8bb583be80293fcf Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Fri, 10 Jul 2026 18:35:49 +0200 Subject: [PATCH 2525/2758] dm-stats: fix merge accounting commit 1917eb2db750ecbdf710f79a8042eaa545a063c7 upstream. There were wrong parentheses when setting stats_aux->merged, so that merging was never properly accounted. This commit fixes it. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4-6 Fixes: fd2ed4d25270 ("dm: add statistics support") Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-stats.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/md/dm-stats.c b/drivers/md/dm-stats.c index 87842d815f4bb..5df710061a116 100644 --- a/drivers/md/dm-stats.c +++ b/drivers/md/dm-stats.c @@ -692,10 +692,8 @@ void dm_stats_account_io(struct dm_stats *stats, unsigned long bi_rw, */ last = raw_cpu_ptr(stats->last); stats_aux->merged = - (bi_sector == (READ_ONCE(last->last_sector) && - ((bi_rw == WRITE) == - (READ_ONCE(last->last_rw) == WRITE)) - )); + bi_sector == READ_ONCE(last->last_sector) && + (bi_rw == WRITE) == (READ_ONCE(last->last_rw) == WRITE); WRITE_ONCE(last->last_sector, end_sector); WRITE_ONCE(last->last_rw, bi_rw); } else From f00105be6a593920e9bc7949a069d4a116888851 Mon Sep 17 00:00:00 2001 From: Ingo Blechschmidt Date: Wed, 17 Jun 2026 16:33:42 +0200 Subject: [PATCH 2526/2758] dm: avoid leaking the caller's thread keyring via the table device file commit 981ccd97f7153d310dfa92a534525bbaf46752c2 upstream. The refactoring in commit a28d893eb327 ("md: port block device access to file") accidentally causes the caller's thread keyring to be kept alive long beyond the caller's lifetime. As a result, "cryptsetup luksSuspend" silently fails to wipe the LUKS volume key from memory. In detail: "cryptsetup luksOpen" uses its supposedly ephemeral thread keyring to pass the volume key to the kernel. dm-crypt's crypt_set_keyring_key() copies the key material into its own crypt_config structure and then drops its own reference to the key in the keyring with key_put(). With this fix, restoring pre-v6.9 behavior, the copy in the thread keyring is then promptly garbage collected, such that exactly one copy of the volume key remains. This single copy is correctly wiped from memory on "cryptsetup luksSuspend". Without this fix, the thread keyring and the volume key in it remains. This second copy is only freed on "luksClose". "luksSuspend" neither knows about this copy nor has any way to remove it, so the key remains recoverable from RAM after a suspend that is documented to have wiped it. This fix should not introduce new security problems, as the code is anyway gated by CAP_SYS_ADMIN. The device-mapper core, not the calling task, is the legitimate owner of this long-lived file. Fixes: a28d893eb327 ("md: port block device access to file") Closes: https://gitlab.com/cryptsetup/cryptsetup/-/work_items/993 Link: https://www.speicherleck.de/iblech/cryptsetup-luksSuspend-issue-reproduction/ Signed-off-by: Ingo Blechschmidt Signed-off-by: Mikulas Patocka Cc: stable@vger.kernel.org Tested-by: Ondrej Kozina Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 8f44fbbcf3da2..1334c411c7224 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -735,7 +735,16 @@ static struct table_device *open_table_device(struct mapped_device *md, return ERR_PTR(-ENOMEM); refcount_set(&td->count, 1); - bdev_file = bdev_file_open_by_dev(dev, mode, _dm_claim_ptr, NULL); + /* + * Open the backing device with kernel rather than caller + * credentials. Otherwise the caller's credentials would be + * pinned in bdev_file->f_cred until the table device is closed. + * That would keep the caller's thread keyring alive long beyond the + * lifetime of the caller, breaking userspace expectation (e.g. + * cryptsetup(8) leaking the LUKS volume key). + */ + scoped_with_kernel_creds() + bdev_file = bdev_file_open_by_dev(dev, mode, _dm_claim_ptr, NULL); if (IS_ERR(bdev_file)) { r = PTR_ERR(bdev_file); goto out_free_td; From 7d8ed7cb844df21e4d93af11250fb3a5cb861147 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:29:11 +0200 Subject: [PATCH 2527/2758] dm_early_create: fix freeing used table on dm_resume failure commit 366665416f20527ff7cad548a32d1ddf23195740 upstream. If dm_resume fails, the kernel attempts to free table with dm_table_destroy, but the table was already instantiated with dm_swap_table. This commit skips the call to dm_table_destroy in this case. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4.6 Fixes: 6bbc923dfcf5 ("dm: add support to directly boot to a mapped device") Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 9ab75c7a007f8..69847fab0353e 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -2351,7 +2351,7 @@ int __init dm_early_create(struct dm_ioctl *dmi, /* resume device */ r = dm_resume(md); if (r) - goto err_destroy_table; + goto err_hash_remove; DMINFO("%s (%s) is ready", md->disk->disk_name, dmi->name); dm_put(md); From 8f0af8493009a61b4313e0a8c6e03fbc36fd43f9 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:22:47 +0200 Subject: [PATCH 2528/2758] dm-integrity: fix leaking uninitialized kernel memory commit 7bb03b2b01b814a9fc14afbfc2cbb2cca5b34750 upstream. If hash size is less than device's tuple size, dm-integrity is supposed to zero the remaining space. There was a bug in the code that zeroing didn't work. This commit fixes it. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4.6 Fixes: fb0987682c62 ("dm-integrity: introduce the Inline mode") Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-integrity.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c index 65c30dec82220..bfed35dbe936c 100644 --- a/drivers/md/dm-integrity.c +++ b/drivers/md/dm-integrity.c @@ -2606,7 +2606,7 @@ static int dm_integrity_map_inline(struct dm_integrity_io *dio, bool from_map) struct bio_vec bv = bio_iter_iovec(bio, dio->bio_details.bi_iter); const char *mem = integrity_kmap(ic, bv.bv_page); if (ic->tag_size < ic->tuple_size) - memset(dio->integrity_payload + pos + ic->tag_size, 0, ic->tuple_size - ic->tuple_size); + memset(dio->integrity_payload + pos + ic->tag_size, 0, ic->tuple_size - ic->tag_size); integrity_sector_checksum(ic, &dio->ahash_req, dio->bio_details.bi_iter.bi_sector, mem, bv.bv_offset, dio->integrity_payload + pos); integrity_kunmap(ic, mem); pos += ic->tuple_size; From 3d1afaa074622859678b1a1e7c1b9c0af74c89b0 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:25:39 +0200 Subject: [PATCH 2529/2758] dm-integrity: fix a bug if the bio is out of limits commit 5a266764fadaff8b5c1fe37a186ebf9b09cb953e upstream. If dm_integrity_check_limits fails, the code would exit with DM_MAPIO_KILL. However, the range would be already locked at this point, and it wouldn't be unlocked, resulting in a deadlock. Let's move the limit check up, so that when it exits, no resources are leaked. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4.6 Fixes: fb0987682c62 ("dm-integrity: introduce the Inline mode") Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-integrity.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c index bfed35dbe936c..d176dfa42d982 100644 --- a/drivers/md/dm-integrity.c +++ b/drivers/md/dm-integrity.c @@ -2523,6 +2523,9 @@ static int dm_integrity_map_inline(struct dm_integrity_io *dio, bool from_map) if (unlikely((bio->bi_opf & REQ_PREFLUSH) != 0)) return DM_MAPIO_REMAPPED; + if (unlikely(!dm_integrity_check_limits(ic, bio->bi_iter.bi_sector, bio))) + return DM_MAPIO_KILL; + retry: if (!dio->integrity_payload) { unsigned digest_size, extra_size; @@ -2587,10 +2590,6 @@ static int dm_integrity_map_inline(struct dm_integrity_io *dio, bool from_map) dio->bio_details.bi_iter = bio->bi_iter; - if (unlikely(!dm_integrity_check_limits(ic, bio->bi_iter.bi_sector, bio))) { - return DM_MAPIO_KILL; - } - bio->bi_iter.bi_sector += ic->start + SB_SECTORS; bip = bio_integrity_alloc(bio, GFP_NOIO, 1); From 829476c06496aab018f14127c055adb164d1a750 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:24:09 +0200 Subject: [PATCH 2530/2758] dm-integrity: don't increment hash_offset twice commit edf025f083854f80032b73a1aad69a3c90db236f upstream. hash_offset is already incremented in the loop "for (i = 0; i < to_copy; i++, ts--)". Do not increment it again. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4.6 Fixes: 84597a44a9d8 ("dm-integrity: dm integrity: add optional discard support") Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-integrity.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c index d176dfa42d982..77c28367ee532 100644 --- a/drivers/md/dm-integrity.c +++ b/drivers/md/dm-integrity.c @@ -1480,9 +1480,6 @@ static int dm_integrity_rw_tag(struct dm_integrity_c *ic, unsigned char *tag, se *metadata_offset = 0; } - if (unlikely(!is_power_of_2(ic->tag_size))) - hash_offset = (hash_offset + to_copy) % ic->tag_size; - total_size -= to_copy; } while (unlikely(total_size)); From f7990c2b0f08b8841fcd2652d1d7002f5994a7a7 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Wed, 1 Jul 2026 18:13:50 +0200 Subject: [PATCH 2531/2758] dm-verity: fix buffer overflow in FEC calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 31d6e6c0ba8d5a7bd59660035a089307100c5e8e upstream. There's a buffer overflow in dm-verity-fec: if (neras && *neras <= v->fec->roots) fio->erasures[(*neras)++] = i; This allows *neras to reach roots + 1 (the post-increment pushes it past roots). This value is then passed as no_eras to decode_rs8(). Inside the RS decoder (lib/reed_solomon/decode_rs.c:113-121), the erasure locator polynomial loop writes lambda[j] where j can reach nroots + 1 — one element past the end of lambda[] (which is sized nroots + 1, valid indices 0..nroots). The out-of-bounds write lands on syn[0], corrupting the syndrome buffer. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4-6 Cc: stable@vger.kernel.org Fixes: a739ff3f543a ("dm verity: add support for forward error correction") Reviewed-by: Sami Tolvanen Signed-off-by: Mikulas Patocka Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-verity-fec.c | 4 ++-- drivers/md/dm-verity-fec.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/md/dm-verity-fec.c b/drivers/md/dm-verity-fec.c index 85ad9dc210ff0..c79f60df3a905 100644 --- a/drivers/md/dm-verity-fec.c +++ b/drivers/md/dm-verity-fec.c @@ -220,7 +220,7 @@ static int fec_read_bufs(struct dm_verity *v, struct dm_verity_io *io, PTR_ERR(bbuf)); /* assume the block is corrupted */ - if (neras && *neras <= v->fec->roots) + if (neras && *neras < v->fec->roots) fio->erasures[(*neras)++] = i; continue; @@ -238,7 +238,7 @@ static int fec_read_bufs(struct dm_verity *v, struct dm_verity_io *io, * skip if we have already found the theoretical * maximum number (i.e. fec->roots) of erasures */ - if (neras && *neras <= v->fec->roots && + if (neras && *neras < v->fec->roots && fec_is_erasure(v, io, want_digest, bbuf)) fio->erasures[(*neras)++] = i; } diff --git a/drivers/md/dm-verity-fec.h b/drivers/md/dm-verity-fec.h index 50b5e187d5cc1..3885b514fc23e 100644 --- a/drivers/md/dm-verity-fec.h +++ b/drivers/md/dm-verity-fec.h @@ -47,7 +47,7 @@ struct dm_verity_fec { /* per-bio data */ struct dm_verity_fec_io { struct rs_control *rs; /* Reed-Solomon state */ - int erasures[DM_VERITY_FEC_MAX_ROOTS + 1]; /* erasures for decode_rs8 */ + int erasures[DM_VERITY_FEC_MAX_ROOTS]; /* erasures for decode_rs8 */ u8 *output; /* buffer for corrected output */ unsigned int level; /* recursion level */ unsigned int nbufs; /* number of buffers allocated */ From 414650265267ac2b5a8d5c1e406703bd3ced81e6 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:33:16 +0200 Subject: [PATCH 2532/2758] dm-verity: avoid double increment of &use_bh_wq_enabled commit 72e9ec2fe32b00994f41719cf77423fca67d48b2 upstream. verity_parse_opt_args is called twice, first with the only_modifier_opts, first with only_modifier_opts == true and then with only_modifier_opts == false. Thus, the static branch &use_bh_wq_enabled was incremented twice and the destructor verity_dtr would only decrement it once. Fix tihs bug by only incrementing it on the first call, on the second call, when v->use_bh_wq is true, do nothing. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4-6 Cc: stable@vger.kernel.org Fixes: df326e7a0699 ("dm verity: allow optional args to alter primary args handling") Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-verity-target.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index 9a9847f94c460..adfda1fdab6aa 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -1262,6 +1262,8 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v, continue; } else if (!strcasecmp(arg_name, DM_VERITY_OPT_TASKLET_VERIFY)) { + if (v->use_bh_wq) + continue; v->use_bh_wq = true; static_branch_inc(&use_bh_wq_enabled); continue; From 81f41d989a32458ff3512f6b05458eaf8926579c Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:35:06 +0200 Subject: [PATCH 2533/2758] dm-verity: fix a possible NULL pointer dereference commit e72b793ae440f6900fb17a4b8518c707b5cd3e17 upstream. Fix a possible NULL pointer dereference dm_verity_loadpin_is_bdev_trusted if the device has no table. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4-6 Fixes: b6c1c5745ccc ("dm: Add verity helpers for LoadPin") Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-verity-loadpin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/dm-verity-loadpin.c b/drivers/md/dm-verity-loadpin.c index 0666699b68581..9a64f575ae5f6 100644 --- a/drivers/md/dm-verity-loadpin.c +++ b/drivers/md/dm-verity-loadpin.c @@ -70,7 +70,7 @@ bool dm_verity_loadpin_is_bdev_trusted(struct block_device *bdev) table = dm_get_live_table(md, &srcu_idx); - if (table->num_targets != 1) + if (!table || table->num_targets != 1) goto out; ti = dm_table_get_target(table, 0); From e96df7fdbec9d9a8797298b62854c662f08d318b Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:36:01 +0200 Subject: [PATCH 2534/2758] dm-verity: increase sprintf buffer size commit 88dd117c92a142253fb7a17e791773902b3babc6 upstream. The prefix "DM_VERITY_ERR_BLOCK_NR" is 22 chars. Add '=', one digit for type, ',', up to 20 digits for a u64 block number, and a NUL terminator: that's 46 bytes. The buffer is 42 bytes. For block numbers >= 16 decimal digits (devices larger than ~16 EB with 4K blocks), snprintf silently truncates the uevent environment variable. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4.6 Fixes: 65ff5b7ddf05 ("dm verity: add error handling modes for corrupted blocks") Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-verity-target.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index adfda1fdab6aa..e63a8290bd3af 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -26,7 +26,7 @@ #define DM_MSG_PREFIX "verity" -#define DM_VERITY_ENV_LENGTH 42 +#define DM_VERITY_ENV_LENGTH 46 #define DM_VERITY_ENV_VAR_NAME "DM_VERITY_ERR_BLOCK_NR" #define DM_VERITY_DEFAULT_PREFETCH_SIZE 262144 From 752e214b2c6f15b40b0d873a2ce27733ce0884c6 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:37:38 +0200 Subject: [PATCH 2535/2758] dm-verity: make error counter atomic commit 8ec4d9c5a5cf4b61fc087f871465b1f79b393325 upstream. The error counter "v->corrupted_errs" was not atomic, thus it could be subject to race conditions. The call to dm_audit_log_target("max-corrupted-errors") may be skipped due to the races. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4.6 Fixes: 65ff5b7ddf05 ("dm verity: add error handling modes for corrupted blocks") Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-verity-target.c | 12 +++++++----- drivers/md/dm-verity.h | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index e63a8290bd3af..1b07630912540 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -180,14 +180,16 @@ static int verity_handle_err(struct dm_verity *v, enum verity_block_type type, char *envp[] = { verity_env, NULL }; const char *type_str = ""; struct mapped_device *md = dm_table_get_md(v->ti->table); + int ce; /* Corruption should be visible in device status in all modes */ v->hash_failed = true; - if (v->corrupted_errs >= DM_VERITY_MAX_CORRUPTED_ERRS) - goto out; - - v->corrupted_errs++; + ce = atomic_read(&v->corrupted_errs); + do { + if (ce >= DM_VERITY_MAX_CORRUPTED_ERRS) + goto out; + } while (!atomic_try_cmpxchg(&v->corrupted_errs, &ce, ce + 1)); switch (type) { case DM_VERITY_BLOCK_TYPE_DATA: @@ -203,7 +205,7 @@ static int verity_handle_err(struct dm_verity *v, enum verity_block_type type, DMERR_LIMIT("%s: %s block %llu is corrupted", v->data_dev->name, type_str, block); - if (v->corrupted_errs == DM_VERITY_MAX_CORRUPTED_ERRS) { + if (ce + 1 == DM_VERITY_MAX_CORRUPTED_ERRS) { DMERR("%s: reached maximum errors", v->data_dev->name); dm_audit_log_target(DM_MSG_PREFIX, "max-corrupted-errors", v->ti, 0); } diff --git a/drivers/md/dm-verity.h b/drivers/md/dm-verity.h index 2922263501f68..e104a651c6574 100644 --- a/drivers/md/dm-verity.h +++ b/drivers/md/dm-verity.h @@ -68,7 +68,7 @@ struct dm_verity { unsigned int digest_size; /* digest size for the current hash algorithm */ enum verity_mode mode; /* mode for handling verification errors */ enum verity_mode error_mode;/* mode for handling I/O errors */ - unsigned int corrupted_errs;/* Number of errors for corrupted blocks */ + atomic_t corrupted_errs;/* Number of errors for corrupted blocks */ struct workqueue_struct *verify_wq; From e2d9a2ea178a5da0b4a6693e8ebca5c7fc4d7051 Mon Sep 17 00:00:00 2001 From: Baineng Shou Date: Mon, 29 Jun 2026 11:13:46 +0800 Subject: [PATCH 2536/2758] dma-fence: Make dma_fence_dedup_array() robust against 0-count input MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 77a9298741f8f9e8b963c977f5582ab21c6d3427 upstream. dma_fence_dedup_array() returns 1 when called with num_fences == 0: the for-loop body never executes, j stays at 0, and the final `return ++j` yields 1. This contradicts both the kernel-doc ("Return: Number of unique fences remaining in the array") and the natural expectation that 0 input gives 0 output. The caller __dma_fence_unwrap_merge() bails out via the `if (count == 0 || count == 1)` fast path and so is save. But amdgpu_userq_wait_*() could reach the dedup call with a zero local count and dereference an uninitialized fence slot in the array. Make the contract match the documentation by returning 0 early. This also skips an unnecessary sort() call on an empty array. Cc: stable@vger.kernel.org Signed-off-by: Baineng Shou Reviewed-by: Christian König Fixes: 575ec9b0c2f1 ("dma-fence: Add helper to sort and deduplicate dma_fence arrays") Signed-off-by: Christian König Link: https://lore.kernel.org/r/20260629031346.3875683-1-shoubaineng@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/dma-buf/dma-fence-unwrap.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/dma-buf/dma-fence-unwrap.c b/drivers/dma-buf/dma-fence-unwrap.c index 07fe9bf45aea7..cc11c036f2b1d 100644 --- a/drivers/dma-buf/dma-fence-unwrap.c +++ b/drivers/dma-buf/dma-fence-unwrap.c @@ -97,6 +97,9 @@ int dma_fence_dedup_array(struct dma_fence **fences, int num_fences) { int i, j; + if (!num_fences) + return 0; + sort(fences, num_fences, sizeof(*fences), fence_cmp, NULL); /* From 562d5e6f9b994058e3b72536f5a1fd461a30bc16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Draszik?= Date: Thu, 18 Jun 2026 13:00:39 +0100 Subject: [PATCH 2537/2758] dma-fence: use correct callback in dma_fence_timeline_name() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 032a6f6ce21fc701468fd15403d2f53a30107f5b upstream. dma_fence_timeline_name() is a wrapper around dma_fence_ops::get_timeline_name(). Since the blamed commit below, it calls an incorrect callback. Update it to restore functionality by calling the intended callback. Fixes: 62918542b7bf ("dma-fence: Fix sparse warnings due __rcu annotations") Cc: # v7.1+ [tursulin: added cc stable] Signed-off-by: André Draszik Reviewed-by: Philipp Stanner Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260618-linux-drm_crtc_fix-v1-1-801f29c9853d@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/dma-buf/dma-fence.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c index b3bfa6943a8e1..5292d714419b1 100644 --- a/drivers/dma-buf/dma-fence.c +++ b/drivers/dma-buf/dma-fence.c @@ -1202,7 +1202,7 @@ const char __rcu *dma_fence_timeline_name(struct dma_fence *fence) /* RCU protection is required for safe access to returned string */ ops = rcu_dereference(fence->ops); if (!dma_fence_test_signaled_flag(fence)) - return (const char __rcu *)ops->get_driver_name(fence); + return (const char __rcu *)ops->get_timeline_name(fence); else return (const char __rcu *)"signaled-timeline"; } From 15ecfdf0ef6f6d874d0a26690d300857b39ebfd0 Mon Sep 17 00:00:00 2001 From: Philipp Stanner Date: Mon, 29 Jun 2026 09:56:37 +0200 Subject: [PATCH 2538/2758] dma-buf: dma-fence: Fix potential NULL pointer dereference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 035219a760edb35ae9a9e96beba7f122e26a997b upstream. The commit mentioned in the fixes tag below introduced a mechanism through which fence producers can fully decouple from fence consumers. This, desirable, mechanism is based on the fence's signaled-bit as the "decoupling point". A sophisticated interaction between RCU and atomic instructions attempts to ensure that fence consumers can still interact with fence producers through the dma_fence_ops (callback pointers into the producer). This is the desired behavior: to check for decoupling, the signaled-bit is first checked. If it's not yet signaled, RCU ensures that the ops pointer cannot yet be NULL. Hereby, dma_fence_signal_timestamp_locked() first sets the signaled-bit, and then sets the ops pointer to NULL. Readers first load the ops pointer, and then check through the signaled-bit whether the pointer can legally be accessed. These set and load operations could occur out of order on weakly ordered platforms. This problem can be solved very elegantly by using the ops pointer itself as the synchronization point. The pointer is either NULL, or cannot become NULL while it is being used thanks to RCU. Replace the signaled-bit check in dma_fence_timeline_name() and dma_fence_driver_name(). Cc: stable@vger.kernel.org Fixes: f4cc3ab824d6 ("dma-buf: protected fence ops by RCU v8") Signed-off-by: Philipp Stanner Reviewed-by: Boris Brezillon Reviewed-by: Christian König Reviewed-by: Danilo Krummrich Link: https://lore.kernel.org/r/20260629075636.2513214-2-phasta@kernel.org Signed-off-by: Christian König Signed-off-by: Greg Kroah-Hartman --- drivers/dma-buf/dma-fence.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c index 5292d714419b1..87797bea91cbd 100644 --- a/drivers/dma-buf/dma-fence.c +++ b/drivers/dma-buf/dma-fence.c @@ -1168,7 +1168,7 @@ const char __rcu *dma_fence_driver_name(struct dma_fence *fence) /* RCU protection is required for safe access to returned string */ ops = rcu_dereference(fence->ops); - if (!dma_fence_test_signaled_flag(fence)) + if (ops) return (const char __rcu *)ops->get_driver_name(fence); else return (const char __rcu *)"detached-driver"; @@ -1201,7 +1201,7 @@ const char __rcu *dma_fence_timeline_name(struct dma_fence *fence) /* RCU protection is required for safe access to returned string */ ops = rcu_dereference(fence->ops); - if (!dma_fence_test_signaled_flag(fence)) + if (ops) return (const char __rcu *)ops->get_timeline_name(fence); else return (const char __rcu *)"signaled-timeline"; From 5da885c39baa70f570a8be35a78e85a351f33918 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Thu, 25 Jun 2026 19:32:39 +0800 Subject: [PATCH 2539/2758] accel/amdxdna: Fix use-after-free in amdxdna_gem_dmabuf_mmap() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 63bbf9ac5dde2ba85e7b39d0a0b7d540e6252ba4 upstream. When vm_insert_pages() fails, the error path calls vma->vm_ops->close(vma) which internally calls drm_gem_vm_close() → drm_gem_object_put(), releasing the GEM object reference acquired at the start of the function. However, the close_vma label then falls through to put_obj, which calls drm_gem_object_put() a second time on the same object. If the first put releases the last reference, the object is freed and the second put accesses freed memory, causing a use-after-free. Fix by returning directly from close_vma instead of falling through to put_obj, since the close handler already performs all necessary cleanup including the object put. Cc: stable@vger.kernel.org Fixes: e486147c912f ("accel/amdxdna: Add BO import and export") Signed-off-by: Wentao Liang Reviewed-by: Lizhi Hou Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260625113239.49764-1-vulab@iscas.ac.cn Signed-off-by: Greg Kroah-Hartman --- drivers/accel/amdxdna/amdxdna_gem.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c index eb440bccd0cbd..640fb923ce91c 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.c +++ b/drivers/accel/amdxdna/amdxdna_gem.c @@ -480,6 +480,7 @@ static int amdxdna_gem_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struc close_vma: vma->vm_ops->close(vma); + return ret; put_obj: drm_gem_object_put(gobj); return ret; From f7d08603c87bae19ca3e424da5ab6d0aee81886e Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Mon, 13 Jul 2026 19:30:29 +0200 Subject: [PATCH 2540/2758] accel/amdxdna: reject command submission on devices without a submit op commit 38953513d7313992676d4136cd425cdb70c6278e upstream. amdxdna_cmd_submit() calls xdna->dev_info->ops->cmd_submit() unconditionally, but only aie2_dev_ops defines that callback. aie4_vf_ops (the AIE4 SR-IOV virtual function) does not, so a user AMDXDNA_EXEC_CMD ioctl on an AIE4 device reaches a NULL function-pointer call and oopses the kernel. AIE4 submits work through a mapped user queue and doorbell, not this ioctl path. Reject the submission early with -EOPNOTSUPP when the device provides no cmd_submit op, so the shared EXEC ioctl is a clean no-op on such devices. Fixes: aac243092b70 ("accel/amdxdna: Add command execution") Cc: stable@vger.kernel.org Found by 0sec automated security-research tooling (https://0sec.ai). Assisted-by: 0sec:claude-opus-4-8 Signed-off-by: Doruk Tan Ozturk Reviewed-by: Lizhi Hou Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260713173030.87541-3-doruk@0sec.ai Signed-off-by: Greg Kroah-Hartman --- drivers/accel/amdxdna/amdxdna_ctx.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/accel/amdxdna/amdxdna_ctx.c b/drivers/accel/amdxdna/amdxdna_ctx.c index 2ab4443e70946..9a96ac2afb038 100644 --- a/drivers/accel/amdxdna/amdxdna_ctx.c +++ b/drivers/accel/amdxdna/amdxdna_ctx.c @@ -496,6 +496,10 @@ int amdxdna_cmd_submit(struct amdxdna_client *client, int ret, idx; XDNA_DBG(xdna, "Command BO hdl %d, Arg BO count %d", cmd_bo_hdl, arg_bo_cnt); + + if (!xdna->dev_info->ops->cmd_submit) + return -EOPNOTSUPP; + job = kzalloc_flex(*job, bos, arg_bo_cnt); if (!job) return -ENOMEM; From fff6509d976f6fae423a5236391ccdbd7e0e9f06 Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Mon, 13 Jul 2026 19:30:28 +0200 Subject: [PATCH 2541/2758] accel/amdxdna: reject user command submission without a command BO commit 261c1fe3327ad24508f54552c6366e3e4db82c15 upstream. amdxdna_drm_submit_execbuf() passes the user-supplied command BO handle straight into amdxdna_cmd_submit() with drv_cmd == NULL. When the handle is AMDXDNA_INVALID_BO_HANDLE (0), the block that fetches job->cmd_bo is skipped, leaving it NULL, and no check rejects it on the user path (the !job->cmd_bo guard lives inside the != INVALID branch). The job is then armed and pushed to the DRM scheduler. aie2_sched_job_run() takes the drv_cmd == NULL path and calls amdxdna_cmd_set_state(job->cmd_bo) -> amdxdna_gem_vmap(NULL) -> to_gobj(NULL)->dev, a NULL pointer dereference in the drm_sched worker. A process with access to the accel node on a system with a probed AMD NPU can trigger a kernel oops with a single AMDXDNA_EXEC_CMD ioctl (cmd_handles = 0). Only internal driver commands (SYNC_DEBUG_BO / ATTACH_DEBUG_BO) legitimately pass AMDXDNA_INVALID_BO_HANDLE, and they always set drv_cmd. Reject the invalid handle for user submissions (drv_cmd == NULL) at the submit choke point so every user path is covered. Fixes: aac243092b70 ("accel/amdxdna: Add command execution") Cc: stable@vger.kernel.org Found by 0sec automated security-research tooling (https://0sec.ai). Assisted-by: 0sec:claude-opus-4-8 Signed-off-by: Doruk Tan Ozturk Reviewed-by: Lizhi Hou Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260713173030.87541-2-doruk@0sec.ai Signed-off-by: Greg Kroah-Hartman --- drivers/accel/amdxdna/amdxdna_ctx.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/accel/amdxdna/amdxdna_ctx.c b/drivers/accel/amdxdna/amdxdna_ctx.c index 9a96ac2afb038..5315466f137b5 100644 --- a/drivers/accel/amdxdna/amdxdna_ctx.c +++ b/drivers/accel/amdxdna/amdxdna_ctx.c @@ -513,6 +513,16 @@ int amdxdna_cmd_submit(struct amdxdna_client *client, ret = -EINVAL; goto free_job; } + } else if (!drv_cmd) { + /* + * Only internal driver commands (drv_cmd != NULL) may omit a + * command BO. A user command submission with the invalid handle + * would leave job->cmd_bo NULL and later fault when the scheduler + * dereferences it in amdxdna_cmd_set_state(). + */ + XDNA_DBG(xdna, "Command BO handle required for user submission"); + ret = -EINVAL; + goto free_job; } ret = amdxdna_arg_bos_lookup(client, job, arg_bo_hdls, arg_bo_cnt); From 216e43d93dd49ec253052741aa476e51a8c54cd8 Mon Sep 17 00:00:00 2001 From: Shuvam Pandey Date: Tue, 16 Jun 2026 02:03:00 +0545 Subject: [PATCH 2542/2758] accel/amdxdna: Use caller client for debug BO sync commit 7caf2a2351d4053075670ff3e26a6815da0a9e1e upstream. amdxdna_drm_sync_bo_ioctl() looks up args->handle in the ioctl caller's drm_file. For SYNC_DIRECT_FROM_DEVICE, it then calls amdxdna_hwctx_sync_debug_bo(), but passes abo->client. amdxdna_hwctx_sync_debug_bo() uses the passed client both as the handle namespace for debug_bo_hdl and as the owner of the hardware context xarray. Those must match the file that supplied args->handle. The BO's stored client pointer is object state, not the ioctl context. Pass filp->driver_priv instead, matching the original handle lookup. Fixes: 7ea046838021 ("accel/amdxdna: Support firmware debug buffer") Cc: stable@vger.kernel.org # v6.19+ Signed-off-by: Shuvam Pandey Reviewed-by: Lizhi Hou Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/178155468039.81818.12173237984867749651@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/accel/amdxdna/amdxdna_gem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c index 640fb923ce91c..cdf113dadc4db 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.c +++ b/drivers/accel/amdxdna/amdxdna_gem.c @@ -1039,6 +1039,7 @@ int amdxdna_drm_get_bo_info_ioctl(struct drm_device *dev, void *data, struct drm int amdxdna_drm_sync_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) { + struct amdxdna_client *client = filp->driver_priv; struct amdxdna_dev *xdna = to_xdna_dev(dev); struct amdxdna_drm_sync_bo *args = data; struct amdxdna_gem_obj *abo; @@ -1073,7 +1074,7 @@ int amdxdna_drm_sync_bo_ioctl(struct drm_device *dev, args->handle, args->offset, args->size); if (args->direction == SYNC_DIRECT_FROM_DEVICE) - ret = amdxdna_hwctx_sync_debug_bo(abo->client, args->handle); + ret = amdxdna_hwctx_sync_debug_bo(client, args->handle); put_obj: drm_gem_object_put(gobj); From 6920e62be4c969a68ce4ebc59da68c6cbc9512e5 Mon Sep 17 00:00:00 2001 From: Jhonraushan Date: Wed, 15 Jul 2026 13:12:06 +0530 Subject: [PATCH 2543/2758] accel/ivpu: Reject firmware log with size smaller than header commit ddb44baed257560f192b145ed36cf8c0a412de47 upstream. fw_log_from_bo() validates the tracing buffer header_size and that the log fits within the BO, but never checks that log->size is at least log->header_size. fw_log_print_buffer() then computes: u32 data_size = log->size - log->header_size; which underflows to a near-U32_MAX value when firmware reports a log whose size is smaller than its header. That huge data_size defeats the log_start/log_end bounds clamps added by commit dd1311bcf0e6 ("accel/ivpu: Add bounds checks for firmware log indices"), so fw_log_print_lines() reads far past the small real data region of the BO. A size of 0 also makes fw_log_from_bo() advance the offset by 0, causing the callers to loop forever on the same header. Reject logs whose size is smaller than the header (which also rejects size == 0). Fixes: d4e4257afa6e ("accel/ivpu: Add firmware tracing support") Cc: stable@vger.kernel.org Signed-off-by: Jhonraushan Reviewed-by: Karol Wachowski Signed-off-by: Karol Wachowski Link: https://patch.msgid.link/20260715074206.867712-1-raushan.jhon@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/accel/ivpu/ivpu_fw_log.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/accel/ivpu/ivpu_fw_log.c b/drivers/accel/ivpu/ivpu_fw_log.c index 275baf844b562..716467aa31567 100644 --- a/drivers/accel/ivpu/ivpu_fw_log.c +++ b/drivers/accel/ivpu/ivpu_fw_log.c @@ -43,6 +43,10 @@ static int fw_log_from_bo(struct ivpu_device *vdev, struct ivpu_bo *bo, u32 *off ivpu_dbg(vdev, FW_BOOT, "Invalid header size 0x%x\n", log->header_size); return -EINVAL; } + if (log->size < log->header_size) { + ivpu_dbg(vdev, FW_BOOT, "Invalid log size 0x%x\n", log->size); + return -EINVAL; + } if ((char *)log + log->size > (char *)ivpu_bo_vaddr(bo) + ivpu_bo_size(bo)) { ivpu_dbg(vdev, FW_BOOT, "Invalid log size 0x%x\n", log->size); return -EINVAL; From 782e1bf48672be44265c48e14602696c3c8ed904 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Tue, 23 Jun 2026 00:00:28 +0800 Subject: [PATCH 2544/2758] scsi: hpsa: Fix DMA mapping leak on IOACCEL2 reset path commit e166bafc483e927150cb9b5f286c9191ea0df84e upstream. If phys_disk->in_reset is set, the function returns directly without undoing the resources acquired for the command. Add the missing error cleanup by unmapping the IOACCEL2 SG chain block when needed, unmapping the SCSI command, and dropping the outstanding IOACCEL command count before returning. Fixes: c5dfd106414f ("scsi: hpsa: correct device resets") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Acked-by: Don Brace Link: https://patch.msgid.link/20260622160028.1240496-1-haoxiang_li2024@163.com Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/hpsa.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index a1b116cd4723d..8edad1830abe4 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -5017,6 +5017,10 @@ static int hpsa_scsi_ioaccel2_queue_command(struct ctlr_info *h, if (phys_disk->in_reset) { cmd->result = DID_RESET << 16; + atomic_dec(&phys_disk->ioaccel_cmds_out); + scsi_dma_unmap(cmd); + if (use_sg > h->ioaccel_maxsg) + hpsa_unmap_ioaccel2_sg_chain_block(h, cp); return -1; } From 0ce5a37f7ddf2fc12210e8700350274da79fbb3a Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Tue, 7 Jul 2026 12:23:02 +0530 Subject: [PATCH 2545/2758] scsi: lpfc: Fix memory leak in lpfc_sli4_driver_resource_setup() commit 1bd28625e25be549ee7c47532e7c3ef91c682410 upstream. The memory allocated for mboxq using mempool_alloc() is not freed in some of the early exit error paths. Fix that by moving the mempool_free() call to an earlier point after last use. Fixes: d79c9e9d4b3d ("scsi: lpfc: Support dynamic unbounded SGL lists on G7 hardware.") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Reviewed-by: Justin Tee Link: https://patch.msgid.link/20260707065304.949135-1-nihaal@cse.iitm.ac.in Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/lpfc/lpfc_init.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 968a25235a2d7..f90ac2aea3f50 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -8189,6 +8189,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) mempool_free(mboxq, phba->mbox_mem_pool); goto out_free_bsmbx; } + mempool_free(mboxq, phba->mbox_mem_pool); /* * 1 for cmd, 1 for rsp, NVME adds an extra one @@ -8311,8 +8312,6 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) goto out_free_sg_dma_buf; } - mempool_free(mboxq, phba->mbox_mem_pool); - /* Verify OAS is supported */ lpfc_sli4_oas_verify(phba); From 3cbabbf1722ebac97408cc34534fc6794a6179e8 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Tue, 7 Jul 2026 11:08:45 +0800 Subject: [PATCH 2546/2758] scsi: sg: Report request-table problems when any status is set commit 1d3a742afeb761eaead774691bde1ced699e9a5d upstream. SG_GET_REQUEST_TABLE reports per-request diagnostic state through sg_req_info::problem. The field is meant to indicate whether there is an error to report for a completed request. sg_fill_request_table() currently combines masked_status, host_status and driver_status with bitwise AND. This only reports a problem when all three status fields are non-zero at the same time. A normal target check condition, for example, has masked_status set while host_status and driver_status may both be zero, so the request is incorrectly reported as clean. Use the same condition as sg_new_read(), which sets SG_INFO_CHECK when any of the three status fields is non-zero. Cc: stable@vger.kernel.org Signed-off-by: Xu Rao Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/54B60C19F7DB8889+20260707030845.970018-1-raoxu@uniontech.com Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/sg.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 74cd4e8a61c2a..5408f002e6c01 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -863,10 +863,9 @@ sg_fill_request_table(Sg_fd *sfp, sg_req_info_t *rinfo) if (val >= SG_MAX_QUEUE) break; rinfo[val].req_state = srp->done + 1; - rinfo[val].problem = - srp->header.masked_status & - srp->header.host_status & - srp->header.driver_status; + rinfo[val].problem = srp->header.masked_status || + srp->header.host_status || + srp->header.driver_status; if (srp->done) rinfo[val].duration = srp->header.duration; From 1357fb32d42ad8da193e6da285f09e6452feabda Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Thu, 11 Jun 2026 08:30:46 -0400 Subject: [PATCH 2547/2758] scsi: xen: scsiback: Free the command tag on the TMR submit-failure path commit 66aefc277ebb796ec285d550305535dc3fc0179f upstream. scsiback_device_action() obtains a command tag in scsiback_get_pend_req() and submits a task-management request with target_submit_tmr(). When target_submit_tmr() fails it returns < 0 and scsiback jumps to the err: label, which sends a response but frees nothing, leaking the tag. Impact: a pvSCSI guest can leak the command tags of a LUN's session, stopping the LUN, by issuing VSCSIIF_ACT_SCSI_ABORT or RESET requests whenever target_submit_tmr() fails. transport_generic_free_cmd() cannot be used here. By the time target_submit_tmr() returns an error it has already run __target_init_cmd() (so se_cmd->cmd_kref is one, not zero), and on its target_get_sess_cmd() error path it has freed se_cmd->se_tmr_req via core_tmr_release_req() while leaving SCF_SCSI_TMR_CDB set and the pointer dangling. Letting the command release run target_free_cmd_mem() would then double-free se_tmr_req. Use the same helper, which returns just the tag, on this path too. Fixes: 2dbcdf33dbf6 ("xen-scsiback: Convert to percpu_ida tag allocation") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Reviewed-by: Juergen Gross Link: https://patch.msgid.link/20260611123046.2323342-3-michael.bommarito@gmail.com Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/xen/xen-scsiback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/xen/xen-scsiback.c b/drivers/xen/xen-scsiback.c index e33f95c91b096..f871bffff3d79 100644 --- a/drivers/xen/xen-scsiback.c +++ b/drivers/xen/xen-scsiback.c @@ -639,7 +639,7 @@ static void scsiback_device_action(struct vscsibk_pend *pending_req, return; err: - scsiback_do_resp_with_sense(NULL, err, 0, pending_req); + scsiback_resp_and_free(pending_req, err); } /* From 1e97c404e44991fb087c38ccd7414f2d326f9b74 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Thu, 11 Jun 2026 08:30:45 -0400 Subject: [PATCH 2548/2758] scsi: xen: scsiback: Free unsubmitted command instead of double-putting it commit ca978f8a93d4d36841839bf2847d29b88c2591d6 upstream. scsiback_get_pend_req() obtains a command tag and returns a vscsibk_pend whose embedded se_cmd has only been memset to 0, so its cmd_kref is 0; the se_cmd is initialised (kref_init() via target_init_cmd()) only later, in scsiback_cmd_exec(), on the successful VSCSIIF_ACT_SCSI_CDB path. The two error paths in scsiback_do_cmd_fn() taken before the command is submitted -- a failed scsiback_gnttab_data_map() and an unknown ring_req.act -- call transport_generic_free_cmd(&pending_req->se_cmd, 0), which kref_put()s a refcount of 0. That underflows it ("refcount_t: underflow; use-after-free") and, as the release function is not run, leaks the command tag. Impact: a pvSCSI guest can leak every command tag of a LUN's session, stopping the LUN, by submitting requests with a bad grant reference or an unknown request type; under panic_on_warn the refcount underflow panics the host. Add a helper that just returns the tag with target_free_tag() and sends the error response. It frees the tag while the v2p reference still pins the session, and snapshots the response fields beforehand because freeing the tag can let another ring reuse the pending_req slot. Fixes: 2dbcdf33dbf6 ("xen-scsiback: Convert to percpu_ida tag allocation") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Reviewed-by: Juergen Gross Link: https://patch.msgid.link/20260611123046.2323342-2-michael.bommarito@gmail.com Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/xen/xen-scsiback.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/drivers/xen/xen-scsiback.c b/drivers/xen/xen-scsiback.c index f871bffff3d79..c7036e0e41bda 100644 --- a/drivers/xen/xen-scsiback.c +++ b/drivers/xen/xen-scsiback.c @@ -611,6 +611,25 @@ static void scsiback_disconnect(struct vscsibk_info *info) xenbus_unmap_ring_vfree(info->dev, info->ring.sring); } +/* + * Send the error response for a request that did not reach the target core + * and return its tag. Free the tag before the response drops the v2p + * reference that keeps the session alive, and snapshot what the response + * needs since returning the tag can let the slot be reused. + */ +static void scsiback_resp_and_free(struct vscsibk_pend *pending_req, + int32_t result) +{ + struct vscsibk_info *info = pending_req->info; + struct v2p_entry *v2p = pending_req->v2p; + struct se_session *se_sess = v2p->tpg->tpg_nexus->tvn_se_sess; + u16 rqid = pending_req->rqid; + + target_free_tag(se_sess, &pending_req->se_cmd); + scsiback_send_response(info, NULL, result, 0, rqid); + kref_put(&v2p->kref, scsiback_free_translation_entry); +} + static void scsiback_device_action(struct vscsibk_pend *pending_req, enum tcm_tmreq_table act, int tag) { @@ -792,9 +811,8 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info, case VSCSIIF_ACT_SCSI_CDB: if (scsiback_gnttab_data_map(&ring_req, pending_req)) { scsiback_fast_flush_area(pending_req); - scsiback_do_resp_with_sense(NULL, - DID_ERROR << 16, 0, pending_req); - transport_generic_free_cmd(&pending_req->se_cmd, 0); + scsiback_resp_and_free(pending_req, + DID_ERROR << 16); } else { scsiback_cmd_exec(pending_req); } @@ -808,9 +826,7 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info, break; default: pr_err_ratelimited("invalid request\n"); - scsiback_do_resp_with_sense(NULL, DID_ERROR << 16, 0, - pending_req); - transport_generic_free_cmd(&pending_req->se_cmd, 0); + scsiback_resp_and_free(pending_req, DID_ERROR << 16); break; } From 555a89846ed888d7401b3f7200934c0fbedcbb46 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Thu, 11 Jun 2026 13:42:26 -0500 Subject: [PATCH 2549/2758] scsi: target: Bound PR-OUT TransportID parsing to the received buffer commit d04a179085c262c9ed577d0a4cbc6482ff1fd9a3 upstream. core_scsi3_decode_spec_i_port() and core_scsi3_emulate_register_and_move() hand the raw PERSISTENT RESERVE OUT parameter buffer to target_parse_pr_out_transport_id() without telling it how many bytes are valid. For an iSCSI TransportID (FORMAT CODE 01b), iscsi_parse_pr_out_transport_id() locates the ",i,0x" ISID separator with an unbounded strstr() (and on the error path prints the name with a further unbounded "%s"). An initiator can submit a TransportID whose iSCSI name contains neither a ",i,0x" substring nor a NUL terminator, filling the parameter list to its end, so the scan runs off the end of the buffer. When the parameter list spans more than one page the buffer is a multi-page vmap (transport_kmap_data_sg()), so the over-read walks into the trailing vmalloc guard page and oopses (KASAN: vmalloc-out-of-bounds in strstr). It is reachable by any fabric that delivers a PR OUT to a device exported through an iSCSI TPG, including a guest via vhost-scsi. Pass the number of received bytes down to the parser and validate the iSCSI TransportID's own self-described length (ADDITIONAL LENGTH + 4) once, up front: reject it if it is below the spc4r17 minimum or larger than the received buffer, then bound the separator search, the ISID walk and the name copy by that length. This is the length check the callers already perform after the parse (core_scsi3_decode_spec_i_port() compares tid_len against tpdl, core_scsi3_emulate_register_and_move() validates it against data_length), moved ahead of the scan. Also drop the unbounded "%s" of the unterminated name. Add per-format explicit name-length checks before copying into i_str, rather than silently truncating with min_t: for FORMAT CODE 00b reject if the descriptor body (tid_len - 4 bytes) cannot fit in i_str[TRANSPORT_IQN_LEN]; for FORMAT CODE 01b reject if the name portion (from &buf[4] up to the separator) cannot fit. Both checks make the bounds intent explicit at each format branch. While here, also reject a FORMAT CODE 01b TransportID whose ",i,0x" separator sits at the very end of the descriptor: that leaves an empty ISID and points the returned port nexus pointer at buf + tid_len, one past the descriptor, which the registration code (__core_scsi3_locate_pr_reg(), __core_scsi3_alloc_registration()) then dereferences as the ISID string -- the same over-read of the parameter buffer for a malformed descriptor. Fixes: c66ac9db8d4a ("[SCSI] target: Add LIO target core v4.0.0-rc6") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Reviewed-by: John Garry Reviewed-by: David Disseldorp Link: https://patch.msgid.link/20260611-b4-disp-9f20739e-v6-1-f6630e2aae44@proton.me Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/target/target_core_fabric_lib.c | 89 +++++++++++++++++++------ drivers/target/target_core_internal.h | 3 +- drivers/target/target_core_pr.c | 4 +- 3 files changed, 73 insertions(+), 23 deletions(-) diff --git a/drivers/target/target_core_fabric_lib.c b/drivers/target/target_core_fabric_lib.c index 87c5d26a50895..2853b95b2c597 100644 --- a/drivers/target/target_core_fabric_lib.c +++ b/drivers/target/target_core_fabric_lib.c @@ -290,13 +290,24 @@ static void sbp_parse_pr_out_transport_id(char *buf, char *i_str) static bool iscsi_parse_pr_out_transport_id( struct se_portal_group *se_tpg, char *buf, + u32 buf_len, u32 *out_tid_len, char **port_nexus_ptr, char *i_str) { char *p; + u32 tid_len; int i; - u8 format_code = (buf[0] & 0xc0); + u8 format_code; + + /* + * The 4-byte iSCSI TransportID header (FORMAT CODE + 2-byte ADDITIONAL + * LENGTH) must be present before any of it can be parsed. + */ + if (buf_len < 4) + return false; + + format_code = buf[0] & 0xc0; /* * Check for FORMAT CODE 00b or 01b from spc4r17, section 7.5.4.6: * @@ -316,15 +327,17 @@ static bool iscsi_parse_pr_out_transport_id( return false; } /* - * If the caller wants the TransportID Length, we set that value for the - * entire iSCSI Tarnsport ID now. + * Reconstruct the self-described TransportID length from the ADDITIONAL + * LENGTH field plus the 4-byte header. Reject it if it is below the + * spc4r17 section 7.5.4.6 minimum (ADDITIONAL LENGTH shall be at least + * 20) or if it runs past the bytes actually received, so that every + * access below stays inside the TransportID. */ - if (out_tid_len) { - /* The shift works thanks to integer promotion rules */ - *out_tid_len = get_unaligned_be16(&buf[2]); - /* Add four bytes for iSCSI Transport ID header */ - *out_tid_len += 4; - } + tid_len = get_unaligned_be16(&buf[2]) + 4; + if (tid_len < 24 || tid_len > buf_len) + return false; + if (out_tid_len) + *out_tid_len = tid_len; /* * Check for ',i,0x' separator between iSCSI Name and iSCSI Initiator @@ -332,16 +345,32 @@ static bool iscsi_parse_pr_out_transport_id( * format. */ if (format_code == 0x40) { - p = strstr(&buf[4], ",i,0x"); + p = strnstr(&buf[4], ",i,0x", tid_len - 4); if (!p) { - pr_err("Unable to locate \",i,0x\" separator" - " for Initiator port identifier: %s\n", - &buf[4]); + pr_err("Unable to locate \",i,0x\" separator in iSCSI TransportID\n"); + return false; + } + /* + * The iSCSI name runs from &buf[4] up to the separator; reject it + * if it cannot fit in i_str[TRANSPORT_IQN_LEN]. + */ + if (p - &buf[4] >= TRANSPORT_IQN_LEN) { + pr_err("iSCSI Initiator port name too long in TransportID\n"); return false; } *p = '\0'; /* Terminate iSCSI Name */ p += 5; /* Skip over ",i,0x" separator */ + /* + * The ISID must follow the separator. A ",i,0x" sitting at the + * very end of the TransportID leaves no ISID and would point the + * port nexus at buf + tid_len, i.e. past the descriptor, which + * the registration code then reads as the ISID string. + */ + if (p >= buf + tid_len) { + pr_err("Missing ISID in iSCSI Initiator port TransportID\n"); + return false; + } *port_nexus_ptr = p; /* * Go ahead and do the lower case conversion of the received @@ -349,7 +378,7 @@ static bool iscsi_parse_pr_out_transport_id( * for comparison against the running iSCSI session's ISID from * iscsi_target.c:lio_sess_get_initiator_sid() */ - for (i = 0; i < 12; i++) { + for (i = 0; i < 12 && p < buf + tid_len; i++) { /* * The first ISCSI INITIATOR SESSION ID field byte * containing an ASCII null character terminates the @@ -367,10 +396,22 @@ static bool iscsi_parse_pr_out_transport_id( *p = tolower(*p); p++; } - } else + strscpy(i_str, &buf[4], TRANSPORT_IQN_LEN); + } else { *port_nexus_ptr = NULL; - - strscpy(i_str, &buf[4], TRANSPORT_IQN_LEN); + /* + * FORMAT CODE 00b: the name occupies buf[4..tid_len-1]. The + * declared length tid_len - 4 must fit in i_str[TRANSPORT_IQN_LEN]. + * (For 01b the same tid_len bound would be over-restrictive: the + * descriptor also carries the separator and ISID, so a legal + * <=223-byte name gives tid_len up to 244.) + */ + if (tid_len - 4 >= TRANSPORT_IQN_LEN) { + pr_err("iSCSI Initiator port name too long in TransportID\n"); + return false; + } + strscpy(i_str, &buf[4], tid_len - 4); + } return true; } @@ -420,8 +461,16 @@ int target_get_pr_transport_id(struct se_node_acl *nacl, } bool target_parse_pr_out_transport_id(struct se_portal_group *tpg, - char *buf, u32 *out_tid_len, char **port_nexus_ptr, char *i_str) + char *buf, u32 buf_len, u32 *out_tid_len, + char **port_nexus_ptr, char *i_str) { + /* + * The fixed-length SAS/SRP/FCP/SBP TransportIDs are 24 bytes; the iSCSI + * format is variable and bounds itself against buf_len below. + */ + if (tpg->proto_id != SCSI_PROTOCOL_ISCSI && buf_len < 24) + return false; + switch (tpg->proto_id) { case SCSI_PROTOCOL_SAS: /* @@ -440,8 +489,8 @@ bool target_parse_pr_out_transport_id(struct se_portal_group *tpg, sbp_parse_pr_out_transport_id(buf, i_str); break; case SCSI_PROTOCOL_ISCSI: - return iscsi_parse_pr_out_transport_id(tpg, buf, out_tid_len, - port_nexus_ptr, i_str); + return iscsi_parse_pr_out_transport_id(tpg, buf, buf_len, + out_tid_len, port_nexus_ptr, i_str); default: pr_err("Unknown proto_id: 0x%02x\n", tpg->proto_id); return false; diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h index 763e6d26e1877..f0886ea290345 100644 --- a/drivers/target/target_core_internal.h +++ b/drivers/target/target_core_internal.h @@ -104,7 +104,8 @@ int target_get_pr_transport_id(struct se_node_acl *nacl, struct t10_pr_registration *pr_reg, int *format_code, unsigned char *buf); bool target_parse_pr_out_transport_id(struct se_portal_group *tpg, - char *buf, u32 *out_tid_len, char **port_nexus_ptr, char *i_str); + char *buf, u32 buf_len, u32 *out_tid_len, + char **port_nexus_ptr, char *i_str); /* target_core_hba.c */ struct se_hba *core_alloc_hba(const char *, u32, u32); diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index 11790f2c5d80f..0b19997c2edde 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -1573,7 +1573,7 @@ core_scsi3_decode_spec_i_port( iport_ptr = NULL; tid_found = target_parse_pr_out_transport_id(tmp_tpg, - ptr, &tid_len, &iport_ptr, i_str); + ptr, tpdl, &tid_len, &iport_ptr, i_str); if (!tid_found) continue; /* @@ -3285,7 +3285,7 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key, goto out; } tid_found = target_parse_pr_out_transport_id(dest_se_tpg, - &buf[24], &tmp_tid_len, &iport_ptr, initiator_str); + &buf[24], tid_len, &tmp_tid_len, &iport_ptr, initiator_str); if (!tid_found) { pr_err("SPC-3 PR REGISTER_AND_MOVE: Unable to locate" " initiator_str from Transport ID\n"); From ef2ee18fec92088c7d8877baf7674e89389ccd66 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Wed, 10 Jun 2026 04:22:48 +0000 Subject: [PATCH 2550/2758] scsi: target: core: Fix iSCSI ISID use-after-free in REGISTER AND MOVE commit fda6a1f3c3d7047b5ce5654487649c2daa738bfc upstream. core_scsi3_emulate_pro_register_and_move() maps the PERSISTENT RESERVE OUT parameter list with transport_kmap_data_sg() and parses the destination TransportID with target_parse_pr_out_transport_id(). For an iSCSI TransportID (FORMAT CODE 01b), iscsi_parse_pr_out_transport_id() returns the ISID in iport_ptr as a raw pointer into that mapped buffer. The function then unmaps the buffer with transport_kunmap_data_sg() before dereferencing iport_ptr in strcmp(), __core_scsi3_locate_pr_reg() and core_scsi3_alloc_registration(). When the parameter list spans more than one page (PARAMETER LIST LENGTH > 4096), transport_kmap_data_sg() uses vmap() and transport_kunmap_data_sg() does vunmap(), so the kernel virtual address backing iport_ptr is torn down and every subsequent dereference is a use-after-free read of the unmapped region. Keep the parameter list mapped until iport_ptr is no longer needed: drop the early transport_kunmap_data_sg() and unmap once on the success path, right before returning. The error paths already unmap through the existing "if (buf) transport_kunmap_data_sg(cmd)" at the out: label, which now runs on every post-map error exit because buf is no longer cleared early. Only reads of the mapping happen while spinlocks are held; the map and unmap calls remain outside any lock. The sibling caller core_scsi3_decode_spec_i_port() already uses the buffer before unmapping it and is left unchanged. Fixes: 4949314c7283 ("target: Allow control CDBs with data > 1 page") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Reviewed-by: John Garry Reviewed-by: David Disseldorp Link: https://patch.msgid.link/20260610042245.35473-1-hexlabsecurity@proton.me Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/target/target_core_pr.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index 0b19997c2edde..1a77b4bb62b00 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -3293,9 +3293,6 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key, goto out; } - transport_kunmap_data_sg(cmd); - buf = NULL; - pr_debug("SPC-3 PR [%s] Extracted initiator %s identifier: %s" " %s\n", dest_tf_ops->fabric_name, (iport_ptr != NULL) ? "port" : "device", initiator_str, (iport_ptr != NULL) ? @@ -3532,6 +3529,11 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key, core_scsi3_update_and_write_aptpl(cmd->se_dev, aptpl); core_scsi3_put_pr_reg(dest_pr_reg); + /* + * iport_ptr aliases the PR-OUT parameter list mapped above, so the + * buffer is unmapped only here on success (and at out: on error). + */ + transport_kunmap_data_sg(cmd); return 0; out: if (buf) From 747eaead2db298abfda2aa505f6d03775b40fe5f Mon Sep 17 00:00:00 2001 From: WenTao Liang Date: Thu, 11 Jun 2026 13:30:37 +0800 Subject: [PATCH 2551/2758] scsi: elx: efct: Fix refcount leak in efct_hw_io_abort() commit 2c007acf7b31c39c08ce4959451ad00b19be4c1f upstream. When efct_hw_reqtag_alloc() fails in efct_hw_io_abort(), the error path returns -ENOSPC without releasing the reference obtained via kref_get_unless_zero() earlier in the function. All other error paths correctly drop the reference. This causes a permanent reference leak on the io_to_abort object. Additionally, the abort_in_progress flag is left set to true on this path, which means future abort attempts for the same I/O will immediately return -EINPROGRESS even though the abort was never submitted, effectively blocking recovery. Fix this by adding the missing kref_put() call and reset abort_in_progress to false, matching the cleanup done in the efct_hw_wq_write() failure path below. Cc: stable@vger.kernel.org Fixes: 63de51327a64 ("scsi: elx: efct: Hardware I/O and SGL initialization") Signed-off-by: WenTao Liang Reviewed-by: Daniel Wagner Link: https://patch.msgid.link/20260611053037.63756-1-vulab@iscas.ac.cn Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/elx/efct/efct_hw.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/elx/efct/efct_hw.c b/drivers/scsi/elx/efct/efct_hw.c index 1838032f64862..b79c6a7ea7912 100644 --- a/drivers/scsi/elx/efct/efct_hw.c +++ b/drivers/scsi/elx/efct/efct_hw.c @@ -1997,6 +1997,8 @@ efct_hw_io_abort(struct efct_hw *hw, struct efct_hw_io *io_to_abort, wqcb = efct_hw_reqtag_alloc(hw, efct_hw_wq_process_abort, io_to_abort); if (!wqcb) { efc_log_err(hw->os, "can't allocate request tag\n"); + io_to_abort->abort_in_progress = false; + kref_put(&io_to_abort->ref, io_to_abort->release); return -ENOSPC; } From 94cbfed191248dc88c23fc881119bb399486ddfd Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Mon, 22 Jun 2026 15:58:44 +0800 Subject: [PATCH 2552/2758] scsi: elx: efct: Fix I/O leak on unsupported additional CDB commit 9cb2d5291dbfe7bed565ead3337047dee9ed1064 upstream. efct_dispatch_fcp_cmd() allocates an efct_io before dispatching an unsolicited FCP command. If the command has an unsupported additional CDB, the function returns -EIO before handing the IO to the SCSI layer. Free the allocated IO before returning from this error path. Fixes: f45ae6aac0a0 ("scsi: elx: efct: Unsolicited FC frame processing routines") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Reviewed-by: Daniel Wagner Link: https://patch.msgid.link/20260622075844.832871-1-haoxiang_li2024@163.com Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/elx/efct/efct_unsol.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/elx/efct/efct_unsol.c b/drivers/scsi/elx/efct/efct_unsol.c index e6addab66a603..6a871a59c9096 100644 --- a/drivers/scsi/elx/efct/efct_unsol.c +++ b/drivers/scsi/elx/efct/efct_unsol.c @@ -385,6 +385,7 @@ efct_dispatch_fcp_cmd(struct efct_node *node, struct efc_hw_sequence *seq) if (cmnd->fc_flags & FCP_CFL_LEN_MASK) { efc_log_err(efct, "Additional CDB not supported\n"); + efct_scsi_io_free(io); return -EIO; } /* From 7d330a1d663381579b9d5dafa642b1b3158a0ce2 Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Mon, 6 Jul 2026 15:46:22 -0700 Subject: [PATCH 2553/2758] fs/resctrl: Fix use-after-free during unmount commit 52fce648607e0d6a76eeb443d78708c49df1c554 upstream. During unmount or failure teardown all mon_data structures that contain monitoring event file private data are freed after which kernfs nodes are removed. However, the RDT_DELETED flag is never set for the statically allocated default resource group. A concurrent reader of an event file associated with the default resource group may, after dropping kernfs active protection, block on rdtgroup_mutex while unmount proceeds to free the file private data and destroy the kernfs node without waiting for the reader. When the mutex is released, the reader wakes up, observes that RDT_DELETED is not set for the default group, and dereferences the already-freed file private data. The scenario can be depicted as follows: CPU0 CPU1 /* * Default resource group's * monitoring data accessible via * kernfs file with kernfs_node::priv * pointing to a struct mon_data. * User opens the file for reading. */ rdtgroup_mondata_show() /* arch encounters fatal error */ rdtgroup_kn_lock_live() resctrl_exit() atomic_inc(&rdtgroup_default.waitcount) cpus_read_lock() kernfs_break_active_protection(kn) mutex_lock(&rdtgroup_mutex) cpus_read_lock() resctrl_fs_teardown() mutex_lock(&rdtgroup_mutex) rmdir_all_sub() mon_put_kn_priv() /* Delete all mon_data structures */ rdtgroup_destroy_root() kernfs_destroy_root() rdtgroup_default.kn = NULL mutex_unlock(&rdtgroup_mutex) /* * rdtgroup_default.flags is empty so * rdtgroup_kn_lock_live() returns * &rdtgroup_default */ md = of->kn->priv; /* md points to freed mon_data */ Set RDT_DELETED for the default group unconditionally since the flag does not lead to the freeing of this statically allocated group. Do not allow a new resctrl mount if there are any waiters on default group of previous mount. A new mount will re-initialize the default group that would appear to waiters from previous mount as though the default group is accessible causing them to access the mon_data structures from the previous mount that have been removed. Fixes: 2a6566038544 ("x86/resctrl: Expand the width of domid by replacing mon_data_bits") Closes: https://sashiko.dev/#/patchset/20260508182143.14592-1-tony.luck%40intel.com?part=2 [1] Reported-by: Sashiko Signed-off-by: Tony Luck Signed-off-by: Reinette Chatre Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Chen Yu Cc: Link: https://patch.msgid.link/49a2ca3ca688f27e1a646cf90e1dc69287021127.1783377598.git.reinette.chatre@intel.com Signed-off-by: Greg Kroah-Hartman --- fs/resctrl/rdtgroup.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c index 5dfdaa6f9d8ff..cdc14f3a3a817 100644 --- a/fs/resctrl/rdtgroup.c +++ b/fs/resctrl/rdtgroup.c @@ -585,14 +585,20 @@ static ssize_t rdtgroup_cpus_write(struct kernfs_open_file *of, * * On resource group creation via a mkdir, an extra kernfs_node reference is * taken to ensure that the rdtgroup structure remains accessible for the - * rdtgroup_kn_unlock() calls where it is removed. + * rdtgroup_kn_unlock() calls where it is removed. The default group is + * statically allocated: it does not have an extra reference but will have + * RDT_DELETED set on unmount to support safe access to its associated files + * via rdtgroup_kn_lock_live/rdtgroup_kn_unlock(). * - * Drop the extra reference here, then free the rdtgroup structure. + * For all but the default group: drop the extra reference, then free the + * rdtgroup structure. * * Return: void */ static void rdtgroup_remove(struct rdtgroup *rdtgrp) { + if (rdtgrp == &rdtgroup_default) + return; kernfs_put(rdtgrp->kn); kfree(rdtgrp); } @@ -2802,6 +2808,12 @@ static int rdt_get_tree(struct fs_context *fc) goto out; } + /* Avoid races from pending operations from a previous mount */ + if (atomic_read(&rdtgroup_default.waitcount) != 0) { + ret = -EBUSY; + goto out; + } + ret = setup_rmid_lru_list(); if (ret) goto out; @@ -3164,6 +3176,7 @@ static void resctrl_fs_teardown(void) mon_put_kn_priv(); rdt_pseudo_lock_release(); rdtgroup_default.mode = RDT_MODE_SHAREABLE; + rdtgroup_default.flags = RDT_DELETED; closid_exit(); schemata_list_destroy(); rdtgroup_destroy_root(); @@ -4264,6 +4277,7 @@ static int rdtgroup_setup_root(struct rdt_fs_context *ctx) ctx->kfc.root = rdt_root; rdtgroup_default.kn = kernfs_root_to_node(rdt_root); + rdtgroup_default.flags = 0; return 0; } From bf0b58ba489d0bdaa18c7dd8beeaeb954dd7dbb7 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:24:47 -0700 Subject: [PATCH 2554/2758] Input: ims-pcu - fix use-after-free and double-free in disconnect commit 462a999917755a3bf77448dfd64307963cf0a9f0 upstream. ims_pcu_disconnect() only intended to perform cleanup when the primary (control) interface is unbound. However, it currently relies on the interface class to distinguish between control and data interfaces. A malicious device could present a data interface with the same class as the control interface, leading to premature cleanup and potential use-after-free or double-free. Switch to verifying that the interface being disconnected is indeed the control interface. Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/misc/ims-pcu.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 7a1cb9333f53c..9f17ac4031a40 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -2071,7 +2071,6 @@ static int ims_pcu_probe(struct usb_interface *intf, static void ims_pcu_disconnect(struct usb_interface *intf) { struct ims_pcu *pcu = usb_get_intfdata(intf); - struct usb_host_interface *alt = intf->cur_altsetting; usb_set_intfdata(intf, NULL); @@ -2079,7 +2078,7 @@ static void ims_pcu_disconnect(struct usb_interface *intf) * See if we are dealing with control or data interface. The cleanup * happens when we unbind primary (control) interface. */ - if (alt->desc.bInterfaceClass != USB_CLASS_COMM) + if (intf != pcu->ctrl_intf) return; ims_pcu_stop_io(pcu); From 73e6687be0c1c323a8ec5b733f29440a93e08ff2 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 31 May 2026 21:42:35 -0700 Subject: [PATCH 2555/2758] Input: ims-pcu - only expose sysfs attributes on control interface commit 001428ea4d2c371107cb984108e266adf99f1f1e upstream. When the driver was converted to use the driver core to instantiate device attributes (via .dev_groups in the usb_driver structure), the attributes started appearing on all interfaces bound to the driver. Since the ims-pcu driver manually claims the secondary data interface during probe, the driver core automatically creates the sysfs attributes for that interface as well. However, the driver only supports these attributes on the primary control interface. Data interfaces lack the necessary descriptors and internal state to handle these requests, and accessing them can lead to unexpected behavior or crashes. Fix this by updating the is_visible() callbacks for both the main and OFN attribute groups to verify that the interface being accessed is indeed the control interface. Fixes: 204d18a7a0c6 ("Input: ims-pcu - use driver core to instantiate device attributes") Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Cc: stable@vger.kernel.org Link: https://patch.msgid.link/ahp23lj4_vXIeUBl@google.com Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/misc/ims-pcu.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 9f17ac4031a40..a2b894546eafd 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -1249,6 +1250,9 @@ static umode_t ims_pcu_is_attr_visible(struct kobject *kobj, struct ims_pcu *pcu = usb_get_intfdata(intf); umode_t mode = attr->mode; + if (intf != pcu->ctrl_intf) + return 0; + if (pcu->bootloader_mode) { if (attr != &dev_attr_update_firmware_status.attr && attr != &dev_attr_update_firmware.attr && @@ -1488,6 +1492,9 @@ static umode_t ims_pcu_ofn_is_attr_visible(struct kobject *kobj, struct ims_pcu *pcu = usb_get_intfdata(intf); umode_t mode = attr->mode; + if (intf != pcu->ctrl_intf) + return SYSFS_GROUP_INVISIBLE; + /* * PCU-B devices, both GEN_1 and GEN_2 do not have OFN sensor. */ From 8c3095c4329172cb4ad7997dbdf050441546299f Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:22:55 -0700 Subject: [PATCH 2556/2758] Input: ims-pcu - release data interface on disconnect commit 441c510a649c8ddce38aa0311334ed8bb546b36c upstream. During probe the driver claims the data interface, but it never releases it. Release it in disconnect to avoid leaving it permanently claimed. Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/misc/ims-pcu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index a2b894546eafd..a349c2d3eb044 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -2096,6 +2096,7 @@ static void ims_pcu_disconnect(struct usb_interface *intf) ims_pcu_destroy_application_mode(pcu); ims_pcu_buffers_free(pcu); + usb_driver_release_interface(&ims_pcu_driver, pcu->data_intf); kfree(pcu); } From cbfa059dfb48b9aa322e34fd44a093d9ca29ad7e Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:29:26 -0700 Subject: [PATCH 2557/2758] Input: ims-pcu - validate control endpoint type commit baf56975806534268e24acf9a8abb1c447ce11e9 upstream. The driver currently assumes that the first endpoint of the control interface is an interrupt IN endpoint without verifying it. A malicious device could provide a different endpoint type, which would then be passed to usb_fill_int_urb(), potentially leading to kernel warnings or undefined behavior. Verify that the control endpoint is an interrupt IN endpoint. Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/misc/ims-pcu.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index a349c2d3eb044..90aecef4b5166 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -1709,6 +1709,12 @@ static int ims_pcu_parse_cdc_data(struct usb_interface *intf, struct ims_pcu *pc return -ENODEV; pcu->ep_ctrl = &alt->endpoint[0].desc; + if (!usb_endpoint_is_int_in(pcu->ep_ctrl)) { + dev_err(pcu->dev, + "Control endpoint is not INTERRUPT IN\n"); + return -EINVAL; + } + pcu->max_ctrl_size = usb_endpoint_maxp(pcu->ep_ctrl); pcu->data_intf = usb_ifnum_to_if(pcu->udev, From f3c63aecca90be42b66cdc66de7c4a6104104d17 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:40:54 -0700 Subject: [PATCH 2558/2758] Input: ims-pcu - add response length checks commit 48c9d92fd4ee3a8f5d2cb46c802a0eff8e67c79c upstream. The driver processes response data from device buffers without verifying that the device actually sent enough data. This can lead to out-of-bounds reads or processing stale data. Add checks for the expected response length before accessing the buffers. Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/misc/ims-pcu.c | 87 +++++++++++++++++++++++++++++++++--- 1 file changed, 81 insertions(+), 6 deletions(-) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 90aecef4b5166..3a954db6bf609 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -407,7 +407,16 @@ static void ims_pcu_destroy_gamepad(struct ims_pcu *pcu) static void ims_pcu_report_events(struct ims_pcu *pcu) { - u32 data = get_unaligned_be32(&pcu->read_buf[3]); + u32 data; + + /* 6-axis setting (1 byte) + button data + checksum */ + if (pcu->read_pos < IMS_PCU_DATA_OFFSET + 1 + sizeof(data) + 1) { + dev_warn(pcu->dev, "Short buttons report: %d bytes\n", + pcu->read_pos); + return; + } + + data = get_unaligned_be32(&pcu->read_buf[IMS_PCU_DATA_OFFSET + 1]); ims_pcu_buttons_report(pcu, data & ~IMS_PCU_GAMEPAD_MASK); if (pcu->gamepad) @@ -687,11 +696,19 @@ static int __ims_pcu_execute_bl_command(struct ims_pcu *pcu, return error; } - if (expected_response && pcu->cmd_buf[2] != expected_response) { - dev_err(pcu->dev, - "Unexpected response from bootloader: 0x%02x, wanted 0x%02x\n", - pcu->cmd_buf[2], expected_response); - return -EINVAL; + if (expected_response) { + if (pcu->cmd_buf_len < 3) { + dev_err(pcu->dev, "Short response from bootloader: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + + if (pcu->cmd_buf[2] != expected_response) { + dev_err(pcu->dev, + "Unexpected response from bootloader: 0x%02x, wanted 0x%02x\n", + pcu->cmd_buf[2], expected_response); + return -EINVAL; + } } return 0; @@ -719,6 +736,12 @@ static int ims_pcu_get_info(struct ims_pcu *pcu) return error; } + if (pcu->cmd_buf_len < IMS_PCU_DATA_OFFSET + IMS_PCU_SET_INFO_SIZE + 1) { + dev_err(pcu->dev, "Short GET_INFO response: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + memcpy(pcu->part_number, &pcu->cmd_buf[IMS_PCU_INFO_PART_OFFSET], sizeof(pcu->part_number)); @@ -816,6 +839,12 @@ static int ims_pcu_verify_block(struct ims_pcu *pcu, return error; } + if (pcu->cmd_buf_len < IMS_PCU_BL_DATA_OFFSET + sizeof(*fragment) + len + 1) { + dev_err(pcu->dev, "Short READ_APP response: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + fragment = (void *)&pcu->cmd_buf[IMS_PCU_BL_DATA_OFFSET]; if (get_unaligned_le32(&fragment->addr) != addr || fragment->len != len) { @@ -1010,6 +1039,10 @@ ims_pcu_backlight_get_brightness(struct led_classdev *cdev) error); /* Assume the LED is OFF */ brightness = LED_OFF; + } else if (pcu->cmd_buf_len < IMS_PCU_DATA_OFFSET + 2 + 1) { + dev_err(pcu->dev, "Short GET_BRIGHTNESS response: %d bytes\n", + pcu->cmd_buf_len); + brightness = LED_OFF; } else { brightness = get_unaligned_le16(&pcu->cmd_buf[IMS_PCU_DATA_OFFSET]); @@ -1286,6 +1319,12 @@ static int ims_pcu_read_ofn_config(struct ims_pcu *pcu, u8 addr, u8 *data) if (error) return error; + if (pcu->cmd_buf_len < OFN_REG_RESULT_OFFSET + 2 + 1) { + dev_err(pcu->dev, "Short OFN_GET_CONFIG response: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + result = (s16)get_unaligned_le16(pcu->cmd_buf + OFN_REG_RESULT_OFFSET); if (result < 0) return -EIO; @@ -1306,6 +1345,12 @@ static int ims_pcu_write_ofn_config(struct ims_pcu *pcu, u8 addr, u8 data) if (error) return error; + if (pcu->cmd_buf_len < OFN_REG_RESULT_OFFSET + 2 + 1) { + dev_err(pcu->dev, "Short OFN_SET_CONFIG response: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + result = (s16)get_unaligned_le16(pcu->cmd_buf + OFN_REG_RESULT_OFFSET); if (result < 0) return -EIO; @@ -1844,6 +1889,12 @@ static int ims_pcu_get_device_info(struct ims_pcu *pcu) return error; } + if (pcu->cmd_buf_len < IMS_PCU_DATA_OFFSET + 6 + 1) { + dev_err(pcu->dev, "Short GET_FW_VERSION response: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + snprintf(pcu->fw_version, sizeof(pcu->fw_version), "%02d%02d%02d%02d.%c%c", pcu->cmd_buf[2], pcu->cmd_buf[3], pcu->cmd_buf[4], pcu->cmd_buf[5], @@ -1856,6 +1907,12 @@ static int ims_pcu_get_device_info(struct ims_pcu *pcu) return error; } + if (pcu->cmd_buf_len < IMS_PCU_DATA_OFFSET + 6 + 1) { + dev_err(pcu->dev, "Short GET_BL_VERSION response: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + snprintf(pcu->bl_version, sizeof(pcu->bl_version), "%02d%02d%02d%02d.%c%c", pcu->cmd_buf[2], pcu->cmd_buf[3], pcu->cmd_buf[4], pcu->cmd_buf[5], @@ -1868,6 +1925,12 @@ static int ims_pcu_get_device_info(struct ims_pcu *pcu) return error; } + if (pcu->cmd_buf_len < IMS_PCU_DATA_OFFSET + 1 + 1) { + dev_err(pcu->dev, "Short RESET_REASON response: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + snprintf(pcu->reset_reason, sizeof(pcu->reset_reason), "%02x", pcu->cmd_buf[IMS_PCU_DATA_OFFSET]); @@ -1894,6 +1957,12 @@ static int ims_pcu_identify_type(struct ims_pcu *pcu, u8 *device_id) return error; } + if (pcu->cmd_buf_len < IMS_PCU_DATA_OFFSET + 1 + 1) { + dev_err(pcu->dev, "Short GET_DEVICE_ID response: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + *device_id = pcu->cmd_buf[IMS_PCU_DATA_OFFSET]; dev_dbg(pcu->dev, "Detected device ID: %d\n", *device_id); @@ -1985,6 +2054,12 @@ static int ims_pcu_init_bootloader_mode(struct ims_pcu *pcu) return error; } + if (pcu->cmd_buf_len < IMS_PCU_DATA_OFFSET + 15 + 4 + 1) { + dev_err(pcu->dev, "Short QUERY_DEVICE response: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + pcu->fw_start_addr = get_unaligned_le32(&pcu->cmd_buf[IMS_PCU_DATA_OFFSET + 11]); pcu->fw_end_addr = From 40693fcc88bc75731d8884c2bcb69edf7225bdee Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:30:44 -0700 Subject: [PATCH 2559/2758] Input: ims-pcu - fix DMA mapping violation in line setup commit 8adf4289d945e8990e4336436a97da71d21d2cae upstream. In ims_pcu_line_setup(), the driver uses pcu->cmd_buf as a transfer buffer for usb_control_msg(). However, pcu->cmd_buf is embedded in the struct ims_pcu allocation, which violates DMA mapping rules regarding cacheline alignment. Use a heap-allocated buffer for the line coding data instead. Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/misc/ims-pcu.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 3a954db6bf609..54ac2123eced3 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -1841,11 +1841,16 @@ static void ims_pcu_stop_io(struct ims_pcu *pcu) static int ims_pcu_line_setup(struct ims_pcu *pcu) { struct usb_host_interface *interface = pcu->ctrl_intf->cur_altsetting; - struct usb_cdc_line_coding *line = (void *)pcu->cmd_buf; + struct usb_cdc_line_coding *line __free(kfree) = + kmalloc(sizeof(*line), GFP_KERNEL); int error; - memset(line, 0, sizeof(*line)); + if (!line) + return -ENOMEM; + line->dwDTERate = cpu_to_le32(57600); + line->bCharFormat = USB_CDC_1_STOP_BITS; + line->bParityType = USB_CDC_NO_PARITY; line->bDataBits = 8; error = usb_control_msg(pcu->udev, usb_sndctrlpipe(pcu->udev, 0), From 47a9889a9325b87698b6d6eaf3187a9af6e4773d Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:25:31 -0700 Subject: [PATCH 2560/2758] Input: ims-pcu - fix firmware leak in async update commit d48795b5cd6828d36b707e8d62fc9e5c90e004ab upstream. The firmware object was not being released if validation failed. Use __free(firmware) to ensure the firmware is always released. Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/misc/ims-pcu.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 54ac2123eced3..c11d8cdf4c889 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -964,9 +964,10 @@ static int ims_pcu_handle_firmware_update(struct ims_pcu *pcu, return retval; } -static void ims_pcu_process_async_firmware(const struct firmware *fw, +static void ims_pcu_process_async_firmware(const struct firmware *_fw, void *context) { + const struct firmware *fw __free(firmware) = _fw; struct ims_pcu *pcu = context; int error; @@ -986,8 +987,6 @@ static void ims_pcu_process_async_firmware(const struct firmware *fw, scoped_guard(mutex, &pcu->cmd_mutex) ims_pcu_handle_firmware_update(pcu, fw); - release_firmware(fw); - out: complete(&pcu->async_firmware_done); } From e555f00621bdeeaaafd0da4e876fe249e32ebab3 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:29:41 -0700 Subject: [PATCH 2561/2758] Input: ims-pcu - fix logic error in packet reset commit 2c9b85a14abb4811e8d4773ccd13559e59792efb upstream. ims_pcu_reset_packet() incorrectly sets have_stx to true, which implies that the start-of-packet delimiter has already been received. This causes the protocol parser to skip waiting for the next STX byte and potentially process garbage data. Correctly set have_stx to false when resetting the packet state. Fixes: 875115b82c29 ("Input: ims-pcu - fix heap-buffer-overflow in ims_pcu_process_data()") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/misc/ims-pcu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index c11d8cdf4c889..761084f92f943 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -450,7 +450,7 @@ static void ims_pcu_handle_response(struct ims_pcu *pcu) static void ims_pcu_reset_packet(struct ims_pcu *pcu) { - pcu->have_stx = true; + pcu->have_stx = false; pcu->have_dle = false; pcu->read_pos = 0; pcu->check_sum = 0; From f97bfc1a0766802a99167b3dc62d1ee7dca929fe Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:30:21 -0700 Subject: [PATCH 2562/2758] Input: ims-pcu - fix out-of-bounds read in ims_pcu_irq() debug logging commit 403b0a6970b1084bb27907c0f8225801fdd0fe1d upstream. The debug logging in ims_pcu_irq() unconditionally prints data from pcu->urb_in_buf. However, if the interrupt fired for pcu->urb_ctrl, the actual data resides in pcu->urb_ctrl_buf. If urb->actual_length for the control URB exceeds pcu->max_in_size, this leads to an out-of-bounds read. Fix this by printing from the correct buffer associated with the URB. Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/misc/ims-pcu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 761084f92f943..38bbced04125a 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -1579,7 +1579,7 @@ static void ims_pcu_irq(struct urb *urb) } dev_dbg(pcu->dev, "%s: received %d: %*ph\n", __func__, - urb->actual_length, urb->actual_length, pcu->urb_in_buf); + urb->actual_length, urb->actual_length, urb->transfer_buffer); if (urb == pcu->urb_in) ims_pcu_process_data(pcu, urb); From bbbe31486cf2d12177462e4a814244c2597c9849 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 21:42:39 -0700 Subject: [PATCH 2563/2758] Input: ims-pcu - fix potential infinite loop in CDC union descriptor parsing commit d4579af29e67ca8722db0a1194227f8015c8981d upstream. The driver parses CDC union descriptors in ims_pcu_get_cdc_union_desc() by iterating through the extra descriptor data. However, it does not verify that the bLength of each descriptor is at least 2. A malicious device could provide a descriptor with bLength = 0, leading to an infinite loop in the driver. Add a check to ensure bLength is at least 2 before proceeding with parsing. Fixes: 628329d52474 (Input: add IMS Passenger Control Unit driver) Cc: stable@vger.kernel.org Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/misc/ims-pcu.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 38bbced04125a..dac92d05cdce6 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -1707,8 +1707,9 @@ ims_pcu_get_cdc_union_desc(struct usb_interface *intf) while (buflen >= sizeof(*union_desc)) { union_desc = (struct usb_cdc_union_desc *)buf; - if (union_desc->bLength > buflen) { - dev_err(&intf->dev, "Too large descriptor\n"); + if (union_desc->bLength < 2 || union_desc->bLength > buflen) { + dev_err(&intf->dev, "Invalid descriptor length: %d\n", + union_desc->bLength); return NULL; } From 025955847e1500ced4719ac178beff6a3b2f0e3c Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:29:04 -0700 Subject: [PATCH 2564/2758] Input: ims-pcu - fix race condition in reset_device sysfs callback commit 411b8c4b274737c3bf08e1e025801161603cfffc upstream. The ims_pcu_reset_device() sysfs callback calls ims_pcu_execute_command() without acquiring pcu->cmd_mutex. This can lead to data races and corruption of the shared command buffer if triggered concurrently with other commands. Acquire pcu->cmd_mutex before calling ims_pcu_execute_command(). Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/misc/ims-pcu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index dac92d05cdce6..3a92b637ab5a0 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -1186,6 +1186,8 @@ static ssize_t ims_pcu_reset_device(struct device *dev, dev_info(pcu->dev, "Attempting to reset device\n"); + guard(mutex)(&pcu->cmd_mutex); + error = ims_pcu_execute_command(pcu, PCU_RESET, &reset_byte, 1); if (error) { dev_info(pcu->dev, From f4cf878dcc4f6f02e7a25294bfaed4361264995e Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:25:11 -0700 Subject: [PATCH 2565/2758] Input: ims-pcu - fix type confusion in CDC union descriptor parsing commit ca459e237bc49567649c56bc72e4c602fb92fd67 upstream. The driver currently trusts the bMasterInterface0 from the CDC union descriptor without verifying that it matches the interface being probed. This could lead to the driver overwriting the private data of another interface. Validate that the control interface found in the descriptor is indeed the one we are probing. Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/misc/ims-pcu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 3a92b637ab5a0..b1ff8c70877fe 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -1747,7 +1747,7 @@ static int ims_pcu_parse_cdc_data(struct usb_interface *intf, struct ims_pcu *pc pcu->ctrl_intf = usb_ifnum_to_if(pcu->udev, union_desc->bMasterInterface0); - if (!pcu->ctrl_intf) + if (pcu->ctrl_intf != intf) return -EINVAL; alt = pcu->ctrl_intf->cur_altsetting; From 618cf6b139503ec18ef93ffd663396aaf99b763a Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Fri, 12 Jun 2026 11:27:56 +0800 Subject: [PATCH 2566/2758] mmc: vub300: fix use-after-free on probe failure commit a3b5f242997a3be7404112fd48784881560aea57 upstream. The vub300 driver lifetime-manages its controller state using vub300->kref, with vub300_delete() freeing the mmc host when the last reference is dropped. The probe error path after the inactivity timer has been armed still bypasses that lifetime rule, however, and falls through to mmc_free_host() directly if mmc_add_host() fails. The race window is between arming the inactivity timer and reaching the probe error unwind after mmc_add_host() fails: probe thread timer/workqueue ------------ --------------- kref_init(&vub300->kref) ref = 1 kref_get(&vub300->kref) ref = 2, timer ref add_timer(inactivity_timer) fires after one second | | race window |<----------------------------------------------------> | mmc_add_host(mmc) inactivity timer fires vub300_queue_dead_work() kref_get() ref = 3 queue_work(deadwork) mmc_add_host() fails timer_delete_sync() mmc_free_host(mmc) frees vub300 deadwork runs use-after-free The inactivity timeout is one second, so this would require mmc_add_host() to both fail and take more than one second to do so. This is unlikely to happen in practice, but the error path is still wrong. timer_delete_sync() only waits for the timer callback itself. It does not flush deadwork that the callback may already have queued. As a result, queued deadwork can still hold a kref while the probe error path directly frees the backing mmc host, including the vub300 storage. Fix this by using the same lifetime mechanism as disconnect. Clear vub300->interface so that the timer callback and any queued deadwork return early and drop their references, then drop the initial probe reference and return without falling through to err_free_host. Fixes: 0613ad2401f8 ("mmc: vub300: fix return value check of mmc_add_host()") Signed-off-by: Guangshuo Li Reviewed-by: Johan Hovold Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/vub300.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index 6c3cb2f1c9d32..c1c21e95f5bf8 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -2336,12 +2336,16 @@ static int vub300_probe(struct usb_interface *interface, interface_to_InterfaceNumber(interface)); retval = mmc_add_host(mmc); if (retval) - goto err_delete_timer; + goto err_stop_io; return 0; -err_delete_timer: - timer_delete_sync(&vub300->inactivity_timer); +err_stop_io: + vub300->interface = NULL; + kref_put(&vub300->kref, vub300_delete); + + return retval; + err_free_host: mmc_free_host(mmc); /* From 088873af13590ebde10de2ade847f57a05ec61c6 Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Sun, 28 Jun 2026 00:30:59 +0200 Subject: [PATCH 2567/2758] net/mlx5e: macsec: fix use-after-free of metadata_dst on RX SC delete commit de74d8fd10291763d97b218f09adcc7513c975e4 upstream. When an offloaded MACsec RX SC is deleted, macsec_del_rxsc_ctx() freed the per-SC metadata_dst with metadata_dst_free(), which kfree()s the object unconditionally and ignores the dst reference count. The RX datapath in mlx5e_macsec_offload_handle_rx_skb() looks up the SC under rcu_read_lock() via xa_load(), takes a reference with dst_hold() and attaches the dst to the skb with skb_dst_set(). A reader that already obtained the rx_sc pointer can race with the delete path and operate on freed memory. Fix the owner side by dropping the reference with dst_release() instead of freeing unconditionally, and convert the RX datapath to dst_hold_safe() so a reader racing the SC delete cannot attach a dst whose last reference was just dropped; only attach it when a reference was actually taken. mlx5e_macsec_add_rxsc() also published sc_xarray_element via xa_alloc() before rx_sc->md_dst was allocated and initialised, so a datapath reader that looked the SC up by fs_id could observe rx_sc with md_dst still NULL or, on weakly-ordered architectures, a non-NULL md_dst pointer whose contents were not yet visible. NULL-check the xa_load() result and md_dst on the datapath, and reorder add_rxsc() so the xa_alloc() publish happens only after md_dst is fully initialised; the xarray RCU publish then pairs with the rcu_read_lock()/xa_load() in the datapath. Note: macsec_del_rxsc_ctx() also kfree()s rx_sc->sc_xarray_element without an RCU grace period while the same datapath reads it under rcu_read_lock(); that is a separate pre-existing issue left to a follow-up patch. Found by 0sec automated security-research tooling (https://0sec.ai). Fixes: b7c9400cbc48 ("net/mlx5e: Implement MACsec Rx data path using MACsec skb_metadata_dst") Cc: stable@vger.kernel.org Signed-off-by: Doruk Tan Ozturk Reviewed-by: Tariq Toukan Link: https://patch.msgid.link/20260627223059.29917-1-doruk@0sec.ai Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- .../mellanox/mlx5/core/en_accel/macsec.c | 47 +++++++++++-------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c index 71b3a059c9647..daff53ba7d09f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c @@ -714,34 +714,43 @@ static int mlx5e_macsec_add_rxsc(struct macsec_context *ctx) } sc_xarray_element->rx_sc = rx_sc; - err = xa_alloc(&macsec->sc_xarray, &sc_xarray_element->fs_id, sc_xarray_element, - XA_LIMIT(1, MLX5_MACEC_RX_FS_ID_MAX), GFP_KERNEL); - if (err) { - if (err == -EBUSY) - netdev_err(ctx->netdev, - "MACsec offload: unable to create entry for RX SC (%d Rx SCs already allocated)\n", - MLX5_MACEC_RX_FS_ID_MAX); - goto destroy_sc_xarray_elemenet; - } rx_sc->md_dst = metadata_dst_alloc(0, METADATA_MACSEC, GFP_KERNEL); if (!rx_sc->md_dst) { err = -ENOMEM; - goto erase_xa_alloc; + goto destroy_sc_xarray_elemenet; } rx_sc->sci = ctx_rx_sc->sci; rx_sc->active = ctx_rx_sc->active; - list_add_rcu(&rx_sc->rx_sc_list_element, rx_sc_list); - rx_sc->sc_xarray_element = sc_xarray_element; rx_sc->md_dst->u.macsec_info.sci = rx_sc->sci; + + /* + * Publish the fully-initialised SC last: xa_alloc() makes + * sc_xarray_element->rx_sc (and rx_sc->md_dst) reachable from the RX + * datapath via xa_load(). Doing it only after md_dst is allocated and + * initialised pairs with the rcu_read_lock()/xa_load() in + * mlx5e_macsec_offload_handle_rx_skb(), so a reader can never observe + * a non-NULL md_dst with uninitialised contents. + */ + err = xa_alloc(&macsec->sc_xarray, &sc_xarray_element->fs_id, sc_xarray_element, + XA_LIMIT(1, MLX5_MACEC_RX_FS_ID_MAX), GFP_KERNEL); + if (err) { + if (err == -EBUSY) + netdev_err(ctx->netdev, + "MACsec offload: unable to create entry for RX SC (%d Rx SCs already allocated)\n", + MLX5_MACEC_RX_FS_ID_MAX); + goto destroy_md_dst; + } + + list_add_rcu(&rx_sc->rx_sc_list_element, rx_sc_list); mutex_unlock(&macsec->lock); return 0; -erase_xa_alloc: - xa_erase(&macsec->sc_xarray, sc_xarray_element->fs_id); +destroy_md_dst: + dst_release(&rx_sc->md_dst->dst); destroy_sc_xarray_elemenet: kfree(sc_xarray_element); destroy_rx_sc: @@ -829,7 +838,7 @@ static void macsec_del_rxsc_ctx(struct mlx5e_macsec *macsec, struct mlx5e_macsec */ list_del_rcu(&rx_sc->rx_sc_list_element); xa_erase(&macsec->sc_xarray, rx_sc->sc_xarray_element->fs_id); - metadata_dst_free(rx_sc->md_dst); + dst_release(&rx_sc->md_dst->dst); kfree(rx_sc->sc_xarray_element); kfree_rcu_mightsleep(rx_sc); } @@ -1695,10 +1704,10 @@ void mlx5e_macsec_offload_handle_rx_skb(struct net_device *netdev, rcu_read_lock(); sc_xarray_element = xa_load(&macsec->sc_xarray, fs_id); - rx_sc = sc_xarray_element->rx_sc; - if (rx_sc) { - dst_hold(&rx_sc->md_dst->dst); - skb_dst_set(skb, &rx_sc->md_dst->dst); + rx_sc = sc_xarray_element ? sc_xarray_element->rx_sc : NULL; + if (rx_sc && rx_sc->md_dst) { + if (dst_hold_safe(&rx_sc->md_dst->dst)) + skb_dst_set(skb, &rx_sc->md_dst->dst); } rcu_read_unlock(); From 05b24f68f78ff3a1ef7f015f7327b35886b741f6 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 7 Jul 2026 12:59:11 -0400 Subject: [PATCH 2568/2758] tracing/user_events: Fix use-after-free in user_event_mm_dup() commit 50fd6dd755c6e48a38af2fa4621167eea56829c2 upstream. user_event_mm_dup() walks the parent mm's enabler list locklessly under rcu_read_lock() during fork() (from copy_process()); it does not take event_mutex: rcu_read_lock(); list_for_each_entry_rcu(enabler, &old_mm->enablers, mm_enablers_link) enabler->event = user_event_get(orig->event); user_event_enabler_destroy() removes an enabler from that list with list_del_rcu() and then, without waiting for a grace period, drops the enabler's user_event reference with user_event_put() and frees the enabler with kfree(). A reader that loaded the enabler before the list_del_rcu() can still be walking it, which leads to two use-after-frees: - kfree(enabler) frees the enabler while that reader dereferences enabler->event. - user_event_put() may drop the last reference to the user_event, which is then freed (via delayed_destroy_user_event() on a work queue), while the same reader does user_event_get(orig->event) on it. Both are reachable by an unprivileged task that can open user_events_data: one multithreaded process that registers an enabler and then concurrently unregisters it and calls fork() triggers the race. KASAN reports a slab-use-after-free in user_event_mm_dup() during clone(), with a "refcount_t: addition on 0" warning when the user_event is freed. The enabler use-after-free was found first; the user_event one was reported by XIAO WU, and the earlier enabler-only fix did not address it. Defer both the user_event_put() and the kfree(enabler) to a work item queued with queue_rcu_work(), so they run only after an RCU grace period, once all readers walking the enabler list have finished. The put must run in process context because user_event_put() takes event_mutex on the last reference, so a work queue is used rather than call_rcu(). The now-unlocked put lets the locked argument of user_event_enabler_destroy() be removed; all callers are updated. Fixes: 7235759084a4 ("tracing/user_events: Use remote writes for event enablement") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260707165912.2560537-2-michael.bommarito@gmail.com Reported-by: XIAO WU Closes: https://lore.kernel.org/all/tencent_89647CE40DC452B891C65C94D1B271DE8E07@qq.com/ Suggested-by: Beau Belgrave Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace_events_user.c | 39 ++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/kernel/trace/trace_events_user.c b/kernel/trace/trace_events_user.c index c4ba484f7b38b..8c82ecb735f41 100644 --- a/kernel/trace/trace_events_user.c +++ b/kernel/trace/trace_events_user.c @@ -109,6 +109,9 @@ struct user_event_enabler { /* Track enable bit, flags, etc. Aligned for bitops. */ unsigned long values; + + /* Defer the event put and enabler free past an RCU grace period. */ + struct rcu_work put_rwork; }; /* Bits 0-5 are for the bit to update upon enable/disable (0-63 allowed) */ @@ -396,17 +399,39 @@ static struct user_event_group *user_event_group_create(void) return NULL; }; -static void user_event_enabler_destroy(struct user_event_enabler *enabler, - bool locked) +static void delayed_user_event_enabler_put(struct work_struct *work) { - list_del_rcu(&enabler->mm_enablers_link); + struct user_event_enabler *enabler = container_of(to_rcu_work(work), + struct user_event_enabler, put_rwork); /* No longer tracking the event via the enabler */ - user_event_put(enabler->event, locked); + user_event_put(enabler->event, false); + /* Run from queue_rcu_work(), the RCU grace period has elapsed */ kfree(enabler); } +static void user_event_enabler_destroy(struct user_event_enabler *enabler) +{ + list_del_rcu(&enabler->mm_enablers_link); + + /* + * The enabler is removed from an RCU-traversed list + * (user_event_mm_dup() walks mm->enablers under rcu_read_lock() only), + * and readers there dereference enabler->event and take a new ref on + * it. Both the put of that event reference and the free of the enabler + * therefore have to wait for a grace period so no reader can be looking + * at the enabler or racing the last put of its event. + * + * The put itself must not run in RCU context: when it drops the last + * reference user_event_put() takes event_mutex, which cannot be taken + * from a softirq/RCU callback. Defer both to a work item scheduled + * after a grace period via queue_rcu_work(). + */ + INIT_RCU_WORK(&enabler->put_rwork, delayed_user_event_enabler_put); + queue_rcu_work(system_percpu_wq, &enabler->put_rwork); +} + static int user_event_mm_fault_in(struct user_event_mm *mm, unsigned long uaddr, int attempt) { @@ -464,7 +489,7 @@ static void user_event_enabler_fault_fixup(struct work_struct *work) /* User asked for enabler to be removed during fault */ if (test_bit(ENABLE_VAL_FREEING_BIT, ENABLE_BITOPS(enabler))) { - user_event_enabler_destroy(enabler, true); + user_event_enabler_destroy(enabler); goto out; } @@ -764,7 +789,7 @@ static void user_event_mm_destroy(struct user_event_mm *mm) struct user_event_enabler *enabler, *next; list_for_each_entry_safe(enabler, next, &mm->enablers, mm_enablers_link) - user_event_enabler_destroy(enabler, false); + user_event_enabler_destroy(enabler); mmdrop(mm->mm); kfree(mm); @@ -2645,7 +2670,7 @@ static long user_events_ioctl_unreg(unsigned long uarg) flags |= enabler->values & ENABLE_VAL_COMPAT_MASK; if (!test_bit(ENABLE_VAL_FAULTING_BIT, ENABLE_BITOPS(enabler))) - user_event_enabler_destroy(enabler, true); + user_event_enabler_destroy(enabler); /* Removed at least one */ ret = 0; From bcf7968cb97ce4312588042cf2712f04caff6d8f Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Mon, 22 Jun 2026 15:53:38 +0800 Subject: [PATCH 2569/2758] wifi: libertas_tf: fix use-after-free in lbtf_free_adapter() commit aa6dcd5c8dd9ba1d7d0f60093bcda41c0d6d438d upstream. lbtf_free_adapter() calls timer_delete(&priv->command_timer), which does not wait for a running command_timer_fn() callback. lbtf_free_adapter() runs on the teardown path right before ieee80211_free_hw() frees priv, both in lbtf_remove_card() and in the probe error path. command_timer is armed by mod_timer() in lbtf_cmd() whenever a firmware command is sent. command_timer_fn() dereferences priv. If a command times out as the device is removed, command_timer_fn() runs concurrently with teardown and dereferences priv after it has been freed. This is the same use-after-free that commit 03cc8f90d053 ("wifi: libertas: fix use-after-free in lbs_free_adapter()") fixed in the sibling libertas driver. The libertas_tf variant has the identical pattern and was left unchanged. Use timer_delete_sync() so any in-flight callback completes before priv is freed. Fixes: 06b16ae53192 ("libertas_tf: main.c, data paths and mac80211 handlers") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Link: https://patch.msgid.link/178211481807.2212567.8773346114561900100@maoyixie.com Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/marvell/libertas_tf/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/marvell/libertas_tf/main.c b/drivers/net/wireless/marvell/libertas_tf/main.c index fb20fe31cd363..42be6fa22f9c6 100644 --- a/drivers/net/wireless/marvell/libertas_tf/main.c +++ b/drivers/net/wireless/marvell/libertas_tf/main.c @@ -174,7 +174,7 @@ static void lbtf_free_adapter(struct lbtf_private *priv) { lbtf_deb_enter(LBTF_DEB_MAIN); lbtf_free_cmd_buffer(priv); - timer_delete(&priv->command_timer); + timer_delete_sync(&priv->command_timer); lbtf_deb_leave(LBTF_DEB_MAIN); } From 83f9fb561c1c3917e19f95523dd933c7d30291aa Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 19 Jun 2026 14:52:08 +0200 Subject: [PATCH 2570/2758] locking/rt: Fix the incorrect RCU protection in rt_spin_unlock() commit 89038cc87d80c77e7aa6f42a64b2573b74af339f upstream. rt_spin_unlock() releases the RCU protection before unlocking the lock. That opens the door for the following UAF scenario: T1 T2 spin_lock(&p->lock); rcu_read_lock(); invalidate(p); p = rcu_dereference(ptr); rcu_assign_pointer(ptr, NULL); if (!p) return; spin_unlock(&p->lock); spin_lock(&p->lock) lock(&lock->lock); rcu_read_lock(); kfree_rcu(p); rcu_read_unlock(); .... spin_unlock(&p->lock) rcu_read_unlock(); // Ends grace period rcu_do_batch() kfree(p); UAF -> rt_mutex_cmpxchg_release(&lock->lock...) Regular spinlocks keep preemption disabled accross the unlock operation, which provides full RCU protection, but the RT substitution fails to resemble that. Same applies for the rwlock substitution. Move the rcu_read_unlock() invocation past the unlock operations to match the non-RT semantics. This makes it asymmetric vs. rt_xxx_lock(), but that's harmless as the caller needs to hold RCU read lock across the lock operation. The migrate_enable() call stays before the unlock operation because there is no per CPU operation in the unlock path which would require migration to be kept disabled. Fixes: 0f383b6dc96e ("locking/spinlock: Provide RT variant") Reported-by: syzbot+000c800a02097aaa10ed@syzkaller.appspotmail.com Decoded-by: Jann Horn Signed-off-by: Thomas Gleixner Reviewed-by: Sebastian Andrzej Siewior Acked-by: Al Viro Cc: stable@vger.kernel.org Link: https://patch.msgid.link/87jyrud75z.ffs@fw13 Signed-off-by: Greg Kroah-Hartman --- kernel/locking/spinlock_rt.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/kernel/locking/spinlock_rt.c b/kernel/locking/spinlock_rt.c index db1e11b45de67..1d5e1b3c60bfa 100644 --- a/kernel/locking/spinlock_rt.c +++ b/kernel/locking/spinlock_rt.c @@ -79,10 +79,27 @@ void __sched rt_spin_unlock(spinlock_t *lock) __releases(RCU) { spin_release(&lock->dep_map, _RET_IP_); migrate_enable(); - rcu_read_unlock(); if (unlikely(!rt_mutex_cmpxchg_release(&lock->lock, current, NULL))) rt_mutex_slowunlock(&lock->lock); + + /* + * This must be last to prevent the following UAF: + * + * T1 T2 + * spin_lock(&p->lock); rcu_read_lock(); + * invalidate(p); p = rcu_dereference(ptr); + * rcu_assign_pointer(ptr, NULL); if (!p) return; + * spin_unlock(&p->lock); spin_lock(&p->lock); + * kfree_rcu(p); rcu_read_unlock(); + * .... + * spin_unlock(&p->lock) + * rcu_read_unlock(); // Ends grace period + * rcu_do_batch() + * kfree(p); + * UAF -> rt_mutex_cmpxchg_release(&p->lock.lock...) + */ + rcu_read_unlock(); } EXPORT_SYMBOL(rt_spin_unlock); @@ -262,17 +279,21 @@ void __sched rt_read_unlock(rwlock_t *rwlock) __releases(RCU) { rwlock_release(&rwlock->dep_map, _RET_IP_); migrate_enable(); - rcu_read_unlock(); rwbase_read_unlock(&rwlock->rwbase, TASK_RTLOCK_WAIT); + + /* This must be last. See comment in rt_spin_unlock() */ + rcu_read_unlock(); } EXPORT_SYMBOL(rt_read_unlock); void __sched rt_write_unlock(rwlock_t *rwlock) __releases(RCU) { rwlock_release(&rwlock->dep_map, _RET_IP_); - rcu_read_unlock(); migrate_enable(); rwbase_write_unlock(&rwlock->rwbase); + + /* This must be last. See comment in rt_spin_unlock() */ + rcu_read_unlock(); } EXPORT_SYMBOL(rt_write_unlock); From 571e1f10b5996ba3e4dc63ea77de4aa309c384ce Mon Sep 17 00:00:00 2001 From: Zhan Xusheng Date: Tue, 16 Jun 2026 19:20:17 +0800 Subject: [PATCH 2571/2758] posix-cpu-timers: Use u64 multiplication in update_rlimit_cpu() commit 26aff38fefb1d6cd87e22525f41cc8f1aa61b24f upstream. update_rlimit_cpu() converts the RLIMIT_CPU value to nanoseconds with u64 nsecs = rlim_new * NSEC_PER_SEC; On 32-bit kernels both rlim_new (unsigned long) and NSEC_PER_SEC (1000000000L) are 32-bit, so the multiplication is performed in unsigned long and truncated for rlim_new > 4 seconds before being widened to u64. The same file already casts to u64 for the matching computation in check_process_timers(): u64 softns = (u64)soft * NSEC_PER_SEC; As a result, the truncated value is installed into the CPUCLOCK_PROF expiry cache (nextevt), causing the process CPU timer to be programmed to fire prematurely for any RLIMIT_CPU soft limit >= 5 seconds. The actual SIGXCPU/SIGKILL decision in check_process_timers() already casts to u64 and is therefore correct, so limit enforcement is not broken; only the expiry-cache programming is wrong. Apply the same cast here so both paths convert rlim_cur identically. 64-bit kernels are unaffected. Fixes: 858cf3a8c599 ("timers/itimer: Convert internal cputime_t units to nsec") Signed-off-by: Zhan Xusheng Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260616112017.1681372-1-zhanxusheng@xiaomi.com Signed-off-by: Greg Kroah-Hartman --- kernel/time/posix-cpu-timers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c index 6f3ddb2b1f469..a86e98912705b 100644 --- a/kernel/time/posix-cpu-timers.c +++ b/kernel/time/posix-cpu-timers.c @@ -41,7 +41,7 @@ void posix_cputimers_group_init(struct posix_cputimers *pct, u64 cpu_limit) */ int update_rlimit_cpu(struct task_struct *task, unsigned long rlim_new) { - u64 nsecs = rlim_new * NSEC_PER_SEC; + u64 nsecs = (u64)rlim_new * NSEC_PER_SEC; unsigned long irq_fl; if (!lock_task_sighand(task, &irq_fl)) From f563358661eab1be3ebf62f6f9237092794f83dc Mon Sep 17 00:00:00 2001 From: Cao Ruichuang Date: Tue, 7 Apr 2026 18:26:13 +0800 Subject: [PATCH 2572/2758] selftests/ftrace: Drop invalid top-level local in test_ownership commit 808c447df2fe234eb7d9e08ecf53159d291c104c upstream. test_ownership.tc is sourced by ftracetest under /bin/sh. The script currently declares mount_point with local at file scope, which makes /bin/sh abort with "local: not in a function" before the test can reach the eventfs ownership checks. Replace the top-level local declaration with a normal shell variable so kernels that support the gid= tracefs mount option can run the test at all. Link: https://lore.kernel.org/r/20260407102613.81419-1-create0818@163.com Fixes: 8b55572e51805 ("tracing/selftests: Add tracefs mount options test") Signed-off-by: Cao Ruichuang Reviewed-by: Steven Rostedt (Google) Acked-by: Masami Hiramatsu (Google) Cc: stable@vger.kernel.org Signed-off-by: Shuah Khan Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/ftrace/test.d/00basic/test_ownership.tc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/ftrace/test.d/00basic/test_ownership.tc b/tools/testing/selftests/ftrace/test.d/00basic/test_ownership.tc index e71cc3ad0bdf9..6d00d3c0f493a 100644 --- a/tools/testing/selftests/ftrace/test.d/00basic/test_ownership.tc +++ b/tools/testing/selftests/ftrace/test.d/00basic/test_ownership.tc @@ -6,7 +6,7 @@ original_group=`stat -c "%g" .` original_owner=`stat -c "%u" .` -local mount_point=$(get_mount_point) +mount_point=$(get_mount_point) mount_options=$(get_mnt_options "$mount_point") From 9f7dc355f62c011e4afe8286cf71130d4bfb9d80 Mon Sep 17 00:00:00 2001 From: Bradley Morgan Date: Fri, 19 Jun 2026 16:37:17 +0000 Subject: [PATCH 2573/2758] cpu: hotplug: Preserve per instance callback errors commit 673db10729fb121ea1b16fe57791a0cb9eac1eb5 upstream. cpuhp_invoke_callback() unwinds earlier callbacks for the same hotplug state when one instance fails. The rollback path currently reuses ret, so a successful rollback can hide the original error and make the failed transition look successful. Keep the rollback result separate from the original error. Fixes: 724a86881d03 ("smp/hotplug: Callback vs state-machine consistency") Signed-off-by: Bradley Morgan Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260619163719.12103-1-include@grrlz.net Signed-off-by: Greg Kroah-Hartman --- kernel/cpu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/cpu.c b/kernel/cpu.c index bc4f7a9ba64e6..65c4b0be75d19 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -174,7 +174,7 @@ static int cpuhp_invoke_callback(unsigned int cpu, enum cpuhp_state state, struct cpuhp_step *step = cpuhp_get_step(state); int (*cbm)(unsigned int cpu, struct hlist_node *node); int (*cb)(unsigned int cpu); - int ret, cnt; + int ret, cnt, rollback_ret; if (st->fail == state) { st->fail = CPUHP_INVALID; @@ -238,12 +238,12 @@ static int cpuhp_invoke_callback(unsigned int cpu, enum cpuhp_state state, break; trace_cpuhp_multi_enter(cpu, st->target, state, cbm, node); - ret = cbm(cpu, node); - trace_cpuhp_exit(cpu, st->state, state, ret); + rollback_ret = cbm(cpu, node); + trace_cpuhp_exit(cpu, st->state, state, rollback_ret); /* * Rollback must not fail, */ - WARN_ON_ONCE(ret); + WARN_ON_ONCE(rollback_ret); } return ret; } From 631d53102da9f469c96b882b770336bec095b833 Mon Sep 17 00:00:00 2001 From: Bradley Morgan Date: Fri, 19 Jun 2026 16:37:18 +0000 Subject: [PATCH 2574/2758] cpu: hotplug: Bound hotplug states sysfs output commit 86f436567f2516a0083b210bedc933544826a2c3 upstream. states_show() adds CPU hotplug state names into a single sysfs buffer using sprintf(). With enough registered states, this can write past the end of the PAGE_SIZE buffer. Use sysfs_emit_at() so output is bounded. Fixes: 98f8cdce1db5 ("cpu/hotplug: Add sysfs state interface") Signed-off-by: Bradley Morgan Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260619163719.12103-2-include@grrlz.net Signed-off-by: Greg Kroah-Hartman --- kernel/cpu.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/kernel/cpu.c b/kernel/cpu.c index 65c4b0be75d19..b9acc261b1279 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -2854,21 +2854,17 @@ static const struct attribute_group cpuhp_cpu_attr_group = { .name = "hotplug", }; -static ssize_t states_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t states_show(struct device *dev, struct device_attribute *attr, char *buf) { - ssize_t cur, res = 0; + ssize_t res = 0; int i; mutex_lock(&cpuhp_state_mutex); for (i = CPUHP_OFFLINE; i <= CPUHP_ONLINE; i++) { struct cpuhp_step *sp = cpuhp_get_step(i); - if (sp->name) { - cur = sprintf(buf, "%3d: %s\n", i, sp->name); - buf += cur; - res += cur; - } + if (sp->name) + res += sysfs_emit_at(buf, res, "%3d: %s\n", i, sp->name); } mutex_unlock(&cpuhp_state_mutex); return res; From 282c5214ca4eb3799158c76782646e86d2945d1b Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Wed, 1 Jul 2026 21:12:36 -0700 Subject: [PATCH 2575/2758] net: mana: Validate the packet length reported by the NIC commit 2e2a83b4998af4384e677d3b2ac08565274279bf upstream. Validate the packet length reported in the RX CQE before passing it to skb processing. The CQE is supplied by the NIC device and should not be blindly trusted. Cc: stable@vger.kernel.org Reviewed-by: Haiyang Zhang Signed-off-by: Dexuan Cui Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)") Link: https://patch.msgid.link/20260702041237.617719-2-decui@microsoft.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/microsoft/mana/mana_en.c | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index 8c66290e9ed6d..1b32483caaa81 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -2182,12 +2182,25 @@ static void mana_process_rx_cqe(struct mana_rxq *rxq, struct mana_cq *cq, rxbuf_oob = &rxq->rx_oobs[curr]; WARN_ON_ONCE(rxbuf_oob->wqe_inf.wqe_size_in_bu != 1); - mana_refill_rx_oob(dev, rxq, rxbuf_oob, &old_buf, &old_fp); + if (unlikely(pktlen > rxq->datasize)) { + /* Increase it even if mana_rx_skb() isn't called. */ + rxq->rx_cq.work_done++; - /* Unsuccessful refill will have old_buf == NULL. - * In this case, mana_rx_skb() will drop the packet. - */ - mana_rx_skb(old_buf, old_fp, oob, rxq, i); + ++ndev->stats.rx_dropped; + netdev_warn_once(ndev, + "Dropped oversized RX packet: len=%u, datasize=%u\n", + pktlen, rxq->datasize); + + /* Reuse the RX buffer since rxbuf_oob is unchanged. */ + } else { + + mana_refill_rx_oob(dev, rxq, rxbuf_oob, &old_buf, &old_fp); + + /* Unsuccessful refill will have old_buf == NULL. + * In this case, mana_rx_skb() will drop the packet. + */ + mana_rx_skb(old_buf, old_fp, oob, rxq, i); + } mana_move_wq_tail(rxq->gdma_rq, rxbuf_oob->wqe_inf.wqe_size_in_bu); From bc650dd5ce6434286b96e2b26a41af81f679cc7c Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Wed, 1 Jul 2026 21:12:37 -0700 Subject: [PATCH 2576/2758] net: mana: Sync page pool RX frags for CPU commit c72a0f09c57f92113df69f9b902d11c9e4b132f5 upstream. MANA allocates RX buffers from page pool fragments when frag_count is greater than 1. In that case the buffers remain DMA mapped by page pool and the RX completion path does not call dma_unmap_single(). As a result, the implicit sync-for-CPU normally performed by dma_unmap_single() is missing before the packet data is passed to the networking stack. This breaks RX on configurations which require explicit DMA syncing, for example when booted with swiotlb=force. Fix this by recording the page pool page and DMA sync offset when the RX buffer is allocated, and syncing the received packet range for CPU access before handing the RX buffer to the stack. Fixes: 730ff06d3f5c ("net: mana: Use page pool fragments for RX buffers instead of full pages to improve memory efficiency.") Cc: stable@vger.kernel.org Reviewed-by: Haiyang Zhang Signed-off-by: Dexuan Cui Link: https://patch.msgid.link/20260702041237.617719-3-decui@microsoft.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/microsoft/mana/mana_en.c | 40 +++++++++++++++---- include/net/mana/mana.h | 8 ++++ 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index 1b32483caaa81..352398ae0376e 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -2056,12 +2056,16 @@ static void mana_rx_skb(void *buf_va, bool from_pool, } static void *mana_get_rxfrag(struct mana_rxq *rxq, struct device *dev, - dma_addr_t *da, bool *from_pool) + dma_addr_t *da, bool *from_pool, + struct page **pp_page, u32 *dma_sync_offset) { struct page *page; u32 offset; void *va; + *from_pool = false; + *pp_page = NULL; + *dma_sync_offset = 0; /* Don't use fragments for jumbo frames or XDP where it's 1 fragment * per page. @@ -2099,31 +2103,47 @@ static void *mana_get_rxfrag(struct mana_rxq *rxq, struct device *dev, va = page_to_virt(page) + offset; *da = page_pool_get_dma_addr(page) + offset + rxq->headroom; *from_pool = true; + *pp_page = page; + *dma_sync_offset = offset + rxq->headroom; return va; } /* Allocate frag for rx buffer, and save the old buf */ static void mana_refill_rx_oob(struct device *dev, struct mana_rxq *rxq, - struct mana_recv_buf_oob *rxoob, void **old_buf, - bool *old_fp) + struct mana_recv_buf_oob *rxoob, u32 pktlen, + void **old_buf, bool *old_fp) { + struct page *pp_page; + u32 dma_sync_offset; bool from_pool; dma_addr_t da; void *va; - va = mana_get_rxfrag(rxq, dev, &da, &from_pool); + va = mana_get_rxfrag(rxq, dev, &da, &from_pool, &pp_page, + &dma_sync_offset); if (!va) return; - if (!rxoob->from_pool || rxq->frag_count == 1) + if (!rxoob->from_pool || rxq->frag_count == 1) { dma_unmap_single(dev, rxoob->sgl[0].address, rxq->datasize, DMA_FROM_DEVICE); + } else { + /* The page pool maps the whole page and only syncs for device + * automatically (PP_FLAG_DMA_SYNC_DEV). Sync the received bytes + * for the CPU before they are read: this is required if DMA + * is incoherent or bounce buffers are used. + */ + page_pool_dma_sync_for_cpu(rxq->page_pool, rxoob->pp_page, + rxoob->dma_sync_offset, pktlen); + } *old_buf = rxoob->buf_va; *old_fp = rxoob->from_pool; rxoob->buf_va = va; rxoob->sgl[0].address = da; rxoob->from_pool = from_pool; + rxoob->pp_page = pp_page; + rxoob->dma_sync_offset = dma_sync_offset; } static void mana_process_rx_cqe(struct mana_rxq *rxq, struct mana_cq *cq, @@ -2194,7 +2214,8 @@ static void mana_process_rx_cqe(struct mana_rxq *rxq, struct mana_cq *cq, /* Reuse the RX buffer since rxbuf_oob is unchanged. */ } else { - mana_refill_rx_oob(dev, rxq, rxbuf_oob, &old_buf, &old_fp); + mana_refill_rx_oob(dev, rxq, rxbuf_oob, pktlen, + &old_buf, &old_fp); /* Unsuccessful refill will have old_buf == NULL. * In this case, mana_rx_skb() will drop the packet. @@ -2592,6 +2613,8 @@ static int mana_fill_rx_oob(struct mana_recv_buf_oob *rx_oob, u32 mem_key, struct mana_rxq *rxq, struct device *dev) { struct mana_port_context *mpc = netdev_priv(rxq->ndev); + struct page *pp_page = NULL; + u32 dma_sync_offset = 0; bool from_pool = false; dma_addr_t da; void *va; @@ -2599,13 +2622,16 @@ static int mana_fill_rx_oob(struct mana_recv_buf_oob *rx_oob, u32 mem_key, if (mpc->rxbufs_pre) va = mana_get_rxbuf_pre(rxq, &da); else - va = mana_get_rxfrag(rxq, dev, &da, &from_pool); + va = mana_get_rxfrag(rxq, dev, &da, &from_pool, &pp_page, + &dma_sync_offset); if (!va) return -ENOMEM; rx_oob->buf_va = va; rx_oob->from_pool = from_pool; + rx_oob->pp_page = pp_page; + rx_oob->dma_sync_offset = dma_sync_offset; rx_oob->sgl[0].address = da; rx_oob->sgl[0].size = rxq->datasize; diff --git a/include/net/mana/mana.h b/include/net/mana/mana.h index 8f721cd4e4a7c..4111b93169d2f 100644 --- a/include/net/mana/mana.h +++ b/include/net/mana/mana.h @@ -305,6 +305,14 @@ struct mana_recv_buf_oob { void *buf_va; bool from_pool; /* allocated from a page pool */ + /* head page of the page_pool fragment; valid only when + * from_pool && frag_count > 1. + */ + struct page *pp_page; + /* Fragment offset plus rxq->headroom, passed to + * page_pool_dma_sync_for_cpu(). + */ + u32 dma_sync_offset; /* SGL of the buffer going to be sent as part of the work request. */ u32 num_sge; From e187f6fbc8d621a707020e2d7a9ce23472460ee2 Mon Sep 17 00:00:00 2001 From: Sergio Paracuellos Date: Fri, 26 Jun 2026 08:01:10 +0200 Subject: [PATCH 2577/2758] gpio: mt7621: more robust management of IRQ domain teardown commit 839738536adabae1a7e98ed3fc332ce9cc991d27 upstream. The driver uses devm_gpiochip_add_data() to register the GPIO chips which means the devres subsystem will unregister them only after the function 'mt7621_gpio_remove()' returns. During the window between domain destruction and devres unregistering the GPIO chips, the chips are still fully active. If a consumer or userspace invokes gpiod_to_irq() during this window, 'mt7621_gpio_to_irq()' can dereference the already-freed irq domain pointer. Thus, manage the IRQ domain teardown using 'devm_add_action_or_reset()' to guarantee it is destroyed strictly after the GPIO chips are removed. Cc: stable@vger.kernel.org Reported-by: Sashiko Fixes: a46f2e5720f5 ("gpio: mt7621: fix interrupt banks mapping on gpio chips") Signed-off-by: Sergio Paracuellos Link: https://patch.msgid.link/20260626060112.2498324-3-sergio.paracuellos@gmail.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Greg Kroah-Hartman --- drivers/gpio/gpio-mt7621.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/gpio-mt7621.c b/drivers/gpio/gpio-mt7621.c index a814885ccd5d1..5e973f8c33329 100644 --- a/drivers/gpio/gpio-mt7621.c +++ b/drivers/gpio/gpio-mt7621.c @@ -270,9 +270,9 @@ static const struct irq_chip mt7621_irq_chip = { }; static void -mt7621_gpio_remove(struct platform_device *pdev) +mt7621_gpio_remove(void *data) { - struct mtk *priv = platform_get_drvdata(pdev); + struct mtk *priv = data; int offset, virq; if (priv->gpio_irq > 0) @@ -473,14 +473,14 @@ mediatek_gpio_probe(struct platform_device *pdev) if (mtk->gpio_irq > 0) { ret = mt7621_gpio_irq_setup(pdev, mtk); if (ret) - goto fail; + return ret; } - return 0; + ret = devm_add_action_or_reset(dev, mt7621_gpio_remove, mtk); + if (ret) + return ret; -fail: - mt7621_gpio_remove(pdev); - return ret; + return 0; } static const struct of_device_id mediatek_gpio_match[] = { @@ -491,7 +491,6 @@ MODULE_DEVICE_TABLE(of, mediatek_gpio_match); static struct platform_driver mediatek_gpio_driver = { .probe = mediatek_gpio_probe, - .remove = mt7621_gpio_remove, .driver = { .name = "mt7621_gpio", .of_match_table = mediatek_gpio_match, From 628c63f96f4564fa145f602af2d41daf9532201f Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Fri, 19 Jun 2026 23:24:39 +0800 Subject: [PATCH 2578/2758] gpio: tegra: do not call pinctrl for GPIO direction commit d3e91a95b2b0fc6336dbf3ec90d831a1654d2720 upstream. tegra_gpio_direction_input() and tegra_gpio_direction_output() already program the GPIO controller direction registers directly. The additional pinctrl_gpio_direction_input/output() calls do not add a Tegra pinctrl operation, because the Tegra pinmux ops provide GPIO request/free handling but no gpio_set_direction hook. The extra call still enters the pinctrl core and takes pctldev->mutex. Shared GPIO users can call the direction path while holding their per-line spinlock, so this otherwise redundant pinctrl direction call can sleep in an atomic context. This was found by our static analysis tool and then confirmed by manual review of tegra_gpio_probe(), the Tegra GPIO direction callbacks and the Tegra pinctrl ops. The reviewed path has a default non-sleeping struct gpio_chip while the direction callback still enters the pinctrl mutex path. A directed runtime validation kept the same non-sleeping chip registration and drove: gpio_shared_proxy_direction_output() gpiod_direction_output_raw_commit() tegra_gpio_direction_output() pinctrl_gpio_direction_output() Lockdep reported a sleep-in-atomic warning with the shared GPIO spinlock held and pinctrl_get_device_gpio_range() plus tegra_gpio_direction_output() on the stack. Do not mark the whole chip as can_sleep to paper over this: can_sleep describes whether get()/set() may sleep, and Tegra value access is MMIO. Remove the redundant pinctrl direction calls and keep pinctrl involvement in the existing request/free path. Fixes: 11da90541283 ("gpio: tegra: Fix offset of pinctrl calls") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Link: https://patch.msgid.link/20260619152439.1239561-1-runyu.xiao@seu.edu.cn Signed-off-by: Bartosz Golaszewski Signed-off-by: Greg Kroah-Hartman --- drivers/gpio/gpio-tegra.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index df06b56a2ade8..fa6c8ee92093c 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -172,18 +172,11 @@ static int tegra_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { struct tegra_gpio_info *tgi = gpiochip_get_data(chip); - int ret; tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 0); tegra_gpio_enable(tgi, offset); - ret = pinctrl_gpio_direction_input(chip, offset); - if (ret < 0) - dev_err(tgi->dev, - "Failed to set pinctrl input direction of GPIO %d: %d", - chip->base + offset, ret); - - return ret; + return 0; } static int tegra_gpio_direction_output(struct gpio_chip *chip, @@ -191,19 +184,12 @@ static int tegra_gpio_direction_output(struct gpio_chip *chip, int value) { struct tegra_gpio_info *tgi = gpiochip_get_data(chip); - int ret; tegra_gpio_set(chip, offset, value); tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 1); tegra_gpio_enable(tgi, offset); - ret = pinctrl_gpio_direction_output(chip, offset); - if (ret < 0) - dev_err(tgi->dev, - "Failed to set pinctrl output direction of GPIO %d: %d", - chip->base + offset, ret); - - return ret; + return 0; } static int tegra_gpio_get_direction(struct gpio_chip *chip, From 422a0567cd1b7e6d7892ce1ab748119c0015b623 Mon Sep 17 00:00:00 2001 From: Sergio Paracuellos Date: Fri, 26 Jun 2026 08:01:11 +0200 Subject: [PATCH 2579/2758] gpio: mt7621: be sure IRQ domain is created before exposing GPIO chips commit 0e024f58291dfcb28d98c512002e1a80fad69798 upstream. Function 'mediatek_gpio_bank_probe()' registers three GPIO chips using 'devm_gpiochip_add_data()'. At this point, the chips become live and visible to consumers. However, the IRQ domain isn't allocated and set up until 'mt7621_gpio_irq_setup()' is called after the GPIO chips setup finishes. If a consumer requests a GPIO IRQ concurrently 'mt7621_gpio_to_irq()' can be called and pass a NULL irq domain pointer irq_create_mapping(), that can corrupt the mappings or cause a crash. Fix this possible problem seting up irq domain before GPIO chips setup is performed. Cc: stable@vger.kernel.org Reported-by: Sashiko Fixes: a46f2e5720f5 ("gpio: mt7621: fix interrupt banks mapping on gpio chips") Signed-off-by: Sergio Paracuellos Link: https://patch.msgid.link/20260626060112.2498324-4-sergio.paracuellos@gmail.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Greg Kroah-Hartman --- drivers/gpio/gpio-mt7621.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-mt7621.c b/drivers/gpio/gpio-mt7621.c index 5e973f8c33329..2f695d8016909 100644 --- a/drivers/gpio/gpio-mt7621.c +++ b/drivers/gpio/gpio-mt7621.c @@ -464,12 +464,6 @@ mediatek_gpio_probe(struct platform_device *pdev) mtk->num_gpios = MTK_BANK_WIDTH * MTK_BANK_CNT; platform_set_drvdata(pdev, mtk); - for (i = 0; i < MTK_BANK_CNT; i++) { - ret = mediatek_gpio_bank_probe(dev, i); - if (ret) - return ret; - } - if (mtk->gpio_irq > 0) { ret = mt7621_gpio_irq_setup(pdev, mtk); if (ret) @@ -480,6 +474,12 @@ mediatek_gpio_probe(struct platform_device *pdev) if (ret) return ret; + for (i = 0; i < MTK_BANK_CNT; i++) { + ret = mediatek_gpio_bank_probe(dev, i); + if (ret) + return ret; + } + return 0; } From b90f24527723049af27cd74d668432c7425f956f Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Mon, 29 Jun 2026 16:07:02 +0200 Subject: [PATCH 2580/2758] gpio-f7188x: Add support for NCT6126D version B commit 9a6c0b6ea12746d50cf53d59a7e05fd83f974bda upstream. The Nuvoton NCT6126D Super-I/O is available in two hardware revisions. According to the manufacturer datasheet revision 2.4, version A reports chip ID 0xD283, while version B reports chip ID 0xD284. The driver currently only recognizes only the version A ID. Version B only contains hardware fixes unrelated to the GPIO functionality, so it can be supported by simply adding its chip ID without any other driver changes. Fixes: 3002b8642f01 ("gpio-f7188x: fix chip name and pin count on Nuvoton chip") Cc: stable@vger.kernel.org Signed-off-by: Paul Louvel Link: https://patch.msgid.link/20260629-gpio-f7188x-nct6126d-version-b-v1-1-a06226c02a2d@bootlin.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Greg Kroah-Hartman --- drivers/gpio/gpio-f7188x.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-f7188x.c b/drivers/gpio/gpio-f7188x.c index 4d5b927ad70fd..fb007b9787290 100644 --- a/drivers/gpio/gpio-f7188x.c +++ b/drivers/gpio/gpio-f7188x.c @@ -48,7 +48,8 @@ /* * Nuvoton devices. */ -#define SIO_NCT6126D_ID 0xD283 /* NCT6126D chipset ID */ +#define SIO_NCT6126D_VER_A_ID 0xD283 /* NCT6126D version A chipset ID */ +#define SIO_NCT6126D_VER_B_ID 0xD284 /* NCT6126D version B chipset ID */ #define SIO_LD_GPIO_NUVOTON 0x07 /* GPIO logical device */ @@ -564,7 +565,8 @@ static int __init f7188x_find(int addr, struct f7188x_sio *sio) case SIO_F81865_ID: sio->type = f81865; break; - case SIO_NCT6126D_ID: + case SIO_NCT6126D_VER_A_ID: + case SIO_NCT6126D_VER_B_ID: sio->device = SIO_LD_GPIO_NUVOTON; sio->type = nct6126d; break; From a60a40c9ba30edd06d3fb4215fdf430ed968728e Mon Sep 17 00:00:00 2001 From: Sergio Paracuellos Date: Fri, 26 Jun 2026 08:01:09 +0200 Subject: [PATCH 2581/2758] gpio: mt7621: avoid corruption of shared interrupt trigger state commit 1781172526d1092323af443fa03f00e6de560401 upstream. The bank-shared fields like 'rising' and 'falling' are modified using non-atomic read-modify-write operations. Since every gpio chip instance represents an entire bank of 32 pins, if 'mediatek_gpio_irq_type()' is called concurrently for different IRQs on the same bank a possible overwrite of each other's configuration is possible. Thus, protect this state with 'gpio_generic_lock_irqsave' lock in the same way it is handled in irp_chip 'mediatek_gpio_irq_mask()' and 'mediatek_gpio_irq_unmask()' callbacks. Cc: stable@vger.kernel.org Reported-by: Sashiko Fixes: 4ba9c3afda41 ("gpio: mt7621: Add a driver for MT7621") Signed-off-by: Sergio Paracuellos Link: https://patch.msgid.link/20260626060112.2498324-2-sergio.paracuellos@gmail.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Greg Kroah-Hartman --- drivers/gpio/gpio-mt7621.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpio/gpio-mt7621.c b/drivers/gpio/gpio-mt7621.c index 2f695d8016909..1b0b5247d3c9a 100644 --- a/drivers/gpio/gpio-mt7621.c +++ b/drivers/gpio/gpio-mt7621.c @@ -187,6 +187,8 @@ mediatek_gpio_irq_type(struct irq_data *d, unsigned int type) struct mtk_gc *rg = gpiochip_get_data(gc); u32 mask = BIT(mt7621_gpio_hwirq_to_offset(d->hwirq, rg)); + guard(gpio_generic_lock_irqsave)(&rg->chip); + if (type == IRQ_TYPE_PROBE) { if ((rg->rising | rg->falling | rg->hlevel | rg->llevel) & mask) From f4170f45c251c2c51756efdc5868976369068fe5 Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Sat, 4 Jul 2026 10:40:54 +0200 Subject: [PATCH 2582/2758] gpios: palmas: add .get_direction() op commit db4a79713ed8e252d5e4edf6eaaa80948b6855a2 upstream. Accessing debug/gpio is quite noisy without a get_direction() implementation. To calm that down add an implementation. Fixes: 3d50a2785271 ("gpio: palmas: Add support for Palmas GPIO") Cc: stable@vger.kernel.org Reviewed-by: Linus Walleij Signed-off-by: Andreas Kemnade Link: https://patch.msgid.link/20260704-palmas-getdirection-v2-1-2fd85fee3832@kemnade.info Signed-off-by: Bartosz Golaszewski Signed-off-by: Greg Kroah-Hartman --- drivers/gpio/gpio-palmas.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c index e377f6dd4ccf1..e64ee04877185 100644 --- a/drivers/gpio/gpio-palmas.c +++ b/drivers/gpio/gpio-palmas.c @@ -116,6 +116,24 @@ static int palmas_gpio_input(struct gpio_chip *gc, unsigned offset) return ret; } +static int palmas_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + struct palmas_gpio *pg = gpiochip_get_data(gc); + struct palmas *palmas = pg->palmas; + unsigned int val; + unsigned int reg; + int ret; + int gpio16 = (offset/8); + + offset %= 8; + reg = (gpio16) ? PALMAS_GPIO_DATA_DIR2 : PALMAS_GPIO_DATA_DIR; + ret = palmas_read(palmas, PALMAS_GPIO_BASE, reg, &val); + if (ret) + return ret; + + return (val & BIT(offset)) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; +} + static int palmas_gpio_to_irq(struct gpio_chip *gc, unsigned offset) { struct palmas_gpio *pg = gpiochip_get_data(gc); @@ -165,6 +183,7 @@ static int palmas_gpio_probe(struct platform_device *pdev) palmas_gpio->gpio_chip.can_sleep = true; palmas_gpio->gpio_chip.direction_input = palmas_gpio_input; palmas_gpio->gpio_chip.direction_output = palmas_gpio_output; + palmas_gpio->gpio_chip.get_direction = palmas_gpio_get_direction; palmas_gpio->gpio_chip.to_irq = palmas_gpio_to_irq; palmas_gpio->gpio_chip.set = palmas_gpio_set; palmas_gpio->gpio_chip.get = palmas_gpio_get; From 99ae3248b33df94201915d9c32e7470cdf08cfcd Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Thu, 18 Jun 2026 15:08:17 +0800 Subject: [PATCH 2583/2758] net: sit: require CAP_NET_ADMIN in the device netns for changelink commit 27ccb68e7cccead5d8c611665a45d23032d468b3 upstream. ipip6_changelink() operates on at most two netns, dev_net(dev) and the tunnel link netns t->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in t->net can rewrite a tunnel that lives in t->net. Gate ipip6_changelink() on rtnl_dev_link_net_capable() at its top, before any attribute is parsed. sit was the one tunnel type not covered by the recent series that added this check to the other changelink() handlers. Fixes: 5e6700b3bf98 ("sit: add support of x-netns") Link: https://lore.kernel.org/netdev/20260612085941.3158249-1-maoyixie.tju@gmail.com/ Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Nicolas Dichtel Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260618070817.3378283-1-maoyixie.tju@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv6/sit.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index b41e231a669be..34db8b886bd25 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -1611,6 +1611,9 @@ static int ipip6_changelink(struct net_device *dev, struct nlattr *tb[], __u32 fwmark = t->fwmark; int err; + if (!rtnl_dev_link_net_capable(dev, net)) + return -EPERM; + if (dev == sitn->fb_tunnel_dev) return -EINVAL; From b3763f7e22ecaa7ad79bf44bf41816d488edbcf8 Mon Sep 17 00:00:00 2001 From: Philippe Schenker Date: Thu, 18 Jun 2026 11:30:24 +0200 Subject: [PATCH 2584/2758] net: ethernet: ti: icssg: guard PA stat lookups commit 27b9daba50609335db6ca81e4cccf50ded21ec76 upstream. icssg_ndo_get_stats64() unconditionally calls emac_get_stat_by_name() with FW PA stat names regardless of whether the PA stats block is present on the hardware. emac_get_stat_by_name() already guards the PA stats lookup with `if (emac->prueth->pa_stats)`; when that pointer is NULL the lookup falls through to netdev_err() and returns -EINVAL. Because ndo_get_stats64 is polled regularly by the networking stack this produces thousands of log entries of the form: icssg-prueth icssg1-eth end0: Invalid stats FW_RX_ERROR A secondary consequence is that the int(-EINVAL) return value is implicitly widened to a near-ULLONG_MAX unsigned value when accumulated into the __u64 fields of rtnl_link_stats64, silently corrupting the rx_errors, rx_dropped and tx_dropped counters reported by `ip -s link`. Every other PA-aware code path in the driver is already guarded with the same `if (emac->prueth->pa_stats)` check. Apply the same guard here. Fixes: 0d15a26b247d ("net: ti: icssg-prueth: Add ICSSG FW Stats") Signed-off-by: Philippe Schenker Reviewed-by: Simon Horman Signed-off-by: Greg Kroah-Hartman Cc: danishanwar@ti.com Cc: rogerq@kernel.org Cc: linux-arm-kernel@lists.infradead.org Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260618093037.3448858-1-dev@pschenker.ch Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/ti/icssg/icssg_common.c | 49 +++++++++++--------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/drivers/net/ethernet/ti/icssg/icssg_common.c b/drivers/net/ethernet/ti/icssg/icssg_common.c index 6973d47142463..4a7d1a6f470b1 100644 --- a/drivers/net/ethernet/ti/icssg/icssg_common.c +++ b/drivers/net/ethernet/ti/icssg/icssg_common.c @@ -1651,28 +1651,35 @@ void icssg_ndo_get_stats64(struct net_device *ndev, stats->rx_over_errors = emac_get_stat_by_name(emac, "rx_over_errors"); stats->multicast = emac_get_stat_by_name(emac, "rx_multicast_frames"); - stats->rx_errors = ndev->stats.rx_errors + - emac_get_stat_by_name(emac, "FW_RX_ERROR") + - emac_get_stat_by_name(emac, "FW_RX_EOF_SHORT_FRMERR") + - emac_get_stat_by_name(emac, "FW_RX_B0_DROP_EARLY_EOF") + - emac_get_stat_by_name(emac, "FW_RX_EXP_FRAG_Q_DROP") + - emac_get_stat_by_name(emac, "FW_RX_FIFO_OVERRUN"); - stats->rx_dropped = ndev->stats.rx_dropped + - emac_get_stat_by_name(emac, "FW_DROPPED_PKT") + - emac_get_stat_by_name(emac, "FW_INF_PORT_DISABLED") + - emac_get_stat_by_name(emac, "FW_INF_SAV") + - emac_get_stat_by_name(emac, "FW_INF_SA_DL") + - emac_get_stat_by_name(emac, "FW_INF_PORT_BLOCKED") + - emac_get_stat_by_name(emac, "FW_INF_DROP_TAGGED") + - emac_get_stat_by_name(emac, "FW_INF_DROP_PRIOTAGGED") + - emac_get_stat_by_name(emac, "FW_INF_DROP_NOTAG") + - emac_get_stat_by_name(emac, "FW_INF_DROP_NOTMEMBER"); + stats->rx_errors = ndev->stats.rx_errors; + stats->rx_dropped = ndev->stats.rx_dropped; stats->tx_errors = ndev->stats.tx_errors; - stats->tx_dropped = ndev->stats.tx_dropped + - emac_get_stat_by_name(emac, "FW_RTU_PKT_DROP") + - emac_get_stat_by_name(emac, "FW_TX_DROPPED_PACKET") + - emac_get_stat_by_name(emac, "FW_TX_TS_DROPPED_PACKET") + - emac_get_stat_by_name(emac, "FW_TX_JUMBO_FRM_CUTOFF"); + stats->tx_dropped = ndev->stats.tx_dropped; + + if (!emac->prueth->pa_stats) + return; + + stats->rx_errors += + emac_get_stat_by_name(emac, "FW_RX_ERROR") + + emac_get_stat_by_name(emac, "FW_RX_EOF_SHORT_FRMERR") + + emac_get_stat_by_name(emac, "FW_RX_B0_DROP_EARLY_EOF") + + emac_get_stat_by_name(emac, "FW_RX_EXP_FRAG_Q_DROP") + + emac_get_stat_by_name(emac, "FW_RX_FIFO_OVERRUN"); + stats->rx_dropped += + emac_get_stat_by_name(emac, "FW_DROPPED_PKT") + + emac_get_stat_by_name(emac, "FW_INF_PORT_DISABLED") + + emac_get_stat_by_name(emac, "FW_INF_SAV") + + emac_get_stat_by_name(emac, "FW_INF_SA_DL") + + emac_get_stat_by_name(emac, "FW_INF_PORT_BLOCKED") + + emac_get_stat_by_name(emac, "FW_INF_DROP_TAGGED") + + emac_get_stat_by_name(emac, "FW_INF_DROP_PRIOTAGGED") + + emac_get_stat_by_name(emac, "FW_INF_DROP_NOTAG") + + emac_get_stat_by_name(emac, "FW_INF_DROP_NOTMEMBER"); + stats->tx_dropped += + emac_get_stat_by_name(emac, "FW_RTU_PKT_DROP") + + emac_get_stat_by_name(emac, "FW_TX_DROPPED_PACKET") + + emac_get_stat_by_name(emac, "FW_TX_TS_DROPPED_PACKET") + + emac_get_stat_by_name(emac, "FW_TX_JUMBO_FRM_CUTOFF"); } EXPORT_SYMBOL_GPL(icssg_ndo_get_stats64); From 0c0a8c7821485f32bdb4923fb22f2dd501a27d8a Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Sun, 21 Jun 2026 11:17:14 +0800 Subject: [PATCH 2585/2758] net: wwan: t7xx: destroy DMA pool on CLDMA late init failure commit 2bd6f26d4ce1e87de4d736b1e8896daf3acf1c0e upstream. t7xx_cldma_late_init() creates md_ctrl->gpd_dmapool before initializing the TX and RX rings. If any ring initialization fails, the error path frees the already initialized rings but leaves the DMA pool allocated. Destroy md_ctrl->gpd_dmapool on the late-init failure path to avoid leaking the DMA pool. Fixes: 39d439047f1d ("net: wwan: t7xx: Add control DMA interface") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Reviewed-by: Loic Poulain Link: https://patch.msgid.link/20260621031714.3605022-1-haoxiang_li2024@163.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/wwan/t7xx/t7xx_hif_cldma.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wwan/t7xx/t7xx_hif_cldma.c b/drivers/net/wwan/t7xx/t7xx_hif_cldma.c index e10cb4f9104e3..2917cee9b8028 100644 --- a/drivers/net/wwan/t7xx/t7xx_hif_cldma.c +++ b/drivers/net/wwan/t7xx/t7xx_hif_cldma.c @@ -1063,6 +1063,9 @@ static int t7xx_cldma_late_init(struct cldma_ctrl *md_ctrl) while (i--) t7xx_cldma_ring_free(md_ctrl, &md_ctrl->tx_ring[i], DMA_TO_DEVICE); + dma_pool_destroy(md_ctrl->gpd_dmapool); + md_ctrl->gpd_dmapool = NULL; + return ret; } From 91850f582783098415a334f8bd0a84c87ff15a8c Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Mon, 22 Jun 2026 12:30:15 +0800 Subject: [PATCH 2586/2758] net: ixp4xx_hss: fix duplicate HDLC netdev allocation commit db818b0e8af7bac16860116a19c341a63d6677b4 upstream. ixp4xx_hss_probe() allocates two HDLC netdevs. The first one is stored in ndev, initialized, and registered with register_hdlc_device(). The second one is stored in port->netdev and later used by the remove path for unregister_hdlc_device() and free_netdev(). This means that the registered netdev is not the same object that is unregistered and freed on remove. It also leaks the first allocation if the second alloc_hdlcdev() call fails, and the first allocation is not checked before ndev is used. Older code allocated the HDLC netdev only once and stored the same object in both the local variable and port->netdev. The buggy conversion split this into two alloc_hdlcdev() calls. A later rename changed the local variable name to ndev, but the underlying mismatch remained. Fix this by allocating the HDLC netdev only once and assigning the same object to port->netdev. Fixes: 99ebe65eb9c0 ("net: ixp4xx_hss: move out assignment in if condition") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260622043015.643637-1-haoxiang_li2024@163.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/wan/ixp4xx_hss.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wan/ixp4xx_hss.c b/drivers/net/wan/ixp4xx_hss.c index 720c5dc889ea3..7f4645ff90aa9 100644 --- a/drivers/net/wan/ixp4xx_hss.c +++ b/drivers/net/wan/ixp4xx_hss.c @@ -1487,11 +1487,11 @@ static int ixp4xx_hss_probe(struct platform_device *pdev) "unable to get CLK internal GPIO\n"); ndev = alloc_hdlcdev(port); - port->netdev = alloc_hdlcdev(port); - if (!port->netdev) { + if (!ndev) { err = -ENOMEM; goto err_plat; } + port->netdev = ndev; SET_NETDEV_DEV(ndev, &pdev->dev); hdlc = dev_to_hdlc(ndev); From f7f45ceb855d9ba1cba594fb3f383255f7013fad Mon Sep 17 00:00:00 2001 From: Zihan Xi Date: Sun, 14 Jun 2026 01:42:39 +0800 Subject: [PATCH 2587/2758] net/sched: act_ct: preserve tc_skb_cb across defragmentation commit 9092e15defbe6c7bc241c306093ca9d358a578e7 upstream. tcf_ct_handle_fragments() calls nf_ct_handle_fragments() without saving and restoring skb->cb. The defrag helper clears IPCB/IP6CB, which aliases the tc_skb_cb/qdisc_skb_cb control buffer. Fragmented traffic through act_ct therefore loses qdisc metadata such as pkt_segs and can trigger WARN_ON_ONCE() in qdisc_pkt_segs() when panic_on_warn is enabled. Save and restore the full tc_skb_cb around nf_ct_handle_fragments(), matching the pattern used by ovs_ct_handle_fragments(). Fixes: ec624fe740b4 ("net/sched: Extend qdisc control block with tc control block") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Signed-off-by: Zihan Xi Signed-off-by: Ren Wei Link: https://patch.msgid.link/510c51217fd7aaf29c6dc298bab8d643fe229b1c.1781358692.git.xizh2024@lzu.edu.cn Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/sched/act_ct.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index f5866a364a74a..be535a261fa04 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -844,11 +844,11 @@ static int tcf_ct_handle_fragments(struct net *net, struct sk_buff *skb, u8 family, u16 zone, bool *defrag) { enum ip_conntrack_info ctinfo; + struct tc_skb_cb cb; struct nf_conn *ct; int err = 0; bool frag; u8 proto; - u16 mru; /* Previously seen (loopback)? Ignore. */ ct = nf_ct_get(skb, &ctinfo); @@ -862,12 +862,13 @@ static int tcf_ct_handle_fragments(struct net *net, struct sk_buff *skb, if (err || !frag) return err; - err = nf_ct_handle_fragments(net, skb, zone, family, &proto, &mru); + cb = *tc_skb_cb(skb); + err = nf_ct_handle_fragments(net, skb, zone, family, &proto, &cb.mru); if (err) return err; *defrag = true; - tc_skb_cb(skb)->mru = mru; + *tc_skb_cb(skb) = cb; return 0; } From 6fed707239c4c1b4d9ca0ee34100afe4900429c6 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Wed, 17 Jun 2026 18:10:39 +1200 Subject: [PATCH 2588/2758] selftests: net: fix file owner for broadcast_ether_dst test commit b8613e9792002add3bf77122868cc06ce142e953 upstream. Ensure the output file is always owned by root (even if tcpdump was compiled with `--with-user`), by passing the `-Z root` argument when invoking it. Cc: stable@vger.kernel.org Reported-by: Edoardo Canepa Closes: https://bugs.launchpad.net/ubuntu-kernel-tests/+bug/2129815 Fixes: bf59028ea8d4 ("selftests: net: add test for destination in broadcast packets") Suggested-by: Edoardo Canepa Tested-by: Ross Porter Signed-off-by: Ross Porter Link: https://patch.msgid.link/20260617061039.79717-2-ross.porter@canonical.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/broadcast_ether_dst.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/broadcast_ether_dst.sh b/tools/testing/selftests/net/broadcast_ether_dst.sh index 334a7eca8a803..cc571f6074297 100755 --- a/tools/testing/selftests/net/broadcast_ether_dst.sh +++ b/tools/testing/selftests/net/broadcast_ether_dst.sh @@ -44,7 +44,7 @@ test_broadcast_ether_dst() { # tcpdump will exit after receiving a single packet # timeout will kill tcpdump if it is still running after 2s timeout 2s ip netns exec "${CLIENT_NS}" \ - tcpdump -i link0 -c 1 -w "${CAPFILE}" icmp &> "${OUTPUT}" & + tcpdump -i link0 -c 1 -w "${CAPFILE}" -Z root icmp &> "${OUTPUT}" & pid=$! slowwait 1 grep -qs "listening" "${OUTPUT}" From 11f68ebc6891d11de5f32b7dee918c5dc18b8de1 Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Tue, 16 Jun 2026 22:24:24 +0800 Subject: [PATCH 2589/2758] net: ena: clean up XDP TX queues when regular TX setup fails commit 1bd6676254b4ab6acd44b662b5e92822c036463a upstream. create_queues_with_size_backoff() creates XDP TX queues before setting up the regular TX path. If the subsequent allocation or creation of regular TX queues fails, the error handling paths omit the teardown of the XDP TX queues, leading to a resource leak. Fix this by explicitly destroying the XDP TX queue subset at the two missing failure points. The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1-rc7. An x86_64 allyesconfig build showed no new warnings. As we do not have an ENA device to test with, no runtime testing was able to be performed. Fixes: 548c4940b9f1 ("net: ena: Implement XDP_TX action") Cc: stable@vger.kernel.org Signed-off-by: Dawei Feng Reviewed-by: Arthur Kiyanovski Tested-by: Arthur Kiyanovski Link: https://patch.msgid.link/20260616142424.4005130-1-dawei.feng@seu.edu.cn Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/amazon/ena/ena_netdev.c | 23 ++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.c b/drivers/net/ethernet/amazon/ena/ena_netdev.c index 92d149d4f0917..5d05020a6d058 100644 --- a/drivers/net/ethernet/amazon/ena/ena_netdev.c +++ b/drivers/net/ethernet/amazon/ena/ena_netdev.c @@ -752,6 +752,18 @@ static void ena_destroy_all_tx_queues(struct ena_adapter *adapter) } } +static void ena_destroy_xdp_tx_queues(struct ena_adapter *adapter) +{ + u16 ena_qid; + int i; + + for (i = adapter->xdp_first_ring; + i < adapter->xdp_first_ring + adapter->xdp_num_queues; i++) { + ena_qid = ENA_IO_TXQ_IDX(i); + ena_com_destroy_io_queue(adapter->ena_dev, ena_qid); + } +} + static void ena_destroy_all_rx_queues(struct ena_adapter *adapter) { u16 ena_qid; @@ -2078,14 +2090,21 @@ static int create_queues_with_size_backoff(struct ena_adapter *adapter) rc = ena_setup_tx_resources_in_range(adapter, 0, adapter->num_io_queues); - if (rc) + if (rc) { + ena_destroy_xdp_tx_queues(adapter); + ena_free_all_io_tx_resources_in_range(adapter, + adapter->xdp_first_ring, + adapter->xdp_num_queues); goto err_setup_tx; + } rc = ena_create_io_tx_queues_in_range(adapter, 0, adapter->num_io_queues); - if (rc) + if (rc) { + ena_destroy_xdp_tx_queues(adapter); goto err_create_tx_queues; + } rc = ena_setup_all_rx_resources(adapter); if (rc) From f97e93ebf2f9b8ef3b87f7a9371255e84d151587 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Fri, 12 Jun 2026 16:59:40 +0800 Subject: [PATCH 2590/2758] net: ip6_vti: require CAP_NET_ADMIN in the device netns for changelink commit e2ac3b242c37dff323a964962e43854f4b1a2b79 upstream. vti6_changelink() operates on at most two netns, dev_net(dev) and the tunnel link netns t->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in t->net can rewrite a tunnel that lives in t->net. Gate vti6_changelink() on rtnl_dev_link_net_capable() at its top, before any attribute is parsed. Reported-by: Xiao Liang Closes: https://lore.kernel.org/netdev/CABAhCOSzP1vaThGV35_VnsRCb=87_CPjPVsTHbq905k8A+BuUg@mail.gmail.com/ Fixes: 61220ab34948 ("vti6: Enable namespace changing") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260612085941.3158249-7-maoyixie.tju@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv6/ip6_vti.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index d871cab6938d3..ab94b3a4ba9c3 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -1046,6 +1046,9 @@ static int vti6_changelink(struct net_device *dev, struct nlattr *tb[], struct __ip6_tnl_parm p; struct vti6_net *ip6n; + if (!rtnl_dev_link_net_capable(dev, net)) + return -EPERM; + ip6n = net_generic(net, vti6_net_id); if (dev == ip6n->fb_tnl_dev) return -EINVAL; From 88b33ee458a6ca5fbef6c53b9dba772da69dab68 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Fri, 12 Jun 2026 16:59:37 +0800 Subject: [PATCH 2591/2758] net: ip_vti: require CAP_NET_ADMIN in the device netns for changelink commit 95cceadbfd52d7239bd730afdda0655287d77425 upstream. vti_changelink() operates on at most two netns, dev_net(dev) and the tunnel link netns t->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in t->net can rewrite a tunnel that lives in t->net. Gate vti_changelink() on rtnl_dev_link_net_capable() at its top, before any attribute is parsed. Reported-by: Xiao Liang Closes: https://lore.kernel.org/netdev/CABAhCOSzP1vaThGV35_VnsRCb=87_CPjPVsTHbq905k8A+BuUg@mail.gmail.com/ Fixes: 895de9a3488a ("vti4: Enable namespace changing") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260612085941.3158249-4-maoyixie.tju@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv4/ip_vti.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index 95b6bb78fcd27..3b80929994a00 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -596,6 +596,9 @@ static int vti_changelink(struct net_device *dev, struct nlattr *tb[], struct ip_tunnel_parm_kern p; __u32 fwmark = t->fwmark; + if (!rtnl_dev_link_net_capable(dev, t->net)) + return -EPERM; + vti_netlink_parms(data, &p, &fwmark); return ip_tunnel_changelink(dev, tb, &p, fwmark); } From d49edcc65e0a37cc9b386a94415c5d6670ca8b71 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Fri, 12 Jun 2026 16:59:36 +0800 Subject: [PATCH 2592/2758] net: ipip: require CAP_NET_ADMIN in the device netns for changelink commit 8211a26324667980a463c069469a818e71207e02 upstream. ipip_changelink() operates on at most two netns, dev_net(dev) and the tunnel link netns t->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in t->net can rewrite a tunnel that lives in t->net. Gate ipip_changelink() on rtnl_dev_link_net_capable() at its top, before any attribute is parsed. Reported-by: Xiao Liang Closes: https://lore.kernel.org/netdev/CABAhCOSzP1vaThGV35_VnsRCb=87_CPjPVsTHbq905k8A+BuUg@mail.gmail.com/ Fixes: 6c742e714d8c ("ipip: add x-netns support") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260612085941.3158249-3-maoyixie.tju@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv4/ipip.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index ff95b1b9908e9..e7378569bd5b6 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -494,6 +494,9 @@ static int ipip_changelink(struct net_device *dev, struct nlattr *tb[], bool collect_md; __u32 fwmark = t->fwmark; + if (!rtnl_dev_link_net_capable(dev, t->net)) + return -EPERM; + if (ip_tunnel_netlink_encap_parms(data, &ipencap)) { int err = ip_tunnel_encap_setup(t, &ipencap); From c38c8b0db3c65b597e7ece317b6cb59de3d15e69 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Fri, 12 Jun 2026 16:59:39 +0800 Subject: [PATCH 2593/2758] net: ip6_gre: require CAP_NET_ADMIN in the device netns for changelink commit f00a50876d2818bd6dc86fa98b3ef360884c53c8 upstream. ip6gre_changelink() and ip6erspan_changelink() operate on at most two netns, dev_net(dev) and the tunnel link netns t->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in t->net can rewrite a tunnel that lives in t->net. Gate both ops on rtnl_dev_link_net_capable() at their top, before any attribute is parsed. Reported-by: Xiao Liang Closes: https://lore.kernel.org/netdev/CABAhCOSzP1vaThGV35_VnsRCb=87_CPjPVsTHbq905k8A+BuUg@mail.gmail.com/ Fixes: 690afc165bb3 ("net: ip6_gre: fix moving ip6gre between namespaces") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260612085941.3158249-6-maoyixie.tju@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv6/ip6_gre.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 365b4059eb203..8ebc99a299c95 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -2047,6 +2047,9 @@ static int ip6gre_changelink(struct net_device *dev, struct nlattr *tb[], struct ip6gre_net *ign = net_generic(t->net, ip6gre_net_id); struct __ip6_tnl_parm p; + if (!rtnl_dev_link_net_capable(dev, t->net)) + return -EPERM; + t = ip6gre_changelink_common(dev, tb, data, &p, extack); if (IS_ERR(t)) return PTR_ERR(t); @@ -2266,6 +2269,9 @@ static int ip6erspan_changelink(struct net_device *dev, struct nlattr *tb[], struct __ip6_tnl_parm p; struct ip6gre_net *ign; + if (!rtnl_dev_link_net_capable(dev, t->net)) + return -EPERM; + ign = net_generic(t->net, ip6gre_net_id); t = ip6gre_changelink_common(dev, tb, data, &p, extack); if (IS_ERR(t)) From d4bcc202a3530c856e1cb183384bc9cc8fddab22 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Fri, 12 Jun 2026 16:59:38 +0800 Subject: [PATCH 2594/2758] net: ip6_tunnel: require CAP_NET_ADMIN in the device netns for changelink commit 2496fa0b7d180b3ad356b514e7ff93bb14e6140a upstream. ip6_tnl_changelink() operates on at most two netns, dev_net(dev) and the tunnel link netns t->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in t->net can rewrite a tunnel that lives in t->net. Gate ip6_tnl_changelink() on rtnl_dev_link_net_capable() at its top, before any attribute is parsed. Reported-by: Xiao Liang Closes: https://lore.kernel.org/netdev/CABAhCOSzP1vaThGV35_VnsRCb=87_CPjPVsTHbq905k8A+BuUg@mail.gmail.com/ Fixes: 0bd8762824e7 ("ip6tnl: add x-netns support") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260612085941.3158249-5-maoyixie.tju@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv6/ip6_tunnel.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index bf1e77f95f181..95da1f087294e 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -2109,6 +2109,9 @@ static int ip6_tnl_changelink(struct net_device *dev, struct nlattr *tb[], struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); struct ip_tunnel_encap ipencap; + if (!rtnl_dev_link_net_capable(dev, net)) + return -EPERM; + if (dev == ip6n->fb_tnl_dev) { if (ip_tunnel_netlink_encap_parms(data, &ipencap)) { /* iproute2 always sets TUNNEL_ENCAP_FLAG_CSUM6, so From d0c880c9f4051100517040d065caff60e913b659 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Tue, 23 Jun 2026 19:43:16 +0800 Subject: [PATCH 2595/2758] octeontx2-af: Free BPID bitmap on setup failure commit 36323f54cd323122a1be89ab2c316a6e55a94e30 upstream. nix_setup_bpids() allocates bp->bpids with rvu_alloc_bitmap(), which uses a plain kcalloc(). If any of the following devm_kcalloc() allocations for the BPID mapping arrays fails, the function returns without freeing the bitmap. Free the BPID bitmap before returning from those error paths. Fixes: d6212d2e41a0 ("octeontx2-af: Create BPIDs free pool") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260623114316.2182271-1-haoxiang_li2024@163.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c index f4c066aff3716..219fc44ab68d4 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -527,19 +527,24 @@ static int nix_setup_bpids(struct rvu *rvu, struct nix_hw *hw, int blkaddr) bp->fn_map = devm_kcalloc(rvu->dev, bp->bpids.max, sizeof(u16), GFP_KERNEL); if (!bp->fn_map) - return -ENOMEM; + goto free_bpids; bp->intf_map = devm_kcalloc(rvu->dev, bp->bpids.max, sizeof(u8), GFP_KERNEL); if (!bp->intf_map) - return -ENOMEM; + goto free_bpids; bp->ref_cnt = devm_kcalloc(rvu->dev, bp->bpids.max, sizeof(u8), GFP_KERNEL); if (!bp->ref_cnt) - return -ENOMEM; + goto free_bpids; return 0; + +free_bpids: + rvu_free_bitmap(&bp->bpids); + bp->bpids.bmap = NULL; + return -ENOMEM; } void rvu_nix_flr_free_bpids(struct rvu *rvu, u16 pcifunc) From 6383248058956f2a52d720b1e9f8921099cdae04 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 20 May 2026 10:16:39 -0400 Subject: [PATCH 2596/2758] ieee802154: admin-gate legacy LLSEC dump operations commit 9c1e0b6d49471a712511d23fc9d06901561135e8 upstream. In net/ieee802154/netlink.c, the legacy IEEE802154_NL family ops table builds the LLSEC dump entries (LLSEC_LIST_KEY, LLSEC_LIST_DEV, LLSEC_LIST_DEVKEY, LLSEC_LIST_SECLEVEL) with IEEE802154_DUMP() which sets no .flags, so generic netlink runs them ungated. The modern nl802154 family admin-gates the equivalent reads via NL802154_CMD_GET_SEC_KEY and friends with .flags = GENL_ADMIN_PERM. Any local uid that can open AF_NETLINK / NETLINK_GENERIC can resolve the "802.15.4 MAC" family and dump LLSEC_LIST_KEY on any wpan netdev that has an LLSEC key installed; the dump handler writes the raw 16-byte AES-128 key bytes (IEEE802154_ATTR_LLSEC_KEY_BYTES, copied verbatim from struct ieee802154_llsec_key.key) into the reply. Recovering the AES key compromises 802.15.4 LLSEC link confidentiality and authenticity, since LLSEC uses CCM* and the same key authenticates and encrypts frames. Impact: any local uid with no capabilities can read the raw 16-byte AES-128 LLSEC key from the kernel keytable on any wpan netdev that has an administrator-installed LLSEC key, by issuing an LLSEC_LIST_KEY dump on the legacy IEEE802154_NL generic-netlink family. Introduce IEEE802154_DUMP_PRIV() mirroring IEEE802154_DUMP() but setting .flags = GENL_ADMIN_PERM, and use it for the four LLSEC dump entries. LIST_PHY and LIST_IFACE retain IEEE802154_DUMP() because the modern nl802154 family exposes their equivalents to unprivileged readers by design (NL802154_CMD_GET_WPAN_PHY and NL802154_CMD_GET_INTERFACE carry "can be retrieved by unprivileged users" annotations). Fixes: 3e9c156e2c21 ("ieee802154: add netlink interfaces for llsec") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Link: https://lore.kernel.org/20260520141640.1149513-2-michael.bommarito@gmail.com Signed-off-by: Stefan Schmidt Signed-off-by: Greg Kroah-Hartman --- net/ieee802154/ieee802154.h | 8 ++++++++ net/ieee802154/netlink.c | 16 ++++++++-------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/net/ieee802154/ieee802154.h b/net/ieee802154/ieee802154.h index c5d91f78301ad..fd9778f705503 100644 --- a/net/ieee802154/ieee802154.h +++ b/net/ieee802154/ieee802154.h @@ -23,6 +23,14 @@ void ieee802154_nl_exit(void); .dumpit = _dump, \ } +#define IEEE802154_DUMP_PRIV(_cmd, _func, _dump) \ + { \ + .cmd = _cmd, \ + .doit = _func, \ + .dumpit = _dump, \ + .flags = GENL_ADMIN_PERM, \ + } + struct genl_info; struct sk_buff *ieee802154_nl_create(int flags, u8 req); diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c index 7d2de4ee6992b..9c9fd14d0ca8b 100644 --- a/net/ieee802154/netlink.c +++ b/net/ieee802154/netlink.c @@ -98,20 +98,20 @@ static const struct genl_small_ops ieee802154_ops[] = { IEEE802154_OP(IEEE802154_SET_MACPARAMS, ieee802154_set_macparams), IEEE802154_OP(IEEE802154_LLSEC_GETPARAMS, ieee802154_llsec_getparams), IEEE802154_OP(IEEE802154_LLSEC_SETPARAMS, ieee802154_llsec_setparams), - IEEE802154_DUMP(IEEE802154_LLSEC_LIST_KEY, NULL, - ieee802154_llsec_dump_keys), + IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_KEY, NULL, + ieee802154_llsec_dump_keys), IEEE802154_OP(IEEE802154_LLSEC_ADD_KEY, ieee802154_llsec_add_key), IEEE802154_OP(IEEE802154_LLSEC_DEL_KEY, ieee802154_llsec_del_key), - IEEE802154_DUMP(IEEE802154_LLSEC_LIST_DEV, NULL, - ieee802154_llsec_dump_devs), + IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_DEV, NULL, + ieee802154_llsec_dump_devs), IEEE802154_OP(IEEE802154_LLSEC_ADD_DEV, ieee802154_llsec_add_dev), IEEE802154_OP(IEEE802154_LLSEC_DEL_DEV, ieee802154_llsec_del_dev), - IEEE802154_DUMP(IEEE802154_LLSEC_LIST_DEVKEY, NULL, - ieee802154_llsec_dump_devkeys), + IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_DEVKEY, NULL, + ieee802154_llsec_dump_devkeys), IEEE802154_OP(IEEE802154_LLSEC_ADD_DEVKEY, ieee802154_llsec_add_devkey), IEEE802154_OP(IEEE802154_LLSEC_DEL_DEVKEY, ieee802154_llsec_del_devkey), - IEEE802154_DUMP(IEEE802154_LLSEC_LIST_SECLEVEL, NULL, - ieee802154_llsec_dump_seclevels), + IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_SECLEVEL, NULL, + ieee802154_llsec_dump_seclevels), IEEE802154_OP(IEEE802154_LLSEC_ADD_SECLEVEL, ieee802154_llsec_add_seclevel), IEEE802154_OP(IEEE802154_LLSEC_DEL_SECLEVEL, From 2953ec261bcf623f8c0d8132f6367259f1ebc308 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 20 May 2026 10:16:40 -0400 Subject: [PATCH 2597/2758] ieee802154: allow legacy LLSEC ADD/DEL ops to pass strict validation commit a6bfdfcc6711d1d5a92e98644359dedc67c0c858 upstream. The LLSEC ADD/DEL doit handlers under the legacy IEEE802154_NL family consume IEEE802154_ATTR_LLSEC_KEY_BYTES and IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS, both declared in net/ieee802154/nl_policy.c as bare length entries with no .type (defaulting to NLA_UNSPEC). Generic netlink strict validation rejects all NLA_UNSPEC attributes via validate_nla(), so every LLSEC_ADD_KEY, LLSEC_DEL_KEY, LLSEC_ADD_DEV, LLSEC_DEL_DEV, LLSEC_ADD_DEVKEY, LLSEC_DEL_DEVKEY, LLSEC_ADD_SECLEVEL, and LLSEC_DEL_SECLEVEL request fails at the dispatcher with "Unsupported attribute" before reaching the handler. The doit path has been silently dead since strict validation became the default for genl families that do not opt out. The dump path is unaffected because dump requests carry no LLSEC attributes to validate, which is why the LLSEC_LIST_KEY read remained reachable (patch 1/2). Introduce IEEE802154_OP_RELAXED() mirroring IEEE802154_OP() but with .validate = GENL_DONT_VALIDATE_STRICT, and use it for the eight legacy LLSEC mutate ops so admin-driven LLSEC configuration via the legacy interface works again. Fixes: 3e9c156e2c21 ("ieee802154: add netlink interfaces for llsec") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Link: https://lore.kernel.org/20260520141640.1149513-3-michael.bommarito@gmail.com Signed-off-by: Stefan Schmidt Signed-off-by: Greg Kroah-Hartman --- net/ieee802154/ieee802154.h | 9 +++++++++ net/ieee802154/netlink.c | 20 ++++++++++---------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/net/ieee802154/ieee802154.h b/net/ieee802154/ieee802154.h index fd9778f705503..e765adc4b88f2 100644 --- a/net/ieee802154/ieee802154.h +++ b/net/ieee802154/ieee802154.h @@ -16,6 +16,15 @@ void ieee802154_nl_exit(void); .flags = GENL_ADMIN_PERM, \ } +#define IEEE802154_OP_RELAXED(_cmd, _func) \ + { \ + .cmd = _cmd, \ + .doit = _func, \ + .dumpit = NULL, \ + .flags = GENL_ADMIN_PERM, \ + .validate = GENL_DONT_VALIDATE_STRICT,\ + } + #define IEEE802154_DUMP(_cmd, _func, _dump) \ { \ .cmd = _cmd, \ diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c index 9c9fd14d0ca8b..cacad21347eca 100644 --- a/net/ieee802154/netlink.c +++ b/net/ieee802154/netlink.c @@ -100,22 +100,22 @@ static const struct genl_small_ops ieee802154_ops[] = { IEEE802154_OP(IEEE802154_LLSEC_SETPARAMS, ieee802154_llsec_setparams), IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_KEY, NULL, ieee802154_llsec_dump_keys), - IEEE802154_OP(IEEE802154_LLSEC_ADD_KEY, ieee802154_llsec_add_key), - IEEE802154_OP(IEEE802154_LLSEC_DEL_KEY, ieee802154_llsec_del_key), + IEEE802154_OP_RELAXED(IEEE802154_LLSEC_ADD_KEY, ieee802154_llsec_add_key), + IEEE802154_OP_RELAXED(IEEE802154_LLSEC_DEL_KEY, ieee802154_llsec_del_key), IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_DEV, NULL, ieee802154_llsec_dump_devs), - IEEE802154_OP(IEEE802154_LLSEC_ADD_DEV, ieee802154_llsec_add_dev), - IEEE802154_OP(IEEE802154_LLSEC_DEL_DEV, ieee802154_llsec_del_dev), + IEEE802154_OP_RELAXED(IEEE802154_LLSEC_ADD_DEV, ieee802154_llsec_add_dev), + IEEE802154_OP_RELAXED(IEEE802154_LLSEC_DEL_DEV, ieee802154_llsec_del_dev), IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_DEVKEY, NULL, ieee802154_llsec_dump_devkeys), - IEEE802154_OP(IEEE802154_LLSEC_ADD_DEVKEY, ieee802154_llsec_add_devkey), - IEEE802154_OP(IEEE802154_LLSEC_DEL_DEVKEY, ieee802154_llsec_del_devkey), + IEEE802154_OP_RELAXED(IEEE802154_LLSEC_ADD_DEVKEY, ieee802154_llsec_add_devkey), + IEEE802154_OP_RELAXED(IEEE802154_LLSEC_DEL_DEVKEY, ieee802154_llsec_del_devkey), IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_SECLEVEL, NULL, ieee802154_llsec_dump_seclevels), - IEEE802154_OP(IEEE802154_LLSEC_ADD_SECLEVEL, - ieee802154_llsec_add_seclevel), - IEEE802154_OP(IEEE802154_LLSEC_DEL_SECLEVEL, - ieee802154_llsec_del_seclevel), + IEEE802154_OP_RELAXED(IEEE802154_LLSEC_ADD_SECLEVEL, + ieee802154_llsec_add_seclevel), + IEEE802154_OP_RELAXED(IEEE802154_LLSEC_DEL_SECLEVEL, + ieee802154_llsec_del_seclevel), }; static const struct genl_multicast_group ieee802154_mcgrps[] = { From 7e3630fbb6aabb844bbf35746dee0bf3894100c7 Mon Sep 17 00:00:00 2001 From: Shitalkumar Gandhi Date: Tue, 21 Apr 2026 13:02:59 +0530 Subject: [PATCH 2598/2758] ieee802154: ca8210: fix cas_ctl leak on spi_async failure commit e09390e439bd7cca30dd10893b1f64802961667a upstream. ca8210_spi_transfer() allocates cas_ctl with kzalloc_obj(GFP_ATOMIC) and relies entirely on the SPI completion callback ca8210_spi_transfer_complete() to free it. The spi_async() API only invokes the completion callback on successful submission. On failure it returns a negative error code without ever queuing the callback, which leaves cas_ctl and its embedded spi_message and spi_transfer orphaned. Every kfree(cas_ctl) in the driver is inside the completion callback, so there is no other reclamation path. ca8210_spi_transfer() is called from ca8210_spi_exchange(), the interrupt handler ca8210_interrupt_handler(), and from the retry path inside the completion callback itself. The exchange and interrupt handler paths loop on -EBUSY, so under sustained SPI bus contention every retry iteration leaks a fresh cas_ctl (~600 bytes per occurrence). Fix it by freeing cas_ctl on the spi_async() error path. While here, correct the misleading error string: the function calls spi_async(), not spi_sync(). Fixes: ded845a781a5 ("ieee802154: Add CA8210 IEEE 802.15.4 device driver") Cc: stable@vger.kernel.org Signed-off-by: Shitalkumar Gandhi Reviewed-by: Miquel Raynal Link: https://lore.kernel.org/20260421073259.2259783-1-shitalkumar.gandhi@cambiumnetworks.com Signed-off-by: Stefan Schmidt Signed-off-by: Greg Kroah-Hartman --- drivers/net/ieee802154/ca8210.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ieee802154/ca8210.c b/drivers/net/ieee802154/ca8210.c index ed4178155a5d6..bf837adfebb21 100644 --- a/drivers/net/ieee802154/ca8210.c +++ b/drivers/net/ieee802154/ca8210.c @@ -919,9 +919,10 @@ static int ca8210_spi_transfer( if (status < 0) { dev_crit( &spi->dev, - "status %d from spi_sync in write\n", + "status %d from spi_async in write\n", status ); + kfree(cas_ctl); } return status; From d8ce67fa6a5e6929f5414e933ff9665176c2bce6 Mon Sep 17 00:00:00 2001 From: Shitalkumar Gandhi Date: Wed, 20 May 2026 16:27:50 +0530 Subject: [PATCH 2599/2758] ieee802154: ca8210: fix pointer truncation in kfifo on 64-bit commit 6d7f7bcf225b2d566176bf6229dbd1252940cb3c upstream. ca8210_test_int_driver_write() and ca8210_test_int_user_read() exchange a kmalloc'd buffer pointer through a struct kfifo, but pass a literal '4' as the byte count to kfifo_in()/kfifo_out(). This is correct on 32-bit (pointer = 4 bytes), but on 64-bit only the low 4 bytes of the 8-byte pointer are written into the FIFO. The reader then reads back 4 bytes into an 8-byte local pointer variable, leaving the upper 4 bytes uninitialized stack data. The first dereference of the reconstructed pointer (fifo_buffer[1]) accesses an arbitrary kernel address and generally results in an oops. Use sizeof(fifo_buffer) so the byte count matches pointer width on every architecture. The driver has no architecture restriction in Kconfig, so any 64-bit build with CONFIG_IEEE802154_CA8210_DEBUGFS=y is exposed. Issue has been latent since the driver was added in 2017 because it is most commonly deployed on 32-bit MCUs. Found via a custom Coccinelle semantic patch hunting for short-byte kfifo I/O on byte-mode kfifos used to shuttle pointers. Fixes: ded845a781a5 ("ieee802154: Add CA8210 IEEE 802.15.4 device driver") Cc: stable@vger.kernel.org Signed-off-by: Shitalkumar Gandhi Reviewed-by: Simon Horman Link: https://lore.kernel.org/20260520105750.30144-1-shitalkumar.gandhi@cambiumnetworks.com Signed-off-by: Stefan Schmidt Signed-off-by: Greg Kroah-Hartman --- drivers/net/ieee802154/ca8210.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ieee802154/ca8210.c b/drivers/net/ieee802154/ca8210.c index bf837adfebb21..01af4f9cf7f29 100644 --- a/drivers/net/ieee802154/ca8210.c +++ b/drivers/net/ieee802154/ca8210.c @@ -595,7 +595,7 @@ static int ca8210_test_int_driver_write( fifo_buffer = kmemdup(buf, len, GFP_KERNEL); if (!fifo_buffer) return -ENOMEM; - kfifo_in(&test->up_fifo, &fifo_buffer, 4); + kfifo_in(&test->up_fifo, &fifo_buffer, sizeof(fifo_buffer)); wake_up_interruptible(&priv->test.readq); return 0; @@ -2526,6 +2526,7 @@ static ssize_t ca8210_test_int_user_read( struct ca8210_priv *priv = filp->private_data; unsigned char *fifo_buffer; unsigned long bytes_not_copied; + unsigned int copied; if (filp->f_flags & O_NONBLOCK) { /* Non-blocking mode */ @@ -2539,7 +2540,8 @@ static ssize_t ca8210_test_int_user_read( ); } - if (kfifo_out(&priv->test.up_fifo, &fifo_buffer, 4) != 4) { + copied = kfifo_out(&priv->test.up_fifo, &fifo_buffer, sizeof(fifo_buffer)); + if (copied != sizeof(fifo_buffer)) { dev_err( &priv->spi->dev, "test_interface: Wrong number of elements popped from upstream fifo\n" From 9f8e7f59b0c2f466be74bd923726b0f5496c27ad Mon Sep 17 00:00:00 2001 From: Ankit Garg Date: Tue, 16 Jun 2026 18:32:08 -0700 Subject: [PATCH 2600/2758] gve: fix header buffer corruption with header-split and HW-GRO commit d676c9a73bdcd8237425dbb826f2bd1a25c36e40 upstream. The DQO RX datapath programs a per-buffer-queue-descriptor header_buf_addr at post time and reads the split header back at completion time. Both the post and the read currently index the header buffer by queue position rather than by the buffer's identity: - post (gve_rx_post_buffers_dqo): header_buf_addr is computed from bufq->tail - read (gve_rx_dqo): the header is read from desc_idx (the completion queue head index) This relies on the buffer-queue index and the completion-queue index being equal for the start of every packet, i.e. on the device consuming posted buffers and returning completions in the exact same order. That assumption does not hold once HW-GRO is enabled with multiple flows: coalesced segments are accepted and completed in an order that may differ from the order buffers were posted, and segments from different flows may interleave. That results in two problems: 1. Wrong header slot on read. Because the read offset is derived from the completion index (desc_idx) while the device wrote the header to the address programmed for the buffer's buf_id, the driver can copy a header belonging to a different packet. This shows up as throughput drop (about 30% drop and large numbers of TCP retransmissions) with header-split and HW-GRO both enabled and many streams. 2. Header buffer reused while still owned by the device. The driver advances bufq->head by one per completion and re-posts buffers based on that. Arrival of N RX completions only guarantees that at least N RX buffer descriptors have been read by the device. It does not guarantee that the device has relinquished the ownership of all the buffers corresponding to those N descriptors. With out-of-order completions (e.g. the completion for a packet copied into buffer N arrives before the completion for a packet copied into buffer N-1), the driver can re-post and overwrite a header buffer that the device is still going to write into, corrupting the header of a packet whose completion has not yet been processed. Fix both issues by indexing the header buffer by buf_id on both the post and read paths. Reading from buf_id's slot is therefore always correct regardless of completion ordering (fixes problem 1). Indexing by buf_id also ties each header slot to the lifetime of its buffer state. A buffer state is only returned to the free/recycle lists when its own completion (buf_id) is processed, so its header slot can only be re-posted after the device is done with it. This makes header slot reuse safe under out-of-order completions (fixes problem 2). Allocate (gve_rx_alloc_hdr_bufs) and free (gve_rx_free_hdr_bufs) the header buffers based on num_buf_states to match the buf_id indexing. Cc: stable@vger.kernel.org Fixes: 5e37d8254e7f ("gve: Add header split data path") Signed-off-by: Ankit Garg Reviewed-by: Praveen Kaligineedi Reviewed-by: Jordan Rhee Reviewed-by: Harshitha Ramamurthy Signed-off-by: Joshua Washington Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260617013208.3781453-1-joshwash@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/google/gve/gve_rx_dqo.c | 28 +++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/google/gve/gve_rx_dqo.c b/drivers/net/ethernet/google/gve/gve_rx_dqo.c index 7924dce719e29..02cba280d81a8 100644 --- a/drivers/net/ethernet/google/gve/gve_rx_dqo.c +++ b/drivers/net/ethernet/google/gve/gve_rx_dqo.c @@ -21,11 +21,13 @@ static void gve_rx_free_hdr_bufs(struct gve_priv *priv, struct gve_rx_ring *rx) { struct device *hdev = &priv->pdev->dev; - int buf_count = rx->dqo.bufq.mask + 1; if (rx->dqo.hdr_bufs.data) { - dma_free_coherent(hdev, priv->header_buf_size * buf_count, - rx->dqo.hdr_bufs.data, rx->dqo.hdr_bufs.addr); + size_t size = + (size_t)priv->header_buf_size * rx->dqo.num_buf_states; + + dma_free_coherent(hdev, size, rx->dqo.hdr_bufs.data, + rx->dqo.hdr_bufs.addr); rx->dqo.hdr_bufs.data = NULL; } } @@ -254,7 +256,7 @@ int gve_rx_alloc_ring_dqo(struct gve_priv *priv, /* Allocate header buffers for header-split */ if (cfg->enable_header_split) - if (gve_rx_alloc_hdr_bufs(priv, rx, buffer_queue_slots)) + if (gve_rx_alloc_hdr_bufs(priv, rx, rx->dqo.num_buf_states)) goto err; /* Allocate RX completion queue */ @@ -381,10 +383,13 @@ void gve_rx_post_buffers_dqo(struct gve_rx_ring *rx) break; } - if (rx->dqo.hdr_bufs.data) + if (rx->dqo.hdr_bufs.data) { + u16 buf_id = le16_to_cpu(desc->buf_id); + desc->header_buf_addr = cpu_to_le64(rx->dqo.hdr_bufs.addr + - priv->header_buf_size * bufq->tail); + (size_t)priv->header_buf_size * buf_id); + } bufq->tail = (bufq->tail + 1) & bufq->mask; complq->num_free_slots--; @@ -826,10 +831,13 @@ static int gve_rx_dqo(struct napi_struct *napi, struct gve_rx_ring *rx, int unsplit = 0; if (hdr_len && !hbo) { - rx->ctx.skb_head = gve_rx_copy_data(priv->dev, napi, - rx->dqo.hdr_bufs.data + - desc_idx * priv->header_buf_size, - hdr_len); + size_t offset = + (size_t)buffer_id * priv->header_buf_size; + + rx->ctx.skb_head = + gve_rx_copy_data(priv->dev, napi, + rx->dqo.hdr_bufs.data + offset, + hdr_len); if (unlikely(!rx->ctx.skb_head)) goto error; rx->ctx.skb_tail = rx->ctx.skb_head; From 59da37fee81a8d76079313348ca13c5bc90dd6ae Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Mon, 15 Jun 2026 23:04:27 +0800 Subject: [PATCH 2601/2758] octeontx2-af: cn10k: restrict VF LMTLINE sharing to its own PF commit 8cdcf3d2caacdee7ddd363705fb4d93b0c1a0915 upstream. rvu_mbox_handler_lmtst_tbl_setup() uses req->base_pcifunc as a direct index into the LMT map table to read another function's LMTLINE physical base address and copy it into the caller's own LMT map table entry. The mailbox dispatcher authenticates req->hdr.pcifunc from the IRQ source, but req->base_pcifunc is a separate payload field and is not sanitized. Reject the request with -EPERM when a VF caller's base_pcifunc is not a valid function under its own PF. is_pf_func_valid() bounds the FUNC field to the PF's configured VF count, keeping the computed index inside the caller's own slot block. Fixes: 893ae97214c3 ("octeontx2-af: cn10k: Support configurable LMTST regions") Reported-by: Yuhao Jiang Cc: stable@vger.kernel.org Signed-off-by: Junrui Luo Link: https://patch.msgid.link/SYBPR01MB78811656934E713B77DA6CEDAFE62@SYBPR01MB7881.ausprd01.prod.outlook.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/marvell/octeontx2/af/rvu_cn10k.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cn10k.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cn10k.c index d2163da28d18f..fa4ea1258d29d 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cn10k.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cn10k.c @@ -178,6 +178,15 @@ int rvu_mbox_handler_lmtst_tbl_setup(struct rvu *rvu, * pcifunc (will be the one who is calling this mailbox). */ if (req->base_pcifunc) { + /* A VF is untrusted and must not redirect its LMTLINE to + * another PF's region, so confine VF callers to their own PF. + */ + if (is_vf(req->hdr.pcifunc) && + (!is_pf_func_valid(rvu, req->base_pcifunc) || + rvu_get_pf(rvu->pdev, req->hdr.pcifunc) != + rvu_get_pf(rvu->pdev, req->base_pcifunc))) + return -EPERM; + /* Calculating the LMT table index equivalent to primary * pcifunc. */ From 9a7f7b55d7d0fbc4662c981ee6c56e081fa66d58 Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Sun, 12 Jul 2026 00:00:00 +0000 Subject: [PATCH 2602/2758] ksmbd: fix stack buffer overflow in multichannel session-key copy commit 610346149d047a52a92c9a0eb329dd565b8f92c5 upstream. Commit 4b706360ffb7 ("ksmbd: fix multichannel binding and enforce channel limit") moved the binding-path session key out of the session-wide sess->sess_key (CIFS_KEY_SIZE = 40) into a new per-channel buffer, and sized both that buffer and the on-stack copy used during binding with SMB2_NTLMV2_SESSKEY_SIZE (16): struct channel { char sess_key[SMB2_NTLMV2_SESSKEY_SIZE]; /* 16 */ ... }; ntlm_authenticate() / krb5_authenticate(): char channel_key[SMB2_NTLMV2_SESSKEY_SIZE] = {}; /* 16 */ char *auth_key = conn->binding ? channel_key : sess->sess_key; The two writers that fill this destination still bound the copy length against CIFS_KEY_SIZE (40), not against the 16-byte buffer: ksmbd_decode_ntlmssp_auth_blob() (NTLM key exchange): if (sess_key_len > CIFS_KEY_SIZE) /* 40 */ return -EINVAL; arc4_crypt(ctx_arc4, sess_key, (char *)authblob + sess_key_off, sess_key_len); ksmbd_krb5_authenticate(): if (resp->session_key_len > sizeof(sess->sess_key)) /* 40 */ ... memcpy(sess_key, resp->payload, resp->session_key_len); On a binding SESSION_SETUP, auth_key points at the 16-byte channel_key, so a client that supplies an NTLM EncryptedRandomSessionKey of up to 40 bytes (with NTLMSSP_NEGOTIATE_KEY_EXCH), or a Kerberos ticket whose session key is longer than 16 bytes (a normal AES256 key is 32), writes past the 16-byte stack buffer -- up to a 24-byte kernel stack overflow. KASAN reports it as a stack-out-of-bounds write in arc4_crypt() called from ksmbd_decode_ntlmssp_auth_blob(). The destinations must be able to hold the full session key the length checks already permit. Size the per-channel key buffer and the two on-stack channel_key buffers with CIFS_KEY_SIZE, matching sess->sess_key. Fixes: 4b706360ffb7 ("ksmbd: fix multichannel binding and enforce channel limit") Signed-off-by: Gil Portnoy Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/mgmt/user_session.h | 2 +- fs/smb/server/smb2pdu.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/smb/server/mgmt/user_session.h b/fs/smb/server/mgmt/user_session.h index 8893a9aaede79..4637a8c8436d0 100644 --- a/fs/smb/server/mgmt/user_session.h +++ b/fs/smb/server/mgmt/user_session.h @@ -19,7 +19,7 @@ struct ksmbd_file_table; struct channel { - char sess_key[SMB2_NTLMV2_SESSKEY_SIZE]; + char sess_key[CIFS_KEY_SIZE]; __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; struct ksmbd_conn *conn; }; diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index e920a283a3116..352ae21e4f005 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -1507,7 +1507,7 @@ static int ntlm_authenticate(struct ksmbd_work *work, struct ksmbd_conn *conn = work->conn; struct ksmbd_session *sess = work->sess; struct ksmbd_user *user; - char channel_key[SMB2_NTLMV2_SESSKEY_SIZE] = {}; + char channel_key[CIFS_KEY_SIZE] = {}; char *auth_key = conn->binding ? channel_key : sess->sess_key; u64 prev_id; bool binding = conn->binding; @@ -1644,7 +1644,7 @@ static int krb5_authenticate(struct ksmbd_work *work, struct ksmbd_conn *conn = work->conn; struct ksmbd_session *sess = work->sess; char *in_blob, *out_blob; - char channel_key[SMB2_NTLMV2_SESSKEY_SIZE] = {}; + char channel_key[CIFS_KEY_SIZE] = {}; char *auth_key = conn->binding ? channel_key : sess->sess_key; u64 prev_sess_id; bool binding = conn->binding; From 744da2443f406e8e4f6afbb40199f81105b86337 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 26 Jun 2026 13:40:42 +0200 Subject: [PATCH 2603/2758] netfilter: nfnetlink_cthelper: cap to maximum number of expectation per master commit bf5355cfdede3e30b30e63a5a74f6bdaafb26082 upstream. If userspace helper policy updates sets maximum number of expectation to zero, cap it to NF_CT_EXPECT_MAX_CNT (255) on updates too. Fixes: 397c8300972f ("netfilter: nf_conntrack_helper: cap maximum number of expectation at helper registration") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nfnetlink_cthelper.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index 866f46ecfae91..8ac2fb3079839 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -163,6 +163,8 @@ nfnl_cthelper_expect_policy(struct nf_conntrack_expect_policy *expect_policy, tb[NFCTH_POLICY_NAME], NF_CT_HELPER_NAME_LEN); expect_policy->max_expected = ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_MAX])); + if (!expect_policy->max_expected) + expect_policy->max_expected = NF_CT_EXPECT_MAX_CNT; if (expect_policy->max_expected > NF_CT_EXPECT_MAX_CNT) return -EINVAL; From fcc621f5b25d15f0ff41eec2bf866dd8dd4e2269 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Sat, 27 Jun 2026 00:02:10 +0200 Subject: [PATCH 2604/2758] platform/x86/amd/pmc: Avoid logging "(null)" for DMI values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit a0738abd042f7406edd2175a819cf2e66388ed97 upstream. dmi_get_system_info(...) can return NULL. Using that as %s arguments of dev_info() would log "(null)" (as part of a message like '... System Vendor: "(null)", Product Name: "(null)" ...'), which may be confusing for users. Use Elvis operator to print "(Unknown)" instead. Fixes: 428b9fd2dce5 ("platform/x86/amd/pmc: Add delay_suspend module parameter") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202606251540.Nr2BtaNu-lkp@intel.com/ Suggested-by: Ilpo Järvinen Signed-off-by: Daniel Gibson Link: https://patch.msgid.link/20260626220210.1761783-2-daniel@gibson.sh Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/amd/pmc/pmc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index 7d772ccd17a67..2cd77a44c7e7c 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -660,11 +660,11 @@ static bool amd_pmc_want_suspend_delay(struct amd_pmc_dev *pdev) } else if (delay_suspend == 1) { if (!intermediate_wakeup) dev_info(pdev->dev, "Delaying suspend by 2.5s because delay_suspend=1. If this solves problems on your machine, please report this whole line to: platform-driver-x86@vger.kernel.org so it can be automatically detected as affected in the future. System Vendor: \"%s\" Product Name: \"%s\" Product Family: \"%s\" Board Vendor: \"%s\" Board Name: \"%s\"\n", - dmi_get_system_info(DMI_SYS_VENDOR), - dmi_get_system_info(DMI_PRODUCT_NAME), - dmi_get_system_info(DMI_PRODUCT_FAMILY), - dmi_get_system_info(DMI_BOARD_VENDOR), - dmi_get_system_info(DMI_BOARD_NAME)); + dmi_get_system_info(DMI_SYS_VENDOR) ?: "(Unknown)", + dmi_get_system_info(DMI_PRODUCT_NAME) ?: "(Unknown)", + dmi_get_system_info(DMI_PRODUCT_FAMILY) ?: "(Unknown)", + dmi_get_system_info(DMI_BOARD_VENDOR) ?: "(Unknown)", + dmi_get_system_info(DMI_BOARD_NAME) ?: "(Unknown)"); return true; } return false; From 227dd2eeab0fd6fceaefe4f8c841bc0a50b52f82 Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Tue, 30 Jun 2026 11:09:22 -0400 Subject: [PATCH 2605/2758] net/sched: sch_teql: move rcu_read_lock()/spin_lock() from _bh variants commit 6301f6a34ed86fe6f3b7b3211ea069f3677fc559 upstream. This is a followup based on sashiko comments [1] on commit e5b811fe7931 ("net/sched: sch_teql: Introduce slaves_lock to avoid race condition and UAF") Use plain rcu_read_lock()/spin_lock() in teql_master_xmit() instead of the _bh variants, since ndo_start_xmit is already invoked with BH disabled by the core stack and the _bh primitives can warn in_hardirq() when xmit is reached through netpoll or a softirq xmit path with hard IRQs disabled. Moves rcu_read_lock() after restart: label + adds rcu_read_unlock() before goto restart (fixes the unbounded RCU hold across retries) [1] https://sashiko.dev/#/patchset/20260628111229.669751-1-jhs%40mojatatu.com Signed-off-by: Jamal Hadi Salim Fixes: e5b811fe7931 ("net/sched: sch_teql: Introduce slaves_lock to avoid race condition and UAF") Link: https://patch.msgid.link/20260630150922.238714-1-jhs@mojatatu.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- net/sched/sch_teql.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index 24ba31f8c8285..5c42a29a981cb 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -311,14 +311,14 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev) int subq = skb_get_queue_mapping(skb); struct sk_buff *skb_res = NULL; - rcu_read_lock_bh(); - - start = rcu_dereference_bh(master->slaves); - restart: nores = 0; busy = 0; + rcu_read_lock(); + + start = rcu_dereference(master->slaves); + q = start; if (!q) goto drop; @@ -345,17 +345,17 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev) netdev_start_xmit(skb, slave, slave_txq, false) == NETDEV_TX_OK) { __netif_tx_unlock(slave_txq); - spin_lock_bh(&master->slaves_lock); + spin_lock(&master->slaves_lock); if (rcu_dereference_protected(master->slaves, lockdep_is_held(&master->slaves_lock)) == q) rcu_assign_pointer(master->slaves, rcu_dereference_protected(NEXT_SLAVE(q), lockdep_is_held(&master->slaves_lock))); - spin_unlock_bh(&master->slaves_lock); + spin_unlock(&master->slaves_lock); netif_wake_queue(dev); master->tx_packets++; master->tx_bytes += length; - rcu_read_unlock_bh(); + rcu_read_unlock(); return NETDEV_TX_OK; } __netif_tx_unlock(slave_txq); @@ -364,37 +364,38 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev) busy = 1; break; case 1: - spin_lock_bh(&master->slaves_lock); + spin_lock(&master->slaves_lock); if (rcu_dereference_protected(master->slaves, lockdep_is_held(&master->slaves_lock)) == q) rcu_assign_pointer(master->slaves, rcu_dereference_protected(NEXT_SLAVE(q), lockdep_is_held(&master->slaves_lock))); - spin_unlock_bh(&master->slaves_lock); - rcu_read_unlock_bh(); + spin_unlock(&master->slaves_lock); + rcu_read_unlock(); return NETDEV_TX_OK; default: nores = 1; break; } __skb_pull(skb, skb_network_offset(skb)); - } while ((q = rcu_dereference_bh(NEXT_SLAVE(q))) != start); + } while ((q = rcu_dereference(NEXT_SLAVE(q))) != start); if (nores && skb_res == NULL) { skb_res = skb; + rcu_read_unlock(); goto restart; } if (busy) { netif_stop_queue(dev); - rcu_read_unlock_bh(); + rcu_read_unlock(); return NETDEV_TX_BUSY; } master->tx_errors++; drop: master->tx_dropped++; - rcu_read_unlock_bh(); + rcu_read_unlock(); dev_kfree_skb(skb); return NETDEV_TX_OK; } From 1b31e160430ca834ef819bc1f3f947b7e5b985a2 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 30 Jun 2026 18:03:03 +0200 Subject: [PATCH 2606/2758] ACPICA: Define acpi_ut_safe_strncpy() as strscpy_pad() alias commit 9825cf2cb59fac7480e7fac9eee13ab9af3f1ea8 upstream. Commit 292db66afd20 ("ACPICA: Unbreak tools build after switching over to strscpy_pad()") added an #ifdef based on a __KERNEL__ check which is sort of nasty to the acpi_ut_safe_strncpy() definition to unbreak ACPICA tools builds broken by commit 97f7d3f9c9ac ("ACPICA: Replace strncpy() with strscpy_pad() in acpi_ut_safe_strncpy()"). However, that #ifdef effectively produces dead code when tools are built because they don't call acpi_ut_safe_strncpy(). Accordingly, drop the existing definition of acpi_ut_safe_strncpy() and define it as a strscpy_pad() alias. Fixes: 292db66afd20 ("ACPICA: Unbreak tools build after switching over to strscpy_pad()") Signed-off-by: Rafael J. Wysocki [ rjw: Tweak the changelog ] Link: https://patch.msgid.link/12941764.O9o76ZdvQC@rafael.j.wysocki Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/acpica/acutils.h | 2 -- drivers/acpi/acpica/utnonansi.c | 16 ---------------- include/acpi/platform/aclinuxex.h | 1 + 3 files changed, 1 insertion(+), 18 deletions(-) diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h index 3990d509bbabf..394ad8d92ff90 100644 --- a/drivers/acpi/acpica/acutils.h +++ b/drivers/acpi/acpica/acutils.h @@ -626,8 +626,6 @@ void acpi_ut_repair_name(char *name); #if defined (ACPI_DEBUGGER) || defined (ACPI_APPLICATION) || defined (ACPI_DEBUG_OUTPUT) u8 acpi_ut_safe_strcpy(char *dest, acpi_size dest_size, char *source); -void acpi_ut_safe_strncpy(char *dest, char *source, acpi_size dest_size); - u8 acpi_ut_safe_strcat(char *dest, acpi_size dest_size, char *source); u8 diff --git a/drivers/acpi/acpica/utnonansi.c b/drivers/acpi/acpica/utnonansi.c index 93867ad7f3421..a465e5a1d3093 100644 --- a/drivers/acpi/acpica/utnonansi.c +++ b/drivers/acpi/acpica/utnonansi.c @@ -164,20 +164,4 @@ acpi_ut_safe_strncat(char *dest, return (FALSE); } -void acpi_ut_safe_strncpy(char *dest, char *source, acpi_size dest_size) -{ - /* Always terminate destination string */ - -#ifdef __KERNEL__ - strscpy_pad(dest, source, dest_size); -#else - /* - * strscpy_pad() is not defined in ACPICA tools builds, so use strncpy() - * and directly NUL-terminate the destination string in that case. - */ - strncpy(dest, source, dest_size); - dest[dest_size - 1] = 0; -#endif -} - #endif diff --git a/include/acpi/platform/aclinuxex.h b/include/acpi/platform/aclinuxex.h index 73265650f46ba..578833c9169f4 100644 --- a/include/acpi/platform/aclinuxex.h +++ b/include/acpi/platform/aclinuxex.h @@ -134,6 +134,7 @@ static inline void acpi_os_terminate_debugger(void) /* * OSL interfaces added by Linux */ +#define acpi_ut_safe_strncpy strscpy_pad #endif /* __KERNEL__ */ From 835a2f9d9f1749995203be3c0c07d27e2aec58f4 Mon Sep 17 00:00:00 2001 From: Shuicheng Lin Date: Tue, 30 Jun 2026 19:22:21 +0000 Subject: [PATCH 2607/2758] drm/xe/userptr: Stub notifier_lock helpers when DRM_GPUSVM=n MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit f5ef65adf81da3dbce4e692e48c1754c0bb95da0 upstream. When CONFIG_DRM_GPUSVM=n (e.g. um-allyesconfig), the only caller of xe_pt_svm_userptr_notifier_lock() is compiled out, triggering: drivers/gpu/drm/xe/xe_pt.c:1418:13: warning: 'xe_pt_svm_userptr_notifier_lock' defined but not used [-Wunused-function] The helpers cannot simply be removed in this case: the matching xe_pt_svm_userptr_notifier_unlock() is also referenced from xe_pt_update_ops_run(), which lives outside any DRM_GPUSVM ifdef and is gated only at runtime by pt_update_ops->needs_svm_lock. The symbol must exist in all builds. Provide empty static inline stubs for !DRM_GPUSVM, matching the pattern used by xe_svm_notifier_lock()/_unlock() in xe_svm.h. Fixes: dca6e08c923a ("drm/xe/userptr: Hold notifier_lock for write on inject test path") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202606302210.QqcLbOEN-lkp@intel.com/ Reviewed-by: Matthew Brost Link: https://patch.msgid.link/20260630192221.2998168-1-shuicheng.lin@intel.com Signed-off-by: Shuicheng Lin (cherry picked from commit 3359422bf0a1140e96d783a19a397686e580a3ca) Signed-off-by: Thomas Hellström Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/xe/xe_pt.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c index 4d8f0a8b072e5..c3a69103b9bbf 100644 --- a/drivers/gpu/drm/xe/xe_pt.c +++ b/drivers/gpu/drm/xe/xe_pt.c @@ -1407,6 +1407,7 @@ static int xe_pt_pre_commit(struct xe_migrate_pt_update *pt_update) pt_update_ops, rftree); } +#if IS_ENABLED(CONFIG_DRM_GPUSVM) /* * Acquire/release the svm notifier_lock around xe_pt_svm_userptr_pre_commit() * and the matching late release in xe_pt_update_ops_run(). Read mode by @@ -1433,6 +1434,10 @@ static void xe_pt_svm_userptr_notifier_unlock(struct xe_vm *vm) xe_svm_notifier_unlock(vm); #endif } +#else +static inline void xe_pt_svm_userptr_notifier_lock(struct xe_vm *vm) { } +static inline void xe_pt_svm_userptr_notifier_unlock(struct xe_vm *vm) { } +#endif #if IS_ENABLED(CONFIG_DRM_GPUSVM) #ifdef CONFIG_DRM_XE_USERPTR_INVAL_INJECT From d1d73a3a37b7725a87c1d137865fb228ab7cc75b Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 1 Jul 2026 12:46:57 +0200 Subject: [PATCH 2608/2758] netfilter: nfnetlink_cthelper: cap to maximum number of expectation per master on updates commit 278296b69fae5dd951599692cd481bae4995215c upstream. Really cap it to NF_CT_EXPECT_MAX_CNT (255) on updates. The commit ("netfilter: nfnetlink_cthelper: cap to maximum number of expectation per master") only covers creation of helpers, not updates. Fixes: 397c8300972f ("netfilter: nf_conntrack_helper: cap maximum number of expectation at helper registration") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nfnetlink_cthelper.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index 8ac2fb3079839..8710fec30df9c 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -316,6 +316,8 @@ nfnl_cthelper_update_policy_one(const struct nf_conntrack_expect_policy *policy, new_policy->max_expected = ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_MAX])); + if (!new_policy->max_expected) + new_policy->max_expected = NF_CT_EXPECT_MAX_CNT; if (new_policy->max_expected > NF_CT_EXPECT_MAX_CNT) return -EINVAL; From fe08be92f2b63e10e0833012542ddaa67cb96306 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 7 Jul 2026 17:29:35 +0100 Subject: [PATCH 2609/2758] KVM: arm64: Fix propagation of TLBI level in kvm_pgtable_stage2_relax_perms() commit 8d187d4b33c262c0f3e44842553521151d8629e8 upstream. Assigning the invalidation level (an s8 value) with TLBI_TTL_UNKNOWN (a 32bit signed value) is not ideal, to say the least. Instead of this, only pass TLBI_TTL_UNKNOWN to __kvm_tlb_flush_vmid_ipa_nsh() when we know for sure that we don't have a provided level. Fixes: 100baf0184896 ("KVM: arm64: Ensure level is always initialized when relaxing perms") Reported-by: Mark Brown Reviewed-by: Oliver Upton Link: https://lore.kernel.org/r/akztC7H2IsEKaq4i@sirena.org.uk Link: https://patch.msgid.link/20260707162935.1900874-1-maz@kernel.org Signed-off-by: Marc Zyngier Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/hyp/pgtable.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c index 31aaca35693a9..f218ac0ec1122 100644 --- a/arch/arm64/kvm/hyp/pgtable.c +++ b/arch/arm64/kvm/hyp/pgtable.c @@ -1358,7 +1358,7 @@ int kvm_pgtable_stage2_relax_perms(struct kvm_pgtable *pgt, u64 addr, enum kvm_pgtable_prot prot, enum kvm_pgtable_walk_flags flags) { kvm_pte_t xn = 0, set = 0, clr = 0; - s8 level = TLBI_TTL_UNKNOWN; + s8 level; int ret; if (prot & KVM_PTE_LEAF_ATTR_HI_SW) @@ -1379,7 +1379,8 @@ int kvm_pgtable_stage2_relax_perms(struct kvm_pgtable *pgt, u64 addr, ret = stage2_update_leaf_attrs(pgt, addr, 1, set, clr, NULL, &level, flags); if (!ret || ret == -EAGAIN) - kvm_call_hyp(__kvm_tlb_flush_vmid_ipa_nsh, pgt->mmu, addr, level); + kvm_call_hyp(__kvm_tlb_flush_vmid_ipa_nsh, pgt->mmu, addr, + (ret == -EAGAIN) ? TLBI_TTL_UNKNOWN : level); return ret; } From c270eaa919f697bf07299acdb503e4546c785398 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 4 May 2026 08:30:50 +0200 Subject: [PATCH 2610/2758] riscv: vdso: Always declare vdso_start symbols MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 499578e22ae0cc33d05803f22e5c71ffb7077b2f ] Make the declarations of vdso_start and its related symbols always visible. With that their users don't have to use ifdeffery but can use the better IS_ENABLED() compile-time checks. Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260504-riscv-cfi-vdso-alternative-v1-1-bcdf3d37f62e@linutronix.de Signed-off-by: Paul Walmsley Signed-off-by: Sasha Levin --- arch/riscv/include/asm/vdso.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/riscv/include/asm/vdso.h b/arch/riscv/include/asm/vdso.h index 35bf830a55761..f7998d9ad9b2d 100644 --- a/arch/riscv/include/asm/vdso.h +++ b/arch/riscv/include/asm/vdso.h @@ -12,12 +12,15 @@ * All systems with an MMU have a VDSO, but systems without an MMU don't * support shared libraries and therefore don't have one. */ -#ifdef CONFIG_MMU #define __VDSO_PAGES 4 #ifndef __ASSEMBLER__ + +#ifdef CONFIG_MMU #include +#endif + #ifdef CONFIG_RISCV_USER_CFI #include #endif @@ -38,15 +41,12 @@ #define COMPAT_VDSO_SYMBOL(base, name) \ (void __user *)((unsigned long)(base) + compat__vdso_##name##_offset) -extern char compat_vdso_start[], compat_vdso_end[]; - #endif /* CONFIG_COMPAT */ extern char vdso_start[], vdso_end[]; extern char vdso_cfi_start[], vdso_cfi_end[]; +extern char compat_vdso_start[], compat_vdso_end[]; #endif /* !__ASSEMBLER__ */ -#endif /* CONFIG_MMU */ - #endif /* _ASM_RISCV_VDSO_H */ From c97d44a5bdf9af0ab6e2208c99fc30f0a6dca65c Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 4 Jun 2026 10:56:31 +0100 Subject: [PATCH 2611/2758] pwm: rzg2l-gpt: Fix period_ticks type from u32 to u64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 2b40d72de9354a76f5e3bb71230a4210eaa92849 upstream. period_ticks is used to store PWM period values that can exceed the 32-bit range, so change its type from u32 to u64 to prevent overflow. Cc: stable@kernel.org Fixes: 061f087f5d0b ("pwm: Add support for RZ/G2L GPT") Signed-off-by: Biju Das Link: https://patch.msgid.link/20260604095647.108654-2-biju.das.jz@bp.renesas.com Signed-off-by: Uwe Kleine-König Signed-off-by: Greg Kroah-Hartman --- drivers/pwm/pwm-rzg2l-gpt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-rzg2l-gpt.c b/drivers/pwm/pwm-rzg2l-gpt.c index 8534a2a9cf750..dfa1d11a48a82 100644 --- a/drivers/pwm/pwm-rzg2l-gpt.c +++ b/drivers/pwm/pwm-rzg2l-gpt.c @@ -81,7 +81,7 @@ struct rzg2l_gpt_chip { void __iomem *mmio; struct mutex lock; /* lock to protect shared channel resources */ unsigned long rate_khz; - u32 period_ticks[RZG2L_MAX_HW_CHANNELS]; + u64 period_ticks[RZG2L_MAX_HW_CHANNELS]; u32 channel_request_count[RZG2L_MAX_HW_CHANNELS]; u32 channel_enable_count[RZG2L_MAX_HW_CHANNELS]; }; From 7bcce38cbebd103a94d9e1d37b87b4e094833758 Mon Sep 17 00:00:00 2001 From: Xuewen Wang Date: Thu, 25 Jun 2026 13:03:49 +0800 Subject: [PATCH 2612/2758] LoongArch: Fix nr passing in set_direct_map_valid_noflush() commit 70378a710598432f13509bdc16a1c0f06b3ecb53 upstream. set_direct_map_valid_noflush() incorrectly passes 1 to __set_memory() instead of nr. This causes only the first page's attr to be updated when nr > 1. Other architectures all pass nr correctly. Cc: stable@vger.kernel.org Fixes: 0c6378a71574 ("arch: introduce set_direct_map_valid_noflush()") Signed-off-by: Xuewen Wang Signed-off-by: Huacai Chen Signed-off-by: Greg Kroah-Hartman --- arch/loongarch/mm/pageattr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/loongarch/mm/pageattr.c b/arch/loongarch/mm/pageattr.c index f5e910b68229d..614ccc7afccbe 100644 --- a/arch/loongarch/mm/pageattr.c +++ b/arch/loongarch/mm/pageattr.c @@ -234,5 +234,5 @@ int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid) clear = __pgprot(_PAGE_PRESENT | _PAGE_VALID); } - return __set_memory(addr, 1, set, clear); + return __set_memory(addr, nr, set, clear); } From e483da960892c41fa7f0cf0d2fc2410d65a483d6 Mon Sep 17 00:00:00 2001 From: Hongchen Zhang Date: Thu, 25 Jun 2026 13:03:49 +0800 Subject: [PATCH 2613/2758] LoongArch: Fix missing dirty page tracking in {pte,pmd}_wrprotect() commit 018e9828eb523c638fa3d9bdf0fd4956b74555b2 upstream. When hardware page table walker (PTW) is enabled on LoongArch, the CPU may set _PAGE_DIRTY directly in the page table entry during a write TLB miss, without going through the software TLB store handler. The software TLB store handler (tlbex.S:254) sets both _PAGE_DIRTY and_PAGE_MODIFIED together: ori t0, t0, (_PAGE_VALID | _PAGE_DIRTY | _PAGE_MODIFIED) Since hardware PTW only sets _PAGE_DIRTY, the software-only bit, i.e. _PAGE_MODIFIED is left unchanged. This creates a window where a PTE has _PAGE_DIRTY set (hardware knows the page is dirty) but _PAGE_MODIFIED clear (software is unaware). When fork()/clone() triggers copy-on-write, __copy_present_ptes() calls pte_wrprotect(), which unconditionally clears both the _PAGE_WRITE and _PAGE_DIRTY bits: pte_val(pte) &= ~(_PAGE_WRITE | _PAGE_DIRTY); Since _PAGE_MODIFIED was never set, the dirtiness information is lost completely. Subsequently, when memory pressure triggers page reclaim, page_mkclean() / try_to_unmap() sees the page as clean (i.e. pte_dirty() returns false) and the page may be freed without writeback, causing data corruption. Fix this by propagating the _PAGE_DIRTY bit to the _PAGE_MODIFIED bit in both pte_wrprotect() and pmd_wrprotect() before clearing writeable bits: if (pte_val(pte) & _PAGE_DIRTY) pte_val(pte) |= _PAGE_MODIFIED; The pmd_wrprotect() fix handles the CONFIG_TRANSPARENT_HUGEPAGE case, where pmd entries need the same treatment. This ensures the software dirty tracking bit (checked by pte_dirty() and pmd_dirty(), which read both the _PAGE_DIRTY and _PAGE_MODIFIED bits) is preserved across fork COW write-protection. The issue was found by the LTP madvise09 test case, which exercises page reclaim after "madvise(MADV_FREE), write and fork" operation sequence on private anonymous mappings. Cc: stable@vger.kernel.org Fixes: 09cfefb7fa70 ("LoongArch: Add memory management") Co-developed-by: Tianyang Zhang Signed-off-by: Tianyang Zhang Signed-off-by: Hongchen Zhang Signed-off-by: Huacai Chen Signed-off-by: Greg Kroah-Hartman --- arch/loongarch/include/asm/pgtable.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/loongarch/include/asm/pgtable.h b/arch/loongarch/include/asm/pgtable.h index 2a0b63ae421f4..223528c04d739 100644 --- a/arch/loongarch/include/asm/pgtable.h +++ b/arch/loongarch/include/asm/pgtable.h @@ -429,6 +429,8 @@ static inline pte_t pte_mkwrite_novma(pte_t pte) static inline pte_t pte_wrprotect(pte_t pte) { + if (pte_val(pte) & _PAGE_DIRTY) + pte_val(pte) |= _PAGE_MODIFIED; pte_val(pte) &= ~(_PAGE_WRITE | _PAGE_DIRTY); return pte; } @@ -535,6 +537,8 @@ static inline pmd_t pmd_mkwrite_novma(pmd_t pmd) static inline pmd_t pmd_wrprotect(pmd_t pmd) { + if (pmd_val(pmd) & _PAGE_DIRTY) + pmd_val(pmd) |= _PAGE_MODIFIED; pmd_val(pmd) &= ~(_PAGE_WRITE | _PAGE_DIRTY); return pmd; } From 7be349d4fcc5e065295b83418a22d27a68afbdb6 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Thu, 21 May 2026 14:06:27 +0100 Subject: [PATCH 2614/2758] ipmi: Fix user refcount underflow in event delivery commit 6aa9e61c46465d231e9beddf56af7effd71be682 upstream. ipmi_alloc_recv_msg(user) takes the temporary user reference owned by the receive message, and ipmi_free_recv_msg() drops it again. If event delivery fails after allocating receive messages for earlier users, handle_read_event_rsp() rolls those messages back with ipmi_free_recv_msg(). That rollback path still drops user->refcount explicitly after freeing each message. The extra put can free a user that remains linked on intf->users, so later event delivery may dereference a freed user or trip refcount_t's addition-on-zero warning when ipmi_alloc_recv_msg() tries to acquire another reference. Remove the stale explicit put and the now-dead user assignment. Keep the list_del() and ipmi_free_recv_msg() calls; they are the required rollback operations. Fixes: b52da4054ee0 ("ipmi: Rework user message limit handling") Cc: stable@vger.kernel.org # v6.18+ Signed-off-by: Matt Fleming Message-ID: <20260521130628.3641050-1-matt@readmodwrite.com> Signed-off-by: Corey Minyard Signed-off-by: Greg Kroah-Hartman --- drivers/char/ipmi/ipmi_msghandler.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 869ac87a4b6ae..52561a880e543 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -4477,10 +4477,8 @@ static int handle_read_event_rsp(struct ipmi_smi *intf, mutex_unlock(&intf->users_mutex); list_for_each_entry_safe(recv_msg, recv_msg2, &msgs, link) { - user = recv_msg->user; list_del(&recv_msg->link); ipmi_free_recv_msg(recv_msg); - kref_put(&user->refcount, free_ipmi_user); } /* * We couldn't allocate memory for the From a338ce41bc933d8f74c39d9b3b6f1d8ca53d9714 Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Fri, 12 Jun 2026 16:11:39 +0200 Subject: [PATCH 2615/2758] espintcp: use sk_msg_free_partial to fix partial send commit 007800408002d871f5699bdb944f985896730b8f upstream. sk_msg_free_partial() ensures consistency of the skmsg at every iteration, without having to manually handle uncharges and offsets. This simplifies the code, and fixes some bugs in skmsg accounting when we don't send the full contents. Cc: stable@vger.kernel.org Fixes: e27cca96cd68 ("xfrm: add espintcp (RFC 8229)") Reported-by: Aaron Esau Reported-by: Yiming Qian Signed-off-by: Sabrina Dubroca Signed-off-by: Steffen Klassert Signed-off-by: Greg Kroah-Hartman --- net/xfrm/espintcp.c | 34 +++++++--------------------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/net/xfrm/espintcp.c b/net/xfrm/espintcp.c index d9035546375eb..374e1b9644383 100644 --- a/net/xfrm/espintcp.c +++ b/net/xfrm/espintcp.c @@ -212,43 +212,23 @@ static int espintcp_sendskmsg_locked(struct sock *sk, struct sk_msg *skmsg = &emsg->skmsg; bool more = flags & MSG_MORE; struct scatterlist *sg; - int done = 0; int ret; - sg = &skmsg->sg.data[skmsg->sg.start]; do { struct bio_vec bvec; - size_t size = sg->length - emsg->offset; - int offset = sg->offset + emsg->offset; - struct page *p; - - emsg->offset = 0; + sg = &skmsg->sg.data[skmsg->sg.start]; if (sg_is_last(sg) && !more) msghdr.msg_flags &= ~MSG_MORE; - p = sg_page(sg); -retry: - bvec_set_page(&bvec, p, size, offset); - iov_iter_bvec(&msghdr.msg_iter, ITER_SOURCE, &bvec, 1, size); - ret = tcp_sendmsg_locked(sk, &msghdr, size); - if (ret < 0) { - emsg->offset = offset - sg->offset; - skmsg->sg.start += done; + bvec_set_page(&bvec, sg_page(sg), sg->length, sg->offset); + iov_iter_bvec(&msghdr.msg_iter, ITER_SOURCE, &bvec, 1, sg->length); + ret = tcp_sendmsg_locked(sk, &msghdr, sg->length); + if (ret < 0) return ret; - } - - if (ret != size) { - offset += ret; - size -= ret; - goto retry; - } - done++; - put_page(p); - sk_mem_uncharge(sk, sg->length); - sg = sg_next(sg); - } while (sg); + sk_msg_free_partial(sk, skmsg, ret); + } while (skmsg->sg.size); memset(emsg, 0, sizeof(*emsg)); From 0fd23994ec8c5436d9f0b50848deb87ed933e6b3 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Wed, 3 Jun 2026 12:06:34 +0000 Subject: [PATCH 2616/2758] ipmi: fix refcount leak in i_ipmi_request() commit a3f3859cecacb64f18fd446271ece9a3b3f2d4de upstream. When a caller provides a `supplied_recv` message to i_ipmi_request(), the function increments the user's `nr_msgs` reference count. If an error occurs later, the out_err cleanup path only frees the recv_msg if the function allocated it itself (i.e., !supplied_recv). In the supplied_recv case the cleanup is skipped, leaving the reference count elevated. The caller ipmi_request_supply_msgs() does not release the supplied_recv on error, so the reference is permanently leaked. Fix this by explicitly reverting the reference count operations when a supplied recv_msg with a valid user pointer is present in the error path: decrement nr_msgs and drop the user's kref. Cc: stable@vger.kernel.org Fixes: b52da4054ee0 ("ipmi: Rework user message limit handling") Signed-off-by: Wentao Liang Message-ID: <20260603120634.3758747-1-vulab@iscas.ac.cn> Signed-off-by: Corey Minyard Signed-off-by: Greg Kroah-Hartman --- drivers/char/ipmi/ipmi_msghandler.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 52561a880e543..3e0b7d60269d9 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -2347,6 +2347,10 @@ static int i_ipmi_request(struct ipmi_user *user, if (smi_msg == NULL) { if (!supplied_recv) ipmi_free_recv_msg(recv_msg); + else if (recv_msg->user) { + atomic_dec(&recv_msg->user->nr_msgs); + kref_put(&recv_msg->user->refcount, free_ipmi_user); + } return -ENOMEM; } } @@ -2420,6 +2424,10 @@ static int i_ipmi_request(struct ipmi_user *user, ipmi_free_smi_msg(smi_msg); if (!supplied_recv) ipmi_free_recv_msg(recv_msg); + else if (recv_msg->user) { + atomic_dec(&recv_msg->user->nr_msgs); + kref_put(&recv_msg->user->refcount, free_ipmi_user); + } } return rv; } From 356077547b1afa34a8ebae77176a577631a89041 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Sat, 20 Jun 2026 11:53:50 +0530 Subject: [PATCH 2617/2758] bnx2x: fix potential memory leak in bnx2x_alloc_mem_bp() commit a986fde914d88af47eb78fd29c5d1af7952c3500 upstream. If the allocation of fp[i].tpa_info fails, the error path will not free the struct bnx2x_fastpath allocated earlier, as it is not linked to the bp structure yet. Fix that by linking it immediately after allocation. Cc: stable@vger.kernel.org Fixes: 15192a8cf8a8 ("bnx2x: Split the FP structure") Signed-off-by: Abdun Nihaal Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260620062402.89549-1-nihaal@cse.iitm.ac.in Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 19e078479b0d4..5b2640bd31c3a 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -4748,6 +4748,7 @@ int bnx2x_alloc_mem_bp(struct bnx2x *bp) fp = kzalloc_objs(*fp, bp->fp_array_size); if (!fp) goto alloc_err; + bp->fp = fp; for (i = 0; i < bp->fp_array_size; i++) { fp[i].tpa_info = kzalloc_objs(struct bnx2x_agg_info, @@ -4756,8 +4757,6 @@ int bnx2x_alloc_mem_bp(struct bnx2x *bp) goto alloc_err; } - bp->fp = fp; - /* allocate sp objs */ bp->sp_objs = kzalloc_objs(struct bnx2x_sp_objs, bp->fp_array_size); if (!bp->sp_objs) From 03f1725f91e8ecd5d986ff9f7ff1fdd763813871 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 2 Jun 2026 20:25:55 +0100 Subject: [PATCH 2618/2758] rtc: renesas-rtca3: Fix PIE clear polling condition in alarm setup error path commit 7e342d87aa8e6b831cf6d21ca41b1f7e032d0fcf upstream. In rtca3_set_alarm(), the setup_failed path attempts to disable the Periodic Interrupt Enable (PIE) bit and wait until it is cleared. However, the polling condition passed to readb_poll_timeout_atomic() uses an incorrect expression: !(tmp & ~RTCA3_RCR1_PIE) As ~RTCA3_RCR1_PIE evaluates to a mask of all bits except PIE, the condition effectively waits for all non-PIE bits to become zero, which is unrelated to the intended operation and is unlikely to ever be true. This causes the poll to time out unnecessarily. Fix the condition to check for the PIE bit itself being cleared: !(tmp & RTCA3_RCR1_PIE) This correctly waits until PIE is deasserted after being cleared. Fixes: d4488377609e3 ("rtc: renesas-rtca3: Add driver for RTCA-3 available on Renesas RZ/G3S SoC") Cc: stable@vger.kernel.org Signed-off-by: Lad Prabhakar Reviewed-by: Claudiu Beznea Tested-by: Claudiu Beznea # on RZ/G3S Link: https://patch.msgid.link/20260602192559.1791344-2-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Alexandre Belloni Signed-off-by: Greg Kroah-Hartman --- drivers/rtc/rtc-renesas-rtca3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-renesas-rtca3.c b/drivers/rtc/rtc-renesas-rtca3.c index cbabaa4dc96a5..2dc080d0eb6cd 100644 --- a/drivers/rtc/rtc-renesas-rtca3.c +++ b/drivers/rtc/rtc-renesas-rtca3.c @@ -455,7 +455,7 @@ static int rtca3_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) * specified timeout for setup. */ writeb(rcr1 & ~RTCA3_RCR1_PIE, priv->base + RTCA3_RCR1); - readb_poll_timeout_atomic(priv->base + RTCA3_RCR1, tmp, !(tmp & ~RTCA3_RCR1_PIE), + readb_poll_timeout_atomic(priv->base + RTCA3_RCR1, tmp, !(tmp & RTCA3_RCR1_PIE), 10, RTCA3_DEFAULT_TIMEOUT_US); atomic_set(&priv->alrm_sstep, RTCA3_ALRM_SSTEP_DONE); } From a36b9528b071718962565ddc6b3004f356fbe93b Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 18 Jun 2026 15:19:21 -0700 Subject: [PATCH 2619/2758] fscrypt: Replace mk_users keyring with simple list commit 696c030e1e3438955aba443b308ee8b6faa3983e upstream. Change mk_users (the set of user claims to an fscrypt master key) from a 'struct key' keyring to a simple linked list. It's still a collection of 'struct key' for quota tracking. It was originally thought to be natural that a collection of 'struct key' should be held in a 'struct key' keyring. In reality, it's just been causing problems, similar to how using 'struct key' for the filesystem keyring caused problems and was removed in commit d7e7b9af104c ("fscrypt: stop using keyrings subsystem for fscrypt_master_key"). Commit d3a7bd420076 ("fscrypt: clear keyring before calling key_put()") fixed mk_users cleanup to be synchronous. But that apparently wasn't enough: the keyring subsystem's redundant locking is still generating lockdep false positives due to the interaction with filesystem reclaim. With the simple list, the redundant locking and lockdep issue goes away. Of course, searching a linked list is linear-time whereas the 'struct key' keyring used a fancy constant-time associative array. But that's fine here, since in practice there's just one entry in the list. In fact the new code is much faster in practice, since it's much smaller and doesn't have to convert the kuid_t into a string to search for it. Reported-by: syzbot+f55b043dacf43776b50c@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=f55b043dacf43776b50c Reported-by: Mohammed EL Kadiri Closes: https://lore.kernel.org/keyrings/20260614150041.21172-1-med08elkadiri@gmail.com/ Fixes: 23c688b54016 ("fscrypt: allow unprivileged users to add/remove keys for v2 policies") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260618221921.87896-1-ebiggers@kernel.org Signed-off-by: Eric Biggers Signed-off-by: Greg Kroah-Hartman --- fs/crypto/fscrypt_private.h | 32 ++++-- fs/crypto/keyring.c | 216 +++++++++++++++--------------------- 2 files changed, 113 insertions(+), 135 deletions(-) diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 4263cac24b329..0053b5c454123 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -496,6 +496,19 @@ fscrypt_is_key_prepared(const struct fscrypt_prepared_key *prep_key, /* keyring.c */ +/* + * fscrypt_master_key_user - a user's claim to a master key + */ +struct fscrypt_master_key_user { + struct list_head link; + kuid_t uid; + /* + * This 'struct key' contains no secret. It exists solely to charge the + * appropriate user's key quota. + */ + struct key *quota_key; +}; + /* * fscrypt_master_key_secret - secret key material of an in-use master key */ @@ -611,19 +624,18 @@ struct fscrypt_master_key { struct fscrypt_key_specifier mk_spec; /* - * Keyring which contains a key of type 'key_type_fscrypt_user' for each - * user who has added this key. Normally each key will be added by just - * one user, but it's possible that multiple users share a key, and in - * that case we need to keep track of those users so that one user can't - * remove the key before the others want it removed too. + * List of user claims to this key (struct fscrypt_master_key_user). + * Normally each key will be added by just one user, but it's possible + * that multiple users share a key, and in that case we need to keep + * track of those users so that one user can't remove the key before the + * others want it removed too. * - * This is NULL for v1 policy keys; those can only be added by root. + * Used only for v2 policy keys. v1 policy keys can be added only by + * root, so user tracking doesn't apply to them. * - * Locking: protected by ->mk_sem. (We don't just rely on the keyrings - * subsystem semaphore ->mk_users->sem, as we need support for atomic - * search+insert along with proper synchronization with other fields.) + * Locking: protected by ->mk_sem. */ - struct key *mk_users; + struct list_head mk_users; /* * List of inodes that were unlocked using this key. This allows the diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c index 5fe0d985a58d5..38b73e7030733 100644 --- a/fs/crypto/keyring.c +++ b/fs/crypto/keyring.c @@ -65,22 +65,19 @@ static void fscrypt_free_master_key(struct rcu_head *head) kfree_sensitive(mk); } +static void clear_mk_users(struct fscrypt_master_key *mk); + void fscrypt_put_master_key(struct fscrypt_master_key *mk) { if (!refcount_dec_and_test(&mk->mk_struct_refs)) return; /* - * No structural references left, so free ->mk_users, and also free the + * No structural references left, so clear ->mk_users, and also free the * fscrypt_master_key struct itself after an RCU grace period ensures * that concurrent keyring lookups can no longer find it. */ WARN_ON_ONCE(refcount_read(&mk->mk_active_refs) != 0); - if (mk->mk_users) { - /* Clear the keyring so the quota gets released right away. */ - keyring_clear(mk->mk_users); - key_put(mk->mk_users); - mk->mk_users = NULL; - } + clear_mk_users(mk); call_rcu(&mk->mk_rcu_head, fscrypt_free_master_key); } @@ -165,8 +162,8 @@ static void fscrypt_user_key_describe(const struct key *key, struct seq_file *m) } /* - * Type of key in ->mk_users. Each key of this type represents a particular - * user who has added a particular master key. + * Type of fscrypt_master_key_user::quota_key. This contains no secret; it + * exists solely to charge a user's key quota. * * Note that the name of this key type really should be something like * ".fscrypt-user" instead of simply ".fscrypt". But the shorter name is chosen @@ -180,30 +177,9 @@ static struct key_type key_type_fscrypt_user = { .describe = fscrypt_user_key_describe, }; -#define FSCRYPT_MK_USERS_DESCRIPTION_SIZE \ - (CONST_STRLEN("fscrypt-") + 2 * FSCRYPT_KEY_IDENTIFIER_SIZE + \ - CONST_STRLEN("-users") + 1) - #define FSCRYPT_MK_USER_DESCRIPTION_SIZE \ (2 * FSCRYPT_KEY_IDENTIFIER_SIZE + CONST_STRLEN(".uid.") + 10 + 1) -static void format_mk_users_keyring_description( - char description[FSCRYPT_MK_USERS_DESCRIPTION_SIZE], - const u8 mk_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]) -{ - sprintf(description, "fscrypt-%*phN-users", - FSCRYPT_KEY_IDENTIFIER_SIZE, mk_identifier); -} - -static void format_mk_user_description( - char description[FSCRYPT_MK_USER_DESCRIPTION_SIZE], - const u8 mk_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]) -{ - - sprintf(description, "%*phN.uid.%u", FSCRYPT_KEY_IDENTIFIER_SIZE, - mk_identifier, __kuid_val(current_fsuid())); -} - /* Create ->s_master_keys if needed. Synchronized by fscrypt_add_key_mutex. */ static int allocate_filesystem_keyring(struct super_block *sb) { @@ -338,91 +314,94 @@ fscrypt_find_master_key(struct super_block *sb, return mk; } -static int allocate_master_key_users_keyring(struct fscrypt_master_key *mk) -{ - char description[FSCRYPT_MK_USERS_DESCRIPTION_SIZE]; - struct key *keyring; - - format_mk_users_keyring_description(description, - mk->mk_spec.u.identifier); - keyring = keyring_alloc(description, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, - current_cred(), KEY_POS_SEARCH | - KEY_USR_SEARCH | KEY_USR_READ | KEY_USR_VIEW, - KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); - if (IS_ERR(keyring)) - return PTR_ERR(keyring); - - mk->mk_users = keyring; - return 0; -} - -/* - * Find the current user's "key" in the master key's ->mk_users. - * Returns ERR_PTR(-ENOKEY) if not found. - */ -static struct key *find_master_key_user(struct fscrypt_master_key *mk) +/* Find the current user's claim in ->mk_users. ->mk_sem must be held. */ +static struct fscrypt_master_key_user * +find_master_key_user(struct fscrypt_master_key *mk) { - char description[FSCRYPT_MK_USER_DESCRIPTION_SIZE]; - key_ref_t keyref; + struct fscrypt_master_key_user *mk_user; + kuid_t uid = current_fsuid(); - format_mk_user_description(description, mk->mk_spec.u.identifier); - - /* - * We need to mark the keyring reference as "possessed" so that we - * acquire permission to search it, via the KEY_POS_SEARCH permission. - */ - keyref = keyring_search(make_key_ref(mk->mk_users, true /*possessed*/), - &key_type_fscrypt_user, description, false); - if (IS_ERR(keyref)) { - if (PTR_ERR(keyref) == -EAGAIN || /* not found */ - PTR_ERR(keyref) == -EKEYREVOKED) /* recently invalidated */ - keyref = ERR_PTR(-ENOKEY); - return ERR_CAST(keyref); + list_for_each_entry(mk_user, &mk->mk_users, link) { + if (uid_eq(mk_user->uid, uid)) + return mk_user; } - return key_ref_to_ptr(keyref); + return NULL; } /* - * Give the current user a "key" in ->mk_users. This charges the user's quota + * Give the current user a claim in ->mk_users. This charges the user's quota * and marks the master key as added by the current user, so that it cannot be * removed by another user with the key. Either ->mk_sem must be held for * write, or the master key must be still undergoing initialization. */ static int add_master_key_user(struct fscrypt_master_key *mk) { + kuid_t uid = current_fsuid(); char description[FSCRYPT_MK_USER_DESCRIPTION_SIZE]; - struct key *mk_user; + struct key *quota_key; + struct fscrypt_master_key_user *mk_user; int err; - format_mk_user_description(description, mk->mk_spec.u.identifier); - mk_user = key_alloc(&key_type_fscrypt_user, description, - current_fsuid(), current_gid(), current_cred(), - KEY_POS_SEARCH | KEY_USR_VIEW, 0, NULL); - if (IS_ERR(mk_user)) - return PTR_ERR(mk_user); + snprintf(description, sizeof(description), "%*phN.uid.%u", + FSCRYPT_KEY_IDENTIFIER_SIZE, mk->mk_spec.u.identifier, + __kuid_val(uid)); + quota_key = key_alloc(&key_type_fscrypt_user, description, uid, + current_gid(), current_cred(), + KEY_POS_SEARCH | KEY_USR_VIEW, 0, NULL); + if (IS_ERR(quota_key)) + return PTR_ERR(quota_key); + + err = key_instantiate_and_link(quota_key, NULL, 0, NULL, NULL); + if (err) { + key_put(quota_key); + return err; + } - err = key_instantiate_and_link(mk_user, NULL, 0, mk->mk_users, NULL); - key_put(mk_user); - return err; + mk_user = kzalloc_obj(*mk_user); + if (!mk_user) { + key_put(quota_key); + return -ENOMEM; + } + mk_user->uid = uid; + mk_user->quota_key = quota_key; + list_add(&mk_user->link, &mk->mk_users); + return 0; +} + +static void unlink_and_free_mk_user(struct fscrypt_master_key_user *mk_user) +{ + list_del(&mk_user->link); + key_put(mk_user->quota_key); + kfree(mk_user); } /* - * Remove the current user's "key" from ->mk_users. + * Remove the current user's claim from ->mk_users. * ->mk_sem must be held for write. * - * Returns 0 if removed, -ENOKEY if not found, or another -errno code. + * Returns 0 if removed or -ENOKEY if not found. */ static int remove_master_key_user(struct fscrypt_master_key *mk) { - struct key *mk_user; - int err; + struct fscrypt_master_key_user *mk_user; mk_user = find_master_key_user(mk); - if (IS_ERR(mk_user)) - return PTR_ERR(mk_user); - err = key_unlink(mk->mk_users, mk_user); - key_put(mk_user); - return err; + if (!mk_user) + return -ENOKEY; + unlink_and_free_mk_user(mk_user); + return 0; +} + +/* + * Clear ->mk_users. Either ->mk_sem must be held for write, or 'mk' must have + * no structural references left. + */ +static void clear_mk_users(struct fscrypt_master_key *mk) +{ + struct fscrypt_master_key_user *mk_user, *tmp; + + list_for_each_entry_safe(mk_user, tmp, &mk->mk_users, link) + unlink_and_free_mk_user(mk_user); } /* @@ -445,15 +424,14 @@ static int add_new_master_key(struct super_block *sb, refcount_set(&mk->mk_struct_refs, 1); mk->mk_spec = *mk_spec; + INIT_LIST_HEAD(&mk->mk_users); + INIT_LIST_HEAD(&mk->mk_decrypted_inodes); spin_lock_init(&mk->mk_decrypted_inodes_lock); INIT_LIST_HEAD(&mk->mk_mode_keys); if (mk_spec->type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) { - err = allocate_master_key_users_keyring(mk); - if (err) - goto out_put; err = add_master_key_user(mk); if (err) goto out_put; @@ -482,19 +460,13 @@ static int add_existing_master_key(struct fscrypt_master_key *mk, int err; /* - * If the current user is already in ->mk_users, then there's nothing to - * do. Otherwise, we need to add the user to ->mk_users. (Neither is - * applicable for v1 policy keys, which have NULL ->mk_users.) + * For v2 policy keys (FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER): If the current + * user is already in ->mk_users, then there's nothing to do. + * Otherwise, add the user to ->mk_users. */ - if (mk->mk_users) { - struct key *mk_user = find_master_key_user(mk); - - if (mk_user != ERR_PTR(-ENOKEY)) { - if (IS_ERR(mk_user)) - return PTR_ERR(mk_user); - key_put(mk_user); + if (mk->mk_spec.type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) { + if (find_master_key_user(mk) != NULL) return 0; - } err = add_master_key_user(mk); if (err) return err; @@ -893,7 +865,6 @@ int fscrypt_verify_key_added(struct super_block *sb, { struct fscrypt_key_specifier mk_spec; struct fscrypt_master_key *mk; - struct key *mk_user; int err; mk_spec.type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER; @@ -905,13 +876,10 @@ int fscrypt_verify_key_added(struct super_block *sb, goto out; } down_read(&mk->mk_sem); - mk_user = find_master_key_user(mk); - if (IS_ERR(mk_user)) { - err = PTR_ERR(mk_user); - } else { - key_put(mk_user); + if (find_master_key_user(mk) != NULL) err = 0; - } + else + err = -ENOKEY; up_read(&mk->mk_sem); fscrypt_put_master_key(mk); out: @@ -1103,16 +1071,18 @@ static int do_remove_key(struct file *filp, void __user *_uarg, bool all_users) down_write(&mk->mk_sem); /* If relevant, remove current user's (or all users) claim to the key */ - if (mk->mk_users && mk->mk_users->keys.nr_leaves_on_tree != 0) { - if (all_users) - err = keyring_clear(mk->mk_users); - else + if (!list_empty(&mk->mk_users)) { + if (all_users) { + clear_mk_users(mk); + err = 0; + } else { err = remove_master_key_user(mk); + } if (err) { up_write(&mk->mk_sem); goto out_put_key; } - if (mk->mk_users->keys.nr_leaves_on_tree != 0) { + if (!list_empty(&mk->mk_users)) { /* * Other users have still added the key too. We removed * the current user's claim to the key, but we still @@ -1198,6 +1168,8 @@ int fscrypt_ioctl_get_key_status(struct file *filp, void __user *uarg) struct super_block *sb = file_inode(filp)->i_sb; struct fscrypt_get_key_status_arg arg; struct fscrypt_master_key *mk; + kuid_t uid; + const struct fscrypt_master_key_user *mk_user; int err; if (copy_from_user(&arg, uarg, sizeof(arg))) @@ -1230,19 +1202,13 @@ int fscrypt_ioctl_get_key_status(struct file *filp, void __user *uarg) } arg.status = FSCRYPT_KEY_STATUS_PRESENT; - if (mk->mk_users) { - struct key *mk_user; - arg.user_count = mk->mk_users->keys.nr_leaves_on_tree; - mk_user = find_master_key_user(mk); - if (!IS_ERR(mk_user)) { + uid = current_fsuid(); + list_for_each_entry(mk_user, &mk->mk_users, link) { + arg.user_count++; + if (uid_eq(mk_user->uid, uid)) arg.status_flags |= FSCRYPT_KEY_STATUS_FLAG_ADDED_BY_SELF; - key_put(mk_user); - } else if (mk_user != ERR_PTR(-ENOKEY)) { - err = PTR_ERR(mk_user); - goto out_release_key; - } } err = 0; out_release_key: From a3020a389cb15f4598d8febf5ae70f0af0579e19 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Wed, 13 May 2026 18:55:55 +0100 Subject: [PATCH 2620/2758] rtc: mpfs: fix counter upload completion condition commit 9792ff8afa9017fe14f436f3ef3cd75f41f9f145 upstream. The condition that needs to be checked for upload completion is the UPLOAD bit in the completion register going low. The original iterations of this driver used a do-while and this was converted to a read_poll_timeout() during upstreaming without the condition being inverted as it should have been. I suspect that this went unnoticed until now because a) the first read was done when the bit was still set, immediately completing the read_poll_timeout() and b) because the RTC doesn't hold time when power is removed from the SoC reducing its utility (I for one keep it disabled). If my first suspicion was true when the driver was upstreamed, it's not true any longer though, hence the detection of the problem. Fixes: 0b31d703598dc ("rtc: Add driver for Microchip PolarFire SoC") CC: stable@vger.kernel.org Signed-off-by: Conor Dooley Tested-by: Valentina Fernandez Link: https://patch.msgid.link/20260513-panhandle-ashy-70c6abf84d59@spud Signed-off-by: Alexandre Belloni Signed-off-by: Greg Kroah-Hartman --- drivers/rtc/rtc-mpfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-mpfs.c b/drivers/rtc/rtc-mpfs.c index 6aa3eae575d2a..ece6de4a6adbd 100644 --- a/drivers/rtc/rtc-mpfs.c +++ b/drivers/rtc/rtc-mpfs.c @@ -112,7 +112,7 @@ static int mpfs_rtc_settime(struct device *dev, struct rtc_time *tm) ctrl |= CONTROL_UPLOAD_BIT; writel(ctrl, rtcdev->base + CONTROL_REG); - ret = read_poll_timeout(readl, prog, prog & CONTROL_UPLOAD_BIT, 0, UPLOAD_TIMEOUT_US, + ret = read_poll_timeout(readl, prog, !(prog & CONTROL_UPLOAD_BIT), 0, UPLOAD_TIMEOUT_US, false, rtcdev->base + CONTROL_REG); if (ret) { dev_err(dev, "timed out uploading time to rtc"); From 8a604fe15d03b687ee3bfa49c5e1ac6743136c5c Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 15 Jun 2026 14:47:31 +0800 Subject: [PATCH 2621/2758] hwmon: (w83627hf) remove VID sysfs files on error and remove commit 5264b389c4e02dec214a46c400eb3ab867a7749a upstream. w83627hf_probe() creates cpu0_vid and vrm with device_create_file() when VID information is available. The error path and remove callback only remove the common and optional attribute groups. Those groups do not contain cpu0_vid or vrm, so the files can remain after a later probe failure or after device removal while their callbacks still expect live driver data. Remove the standalone VID sysfs files from both the probe error path and the remove callback. Signed-off-by: Pengpeng Hou Link: https://lore.kernel.org/r/20260615064732.48113-1-pengpeng@iscas.ac.cn Cc: stable@vger.kernel.org Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/w83627hf.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c index 95115d7b863e3..bb993bb09f401 100644 --- a/drivers/hwmon/w83627hf.c +++ b/drivers/hwmon/w83627hf.c @@ -1823,6 +1823,8 @@ static int w83627hf_probe(struct platform_device *pdev) return 0; error: + device_remove_file(dev, &dev_attr_vrm); + device_remove_file(dev, &dev_attr_cpu0_vid); sysfs_remove_group(&dev->kobj, &w83627hf_group); sysfs_remove_group(&dev->kobj, &w83627hf_group_opt); return err; @@ -1834,6 +1836,8 @@ static void w83627hf_remove(struct platform_device *pdev) hwmon_device_unregister(data->hwmon_dev); + device_remove_file(&pdev->dev, &dev_attr_vrm); + device_remove_file(&pdev->dev, &dev_attr_cpu0_vid); sysfs_remove_group(&pdev->dev.kobj, &w83627hf_group); sysfs_remove_group(&pdev->dev.kobj, &w83627hf_group_opt); } From 1f11a29a3c809949f6a16cc338484b785745f980 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 15 Jun 2026 14:48:06 +0800 Subject: [PATCH 2622/2758] hwmon: (w83793) remove vrm sysfs file on probe failure commit 77b983757280c69b0290811669ff1d31022e5f1d upstream. w83793_probe() creates the vrm sysfs file after creating the VID files when VID support is present. The normal remove path deletes vrm, but the probe error path only removes the sensor, SDA, VID, fan, PWM and temperature files. A later probe failure can therefore leave vrm behind after the driver data has been freed. Remove vrm in the probe error path next to the VID files, matching the normal remove path. Signed-off-by: Pengpeng Hou Link: https://lore.kernel.org/r/20260615064806.51139-1-pengpeng@iscas.ac.cn Cc: stable@vger.kernel.org Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/w83793.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c index 24772cfbecb32..8726c5fe8a952 100644 --- a/drivers/hwmon/w83793.c +++ b/drivers/hwmon/w83793.c @@ -1917,6 +1917,7 @@ static int w83793_probe(struct i2c_client *client) for (i = 0; i < ARRAY_SIZE(w83793_vid); i++) device_remove_file(dev, &w83793_vid[i].dev_attr); + device_remove_file(dev, &dev_attr_vrm); for (i = 0; i < ARRAY_SIZE(w83793_left_fan); i++) device_remove_file(dev, &w83793_left_fan[i].dev_attr); From 4140c516473a0e116ab3a73f9320c2aa5800210b Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Wed, 24 Jun 2026 14:40:13 +0800 Subject: [PATCH 2623/2758] net: liquidio: fix BAR resource leak on PF number failure commit c63ee62a3c4ac1a1542f4c1a4b87e2f41df5a496 upstream. If cn23xx_get_pf_num() fails, the function returns without unmapping either BAR. Unmap both BARs before returning from the error path. Found by manual code review. Fixes: 0c45d7fe12c7 ("liquidio: fix use of pf in pass-through mode in a virtual machine") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Reviewed-by: Larysa Zaremba Link: https://patch.msgid.link/20260624064013.2809570-1-haoxiang_li2024@163.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- .../cavium/liquidio/cn23xx_pf_device.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c index 75f22f74774cb..06b4424e778ed 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c @@ -1163,18 +1163,14 @@ int setup_cn23xx_octeon_pf_device(struct octeon_device *oct) if (octeon_map_pci_barx(oct, 1, MAX_BAR1_IOREMAP_SIZE)) { dev_err(&oct->pci_dev->dev, "%s CN23XX BAR1 map failed\n", __func__); - octeon_unmap_pci_barx(oct, 0); - return 1; + goto err_unmap_bar0; } if (cn23xx_get_pf_num(oct) != 0) - return 1; + goto err_unmap_bar1; - if (cn23xx_sriov_config(oct)) { - octeon_unmap_pci_barx(oct, 0); - octeon_unmap_pci_barx(oct, 1); - return 1; - } + if (cn23xx_sriov_config(oct)) + goto err_unmap_bar1; octeon_write_csr64(oct, CN23XX_SLI_MAC_CREDIT_CNT, 0x3F802080802080ULL); @@ -1205,6 +1201,12 @@ int setup_cn23xx_octeon_pf_device(struct octeon_device *oct) oct->coproc_clock_rate = 1000000ULL * cn23xx_coprocessor_clock(oct); return 0; + +err_unmap_bar1: + octeon_unmap_pci_barx(oct, 1); +err_unmap_bar0: + octeon_unmap_pci_barx(oct, 0); + return 1; } EXPORT_SYMBOL_GPL(setup_cn23xx_octeon_pf_device); From 8519e89c7f4d7e1c19d18136efc689afe6a71b9a Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Thu, 25 Jun 2026 21:29:03 +0800 Subject: [PATCH 2624/2758] ACPI: TAD: Check AC wake capability before enabling wakeup commit 8522d806d84e2c3816c275ae6dd79e124c1b3dac upstream. ACPI_TAD_AC_WAKE is a non-zero bit definition, so testing the macro itself is always true. As a result, every TAD device is initialized as a system wakeup device, including RTC-only devices and devices whose wake capability bits were cleared because _PRW is absent. Test the capability value returned by _GCP instead. This keeps RTC-only TAD devices usable without advertising a wakeup capability that the firmware does not provide. Fixes: 6c711fde3a1c ("ACPI: TAD: Support RTC without wakeup") Cc: All applicable Signed-off-by: Xu Rao Link: https://patch.msgid.link/961A84FF37B50665+20260625132903.2840457-1-raoxu@uniontech.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/acpi_tad.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/acpi_tad.c b/drivers/acpi/acpi_tad.c index 386fc1abcbdcb..fc43df083738c 100644 --- a/drivers/acpi/acpi_tad.c +++ b/drivers/acpi/acpi_tad.c @@ -856,7 +856,7 @@ static int acpi_tad_probe(struct platform_device *pdev) * runtime suspend. Everything else should be taken care of by the ACPI * PM domain callbacks. */ - if (ACPI_TAD_AC_WAKE) { + if (caps & ACPI_TAD_AC_WAKE) { device_init_wakeup(dev, true); dev_pm_set_driver_flags(dev, DPM_FLAG_SMART_SUSPEND | DPM_FLAG_MAY_SKIP_RESUME); From 7ee43ec8e6774774abbe9dc3027225e01bc964a5 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Fri, 19 Jun 2026 09:59:38 +0800 Subject: [PATCH 2625/2758] hwmon: (occ) unregister sysfs devices outside occ lock commit e31408734332b8cc611342cdaaab6ba492180156 upstream. occ_active(false) and occ_shutdown() unregister sysfs-backed devices while occ->lock is held. hwmon_device_unregister() and sysfs_remove_group() can wait for active sysfs callbacks to drain, and those callbacks can enter the OCC update path and try to take occ->lock again. That gives the unregister paths the lock ordering occ->lock -> sysfs callback drain, while a callback has the opposite edge sysfs callback -> occ->lock. This issue was found by our static analysis tool and then manually reviewed against the current tree. The grounded PoC kept the real unregister and callback carrier: occ_shutdown() hwmon_device_unregister() occ_show_temp_1() occ_update_response() Lockdep reported the circular dependency with occ_shutdown() already holding the OCC mutex and hwmon_device_unregister() waiting on the sysfs side: WARNING: possible circular locking dependency detected ... (sysfs_lock) ... at: hwmon_device_unregister+0x12/0x30 [vuln_msv] ... (&test_occ.lock) ... at: occ_shutdown.constprop.0+0xe/0x40 [vuln_msv] occ_update_response.isra.0+0xb/0x20 [vuln_msv] occ_show_temp_1.constprop.0.isra.0+0x23/0x40 [vuln_msv] *** DEADLOCK *** Serialize hwmon registration and removal with a separate hwmon_lock. Under that lock, detach occ->hwmon and update occ->active while occ->lock is held so concurrent OCC state changes still see a stable state, then drop occ->lock before calling hwmon_device_unregister(). Remove the driver sysfs group before taking occ->lock in occ_shutdown(), so draining the driver attributes cannot wait while the OCC mutex is held. Also make OCC update callbacks return -ENODEV after deactivation, so callbacks that already passed sysfs active protection do not poll the hardware after teardown has detached the hwmon device. Fixes: 849b0156d996 ("hwmon: (occ) Delay hwmon registration until user request") Fixes: ac6888ac5a11 ("hwmon: (occ) Lock mutex in shutdown to prevent race with occ_active") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Link: https://lore.kernel.org/r/20260619015938.494464-1-runyu.xiao@seu.edu.cn Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/occ/common.c | 34 ++++++++++++++++++++++++++++------ drivers/hwmon/occ/common.h | 1 + 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c index 42cc6068bb08b..e18e80e832fd3 100644 --- a/drivers/hwmon/occ/common.c +++ b/drivers/hwmon/occ/common.c @@ -214,6 +214,11 @@ int occ_update_response(struct occ *occ) if (rc) return rc; + if (!occ->active) { + rc = -ENODEV; + goto unlock; + } + /* limit the maximum rate of polling the OCC */ if (time_after(jiffies, occ->next_update)) { rc = occ_poll(occ); @@ -222,6 +227,7 @@ int occ_update_response(struct occ *occ) rc = occ->last_error; } +unlock: mutex_unlock(&occ->lock); return rc; } @@ -1105,11 +1111,16 @@ static void occ_parse_poll_response(struct occ *occ) int occ_active(struct occ *occ, bool active) { - int rc = mutex_lock_interruptible(&occ->lock); + struct device *hwmon = NULL; + int rc = mutex_lock_interruptible(&occ->hwmon_lock); if (rc) return rc; + rc = mutex_lock_interruptible(&occ->lock); + if (rc) + goto unlock_hwmon; + if (active) { if (occ->active) { rc = -EALREADY; @@ -1154,14 +1165,17 @@ int occ_active(struct occ *occ, bool active) goto unlock; } - if (occ->hwmon) - hwmon_device_unregister(occ->hwmon); + hwmon = occ->hwmon; occ->active = false; occ->hwmon = NULL; } unlock: mutex_unlock(&occ->lock); + if (hwmon) + hwmon_device_unregister(hwmon); +unlock_hwmon: + mutex_unlock(&occ->hwmon_lock); return rc; } @@ -1170,6 +1184,7 @@ int occ_setup(struct occ *occ) int rc; mutex_init(&occ->lock); + mutex_init(&occ->hwmon_lock); occ->groups[0] = &occ->group; rc = occ_setup_sysfs(occ); @@ -1190,15 +1205,22 @@ EXPORT_SYMBOL_GPL(occ_setup); void occ_shutdown(struct occ *occ) { - mutex_lock(&occ->lock); + struct device *hwmon; occ_shutdown_sysfs(occ); - if (occ->hwmon) - hwmon_device_unregister(occ->hwmon); + mutex_lock(&occ->hwmon_lock); + mutex_lock(&occ->lock); + + hwmon = occ->hwmon; + occ->active = false; occ->hwmon = NULL; mutex_unlock(&occ->lock); + + if (hwmon) + hwmon_device_unregister(hwmon); + mutex_unlock(&occ->hwmon_lock); } EXPORT_SYMBOL_GPL(occ_shutdown); diff --git a/drivers/hwmon/occ/common.h b/drivers/hwmon/occ/common.h index 7ac4b2febce65..82f600093c7fa 100644 --- a/drivers/hwmon/occ/common.h +++ b/drivers/hwmon/occ/common.h @@ -101,6 +101,7 @@ struct occ { unsigned long next_update; struct mutex lock; /* lock OCC access */ + struct mutex hwmon_lock; /* serialize hwmon registration/removal */ struct device *hwmon; struct occ_attribute *attrs; From ca096be8de31256594a67c78b6e0eee89a7ddc89 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Thu, 25 Jun 2026 08:48:34 +0800 Subject: [PATCH 2626/2758] fsl/fman: Free init resources on KeyGen failure in fman_init() commit d288efa2b94abc2e45a061fceb156b4f4e5b37be upstream. fman_muram_alloc() allocates initialization resources before initializing the KeyGen block. If keygen_init() fails, the function returns -EINVAL directly and leaves those resources allocated. Free the initialization resources before returning from the KeyGen failure path. Fixes: 7472f4f281d0 ("fsl/fman: enable FMan Keygen") Cc: stable@kernel.org Signed-off-by: Haoxiang Li Reviewed-by: Pavan Chebbi Reviewed-by: Breno Leitao Link: https://patch.msgid.link/20260625004834.3394389-1-haoxiang_li2024@163.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/freescale/fman/fman.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/fman/fman.c b/drivers/net/ethernet/freescale/fman/fman.c index 013273a2de32f..299bab043175b 100644 --- a/drivers/net/ethernet/freescale/fman/fman.c +++ b/drivers/net/ethernet/freescale/fman/fman.c @@ -1995,8 +1995,10 @@ static int fman_init(struct fman *fman) /* Init KeyGen */ fman->keygen = keygen_init(fman->kg_regs); - if (!fman->keygen) + if (!fman->keygen) { + free_init_resources(fman); return -EINVAL; + } err = enable(fman, cfg); if (err != 0) From b99e890e6b32ffa11c145a16fefcf2c7137a9578 Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Fri, 26 Jun 2026 18:32:18 +0200 Subject: [PATCH 2627/2758] net: lan743x: Initialize eth_syslock spinlock before use commit 39139b1c1c2b614096519b526112c726adb12ff0 upstream. lan743x_hardware_init() calls pci11x1x_strap_get_status() during the PCI11x1x probe sequence. That helper acquires the Ethernet subsystem hardware lock via lan743x_hs_syslock_acquire(), which relies on adapter->eth_syslock_spinlock to serialize access. The spinlock is currently initialized only after the strap status is read. With CONFIG_DEBUG_SPINLOCK enabled, taking the zeroed initialized spinlock can trip the spinlock debug check. Fix by initializing adapter->eth_syslock_spinlock before reading the strap status so the probe path never attempts to lock an uninitialized spinlock. Fixes: 46b777ad9a8c ("net: lan743x: Add support to SGMII 1G and 2.5G") Cc: stable@vger.kernel.org # v6.0+ Signed-off-by: Andrea Righi Reviewed-by: David Thompson Reviewed-by: Thangaraj Samynathan Link: https://patch.msgid.link/20260626163218.3591486-1-arighi@nvidia.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/microchip/lan743x_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/microchip/lan743x_main.c b/drivers/net/ethernet/microchip/lan743x_main.c index ffac22883e497..95257c2bc451f 100644 --- a/drivers/net/ethernet/microchip/lan743x_main.c +++ b/drivers/net/ethernet/microchip/lan743x_main.c @@ -3542,8 +3542,8 @@ static int lan743x_hardware_init(struct lan743x_adapter *adapter, adapter->max_tx_channels = PCI11X1X_MAX_TX_CHANNELS; adapter->used_tx_channels = PCI11X1X_USED_TX_CHANNELS; adapter->max_vector_count = PCI11X1X_MAX_VECTOR_COUNT; - pci11x1x_strap_get_status(adapter); spin_lock_init(&adapter->eth_syslock_spinlock); + pci11x1x_strap_get_status(adapter); mutex_init(&adapter->sgmii_rw_lock); pci11x1x_set_rfe_rd_fifo_threshold(adapter); sgmii_ctl = lan743x_csr_read(adapter, SGMII_CTL); From fffeb2ab5eeb823d4c2330571a098f63237c9049 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Thu, 25 Jun 2026 04:51:20 -0500 Subject: [PATCH 2628/2758] net/sched: sch_multiq: Replace direct dequeue call with peek and qdisc_dequeue_peeked commit 54f6b0c843e228d499eb4b6bbb89df68cad9ad5d upstream. multiq_dequeue() takes a packet from a band's child with a direct ->dequeue() call after multiq_peek() peeked it. When the child is non-work-conserving the peek stashes the skb in the child's gso_skb, so the direct dequeue returns a different skb and orphans the stash, desyncing the child's qlen/backlog. With a qfq child reached through a peeking parent (e.g. tbf) this re-enters the child on an emptied list and dereferences NULL, panicking the kernel from softirq on ordinary egress. Take the packet through qdisc_dequeue_peeked(), as sch_prio already does and as sch_red and sch_sfb were just fixed to do. The helper is a no-op when the child has no stash, so a work-conserving child is unaffected. Fixes: 77be155cba4e ("pkt_sched: Add peek emulation for non-work-conserving qdiscs.") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Reviewed-by: Victor Nogueira Acked-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260625-b4-disp-31bcb279-v1-2-85c40b83c529@proton.me Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/sched/sch_multiq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c index 4e465d11e3d75..a467dd1223697 100644 --- a/net/sched/sch_multiq.c +++ b/net/sched/sch_multiq.c @@ -103,7 +103,7 @@ static struct sk_buff *multiq_dequeue(struct Qdisc *sch) if (!netif_xmit_stopped( netdev_get_tx_queue(qdisc_dev(sch), q->curband))) { qdisc = q->queues[q->curband]; - skb = qdisc->dequeue(qdisc); + skb = qdisc_dequeue_peeked(qdisc); if (skb) { qdisc_bstats_update(sch, skb); qdisc_qlen_dec(sch); From 2dcebbd1ad2e180fe7b98bf346ced69a872e11e6 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Thu, 25 Jun 2026 04:51:19 -0500 Subject: [PATCH 2629/2758] net/sched: sch_taprio: Replace direct dequeue call with peek and qdisc_dequeue_peeked commit e056e1dfcddca877dd46d704e8ec9860cfc9ec44 upstream. When taprio's software path peeks a non-work-conserving child qdisc, the child stashes the peeked skb in its gso_skb; taprio_dequeue_from_txq() then takes the packet with a direct child ->dequeue() call, which ignores that stash, orphans the peeked skb and desyncs the child's qlen/backlog. With a qfq child this re-enters the child on an emptied list and dereferences NULL, panicking the kernel from softirq on ordinary egress. Take the packet through qdisc_dequeue_peeked(), as sch_red and sch_sfb now do. The helper returns the child's stashed skb first and is a no-op when there is none, so a work-conserving child is unaffected and the gated path now consumes the skb whose length was charged to the budget. Fixes: 5a781ccbd19e ("tc: Add support for configuring the taprio scheduler") Cc: stable@vger.kernel.org Cc: Vladimir Oltean Signed-off-by: Bryam Vargas Reviewed-by: Victor Nogueira Acked-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260625-b4-disp-31bcb279-v1-1-85c40b83c529@proton.me Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/sched/sch_taprio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c index 06d93d9269187..d2d11a127f039 100644 --- a/net/sched/sch_taprio.c +++ b/net/sched/sch_taprio.c @@ -749,7 +749,7 @@ static struct sk_buff *taprio_dequeue_from_txq(struct Qdisc *sch, int txq, return NULL; skip_peek_checks: - skb = child->ops->dequeue(child); + skb = qdisc_dequeue_peeked(child); if (unlikely(!skb)) return NULL; From 6ee183d89261bf1d1cf9f06d80a40dab8f36ee55 Mon Sep 17 00:00:00 2001 From: David Lee Date: Wed, 1 Jul 2026 11:44:28 +0000 Subject: [PATCH 2630/2758] fhandle: reject detached mounts in capable_wrt_mount() commit 6c732471740bc2ac9b0946134f9f551dc75f4369 upstream. The recent fhandle RCU fix moved the mount namespace capability check into capable_wrt_mount(), so a non-NULL mnt_namespace survives the ns_capable() dereference. The helper still assumes the later READ_ONCE(mount->mnt_ns) must be non-NULL because may_decode_fh() checked is_mounted() first. That assumption is not stable. A detached mount from open_tree(..., OPEN_TREE_CLONE) can be dissolved on fput while open_by_handle_at() is between those checks, and umount_tree() can clear mount->mnt_ns. If the helper observes NULL, it dereferences mnt_ns->user_ns and panics. Return false when the RCU read observes a detached mount. This keeps the relaxed permission path conservative: a mount no longer attached to a namespace cannot authorize open_by_handle_at() access. Fixes: 620c266f3949 ("fhandle: relax open_by_handle_at() permission checks") Cc: stable@vger.kernel.org Signed-off-by: David Lee Assisted-by: LLM Link: https://patch.msgid.link/20260701114438.24431-1-david.lee@trailofbits.com Reviewed-by: Jeff Layton Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/fhandle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/fhandle.c b/fs/fhandle.c index 1ca7eb3a6cb51..f8829231e3d75 100644 --- a/fs/fhandle.c +++ b/fs/fhandle.c @@ -295,7 +295,7 @@ static bool capable_wrt_mount(struct mount *mount) */ guard(rcu)(); mnt_ns = READ_ONCE(mount->mnt_ns); - return ns_capable(mnt_ns->user_ns, CAP_SYS_ADMIN); + return mnt_ns && ns_capable(mnt_ns->user_ns, CAP_SYS_ADMIN); } static inline int may_decode_fh(struct handle_to_path_ctx *ctx, From 4a97d08d4ace292afb144d119657abf3413538ea Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Mon, 29 Jun 2026 21:17:39 +0200 Subject: [PATCH 2631/2758] hwmon: (max1619) add missing 'select REGMAP' to Kconfig commit 943a749bdffdd2132fab9240db890e07d93e1fcf upstream. The Kconfig entry for the MAX1619 sensor doesn't contain a `select REGMAP` parameter, causing build failures if regmap isn't selected previously during the build process. Fixes: f8016132ce49 ("hwmon: (max1619) Convert to use regmap") Cc: stable@vger.kernel.org Signed-off-by: Joshua Crofts Link: https://lore.kernel.org/r/20260629-add-kconfig-deps-v1-1-8104df929b1a@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index b9a225076765a..b1f121b84bf68 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1215,6 +1215,7 @@ config SENSORS_MAX16065 config SENSORS_MAX1619 tristate "Maxim MAX1619 sensor chip" depends on I2C + select REGMAP help If you say yes here you get support for MAX1619 sensor chip. From aac98ec816b080cc49c4dd55e8e61ffdfae73aee Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Thu, 25 Jun 2026 08:34:47 +0900 Subject: [PATCH 2632/2758] tracing/probes: Fix double addition of offset for @+FOFFSET commit 9a667b7750dda88cbf1cca96a53a2163b2ee71f7 upstream. Since commit 533059281ee5 ("tracing: probeevent: Introduce new argument fetching code") wrongly use @offset local variable during the parsing, the offset value is added twice when dereferencing. Reset the @offset after setting it in FETCH_OP_FOFFS. Link: https://lore.kernel.org/all/178217905962.643090.1978577464942171332.stgit@devnote2/ Fixes: 533059281ee5 ("tracing: probeevent: Introduce new argument fetching code") Signed-off-by: Masami Hiramatsu (Google) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace_probe.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index 3f928ed35180b..733ce83e1e11c 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -1248,6 +1248,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type, code->op = FETCH_OP_FOFFS; code->immediate = (unsigned long)offset; // imm64? + offset = 0; } else { /* uprobes don't support symbols */ if (!(ctx->flags & TPARG_FL_KERNEL)) { From e3d325c0bdb7bc5d1b4cc8d8441d79794cd03729 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Fri, 19 Jun 2026 04:38:20 -0500 Subject: [PATCH 2633/2758] orangefs: keep the readdir entry size 64-bit in fill_from_part() commit 18227a6bc98bd0ba96ed3ce9d5b28776a5a28dfc upstream. fill_from_part() computes the size of a directory entry in size_t but stores it in a __u32. An entry length near U32_MAX wraps it to a small value, bypasses the bounds check, and is then used to index the entry, reading far past the directory part -- an out-of-bounds read that oopses the kernel. Compute the size as a u64 so it cannot truncate; the bounds check then rejects the entry. The trailer is supplied by the userspace client. Fixes: 480e3e532e31 ("orangefs: support very large directories") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Link: https://patch.msgid.link/20260619-b4-disp-50d2bd59-v1-1-ce332969b4a2@proton.me Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/orangefs/dir.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/orangefs/dir.c b/fs/orangefs/dir.c index 6e2ebc8b9867b..115b2c2f52694 100644 --- a/fs/orangefs/dir.c +++ b/fs/orangefs/dir.c @@ -191,7 +191,8 @@ static int fill_from_part(struct orangefs_dir_part *part, { const int offset = sizeof(struct orangefs_readdir_response_s); struct orangefs_khandle *khandle; - __u32 *len, padlen; + __u32 *len; + u64 padlen; loff_t i; char *s; i = ctx->pos & ~PART_MASK; @@ -215,8 +216,8 @@ static int fill_from_part(struct orangefs_dir_part *part, * len is the size of the string itself. padlen is the * total size of the encoded string. */ - padlen = (sizeof *len + *len + 1) + - (8 - (sizeof *len + *len + 1)%8)%8; + padlen = (u64)sizeof *len + *len + 1; + padlen += (8 - padlen % 8) % 8; if (part->len < i + padlen + sizeof *khandle) goto next; s = (void *)part + offset + i + sizeof *len; From ae0265f0a95aaacef59d560a3e1ea36db8be9a52 Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Mon, 29 Jun 2026 14:40:49 +0800 Subject: [PATCH 2634/2758] net/mlx5: HWS, fix matcher leak on resize target setup failure commit bb09d0e64ecaa0aa0f7d1133a1696ed74dead295 upstream. hws_bwc_matcher_move() allocates a replacement matcher before setting it as the resize target. If mlx5hws_matcher_resize_set_target() fails, the replacement matcher is not attached anywhere and is leaked. Fix the leak by destroying the replacement matcher before returning from the resize-target failure path. The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1.1. An x86_64 allyesconfig build showed no new warnings. As we do not have a mlx5 HWS-capable device to test with, no runtime testing was able to be performed. Fixes: 2111bb970c78 ("net/mlx5: HWS, added backward-compatible API handling") Cc: stable@vger.kernel.org Signed-off-by: Dawei Feng Reviewed-by: Yevgeny Kliteynik Acked-by: Tariq Toukan Link: https://patch.msgid.link/20260629064049.3852759-1-dawei.feng@seu.edu.cn Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/mellanox/mlx5/core/steering/hws/bwc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/bwc.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/bwc.c index 6dcd9c2a78aa8..e60435eaebc56 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/bwc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/bwc.c @@ -205,6 +205,7 @@ static int hws_bwc_matcher_move(struct mlx5hws_bwc_matcher *bwc_matcher) ret = mlx5hws_matcher_resize_set_target(old_matcher, new_matcher); if (ret) { mlx5hws_err(ctx, "Rehash error: failed setting resize target\n"); + mlx5hws_matcher_destroy(new_matcher); return ret; } From 299739909c486bcea7445f5c3b066fdbc0d2df96 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Fri, 19 Jun 2026 21:54:02 -0500 Subject: [PATCH 2635/2758] ata: libata-core: Add NOLPM quirk for PNY CS900 1TB SSD commit 462775c620197adaabc983ce847e5b9878ff4cb0 upstream. The PNY CS900 1TB SSD (Phison PS3111-S11, DRAM-less) drops off the bus after entering Device-Initiated Slumber during idle. With the default med_power_with_dipm policy the link goes down (SStatus 1 SControl 300) and does not recover, forcing the filesystem read-only. Forcing max_performance keeps the link stable across prolonged idle. Add a NOLPM quirk so link power management is disabled for this drive specifically, leaving it intact for other devices on the host. Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Reviewed-by: Niklas Cassel Signed-off-by: Damien Le Moal Signed-off-by: Greg Kroah-Hartman --- drivers/ata/libata-core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 3e19a00a92393..cbf1fec858839 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4295,6 +4295,9 @@ static const struct ata_dev_quirks_entry __ata_dev_quirks[] = { /* Apacer models with LPM issues */ { "Apacer AS340*", NULL, ATA_QUIRK_NOLPM }, + /* PNY CS900 (Phison PS3111-S11, DRAM-less) drops the link on DIPM */ + { "PNY CS900 1TB SSD", NULL, ATA_QUIRK_NOLPM }, + /* Silicon Motion models with LPM issues */ { "MD619HXCLDE3TC", "TCVAID", ATA_QUIRK_NOLPM }, { "MD619GXCLDE3TC", "TCV35D", ATA_QUIRK_NOLPM }, From 7ba60286ed14d3162cbb705b988779fdb53811a5 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Thu, 25 Jun 2026 22:18:37 +0800 Subject: [PATCH 2636/2758] ata: pata_pxa: Fix DMA channel leak on probe error commit fcaf242e7fc406e78f444a35441e3b58f5e28781 upstream. When dmaengine_slave_config() fails, the DMA channel acquired by dma_request_chan() is not released before returning the error, leaking the channel reference. Fix by adding dma_release_channel() in the error path. The ata_host_activate() error path already correctly releases the DMA channel. Cc: stable@vger.kernel.org Fixes: 88622d80af82 ("ata: pata_pxa: dmaengine conversion") Signed-off-by: Wentao Liang Reviewed-by: Niklas Cassel Signed-off-by: Damien Le Moal Signed-off-by: Greg Kroah-Hartman --- drivers/ata/pata_pxa.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/ata/pata_pxa.c b/drivers/ata/pata_pxa.c index 03dbaf4a13a75..9f63bdfb8576e 100644 --- a/drivers/ata/pata_pxa.c +++ b/drivers/ata/pata_pxa.c @@ -286,6 +286,7 @@ static int pxa_ata_probe(struct platform_device *pdev) ret = dmaengine_slave_config(data->dma_chan, &config); if (ret < 0) { dev_err(&pdev->dev, "dma configuration failed: %d\n", ret); + dma_release_channel(data->dma_chan); return ret; } From d43efd1b5d976203e6f1ef26f67e8b1a7bc2751b Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Mon, 22 Jun 2026 22:23:45 -0500 Subject: [PATCH 2637/2758] ata: libata-core: Reject an invalid concurrent positioning ranges count commit 533a0b940f901c15e5cbbd4b5d66e871c209e8ce upstream. ata_dev_config_cpr() takes the number of range descriptors from buf[0] of the concurrent positioning ranges log (up to 255), which the device reports independently of the log size in the GPL directory. The count is then walked at a fixed 32-byte stride in two places with no bound: the log read here, and the INQUIRY VPD page B9h emitter, which writes one descriptor per range into the fixed 2048-byte ata_scsi_rbuf. A device reporting a count larger than its own log overflows the read buffer (up to 7704 bytes past a 512-byte slab), and a count above 62 overflows the response buffer on the emit side. Bound the count once, on probe, against both the log the device returned and the number of descriptors the VPD B9h response buffer can hold (ATA_DEV_MAX_CPR, derived from the rbuf size). Reject an out-of-range count with a warning; this keeps the emitter in bounds with no separate change there. Suggested-by: Damien Le Moal Fixes: fe22e1c2f705 ("libata: support concurrent positioning ranges log") Fixes: c745dfc541e7 ("libata: fix reading concurrent positioning ranges log") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Reviewed-by: Niklas Cassel Signed-off-by: Damien Le Moal Signed-off-by: Greg Kroah-Hartman --- drivers/ata/libata-core.c | 18 ++++++++++++++++++ drivers/ata/libata-scsi.c | 2 -- drivers/ata/libata.h | 9 +++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index cbf1fec858839..0bba44a5a0449 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -2847,6 +2847,24 @@ static void ata_dev_config_cpr(struct ata_device *dev) if (!nr_cpr) goto out; + /* + * The device reports the number of CPR descriptors independently of the + * log size, and that count is also used to emit VPD page B9h into the + * fixed-size rbuf. Reject a count larger than what that buffer can hold + * (ATA_DEV_MAX_CPR) or larger than the log the device actually returned. + */ + if (nr_cpr > ATA_DEV_MAX_CPR) { + ata_dev_warn(dev, + "Too many concurrent positioning ranges\n"); + goto out; + } + + if (buf_len < 64 + (size_t)nr_cpr * 32) { + ata_dev_warn(dev, + "Invalid number of concurrent positioning ranges\n"); + goto out; + } + cpr_log = kzalloc_flex(*cpr_log, cpr, nr_cpr); if (!cpr_log) goto out; diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index a4e0407c7481d..41b3b47327764 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -37,8 +37,6 @@ #include "libata.h" #include "libata-transport.h" -#define ATA_SCSI_RBUF_SIZE 2048 - static DEFINE_SPINLOCK(ata_scsi_rbuf_lock); static u8 ata_scsi_rbuf[ATA_SCSI_RBUF_SIZE]; diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index b5423b6e97de1..329e9c5776f0d 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -148,6 +148,15 @@ static inline bool ata_acpi_dev_manage_restart(struct ata_device *dev) { return #endif /* libata-scsi.c */ +#define ATA_SCSI_RBUF_SIZE 2048 + +/* + * Maximum number of concurrent positioning ranges (CPR) supported. The ACS + * specifications allow up to 255, but we limit this to the number of CPR + * descriptors that fit in the rbuf buffer used to emit VPD page B9h. + */ +#define ATA_DEV_MAX_CPR min(255, ((ATA_SCSI_RBUF_SIZE - 64) / 32)) + extern struct ata_device *ata_scsi_find_dev(struct ata_port *ap, const struct scsi_device *scsidev); extern int ata_scsi_add_hosts(struct ata_host *host, From 77f0023f22f6a2616ae128e9c93961b24ae52611 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Thu, 25 Jun 2026 14:17:28 +0800 Subject: [PATCH 2638/2758] net: wwan: iosm: bound device offsets in the MUX downlink decoder commit 526b8ef54668780c8f69e0211c342763d5dcbad1 upstream. mux_dl_adb_decode() walks a chain of aggregated datagram tables using offsets and lengths taken from the modem. first_table_index, next_table_index, table_length, datagram_index and datagram_length are all device supplied le values. Only first_table_index was checked, and only for being non zero. The decoder then formed adth = block + adth_index and read the table header and the datagram entries with no bound against the received skb. A modem that reports an index or a length past the downlink buffer makes the decoder read out of bounds. The buffer is IPC_MEM_MAX_DL_MUX_LITE_BUF_SIZE and skb->len is at most that, so skb->len is the real limit, but none of these in band offsets were checked against it. The table chain is also followed with no forward progress check. The loop takes the next table from adth->next_table_index and stops only when that reaches zero. A modem can stage two tables that point at each other, so the loop never ends. It runs in softirq and clones the skb on every pass. Validate every device offset and length against skb->len before use. The block header must fit. Each table header, on entry and after every next_table_index, must lie inside the skb. The datagram table must fit. Each datagram index and length must stay inside the skb. The header padding must not exceed the datagram length so the receive length does not wrap. Require each next_table_index to move forward so the chain cannot cycle. This was reproduced under KASAN as a slab out of bounds read on a normal downlink receive once the iosm net device is up. Fixes: 1f52d7b62285 ("net: wwan: iosm: Enable M.2 7360 WWAN card support") Suggested-by: Loic Poulain Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Simon Horman Reviewed-by: Loic Poulain Link: https://patch.msgid.link/178236824878.3259367.5389624724479864947@maoyixie.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/wwan/iosm/iosm_ipc_mux_codec.c | 40 ++++++++++++++++------ 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c b/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c index bff46f7ca59f3..0bbd41263cc2f 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c +++ b/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c @@ -553,19 +553,21 @@ static int mux_dl_process_dg(struct iosm_mux *ipc_mux, struct mux_adbh *adbh, u32 packet_offset, i, rc, dg_len; for (i = 0; i < nr_of_dg; i++, dg++) { - if (le32_to_cpu(dg->datagram_index) - < sizeof(struct mux_adbh)) + u32 dg_index = le32_to_cpu(dg->datagram_index); + + dg_len = le16_to_cpu(dg->datagram_length); + + if (dg_index < sizeof(struct mux_adbh)) goto dg_error; - /* Is the packet inside of the ADB */ - if (le32_to_cpu(dg->datagram_index) >= - le32_to_cpu(adbh->block_length)) { + /* Is the packet inside of the ADB and the received skb ? */ + if (dg_index >= le32_to_cpu(adbh->block_length) || + dg_index >= skb->len || + dg_len > skb->len - dg_index || + dl_head_pad_len >= dg_len) { goto dg_error; } else { - packet_offset = - le32_to_cpu(dg->datagram_index) + - dl_head_pad_len; - dg_len = le16_to_cpu(dg->datagram_length); + packet_offset = dg_index + dl_head_pad_len; /* Pass the packet to the netif layer. */ rc = ipc_mux_net_receive(ipc_mux, if_id, ipc_mux->wwan, packet_offset, @@ -589,12 +591,16 @@ static void mux_dl_adb_decode(struct iosm_mux *ipc_mux, struct mux_adbh *adbh; struct mux_adth *adth; int nr_of_dg, if_id; - u32 adth_index; + u32 adth_index, prev_index = 0; u8 *block; block = skb->data; adbh = (struct mux_adbh *)block; + /* The block header itself must fit in the received skb. */ + if (skb->len < sizeof(struct mux_adbh)) + goto adb_decode_err; + /* Process the aggregated datagram tables. */ adth_index = le32_to_cpu(adbh->first_table_index); @@ -606,6 +612,16 @@ static void mux_dl_adb_decode(struct iosm_mux *ipc_mux, /* Loop through mixed session tables. */ while (adth_index) { + /* The table header must lie within the received skb, and the + * chain must move forward so a modem cannot make the loop + * cycle between two tables. + */ + if (adth_index <= prev_index || + adth_index < sizeof(struct mux_adbh) || + adth_index > skb->len - sizeof(struct mux_adth)) + goto adb_decode_err; + prev_index = adth_index; + /* Get the reference to the table header. */ adth = (struct mux_adth *)(block + adth_index); @@ -629,6 +645,10 @@ static void mux_dl_adb_decode(struct iosm_mux *ipc_mux, if (le16_to_cpu(adth->table_length) < sizeof(struct mux_adth)) goto adb_decode_err; + /* The whole datagram table must fit in the received skb. */ + if (le16_to_cpu(adth->table_length) > skb->len - adth_index) + goto adb_decode_err; + /* Calculate the number of datagrams. */ nr_of_dg = (le16_to_cpu(adth->table_length) - sizeof(struct mux_adth)) / From 3034e5d67ea66d9a6256e0f1c9420e5614b1b287 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Wed, 24 Jun 2026 14:59:55 +0800 Subject: [PATCH 2639/2758] net: ipa: fix SMEM state handle leaks in SMP2P init commit 96ca1e658ae459276292bd6d971ab5d8c7e0379a upstream. ipa_smp2p_init() acquires two Qualcomm SMEM state handles with qcom_smem_state_get(). However, neither the init error paths nor ipa_smp2p_exit() release them. Release both handles with qcom_smem_state_put() in the init error paths and in ipa_smp2p_exit(). Fixes: 530f9216a953 ("soc: qcom: ipa: AP/modem communications") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Reviewed-by: Larysa Zaremba Reviewed-by: Alex Elder Link: https://patch.msgid.link/20260624065955.2822765-1-haoxiang_li2024@163.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ipa/ipa_smp2p.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/drivers/net/ipa/ipa_smp2p.c b/drivers/net/ipa/ipa_smp2p.c index 2f0ccdd937ccb..331c00ad02c05 100644 --- a/drivers/net/ipa/ipa_smp2p.c +++ b/drivers/net/ipa/ipa_smp2p.c @@ -232,19 +232,27 @@ ipa_smp2p_init(struct ipa *ipa, struct platform_device *pdev, bool modem_init) &valid_bit); if (IS_ERR(valid_state)) return PTR_ERR(valid_state); - if (valid_bit >= 32) /* BITS_PER_U32 */ - return -EINVAL; + if (valid_bit >= 32) { /* BITS_PER_U32 */ + ret = -EINVAL; + goto err_valid_state_put; + } enabled_state = qcom_smem_state_get(dev, "ipa-clock-enabled", &enabled_bit); - if (IS_ERR(enabled_state)) - return PTR_ERR(enabled_state); - if (enabled_bit >= 32) /* BITS_PER_U32 */ - return -EINVAL; + if (IS_ERR(enabled_state)) { + ret = PTR_ERR(enabled_state); + goto err_valid_state_put; + } + if (enabled_bit >= 32) { /* BITS_PER_U32 */ + ret = -EINVAL; + goto err_enabled_state_put; + } smp2p = kzalloc_obj(*smp2p); - if (!smp2p) - return -ENOMEM; + if (!smp2p) { + ret = -ENOMEM; + goto err_enabled_state_put; + } smp2p->ipa = ipa; @@ -289,6 +297,10 @@ ipa_smp2p_init(struct ipa *ipa, struct platform_device *pdev, bool modem_init) ipa->smp2p = NULL; mutex_destroy(&smp2p->mutex); kfree(smp2p); +err_enabled_state_put: + qcom_smem_state_put(enabled_state); +err_valid_state_put: + qcom_smem_state_put(valid_state); return ret; } @@ -305,6 +317,8 @@ void ipa_smp2p_exit(struct ipa *ipa) ipa_smp2p_power_release(ipa); ipa->smp2p = NULL; mutex_destroy(&smp2p->mutex); + qcom_smem_state_put(smp2p->enabled_state); + qcom_smem_state_put(smp2p->valid_state); kfree(smp2p); } From b770fcfcdced569bcf7c6982aeea8c3d11a21c2b Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Fri, 19 Jun 2026 21:27:46 +0900 Subject: [PATCH 2640/2758] hwmon: (asus_atk0110) Check package count before accessing element commit e2735b39f044bad7bf2017aef248935525bc0b97 upstream. atk_ec_present() walks the management group package returned by the GGRP ACPI method and, for each sub-package, reads its first element: id = &obj->package.elements[0]; if (id->type != ACPI_TYPE_INTEGER) without checking that the sub-package is non-empty. ACPICA allocates the element array with exactly package.count entries, so for a sub-package with a zero count this reads past the allocation. The sibling function atk_debugfs_ggrp_open() performs the same access but skips empty packages with a package.count check first. Add the same check to atk_ec_present() so a malformed firmware package cannot trigger an out-of-bounds read. Fixes: 9e6eba610c2e ("hwmon: (asus_atk0110) Enable the EC") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: HyeongJun An Link: https://lore.kernel.org/r/20260619122746.721981-1-sammiee5311@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/asus_atk0110.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/hwmon/asus_atk0110.c b/drivers/hwmon/asus_atk0110.c index 109318b0434d9..92afb64c09df8 100644 --- a/drivers/hwmon/asus_atk0110.c +++ b/drivers/hwmon/asus_atk0110.c @@ -1037,6 +1037,9 @@ static int atk_ec_present(struct atk_data *data) if (obj->type != ACPI_TYPE_PACKAGE) continue; + if (!obj->package.count) + continue; + id = &obj->package.elements[0]; if (id->type != ACPI_TYPE_INTEGER) continue; From 91b4d76dd07f1a1f20f73dfebb42ba04ac911a56 Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Tue, 30 Jun 2026 21:40:03 +0200 Subject: [PATCH 2641/2758] riscv: probes: save original sp in rethook trampoline commit bc7b086a45521a986a49045907f017e3e46c763e upstream. Reading a word from the stack in a kretprobe crashes a risc-v kernel. $ cd /sys/kernel/tracing/ $ echo 'r n_tty_write $stack0' > dynamic_events $ echo 1 > events/kprobes/enable Unable to handle kernel paging request at virtual address 0000000200000128 ... [] regs_get_kernel_stack_nth+0x26/0x38 [] process_fetch_insn+0x3ee/0x760 [] kretprobe_trace_func+0x116/0x1f0 [] kretprobe_dispatcher+0x4a/0x58 [] kretprobe_rethook_handler+0x5e/0x90 [] rethook_trampoline_handler+0x70/0x108 [] arch_rethook_trampoline_callback+0x12/0x1c [] arch_rethook_trampoline+0x48/0x94 [] tty_write+0x1a/0x30 In regs_get_kernel_stack_nth, regs->sp contains an arbitrary value. arch_rethook_trampoline saves the registers from the probed function in a struct pt_regs. sp is not saved. Instead, sp is decremented for arch_rethook_trampoline's local stack. Fix this crash and save the original sp along with the other registers. Use a0 as a temporary register, it is overwritten anyway. Cc: stable@vger.kernel.org Fixes: c22b0bcb1dd02 ("riscv: Add kprobes supported") Signed-off-by: Martin Kaiser Acked-by: Masami Hiramatsu (Google) Link: https://patch.msgid.link/20260630194010.1824039-1-martin@kaiser.cx [pjw@kernel.org: added Fixes tag; cc'ed stable] Signed-off-by: Paul Walmsley Signed-off-by: Greg Kroah-Hartman --- arch/riscv/kernel/probes/rethook_trampoline.S | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/riscv/kernel/probes/rethook_trampoline.S b/arch/riscv/kernel/probes/rethook_trampoline.S index f2cd83d9b0f00..c3aa8d8cf5aff 100644 --- a/arch/riscv/kernel/probes/rethook_trampoline.S +++ b/arch/riscv/kernel/probes/rethook_trampoline.S @@ -41,6 +41,9 @@ REG_S x29, PT_T4(sp) REG_S x30, PT_T5(sp) REG_S x31, PT_T6(sp) + /* save original sp */ + addi a0, sp, PT_SIZE_ON_STACK + REG_S a0, PT_SP(sp) .endm .macro restore_all_base_regs From 23afc3786acf359c135093893fb43a436768c832 Mon Sep 17 00:00:00 2001 From: Zi Yan Date: Mon, 22 Jun 2026 11:30:42 -0400 Subject: [PATCH 2642/2758] mm/compaction: handle free_pages_prepare() properly in compaction_free() commit 7da7d599b8a83271c464adfd5ef160202b470570 upstream. free_pages_prepare() can fail but compaction_free() does not handle the failure case. Failed pages should not be added back to cc->freepages for future use, since they can be either PageHWPoison or free_page_is_bad() and might cause data corruption. Link: https://lore.kernel.org/20260622-handle_free_pages_prepare_in_compaction_free-v1-1-fcf3b14abcf7@nvidia.com Fixes: 733aea0b3a7b ("mm/compaction: add support for >0 order folio memory compaction.") Signed-off-by: Zi Yan Reviewed-by: Vlastimil Babka (SUSE) Acked-by: Johannes Weiner Reviewed-by: Baolin Wang Reviewed-by: Lance Yang Cc: Brendan Jackman Cc: Jiaqi Yan Cc: Michal Hocko Cc: Suren Baghdasaryan Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/compaction.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mm/compaction.c b/mm/compaction.c index 3648ce22c8072..42eec51c06043 100644 --- a/mm/compaction.c +++ b/mm/compaction.c @@ -1875,15 +1875,14 @@ static void compaction_free(struct folio *dst, unsigned long data) int order = folio_order(dst); struct page *page = &dst->page; - if (folio_put_testzero(dst)) { - free_pages_prepare(page, order); + if (folio_put_testzero(dst) && free_pages_prepare(page, order)) { list_add(&dst->lru, &cc->freepages[order]); cc->nr_freepages += 1 << order; } cc->nr_migratepages += 1 << order; /* - * someone else has referenced the page, we cannot take it back to our - * free list. + * someone else has referenced the page or free_pages_prepare() fails, + * we cannot take it back to our free list. */ } From b321a046d7717225c07ba3f4b7b0a4758c2f9d58 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Tue, 23 Jun 2026 15:37:44 +0800 Subject: [PATCH 2643/2758] irqchip/irq-riscv-imsic-early: Fix fwnode leak on state setup failure commit 1358126fbed104e5657955d3ba029b283687ba02 upstream. imsic_early_acpi_init() allocates a firmware node before setting up the IMSIC state. If imsic_setup_state() fails, the function returns without freeing the allocated fwnode. Free the fwnode and clear the global pointer on this error path, matching the cleanup already done when imsic_early_probe() fails. [ tglx: Use a common cleanup path instead of copying code around ] Fixes: fbe826b1c106 ("irqchip/riscv-imsic: Add ACPI support") Signed-off-by: Haoxiang Li Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260623073744.2009137-1-haoxiang_li2024@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/irqchip/irq-riscv-imsic-early.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/irqchip/irq-riscv-imsic-early.c b/drivers/irqchip/irq-riscv-imsic-early.c index a7a1852b548c4..12efd241ce880 100644 --- a/drivers/irqchip/irq-riscv-imsic-early.c +++ b/drivers/irqchip/irq-riscv-imsic-early.c @@ -272,16 +272,13 @@ static int __init imsic_early_acpi_init(union acpi_subtable_headers *header, rc = imsic_setup_state(imsic_acpi_fwnode, imsic); if (rc) { pr_err("%pfwP: failed to setup state (error %d)\n", imsic_acpi_fwnode, rc); - return rc; + goto cleanup; } /* Do early setup of IMSIC state and IPIs */ rc = imsic_early_probe(imsic_acpi_fwnode); - if (rc) { - irq_domain_free_fwnode(imsic_acpi_fwnode); - imsic_acpi_fwnode = NULL; - return rc; - } + if (rc) + goto cleanup; rc = imsic_platform_acpi_probe(imsic_acpi_fwnode); @@ -300,8 +297,12 @@ static int __init imsic_early_acpi_init(union acpi_subtable_headers *header, * DT where IPI works but MSI probe fails for some reason. */ return 0; -} +cleanup: + irq_domain_free_fwnode(imsic_acpi_fwnode); + imsic_acpi_fwnode = NULL; + return rc; +} IRQCHIP_ACPI_DECLARE(riscv_imsic, ACPI_MADT_TYPE_IMSIC, NULL, 1, imsic_early_acpi_init); #endif From ae5347f3db1782c6118f6cc0d9fd8b1d43397db3 Mon Sep 17 00:00:00 2001 From: Gerald Schaefer Date: Tue, 23 Jun 2026 19:44:06 +0200 Subject: [PATCH 2644/2758] s390/monwriter: Reject buffer reuse with different data length commit 2995ccec260caa9e85b3301a4aba1e66ed80ad74 upstream. When data buffers are reused, e.g. for interval sample records, the first record determines the data length, and the size of the buffer for user copy. Current monwriter code does not check if the data length was changed for subsequent records, which also would never happen for valid user programs. However, a malicious user could change the data length, resulting in out of bounds user copy to the kernel buffer, and memory corruption. By default, the monwriter misc device is created with root-only permissions, so practical impact is typically low. Fix this by checking for changed data length and rejecting such records. Cc: stable@vger.kernel.org Signed-off-by: Gerald Schaefer Reviewed-by: Christian Borntraeger Signed-off-by: Vasily Gorbik Signed-off-by: Greg Kroah-Hartman --- drivers/s390/char/monwriter.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/s390/char/monwriter.c b/drivers/s390/char/monwriter.c index eaeb4a6384d1f..ecf121a87f88f 100644 --- a/drivers/s390/char/monwriter.c +++ b/drivers/s390/char/monwriter.c @@ -122,6 +122,9 @@ static int monwrite_new_hdr(struct mon_private *monpriv) kfree(monbuf->data); kfree(monbuf); monbuf = NULL; + } else if (monbuf->hdr.datalen != monhdr->datalen) { + /* Data with buffer reuse must not change its length */ + return -EINVAL; } } else if (monhdr->mon_function != MONWRITE_STOP_INTERVAL) { if (mon_buf_count >= mon_max_bufs) From d8b5b66388a51febe4b8505b0ecd9da15b4ba639 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Wed, 1 Jul 2026 18:42:22 +0200 Subject: [PATCH 2645/2758] mac802154: remove interfaces with RCU list deletion commit 539dfcf69105d8d3d4d677b71de6e5ede2e6dfa0 upstream. Queue wake, stop, and disable paths walk local->interfaces under RCU. The bulk hardware teardown path removes entries with list_del(), so an asynchronous transmit completion can follow a poisoned list node in ieee802154_wake_queue(). Use list_del_rcu() as in the single-interface removal path. The following unregister_netdevice() waits for in-flight RCU readers before freeing the netdevice, so no separate grace-period wait is needed. Fixes: 592dfbfc72f5 ("mac820154: move interface unregistration into iface") Reported-by: syzbot+36256deb69a588e9290e@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=36256deb69a588e9290e Cc: stable@vger.kernel.org Signed-off-by: Yousef Alhouseen Reviewed-by: Kuniyuki Iwashima Reviewed-by: Miquel Raynal Link: https://patch.msgid.link/20260701164222.9094-1-alhouseenyousef@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- net/mac802154/iface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac802154/iface.c b/net/mac802154/iface.c index 000be60d95803..b823720630e72 100644 --- a/net/mac802154/iface.c +++ b/net/mac802154/iface.c @@ -703,7 +703,7 @@ void ieee802154_remove_interfaces(struct ieee802154_local *local) mutex_lock(&local->iflist_mtx); list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) { - list_del(&sdata->list); + list_del_rcu(&sdata->list); unregister_netdevice(sdata->dev); } From 23d917acd9c9a9fd999688ec3fdde7aa58ab8a14 Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Tue, 30 Jun 2026 15:16:25 +0800 Subject: [PATCH 2646/2758] octeontx2-pf: fix SQB pointer leak on init failure commit 62e7df6d042aeebd5efb581074e28865c04477be upstream. otx2_init_hw_resources() initializes SQ aura and pool resources before several later setup steps. On failure, err_free_sq_ptrs only frees SQB pages, leaving the per-SQ sqb_ptrs arrays behind. Use otx2_free_sq_res() for the SQ unwind path and let it free sqb_ptrs even when sq->sqe has not been allocated yet. The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1.1. An x86_64 allyesconfig build showed no new warnings. As we do not have an OcteonTX2 PF device and the corresponding AF mailbox setup to test with, no runtime testing was able to be performed. Fixes: caa2da34fd25 ("octeontx2-pf: Initialize and config queues") Cc: stable@vger.kernel.org Reviewed-by: Ratheesh Kannoth Signed-off-by: Dawei Feng Link: https://patch.msgid.link/20260630071625.349996-1-dawei.feng@seu.edu.cn Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- .../ethernet/marvell/octeontx2/nic/otx2_pf.c | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index e2505631d215c..617a0db97e804 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -1569,15 +1569,15 @@ static void otx2_free_sq_res(struct otx2_nic *pf) otx2_sq_free_sqbs(pf); for (qidx = 0; qidx < otx2_get_total_tx_queues(pf); qidx++) { sq = &qset->sq[qidx]; - /* Skip freeing Qos queues if they are not initialized */ - if (!sq->sqe) - continue; - qmem_free(pf->dev, sq->sqe); - qmem_free(pf->dev, sq->sqe_ring); - qmem_free(pf->dev, sq->cpt_resp); - qmem_free(pf->dev, sq->tso_hdrs); - qmem_free(pf->dev, sq->timestamps); - kfree(sq->sg); + /* sq->sqe is not initialized for unused QoS queues */ + if (sq->sqe) { + qmem_free(pf->dev, sq->sqe); + qmem_free(pf->dev, sq->sqe_ring); + qmem_free(pf->dev, sq->cpt_resp); + qmem_free(pf->dev, sq->tso_hdrs); + qmem_free(pf->dev, sq->timestamps); + kfree(sq->sg); + } kfree(sq->sqb_ptrs); } } @@ -1712,13 +1712,12 @@ int otx2_init_hw_resources(struct otx2_nic *pf) return err; err_free_nix_queues: - otx2_free_sq_res(pf); otx2_free_cq_res(pf); otx2_ctx_disable(mbox, NIX_AQ_CTYPE_RQ, false); err_free_txsch: otx2_txschq_stop(pf); err_free_sq_ptrs: - otx2_sq_free_sqbs(pf); + otx2_free_sq_res(pf); err_free_rq_ptrs: otx2_free_aura_ptr(pf, AURA_NIX_RQ); otx2_ctx_disable(mbox, NPA_AQ_CTYPE_POOL, true); From 680d9dcbf428317250ea269ace4dd670610f38e0 Mon Sep 17 00:00:00 2001 From: Nirmoy Das Date: Tue, 30 Jun 2026 09:51:57 -0700 Subject: [PATCH 2647/2758] selftests: net: make busywait timeout clock portable commit dd6a23bac306b7aa322e0aaccb60c6e32a198fb3 upstream. loopy_wait() expects millisecond timestamps. However, Ubuntu Resolute can use uutils date, where `date -u +%s%3N` returns seconds plus full nanoseconds instead of a 3-digit millisecond field. This makes busywait expire too early and can make vlan_bridge_binding.sh read a stale operstate. Fixes: 25ae948b4478 ("selftests/net: add lib.sh") Cc: stable@vger.kernel.org # 6.8+ Link: https://github.com/uutils/coreutils/issues/11658 Signed-off-by: Nirmoy Das Link: https://patch.msgid.link/20260630165157.3814871-1-nirmoyd@nvidia.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/lib.sh | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/net/lib.sh b/tools/testing/selftests/net/lib.sh index b3827b43782b0..d46d2cec89e45 100644 --- a/tools/testing/selftests/net/lib.sh +++ b/tools/testing/selftests/net/lib.sh @@ -70,12 +70,33 @@ ksft_exit_status_merge() $ksft_xfail $ksft_pass $ksft_skip $ksft_fail } +timestamp_ms() +{ + local now + local seconds + local nanoseconds + + now=$(date -u +%s:%N) || return + seconds=${now%:*} + nanoseconds=${now#*:} + + if [[ $nanoseconds =~ ^[0-9]+$ ]]; then + nanoseconds=${nanoseconds:0:9} + else + nanoseconds=0 + fi + + echo $((seconds * 1000 + 10#$nanoseconds / 1000000)) +} + loopy_wait() { local sleep_cmd=$1; shift local timeout_ms=$1; shift + local start_time + local current_time - local start_time="$(date -u +%s%3N)" + start_time=$(timestamp_ms) || return while true do local out @@ -84,7 +105,7 @@ loopy_wait() return 0 fi - local current_time="$(date -u +%s%3N)" + current_time=$(timestamp_ms) || return if ((current_time - start_time > timeout_ms)); then echo -n "$out" return 1 From 21a537606fe35be2b85971a5fd35c0023b6ef98f Mon Sep 17 00:00:00 2001 From: Shuangpeng Bai Date: Tue, 30 Jun 2026 15:48:56 -0400 Subject: [PATCH 2648/2758] llc: fix SAP refcount leak in llc_ui_autobind() commit 660667cd406648bbaffbd5c0d897c2263a852f11 upstream. llc_ui_autobind() opens a SAP after choosing a dynamic LSAP. llc_sap_open() returns a reference owned by the caller, and llc_sap_add_socket() takes a second reference for the socket's membership in the SAP hash tables. llc_ui_bind() drops the caller's reference after adding the socket, but llc_ui_autobind() keeps it. When the socket is closed, llc_sap_remove_socket() releases only the socket reference, leaving the SAP on llc_sap_list with sk_count == 0. This is user-visible because repeated autobind and close cycles can consume all dynamic SAP values and make later autobinds fail with -EUSERS. Drop the caller's reference after a successful autobind, matching llc_ui_bind()'s ownership model. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Shuangpeng Bai Link: https://patch.msgid.link/20260630194856.1036497-1-shuangpeng.kernel@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- net/llc/af_llc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c index 1b210db3119e8..f4e20d1218e91 100644 --- a/net/llc/af_llc.c +++ b/net/llc/af_llc.c @@ -317,6 +317,7 @@ static int llc_ui_autobind(struct socket *sock, struct sockaddr_llc *addr) /* assign new connection to its SAP */ llc_sap_add_socket(sap, sk); sock_reset_flag(sk, SOCK_ZAPPED); + llc_sap_put(sap); rc = 0; out: dev_put(dev); From e5d0bb8871668f20de8f3c94b5ae3f372346bc6e Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Mon, 6 Jul 2026 18:16:24 +0800 Subject: [PATCH 2649/2758] ipvs: use parsed transport offset in SCTP state lookup commit 2f75c0faa3361b28e36cc0512b3299e163e25789 upstream. set_sctp_state() reads the SCTP chunk header again in order to drive the IPVS SCTP state table. For IPv6 it computes the offset with sizeof(struct ipv6hdr), while the surrounding IPVS code uses iph.len from ip_vs_fill_iph_skb(), where ipv6_find_hdr() has already skipped extension headers and found the real transport header. This makes the state machine read from the wrong offset for IPv6 SCTP packets that carry extension headers. For example, an INIT packet with an 8-byte destination options header can be scheduled correctly by sctp_conn_schedule(), but set_sctp_state() reads the first byte of the SCTP verification tag as a DATA chunk type. The connection then moves from NONE to ESTABLISHED instead of INIT1, gets the longer established timeout, and updates the active/inactive destination counters incorrectly. This happens even though the SCTP handshake has not completed. Use the parsed transport offset passed down from ip_vs_set_state() for the SCTP chunk-header lookup. For IPv4 and IPv6 packets without extension headers this preserves the existing offset. Fixes: 2906f66a5682 ("ipvs: SCTP Trasport Loadbalancing Support") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/netdev/20260705123040.35755-1-zhaoyz24@mails.tsinghua.edu.cn/ Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Assisted-by: Claude Code:GLM-5.2 Signed-off-by: Yizhou Zhao Acked-by: Julian Anastasov Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/ipvs/ip_vs_proto_sctp.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index 394367b7b3887..c67317be17dfa 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -372,20 +372,15 @@ static const char *sctp_state_name(int state) static inline void set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, - int direction, const struct sk_buff *skb) + int direction, const struct sk_buff *skb, + unsigned int iph_len) { struct sctp_chunkhdr _sctpch, *sch; unsigned char chunk_type; int event, next_state; - int ihl, cofs; - -#ifdef CONFIG_IP_VS_IPV6 - ihl = cp->af == AF_INET ? ip_hdrlen(skb) : sizeof(struct ipv6hdr); -#else - ihl = ip_hdrlen(skb); -#endif + int cofs; - cofs = ihl + sizeof(struct sctphdr); + cofs = iph_len + sizeof(struct sctphdr); sch = skb_header_pointer(skb, cofs, sizeof(_sctpch), &_sctpch); if (sch == NULL) return; @@ -472,7 +467,7 @@ sctp_state_transition(struct ip_vs_conn *cp, int direction, unsigned int iph_len) { spin_lock_bh(&cp->lock); - set_sctp_state(pd, cp, direction, skb); + set_sctp_state(pd, cp, direction, skb, iph_len); spin_unlock_bh(&cp->lock); } From 6335ab62d5fc9ed875279238233fba3462c168f5 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Thu, 2 Jul 2026 19:28:36 +0800 Subject: [PATCH 2650/2758] ipvs: reset full ip_vs_seq structs in ip_vs_conn_new commit 2975324d164c552b028632f107b567302863b7f6 upstream. Commit 9a05475cebdd ("ipvs: avoid kmem_cache_zalloc in ip_vs_conn_new") changed ip_vs_conn_new() to allocate an ip_vs_conn object with kmem_cache_alloc(). The function then initializes many fields explicitly, but only resets in_seq.delta and out_seq.delta in the two struct ip_vs_seq members. That leaves init_seq and previous_delta uninitialized. This is normally harmless while the corresponding IP_VS_CONN_F_IN_SEQ or IP_VS_CONN_F_OUT_SEQ flag is clear. For connections learned from a sync message, however, ip_vs_proc_conn() preserves those flags from IP_VS_CONN_F_BACKUP_MASK and passes opt=NULL when the message omits IPVS_OPT_SEQ_DATA. In that case the new connection can be hashed with SEQ flags set but with the rest of in_seq/out_seq still containing stale slab data. When a packet for such a connection is later handled by an IPVS application helper, vs_fix_seq() and vs_fix_ack_seq() use previous_delta and init_seq to rewrite TCP sequence numbers. A malformed sync message can therefore make forwarded packets carry stale slab bytes in their TCP seq/ack numbers, and can also corrupt the forwarded TCP flow. Reset both struct ip_vs_seq members completely before publishing the connection. This matches the existing "reset struct ip_vs_seq" comment and keeps the sequence-adjustment gates inactive unless valid sequence data is installed later. Fixes: 9a05475cebdd ("ipvs: avoid kmem_cache_zalloc in ip_vs_conn_new") Cc: stable@vger.kernel.org Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Assisted-by: Claude-Code:GLM-5.2 Signed-off-by: Yizhou Zhao Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/ipvs/ip_vs_conn.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 9ea6b4fa78bf0..b457dd2f6bc89 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -1411,8 +1411,8 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p, int dest_af, cp->app = NULL; cp->app_data = NULL; /* reset struct ip_vs_seq */ - cp->in_seq.delta = 0; - cp->out_seq.delta = 0; + memset(&cp->in_seq, 0, sizeof(cp->in_seq)); + memset(&cp->out_seq, 0, sizeof(cp->out_seq)); atomic_inc(&ipvs->conn_count); if (unlikely(flags & IP_VS_CONN_F_NO_CPORT)) { From c39087ad0b97fc11a3b058dfc8db9fd370762cb9 Mon Sep 17 00:00:00 2001 From: Daehyeon Ko <4ncienth@gmail.com> Date: Fri, 3 Jul 2026 17:36:33 +0900 Subject: [PATCH 2651/2758] macsec: don't read an unset MAC header in macsec_encrypt() commit f5089008f90c0a7c5520dff3934e0af00adf322d upstream. macsec_encrypt() reads the Ethernet header via eth_hdr(skb) (skb->head + skb->mac_header) to memmove() the 12 source/destination MAC bytes forward and make room for the SecTAG. On the AF_PACKET SOCK_RAW + PACKET_QDISC_BYPASS transmit path the skb reaches the macsec ndo_start_xmit() with the MAC header unset, so eth_hdr(skb) resolves to skb->head + (u16)~0 and the read is out of bounds: a 12-byte heap over-read that is also emitted on the wire as the frame's outer source/destination MAC. KASAN reports a slab-out-of-bounds read in macsec_start_xmit() on 6.0; on current mainline a CONFIG_DEBUG_NET build flags it as an unset mac header in skb_mac_header(). On the TX path the L2 header is at skb->data, so use skb_eth_hdr(), added by commit 96cc4b69581d ("macvlan: do not assume mac_header is set in macvlan_broadcast()") for exactly this purpose. Fixes: c09440f7dcb3 ("macsec: introduce IEEE 802.1AE driver") Cc: stable@vger.kernel.org Signed-off-by: Daehyeon Ko <4ncienth@gmail.com> Reviewed-by: Sabrina Dubroca Link: https://patch.msgid.link/20260703083634.2035145-1-4ncienth@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- drivers/net/macsec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index fb009120a9241..dd89282f01791 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -646,7 +646,7 @@ static struct sk_buff *macsec_encrypt(struct sk_buff *skb, } unprotected_len = skb->len; - eth = eth_hdr(skb); + eth = skb_eth_hdr(skb); sci_present = macsec_send_sci(secy); hh = skb_push(skb, macsec_extra_len(sci_present)); memmove(hh, eth, 2 * ETH_ALEN); From 94fe0ab01b480b52bd8f977edbd845ef375d69fd Mon Sep 17 00:00:00 2001 From: Dust Li Date: Tue, 7 Jul 2026 15:43:18 +0800 Subject: [PATCH 2652/2758] dibs: loopback: validate offset and size in move_data() commit 78237e3c0720fcc6eb9b87e90fd70f63eeca886f upstream. The loopback move_data() performs a memcpy into the registered DMB without checking whether offset + size exceeds the DMB length. Unlike real ISM hardware, which enforces memory region bounds natively, the software loopback has no such protection. A peer-supplied out-of-bounds offset or oversized write would result in an OOB write past the allocated kernel buffer. Add an explicit bounds check before the memcpy to reject such requests with -EINVAL. Fixes: f7a22071dbf3 ("net/smc: implement DMB-related operations of loopback-ism") Cc: stable@vger.kernel.org Reported-by: Federico Kirschbaum Signed-off-by: Dust Li Reported-by: Baul Lee Link: https://patch.msgid.link/20260707074318.1448662-1-dust.li@linux.alibaba.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- drivers/dibs/dibs_loopback.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/dibs/dibs_loopback.c b/drivers/dibs/dibs_loopback.c index ec3b48cb0e874..0f2e093111526 100644 --- a/drivers/dibs/dibs_loopback.c +++ b/drivers/dibs/dibs_loopback.c @@ -254,6 +254,11 @@ static int dibs_lo_move_data(struct dibs_dev *dibs, u64 dmb_tok, read_unlock_bh(&ldev->dmb_ht_lock); return -EINVAL; } + if ((u64)offset + size > rmb_node->len) { + read_unlock_bh(&ldev->dmb_ht_lock); + return -EINVAL; + } + memcpy((char *)rmb_node->cpu_addr + offset, data, size); sba_idx = rmb_node->sba_idx; read_unlock_bh(&ldev->dmb_ht_lock); From 109241d9880488aafd8e104832b4d4859ad57244 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Lebrun?= Date: Thu, 2 Jul 2026 17:37:02 +0200 Subject: [PATCH 2653/2758] net: macb: drop in-flight Tx SKBs on close MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 27f575836cfebbf872dec020428742b10650a955 upstream. The MACB driver has since forever leaked the outgoing SKBs that have not yet been marked as completed. They live in queue->tx_skb which gets freed without remorse nor checking. macb_free_consistent() gets called in a few codepaths, but only close will trigger the added expressions. In macb_open() and macb_alloc_consistent() failure cases, queues' tx_skb just got allocated and are empty. Fixes: 89e5785fc8a6 ("[PATCH] Atmel MACB ethernet driver") Cc: stable@vger.kernel.org Reviewed-by: Nicolai Buchwitz Signed-off-by: Théo Lebrun Link: https://patch.msgid.link/20260702-macb-drop-tx-v4-1-1c833eebdbc8@bootlin.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/cadence/macb_main.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index fd282a1700fb9..d394f1f43b685 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -2668,8 +2668,25 @@ static void macb_free_consistent(struct macb *bp) dma_free_coherent(dev, size, bp->queues[0].rx_ring, bp->queues[0].rx_ring_dma); for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { - kfree(queue->tx_skb); - queue->tx_skb = NULL; + if (queue->tx_skb) { + unsigned int dropped = 0, tail; + + for (tail = queue->tx_tail; tail != queue->tx_head; + tail++) { + if (macb_tx_skb(queue, tail)->skb) + dropped++; + macb_tx_unmap(bp, macb_tx_skb(queue, tail), 0); + } + + queue->stats.tx_dropped += dropped; + bp->dev->stats.tx_dropped += dropped; + + kfree(queue->tx_skb); + queue->tx_skb = NULL; + } + + queue->tx_head = 0; + queue->tx_tail = 0; queue->tx_ring = NULL; queue->rx_ring = NULL; } From f50d87f9752791380f31b03d69a18377907fc465 Mon Sep 17 00:00:00 2001 From: Zhu Lingshan Date: Wed, 1 Jul 2026 16:08:12 +0800 Subject: [PATCH 2654/2758] amdkfd: properly free secondary context id commit a88419af8dbd5abf8b1dfdfd9346376bce5413b4 upstream. Function kfd_process_free_id() should skip over the primary kfd process because its context id is fixed assigned, not allocated through the ida table. This function should only work on secondary contexts. Fixes: fac682a1d1af ("amdkfd: identify a secondary kfd process by its id") Signed-off-by: Zhu Lingshan Reviewed-by: Felix Kuehling Signed-off-by: Alex Deucher (cherry picked from commit 8799ba6fb6a48438aea20c82e74c2f2a3d2b2e7a) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdkfd/kfd_process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c b/drivers/gpu/drm/amd/amdkfd/kfd_process.c index d28ca581cad05..888fbaf069be8 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c @@ -910,7 +910,7 @@ static void kfd_process_free_id(struct kfd_process *process) { struct kfd_process *primary_process; - if (process->context_id != KFD_CONTEXT_ID_PRIMARY) + if (process->context_id == KFD_CONTEXT_ID_PRIMARY) return; primary_process = kfd_lookup_process_by_mm(process->lead_thread->mm); From c3f200efb45470417824a36fcd2147bd8f5fb78b Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Wed, 10 Jun 2026 15:52:01 +0800 Subject: [PATCH 2655/2758] arm64: smp: Fix hot-unplug tearing by forcing unregistration commit 18a4e5cf633fad5c40ac9d936c51bf38db68796d upstream. Sashiko review pointed out the following issue[1]. Commit eba4675008a6 ("arm64: arch_register_cpu() variant to check if an ACPI handle is now available.") introduced architectural safety blocks inside arch_unregister_cpu(). If a hot-unplug operation is determined to be a physical hardware removal (where _STA evaluates to !ACPI_STA_DEVICE_PRESENT), or if firmware evaluation fails, it aborts the unregistration transaction early to protect unreadied arm64 infrastructure. However, returning early from arch_unregister_cpu() causes a catastrophic state tearing because the generic ACPI layer (acpi_processor_post_eject()) unconditionally continues its cleanup flow. This leaves the stale sysfs device leaked in the memory, deadlocking any subsequent hot-add attempts on the same CPU. Fix it by simplifying arch_unregister_cpu() to always proceed with the unregistration, as a pr_err_once() warning is sufficient to make it more visible for currently not supported physical CPU removal. Also remove the redundant NULL check on acpi_handle as it cannot be NULL when calling arch_unregister_cpu(). Cc: Catalin Marinas Cc: Jonathan Cameron Cc: James Morse Cc: stable@vger.kernel.org Link: https://sashiko.dev/#/patchset/20260520022023.126670-1-ruanjinjie@huawei.com [1] Fixes: eba4675008a6 ("arm64: arch_register_cpu() variant to check if an ACPI handle is now available.") Suggested-by: Catalin Marinas Signed-off-by: Jinjie Ruan Reviewed-by: Catalin Marinas Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kernel/smp.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index 1aa324104afb4..543d3459f6e30 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -535,23 +535,13 @@ void arch_unregister_cpu(int cpu) { acpi_handle acpi_handle = acpi_get_processor_handle(cpu); struct cpu *c = &per_cpu(cpu_devices, cpu); - acpi_status status; unsigned long long sta; - - if (!acpi_handle) { - pr_err_once("Removing a CPU without associated ACPI handle\n"); - return; - } + acpi_status status; status = acpi_evaluate_integer(acpi_handle, "_STA", NULL, &sta); - if (ACPI_FAILURE(status)) - return; - - /* For now do not allow anything that looks like physical CPU HP */ - if (cpu_present(cpu) && !(sta & ACPI_STA_DEVICE_PRESENT)) { + if (!ACPI_FAILURE(status) && + cpu_present(cpu) && !(sta & ACPI_STA_DEVICE_PRESENT)) pr_err_once("Changing CPU present bit is not supported\n"); - return; - } unregister_cpu(c); } From 901a489d89ee9c854624c8444090e38e70aed234 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Wed, 10 Jun 2026 15:52:02 +0800 Subject: [PATCH 2656/2758] cpu/hotplug: Fix NULL kobject warning in cpuhp_smt_enable() commit f9a82544c7174851f5c7524622f5966dcafd3a47 upstream. On arm64, when booting with `maxcpus` greater than the number of present CPUs (e.g., QEMU -smp cpus=4,maxcpus=8), some CPUs are marked as 'present' but have not yet been registered via register_cpu(). Consequently, the per-cpu device objects for these CPUs are not yet initialized. In cpuhp_smt_enable(), the code iterates over all present CPUs. Calling _cpu_up() for these unregistered CPUs eventually leads to sysfs_create_group() being called with a NULL kobject (or a kobject without a directory), triggering the following warning in fs/sysfs/group.c: WARNING: fs/sysfs/group.c:137 at internal_create_group+0x41c/0x4bc, CPU#2: sh/181 [...] Call trace: internal_create_group+0x41c/0x4bc (P) sysfs_create_group+0x18/0x24 topology_add_dev+0x1c/0x28 cpuhp_invoke_callback+0x104/0x20c __cpuhp_invoke_callback_range+0x94/0x11c _cpu_up+0x200/0x37c When booting with ACPI, arm64 smp_prepare_cpus() currently sets all enumerated CPUs as "present" regardless of their status in the MADT. This causes issues with SMT hotplug control. For instance, with QEMU's "-smp 4,maxcpus=8" configuration, the MADT GICC entries are populated as follows: 1. The first four CPUs: `Enabled` set but `Online Capable` not set. 2. The remaining four CPUs: `Online Capable` set but `Enabled` not set to support potential hot-plugging. Fix this by: 1. When booting with ACPI, checking the ACPI_MADT_ENABLED flag in the GICC entry before calling set_cpu_present() during SMP initialization. 2. Properly managing the present mask in acpi_map_cpu() and acpi_unmap_cpu() to support actual CPU hotplug events, This aligns with other architectures like x86 and LoongArch. 3. Update the arm64 CPU hotplug documentation to no longer state that all online-capable vCPUs are marked as present by the kernel at boot time. This ensures that only physically available or explicitly enabled CPUs are in the present mask, keeping the SMT control logic consistent with the actual hardware state. Cc: Catalin Marinas Cc: Jonathan Cameron Cc: James Morse Cc: Yicong Yang Cc: stable@vger.kernel.org Link: https://uefi.org/specs/ACPI/6.5/05_ACPI_Software_Programming_Model.html#gic-cpu-interface-gicc-structure Fixes: eed4583bcf9a ("arm64: Kconfig: Enable HOTPLUG_SMT") Reviewed-by: Catalin Marinas Suggested-by: Catalin Marinas Signed-off-by: Jinjie Ruan Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman --- Documentation/arch/arm64/cpu-hotplug.rst | 28 ++++++++++++++---------- arch/arm64/kernel/acpi.c | 2 ++ arch/arm64/kernel/smp.c | 12 +++++++++- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/Documentation/arch/arm64/cpu-hotplug.rst b/Documentation/arch/arm64/cpu-hotplug.rst index 8fb438bf77816..7c3379b704aaf 100644 --- a/Documentation/arch/arm64/cpu-hotplug.rst +++ b/Documentation/arch/arm64/cpu-hotplug.rst @@ -47,11 +47,12 @@ ever have can be described at boot. There are no power-domain considerations as such devices are emulated. CPU Hotplug on virtual systems is supported. It is distinct from physical -CPU Hotplug as all resources are described as ``present``, but CPUs may be -marked as disabled by firmware. Only the CPU's online/offline behaviour is -influenced by firmware. An example is where a virtual machine boots with a -single CPU, and additional CPUs are added once a cloud orchestrator deploys -the workload. +CPU Hotplug as all vCPU resources are statically described in the firmware +configuration tables (e.g. MADT), meaning their maximum possible count is +known at boot. However, vCPUs that are not enabled at boot are not marked +as ``present`` by the kernel until they are hotplugged. An example is where +a virtual machine boots with a single CPU, and additional CPUs are added +once a cloud orchestrator deploys the workload. For a virtual machine, the VMM (e.g. Qemu) plays the part of firmware. @@ -60,16 +61,19 @@ brought online. Firmware can enforce its policy via PSCI's return codes. e.g. ``DENIED``. The ACPI tables must describe all the resources of the virtual machine. CPUs -that firmware wishes to disable either from boot (or later) should not be -``enabled`` in the MADT GICC structures, but should have the ``online capable`` -bit set, to indicate they can be enabled later. The boot CPU must be marked as -``enabled``. The 'always on' GICR structure must be used to describe the -redistributors. +that are hot-pluggable must have the ``online capable`` bit set and the +``enabled`` bit cleared in the MADT GICC structures to indicate they can be +enabled later. The boot CPU must be marked as ``enabled`` with its +``online capable`` bit cleared. The 'always on' GICR structure must be used +to describe the redistributors. CPUs described as ``online capable`` but not ``enabled`` can be set to enabled by the DSDT's Processor object's _STA method. On virtual systems the _STA method -must always report the CPU as ``present``. Changes to the firmware policy can -be notified to the OS via device-check or eject-request. +must always set the ``ACPI_STA_DEVICE_PRESENT`` bit, while toggling the +``ACPI_STA_DEVICE_ENABLED`` bit to reflect its plug status. The kernel will +then dynamically mark the vCPU as ``present`` within the OS when the +``ACPI_STA_DEVICE_ENABLED`` bit becomes set during hot-add. Changes to the +firmware policy can be notified to the OS via device-check or eject-request. CPUs described as ``enabled`` in the static table, should not have their _STA modified dynamically by firmware. Soft-restart features such as kexec will diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c index 5891f92c20354..681aa2bbc3995 100644 --- a/arch/arm64/kernel/acpi.c +++ b/arch/arm64/kernel/acpi.c @@ -448,12 +448,14 @@ int acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, u32 apci_id, return *pcpu; } + set_cpu_present(*pcpu, true); return 0; } EXPORT_SYMBOL(acpi_map_cpu); int acpi_unmap_cpu(int cpu) { + set_cpu_present(cpu, false); return 0; } EXPORT_SYMBOL(acpi_unmap_cpu); diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index 543d3459f6e30..24f8448e1fbba 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -556,6 +556,11 @@ struct acpi_madt_generic_interrupt *acpi_cpu_get_madt_gicc(int cpu) } EXPORT_SYMBOL_GPL(acpi_cpu_get_madt_gicc); +static bool acpi_cpu_is_present(int cpu) +{ + return acpi_cpu_get_madt_gicc(cpu)->flags & ACPI_MADT_ENABLED; +} + /* * acpi_map_gic_cpu_interface - parse processor MADT entry * @@ -660,6 +665,10 @@ static void __init acpi_parse_and_init_cpus(void) early_map_cpu_to_node(i, acpi_numa_get_nid(i)); } #else +static bool acpi_cpu_is_present(int cpu) +{ + return false; +} #define acpi_parse_and_init_cpus(...) do { } while (0) #endif @@ -798,7 +807,8 @@ void __init smp_prepare_cpus(unsigned int max_cpus) if (err) continue; - set_cpu_present(cpu, true); + if (acpi_disabled || acpi_cpu_is_present(cpu)) + set_cpu_present(cpu, true); numa_store_cpu_info(cpu); } } From fa5c7c313018220bb5df6aea619cc6ad11484363 Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Mon, 6 Jul 2026 15:46:21 -0700 Subject: [PATCH 2657/2758] fs/resctrl: Free mon_data structures on rdt_get_tree() failure commit ca0676ae2e1a23ac3e858224fc630b99f9e216ea upstream. If mkdir_mondata_all() or a subsequent call in rdt_get_tree() fails, the mon_data structures allocated by mon_get_kn_priv() are leaked. Add mon_put_kn_priv() to the out_mongrp error path to free the mon_data structures. Fixes: 2a6566038544 ("x86/resctrl: Expand the width of domid by replacing mon_data_bits") Closes: https://lore.kernel.org/lkml/5d38c1fb-8f91-472b-8897-24b2f50c772b@intel.com/ Reported-by: Reinette Chatre Signed-off-by: Tony Luck Signed-off-by: Reinette Chatre Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Chen Yu Reviewed-by: Ben Horgan Cc: Link: https://patch.msgid.link/433623b7e3316ffd52323255d1aa4f156ad97cb1.1783377598.git.reinette.chatre@intel.com Signed-off-by: Greg Kroah-Hartman --- fs/resctrl/rdtgroup.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c index cdc14f3a3a817..729370eddc91e 100644 --- a/fs/resctrl/rdtgroup.c +++ b/fs/resctrl/rdtgroup.c @@ -74,6 +74,8 @@ static int rdtgroup_setup_root(struct rdt_fs_context *ctx); static void rdtgroup_destroy_root(void); +static void mon_put_kn_priv(void); + struct dentry *debugfs_resctrl; /* @@ -2895,6 +2897,7 @@ static int rdt_get_tree(struct fs_context *fc) kernfs_remove(kn_mondata); out_mongrp: if (resctrl_arch_mon_capable()) { + mon_put_kn_priv(); rdtgroup_unassign_cntrs(&rdtgroup_default); kernfs_remove(kn_mongrp); } From f7628eea9212e185a09df3aea603ca8580b8678d Mon Sep 17 00:00:00 2001 From: Reinette Chatre Date: Mon, 6 Jul 2026 15:46:23 -0700 Subject: [PATCH 2658/2758] fs/resctrl: Fix double-add of pseudo-locked region's RMID to free list commit b9f089723aee892efc77c349ae47a6b452b293c4 upstream. A pseudo-locked group's RMID is freed when it is created. On unmount rmdir_all_sub() unconditionally frees all RMID of all groups, resulting in a double-free of the pseudo-locked group's RMID. The consequence of this is that the original free results in the pseudo-locked group's RMID being added to the rmid_free_lru linked list and the second free then attempts to add the same RMID entry to the rmid_free_lru again. Do not double-free a pseudo-locked group's RMID. Fixes: e0bdfe8e36f3 ("x86/intel_rdt: Support creation/removal of pseudo-locked region") Signed-off-by: Reinette Chatre Signed-off-by: Borislav Petkov (AMD) Cc: Link: https://patch.msgid.link/551432dd7e624a862b8e58314c38aaba0afff3e9.1783377598.git.reinette.chatre@intel.com Signed-off-by: Greg Kroah-Hartman --- fs/resctrl/rdtgroup.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c index 729370eddc91e..e7c5a4e0590df 100644 --- a/fs/resctrl/rdtgroup.c +++ b/fs/resctrl/rdtgroup.c @@ -3074,10 +3074,6 @@ static void rmdir_all_sub(void) if (rdtgrp == &rdtgroup_default) continue; - if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP || - rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED) - rdtgroup_pseudo_lock_remove(rdtgrp); - /* * Give any CPUs back to the default group. We cannot copy * cpu_online_mask because a CPU might have executed the @@ -3088,7 +3084,13 @@ static void rmdir_all_sub(void) rdtgroup_unassign_cntrs(rdtgrp); - free_rmid(rdtgrp->closid, rdtgrp->mon.rmid); + if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP || + rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED) { + rdtgroup_pseudo_lock_remove(rdtgrp); + } else { + /* Pseudo-locked group's RMID is freed during setup. */ + free_rmid(rdtgrp->closid, rdtgrp->mon.rmid); + } kernfs_remove(rdtgrp->kn); list_del(&rdtgrp->rdtgroup_list); From f723ea50a96d8670fd8b7e04cc2d4c1a2956835c Mon Sep 17 00:00:00 2001 From: TJ Adams Date: Mon, 6 Jul 2026 14:00:53 -0700 Subject: [PATCH 2659/2758] ata: libata-core: Skip HPA resize for locked drives commit 917d0a4b95ea7ba01ed6296fb808f752d5d81107 upstream. Skip HPA resize in ata_hpa_resize() if the drive is security locked. If the drive is locked, the command to read the native max address fails with -EACCES, which currently causes the sticky quirk ATA_QUIRK_BROKEN_HPA to be set on the device. Setting this sticky quirk causes subsequent revalidations (after the drive is unlocked) to bypass HPA checks, preventing the unlocked drive from exposing its full native capacity without a reboot or device removal. Cc: stable@vger.kernel.org Signed-off-by: Terrence Adams Signed-off-by: Damien Le Moal Signed-off-by: Greg Kroah-Hartman --- drivers/ata/libata-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 0bba44a5a0449..88833fb5bb81f 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -1338,7 +1338,7 @@ static int ata_hpa_resize(struct ata_device *dev) /* do we need to do it? */ if ((dev->class != ATA_DEV_ATA && dev->class != ATA_DEV_ZAC) || !ata_id_has_lba(dev->id) || !ata_id_hpa_enabled(dev->id) || - (dev->quirks & ATA_QUIRK_BROKEN_HPA)) + (dev->quirks & ATA_QUIRK_BROKEN_HPA) || ata_id_is_locked(dev->id)) return 0; /* read native max address */ From f713d7a7e0f23aff134fc773936063184d9be9e3 Mon Sep 17 00:00:00 2001 From: TJ Adams Date: Mon, 6 Jul 2026 14:00:54 -0700 Subject: [PATCH 2660/2758] ata: libata-core: Allow capacity transition to zero for locked drives commit 793bf193b18e9bff6c4280268bbffd16a5b533e5 upstream. Commit 91842ed844a0 ("ata: libata-core: Set capacity to zero for a security locked drive") introduced setting the device capacity (n_sectors) to zero in ata_dev_configure() if the drive is security locked. However, during runtime revalidation, ata_dev_revalidate() compares the new capacity (now 0) with the old capacity (>0) and detects a mismatch. Since it does not consider the locked status, it returns -ENODEV. This revalidation failure can occur when doing a reset of the PHY (e.g. hard reset) for a controller that has I/Os in flight. The timed out I/Os trigger the SCSI Error Handling (EH) path, which in turn invokes libata device revalidation. If the drive is locked at runtime (e.g. it lost power during reset and relocked), revalidation sees the capacity transition to zero and fails, eventually disabling the device. Fix this by allowing the capacity transition to zero in ata_dev_revalidate() if the drive is reported as security locked by ata_id_is_locked(). Fixes: 91842ed844a0 ("ata: libata-core: Set capacity to zero for a security locked drive") Cc: stable@vger.kernel.org Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Terrence Adams Signed-off-by: Damien Le Moal Signed-off-by: Greg Kroah-Hartman --- drivers/ata/libata-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 88833fb5bb81f..80196326ff8ff 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -3992,7 +3992,7 @@ int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class, /* verify n_sectors hasn't changed */ if (dev->class != ATA_DEV_ATA || !n_sectors || - dev->n_sectors == n_sectors) + dev->n_sectors == n_sectors || ata_id_is_locked(dev->id)) return 0; /* n_sectors has changed */ From 648d4317326e6aa3f8c05cbf0fd14cc2eba6ca99 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Thu, 9 Jul 2026 22:28:37 -0400 Subject: [PATCH 2661/2758] drbd: reject data replies with an out-of-range payload size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit bd910a7660d280595ef94cb6d193951d855d330f upstream. recv_dless_read() receives a P_DATA_REPLY from a peer into the bio of an outstanding read request. The peer-supplied payload length reaches it as the signed int data_size, and two peer-controlled inputs can make it negative. With a negotiated data-integrity-alg the digest length is subtracted first, so a reply whose payload is smaller than the digest underflows data_size. With no integrity algorithm (the default) data_size is assigned from the unsigned h95/h100 wire length and drbdd() never bounds it for a payload-carrying command, so a length above INT_MAX casts it negative; this path needs no non-default feature. The bio receive loop then computes expect = min_t(int, data_size, bv_len), which is negative, and drbd_recv_all_warn(mapped, expect) receives with a size_t of SIZE_MAX into the first mapped page. The sibling receive path read_in_block() is not affected: it uses an unsigned size and rejects it against DRBD_MAX_BIO_SIZE before receiving. Reject a data reply whose size is negative after the optional digest subtraction, covering both triggers. Impact: a malicious or man-in-the-middle DRBD peer copies attacker-chosen bytes past a bio page in the receiver, corrupting kernel memory. A node that reads from its peer (a diskless node, or read-balancing to the peer) is exposed in the default configuration; data-integrity-alg is not required. Fixes: b411b3637fa7 ("The DRBD driver") Cc: stable@vger.kernel.org Assisted-by: Codex:gpt-5-5-xhigh Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Reviewed-by: Christoph Böhmwalder Link: https://patch.msgid.link/20260710022837.3738461-1-michael.bommarito@gmail.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/block/drbd/drbd_receiver.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c index 58b95bf4bdca6..2135c14354a85 100644 --- a/drivers/block/drbd/drbd_receiver.c +++ b/drivers/block/drbd/drbd_receiver.c @@ -1810,6 +1810,11 @@ static int recv_dless_read(struct drbd_peer_device *peer_device, struct drbd_req data_size -= digest_size; } + if (data_size < 0) { + drbd_err(peer_device, "Invalid data reply size\n"); + return -EIO; + } + /* optimistically update recv_cnt. if receiving fails below, * we disconnect anyways, and counters will be reset. */ peer_device->device->recv_cnt += data_size>>9; From d5b2752a17efc165793ae38f2c24b5ed9c04ffde Mon Sep 17 00:00:00 2001 From: Tao Liu Date: Mon, 6 Jul 2026 11:27:07 +1200 Subject: [PATCH 2662/2758] riscv: Prevent NULL pointer dereference in machine_kexec_prepare() commit 81bbcff0c053c4f5c711c31a9b72fc492bd96c3f upstream. A NULL pointer dereference issue is noticed in riscv's machine_kexec_prepare(), where image->segment[i].buf might be NULL and copied unchecked. The NULL buf comes from ima_add_kexec_buffer(), where kbuf is added by kexec_add_buffer(), but kbuf.buffer is NULL, then it is copied without a check in machine_kexec_prepare(): kexec_file_load -> kimage_file_alloc_init() -> kimage_file_prepare_segments() -> ima_add_kexec_buffer() -> kexec_add_buffer() -> machine_kexec_prepare() -> memcpy() Address this by adding a check before the data copy attempt. Fixes: b7fb4d78a6ad ("RISC-V: use memcpy for kexec_file mode") Cc: stable@vger.kernel.org Closes: https://lore.kernel.org/kexec/CAO7dBbVftLUhd2qrh7hmijTB3PEPfZAhykCGqEfrPoOcSrrj-w@mail.gmail.com/ Acked-by: Baoquan He Acked-by: Pratyush Yadav Reviewed-by: Nutty Liu Signed-off-by: Tao Liu Link: https://patch.msgid.link/20260705232706.30265-2-ltao@redhat.com Signed-off-by: Paul Walmsley Signed-off-by: Greg Kroah-Hartman --- arch/riscv/kernel/machine_kexec.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/riscv/kernel/machine_kexec.c b/arch/riscv/kernel/machine_kexec.c index 2306ce3e5f229..738df176ff6f1 100644 --- a/arch/riscv/kernel/machine_kexec.c +++ b/arch/riscv/kernel/machine_kexec.c @@ -41,6 +41,9 @@ machine_kexec_prepare(struct kimage *image) if (image->segment[i].memsz <= sizeof(fdt)) continue; + if (!image->segment[i].buf) + continue; + if (image->file_mode) memcpy(&fdt, image->segment[i].buf, sizeof(fdt)); else if (copy_from_user(&fdt, image->segment[i].buf, sizeof(fdt))) From dd0160a0842337f12e7694d68b184050afc6d3a4 Mon Sep 17 00:00:00 2001 From: Crystal Wood Date: Mon, 8 Jun 2026 23:54:30 -0500 Subject: [PATCH 2663/2758] tracing/osnoise: Call synchronize_rcu() when unregistering commit fe58f457ad8d0a2bef4e053cfecca4b5cd266b1a upstream. This ensures that any RCU readers traversing the instance list have finished, before releasing the reference on the tracer that the instance points to. Cc: stable@vger.kernel.org Fixes: a6ed2aee54644 ("tracing: Switch to kvfree_rcu() API") Link: https://patch.msgid.link/20260609045430.1589786-1-crwood@redhat.com Suggested-by: Steven Rostedt Signed-off-by: Crystal Wood Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace_osnoise.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/trace/trace_osnoise.c b/kernel/trace/trace_osnoise.c index 75678053b21c5..7a244ef814382 100644 --- a/kernel/trace/trace_osnoise.c +++ b/kernel/trace/trace_osnoise.c @@ -162,7 +162,9 @@ static void osnoise_unregister_instance(struct trace_array *tr) if (!found) return; - kvfree_rcu_mightsleep(inst); + /* Do a full sync to ensure that tr remains valid, not just inst */ + synchronize_rcu(); + kvfree(inst); } /* From 83fe36f81200b7a85f8efe0a68a4e58be84d5f64 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 22 Jun 2026 16:31:24 +0200 Subject: [PATCH 2664/2758] s390/diag: Add missing array_index_nospec() call to memtop_get_page_count() commit b7577fe4c47a31ca7c99714c53244a44af03cdfe upstream. 'level' is user space controlled and used to read from an array. Add the missing array_index_nospec() call to prevent speculative execution. Cc: stable@vger.kernel.org Fixes: 0d30871739ab ("s390/diag: Add memory topology information via diag310") Signed-off-by: Heiko Carstens Reviewed-by: Mete Durlu Signed-off-by: Vasily Gorbik Signed-off-by: Greg Kroah-Hartman --- arch/s390/kernel/diag/diag310.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/s390/kernel/diag/diag310.c b/arch/s390/kernel/diag/diag310.c index f411562aa7f6b..c68ec9d28513d 100644 --- a/arch/s390/kernel/diag/diag310.c +++ b/arch/s390/kernel/diag/diag310.c @@ -190,17 +190,18 @@ static int memtop_get_stride_len(unsigned long *res) static int memtop_get_page_count(unsigned long *res, unsigned long level) { static unsigned long memtop_pages[DIAG310_LEVELMAX]; - unsigned long pages; + unsigned long pages, idx; int rc; if (level > DIAG310_LEVELMAX || level < DIAG310_LEVELMIN) return -EINVAL; - pages = READ_ONCE(memtop_pages[level - 1]); + idx = array_index_nospec(level - 1, ARRAY_SIZE(memtop_pages)); + pages = READ_ONCE(memtop_pages[idx]); if (!pages) { rc = diag310_get_memtop_size(&pages, level); if (rc) return rc; - WRITE_ONCE(memtop_pages[level - 1], pages); + WRITE_ONCE(memtop_pages[idx], pages); } *res = pages; return 0; From 28673209eeeab3f3c1abcaded43dac527ca2b58f Mon Sep 17 00:00:00 2001 From: Gerald Schaefer Date: Tue, 30 Jun 2026 19:32:14 +0200 Subject: [PATCH 2665/2758] s390/mm: Fix type mismatch in get_align_mask(). commit ec84aad4c3594307d103af563991b4415ac5c8ab upstream. Commit 86f48f922ba79 ("s390/mmap: disable mmap alignment when randomize_va_space = 0") introduced get_align_mask() with return type of 'int', while the target field 'info.align_mask' in struct vm_unmapped_area_info is 'unsigned long'. With currently used masks, this should not cause truncation issues, but fix it and return 'unsigned long' to avoid future problems. Fixes: 86f48f922ba79 ("s390/mmap: disable mmap alignment when randomize_va_space = 0") Cc: stable@vger.kernel.org # v6.9+ Signed-off-by: Gerald Schaefer Reviewed-by: Sven Schnelle Signed-off-by: Vasily Gorbik Signed-off-by: Greg Kroah-Hartman --- arch/s390/mm/mmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/mm/mmap.c b/arch/s390/mm/mmap.c index 2a222a7e14f48..ef7bfc87758c8 100644 --- a/arch/s390/mm/mmap.c +++ b/arch/s390/mm/mmap.c @@ -64,7 +64,7 @@ static inline unsigned long mmap_base(unsigned long rnd, return PAGE_ALIGN(STACK_TOP - gap - rnd); } -static int get_align_mask(struct file *filp, unsigned long flags) +static unsigned long get_align_mask(struct file *filp, unsigned long flags) { if (filp && is_file_hugepages(filp)) return huge_page_mask_align(filp); From f45c8d3818da7861ac78f631b5be80f7403f9760 Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Tue, 7 Jul 2026 16:23:48 +0800 Subject: [PATCH 2666/2758] selftests/rseq: Fix a building error for riscv arch commit a2ac823d8a228e392bc6a79ea63b550ab84a55b2 upstream. RISC-V rseq selftests include asm/fence.h from tools/arch/riscv, but the rseq Makefile only adds tools/include in the CFLAGS, this results in the building failure both for native and cross build: In file included from rseq.h:131, from rseq.c:37: rseq-riscv.h:11:10: fatal error: asm/fence.h: No such file or directory To fix it, add the matching tools/arch/$(ARCH)/include path in the CFLAGS and derive ARCH from SUBARCH for standalone native builds where ARCH is not set. Fixes: c92786e179e0 ("KVM: riscv: selftests: Use the existing RISCV_FENCE macro in `rseq-riscv.h`") Cc: stable@vger.kernel.org Signed-off-by: Hui Wang Link: https://patch.msgid.link/20260707082348.36896-1-hui.wang@canonical.com Signed-off-by: Paul Walmsley Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/rseq/Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/rseq/Makefile b/tools/testing/selftests/rseq/Makefile index 50d69e22ee7a6..aba6317f6cb8e 100644 --- a/tools/testing/selftests/rseq/Makefile +++ b/tools/testing/selftests/rseq/Makefile @@ -5,9 +5,13 @@ CLANG_FLAGS += -no-integrated-as endif top_srcdir = ../../../.. +include $(top_srcdir)/scripts/subarch.include +ARCH ?= $(SUBARCH) +LINUX_TOOL_ARCH_INCLUDE = $(top_srcdir)/tools/arch/$(ARCH)/include CFLAGS += -O2 -Wall -g -I./ $(KHDR_INCLUDES) -L$(OUTPUT) -Wl,-rpath=./ \ - $(CLANG_FLAGS) -I$(top_srcdir)/tools/include + $(CLANG_FLAGS) -I$(top_srcdir)/tools/include \ + -I$(LINUX_TOOL_ARCH_INCLUDE) LDLIBS += -lpthread -ldl # Own dependencies because we only want to build against 1st prerequisite, but From c17f06d8a085d6be58b544a440ce243f5e441a60 Mon Sep 17 00:00:00 2001 From: Farhad Alemi Date: Mon, 6 Jul 2026 10:20:23 +0200 Subject: [PATCH 2667/2758] cgroup/cpuset: rebind mm mempolicy to effective_mems, not mems_allowed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit b983c56426383e4a06fa5970c4e33cee879b1482 upstream. Creating a child cpuset where cpuset.mems is never set leads to a div/0 when a VMA mempolicy with MPOL_F_RELATIVE_NODES rebinds in response to a CPU hotplug event. Reproduction steps: 1) Create a cgroup w/ cpuset controls (do not set cpuset.mems) 2) Move the task into the child cpuset 3) Create a VMA mempolicy for that task with MPOL_F_RELATIVE_NODES 4) unplug and hotplug a cpu echo 0 > /sys/devices/system/cpu/cpu1/online echo 1 > /sys/devices/system/cpu/cpu1/online 5) mempolicy rebind does a div/0 in mpol_relative_nodemask on the call to __nodes_fold() The cpuset code passes (cs->mems_allowed) which is not guaranteed to have nodes to the rebind routine. Use cs->effective_mems instead, which is guaranteed to have a non-empty nodemask once we reach that code path. Link: https://lore.kernel.org/all/CA+0ovCiEz6SP_sn3kN4Tb+_oC=eHMXy_Ffj=usV3wREdQrUtww@mail.gmail.com/ Fixes: ae1c802382f7 ("cpuset: apply cs->effective_{cpus,mems}") Closes: https://lore.kernel.org/linux-mm/CA+0ovCgxbZkXa+OU8w3s84R3KNPNxxRfmsNR-udh+afQBbGNmw@mail.gmail.com/ Suggested-by: Gregory Price Suggested-by: Waiman Long Acked-by: Waiman Long Signed-off-by: Farhad Alemi Cc: Andrew Morton Cc: Alistair Popple Cc: Byungchul Park Cc: Gregory Price Cc: "Huang, Ying" Cc: Joshua Hahn Cc: Matthew Brost Cc: Rakie Kim Cc: Rasmus Villemoes Cc: Zi Yan Cc: Tejun Heo Cc: Ridong Chen Cc: Johannes Weiner Cc: "Michal Koutný" Cc: [ david: add a comment, slightly rephrase description ] Signed-off-by: David Hildenbrand (Arm) Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- kernel/cgroup/cpuset.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index c9e14fda3d6fd..8407bf44e807b 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -2650,7 +2650,12 @@ void cpuset_update_tasks_nodemask(struct cpuset *cs) migrate = is_memory_migrate(cs); - mpol_rebind_mm(mm, &cs->mems_allowed); + /* + * For v1 we can have empty effective_mems, but we cannot + * attach any tasks (see cpuset_can_attach_check()). For v2, + * effective_mems is guaranteed to not be empty. + */ + mpol_rebind_mm(mm, &cs->effective_mems); if (migrate) cpuset_migrate_mm(mm, &cs->old_mems_allowed, &newmems); else From 4ba6d7166750d0b810c6cfc0b1df7585f513b48c Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Tue, 9 Jun 2026 14:26:41 +0800 Subject: [PATCH 2668/2758] pmdomain: imx93-blk-ctrl: Extract PHY as shared domain for DSI/CSI commit 99611233f8cda833169fa6487d5dacdf189e5cb0 upstream. The MIPI DSI and CSI domains share control bits for clock and reset, which can lead to incorrect behavior if one domain disables the shared resource while the other is still active. To fix the issue, introduce a shared MIPI PHY power domain to own the common resources and make DSI and CSI its subdomains. This ensures the shared bits are properly managed and not disabled while still in use. Fixes: e9aa77d413c9 ("soc: imx: add i.MX93 media blk ctrl driver") Cc: stable@vger.kernel.org Signed-off-by: Guoniu Zhou Reviewed-by: Frank Li Reviewed-by: Peng Fan Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/pmdomain/imx/imx93-blk-ctrl.c | 60 ++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/drivers/pmdomain/imx/imx93-blk-ctrl.c b/drivers/pmdomain/imx/imx93-blk-ctrl.c index 1afc78b034fad..243ce939ba680 100644 --- a/drivers/pmdomain/imx/imx93-blk-ctrl.c +++ b/drivers/pmdomain/imx/imx93-blk-ctrl.c @@ -48,6 +48,8 @@ #define PRIO(X) (X) +#define BLK_CTRL_NO_PARENT UINT_MAX + struct imx93_blk_ctrl_domain; struct imx93_blk_ctrl { @@ -68,12 +70,18 @@ struct imx93_blk_ctrl_qos { u32 cfg_prio; }; +struct imx93_blk_ctrl_subdomain_link { + struct generic_pm_domain *parent; + struct generic_pm_domain *subdomain; +}; + struct imx93_blk_ctrl_domain_data { const char *name; const char * const *clk_names; int num_clks; u32 rst_mask; u32 clk_mask; + u32 parent; int num_qos; struct imx93_blk_ctrl_qos qos[DOMAIN_MAX_QOS]; }; @@ -203,6 +211,13 @@ static void imx93_release_pm_genpd(void *data) pm_genpd_remove(genpd); } +static void imx93_release_subdomain(void *data) +{ + struct imx93_blk_ctrl_subdomain_link *link = data; + + pm_genpd_remove_subdomain(link->parent, link->subdomain); +} + static struct lock_class_key blk_ctrl_genpd_lock_class; static int imx93_blk_ctrl_probe(struct platform_device *pdev) @@ -302,6 +317,34 @@ static int imx93_blk_ctrl_probe(struct platform_device *pdev) bc->onecell_data.domains[i] = &domain->genpd; } + for (i = 0; i < bc_data->num_domains; i++) { + struct imx93_blk_ctrl_domain *domain = &bc->domains[i]; + const struct imx93_blk_ctrl_domain_data *data = domain->data; + struct imx93_blk_ctrl_subdomain_link *link; + + if (bc_data->skip_mask & BIT(i) || + data->parent == BLK_CTRL_NO_PARENT) + continue; + + link = devm_kzalloc(dev, sizeof(*link), GFP_KERNEL); + if (!link) + return -ENOMEM; + + link->parent = &bc->domains[data->parent].genpd; + link->subdomain = &domain->genpd; + + ret = pm_genpd_add_subdomain(&bc->domains[data->parent].genpd, + &domain->genpd); + if (ret) + return dev_err_probe(dev, ret, "failed to add subdomain %s\n", + domain->genpd.name); + + ret = devm_add_action_or_reset(dev, imx93_release_subdomain, link); + if (ret) + return dev_err_probe(dev, ret, + "failed to add subdomain release callback\n"); + } + ret = devm_pm_runtime_enable(dev); if (ret) return dev_err_probe(dev, ret, "failed to enable pm-runtime\n"); @@ -326,8 +369,9 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] .name = "mediablk-mipi-dsi", .clk_names = (const char *[]){ "dsi" }, .num_clks = 1, - .rst_mask = BIT(11) | BIT(12), - .clk_mask = BIT(11) | BIT(12), + .rst_mask = BIT(11), + .clk_mask = BIT(11), + .parent = IMX93_MEDIABLK_PD_MIPI_PHY, }, [IMX93_MEDIABLK_PD_MIPI_CSI] = { .name = "mediablk-mipi-csi", @@ -335,6 +379,7 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] .num_clks = 2, .rst_mask = BIT(9) | BIT(10), .clk_mask = BIT(9) | BIT(10), + .parent = IMX93_MEDIABLK_PD_MIPI_PHY, }, [IMX93_MEDIABLK_PD_PXP] = { .name = "mediablk-pxp", @@ -342,6 +387,7 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] .num_clks = 1, .rst_mask = BIT(7) | BIT(8), .clk_mask = BIT(7) | BIT(8), + .parent = BLK_CTRL_NO_PARENT, .num_qos = 2, .qos = { { @@ -363,6 +409,7 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] .num_clks = 2, .rst_mask = BIT(4) | BIT(5) | BIT(6), .clk_mask = BIT(4) | BIT(5) | BIT(6), + .parent = BLK_CTRL_NO_PARENT, .num_qos = 1, .qos = { { @@ -379,6 +426,7 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] .num_clks = 1, .rst_mask = BIT(2) | BIT(3), .clk_mask = BIT(2) | BIT(3), + .parent = BLK_CTRL_NO_PARENT, .num_qos = 4, .qos = { { @@ -404,6 +452,14 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] } } }, + [IMX93_MEDIABLK_PD_MIPI_PHY] = { + .name = "mediablk-mipi-phy", + .clk_names = NULL, + .num_clks = 0, + .rst_mask = BIT(12), + .clk_mask = BIT(12), + .parent = BLK_CTRL_NO_PARENT, + }, }; static const struct regmap_range imx93_media_blk_ctl_yes_ranges[] = { From 36c2d7728540252474752172027f0113028ab00c Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Thu, 9 Jul 2026 10:46:32 +0200 Subject: [PATCH 2669/2758] pmdomain: mediatek: Fix possible nullptr KP in HWV cleanup/on-check commit a4876f11aa1d076802676e23f8af500706e780e3 upstream. Should probe fail for HW_VOTER type power domains, this driver was unconditionally trying to perform cleanup for DIRECT_CTL domains, but only after checking if the target domain is powered on... with the DIRECT_CTL scpsys_domain_is_on() code again. And there's more: the scpsys_domain_is_on() function is also being unconditionally used in the probe path, for any power domain that has flag MTK_SCPD_KEEP_DEFAULT_OFF! This bug was never experienced by anyone because the HWV domains never failed probe, and because none of those is declared with the aforementioned flag - but it's still something critical. In order to fix this, add a check for MTCMOS Type and, based on that, call the correct functions for an "is on" check, and also do the same for the cleanup path, calling the correct functions for the "power off" action. For the latter, since there's a call to pm_genpd_remove() right before calling power_off, be cautious and add a variation of the power off functions (with a _internal suffix) for those to get a pointer to scpsys_domain instead of one to generic_pm_domain as, even if that's still working, this is way too much fragile and would break at some point. Fixes: 88914db077b6 ("pmdomain: mediatek: Add support for Hardware Voter power domains") Signed-off-by: AngeloGioacchino Del Regno Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/pmdomain/mediatek/mtk-pm-domains.c | 40 ++++++++++++++++++---- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.c b/drivers/pmdomain/mediatek/mtk-pm-domains.c index e1cfd42234734..f0a6339affd75 100644 --- a/drivers/pmdomain/mediatek/mtk-pm-domains.c +++ b/drivers/pmdomain/mediatek/mtk-pm-domains.c @@ -393,9 +393,8 @@ static int scpsys_hwv_power_on(struct generic_pm_domain *genpd) return ret; }; -static int scpsys_hwv_power_off(struct generic_pm_domain *genpd) +static int scpsys_hwv_power_off_internal(struct scpsys_domain *pd) { - struct scpsys_domain *pd = container_of(genpd, struct scpsys_domain, genpd); const struct scpsys_hwv_domain_data *hwv = pd->hwv_data; struct scpsys *scpsys = pd->scpsys; u32 val; @@ -464,6 +463,13 @@ static int scpsys_hwv_power_off(struct generic_pm_domain *genpd) return ret; }; +static int scpsys_hwv_power_off(struct generic_pm_domain *genpd) +{ + struct scpsys_domain *pd = container_of(genpd, struct scpsys_domain, genpd); + + return scpsys_hwv_power_off_internal(pd); +} + static int scpsys_ctl_pwrseq_on(struct scpsys_domain *pd) { struct scpsys *scpsys = pd->scpsys; @@ -694,9 +700,8 @@ static int scpsys_power_on(struct generic_pm_domain *genpd) return ret; } -static int scpsys_power_off(struct generic_pm_domain *genpd) +static int scpsys_power_off_internal(struct scpsys_domain *pd) { - struct scpsys_domain *pd = container_of(genpd, struct scpsys_domain, genpd); struct scpsys *scpsys = pd->scpsys; bool tmp; int ret; @@ -737,6 +742,13 @@ static int scpsys_power_off(struct generic_pm_domain *genpd) return 0; } +static int scpsys_power_off(struct generic_pm_domain *genpd) +{ + struct scpsys_domain *pd = container_of(genpd, struct scpsys_domain, genpd); + + return scpsys_power_off_internal(pd); +} + static struct generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_node *node) { @@ -884,7 +896,14 @@ generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_no * late_init time. */ if (MTK_SCPD_CAPS(pd, MTK_SCPD_KEEP_DEFAULT_OFF)) { - if (scpsys_domain_is_on(pd)) + bool domain_is_on; + + if (scpsys->soc_data->type == SCPSYS_MTCMOS_TYPE_HW_VOTER) + domain_is_on = scpsys_hwv_domain_is_enable_done(pd); + else + domain_is_on = scpsys_domain_is_on(pd); + + if (domain_is_on) dev_warn(scpsys->dev, "%pOF: A default off power domain has been ON\n", node); } else { @@ -973,6 +992,7 @@ static int scpsys_add_subdomain(struct scpsys *scpsys, struct device_node *paren static void scpsys_remove_one_domain(struct scpsys_domain *pd) { + struct scpsys *scpsys = pd->scpsys; int ret; /* @@ -984,8 +1004,14 @@ static void scpsys_remove_one_domain(struct scpsys_domain *pd) dev_err(pd->scpsys->dev, "failed to remove domain '%s' : %d - state may be inconsistent\n", pd->genpd.name, ret); - if (scpsys_domain_is_on(pd)) - scpsys_power_off(&pd->genpd); + + if (scpsys->soc_data->type == SCPSYS_MTCMOS_TYPE_HW_VOTER) { + if (scpsys_hwv_domain_is_enable_done(pd)) + scpsys_hwv_power_off_internal(pd); + } else { + if (scpsys_domain_is_on(pd)) + scpsys_power_off_internal(pd); + } clk_bulk_put(pd->num_clks, pd->clks); clk_bulk_put(pd->num_subsys_clks, pd->subsys_clks); From 331ee3bc4edf9e0301cec11882aa651655671e69 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 10 Jun 2026 22:39:10 +0800 Subject: [PATCH 2670/2758] pmdomain: imx: Fix i.MX8MP power notifier commit 72422525f641f68bed6ca3389d29ee3f41fdea33 upstream. Using imx8mm_vpu_power_notifier() for i.MX8MP is wrong, as it ungates the VPU clocks to provide the ADB clock, which is necessary on i.MX8MM, but on i.MX8MP there is a separate gate (bit 3) for the NoC. So add imx8mp_vpu_power_notifier() for i.MX8MP. Fixes: a1a5f15f7f6cb ("soc: imx: imx8m-blk-ctrl: add i.MX8MP VPU blk ctrl") Cc: stable@vger.kernel.org Reviewed-by: Frank Li Signed-off-by: Peng Fan Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/pmdomain/imx/imx8m-blk-ctrl.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/drivers/pmdomain/imx/imx8m-blk-ctrl.c b/drivers/pmdomain/imx/imx8m-blk-ctrl.c index 19e992d2ee3b8..e13a47eeed75d 100644 --- a/drivers/pmdomain/imx/imx8m-blk-ctrl.c +++ b/drivers/pmdomain/imx/imx8m-blk-ctrl.c @@ -514,9 +514,34 @@ static const struct imx8m_blk_ctrl_domain_data imx8mp_vpu_blk_ctl_domain_data[] }, }; +static int imx8mp_vpu_power_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl, + power_nb); + + if (action == GENPD_NOTIFY_ON) { + /* + * On power up we have no software backchannel to the GPC to + * wait for the ADB handshake to happen, so we just delay for a + * bit. On power down the GPC driver waits for the handshake. + */ + + udelay(5); + + /* set "fuse" bits to enable the VPUs */ + regmap_set_bits(bc->regmap, 0x8, 0xffffffff); + regmap_set_bits(bc->regmap, 0xc, 0xffffffff); + regmap_set_bits(bc->regmap, 0x10, 0xffffffff); + regmap_set_bits(bc->regmap, 0x14, 0xffffffff); + } + + return NOTIFY_OK; +} + static const struct imx8m_blk_ctrl_data imx8mp_vpu_blk_ctl_dev_data = { .max_reg = 0x18, - .power_notifier_fn = imx8mm_vpu_power_notifier, + .power_notifier_fn = imx8mp_vpu_power_notifier, .domains = imx8mp_vpu_blk_ctl_domain_data, .num_domains = ARRAY_SIZE(imx8mp_vpu_blk_ctl_domain_data), }; From 4907f4c2d98b97e640191af5bcf2814ee1034b76 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 10 Jun 2026 22:39:11 +0800 Subject: [PATCH 2671/2758] pmdomain: imx: Fix i.MX8MP VC8000E power up sequence commit 25e252bcf1593b420b12a7231d9dd64b885a2ae2 upstream. Per errata[1]: ERR050531: VPU_NOC power down handshake may hang during VC8000E/VPUMIX power up/down cycling. Description: VC8000E reset de-assertion edge and AXI clock may have a timing issue. Workaround: Set bit2 (vc8000e_clk_en) of BLK_CLK_EN_CSR to 0 to gate off both AXI clock and VC8000E clock sent to VC8000E and AXI clock sent to VPU_NOC m_v_2 interface during VC8000E power up(VC8000E reset is de-asserted by HW) Add a bool variable is_errata_err050531 in 'struct imx8m_blk_ctrl_domain_data' to represent whether the workaround is needed. If is_errata_err050531 is true, first clear the clk before powering up gpc, then enable the clk after powering up gpc. [1] https://www.nxp.com/webapp/Download?colCode=IMX8MP_1P33A Fixes: a1a5f15f7f6cb ("soc: imx: imx8m-blk-ctrl: add i.MX8MP VPU blk ctrl") Cc: stable@vger.kernel.org Signed-off-by: Peng Fan Reviewed-by: Frank Li Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/pmdomain/imx/imx8m-blk-ctrl.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/pmdomain/imx/imx8m-blk-ctrl.c b/drivers/pmdomain/imx/imx8m-blk-ctrl.c index e13a47eeed75d..99d100e1d923f 100644 --- a/drivers/pmdomain/imx/imx8m-blk-ctrl.c +++ b/drivers/pmdomain/imx/imx8m-blk-ctrl.c @@ -54,6 +54,15 @@ struct imx8m_blk_ctrl_domain_data { * register. */ u32 mipi_phy_rst_mask; + + /* + * VC8000E reset de-assertion edge and AXI clock may have a timing issue. + * Workaround: Set bit2 (vc8000e_clk_en) of BLK_CLK_EN_CSR to 0 to gate off + * both AXI clock and VC8000E clock sent to VC8000E and AXI clock sent to + * VPU_NOC m_v_2 interface during VC8000E power up(VC8000E reset is + * de-asserted by HW) + */ + bool is_errata_err050531; }; #define DOMAIN_MAX_CLKS 4 @@ -108,7 +117,11 @@ static int imx8m_blk_ctrl_power_on(struct generic_pm_domain *genpd) dev_err(bc->dev, "failed to enable clocks\n"); goto bus_put; } - regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask); + + if (data->is_errata_err050531) + regmap_clear_bits(bc->regmap, BLK_CLK_EN, data->clk_mask); + else + regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask); /* power up upstream GPC domain */ ret = pm_runtime_get_sync(domain->power_dev); @@ -117,6 +130,9 @@ static int imx8m_blk_ctrl_power_on(struct generic_pm_domain *genpd) goto clk_disable; } + if (data->is_errata_err050531) + regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask); + /* wait for reset to propagate */ udelay(5); @@ -511,6 +527,7 @@ static const struct imx8m_blk_ctrl_domain_data imx8mp_vpu_blk_ctl_domain_data[] .clk_mask = BIT(2), .path_names = (const char *[]){"vc8000e"}, .num_paths = 1, + .is_errata_err050531 = true, }, }; From 19a1785250c76761a0e9e8129659740f38d98a74 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 10 Jul 2026 10:16:42 +0200 Subject: [PATCH 2672/2758] selftests/landlock: Skip scoped_signal subtest with MSG_OOB if not available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 5ab1dc6d110db6bee167a32fd94c53ea0e7ad6d2 upstream. MSG_OOB might be disabled in the kernel for unix sockets (by not selecting CONFIG_AF_UNIX_OOB), and in this case the related tests of the scoped_signal_test are currently failing. Add a runtime probe using socketpair() to detect MSG_OOB support and skip the test gracefully if it is unavailable. Signed-off-by: Thomas Huth Link: https://patch.msgid.link/20260710081642.405916-1-thuth@redhat.com Cc: stable@vger.kernel.org Fixes: f34e9ce5f479 ("selftests/landlock: Test signal created by out-of-bound message") Signed-off-by: Mickaël Salaün Signed-off-by: Greg Kroah-Hartman --- .../selftests/landlock/scoped_signal_test.c | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tools/testing/selftests/landlock/scoped_signal_test.c b/tools/testing/selftests/landlock/scoped_signal_test.c index f24f2c28f62e5..01d1c8810af30 100644 --- a/tools/testing/selftests/landlock/scoped_signal_test.c +++ b/tools/testing/selftests/landlock/scoped_signal_test.c @@ -400,6 +400,24 @@ static int setup_signal_handler(int signal) return sigaction(SIGURG, &sa, NULL); } +/* + * MSG_OOB might be disabled in the kernel via the CONFIG_AF_UNIX_OOB + * switch, so this function can be used for probing for its availability. + */ +static bool has_af_unix_oob(void) +{ + bool available = false; + int sp[2]; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == 0) { + available = (send(sp[0], ".", 1, MSG_OOB) == 1); + close(sp[0]); + close(sp[1]); + } + + return available; +} + /* clang-format off */ FIXTURE(fown) {}; /* clang-format on */ @@ -462,6 +480,9 @@ TEST_F(fown, sigurg_socket) int pipe_parent[2], pipe_child[2]; pid_t child; + if (!has_af_unix_oob()) + SKIP(return, "CONFIG_AF_UNIX_OOB / MSG_OOB not available"); + memset(&server_address, 0, sizeof(server_address)); set_unix_address(&server_address, 0); From 90dfffc360dfb382c4843980ae3c1b787b638925 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 9 Jul 2026 18:43:40 +0200 Subject: [PATCH 2673/2758] selftests/landlock: Fix screwed up pointers in the scoped_signal_test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit d793186aa3bb833c878ae6826c87e62c843afaa3 upstream. The scoped_signal_test uses pthread_join(..., (void **)&ret)) in a couple of places, i.e. the return value of the thread is stored in the shape of a "void *" into the memory location of &ret. Pointers are 64-bit on modern computers, but the ret variable is declared as a simple "enum thread_return" which is only 32 bits. So the pthread_join() will overflow the ret variable by 4 byte. The problem is very visible on big endian systems like s390x where the test is failing: The least significant byte that carries the return code of the thread is not written into the ret variable here, but somewhere else in the stack frame, so the comparison for the right return code is failing here. Fix it by getting rid of the enum and defining the THREAD_* constants and "ret" variables as proper "void *" pointers. This way we can also get rid of some ugly (void *) castings in a couple of spots. Signed-off-by: Thomas Huth Link: https://patch.msgid.link/20260709164340.339656-1-thuth@redhat.com Cc: stable@vger.kernel.org Fixes: c8994965013e ("selftests/landlock: Test signal scoping for threads") [mic: Add clang-format markups] Signed-off-by: Mickaël Salaün Signed-off-by: Greg Kroah-Hartman --- .../selftests/landlock/scoped_signal_test.c | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/tools/testing/selftests/landlock/scoped_signal_test.c b/tools/testing/selftests/landlock/scoped_signal_test.c index 01d1c8810af30..2d37d0c06c069 100644 --- a/tools/testing/selftests/landlock/scoped_signal_test.c +++ b/tools/testing/selftests/landlock/scoped_signal_test.c @@ -249,12 +249,12 @@ TEST_F(scoped_domains, check_access_signal) _metadata->exit_code = KSFT_FAIL; } -enum thread_return { - THREAD_INVALID = 0, - THREAD_SUCCESS = 1, - THREAD_ERROR = 2, - THREAD_TEST_FAILED = 3, -}; +/* clang-format off */ +#define THREAD_INVALID ((void *)0) +#define THREAD_SUCCESS ((void *)1) +#define THREAD_ERROR ((void *)2) +#define THREAD_TEST_FAILED ((void *)3) +/* clang-format on */ static void *thread_sync(void *arg) { @@ -262,15 +262,15 @@ static void *thread_sync(void *arg) char buf; if (read(pipe_read, &buf, 1) != 1) - return (void *)THREAD_ERROR; + return THREAD_ERROR; - return (void *)THREAD_SUCCESS; + return THREAD_SUCCESS; } TEST(signal_scoping_thread_before) { pthread_t no_sandbox_thread; - enum thread_return ret = THREAD_INVALID; + void *ret = THREAD_INVALID; int thread_pipe[2]; drop_caps(_metadata); @@ -285,7 +285,7 @@ TEST(signal_scoping_thread_before) EXPECT_EQ(0, pthread_kill(no_sandbox_thread, 0)); EXPECT_EQ(1, write(thread_pipe[1], ".", 1)); - EXPECT_EQ(0, pthread_join(no_sandbox_thread, (void **)&ret)); + EXPECT_EQ(0, pthread_join(no_sandbox_thread, &ret)); EXPECT_EQ(THREAD_SUCCESS, ret); EXPECT_EQ(0, close(thread_pipe[0])); @@ -295,7 +295,7 @@ TEST(signal_scoping_thread_before) TEST(signal_scoping_thread_after) { pthread_t scoped_thread; - enum thread_return ret = THREAD_INVALID; + void *ret = THREAD_INVALID; int thread_pipe[2]; drop_caps(_metadata); @@ -310,7 +310,7 @@ TEST(signal_scoping_thread_after) EXPECT_EQ(0, pthread_kill(scoped_thread, 0)); EXPECT_EQ(1, write(thread_pipe[1], ".", 1)); - EXPECT_EQ(0, pthread_join(scoped_thread, (void **)&ret)); + EXPECT_EQ(0, pthread_join(scoped_thread, &ret)); EXPECT_EQ(THREAD_SUCCESS, ret); EXPECT_EQ(0, close(thread_pipe[0])); @@ -327,20 +327,20 @@ void *thread_setuid(void *ptr) char buf; if (read(arg->pipe_read, &buf, 1) != 1) - return (void *)THREAD_ERROR; + return THREAD_ERROR; /* libc's setuid() should update all thread's credentials. */ if (getuid() != arg->new_uid) - return (void *)THREAD_TEST_FAILED; + return THREAD_TEST_FAILED; - return (void *)THREAD_SUCCESS; + return THREAD_SUCCESS; } TEST(signal_scoping_thread_setuid) { struct thread_setuid_args arg; pthread_t no_sandbox_thread; - enum thread_return ret = THREAD_INVALID; + void *ret = THREAD_INVALID; int pipe_parent[2]; int prev_uid; @@ -367,7 +367,7 @@ TEST(signal_scoping_thread_setuid) EXPECT_EQ(arg.new_uid, getuid()); EXPECT_EQ(1, write(pipe_parent[1], ".", 1)); - EXPECT_EQ(0, pthread_join(no_sandbox_thread, (void **)&ret)); + EXPECT_EQ(0, pthread_join(no_sandbox_thread, &ret)); EXPECT_EQ(THREAD_SUCCESS, ret); clear_cap(_metadata, CAP_SETUID); @@ -688,20 +688,20 @@ static void *thread_setown_scoped(void *arg) ruleset_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); if (ruleset_fd < 0) - return (void *)THREAD_ERROR; + return THREAD_ERROR; if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) || landlock_restrict_self(ruleset_fd, 0)) { close(ruleset_fd); - return (void *)THREAD_ERROR; + return THREAD_ERROR; } close(ruleset_fd); /* Makes this process group own the SIGIO source. */ if (fcntl(fd, F_SETSIG, SIGURG) || fcntl(fd, F_SETOWN, -getpgrp()) || fcntl(fd, F_SETFL, O_ASYNC)) - return (void *)THREAD_ERROR; + return THREAD_ERROR; - return (void *)THREAD_SUCCESS; + return THREAD_SUCCESS; } /* @@ -723,7 +723,7 @@ TEST(sigio_to_pgid_self) { int trigger[2]; pthread_t thread; - enum thread_return ret = THREAD_INVALID; + void *ret = THREAD_INVALID; int i; drop_caps(_metadata); @@ -743,7 +743,7 @@ TEST(sigio_to_pgid_self) */ ASSERT_EQ(0, pthread_create(&thread, NULL, thread_setown_scoped, &trigger[0])); - ASSERT_EQ(0, pthread_join(thread, (void **)&ret)); + ASSERT_EQ(0, pthread_join(thread, &ret)); ASSERT_EQ(THREAD_SUCCESS, ret); /* Fans SIGURG out to the process group. */ From cc5c99b606ffb33349298278ae616b4a24997a5b Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:14 +0800 Subject: [PATCH 2674/2758] mmc: sdhci-esdhc-imx: restore pinctrl before restoring ios timing on resume commit 1db87818bde3d2295613660879378b43a70d31f8 upstream. SDIO devices such as WiFi may keep power during suspend, so the MMC core skips full card re-initialization on resume and directly restores the host controller's ios timing to match the card. For DDR mode, pm_runtime_force_resume() sets DDR_EN before the pin configuration is restored from sleep state. This is related to the SoC IP integration: switching pinctrl setting (changing alt from GPIO to USDHC) impacts the internal loopback path. If pinctrl configures the pad to GPIO function, once DDR_EN is set, the DLL delay will be fixed based on the GPIO function loopback path. When the pinctrl is later changed to USDHC function, the internal loopback path changes, making the original fixed sample point no longer suitable for the current loopback path. This causes persistent read CRC errors on subsequent data transfers. SD/eMMC running in DDR mode are unaffected as they are fully re-initialized from legacy timing after resume. Fix this by restoring the pinctrl state based on current timing mode using esdhc_change_pinstate() before pm_runtime_force_resume(). This ensures the correct pin configuration (e.g., 100/200MHz for UHS modes) is applied before DDR_EN is set. Only restore for non-wakeup devices since wakeup devices kept their active pin state during suspend. Fixes: 676a83855614 ("mmc: host: sdhci-esdhc-imx: refactor the system PM logic") Acked-by: Adrian Hunter Reviewed-by: Haibo Chen Signed-off-by: Luke Wang Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-esdhc-imx.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 18ecddd6df6f2..ccdde48e74ef1 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -2106,6 +2106,12 @@ static int sdhci_esdhc_resume(struct device *dev) struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host); int ret; + if (!device_may_wakeup(dev)) { + ret = esdhc_change_pinstate(host, host->timing); + if (ret) + dev_warn(dev, "Failed to restore pinctrl state\n"); + } + pm_runtime_force_resume(dev); ret = mmc_gpio_set_cd_wake(host->mmc, false); From 0b24b11ecda4de79124cbb74e0debb142e82fc7f Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sun, 14 Jun 2026 16:23:56 +0200 Subject: [PATCH 2675/2758] powerpc/pseries: fix memory leak on krealloc failure in papr_init commit bd83c98b988d2c560531084e296dbfb530aff829 upstream. When krealloc() fails, free the original esi_buf before returning to avoid a memory leak. Fixes: 3c14b73454cf ("powerpc/pseries: Interface to represent PAPR firmware attributes") Cc: stable@vger.kernel.org Signed-off-by: Thorsten Blum Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260614142356.658212-2-thorsten.blum@linux.dev Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/platforms/pseries/papr_platform_attributes.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/platforms/pseries/papr_platform_attributes.c b/arch/powerpc/platforms/pseries/papr_platform_attributes.c index c6159870de0e1..9c3758aa54c6e 100644 --- a/arch/powerpc/platforms/pseries/papr_platform_attributes.c +++ b/arch/powerpc/platforms/pseries/papr_platform_attributes.c @@ -271,11 +271,9 @@ static int __init papr_init(void) esi_buf_size = ESI_HDR_SIZE + (CURR_MAX_ESI_ATTRS * max_esi_attrs); temp_esi_buf = krealloc(esi_buf, esi_buf_size, GFP_KERNEL); - if (temp_esi_buf) - esi_buf = temp_esi_buf; - else - return -ENOMEM; - + if (!temp_esi_buf) + goto out_free_esi_buf; + esi_buf = temp_esi_buf; goto retry; } From 6eb4cf2fa8997f62c11e0006dc010a1fd89c5a75 Mon Sep 17 00:00:00 2001 From: Zhiping Zhang Date: Thu, 2 Jul 2026 15:24:58 -0700 Subject: [PATCH 2676/2758] net/mlx5: free mlx5_st_idx_data on final dealloc commit df6134b527a88b3e65ba6ae5073664af091d5fd2 upstream. Workloads that repeatedly allocate and release mkeys carrying TPH steering-tag hints (e.g. churning RDMA MRs) leak one struct mlx5_st_idx_data per cycle; kmemleak flags it as unreferenced and the kmalloc slab grows over time. When the last reference to an ST table entry is dropped, mlx5_st_dealloc_index() removed the entry from idx_xa but the backing mlx5_st_idx_data allocation was never freed. Free idx_data after the xa_erase() so the lifetime of the bookkeeping struct matches the lifetime of the ST entry it tracks. Cc: stable@vger.kernel.org Fixes: 888a7776f4fb ("net/mlx5: Add support for device steering tag") Reviewed-by: Michael Gur Signed-off-by: Zhiping Zhang Reviewed-by: Leon Romanovsky Reviewed-by: Tariq Toukan Link: https://patch.msgid.link/20260702222507.1234467-1-zhipingz@meta.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/mellanox/mlx5/core/lib/st.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/st.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/st.c index 997be91f0a135..7cedc348790db 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/st.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/st.c @@ -175,6 +175,7 @@ int mlx5_st_dealloc_index(struct mlx5_core_dev *dev, u16 st_index) if (refcount_dec_and_test(&idx_data->usecount)) { xa_erase(&st->idx_xa, st_index); + kfree(idx_data); /* We leave PCI config space as was before, no mkey will refer to it */ } From 56994852d704535ea354a4627ca667b1b4fa0deb Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Fri, 19 Jun 2026 15:31:04 +0800 Subject: [PATCH 2677/2758] wifi: rt2x00: avoid full teardown before work setup in probe commit 536fb3d739d75a03cb318c0c6fe799425cfea501 upstream. rt2x00lib_probe_dev() uses the full rt2x00lib_remove_dev() teardown for all probe failures. However, drv_data allocation and workqueue allocation can fail before intf_work, autowakeup_work and sleep_work have been initialized. Do not enter the full remove path until the probe has reached the point where those work items are set up. Return directly for drv_data allocation failure, and use a small early cleanup path for workqueue allocation failure. This issue was found by our static analysis tool and then confirmed by manual review of rt2x00lib_probe_dev() and rt2x00lib_remove_dev(). The early probe exits should not call a common teardown path that assumes the later work setup has already completed. A QEMU PoC forced alloc_ordered_workqueue() to fail before the work initializers are reached. The resulting fail path entered rt2x00lib_remove_dev(), and DEBUG_OBJECTS reported invalid work drains with rt2x00lib_probe_dev() and rt2x00lib_remove_dev() in the stack. Fixes: 1ebbc48520a0 ("rt2x00: Introduce concept of driver data in struct rt2x00_dev.") Fixes: 0439f5367c8d ("rt2x00: Move TX/RX work into dedicated workqueue") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Link: https://patch.msgid.link/20260619073104.1809161-1-runyu.xiao@seu.edu.cn Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ralink/rt2x00/rt2x00dev.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c index 82fb230a73bb7..4d94b7062f440 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c @@ -1388,7 +1388,7 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) GFP_KERNEL); if (!rt2x00dev->drv_data) { retval = -ENOMEM; - goto exit; + return retval; } } @@ -1422,7 +1422,7 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) alloc_ordered_workqueue("%s", 0, wiphy_name(rt2x00dev->hw->wiphy)); if (!rt2x00dev->workqueue) { retval = -ENOMEM; - goto exit; + goto exit_free_drv_data; } INIT_WORK(&rt2x00dev->intf_work, rt2x00lib_intf_scheduled); @@ -1494,6 +1494,14 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) exit: rt2x00lib_remove_dev(rt2x00dev); + return retval; + +exit_free_drv_data: + clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); + + kfree(rt2x00dev->drv_data); + rt2x00dev->drv_data = NULL; + return retval; } EXPORT_SYMBOL_GPL(rt2x00lib_probe_dev); From 564e3fce81eb8435d55e134a6e2e967d7c2d0138 Mon Sep 17 00:00:00 2001 From: Rafael Beims Date: Wed, 10 Jun 2026 12:00:18 -0300 Subject: [PATCH 2678/2758] wifi: mwifiex: fix roaming to different channel in host_mlme mode commit a707e4127c0f893c7a7703500ab56297a5bd2d51 upstream. When host MLME is enabled, mwifiex_cfg80211_authenticate() transmits the authentication frame on a remain-on-channel (ROC) reservation so that the frame is sent on the target BSS's channel. The ROC is only configured when priv->auth_flag is zero. priv->auth_flag is set to HOST_MLME_AUTH_PENDING when the auth frame is queued and advances to HOST_MLME_AUTH_DONE once authentication completes. It is only cleared back to zero on a disconnect, deauth or timeout path; nothing clears it when an association succeeds. It therefore stays at HOST_MLME_AUTH_DONE for the whole connected session. When the station later roams to a BSS on a different channel, the next authentication finds auth_flag != 0, skips the ROC setup, and the auth frame is transmitted on the currently-associated channel instead of the target's channel. Authentication times out on the new AP and the device stays connected to the original AP. Gate the ROC setup on HOST_MLME_AUTH_PENDING instead of on auth_flag being completely clear. This re-arms the remain-on-channel for every new authentication attempt, while still suppressing a redundant ROC during the multi-frame SAE exchange, where auth_flag stays PENDING between the commit and confirm frames. This change was tested in 3 different devices: Verdin AM62 (IW412 SD-UART) - (16.92.21.p142) Verdin iMX8MM (W8997 SD-SD) - (16.68.1.p197) Verdin iMX8MP (W8997 SD-UART) - (16.92.21.p137) There following loop tests were performed: 1) force roaming between two AP's, one 5GHz and one 2.4GHz, same SSID. Use wpa_cli to trigger the roaming behavior, sleep 2s between iterations. 2) force a disconnection to AP 1 and a connection to AP 2, test scan. Use wpa_cli to trigger the connection changes, sleep 2s between iterations. Each test ran in each device for at least 3 hours. Fixes: 36995892c271 ("wifi: mwifiex: add host mlme for client mode") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Rafael Beims Reviewed-by: Francesco Dolcini Link: https://patch.msgid.link/20260610150021.1018611-1-rafael@beims.me Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index c9a651bdf8822..ca04aaae1e2f3 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -4333,7 +4333,7 @@ mwifiex_cfg80211_authenticate(struct wiphy *wiphy, return -EOPNOTSUPP; } - if (!priv->auth_flag) { + if (!(priv->auth_flag & HOST_MLME_AUTH_PENDING)) { ret = mwifiex_remain_on_chan_cfg(priv, HostCmd_ACT_GEN_SET, req->bss->channel, AUTH_TX_DEFAULT_WAIT_TIME); From a7584f261e64fe14e45bdf70f343e16c1ee3a037 Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Mon, 6 Jul 2026 22:35:07 +0800 Subject: [PATCH 2679/2758] wifi: mac80211: fix memory leak in ieee80211_register_hw() commit 95fc02722edde02946d0d475221f2b2054d3d8ba upstream. If kmemdup() fails while copying supported band structures, the error path jumps to fail_rate. This skips rate_control_deinitialize() and leaks the initialized local->rate_ctrl. Fix this by adding a fail_band label that shares the rate-control cleanup path before falling through to the remaining teardown. The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1-rc7. An x86_64 allyesconfig build showed no new warnings. As we do not have a suitable mac80211 device/driver combination to test with, no runtime testing was able to be performed. Fixes: 09b4a4faf9d0 ("mac80211: introduce capability flags for VHT EXT NSS support") Cc: stable@vger.kernel.org Reviewed-by: Zilin Guan Signed-off-by: Dawei Feng Link: https://patch.msgid.link/20260706143507.146131-1-dawei.feng@seu.edu.cn Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- net/mac80211/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/mac80211/main.c b/net/mac80211/main.c index f47dd58770adb..dba66dd964af3 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1599,7 +1599,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) sband = kmemdup(sband, sizeof(*sband), GFP_KERNEL); if (!sband) { result = -ENOMEM; - goto fail_rate; + goto fail_band; } wiphy_dbg(hw->wiphy, "copying sband (band %d) due to VHT EXT NSS BW flag\n", @@ -1675,6 +1675,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) #endif wiphy_unregister(local->hw.wiphy); fail_wiphy_register: + fail_band: rtnl_lock(); rate_control_deinitialize(local); ieee80211_remove_interfaces(local); From 185bb156c427d0f865d344a6d0eaa02c6d05cc57 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Sat, 27 Jun 2026 21:13:13 +0800 Subject: [PATCH 2680/2758] wifi: brcmfmac: cyw: fix heap overflow on a short auth frame commit 240c8d2c717b3f8153e7e877b22a82518d78dbdc upstream. brcmf_notify_auth_frame_rx() takes the frame length from the firmware event and copies the frame body with the management header offset subtracted: u32 mgmt_frame_len = e->datalen - sizeof(struct brcmf_rx_mgmt_data); ... memcpy(&mgmt_frame->u, frame, mgmt_frame_len - offsetof(struct ieee80211_mgmt, u)); The only length check is e->datalen >= sizeof(*rxframe), so mgmt_frame_len can be anything from 0 up. offsetof(struct ieee80211_mgmt, u) is 24. When mgmt_frame_len is below that, the subtraction wraps as an unsigned value to a huge length. The memcpy then runs far past the kzalloc'd buffer. A malicious or malfunctioning AP can make the frame short during the external SAE auth exchange, so this is a remotely triggered heap overflow. Reject frames shorter than the management header offset before the copy. Fixes: 66f909308a7c ("wifi: brcmfmac: cyw: support external SAE authentication in station mode") Link: https://lore.kernel.org/r/178214417708.2368577.16740907093694208834@maoyixie.com Cc: stable@vger.kernel.org Co-developed-by: Kaixuan Li Signed-off-by: Kaixuan Li Signed-off-by: Maoyi Xie Acked-by: Arend van Spriel Link: https://patch.msgid.link/20260627131313.3878893-1-maoyixie.tju@gmail.com Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c index ce09d44fa73cf..873754be5174b 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c @@ -293,6 +293,12 @@ brcmf_notify_auth_frame_rx(struct brcmf_if *ifp, return -EINVAL; } + if (mgmt_frame_len < offsetof(struct ieee80211_mgmt, u)) { + bphy_err(drvr, "Event %s (%d) frame too small. Ignore\n", + brcmf_fweh_event_name(e->event_code), e->event_code); + return -EINVAL; + } + wdev = &ifp->vif->wdev; WARN_ON(!wdev); From 73a7bdf06dbde81dd937a37456670d96fcce841c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Wed, 1 Jul 2026 11:21:22 +0200 Subject: [PATCH 2681/2758] riscv: vdso: Do not use LTO for the vDSO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit ad6dcfa023762e37962f77ee48e752b7570e9440 upstream. With LTO enabled the compiler assumes that the vDSO functions are not used and optimizes them away completely. Currently this happens to __vdso_clock_getres(), __vdso_clock_gettime(), __vdso_getrandom(), __vdso_gettimeofday() and __vdso_riscv_hwprobe(). Disable LTO for the vDSO, as these functions are hand-optimized anyways. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202606301855.WvkSC4kD-lkp@intel.com/ Fixes: 021d23428bdb ("RISC-V: build: Allow LTO to be selected") Cc: stable@vger.kernel.org Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260701-riscv-vdso-lto-v1-1-89db0cd82077@linutronix.de Signed-off-by: Paul Walmsley Signed-off-by: Greg Kroah-Hartman --- arch/riscv/kernel/vdso/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/riscv/kernel/vdso/Makefile b/arch/riscv/kernel/vdso/Makefile index a842dc034571d..43ee881f6c6fd 100644 --- a/arch/riscv/kernel/vdso/Makefile +++ b/arch/riscv/kernel/vdso/Makefile @@ -69,9 +69,9 @@ CPPFLAGS_$(vdso_lds) += -DHAS_VGETTIMEOFDAY endif # Disable -pg to prevent insert call site -CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) -CFLAGS_REMOVE_getrandom.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) -CFLAGS_REMOVE_hwprobe.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) +CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) $(CC_FLAGS_LTO) +CFLAGS_REMOVE_getrandom.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) $(CC_FLAGS_LTO) +CFLAGS_REMOVE_hwprobe.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) $(CC_FLAGS_LTO) # Force dependency $(obj)/$(vdso_o): $(obj)/$(vdso_so) From c8874e338d51aa59f12a88442868f49c7c5c4cda Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Tue, 23 Jun 2026 22:40:57 +0200 Subject: [PATCH 2682/2758] arch/riscv: vdso: remove CFI landing pad from rt_sigreturn commit e4bf6eb4c7b61db1cf24487e14e6ae8755e61e3d upstream. When CONFIG_RISCV_USER_CFI is enabled, the CFI version of the vDSO, has a CFI landing pad instruction at the start of __vdso_rt_sigreturn. This breaks libgcc's unwinding code which matches on the first two instructions. Other unwinders that rely on similar instruction matching may also be affected. Since __vdso_rt_sigreturn is reached as part of signal-return handling rather than via an indirect call/jump from userspace, it does not need a CFI landing pad. Remove it and restore the instruction sequence expected by existing unwinding code. This matches what was done on arm64 in commit 9a964285572b ("arm64: vdso: Don't prefix sigreturn trampoline with a BTI C instruction") for a similar issue. Cc: stable@vger.kernel.org Fixes: 37f57bd3faea ("arch/riscv: compile vdso with landing pad and shadow stack note") Co-authored-by: Joel Stanley Signed-off-by: Aurelien Jarno Signed-off-by: Joel Stanley Link: https://patch.msgid.link/20260623204058.498120-1-aurelien@aurel32.net [pjw@kernel.org: fixed comment style] Signed-off-by: Paul Walmsley Signed-off-by: Greg Kroah-Hartman --- arch/riscv/kernel/vdso/rt_sigreturn.S | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/riscv/kernel/vdso/rt_sigreturn.S b/arch/riscv/kernel/vdso/rt_sigreturn.S index e82987dc37394..d6f96b1abe409 100644 --- a/arch/riscv/kernel/vdso/rt_sigreturn.S +++ b/arch/riscv/kernel/vdso/rt_sigreturn.S @@ -7,11 +7,19 @@ #include #include +/* + * WARNING: Do NOT add a CFI landing pad at the start of this function. + * Unwinders such as libgcc identify the sigreturn trampoline by matching the + * instruction sequence. Adding a landing pad here would break unwinding from + * signal handlers. + * + * This trampoline is used only for signal return and not via an indirect + * call/jump from userspace, so adding CFI landing pad is unnecessary. + */ .text SYM_FUNC_START(__vdso_rt_sigreturn) .cfi_startproc .cfi_signal_frame - vdso_lpad li a7, __NR_rt_sigreturn ecall .cfi_endproc From b7f5bd59ed1c3a950a41130e7e1709d11db5e150 Mon Sep 17 00:00:00 2001 From: Abhishek Ojha Date: Wed, 15 Jul 2026 13:04:08 -0400 Subject: [PATCH 2683/2758] regulator: ltc3676: Fix incorrect IRQSTAT bit offsets commit 50dce2e2f84b56d8b4b406d97a1543709e8a87f5 upstream. The LTC3676_IRQSTAT_* bit definitions do not match the IRQSTAT (Interrupt Request Status) register layout documented in Table 15 of the LTC3676/LTC3676-1 datasheet: bit 0 - Pushbutton Status Active bit 1 - Hard Reset Occurred bit 2 - PGOOD Timeout Occurred bit 3 - Undervoltage Warning bit 4 - Undervoltage Standby (Fault) Occurred bit 5 - Overtemperature Warning bit 6 - Overtemperature Standby (Fault) Occurred bit 7 - Reserved The driver instead defines these starting at bit 3, one bit higher than the datasheet specifies, which causes ltc3676_regulator_isr() to check the wrong status bits and misreport (or miss) PGOOD timeout, undervoltage and thermal warning/fault conditions. Fix the bit offsets to match the datasheet. Fixes: 37b918a034fe ("regulator: Add LTC3676 support") Cc: stable@vger.kernel.org Signed-off-by: Abhishek Ojha Link: https://patch.msgid.link/20260715170408.295552-1-Abhishek.ojha@savoirfairelinux.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/regulator/ltc3676.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/regulator/ltc3676.c b/drivers/regulator/ltc3676.c index 73d511eb1c1dc..768cdce0d4ecf 100644 --- a/drivers/regulator/ltc3676.c +++ b/drivers/regulator/ltc3676.c @@ -45,11 +45,11 @@ #define LTC3676_DVBxA_REF_SELECT BIT(5) #define LTC3676_DVBxB_PGOOD_MASK BIT(5) -#define LTC3676_IRQSTAT_PGOOD_TIMEOUT BIT(3) -#define LTC3676_IRQSTAT_UNDERVOLT_WARN BIT(4) -#define LTC3676_IRQSTAT_UNDERVOLT_FAULT BIT(5) -#define LTC3676_IRQSTAT_THERMAL_WARN BIT(6) -#define LTC3676_IRQSTAT_THERMAL_FAULT BIT(7) +#define LTC3676_IRQSTAT_PGOOD_TIMEOUT BIT(2) +#define LTC3676_IRQSTAT_UNDERVOLT_WARN BIT(3) +#define LTC3676_IRQSTAT_UNDERVOLT_FAULT BIT(4) +#define LTC3676_IRQSTAT_THERMAL_WARN BIT(5) +#define LTC3676_IRQSTAT_THERMAL_FAULT BIT(6) enum ltc3676_reg { LTC3676_SW1, From 1b41cbe05b184f8861712f0806cc0c4f5d8c6dfe Mon Sep 17 00:00:00 2001 From: Asim Viladi Oglu Manizada Date: Mon, 6 Jul 2026 09:44:10 +0000 Subject: [PATCH 2684/2758] net: openvswitch: reject oversized nested action attrs commit 3f1f755366687d051174739fb99f7d560202f60b upstream. Open vSwitch stores generated flow actions as nlattrs, whose nla_len field is u16. Commit a1e64addf3ff ("net: openvswitch: remove misbehaving actions length check") allowed the total sw_flow_actions stream to grow beyond 64 KiB, which is valid, but also removed the last guard preventing a generated nested action attribute from exceeding U16_MAX. An oversized generated container can thus be closed with a truncated nla_len. A later dump or teardown then walks a structurally different stream than the one that was validated. In particular, an oversized nested CLONE/CT action may cause subsequent bytes in the generated stream to be interpreted as independent actions. Keep the larger total-action-stream behavior, but make nested action close reject generated containers that do not fit in nla_len, and return the error through all callers. For recursive SAMPLE, CLONE, DEC_TTL, and CHECK_PKT_LEN builders, trim resource-owning action-list tails in reverse construction order before discarding failed wrappers, so resources copied into the rejected tails are released before the wrappers are removed. Most failed outer wrappers are discarded by truncating actions_len after child resources have been released. CHECK_PKT_LEN also trims its parent after branch resources are gone. SET/TUNNEL close failures unwind their known tun_dst ownership directly, and SET_TO_MASKED has no external ownership and truncates on close failure. Fixes: a1e64addf3ff ("net: openvswitch: remove misbehaving actions length check") Cc: stable@vger.kernel.org Assisted-by: avom-custom-harness:gpt-5.5-qwen3.6-mod-mix Signed-off-by: Asim Viladi Oglu Manizada Reviewed-by: Eelco Chaudron Reviewed-by: Aaron Conole Reviewed-by: Ilya Maximets Link: https://patch.msgid.link/20260706094336.38639-1-manizada@pm.me Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- net/openvswitch/flow_netlink.c | 201 +++++++++++++++++++++++++-------- 1 file changed, 157 insertions(+), 44 deletions(-) diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index 13052408a132f..d8079dee700ed 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -2496,13 +2496,56 @@ static inline int add_nested_action_start(struct sw_flow_actions **sfa, return used; } -static inline void add_nested_action_end(struct sw_flow_actions *sfa, - int st_offset) +static inline int add_nested_action_end(struct sw_flow_actions *sfa, + int st_offset) { - struct nlattr *a = (struct nlattr *) ((unsigned char *)sfa->actions + - st_offset); + struct nlattr *a; + u32 attr_len; + + if (WARN_ON_ONCE(st_offset < 0 || + (u32)st_offset > sfa->actions_len)) + return -EINVAL; + + attr_len = sfa->actions_len - (u32)st_offset; + if (WARN_ON_ONCE(attr_len < NLA_HDRLEN)) + return -EINVAL; - a->nla_len = sfa->actions_len - st_offset; + if (attr_len > U16_MAX) + return -EMSGSIZE; + + a = (struct nlattr *)((u8 *)sfa->actions + st_offset); + a->nla_len = attr_len; + return 0; +} + +/* Free the generated action-list tail at @start and truncate it. + * If @nested, @start points to its containing nlattr header. + */ +static void ovs_nla_trim(struct sw_flow_actions *sfa, int start, bool nested) +{ + const struct nlattr *actions; + u32 len; + + if (start < 0) + return; + + if (WARN_ON_ONCE((u32)start > sfa->actions_len)) + return; + + actions = (const struct nlattr *)((u8 *)sfa->actions + start); + len = sfa->actions_len - (u32)start; + + if (nested) { + if (len < NLA_HDRLEN) + goto out; + + actions = (const struct nlattr *)((u8 *)actions + NLA_HDRLEN); + len -= NLA_HDRLEN; + } + + ovs_nla_free_nested_actions(actions, len); +out: + sfa->actions_len = start; } static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, @@ -2522,6 +2565,7 @@ static int validate_and_copy_sample(struct net *net, const struct nlattr *attr, const struct nlattr *attrs[OVS_SAMPLE_ATTR_MAX + 1]; const struct nlattr *probability, *actions; const struct nlattr *a; + int actions_start; int rem, start, err; struct sample_arg arg; @@ -2565,18 +2609,27 @@ static int validate_and_copy_sample(struct net *net, const struct nlattr *attr, err = ovs_nla_add_action(sfa, OVS_SAMPLE_ATTR_ARG, &arg, sizeof(arg), log); if (err) - return err; + goto err; + actions_start = (*sfa)->actions_len; err = __ovs_nla_copy_actions(net, actions, key, sfa, eth_type, vlan_tci, mpls_label_count, log, depth + 1); if (err) - return err; + goto err_free; - add_nested_action_end(*sfa, start); + err = add_nested_action_end(*sfa, start); + if (err) + goto err_free; return 0; + +err_free: + ovs_nla_trim(*sfa, actions_start, false); +err: + (*sfa)->actions_len = start; + return err; } static int validate_and_copy_dec_ttl(struct net *net, @@ -2624,18 +2677,31 @@ static int validate_and_copy_dec_ttl(struct net *net, return start; action_start = add_nested_action_start(sfa, OVS_DEC_TTL_ATTR_ACTION, log); - if (action_start < 0) - return action_start; + if (action_start < 0) { + err = action_start; + goto err; + } err = __ovs_nla_copy_actions(net, actions, key, sfa, eth_type, vlan_tci, mpls_label_count, log, depth + 1); if (err) - return err; + goto err_free; + + err = add_nested_action_end(*sfa, action_start); + if (err) + goto err_free; - add_nested_action_end(*sfa, action_start); - add_nested_action_end(*sfa, start); + err = add_nested_action_end(*sfa, start); + if (err) + goto err_free; return 0; + +err_free: + ovs_nla_trim(*sfa, action_start, true); +err: + (*sfa)->actions_len = start; + return err; } static int validate_and_copy_clone(struct net *net, @@ -2646,6 +2712,7 @@ static int validate_and_copy_clone(struct net *net, u32 mpls_label_count, bool log, bool last, u32 depth) { + int actions_start; int start, err; u32 exec; @@ -2661,17 +2728,26 @@ static int validate_and_copy_clone(struct net *net, err = ovs_nla_add_action(sfa, OVS_CLONE_ATTR_EXEC, &exec, sizeof(exec), log); if (err) - return err; + goto err; + actions_start = (*sfa)->actions_len; err = __ovs_nla_copy_actions(net, attr, key, sfa, eth_type, vlan_tci, mpls_label_count, log, depth + 1); if (err) - return err; + goto err_free; - add_nested_action_end(*sfa, start); + err = add_nested_action_end(*sfa, start); + if (err) + goto err_free; return 0; + +err_free: + ovs_nla_trim(*sfa, actions_start, false); +err: + (*sfa)->actions_len = start; + return err; } void ovs_match_init(struct sw_flow_match *match, @@ -2763,20 +2839,20 @@ static int validate_and_copy_set_tun(const struct nlattr *attr, tun_dst = metadata_dst_alloc(key.tun_opts_len, METADATA_IP_TUNNEL, GFP_KERNEL); - if (!tun_dst) - return -ENOMEM; + if (!tun_dst) { + err = -ENOMEM; + goto err; + } err = dst_cache_init(&tun_dst->u.tun_info.dst_cache, GFP_KERNEL); - if (err) { - dst_release((struct dst_entry *)tun_dst); - return err; - } + if (err) + goto err_free_tun_dst; a = __add_action(sfa, OVS_KEY_ATTR_TUNNEL_INFO, NULL, sizeof(*ovs_tun), log); if (IS_ERR(a)) { - dst_release((struct dst_entry *)tun_dst); - return PTR_ERR(a); + err = PTR_ERR(a); + goto err_free_tun_dst; } ovs_tun = nla_data(a); @@ -2797,8 +2873,16 @@ static int validate_and_copy_set_tun(const struct nlattr *attr, ip_tunnel_info_opts_set(tun_info, TUN_METADATA_OPTS(&key, key.tun_opts_len), key.tun_opts_len, dst_opt_type); - add_nested_action_end(*sfa, start); + err = add_nested_action_end(*sfa, start); + if (WARN_ON_ONCE(err)) + goto err_free_tun_dst; + + return 0; +err_free_tun_dst: + dst_release((struct dst_entry *)tun_dst); +err: + (*sfa)->actions_len = start; return err; } @@ -2971,7 +3055,7 @@ static int validate_set(const struct nlattr *a, /* Convert non-masked non-tunnel set actions to masked set actions. */ if (!masked && key_type != OVS_KEY_ATTR_TUNNEL) { - int start, len = key_len * 2; + int err, start, len = key_len * 2; struct nlattr *at; *skip_copy = true; @@ -2983,8 +3067,11 @@ static int validate_set(const struct nlattr *a, return start; at = __add_action(sfa, key_type, NULL, len, log); - if (IS_ERR(at)) - return PTR_ERR(at); + if (IS_ERR(at)) { + err = PTR_ERR(at); + (*sfa)->actions_len = start; + return err; + } memcpy(nla_data(at), nla_data(ovs_key), key_len); /* Key. */ memset(nla_data(at) + key_len, 0xff, key_len); /* Mask. */ @@ -2994,7 +3081,11 @@ static int validate_set(const struct nlattr *a, mask->ipv6_label &= htonl(0x000FFFFF); } - add_nested_action_end(*sfa, start); + err = add_nested_action_end(*sfa, start); + if (WARN_ON_ONCE(err)) { + (*sfa)->actions_len = start; + return err; + } } return 0; @@ -3040,7 +3131,8 @@ static int validate_and_copy_check_pkt_len(struct net *net, const struct nlattr *acts_if_greater, *acts_if_lesser_eq; struct nlattr *a[OVS_CHECK_PKT_LEN_ATTR_MAX + 1]; struct check_pkt_len_arg arg; - int nested_acts_start; + int greater_acts_start = -1; + int lesser_acts_start = -1; int start, err; err = nla_parse_deprecated_strict(a, OVS_CHECK_PKT_LEN_ATTR_MAX, @@ -3075,37 +3167,58 @@ static int validate_and_copy_check_pkt_len(struct net *net, err = ovs_nla_add_action(sfa, OVS_CHECK_PKT_LEN_ATTR_ARG, &arg, sizeof(arg), log); if (err) - return err; + goto err_free; - nested_acts_start = add_nested_action_start(sfa, - OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL, log); - if (nested_acts_start < 0) - return nested_acts_start; + lesser_acts_start = + add_nested_action_start(sfa, + OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL, + log); + if (lesser_acts_start < 0) { + err = lesser_acts_start; + goto err_free; + } err = __ovs_nla_copy_actions(net, acts_if_lesser_eq, key, sfa, eth_type, vlan_tci, mpls_label_count, log, depth + 1); if (err) - return err; + goto err_free; - add_nested_action_end(*sfa, nested_acts_start); + err = add_nested_action_end(*sfa, lesser_acts_start); + if (err) + goto err_free; - nested_acts_start = add_nested_action_start(sfa, - OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER, log); - if (nested_acts_start < 0) - return nested_acts_start; + greater_acts_start = + add_nested_action_start(sfa, + OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER, + log); + if (greater_acts_start < 0) { + err = greater_acts_start; + goto err_free; + } err = __ovs_nla_copy_actions(net, acts_if_greater, key, sfa, eth_type, vlan_tci, mpls_label_count, log, depth + 1); if (err) - return err; + goto err_free; + + err = add_nested_action_end(*sfa, greater_acts_start); + if (err) + goto err_free; - add_nested_action_end(*sfa, nested_acts_start); - add_nested_action_end(*sfa, start); + err = add_nested_action_end(*sfa, start); + if (err) + goto err_free; return 0; + +err_free: + ovs_nla_trim(*sfa, greater_acts_start, true); + ovs_nla_trim(*sfa, lesser_acts_start, true); + ovs_nla_trim(*sfa, start, false); + return err; } static int validate_psample(const struct nlattr *attr) From f1ca750c0510bdbb504bf084d2f196ef2af92ea6 Mon Sep 17 00:00:00 2001 From: Laxman Acharya Padhya Date: Fri, 10 Jul 2026 23:10:03 +0545 Subject: [PATCH 2685/2758] Bluetooth: btrtl: validate firmware patch bounds commit 609c5b04a28dc1b0f3af6a7bc93055135b2d2059 upstream. rtlbt_parse_firmware() copies patch_length - 4 bytes before appending the firmware version. A malformed firmware patch shorter than the version field can make this subtraction underflow and turn the copy into an oversized read and write during Bluetooth setup. The existing patch_offset + patch_length check can also wrap on 32-bit architectures. Validate the patch length and range without arithmetic overflow before allocating or copying the patch. Fixes: db33c77dddc2 ("Bluetooth: btrtl: Create separate module for Realtek BT driver") Cc: stable@vger.kernel.org Signed-off-by: Laxman Acharya Padhya Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/btrtl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c index 62f9d4df3a4f8..03fa9409e3ee4 100644 --- a/drivers/bluetooth/btrtl.c +++ b/drivers/bluetooth/btrtl.c @@ -797,8 +797,9 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev, } BT_DBG("length=%x offset=%x index %d", patch_length, patch_offset, i); - min_size = patch_offset + patch_length; - if (btrtl_dev->fw_len < min_size) + if (patch_length < sizeof(epatch_info->fw_version) || + patch_offset > btrtl_dev->fw_len || + patch_length > btrtl_dev->fw_len - patch_offset) return -EINVAL; /* Copy the firmware into a new buffer and write the version at From 5c1e8f56d84cc416aa36fff5755f8e4947c76574 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 8 Jul 2026 22:29:54 -0400 Subject: [PATCH 2686/2758] crypto: aes - Fix conditions for selecting MAC dependencies commit e073f1238ecaea366f53e98724c40b31856da56a upstream. Starting in commit 7137cbf2b5c9 ("crypto: aes - Add cmac, xcbc, and cbcmac algorithms using library"), the aes module (CRYPTO_AES) supports CBC based MACs using the corresponding library functions. To avoid including unneeded functionality, that support honors the existing CRYPTO_CMAC, CRYPTO_XCBC, and CRYPTO_CCM kconfig options. The dependencies are selected if at least one of those is enabled. However, the select statements don't correctly handle the case where CRYPTO_AES=y and (for example) CRYPTO_CMAC=m. In that case the dependencies get selected at level 'm', due to how the kconfig language works. That causes a linker error. Fix this by changing the selection conditions to use '!= n'. A similar issue also exists for CRYPTO_LIB_AES's conditional selection of CRYPTO_LIB_UTILS. The same '!= n' would work, but instead just make CRYPTO_LIB_AES always select CRYPTO_LIB_UTILS. CRYPTO_LIB_UTILS is lightweight, and it's needed by most AES modes and many other things. Fixes: 7137cbf2b5c9 ("crypto: aes - Add cmac, xcbc, and cbcmac algorithms using library") Fixes: 309a7e514da7 ("lib/crypto: aes: Add support for CBC-based MACs") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260709022954.45113-1-ebiggers@kernel.org Signed-off-by: Eric Biggers Signed-off-by: Greg Kroah-Hartman --- crypto/Kconfig | 4 ++-- lib/crypto/Kconfig | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/crypto/Kconfig b/crypto/Kconfig index 103d1f58cb7c2..b07e7de61198a 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -358,8 +358,8 @@ config CRYPTO_AES tristate "AES (Advanced Encryption Standard)" select CRYPTO_ALGAPI select CRYPTO_LIB_AES - select CRYPTO_LIB_AES_CBC_MACS if CRYPTO_CMAC || CRYPTO_XCBC || CRYPTO_CCM - select CRYPTO_HASH if CRYPTO_CMAC || CRYPTO_XCBC || CRYPTO_CCM + select CRYPTO_LIB_AES_CBC_MACS if CRYPTO_CMAC != n || CRYPTO_XCBC != n || CRYPTO_CCM != n + select CRYPTO_HASH if CRYPTO_CMAC != n || CRYPTO_XCBC != n || CRYPTO_CCM != n help AES cipher algorithms (Rijndael)(FIPS-197, ISO/IEC 18033-3) diff --git a/lib/crypto/Kconfig b/lib/crypto/Kconfig index d3904b72dae7a..1ee6cfadcd84f 100644 --- a/lib/crypto/Kconfig +++ b/lib/crypto/Kconfig @@ -8,8 +8,7 @@ config CRYPTO_LIB_UTILS config CRYPTO_LIB_AES tristate - # Select dependencies of modes that are part of libaes. - select CRYPTO_LIB_UTILS if CRYPTO_LIB_AES_CBC_MACS + select CRYPTO_LIB_UTILS config CRYPTO_LIB_AES_ARCH bool From 3cc37687227b5be2c7c753371ed4b71cc5ece1cf Mon Sep 17 00:00:00 2001 From: Xuanqiang Luo Date: Sun, 12 Jul 2026 21:03:43 +0800 Subject: [PATCH 2687/2758] llc: fix SAP refcount leak when creating incoming sockets commit 2c72eb6286347d05a885412fb076993bd5286b53 upstream. llc_sap_add_socket() takes a SAP reference for each socket added to a SAP, and llc_sap_remove_socket() releases it. llc_create_incoming_sock() takes an additional SAP reference after adding the child socket. This extra reference was balanced by an explicit llc_sap_put() in llc_ui_release() until commit 3100aa9d74db ("llc: fix SAP reference counting w.r.t. socket handling") removed that put. The corresponding hold in the accept path was left behind. When such a child socket is removed, only the reference taken by llc_sap_add_socket() is released. The extra reference keeps the SAP alive after its last socket is removed. Remove the obsolete hold. Fixes: 3100aa9d74db ("llc: fix SAP reference counting w.r.t. socket handling") Cc: stable@vger.kernel.org Signed-off-by: Xuanqiang Luo Link: https://patch.msgid.link/20260712130343.518797-1-xuanqiang.luo@linux.dev Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- net/llc/llc_conn.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c index 5c0ac243b248f..b4d732baafb89 100644 --- a/net/llc/llc_conn.c +++ b/net/llc/llc_conn.c @@ -773,7 +773,6 @@ static struct sock *llc_create_incoming_sock(struct sock *sk, newllc->dev = dev; dev_hold(dev); llc_sap_add_socket(llc->sap, newsk); - llc_sap_hold(llc->sap); out: return newsk; } From 492cf7778a5516ea270582878c0d922c74e6af72 Mon Sep 17 00:00:00 2001 From: James Raphael Tiovalen Date: Sun, 5 Jul 2026 19:36:29 +0800 Subject: [PATCH 2688/2758] macsec: fix promiscuity refcount leak in macsec_dev_open() commit 7410d11460eb90d6c9281162ccc6a128534d897d upstream. When a MACsec interface with IFF_PROMISC set is brought up on top of a device that has hardware offload enabled, macsec_dev_open() first calls dev_set_promiscuity(real_dev, 1) and then propagates the open to the offload device. If that propagation fails, the error path jumps to the clear_allmulti label, which only reverts allmulti and the unicast address. The promiscuity taken on the lower device is never dropped, so real_dev is left permanently stuck in promiscuous mode. Its promiscuity count can no longer be balanced from software. Add a clear_promisc label that drops the promiscuity reference and route the two offload failure paths to it. The dev_set_promiscuity() failure itself still jumps to clear_allmulti, since on that failure the count was not incremented. Fixes: 3cf3227a21d1 ("net: macsec: hardware offloading infrastructure") Cc: stable@vger.kernel.org Signed-off-by: James Raphael Tiovalen Reviewed-by: Sabrina Dubroca Link: https://patch.msgid.link/20260705113629.187490-1-jamestiotio@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- drivers/net/macsec.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index dd89282f01791..ee0e2eb7dbc61 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -3615,19 +3615,22 @@ static int macsec_dev_open(struct net_device *dev) ops = macsec_get_ops(netdev_priv(dev), &ctx); if (!ops) { err = -EOPNOTSUPP; - goto clear_allmulti; + goto clear_promisc; } ctx.secy = &macsec->secy; err = macsec_offload(ops->mdo_dev_open, &ctx); if (err) - goto clear_allmulti; + goto clear_promisc; } if (netif_carrier_ok(real_dev)) netif_carrier_on(dev); return 0; +clear_promisc: + if (dev->flags & IFF_PROMISC) + dev_set_promiscuity(real_dev, -1); clear_allmulti: if (dev->flags & IFF_ALLMULTI) dev_set_allmulti(real_dev, -1); From 47f0c7d856c67c9935546d2644f18c0d0131b449 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Thu, 2 Jul 2026 16:27:45 +0800 Subject: [PATCH 2689/2758] memstick: ms_block: reject a card that reports too many blocks commit 718178f524b98bc920d74bc771aed823c8b81425 upstream. msb_ftl_initialize() computes the zone count from the card block count with no bound: msb->zone_count = msb->block_count / MS_BLOCKS_IN_ZONE; ... for (i = 0; i < msb->zone_count; i++) msb->free_block_count[i] = MS_BLOCKS_IN_ZONE; msb->block_count is a card value. msb_read_boot_blocks() reads number_of_blocks from the card boot page and byte swaps it. free_block_count is a fixed int[MS_MAX_ZONES]. MS_MAX_ZONES is 16, so the valid indices are 0 to 15. The init loop above indexes it by zone_count. msb_mark_block_used() and msb_mark_block_unused() index it by pba / MS_BLOCKS_IN_ZONE, for pba up to block_count - 1. A card may report up to 65535 blocks. A block_count above 8192 (MS_MAX_ZONES * MS_BLOCKS_IN_ZONE) lets the pba index reach 16. That writes past free_block_count[] and corrupts struct msb_data. A larger count runs the init loop past the end too. A real Memory Stick has at most 16 zones. So it has at most 8192 blocks. msb_ftl_initialize() now rejects a card that reports more than MS_MAX_ZONES * MS_BLOCKS_IN_ZONE blocks. Fixes: 0ab30494bc4f ("memstick: add support for legacy memorysticks") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/memstick/core/ms_block.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/memstick/core/ms_block.c b/drivers/memstick/core/ms_block.c index a01fe313558e7..ce33907bfc242 100644 --- a/drivers/memstick/core/ms_block.c +++ b/drivers/memstick/core/ms_block.c @@ -1338,6 +1338,10 @@ static int msb_ftl_initialize(struct msb_data *msb) return 0; msb->zone_count = msb->block_count / MS_BLOCKS_IN_ZONE; + if (msb->block_count > MS_MAX_ZONES * MS_BLOCKS_IN_ZONE) { + pr_err("Too many blocks: %d\n", msb->block_count); + return -EINVAL; + } msb->logical_block_count = msb->zone_count * 496 - 2; msb->used_blocks_bitmap = bitmap_zalloc(msb->block_count, GFP_KERNEL); From 905d7a363ade96a19f214815361e11981142c547 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Wed, 8 Jul 2026 21:03:15 +0300 Subject: [PATCH 2690/2758] ipvs: fix more places with wrong ipv6 transport offsets commit b3fe4cbd583895987935a9bdad01c8f9d3a02310 upstream. Sashiko reports for more incorrect IPv6 transport offsets. The app code for TCP was assuming IPv4 network header even after the ipvsh argument was provided. This can cause problems with apps over IPv6. As for the only official app in the kernel tree (FTP) this problem is harmless because we use Netfilter to mangle the FTP ports and we do not adjust the TCP seq numbers. Also, provide correct offset of the ICMPV6 header in ip_vs_out_icmp_v6() for correct checksum checks when the IPv6 packet has extension headers. Fixes: d12e12299a69 ("ipvs: add ipv6 support to ftp") Fixes: 2a3b791e6e11 ("IPVS: Add/adjust Netfilter hook functions and helpers for v6") Cc: stable@vger.kernel.org Link: https://sashiko.dev/#/patchset/20260706101624.69471-1-zhaoyz24%40mails.tsinghua.edu.cn Signed-off-by: Julian Anastasov Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/ipvs/ip_vs_app.c | 10 ++++------ net/netfilter/ipvs/ip_vs_core.c | 3 +-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c index d54d7da583346..b0e00be85cb12 100644 --- a/net/netfilter/ipvs/ip_vs_app.c +++ b/net/netfilter/ipvs/ip_vs_app.c @@ -361,14 +361,13 @@ static inline int app_tcp_pkt_out(struct ip_vs_conn *cp, struct sk_buff *skb, struct ip_vs_iphdr *ipvsh) { int diff; - const unsigned int tcp_offset = ip_hdrlen(skb); struct tcphdr *th; __u32 seq; - if (skb_ensure_writable(skb, tcp_offset + sizeof(*th))) + if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th))) return 0; - th = (struct tcphdr *)(skb_network_header(skb) + tcp_offset); + th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len); /* * Remember seq number in case this pkt gets resized @@ -438,14 +437,13 @@ static inline int app_tcp_pkt_in(struct ip_vs_conn *cp, struct sk_buff *skb, struct ip_vs_iphdr *ipvsh) { int diff; - const unsigned int tcp_offset = ip_hdrlen(skb); struct tcphdr *th; __u32 seq; - if (skb_ensure_writable(skb, tcp_offset + sizeof(*th))) + if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th))) return 0; - th = (struct tcphdr *)(skb_network_header(skb) + tcp_offset); + th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len); /* * Remember seq number in case this pkt gets resized diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 35cbe821c2596..bafab93451d03 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -1219,8 +1219,7 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, snet.in6 = ciph.saddr.in6; offset = ciph.len; return handle_response_icmp(AF_INET6, skb, &snet, ciph.protocol, cp, - pp, offset, sizeof(struct ipv6hdr), - hooknum); + pp, offset, ipvsh->len, hooknum); } #endif From ad1e14710b360bda087ebf9fb82460eb5ef775de Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 8 Jul 2026 16:21:30 +0200 Subject: [PATCH 2691/2758] ipvs: reload ip header after head reallocation commit a2f57827bf7c695b8c72dc4511cae8e86582369d upstream. __ip_vs_get_out_rt() calls skb_ensure_writable() which may reallocate skb->head. Fixes: 8d8e20e2d7bb ("ipvs: Decrement ttl") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-sonnet-4-6 Acked-by: Julian Anastasov Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/ipvs/ip_vs_xmit.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index ce542ed4b013c..9fef4335da13f 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -736,13 +736,11 @@ int ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh) { - struct iphdr *iph = ip_hdr(skb); - - if (__ip_vs_get_out_rt(cp->ipvs, cp->af, skb, NULL, iph->daddr, + if (__ip_vs_get_out_rt(cp->ipvs, cp->af, skb, NULL, ip_hdr(skb)->daddr, IP_VS_RT_MODE_NON_LOCAL, NULL, ipvsh) < 0) goto tx_error; - ip_send_check(iph); + ip_send_check(ip_hdr(skb)); /* Another hack: avoid icmp_send in ip_fragment */ skb->ignore_df = 1; From 68176d47421f255734af492c879633047e530cef Mon Sep 17 00:00:00 2001 From: Robby Cai Date: Fri, 19 Jun 2026 15:31:15 +0800 Subject: [PATCH 2692/2758] reset: imx7: Correct polarity of MIPI CSI resets on i.MX8MQ commit 71827776667f4e4677a4fa806bcfb24d4b8dd9d7 upstream. On i.MX8MQ, the MIPI CSI reset lines are active-low and not self-clearing. Writing '0' asserts reset and it remains asserted until explicitly deasserted by software. This driver previously treated the MIPI CSI reset signals as active-high, which led to incorrect reset assert/deassert sequencing. This issue was exposed by commit 6d79bb8fd2aa ("media: imx8mq-mipi-csi2: Explicitly release reset"). Fix this by reflecting the correct reset polarity and ensuring proper reset handling. Fixes: c979dbf59987 ("reset: imx7: Add support for i.MX8MQ IP block variant") Cc: stable@vger.kernel.org # 6d79bb8fd2aa: media: imx8mq-mipi-csi2: Explicitly release reset Reviewed-by: Philipp Zabel Signed-off-by: Robby Cai Reviewed-by: Guoniu Zhou Reviewed-by: Frank Li Signed-off-by: Philipp Zabel Signed-off-by: Greg Kroah-Hartman --- drivers/reset/reset-imx7.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/reset/reset-imx7.c b/drivers/reset/reset-imx7.c index dd01fe11c5cb3..a3cb8244d76a6 100644 --- a/drivers/reset/reset-imx7.c +++ b/drivers/reset/reset-imx7.c @@ -236,6 +236,12 @@ static int imx8mq_reset_set(struct reset_controller_dev *rcdev, case IMX8MQ_RESET_PCIE_CTRL_APPS_EN: case IMX8MQ_RESET_PCIE2_CTRL_APPS_EN: + case IMX8MQ_RESET_MIPI_CSI1_CORE_RESET: + case IMX8MQ_RESET_MIPI_CSI1_PHY_REF_RESET: + case IMX8MQ_RESET_MIPI_CSI1_ESC_RESET: + case IMX8MQ_RESET_MIPI_CSI2_CORE_RESET: + case IMX8MQ_RESET_MIPI_CSI2_PHY_REF_RESET: + case IMX8MQ_RESET_MIPI_CSI2_ESC_RESET: case IMX8MQ_RESET_MIPI_DSI_PCLK_RESET_N: case IMX8MQ_RESET_MIPI_DSI_ESC_RESET_N: case IMX8MQ_RESET_MIPI_DSI_DPI_RESET_N: From d826d3e04c5bd9d284ba29f330246a317a8a7023 Mon Sep 17 00:00:00 2001 From: Zhao Dongdong Date: Wed, 17 Jun 2026 11:16:27 +0800 Subject: [PATCH 2693/2758] reset: sunxi: fix memory region leak on ioremap failure commit 1a8c89f8c112c75e84ff9a140f969e372aed0c9a upstream. In sunxi_reset_init(), when ioremap() fails, the memory region obtained via request_mem_region() is not released, leading to a resource leak. Add an err_mem_region label to properly release the memory region before freeing the data structure. Fixes: 8f1ae77f4666 ("reset: Add Allwinner SoCs Reset Controller Driver") Cc: stable@vger.kernel.org Signed-off-by: Zhao Dongdong Reviewed-by: Philipp Zabel Acked-by: Jernej Skrabec Signed-off-by: Philipp Zabel Signed-off-by: Greg Kroah-Hartman --- drivers/reset/reset-sunxi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/reset/reset-sunxi.c b/drivers/reset/reset-sunxi.c index 2544de6576e44..2f6df7707cad1 100644 --- a/drivers/reset/reset-sunxi.c +++ b/drivers/reset/reset-sunxi.c @@ -44,7 +44,7 @@ static int sunxi_reset_init(struct device_node *np) data->membase = ioremap(res.start, size); if (!data->membase) { ret = -ENOMEM; - goto err_alloc; + goto err_mem_region; } spin_lock_init(&data->lock); @@ -57,6 +57,8 @@ static int sunxi_reset_init(struct device_node *np) return reset_controller_register(&data->rcdev); +err_mem_region: + release_mem_region(res.start, size); err_alloc: kfree(data); return ret; From 4efa313b15925bdd864784865d6585174979294b Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Mon, 1 Jun 2026 15:50:00 +0800 Subject: [PATCH 2694/2758] powerpc/spufs: fix out-of-bounds access in spufs_mem_mmap_access() commit 47b87f469a35b5ffc81c16eee6b13a9b6c8d55c6 upstream. spufs_mem_mmap_access() computes the local store offset as address - vma->vm_start, but bounds-checks it against vma->vm_end instead of the local store size. On 64-bit, offset is always well below vma->vm_end, so the clamp never fires and len stays unbounded against the LS_SIZE buffer returned by ctx->ops->get_ls(). Reject offsets at or beyond LS_SIZE and clamp len to the remaining space, mirroring the guard already used by spufs_mem_mmap_fault() and spufs_ps_fault(). Fixes: a352894d0705 ("spufs: use new vm_ops->access to allow local state access from gdb") Reported-by: Yuhao Jiang Cc: stable@vger.kernel.org Signed-off-by: Junrui Luo Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/SYBPR01MB7881EE775E8B51C09F5A29E7AF152@SYBPR01MB7881.ausprd01.prod.outlook.com Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/platforms/cell/spufs/file.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c index 10fa9b844fccd..94c1ffa8792e5 100644 --- a/arch/powerpc/platforms/cell/spufs/file.c +++ b/arch/powerpc/platforms/cell/spufs/file.c @@ -268,10 +268,12 @@ static int spufs_mem_mmap_access(struct vm_area_struct *vma, if (write && !(vma->vm_flags & VM_WRITE)) return -EACCES; + if (offset >= LS_SIZE) + return -EFAULT; if (spu_acquire(ctx)) return -EINTR; - if ((offset + len) > vma->vm_end) - len = vma->vm_end - offset; + if ((offset + len) > LS_SIZE) + len = LS_SIZE - offset; local_store = ctx->ops->get_ls(ctx); if (write) memcpy_toio(local_store + offset, buf, len); From d5c234774a82f27b594f70c15fc45ce3648e4295 Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Mon, 15 Jun 2026 16:37:26 -0700 Subject: [PATCH 2695/2758] powerpc/uaccess: correct check for CONFIG_PPC_E500 in mask_user_address() commit d610d3ab18197d87618da11ec5fe8b3cebf32208 upstream. mask_user_address() incorrectly checks for CONFIG_E500 instead of CONFIG_PPC_E500, causing mask_user_address_isel() to not be used on E500 hardware. Fix the check to use the correct name. Fixes: 861574d51bbd ("powerpc/uaccess: Implement masked user access") Cc: stable@vger.kernel.org # 7.0+ Signed-off-by: Ethan Nelson-Moore Fixes: 861574d51bbd ("powerpc/uaccess: Implement masked user access") Reviewed-by: Christophe Leroy (CS GROUP) Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260615233729.29386-1-enelsonmoore@gmail.com Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/include/asm/uaccess.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/uaccess.h b/arch/powerpc/include/asm/uaccess.h index e98c628e38993..619270bb7380c 100644 --- a/arch/powerpc/include/asm/uaccess.h +++ b/arch/powerpc/include/asm/uaccess.h @@ -511,7 +511,7 @@ static inline void __user *mask_user_address(const void __user *ptr) if (IS_ENABLED(CONFIG_PPC64)) return mask_user_address_simple(ptr); - if (IS_ENABLED(CONFIG_E500)) + if (IS_ENABLED(CONFIG_PPC_E500)) return mask_user_address_isel(ptr); if (TASK_SIZE <= UL(SZ_2G) && border >= UL(SZ_2G)) return mask_user_address_simple(ptr); From 3b0505e43da8fb5b2a7994c3c3604e5a74692154 Mon Sep 17 00:00:00 2001 From: Haofeng Li Date: Wed, 1 Jul 2026 17:33:27 +0800 Subject: [PATCH 2696/2758] wifi: cfg80211: validate EHT MLE before MLD ID read commit 74e27cd1d98b546fdb276008a83708d062339661 upstream. cfg80211_gen_new_ie() copies ML probe response elements from the parent frame when the parent EHT multi-link element has an MLD ID matching the nontransmitted BSSID index. The code only checked that the extension element had more than one byte before calling ieee80211_mle_get_mld_id(). That helper assumes a BASIC MLE with enough common info and documents that callers must first use ieee80211_mle_type_ok(). Attack chain: malicious AP sends a short EHT MLE in an MBSSID beacon. cfg80211_inform_bss_frame_data() stores the copied IE buffer. cfg80211_parse_mbssid_data() builds the nontransmitted BSS IE. cfg80211_gen_new_ie() sees the EHT MLE in the parent frame. ieee80211_mle_get_mld_id() then reads past the IE boundary. Validate the MLE type and size before reading the MLD ID. This matches the contract required by the MLE helper and rejects the short element before any internal MLE fields are accessed. Cc: stable@vger.kernel.org Fixes: 61dcfa8c2a8f ("wifi: cfg80211: copy multi-link element from the multi-link probe request's frame body to the generated elements") Signed-off-by: Haofeng Li Link: https://patch.msgid.link/20260701093327.2680709-1-lihaofeng@kylinos.cn Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- net/wireless/scan.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 27a56ee2e8f0b..d9f2a77e127ce 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -326,8 +326,11 @@ cfg80211_gen_new_ie(const u8 *ie, size_t ielen, /* For ML probe response, match the MLE in the frame body with * MLD id being 'bssid_index' */ - if (parent->id == WLAN_EID_EXTENSION && parent->datalen > 1 && + if (parent->id == WLAN_EID_EXTENSION && parent->data[0] == WLAN_EID_EXT_EHT_MULTI_LINK && + ieee80211_mle_type_ok(parent->data + 1, + IEEE80211_ML_CONTROL_TYPE_BASIC, + parent->datalen - 1) && bssid_index == ieee80211_mle_get_mld_id(parent->data + 1)) { if (!cfg80211_copy_elem_with_frags(parent, ie, ielen, From 2b1589fd9a076727a73bfb39e96622a76415ad32 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 01:35:07 +0800 Subject: [PATCH 2697/2758] wifi: ieee80211: validate MLE common info length commit 293baeae9b2434a3e432629d7720b5603db2d77e upstream. ieee80211_mle_common_size() uses the first common-info octet as the common information length for all known MLE types. However, ieee80211_mle_size_ok() only validates that octet for Basic, Probe Request, and TDLS MLEs. Reconfiguration MLEs also skipped the length octet when calculating the minimum common size, and Priority Access MLEs skipped validation of the advertised common information length. Account for the Reconfiguration common-info length octet and validate the advertised common information length for all known MLE types. Keep unknown-type handling unchanged. Fixes: 0f48b8b88aa9 ("wifi: ieee80211: add definitions for multi-link element") Cc: stable@vger.kernel.org Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260611173506.36838-2-enderaoelyther@gmail.com [remove now misleading comment] Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- include/linux/ieee80211-eht.h | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/include/linux/ieee80211-eht.h b/include/linux/ieee80211-eht.h index a97b1d01f3acf..05503950e0f1d 100644 --- a/include/linux/ieee80211-eht.h +++ b/include/linux/ieee80211-eht.h @@ -844,7 +844,7 @@ static inline bool ieee80211_mle_size_ok(const u8 *data, size_t len) const struct ieee80211_multi_link_elem *mle = (const void *)data; u8 fixed = sizeof(*mle); u8 common = 0; - bool check_common_len = false; + u8 common_len; u16 control; if (!data || len < fixed) @@ -855,7 +855,6 @@ static inline bool ieee80211_mle_size_ok(const u8 *data, size_t len) switch (u16_get_bits(control, IEEE80211_ML_CONTROL_TYPE)) { case IEEE80211_ML_CONTROL_TYPE_BASIC: common += sizeof(struct ieee80211_mle_basic_common_info); - check_common_len = true; if (control & IEEE80211_MLC_BASIC_PRES_LINK_ID) common += 1; if (control & IEEE80211_MLC_BASIC_PRES_BSS_PARAM_CH_CNT) @@ -875,9 +874,9 @@ static inline bool ieee80211_mle_size_ok(const u8 *data, size_t len) common += sizeof(struct ieee80211_mle_preq_common_info); if (control & IEEE80211_MLC_PREQ_PRES_MLD_ID) common += 1; - check_common_len = true; break; case IEEE80211_ML_CONTROL_TYPE_RECONF: + common += 1; if (control & IEEE80211_MLC_RECONF_PRES_MLD_MAC_ADDR) common += ETH_ALEN; if (control & IEEE80211_MLC_RECONF_PRES_EML_CAPA) @@ -889,7 +888,6 @@ static inline bool ieee80211_mle_size_ok(const u8 *data, size_t len) break; case IEEE80211_ML_CONTROL_TYPE_TDLS: common += sizeof(struct ieee80211_mle_tdls_common_info); - check_common_len = true; break; case IEEE80211_ML_CONTROL_TYPE_PRIO_ACCESS: common = ETH_ALEN + 1; @@ -902,11 +900,9 @@ static inline bool ieee80211_mle_size_ok(const u8 *data, size_t len) if (len < fixed + common) return false; - if (!check_common_len) - return true; + common_len = mle->variable[0]; - /* if present, common length is the first octet there */ - return mle->variable[0] >= common; + return common_len >= common && common_len <= len - fixed; } /** From 179d9be632d82aa654bae50fd3efc556822458c1 Mon Sep 17 00:00:00 2001 From: Zhiling Zou Date: Sat, 27 Jun 2026 00:58:30 +0800 Subject: [PATCH 2698/2758] wifi: mac80211: free ack status frame on TX header build failure commit 2c51457d930f723e5f2903af90f5847f7df53f42 upstream. ieee80211_build_hdr() stores an ACK status frame before it has finished all validation and header construction. If a later error path is taken, the transmit skb is freed but the stored ACK status frame remains in local->ack_status_frames. This can happen for control port frames when the requested MLO link ID does not match the link selected for a non-MLO station. Repeated failures can fill the ACK status IDR and leave pending ACK frames until hardware teardown. Remove any stored ACK status frame before returning an error after it has been inserted into the IDR. Fixes: a729cff8ad51 ("mac80211: implement wifi TX status") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Assisted-by: Codex:gpt-5.4 Signed-off-by: Zhiling Zou Signed-off-by: Ren Wei Link: https://patch.msgid.link/9de0423da840e92084915b8f92e66a421245c4b8.1782462409.git.roxy520tt@gmail.com Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- net/mac80211/tx.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index ea7f63e1fc17e..8e7f43ee10e5c 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2597,6 +2597,18 @@ static u16 ieee80211_store_ack_skb(struct ieee80211_local *local, return info_id; } +static void ieee80211_remove_ack_skb(struct ieee80211_local *local, u16 info_id) +{ + struct sk_buff *ack_skb; + unsigned long flags; + + spin_lock_irqsave(&local->ack_status_lock, flags); + ack_skb = idr_remove(&local->ack_status_frames, info_id); + spin_unlock_irqrestore(&local->ack_status_lock, flags); + + kfree_skb(ack_skb); +} + /** * ieee80211_build_hdr - build 802.11 header in the given frame * @sdata: virtual interface to build the header for @@ -2972,7 +2984,8 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, if (ieee80211_skb_resize(sdata, skb, head_need, ENCRYPT_DATA)) { ieee80211_free_txskb(&local->hw, skb); skb = NULL; - return ERR_PTR(-ENOMEM); + ret = -ENOMEM; + goto free; } } @@ -3040,6 +3053,8 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, return skb; free: + if (info_id) + ieee80211_remove_ack_skb(local, info_id); kfree_skb(skb); return ERR_PTR(ret); } From 625fc704b19cb48d7d269ad54ffffb4d3bf9c7ed Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 00:19:45 +0800 Subject: [PATCH 2699/2758] wifi: mac80211: validate extension-frame layout before RX commit 57d503ce32eccfa7650065ca4c560f7e29a2e676 upstream. Extension frames only have the extension header at the regular 802.11 header offset. The generic RX path can still reach helpers and interface dispatch code that read regular header address fields before unsupported extension subtypes are dropped. mac80211 currently only handles S1G beacon extension frames. Drop other extension subtypes before they can reach regular-header RX processing. For S1G beacons, linearize the SKB with the management-frame path and require the fixed S1G beacon header, including optional fixed fields indicated by frame control, before generic RX dispatch. Route S1G beacons through the station/default-link RX path without regular-header station lookup. Avoid regular-header address reads in the mac80211 RX paths that process S1G extension beacons, including accept-frame, duplicate-detection, address-copy, and MLO address-translation paths. Also make ieee80211_get_bssid() length-safe before returning the S1G source-address pointer. Fixes: 09a740ce352e ("mac80211: receive and process S1G beacons") Cc: stable@vger.kernel.org Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260611161943.91069-5-enderaoelyther@gmail.com Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- net/mac80211/rx.c | 34 ++++++++++++++++++++++++++++++++-- net/mac80211/util.c | 3 +++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 3fb40449c6c5c..fe583636d326e 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1526,6 +1526,9 @@ ieee80211_rx_h_check_dup(struct ieee80211_rx_data *rx) if (status->flag & RX_FLAG_DUP_VALIDATED) return RX_CONTINUE; + if (ieee80211_is_ext(hdr->frame_control)) + return RX_CONTINUE; + /* * Drop duplicate 802.11 retransmissions * (IEEE 802.11-2012: 9.3.2.10 "Duplicate detection and recovery") @@ -4487,12 +4490,16 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx) struct ieee80211_hdr *hdr = (void *)skb->data; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); u8 *bssid = ieee80211_get_bssid(hdr, skb->len, sdata->vif.type); - bool multicast = is_multicast_ether_addr(hdr->addr1) || - ieee80211_is_s1g_beacon(hdr->frame_control); + bool multicast; static const u8 nan_network_id[ETH_ALEN] __aligned(2) = { 0x51, 0x6F, 0x9A, 0x01, 0x00, 0x00 }; + if (ieee80211_is_s1g_beacon(hdr->frame_control)) + return sdata->vif.type == NL80211_IFTYPE_STATION && bssid; + + multicast = is_multicast_ether_addr(hdr->addr1); + switch (sdata->vif.type) { case NL80211_IFTYPE_STATION: if (!bssid && !sdata->u.mgd.use_4addr) @@ -5174,6 +5181,11 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx, hdr = (struct ieee80211_hdr *)rx->skb->data; } + if (ieee80211_is_s1g_beacon(hdr->frame_control)) { + ieee80211_invoke_rx_handlers(rx); + return true; + } + /* Store a copy of the pre-translated link addresses for SW crypto */ if (unlikely(is_unicast_ether_addr(hdr->addr1) && !ieee80211_is_data(hdr->frame_control))) @@ -5263,6 +5275,13 @@ static bool ieee80211_rx_for_interface(struct ieee80211_rx_data *rx, struct sta_info *sta; int link_id = -1; + if (ieee80211_is_s1g_beacon(hdr->frame_control)) { + if (!ieee80211_rx_data_set_sta(rx, NULL, -1)) + return false; + + return ieee80211_prepare_and_rx_handle(rx, skb, consume); + } + /* * Look up link station first, in case there's a * chance that they might have a link address that @@ -5338,6 +5357,17 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, err = -ENOBUFS; else err = skb_linearize(skb); + } else if (ieee80211_is_s1g_beacon(fc)) { + size_t s1g_hdr_len = offsetof(struct ieee80211_ext, + u.s1g_beacon.variable) + + ieee80211_s1g_optional_len(fc); + + if (skb->len < s1g_hdr_len) + err = -ENOBUFS; + else + err = skb_linearize(skb); + } else if (ieee80211_is_ext(fc)) { + err = -EINVAL; } else { err = !pskb_may_pull(skb, ieee80211_hdrlen(fc)); } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 2529b01e2cd55..5bc719222a87d 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -73,6 +73,9 @@ u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len, if (ieee80211_is_s1g_beacon(fc)) { struct ieee80211_ext *ext = (void *) hdr; + if (len < offsetofend(struct ieee80211_ext, u.s1g_beacon.sa)) + return NULL; + return ext->u.s1g_beacon.sa; } From 6126e12bf8c87badeab41a164c9689ac88e5c160 Mon Sep 17 00:00:00 2001 From: Rafael Beims Date: Fri, 12 Jun 2026 09:25:46 -0300 Subject: [PATCH 2700/2758] wifi: mwifiex: fix permanently busy scans after multiple roam iterations commit d78a407bad6f500884a8606aea1a5a9207be4030 upstream. In order for the firmware to sleep, the driver has to confirm a previously received sleep request. The normal sequence of evets goes like this: EVENT_SLEEP -> adapter->ps_state = PS_STATE_PRE_SLEEP -> sleep-confirm -> SLEEP -> EVENT_AWAKE -> AWAKE. Before sending the sleep-confirm command, the driver must make sure there are no commands either running or waiting to be completed. mwifiex_ret_802_11_associate() unconditionally sets ps_state = PS_STATE_AWAKE when it processes the association command response, outside of the normal powersave management flow. If EVENT_SLEEP arrives while the association command is in flight, ps_state is PRE_SLEEP when the association command response is parsed, and the forced AWAKE overwrites it. The deferred sleep-confirm is never sent. A subsequent scan_start command is correctly acknowledged, but the firmware doesn't generate scan_result events. The scan request never finishes, and additional requests from userspace fail with -EBUSY. After testing on both IW412 and W8997, I could only trigger the bug on the IW412 and observed the firmwares behave differently. On the IW412 the firmware still sends EVENT_SLEEP while the authentication / association process is ongoing. A W8997 under the same conditions seems to suppress power-save for the duration of the association, so PRE_SLEEP never coincided with the association response even after extended periods of testing using the loops described below (>12hours). On the IW412, the delay between commands that triggers an EVENT_SLEEP was empirically determined to be ~20ms. This delay can naturally occur when the driver is outputting debugging information (debug_mask = 0x00000037), in which situation the busy scans issue is repeatable while running "test 1)" as described below. If the delay between commands is less than ~20ms, the firmware stays awake and the issue was not reproducible running the same test. The host_mlme=false path also behaves differently. In this case, the entire authentication / association transaction is executed by one command (HostCmd_CMD_802_11_ASSOCIATE), and the firmware doesn't emit EVENT_SLEEP while the command is running. Remove the assignment so the ps_state is only manipulated in the paths that are related to powersave event handling and on the main workqueue for correct sleep confirmation. The following loop tests were performed (with debugging output enabled): 1) force roaming between two AP's, one 5GHz and one 2.4GHz, same SSID. Use wpa_cli to trigger the roaming behavior, sleep 2s between iterations. 2) force a disconnection to AP 1 and a connection to AP 2, test scan. Use wpa_cli to trigger the connection changes, sleep 2s between iterations. Each test ran in each device for at least 3 hours. Fixes: 5e6e3a92b9a4 ("wireless: mwifiex: initial commit for Marvell mwifiex driver") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Rafael Beims Reviewed-by: Jeff Chen Link: https://patch.msgid.link/20260612122547.1586872-2-rafael@beims.me Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/marvell/mwifiex/join.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/marvell/mwifiex/join.c b/drivers/net/wireless/marvell/mwifiex/join.c index 5a1a0287c1d58..b48f7febaf03f 100644 --- a/drivers/net/wireless/marvell/mwifiex/join.c +++ b/drivers/net/wireless/marvell/mwifiex/join.c @@ -736,7 +736,6 @@ int mwifiex_ret_802_11_associate(struct mwifiex_private *priv, /* Send a Media Connected event, according to the Spec */ priv->media_connected = true; - priv->adapter->ps_state = PS_STATE_AWAKE; priv->adapter->pps_uapsd_mode = false; priv->adapter->tx_lock_flag = false; From 0e65079d28e5c461b6813ea5821be4dfff24ff63 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Fri, 26 Jun 2026 10:13:38 +0800 Subject: [PATCH 2701/2758] mtd: virt-concat: free duplicate generated name commit caa0ecbeff4f7fbf70f22bd8ca598918bffb1b78 upstream. Every MTD registration runs mtd_virt_concat_create_join(). Once a virtual concat has already been registered, the function builds the same name again and takes the equal-name branch. That branch skips to the next item without freeing the newly allocated string. Free the temporary name before continuing. Fixes: 43db6366fc2d ("mtd: Add driver for concatenating devices") Cc: stable@vger.kernel.org Signed-off-by: Xu Rao Signed-off-by: Miquel Raynal Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/mtd_virt_concat.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/mtd_virt_concat.c b/drivers/mtd/mtd_virt_concat.c index 37075ead0f337..5db6e648927e2 100644 --- a/drivers/mtd/mtd_virt_concat.c +++ b/drivers/mtd/mtd_virt_concat.c @@ -321,8 +321,10 @@ int mtd_virt_concat_create_join(void) if (concat->mtd.name) { ret = memcmp(concat->mtd.name, name, name_sz); - if (ret == 0) + if (ret == 0) { + kfree(name); continue; + } } mtd = mtd_concat_create(concat->subdev, concat->num_subdev, name); if (!mtd) { From 9fc23fc52fc90d7abf328d9e4e45443d6997b8ad Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Fri, 3 Jul 2026 15:43:50 +0800 Subject: [PATCH 2702/2758] mtd: onenand: samsung: report DMA completion timeouts commit d03a19bd6c7f86b99ca8fb61a6ec2345cee1d9d6 upstream. The S5PC110 OneNAND DMA helpers have bounded waits for transfer completion. The polling helper falls out of its timeout loop and returns success, and the IRQ helper ignores wait_for_completion_timeout(). Return -ETIMEDOUT when the DMA transfer-done bit or completion does not arrive before the timeout so callers can treat the buffer transfer as failed. Fixes: e23abf4b7743 ("mtd: OneNAND: S5PC110: Implement DMA interrupt method") Cc: stable@vger.kernel.org Signed-off-by: Pengpeng Hou Signed-off-by: Miquel Raynal Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/nand/onenand/onenand_samsung.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/onenand/onenand_samsung.c b/drivers/mtd/nand/onenand/onenand_samsung.c index 6d6aa709a21f8..b7b7758ce4d86 100644 --- a/drivers/mtd/nand/onenand/onenand_samsung.c +++ b/drivers/mtd/nand/onenand/onenand_samsung.c @@ -554,6 +554,9 @@ static int s5pc110_dma_poll(dma_addr_t dst, dma_addr_t src, size_t count, int di } while (!(status & S5PC110_DMA_TRANS_STATUS_TD) && time_before(jiffies, timeout)); + if (!(status & S5PC110_DMA_TRANS_STATUS_TD)) + return -ETIMEDOUT; + writel(S5PC110_DMA_TRANS_CMD_TDC, base + S5PC110_DMA_TRANS_CMD); return 0; @@ -608,7 +611,9 @@ static int s5pc110_dma_irq(dma_addr_t dst, dma_addr_t src, size_t count, int dir writel(S5PC110_DMA_TRANS_CMD_TR, base + S5PC110_DMA_TRANS_CMD); - wait_for_completion_timeout(&onenand->complete, msecs_to_jiffies(20)); + if (!wait_for_completion_timeout(&onenand->complete, + msecs_to_jiffies(20))) + return -ETIMEDOUT; return 0; } From 09e044192a42f716215fbd1a100ee87fa57426aa Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Fri, 3 Jul 2026 15:40:52 +0800 Subject: [PATCH 2703/2758] mtd: mchp23k256: use SPI match data for chip caps commit d322e40f4edf92bf0ca329e5aa4ae1c0316feb38 upstream. The driver stores chip capacity information in both the OF match table and the SPI id table. Probe currently uses of_device_get_match_data(), so a non-OF SPI modalias match falls back to mchp23k256_caps even when the SPI id table selected a different part. Use spi_get_device_match_data() so SPI id-table driver_data is consumed when OF match data is absent. This keeps the existing default fallback while avoiding the wrong MTD geometry for id-table-only matches. Fixes: 4379075a870b ("mtd: mchp23k256: Add support for mchp23lcv1024") Cc: stable@vger.kernel.org Signed-off-by: Pengpeng Hou Signed-off-by: Miquel Raynal Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/devices/mchp23k256.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/devices/mchp23k256.c b/drivers/mtd/devices/mchp23k256.c index cef5f9677d39e..66e058df4c328 100644 --- a/drivers/mtd/devices/mchp23k256.c +++ b/drivers/mtd/devices/mchp23k256.c @@ -188,7 +188,7 @@ static int mchp23k256_probe(struct spi_device *spi) data = dev_get_platdata(&spi->dev); - flash->caps = of_device_get_match_data(&spi->dev); + flash->caps = spi_get_device_match_data(spi); if (!flash->caps) flash->caps = &mchp23k256_caps; From c2e1d33929565fa14c48d8a5a45edc3ebfc941b2 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Wed, 17 Jun 2026 23:23:19 +0800 Subject: [PATCH 2704/2758] mmc: vub300: defer reset until cmd_mutex is unlocked commit ee5fb641c4ccac8406c668d3e947eb20ce44f233 upstream. vub300_cmndwork_thread() holds cmd_mutex while it sends a command and waits for the command response. If the response wait times out, __vub300_command_response() kills the command URBs and then synchronously resets the USB device through usb_reset_device(). That reset path re-enters the driver through vub300_pre_reset(), which also takes cmd_mutex. The worker therefore tries to acquire the same mutex recursively while it is still holding it from the command path. This issue was found by our static analysis tool and then manually reviewed against the current tree. The grounded PoC kept the real worker and timeout/reset carrier: vub300_cmndwork_thread() __vub300_command_response() usb_lock_device_for_reset() usb_reset_device() vub300_pre_reset() Lockdep reported the same-task recursive acquisition on cmd_mutex: WARNING: possible recursive locking detected ... (&test_vub300.cmd_mutex) ... at: usb_reset_device... [vuln_msv] ... (&test_vub300.cmd_mutex) ... at: vub300_cmndwork_thread+0x12/0x20 [vuln_msv] Workqueue: vub300_cmd_wq vub300_cmndwork_thread [vuln_msv] *** DEADLOCK *** Return a flag from __vub300_command_response() when the timeout path needs a device reset, then perform the reset after vub300_cmndwork_thread() has cleared the in-flight command state and dropped cmd_mutex. The reset is still attempted before mmc_request_done(), preserving the existing request completion ordering while avoiding the recursive lock. Fixes: 88095e7b473a ("mmc: Add new VUB300 USB-to-SD/SDIO/MMC driver") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/vub300.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index c1c21e95f5bf8..2dae474dcd067 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -1586,7 +1586,7 @@ static int __command_write_data(struct vub300_mmc_host *vub300, return linear_length; } -static void __vub300_command_response(struct vub300_mmc_host *vub300, +static bool __vub300_command_response(struct vub300_mmc_host *vub300, struct mmc_command *cmd, struct mmc_data *data, int data_length) { @@ -1598,17 +1598,11 @@ static void __vub300_command_response(struct vub300_mmc_host *vub300, msecs_to_jiffies(msec_timeout)); if (respretval == 0) { /* TIMED OUT */ /* we don't know which of "out" and "res" if any failed */ - int result; vub300->usb_timed_out = 1; usb_kill_urb(vub300->command_out_urb); usb_kill_urb(vub300->command_res_urb); cmd->error = -ETIMEDOUT; - result = usb_lock_device_for_reset(vub300->udev, - vub300->interface); - if (result == 0) { - result = usb_reset_device(vub300->udev); - usb_unlock_device(vub300->udev); - } + return true; } else if (respretval < 0) { /* we don't know which of "out" and "res" if any failed */ usb_kill_urb(vub300->command_out_urb); @@ -1704,6 +1698,8 @@ static void __vub300_command_response(struct vub300_mmc_host *vub300, } else { cmd->error = -EINVAL; } + + return false; } static void construct_request_response(struct vub300_mmc_host *vub300, @@ -1749,6 +1745,7 @@ static void vub300_cmndwork_thread(struct work_struct *work) struct mmc_request *req = vub300->req; struct mmc_command *cmd = vub300->cmd; struct mmc_data *data = vub300->data; + bool reset_device; int data_length; mutex_lock(&vub300->cmd_mutex); init_completion(&vub300->command_complete); @@ -1771,7 +1768,8 @@ static void vub300_cmndwork_thread(struct work_struct *work) data_length = __command_read_data(vub300, cmd, data); else data_length = __command_write_data(vub300, cmd, data); - __vub300_command_response(vub300, cmd, data, data_length); + reset_device = __vub300_command_response(vub300, cmd, + data, data_length); vub300->req = NULL; vub300->cmd = NULL; vub300->data = NULL; @@ -1779,6 +1777,16 @@ static void vub300_cmndwork_thread(struct work_struct *work) if (cmd->error == -ENOMEDIUM) check_vub300_port_status(vub300); mutex_unlock(&vub300->cmd_mutex); + if (reset_device) { + int result; + + result = usb_lock_device_for_reset(vub300->udev, + vub300->interface); + if (result == 0) { + result = usb_reset_device(vub300->udev); + usb_unlock_device(vub300->udev); + } + } mmc_request_done(vub300->mmc, req); kref_put(&vub300->kref, vub300_delete); return; From 1773c6e292b044cecf252e8269f0bbfc94578e98 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Fri, 3 Jul 2026 15:42:33 +0800 Subject: [PATCH 2705/2758] mtd: rawnand: fsl_ifc: return errors for failed page reads commit f9a13e05a327080c3a1c8165adf9e678fb68fef2 upstream. fsl_ifc_run_command() logs controller timeout and other non-OPC completion states in ctrl->nand_stat. fsl_ifc_read_page() then only increments the ECC failure counter for non-OPC status and still returns max_bitflips, which can be zero. Return -ETIMEDOUT when the command did not complete at all and -EIO for other non-OPC read completions so the NAND core does not treat a failed page read as a clean page. Fixes: 82771882d960 ("NAND Machine support for Integrated Flash Controller") Cc: stable@vger.kernel.org Signed-off-by: Pengpeng Hou Signed-off-by: Miquel Raynal Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/nand/raw/fsl_ifc_nand.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c index fad0334f759dd..a88ac2cfaccdc 100644 --- a/drivers/mtd/nand/raw/fsl_ifc_nand.c +++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c @@ -684,8 +684,15 @@ static int fsl_ifc_read_page(struct nand_chip *chip, uint8_t *buf, return check_erased_page(chip, buf); } - if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) + if (!ctrl->nand_stat) { mtd->ecc_stats.failed++; + return -ETIMEDOUT; + } + + if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) { + mtd->ecc_stats.failed++; + return -EIO; + } return nctrl->max_bitflips; } From 791fc00d116e6f7076c2bb95c29d63a56aad63de Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Fri, 3 Jul 2026 15:37:59 +0800 Subject: [PATCH 2706/2758] mtd: rawnand: lpc32xx_mlc: fail DMA transfers on timeout commit dbf590b662695b16fbf5917ef129697be4410ea9 upstream. lpc32xx_xmit_dma() starts a DMA transfer and waits up to one second for its completion, but it ignores the wait result and returns success after unmapping the buffer. A timed out read can therefore return success with incomplete data, and a timed out write can continue the NAND operation without proof that the DMA payload reached the controller. Terminate the DMA channel on timeout, unmap the scatterlist through the existing cleanup path, and return -ETIMEDOUT to the NAND read/write callers. Initialize the shared cleanup-path result before using it for dmaengine_prep_slave_sg() failures. Fixes: 70f7cb78ec53 ("mtd: add LPC32xx MLC NAND driver") Cc: stable@vger.kernel.org Reviewed-by: Vladimir Zapolskiy Signed-off-by: Pengpeng Hou Signed-off-by: Miquel Raynal Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/nand/raw/lpc32xx_mlc.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/raw/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c index 19b13ae536d48..8f6a89d9ba83f 100644 --- a/drivers/mtd/nand/raw/lpc32xx_mlc.c +++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c @@ -396,6 +396,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len, struct lpc32xx_nand_host *host = nand_get_controller_data(chip); struct dma_async_tx_descriptor *desc; int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; + unsigned long time_left; int res; sg_init_one(&host->sgl, mem, len); @@ -410,6 +411,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len, flags); if (!desc) { dev_err(mtd->dev.parent, "Failed to prepare slave sg\n"); + res = -ENXIO; goto out1; } @@ -420,7 +422,13 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len, dmaengine_submit(desc); dma_async_issue_pending(host->dma_chan); - wait_for_completion_timeout(&host->comp_dma, msecs_to_jiffies(1000)); + time_left = wait_for_completion_timeout(&host->comp_dma, + msecs_to_jiffies(1000)); + if (!time_left) { + dmaengine_terminate_sync(host->dma_chan); + res = -ETIMEDOUT; + goto out1; + } dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, DMA_BIDIRECTIONAL); @@ -428,7 +436,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len, out1: dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, DMA_BIDIRECTIONAL); - return -ENXIO; + return res; } static int lpc32xx_read_page(struct nand_chip *chip, uint8_t *buf, From cb2031f8b226efbd13735c07b075e5f14ec11f6d Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Fri, 3 Jul 2026 15:39:43 +0800 Subject: [PATCH 2707/2758] mtd: rawnand: lpc32xx_slc: fail DMA transfer on completion timeout commit 17a8ce84964f243c8f89dc7353ac7e8d3137bc74 upstream. lpc32xx_xmit_dma() waits for the DMA completion callback but ignores wait_for_completion_timeout(). A timed out DMA transfer is therefore unmapped and reported as successful to the NAND read/write path. Return -ETIMEDOUT when the completion wait expires. Terminate the DMA channel before unmapping the scatterlist so the timed out transfer cannot continue to access the buffer after the error is returned. Fixes: 2944a44da09e ("mtd: add LPC32xx SLC NAND driver") Cc: stable@vger.kernel.org Reviewed-by: Vladimir Zapolskiy Signed-off-by: Pengpeng Hou Signed-off-by: Miquel Raynal Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/nand/raw/lpc32xx_slc.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c index 3ca30e7dce336..10c8080207f49 100644 --- a/drivers/mtd/nand/raw/lpc32xx_slc.c +++ b/drivers/mtd/nand/raw/lpc32xx_slc.c @@ -430,6 +430,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, dma_addr_t dma, struct dma_async_tx_descriptor *desc; int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; int res; + unsigned long time_left; host->dma_slave_config.direction = dir; host->dma_slave_config.src_addr = dma; @@ -467,12 +468,19 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, dma_addr_t dma, dmaengine_submit(desc); dma_async_issue_pending(host->dma_chan); - wait_for_completion_timeout(&host->comp, msecs_to_jiffies(1000)); + time_left = wait_for_completion_timeout(&host->comp, + msecs_to_jiffies(1000)); + if (!time_left) { + dmaengine_terminate_sync(host->dma_chan); + res = -ETIMEDOUT; + } else { + res = 0; + } dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, DMA_BIDIRECTIONAL); - return 0; + return res; out1: dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, DMA_BIDIRECTIONAL); From 0e93010b52bba0b55a05597f5d3a24ce07770295 Mon Sep 17 00:00:00 2001 From: Ao Sun Date: Mon, 6 Jul 2026 11:43:00 +0000 Subject: [PATCH 2708/2758] mmc: block: fix RPMB device unregister ordering commit 86152fef52cac15cd662ed3bfc7604fbfef378f0 upstream. Since commit 7852028a35f0 ("mmc: block: register RPMB partition with the RPMB subsystem"), each mmc RPMB partition is represented by two device objects: - the mmc-owned device (`rpmb->dev`, backing the legacy /dev/mmcblkXrpmb char device) and - the rpmb-core device (`rdev`, backing /dev/rpmbN). The child RPMB device holds a reference to its parent, so the parent's release callback cannot be invoked if the child device is still registered. Remove rpmb_dev_unregister() from the parent release handler and unregister the child RPMB device in the remove path before tearing down the parent device. Also delete the extra blank line between mmc_blk_remove_rpmb_part() and {. Fixes: 7852028a35f0 ("mmc: block: register RPMB partition with the RPMB subsystem") Cc: stable@vger.kernel.org Signed-off-by: Jiazi Li Signed-off-by: Ao Sun Reviewed-by: Avri Altman Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/core/block.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 0274e8d07660d..54a923ba4f1e7 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -2715,7 +2715,6 @@ static void mmc_blk_rpmb_device_release(struct device *dev) { struct mmc_rpmb_data *rpmb = dev_get_drvdata(dev); - rpmb_dev_unregister(rpmb->rdev); mmc_blk_put(rpmb->md); ida_free(&mmc_rpmb_ida, rpmb->id); kfree(rpmb); @@ -2930,8 +2929,8 @@ static int mmc_blk_alloc_rpmb_part(struct mmc_card *card, } static void mmc_blk_remove_rpmb_part(struct mmc_rpmb_data *rpmb) - { + rpmb_dev_unregister(rpmb->rdev); cdev_device_del(&rpmb->chrdev, &rpmb->dev); put_device(&rpmb->dev); } From f59d0244d90b0abd0467a44a3e03155623b81125 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 2 Jun 2026 21:13:44 +0100 Subject: [PATCH 2709/2758] mmc: mmc_test: Fix __counted_by handling after kzalloc_flex() conversion commit c35fac6481a1dbb2d114dd337b54a40799437546 upstream. Fix logic issues introduced by the kzalloc_flex() conversion in mmc_test_alloc_mem() due to interaction with the __counted_by annotation on the flexible array. Bounds-checking sanitizers rely on the counter field reflecting the allocated array size before any array access occurs. However, use mem->cnt both as the allocation size and as the runtime insertion index, causing incorrect indexing and potentially invalid bounds tracking. Initialize mem->cnt to the maximum allocated number of segments immediately after kzalloc_flex(), then use a separate local index variable to track successfully allocated entries. Update mem->cnt to the actual number of initialized elements before returning or entering the cleanup path. Also rewrite mmc_test_free_mem() to use a forward for-loop, improving readability and ensuring only initialized entries are freed. Fixes: c3126dccfd7b ("mmc: mmc_test: use kzalloc_flex") Signed-off-by: Lad Prabhakar Reviewed-by: Geert Uytterhoeven Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/core/mmc_test.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/core/mmc_test.c b/drivers/mmc/core/mmc_test.c index ab38e4c45a8da..4dc16649e61d4 100644 --- a/drivers/mmc/core/mmc_test.c +++ b/drivers/mmc/core/mmc_test.c @@ -318,9 +318,9 @@ static void mmc_test_free_mem(struct mmc_test_mem *mem) { if (!mem) return; - while (mem->cnt--) - __free_pages(mem->arr[mem->cnt].page, - mem->arr[mem->cnt].order); + for (unsigned int i = 0; i < mem->cnt; i++) + __free_pages(mem->arr[i].page, + mem->arr[i].order); kfree(mem); } @@ -341,6 +341,7 @@ static struct mmc_test_mem *mmc_test_alloc_mem(unsigned long min_sz, unsigned long page_cnt = 0; unsigned long limit = nr_free_buffer_pages() >> 4; struct mmc_test_mem *mem; + unsigned int idx = 0; if (max_page_cnt > limit) max_page_cnt = limit; @@ -375,23 +376,26 @@ static struct mmc_test_mem *mmc_test_alloc_mem(unsigned long min_sz, goto out_free; break; } - mem->arr[mem->cnt].page = page; - mem->arr[mem->cnt].order = order; - mem->cnt += 1; + mem->arr[idx].page = page; + mem->arr[idx].order = order; + idx += 1; if (max_page_cnt <= (1UL << order)) break; max_page_cnt -= 1UL << order; page_cnt += 1UL << order; - if (mem->cnt >= max_segs) { + if (idx >= mem->cnt) { if (page_cnt < min_page_cnt) goto out_free; break; } } + mem->cnt = idx; + return mem; out_free: + mem->cnt = idx; mmc_test_free_mem(mem); return NULL; } From 657e0acce5b8d397011b7045f1c4d6e86b66a415 Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Mon, 1 Jun 2026 17:49:01 +0300 Subject: [PATCH 2710/2758] mmc: sdhci-of-dwcmshc: check bus clock enable result in the probe() method commit 521f39ca93cc43ce1b3eae8d44201f8f55dd9151 upstream. In the driver's probe() method, clk_disable_unprepare() for the bus clock is called on the error path even if the prior clk_prepare_enable() call has failed (and the same thing happens in the remove() method as well) -- that would cause the prepare/enable counter imbalance. Also, the same problem can happen in the driver's suspend() method; note that the resume() method does check the clk_prepare_enable()'s result -- let's be consistent and do that in probe() method as well. BTW, I don't know for sure what does the bus clock control -- if it affects the register accesses, the driver will likely cause (e.g. on ARM) a kernel oops if it fails to prepare/enable the bus clock in the probe() method... Found by Linux Verification Center (linuxtesting.org) with the Svace static analysis tool. Fixes: e438cf49b305 ("mmc: sdhci-of-dwcmshc: add SDHCI OF Synopsys DWC MSHC driver") Fixes: bccce2ec7790 ("mmc: sdhci-of-dwcmshc: add suspend/resume support") Signed-off-by: Sergey Shtylyov Acked-by: Adrian Hunter Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-of-dwcmshc.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index b9ecd91f44ad4..29af07561a861 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -2441,13 +2441,16 @@ static int dwcmshc_probe(struct platform_device *pdev) return err; priv->bus_clk = devm_clk_get(dev, "bus"); - if (!IS_ERR(priv->bus_clk)) - clk_prepare_enable(priv->bus_clk); + if (!IS_ERR(priv->bus_clk)) { + err = clk_prepare_enable(priv->bus_clk); + if (err) + goto err_clk; + } } err = mmc_of_parse(host->mmc); if (err) - goto err_clk; + goto err_bus_clk; sdhci_get_of_property(pdev); @@ -2461,7 +2464,7 @@ static int dwcmshc_probe(struct platform_device *pdev) if (pltfm_data->init) { err = pltfm_data->init(&pdev->dev, host, priv); if (err) - goto err_clk; + goto err_bus_clk; } #ifdef CONFIG_ACPI @@ -2507,9 +2510,10 @@ static int dwcmshc_probe(struct platform_device *pdev) err_rpm: pm_runtime_disable(dev); pm_runtime_put_noidle(dev); +err_bus_clk: + clk_disable_unprepare(priv->bus_clk); err_clk: clk_disable_unprepare(pltfm_host->clk); - clk_disable_unprepare(priv->bus_clk); clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks); return err; } From 48188934d5d2b4aa806f462859b1fc4d7d2ae0f4 Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:11 +0800 Subject: [PATCH 2711/2758] mmc: sdhci-esdhc-imx: remove unnecessary mmc_card_wake_sdio_irq check for tuning save/restore commit 899160e2774d9952e9f2770b38f701ff1906c0b2 upstream. The tuning save/restore during system PM is conditioned on mmc_card_wake_sdio_irq(), but this check is unrelated to whether tuning values need to be preserved. The actual requirement is that the card keeps power during suspend and the controller is a uSDHC. SDIO devices using out-of-band GPIO wakeup maintain power during suspend but do not set the SDIO IRQ wake flag. In this case the tuning delay values are not saved/restored. Remove the unnecessary mmc_card_wake_sdio_irq() condition from both the suspend save and resume restore paths. Fixes: c63d25cdc59a ("mmc: sdhci-esdhc-imx: Save tuning value when card stays powered in suspend") Acked-by: Adrian Hunter Reviewed-by: Frank Li Reviewed-by: Haibo Chen Signed-off-by: Luke Wang Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-esdhc-imx.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index ccdde48e74ef1..a773f30a10b29 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -2064,8 +2064,7 @@ static int sdhci_esdhc_suspend(struct device *dev) * to save the tuning delay value just in case the usdhc * lost power during system PM. */ - if (mmc_card_keep_power(host->mmc) && mmc_card_wake_sdio_irq(host->mmc) && - esdhc_is_usdhc(imx_data)) + if (mmc_card_keep_power(host->mmc) && esdhc_is_usdhc(imx_data)) sdhc_esdhc_tuning_save(host); if (device_may_wakeup(dev)) { @@ -2130,8 +2129,7 @@ static int sdhci_esdhc_resume(struct device *dev) * restore the saved tuning delay value for the device which keep * power during system PM. */ - if (mmc_card_keep_power(host->mmc) && mmc_card_wake_sdio_irq(host->mmc) && - esdhc_is_usdhc(imx_data)) + if (mmc_card_keep_power(host->mmc) && esdhc_is_usdhc(imx_data)) sdhc_esdhc_tuning_restore(host); pm_runtime_put_autosuspend(dev); From 24300decd8bd6cdbc32dc39cb78a929c713e485c Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:12 +0800 Subject: [PATCH 2712/2758] mmc: sdhci-esdhc-imx: restore DLL override for DDR modes on resume commit 2439becd91bad6883b135044f85f83a0538b96a6 upstream. sdhci_esdhc_imx_hwinit() unconditionally clears ESDHC_DLL_CTRL by writing zero. For SDIO devices that keep power during system suspend and operate in DDR mode, the card remains in DDR timing while the host DLL override configuration is lost. Extract the DLL override setup from esdhc_set_uhs_signaling() into a helper esdhc_set_dll_override(), and call it on the resume path when the card kept power and is using a DDR timing mode. Fixes: 676a83855614 ("mmc: host: sdhci-esdhc-imx: refactor the system PM logic") Acked-by: Adrian Hunter Reviewed-by: Frank Li Reviewed-by: Haibo Chen Signed-off-by: Luke Wang Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-esdhc-imx.c | 39 +++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index a773f30a10b29..3b1e63425a193 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1349,6 +1349,23 @@ static int esdhc_change_pinstate(struct sdhci_host *host, return pinctrl_select_state(imx_data->pinctrl, pinctrl); } +static void esdhc_set_dll_override(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host); + struct esdhc_platform_data *boarddata = &imx_data->boarddata; + u32 v; + + if (!boarddata->delay_line) + return; + + v = boarddata->delay_line << ESDHC_DLL_OVERRIDE_VAL_SHIFT | + (1 << ESDHC_DLL_OVERRIDE_EN_SHIFT); + if (is_imx53_esdhc(imx_data)) + v <<= 1; + writel(v, host->ioaddr + ESDHC_DLL_CTRL); +} + /* * For HS400 eMMC, there is a data_strobe line. This signal is generated * by the device and used for data output and CRC status response output @@ -1404,7 +1421,6 @@ static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing) u32 m; struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host); - struct esdhc_platform_data *boarddata = &imx_data->boarddata; /* disable ddr mode and disable HS400 mode */ m = readl(host->ioaddr + ESDHC_MIX_CTRL); @@ -1425,15 +1441,7 @@ static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing) m |= ESDHC_MIX_CTRL_DDREN; writel(m, host->ioaddr + ESDHC_MIX_CTRL); imx_data->is_ddr = 1; - if (boarddata->delay_line) { - u32 v; - v = boarddata->delay_line << - ESDHC_DLL_OVERRIDE_VAL_SHIFT | - (1 << ESDHC_DLL_OVERRIDE_EN_SHIFT); - if (is_imx53_esdhc(imx_data)) - v <<= 1; - writel(v, host->ioaddr + ESDHC_DLL_CTRL); - } + esdhc_set_dll_override(host); break; case MMC_TIMING_MMC_HS400: m |= ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN; @@ -2129,9 +2137,18 @@ static int sdhci_esdhc_resume(struct device *dev) * restore the saved tuning delay value for the device which keep * power during system PM. */ - if (mmc_card_keep_power(host->mmc) && esdhc_is_usdhc(imx_data)) + if (mmc_card_keep_power(host->mmc) && esdhc_is_usdhc(imx_data)) { sdhc_esdhc_tuning_restore(host); + /* + * Restore DLL override for DDR modes. hwinit unconditionally + * clears ESDHC_DLL_CTRL, but the card is still in DDR mode. + */ + if (host->timing == MMC_TIMING_UHS_DDR50 || + host->timing == MMC_TIMING_MMC_DDR52) + esdhc_set_dll_override(host); + } + pm_runtime_put_autosuspend(dev); return ret; From bb72b2398c05fd0a4ebf13f49c7d599d7023d484 Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:13 +0800 Subject: [PATCH 2713/2758] mmc: sdhci-esdhc-imx: fix esdhc_change_pinstate() to allow default state restore commit 5adc14cd4b905629d5b9163b3a416dcab24c7ce2 upstream. esdhc_change_pinstate() checks for pins_100mhz and pins_200mhz at the top of the function and returns -EINVAL if either is not defined. This prevents the default case from ever being reached, which means devices with a sleep pinctrl state but without high-speed pin states (100mhz/ 200mhz) can never restore their default pin configuration. Move the IS_ERR checks for pins_100mhz and pins_200mhz into their respective switch cases. Fixes: 676a83855614 ("mmc: host: sdhci-esdhc-imx: refactor the system PM logic") Acked-by: Adrian Hunter Reviewed-by: Frank Li Signed-off-by: Luke Wang Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-esdhc-imx.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 3b1e63425a193..a54da5757acc1 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1326,19 +1326,21 @@ static int esdhc_change_pinstate(struct sdhci_host *host, dev_dbg(mmc_dev(host->mmc), "change pinctrl state for uhs %d\n", uhs); - if (IS_ERR(imx_data->pinctrl) || - IS_ERR(imx_data->pins_100mhz) || - IS_ERR(imx_data->pins_200mhz)) + if (IS_ERR(imx_data->pinctrl)) return -EINVAL; switch (uhs) { case MMC_TIMING_UHS_SDR50: case MMC_TIMING_UHS_DDR50: + if (IS_ERR(imx_data->pins_100mhz)) + return -EINVAL; pinctrl = imx_data->pins_100mhz; break; case MMC_TIMING_UHS_SDR104: case MMC_TIMING_MMC_HS200: case MMC_TIMING_MMC_HS400: + if (IS_ERR(imx_data->pins_200mhz)) + return -EINVAL; pinctrl = imx_data->pins_200mhz; break; default: From 9bf4ee05a1109d889de9151ee78bd80293923f70 Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:15 +0800 Subject: [PATCH 2714/2758] mmc: sdhci-esdhc-imx: disable irq during suspend to fix unhandled interrupt commit 9d87eaf985cef9581b6ed99b461b38e8cd666480 upstream. When using WIFI out-of-band wakeup, an "irq xxx: nobody cared" warning occurs. This happens because the usdhc interrupt is not disabled during system suspend when device_may_wakeup() returns false. The sequence of events leading to this issue: 1. System enters suspend without disabling usdhc interrupt (because device_may_wakeup() returns false for usdhc device) 2. WIFI out-of-band wakeup triggers system resume via GPIO interrupt 3. WIFI sends a Card interrupt before usdhc has fully resumed 4. usdhc is still in runtime suspend state and cannot handle the interrupt properly 5. The unhandled interrupt triggers "nobody cared" warning Fix this by unconditionally disabling the usdhc interrupt during suspend and re-enabling it during resume, regardless of the wakeup capability. This ensures no interrupts are processed during the suspend/resume transition. Fixes: 676a83855614 ("mmc: host: sdhci-esdhc-imx: refactor the system PM logic") Acked-by: Adrian Hunter Reviewed-by: Haibo Chen Signed-off-by: Luke Wang Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-esdhc-imx.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index a54da5757acc1..d980dea9cad1f 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -2077,9 +2077,10 @@ static int sdhci_esdhc_suspend(struct device *dev) if (mmc_card_keep_power(host->mmc) && esdhc_is_usdhc(imx_data)) sdhc_esdhc_tuning_save(host); + /* The irqs of imx are not shared. It is safe to disable */ + disable_irq(host->irq); + if (device_may_wakeup(dev)) { - /* The irqs of imx are not shared. It is safe to disable */ - disable_irq(host->irq); ret = sdhci_enable_irq_wakeups(host); if (!ret) dev_warn(dev, "Failed to enable irq wakeup\n"); @@ -2130,10 +2131,10 @@ static int sdhci_esdhc_resume(struct device *dev) /* re-initialize hw state in case it's lost in low power mode */ sdhci_esdhc_imx_hwinit(host); - if (host->irq_wake_enabled) { + if (host->irq_wake_enabled) sdhci_disable_irq_wakeups(host); - enable_irq(host->irq); - } + + enable_irq(host->irq); /* * restore the saved tuning delay value for the device which keep From 6355749aebf6c78081f7096a52edcd28d2aa0fab Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:16 +0800 Subject: [PATCH 2715/2758] mmc: sdhci-esdhc-imx: use pm_runtime_resume_and_get() in suspend commit 8da5930144712412d85e7f868693d96ec5c2018c upstream. Replace pm_runtime_get_sync() with pm_runtime_resume_and_get() to simplify error handling. pm_runtime_resume_and_get() automatically drops the usage counter on failure, avoiding the need for a separate pm_runtime_put_noidle() call. If it fails, the device is unclocked and accessing hardware registers would cause a kernel panic, so return the error immediately. Fixes: 676a83855614 ("mmc: host: sdhci-esdhc-imx: refactor the system PM logic") Acked-by: Adrian Hunter Signed-off-by: Luke Wang Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-esdhc-imx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index d980dea9cad1f..7818f351a58ae 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -2061,7 +2061,9 @@ static int sdhci_esdhc_suspend(struct device *dev) * 2, make sure the pm_runtime_force_resume() in sdhci_esdhc_resume() really * invoke its ->runtime_resume callback (needs_force_resume = 1). */ - pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; if ((imx_data->socdata->flags & ESDHC_FLAG_STATE_LOST_IN_LPMODE) && (host->tuning_mode != SDHCI_TUNING_MODE_1)) { From 89b63cd133fe96cdf2c4eb73cf1d1a8a92a26f96 Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:17 +0800 Subject: [PATCH 2716/2758] mmc: sdhci-esdhc-imx: make non-fatal errors non-blocking in suspend commit 6aa00a43bbd3d994558a55586351757cebbff236 upstream. Make pinctrl_pm_select_sleep_state() and mmc_gpio_set_cd_wake() failures non-fatal in the suspend path. These failures only mean slightly higher power consumption or missing CD wakeup capability, but should not block system suspend. Also change the function to always return 0 on the success path instead of propagating non-fatal warning return values. Fixes: 676a83855614 ("mmc: host: sdhci-esdhc-imx: refactor the system PM logic") Acked-by: Adrian Hunter Signed-off-by: Luke Wang Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-esdhc-imx.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 7818f351a58ae..290a3172931bf 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -2083,8 +2083,7 @@ static int sdhci_esdhc_suspend(struct device *dev) disable_irq(host->irq); if (device_may_wakeup(dev)) { - ret = sdhci_enable_irq_wakeups(host); - if (!ret) + if (!sdhci_enable_irq_wakeups(host)) dev_warn(dev, "Failed to enable irq wakeup\n"); } else { /* @@ -2095,12 +2094,12 @@ static int sdhci_esdhc_suspend(struct device *dev) * other function like GPIO function to save power in PM, * which finally block the SDIO wakeup function. */ - ret = pinctrl_pm_select_sleep_state(dev); - if (ret) - return ret; + if (pinctrl_pm_select_sleep_state(dev)) + dev_warn(dev, "Failed to select sleep pinctrl state\n"); } - ret = mmc_gpio_set_cd_wake(host->mmc, true); + if (mmc_gpio_set_cd_wake(host->mmc, true)) + dev_warn(dev, "Failed to enable cd wake\n"); /* * Make sure invoke runtime_suspend to gate off clock. @@ -2108,7 +2107,7 @@ static int sdhci_esdhc_suspend(struct device *dev) */ pm_runtime_force_suspend(dev); - return ret; + return 0; } static int sdhci_esdhc_resume(struct device *dev) From a419421281fb4b61ab4c277b7e97fd04d3be9b94 Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:18 +0800 Subject: [PATCH 2717/2758] mmc: sdhci-esdhc-imx: fix resume error handling commit e27c946b589c53520409a0956b33d52ef7a0898f upstream. Check pm_runtime_force_resume() return value in resume. If it fails (clock enable failure), return immediately since accessing hardware registers on an unclocked device would cause a kernel panic. The early return intentionally skips enable_irq() and sdhci_disable_irq_wakeups() because the IRQ handler reads SDHCI_INT_STATUS, which would also fault without clocks. The PM runtime usage counter leak only affects this already-broken device instance and is an acceptable tradeoff to preserve system stability. Remove the return value check for mmc_gpio_set_cd_wake(host->mmc, false) since disable_irq_wake() called internally always returns 0. Also return 0 explicitly on the success path instead of propagating stale return values. Fixes: 676a83855614 ("mmc: host: sdhci-esdhc-imx: refactor the system PM logic") Acked-by: Adrian Hunter Reviewed-by: Frank Li Signed-off-by: Luke Wang Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-esdhc-imx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 290a3172931bf..18f4905c15b9b 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -2123,12 +2123,12 @@ static int sdhci_esdhc_resume(struct device *dev) dev_warn(dev, "Failed to restore pinctrl state\n"); } - pm_runtime_force_resume(dev); - - ret = mmc_gpio_set_cd_wake(host->mmc, false); + ret = pm_runtime_force_resume(dev); if (ret) return ret; + mmc_gpio_set_cd_wake(host->mmc, false); + /* re-initialize hw state in case it's lost in low power mode */ sdhci_esdhc_imx_hwinit(host); @@ -2155,7 +2155,7 @@ static int sdhci_esdhc_resume(struct device *dev) pm_runtime_put_autosuspend(dev); - return ret; + return 0; } static int sdhci_esdhc_runtime_suspend(struct device *dev) From 314bd592085c0720ef519f6edbc5f41440ff78d4 Mon Sep 17 00:00:00 2001 From: Sun Jian Date: Tue, 14 Jul 2026 02:38:45 -0700 Subject: [PATCH 2718/2758] bpf: Reject negative const offsets for buffer pointers commit fd4cfa8c8f9a17cdec0539334d28754bc1d8a5d9 upstream. The verifier rejects variable offsets for PTR_TO_TP_BUFFER and PTR_TO_BUF accesses, but it currently accepts a constant negative offset produced by pointer arithmetic. Commit 022ac0750883 ("bpf: use reg->var_off instead of reg->off for pointers") moved constant pointer offsets from reg->off to reg->var_off. However, __check_buffer_access() continued to check only the instruction offset. An access with reg->var_off equal to -8 and an instruction offset of zero therefore passes verification. For writable raw tracepoints, the access end is also calculated from the unsigned reg->var_off.value. An eight-byte access starting at -8 wraps the calculated end to zero, allowing the program to load and attach without increasing max_tp_access. After ensuring that reg->var_off is constant, calculate the effective access start using signed arithmetic and reject it when it is negative. Use the validated start to calculate the access end for both PTR_TO_TP_BUFFER and PTR_TO_BUF. Fixes: 022ac0750883 ("bpf: use reg->var_off instead of reg->off for pointers") Signed-off-by: Sun Jian Acked-by: Shung-Hsi Yu Cc: stable@vger.kernel.org # 5.2.0 Link: https://patch.msgid.link/20260714093846.18159-2-sun.jian.kdev@gmail.com Signed-off-by: Eduard Zingerman Signed-off-by: Greg Kroah-Hartman --- kernel/bpf/verifier.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 52e405a64afe2..523f00e609e2b 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -5559,14 +5559,11 @@ static int check_max_stack_depth(struct bpf_verifier_env *env) static int __check_buffer_access(struct bpf_verifier_env *env, const char *buf_info, const struct bpf_reg_state *reg, - int regno, int off, int size) + int regno, int off, int size, + u32 *access_end) { - if (off < 0) { - verbose(env, - "R%d invalid %s buffer access: off=%d, size=%d\n", - regno, buf_info, off, size); - return -EACCES; - } + s64 start; + if (!tnum_is_const(reg->var_off)) { char tn_buf[48]; @@ -5577,6 +5574,15 @@ static int __check_buffer_access(struct bpf_verifier_env *env, return -EACCES; } + start = (s64)reg->var_off.value + off; + if (start < 0) { + verbose(env, + "R%d invalid negative %s buffer offset: off=%d, var_off=%lld\n", + regno, buf_info, off, (s64)reg->var_off.value); + return -EACCES; + } + + *access_end = start + size; return 0; } @@ -5584,14 +5590,14 @@ static int check_tp_buffer_access(struct bpf_verifier_env *env, const struct bpf_reg_state *reg, int regno, int off, int size) { + u32 access_end; int err; - err = __check_buffer_access(env, "tracepoint", reg, regno, off, size); + err = __check_buffer_access(env, "tracepoint", reg, regno, off, size, &access_end); if (err) return err; - env->prog->aux->max_tp_access = max(reg->var_off.value + off + size, - env->prog->aux->max_tp_access); + env->prog->aux->max_tp_access = max(access_end, env->prog->aux->max_tp_access); return 0; } @@ -5603,13 +5609,14 @@ static int check_buffer_access(struct bpf_verifier_env *env, u32 *max_access) { const char *buf_info = type_is_rdonly_mem(reg->type) ? "rdonly" : "rdwr"; + u32 access_end; int err; - err = __check_buffer_access(env, buf_info, reg, regno, off, size); + err = __check_buffer_access(env, buf_info, reg, regno, off, size, &access_end); if (err) return err; - *max_access = max(reg->var_off.value + off + size, *max_access); + *max_access = max(access_end, *max_access); return 0; } From 810c9ae71dadc5887c6b37308fabad5f6f3bd870 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Wed, 20 May 2026 08:03:10 -0700 Subject: [PATCH 2719/2758] mm/damon/core: trace esz at first setup commit 7e6cc35f5283eab81a14231a64ecd640b690c48c upstream. DAMON traces effective size quota from the second update, only if a change has been made by the update. Tracing only changed updates was an intentional decision to avoid unnecessary same value tracing. Always skipping the first value is just an unintended mistake. The mistake makes the tracepoint based investigation incomplete, because the first effective size quota is never traced. It is not a big issue when the 'consist' quota tuner is used, because it keeps changing the quota in the usual setup. However, when the 'temporal' tuner is used, the quota value is not changed before the goal achievement status is completely changed. For example, if the DAMOS scheme is started with an under-achieved goal, the quota is set to the maximum value, and kept the same value until the goal is achieved. Because DAMON skips the first value, the user cannot know what effective quota the current scheme is using. Only after the goal is achieved, the effective quota is changed to zero, and traced. Unconditionally trace the initial quota value to fix this problem. Note that the 'temporal' quota tuner was introduced by commit af738a6a00c1 ("mm/damon/core: introduce DAMOS_QUOTA_GOAL_TUNER_TEMPORAL"), which was added to 7.1-rc1. But even with the 'consist' quota tuner, the tracing is unintentionally incomplete. Hence this commit marks the introduction of the trace event as the broken commit. Link: https://lore.kernel.org/20260520150311.80925-1-sj@kernel.org Fixes: a86d695193bf ("mm/damon: add trace event for effective size quota") Signed-off-by: SeongJae Park Cc: # 6.17.x Signed-off-by: Andrew Morton Signed-off-by: SJ Park Signed-off-by: Greg Kroah-Hartman --- mm/damon/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mm/damon/core.c b/mm/damon/core.c index eeea7d5c45e23..60f5f191e17a0 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -2492,6 +2492,8 @@ static void damos_adjust_quota(struct damon_ctx *c, struct damos *s) if (!quota->total_charged_sz && !quota->charged_from) { quota->charged_from = jiffies; damos_set_effective_quota(quota, c); + if (trace_damos_esz_enabled()) + damos_trace_esz(c, s, quota); } /* New charge window starts */ From db20589d7b248211f63f4a7f642a49c0832b13ae Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Mon, 29 Jun 2026 07:44:31 -0700 Subject: [PATCH 2720/2758] samples/damon/mtier: fail early if address range parameters are invalid commit 7746d72c64054976887928d64d2caf25c5a6dcc0 upstream. The comment on top of `struct damon_region` clearly says that For any use case, @ar should be non-zero positive size. which is now verified in damon_verify_new_region() if the kernel is built with DAMON_DEBUG_SANITY. The WARN_ONCE() can be triggered if the mtier sample module is enabled before node{0,1}_{start,end}_addr have been properly initialized, which is obviously not good. ------------[ cut here ]------------ start 0 >= end 0 WARNING: mm/damon/core.c:217 at damon_new_region+0xf4/0x118, CPU#59: bash/341468 Call trace: damon_new_region+0xf4/0x118 (P) damon_set_regions+0xfc/0x3c0 damon_sample_mtier_build_ctx+0xe8/0x3a8 damon_sample_mtier_start+0x1c/0x90 damon_sample_mtier_enable_store+0x98/0xb0 param_attr_store+0xb4/0x128 module_attr_store+0x2c/0x50 sysfs_kf_write+0x58/0x90 kernfs_fop_write_iter+0x16c/0x238 vfs_write+0x2c0/0x370 ksys_write+0x74/0x118 __arm64_sys_write+0x24/0x38 invoke_syscall+0xa8/0x118 el0_svc_common.constprop.0+0x48/0xf0 do_el0_svc+0x24/0x38 el0_svc+0x54/0x370 el0t_64_sync_handler+0xa0/0xe8 el0t_64_sync+0x1ac/0x1b0 ---[ end trace 0000000000000000 ]--- Note that the same issue can happen if detect_node_addresses is true, and node 0 or 1 is memoryless. Fix it together by checking the validity of parameters right before damon_new_region() and fail early if they're invalid. Link: https://lore.kernel.org/20260629144432.133962-1-sj@kernel.org Fixes: 82a08bde3cf7 ("samples/damon: implement a DAMON module for memory tiering") Signed-off-by: Zenghui Yu Signed-off-by: SJ Park Reviewed-by: SJ Park Cc: # 6.16.x Signed-off-by: Andrew Morton Signed-off-by: SJ Park Signed-off-by: Greg Kroah-Hartman --- samples/damon/mtier.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/samples/damon/mtier.c b/samples/damon/mtier.c index 775838a23d935..7f84367ad3e58 100644 --- a/samples/damon/mtier.c +++ b/samples/damon/mtier.c @@ -120,6 +120,9 @@ static struct damon_ctx *damon_sample_mtier_build_ctx(bool promote) addr.end = promote ? node1_end_addr : node0_end_addr; } + if (addr.start >= addr.end) + goto free_out; + region = damon_new_region(addr.start, addr.end); if (!region) goto free_out; From c81e2af41de6a159837c7129a4fc444ac6e48046 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 2 Jul 2026 10:29:56 -0400 Subject: [PATCH 2721/2758] device property: initialize the remaining fields of fwnode_handle in fwnode_init() [ Upstream commit 7eba000621fff223dd7bab484d48918c7c77a307 ] If a firmware node is allocated on the stack (for instance: temporary software node whose life-time we control) or on the heap - but using a non-zeroing allocation function - and initialized using fwnode_init(), its secondary pointer will contain uninitialized memory which likely will be neither NULL nor IS_ERR() and so may end up being dereferenced (for example: in dev_to_swnode()). Set fwnode->secondary to NULL on initialization. While at it: initialize the remaining fields of struct fwnode_handle too just to be sure. Cc: stable@vger.kernel.org Fixes: 01bb86b380a3 ("driver core: Add fwnode_init()") Reviewed-by: Sakari Ailus Reviewed-by: Rafael J. Wysocki (Intel) Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260511074927.9473-1-bartosz.golaszewski@oss.qualcomm.com [ Fix typo in commit message. - Danilo ] Signed-off-by: Danilo Krummrich Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/linux/fwnode.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index 5e9cef1e4d448..4e86e6990d284 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -210,8 +210,10 @@ static inline void fwnode_init(struct fwnode_handle *fwnode, { fwnode->secondary = NULL; fwnode->ops = ops; + fwnode->dev = NULL; INIT_LIST_HEAD(&fwnode->consumers); INIT_LIST_HEAD(&fwnode->suppliers); + fwnode->flags = 0; } static inline void fwnode_set_flag(struct fwnode_handle *fwnode, From cf8b5937b7b258927ccca267995e13e76a07b38e Mon Sep 17 00:00:00 2001 From: Ruipeng Qi Date: Fri, 3 Jul 2026 10:26:29 -0400 Subject: [PATCH 2722/2758] f2fs: fix potential deadlock in f2fs_balance_fs() [ Upstream commit dd3114870771562036fdcf5abe813956f36d224d ] When the f2fs filesystem space is nearly exhausted, we encounter deadlock issues as below: INFO: task A:1890 blocked for more than 120 seconds. Tainted: G O 6.12.41-g3fe07ddf05ab #1 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. task:A state:D stack:0 pid:1890 tgid:1626 ppid:1153 flags:0x00000204 Call trace: __switch_to+0xf4/0x158 __schedule+0x27c/0x908 schedule+0x3c/0x118 io_schedule+0x44/0x68 folio_wait_bit_common+0x174/0x370 folio_wait_bit+0x20/0x38 folio_wait_writeback+0x54/0xc8 truncate_inode_partial_folio+0x70/0x1e0 truncate_inode_pages_range+0x1b0/0x450 truncate_pagecache+0x54/0x88 f2fs_file_write_iter+0x3e8/0xb80 do_iter_readv_writev+0xf0/0x1e0 vfs_writev+0x138/0x2c8 do_writev+0x88/0x130 __arm64_sys_writev+0x28/0x40 invoke_syscall+0x50/0x120 el0_svc_common.constprop.0+0xc8/0xf0 do_el0_svc+0x24/0x38 el0_svc+0x30/0xf8 el0t_64_sync_handler+0x120/0x130 el0t_64_sync+0x190/0x198 INFO: task kworker/u8:11:2680853 blocked for more than 120 seconds. Tainted: G O 6.12.41-g3fe07ddf05ab #1 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. task:kworker/u8:11 state:D stack:0 pid:2680853 tgid:2680853 ppid:2 flags:0x00000208 Workqueue: writeback wb_workfn (flush-254:0) Call trace: __switch_to+0xf4/0x158 __schedule+0x27c/0x908 schedule+0x3c/0x118 io_schedule+0x44/0x68 folio_wait_bit_common+0x174/0x370 __filemap_get_folio+0x214/0x348 pagecache_get_page+0x20/0x70 f2fs_get_read_data_page+0x150/0x3e8 f2fs_get_lock_data_page+0x2c/0x160 move_data_page+0x50/0x478 do_garbage_collect+0xd38/0x1528 f2fs_gc+0x240/0x7e0 f2fs_balance_fs+0x1a0/0x208 f2fs_write_single_data_page+0x6e4/0x730 f2fs_write_cache_pages+0x378/0x9b0 f2fs_write_data_pages+0x2e4/0x388 do_writepages+0x8c/0x2c8 __writeback_single_inode+0x4c/0x498 writeback_sb_inodes+0x234/0x4a8 __writeback_inodes_wb+0x58/0x118 wb_writeback+0x2f8/0x3c0 wb_workfn+0x2c4/0x508 process_one_work+0x180/0x408 worker_thread+0x258/0x368 kthread+0x118/0x128 ret_from_fork+0x10/0x200 INFO: task kworker/u8:8:2641297 blocked for more than 120 seconds. Tainted: G O 6.12.41-g3fe07ddf05ab #1 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. task:kworker/u8:8 state:D stack:0 pid:2641297 tgid:2641297 ppid:2 flags:0x00000208 Workqueue: writeback wb_workfn (flush-254:0) Call trace: __switch_to+0xf4/0x158 __schedule+0x27c/0x908 rt_mutex_schedule+0x30/0x60 __rt_mutex_slowlock_locked.constprop.0+0x460/0x8a8 rwbase_write_lock+0x24c/0x378 down_write+0x1c/0x30 f2fs_balance_fs+0x184/0x208 f2fs_write_inode+0xf4/0x328 __writeback_single_inode+0x370/0x498 writeback_sb_inodes+0x234/0x4a8 __writeback_inodes_wb+0x58/0x118 wb_writeback+0x2f8/0x3c0 wb_workfn+0x2c4/0x508 process_one_work+0x180/0x408 worker_thread+0x258/0x368 kthread+0x118/0x128 ret_from_fork+0x10/0x20 INFO: task B:1902 blocked for more than 120 seconds. Tainted: G O 6.12.41-g3fe07ddf05ab #1 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. task:B state:D stack:0 pid:1902 tgid:1626 ppid:1153 flags:0x0000020c Call trace: __switch_to+0xf4/0x158 __schedule+0x27c/0x908 rt_mutex_schedule+0x30/0x60 __rt_mutex_slowlock_locked.constprop.0+0x460/0x8a8 rwbase_write_lock+0x24c/0x378 down_write+0x1c/0x30 f2fs_balance_fs+0x184/0x208 f2fs_map_blocks+0x94c/0x1110 f2fs_file_write_iter+0x228/0xb80 do_iter_readv_writev+0xf0/0x1e0 vfs_writev+0x138/0x2c8 do_writev+0x88/0x130 __arm64_sys_writev+0x28/0x40 invoke_syscall+0x50/0x120 el0_svc_common.constprop.0+0xc8/0xf0 do_el0_svc+0x24/0x38 el0_svc+0x30/0xf8 el0t_64_sync_handler+0x120/0x130 el0t_64_sync+0x190/0x198 INFO: task sync:2769849 blocked for more than 120 seconds. Tainted: G O 6.12.41-g3fe07ddf05ab #1 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. task:sync state:D stack:0 pid:2769849 tgid:2769849 ppid:736 flags:0x0000020c Call trace: __switch_to+0xf4/0x158 __schedule+0x27c/0x908 schedule+0x3c/0x118 wb_wait_for_completion+0xb0/0xe8 sync_inodes_sb+0xc8/0x2b0 sync_inodes_one_sb+0x24/0x38 iterate_supers+0xa8/0x138 ksys_sync+0x54/0xc8 __arm64_sys_sync+0x18/0x30 invoke_syscall+0x50/0x120 el0_svc_common.constprop.0+0xc8/0xf0 do_el0_svc+0x24/0x38 el0_svc+0x30/0xf8 el0t_64_sync_handler+0x120/0x130 el0t_64_sync+0x190/0x198 The root cause is a potential deadlock between the following tasks: kworker/u8:11 Thread A - f2fs_write_single_data_page - f2fs_do_write_data_page - folio_start_writeback(X) - f2fs_outplace_write_data - bio_add_folio(X) - folio_unlock(X) - truncate_inode_pages_range - __filemap_get_folio(X, FGP_LOCK) - truncate_inode_partial_folio(X) - folio_wait_writeback(X) - f2fs_balance_fs - f2fs_gc - do_garbage_collect - move_data_page - f2fs_get_lock_data_page - __filemap_get_folio(X, FGP_LOCK) Both threads try to access folio X. Thread A holds the lock but waits for writeback, while kworker waits for the lock. This causes a deadlock. Other threads also enter D state, waiting for locks such as gc_lock and writepages. OPU/IPU DATA folio are all affected by this issue. To avoid such potential deadlocks, always commit these cached folios before triggering f2fs_gc() in f2fs_balance_fs(). Suggested-by: Chao Yu Reviewed-by: Chao Yu Signed-off-by: Ruipeng Qi Signed-off-by: Jaegeuk Kim Stable-dep-of: 8b4468ec023d ("f2fs: fix potential deadlock in gc_merge path of f2fs_balance_fs()") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/data.c | 29 +++++++++++++++++++++++++++++ fs/f2fs/f2fs.h | 1 + fs/f2fs/segment.c | 8 ++++++++ 3 files changed, 38 insertions(+) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index e24b74859427c..b5825726743f5 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -943,6 +943,35 @@ void f2fs_submit_merged_ipu_write(struct f2fs_sb_info *sbi, } } +void f2fs_submit_all_merged_ipu_writes(struct f2fs_sb_info *sbi) +{ + struct bio_entry *be, *tmp; + struct f2fs_bio_info *io; + enum temp_type temp; + + for (temp = HOT; temp < NR_TEMP_TYPE; temp++) { + LIST_HEAD(list); + + io = sbi->write_io[DATA] + temp; + + /* A lockless list_empty() check is safe here: any bios from + * other kworkers that we miss will be submitted by those + * kworkers accordingly. + */ + if (list_empty(&io->bio_list)) + continue; + + f2fs_down_write(&io->bio_list_lock); + list_splice_init(&io->bio_list, &list); + f2fs_up_write(&io->bio_list_lock); + + list_for_each_entry_safe(be, tmp, &list, list) { + f2fs_submit_write_bio(sbi, be->bio, DATA); + del_bio_entry(be); + } + } +} + int f2fs_merge_page_bio(struct f2fs_io_info *fio) { struct bio *bio = *fio->bio; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 069595fc5e1a0..935dd37430323 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -4154,6 +4154,7 @@ void f2fs_submit_merged_write_folio(struct f2fs_sb_info *sbi, struct folio *folio, enum page_type type); void f2fs_submit_merged_ipu_write(struct f2fs_sb_info *sbi, struct bio **bio, struct folio *folio); +void f2fs_submit_all_merged_ipu_writes(struct f2fs_sb_info *sbi); void f2fs_flush_merged_writes(struct f2fs_sb_info *sbi); int f2fs_submit_page_bio(struct f2fs_io_info *fio); int f2fs_merge_page_bio(struct f2fs_io_info *fio); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 788f8b0502492..22a9a7c85ee9a 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -462,6 +462,14 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need) .should_migrate_blocks = false, .err_gc_skipped = false, .nr_free_secs = 1 }; + + /* + * Submit all cached OPU/IPU DATA bios before triggering + * foreground GC to avoid potential deadlocks. + */ + f2fs_submit_merged_write(sbi, DATA); + f2fs_submit_all_merged_ipu_writes(sbi); + f2fs_down_write_trace(&sbi->gc_lock, &gc_control.lc); stat_inc_gc_call_count(sbi, FOREGROUND); f2fs_gc(sbi, &gc_control); From aa807064473abd6f2cafe419fb77ea402d3e3104 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 3 Jul 2026 10:26:30 -0400 Subject: [PATCH 2723/2758] f2fs: fix potential deadlock in gc_merge path of f2fs_balance_fs() [ Upstream commit 8b4468ec023d0d1b4669dfb867588997cc03a06b ] When we mount device w/ gc_merge mount option, we may suffer below potential deadlock: Kworker GC trehad Truncator - f2fs_write_cache_pages - f2fs_write_single_data_page - f2fs_do_write_data_page - folio_start_writeback --- set writeback flag on folio - f2fs_outplace_write_data : cached folio in internal bio cache - f2fs_balance_fs - wake_up(gc_thread) : wake up gc thread to run foreground GC - finish_wait(fggc_wq) : wait on the waitqueue --- wait on GC thread to finish the work - truncate_inode_pages_range - __filemap_get_folio(, FGP_LOCK) --- lock folio - truncate_inode_partial_folio - folio_wait_writeback --- wait on writeback being cleared - do_garbage_collect - move_data_page - f2fs_get_lock_data_folio - lock on folio --- blocked on folio's lock In order to avoid such deadlock, let's call below functions to commit cached bios in GC_MERGE path of f2fs_balance_fs() as the same as we did in NOGC_MERGE path. - f2fs_submit_merged_write(sbi, DATA); - f2fs_submit_all_merged_ipu_writes(sbi); Cc: stable@kernel.org Fixes: 351df4b20115 ("f2fs: add segment operations") Cc: Ruipeng Qi Reported: Sandeep Dhavale Signed-off-by: Chao Yu Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/segment.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 22a9a7c85ee9a..c0276a3018560 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -444,6 +444,13 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need) if (has_enough_free_secs(sbi, 0, 0)) return; + /* + * Submit all cached OPU/IPU DATA bios before triggering + * foreground GC to avoid potential deadlocks. + */ + f2fs_submit_merged_write(sbi, DATA); + f2fs_submit_all_merged_ipu_writes(sbi); + if (test_opt(sbi, GC_MERGE) && sbi->gc_thread && sbi->gc_thread->f2fs_gc_task) { DEFINE_WAIT(wait); @@ -463,13 +470,6 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need) .err_gc_skipped = false, .nr_free_secs = 1 }; - /* - * Submit all cached OPU/IPU DATA bios before triggering - * foreground GC to avoid potential deadlocks. - */ - f2fs_submit_merged_write(sbi, DATA); - f2fs_submit_all_merged_ipu_writes(sbi); - f2fs_down_write_trace(&sbi->gc_lock, &gc_control.lc); stat_inc_gc_call_count(sbi, FOREGROUND); f2fs_gc(sbi, &gc_control); From 3c0dbfecd859fd02fe9008f33a83146102ccd9ba Mon Sep 17 00:00:00 2001 From: Keshav Verma Date: Fri, 3 Jul 2026 14:24:22 -0400 Subject: [PATCH 2724/2758] f2fs: fix listxattr handling of corrupted xattr entries [ Upstream commit 5ef5bc304f23c3fe255d4936472378dcb74d0e94 ] Validate the xattr entry before reading its fields in f2fs_listxattr(). Return -EFSCORRUPTED when the entry is outside the valid xattr storage area instead of returning a successful partial result. Fixes: 688078e7f36c ("f2fs: fix to avoid memory leakage in f2fs_listxattr") Cc: stable@kernel.org Reviewed-by: Chao Yu Signed-off-by: Keshav Verma Signed-off-by: Jaegeuk Kim Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/xattr.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 610d5810074dc..1b8952b3a4478 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -581,8 +581,6 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) size_t prefix_len; size_t size; - prefix = f2fs_xattr_prefix(entry->e_name_index, dentry); - if ((void *)(entry) + sizeof(__u32) > last_base_addr || (void *)XATTR_NEXT_ENTRY(entry) > last_base_addr) { f2fs_err(F2FS_I_SB(inode), "list inode (%llu) has corrupted xattr", @@ -590,9 +588,11 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) set_sbi_flag(F2FS_I_SB(inode), SBI_NEED_FSCK); f2fs_handle_error(F2FS_I_SB(inode), ERROR_CORRUPTED_XATTR); - break; + error = -EFSCORRUPTED; + goto cleanup; } + prefix = f2fs_xattr_prefix(entry->e_name_index, dentry); if (!prefix) continue; From b80d602496863df573863165e8d6c92ce2f8279a Mon Sep 17 00:00:00 2001 From: Zhang Lixu Date: Mon, 13 Jul 2026 13:53:24 -0400 Subject: [PATCH 2725/2758] iio: hid-sensor-rotation: Fix stale or zero output when reading raw values [ Upstream commit 3ce8d099e0afc5a7da75a2007a67f67c4f5a4af1 ] When reading the raw quaternion attribute (in_rot_quaternion_raw), the driver currently returns either all zeros (if the sensor was never enabled) or stale data (if the sensor was previously enabled) because it reads from the internal buffer without explicitly requesting a new sample from the sensor. To fix this, power up the sensor, call sensor_hub_input_attr_read_values() to issue a synchronous GET_REPORT and receive the full quaternion data directly into a local buffer, then decode the four components. Fixes: fc18dddc0625 ("iio: hid-sensors: Added device rotation support") Signed-off-by: Zhang Lixu Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/iio/orientation/hid-sensor-rotation.c | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c index 5a5e6e4fbe34b..83a0b0283605b 100644 --- a/drivers/iio/orientation/hid-sensor-rotation.c +++ b/drivers/iio/orientation/hid-sensor-rotation.c @@ -74,6 +74,13 @@ static int dev_rot_read_raw(struct iio_dev *indio_dev, long mask) { struct dev_rot_state *rot_state = iio_priv(indio_dev); + struct hid_sensor_hub_device *hsdev = rot_state->common_attributes.hsdev; + struct hid_sensor_hub_attribute_info *info = &rot_state->quaternion; + u32 usage_id = HID_USAGE_SENSOR_ORIENT_QUATERNION; + union { + s16 val16[4]; + s32 val32[4]; + } raw_buf; int ret_type; int i; @@ -83,8 +90,37 @@ static int dev_rot_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: if (size >= 4) { - for (i = 0; i < 4; ++i) - vals[i] = rot_state->scan.sampled_vals[i]; + if (info->size <= 0 || info->size > sizeof(raw_buf)) + return -EINVAL; + + hid_sensor_power_state(&rot_state->common_attributes, true); + + ret_type = sensor_hub_input_attr_read_values(hsdev, + hsdev->usage, + usage_id, + info->report_id, + SENSOR_HUB_SYNC, + info->size, + (u8 *)&raw_buf); + + hid_sensor_power_state(&rot_state->common_attributes, false); + + if (ret_type < 0) + return ret_type; + + switch (info->size) { + case sizeof(raw_buf.val16): + for (i = 0; i < ARRAY_SIZE(raw_buf.val16); i++) + vals[i] = raw_buf.val16[i]; + break; + case sizeof(raw_buf.val32): + for (i = 0; i < ARRAY_SIZE(raw_buf.val32); i++) + vals[i] = raw_buf.val32[i]; + break; + default: + return -EINVAL; + } + ret_type = IIO_VAL_INT_MULTIPLE; *val_len = 4; } else From 53e3dcfa74b350062742b46a71c3524bf0864c68 Mon Sep 17 00:00:00 2001 From: "Geoffrey D. Bennett" Date: Mon, 13 Jul 2026 21:16:22 -0400 Subject: [PATCH 2726/2758] ALSA: scarlett2: Allow selecting config_set by firmware version [ Upstream commit 732a6397a526c025cd29c3c9309b0db6a2c08837 ] The Scarlett 2i2 Gen 4 firmware 2417 moved the direct monitor gain parameters, so we now need to allow each device to list multiple scarlett2_config_set entries, one per applicable firmware version range, and pick the matching one at probe time. No functional change yet: each device gets a single config_sets entry whose from_firmware_version matches the existing min_firmware_version (0 where none was set). This both prepares for selection and lets a follow-up commit remove the now-redundant min_firmware_version field. scarlett2_count_io() depends on the resolved config_set so it moves out of scarlett2_init_private() into snd_scarlett2_controls_create() after the firmware version has been read. Signed-off-by: Geoffrey D. Bennett Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/ae1695b4c4825f365b4c86b22174035f742807e3.1777151532.git.g@b4.vu Stable-dep-of: 3ca15754b561 ("ALSA: scarlett2: Update offsets for 2i2 Gen 4 firmware 2417") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- sound/usb/mixer_scarlett2.c | 143 ++++++++++++++++++++++++++++++------ 1 file changed, 121 insertions(+), 22 deletions(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index a4fac46522013..4be667ed0c5b0 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -612,6 +612,20 @@ struct scarlett2_config_set { const struct scarlett2_config items[SCARLETT2_CONFIG_COUNT]; }; +/* Map firmware versions to config sets per-device. + * + * Each device lists one or more entries, sorted in ascending order of + * from_firmware_version. At probe time the running firmware version + * is looked up against this list and the last entry whose + * from_firmware_version is <= the running version is selected. + * + * The list is terminated by a sentinel entry with config_set == NULL. + */ +struct scarlett2_config_set_entry { + u16 from_firmware_version; + const struct scarlett2_config_set *config_set; +}; + /* Input gain TLV dB ranges */ static const DECLARE_TLV_DB_MINMAX( @@ -1100,8 +1114,8 @@ struct scarlett2_meter_entry { }; struct scarlett2_device_info { - /* which set of configuration parameters the device uses */ - const struct scarlett2_config_set *config_set; + /* which sets of configuration parameters the device uses */ + const struct scarlett2_config_set_entry *config_sets; /* minimum firmware version required */ u16 min_firmware_version; @@ -1343,7 +1357,10 @@ struct scarlett2_data { /*** Model-specific data ***/ static const struct scarlett2_device_info s6i6_gen2_info = { - .config_set = &scarlett2_config_set_gen2a, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_gen2a }, + { } + }, .level_input_count = 2, .pad_input_count = 2, @@ -1393,7 +1410,10 @@ static const struct scarlett2_device_info s6i6_gen2_info = { }; static const struct scarlett2_device_info s18i8_gen2_info = { - .config_set = &scarlett2_config_set_gen2a, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_gen2a }, + { } + }, .level_input_count = 2, .pad_input_count = 4, @@ -1446,7 +1466,10 @@ static const struct scarlett2_device_info s18i8_gen2_info = { }; static const struct scarlett2_device_info s18i20_gen2_info = { - .config_set = &scarlett2_config_set_gen2b, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_gen2b }, + { } + }, .line_out_descrs = { "Monitor L", @@ -1503,7 +1526,10 @@ static const struct scarlett2_device_info s18i20_gen2_info = { }; static const struct scarlett2_device_info solo_gen3_info = { - .config_set = &scarlett2_config_set_gen3a, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_gen3a }, + { } + }, .level_input_count = 1, .level_input_first = 1, .air_input_count = 1, @@ -1513,7 +1539,10 @@ static const struct scarlett2_device_info solo_gen3_info = { }; static const struct scarlett2_device_info s2i2_gen3_info = { - .config_set = &scarlett2_config_set_gen3a, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_gen3a }, + { } + }, .level_input_count = 2, .air_input_count = 2, .phantom_count = 1, @@ -1522,7 +1551,10 @@ static const struct scarlett2_device_info s2i2_gen3_info = { }; static const struct scarlett2_device_info s4i4_gen3_info = { - .config_set = &scarlett2_config_set_gen3b, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_gen3b }, + { } + }, .level_input_count = 2, .pad_input_count = 2, .air_input_count = 2, @@ -1571,7 +1603,10 @@ static const struct scarlett2_device_info s4i4_gen3_info = { }; static const struct scarlett2_device_info s8i6_gen3_info = { - .config_set = &scarlett2_config_set_gen3b, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_gen3b }, + { } + }, .level_input_count = 2, .pad_input_count = 2, .air_input_count = 2, @@ -1637,7 +1672,10 @@ static const char * const scarlett2_spdif_s18i8_gen3_texts[] = { }; static const struct scarlett2_device_info s18i8_gen3_info = { - .config_set = &scarlett2_config_set_gen3c, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_gen3c }, + { } + }, .has_speaker_switching = 1, .level_input_count = 2, .pad_input_count = 4, @@ -1729,7 +1767,10 @@ static const char * const scarlett2_spdif_s18i20_gen3_texts[] = { }; static const struct scarlett2_device_info s18i20_gen3_info = { - .config_set = &scarlett2_config_set_gen3c, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_gen3c }, + { } + }, .has_speaker_switching = 1, .has_talkback = 1, .level_input_count = 2, @@ -1803,7 +1844,10 @@ static const struct scarlett2_device_info s18i20_gen3_info = { }; static const struct scarlett2_device_info vocaster_one_info = { - .config_set = &scarlett2_config_set_vocaster, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 1769, &scarlett2_config_set_vocaster }, + { } + }, .min_firmware_version = 1769, .has_devmap = 1, @@ -1847,7 +1891,10 @@ static const struct scarlett2_device_info vocaster_one_info = { }; static const struct scarlett2_device_info vocaster_two_info = { - .config_set = &scarlett2_config_set_vocaster, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 1769, &scarlett2_config_set_vocaster }, + { } + }, .min_firmware_version = 1769, .has_devmap = 1, @@ -1892,7 +1939,10 @@ static const struct scarlett2_device_info vocaster_two_info = { }; static const struct scarlett2_device_info solo_gen4_info = { - .config_set = &scarlett2_config_set_gen4_solo, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 2115, &scarlett2_config_set_gen4_solo }, + { } + }, .min_firmware_version = 2115, .has_devmap = 1, @@ -1947,7 +1997,10 @@ static const struct scarlett2_device_info solo_gen4_info = { }; static const struct scarlett2_device_info s2i2_gen4_info = { - .config_set = &scarlett2_config_set_gen4_2i2, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 2115, &scarlett2_config_set_gen4_2i2 }, + { } + }, .min_firmware_version = 2115, .has_devmap = 1, @@ -2002,7 +2055,10 @@ static const struct scarlett2_device_info s2i2_gen4_info = { }; static const struct scarlett2_device_info s4i4_gen4_info = { - .config_set = &scarlett2_config_set_gen4_4i4, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 2089, &scarlett2_config_set_gen4_4i4 }, + { } + }, .min_firmware_version = 2089, .has_devmap = 1, @@ -2051,7 +2107,10 @@ static const struct scarlett2_device_info s4i4_gen4_info = { }; static const struct scarlett2_device_info clarett_2pre_info = { - .config_set = &scarlett2_config_set_clarett, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_clarett }, + { } + }, .level_input_count = 2, .air_input_count = 2, @@ -2107,7 +2166,10 @@ static const char * const scarlett2_spdif_clarett_texts[] = { }; static const struct scarlett2_device_info clarett_4pre_info = { - .config_set = &scarlett2_config_set_clarett, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_clarett }, + { } + }, .level_input_count = 2, .air_input_count = 4, @@ -2163,7 +2225,10 @@ static const struct scarlett2_device_info clarett_4pre_info = { }; static const struct scarlett2_device_info clarett_8pre_info = { - .config_set = &scarlett2_config_set_clarett, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_clarett }, + { } + }, .level_input_count = 2, .air_input_count = 8, @@ -8211,10 +8276,32 @@ static void scarlett2_private_suspend(struct usb_mixer_interface *mixer) /*** Initialisation ***/ +/* Select the config_set matching the running firmware version. + * + * The device info's config_sets array is ordered by ascending + * from_firmware_version; pick the last entry whose version is <= the + * running firmware version. If the running firmware is older than the + * first entry's from_firmware_version (i.e. older than the driver's + * minimum supported version for this device), the first entry's + * config_set is selected anyway so firmware updates can still be done + * (requires only the ACK handler), but the usual mixer controls + * aren't created. + */ +static void scarlett2_resolve_config_set(struct scarlett2_data *private) +{ + const struct scarlett2_config_set_entry *entry = + private->info->config_sets; + + private->config_set = entry->config_set; + for (entry++; entry->config_set; entry++) + if (entry->from_firmware_version <= private->firmware_version) + private->config_set = entry->config_set; +} + static void scarlett2_count_io(struct scarlett2_data *private) { const struct scarlett2_device_info *info = private->info; - const struct scarlett2_config_set *config_set = info->config_set; + const struct scarlett2_config_set *config_set = private->config_set; const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; int port_type, srcs = 0, dsts = 0, i; @@ -8311,9 +8398,14 @@ static int scarlett2_init_private(struct usb_mixer_interface *mixer, mixer->private_suspend = scarlett2_private_suspend; private->info = entry->info; - private->config_set = entry->info->config_set; + + /* Set config_set to the first entry's config_set so the + * notify handler has a valid pointer while USB init runs; it + * is re-resolved once the firmware version has been read. + */ + private->config_set = entry->info->config_sets[0].config_set; + private->series_name = entry->series_name; - scarlett2_count_io(private); private->scarlett2_seq = 0; private->mixer = mixer; @@ -8717,6 +8809,13 @@ static int snd_scarlett2_controls_create( if (err < 0) return err; + /* Now that the firmware version is known, pick the matching + * config_set + */ + scarlett2_resolve_config_set(private); + + scarlett2_count_io(private); + /* Get the upgrade & settings flash segment numbers */ err = scarlett2_get_flash_segment_nums(mixer); if (err < 0) From ac1328962db1b6297ce880e8d98cefcd005fbba4 Mon Sep 17 00:00:00 2001 From: "Geoffrey D. Bennett" Date: Mon, 13 Jul 2026 21:16:23 -0400 Subject: [PATCH 2727/2758] ALSA: scarlett2: Update offsets for 2i2 Gen 4 firmware 2417 [ Upstream commit 3ca15754b561483aa7a1bce51677d6389f8ff5bb ] Firmware 2417 for the Scarlett 4th Gen 2i2 moved the direct monitor gain parameters, so add a second config_set with the shifted offset and select it for firmware versions >= 2417. Fixes: 4e809a299677 ("ALSA: scarlett2: Add support for Solo, 2i2, and 4i4 Gen 4") Cc: stable@vger.kernel.org # ALSA: scarlett2: Allow selecting config_set by firmware version Cc: stable@vger.kernel.org # ALSA: scarlett2: Fold min_firmware_version into config_sets Cc: stable@vger.kernel.org Signed-off-by: Geoffrey D. Bennett Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/ad0fc5a131e76eb656a24e0e198382f7134068fe.1777151532.git.g@b4.vu Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- sound/usb/mixer_scarlett2.c | 58 +++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 4be667ed0c5b0..d7b8c204110c3 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -937,6 +937,63 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2 = { } }; +/* 2i2 Gen 4, firmware version 2417 and above + * + * Firmware 2417 shifted DIRECT_MONITOR_GAIN by 4 bytes; all other + * offsets are unchanged from scarlett2_config_set_gen4_2i2. + */ +static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2_2417 = { + .notifications = scarlett4_2i2_notifications, + .param_buf_addr = 0xfc, + .input_gain_tlv = db_scale_gen4_gain, + .autogain_status_texts = scarlett2_autogain_status_gen4, + .items = { + [SCARLETT2_CONFIG_MSD_SWITCH] = { + .offset = 0x49, .size = 8, .activate = 4 }, + + [SCARLETT2_CONFIG_DIRECT_MONITOR] = { + .offset = 0x14a, .size = 8, .activate = 16, .pbuf = 1 }, + + [SCARLETT2_CONFIG_AUTOGAIN_SWITCH] = { + .offset = 0x135, .size = 8, .activate = 10, .pbuf = 1 }, + + [SCARLETT2_CONFIG_AUTOGAIN_STATUS] = { + .offset = 0x137, .size = 8 }, + + [SCARLETT2_CONFIG_AG_MEAN_TARGET] = { + .offset = 0x131, .size = 8, .activate = 29, .pbuf = 1 }, + + [SCARLETT2_CONFIG_AG_PEAK_TARGET] = { + .offset = 0x132, .size = 8, .activate = 30, .pbuf = 1 }, + + [SCARLETT2_CONFIG_PHANTOM_SWITCH] = { + .offset = 0x48, .size = 8, .activate = 11, .pbuf = 1, + .mute = 1 }, + + [SCARLETT2_CONFIG_INPUT_GAIN] = { + .offset = 0x4b, .size = 8, .activate = 12, .pbuf = 1 }, + + [SCARLETT2_CONFIG_LEVEL_SWITCH] = { + .offset = 0x3c, .size = 8, .activate = 13, .pbuf = 1, + .mute = 1 }, + + [SCARLETT2_CONFIG_SAFE_SWITCH] = { + .offset = 0x147, .size = 8, .activate = 14, .pbuf = 1 }, + + [SCARLETT2_CONFIG_AIR_SWITCH] = { + .offset = 0x3e, .size = 8, .activate = 15, .pbuf = 1 }, + + [SCARLETT2_CONFIG_INPUT_SELECT_SWITCH] = { + .offset = 0x14b, .size = 8, .activate = 17, .pbuf = 1 }, + + [SCARLETT2_CONFIG_INPUT_LINK_SWITCH] = { + .offset = 0x14e, .size = 8, .activate = 18, .pbuf = 1 }, + + [SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN] = { + .offset = 0x2a4, .size = 16, .activate = 36 } + } +}; + /* 4i4 Gen 4 */ static const struct scarlett2_config_set scarlett2_config_set_gen4_4i4 = { .notifications = scarlett4_4i4_notifications, @@ -1999,6 +2056,7 @@ static const struct scarlett2_device_info solo_gen4_info = { static const struct scarlett2_device_info s2i2_gen4_info = { .config_sets = (const struct scarlett2_config_set_entry[]) { { 2115, &scarlett2_config_set_gen4_2i2 }, + { 2417, &scarlett2_config_set_gen4_2i2_2417 }, { } }, .min_firmware_version = 2115, From cd992747d717c2152fef77ec6256b8a1d8b75d68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Mon, 13 Jul 2026 23:32:55 -0400 Subject: [PATCH 2728/2758] firmware_loader: Add cancel helper for async requests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit b9bdd68b8b979c7e9de58b2e7d21e1d7d932c755 ] request_firmware_nowait() keeps the callback module pinned and holds a device reference until the firmware work completes. Callers still have no way to cancel or synchronize the queued callback before tearing down their driver-private state. Track scheduled async firmware work in an internal list and add request_firmware_nowait_cancel(). The helper cancels work matching the device, callback context and callback function. It cancels work that has not started yet and waits for an already-running callback to return. If the request has already completed, it is a no-op. Keep the existing request_firmware_nowait() lifetime model manual. A devres-managed variant can be layered on top separately if needed. Signed-off-by: Cássio Gabriel Reviewed-by: Takashi Iwai Acked-by: Danilo Krummrich Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260505-alsa-hda-tas2781-fw-callback-teardown-v4-1-e7c4bf930dc8@gmail.com Stable-dep-of: 5367e2ad14f0 ("ALSA: hda/tas2781: Cancel async firmware request at unbind") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_loader/main.c | 68 +++++++++++++++++++++++++++-- include/linux/firmware.h | 10 +++++ 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c index c96312ac2be7b..24213a0ea8317 100644 --- a/drivers/base/firmware_loader/main.c +++ b/drivers/base/firmware_loader/main.c @@ -1132,6 +1132,7 @@ EXPORT_SYMBOL(release_firmware); /* Async support */ struct firmware_work { struct work_struct work; + struct list_head list; struct module *module; const char *name; struct device *device; @@ -1140,6 +1141,17 @@ struct firmware_work { u32 opt_flags; }; +static LIST_HEAD(firmware_work_list); +static DEFINE_SPINLOCK(firmware_work_lock); + +static void firmware_work_free(struct firmware_work *fw_work) +{ + put_device(fw_work->device); /* taken in request_firmware_nowait() */ + module_put(fw_work->module); + kfree_const(fw_work->name); + kfree(fw_work); +} + static void request_firmware_work_func(struct work_struct *work) { struct firmware_work *fw_work; @@ -1150,11 +1162,15 @@ static void request_firmware_work_func(struct work_struct *work) _request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0, 0, fw_work->opt_flags); fw_work->cont(fw, fw_work->context); - put_device(fw_work->device); /* taken in request_firmware_nowait() */ - module_put(fw_work->module); - kfree_const(fw_work->name); - kfree(fw_work); + spin_lock_irq(&firmware_work_lock); + if (!list_empty(&fw_work->list)) { + list_del_init(&fw_work->list); + spin_unlock_irq(&firmware_work_lock); + firmware_work_free(fw_work); + return; + } + spin_unlock_irq(&firmware_work_lock); } @@ -1164,6 +1180,7 @@ static int _request_firmware_nowait( void (*cont)(const struct firmware *fw, void *context), bool nowarn) { struct firmware_work *fw_work; + unsigned long flags; fw_work = kzalloc_obj(struct firmware_work, gfp); if (!fw_work) @@ -1196,7 +1213,12 @@ static int _request_firmware_nowait( get_device(fw_work->device); INIT_WORK(&fw_work->work, request_firmware_work_func); + + spin_lock_irqsave(&firmware_work_lock, flags); + list_add_tail(&fw_work->list, &firmware_work_list); schedule_work(&fw_work->work); + spin_unlock_irqrestore(&firmware_work_lock, flags); + return 0; } @@ -1259,6 +1281,44 @@ int firmware_request_nowait_nowarn( } EXPORT_SYMBOL_GPL(firmware_request_nowait_nowarn); +/** + * request_firmware_nowait_cancel() - cancel an async firmware request + * @device: device for which the firmware is being loaded + * @context: context passed to request_firmware_nowait() + * @cont: callback passed to request_firmware_nowait() + * + * Cancel a pending request_firmware_nowait() request for @device, @context + * and @cont. If the associated work has already started, this function waits + * until the callback has returned. If the callback has already completed, this + * function does nothing. + * + * This function may sleep. + */ +void request_firmware_nowait_cancel(struct device *device, void *context, + void (*cont)(const struct firmware *fw, + void *context)) +{ + struct firmware_work *fw_work = NULL; + struct firmware_work *tmp; + + spin_lock_irq(&firmware_work_lock); + list_for_each_entry_reverse(tmp, &firmware_work_list, list) { + if (tmp->device == device && tmp->context == context && + tmp->cont == cont) { + fw_work = tmp; + list_del_init(&fw_work->list); + break; + } + } + spin_unlock_irq(&firmware_work_lock); + + if (!fw_work) + return; + cancel_work_sync(&fw_work->work); + firmware_work_free(fw_work); +} +EXPORT_SYMBOL_GPL(request_firmware_nowait_cancel); + #ifdef CONFIG_FW_CACHE static ASYNC_DOMAIN_EXCLUSIVE(fw_cache_domain); diff --git a/include/linux/firmware.h b/include/linux/firmware.h index aae1b85ffc10e..0fa3b027f02f1 100644 --- a/include/linux/firmware.h +++ b/include/linux/firmware.h @@ -110,6 +110,9 @@ int request_firmware_nowait( struct module *module, bool uevent, const char *name, struct device *device, gfp_t gfp, void *context, void (*cont)(const struct firmware *fw, void *context)); +void request_firmware_nowait_cancel(struct device *device, void *context, + void (*cont)(const struct firmware *fw, + void *context)); int request_firmware_direct(const struct firmware **fw, const char *name, struct device *device); int request_firmware_into_buf(const struct firmware **firmware_p, @@ -157,6 +160,13 @@ static inline int request_firmware_nowait( return -EINVAL; } +static inline void request_firmware_nowait_cancel(struct device *device, + void *context, + void (*cont)(const struct firmware *fw, + void *context)) +{ +} + static inline void release_firmware(const struct firmware *fw) { } From da9e3be9cf31d7138d23e9e2f7ba1c102ef09f07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Mon, 13 Jul 2026 23:32:56 -0400 Subject: [PATCH 2729/2758] ALSA: hda/tas2781: Cancel async firmware request at unbind MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 5367e2ad14f0ae9350a7aaf2e77c87de39a43ae9 ] TAS2781 HDA I2C and SPI queue RCA firmware loading from component bind with request_firmware_nowait(). The firmware loader keeps the callback module pinned and holds a device reference, but the callback still uses driver-private HDA state. Component unbind removes controls and DSP state immediately. Later device removal tears down the TAS2781 private data, including codec_lock. If the async firmware callback runs after unbind has started, it can operate on state that is being torn down. Cancel or synchronize the async firmware request before removing controls and DSP state. A queued callback is cancelled, and an already-running callback is allowed to finish before unbind continues. Fixes: 5be27f1e3ec9 ("ALSA: hda/tas2781: Add tas2781 HDA driver") Fixes: bb5f86ea50ff ("ALSA: hda/tas2781: Add tas2781 hda SPI driver") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Reviewed-by: Takashi Iwai Acked-by: Danilo Krummrich Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260505-alsa-hda-tas2781-fw-callback-teardown-v4-2-e7c4bf930dc8@gmail.com Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- sound/hda/codecs/side-codecs/tas2781_hda_i2c.c | 3 +++ sound/hda/codecs/side-codecs/tas2781_hda_spi.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c index 67240ce184e1a..dd1b0cc63ad6c 100644 --- a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c @@ -588,6 +588,9 @@ static void tas2781_hda_unbind(struct device *dev, comp->playback_hook = NULL; } + request_firmware_nowait_cancel(tas_hda->priv->dev, tas_hda->priv, + tasdev_fw_ready); + tas2781_hda_remove_controls(tas_hda); tasdevice_config_info_remove(tas_hda->priv); diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c index 0e4f3553f2738..d243baff95a72 100644 --- a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c @@ -750,6 +750,9 @@ static void tas2781_hda_unbind(struct device *dev, struct device *master, comp->playback_hook = NULL; } + request_firmware_nowait_cancel(tas_priv->dev, tas_priv, + tasdev_fw_ready); + tas2781_hda_remove_controls(tas_hda); tasdevice_config_info_remove(tas_priv); From 3f54f2310de0a91099e2ac7e2aed312df6eaa1d8 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Tue, 14 Jul 2026 09:40:05 -0400 Subject: [PATCH 2730/2758] binder: Use LIST_HEAD() to initialize on stack list head [ Upstream commit 91f818b184b44b105b1c92859ea8d2d6f47912a9 ] Use LIST_HEAD to initialize on stack list head. No intentional functional impact. Change generated with below coccinelle script: @@ identifier name; @@ - struct list_head name; + LIST_HEAD(name); ... when != name - INIT_LIST_HEAD(&name); Signed-off-by: Jisheng Zhang Link: https://patch.msgid.link/20260519055623.13142-1-jszhang@kernel.org Signed-off-by: Greg Kroah-Hartman Stable-dep-of: b34826e55aad ("binder: cache secctx size before release zeroes it") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index b85920c39694c..b27fd4b30358a 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -3097,12 +3097,10 @@ static void binder_transaction(struct binder_proc *proc, int t_debug_id = atomic_inc_return(&binder_last_id); ktime_t t_start_time = ktime_get(); struct lsm_context lsmctx = { }; - struct list_head sgc_head; - struct list_head pf_head; + LIST_HEAD(sgc_head); + LIST_HEAD(pf_head); const void __user *user_buffer = (const void __user *) (uintptr_t)tr->data.ptr.buffer; - INIT_LIST_HEAD(&sgc_head); - INIT_LIST_HEAD(&pf_head); e = binder_transaction_log_add(&binder_transaction_log); e->debug_id = t_debug_id; From 4257f45ee1fddd0558e77b62af8bb63fd87b2162 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Tue, 14 Jul 2026 09:40:06 -0400 Subject: [PATCH 2731/2758] binder: cache secctx size before release zeroes it [ Upstream commit b34826e55aad3520ec813f1f367c11b24b29dc9f ] binder_transaction() bounds the scatter-gather buffer area with sg_buf_end_offset and subtracts the aligned LSM context size because the secctx is written at the tail of that area. The subtraction reads lsmctx.len, but that field has already been cleared by the time the line runs: security_secid_to_secctx(secid, &lsmctx) /* lsmctx.len set */ lsmctx_aligned_size = ALIGN(lsmctx.len, sizeof(u64)) extra_buffers_size += lsmctx_aligned_size ... security_release_secctx(&lsmctx) /* memset zeroes len */ ... sg_buf_end_offset = sg_buf_offset + extra_buffers_size - ALIGN(lsmctx.len, sizeof(u64)) /* ALIGN(0,8) */ security_release_secctx() does memset(cp, 0, sizeof(*cp)), so lsmctx.len reads back as 0 and the subtraction contributes nothing, leaving sg_buf_end_offset too large by the aligned secctx size on every transaction to a txn_security_ctx node. Each BINDER_TYPE_PTR object then derives buf_left = sg_buf_end_offset - sg_buf_offset as the sole upper bound on its copy, so the inflated end offset lets the copy run into the bytes that already hold the secctx. The aligned size must therefore be cached before release rather than re-read from the now-cleared field. Fix by caching it in lsmctx_aligned_size at function scope when it is first computed and subtracting lsmctx_aligned_size instead of re-reading lsmctx.len after release. Reuse the same value for the earlier buf_offset computation. Fixes: 6fba89813ccf ("lsm: ensure the correct LSM context releaser") Cc: stable Assisted-by: kres:claude-opus-4-8 Signed-off-by: Chris Mason Reviewed-by: Alice Ryhl Acked-by: Carlos Llamas Link: https://patch.msgid.link/20260603174506.1957278-1-clm@meta.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index b27fd4b30358a..8f2ef1bd539f1 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -3097,6 +3097,7 @@ static void binder_transaction(struct binder_proc *proc, int t_debug_id = atomic_inc_return(&binder_last_id); ktime_t t_start_time = ktime_get(); struct lsm_context lsmctx = { }; + size_t lsmctx_aligned_size = 0; LIST_HEAD(sgc_head); LIST_HEAD(pf_head); const void __user *user_buffer = (const void __user *) @@ -3363,7 +3364,6 @@ static void binder_transaction(struct binder_proc *proc, if (target_node && target_node->txn_security_ctx) { u32 secid; - size_t added_size; security_cred_getsecid(proc->cred, &secid); ret = security_secid_to_secctx(secid, &lsmctx); @@ -3375,9 +3375,9 @@ static void binder_transaction(struct binder_proc *proc, return_error_line = __LINE__; goto err_get_secctx_failed; } - added_size = ALIGN(lsmctx.len, sizeof(u64)); - extra_buffers_size += added_size; - if (extra_buffers_size < added_size) { + lsmctx_aligned_size = ALIGN(lsmctx.len, sizeof(u64)); + extra_buffers_size += lsmctx_aligned_size; + if (extra_buffers_size < lsmctx_aligned_size) { binder_txn_error("%d:%d integer overflow of extra_buffers_size\n", thread->pid, proc->pid); return_error = BR_FAILED_REPLY; @@ -3414,7 +3414,7 @@ static void binder_transaction(struct binder_proc *proc, size_t buf_offset = ALIGN(tr->data_size, sizeof(void *)) + ALIGN(tr->offsets_size, sizeof(void *)) + ALIGN(extra_buffers_size, sizeof(void *)) - - ALIGN(lsmctx.len, sizeof(u64)); + lsmctx_aligned_size; t->security_ctx = t->buffer->user_data + buf_offset; err = binder_alloc_copy_to_buffer(&target_proc->alloc, @@ -3469,7 +3469,7 @@ static void binder_transaction(struct binder_proc *proc, off_end_offset = off_start_offset + tr->offsets_size; sg_buf_offset = ALIGN(off_end_offset, sizeof(void *)); sg_buf_end_offset = sg_buf_offset + extra_buffers_size - - ALIGN(lsmctx.len, sizeof(u64)); + lsmctx_aligned_size; off_min = 0; for (buffer_offset = off_start_offset; buffer_offset < off_end_offset; buffer_offset += sizeof(binder_size_t)) { From a2a2f68c42e0a48fe9463352888ac1df4c05eed1 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Wed, 15 Jul 2026 16:49:39 -0400 Subject: [PATCH 2732/2758] Bluetooth: 6lowpan: fix cyclic locking warning on netdev unregister [ Upstream commit 9707a015fe8f3ba8ec7c270f3b2b8efb38823d6b ] 6lowpan.c has theoretically conflicting lock orderings, which lockdep complains about: a) rtnl_lock > hdev->workqueue from 6lowpan.c:delete_netdev -> rtnl_lock -> device_del -> put_device(parent) -> hci_release_dev -> destroy_workqueue b) hdev->workqueue > l2cap_conn->lock > chan->lock > rtnl_lock from hci_rx_work -> 6lowpan.c:chan_ready_cb -> lowpan_register_netdev, ifup -> rtnl_lock Actual deadlock appears not possible, as hci_rx_work is disabled and l2cap_conn flushed already on hdev unregister. Hence, do minimal thing to make lockdep happy by breaking chain a) by holding hdev refcount until after netdev put in 6lowpan.c. Fixes the lockdep complaint: WARNING: possible circular locking dependency detected. kworker/0:1/11 is trying to acquire lock: ffff8880023b3940 ((wq_completion)hci0#2){+.+.}-{0:0}, at: touch_wq_lockdep_map+0x8b/0x130 but task is already holding lock: ffffffff95e4f9c0 (rtnl_mutex){+.+.}-{4:4}, at: lowpan_unregister_netdev+0xd/0x30 Workqueue: events delete_netdev Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz Stable-dep-of: 6fef032af009 ("Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/6lowpan.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index e0a47b855928d..e7be18a3af330 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -760,13 +760,33 @@ static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan) return chan; } +static void unregister_dev(struct lowpan_btle_dev *dev) +{ + struct hci_dev *hdev = READ_ONCE(dev->hdev); + + /* If netdev holds last reference to hci_dev (its parent device), this + * leads to theoretical cyclic locking on lowpan_unregister_netdev: + * + * rtnl_lock -> put_device(parent) -> hci_release_dev -> + * destroy_workqueue -> hci_rx_work -> l2cap_recv_acldata -> + * chan_ready_cb -> ifup -> rtnl_lock + * + * However, hci_rx_work is disabled in hci_unregister_dev, so this + * should not occur. Make lockdep happy by postponing hdev release after + * netdev put. + */ + hci_dev_hold(hdev); + lowpan_unregister_netdev(dev->netdev); + hci_dev_put(hdev); +} + static void delete_netdev(struct work_struct *work) { struct lowpan_btle_dev *entry = container_of(work, struct lowpan_btle_dev, delete_netdev); - lowpan_unregister_netdev(entry->netdev); + unregister_dev(entry); /* The entry pointer is deleted by the netdev destructor. */ } @@ -1230,6 +1250,7 @@ static void disconnect_devices(void) break; new_dev->netdev = entry->netdev; + new_dev->hdev = entry->hdev; INIT_LIST_HEAD(&new_dev->list); list_add_rcu(&new_dev->list, &devices); @@ -1241,7 +1262,7 @@ static void disconnect_devices(void) ifdown(entry->netdev); BT_DBG("Unregistering netdev %s %p", entry->netdev->name, entry->netdev); - lowpan_unregister_netdev(entry->netdev); + unregister_dev(entry); kfree(entry); } } From 733e76e74e406c1d1ddc7369420dd8a47f48bb8a Mon Sep 17 00:00:00 2001 From: Siwei Zhang Date: Wed, 15 Jul 2026 16:49:40 -0400 Subject: [PATCH 2733/2758] Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb() [ Upstream commit 6fef032af0092ed5ccb767239a9ac1bc38c08a40 ] l2cap_sock_new_connection_cb() returned l2cap_pi(sk)->chan after release_sock(parent). Once the parent lock is dropped the newly enqueued child socket sk is reachable via the accept queue, so another task can accept and free it before the callback dereferences sk, resulting in a use-after-free. Rework the ->new_connection() op so the core, rather than the callback, owns the child channel's lifetime. The op now receives a pre-allocated new_chan and returns an errno instead of allocating and returning a channel. l2cap_new_connection() allocates the child channel and links it into the conn list via __l2cap_chan_add() before invoking the callback, so the conn-list reference keeps the channel alive once release_sock(parent) exposes the socket to other tasks. Channel configuration that was duplicated in l2cap_sock_init() and the various new_connection callbacks is consolidated into l2cap_chan_set_defaults(), which now inherits from the parent channel when one is supplied. Fixes: 8ffb929098a5 ("Bluetooth: Remove parent socket usage from l2cap_core.c") Cc: stable@kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Siwei Zhang Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/net/bluetooth/l2cap.h | 10 ++-- net/bluetooth/6lowpan.c | 18 +----- net/bluetooth/l2cap_core.c | 78 ++++++++++++++++++++----- net/bluetooth/l2cap_sock.c | 103 ++++++++++++++++------------------ net/bluetooth/smp.c | 27 ++------- 5 files changed, 127 insertions(+), 109 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 6578da0048fb4..265afda2e13bc 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -620,7 +620,8 @@ struct l2cap_chan { struct l2cap_ops { char *name; - struct l2cap_chan *(*new_connection) (struct l2cap_chan *chan); + int (*new_connection)(struct l2cap_chan *chan, + struct l2cap_chan *new_chan); int (*recv) (struct l2cap_chan * chan, struct sk_buff *skb); void (*teardown) (struct l2cap_chan *chan, int err); @@ -885,9 +886,10 @@ static inline __u16 __next_seq(struct l2cap_chan *chan, __u16 seq) return (seq + 1) % (chan->tx_win_max + 1); } -static inline struct l2cap_chan *l2cap_chan_no_new_connection(struct l2cap_chan *chan) +static inline int l2cap_chan_no_new_connection(struct l2cap_chan *chan, + struct l2cap_chan *new_chan) { - return NULL; + return -EOPNOTSUPP; } static inline int l2cap_chan_no_recv(struct l2cap_chan *chan, struct sk_buff *skb) @@ -964,7 +966,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, void l2cap_chan_busy(struct l2cap_chan *chan, int busy); void l2cap_chan_rx_avail(struct l2cap_chan *chan, ssize_t rx_avail); int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator); -void l2cap_chan_set_defaults(struct l2cap_chan *chan); +void l2cap_chan_set_defaults(struct l2cap_chan *chan, struct l2cap_chan *pchan); int l2cap_ertm_init(struct l2cap_chan *chan); void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index e7be18a3af330..d504a363a30f3 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -632,7 +632,7 @@ static struct l2cap_chan *chan_create(void) if (!chan) return NULL; - l2cap_chan_set_defaults(chan); + l2cap_chan_set_defaults(chan, NULL); chan->chan_type = L2CAP_CHAN_CONN_ORIENTED; chan->mode = L2CAP_MODE_LE_FLOWCTL; @@ -745,21 +745,6 @@ static inline void chan_ready_cb(struct l2cap_chan *chan) ifup(dev->netdev); } -static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan) -{ - struct l2cap_chan *chan; - - chan = chan_create(); - if (!chan) - return NULL; - - chan->ops = pchan->ops; - - BT_DBG("chan %p pchan %p", chan, pchan); - - return chan; -} - static void unregister_dev(struct lowpan_btle_dev *dev) { struct hci_dev *hdev = READ_ONCE(dev->hdev); @@ -889,7 +874,6 @@ static long chan_get_sndtimeo_cb(struct l2cap_chan *chan) static const struct l2cap_ops bt_6lowpan_chan_ops = { .name = "L2CAP 6LoWPAN channel", - .new_connection = chan_new_conn_cb, .recv = chan_recv_cb, .close = chan_close_cb, .state_change = chan_state_change_cb, diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 93507cb10bc92..189085000c73b 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -525,7 +525,10 @@ void l2cap_chan_put(struct l2cap_chan *c) } EXPORT_SYMBOL_GPL(l2cap_chan_put); -void l2cap_chan_set_defaults(struct l2cap_chan *chan) +/* Initialise @chan with default values, inheriting from the parent channel + * @pchan when it is given. + */ +void l2cap_chan_set_defaults(struct l2cap_chan *chan, struct l2cap_chan *pchan) { chan->fcs = L2CAP_FCS_CRC16; chan->max_tx = L2CAP_DEFAULT_MAX_TX; @@ -539,6 +542,31 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan) chan->retrans_timeout = L2CAP_DEFAULT_RETRANS_TO; chan->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO; + if (pchan) { + BT_DBG("chan %p pchan %p", chan, pchan); + + chan->chan_type = pchan->chan_type; + chan->imtu = pchan->imtu; + chan->omtu = pchan->omtu; + chan->mode = pchan->mode; + chan->fcs = pchan->fcs; + chan->max_tx = pchan->max_tx; + chan->tx_win = pchan->tx_win; + chan->tx_win_max = pchan->tx_win_max; + chan->sec_level = pchan->sec_level; + chan->conf_state = pchan->conf_state; + chan->flags = pchan->flags; + chan->tx_credits = pchan->tx_credits; + chan->rx_credits = pchan->rx_credits; + + if (chan->chan_type == L2CAP_CHAN_FIXED) { + chan->scid = pchan->scid; + chan->dcid = pchan->scid; + } + + return; + } + chan->conf_state = 0; set_bit(CONF_NOT_COMPLETE, &chan->conf_state); @@ -4027,6 +4055,38 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, return 0; } +/* Allocate and initialise a channel for an incoming connection. + * + * The channel inherits its configuration from @pchan and is linked into @conn + * before ->new_connection() runs, so the conn list reference keeps it alive if + * the callback exposes it (e.g. via the socket accept queue) before this + * returns. The l2cap_chan_create() reference is taken over by the subsystem on + * success and dropped here on failure. + */ +static struct l2cap_chan *l2cap_new_connection(struct l2cap_conn *conn, + struct l2cap_chan *pchan) +{ + struct l2cap_chan *chan; + + chan = l2cap_chan_create(); + if (!chan) + return NULL; + + l2cap_chan_set_defaults(chan, pchan); + chan->ops = pchan->ops; + + __l2cap_chan_add(conn, chan); + + if (pchan->ops->new_connection && + pchan->ops->new_connection(pchan, chan) < 0) { + l2cap_chan_del(chan, 0); + l2cap_chan_put(chan); + return NULL; + } + + return chan; +} + static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data, u8 rsp_code) { @@ -4073,7 +4133,7 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, goto response; } - chan = pchan->ops->new_connection(pchan); + chan = l2cap_new_connection(conn, pchan); if (!chan) goto response; @@ -4091,8 +4151,6 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, chan->psm = psm; chan->dcid = scid; - __l2cap_chan_add(conn, chan); - dcid = chan->scid; __set_chan_timer(chan, chan->ops->get_sndtimeo(chan)); @@ -4976,7 +5034,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn, goto response_unlock; } - chan = pchan->ops->new_connection(pchan); + chan = l2cap_new_connection(conn, pchan); if (!chan) { result = L2CAP_CR_LE_NO_MEM; goto response_unlock; @@ -4991,8 +5049,6 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn, chan->omtu = mtu; chan->remote_mps = mps; - __l2cap_chan_add(conn, chan); - l2cap_le_flowctl_init(chan, __le16_to_cpu(req->credits)); dcid = chan->scid; @@ -5200,7 +5256,7 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn, continue; } - chan = pchan->ops->new_connection(pchan); + chan = l2cap_new_connection(conn, pchan); if (!chan) { result = L2CAP_CR_LE_NO_MEM; continue; @@ -5215,8 +5271,6 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn, chan->omtu = mtu; chan->remote_mps = mps; - __l2cap_chan_add(conn, chan); - l2cap_ecred_init(chan, __le16_to_cpu(req->credits)); /* Init response */ @@ -7501,14 +7555,12 @@ static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status) goto next; l2cap_chan_lock(pchan); - chan = pchan->ops->new_connection(pchan); + chan = l2cap_new_connection(conn, pchan); if (chan) { bacpy(&chan->src, &hcon->src); bacpy(&chan->dst, &hcon->dst); chan->src_type = bdaddr_src_type(hcon); chan->dst_type = dst_type; - - __l2cap_chan_add(conn, chan); } l2cap_chan_unlock(pchan); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 1e93de1559871..d8cbe27844377 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -45,7 +45,8 @@ static struct bt_sock_list l2cap_sk_list = { static const struct proto_ops l2cap_sock_ops; static void l2cap_sock_init(struct sock *sk, struct sock *parent); static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, - int proto, gfp_t prio, int kern); + int proto, gfp_t prio, int kern, + struct l2cap_chan *chan); static void l2cap_sock_cleanup_listen(struct sock *parent); bool l2cap_is_socket(struct socket *sock) @@ -1274,6 +1275,23 @@ static int l2cap_sock_recvmsg(struct socket *sock, struct msghdr *msg, return err; } +/* Release the sock's ref on chan and clear the pointer so that the ref is + * dropped exactly once even if both l2cap_sock_kill() and + * l2cap_sock_destruct() run. Setting chan->data to NULL first stops any other + * task from dereferencing the now-dead sock pointer. + */ +static void l2cap_sock_put_chan(struct sock *sk) +{ + struct l2cap_chan *chan = l2cap_pi(sk)->chan; + + if (!chan) + return; + + chan->data = NULL; + l2cap_pi(sk)->chan = NULL; + l2cap_chan_put(chan); +} + /* Kill socket (only if zapped and orphan) * Must be called on unlocked socket, with l2cap channel lock. */ @@ -1284,13 +1302,9 @@ static void l2cap_sock_kill(struct sock *sk) BT_DBG("sk %p state %s", sk, state_to_string(sk->sk_state)); - /* Sock is dead, so set chan data to NULL, avoid other task use invalid - * sock pointer. - */ - l2cap_pi(sk)->chan->data = NULL; - /* Kill poor orphan */ + l2cap_sock_put_chan(sk); - l2cap_chan_put(l2cap_pi(sk)->chan); + /* Kill poor orphan */ sock_set_flag(sk, SOCK_DEAD); sock_put(sk); } @@ -1533,12 +1547,13 @@ static void l2cap_sock_cleanup_listen(struct sock *parent) } } -static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan) +static int l2cap_sock_new_connection_cb(struct l2cap_chan *chan, + struct l2cap_chan *new_chan) { struct sock *sk, *parent = chan->data; if (!parent) - return NULL; + return -EINVAL; lock_sock(parent); @@ -1546,25 +1561,28 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan) if (sk_acceptq_is_full(parent)) { BT_DBG("backlog full %d", parent->sk_ack_backlog); release_sock(parent); - return NULL; + return -ENOBUFS; } sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP, - GFP_ATOMIC, 0); + GFP_ATOMIC, 0, new_chan); if (!sk) { release_sock(parent); - return NULL; - } + return -ENOMEM; + } bt_sock_reclassify_lock(sk, BTPROTO_L2CAP); l2cap_sock_init(sk, parent); + /* The conn list reference taken by l2cap_new_connection() keeps new_chan + * alive once release_sock() lets another task free this socket. + */ bt_accept_enqueue(parent, sk, false); release_sock(parent); - return l2cap_pi(sk)->chan; + return 0; } static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) @@ -1861,10 +1879,7 @@ static void l2cap_sock_destruct(struct sock *sk) BT_DBG("sk %p", sk); - if (l2cap_pi(sk)->chan) { - l2cap_pi(sk)->chan->data = NULL; - l2cap_chan_put(l2cap_pi(sk)->chan); - } + l2cap_sock_put_chan(sk); list_for_each_entry_safe(rx_busy, next, &l2cap_pi(sk)->rx_busy, list) { kfree_skb(rx_busy->skb); @@ -1897,30 +1912,12 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) BT_DBG("sk %p", sk); if (parent) { - struct l2cap_chan *pchan = l2cap_pi(parent)->chan; - sk->sk_type = parent->sk_type; bt_sk(sk)->flags = bt_sk(parent)->flags; - chan->chan_type = pchan->chan_type; - chan->imtu = pchan->imtu; - chan->omtu = pchan->omtu; - chan->conf_state = pchan->conf_state; - chan->mode = pchan->mode; - chan->fcs = pchan->fcs; - chan->max_tx = pchan->max_tx; - chan->tx_win = pchan->tx_win; - chan->tx_win_max = pchan->tx_win_max; - chan->sec_level = pchan->sec_level; - chan->flags = pchan->flags; - chan->tx_credits = pchan->tx_credits; - chan->rx_credits = pchan->rx_credits; - - if (chan->chan_type == L2CAP_CHAN_FIXED) { - chan->scid = pchan->scid; - chan->dcid = pchan->scid; - } - + /* Channel configuration is inherited from the parent by + * l2cap_new_connection(). + */ security_sk_clone(parent, sk); } else { switch (sk->sk_type) { @@ -1946,7 +1943,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) chan->mode = L2CAP_MODE_BASIC; } - l2cap_chan_set_defaults(chan); + l2cap_chan_set_defaults(chan, NULL); } /* Default config options */ @@ -1965,10 +1962,10 @@ static struct proto l2cap_proto = { }; static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, - int proto, gfp_t prio, int kern) + int proto, gfp_t prio, int kern, + struct l2cap_chan *chan) { struct sock *sk; - struct l2cap_chan *chan; sk = bt_sock_alloc(net, sock, &l2cap_proto, proto, prio, kern); if (!sk) @@ -1979,16 +1976,7 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, INIT_LIST_HEAD(&l2cap_pi(sk)->rx_busy); - chan = l2cap_chan_create(); - if (!chan) { - sk_free(sk); - if (sock) - sock->sk = NULL; - return NULL; - } - - l2cap_chan_hold(chan); - + /* The sock takes ownership of the caller's reference on chan. */ l2cap_pi(sk)->chan = chan; return sk; @@ -1998,6 +1986,7 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol, int kern) { struct sock *sk; + struct l2cap_chan *chan; BT_DBG("sock %p", sock); @@ -2012,10 +2001,16 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol, sock->ops = &l2cap_sock_ops; - sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern); - if (!sk) + chan = l2cap_chan_create(); + if (!chan) return -ENOMEM; + sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern, chan); + if (!sk) { + l2cap_chan_put(chan); + return -ENOMEM; + } + l2cap_sock_init(sk, NULL); bt_sock_link(&l2cap_sk_list, sk); return 0; diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 98f1da4f5f559..4787f8cd29e41 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -3234,34 +3234,19 @@ static const struct l2cap_ops smp_chan_ops = { .get_sndtimeo = l2cap_chan_no_get_sndtimeo, }; -static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan) +static inline int smp_new_conn_cb(struct l2cap_chan *chan, + struct l2cap_chan *new_chan) { - struct l2cap_chan *chan; - - BT_DBG("pchan %p", pchan); - - chan = l2cap_chan_create(); - if (!chan) - return NULL; - - chan->chan_type = pchan->chan_type; - chan->ops = &smp_chan_ops; - chan->scid = pchan->scid; - chan->dcid = chan->scid; - chan->imtu = pchan->imtu; - chan->omtu = pchan->omtu; - chan->mode = pchan->mode; + new_chan->ops = &smp_chan_ops; /* Other L2CAP channels may request SMP routines in order to * change the security level. This means that the SMP channel * lock must be considered in its own category to avoid lockdep * warnings. */ - atomic_set(&chan->nesting, L2CAP_NESTING_SMP); - - BT_DBG("created chan %p", chan); + atomic_set(&new_chan->nesting, L2CAP_NESTING_SMP); - return chan; + return 0; } static const struct l2cap_ops smp_root_chan_ops = { @@ -3332,7 +3317,7 @@ static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid) l2cap_add_scid(chan, cid); - l2cap_chan_set_defaults(chan); + l2cap_chan_set_defaults(chan, NULL); if (cid == L2CAP_CID_SMP) { u8 bdaddr_type; From 98185b3025beeae92d1fe700d5db26b9ac4bf025 Mon Sep 17 00:00:00 2001 From: Davide Ornaghi Date: Wed, 15 Jul 2026 21:45:17 -0400 Subject: [PATCH 2734/2758] ksmbd: fix path resolution in ksmbd_vfs_kern_path_create [ Upstream commit 1c8951963d8ed357f70f59e0ad4ddce2199d2016 ] The SMB2 open lookup is rooted at the share with LOOKUP_BENEATH, but the create/mkdir/hardlink sink is not: ksmbd_vfs_kern_path_create() builds an absolute path with convert_to_unix_name() and resolves it from AT_FDCWD via start_creating_path(), so a ".." component is walked from the real filesystem root and escapes the export. An authenticated client races a missing path component so the rooted open lookup returns -ENOENT (taking the create branch) while the same component is present (a directory) when the create walk runs; the create then resolves ".." out of the share. Root the create walk at the share like the lookup and rename paths already are: resolve the parent with vfs_path_parent_lookup(..., LOOKUP_BENEATH, &share_conf->vfs_path) and create the final component with start_creating_noperm(). convert_to_unix_name() then has no callers and is removed. Fixes: 265fd1991c1d ("ksmbd: use LOOKUP_BENEATH to prevent the out of share access") Cc: stable@vger.kernel.org Signed-off-by: Davide Ornaghi Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/misc.c | 33 --------------------------------- fs/smb/server/misc.h | 1 - fs/smb/server/vfs.c | 33 +++++++++++++++++++++++++++------ 3 files changed, 27 insertions(+), 40 deletions(-) diff --git a/fs/smb/server/misc.c b/fs/smb/server/misc.c index a543ec9d35817..966004c414a8c 100644 --- a/fs/smb/server/misc.c +++ b/fs/smb/server/misc.c @@ -283,39 +283,6 @@ char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename) return ksmbd_casefold_sharename(um, name); } -/** - * convert_to_unix_name() - convert windows name to unix format - * @share: ksmbd_share_config pointer - * @name: file name that is relative to share - * - * Return: converted name on success, otherwise NULL - */ -char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name) -{ - int no_slash = 0, name_len, path_len; - char *new_name; - - if (name[0] == '/') - name++; - - path_len = share->path_sz; - name_len = strlen(name); - new_name = kmalloc(path_len + name_len + 2, KSMBD_DEFAULT_GFP); - if (!new_name) - return new_name; - - memcpy(new_name, share->path, path_len); - if (new_name[path_len - 1] != '/') { - new_name[path_len] = '/'; - no_slash = 1; - } - - memcpy(new_name + path_len + no_slash, name, name_len); - path_len += name_len + no_slash; - new_name[path_len] = 0x00; - return new_name; -} - char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info, const struct nls_table *local_nls, int *conv_len) diff --git a/fs/smb/server/misc.h b/fs/smb/server/misc.h index 13423696ae8c6..3909104e18ad4 100644 --- a/fs/smb/server/misc.h +++ b/fs/smb/server/misc.h @@ -25,7 +25,6 @@ void ksmbd_strip_last_slash(char *path); void ksmbd_conv_path_to_windows(char *path); char *ksmbd_casefold_sharename(struct unicode_map *um, const char *name); char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename); -char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name); #define KSMBD_DIR_INFO_ALIGNMENT 8 struct ksmbd_dir_info; diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index d17b4f0ee30ab..cdcbbc72af5c7 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -1261,15 +1261,36 @@ struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, unsigned int flags, struct path *path) { - char *abs_name; + struct ksmbd_share_config *share_conf = work->tcon->share_conf; + struct qstr last; struct dentry *dent; + int err, type; - abs_name = convert_to_unix_name(work->tcon->share_conf, name); - if (!abs_name) - return ERR_PTR(-ENOMEM); + /* resolve the name beneath the share root so ".." cannot escape */ + CLASS(filename_kernel, filename)(name); + + err = vfs_path_parent_lookup(filename, flags | LOOKUP_BENEATH, + path, &last, &type, + &share_conf->vfs_path); + if (err) + return ERR_PTR(err); - dent = start_creating_path(AT_FDCWD, abs_name, path, flags); - kfree(abs_name); + if (unlikely(type != LAST_NORM)) { + path_put(path); + return ERR_PTR(-EINVAL); + } + + err = mnt_want_write(path->mnt); + if (err) { + path_put(path); + return ERR_PTR(err); + } + + dent = start_creating_noperm(path->dentry, &last); + if (IS_ERR(dent)) { + mnt_drop_write(path->mnt); + path_put(path); + } return dent; } From cba4ee1092b3301ec4958f7100aa5ae2fdee8ccf Mon Sep 17 00:00:00 2001 From: ChenXiaoSong Date: Wed, 15 Jul 2026 22:05:57 -0400 Subject: [PATCH 2735/2758] smb: move compression definitions into common/fscc.h [ Upstream commit 9d357903ec9b0da60cccc4d311f99763ba0924a2 ] These definitions will also be used by ksmbd, move them into common header file. Signed-off-by: ChenXiaoSong Acked-by: Namjae Jeon Signed-off-by: Steve French Stable-dep-of: c6394bcaf254 ("ksmbd: use opener credentials for FSCTL mutations") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb1pdu.h | 5 ----- fs/smb/client/smb2pdu.h | 4 ---- fs/smb/common/fscc.h | 18 ++++++++++++++++++ fs/smb/server/smb2pdu.h | 3 --- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/fs/smb/client/smb1pdu.h b/fs/smb/client/smb1pdu.h index 7584e94d9b2bf..0870949144abf 100644 --- a/fs/smb/client/smb1pdu.h +++ b/fs/smb/client/smb1pdu.h @@ -1211,11 +1211,6 @@ typedef struct smb_com_transaction_compr_ioctl_req { __le16 compression_state; /* See below for valid flags */ } __packed TRANSACT_COMPR_IOCTL_REQ; -/* compression state flags */ -#define COMPRESSION_FORMAT_NONE 0x0000 -#define COMPRESSION_FORMAT_DEFAULT 0x0001 -#define COMPRESSION_FORMAT_LZNT1 0x0002 - typedef struct smb_com_transaction_ioctl_rsp { struct smb_hdr hdr; /* wct = 19 */ __u8 Reserved[3]; diff --git a/fs/smb/client/smb2pdu.h b/fs/smb/client/smb2pdu.h index 30d70097fe2fa..b9bf2fa989d5b 100644 --- a/fs/smb/client/smb2pdu.h +++ b/fs/smb/client/smb2pdu.h @@ -195,10 +195,6 @@ struct network_resiliency_req { #define NO_FILE_ID 0xFFFFFFFFFFFFFFFFULL /* general ioctls to srv not to file */ -struct compress_ioctl { - __le16 CompressionState; /* See cifspdu.h for possible flag values */ -} __packed; - /* * Maximum number of iovs we need for an ioctl request. * [0] : struct smb2_ioctl_req diff --git a/fs/smb/common/fscc.h b/fs/smb/common/fscc.h index bc3012cc295da..859849a42fec5 100644 --- a/fs/smb/common/fscc.h +++ b/fs/smb/common/fscc.h @@ -100,6 +100,24 @@ struct duplicate_extents_to_file_ex { __le32 Reserved; } __packed; +/* + * compression state flags + * See MS-FSCC 2.3.18 + * MS-FSCC 2.3.67 + * MS-FSCC 2.4.9 + */ +#define COMPRESSION_FORMAT_NONE 0x0000 +#define COMPRESSION_FORMAT_DEFAULT 0x0001 +#define COMPRESSION_FORMAT_LZNT1 0x0002 + +/* + * See MS-FSCC 2.3.18 + * MS-FSCC 2.3.67 + */ +struct compress_ioctl { + __le16 CompressionState; +} __packed; + /* See MS-FSCC 2.3.20 */ struct fsctl_get_integrity_information_rsp { __le16 ChecksumAlgorithm; diff --git a/fs/smb/server/smb2pdu.h b/fs/smb/server/smb2pdu.h index e7cf573e59f00..3bed676bb5ad4 100644 --- a/fs/smb/server/smb2pdu.h +++ b/fs/smb/server/smb2pdu.h @@ -230,9 +230,6 @@ struct smb2_file_mode_info { __le32 Mode; } __packed; -#define COMPRESSION_FORMAT_NONE 0x0000 -#define COMPRESSION_FORMAT_LZNT1 0x0002 - struct smb2_file_comp_info { __le64 CompressedFileSize; __le16 CompressionFormat; From cfb2c6f71d61ed807c9d7a7af331d406f1f31877 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 15 Jul 2026 22:05:58 -0400 Subject: [PATCH 2736/2758] ksmbd: use opener credentials for FSCTL mutations [ Upstream commit c6394bcaf254c5baf9aff43376020be5db6d3316 ] SET_SPARSE, SET_ZERO_DATA and SET_COMPRESSION operate on an open SMB handle but call VFS xattr, fallocate or fileattr helpers with the current ksmbd worker credentials. Those helpers can revalidate inode permissions, ownership and LSM policy independently of the SMB handle access mask. Run each operation with the credentials captured in the target file when the handle was opened. Keep credential handling local to these single-file FSCTLs rather than applying session credentials to the complete IOCTL handler, which also contains handle-less and multi-handle operations. Cc: stable@vger.kernel.org Reported-by: Musaab Khan Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smb2pdu.c | 3 +++ fs/smb/server/vfs.c | 20 +++++++++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 352ae21e4f005..ef65b26270813 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -8320,6 +8320,7 @@ static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id, if (fp->f_ci->m_fattr != old_fattr && test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { + const struct cred *saved_cred; struct xattr_dos_attrib da; ret = ksmbd_vfs_get_dos_attrib_xattr(idmap, @@ -8328,9 +8329,11 @@ static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id, goto out; da.attr = le32_to_cpu(fp->f_ci->m_fattr); + saved_cred = override_creds(fp->filp->f_cred); ret = ksmbd_vfs_set_dos_attrib_xattr(idmap, &fp->filp->f_path, &da, true); + revert_creds(saved_cred); if (ret) fp->f_ci->m_fattr = old_fattr; } diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index cdcbbc72af5c7..8b6b2ec896286 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -926,15 +926,21 @@ void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option) int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, loff_t off, loff_t len) { + const struct cred *saved_cred; + int err; + smb_break_all_levII_oplock(work, fp, 1); + saved_cred = override_creds(fp->filp->f_cred); if (fp->f_ci->m_fattr & FILE_ATTRIBUTE_SPARSE_FILE_LE) - return vfs_fallocate(fp->filp, - FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, - off, len); - - return vfs_fallocate(fp->filp, - FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE, - off, len); + err = vfs_fallocate(fp->filp, + FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, + off, len); + else + err = vfs_fallocate(fp->filp, + FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE, + off, len); + revert_creds(saved_cred); + return err; } int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, From 615104cd66f878086db80956ec23a887955e8d66 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 18 Jul 2026 07:51:02 -0400 Subject: [PATCH 2737/2758] xfs: factor out a xfs_zone_mark_free helper [ Upstream commit 16fc9d358d12e6e9cc553162b4064ad58e7fa869 ] Add a helper for adding a zone to the free pool in preparation of adding another caller. Signed-off-by: Christoph Hellwig Reviewed-by: Damien Le Moal Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino Stable-dep-of: ae3692c7f440 ("xfs: add newly added RTGs to the free pool in growfs") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/xfs/xfs_zone_alloc.c | 11 +++++++++-- fs/xfs/xfs_zone_alloc.h | 1 + fs/xfs/xfs_zone_gc.c | 4 +--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/fs/xfs/xfs_zone_alloc.c b/fs/xfs/xfs_zone_alloc.c index 5e297b75a85f6..08d8b34f467e7 100644 --- a/fs/xfs/xfs_zone_alloc.c +++ b/fs/xfs/xfs_zone_alloc.c @@ -944,6 +944,14 @@ xfs_zone_rgbno_is_valid( rtg_rgno(rtg), XFS_RTG_FREE); } +void +xfs_zone_mark_free( + struct xfs_rtgroup *rtg) +{ + xfs_group_set_mark(rtg_group(rtg), XFS_RTG_FREE); + atomic_inc(&rtg_mount(rtg)->m_zone_info->zi_nr_free_zones); +} + static void xfs_free_open_zones( struct xfs_zone_info *zi) @@ -1082,8 +1090,7 @@ xfs_init_zone( if (write_pointer == 0) { /* zone is empty */ - atomic_inc(&zi->zi_nr_free_zones); - xfs_group_set_mark(rtg_group(rtg), XFS_RTG_FREE); + xfs_zone_mark_free(rtg); iz->available += rtg_blocks(rtg); } else if (write_pointer < rtg_blocks(rtg)) { /* zone is open */ diff --git a/fs/xfs/xfs_zone_alloc.h b/fs/xfs/xfs_zone_alloc.h index 8b2ef98c81eff..abf8358bb2662 100644 --- a/fs/xfs/xfs_zone_alloc.h +++ b/fs/xfs/xfs_zone_alloc.h @@ -42,6 +42,7 @@ void xfs_zoned_wake_all(struct xfs_mount *mp); bool xfs_zone_rgbno_is_valid(struct xfs_rtgroup *rtg, xfs_rgnumber_t rgbno); void xfs_mark_rtg_boundary(struct iomap_ioend *ioend); +void xfs_zone_mark_free(struct xfs_rtgroup *rtg); uint64_t xfs_zoned_default_resblks(struct xfs_mount *mp, enum xfs_free_counter ctr); void xfs_zoned_show_stats(struct seq_file *m, struct xfs_mount *mp); diff --git a/fs/xfs/xfs_zone_gc.c b/fs/xfs/xfs_zone_gc.c index f03211e4354ad..f76a091308528 100644 --- a/fs/xfs/xfs_zone_gc.c +++ b/fs/xfs/xfs_zone_gc.c @@ -924,9 +924,7 @@ xfs_zone_gc_finish_reset( goto out; } - xfs_group_set_mark(rtg_group(rtg), XFS_RTG_FREE); - atomic_inc(&zi->zi_nr_free_zones); - + xfs_zone_mark_free(rtg); xfs_zoned_add_available(mp, rtg_blocks(rtg)); wake_up_all(&zi->zi_zone_wait); From 28f19c97eab4170c08947c347fbe61707b36e184 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 18 Jul 2026 07:51:03 -0400 Subject: [PATCH 2738/2758] xfs: add newly added RTGs to the free pool in growfs [ Upstream commit ae3692c7f440c1ff577aae1a51202415ec4a794b ] When growing a zoned RT section, the newly added RTGs also need to be tagged as free in the radix tree and add to the nr_free_zones counters. Call xfs_add_free_zone to do that, otherwise using up the newly added space will wait for free zones forever. Fixes: 01b71e64bb87 ("xfs: support growfs on zoned file systems") Cc: stable@vger.kernel.org # v6.15 Reviewed-by: Damien Le Moal Reviewed-by: "Darrick J. Wong" Signed-off-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/xfs/xfs_rtalloc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/xfs/xfs_rtalloc.c b/fs/xfs/xfs_rtalloc.c index 419140dd1d519..7a3f97686989d 100644 --- a/fs/xfs/xfs_rtalloc.c +++ b/fs/xfs/xfs_rtalloc.c @@ -933,6 +933,14 @@ xfs_growfs_rt_zoned( mp->m_features |= XFS_FEAT_REALTIME; xfs_rtrmapbt_compute_maxlevels(mp); xfs_rtrefcountbt_compute_maxlevels(mp); + + /* + * Finally add the newly added zone to the freelist and add the space + * to the available counter. The order is important here: only add + * the available space after the zones, as available space guarantees + * that zones to back it are available. + */ + xfs_zone_mark_free(rtg); xfs_zoned_add_available(mp, freed_rtx); out_free: kfree(nmp); From 10cfea5091f04c828fa23c3fbb942f5ba05fdf0e Mon Sep 17 00:00:00 2001 From: Mauricio Faria de Oliveira Date: Sat, 18 Jul 2026 17:50:31 -0400 Subject: [PATCH 2739/2758] usb: atm: ueagle-atm: use dev_dbg() for 'device found' message [ Upstream commit 05c5075c3115010f97434c53d353d9a0d4b0e64e ] Convert dev_info() to dev_dbg(). Per 'Documentation/process/coding-style.rst': 13) Printing kernel messages ... When drivers are working properly they are quiet, so prefer to use dev_dbg/pr_debug unless something is wrong. While in there, correct the verb form and add 'with' for clarity. Suggested-by: Greg Kroah-Hartman Link: https://lore.kernel.org/all/2026051628-squatter-stature-c0e0@gregkh/ Signed-off-by: Mauricio Faria de Oliveira Acked-by: Stanislaw Gruszka Link: https://patch.msgid.link/20260520-ueagle-atm-cleanup-v1-1-010c8bc7b214@igalia.com Signed-off-by: Greg Kroah-Hartman Stable-dep-of: e2674dfbed8a ("usb: atm: ueagle-atm: wait for pre-firmware load in .disconnect()") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/ueagle-atm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index f3ae72feb5bfc..71559a934133a 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -2590,7 +2590,7 @@ static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id) int ret; uea_enters(usb); - uea_info(usb, "ADSL device founded vid (%#X) pid (%#X) Rev (%#X): %s\n", + uea_dbg(usb, "ADSL device found with vid (%#X) pid (%#X) Rev (%#X): %s\n", le16_to_cpu(usb->descriptor.idVendor), le16_to_cpu(usb->descriptor.idProduct), le16_to_cpu(usb->descriptor.bcdDevice), From 53430a3768b5ff6adfc319224835ef51e3bc899a Mon Sep 17 00:00:00 2001 From: Mauricio Faria de Oliveira Date: Sat, 18 Jul 2026 17:50:32 -0400 Subject: [PATCH 2740/2758] usb: atm: ueagle-atm: remove function entry/exit debug messages [ Upstream commit 1414b2242c2e9d670c4d8b83480f13db57627ba9 ] Remove the driver-internal function entry/exit debug messages in favor of existing kernel-level function tracing mechanisms. Suggested-by: Greg Kroah-Hartman Link: https://lore.kernel.org/all/2026051657-scruffy-embark-45ea@gregkh/ Signed-off-by: Mauricio Faria de Oliveira Acked-by: Stanislaw Gruszka Link: https://patch.msgid.link/20260520-ueagle-atm-cleanup-v1-2-010c8bc7b214@igalia.com Signed-off-by: Greg Kroah-Hartman Stable-dep-of: e2674dfbed8a ("usb: atm: ueagle-atm: wait for pre-firmware load in .disconnect()") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/ueagle-atm.c | 56 ++++-------------------------------- 1 file changed, 6 insertions(+), 50 deletions(-) diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index 71559a934133a..ed2611aacb252 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -51,12 +51,6 @@ "[ueagle-atm vdbg] " format, ##args); \ } while (0) -#define uea_enters(usb_dev) \ - uea_vdbg(usb_dev, "entering %s\n" , __func__) - -#define uea_leaves(usb_dev) \ - uea_vdbg(usb_dev, "leaving %s\n" , __func__) - #define uea_err(usb_dev, format, args...) \ dev_err(&(usb_dev)->dev , "[UEAGLE-ATM] " format , ##args) @@ -606,7 +600,6 @@ static void uea_upload_pre_firmware(const struct firmware *fw_entry, u32 crc = 0; int ret, size; - uea_enters(usb); if (!fw_entry) { uea_err(usb, "firmware is not available\n"); goto err; @@ -670,7 +663,6 @@ static void uea_upload_pre_firmware(const struct firmware *fw_entry, uea_err(usb, "firmware is corrupted\n"); err: release_firmware(fw_entry); - uea_leaves(usb); } /* @@ -681,7 +673,6 @@ static int uea_load_firmware(struct usb_device *usb, unsigned int ver) int ret; char *fw_name = EAGLE_FIRMWARE; - uea_enters(usb); uea_info(usb, "pre-firmware device, uploading firmware\n"); switch (ver) { @@ -710,7 +701,6 @@ static int uea_load_firmware(struct usb_device *usb, unsigned int ver) else uea_info(usb, "loading firmware %s\n", fw_name); - uea_leaves(usb); return ret; } @@ -1137,7 +1127,6 @@ static int uea_cmv_e1(struct uea_softc *sc, struct cmv_e1 cmv; int ret; - uea_enters(INS_TO_USBDEV(sc)); uea_vdbg(INS_TO_USBDEV(sc), "Function : %d-%d, Address : %c%c%c%c, " "offset : 0x%04x, data : 0x%08x\n", E1_FUNCTION_TYPE(function), @@ -1164,9 +1153,8 @@ static int uea_cmv_e1(struct uea_softc *sc, sizeof(cmv), &cmv); if (ret < 0) return ret; - ret = wait_cmv_ack(sc); - uea_leaves(INS_TO_USBDEV(sc)); - return ret; + + return wait_cmv_ack(sc); } static int uea_cmv_e4(struct uea_softc *sc, @@ -1175,7 +1163,6 @@ static int uea_cmv_e4(struct uea_softc *sc, struct cmv_e4 cmv; int ret; - uea_enters(INS_TO_USBDEV(sc)); memset(&cmv, 0, sizeof(cmv)); uea_vdbg(INS_TO_USBDEV(sc), "Function : %d-%d, Group : 0x%04x, " @@ -1199,9 +1186,8 @@ static int uea_cmv_e4(struct uea_softc *sc, sizeof(cmv), &cmv); if (ret < 0) return ret; - ret = wait_cmv_ack(sc); - uea_leaves(INS_TO_USBDEV(sc)); - return ret; + + return wait_cmv_ack(sc); } static inline int uea_read_cmv_e1(struct uea_softc *sc, @@ -1295,7 +1281,6 @@ static int uea_stat_e1(struct uea_softc *sc) u32 data; int ret; - uea_enters(INS_TO_USBDEV(sc)); data = sc->stats.phy.state; ret = uea_read_cmv_e1(sc, E1_SA_STAT, 0, &sc->stats.phy.state); @@ -1438,7 +1423,6 @@ static int uea_stat_e4(struct uea_softc *sc) u32 tmp_arr[2]; int ret; - uea_enters(INS_TO_USBDEV(sc)); data = sc->stats.phy.state; /* XXX only need to be done before operationnal... */ @@ -1805,7 +1789,6 @@ static int uea_start_reset(struct uea_softc *sc) u16 zero = 0; /* ;-) */ int ret; - uea_enters(INS_TO_USBDEV(sc)); uea_info(INS_TO_USBDEV(sc), "(re)booting started\n"); /* mask interrupt */ @@ -1873,7 +1856,6 @@ static int uea_start_reset(struct uea_softc *sc) return ret; sc->reset = 0; - uea_leaves(INS_TO_USBDEV(sc)); return ret; } @@ -1889,7 +1871,6 @@ static int uea_kthread(void *data) int ret = -EAGAIN; set_freezable(); - uea_enters(INS_TO_USBDEV(sc)); while (!kthread_should_stop()) { if (ret < 0 || sc->reset) ret = uea_start_reset(sc); @@ -1898,7 +1879,7 @@ static int uea_kthread(void *data) if (ret != -EAGAIN) uea_wait(sc, 0, msecs_to_jiffies(1000)); } - uea_leaves(INS_TO_USBDEV(sc)); + return ret; } @@ -1911,8 +1892,6 @@ static int load_XILINX_firmware(struct uea_softc *sc) u8 value; char *fw_name = FPGA930_FIRMWARE; - uea_enters(INS_TO_USBDEV(sc)); - ret = request_firmware(&fw_entry, fw_name, &sc->usb_dev->dev); if (ret) { uea_err(INS_TO_USBDEV(sc), "firmware %s is not available\n", @@ -1956,7 +1935,6 @@ static int load_XILINX_firmware(struct uea_softc *sc) err1: release_firmware(fw_entry); err0: - uea_leaves(INS_TO_USBDEV(sc)); return ret; } @@ -1966,7 +1944,6 @@ static void uea_dispatch_cmv_e1(struct uea_softc *sc, struct intr_pkt *intr) struct cmv_dsc_e1 *dsc = &sc->cmv_dsc.e1; struct cmv_e1 *cmv = &intr->u.e1.s2.cmv; - uea_enters(INS_TO_USBDEV(sc)); if (le16_to_cpu(cmv->wPreamble) != E1_PREAMBLE) goto bad1; @@ -1990,7 +1967,6 @@ static void uea_dispatch_cmv_e1(struct uea_softc *sc, struct intr_pkt *intr) if (cmv->bFunction == E1_MAKEFUNCTION(E1_ADSLDIRECTIVE, E1_MODEMREADY)) { wake_up_cmv_ack(sc); - uea_leaves(INS_TO_USBDEV(sc)); return; } @@ -2004,7 +1980,6 @@ static void uea_dispatch_cmv_e1(struct uea_softc *sc, struct intr_pkt *intr) sc->data = sc->data << 16 | sc->data >> 16; wake_up_cmv_ack(sc); - uea_leaves(INS_TO_USBDEV(sc)); return; bad2: @@ -2012,14 +1987,12 @@ static void uea_dispatch_cmv_e1(struct uea_softc *sc, struct intr_pkt *intr) "Function : %d, Subfunction : %d\n", E1_FUNCTION_TYPE(cmv->bFunction), E1_FUNCTION_SUBTYPE(cmv->bFunction)); - uea_leaves(INS_TO_USBDEV(sc)); return; bad1: uea_err(INS_TO_USBDEV(sc), "invalid cmv received, " "wPreamble %d, bDirection %d\n", le16_to_cpu(cmv->wPreamble), cmv->bDirection); - uea_leaves(INS_TO_USBDEV(sc)); } /* The modem send us an ack. First with check if it right */ @@ -2028,7 +2001,6 @@ static void uea_dispatch_cmv_e4(struct uea_softc *sc, struct intr_pkt *intr) struct cmv_dsc_e4 *dsc = &sc->cmv_dsc.e4; struct cmv_e4 *cmv = &intr->u.e4.s2.cmv; - uea_enters(INS_TO_USBDEV(sc)); uea_dbg(INS_TO_USBDEV(sc), "cmv %x %x %x %x %x %x\n", be16_to_cpu(cmv->wGroup), be16_to_cpu(cmv->wFunction), be16_to_cpu(cmv->wOffset), be16_to_cpu(cmv->wAddress), @@ -2040,7 +2012,6 @@ static void uea_dispatch_cmv_e4(struct uea_softc *sc, struct intr_pkt *intr) if (be16_to_cpu(cmv->wFunction) == E4_MAKEFUNCTION(E4_ADSLDIRECTIVE, E4_MODEMREADY, 1)) { wake_up_cmv_ack(sc); - uea_leaves(INS_TO_USBDEV(sc)); return; } @@ -2053,7 +2024,6 @@ static void uea_dispatch_cmv_e4(struct uea_softc *sc, struct intr_pkt *intr) sc->data = be32_to_cpu(cmv->dwData[0]); sc->data1 = be32_to_cpu(cmv->dwData[1]); wake_up_cmv_ack(sc); - uea_leaves(INS_TO_USBDEV(sc)); return; bad2: @@ -2061,7 +2031,6 @@ static void uea_dispatch_cmv_e4(struct uea_softc *sc, struct intr_pkt *intr) "Function : %d, Subfunction : %d\n", E4_FUNCTION_TYPE(cmv->wFunction), E4_FUNCTION_SUBTYPE(cmv->wFunction)); - uea_leaves(INS_TO_USBDEV(sc)); return; } @@ -2089,8 +2058,6 @@ static void uea_intr(struct urb *urb) struct intr_pkt *intr = urb->transfer_buffer; int status = urb->status; - uea_enters(INS_TO_USBDEV(sc)); - if (unlikely(status < 0)) { uea_err(INS_TO_USBDEV(sc), "uea_intr() failed with %d\n", status); @@ -2130,8 +2097,6 @@ static int uea_boot(struct uea_softc *sc, struct usb_interface *intf) int ret = -ENOMEM; int size; - uea_enters(INS_TO_USBDEV(sc)); - if (UEA_CHIP_VERSION(sc) == EAGLE_IV) { size = E4_INTR_PKT_SIZE; sc->dispatch_cmv = uea_dispatch_cmv_e4; @@ -2188,7 +2153,6 @@ static int uea_boot(struct uea_softc *sc, struct usb_interface *intf) goto err2; } - uea_leaves(INS_TO_USBDEV(sc)); return 0; err2: @@ -2198,7 +2162,6 @@ static int uea_boot(struct uea_softc *sc, struct usb_interface *intf) sc->urb_int = NULL; kfree(intr); err0: - uea_leaves(INS_TO_USBDEV(sc)); return ret; } @@ -2208,7 +2171,7 @@ static int uea_boot(struct uea_softc *sc, struct usb_interface *intf) static void uea_stop(struct uea_softc *sc) { int ret; - uea_enters(INS_TO_USBDEV(sc)); + ret = kthread_stop(sc->kthread); uea_dbg(INS_TO_USBDEV(sc), "kthread finish with status %d\n", ret); @@ -2222,7 +2185,6 @@ static void uea_stop(struct uea_softc *sc) flush_work(&sc->task); release_firmware(sc->dsp_firm); - uea_leaves(INS_TO_USBDEV(sc)); } /* syfs interface */ @@ -2495,8 +2457,6 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf, int ret, ifnum = intf->altsetting->desc.bInterfaceNumber; unsigned int alt; - uea_enters(usb); - /* interface 0 is for firmware/monitoring */ if (ifnum != UEA_INTR_IFACE_NO) return -ENODEV; @@ -2589,7 +2549,6 @@ static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id) struct usb_device *usb = interface_to_usbdev(intf); int ret; - uea_enters(usb); uea_dbg(usb, "ADSL device found with vid (%#X) pid (%#X) Rev (%#X): %s\n", le16_to_cpu(usb->descriptor.idVendor), le16_to_cpu(usb->descriptor.idProduct), @@ -2620,7 +2579,6 @@ static void uea_disconnect(struct usb_interface *intf) { struct usb_device *usb = interface_to_usbdev(intf); int ifnum = intf->altsetting->desc.bInterfaceNumber; - uea_enters(usb); /* ADI930 has 2 interfaces and eagle 3 interfaces. * Pre-firmware device has one interface @@ -2631,8 +2589,6 @@ static void uea_disconnect(struct usb_interface *intf) mutex_unlock(&uea_mutex); uea_info(usb, "ADSL device removed\n"); } - - uea_leaves(usb); } /* From ddcdac47e1f2651c7be60e299f98faf981522797 Mon Sep 17 00:00:00 2001 From: Mauricio Faria de Oliveira Date: Sat, 18 Jul 2026 17:50:33 -0400 Subject: [PATCH 2741/2758] usb: atm: ueagle-atm: wait for pre-firmware load in .disconnect() [ Upstream commit e2674dfbed8a30d57e2bc872c4bfa6c3eec918bf ] ueagle-atm uses the asynchronous request_firmware_nowait() in .probe(), but does not wait for its completion, not even in .disconnect(); so, if the device is unplugged meanwhile, its teardown runs concurrently with that. Even though this inconsistency is worth addressing on its own, it has also triggered several bug reports in syzbot over the years (some auto-closed) where the firmware sysfs fallback mechanism (CONFIG_FW_LOADER_USER_HELPER) creates a firmware subdirectory in the device directory during its removal, which might hit unexpected conditions in kernfs, apparently, depending at which point the add and remove operations raced. (See links.) The pattern is: usb ?-?: Direct firmware load for ueagle-atm/eagle?.fw failed with error -2 usb ?-?: Falling back to sysfs fallback for: ueagle-atm/eagle?.fw Call trace: ... kernfs_create_dir_ns sysfs_create_dir_ns create_dir kobject_add_internal kobject_add_varg kobject_add class_dir_create_and_add get_device_parent device_add fw_load_sysfs_fallback fw_load_from_user_helper firmware_fallback_sysfs _request_firmware request_firmware_work_func ... (Some variations are observed, after fw_load_sysfs_fallback(), e.g., [1].) While the kernfs side is being looked at, the ueagle-atm side can be fixed by waiting for the pre-firmware load in the .disconnect() handler. This change has a similar approach to previous work by Andrey Tsygunka [2] (wait_for_completion() in .disconnect()), but it is relatively different in design/implementation; using the Originally-by tag for credit assignment. This has been tested with: - synthetic reproducer to check the error path; - USB gadget (virtual device) to check the firmware upload path; - QEMU device emulator to check the device ID re-enumeration path; (The latter two were written by Claude; no other code/text in this commit.) Links (year first reported): 2025 https://syzbot.org/bug?extid=ce1e5a1b4e086b43e56d 2025 https://syzbot.org/bug?extid=9af8471255ac36e34fd4 2024 https://syzbot.org/bug?extid=306212936b13e520679d 2023 https://syzkaller.appspot.com/bug?extid=457452d30bcdda75ead2 2022 https://syzbot.org/bug?extid=782984d6f1701b526edb 2021 https://syzbot.org/bug?id=f3f221579f4ef7e9691281f3c6f56c05f83e8490 2021 https://syzbot.org/bug?id=84d86f0d71394829df6fc53daf6642c045983881 2021 https://syzbot.org/bug?id=3302dc1c0e2b9c94f2e8edb404eabc9267bc6f90 [1] https://syzkaller.appspot.com/bug?extid=457452d30bcdda75ead2 [2] https://lore.kernel.org/lkml/20250410093146.3776801-2-aitsygunka@yandex.ru/ Cc: stable Reported-by: syzbot+ce1e5a1b4e086b43e56d@syzkaller.appspotmail.com Closes: https://syzbot.org/bug?extid=ce1e5a1b4e086b43e56d Reported-by: syzbot+306212936b13e520679d@syzkaller.appspotmail.com Closes: https://syzbot.org/bug?extid=306212936b13e520679d Reported-by: syzbot+457452d30bcdda75ead2@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=457452d30bcdda75ead2 Originally-by: Andrey Tsygunka Fixes: b72458a80c75 ("[PATCH] USB: Eagle and ADI 930 usb adsl modem driver") Assisted-by: Claude:claude-opus-4.7 # usb gadget & qemu device for testing Signed-off-by: Mauricio Faria de Oliveira Acked-by: Stanislaw Gruszka Link: https://patch.msgid.link/20260526-ueagle-atm_req-fw-sync-v3-1-93c01961daaf@igalia.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/ueagle-atm.c | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index ed2611aacb252..1e12fc19f8726 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -594,7 +594,9 @@ static int uea_send_modem_cmd(struct usb_device *usb, static void uea_upload_pre_firmware(const struct firmware *fw_entry, void *context) { - struct usb_device *usb = context; + struct usb_interface *intf = context; + struct usb_device *usb = interface_to_usbdev(intf); + struct completion *fw_done = usb_get_intfdata(intf); const u8 *pfw; u8 value; u32 crc = 0; @@ -663,15 +665,17 @@ static void uea_upload_pre_firmware(const struct firmware *fw_entry, uea_err(usb, "firmware is corrupted\n"); err: release_firmware(fw_entry); + complete(fw_done); } /* * uea_load_firmware - Load usb firmware for pre-firmware devices. */ -static int uea_load_firmware(struct usb_device *usb, unsigned int ver) +static int uea_load_firmware(struct usb_interface *intf, unsigned int ver) { int ret; char *fw_name = EAGLE_FIRMWARE; + struct usb_device *usb = interface_to_usbdev(intf); uea_info(usb, "pre-firmware device, uploading firmware\n"); @@ -694,7 +698,7 @@ static int uea_load_firmware(struct usb_device *usb, unsigned int ver) } ret = request_firmware_nowait(THIS_MODULE, 1, fw_name, &usb->dev, - GFP_KERNEL, usb, + GFP_KERNEL, intf, uea_upload_pre_firmware); if (ret) uea_err(usb, "firmware %s is not available\n", fw_name); @@ -2557,8 +2561,23 @@ static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id) usb_reset_device(usb); - if (UEA_IS_PREFIRM(id)) - return uea_load_firmware(usb, UEA_CHIP_VERSION(id)); + if (UEA_IS_PREFIRM(id)) { + struct completion *fw_done; + + /* Wait for the firmware load to be done, in .disconnect() */ + fw_done = kzalloc_obj(*fw_done); + if (!fw_done) + return -ENOMEM; + + init_completion(fw_done); + usb_set_intfdata(intf, fw_done); + + ret = uea_load_firmware(intf, UEA_CHIP_VERSION(id)); + if (ret) + kfree(fw_done); + + return ret; + } ret = usbatm_usb_probe(intf, id, &uea_usbatm_driver); if (ret == 0) { @@ -2588,6 +2607,13 @@ static void uea_disconnect(struct usb_interface *intf) usbatm_usb_disconnect(intf); mutex_unlock(&uea_mutex); uea_info(usb, "ADSL device removed\n"); + } else if (usb->config->desc.bNumInterfaces == 1) { + struct completion *fw_done = usb_get_intfdata(intf); + + uea_dbg(usb, "pre-firmware device, waiting firmware upload\n"); + wait_for_completion(fw_done); + uea_dbg(usb, "pre-firmware device, finished waiting\n"); + kfree(fw_done); } } From 3dc8a46d08a8a060d4128e9f497060b8d03c1595 Mon Sep 17 00:00:00 2001 From: "Pratyush Yadav (Google)" Date: Mon, 20 Jul 2026 20:45:13 -0400 Subject: [PATCH 2742/2758] liveupdate: validate session type before performing operation [ Upstream commit 507e3b479f9c6d85135eb5e1a77fb3fddb259ad8 ] The sessions ioctls are not applicable to all session types. PRESERVE_FD is only applicable to outgoing sessions. RETRIEVE_FD and FINISH are only valid for incoming session. Calling a incoming ioctl on an outgoing session is invalid and can cause file handlers to run into unexpected errors. For example, a user can create a (outgoing) session, preserve a memfd, and then immediately do a retrieve without doing a kexec in between. This would result in memfd's retrieve handler to run. The handlers expects to be called from a post-kexec context, and will try to do a kho_restore_vmalloc() or kho_restore_folio() to try and restore memory. KHO catches this (thanks to KHO_PAGE_MAGIC) and returns an error, but since this is considered an internal error and KHO throws out a bunch of WARN()s. Associate a type with each ioctl op and validate the type in luo_session_ioctl() before dispatching the ioctl handler to make sure the op is being called for the right session type. Fixes: 16cec0d26521 ("liveupdate: luo_session: add ioctls for file preservation") Cc: stable@vger.kernel.org Signed-off-by: Pratyush Yadav (Google) Link: https://patch.msgid.link/20260519122428.2378446-1-pratyush@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- kernel/liveupdate/luo_session.c | 37 ++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c index 5486e834c4514..174de2cc56a6b 100644 --- a/kernel/liveupdate/luo_session.c +++ b/kernel/liveupdate/luo_session.c @@ -330,32 +330,58 @@ union ucmd_buffer { struct liveupdate_session_retrieve_fd retrieve; }; +/* Type of sessions the ioctl applies to. */ +enum luo_ioctl_type { + LUO_IOCTL_INCOMING, + LUO_IOCTL_OUTGOING, + LUO_IOCTL_ALL, +}; + struct luo_ioctl_op { unsigned int size; unsigned int min_size; unsigned int ioctl_num; + enum luo_ioctl_type type; int (*execute)(struct luo_session *session, struct luo_ucmd *ucmd); }; -#define IOCTL_OP(_ioctl, _fn, _struct, _last) \ +#define IOCTL_OP(_ioctl, _fn, _struct, _last, _type) \ [_IOC_NR(_ioctl) - LIVEUPDATE_CMD_SESSION_BASE] = { \ .size = sizeof(_struct) + \ BUILD_BUG_ON_ZERO(sizeof(union ucmd_buffer) < \ sizeof(_struct)), \ .min_size = offsetofend(_struct, _last), \ .ioctl_num = _ioctl, \ + .type = _type, \ .execute = _fn, \ } static const struct luo_ioctl_op luo_session_ioctl_ops[] = { IOCTL_OP(LIVEUPDATE_SESSION_FINISH, luo_session_finish, - struct liveupdate_session_finish, reserved), + struct liveupdate_session_finish, reserved, LUO_IOCTL_INCOMING), IOCTL_OP(LIVEUPDATE_SESSION_PRESERVE_FD, luo_session_preserve_fd, - struct liveupdate_session_preserve_fd, token), + struct liveupdate_session_preserve_fd, token, LUO_IOCTL_OUTGOING), IOCTL_OP(LIVEUPDATE_SESSION_RETRIEVE_FD, luo_session_retrieve_fd, - struct liveupdate_session_retrieve_fd, token), + struct liveupdate_session_retrieve_fd, token, LUO_IOCTL_INCOMING), }; +static bool luo_ioctl_type_valid(struct luo_session *session, + const struct luo_ioctl_op *op) +{ + switch (op->type) { + case LUO_IOCTL_INCOMING: + /* Retrieved is only set on incoming sessions */ + return session->retrieved; + case LUO_IOCTL_OUTGOING: + return !session->retrieved; + case LUO_IOCTL_ALL: + return true; + } + + /* Catch-all. */ + return false; +} + static long luo_session_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { @@ -380,6 +406,8 @@ static long luo_session_ioctl(struct file *filep, unsigned int cmd, op = &luo_session_ioctl_ops[nr - LIVEUPDATE_CMD_SESSION_BASE]; if (op->ioctl_num != cmd) return -ENOIOCTLCMD; + if (!luo_ioctl_type_valid(session, op)) + return -EINVAL; if (ucmd.user_size < op->min_size) return -EINVAL; @@ -659,4 +687,3 @@ int luo_session_serialize(void) return err; } - From 9634db561e1594c151923f4950c012161c545c93 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 31 May 2026 12:17:35 -0700 Subject: [PATCH 2743/2758] crypto: xilinx-trng - Remove crypto_rng interface commit 32b4d29280ed2a991dc196d5845b892acedc63b8 upstream. Implementing the crypto_rng interface has no purpose, as it isn't used in practice. It's being removed from other drivers too. Just remove it. This leaves hwrng, which is actually used. Tagging with 'Cc stable' due to the bugs that this removes: - xtrng_trng_generate() sometimes returned success even when it didn't fill in all the bytes. - It was possible for xtrng_trng_generate() and xtrng_hwrng_trng_read() to run concurrently and interfere with each other, as the locking code in xtrng_hwrng_trng_read() was broken. Fixes: 8979744aca80 ("crypto: xilinx - Add TRNG driver for Versal") Cc: stable@vger.kernel.org Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/Kconfig | 1 - drivers/crypto/xilinx/xilinx-trng.c | 85 ++--------------------------- 2 files changed, 4 insertions(+), 82 deletions(-) diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index 0712e9bf6e54b..8f84cce69fbc9 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -721,7 +721,6 @@ config CRYPTO_DEV_XILINX_TRNG tristate "Support for Xilinx True Random Generator" depends on ZYNQMP_FIRMWARE || COMPILE_TEST select CRYPTO_DF80090A - select CRYPTO_RNG select HW_RANDOM help Xilinx Versal SoC driver provides kernel-side support for True Random Number diff --git a/drivers/crypto/xilinx/xilinx-trng.c b/drivers/crypto/xilinx/xilinx-trng.c index 5276ac2d82bbe..e3ced111c4095 100644 --- a/drivers/crypto/xilinx/xilinx-trng.c +++ b/drivers/crypto/xilinx/xilinx-trng.c @@ -6,7 +6,6 @@ #include #include -#include #include #include #include @@ -14,14 +13,12 @@ #include #include #include -#include #include #include #include #include #include #include -#include /* TRNG Registers Offsets */ #define TRNG_STATUS_OFFSET 0x4U @@ -61,16 +58,9 @@ struct xilinx_rng { struct device *dev; unsigned char *scratchpadbuf; struct aes_enckey *aeskey; - struct mutex lock; /* Protect access to TRNG device */ struct hwrng trng; }; -struct xilinx_rng_ctx { - struct xilinx_rng *rng; -}; - -static struct xilinx_rng *xilinx_rng_dev; - static void xtrng_readwrite32(void __iomem *addr, u32 mask, u8 value) { u32 val; @@ -246,47 +236,6 @@ static int xtrng_random_bytes_generate(struct xilinx_rng *rng, u8 *rand_buf_ptr, return nbytes; } -static int xtrng_trng_generate(struct crypto_rng *tfm, const u8 *src, u32 slen, - u8 *dst, u32 dlen) -{ - struct xilinx_rng_ctx *ctx = crypto_rng_ctx(tfm); - int ret; - - mutex_lock(&ctx->rng->lock); - ret = xtrng_random_bytes_generate(ctx->rng, dst, dlen, true); - mutex_unlock(&ctx->rng->lock); - - return ret < 0 ? ret : 0; -} - -static int xtrng_trng_seed(struct crypto_rng *tfm, const u8 *seed, unsigned int slen) -{ - return 0; -} - -static int xtrng_trng_init(struct crypto_tfm *rtfm) -{ - struct xilinx_rng_ctx *ctx = crypto_tfm_ctx(rtfm); - - ctx->rng = xilinx_rng_dev; - - return 0; -} - -static struct rng_alg xtrng_trng_alg = { - .generate = xtrng_trng_generate, - .seed = xtrng_trng_seed, - .seedsize = 0, - .base = { - .cra_name = "stdrng", - .cra_driver_name = "xilinx-trng", - .cra_priority = 300, - .cra_ctxsize = sizeof(struct xilinx_rng_ctx), - .cra_module = THIS_MODULE, - .cra_init = xtrng_trng_init, - }, -}; - static int xtrng_hwrng_trng_read(struct hwrng *hwrng, void *data, size_t max, bool wait) { u8 buf[TRNG_SEC_STRENGTH_BYTES]; @@ -294,12 +243,6 @@ static int xtrng_hwrng_trng_read(struct hwrng *hwrng, void *data, size_t max, bo int ret = -EINVAL, i = 0; rng = container_of(hwrng, struct xilinx_rng, trng); - /* Return in case wait not set and lock not available. */ - if (!mutex_trylock(&rng->lock) && !wait) - return 0; - else if (!mutex_is_locked(&rng->lock) && wait) - mutex_lock(&rng->lock); - while (i < max) { ret = xtrng_random_bytes_generate(rng, buf, TRNG_SEC_STRENGTH_BYTES, wait); if (ret < 0) @@ -308,8 +251,6 @@ static int xtrng_hwrng_trng_read(struct hwrng *hwrng, void *data, size_t max, bo memcpy(data + i, buf, min_t(int, ret, (max - i))); i += min_t(int, ret, (max - i)); } - mutex_unlock(&rng->lock); - return ret; } @@ -355,40 +296,24 @@ static int xtrng_probe(struct platform_device *pdev) sb_size = crypto_drbg_ctr_df_datalen(TRNG_SEED_LEN_BYTES, AES_BLOCK_SIZE); rng->scratchpadbuf = devm_kzalloc(&pdev->dev, sb_size, GFP_KERNEL); - if (!rng->scratchpadbuf) { - ret = -ENOMEM; - goto end; - } + if (!rng->scratchpadbuf) + return -ENOMEM; xtrng_trng_reset(rng->rng_base); ret = xtrng_reseed_internal(rng); if (ret) { dev_err(&pdev->dev, "TRNG Seed fail\n"); - goto end; - } - - xilinx_rng_dev = rng; - mutex_init(&rng->lock); - ret = crypto_register_rng(&xtrng_trng_alg); - if (ret) { - dev_err(&pdev->dev, "Crypto Random device registration failed: %d\n", ret); - goto end; + return ret; } ret = xtrng_hwrng_register(&rng->trng); if (ret) { dev_err(&pdev->dev, "HWRNG device registration failed: %d\n", ret); - goto crypto_rng_free; + return ret; } platform_set_drvdata(pdev, rng); return 0; - -crypto_rng_free: - crypto_unregister_rng(&xtrng_trng_alg); - -end: - return ret; } static void xtrng_remove(struct platform_device *pdev) @@ -398,13 +323,11 @@ static void xtrng_remove(struct platform_device *pdev) rng = platform_get_drvdata(pdev); xtrng_hwrng_unregister(&rng->trng); - crypto_unregister_rng(&xtrng_trng_alg); xtrng_write_multiple_registers(rng->rng_base + TRNG_EXT_SEED_OFFSET, zero, TRNG_NUM_INIT_REGS); xtrng_write_multiple_registers(rng->rng_base + TRNG_PER_STRNG_OFFSET, zero, TRNG_NUM_INIT_REGS); xtrng_hold_reset(rng->rng_base); - xilinx_rng_dev = NULL; } static const struct of_device_id xtrng_of_match[] = { From 69b31ef6f8530583dc2a09f782541fc881b38877 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 21 Jul 2026 07:46:52 -0400 Subject: [PATCH 2744/2758] exfat: replace unsafe macros with static inline functions [ Upstream commit f07db38084dc45162d5fe2871ed72674ddc0f9ae ] The current exFAT driver relies on various macros for unit conversions between clusters, blocks, sectors, and directory entries. These macros are structurally unsafe as they lack type enforcement and are prone to potential integer overflows during bit-shift operations, especially on 64-bit architectures. Replace all arithmetic macros with static inline functions to provide strict type checking and explicit casting. Acked-by: Christoph Hellwig Acked-by: "Darrick J. Wong" Signed-off-by: Namjae Jeon Stable-dep-of: 044472d5ee7d ("iomap: consolidate bio submission") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/exfat/balloc.c | 2 +- fs/exfat/dir.c | 48 ++++++++--------- fs/exfat/exfat_fs.h | 122 ++++++++++++++++++++++++++++++++------------ fs/exfat/fatent.c | 4 +- fs/exfat/file.c | 8 +-- fs/exfat/inode.c | 19 +++---- fs/exfat/namei.c | 26 +++++----- fs/exfat/super.c | 4 +- 8 files changed, 147 insertions(+), 86 deletions(-) diff --git a/fs/exfat/balloc.c b/fs/exfat/balloc.c index 625f2f14d4fe0..e66ebf899778c 100644 --- a/fs/exfat/balloc.c +++ b/fs/exfat/balloc.c @@ -112,7 +112,7 @@ static int exfat_allocate_bitmap(struct super_block *sb, } if (exfat_test_bitmap_range(sb, sbi->map_clu, - EXFAT_B_TO_CLU_ROUND_UP(map_size, sbi)) == false) + exfat_bytes_to_cluster_round_up(sbi, map_size)) == false) goto err_out; return 0; diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c index 500094c60c138..b316541a09aed 100644 --- a/fs/exfat/dir.c +++ b/fs/exfat/dir.c @@ -76,7 +76,7 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent struct super_block *sb = inode->i_sb; struct exfat_sb_info *sbi = EXFAT_SB(sb); struct exfat_inode_info *ei = EXFAT_I(inode); - unsigned int dentry = EXFAT_B_TO_DEN(*cpos) & 0xFFFFFFFF; + unsigned int dentry = exfat_bytes_to_dentries(*cpos) & 0xFFFFFFFF; struct buffer_head *bh; /* check if the given file ID is opened */ @@ -84,13 +84,13 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent return -EPERM; exfat_chain_set(&dir, ei->start_clu, - EXFAT_B_TO_CLU(i_size_read(inode), sbi), ei->flags); + exfat_bytes_to_cluster(sbi, i_size_read(inode)), ei->flags); dentries_per_clu = sbi->dentries_per_clu; - max_dentries = (unsigned int)min_t(u64, MAX_EXFAT_DENTRIES, - (u64)EXFAT_CLU_TO_DEN(sbi->num_clusters, sbi)); + max_dentries = min(MAX_EXFAT_DENTRIES, + exfat_cluster_to_dentries(sbi, sbi->num_clusters)); - clu_offset = EXFAT_DEN_TO_CLU(dentry, sbi); + clu_offset = exfat_dentries_to_cluster(sbi, dentry); exfat_chain_dup(&clu, &dir); if (clu.flags == ALLOC_FAT_CHAIN) { @@ -147,10 +147,10 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent dir_entry->dir = clu; brelse(bh); - ei->hint_bmap.off = EXFAT_DEN_TO_CLU(dentry, sbi); + ei->hint_bmap.off = exfat_dentries_to_cluster(sbi, dentry); ei->hint_bmap.clu = clu.dir; - *cpos = EXFAT_DEN_TO_B(dentry + 1 + num_ext); + *cpos = exfat_dentries_to_bytes(dentry + 1 + num_ext); return 0; } @@ -160,7 +160,7 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent out: dir_entry->namebuf.lfn[0] = '\0'; - *cpos = EXFAT_DEN_TO_B(dentry); + *cpos = exfat_dentries_to_bytes(dentry); return 0; } @@ -465,7 +465,7 @@ static void exfat_free_benign_secondary_clusters(struct inode *inode, return; exfat_chain_set(&dir, start_clu, - EXFAT_B_TO_CLU_ROUND_UP(size, EXFAT_SB(sb)), + exfat_bytes_to_cluster_round_up(EXFAT_SB(sb), size), flags); exfat_free_cluster(inode, &dir); } @@ -594,10 +594,11 @@ static int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir unsigned int off, clu = 0; struct exfat_sb_info *sbi = EXFAT_SB(sb); - off = EXFAT_DEN_TO_B(entry); + off = exfat_dentries_to_bytes(entry); clu = p_dir->dir; - ret = exfat_cluster_walk(sb, &clu, EXFAT_B_TO_CLU(off, sbi), p_dir->flags); + ret = exfat_cluster_walk(sb, &clu, exfat_bytes_to_cluster(sbi, off), + p_dir->flags); if (ret) return ret; @@ -605,7 +606,7 @@ static int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir exfat_fs_error(sb, "unexpected early break in cluster chain (clu : %u, len : %d)", p_dir->dir, - EXFAT_B_TO_CLU(off, sbi)); + exfat_bytes_to_cluster(sbi, off)); return -EIO; } @@ -615,13 +616,13 @@ static int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir } /* byte offset in cluster */ - off = EXFAT_CLU_OFFSET(off, sbi); + off = exfat_cluster_offset(sbi, off); /* byte offset in sector */ - *offset = EXFAT_BLK_OFFSET(off, sb); + *offset = exfat_block_offset(sb, off); /* sector offset in cluster */ - *sector = EXFAT_B_TO_BLK(off, sb); + *sector = exfat_bytes_to_block(sb, off); *sector += exfat_cluster_to_sector(sbi, clu); return 0; } @@ -631,7 +632,7 @@ struct exfat_dentry *exfat_get_dentry(struct super_block *sb, { struct exfat_sb_info *sbi = EXFAT_SB(sb); unsigned int sect_per_clus = sbi->sect_per_clus; - unsigned int dentries_per_page = EXFAT_B_TO_DEN(PAGE_SIZE); + unsigned int dentries_per_page = exfat_bytes_to_dentries(PAGE_SIZE); int off; sector_t sec; @@ -710,8 +711,8 @@ struct exfat_dentry *exfat_get_dentry_cached( struct exfat_entry_set_cache *es, int num) { int off = es->start_off + num * DENTRY_SIZE; - struct buffer_head *bh = es->bh[EXFAT_B_TO_BLK(off, es->sb)]; - char *p = bh->b_data + EXFAT_BLK_OFFSET(off, es->sb); + struct buffer_head *bh = es->bh[exfat_bytes_to_block(es->sb, off)]; + char *p = bh->b_data + exfat_block_offset(es->sb, off); return (struct exfat_dentry *)p; } @@ -779,7 +780,7 @@ static int __exfat_get_dentry_set(struct exfat_entry_set_cache *es, es->num_entries = num_entries; - num_bh = EXFAT_B_TO_BLK_ROUND_UP(off + num_entries * DENTRY_SIZE, sb); + num_bh = exfat_bytes_to_block_round_up(sb, off + num_entries * DENTRY_SIZE); if (num_bh > ARRAY_SIZE(es->__bh)) { es->bh = kmalloc_objs(*es->bh, num_bh, GFP_NOFS); if (!es->bh) { @@ -868,7 +869,7 @@ static int exfat_validate_empty_dentry_set(struct exfat_entry_set_cache *es) err_used_follow_unused: off = es->start_off + (i << DENTRY_SIZE_BITS); - bh = es->bh[EXFAT_B_TO_BLK(off, es->sb)]; + bh = es->bh[exfat_bytes_to_block(es->sb, off)]; exfat_fs_error(es->sb, "in sector %lld, dentry %d should be unused, but 0x%x", @@ -877,7 +878,8 @@ static int exfat_validate_empty_dentry_set(struct exfat_entry_set_cache *es) return -EIO; count_skip_entries: - es->num_entries = EXFAT_B_TO_DEN(EXFAT_BLK_TO_B(es->num_bh, es->sb) - es->start_off); + es->num_entries = + exfat_bytes_to_dentries(exfat_block_to_bytes(es->sb, es->num_bh) - es->start_off); for (; i < es->num_entries; i++) { ep = exfat_get_dentry_cached(es, i); if (IS_EXFAT_DELETED(ep->type)) @@ -930,7 +932,7 @@ static inline void exfat_set_empty_hint(struct exfat_inode_info *ei, { if (ei->hint_femp.eidx == EXFAT_HINT_NONE || ei->hint_femp.eidx > dentry) { - int total_entries = EXFAT_B_TO_DEN(i_size_read(&ei->vfs_inode)); + int total_entries = exfat_bytes_to_dentries(i_size_read(&ei->vfs_inode)); if (candi_empty->count == 0) { candi_empty->cur = *clu; @@ -1258,7 +1260,7 @@ static int exfat_get_volume_label_dentry(struct super_block *sb, es->bh = es->__bh; es->bh[0] = bh; es->num_bh = 1; - es->start_off = EXFAT_DEN_TO_B(i) % sb->s_blocksize; + es->start_off = exfat_dentries_to_bytes(i) % sb->s_blocksize; return 0; } diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h index e22b4ca3ec7fb..6ec480b53fe6d 100644 --- a/fs/exfat/exfat_fs.h +++ b/fs/exfat/exfat_fs.h @@ -84,38 +84,6 @@ enum { (min_t(blkcnt_t, (sb)->s_bdi->ra_pages, (sb)->s_bdi->io_pages) \ << (PAGE_SHIFT - (sb)->s_blocksize_bits)) -/* - * helpers for cluster size to byte conversion. - */ -#define EXFAT_CLU_TO_B(b, sbi) ((b) << (sbi)->cluster_size_bits) -#define EXFAT_B_TO_CLU(b, sbi) ((b) >> (sbi)->cluster_size_bits) -#define EXFAT_B_TO_CLU_ROUND_UP(b, sbi) \ - (((b - 1) >> (sbi)->cluster_size_bits) + 1) -#define EXFAT_CLU_OFFSET(off, sbi) ((off) & ((sbi)->cluster_size - 1)) - -/* - * helpers for block size to byte conversion. - */ -#define EXFAT_BLK_TO_B(b, sb) ((b) << (sb)->s_blocksize_bits) -#define EXFAT_B_TO_BLK(b, sb) ((b) >> (sb)->s_blocksize_bits) -#define EXFAT_B_TO_BLK_ROUND_UP(b, sb) \ - (((b - 1) >> (sb)->s_blocksize_bits) + 1) -#define EXFAT_BLK_OFFSET(off, sb) ((off) & ((sb)->s_blocksize - 1)) - -/* - * helpers for block size to dentry size conversion. - */ -#define EXFAT_B_TO_DEN(b) ((b) >> DENTRY_SIZE_BITS) -#define EXFAT_DEN_TO_B(b) ((b) << DENTRY_SIZE_BITS) - -/* - * helpers for cluster size to dentry size conversion. - */ -#define EXFAT_CLU_TO_DEN(clu, sbi) \ - ((clu) << ((sbi)->cluster_size_bits - DENTRY_SIZE_BITS)) -#define EXFAT_DEN_TO_CLU(dentry, sbi) \ - ((dentry) >> ((sbi)->cluster_size_bits - DENTRY_SIZE_BITS)) - /* * helpers for fat entry. */ @@ -149,7 +117,7 @@ enum { * The 608 bytes are in 3 sectors at most (even 512 Byte sector). */ #define DIR_CACHE_SIZE \ - (DIV_ROUND_UP(EXFAT_DEN_TO_B(ES_MAX_ENTRY_NUM), SECTOR_SIZE) + 1) + (DIV_ROUND_UP(ES_MAX_ENTRY_NUM << DENTRY_SIZE_BITS, SECTOR_SIZE) + 1) /* Superblock flags */ #define EXFAT_FLAGS_SHUTDOWN 1 @@ -432,6 +400,94 @@ static inline loff_t exfat_ondisk_size(const struct inode *inode) return ((loff_t)inode->i_blocks) << 9; } +/* + * helpers for cluster size to byte conversion. + */ +static inline loff_t exfat_cluster_to_bytes(struct exfat_sb_info *sbi, + u32 nr_clusters) +{ + return (loff_t)nr_clusters << sbi->cluster_size_bits; +} + +static inline blkcnt_t exfat_cluster_to_sectors(struct exfat_sb_info *sbi, + u32 nr_clusters) +{ + return (blkcnt_t)nr_clusters << (sbi->cluster_size_bits - 9); +} + +static inline u32 exfat_bytes_to_cluster(struct exfat_sb_info *sbi, loff_t size) +{ + return (u32)(size >> sbi->cluster_size_bits); +} + +static inline u32 exfat_bytes_to_cluster_round_up(struct exfat_sb_info *sbi, + loff_t size) +{ + if (size <= 0) + return 0; + return (u32)((size - 1) >> sbi->cluster_size_bits) + 1; +} + +static inline u32 exfat_cluster_offset(struct exfat_sb_info *sbi, loff_t off) +{ + return off & (sbi->cluster_size - 1); +} + +/* + * helpers for block size to byte conversion. + */ +static inline loff_t exfat_block_to_bytes(struct super_block *sb, + sector_t block) +{ + return (loff_t)block << sb->s_blocksize_bits; +} + +static inline sector_t exfat_bytes_to_block(struct super_block *sb, loff_t size) +{ + return (sector_t)(size >> sb->s_blocksize_bits); +} + +static inline sector_t exfat_bytes_to_block_round_up(struct super_block *sb, + loff_t size) +{ + if (size <= 0) + return 0; + return (sector_t)(((size - 1) >> sb->s_blocksize_bits) + 1); +} + +static inline u32 exfat_block_offset(struct super_block *sb, loff_t off) +{ + return (u32)(off & (sb->s_blocksize - 1)); +} + +/* + * helpers for block size to dentry size conversion. + */ +static inline u32 exfat_bytes_to_dentries(loff_t b) +{ + return (u32)(b >> DENTRY_SIZE_BITS); +} + +static inline u32 exfat_dentries_to_bytes(u32 dentry) +{ + return dentry << DENTRY_SIZE_BITS; +} + +/* + * helpers for cluster size to dentry size conversion. + */ +static inline u32 exfat_cluster_to_dentries(struct exfat_sb_info *sbi, + u32 nr_clusters) +{ + return nr_clusters << (sbi->cluster_size_bits - DENTRY_SIZE_BITS); +} + +static inline u32 exfat_dentries_to_cluster(struct exfat_sb_info *sbi, + u32 dentry) +{ + return dentry >> (sbi->cluster_size_bits - DENTRY_SIZE_BITS); +} + /* super.c */ int exfat_set_volume_dirty(struct super_block *sb); int exfat_clear_volume_dirty(struct super_block *sb); diff --git a/fs/exfat/fatent.c b/fs/exfat/fatent.c index dce0955e689aa..45b0b754a2e4b 100644 --- a/fs/exfat/fatent.c +++ b/fs/exfat/fatent.c @@ -412,8 +412,8 @@ int exfat_zeroed_cluster(struct inode *dir, unsigned int clu) if (IS_DIRSYNC(dir)) return sync_blockdev_range(sb->s_bdev, - EXFAT_BLK_TO_B(blknr, sb), - EXFAT_BLK_TO_B(last_blknr, sb) - 1); + exfat_block_to_bytes(sb, blknr), + exfat_block_to_bytes(sb, last_blknr) - 1); return 0; } diff --git a/fs/exfat/file.c b/fs/exfat/file.c index 354bdcfe4abcd..29a36a80e29bc 100644 --- a/fs/exfat/file.c +++ b/fs/exfat/file.c @@ -33,9 +33,9 @@ static int exfat_cont_expand(struct inode *inode, loff_t size) if (ret) return ret; - num_clusters = EXFAT_B_TO_CLU(exfat_ondisk_size(inode), sbi); + num_clusters = exfat_bytes_to_cluster(sbi, exfat_ondisk_size(inode)); /* integer overflow is already checked in inode_newsize_ok(). */ - new_num_clusters = EXFAT_B_TO_CLU_ROUND_UP(size, sbi); + new_num_clusters = exfat_bytes_to_cluster_round_up(sbi, size); if (new_num_clusters == num_clusters) goto out; @@ -200,8 +200,8 @@ int __exfat_truncate(struct inode *inode) exfat_set_volume_dirty(sb); - num_clusters_new = EXFAT_B_TO_CLU_ROUND_UP(i_size_read(inode), sbi); - num_clusters_phys = EXFAT_B_TO_CLU(exfat_ondisk_size(inode), sbi); + num_clusters_new = exfat_bytes_to_cluster_round_up(sbi, i_size_read(inode)); + num_clusters_phys = exfat_bytes_to_cluster(sbi, exfat_ondisk_size(inode)); exfat_chain_set(&clu, ei->start_clu, num_clusters_phys, ei->flags); diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c index 1ea4c740fef9e..249a35e2b4b23 100644 --- a/fs/exfat/inode.c +++ b/fs/exfat/inode.c @@ -135,7 +135,7 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset, unsigned int local_clu_offset = clu_offset; unsigned int num_to_be_allocated = 0, num_clusters; - num_clusters = EXFAT_B_TO_CLU(exfat_ondisk_size(inode), sbi); + num_clusters = exfat_bytes_to_cluster(sbi, exfat_ondisk_size(inode)); if (clu_offset >= num_clusters) num_to_be_allocated = clu_offset - num_clusters + 1; @@ -216,7 +216,8 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset, *clu = new_clu.dir; - inode->i_blocks += EXFAT_CLU_TO_B(num_to_be_allocated, sbi) >> 9; + inode->i_blocks += + exfat_cluster_to_sectors(sbi, num_to_be_allocated); /* * Move *clu pointer along FAT chains (hole care) because the @@ -254,12 +255,12 @@ static int exfat_get_block(struct inode *inode, sector_t iblock, mutex_lock(&sbi->s_lock); i_size = i_size_read(inode); - last_block = EXFAT_B_TO_BLK_ROUND_UP(i_size, sb); + last_block = exfat_bytes_to_block_round_up(sb, i_size); if (iblock >= last_block && !create) goto done; /* Is this block already allocated? */ - count = EXFAT_B_TO_CLU_ROUND_UP(bh_result->b_size, sbi); + count = exfat_bytes_to_cluster_round_up(sbi, bh_result->b_size); err = exfat_map_cluster(inode, iblock >> sbi->sect_per_clus_bits, &cluster, &count, create); if (err) { @@ -296,9 +297,9 @@ static int exfat_get_block(struct inode *inode, sector_t iblock, * care the last nested block if valid_size is not equal to i_size. */ if (i_size == ei->valid_size || create || !bh_result->b_folio) - valid_blks = EXFAT_B_TO_BLK_ROUND_UP(ei->valid_size, sb); + valid_blks = exfat_bytes_to_block_round_up(sb, ei->valid_size); else - valid_blks = EXFAT_B_TO_BLK(ei->valid_size, sb); + valid_blks = exfat_bytes_to_block(sb, ei->valid_size); /* The range has been fully written, map it */ if (iblock + max_blocks < valid_blks) @@ -313,7 +314,7 @@ static int exfat_get_block(struct inode *inode, sector_t iblock, /* The area has not been written, map and mark as new for create case */ if (create) { set_buffer_new(bh_result); - ei->valid_size = EXFAT_BLK_TO_B(iblock + max_blocks, sb); + ei->valid_size = exfat_block_to_bytes(sb, iblock + max_blocks); mark_inode_dirty(inode); goto done; } @@ -343,7 +344,7 @@ static int exfat_get_block(struct inode *inode, sector_t iblock, goto done; } - pos = EXFAT_BLK_TO_B(iblock, sb); + pos = exfat_block_to_bytes(sb, iblock); size = ei->valid_size - pos; addr = folio_address(bh_result->b_folio) + offset_in_folio(bh_result->b_folio, pos); @@ -374,7 +375,7 @@ static int exfat_get_block(struct inode *inode, sector_t iblock, */ clear_buffer_mapped(bh_result); done: - bh_result->b_size = EXFAT_BLK_TO_B(max_blocks, sb); + bh_result->b_size = exfat_block_to_bytes(sb, max_blocks); if (err < 0) clear_buffer_mapped(bh_result); unlock_ret: diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c index 76b2e2db80fba..3191b88113e3b 100644 --- a/fs/exfat/namei.c +++ b/fs/exfat/namei.c @@ -208,7 +208,7 @@ static int exfat_search_empty_slot(struct super_block *sb, int dentries_per_clu; struct exfat_chain clu; struct exfat_sb_info *sbi = EXFAT_SB(sb); - int total_entries = EXFAT_CLU_TO_DEN(p_dir->size, sbi); + unsigned int total_entries = exfat_cluster_to_dentries(sbi, p_dir->size); dentries_per_clu = sbi->dentries_per_clu; @@ -266,7 +266,7 @@ static int exfat_search_empty_slot(struct super_block *sb, static int exfat_check_max_dentries(struct inode *inode) { - if (EXFAT_B_TO_DEN(i_size_read(inode)) >= MAX_EXFAT_DENTRIES) { + if (exfat_bytes_to_dentries(i_size_read(inode)) >= MAX_EXFAT_DENTRIES) { /* * exFAT spec allows a dir to grow up to 8388608(256MB) * dentries @@ -314,7 +314,8 @@ int exfat_find_empty_entry(struct inode *inode, } exfat_chain_set(p_dir, ei->start_clu, - EXFAT_B_TO_CLU(i_size_read(inode), sbi), ei->flags); + exfat_bytes_to_cluster(sbi, i_size_read(inode)), + ei->flags); while ((dentry = exfat_search_empty_slot(sb, &hint_femp, p_dir, num_entries, es)) < 0) { @@ -375,7 +376,7 @@ int exfat_find_empty_entry(struct inode *inode, hint_femp.cur.size++; p_dir->size++; - size = EXFAT_CLU_TO_B(p_dir->size, sbi); + size = exfat_cluster_to_bytes(sbi, p_dir->size); /* directory inode should be updated in here */ i_size_write(inode, size); @@ -604,7 +605,7 @@ static int exfat_find(struct inode *dir, const struct qstr *qname, return ret; exfat_chain_set(&cdir, ei->start_clu, - EXFAT_B_TO_CLU(i_size_read(dir), sbi), ei->flags); + exfat_bytes_to_cluster(sbi, i_size_read(dir)), ei->flags); /* check the validation of hint_stat and initialize it if required */ if (ei->version != (inode_peek_iversion_raw(dir) & 0xffffffff)) { @@ -681,7 +682,7 @@ static int exfat_find(struct inode *dir, const struct qstr *qname, return -EIO; } - if (unlikely(EXFAT_B_TO_CLU_ROUND_UP(info->size, sbi) > sbi->used_clusters)) { + if (unlikely(exfat_bytes_to_cluster_round_up(sbi, info->size) > sbi->used_clusters)) { exfat_fs_error(sb, "data size is invalid(%lld)", info->size); return -EIO; } @@ -695,7 +696,8 @@ static int exfat_find(struct inode *dir, const struct qstr *qname, if (info->type == TYPE_DIR) { exfat_chain_set(&cdir, info->start_clu, - EXFAT_B_TO_CLU(info->size, sbi), info->flags); + exfat_bytes_to_cluster(sbi, info->size), + info->flags); count = exfat_count_dir_entries(sb, &cdir); if (count < 0) return -EIO; @@ -951,7 +953,7 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry) } exfat_chain_set(&clu_to_free, ei->start_clu, - EXFAT_B_TO_CLU_ROUND_UP(i_size_read(inode), sbi), ei->flags); + exfat_bytes_to_cluster_round_up(sbi, i_size_read(inode)), ei->flags); err = exfat_check_dir_empty(sb, &clu_to_free); if (err) { @@ -1207,8 +1209,8 @@ static int __exfat_rename(struct inode *old_parent_inode, new_clu.dir = new_ei->start_clu; new_clu.size = - EXFAT_B_TO_CLU_ROUND_UP(i_size_read(new_inode), - sbi); + exfat_bytes_to_cluster_round_up(sbi, + i_size_read(new_inode)); new_clu.flags = new_ei->flags; ret = exfat_check_dir_empty(sb, &new_clu); @@ -1252,8 +1254,8 @@ static int __exfat_rename(struct inode *old_parent_inode, struct exfat_chain new_clu_to_free; exfat_chain_set(&new_clu_to_free, new_ei->start_clu, - EXFAT_B_TO_CLU_ROUND_UP(i_size_read(new_inode), - sbi), new_ei->flags); + exfat_bytes_to_cluster_round_up(sbi, i_size_read(new_inode)), + new_ei->flags); if (exfat_free_cluster(new_inode, &new_clu_to_free)) { /* just set I/O error only */ diff --git a/fs/exfat/super.c b/fs/exfat/super.c index 95d87e2d7717f..cb2f8eefff995 100644 --- a/fs/exfat/super.c +++ b/fs/exfat/super.c @@ -369,7 +369,7 @@ static int exfat_read_root(struct inode *inode, struct exfat_chain *root_clu) ei->hint_stat.clu = sbi->root_dir; ei->hint_femp.eidx = EXFAT_HINT_NONE; - i_size_write(inode, EXFAT_CLU_TO_B(root_clu->size, sbi)); + i_size_write(inode, exfat_cluster_to_bytes(sbi, root_clu->size)); num_subdirs = exfat_count_dir_entries(sb, root_clu); if (num_subdirs < 0) @@ -538,7 +538,7 @@ static int exfat_read_boot_sector(struct super_block *sb) * machines. */ sb->s_maxbytes = min(MAX_LFS_FILESIZE, - EXFAT_CLU_TO_B((loff_t)EXFAT_MAX_NUM_CLUSTER, sbi)); + exfat_cluster_to_bytes(sbi, (loff_t)EXFAT_MAX_NUM_CLUSTER)); /* check logical sector size */ if (exfat_calibrate_blocksize(sb, 1 << p_boot->sect_size_bits)) From 390f1d72a478b5e259d12565d6ad9b79027fed46 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 21 Jul 2026 07:46:53 -0400 Subject: [PATCH 2745/2758] exfat: add balloc parameter to exfat_map_cluster() for iomap support [ Upstream commit 3474416bb9abafca2e79b7ff700a100b7e8bc766 ] In preparation for supporting the iomap infrastructure, we need to know whether a new cluster was allocated or not in exfat_map_cluster(). Add an optional 'bool *balloc' output parameter. When a new cluster is allocated, *balloc is set to true. Pass NULL from exfat_get_block() to preserve the existing behavior. Acked-by: Christoph Hellwig Acked-by: "Darrick J. Wong" Signed-off-by: Namjae Jeon Stable-dep-of: 044472d5ee7d ("iomap: consolidate bio submission") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/exfat/inode.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c index 249a35e2b4b23..a10d4f3c66a17 100644 --- a/fs/exfat/inode.c +++ b/fs/exfat/inode.c @@ -124,7 +124,8 @@ void exfat_sync_inode(struct inode *inode) * *clu = (~0), if it's unable to allocate a new cluster */ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset, - unsigned int *clu, unsigned int *count, int create) + unsigned int *clu, unsigned int *count, int create, + bool *balloc) { int ret; unsigned int last_clu; @@ -229,6 +230,8 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset, if (exfat_cluster_walk(sb, clu, num_to_be_allocated - 1, ei->flags)) return -EIO; *count = 1; + if (balloc) + *balloc = true; } /* hint information */ @@ -262,7 +265,7 @@ static int exfat_get_block(struct inode *inode, sector_t iblock, /* Is this block already allocated? */ count = exfat_bytes_to_cluster_round_up(sbi, bh_result->b_size); err = exfat_map_cluster(inode, iblock >> sbi->sect_per_clus_bits, - &cluster, &count, create); + &cluster, &count, create, NULL); if (err) { if (err != -ENOSPC) exfat_fs_error_ratelimit(sb, From 8e4e884f1bef941d0940f3f321ad5aee5131a9ac Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 21 Jul 2026 07:46:54 -0400 Subject: [PATCH 2746/2758] exfat: add data_start_bytes and exfat_cluster_to_phys_bytes() helper [ Upstream commit 3b9450beeb961c7ddf4cccddba37fcfa6d1fcb7a ] This caches the data area start offset in bytes (data_start_bytes) and introduces a helper function exfat_cluster_to_phys_bytes() to compute the physical byte position of a given cluster. Acked-by: Christoph Hellwig Acked-by: "Darrick J. Wong" Signed-off-by: Namjae Jeon Stable-dep-of: 044472d5ee7d ("iomap: consolidate bio submission") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/exfat/exfat_fs.h | 8 ++++++++ fs/exfat/super.c | 1 + 2 files changed, 9 insertions(+) diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h index 6ec480b53fe6d..d2eec3a0bbc07 100644 --- a/fs/exfat/exfat_fs.h +++ b/fs/exfat/exfat_fs.h @@ -227,6 +227,7 @@ struct exfat_sb_info { unsigned long long FAT1_start_sector; /* FAT1 start sector */ unsigned long long FAT2_start_sector; /* FAT2 start sector */ unsigned long long data_start_sector; /* data area start sector */ + unsigned long long data_start_bytes; unsigned int num_FAT_sectors; /* num of FAT sectors */ unsigned int root_dir; /* root dir cluster */ unsigned int dentries_per_clu; /* num of dentries per cluster */ @@ -400,6 +401,13 @@ static inline loff_t exfat_ondisk_size(const struct inode *inode) return ((loff_t)inode->i_blocks) << 9; } +static inline loff_t exfat_cluster_to_phys_bytes(struct exfat_sb_info *sbi, + unsigned int clus) +{ + return ((loff_t)(clus - EXFAT_RESERVED_CLUSTERS) << sbi->cluster_size_bits) + + sbi->data_start_bytes; +} + /* * helpers for cluster size to byte conversion. */ diff --git a/fs/exfat/super.c b/fs/exfat/super.c index cb2f8eefff995..388db271c6bf5 100644 --- a/fs/exfat/super.c +++ b/fs/exfat/super.c @@ -499,6 +499,7 @@ static int exfat_read_boot_sector(struct super_block *sb) if (p_boot->num_fats == 2) sbi->FAT2_start_sector += sbi->num_FAT_sectors; sbi->data_start_sector = le32_to_cpu(p_boot->clu_offset); + sbi->data_start_bytes = sbi->data_start_sector << p_boot->sect_size_bits; sbi->num_sectors = le64_to_cpu(p_boot->vol_length); /* because the cluster index starts with 2 */ sbi->num_clusters = le32_to_cpu(p_boot->clu_count) + From e2f0122bd56655237b77d6a3a4673700aa4fbf08 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 21 Jul 2026 07:46:55 -0400 Subject: [PATCH 2747/2758] exfat: fix implicit declaration of brelse() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit f0d1b2e1e02c11e29c953f22a485449e3da2a575 ] exfat_cluster_walk() calls brelse(bh) without including the header that declares the function, causing the following build error: fs/exfat/exfat_fs.h:542:9: error: implicit declaration of function ‘brelse’ [-Werror=implicit-function-declaration] Fix this by adding the missing buffer_head.h in exfat_fs.h. Acked-by: Christoph Hellwig Acked-by: "Darrick J. Wong" Signed-off-by: Namjae Jeon Stable-dep-of: 044472d5ee7d ("iomap: consolidate bio submission") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/exfat/exfat_fs.h | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h index d2eec3a0bbc07..c634540e0de62 100644 --- a/fs/exfat/exfat_fs.h +++ b/fs/exfat/exfat_fs.h @@ -12,6 +12,7 @@ #include #include #include +#include #define EXFAT_ROOT_INO 1 From 570967e9c615ccea7a819cdaf4647531f71b05cb Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 21 Jul 2026 07:46:56 -0400 Subject: [PATCH 2748/2758] iomap: consolidate bio submission [ Upstream commit 044472d5ee7d71f918fa3f61bd65e4933a0c006e ] Add a iomap_bio_submit_read_endio helper factored out of iomap_bio_submit_read to that all ->submit_read implementations for iomap_read_ops that use iomap_bio_read_folio_range can shared the logic. Right now that logic is mostly trivial, but already has a bug for XFS because the XFS version is too trivial: file system integrity validation needs a workqueue context and thus can't happen from the default iomap bi_end_io I/O handler. Unfortunately the iomap refactoring just before fs integrity landed moved code around here and the call go misplaced, meaning it never got called. The PI information still is verified by the block layer, but the offloading is less efficient (and the future userspace interface can't get at it). Fixes: 0b10a370529c ("iomap: support T10 protection information") Cc: stable@vger.kernel.org # v7.1 Signed-off-by: Christoph Hellwig Link: https://patch.msgid.link/20260629121750.3392300-2-hch@lst.de Acked-by: Namjae Jeon Reviewed-by: "Darrick J. Wong" Reviewed-by: Joanne Koong Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/iomap/bio.c | 13 ++++++++++--- fs/ntfs/aops.c | 6 ++---- fs/ntfs3/inode.c | 5 +---- fs/xfs/xfs_aops.c | 3 +-- include/linux/iomap.h | 2 ++ 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/fs/iomap/bio.c b/fs/iomap/bio.c index 4504f4633f175..0f31e35567b4b 100644 --- a/fs/iomap/bio.c +++ b/fs/iomap/bio.c @@ -78,15 +78,23 @@ u32 iomap_finish_ioend_buffered_read(struct iomap_ioend *ioend) return __iomap_read_end_io(&ioend->io_bio, ioend->io_error); } -static void iomap_bio_submit_read(const struct iomap_iter *iter, - struct iomap_read_folio_ctx *ctx) +void iomap_bio_submit_read_endio(const struct iomap_iter *iter, + struct iomap_read_folio_ctx *ctx, bio_end_io_t end_io) { struct bio *bio = ctx->read_ctx; + bio->bi_end_io = end_io; if (iter->iomap.flags & IOMAP_F_INTEGRITY) fs_bio_integrity_alloc(bio); submit_bio(bio); } +EXPORT_SYMBOL_GPL(iomap_bio_submit_read_endio); + +static void iomap_bio_submit_read(const struct iomap_iter *iter, + struct iomap_read_folio_ctx *ctx) +{ + return iomap_bio_submit_read_endio(iter, ctx, iomap_read_end_io); +} static struct bio_set *iomap_read_bio_set(struct iomap_read_folio_ctx *ctx) { @@ -127,7 +135,6 @@ static void iomap_read_alloc_bio(const struct iomap_iter *iter, if (ctx->rac) bio->bi_opf |= REQ_RAHEAD; bio->bi_iter.bi_sector = iomap_sector(iomap, iter->pos); - bio->bi_end_io = iomap_read_end_io; bio_add_folio_nofail(bio, folio, plen, offset_in_folio(folio, iter->pos)); ctx->read_ctx = bio; diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c index 88e5b2def66ca..173de4cbee0f7 100644 --- a/fs/ntfs/aops.c +++ b/fs/ntfs/aops.c @@ -38,11 +38,9 @@ static void ntfs_iomap_read_end_io(struct bio *bio) } static void ntfs_iomap_bio_submit_read(const struct iomap_iter *iter, - struct iomap_read_folio_ctx *ctx) + struct iomap_read_folio_ctx *ctx) { - struct bio *bio = ctx->read_ctx; - bio->bi_end_io = ntfs_iomap_read_end_io; - submit_bio(bio); + iomap_bio_submit_read_endio(iter, ctx, ntfs_iomap_read_end_io); } static const struct iomap_read_ops ntfs_iomap_bio_read_ops = { diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c index 031f85fd53d4e..b6c06fe62a9d8 100644 --- a/fs/ntfs3/inode.c +++ b/fs/ntfs3/inode.c @@ -609,10 +609,7 @@ static void ntfs_iomap_read_end_io(struct bio *bio) static void ntfs_iomap_bio_submit_read(const struct iomap_iter *iter, struct iomap_read_folio_ctx *ctx) { - struct bio *bio = ctx->read_ctx; - - bio->bi_end_io = ntfs_iomap_read_end_io; - submit_bio(bio); + iomap_bio_submit_read_endio(iter, ctx, ntfs_iomap_read_end_io); } static const struct iomap_read_ops ntfs_iomap_bio_read_ops = { diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c index f279055fcea03..a93d8e2cef402 100644 --- a/fs/xfs/xfs_aops.c +++ b/fs/xfs/xfs_aops.c @@ -753,8 +753,7 @@ xfs_bio_submit_read( /* defer read completions to the ioend workqueue */ iomap_init_ioend(iter->inode, bio, ctx->read_ctx_file_offset, 0); - bio->bi_end_io = xfs_end_bio; - submit_bio(bio); + iomap_bio_submit_read_endio(iter, ctx, xfs_end_bio); } static const struct iomap_read_ops xfs_iomap_read_ops = { diff --git a/include/linux/iomap.h b/include/linux/iomap.h index 2c5685adf3a97..45b6888af4c6e 100644 --- a/include/linux/iomap.h +++ b/include/linux/iomap.h @@ -615,6 +615,8 @@ extern struct bio_set iomap_ioend_bioset; #ifdef CONFIG_BLOCK int iomap_bio_read_folio_range(const struct iomap_iter *iter, struct iomap_read_folio_ctx *ctx, size_t plen); +void iomap_bio_submit_read_endio(const struct iomap_iter *iter, + struct iomap_read_folio_ctx *ctx, bio_end_io_t end_io); extern const struct iomap_read_ops iomap_bio_read_ops; From 5638dbfe9cf11e98a4362d2e2226eb8561cccb94 Mon Sep 17 00:00:00 2001 From: Dillon Varone Date: Thu, 19 Mar 2026 11:57:08 -0400 Subject: [PATCH 2749/2758] drm/amd/display: Fix Color Manager (3DLUT, Shaper, Blend) commit 12f58a6caad3be54d7788b338b3f57d7c17bbef7 upstream. [WHY & HOW] The original refactor and fixes are causing regressions. Revert them for now until they can be resolved Fixes: e56e3cff2a1b ("drm/amd/display: Sync dcn42 with DC 3.2.373") Reviewed-by: Tom Chung Signed-off-by: Dillon Varone Signed-off-by: James Lin Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/display/dc/core/dc.c | 81 +-- .../gpu/drm/amd/display/dc/core/dc_stream.c | 2 + drivers/gpu/drm/amd/display/dc/dc.h | 58 +- drivers/gpu/drm/amd/display/dc/dc_types.h | 24 - .../amd/display/dc/hubp/dcn401/dcn401_hubp.c | 201 +++---- .../amd/display/dc/hubp/dcn401/dcn401_hubp.h | 23 +- .../amd/display/dc/hubp/dcn42/dcn42_hubp.c | 78 +-- .../amd/display/dc/hubp/dcn42/dcn42_hubp.h | 10 +- .../amd/display/dc/hwss/dcn401/dcn401_hwseq.c | 389 +++++++++----- .../amd/display/dc/hwss/dcn42/dcn42_hwseq.c | 499 ++++++++++++++++-- .../amd/display/dc/hwss/dcn42/dcn42_hwseq.h | 9 +- drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h | 35 +- drivers/gpu/drm/amd/display/dc/inc/hw/mpc.h | 79 ++- .../amd/display/dc/mpc/dcn401/dcn401_mpc.c | 177 +++---- .../amd/display/dc/mpc/dcn401/dcn401_mpc.h | 25 +- .../drm/amd/display/dc/mpc/dcn42/dcn42_mpc.c | 388 ++++++++++++-- .../drm/amd/display/dc/mpc/dcn42/dcn42_mpc.h | 49 +- .../dc/resource/dcn42/dcn42_resource.c | 1 - 18 files changed, 1439 insertions(+), 689 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index b3530fbf32f7f..5f2c62fcba9bf 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -2956,27 +2956,16 @@ static struct surface_update_descriptor det_surface_update( elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM); } - if (u->cm || (u->gamma && dce_use_lut(u->plane_info ? u->plane_info->format : u->surface->format))) { + if (u->blend_tf || (u->gamma && dce_use_lut(u->plane_info ? u->plane_info->format : u->surface->format))) { update_flags->bits.gamma_change = 1; elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM); } - if (u->cm && (u->cm->flags.bits.lut3d_enable || u->surface->cm.flags.bits.lut3d_enable)) { + if (u->lut3d_func || u->func_shaper) { update_flags->bits.lut_3d = 1; elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM); } - if (u->cm && u->cm->flags.bits.lut3d_dma_enable != u->surface->cm.flags.bits.lut3d_dma_enable && - u->cm->flags.bits.lut3d_enable && u->surface->cm.flags.bits.lut3d_enable) { - /* Toggling 3DLUT loading between DMA and Host is illegal */ - BREAK_TO_DEBUGGER(); - } - - if (u->cm && u->cm->flags.bits.lut3d_enable && !u->cm->flags.bits.lut3d_dma_enable) { - /* Host loading 3DLUT requires full update but only stream lock */ - elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_STREAM); - } - if (u->hdr_mult.value) if (u->hdr_mult.value != u->surface->hdr_mult.value) { // TODO: Should be fast? @@ -2995,6 +2984,20 @@ static struct surface_update_descriptor det_surface_update( update_flags->bits.cm_hist_change = 1; elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM); } + if (u->cm2_params) { + if (u->cm2_params->component_settings.shaper_3dlut_setting != u->surface->mcm_shaper_3dlut_setting + || u->cm2_params->component_settings.lut1d_enable != u->surface->mcm_lut1d_enable + || u->cm2_params->cm2_luts.lut3d_data.lut3d_src != u->surface->mcm_luts.lut3d_data.lut3d_src) { + update_flags->bits.mcm_transfer_function_enable_change = 1; + elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL); + } + } + + if (update_flags->bits.lut_3d && + u->surface->mcm_luts.lut3d_data.lut3d_src != DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM) { + elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL); + } + if (check_config->enable_legacy_fast_update && (update_flags->bits.gamma_change || update_flags->bits.gamut_remap_change || @@ -3250,12 +3253,24 @@ static void copy_surface_update_to_plane( sizeof(struct dc_transfer_func_distributed_points)); } - /* Shaper, 3DLUT, 1DLUT */ - if (srf_update->cm) { - memcpy(&surface->cm, srf_update->cm, - sizeof(surface->cm)); + if (srf_update->cm2_params) { + surface->mcm_shaper_3dlut_setting = srf_update->cm2_params->component_settings.shaper_3dlut_setting; + surface->mcm_lut1d_enable = srf_update->cm2_params->component_settings.lut1d_enable; + surface->mcm_luts = srf_update->cm2_params->cm2_luts; } + if (srf_update->func_shaper) { + memcpy(&surface->in_shaper_func, srf_update->func_shaper, + sizeof(surface->in_shaper_func)); + + if (surface->mcm_shaper_3dlut_setting >= DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER) + surface->mcm_luts.shaper = &surface->in_shaper_func; + } + + if (srf_update->lut3d_func) + memcpy(&surface->lut3d_func, srf_update->lut3d_func, + sizeof(surface->lut3d_func)); + if (srf_update->hdr_mult.value) surface->hdr_mult = srf_update->hdr_mult; @@ -3264,6 +3279,17 @@ static void copy_surface_update_to_plane( surface->sdr_white_level_nits = srf_update->sdr_white_level_nits; + if (srf_update->blend_tf) { + memcpy(&surface->blend_tf, srf_update->blend_tf, + sizeof(surface->blend_tf)); + + if (surface->mcm_lut1d_enable) + surface->mcm_luts.lut1d_func = &surface->blend_tf; + } + + if (srf_update->cm2_params || srf_update->blend_tf) + surface->lut_bank_a = !surface->lut_bank_a; + if (srf_update->input_csc_color_matrix) surface->input_csc_color_matrix = *srf_update->input_csc_color_matrix; @@ -4550,9 +4576,11 @@ static void commit_planes_for_stream(struct dc *dc, if (!should_update_pipe_for_plane(context, pipe_ctx, plane_state)) continue; - if (srf_updates[i].cm && - srf_updates[i].cm->flags.bits.lut3d_enable && - srf_updates[i].cm->flags.bits.lut3d_dma_enable && + if (srf_updates[i].cm2_params && + srf_updates[i].cm2_params->cm2_luts.lut3d_data.lut3d_src == + DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM && + srf_updates[i].cm2_params->component_settings.shaper_3dlut_setting == + DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER_3DLUT && dc->hwss.trigger_3dlut_dma_load) dc->hwss.trigger_3dlut_dma_load(dc, pipe_ctx); @@ -5209,12 +5237,6 @@ static bool full_update_required( const struct dc_stream_update *stream_update, const struct dc_stream_state *stream) { - const union dc_plane_cm_flags blend_only_flags = { - .bits = { - .blend_enable = 1, - } - }; - if (full_update_required_weak(dc, srf_updates, surface_count, stream_update, stream)) return true; @@ -5229,10 +5251,7 @@ static bool full_update_required( srf_updates[i].in_transfer_func || srf_updates[i].surface->force_full_update || (srf_updates[i].flip_addr && - srf_updates[i].flip_addr->address.tmz_surface != srf_updates[i].surface->address.tmz_surface) || - (srf_updates[i].cm && - ((srf_updates[i].cm->flags.all != blend_only_flags.all && srf_updates[i].cm->flags.all != 0) || - (srf_updates[i].surface->cm.flags.all != blend_only_flags.all && srf_updates[i].surface->cm.flags.all != 0))))) + srf_updates[i].flip_addr->address.tmz_surface != srf_updates[i].surface->address.tmz_surface))) return true; } @@ -6966,7 +6985,7 @@ bool dc_capture_register_software_state(struct dc *dc, struct dc_register_softwa struct dc_plane_state *plane_state = pipe_ctx->plane_state; /* MPCC blending tree and mode control - capture actual blend configuration */ - state->mpc.mpcc_mode[i] = (plane_state->cm.blend_func.type != TF_TYPE_BYPASS) ? 1 : 0; + state->mpc.mpcc_mode[i] = (plane_state->blend_tf.type != TF_TYPE_BYPASS) ? 1 : 0; state->mpc.mpcc_alpha_blend_mode[i] = plane_state->per_pixel_alpha ? 1 : 0; state->mpc.mpcc_alpha_multiplied_mode[i] = plane_state->pre_multiplied_alpha ? 1 : 0; state->mpc.mpcc_blnd_active_overlap_only[i] = 0; /* Default - no overlap restriction */ diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c index e16de323f39c0..717451828361a 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c @@ -996,6 +996,7 @@ void dc_stream_release_3dlut_for_stream( if (rmcm_3dlut) { rmcm_3dlut->isInUse = false; rmcm_3dlut->stream = NULL; + rmcm_3dlut->protection_bits = 0; } } @@ -1007,6 +1008,7 @@ void dc_stream_init_rmcm_3dlut(struct dc *dc) for (int i = 0; i < num_rmcm; i++) { dc->res_pool->rmcm_3dlut[i].isInUse = false; dc->res_pool->rmcm_3dlut[i].stream = NULL; + dc->res_pool->rmcm_3dlut[i].protection_bits = 0; } } diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index 37714d4371fba..55152f12af48b 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -1407,50 +1407,15 @@ struct lut_mem_mapping { struct dc_rmcm_3dlut { bool isInUse; const struct dc_stream_state *stream; + uint8_t protection_bits; }; struct dc_3dlut { struct kref refcount; struct tetrahedral_params lut_3d; + struct fixed31_32 hdr_multiplier; union dc_3dlut_state state; }; - -/* 3DLUT DMA (Fast Load) params */ -struct dc_3dlut_dma { - struct dc_plane_address addr; - enum dc_cm_lut_swizzle swizzle; - enum dc_cm_lut_pixel_format format; - uint16_t bias; /* FP1.5.10 */ - uint16_t scale; /* FP1.5.10 */ - enum dc_cm_lut_size size; -}; - -/* color manager */ -union dc_plane_cm_flags { - unsigned int all; - struct { - unsigned int shaper_enable : 1; - unsigned int lut3d_enable : 1; - unsigned int blend_enable : 1; - /* whether legacy (lut3d_func) or DMA is valid */ - unsigned int lut3d_dma_enable : 1; - /* RMCM lut to be used instead of MCM */ - unsigned int rmcm_enable : 1; - unsigned int reserved: 27; - } bits; -}; - -struct dc_plane_cm { - struct kref refcount; - struct dc_transfer_func shaper_func; - union { - struct dc_3dlut lut3d_func; - struct dc_3dlut_dma lut3d_dma; - }; - struct dc_transfer_func blend_func; - union dc_plane_cm_flags flags; -}; - /* * This structure is filled in by dc_surface_get_status and contains * the last requested address and the currently active address so the called @@ -1528,18 +1493,14 @@ struct dc_plane_state { struct fixed31_32 hdr_mult; struct colorspace_transform gamut_remap_matrix; + // TODO: No longer used, remove + struct dc_hdr_static_metadata hdr_static_ctx; + enum dc_color_space color_space; - bool lut_bank_a; - struct dc_hdr_static_metadata hdr_static_ctx; struct dc_3dlut lut3d_func; struct dc_transfer_func in_shaper_func; struct dc_transfer_func blend_tf; - enum dc_cm2_shaper_3dlut_setting mcm_shaper_3dlut_setting; - bool mcm_lut1d_enable; - struct dc_cm2_func_luts mcm_luts; - enum mpcc_movable_cm_location mcm_location; - struct dc_plane_cm cm; struct dc_transfer_func *gamcor_tf; enum surface_pixel_format format; @@ -1576,6 +1537,11 @@ struct dc_plane_state { bool is_statically_allocated; enum chroma_cositing cositing; + enum dc_cm2_shaper_3dlut_setting mcm_shaper_3dlut_setting; + bool mcm_lut1d_enable; + struct dc_cm2_func_luts mcm_luts; + bool lut_bank_a; + enum mpcc_movable_cm_location mcm_location; struct dc_csc_transform cursor_csc_color_matrix; bool adaptive_sharpness_en; int adaptive_sharpness_policy; @@ -1968,10 +1934,6 @@ struct dc_3dlut *dc_create_3dlut_func(void); void dc_3dlut_func_release(struct dc_3dlut *lut); void dc_3dlut_func_retain(struct dc_3dlut *lut); -struct dc_plane_cm *dc_plane_cm_create(void); -void dc_plane_cm_release(struct dc_plane_cm *cm); -void dc_plane_cm_retain(struct dc_plane_cm *cm); - void dc_post_update_surfaces_to_stream( struct dc *dc); diff --git a/drivers/gpu/drm/amd/display/dc/dc_types.h b/drivers/gpu/drm/amd/display/dc/dc_types.h index c08d5c005df6e..62e35dcb585cb 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_types.h +++ b/drivers/gpu/drm/amd/display/dc/dc_types.h @@ -1493,28 +1493,4 @@ struct dc_validation_dpia_set { uint32_t required_bw; }; -enum dc_cm_lut_swizzle { - CM_LUT_3D_SWIZZLE_LINEAR_RGB, - CM_LUT_3D_SWIZZLE_LINEAR_BGR, - CM_LUT_1D_PACKED_LINEAR -}; - -enum dc_cm_lut_pixel_format { - CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12MSB, - CM_LUT_PIXEL_FORMAT_BGRA16161616_UNORM_12MSB, - CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12LSB, - CM_LUT_PIXEL_FORMAT_BGRA16161616_UNORM_12LSB, - CM_LUT_PIXEL_FORMAT_RGBA16161616_FLOAT_FP1_5_10, - CM_LUT_PIXEL_FORMAT_BGRA16161616_FLOAT_FP1_5_10 -}; - -enum dc_cm_lut_size { - CM_LUT_SIZE_NONE, - CM_LUT_SIZE_999, - CM_LUT_SIZE_171717, - CM_LUT_SIZE_333333, - CM_LUT_SIZE_454545, - CM_LUT_SIZE_656565, -}; - #endif /* DC_TYPES_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c index 5a816442deee9..3c7a6569b692f 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c @@ -41,12 +41,12 @@ hubp2->hubp_shift->field_name, hubp2->hubp_mask->field_name void hubp401_program_3dlut_fl_addr(struct hubp *hubp, - const struct dc_plane_address *address) + const struct dc_plane_address address) { struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); - REG_UPDATE(HUBP_3DLUT_ADDRESS_HIGH, HUBP_3DLUT_ADDRESS_HIGH, address->lut3d.addr.high_part); - REG_WRITE(HUBP_3DLUT_ADDRESS_LOW, address->lut3d.addr.low_part); + REG_UPDATE(HUBP_3DLUT_ADDRESS_HIGH, HUBP_3DLUT_ADDRESS_HIGH, address.lut3d.addr.high_part); + REG_WRITE(HUBP_3DLUT_ADDRESS_LOW, address.lut3d.addr.low_part); } void hubp401_program_3dlut_fl_dlg_param(struct hubp *hubp, int refcyc_per_3dlut_group) @@ -72,169 +72,96 @@ int hubp401_get_3dlut_fl_done(struct hubp *hubp) return ret; } -static void hubp401_get_3dlut_fl_xbar_map( - const enum dc_cm_lut_pixel_format format, - enum hubp_3dlut_fl_crossbar_bit_slice *bit_slice_y_g, - enum hubp_3dlut_fl_crossbar_bit_slice *bit_slice_cb_b, - enum hubp_3dlut_fl_crossbar_bit_slice *bit_slice_cr_r) +void hubp401_program_3dlut_fl_addressing_mode(struct hubp *hubp, enum hubp_3dlut_fl_addressing_mode addr_mode) { - switch (format) { - case CM_LUT_PIXEL_FORMAT_BGRA16161616_UNORM_12MSB: - case CM_LUT_PIXEL_FORMAT_BGRA16161616_UNORM_12LSB: - case CM_LUT_PIXEL_FORMAT_BGRA16161616_FLOAT_FP1_5_10: - /* BGRA */ - *bit_slice_cr_r = hubp_3dlut_fl_crossbar_bit_slice_32_47; - *bit_slice_y_g = hubp_3dlut_fl_crossbar_bit_slice_16_31; - *bit_slice_cb_b = hubp_3dlut_fl_crossbar_bit_slice_0_15; - break; - case CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12MSB: - case CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12LSB: - case CM_LUT_PIXEL_FORMAT_RGBA16161616_FLOAT_FP1_5_10: - default: - /* RGBA */ - *bit_slice_cr_r = hubp_3dlut_fl_crossbar_bit_slice_0_15; - *bit_slice_y_g = hubp_3dlut_fl_crossbar_bit_slice_16_31; - *bit_slice_cb_b = hubp_3dlut_fl_crossbar_bit_slice_32_47; - break; - } + struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); + + REG_UPDATE(HUBP_3DLUT_CONTROL, HUBP_3DLUT_ADDRESSING_MODE, addr_mode); } -void hubp401_program_3dlut_fl_crossbar(struct hubp *hubp, - const enum dc_cm_lut_pixel_format format) +void hubp401_program_3dlut_fl_width(struct hubp *hubp, enum hubp_3dlut_fl_width width) { struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); - enum hubp_3dlut_fl_crossbar_bit_slice bit_slice_y_g = 0; - enum hubp_3dlut_fl_crossbar_bit_slice bit_slice_cb_b = 0; - enum hubp_3dlut_fl_crossbar_bit_slice bit_slice_cr_r = 0; + REG_UPDATE(HUBP_3DLUT_CONTROL, HUBP_3DLUT_WIDTH, width); +} - hubp401_get_3dlut_fl_xbar_map(format, - &bit_slice_y_g, - &bit_slice_cb_b, - &bit_slice_cr_r); +void hubp401_program_3dlut_fl_tmz_protected(struct hubp *hubp, uint8_t protection_bits) +{ + struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); - REG_UPDATE_3(HUBP_3DLUT_CONTROL, - HUBP_3DLUT_CROSSBAR_SELECT_Y_G, bit_slice_y_g, - HUBP_3DLUT_CROSSBAR_SELECT_CB_B, bit_slice_cb_b, - HUBP_3DLUT_CROSSBAR_SELECT_CR_R, bit_slice_cr_r); + REG_UPDATE(HUBP_3DLUT_CONTROL, HUBP_3DLUT_TMZ, protection_bits); } -static enum hubp_3dlut_fl_width hubp401_get_3dlut_fl_width( - const enum dc_cm_lut_size size, - const enum dc_cm_lut_swizzle swizzle) +void hubp401_program_3dlut_fl_crossbar(struct hubp *hubp, + enum hubp_3dlut_fl_crossbar_bit_slice bit_slice_y_g, + enum hubp_3dlut_fl_crossbar_bit_slice bit_slice_cb_b, + enum hubp_3dlut_fl_crossbar_bit_slice bit_slice_cr_r) { - enum hubp_3dlut_fl_width width = 0; - - switch (size) { - case CM_LUT_SIZE_333333: - ASSERT(swizzle != CM_LUT_1D_PACKED_LINEAR); - width = hubp_3dlut_fl_width_33; - break; - case CM_LUT_SIZE_171717: - if (swizzle != CM_LUT_1D_PACKED_LINEAR) { - width = hubp_3dlut_fl_width_17; - } else { - width = hubp_3dlut_fl_width_17_transformed; - } - break; - default: - width = 0; - break; - } + struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); - return width; + REG_UPDATE_3(HUBP_3DLUT_CONTROL, + HUBP_3DLUT_CROSSBAR_SELECT_Y_G, bit_slice_y_g, + HUBP_3DLUT_CROSSBAR_SELECT_CB_B, bit_slice_cb_b, + HUBP_3DLUT_CROSSBAR_SELECT_CR_R, bit_slice_cr_r); } -static enum hubp_3dlut_fl_format hubp401_get_3dlut_fl_format( - const enum dc_cm_lut_pixel_format dc_format) +void hubp401_update_3dlut_fl_bias_scale(struct hubp *hubp, uint16_t bias, uint16_t scale) { - enum hubp_3dlut_fl_format hubp_format = hubp_3dlut_fl_format_unorm_12msb_bitslice; - - switch (dc_format) { - case CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12MSB: - case CM_LUT_PIXEL_FORMAT_BGRA16161616_UNORM_12MSB: - hubp_format = hubp_3dlut_fl_format_unorm_12msb_bitslice; - break; - case CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12LSB: - case CM_LUT_PIXEL_FORMAT_BGRA16161616_UNORM_12LSB: - hubp_format = hubp_3dlut_fl_format_unorm_12lsb_bitslice; - break; - case CM_LUT_PIXEL_FORMAT_RGBA16161616_FLOAT_FP1_5_10: - case CM_LUT_PIXEL_FORMAT_BGRA16161616_FLOAT_FP1_5_10: - hubp_format = hubp_3dlut_fl_format_float_fp1_5_10; - break; - default: - BREAK_TO_DEBUGGER(); - break; - } + struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); - return hubp_format; + REG_UPDATE_2(_3DLUT_FL_BIAS_SCALE, HUBP0_3DLUT_FL_BIAS, bias, HUBP0_3DLUT_FL_SCALE, scale); } -static enum hubp_3dlut_fl_addressing_mode hubp401_get_3dlut_fl_addr_mode( - const enum dc_cm_lut_swizzle swizzle) +void hubp401_program_3dlut_fl_mode(struct hubp *hubp, enum hubp_3dlut_fl_mode mode) { - enum hubp_3dlut_fl_addressing_mode addr_mode; - - switch (swizzle) { - case CM_LUT_1D_PACKED_LINEAR: - addr_mode = hubp_3dlut_fl_addressing_mode_simple_linear; - break; - case CM_LUT_3D_SWIZZLE_LINEAR_RGB: - case CM_LUT_3D_SWIZZLE_LINEAR_BGR: - default: - addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear; - break; - } + struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); - return addr_mode; + REG_UPDATE(_3DLUT_FL_CONFIG, HUBP0_3DLUT_FL_MODE, mode); } -static enum hubp_3dlut_fl_mode hubp401_get_3dlut_fl_mode( - const enum dc_cm_lut_swizzle swizzle) +void hubp401_program_3dlut_fl_format(struct hubp *hubp, enum hubp_3dlut_fl_format format) { - enum hubp_3dlut_fl_mode mode; - - switch (swizzle) { - case CM_LUT_3D_SWIZZLE_LINEAR_RGB: - mode = hubp_3dlut_fl_mode_native_1; - break; - case CM_LUT_3D_SWIZZLE_LINEAR_BGR: - mode = hubp_3dlut_fl_mode_native_2; - break; - case CM_LUT_1D_PACKED_LINEAR: - mode = hubp_3dlut_fl_mode_transform; - break; - default: - mode = hubp_3dlut_fl_mode_disable; - break; - } + struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); - return mode; + REG_UPDATE(_3DLUT_FL_CONFIG, HUBP0_3DLUT_FL_FORMAT, format); } -void hubp401_program_3dlut_fl_config(struct hubp *hubp, - const struct dc_3dlut_dma *config) +void hubp401_program_3dlut_fl_config( + struct hubp *hubp, + struct hubp_fl_3dlut_config *cfg) { struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); - enum hubp_3dlut_fl_width width = hubp401_get_3dlut_fl_width(config->size, config->swizzle); - enum hubp_3dlut_fl_format format = hubp401_get_3dlut_fl_format(config->format); - enum hubp_3dlut_fl_addressing_mode addr_mode = hubp401_get_3dlut_fl_addr_mode(config->swizzle); - enum hubp_3dlut_fl_mode mode = hubp401_get_3dlut_fl_mode(config->swizzle); + uint32_t mpc_width = {(cfg->width == 17) ? 0 : 1}; + uint32_t width = {cfg->width}; + + if (cfg->layout == DC_CM2_GPU_MEM_LAYOUT_1D_PACKED_LINEAR) + width = (cfg->width == 17) ? 4916 : 35940; REG_UPDATE_2(_3DLUT_FL_CONFIG, - HUBP0_3DLUT_FL_MODE, mode, - HUBP0_3DLUT_FL_FORMAT, format); + HUBP0_3DLUT_FL_MODE, cfg->mode, + HUBP0_3DLUT_FL_FORMAT, cfg->format); REG_UPDATE_2(_3DLUT_FL_BIAS_SCALE, - HUBP0_3DLUT_FL_BIAS, config->bias, - HUBP0_3DLUT_FL_SCALE, config->scale); - - REG_UPDATE_3(HUBP_3DLUT_CONTROL, - HUBP_3DLUT_WIDTH, width, - HUBP_3DLUT_ADDRESSING_MODE, addr_mode, - HUBP_3DLUT_TMZ, config->addr.tmz_surface); + HUBP0_3DLUT_FL_BIAS, cfg->bias, + HUBP0_3DLUT_FL_SCALE, cfg->scale); + + REG_UPDATE(HUBP_3DLUT_ADDRESS_HIGH, + HUBP_3DLUT_ADDRESS_HIGH, cfg->address.lut3d.addr.high_part); + REG_UPDATE(HUBP_3DLUT_ADDRESS_LOW, + HUBP_3DLUT_ADDRESS_LOW, cfg->address.lut3d.addr.low_part); + + //cross bar + REG_UPDATE_8(HUBP_3DLUT_CONTROL, + HUBP_3DLUT_MPC_WIDTH, mpc_width, + HUBP_3DLUT_WIDTH, width, + HUBP_3DLUT_CROSSBAR_SELECT_CR_R, cfg->crossbar_bit_slice_cr_r, + HUBP_3DLUT_CROSSBAR_SELECT_Y_G, cfg->crossbar_bit_slice_y_g, + HUBP_3DLUT_CROSSBAR_SELECT_CB_B, cfg->crossbar_bit_slice_cb_b, + HUBP_3DLUT_ADDRESSING_MODE, cfg->addr_mode, + HUBP_3DLUT_TMZ, cfg->protection_bits, + HUBP_3DLUT_ENABLE, cfg->enabled ? 1 : 0); } void hubp401_update_mall_sel(struct hubp *hubp, uint32_t mall_sel, bool c_cursor) @@ -1135,13 +1062,19 @@ static struct hubp_funcs dcn401_hubp_funcs = { .hubp_update_mall_sel = hubp401_update_mall_sel, .hubp_prepare_subvp_buffering = hubp32_prepare_subvp_buffering, .hubp_program_mcache_id_and_split_coordinate = hubp401_program_mcache_id_and_split_coordinate, + .hubp_update_3dlut_fl_bias_scale = hubp401_update_3dlut_fl_bias_scale, + .hubp_program_3dlut_fl_mode = hubp401_program_3dlut_fl_mode, + .hubp_program_3dlut_fl_format = hubp401_program_3dlut_fl_format, .hubp_program_3dlut_fl_addr = hubp401_program_3dlut_fl_addr, - .hubp_program_3dlut_fl_config = hubp401_program_3dlut_fl_config, .hubp_program_3dlut_fl_dlg_param = hubp401_program_3dlut_fl_dlg_param, .hubp_enable_3dlut_fl = hubp401_enable_3dlut_fl, + .hubp_program_3dlut_fl_addressing_mode = hubp401_program_3dlut_fl_addressing_mode, + .hubp_program_3dlut_fl_width = hubp401_program_3dlut_fl_width, + .hubp_program_3dlut_fl_tmz_protected = hubp401_program_3dlut_fl_tmz_protected, .hubp_program_3dlut_fl_crossbar = hubp401_program_3dlut_fl_crossbar, .hubp_get_3dlut_fl_done = hubp401_get_3dlut_fl_done, .hubp_clear_tiling = hubp401_clear_tiling, + .hubp_program_3dlut_fl_config = hubp401_program_3dlut_fl_config, .hubp_read_reg_state = hubp3_read_reg_state }; diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.h b/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.h index 043948f64b862..4570b8016de5a 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.h +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.h @@ -328,17 +328,32 @@ int hubp401_get_3dlut_fl_done(struct hubp *hubp); void hubp401_set_unbounded_requesting(struct hubp *hubp, bool enable); +void hubp401_update_3dlut_fl_bias_scale(struct hubp *hubp, uint16_t bias, uint16_t scale); + void hubp401_program_3dlut_fl_crossbar(struct hubp *hubp, - const enum dc_cm_lut_pixel_format format); + enum hubp_3dlut_fl_crossbar_bit_slice bit_slice_y_g, + enum hubp_3dlut_fl_crossbar_bit_slice bit_slice_cb_b, + enum hubp_3dlut_fl_crossbar_bit_slice bit_slice_cr_r); + +void hubp401_program_3dlut_fl_tmz_protected(struct hubp *hubp, uint8_t protection_bits); + +void hubp401_program_3dlut_fl_width(struct hubp *hubp, enum hubp_3dlut_fl_width width); + +void hubp401_program_3dlut_fl_addressing_mode(struct hubp *hubp, enum hubp_3dlut_fl_addressing_mode addr_mode); void hubp401_enable_3dlut_fl(struct hubp *hubp, bool enable); void hubp401_program_3dlut_fl_dlg_param(struct hubp *hubp, int refcyc_per_3dlut_group); -void hubp401_program_3dlut_fl_addr(struct hubp *hubp, const struct dc_plane_address *address); +void hubp401_program_3dlut_fl_addr(struct hubp *hubp, const struct dc_plane_address address); -void hubp401_program_3dlut_fl_config(struct hubp *hubp, - const struct dc_3dlut_dma *config); +void hubp401_program_3dlut_fl_format(struct hubp *hubp, enum hubp_3dlut_fl_format format); + +void hubp401_program_3dlut_fl_mode(struct hubp *hubp, enum hubp_3dlut_fl_mode mode); + +void hubp401_program_3dlut_fl_config( + struct hubp *hubp, + struct hubp_fl_3dlut_config *cfg); void hubp401_clear_tiling(struct hubp *hubp); diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn42/dcn42_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn42/dcn42_hubp.c index ad6badcceb12a..e4602c3ddc662 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn42/dcn42_hubp.c +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn42/dcn42_hubp.c @@ -311,84 +311,19 @@ static void hubp42_program_surface_config( hubp42_program_pixel_format(hubp, format); } -static void hubp42_get_3dlut_fl_xbar_map( - const enum dc_cm_lut_pixel_format format, - enum hubp_3dlut_fl_crossbar_bit_slice *bit_slice_y_g, - enum hubp_3dlut_fl_crossbar_bit_slice *bit_slice_cb_b, - enum hubp_3dlut_fl_crossbar_bit_slice *bit_slice_cr_r) -{ - switch (format) { - case CM_LUT_PIXEL_FORMAT_BGRA16161616_UNORM_12MSB: - case CM_LUT_PIXEL_FORMAT_BGRA16161616_UNORM_12LSB: - case CM_LUT_PIXEL_FORMAT_BGRA16161616_FLOAT_FP1_5_10: - /* BGRA */ - *bit_slice_cr_r = hubp_3dlut_fl_crossbar_bit_slice_32_47; - *bit_slice_y_g = hubp_3dlut_fl_crossbar_bit_slice_16_31; - *bit_slice_cb_b = hubp_3dlut_fl_crossbar_bit_slice_0_15; - break; - case CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12MSB: - case CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12LSB: - case CM_LUT_PIXEL_FORMAT_RGBA16161616_FLOAT_FP1_5_10: - default: - /* RGBA */ - *bit_slice_cr_r = hubp_3dlut_fl_crossbar_bit_slice_0_15; - *bit_slice_y_g = hubp_3dlut_fl_crossbar_bit_slice_16_31; - *bit_slice_cb_b = hubp_3dlut_fl_crossbar_bit_slice_32_47; - break; - } -} - void hubp42_program_3dlut_fl_crossbar(struct hubp *hubp, - const enum dc_cm_lut_pixel_format format) + enum hubp_3dlut_fl_crossbar_bit_slice bit_slice_r, + enum hubp_3dlut_fl_crossbar_bit_slice bit_slice_g, + enum hubp_3dlut_fl_crossbar_bit_slice bit_slice_b) { struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); - enum hubp_3dlut_fl_crossbar_bit_slice bit_slice_g = 0; - enum hubp_3dlut_fl_crossbar_bit_slice bit_slice_b = 0; - enum hubp_3dlut_fl_crossbar_bit_slice bit_slice_r = 0; - - hubp42_get_3dlut_fl_xbar_map(format, - &bit_slice_g, - &bit_slice_b, - &bit_slice_r); - REG_UPDATE_3(HUBP_3DLUT_CONTROL, HUBP_3DLUT_CROSSBAR_SEL_R, bit_slice_r, HUBP_3DLUT_CROSSBAR_SEL_G, bit_slice_g, HUBP_3DLUT_CROSSBAR_SEL_B, bit_slice_b); } -static uint32_t hubp42_get_3dlut_fl_mpc_width( - const enum dc_cm_lut_size size) -{ - uint32_t width = 0; - - switch (size) { - case CM_LUT_SIZE_333333: - width = 1; - break; - case CM_LUT_SIZE_171717: - default: - width = 0; - break; - } - - return width; -} - -void hubp42_program_3dlut_fl_config(struct hubp *hubp, - const struct dc_3dlut_dma *config) -{ - struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); - - uint32_t mpc_width = hubp42_get_3dlut_fl_mpc_width(config->size); - - REG_UPDATE(HUBP_3DLUT_CONTROL, - HUBP_3DLUT_MPC_WIDTH, mpc_width); - - hubp401_program_3dlut_fl_config(hubp, config); -} - static bool hubp42_program_surface_flip_and_addr( struct hubp *hubp, const struct dc_plane_address *address, @@ -670,10 +605,15 @@ struct hubp_funcs dcn42_hubp_funcs = { .hubp_set_flip_int = hubp1_set_flip_int, .hubp_in_blank = hubp1_in_blank, .program_extended_blank = hubp31_program_extended_blank_value, + .hubp_update_3dlut_fl_bias_scale = hubp401_update_3dlut_fl_bias_scale, + .hubp_program_3dlut_fl_mode = hubp401_program_3dlut_fl_mode, + .hubp_program_3dlut_fl_format = hubp401_program_3dlut_fl_format, .hubp_program_3dlut_fl_addr = hubp401_program_3dlut_fl_addr, - .hubp_program_3dlut_fl_config = hubp42_program_3dlut_fl_config, .hubp_program_3dlut_fl_dlg_param = hubp401_program_3dlut_fl_dlg_param, .hubp_enable_3dlut_fl = hubp401_enable_3dlut_fl, + .hubp_program_3dlut_fl_addressing_mode = hubp401_program_3dlut_fl_addressing_mode, + .hubp_program_3dlut_fl_width = hubp401_program_3dlut_fl_width, + .hubp_program_3dlut_fl_tmz_protected = hubp401_program_3dlut_fl_tmz_protected, .hubp_program_3dlut_fl_crossbar = hubp42_program_3dlut_fl_crossbar, .hubp_get_3dlut_fl_done = hubp401_get_3dlut_fl_done, .hubp_clear_tiling = hubp3_clear_tiling, diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn42/dcn42_hubp.h b/drivers/gpu/drm/amd/display/dc/hubp/dcn42/dcn42_hubp.h index 88bb1337ab9d6..f0e614136228c 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn42/dcn42_hubp.h +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn42/dcn42_hubp.h @@ -58,11 +58,11 @@ bool hubp42_construct( const struct dcn_hubp2_shift *hubp_shift, const struct dcn_hubp2_mask *hubp_mask); -void hubp42_program_3dlut_fl_crossbar(struct hubp *hubp, - const enum dc_cm_lut_pixel_format format); - -void hubp42_program_3dlut_fl_config(struct hubp *hubp, - const struct dc_3dlut_dma *config); +void hubp42_program_3dlut_fl_crossbar( + struct hubp *hubp, + enum hubp_3dlut_fl_crossbar_bit_slice bit_slice_r, + enum hubp_3dlut_fl_crossbar_bit_slice bit_slice_g, + enum hubp_3dlut_fl_crossbar_bit_slice bit_slice_b); void hubp42_read_state(struct hubp *hubp); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c index 9768b8f6b6fb6..98e78aa2259cc 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c @@ -95,6 +95,10 @@ void dcn401_program_gamut_remap(struct pipe_ctx *pipe_ctx) unsigned int mpcc_id = pipe_ctx->plane_res.mpcc_inst; struct mpc *mpc = pipe_ctx->stream_res.opp->ctx->dc->res_pool->mpc; + //For now assert if location is not pre-blend + if (pipe_ctx->plane_state) + ASSERT(pipe_ctx->plane_state->mcm_location == MPCC_MOVABLE_CM_LOCATION_BEFORE); + // program MPCC_MCM_FIRST_GAMUT_REMAP memset(&mpc_adjust, 0, sizeof(mpc_adjust)); mpc_adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS; @@ -374,180 +378,300 @@ void dcn401_init_hw(struct dc *dc) } } -void dcn401_trigger_3dlut_dma_load(struct dc *dc, struct pipe_ctx *pipe_ctx) +static void dcn401_get_mcm_lut_xable_from_pipe_ctx(struct dc *dc, struct pipe_ctx *pipe_ctx, + enum MCM_LUT_XABLE *shaper_xable, + enum MCM_LUT_XABLE *lut3d_xable, + enum MCM_LUT_XABLE *lut1d_xable) { - (void)dc; - struct hubp *hubp = pipe_ctx->plane_res.hubp; + enum dc_cm2_shaper_3dlut_setting shaper_3dlut_setting = DC_CM2_SHAPER_3DLUT_SETTING_BYPASS_ALL; + bool lut1d_enable = false; + struct mpc *mpc = dc->res_pool->mpc; + int mpcc_id = pipe_ctx->plane_res.hubp->inst; - if (hubp->funcs->hubp_enable_3dlut_fl) { - hubp->funcs->hubp_enable_3dlut_fl(hubp, true); + if (!pipe_ctx->plane_state) + return; + shaper_3dlut_setting = pipe_ctx->plane_state->mcm_shaper_3dlut_setting; + lut1d_enable = pipe_ctx->plane_state->mcm_lut1d_enable; + mpc->funcs->set_movable_cm_location(mpc, MPCC_MOVABLE_CM_LOCATION_BEFORE, mpcc_id); + pipe_ctx->plane_state->mcm_location = MPCC_MOVABLE_CM_LOCATION_BEFORE; + + *lut1d_xable = lut1d_enable ? MCM_LUT_ENABLE : MCM_LUT_DISABLE; + + switch (shaper_3dlut_setting) { + case DC_CM2_SHAPER_3DLUT_SETTING_BYPASS_ALL: + *lut3d_xable = *shaper_xable = MCM_LUT_DISABLE; + break; + case DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER: + *lut3d_xable = MCM_LUT_DISABLE; + *shaper_xable = MCM_LUT_ENABLE; + break; + case DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER_3DLUT: + *lut3d_xable = *shaper_xable = MCM_LUT_ENABLE; + break; } } -bool dcn401_set_mcm_luts(struct pipe_ctx *pipe_ctx, - const struct dc_plane_state *plane_state) +void dcn401_populate_mcm_luts(struct dc *dc, + struct pipe_ctx *pipe_ctx, + struct dc_cm2_func_luts mcm_luts, + bool lut_bank_a) { - struct dc *dc = pipe_ctx->plane_res.hubp->ctx->dc; struct dpp *dpp_base = pipe_ctx->plane_res.dpp; struct hubp *hubp = pipe_ctx->plane_res.hubp; - const struct dc_plane_cm *cm = &plane_state->cm; int mpcc_id = hubp->inst; struct mpc *mpc = dc->res_pool->mpc; union mcm_lut_params m_lut_params; - struct dc_3dlut_dma lut3d_dma; - bool lut_enable; - bool lut_bank_a; + enum dc_cm2_transfer_func_source lut3d_src = mcm_luts.lut3d_data.lut3d_src; + enum hubp_3dlut_fl_format format = 0; + enum hubp_3dlut_fl_mode mode; + enum hubp_3dlut_fl_width width = 0; + enum hubp_3dlut_fl_addressing_mode addr_mode; + enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_y_g = 0; + enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cb_b = 0; + enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cr_r = 0; + enum MCM_LUT_XABLE shaper_xable = MCM_LUT_DISABLE; + enum MCM_LUT_XABLE lut3d_xable = MCM_LUT_DISABLE; + enum MCM_LUT_XABLE lut1d_xable = MCM_LUT_DISABLE; bool rval; - bool result = true; - /* decide LUT bank based on current in use */ - mpc->funcs->get_lut_mode(mpc, MCM_LUT_1DLUT, mpcc_id, &lut_enable, &lut_bank_a); - if (!lut_enable) { - mpc->funcs->get_lut_mode(mpc, MCM_LUT_SHAPER, mpcc_id, &lut_enable, &lut_bank_a); - } - if (!lut_enable) { - mpc->funcs->get_lut_mode(mpc, MCM_LUT_3DLUT, mpcc_id, &lut_enable, &lut_bank_a); - } - - /* switch to the next bank */ - if (lut_enable) { - lut_bank_a = !lut_bank_a; - } - - /* MCM location fixed to pre-blend */ - mpc->funcs->set_movable_cm_location(mpc, MPCC_MOVABLE_CM_LOCATION_BEFORE, mpcc_id); + dcn401_get_mcm_lut_xable_from_pipe_ctx(dc, pipe_ctx, &shaper_xable, &lut3d_xable, &lut1d_xable); /* 1D LUT */ - lut_enable = cm->flags.bits.blend_enable; - memset(&m_lut_params, 0, sizeof(m_lut_params)); - if (lut_enable) { - if (cm->blend_func.type == TF_TYPE_HWPWL) - m_lut_params.pwl = &cm->blend_func.pwl; - else if (cm->blend_func.type == TF_TYPE_DISTRIBUTED_POINTS) { - rval = cm3_helper_translate_curve_to_hw_format(plane_state->ctx, - &cm->blend_func, - &dpp_base->regamma_params, - false); + if (mcm_luts.lut1d_func) { + memset(&m_lut_params, 0, sizeof(m_lut_params)); + if (mcm_luts.lut1d_func->type == TF_TYPE_HWPWL) + m_lut_params.pwl = &mcm_luts.lut1d_func->pwl; + else if (mcm_luts.lut1d_func->type == TF_TYPE_DISTRIBUTED_POINTS) { + rval = cm3_helper_translate_curve_to_hw_format(mpc->ctx, + mcm_luts.lut1d_func, + &dpp_base->regamma_params, false); m_lut_params.pwl = rval ? &dpp_base->regamma_params : NULL; } - - if (!m_lut_params.pwl) { - lut_enable = false; + if (m_lut_params.pwl) { + if (mpc->funcs->populate_lut) + mpc->funcs->populate_lut(mpc, MCM_LUT_1DLUT, m_lut_params, lut_bank_a, mpcc_id); } - } else { - lut_enable = false; + if (mpc->funcs->program_lut_mode) + mpc->funcs->program_lut_mode(mpc, MCM_LUT_1DLUT, lut1d_xable && m_lut_params.pwl, lut_bank_a, mpcc_id); } - if (mpc->funcs->program_lut_mode) - mpc->funcs->program_lut_mode(mpc, MCM_LUT_1DLUT, lut_enable, lut_bank_a, CM_LUT_SIZE_NONE, mpcc_id); - if (lut_enable && mpc->funcs->populate_lut) - mpc->funcs->populate_lut(mpc, MCM_LUT_1DLUT, &m_lut_params, lut_bank_a, mpcc_id); - /* Shaper */ - lut_enable = cm->flags.bits.shaper_enable; - if (lut_enable) { + if (mcm_luts.shaper && mcm_luts.lut3d_data.mpc_3dlut_enable) { memset(&m_lut_params, 0, sizeof(m_lut_params)); - if (cm->shaper_func.type == TF_TYPE_HWPWL) - m_lut_params.pwl = &cm->shaper_func.pwl; - else if (cm->shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) { + if (mcm_luts.shaper->type == TF_TYPE_HWPWL) + m_lut_params.pwl = &mcm_luts.shaper->pwl; + else if (mcm_luts.shaper->type == TF_TYPE_DISTRIBUTED_POINTS) { ASSERT(false); - rval = cm3_helper_translate_curve_to_hw_format(plane_state->ctx, - &cm->shaper_func, - &dpp_base->shaper_params, - true); - m_lut_params.pwl = rval ? &dpp_base->shaper_params : NULL; + rval = cm3_helper_translate_curve_to_hw_format(mpc->ctx, + mcm_luts.shaper, + &dpp_base->regamma_params, true); + m_lut_params.pwl = rval ? &dpp_base->regamma_params : NULL; } - if (!m_lut_params.pwl) { - lut_enable = false; + if (m_lut_params.pwl) { + if (mpc->funcs->mcm.populate_lut) + mpc->funcs->mcm.populate_lut(mpc, m_lut_params, lut_bank_a, mpcc_id); + if (mpc->funcs->program_lut_mode) + mpc->funcs->program_lut_mode(mpc, MCM_LUT_SHAPER, MCM_LUT_ENABLE, lut_bank_a, mpcc_id); } - } else { - lut_enable = false; } - if (mpc->funcs->program_lut_mode) - mpc->funcs->program_lut_mode(mpc, MCM_LUT_SHAPER, lut_enable, lut_bank_a, CM_LUT_SIZE_NONE, mpcc_id); - if (lut_enable && mpc->funcs->populate_lut) - mpc->funcs->populate_lut(mpc, MCM_LUT_SHAPER, &m_lut_params, lut_bank_a, mpcc_id); + /* 3DLUT */ + switch (lut3d_src) { + case DC_CM2_TRANSFER_FUNC_SOURCE_SYSMEM: + memset(&m_lut_params, 0, sizeof(m_lut_params)); + if (hubp->funcs->hubp_enable_3dlut_fl) + hubp->funcs->hubp_enable_3dlut_fl(hubp, false); - /* NOTE: Toggling from DMA->Host is not supported atomically as hardware - * blocks writes until 3DLUT FL mode is cleared from HUBP on VUpdate. - * Expectation is either option is used consistently. - */ + if (mcm_luts.lut3d_data.lut3d_func && mcm_luts.lut3d_data.lut3d_func->state.bits.initialized) { + m_lut_params.lut3d = &mcm_luts.lut3d_data.lut3d_func->lut_3d; + if (mpc->funcs->populate_lut) + mpc->funcs->populate_lut(mpc, MCM_LUT_3DLUT, m_lut_params, lut_bank_a, mpcc_id); + if (mpc->funcs->program_lut_mode) + mpc->funcs->program_lut_mode(mpc, MCM_LUT_3DLUT, lut3d_xable, lut_bank_a, + mpcc_id); + } + break; + case DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM: + switch (mcm_luts.lut3d_data.gpu_mem_params.size) { +#if defined(CONFIG_DRM_AMD_DC_DCN4_2) + case DC_CM2_GPU_MEM_SIZE_333333: + if (dc->caps.color.mpc.rmcm_3d_lut_caps.lut_dim_caps.dim_33) + width = hubp_3dlut_fl_width_33; + break; +#endif + case DC_CM2_GPU_MEM_SIZE_171717: + width = hubp_3dlut_fl_width_17; + break; + case DC_CM2_GPU_MEM_SIZE_TRANSFORMED: + width = hubp_3dlut_fl_width_transformed; + break; + default: + //TODO: handle default case + break; + } - /* 3DLUT */ - lut_enable = cm->flags.bits.lut3d_enable; - if (lut_enable && cm->flags.bits.lut3d_dma_enable) { - /* Fast (DMA) Load Mode */ - /* MPC */ - if (mpc->funcs->program_lut_mode) - mpc->funcs->program_lut_mode(mpc, MCM_LUT_3DLUT, lut_enable, lut_bank_a, cm->lut3d_dma.size, mpcc_id); + //check for support + if (mpc->funcs->mcm.is_config_supported && + !mpc->funcs->mcm.is_config_supported(width)) + break; - /* only supports 12 bit */ if (mpc->funcs->program_lut_read_write_control) - mpc->funcs->program_lut_read_write_control(mpc, MCM_LUT_3DLUT, lut_bank_a, 12, mpcc_id); + mpc->funcs->program_lut_read_write_control(mpc, MCM_LUT_3DLUT, lut_bank_a, mpcc_id); + if (mpc->funcs->program_lut_mode) + mpc->funcs->program_lut_mode(mpc, MCM_LUT_3DLUT, lut3d_xable, lut_bank_a, mpcc_id); - if (mpc->funcs->update_3dlut_fast_load_select) - mpc->funcs->update_3dlut_fast_load_select(mpc, mpcc_id, hubp->inst); + if (hubp->funcs->hubp_program_3dlut_fl_addr) + hubp->funcs->hubp_program_3dlut_fl_addr(hubp, mcm_luts.lut3d_data.gpu_mem_params.addr); - /* HUBP */ - if (hubp->funcs->hubp_program_3dlut_fl_config) - hubp->funcs->hubp_program_3dlut_fl_config(hubp, &cm->lut3d_dma); + if (mpc->funcs->mcm.program_bit_depth) + mpc->funcs->mcm.program_bit_depth(mpc, mcm_luts.lut3d_data.gpu_mem_params.bit_depth, mpcc_id); - if (hubp->funcs->hubp_program_3dlut_fl_crossbar) - hubp->funcs->hubp_program_3dlut_fl_crossbar(hubp, cm->lut3d_dma.format); + switch (mcm_luts.lut3d_data.gpu_mem_params.layout) { + case DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_RGB: + mode = hubp_3dlut_fl_mode_native_1; + addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear; + break; + case DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_BGR: + mode = hubp_3dlut_fl_mode_native_2; + addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear; + break; + case DC_CM2_GPU_MEM_LAYOUT_1D_PACKED_LINEAR: + mode = hubp_3dlut_fl_mode_transform; + addr_mode = hubp_3dlut_fl_addressing_mode_simple_linear; + break; + default: + mode = hubp_3dlut_fl_mode_disable; + addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear; + break; + } + if (hubp->funcs->hubp_program_3dlut_fl_mode) + hubp->funcs->hubp_program_3dlut_fl_mode(hubp, mode); - if (hubp->funcs->hubp_program_3dlut_fl_addr) - hubp->funcs->hubp_program_3dlut_fl_addr(hubp, &cm->lut3d_dma.addr); + if (hubp->funcs->hubp_program_3dlut_fl_addressing_mode) + hubp->funcs->hubp_program_3dlut_fl_addressing_mode(hubp, addr_mode); - if (hubp->funcs->hubp_enable_3dlut_fl) { - hubp->funcs->hubp_enable_3dlut_fl(hubp, true); - } else { - /* GPU memory only supports fast load path */ - BREAK_TO_DEBUGGER(); - lut_enable = false; - result = false; + switch (mcm_luts.lut3d_data.gpu_mem_params.format_params.format) { + case DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12MSB: + format = hubp_3dlut_fl_format_unorm_12msb_bitslice; + break; + case DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12LSB: + format = hubp_3dlut_fl_format_unorm_12lsb_bitslice; + break; + case DC_CM2_GPU_MEM_FORMAT_16161616_FLOAT_FP1_5_10: + format = hubp_3dlut_fl_format_float_fp1_5_10; + break; + } + if (hubp->funcs->hubp_program_3dlut_fl_format) + hubp->funcs->hubp_program_3dlut_fl_format(hubp, format); + if (hubp->funcs->hubp_update_3dlut_fl_bias_scale && + mpc->funcs->mcm.program_bias_scale) { + mpc->funcs->mcm.program_bias_scale(mpc, + mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.bias, + mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.scale, + mpcc_id); + hubp->funcs->hubp_update_3dlut_fl_bias_scale(hubp, + mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.bias, + mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.scale); } - } else { - /* Legacy (Host) Load Mode */ - memset(&m_lut_params, 0, sizeof(m_lut_params)); - if (cm->flags.bits.lut3d_enable && cm->lut3d_func.state.bits.initialized) { - m_lut_params.lut3d = &cm->lut3d_func.lut_3d; - } else { - lut_enable = false; + //navi 4x has a bug and r and blue are swapped and need to be worked around here in + //TODO: need to make a method for get_xbar per asic OR do the workaround in program_crossbar for 4x + switch (mcm_luts.lut3d_data.gpu_mem_params.component_order) { + case DC_CM2_GPU_MEM_PIXEL_COMPONENT_ORDER_RGBA: + default: + crossbar_bit_slice_cr_r = hubp_3dlut_fl_crossbar_bit_slice_0_15; + crossbar_bit_slice_y_g = hubp_3dlut_fl_crossbar_bit_slice_16_31; + crossbar_bit_slice_cb_b = hubp_3dlut_fl_crossbar_bit_slice_32_47; + break; } - /* MPC */ - if (mpc->funcs->program_lut_mode) - mpc->funcs->program_lut_mode(mpc, - MCM_LUT_3DLUT, - lut_enable, - lut_bank_a, - cm->lut3d_func.lut_3d.use_tetrahedral_9 ? CM_LUT_SIZE_999 : CM_LUT_SIZE_171717, - mpcc_id); - - if (lut_enable) { - if (mpc->funcs->program_lut_read_write_control) - mpc->funcs->program_lut_read_write_control(mpc, - MCM_LUT_3DLUT, - lut_bank_a, - cm->lut3d_func.lut_3d.use_12bits ? 12 : 10, - mpcc_id); + if (hubp->funcs->hubp_program_3dlut_fl_crossbar) + hubp->funcs->hubp_program_3dlut_fl_crossbar(hubp, + crossbar_bit_slice_cr_r, + crossbar_bit_slice_y_g, + crossbar_bit_slice_cb_b); - if (mpc->funcs->update_3dlut_fast_load_select) - mpc->funcs->update_3dlut_fast_load_select(mpc, mpcc_id, 0xf); + if (mpc->funcs->mcm.program_lut_read_write_control) + mpc->funcs->mcm.program_lut_read_write_control(mpc, MCM_LUT_3DLUT, lut_bank_a, true, mpcc_id); - if (mpc->funcs->populate_lut) - mpc->funcs->populate_lut(mpc, MCM_LUT_3DLUT, &m_lut_params, lut_bank_a, mpcc_id); - } + if (mpc->funcs->mcm.program_3dlut_size) + mpc->funcs->mcm.program_3dlut_size(mpc, width, mpcc_id); - /* HUBP */ - memset(&lut3d_dma, 0, sizeof(lut3d_dma)); - if (hubp->funcs->hubp_program_3dlut_fl_config) - hubp->funcs->hubp_program_3dlut_fl_config(hubp, &lut3d_dma); + if (mpc->funcs->update_3dlut_fast_load_select) + mpc->funcs->update_3dlut_fast_load_select(mpc, mpcc_id, hubp->inst); if (hubp->funcs->hubp_enable_3dlut_fl) - hubp->funcs->hubp_enable_3dlut_fl(hubp, false); + hubp->funcs->hubp_enable_3dlut_fl(hubp, true); + else { + if (mpc->funcs->program_lut_mode) { + mpc->funcs->program_lut_mode(mpc, MCM_LUT_SHAPER, MCM_LUT_DISABLE, lut_bank_a, mpcc_id); + mpc->funcs->program_lut_mode(mpc, MCM_LUT_3DLUT, MCM_LUT_DISABLE, lut_bank_a, mpcc_id); + mpc->funcs->program_lut_mode(mpc, MCM_LUT_1DLUT, MCM_LUT_DISABLE, lut_bank_a, mpcc_id); + } + } + break; + + } +} + +void dcn401_trigger_3dlut_dma_load(struct dc *dc, struct pipe_ctx *pipe_ctx) +{ + (void)dc; + struct hubp *hubp = pipe_ctx->plane_res.hubp; + + if (hubp->funcs->hubp_enable_3dlut_fl) { + hubp->funcs->hubp_enable_3dlut_fl(hubp, true); + } +} + +bool dcn401_set_mcm_luts(struct pipe_ctx *pipe_ctx, + const struct dc_plane_state *plane_state) +{ + struct dpp *dpp_base = pipe_ctx->plane_res.dpp; + int mpcc_id = pipe_ctx->plane_res.hubp->inst; + struct dc *dc = pipe_ctx->stream_res.opp->ctx->dc; + struct mpc *mpc = dc->res_pool->mpc; + bool result; + const struct pwl_params *lut_params = NULL; + bool rval; + + if (plane_state->mcm_luts.lut3d_data.lut3d_src == DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM) { + dcn401_populate_mcm_luts(dc, pipe_ctx, plane_state->mcm_luts, plane_state->lut_bank_a); + return true; + } + + mpc->funcs->set_movable_cm_location(mpc, MPCC_MOVABLE_CM_LOCATION_BEFORE, mpcc_id); + pipe_ctx->plane_state->mcm_location = MPCC_MOVABLE_CM_LOCATION_BEFORE; + // 1D LUT + if (plane_state->blend_tf.type == TF_TYPE_HWPWL) + lut_params = &plane_state->blend_tf.pwl; + else if (plane_state->blend_tf.type == TF_TYPE_DISTRIBUTED_POINTS) { + rval = cm3_helper_translate_curve_to_hw_format(plane_state->ctx, + &plane_state->blend_tf, + &dpp_base->regamma_params, false); + lut_params = rval ? &dpp_base->regamma_params : NULL; + } + result = mpc->funcs->program_1dlut(mpc, lut_params, mpcc_id); + lut_params = NULL; + + // Shaper + if (plane_state->in_shaper_func.type == TF_TYPE_HWPWL) + lut_params = &plane_state->in_shaper_func.pwl; + else if (plane_state->in_shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) { + // TODO: dpp_base replace + rval = cm3_helper_translate_curve_to_hw_format(plane_state->ctx, + &plane_state->in_shaper_func, + &dpp_base->shaper_params, true); + lut_params = rval ? &dpp_base->shaper_params : NULL; + } + result &= mpc->funcs->program_shaper(mpc, lut_params, mpcc_id); + + // 3D + if (mpc->funcs->program_3dlut) { + if (plane_state->lut3d_func.state.bits.initialized == 1) + result &= mpc->funcs->program_3dlut(mpc, &plane_state->lut3d_func.lut_3d, mpcc_id); + else + result &= mpc->funcs->program_3dlut(mpc, NULL, mpcc_id); } return result; @@ -1824,9 +1948,10 @@ void dcn401_perform_3dlut_wa_unlock(struct pipe_ctx *pipe_ctx) for (odm_pipe = pipe_ctx; odm_pipe != NULL; odm_pipe = odm_pipe->next_odm_pipe) { for (mpc_pipe = odm_pipe; mpc_pipe != NULL; mpc_pipe = mpc_pipe->bottom_pipe) { - if (mpc_pipe->plane_state && - mpc_pipe->plane_state->cm.flags.bits.lut3d_enable && - mpc_pipe->plane_state->cm.flags.bits.lut3d_dma_enable) { + if (mpc_pipe->plane_state && mpc_pipe->plane_state->mcm_luts.lut3d_data.lut3d_src + == DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM + && mpc_pipe->plane_state->mcm_shaper_3dlut_setting + == DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER_3DLUT) { wa_pipes[wa_pipe_ct++] = mpc_pipe; } } diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c index 46f2f9833d9e5..53ff5a02af023 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c @@ -394,6 +394,68 @@ void dcn42_program_cm_hist( plane_state->cm_hist_control, plane_state->color_space); } +static void dc_get_lut_xbar( + enum dc_cm2_gpu_mem_pixel_component_order order, + enum hubp_3dlut_fl_crossbar_bit_slice *cr_r, + enum hubp_3dlut_fl_crossbar_bit_slice *y_g, + enum hubp_3dlut_fl_crossbar_bit_slice *cb_b) +{ + switch (order) { + case DC_CM2_GPU_MEM_PIXEL_COMPONENT_ORDER_RGBA: + *cr_r = hubp_3dlut_fl_crossbar_bit_slice_32_47; + *y_g = hubp_3dlut_fl_crossbar_bit_slice_16_31; + *cb_b = hubp_3dlut_fl_crossbar_bit_slice_0_15; + break; + case DC_CM2_GPU_MEM_PIXEL_COMPONENT_ORDER_BGRA: + *cr_r = hubp_3dlut_fl_crossbar_bit_slice_0_15; + *y_g = hubp_3dlut_fl_crossbar_bit_slice_16_31; + *cb_b = hubp_3dlut_fl_crossbar_bit_slice_32_47; + break; + } +} + +static void dc_get_lut_mode( + enum dc_cm2_gpu_mem_layout layout, + enum hubp_3dlut_fl_mode *mode, + enum hubp_3dlut_fl_addressing_mode *addr_mode) +{ + switch (layout) { + case DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_RGB: + *mode = hubp_3dlut_fl_mode_native_1; + *addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear; + break; + case DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_BGR: + *mode = hubp_3dlut_fl_mode_native_2; + *addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear; + break; + case DC_CM2_GPU_MEM_LAYOUT_1D_PACKED_LINEAR: + *mode = hubp_3dlut_fl_mode_transform; + *addr_mode = hubp_3dlut_fl_addressing_mode_simple_linear; + break; + default: + *mode = hubp_3dlut_fl_mode_disable; + *addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear; + break; + } +} + +static void dc_get_lut_format( + enum dc_cm2_gpu_mem_format dc_format, + enum hubp_3dlut_fl_format *format) +{ + switch (dc_format) { + case DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12MSB: + *format = hubp_3dlut_fl_format_unorm_12msb_bitslice; + break; + case DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12LSB: + *format = hubp_3dlut_fl_format_unorm_12lsb_bitslice; + break; + case DC_CM2_GPU_MEM_FORMAT_16161616_FLOAT_FP1_5_10: + *format = hubp_3dlut_fl_format_float_fp1_5_10; + break; + } +} + static bool dc_is_rmcm_3dlut_supported(struct hubp *hubp, struct mpc *mpc) { if (mpc->funcs->rmcm.power_on_shaper_3dlut && @@ -404,17 +466,119 @@ static bool dc_is_rmcm_3dlut_supported(struct hubp *hubp, struct mpc *mpc) return false; } +static bool is_rmcm_3dlut_fl_supported(struct dc *dc, enum dc_cm2_gpu_mem_size size) +{ + if (!dc->caps.color.mpc.rmcm_3d_lut_caps.dma_3d_lut) + return false; + if (size == DC_CM2_GPU_MEM_SIZE_171717) + return (dc->caps.color.mpc.rmcm_3d_lut_caps.lut_dim_caps.dim_17); + else if (size == DC_CM2_GPU_MEM_SIZE_333333) + return (dc->caps.color.mpc.rmcm_3d_lut_caps.lut_dim_caps.dim_33); + return false; +} + +static void dcn42_set_mcm_location_post_blend(struct dc *dc, struct pipe_ctx *pipe_ctx, bool bPostBlend) +{ + struct mpc *mpc = dc->res_pool->mpc; + int mpcc_id = pipe_ctx->plane_res.hubp->inst; + + if (!pipe_ctx->plane_state) + return; + + mpc->funcs->set_movable_cm_location(mpc, MPCC_MOVABLE_CM_LOCATION_BEFORE, mpcc_id); + pipe_ctx->plane_state->mcm_location = (bPostBlend) ? + MPCC_MOVABLE_CM_LOCATION_AFTER : + MPCC_MOVABLE_CM_LOCATION_BEFORE; +} + +static void dcn42_get_mcm_lut_xable_from_pipe_ctx(struct dc *dc, struct pipe_ctx *pipe_ctx, + enum MCM_LUT_XABLE *shaper_xable, + enum MCM_LUT_XABLE *lut3d_xable, + enum MCM_LUT_XABLE *lut1d_xable) +{ + enum dc_cm2_shaper_3dlut_setting shaper_3dlut_setting = DC_CM2_SHAPER_3DLUT_SETTING_BYPASS_ALL; + bool lut1d_enable = false; + struct mpc *mpc = dc->res_pool->mpc; + int mpcc_id = pipe_ctx->plane_res.hubp->inst; + + if (!pipe_ctx->plane_state) + return; + shaper_3dlut_setting = pipe_ctx->plane_state->mcm_shaper_3dlut_setting; + lut1d_enable = pipe_ctx->plane_state->mcm_lut1d_enable; + mpc->funcs->set_movable_cm_location(mpc, MPCC_MOVABLE_CM_LOCATION_BEFORE, mpcc_id); + pipe_ctx->plane_state->mcm_location = MPCC_MOVABLE_CM_LOCATION_BEFORE; + + *lut1d_xable = lut1d_enable ? MCM_LUT_ENABLE : MCM_LUT_DISABLE; + + switch (shaper_3dlut_setting) { + case DC_CM2_SHAPER_3DLUT_SETTING_BYPASS_ALL: + *lut3d_xable = *shaper_xable = MCM_LUT_DISABLE; + break; + case DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER: + *lut3d_xable = MCM_LUT_DISABLE; + *shaper_xable = MCM_LUT_ENABLE; + break; + case DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER_3DLUT: + *lut3d_xable = *shaper_xable = MCM_LUT_ENABLE; + break; + } +} + +static void fl_get_lut_mode( + enum dc_cm2_gpu_mem_layout layout, + enum dc_cm2_gpu_mem_size size, + enum hubp_3dlut_fl_mode *mode, + enum hubp_3dlut_fl_addressing_mode *addr_mode, + enum hubp_3dlut_fl_width *width) +{ + *width = hubp_3dlut_fl_width_17; + + if (size == DC_CM2_GPU_MEM_SIZE_333333) + *width = hubp_3dlut_fl_width_33; + + switch (layout) { + case DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_RGB: + *mode = hubp_3dlut_fl_mode_native_1; + *addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear; + break; + case DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_BGR: + *mode = hubp_3dlut_fl_mode_native_2; + *addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear; + break; + case DC_CM2_GPU_MEM_LAYOUT_1D_PACKED_LINEAR: + *mode = hubp_3dlut_fl_mode_transform; + *addr_mode = hubp_3dlut_fl_addressing_mode_simple_linear; + break; + default: + *mode = hubp_3dlut_fl_mode_disable; + *addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear; + break; + } +} + bool dcn42_program_rmcm_luts( struct hubp *hubp, struct pipe_ctx *pipe_ctx, - const struct dc_plane_cm *cm, + enum dc_cm2_transfer_func_source lut3d_src, + struct dc_cm2_func_luts *mcm_luts, struct mpc *mpc, + bool lut_bank_a, int mpcc_id) { struct dpp *dpp_base = pipe_ctx->plane_res.dpp; union mcm_lut_params m_lut_params = {0}; + enum MCM_LUT_XABLE shaper_xable, lut3d_xable = MCM_LUT_DISABLE, lut1d_xable; + enum hubp_3dlut_fl_mode mode; + enum hubp_3dlut_fl_addressing_mode addr_mode; + enum hubp_3dlut_fl_format format = hubp_3dlut_fl_format_unorm_12msb_bitslice; + enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_y_g = hubp_3dlut_fl_crossbar_bit_slice_16_31; + enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cb_b = hubp_3dlut_fl_crossbar_bit_slice_0_15; + enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cr_r = hubp_3dlut_fl_crossbar_bit_slice_32_47; + enum hubp_3dlut_fl_width width = hubp_3dlut_fl_width_17; + struct dc *dc = hubp->ctx->dc; + struct hubp_fl_3dlut_config fl_config; struct mpc_fl_3dlut_config mpc_fl_config; struct dc_stream_state *stream = pipe_ctx->stream; @@ -422,23 +586,25 @@ bool dcn42_program_rmcm_luts( // true->false when it can be allocated at DI time struct dc_rmcm_3dlut *rmcm_3dlut = dc_stream_get_3dlut_for_stream(dc, stream, false); - bool lut_bank_a = true; // TODO get from HW - //check to see current pipe is part of a stream with allocated rmcm 3dlut if (!rmcm_3dlut) return false; + rmcm_3dlut->protection_bits = mcm_luts->lut3d_data.rmcm_tmz; + + dcn42_get_mcm_lut_xable_from_pipe_ctx(dc, pipe_ctx, &shaper_xable, &lut3d_xable, &lut1d_xable); + /* Shaper */ - if (cm->flags.bits.shaper_enable) { + if (mcm_luts->shaper) { memset(&m_lut_params, 0, sizeof(m_lut_params)); - if (cm->shaper_func.type == TF_TYPE_HWPWL) { - m_lut_params.pwl = &cm->shaper_func.pwl; - } else if (cm->shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) { + if (mcm_luts->shaper->type == TF_TYPE_HWPWL) { + m_lut_params.pwl = &mcm_luts->shaper->pwl; + } else if (mcm_luts->shaper->type == TF_TYPE_DISTRIBUTED_POINTS) { ASSERT(false); cm_helper_translate_curve_to_hw_format( dc->ctx, - &cm->shaper_func, + mcm_luts->shaper, &dpp_base->shaper_params, true); m_lut_params.pwl = &dpp_base->shaper_params; } @@ -454,21 +620,58 @@ bool dcn42_program_rmcm_luts( } /* 3DLUT */ - if (!cm->flags.bits.lut3d_dma_enable) { + switch (lut3d_src) { + case DC_CM2_TRANSFER_FUNC_SOURCE_SYSMEM: memset(&m_lut_params, 0, sizeof(m_lut_params)); // Don't know what to do in this case. - } else { - if (!dc_is_rmcm_3dlut_supported(hubp, mpc)) + //case DC_CM2_TRANSFER_FUNC_SOURCE_SYSMEM: + break; + case DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM: + fl_get_lut_mode(mcm_luts->lut3d_data.gpu_mem_params.layout, + mcm_luts->lut3d_data.gpu_mem_params.size, + &mode, + &addr_mode, + &width); + + if (!dc_is_rmcm_3dlut_supported(hubp, mpc) || + !mpc->funcs->rmcm.is_config_supported( + (width == hubp_3dlut_fl_width_17 || + width == hubp_3dlut_fl_width_transformed) ? 17 : 33)) return false; + // setting native or transformed mode, + dc_get_lut_mode(mcm_luts->lut3d_data.gpu_mem_params.layout, &mode, &addr_mode); + //seems to be only for the MCM - mpc_fl_config.enabled = cm->flags.bits.lut3d_enable; - mpc_fl_config.size = cm->lut3d_dma.size; + dc_get_lut_format(mcm_luts->lut3d_data.gpu_mem_params.format_params.format, &format); + + dc_get_lut_xbar( + mcm_luts->lut3d_data.gpu_mem_params.component_order, + &crossbar_bit_slice_cr_r, + &crossbar_bit_slice_y_g, + &crossbar_bit_slice_cb_b); + + fl_config.mode = mode; + fl_config.enabled = lut3d_xable != MCM_LUT_DISABLE; + fl_config.address = mcm_luts->lut3d_data.gpu_mem_params.addr; + fl_config.format = format; + fl_config.crossbar_bit_slice_y_g = crossbar_bit_slice_y_g; + fl_config.crossbar_bit_slice_cb_b = crossbar_bit_slice_cb_b; + fl_config.crossbar_bit_slice_cr_r = crossbar_bit_slice_cr_r; + fl_config.width = width; + fl_config.protection_bits = rmcm_3dlut->protection_bits; + fl_config.addr_mode = addr_mode; + fl_config.layout = mcm_luts->lut3d_data.gpu_mem_params.layout; + fl_config.bias = mcm_luts->lut3d_data.gpu_mem_params.format_params.float_params.bias; + fl_config.scale = mcm_luts->lut3d_data.gpu_mem_params.format_params.float_params.scale; + + mpc_fl_config.enabled = fl_config.enabled; + mpc_fl_config.width = width; mpc_fl_config.select_lut_bank_a = lut_bank_a; - mpc_fl_config.bit_depth = 0; + mpc_fl_config.bit_depth = mcm_luts->lut3d_data.gpu_mem_params.bit_depth; mpc_fl_config.hubp_index = hubp->inst; - mpc_fl_config.bias = cm->lut3d_dma.bias; - mpc_fl_config.scale = cm->lut3d_dma.scale; + mpc_fl_config.bias = mcm_luts->lut3d_data.gpu_mem_params.format_params.float_params.bias; + mpc_fl_config.scale = mcm_luts->lut3d_data.gpu_mem_params.format_params.float_params.scale; //1. power down the block mpc->funcs->rmcm.power_on_shaper_3dlut(mpc, mpcc_id, false); @@ -476,44 +679,268 @@ bool dcn42_program_rmcm_luts( //2. program RMCM - 3dlut reg programming mpc->funcs->rmcm.fl_3dlut_configure(mpc, &mpc_fl_config, mpcc_id); - /* HUBP */ - if (hubp->funcs->hubp_program_3dlut_fl_config) - hubp->funcs->hubp_program_3dlut_fl_config(hubp, &cm->lut3d_dma); - - if (hubp->funcs->hubp_program_3dlut_fl_addr) - hubp->funcs->hubp_program_3dlut_fl_addr(hubp, &cm->lut3d_dma.addr); + hubp->funcs->hubp_program_3dlut_fl_config(hubp, &fl_config); //3. power on the block mpc->funcs->rmcm.power_on_shaper_3dlut(mpc, mpcc_id, true); + + break; + default: + return false; } return true; } -bool dcn42_set_mcm_luts(struct pipe_ctx *pipe_ctx, - const struct dc_plane_state *plane_state) +void dcn42_populate_mcm_luts(struct dc *dc, + struct pipe_ctx *pipe_ctx, + struct dc_cm2_func_luts mcm_luts, + bool lut_bank_a) { - struct dc *dc = pipe_ctx->plane_res.hubp->ctx->dc; + struct dpp *dpp_base = pipe_ctx->plane_res.dpp; struct hubp *hubp = pipe_ctx->plane_res.hubp; - const struct dc_plane_cm *cm = &plane_state->cm; - struct mpc *mpc = dc->res_pool->mpc; int mpcc_id = hubp->inst; - bool result; - - /* MCM */ - result = dcn401_set_mcm_luts(pipe_ctx, plane_state); - - /* RMCM */ - if (cm->flags.bits.rmcm_enable && cm->flags.bits.lut3d_dma_enable) { - /* TODO - move RMCM to its own block */ + struct mpc *mpc = dc->res_pool->mpc; + union mcm_lut_params m_lut_params; + enum dc_cm2_transfer_func_source lut3d_src = mcm_luts.lut3d_data.lut3d_src; + enum hubp_3dlut_fl_format format = 0; + enum hubp_3dlut_fl_mode mode; + enum hubp_3dlut_fl_width width = 0; + enum hubp_3dlut_fl_addressing_mode addr_mode; + enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_y_g = 0; + enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cb_b = 0; + enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cr_r = 0; + enum MCM_LUT_XABLE shaper_xable = MCM_LUT_DISABLE; + enum MCM_LUT_XABLE lut3d_xable = MCM_LUT_DISABLE; + enum MCM_LUT_XABLE lut1d_xable = MCM_LUT_DISABLE; + bool rval; + + dcn42_get_mcm_lut_xable_from_pipe_ctx(dc, pipe_ctx, &shaper_xable, &lut3d_xable, &lut1d_xable); + + //MCM - setting its location (Before/After) blender + //set to post blend (true) + dcn42_set_mcm_location_post_blend( + dc, + pipe_ctx, + mcm_luts.lut3d_data.mpc_mcm_post_blend); + + //RMCM - 3dLUT+Shaper + if (mcm_luts.lut3d_data.rmcm_3dlut_enable && + is_rmcm_3dlut_fl_supported(dc, mcm_luts.lut3d_data.gpu_mem_params.size)) { dcn42_program_rmcm_luts( hubp, pipe_ctx, - cm, + lut3d_src, + &mcm_luts, mpc, + lut_bank_a, mpcc_id); } + /* 1D LUT */ + if (mcm_luts.lut1d_func) { + memset(&m_lut_params, 0, sizeof(m_lut_params)); + if (mcm_luts.lut1d_func->type == TF_TYPE_HWPWL) + m_lut_params.pwl = &mcm_luts.lut1d_func->pwl; + else if (mcm_luts.lut1d_func->type == TF_TYPE_DISTRIBUTED_POINTS) { + rval = cm3_helper_translate_curve_to_hw_format(mpc->ctx, + mcm_luts.lut1d_func, + &dpp_base->regamma_params, false); + m_lut_params.pwl = rval ? &dpp_base->regamma_params : NULL; + } + if (m_lut_params.pwl) { + if (mpc->funcs->populate_lut) + mpc->funcs->populate_lut(mpc, MCM_LUT_1DLUT, m_lut_params, lut_bank_a, mpcc_id); + } + if (mpc->funcs->program_lut_mode) + mpc->funcs->program_lut_mode(mpc, MCM_LUT_1DLUT, lut1d_xable && m_lut_params.pwl, lut_bank_a, mpcc_id); + } + + /* Shaper */ + if (mcm_luts.shaper && mcm_luts.lut3d_data.mpc_3dlut_enable) { + memset(&m_lut_params, 0, sizeof(m_lut_params)); + if (mcm_luts.shaper->type == TF_TYPE_HWPWL) + m_lut_params.pwl = &mcm_luts.shaper->pwl; + else if (mcm_luts.shaper->type == TF_TYPE_DISTRIBUTED_POINTS) { + ASSERT(false); + rval = cm3_helper_translate_curve_to_hw_format(mpc->ctx, + mcm_luts.shaper, + &dpp_base->regamma_params, true); + m_lut_params.pwl = rval ? &dpp_base->regamma_params : NULL; + } + if (m_lut_params.pwl) { + if (mpc->funcs->mcm.populate_lut) + mpc->funcs->mcm.populate_lut(mpc, m_lut_params, lut_bank_a, mpcc_id); + if (mpc->funcs->program_lut_mode) + mpc->funcs->program_lut_mode(mpc, MCM_LUT_SHAPER, MCM_LUT_ENABLE, lut_bank_a, mpcc_id); + } + } + + /* 3DLUT */ + switch (lut3d_src) { + case DC_CM2_TRANSFER_FUNC_SOURCE_SYSMEM: + memset(&m_lut_params, 0, sizeof(m_lut_params)); + if (hubp->funcs->hubp_enable_3dlut_fl) + hubp->funcs->hubp_enable_3dlut_fl(hubp, false); + + if (mcm_luts.lut3d_data.lut3d_func && mcm_luts.lut3d_data.lut3d_func->state.bits.initialized) { + m_lut_params.lut3d = &mcm_luts.lut3d_data.lut3d_func->lut_3d; + if (mpc->funcs->populate_lut) + mpc->funcs->populate_lut(mpc, MCM_LUT_3DLUT, m_lut_params, lut_bank_a, mpcc_id); + if (mpc->funcs->program_lut_mode) + mpc->funcs->program_lut_mode(mpc, MCM_LUT_3DLUT, lut3d_xable, lut_bank_a, + mpcc_id); + } + break; + case DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM: + switch (mcm_luts.lut3d_data.gpu_mem_params.size) { + case DC_CM2_GPU_MEM_SIZE_333333: + width = hubp_3dlut_fl_width_33; + break; + case DC_CM2_GPU_MEM_SIZE_171717: + width = hubp_3dlut_fl_width_17; + break; + case DC_CM2_GPU_MEM_SIZE_TRANSFORMED: + width = hubp_3dlut_fl_width_transformed; + break; + default: + //TODO: Handle default case + break; + } + + //check for support + if (mpc->funcs->mcm.is_config_supported && + !mpc->funcs->mcm.is_config_supported(width)) + break; + + if (mpc->funcs->program_lut_read_write_control) + mpc->funcs->program_lut_read_write_control(mpc, MCM_LUT_3DLUT, lut_bank_a, mpcc_id); + if (mpc->funcs->program_lut_mode) + mpc->funcs->program_lut_mode(mpc, MCM_LUT_3DLUT, lut3d_xable, lut_bank_a, mpcc_id); + + if (hubp->funcs->hubp_program_3dlut_fl_addr) + hubp->funcs->hubp_program_3dlut_fl_addr(hubp, mcm_luts.lut3d_data.gpu_mem_params.addr); + + if (mpc->funcs->mcm.program_bit_depth) + mpc->funcs->mcm.program_bit_depth(mpc, mcm_luts.lut3d_data.gpu_mem_params.bit_depth, mpcc_id); + + dc_get_lut_mode(mcm_luts.lut3d_data.gpu_mem_params.layout, &mode, &addr_mode); + if (hubp->funcs->hubp_program_3dlut_fl_mode) + hubp->funcs->hubp_program_3dlut_fl_mode(hubp, mode); + + if (hubp->funcs->hubp_program_3dlut_fl_addressing_mode) + hubp->funcs->hubp_program_3dlut_fl_addressing_mode(hubp, addr_mode); + + switch (mcm_luts.lut3d_data.gpu_mem_params.format_params.format) { + case DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12MSB: + format = hubp_3dlut_fl_format_unorm_12msb_bitslice; + break; + case DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12LSB: + format = hubp_3dlut_fl_format_unorm_12lsb_bitslice; + break; + case DC_CM2_GPU_MEM_FORMAT_16161616_FLOAT_FP1_5_10: + format = hubp_3dlut_fl_format_float_fp1_5_10; + break; + } + if (hubp->funcs->hubp_program_3dlut_fl_format) + hubp->funcs->hubp_program_3dlut_fl_format(hubp, format); + if (hubp->funcs->hubp_update_3dlut_fl_bias_scale && + mpc->funcs->mcm.program_bias_scale) { + mpc->funcs->mcm.program_bias_scale(mpc, + mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.bias, + mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.scale, + mpcc_id); + hubp->funcs->hubp_update_3dlut_fl_bias_scale(hubp, + mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.bias, + mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.scale); + } + + //navi 4x has a bug and r and blue are swapped and need to be worked around here in + //TODO: need to make a method for get_xbar per asic OR do the workaround in program_crossbar for 4x + dc_get_lut_xbar( + mcm_luts.lut3d_data.gpu_mem_params.component_order, + &crossbar_bit_slice_cr_r, + &crossbar_bit_slice_y_g, + &crossbar_bit_slice_cb_b); + + if (hubp->funcs->hubp_program_3dlut_fl_crossbar) + hubp->funcs->hubp_program_3dlut_fl_crossbar(hubp, + crossbar_bit_slice_cr_r, + crossbar_bit_slice_y_g, + crossbar_bit_slice_cb_b); + + if (mpc->funcs->mcm.program_lut_read_write_control) + mpc->funcs->mcm.program_lut_read_write_control(mpc, MCM_LUT_3DLUT, lut_bank_a, true, mpcc_id); + + if (mpc->funcs->mcm.program_3dlut_size) + mpc->funcs->mcm.program_3dlut_size(mpc, width, mpcc_id); + + if (mpc->funcs->update_3dlut_fast_load_select) + mpc->funcs->update_3dlut_fast_load_select(mpc, mpcc_id, hubp->inst); + + if (hubp->funcs->hubp_enable_3dlut_fl) + hubp->funcs->hubp_enable_3dlut_fl(hubp, true); + else { + if (mpc->funcs->program_lut_mode) { + mpc->funcs->program_lut_mode(mpc, MCM_LUT_SHAPER, MCM_LUT_DISABLE, lut_bank_a, mpcc_id); + mpc->funcs->program_lut_mode(mpc, MCM_LUT_3DLUT, MCM_LUT_DISABLE, lut_bank_a, mpcc_id); + mpc->funcs->program_lut_mode(mpc, MCM_LUT_1DLUT, MCM_LUT_DISABLE, lut_bank_a, mpcc_id); + } + } + break; + } +} + +bool dcn42_set_mcm_luts(struct pipe_ctx *pipe_ctx, + const struct dc_plane_state *plane_state) +{ + struct dpp *dpp_base = pipe_ctx->plane_res.dpp; + int mpcc_id = pipe_ctx->plane_res.hubp->inst; + struct dc *dc = pipe_ctx->stream_res.opp->ctx->dc; + struct mpc *mpc = dc->res_pool->mpc; + bool result; + const struct pwl_params *lut_params = NULL; + bool rval; + + if (plane_state->mcm_luts.lut3d_data.lut3d_src == DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM) { + dcn42_populate_mcm_luts(dc, pipe_ctx, plane_state->mcm_luts, plane_state->lut_bank_a); + return true; + } + + mpc->funcs->set_movable_cm_location(mpc, MPCC_MOVABLE_CM_LOCATION_BEFORE, mpcc_id); + pipe_ctx->plane_state->mcm_location = MPCC_MOVABLE_CM_LOCATION_BEFORE; + // 1D LUT + if (plane_state->blend_tf.type == TF_TYPE_HWPWL) + lut_params = &plane_state->blend_tf.pwl; + else if (plane_state->blend_tf.type == TF_TYPE_DISTRIBUTED_POINTS) { + rval = cm3_helper_translate_curve_to_hw_format(plane_state->ctx, + &plane_state->blend_tf, + &dpp_base->regamma_params, false); + lut_params = rval ? &dpp_base->regamma_params : NULL; + } + result = mpc->funcs->program_1dlut(mpc, lut_params, mpcc_id); + lut_params = NULL; + + // Shaper + if (plane_state->in_shaper_func.type == TF_TYPE_HWPWL) + lut_params = &plane_state->in_shaper_func.pwl; + else if (plane_state->in_shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) { + // TODO: dpp_base replace + rval = cm3_helper_translate_curve_to_hw_format(plane_state->ctx, + &plane_state->in_shaper_func, + &dpp_base->shaper_params, true); + lut_params = rval ? &dpp_base->shaper_params : NULL; + } + result &= mpc->funcs->program_shaper(mpc, lut_params, mpcc_id); + + // 3D + if (mpc->funcs->program_3dlut) { + if (plane_state->lut3d_func.state.bits.initialized == 1) + result &= mpc->funcs->program_3dlut(mpc, &plane_state->lut3d_func.lut_3d, mpcc_id); + else + result &= mpc->funcs->program_3dlut(mpc, NULL, mpcc_id); + } + return result; } void dcn42_hardware_release(struct dc *dc) diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.h index c4cfeed45b190..0539ee0ffaee3 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.h @@ -18,11 +18,18 @@ void dcn42_program_cm_hist( bool dcn42_set_mcm_luts(struct pipe_ctx *pipe_ctx, const struct dc_plane_state *plane_state); +void dcn42_populate_mcm_luts(struct dc *dc, + struct pipe_ctx *pipe_ctx, + struct dc_cm2_func_luts mcm_luts, + bool lut_bank_a); + bool dcn42_program_rmcm_luts( struct hubp *hubp, struct pipe_ctx *pipe_ctx, - const struct dc_plane_cm *cm, + enum dc_cm2_transfer_func_source lut3d_src, + struct dc_cm2_func_luts *mcm_luts, struct mpc *mpc, + bool lut_bank_a, int mpcc_id); void dcn42_hardware_release(struct dc *dc); diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h b/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h index 2a5a81d159507..fc005aadfd551 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h @@ -89,7 +89,7 @@ enum hubp_3dlut_fl_addressing_mode { enum hubp_3dlut_fl_width { hubp_3dlut_fl_width_17 = 17, hubp_3dlut_fl_width_33 = 33, - hubp_3dlut_fl_width_17_transformed = 4916, //mpc default + hubp_3dlut_fl_width_transformed = 4916, //mpc default }; enum hubp_3dlut_fl_crossbar_bit_slice { @@ -99,6 +99,22 @@ enum hubp_3dlut_fl_crossbar_bit_slice { hubp_3dlut_fl_crossbar_bit_slice_48_63 = 3 }; +struct hubp_fl_3dlut_config { + bool enabled; + enum hubp_3dlut_fl_width width; + enum hubp_3dlut_fl_mode mode; + enum hubp_3dlut_fl_format format; + uint16_t bias; + uint16_t scale; + struct dc_plane_address address; + enum hubp_3dlut_fl_addressing_mode addr_mode; + enum dc_cm2_gpu_mem_layout layout; + uint8_t protection_bits; + enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_y_g; + enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cb_b; + enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cr_r; +}; + struct hubp { const struct hubp_funcs *funcs; struct dc_context *ctx; @@ -273,15 +289,24 @@ struct hubp_funcs { void (*hubp_wait_pipe_read_start)(struct hubp *hubp); void (*hubp_program_mcache_id_and_split_coordinate)(struct hubp *hubp, struct dml2_hubp_pipe_mcache_regs *mcache_regs); + void (*hubp_update_3dlut_fl_bias_scale)(struct hubp *hubp, uint16_t bias, uint16_t scale); + void (*hubp_program_3dlut_fl_mode)(struct hubp *hubp, + enum hubp_3dlut_fl_mode mode); + void (*hubp_program_3dlut_fl_format)(struct hubp *hubp, + enum hubp_3dlut_fl_format format); void (*hubp_program_3dlut_fl_addr)(struct hubp *hubp, - const struct dc_plane_address *address); - void (*hubp_program_3dlut_fl_config)(struct hubp *hubp, - const struct dc_3dlut_dma *config); + const struct dc_plane_address address); void (*hubp_program_3dlut_fl_dlg_param)(struct hubp *hubp, int refcyc_per_3dlut_group); void (*hubp_enable_3dlut_fl)(struct hubp *hubp, bool enable); + void (*hubp_program_3dlut_fl_addressing_mode)(struct hubp *hubp, enum hubp_3dlut_fl_addressing_mode addr_mode); + void (*hubp_program_3dlut_fl_width)(struct hubp *hubp, enum hubp_3dlut_fl_width width); + void (*hubp_program_3dlut_fl_tmz_protected)(struct hubp *hubp, uint8_t protection_bits); void (*hubp_program_3dlut_fl_crossbar)(struct hubp *hubp, - enum dc_cm_lut_pixel_format format); + enum hubp_3dlut_fl_crossbar_bit_slice bit_slice_y_g, + enum hubp_3dlut_fl_crossbar_bit_slice bit_slice_cb_b, + enum hubp_3dlut_fl_crossbar_bit_slice bit_slice_cr_r); int (*hubp_get_3dlut_fl_done)(struct hubp *hubp); + void (*hubp_program_3dlut_fl_config)(struct hubp *hubp, struct hubp_fl_3dlut_config *cfg); void (*hubp_clear_tiling)(struct hubp *hubp); uint32_t (*hubp_get_current_read_line)(struct hubp *hubp); uint32_t (*hubp_get_det_config_error)(struct hubp *hubp); diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/mpc.h b/drivers/gpu/drm/amd/display/dc/inc/hw/mpc.h index f5617674bea8e..54eb2eba68bf4 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/mpc.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/mpc.h @@ -54,7 +54,6 @@ #include "dc_hw_types.h" #include "hw_shared.h" #include "transform.h" -#include "dc_types.h" #define MAX_MPCC 6 #define MAX_OPP 6 @@ -102,6 +101,13 @@ enum mpcc_movable_cm_location { MPCC_MOVABLE_CM_LOCATION_AFTER, }; +enum MCM_LUT_XABLE { + MCM_LUT_DISABLE, + MCM_LUT_DISABLED = MCM_LUT_DISABLE, + MCM_LUT_ENABLE, + MCM_LUT_ENABLED = MCM_LUT_ENABLE, +}; + enum MCM_LUT_ID { MCM_LUT_3DLUT, MCM_LUT_1DLUT, @@ -110,7 +116,7 @@ enum MCM_LUT_ID { struct mpc_fl_3dlut_config { bool enabled; - enum dc_cm_lut_size size; + uint16_t width; bool select_lut_bank_a; uint16_t bit_depth; int hubp_index; @@ -1069,11 +1075,8 @@ struct mpc_funcs { * * void */ - void (*populate_lut)(struct mpc *mpc, - const enum MCM_LUT_ID id, - const union mcm_lut_params *params, - const bool lut_bank_a, - const int mpcc_id); + void (*populate_lut)(struct mpc *mpc, const enum MCM_LUT_ID id, const union mcm_lut_params params, + bool lut_bank_a, int mpcc_id); /** * @program_lut_read_write_control: @@ -1084,18 +1087,13 @@ struct mpc_funcs { * - [in/out] mpc - MPC context. * - [in] id * - [in] lut_bank_a - * - [in] bit_depth * - [in] mpcc_id * * Return: * * void */ - void (*program_lut_read_write_control)(struct mpc *mpc, - const enum MCM_LUT_ID id, - const bool lut_bank_a, - const unsigned int bit_depth, - const int mpcc_id); + void (*program_lut_read_write_control)(struct mpc *mpc, const enum MCM_LUT_ID id, bool lut_bank_a, int mpcc_id); /** * @program_lut_mode: @@ -1105,44 +1103,33 @@ struct mpc_funcs { * Parameters: * - [in/out] mpc - MPC context. * - [in] id - * - [in] enable + * - [in] xable * - [in] lut_bank_a - * - [in] size * - [in] mpcc_id * * Return: * * void */ - void (*program_lut_mode)(struct mpc *mpc, - const enum MCM_LUT_ID id, - const bool enable, - const bool lut_bank_a, - const enum dc_cm_lut_size size, - const int mpcc_id); - + void (*program_lut_mode)(struct mpc *mpc, const enum MCM_LUT_ID id, const enum MCM_LUT_XABLE xable, + bool lut_bank_a, int mpcc_id); /** - * @get_lut_mode: - * - * Obtains enablement and ram bank status. - * - * Parameters: - * - [in/out] mpc - MPC context. - * - [in] id - * - [in] mpcc_id - * - [out] enable - * - [out] lut_bank_a - * - * Return: - * - * void - */ - void (*get_lut_mode)(struct mpc *mpc, - const enum MCM_LUT_ID id, - const int mpcc_id, - bool *enable, - bool *lut_bank_a); + * @mcm: + * + * MPC MCM new HW sequential programming functions + */ + struct { + void (*program_3dlut_size)(struct mpc *mpc, uint32_t width, int mpcc_id); + void (*program_bias_scale)(struct mpc *mpc, uint16_t bias, uint16_t scale, int mpcc_id); + void (*program_bit_depth)(struct mpc *mpc, uint16_t bit_depth, int mpcc_id); + bool (*is_config_supported)(uint32_t width); + void (*program_lut_read_write_control)(struct mpc *mpc, const enum MCM_LUT_ID id, + bool lut_bank_a, bool enabled, int mpcc_id); + + void (*populate_lut)(struct mpc *mpc, const union mcm_lut_params params, + bool lut_bank_a, int mpcc_id); + } mcm; /** * @rmcm: @@ -1155,11 +1142,9 @@ struct mpc_funcs { void (*update_3dlut_fast_load_select)(struct mpc *mpc, int mpcc_id, int hubp_idx); void (*program_lut_read_write_control)(struct mpc *mpc, const enum MCM_LUT_ID id, bool lut_bank_a, bool enabled, int mpcc_id); - void (*program_lut_mode)(struct mpc *mpc, - bool enable, - bool lut_bank_a, - int mpcc_id); - void (*program_3dlut_size)(struct mpc *mpc, const enum dc_cm_lut_size size, int mpcc_id); + void (*program_lut_mode)(struct mpc *mpc, const enum MCM_LUT_XABLE xable, + bool lut_bank_a, int mpcc_id); + void (*program_3dlut_size)(struct mpc *mpc, uint32_t width, int mpcc_id); void (*program_bias_scale)(struct mpc *mpc, uint16_t bias, uint16_t scale, int mpcc_id); void (*program_bit_depth)(struct mpc *mpc, uint16_t bit_depth, int mpcc_id); bool (*is_config_supported)(uint32_t width); diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.c b/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.c index e0617db2d0c1d..ce1ee2062e41d 100644 --- a/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.c +++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.c @@ -73,15 +73,56 @@ void mpc401_set_movable_cm_location(struct mpc *mpc, enum mpcc_movable_cm_locati } } -void mpc401_populate_lut(struct mpc *mpc, - const enum MCM_LUT_ID id, - const union mcm_lut_params *params, - const bool lut_bank_a, - const int mpcc_id) +static enum dc_lut_mode get3dlut_config( + struct mpc *mpc, + bool *is_17x17x17, + bool *is_12bits_color_channel, + int mpcc_id) +{ + uint32_t i_mode, i_enable_10bits, lut_size; + enum dc_lut_mode mode; + struct dcn401_mpc *mpc401 = TO_DCN401_MPC(mpc); + + REG_GET(MPCC_MCM_3DLUT_MODE[mpcc_id], + MPCC_MCM_3DLUT_MODE_CURRENT, &i_mode); + + REG_GET(MPCC_MCM_3DLUT_READ_WRITE_CONTROL[mpcc_id], + MPCC_MCM_3DLUT_30BIT_EN, &i_enable_10bits); + + switch (i_mode) { + case 0: + mode = LUT_BYPASS; + break; + case 1: + mode = LUT_RAM_A; + break; + case 2: + mode = LUT_RAM_B; + break; + default: + mode = LUT_BYPASS; + break; + } + if (i_enable_10bits > 0) + *is_12bits_color_channel = false; + else + *is_12bits_color_channel = true; + + REG_GET(MPCC_MCM_3DLUT_MODE[mpcc_id], MPCC_MCM_3DLUT_SIZE, &lut_size); + + if (lut_size == 0) + *is_17x17x17 = true; + else + *is_17x17x17 = false; + + return mode; +} + +void mpc401_populate_lut(struct mpc *mpc, const enum MCM_LUT_ID id, const union mcm_lut_params params, bool lut_bank_a, int mpcc_id) { const enum dc_lut_mode next_mode = lut_bank_a ? LUT_RAM_A : LUT_RAM_B; - const struct pwl_params *lut1d = params->pwl; - const struct pwl_params *lut_shaper = params->pwl; + const struct pwl_params *lut1d = params.pwl; + const struct pwl_params *lut_shaper = params.pwl; bool is_17x17x17; bool is_12bits_color_channel; const struct dc_rgb *lut0; @@ -90,7 +131,7 @@ void mpc401_populate_lut(struct mpc *mpc, const struct dc_rgb *lut3; int lut_size0; int lut_size; - const struct tetrahedral_params *lut3d = params->lut3d; + const struct tetrahedral_params *lut3d = params.lut3d; switch (id) { case MCM_LUT_1DLUT: @@ -133,6 +174,8 @@ void mpc401_populate_lut(struct mpc *mpc, mpc32_power_on_shaper_3dlut(mpc, mpcc_id, true); + get3dlut_config(mpc, &is_17x17x17, &is_12bits_color_channel, mpcc_id); + is_17x17x17 = !lut3d->use_tetrahedral_9; is_12bits_color_channel = lut3d->use_12bits; if (is_17x17x17) { @@ -155,6 +198,8 @@ void mpc401_populate_lut(struct mpc *mpc, sizeof(lut3d->tetrahedral_9.lut1[0]); } + mpc32_select_3dlut_ram(mpc, next_mode, + is_12bits_color_channel, mpcc_id); mpc32_select_3dlut_ram_mask(mpc, 0x1, mpcc_id); if (is_12bits_color_channel) mpc32_set3dlut_ram12(mpc, lut0, lut_size0, mpcc_id); @@ -187,69 +232,46 @@ void mpc401_populate_lut(struct mpc *mpc, } -static uint32_t mpc401_cm_lut_size_to_3dlut_size(const enum dc_cm_lut_size cm_size) -{ - uint32_t size = 0; - - switch (cm_size) { - case CM_LUT_SIZE_999: - size = 1; - break; - case CM_LUT_SIZE_171717: - size = 0; - break; - default: - /* invalid LUT size */ - ASSERT(false); - size = 0; - break; - } - - return size; -} - void mpc401_program_lut_mode( struct mpc *mpc, const enum MCM_LUT_ID id, - const bool enable, - const bool lut_bank_a, - const enum dc_cm_lut_size size, - const int mpcc_id) + const enum MCM_LUT_XABLE xable, + bool lut_bank_a, + int mpcc_id) { - uint32_t lut_size; struct dcn401_mpc *mpc401 = TO_DCN401_MPC(mpc); switch (id) { case MCM_LUT_3DLUT: - if (enable) { - lut_size = mpc401_cm_lut_size_to_3dlut_size(size); - REG_UPDATE_2(MPCC_MCM_3DLUT_MODE[mpcc_id], - MPCC_MCM_3DLUT_MODE, lut_bank_a ? 1 : 2, - MPCC_MCM_3DLUT_SIZE, lut_size); - } else { - if (mpc->ctx->dc->debug.enable_mem_low_power.bits.mpc) - mpc32_power_on_shaper_3dlut(mpc, mpcc_id, false); + switch (xable) { + case MCM_LUT_DISABLE: REG_UPDATE(MPCC_MCM_3DLUT_MODE[mpcc_id], MPCC_MCM_3DLUT_MODE, 0); + break; + case MCM_LUT_ENABLE: + REG_UPDATE(MPCC_MCM_3DLUT_MODE[mpcc_id], MPCC_MCM_3DLUT_MODE, lut_bank_a ? 1 : 2); + break; } break; case MCM_LUT_SHAPER: - if (enable) { - REG_UPDATE(MPCC_MCM_SHAPER_CONTROL[mpcc_id], MPCC_MCM_SHAPER_LUT_MODE, lut_bank_a ? 1 : 2); - } else { - if (mpc->ctx->dc->debug.enable_mem_low_power.bits.mpc) - mpc32_power_on_shaper_3dlut(mpc, mpcc_id, false); + switch (xable) { + case MCM_LUT_DISABLE: REG_UPDATE(MPCC_MCM_SHAPER_CONTROL[mpcc_id], MPCC_MCM_SHAPER_LUT_MODE, 0); + break; + case MCM_LUT_ENABLE: + REG_UPDATE(MPCC_MCM_SHAPER_CONTROL[mpcc_id], MPCC_MCM_SHAPER_LUT_MODE, lut_bank_a ? 1 : 2); + break; } break; case MCM_LUT_1DLUT: - if (enable) { - REG_UPDATE(MPCC_MCM_1DLUT_CONTROL[mpcc_id], - MPCC_MCM_1DLUT_MODE, 2); - } else { - if (mpc->ctx->dc->debug.enable_mem_low_power.bits.mpc) - mpc32_power_on_blnd_lut(mpc, mpcc_id, false); + switch (xable) { + case MCM_LUT_DISABLE: REG_UPDATE(MPCC_MCM_1DLUT_CONTROL[mpcc_id], MPCC_MCM_1DLUT_MODE, 0); + break; + case MCM_LUT_ENABLE: + REG_UPDATE(MPCC_MCM_1DLUT_CONTROL[mpcc_id], + MPCC_MCM_1DLUT_MODE, 2); + break; } REG_UPDATE(MPCC_MCM_1DLUT_CONTROL[mpcc_id], MPCC_MCM_1DLUT_SELECT, lut_bank_a ? 0 : 1); @@ -257,20 +279,14 @@ void mpc401_program_lut_mode( } } -void mpc401_program_lut_read_write_control(struct mpc *mpc, - const enum MCM_LUT_ID id, - const bool lut_bank_a, - const unsigned int bit_depth, - const int mpcc_id) +void mpc401_program_lut_read_write_control(struct mpc *mpc, const enum MCM_LUT_ID id, bool lut_bank_a, int mpcc_id) { struct dcn401_mpc *mpc401 = TO_DCN401_MPC(mpc); switch (id) { case MCM_LUT_3DLUT: mpc32_select_3dlut_ram_mask(mpc, 0xf, mpcc_id); - REG_UPDATE_2(MPCC_MCM_3DLUT_READ_WRITE_CONTROL[mpcc_id], - MPCC_MCM_3DLUT_30BIT_EN, (bit_depth == 10) ? 1 : 0, - MPCC_MCM_3DLUT_RAM_SEL, lut_bank_a ? 0 : 1); + REG_UPDATE(MPCC_MCM_3DLUT_READ_WRITE_CONTROL[mpcc_id], MPCC_MCM_3DLUT_RAM_SEL, lut_bank_a ? 0 : 1); break; case MCM_LUT_SHAPER: mpc32_configure_shaper_lut(mpc, lut_bank_a, mpcc_id); @@ -562,44 +578,6 @@ void mpc401_get_gamut_remap(struct mpc *mpc, arr_reg_val, ARRAY_SIZE(arr_reg_val)); } -void mpc401_get_lut_mode(struct mpc *mpc, - const enum MCM_LUT_ID id, - const int mpcc_id, - bool *enable, - bool *lut_bank_a) -{ - struct dcn401_mpc *mpc401 = TO_DCN401_MPC(mpc); - - uint32_t lut_mode = 0; - uint32_t lut_select = 0; - - *enable = false; - *lut_bank_a = true; - - switch (id) { - case MCM_LUT_SHAPER: - REG_GET(MPCC_MCM_SHAPER_CONTROL[mpcc_id], - MPCC_MCM_SHAPER_MODE_CURRENT, &lut_mode); - *enable = lut_mode != 0; - *lut_bank_a = lut_mode != 2; - break; - case MCM_LUT_1DLUT: - REG_GET_2(MPCC_MCM_1DLUT_CONTROL[mpcc_id], - MPCC_MCM_1DLUT_MODE_CURRENT, &lut_mode, - MPCC_MCM_1DLUT_SELECT_CURRENT, &lut_select); - *enable = lut_mode != 0; - *lut_bank_a = lut_mode == 0 || lut_select == 0; - break; - case MCM_LUT_3DLUT: - default: - REG_GET(MPCC_MCM_3DLUT_MODE[mpcc_id], - MPCC_MCM_3DLUT_MODE_CURRENT, &lut_mode); - *enable = lut_mode != 0; - *lut_bank_a = lut_mode != 2; - break; - } -} - static const struct mpc_funcs dcn401_mpc_funcs = { .read_mpcc_state = mpc1_read_mpcc_state, .insert_plane = mpc1_insert_plane, @@ -638,7 +616,6 @@ static const struct mpc_funcs dcn401_mpc_funcs = { .populate_lut = mpc401_populate_lut, .program_lut_read_write_control = mpc401_program_lut_read_write_control, .program_lut_mode = mpc401_program_lut_mode, - .get_lut_mode = mpc401_get_lut_mode, }; diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.h b/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.h index c16560c84453e..6d842d7b95c7e 100644 --- a/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.h +++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.h @@ -206,32 +206,21 @@ void dcn401_mpc_construct(struct dcn401_mpc *mpc401, int num_rmu); void mpc401_set_movable_cm_location(struct mpc *mpc, enum mpcc_movable_cm_location location, int mpcc_id); -void mpc401_populate_lut(struct mpc *mpc, - const enum MCM_LUT_ID id, - const union mcm_lut_params *params, - bool lut_bank_a, - int mpcc_id); +void mpc401_populate_lut(struct mpc *mpc, const enum MCM_LUT_ID id, const union mcm_lut_params params, + bool lut_bank_a, int mpcc_id); void mpc401_program_lut_mode( struct mpc *mpc, const enum MCM_LUT_ID id, - const bool enable, - const bool lut_bank_a, - const enum dc_cm_lut_size size, - const int mpcc_id); - -void mpc401_get_lut_mode(struct mpc *mpc, - const enum MCM_LUT_ID id, - const int mpcc_id, - bool *enable, - bool *lut_bank_a); + const enum MCM_LUT_XABLE xable, + bool lut_bank_a, + int mpcc_id); void mpc401_program_lut_read_write_control( struct mpc *mpc, const enum MCM_LUT_ID id, - const bool lut_bank_a, - const unsigned int bit_depth, - const int mpcc_id); + bool lut_bank_a, + int mpcc_id); void mpc401_set_gamut_remap( struct mpc *mpc, diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn42/dcn42_mpc.c b/drivers/gpu/drm/amd/display/dc/mpc/dcn42/dcn42_mpc.c index 507dbdbea6008..38c0e8f96d40e 100644 --- a/drivers/gpu/drm/amd/display/dc/mpc/dcn42/dcn42_mpc.c +++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn42/dcn42_mpc.c @@ -63,6 +63,154 @@ void mpc42_update_blending( mpcc->blnd_cfg = *blnd_cfg; } +/* Shaper functions */ +void mpc42_power_on_shaper_3dlut( + struct mpc *mpc, + uint32_t mpcc_id, + bool power_on) +{ + uint32_t power_status_shaper = 2; + uint32_t power_status_3dlut = 2; + struct dcn42_mpc *mpc42 = TO_DCN42_MPC(mpc); + int max_retries = 10; + + REG_SET(MPCC_MCM_MEM_PWR_CTRL[mpcc_id], 0, + MPCC_MCM_3DLUT_MEM_PWR_DIS, power_on == true ? 1:0); + REG_SET(MPCC_MCM_MEM_PWR_CTRL[mpcc_id], 0, + MPCC_MCM_SHAPER_MEM_PWR_DIS, power_on == true ? 1:0); + /* wait for memory to fully power up */ + if (power_on && mpc->ctx->dc->debug.enable_mem_low_power.bits.mpc) { + REG_WAIT(MPCC_MCM_MEM_PWR_CTRL[mpcc_id], MPCC_MCM_SHAPER_MEM_PWR_STATE, 0, 1, max_retries); + REG_WAIT(MPCC_MCM_MEM_PWR_CTRL[mpcc_id], MPCC_MCM_3DLUT_MEM_PWR_STATE, 0, 1, max_retries); + } + + /*read status is not mandatory, it is just for debugging*/ + REG_GET(MPCC_MCM_MEM_PWR_CTRL[mpcc_id], MPCC_MCM_SHAPER_MEM_PWR_STATE, &power_status_shaper); + REG_GET(MPCC_MCM_MEM_PWR_CTRL[mpcc_id], MPCC_MCM_3DLUT_MEM_PWR_STATE, &power_status_3dlut); + + if (power_status_shaper != 0 && power_on == true) + BREAK_TO_DEBUGGER(); + + if (power_status_3dlut != 0 && power_on == true) + BREAK_TO_DEBUGGER(); +} + +void mpc42_configure_shaper_lut( + struct mpc *mpc, + bool is_ram_a, + uint32_t mpcc_id) +{ + struct dcn42_mpc *mpc42 = TO_DCN42_MPC(mpc); + + REG_UPDATE(MPCC_MCM_SHAPER_SCALE_G_B[mpcc_id], + MPCC_MCM_SHAPER_SCALE_B, 0x7000); + REG_UPDATE(MPCC_MCM_SHAPER_SCALE_G_B[mpcc_id], + MPCC_MCM_SHAPER_SCALE_G, 0x7000); + REG_UPDATE(MPCC_MCM_SHAPER_SCALE_R[mpcc_id], + MPCC_MCM_SHAPER_SCALE_R, 0x7000); + REG_UPDATE(MPCC_MCM_SHAPER_LUT_WRITE_EN_MASK[mpcc_id], + MPCC_MCM_SHAPER_LUT_WRITE_EN_MASK, 7); + REG_UPDATE(MPCC_MCM_SHAPER_LUT_WRITE_EN_MASK[mpcc_id], + MPCC_MCM_SHAPER_LUT_WRITE_SEL, is_ram_a == true ? 0:1); + REG_SET(MPCC_MCM_SHAPER_LUT_INDEX[mpcc_id], 0, MPCC_MCM_SHAPER_LUT_INDEX, 0); +} + + +void mpc42_program_3dlut_size(struct mpc *mpc, uint32_t width, int mpcc_id) +{ + struct dcn42_mpc *mpc42 = TO_DCN42_MPC(mpc); + uint32_t size = 0xff; + + REG_GET(MPCC_MCM_3DLUT_MODE[mpcc_id], MPCC_MCM_3DLUT_SIZE, &size); + + REG_UPDATE(MPCC_MCM_3DLUT_MODE[mpcc_id], MPCC_MCM_3DLUT_SIZE, + (width == 33) ? 2 : + (width == 17) ? 0 : 2); + + REG_GET(MPCC_MCM_3DLUT_MODE[mpcc_id], MPCC_MCM_3DLUT_SIZE, &size); +} + +void mpc42_program_3dlut_fl_bias_scale(struct mpc *mpc, uint16_t bias, uint16_t scale, int mpcc_id) +{ + struct dcn42_mpc *mpc42 = TO_DCN42_MPC(mpc); + + REG_UPDATE_2(MPCC_MCM_3DLUT_OUT_OFFSET_R[mpcc_id], + MPCC_MCM_3DLUT_OUT_OFFSET_R, bias, + MPCC_MCM_3DLUT_OUT_SCALE_R, scale); + + REG_UPDATE_2(MPCC_MCM_3DLUT_OUT_OFFSET_G[mpcc_id], + MPCC_MCM_3DLUT_OUT_OFFSET_G, bias, + MPCC_MCM_3DLUT_OUT_SCALE_G, scale); + + REG_UPDATE_2(MPCC_MCM_3DLUT_OUT_OFFSET_B[mpcc_id], + MPCC_MCM_3DLUT_OUT_OFFSET_B, bias, + MPCC_MCM_3DLUT_OUT_SCALE_B, scale); +} + +void mpc42_program_bit_depth(struct mpc *mpc, uint16_t bit_depth, int mpcc_id) +{ + struct dcn42_mpc *mpc42 = TO_DCN42_MPC(mpc); + + REG_UPDATE(MPCC_MCM_3DLUT_READ_WRITE_CONTROL[mpcc_id], MPCC_MCM_3DLUT_WRITE_EN_MASK, 0xF); + + //program bit_depth + REG_UPDATE(MPCC_MCM_3DLUT_READ_WRITE_CONTROL[mpcc_id], + MPCC_MCM_3DLUT_30BIT_EN, + (bit_depth == 10) ? 1 : 0); +} + +bool mpc42_is_config_supported(uint32_t width) +{ + if (width == 17) + return true; + + return false; +} + +void mpc42_populate_lut(struct mpc *mpc, const union mcm_lut_params params, + bool lut_bank_a, int mpcc_id) +{ + const enum dc_lut_mode next_mode = lut_bank_a ? LUT_RAM_A : LUT_RAM_B; + const struct pwl_params *lut_shaper = params.pwl; + + if (lut_shaper == NULL) + return; + if (mpc->ctx->dc->debug.enable_mem_low_power.bits.mpc) + mpc42_power_on_shaper_3dlut(mpc, mpcc_id, true); + + mpc42_configure_shaper_lut(mpc, next_mode == LUT_RAM_A, mpcc_id); + + if (next_mode == LUT_RAM_A) + mpc32_program_shaper_luta_settings(mpc, lut_shaper, mpcc_id); + else + mpc32_program_shaper_lutb_settings(mpc, lut_shaper, mpcc_id); + + mpc32_program_shaper_lut( + mpc, lut_shaper->rgb_resulted, lut_shaper->hw_points_num, mpcc_id); + + mpc42_power_on_shaper_3dlut(mpc, mpcc_id, false); +} + +void mpc42_program_lut_read_write_control(struct mpc *mpc, const enum MCM_LUT_ID id, + bool lut_bank_a, bool enabled, int mpcc_id) +{ + struct dcn42_mpc *mpc42 = TO_DCN42_MPC(mpc); + + switch (id) { + case MCM_LUT_3DLUT: + REG_UPDATE(MPCC_MCM_3DLUT_MODE[mpcc_id], MPCC_MCM_3DLUT_MODE, + (!enabled) ? 0 : + (lut_bank_a) ? 1 : 2); + REG_UPDATE(MPCC_MCM_3DLUT_READ_WRITE_CONTROL[mpcc_id], MPCC_MCM_3DLUT_RAM_SEL, lut_bank_a ? 0 : 1); + break; + case MCM_LUT_SHAPER: + mpc32_configure_shaper_lut(mpc, lut_bank_a, mpcc_id); + break; + default: + break; + } +} + /* RMCM Shaper functions */ void mpc42_power_on_rmcm_shaper_3dlut( struct mpc *mpc, @@ -526,47 +674,32 @@ void mpc42_program_rmcm_lut_read_write_control(struct mpc *mpc, const enum MCM_L } } -void mpc42_program_lut_mode(struct mpc *mpc, - bool enable, - bool lut_bank_a, - int mpcc_id) +void mpc42_program_lut_mode(struct mpc *mpc, const enum MCM_LUT_XABLE xable, + bool lut_bank_a, int mpcc_id) { struct dcn42_mpc *mpc42 = TO_DCN42_MPC(mpc); - if (enable) { - REG_UPDATE(MPC_RMCM_SHAPER_CONTROL[mpcc_id], MPC_RMCM_SHAPER_LUT_MODE, lut_bank_a ? 1 : 2); - } else { + switch (xable) { + case MCM_LUT_DISABLE: REG_UPDATE(MPC_RMCM_SHAPER_CONTROL[mpcc_id], MPC_RMCM_SHAPER_LUT_MODE, 0); - } -} - -static uint32_t mpc42_get_rmcm_3dlut_width( - const enum dc_cm_lut_size size) -{ - uint32_t width = 0; - - switch (size) { - case CM_LUT_SIZE_333333: - width = 2; break; - case CM_LUT_SIZE_171717: - default: - width = 0; + case MCM_LUT_ENABLE: + REG_UPDATE(MPC_RMCM_SHAPER_CONTROL[mpcc_id], MPC_RMCM_SHAPER_LUT_MODE, lut_bank_a ? 1 : 2); break; } - - return width; } -void mpc42_program_rmcm_3dlut_size(struct mpc *mpc, - const enum dc_cm_lut_size size, - int mpcc_id) +void mpc42_program_rmcm_3dlut_size(struct mpc *mpc, uint32_t width, int mpcc_id) { struct dcn42_mpc *mpc42 = TO_DCN42_MPC(mpc); - uint32_t width = mpc42_get_rmcm_3dlut_width(size); + uint32_t size = 0xff; + + REG_GET(MPC_RMCM_3DLUT_MODE[mpcc_id], MPC_RMCM_3DLUT_SIZE, &size); - REG_UPDATE(MPC_RMCM_3DLUT_MODE[mpcc_id], - MPC_RMCM_3DLUT_SIZE, width); + REG_UPDATE(MPC_RMCM_3DLUT_MODE[mpcc_id], MPC_RMCM_3DLUT_SIZE, + (width == 33) ? 2 : 0); + + REG_GET(MPC_RMCM_3DLUT_MODE[mpcc_id], MPC_RMCM_3DLUT_SIZE, &size); } void mpc42_program_rmcm_3dlut_fast_load_bias_scale(struct mpc *mpc, uint16_t bias, uint16_t scale, int mpcc_id) @@ -598,6 +731,14 @@ void mpc42_program_rmcm_bit_depth(struct mpc *mpc, uint16_t bit_depth, int mpcc_ (bit_depth == 10) ? 1 : 0); } +bool mpc42_is_rmcm_config_supported(uint32_t width) +{ + if (width == 17 || width == 33) + return true; + + return false; +} + void mpc42_set_fl_config( struct mpc *mpc, struct mpc_fl_3dlut_config *cfg, @@ -605,7 +746,6 @@ void mpc42_set_fl_config( { struct dcn42_mpc *mpc42 = TO_DCN42_MPC(mpc); - uint32_t width = mpc42_get_rmcm_3dlut_width(cfg->size); /* From: Jie Zhou @@ -646,7 +786,7 @@ void mpc42_set_fl_config( //width REG_UPDATE_2(MPC_RMCM_3DLUT_MODE[mpcc_id], - MPC_RMCM_3DLUT_SIZE, width, + MPC_RMCM_3DLUT_SIZE, (cfg->width == 33) ? 2 : 0, MPC_RMCM_3DLUT_MODE, (!cfg->enabled) ? 0 : (cfg->select_lut_bank_a) ? 1 : 2); //connect to hubp @@ -659,6 +799,182 @@ void mpc42_set_fl_config( REG_UPDATE(MPC_RMCM_CNTL[mpcc_id], MPC_RMCM_CNTL, cfg->enabled ? 0 : 0xF); } +//static void rmcm_program_gamut_remap( +// struct mpc *mpc, +// unsigned int mpcc_id, +// const uint16_t *regval, +// enum mpcc_gamut_remap_id gamut_remap_block_id, +// enum mpcc_gamut_remap_mode_select mode_select) +//{ +// struct color_matrices_reg gamut_regs; +// struct dcn42_mpc *mpc42 = TO_DCN42_MPC(mpc); +// +// if (gamut_remap_block_id == MPCC_OGAM_GAMUT_REMAP || +// gamut_remap_block_id == MPCC_MCM_FIRST_GAMUT_REMAP || +// gamut_remap_block_id == MPCC_MCM_SECOND_GAMUT_REMAP) { +// mpc_program_gamut_remap(mpc, mpcc_id, regval, gamut_remap_block_id, mode_select); +// return; +// } +// if (gamut_remap_block_id == MPCC_OGAM_GAMUT_REMAP) { +// +// if (regval == NULL || mode_select == MPCC_GAMUT_REMAP_MODE_SELECT_0) { +// REG_SET(MPC_RMCM_GAMUT_REMAP_MODE[mpcc_id], 0, +// MPC_RMCM_GAMUT_REMAP_MODE, mode_select); +// return; +// } +// +// gamut_regs.shifts.csc_c11 = mpc42->mpc_shift->MPCC_GAMUT_REMAP_C11_A; +// gamut_regs.masks.csc_c11 = mpc42->mpc_mask->MPCC_GAMUT_REMAP_C11_A; +// gamut_regs.shifts.csc_c12 = mpc42->mpc_shift->MPCC_GAMUT_REMAP_C12_A; +// gamut_regs.masks.csc_c12 = mpc42->mpc_mask->MPCC_GAMUT_REMAP_C12_A; +// +// switch (mode_select) { +// case MPCC_GAMUT_REMAP_MODE_SELECT_1: +// gamut_regs.csc_c11_c12 = REG(MPC_RMCM_GAMUT_REMAP_C11_C12_A[mpcc_id]); +// gamut_regs.csc_c33_c34 = REG(MPC_RMCM_GAMUT_REMAP_C33_C34_A[mpcc_id]); +// break; +// case MPCC_GAMUT_REMAP_MODE_SELECT_2: +// gamut_regs.csc_c11_c12 = REG(MPC_RMCM_GAMUT_REMAP_C11_C12_B[mpcc_id]); +// gamut_regs.csc_c33_c34 = REG(MPC_RMCM_GAMUT_REMAP_C33_C34_B[mpcc_id]); +// break; +// default: +// break; +// } +// +// cm_helper_program_color_matrices( +// mpc->ctx, +// regval, +// &gamut_regs); +// +// //select coefficient set to use, set A (MODE_1) or set B (MODE_2) +// REG_SET(MPC_RMCM_GAMUT_REMAP_MODE[mpcc_id], 0, MPC_RMCM_GAMUT_REMAP_MODE, mode_select); +// } +//} + +//static bool is_mpc_legacy_gamut_id(enum mpcc_gamut_remap_id gamut_remap_block_id) +//{ +// if (gamut_remap_block_id == MPCC_OGAM_GAMUT_REMAP || +// gamut_remap_block_id == MPCC_MCM_FIRST_GAMUT_REMAP || +// gamut_remap_block_id == MPCC_MCM_SECOND_GAMUT_REMAP) { +// return true; +// } +// return false; +//} +//static void program_gamut_remap( +// struct mpc *mpc, +// unsigned int mpcc_id, +// const uint16_t *regval, +// enum mpcc_gamut_remap_id gamut_remap_block_id, +// enum mpcc_gamut_remap_mode_select mode_select) +//{ +// if (is_mpc_legacy_gamut_id(gamut_remap_block_id)) +// mpc_program_gamut_remap(mpc, mpcc_id, regval, gamut_remap_block_id, mode_select); +// else +// rmcm_program_gamut_remap(mpc, mpcc_id, regval, gamut_remap_block_id, mode_select); +//} + +//void mpc42_set_gamut_remap( +// struct mpc *mpc, +// int mpcc_id, +// const struct mpc_grph_gamut_adjustment *adjust) +//{ +// struct dcn42_mpc *mpc42 = TO_DCN42_MPC(mpc); +// unsigned int i = 0; +// uint32_t mode_select = 0; +// +// if (adjust->gamut_adjust_type != GRAPHICS_GAMUT_ADJUST_TYPE_SW) { +// /* Bypass / Disable if type is bypass or hw */ +// program_gamut_remap(mpc, mpcc_id, NULL, +// adjust->mpcc_gamut_remap_block_id, MPCC_GAMUT_REMAP_MODE_SELECT_0); +// } else { +// struct fixed31_32 arr_matrix[12]; +// uint16_t arr_reg_val[12]; +// +// for (i = 0; i < 12; i++) +// arr_matrix[i] = adjust->temperature_matrix[i]; +// +// convert_float_matrix(arr_reg_val, arr_matrix, 12); +// +// if (is_mpc_legacy_gamut_id(adjust->mpcc_gamut_remap_block_id)) +// REG_GET(MPCC_GAMUT_REMAP_MODE[mpcc_id], +// MPCC_GAMUT_REMAP_MODE_CURRENT, &mode_select); +// else +// REG_GET(MPC_RMCM_GAMUT_REMAP_MODE[mpcc_id], +// MPC_RMCM_GAMUT_REMAP_MODE_CURRENT, &mode_select); +// +// //If current set in use not set A (MODE_1), then use set A, otherwise use set B +// if (mode_select != MPCC_GAMUT_REMAP_MODE_SELECT_1) +// mode_select = MPCC_GAMUT_REMAP_MODE_SELECT_1; +// else +// mode_select = MPCC_GAMUT_REMAP_MODE_SELECT_2; +// +// program_gamut_remap(mpc, mpcc_id, arr_reg_val, +// adjust->mpcc_gamut_remap_block_id, mode_select); +// } +//} + +//static void read_gamut_remap(struct mpc *mpc, +// int mpcc_id, +// uint16_t *regval, +// enum mpcc_gamut_remap_id gamut_remap_block_id, +// uint32_t *mode_select) +//{ +// struct color_matrices_reg gamut_regs = {0}; +// struct dcn42_mpc *mpc42 = TO_DCN42_MPC(mpc); +// +// if (is_mpc_legacy_gamut_id(gamut_remap_block_id)) { +// mpc_read_gamut_remap(mpc, mpcc_id, regval, gamut_remap_block_id, mode_select); +// } +// if (gamut_remap_block_id == MPCC_RMCM_GAMUT_REMAP) { +// //current coefficient set in use +// REG_GET(MPC_RMCM_GAMUT_REMAP_MODE[mpcc_id], MPC_RMCM_GAMUT_REMAP_MODE, mode_select); +// +// gamut_regs.shifts.csc_c11 = mpc42->mpc_shift->MPCC_GAMUT_REMAP_C11_A; +// gamut_regs.masks.csc_c11 = mpc42->mpc_mask->MPCC_GAMUT_REMAP_C11_A; +// gamut_regs.shifts.csc_c12 = mpc42->mpc_shift->MPCC_GAMUT_REMAP_C12_A; +// gamut_regs.masks.csc_c12 = mpc42->mpc_mask->MPCC_GAMUT_REMAP_C12_A; +// +// switch (*mode_select) { +// case MPCC_GAMUT_REMAP_MODE_SELECT_1: +// gamut_regs.csc_c11_c12 = REG(MPC_RMCM_GAMUT_REMAP_C11_C12_A[mpcc_id]); +// gamut_regs.csc_c33_c34 = REG(MPC_RMCM_GAMUT_REMAP_C33_C34_A[mpcc_id]); +// break; +// case MPCC_GAMUT_REMAP_MODE_SELECT_2: +// gamut_regs.csc_c11_c12 = REG(MPC_RMCM_GAMUT_REMAP_C11_C12_B[mpcc_id]); +// gamut_regs.csc_c33_c34 = REG(MPC_RMCM_GAMUT_REMAP_C33_C34_B[mpcc_id]); +// break; +// default: +// break; +// } +// } +// +// if (*mode_select != MPCC_GAMUT_REMAP_MODE_SELECT_0) { +// cm_helper_read_color_matrices( +// mpc42->base.ctx, +// regval, +// &gamut_regs); +// } +//} + +//void mpc42_get_gamut_remap(struct mpc *mpc, +// int mpcc_id, +// struct mpc_grph_gamut_adjustment *adjust) +//{ +// uint16_t arr_reg_val[12] = {0}; +// uint32_t mode_select; +// +// read_gamut_remap(mpc, mpcc_id, arr_reg_val, adjust->mpcc_gamut_remap_block_id, &mode_select); +// +// if (mode_select == MPCC_GAMUT_REMAP_MODE_SELECT_0) { +// adjust->gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS; +// return; +// } +// +// adjust->gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW; +// convert_hw_matrix(adjust->temperature_matrix, +// arr_reg_val, ARRAY_SIZE(arr_reg_val)); +//} + void mpc42_read_mpcc_state( struct mpc *mpc, int mpcc_inst, @@ -755,7 +1071,14 @@ static const struct mpc_funcs dcn42_mpc_funcs = { .populate_lut = mpc401_populate_lut, .program_lut_read_write_control = mpc401_program_lut_read_write_control, .program_lut_mode = mpc401_program_lut_mode, - .get_lut_mode = mpc401_get_lut_mode, + .mcm = { + .program_lut_read_write_control = mpc42_program_lut_read_write_control, + .program_3dlut_size = mpc42_program_3dlut_size, + .program_bias_scale = mpc42_program_3dlut_fl_bias_scale, + .program_bit_depth = mpc42_program_bit_depth, + .is_config_supported = mpc42_is_config_supported, + .populate_lut = mpc42_populate_lut, + }, .rmcm = { .enable_3dlut_fl = mpc42_enable_3dlut_fl, .update_3dlut_fast_load_select = mpc42_update_3dlut_fast_load_select, @@ -764,6 +1087,7 @@ static const struct mpc_funcs dcn42_mpc_funcs = { .program_3dlut_size = mpc42_program_rmcm_3dlut_size, .program_bias_scale = mpc42_program_rmcm_3dlut_fast_load_bias_scale, .program_bit_depth = mpc42_program_rmcm_bit_depth, + .is_config_supported = mpc42_is_rmcm_config_supported, .power_on_shaper_3dlut = mpc42_power_on_rmcm_shaper_3dlut, .populate_lut = mpc42_populate_rmcm_lut, .fl_3dlut_configure = mpc42_set_fl_config, diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn42/dcn42_mpc.h b/drivers/gpu/drm/amd/display/dc/mpc/dcn42/dcn42_mpc.h index a5f7f4f2bb3bc..12a12c28e5537 100644 --- a/drivers/gpu/drm/amd/display/dc/mpc/dcn42/dcn42_mpc.h +++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn42/dcn42_mpc.h @@ -884,6 +884,49 @@ void dcn42_mpc_construct(struct dcn42_mpc *mpc401, void mpc42_init_mpcc(struct mpcc *mpcc, int mpcc_inst); +void mpc42_program_shaper_lutb_settings( + struct mpc *mpc, + const struct pwl_params *params, + uint32_t mpcc_id); +void mpc42_program_shaper_luta_settings( + struct mpc *mpc, + const struct pwl_params *params, + uint32_t mpcc_id); +void mpc42_configure_shaper_lut( + struct mpc *mpc, + bool is_ram_a, + uint32_t mpcc_id); +void mpc42_power_on_shaper_3dlut( + struct mpc *mpc, + uint32_t mpcc_id, + bool power_on); +void mpc42_program_3dlut_size( + struct mpc *mpc, + uint32_t width, + int mpcc_id); +void mpc42_program_3dlut_fl_bias_scale( + struct mpc *mpc, + uint16_t bias, + uint16_t scale, + int mpcc_id); +void mpc42_program_bit_depth( + struct mpc *mpc, + uint16_t bit_depth, + int mpcc_id); +void mpc42_populate_lut( + struct mpc *mpc, + const union mcm_lut_params params, + bool lut_bank_a, + int mpcc_id); +void mpc42_program_lut_read_write_control( + struct mpc *mpc, + const enum MCM_LUT_ID id, + bool lut_bank_a, + bool enabled, + int mpcc_id); + +bool mpc42_is_config_supported(uint32_t width); + /* RMCM */ void mpc42_program_rmcm_shaper_lut( struct mpc *mpc, @@ -927,12 +970,12 @@ void mpc42_program_rmcm_lut_read_write_control( int mpcc_id); void mpc42_program_lut_mode( struct mpc *mpc, - bool enable, + const enum MCM_LUT_XABLE xable, bool lut_bank_a, int mpcc_id); void mpc42_program_rmcm_3dlut_size( struct mpc *mpc, - const enum dc_cm_lut_size size, + uint32_t width, int mpcc_id); void mpc42_program_rmcm_3dlut_fast_load_bias_scale( struct mpc *mpc, @@ -944,6 +987,8 @@ void mpc42_program_rmcm_bit_depth( uint16_t bit_depth, int mpcc_id); +bool mpc42_is_rmcm_config_supported(uint32_t width); + void mpc42_set_fl_config( struct mpc *mpc, struct mpc_fl_3dlut_config *cfg, diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c index c0d37f00fed91..2c1f72cf1a63f 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c @@ -666,7 +666,6 @@ static const struct resource_caps res_cap_dcn42 = { .num_vmid = 16, .num_mpc_3dlut = 2, .num_dsc = 4, - .num_rmcm = 2, }; static const struct dc_plane_cap plane_cap = { From 60325bf5e2c155e807270ed788c7610fd39bdbe9 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 8 Apr 2026 13:53:56 +0200 Subject: [PATCH 2750/2758] posix-timers: Expand timer_[re]arm() callbacks with a boolean return value commit 6fdb2677a594ab38eade927919bbd4d9688bfa1c upstream. In order to catch expiry times which are already in the past the timer_arm() and timer_rearm() callbacks need to be able to report back to the caller whether the timer has been queued or not. Change the function signature and let all implementations return true for now. While at it simplify posix_cpu_timer_rearm(). No functional change intended. Signed-off-by: Thomas Gleixner Reviewed-by: Frederic Weisbecker Acked-by: Peter Zijlstra (Intel) Acked-by: John Stultz Link: https://patch.msgid.link/20260408114952.130222296@kernel.org Signed-off-by: Salvatore Bonaccorso Signed-off-by: Greg Kroah-Hartman --- kernel/time/alarmtimer.c | 6 ++++-- kernel/time/posix-cpu-timers.c | 18 ++++++++++-------- kernel/time/posix-timers.c | 6 ++++-- kernel/time/posix-timers.h | 4 ++-- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c index fe9c42c03523c..c9aac73ba97a3 100644 --- a/kernel/time/alarmtimer.c +++ b/kernel/time/alarmtimer.c @@ -525,12 +525,13 @@ static void alarm_handle_timer(struct alarm *alarm, ktime_t now) * alarm_timer_rearm - Posix timer callback for rearming timer * @timr: Pointer to the posixtimer data struct */ -static void alarm_timer_rearm(struct k_itimer *timr) +static bool alarm_timer_rearm(struct k_itimer *timr) { struct alarm *alarm = &timr->it.alarm.alarmtimer; timr->it_overrun += alarm_forward_now(alarm, timr->it_interval); alarm_start(alarm, alarm->node.expires); + return true; } /** @@ -586,7 +587,7 @@ static void alarm_timer_wait_running(struct k_itimer *timr) * @absolute: Expiry value is absolute time * @sigev_none: Posix timer does not deliver signals */ -static void alarm_timer_arm(struct k_itimer *timr, ktime_t expires, +static bool alarm_timer_arm(struct k_itimer *timr, ktime_t expires, bool absolute, bool sigev_none) { struct alarm *alarm = &timr->it.alarm.alarmtimer; @@ -598,6 +599,7 @@ static void alarm_timer_arm(struct k_itimer *timr, ktime_t expires, alarm->node.expires = expires; else alarm_start(&timr->it.alarm.alarmtimer, expires); + return true; } /** diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c index a86e98912705b..5e633d8750d1f 100644 --- a/kernel/time/posix-cpu-timers.c +++ b/kernel/time/posix-cpu-timers.c @@ -19,7 +19,7 @@ #include "posix-timers.h" -static void posix_cpu_timer_rearm(struct k_itimer *timer); +static bool posix_cpu_timer_rearm(struct k_itimer *timer); void posix_cputimers_group_init(struct posix_cputimers *pct, u64 cpu_limit) { @@ -1011,24 +1011,27 @@ static void check_process_timers(struct task_struct *tsk, /* * This is called from the signal code (via posixtimer_rearm) * when the last timer signal was delivered and we have to reload the timer. + * + * Return true unconditionally so the core code assumes the timer to be + * armed. Otherwise it would requeue the signal. */ -static void posix_cpu_timer_rearm(struct k_itimer *timer) +static bool posix_cpu_timer_rearm(struct k_itimer *timer) { clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock); - struct task_struct *p; struct sighand_struct *sighand; + struct task_struct *p; unsigned long flags; u64 now; - rcu_read_lock(); + guard(rcu)(); p = cpu_timer_task_rcu(timer); if (!p) - goto out; + return true; /* Protect timer list r/w in arm_timer() */ sighand = lock_task_sighand(p, &flags); if (unlikely(sighand == NULL)) - goto out; + return true; /* * Fetch the current sample and update the timer's expiry time. @@ -1045,8 +1048,7 @@ static void posix_cpu_timer_rearm(struct k_itimer *timer) */ arm_timer(timer, p); unlock_task_sighand(p, &flags); -out: - rcu_read_unlock(); + return true; } /** diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c index 9331e16141246..da04ed42bf824 100644 --- a/kernel/time/posix-timers.c +++ b/kernel/time/posix-timers.c @@ -288,12 +288,13 @@ static inline int timer_overrun_to_int(struct k_itimer *timr) return (int)timr->it_overrun_last; } -static void common_hrtimer_rearm(struct k_itimer *timr) +static bool common_hrtimer_rearm(struct k_itimer *timr) { struct hrtimer *timer = &timr->it.real.timer; timr->it_overrun += hrtimer_forward_now(timer, timr->it_interval); hrtimer_restart(timer); + return true; } static bool __posixtimer_deliver_signal(struct kernel_siginfo *info, struct k_itimer *timr) @@ -795,7 +796,7 @@ SYSCALL_DEFINE1(timer_getoverrun, timer_t, timer_id) return timer_overrun_to_int(scoped_timer); } -static void common_hrtimer_arm(struct k_itimer *timr, ktime_t expires, +static bool common_hrtimer_arm(struct k_itimer *timr, ktime_t expires, bool absolute, bool sigev_none) { struct hrtimer *timer = &timr->it.real.timer; @@ -822,6 +823,7 @@ static void common_hrtimer_arm(struct k_itimer *timr, ktime_t expires, if (!sigev_none) hrtimer_start_expires(timer, HRTIMER_MODE_ABS); + return true; } static int common_hrtimer_try_to_cancel(struct k_itimer *timr) diff --git a/kernel/time/posix-timers.h b/kernel/time/posix-timers.h index 7f259e845d240..4ea9611dd7165 100644 --- a/kernel/time/posix-timers.h +++ b/kernel/time/posix-timers.h @@ -27,11 +27,11 @@ struct k_clock { int (*timer_del)(struct k_itimer *timr); void (*timer_get)(struct k_itimer *timr, struct itimerspec64 *cur_setting); - void (*timer_rearm)(struct k_itimer *timr); + bool (*timer_rearm)(struct k_itimer *timr); s64 (*timer_forward)(struct k_itimer *timr, ktime_t now); ktime_t (*timer_remaining)(struct k_itimer *timr, ktime_t now); int (*timer_try_to_cancel)(struct k_itimer *timr); - void (*timer_arm)(struct k_itimer *timr, ktime_t expires, + bool (*timer_arm)(struct k_itimer *timr, ktime_t expires, bool absolute, bool sigev_none); void (*timer_wait_running)(struct k_itimer *timr); }; From ad1cafa1bdaa71da85d71cac053838bbe97852b6 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 3 Jul 2026 12:02:38 +0200 Subject: [PATCH 2751/2758] posix-cpu-timers: Prevent UAF caused by non-leader exec() race commit 920f893f735e92ba3a1cd9256899a186b161928d upstream. Wongi and Jungwoo decoded and reported a non-leader exec() related race which can result in an UAF: sys_timer_delete() exec() posix_cpu_timer_del() // Observes old leader p = pid_task(pid, pid_type); de_thread() switch_leader(); release_task(old_leader) __exit_signal(old_leader) sighand = lock(old_leader, sighand); posix_cpu_timers*_exit(); sighand = lock_task_sighand(p) unhash_task(old_leader); sh = lock(p, sighand) old_leader->sighand = NULL; unlock(sighand); (p->sighand == NULL) unlock(sh) return NULL; // Returns without action if(!sighand) return 0; free_posix_timer(); This is "harmless" unless the deleted timer was armed and enqueued in p->signal because on exec() a TGID targeted timer is inherited. As sys_timer_delete() freed the underlying posix timer object run_posix_cpu_timers() or any timerqueue related add/delete operations on other timers will access the freed object's timerqueue node, which results in an UAF. There is a similar problem vs. posix_cpu_timer_set(). For regular posix timers it just transiently returns -ESRCH to user space, but for the use case in do_cpu_nanosleep() it's the same UAF just that the k_itimer is allocated on the stack. Also posix_cpu_timer_rearm() fails to rearm the timer, which means it stops to expire. While debating solutions Frederic pointed out another problem: posix_cpu_timer_del(tmr) __exit_signal(p) posix_cpu_timers*_exit(p); unhash_task(p); p->sighand = NULL; sh = lock_task_sighand(p) sighand = p->sighand; if (!sighand) return NULL; lock(sighand); if (!sh) WARN_ON_ONCE(timer_queued(tmr)); On weakly ordered architectures it is not guaranteed that posix_cpu_timer_del() will observe the stores in posix_cpu_timers*_exit() when p->sighand is observed as NULL, which means the WARN() can be a false positive. Solve these issues by: 1) Changing the store in __exit_signal() to smp_store_release(). 2) Adding a smp_acquire__after_ctrl_dep() into the !sighand path of lock_task_sighand(). 3) Creating a helper function for looking up the task and locking sighand which does not return when sighand == NULL. Instead it retries the task lookup and only if that fails it gives up. 4) Using that helper in the three affected functions. observes all preceeding stores, i.e. the stores in posix_cpu_timers*_exit() and the ones in unhash_task(). gracefully. When the task lookup returns the old leader, but sighand == NULL then it retries. In the non-leader exec() case the subsequent task lookup will observe the new leader due to #1/#2. In normal exit() scenarios the subsequent lookup fails. When the task lookup fails, the function also checks whether the timer is still enqueued and issues a warning if that's the case. Unfortunately there is nothing which can be done about it, but as the task is already not longer visible the timer should not be accessed anymore. This check also requires memory ordering, which is not provided when the first lookup fails. To achieve that the check is preceeded by a smp_rmb() which pairs with the smp_wmb() in write_seqlock() in __exit_signal(). That ensures that the stores in posix_cpu_timers*_exit() are visible. The history of the non-leader exec() issue goes back to the early days of posix CPU timers, which stored a pointer to the group leader task in the timer. That obviously fails when a non-leader exec() switches the leader. commit e0a70217107e ("posix-cpu-timers: workaround to suppress the problems with mt exec") added a temporary workaround for that in 2010 which survived about 10 years. The fix for the workaround changed the task pointer to a pid pointer, but failed to see the subtle race described above. So the Fixes tag picks that commit, which seems to be halfways accurate. Thanks to Frederic Weissbecker, Oleg Nesterov and Peter Zijlstra for review, feedback and suggestions and to Wongi and Jungwoo for the excellent bug report and analysis! Fixes: 55e8c8eb2c7b ("posix-cpu-timers: Store a reference to a pid not a task") Reported-by: Wongi Lee Reported-by: Jungwoo Lee Signed-off-by: Thomas Gleixner Reviewed-by: Oleg Nesterov Cc: stable@vger.kernel.org Signed-off-by: Salvatore Bonaccorso Signed-off-by: Greg Kroah-Hartman --- kernel/exit.c | 7 +- kernel/signal.c | 10 +- kernel/time/posix-cpu-timers.c | 173 ++++++++++++++++++++++----------- 3 files changed, 130 insertions(+), 60 deletions(-) diff --git a/kernel/exit.c b/kernel/exit.c index f50d73c272d6e..7ba7966a23b9a 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -212,7 +212,12 @@ static void __exit_signal(struct release_task_post *post, struct task_struct *ts __unhash_process(post, tsk, group_dead); write_sequnlock(&sig->stats_lock); - tsk->sighand = NULL; + /* + * Ensure that all preceeding state is visible. Pairs with + * the smp_acquire__after_ctrl_dep() in the sighand == NULL + * path of lock_task_sighand(). + */ + smp_store_release(&tsk->sighand, NULL); spin_unlock(&sighand->siglock); __cleanup_sighand(sighand); diff --git a/kernel/signal.c b/kernel/signal.c index 9c2b32c4d7553..bbc0fd4cc4d7c 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1362,8 +1362,16 @@ struct sighand_struct *lock_task_sighand(struct task_struct *tsk, rcu_read_lock(); for (;;) { sighand = rcu_dereference(tsk->sighand); - if (unlikely(sighand == NULL)) + if (unlikely(sighand == NULL)) { + /* + * Pairs with the smp_store_release() in + * __exit_signal(). It ensures that all state + * modifications to the task preceeding the store are + * visible to the callers of lock_task_sighand(). + */ + smp_acquire__after_ctrl_dep(); break; + } /* * This sighand can be already freed and even reused, but diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c index 5e633d8750d1f..a7d3e8229c4ba 100644 --- a/kernel/time/posix-cpu-timers.c +++ b/kernel/time/posix-cpu-timers.c @@ -461,6 +461,109 @@ static void disarm_timer(struct k_itimer *timer, struct task_struct *p) trigger_base_recalc_expires(timer, p); } +/* + * Lookup the task via timer->it.cpu.pid and attempt to lock the task's sighand. + * + * This can race with the reaping of the task: + * + * CPU0 CPU1 + * + * // Finds task + * p = pid_task(pid, pid_type); __exit_signal(p) + * lock(p, sighand); + * posix_cpu_timers*_exit(); + * sighand = lock_task_sighand(p); unhash_task(p); + * p->sighand = NULL; + * unlock(sighand); + * + * In this case sighand is NULL, which means the task and the associated timer + * queue cannot be longer accessed safely. + * + * __exit_signal() invokes posix_cpu_timers_exit() and if the thread group is + * dead it also invokes posix_cpu_timers_group_exit(). These functions delete + * all pending timers from the related timer queues. The POSIX timers (k_itimer) + * themself are still accessible, but not longer connected to the task. + * + * exec() works slightly differently. The task which exec()'s terminates all + * other threads in the thread group and runs __exit_signal() on them. As the + * thread group is not dead they only clean up the per task timers via + * posix_cpu_timers_exit(). + * + * As the TGID on exec() stays the same per process timers stay queued, if they + * are armed. This works without a problem when exec() is done by the thread + * group leader. If a non-leader thread exec()'s this can end up in the + * following scenario: + * + * CPU0 CPU1 + * // Returns old leader + * p = pid_task(pid, pid_type); de_thread() + * switch_leader() + * release_task(old leader) + * __exit_signal() + * old_leader->sighand = NULL; + * // Returns NULL + * sighand = lock_task_sighand(p) + * + * That's problematic for several functions: + * + * - posix_cpu_timer_del(): If the timer is still enqueued on the task the + * underlying k_itimer will be freed which results in a UAF in + * run_posix_cpu_timers() or on timerqueue related add/delete operations. + * If the timer is not enqueued, the failure is harmless + * + * - posix_cpu_timer_set(): Independent of the enqueued state that results in a + * transient failure which is user space visible (-ESRCH) for regular posix + * timers. But for the use case in do_cpu_nanosleep() it's the same UAF + * problem just that the timer is allocated on the stack. + * + * - posix_cpu_timer_rearm(): Timer is not enqueued at that point, but this + * silently ignores the rearm request, which is a functional problem as the + * timer wont expire anymore. + */ +static struct task_struct *timer_lock_sighand(struct k_itimer *timer, unsigned long *flags) +{ + enum pid_type type = clock_pid_type(timer->it_clock); + struct cpu_timer *ctmr = &timer->it.cpu; + + guard(rcu)(); + + for (;;) { + struct task_struct *t = pid_task(timer->it.cpu.pid, type); + + /* Fail if the task cannot be found. */ + if (!t) + break; + + /* Try to lock the task's sighand */ + if (lock_task_sighand(t, flags)) + return t; + + /* + * The next PID lookup might either fail or return the new + * leader. This is correct for both exit() and exec(). + */ + } + + /* + * If the timer is still enqueued, warn. There is nothing safe to do + * here as there might be two timers in there which are removed in + * parallel and that will cause more damage than good. This should never + * happen! + * + * Ensure that the stores to the timer and timerqueue are visible: + * + * __exit_signal() + * posix_cpu_timers*_exit() + * write_seqlock(seqlock) + * smp_wmb(); <------- + * __unhash_process() | !pid_task() + * ----> smp_rmb(); + * WARN_ON_ONCE(...) + */ + smp_rmb(); + WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node)); + return NULL; +} /* * Clean up a CPU-clock timer that is about to be destroyed. @@ -470,29 +573,13 @@ static void disarm_timer(struct k_itimer *timer, struct task_struct *p) */ static int posix_cpu_timer_del(struct k_itimer *timer) { - struct cpu_timer *ctmr = &timer->it.cpu; - struct sighand_struct *sighand; struct task_struct *p; unsigned long flags; int ret = 0; - rcu_read_lock(); - p = cpu_timer_task_rcu(timer); - if (!p) - goto out; + p = timer_lock_sighand(timer, &flags); - /* - * Protect against sighand release/switch in exit/exec and process/ - * thread timer list entry concurrent read/writes. - */ - sighand = lock_task_sighand(p, &flags); - if (unlikely(sighand == NULL)) { - /* - * This raced with the reaping of the task. The exit cleanup - * should have removed this timer from the timer queue. - */ - WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node)); - } else { + if (likely(p)) { if (timer->it.cpu.firing) { /* * Prevent signal delivery. The timer cannot be dequeued @@ -508,11 +595,8 @@ static int posix_cpu_timer_del(struct k_itimer *timer) unlock_task_sighand(p, &flags); } -out: - rcu_read_unlock(); - if (!ret) { - put_pid(ctmr->pid); + put_pid(timer->it.cpu.pid); timer->it_status = POSIX_TIMER_DISARMED; } return ret; @@ -626,21 +710,17 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock); struct cpu_timer *ctmr = &timer->it.cpu; u64 old_expires, new_expires, now; - struct sighand_struct *sighand; struct task_struct *p; unsigned long flags; int ret = 0; - rcu_read_lock(); - p = cpu_timer_task_rcu(timer); - if (!p) { - /* - * If p has just been reaped, we can no - * longer get any information about it at all. - */ - rcu_read_unlock(); + p = timer_lock_sighand(timer, &flags); + /* + * If p has just been reaped, we can no longer get any information about + * it at all. + */ + if (!p) return -ESRCH; - } /* * Use the to_ktime conversion because that clamps the maximum @@ -648,20 +728,6 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, */ new_expires = ktime_to_ns(timespec64_to_ktime(new->it_value)); - /* - * Protect against sighand release/switch in exit/exec and p->cpu_timers - * and p->signal->cpu_timers read/write in arm_timer() - */ - sighand = lock_task_sighand(p, &flags); - /* - * If p has just been reaped, we can no - * longer get any information about it at all. - */ - if (unlikely(sighand == NULL)) { - rcu_read_unlock(); - return -ESRCH; - } - /* Retrieve the current expiry time before disarming the timer */ old_expires = cpu_timer_getexpires(ctmr); @@ -698,7 +764,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, /* Retry if the timer expiry is running concurrently */ if (unlikely(ret)) { unlock_task_sighand(p, &flags); - goto out; + return ret; } /* Convert relative expiry time to absolute */ @@ -733,8 +799,6 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, */ if (!sigev_none && new_expires && now >= new_expires) cpu_timer_fire(timer); -out: - rcu_read_unlock(); return ret; } @@ -1018,19 +1082,12 @@ static void check_process_timers(struct task_struct *tsk, static bool posix_cpu_timer_rearm(struct k_itimer *timer) { clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock); - struct sighand_struct *sighand; struct task_struct *p; unsigned long flags; u64 now; - guard(rcu)(); - p = cpu_timer_task_rcu(timer); - if (!p) - return true; - - /* Protect timer list r/w in arm_timer() */ - sighand = lock_task_sighand(p, &flags); - if (unlikely(sighand == NULL)) + p = timer_lock_sighand(timer, &flags); + if (unlikely(!p)) return true; /* From 872380f930c9da8e8f08837fe0dade5023ed9bca Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 30 May 2026 12:29:07 +0200 Subject: [PATCH 2752/2758] Revert "gpib: cb7210: Fix region leak when request_irq fails" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 285ecb7d9e1b2401c2eccca4d0f26615f4d651eb upstream. This reverts commit 5ad28496055858166eb2268344c8fda2c26d3561. Turns out not to be correct. Link: https://lore.kernel.org/r/PpNUbGhrvT8I_KayoDvQYI2PYjmMw1QEkuVBDZz2PwBsVVgPkBXJarc2mBM0IhiH3AQG0GtgqEsDRXNj3yUKEDBaZa25u73pAjvcE6vfRsg=@protonmail.com Reported-by: Dominik Karol Piątkowski Cc: Mark Brown Cc: Hongling Zeng Cc: Hongling Zeng Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/cb7210/cb7210.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpib/cb7210/cb7210.c b/drivers/gpib/cb7210/cb7210.c index 05058bf2cd50b..6dd8637c5964b 100644 --- a/drivers/gpib/cb7210/cb7210.c +++ b/drivers/gpib/cb7210/cb7210.c @@ -1062,7 +1062,6 @@ static int cb_isa_attach(struct gpib_board *board, const struct gpib_board_confi // install interrupt handler if (request_irq(config->ibirq, cb7210_interrupt, isr_flags, DRV_NAME, board)) { dev_err(board->gpib_dev, "failed to obtain IRQ %d\n", config->ibirq); - release_region(nec7210_iobase(cb_priv), cb7210_iosize); return -EBUSY; } cb_priv->irq = config->ibirq; From 155b42bec9cbb6b8cdc47dd9bd09503a81fbe493 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 24 Jul 2026 16:21:27 +0200 Subject: [PATCH 2753/2758] Linux 7.1.5 Link: https://lore.kernel.org/r/20260721152552.646164743@linuxfoundation.org Tested-by: Ronald Warsow Tested-by: Salvatore Bonaccorso Tested-by: Brett A C Sheffield Tested-by: Peter Schneider Tested-by: Ron Economos Tested-by: Takeshi Ogasawara Link: https://lore.kernel.org/r/20260722142947.085426486@linuxfoundation.org Tested-by: Ronald Warsow Tested-by: Brett A C Sheffield Tested-by: Jeffrin Jose T Tested-by: Shuah Khan Tested-by: Pavel Machek (CIP) Tested-by: Florian Fainelli Tested-by: Justin M. Forbes Tested-by: Barry K. Nathan Tested-by: Takeshi Ogasawara Tested-by: Miguel Ojeda Tested-by: Mark Brown Tested-by: Peter Schneider Tested-by: Benjamin Boortz Signed-off-by: Greg Kroah-Hartman --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b3496dd21312c..58f3b3e2dc1cf 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 7 PATCHLEVEL = 1 -SUBLEVEL = 4 +SUBLEVEL = 5 EXTRAVERSION = NAME = Baby Opossum Posse From 651c8f54adfcb671da2974e0b1e0c89d76677917 Mon Sep 17 00:00:00 2001 From: "Derek J. Clark" Date: Sat, 26 Jul 2025 13:40:38 -0700 Subject: [PATCH 2754/2758] [FROM-ML] platform/x86: (ayn-ec) Add PWM Fan HWMON Interface Adds platform driver for AYN Loki and Tectoy Zeenix lines of handheld devices. This patch implements a hwmon interface for EC provided manual PWM fan control and user defined fan curves. A global ACPI lock is used when reading or writing from the EC. There are 4 fan modes implemented in this patch. Modes 0-3 act in accordance with the standard hwmon logic where 0 is 100% fan speed, 1 is manual control, and 2 is automatic control. As the EC only provides 3 modes by default, mode 0 is implemented by setting the device to manual and then setting fan speed to 100% directly. In mode 1 the PWM duty cycle is set in sysfs with values [0-255], which are then scaled to the EC max of 128. Mode 4 is an automatic mode where the fan curve is user defined. There are 5 total set points and each set point takes a temperature in Celsius [0-100] and a PWM duty cycle [0-255]. When the CPU temperature reaches a given set point, the corresponding duty cycle is automatically set by the EC. Signed-off-by: Derek J. Clark --- MAINTAINERS | 6 + drivers/platform/x86/Kconfig | 12 + drivers/platform/x86/Makefile | 3 + drivers/platform/x86/ayn-ec.c | 520 ++++++++++++++++++++++++++++++++++ 4 files changed, 541 insertions(+) create mode 100644 drivers/platform/x86/ayn-ec.c diff --git a/MAINTAINERS b/MAINTAINERS index f74da642290aa..936c21dbf1f06 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4373,6 +4373,12 @@ F: Documentation/devicetree/bindings/spi/axiado,ax3000-spi.yaml F: drivers/spi/spi-axiado.c F: drivers/spi/spi-axiado.h +AYN PLATFORM EC DRIVER +M: Derek J. Clark +L: platform-driver-x86@vger.kernel.org +S: Maintained +F: drivers/platform/x86/ayn-ec.c + AYANEO PLATFORM EC DRIVER M: Antheas Kapenekakis L: platform-driver-x86@vger.kernel.org diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 7a4956088300c..d06f47aea7752 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -330,6 +330,18 @@ config ASUS_TF103C_DOCK If you have an Asus TF103C tablet say Y or M here, for a generic x86 distro config say M here. +config AYN_EC + tristate "AYN x86 devices EC platform control" + depends on ACPI + depends on HWMON + help + This is a driver for AYN and Tectoy x86 handheld devices. It provides + temperature monitoring, manual fan speed control, fan curve control, + and chassis RGB settings. + + If you have an x86 AYN or Tectoy handheld device say M here. The module + will be called ayn-platform. + config AYANEO_EC tristate "Ayaneo EC platform control" depends on DMI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 872ac3842391f..31b4fd9083948 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -40,6 +40,9 @@ obj-$(CONFIG_ASUS_TF103C_DOCK) += asus-tf103c-dock.o obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o obj-$(CONFIG_EEEPC_WMI) += eeepc-wmi.o +# Ayn +obj-$(CONFIG_AYN_EC) += ayn-ec.o + # Ayaneo obj-$(CONFIG_AYANEO_EC) += ayaneo-ec.o diff --git a/drivers/platform/x86/ayn-ec.c b/drivers/platform/x86/ayn-ec.c new file mode 100644 index 0000000000000..8bd3ed1c69ebb --- /dev/null +++ b/drivers/platform/x86/ayn-ec.c @@ -0,0 +1,520 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Platform driver for AYN x86 Handhelds. + * + * Implements multiple attributes provided by the EC. Fan reading and control, + * as well as temperature sensor readings are exposed via hwmon sysfs. EC RGB + * control is exposed via an led-class-multicolor interface. + * + * Fan control is provided via a pwm interface in the range [0-255]. AYN use + * [0-128] as the range in the EC, the written value is scaled to accommodate. + * The EC also provides a configurable fan curve with five set points that + * associate a temperature in Celcius [0-100] with a fan speed [0-128]. The + * auto_point fan speeds are also scaled from the range [0-255]. Temperature + * readings are scaled from degrees to millidegrees when read. + * + * RGB control is provided using 4 registers. One each for the colors red, + * green, and blue are [0-255]. There is also a effect register that takes + * switches between an EC controlled breathing that cycles through all colors + * and fades in/out, and manual, which enables setting a user defined color. + * + * Copyright (C) 2025 Derek J. Clark + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Fan speed and PWM registers */ +#define AYN_SENSOR_PWM_FAN_ENABLE_REG 0x10 /* PWM operating mode */ +#define AYN_SENSOR_PWM_FAN_SET_REG 0x11 /* PWM duty cycle */ +#define AYN_SENSOR_PWM_FAN_SPEED_REG 0x20 /* Fan speed */ + +/* EC controlled fan curve registers */ +#define AYN_SENSOR_PWM_FAN_SPEED_1_REG 0x12 +#define AYN_SENSOR_PWM_FAN_SPEED_2_REG 0x14 +#define AYN_SENSOR_PWM_FAN_SPEED_3_REG 0x16 +#define AYN_SENSOR_PWM_FAN_SPEED_4_REG 0x18 +#define AYN_SENSOR_PWM_FAN_SPEED_5_REG 0x1A +#define AYN_SENSOR_PWM_FAN_TEMP_1_REG 0x13 +#define AYN_SENSOR_PWM_FAN_TEMP_2_REG 0x15 +#define AYN_SENSOR_PWM_FAN_TEMP_3_REG 0x17 +#define AYN_SENSOR_PWM_FAN_TEMP_4_REG 0x19 +#define AYN_SENSOR_PWM_FAN_TEMP_5_REG 0x1B + +/* AYN EC PWM Fan modes */ +#define AYN_PWM_FAN_MODE_MANUAL 0x00 +#define AYN_PWM_FAN_MODE_AUTO 0x01 +#define AYN_PWM_FAN_MODE_EC_CURVE 0x02 + +/* hwmon fan modes */ +#define HWMON_PWM_FAN_MODE_FULL 0x00 +#define HWMON_PWM_FAN_MODE_MANUAL 0x01 +#define HWMON_PWM_FAN_MODE_AUTO 0x02 +#define HWMON_PWM_FAN_MODE_EC_CURVE 0x03 + +/* Handle ACPI lock mechanism */ +#define ACPI_LOCK_DELAY_MS 500 + +int ayn_pwm_curve_registers[10] = { + AYN_SENSOR_PWM_FAN_SPEED_1_REG, + AYN_SENSOR_PWM_FAN_SPEED_2_REG, + AYN_SENSOR_PWM_FAN_SPEED_3_REG, + AYN_SENSOR_PWM_FAN_SPEED_4_REG, + AYN_SENSOR_PWM_FAN_SPEED_5_REG, + AYN_SENSOR_PWM_FAN_TEMP_1_REG, + AYN_SENSOR_PWM_FAN_TEMP_2_REG, + AYN_SENSOR_PWM_FAN_TEMP_3_REG, + AYN_SENSOR_PWM_FAN_TEMP_4_REG, + AYN_SENSOR_PWM_FAN_TEMP_5_REG, +}; + +struct ayn_device { + u32 ayn_lock; /* ACPI EC Lock */ +} drvdata; + +/* Handle ACPI lock mechanism */ +#define ACPI_LOCK_DELAY_MS 500 + +static bool lock_global_acpi_lock(void) +{ + return ACPI_SUCCESS(acpi_acquire_global_lock(ACPI_LOCK_DELAY_MS, + &drvdata.ayn_lock)); +} + +static bool unlock_global_acpi_lock(void) +{ + return ACPI_SUCCESS(acpi_release_global_lock(drvdata.ayn_lock)); +} + +/** + * read_from_ec() - Reads a value from the embedded controller. + * + * @reg: The register to start the read from. + * @size: The number of sequential registers the data is contained in. + * @val: Pointer to return the data with. + * + * Return: 0, or an error. + */ +static int read_from_ec(u8 reg, int size, long *val) +{ + int ret, i; + u8 buf; + + if (!lock_global_acpi_lock()) + return -EBUSY; + + *val = 0; + for (i = 0; i < size; i++) { + ret = ec_read(reg + i, &buf); + if (ret) + return ret; + *val <<= i * 8; + *val += buf; + } + + if (!unlock_global_acpi_lock()) + return -EBUSY; + + return 0; +} + +/** + * write_to_ec() - Writes a value to the embedded controller. + * + * @reg: The register to write to. + * @val: Value to write + * + * Return: 0, or an error. + */ +static int write_to_ec(u8 reg, u8 val) +{ + int ret; + + if (!lock_global_acpi_lock()) + return -EBUSY; + + pr_info("Writing EC value %d to register %u\n", val, reg); + ret = ec_write(reg, val); + + if (!unlock_global_acpi_lock()) + return -EBUSY; + + return ret; +} + +/** + * ayn_pwm_manual() - Enable manual control of the fan. + */ +static int ayn_pwm_manual(void) +{ + return write_to_ec(AYN_SENSOR_PWM_FAN_ENABLE_REG, 0x00); +} + +/** + * ayn_pwm_full() - Set fan to 100% speed. + */ +static int ayn_pwm_full(void) +{ + int ret; + + ret = write_to_ec(AYN_SENSOR_PWM_FAN_ENABLE_REG, 0x00); + if (ret) + return ret; + + return write_to_ec(AYN_SENSOR_PWM_FAN_SET_REG, 128); +} + +/** + * ayn_pwm_auto() - Enable automatic EC control of the fan. + */ +static int ayn_pwm_auto(void) +{ + return write_to_ec(AYN_SENSOR_PWM_FAN_ENABLE_REG, 0x01); +} + +/** + * ayn_pwm_ec_curve() - Enable manually setting the fan curve for automatic + * EC control of the fan. + */ +static int ayn_pwm_ec_curve(void) +{ + return write_to_ec(AYN_SENSOR_PWM_FAN_ENABLE_REG, 0x02); +} + +/** + * ayn_ec_hwmon_is_visible() - Determines RO or RW for hwmon attribute sysfs. + * + * @drvdata: Unused void pointer to context data. + * @type: The hwmon_sensor_types type. + * @attr: The attribute to set RO/RW on. + * @channel: HWMON subsystem usage flags for the attribute. + * + * Return: Permission level. + */ +static umode_t ayn_ec_hwmon_is_visible(const void *drvdata, + enum hwmon_sensor_types type, u32 attr, + int channel) +{ + switch (type) { + case hwmon_fan: + return 0444; + case hwmon_pwm: + return 0644; + default: + return 0; + } +} + +/** + * ayn_pwm_fan_read() - Read from a hwmon pwm or fan attribute. + * + * @dev: parent device of the given attribute. + * @type: The hwmon_sensor_types type. + * @attr: The attribute to read from. + * @channel: HWMON subsystem usage flags for the attribute. + * @val: Pointer to return the read value from. + * + * Return: 0, or an error. + */ +static int ayn_pwm_fan_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + int ret; + + switch (type) { + case hwmon_fan: + switch (attr) { + case hwmon_fan_input: + return read_from_ec(AYN_SENSOR_PWM_FAN_SPEED_REG, 2, + val); + default: + break; + } + break; + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_enable: + ret = read_from_ec(AYN_SENSOR_PWM_FAN_ENABLE_REG, 1, + val); + if (ret) + return ret; + + /* EC uses 0 for manual, 1 for automatic, 2 for user + * fan curve. Reflect hwmon usage instead. + */ + if (*val == 1) { + *val = 2; + return 0; + } + + if (*val == 2) { + *val = 3; + return 0; + } + + /* Return 0 when fan at max, otherwise 1 for manual. */ + ret = read_from_ec(AYN_SENSOR_PWM_FAN_SET_REG, 1, val); + if (ret) + return ret; + + if (*val == 128) + *val = 0; + else + *val = 1; + + return ret; + case hwmon_pwm_input: + ret = read_from_ec(AYN_SENSOR_PWM_FAN_SET_REG, 1, val); + if (ret) + return ret; + + *val = *val << 1; /* Max value is 128, scale to 255 */ + + return 0; + default: + break; + } + break; + default: + break; + } + return -EOPNOTSUPP; +} + +/** + * ayn_pwm_fan_write() - Write to a hwmon pwm attribute. + * + * @dev: parent device of the given attribute. + * @type: The hwmon_sensor_types type. + * @attr: The attribute to write to. + * @channel: HWMON subsystem usage flags for the attribute. + * @val: Value to write. + * + * Return: 0, or an error. + */ +static int ayn_pwm_fan_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + if (type != hwmon_pwm) + return -EOPNOTSUPP; + switch (attr) { + case hwmon_pwm_enable: + switch (val) { + case HWMON_PWM_FAN_MODE_FULL: + return ayn_pwm_full(); + case HWMON_PWM_FAN_MODE_MANUAL: + return ayn_pwm_manual(); + case HWMON_PWM_FAN_MODE_AUTO: + return ayn_pwm_auto(); + case HWMON_PWM_FAN_MODE_EC_CURVE: + return ayn_pwm_ec_curve(); + default: + return -EINVAL; + } + case hwmon_pwm_input: + if (val < 0 || val > 255) + return -EINVAL; + + val = val >> 1; /* Max value is 128, scale from 255 */ + + return write_to_ec(AYN_SENSOR_PWM_FAN_SET_REG, val); + default: + return -EOPNOTSUPP; + } +} + +static const struct hwmon_channel_info *ayn_ec_sensors[] = { + HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT), + HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE), + NULL, +}; + +static const struct hwmon_ops ayn_ec_hwmon_ops = { + .is_visible = ayn_ec_hwmon_is_visible, + .read = ayn_pwm_fan_read, + .write = ayn_pwm_fan_write, +}; + +static const struct hwmon_chip_info ayn_ec_chip_info = { + .ops = &ayn_ec_hwmon_ops, + .info = ayn_ec_sensors, +}; + +/** + * pwm_curve_store() - Write a fan curve speed or temperature value. + * + * @dev: The attribute's parent device. + * @attr: The attribute to read. + * @buf: Input value string from sysfs write. + * + * Return: Number of bytes read, or an error. + */ +static ssize_t pwm_curve_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + int i = to_sensor_dev_attr(attr)->index; + int ret, val; + u8 reg; + + ret = kstrtoint(buf, 0, &val); + if (ret) + return ret; + + if (i < 5) { + if (val < 0 || val > 255) + return -EINVAL; + val = val >> 1; /* Max EC value is 128, scale from 255 */ + } else + if (val < 0 || val > 100) + return -EINVAL; + + reg = ayn_pwm_curve_registers[i]; + + ret = write_to_ec(reg, val); + if (ret) + return ret; + + return count; +} + +/** + * pwm_curve_show() - Read a fan curve speed or temperature value. + * + * @dev: The attribute's parent device. + * @attr: The attribute to read. + * @buf: Output buffer. + * + * Return: Number of bytes read, or an error. + */ +static ssize_t pwm_curve_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int i = to_sensor_dev_attr(attr)->index; + long val; + int ret; + u8 reg; + + reg = ayn_pwm_curve_registers[i]; + + ret = read_from_ec(reg, 1, &val); + if (ret) + return ret; + + if (i < 5) + val = val << 1; /* Max EC value is 128, scale to 255 */ + + return sysfs_emit(buf, "%ld\n", val); +} + +/* Fan curve attributes */ +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point1_pwm, pwm_curve, 0); +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point2_pwm, pwm_curve, 1); +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point3_pwm, pwm_curve, 2); +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point4_pwm, pwm_curve, 3); +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point5_pwm, pwm_curve, 4); +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point1_temp, pwm_curve, 5); +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point2_temp, pwm_curve, 6); +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point3_temp, pwm_curve, 7); +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point4_temp, pwm_curve, 8); +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point5_temp, pwm_curve, 9); + +static struct attribute *ayn_sensors_attrs[] = { + &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point4_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point4_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point5_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point5_temp.dev_attr.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(ayn_sensors); + +static int ayn_ec_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device *hwdev; + + hwdev = devm_hwmon_device_register_with_info(dev, "aynec", NULL, + &ayn_ec_chip_info, + ayn_sensors_groups); + return PTR_ERR_OR_ZERO(hwdev); +} + +static struct platform_driver ayn_ec_driver = { + .driver = { + .name = "ayn-ec", + }, + .probe = ayn_ec_probe, +}; + +static struct platform_device *ayn_ec_device; + +static int __init ayn_ec_init(void) +{ + ayn_ec_device = platform_create_bundle(&ayn_ec_driver, ayn_ec_probe, + NULL, 0, NULL, 0); + + return PTR_ERR_OR_ZERO(ayn_ec_device); +} + +static void __exit ayn_ec_exit(void) +{ + platform_device_unregister(ayn_ec_device); + platform_driver_unregister(&ayn_ec_driver); +} + +static const struct dmi_system_id ayn_dmi_table[] = { + { + .ident = "AYN Loki Max", + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "ayn"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "Loki Max"), + }, + }, + { + .ident = "AYN Loki MiniPro", + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "ayn"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "Loki MiniPro"), + }, + }, + { + .ident = "AYN Loki Zero", + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "ayn"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "Loki Zero"), + }, + }, + { + .ident = "Tectoy Zeenix Lite", + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Tectoy"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "Zeenix Lite"), + }, + }, + {}, +}; + +MODULE_DEVICE_TABLE(dmi, ayn_dmi_table); + +module_init(ayn_ec_init); +module_exit(ayn_ec_exit); + +MODULE_AUTHOR("Derek J. Clark "); +MODULE_DESCRIPTION("Platform driver that handles EC sensors of AYN x86 devices"); +MODULE_LICENSE("GPL"); From 9ecb1c6bce32d7948f57bfaa99d6f8ae055a1868 Mon Sep 17 00:00:00 2001 From: "Derek J. Clark" Date: Sat, 26 Jul 2025 13:40:39 -0700 Subject: [PATCH 2755/2758] [FROM-ML] platform/x86: (ayn-ec) Add Temperature Sensors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds temperature sensors to the ayn-ec hwmon interface. These read-only values include Battery, Motherboard, Charger IC, vCore, and CPU Core, as well as labels for each entry. The temperature values provided by the EC are whole numbers in degrees Celsius. As hwmon expects millidegrees, we scale the raw value up. `sensors` output after this patch is applied: aynec-isa-0000 Adapter: ISA adapter fan1: 1876 RPM Battery: +29.0°C Motherboard: +30.0°C Charger IC: +30.0°C vCore: +36.0°C CPU Core: +48.0°C Signed-off-by: Derek J. Clark --- drivers/platform/x86/ayn-ec.c | 88 ++++++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/ayn-ec.c b/drivers/platform/x86/ayn-ec.c index 8bd3ed1c69ebb..466cc33adcb08 100644 --- a/drivers/platform/x86/ayn-ec.c +++ b/drivers/platform/x86/ayn-ec.c @@ -61,6 +61,14 @@ #define HWMON_PWM_FAN_MODE_AUTO 0x02 #define HWMON_PWM_FAN_MODE_EC_CURVE 0x03 +/* EC Temperature Sensors */ +#define AYN_SENSOR_BAT_TEMP_REG 0x04 /* Battery */ +#define AYN_SENSOR_CHARGE_TEMP_REG 0x07 /* Charger IC */ +#define AYN_SENSOR_MB_TEMP_REG 0x05 /* Motherboard */ +#define AYN_SENSOR_PROC_TEMP_REG 0x09 /* CPU Core */ +#define AYN_SENSOR_VCORE_TEMP_REG 0x08 /* vCore */ + + /* Handle ACPI lock mechanism */ #define ACPI_LOCK_DELAY_MS 500 @@ -81,8 +89,19 @@ struct ayn_device { u32 ayn_lock; /* ACPI EC Lock */ } drvdata; -/* Handle ACPI lock mechanism */ -#define ACPI_LOCK_DELAY_MS 500 +struct thermal_sensor { + char *name; + int reg; +}; + +static struct thermal_sensor thermal_sensors[] = { + { "Battery", AYN_SENSOR_BAT_TEMP_REG }, + { "Motherboard", AYN_SENSOR_MB_TEMP_REG }, + { "Charger IC", AYN_SENSOR_CHARGE_TEMP_REG }, + { "vCore", AYN_SENSOR_VCORE_TEMP_REG }, + { "CPU Core", AYN_SENSOR_PROC_TEMP_REG }, + {} +}; static bool lock_global_acpi_lock(void) { @@ -428,6 +447,61 @@ static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point3_temp, pwm_curve, 7); static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point4_temp, pwm_curve, 8); static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point5_temp, pwm_curve, 9); +/** + * thermal_sensor_show() - Read a thermal sensor attribute value. + * + * @dev: The attribute's parent device. + * @attr: The attribute to read. + * @buf: Buffer to write the result into. + * + * Return: Number of bytes read, or an error. + */ +static ssize_t thermal_sensor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + long ret, val; + int i; + + i = to_sensor_dev_attr(attr)->index; + + ret = read_from_ec(thermal_sensors[i].reg, 1, &val); + if (ret) + return ret; + + val = val * 1000L; + + return sysfs_emit(buf, "%ld\n", val); +} + +/** + * thermal_sensor_label_show() - Read a thermal sensor attribute label. + * + * @dev: The attribute's parent device. + * @attr: The attribute to read. + * @buf: Buffer to read to. + * + * Return: Number of bytes read, or an error. + */ +static ssize_t thermal_sensor_label_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int i = to_sensor_dev_attr(attr)->index; + + return sysfs_emit(buf, "%s\n", thermal_sensors[i].name); +} + +static SENSOR_DEVICE_ATTR_RO(temp1_input, thermal_sensor, 0); +static SENSOR_DEVICE_ATTR_RO(temp2_input, thermal_sensor, 1); +static SENSOR_DEVICE_ATTR_RO(temp3_input, thermal_sensor, 2); +static SENSOR_DEVICE_ATTR_RO(temp4_input, thermal_sensor, 3); +static SENSOR_DEVICE_ATTR_RO(temp5_input, thermal_sensor, 4); +static SENSOR_DEVICE_ATTR_RO(temp1_label, thermal_sensor_label, 0); +static SENSOR_DEVICE_ATTR_RO(temp2_label, thermal_sensor_label, 1); +static SENSOR_DEVICE_ATTR_RO(temp3_label, thermal_sensor_label, 2); +static SENSOR_DEVICE_ATTR_RO(temp4_label, thermal_sensor_label, 3); +static SENSOR_DEVICE_ATTR_RO(temp5_label, thermal_sensor_label, 4); + static struct attribute *ayn_sensors_attrs[] = { &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr, @@ -439,6 +513,16 @@ static struct attribute *ayn_sensors_attrs[] = { &sensor_dev_attr_pwm1_auto_point4_temp.dev_attr.attr, &sensor_dev_attr_pwm1_auto_point5_pwm.dev_attr.attr, &sensor_dev_attr_pwm1_auto_point5_temp.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_label.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_label.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_label.dev_attr.attr, + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp4_label.dev_attr.attr, + &sensor_dev_attr_temp5_input.dev_attr.attr, + &sensor_dev_attr_temp5_label.dev_attr.attr, NULL, }; From d99585affe30ecc5f3f38ff6551bd21e9fde9966 Mon Sep 17 00:00:00 2001 From: "Derek J. Clark" Date: Sat, 26 Jul 2025 13:40:40 -0700 Subject: [PATCH 2756/2758] [FROM-ML] platform/x86: (ayn-ec) Add RGB Interface Adds an EC controlled LED Multicolor Class Device for controlling the RGB rings around the joysticks. The EC provides a single register for each of the colors red, green, and blue, as well as a mode switching register. The EC accepts values [0-255] for all colors. There are two available effects: breathe, which is the default when the device is started, and monocolor. When resuming from sleep the user selected effect will be overwritten by the EC, so the driver retains the last setting and resets on resume. When setting a color, each color register is set before a final "write" code is sent to the device. The EC may briefly reflect the "write" code when writing, but quickly changes to the "monocolor" value once complete. The driver interprets both of these values as "monocolor" in _show to simplify the sysfs exposed to the user. Two custom attributes are added to the standard LED parent device: effect, a RW file descriptor used to set the effect, and effect_index, which enumerates the available valid options. Signed-off-by: Derek J. Clark --- drivers/platform/x86/Kconfig | 3 + drivers/platform/x86/ayn-ec.c | 285 ++++++++++++++++++++++++++++++++++ 2 files changed, 288 insertions(+) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index d06f47aea7752..cb97aac80248d 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -334,6 +334,9 @@ config AYN_EC tristate "AYN x86 devices EC platform control" depends on ACPI depends on HWMON + depends on NEW_LEDS + select LEDS_CLASS + select LEDS_CLASS_MULTICOLOR help This is a driver for AYN and Tectoy x86 handheld devices. It provides temperature monitoring, manual fan speed control, fan curve control, diff --git a/drivers/platform/x86/ayn-ec.c b/drivers/platform/x86/ayn-ec.c index 466cc33adcb08..25f748d7db18e 100644 --- a/drivers/platform/x86/ayn-ec.c +++ b/drivers/platform/x86/ayn-ec.c @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include #include @@ -68,6 +70,16 @@ #define AYN_SENSOR_PROC_TEMP_REG 0x09 /* CPU Core */ #define AYN_SENSOR_VCORE_TEMP_REG 0x08 /* vCore */ +/* EC Controlled RGB registers */ +#define AYN_LED_MC_RED_REG 0xB0 /* Range 0x00-0xFF */ +#define AYN_LED_MC_GREEN_REG 0xB1 /* Range 0x00-0xFF */ +#define AYN_LED_MC_BLUE_REG 0xB2 /* Range 0x00-0xFF */ +#define AYN_RGB_EFFECT_REG 0xB3 + +/* RGB effect modes */ +#define AYN_RGB_EFFECT_BREATHE 0x00 +#define AYN_RGB_EFFECT_MONOCOLOR 0x55 +#define AYN_RGB_EFFECT_WRITE 0xAA /* Handle ACPI lock mechanism */ #define ACPI_LOCK_DELAY_MS 500 @@ -86,7 +98,9 @@ int ayn_pwm_curve_registers[10] = { }; struct ayn_device { + struct led_classdev *led_cdev; u32 ayn_lock; /* ACPI EC Lock */ + u8 rgb_effect; } drvdata; struct thermal_sensor { @@ -103,6 +117,33 @@ static struct thermal_sensor thermal_sensors[] = { {} }; +#define DEVICE_ATTR_RW_NAMED(_name, _attrname) \ + struct device_attribute dev_attr_##_name = { \ + .attr = { .name = _attrname, .mode = 0644 }, \ + .show = _name##_show, \ + .store = _name##_store, \ + } + +#define DEVICE_ATTR_RO_NAMED(_name, _attrname) \ + struct device_attribute dev_attr_##_name = { \ + .attr = { .name = _attrname, .mode = 0444 }, \ + .show = _name##_show, \ + } + +/* Handle ACPI lock mechanism */ +#define ACPI_LOCK_DELAY_MS 500 + +/* RGB effect values */ +enum RGB_EFFECT_OPTION { + BREATHE, + MONOCOLOR, +}; + +static const char *const RGB_EFFECT_TEXT[] = { + [BREATHE] = "breathe", + [MONOCOLOR] = "monocolor", +}; + static bool lock_global_acpi_lock(void) { return ACPI_SUCCESS(acpi_acquire_global_lock(ACPI_LOCK_DELAY_MS, @@ -528,10 +569,253 @@ static struct attribute *ayn_sensors_attrs[] = { ATTRIBUTE_GROUPS(ayn_sensors); +/** + * rgb_effect_write() - Set the RGB effect stored in drvdata.rgb_effect. + */ +static int rgb_effect_write(void) +{ + return write_to_ec(AYN_RGB_EFFECT_REG, drvdata.rgb_effect); +}; + +/** + * rgb_effect_read() - Read the RGB effect and store it in drvdata.rgb_effect. + */ +static int rgb_effect_read(void) +{ + int ret; + long effect; + + ret = read_from_ec(AYN_RGB_EFFECT_REG, 1, &effect); + if (ret) + return ret; + + switch (effect) { + case AYN_RGB_EFFECT_WRITE: + case AYN_RGB_EFFECT_MONOCOLOR: + drvdata.rgb_effect = AYN_RGB_EFFECT_WRITE; + break; + default: + drvdata.rgb_effect = AYN_RGB_EFFECT_BREATHE; + } + + return 0; +} + +/** + * rgb_effect_store() - Store the given RGB effect and set it. + * + * @dev: parent device of the given attribute. + * @attr: The attribute to write to. + * @buf: Input value string from sysfs write. + * @count: The number of bytes written. + * + * Return: The number of bytes written, or an error. + */ +static ssize_t rgb_effect_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + int ret; + + ret = sysfs_match_string(RGB_EFFECT_TEXT, buf); + if (ret < 0) + return ret; + + if (ret) + drvdata.rgb_effect = AYN_RGB_EFFECT_WRITE; + else + drvdata.rgb_effect = AYN_RGB_EFFECT_BREATHE; + + ret = rgb_effect_write(); + if (ret) + return ret; + + return count; +}; + +/** + * rgb_effect_show() - Read the current RGB effect. + * + * @dev: parent device of the given attribute. + * @attr: The attribute to read. + * @buf: Buffer to read to. + * + * Return: The number of bytes read, or an error. + */ +static ssize_t rgb_effect_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret, i; + + ret = rgb_effect_read(); + if (ret) + return ret; + + switch (drvdata.rgb_effect) { + case AYN_RGB_EFFECT_WRITE: + case AYN_RGB_EFFECT_MONOCOLOR: + i = MONOCOLOR; + break; + default: + i = BREATHE; + break; + } + + return sysfs_emit(buf, "%s\n", RGB_EFFECT_TEXT[i]); +}; + +static DEVICE_ATTR_RW_NAMED(rgb_effect, "effect"); + +/** + * rgb_effect_show() - Display the RGB effects available. + * + * @dev: parent device of the given attribute. + * @attr: The attribute to read. + * @buf: Buffer to read to. + * + * Return: The number of bytes read, or an error. + */ +static ssize_t rgb_effect_index_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + size_t count = 0; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(RGB_EFFECT_TEXT); i++) + count += sysfs_emit_at(buf, count, "%s ", RGB_EFFECT_TEXT[i]); + + buf[count - 1] = '\n'; + + return count; +} + +static DEVICE_ATTR_RO_NAMED(rgb_effect_index, "effect_index"); + +/** + * ayn_led_mc_brightness_set() - Write the brightness for the RGB LED. + * + * @led_cdev: Parent LED device for the led_classdev_mc. + * @brightness: Brightness value to write [0-255]. + */ +static void ayn_led_mc_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct led_classdev_mc *led_cdev_mc = lcdev_to_mccdev(led_cdev); + struct mc_subled s_led; + int i, ret, val; + + switch (drvdata.rgb_effect) { + case AYN_RGB_EFFECT_WRITE: + case AYN_RGB_EFFECT_MONOCOLOR: + break; + case AYN_RGB_EFFECT_BREATHE: + return; + } + + led_cdev->brightness = brightness; + for (i = 0; i < led_cdev_mc->num_colors; i++) { + s_led = led_cdev_mc->subled_info[i]; + val = brightness * s_led.intensity / led_cdev->max_brightness; + ret = write_to_ec(s_led.channel, val); + if (ret) { + dev_err(led_cdev->dev, + "Error setting brightness: %d\n", ret); + return; + } + } + + /* Must write mode again to change to set color */ + write_to_ec(AYN_RGB_EFFECT_REG, AYN_RGB_EFFECT_WRITE); +}; + +/** + * ayn_led_mc_brightness_get() - Get the brightness for the RGB LED. + * + * @led_cdev: Parent LED device for the led_classdev_mc. + * + * Return: Current brightness. + */ +static enum led_brightness ayn_led_mc_brightness_get(struct led_classdev *led_cdev) +{ + return led_cdev->brightness; +}; + +static struct attribute *ayn_led_mc_attrs[] = { + &dev_attr_rgb_effect.attr, + &dev_attr_rgb_effect_index.attr, + NULL, +}; + +static struct attribute_group ayn_led_mc_group = { + .attrs = ayn_led_mc_attrs, +}; + +struct mc_subled ayn_led_mc_subled_info[] = { + { + .color_index = LED_COLOR_ID_RED, + .brightness = 0, + .intensity = 0, + .channel = AYN_LED_MC_RED_REG, + }, + { + .color_index = LED_COLOR_ID_GREEN, + .brightness = 0, + .intensity = 0, + .channel = AYN_LED_MC_GREEN_REG, + }, + { + .color_index = LED_COLOR_ID_BLUE, + .brightness = 0, + .intensity = 0, + .channel = AYN_LED_MC_BLUE_REG, + }, +}; + +struct led_classdev_mc ayn_led_mc = { + .led_cdev = { + .name = "ayn:rgb:joystick_rings", + .brightness = 0, + .max_brightness = 255, + .brightness_set = ayn_led_mc_brightness_set, + .brightness_get = ayn_led_mc_brightness_get, + .color = LED_COLOR_ID_RGB, + }, + .num_colors = ARRAY_SIZE(ayn_led_mc_subled_info), + .subled_info = ayn_led_mc_subled_info, +}; + +static int ayn_ec_resume(struct platform_device *pdev) +{ + struct led_classdev *led_cdev = drvdata.led_cdev; + int ret; + + ret = rgb_effect_write(); + if (ret) + return ret; + + ayn_led_mc_brightness_set(led_cdev, led_cdev->brightness); + + return 0; +} + static int ayn_ec_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device *hwdev; + int ret; + + ret = devm_led_classdev_multicolor_register(dev, &ayn_led_mc); + if (ret) + return ret; + + ret = devm_device_add_group(ayn_led_mc.led_cdev.dev, &ayn_led_mc_group); + if (ret) + return ret; + + drvdata.led_cdev = &ayn_led_mc.led_cdev; + ret = rgb_effect_read(); + if (ret) + return ret; hwdev = devm_hwmon_device_register_with_info(dev, "aynec", NULL, &ayn_ec_chip_info, @@ -544,6 +828,7 @@ static struct platform_driver ayn_ec_driver = { .name = "ayn-ec", }, .probe = ayn_ec_probe, + .resume = ayn_ec_resume, }; static struct platform_device *ayn_ec_device; From 2754a417642ce88f2921d4de3d9e5eda4e63b082 Mon Sep 17 00:00:00 2001 From: "Derek J. Clark" Date: Sat, 26 Jul 2025 13:40:41 -0700 Subject: [PATCH 2757/2758] [FROM-ML] platform/x86: (ayn-ec) Add AYN EC Platform Documentation Adds ABI documentation for the ayn-ec platform driver Signed-off-by: Derek J. Clark --- .../ABI/testing/sysfs-platform-ayn-ec | 59 +++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 60 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-platform-ayn-ec diff --git a/Documentation/ABI/testing/sysfs-platform-ayn-ec b/Documentation/ABI/testing/sysfs-platform-ayn-ec new file mode 100644 index 0000000000000..32cb6f7ca2fc5 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-platform-ayn-ec @@ -0,0 +1,59 @@ +What: /sys/class/hwmon/hwmon[0-9]/pwm1_enable +Date: July 2025 +KernelVersion: 6.17 +Contact: "Derek J. Clark" +Description: + This sets the PWM fan mode of operation. Valid values are [0-3]. + Values [0-2] conform with standard hwmon operating modes. Value 3 + enables user defined fan curve settings. + + Applies to AYN Loki and Tectoy Zeenix lines of handheld devices. + +What: /sys/class/hwmon/hwmon[0-9]/pwm1_auto_point[1-5]_pwm +Date: July 2025 +KernelVersion: 6.17 +Contact: "Derek J. Clark" +Description: + This sets the PWM fan duty cycle for the given index of the fan curve. + When the temperature reaches the corresponding pwm1_auto_point[1-5]_temp, + the EC will automatically increase the fan duty cycle to the given value. + + Values are [0-255] + + Applies to AYN Loki and Tectoy Zeenix lines of handheld devices. + +What: /sys/class/hwmon/hwmon[0-9]/pwm1_auto_point[1-5]_temp +Date: July 2025 +KernelVersion: 6.17 +Contact: "Derek J. Clark" +Description: + This sets the activation temperature for the given index of the fan curve. + When the temperature reaches the given value, the EC will automatically + increase the fan duty cycle to the corresponding pwm1_auto_point[1-5]_pwm + value. + + Values are [0-100] + + Applies to AYN Loki and Tectoy Zeenix lines of handheld devices. + +What: /sys/class/leds/ayn:rgb:joystick_rings/effect +Date: July 2025 +KernelVersion: 6.17 +Contact: "Derek J. Clark" +Description: + This controls the display effect of the RGB interface. + + Values are monocolor or breathe. + + Applies to AYN Loki and Tectoy Zeenix lines of handheld devices. + +What: /sys/class/leds/ayn:rgb:joystick_rings/effect_index +Date: July 2025 +KernelVersion: 6.17 +Contact: "Derek J. Clark" +Description: + This displays the available options for the effect attribute. + + Values are monocolor or breathe. + + Applies to AYN Loki and Tectoy Zeenix lines of handheld devices. diff --git a/MAINTAINERS b/MAINTAINERS index 936c21dbf1f06..53b0ed43cd395 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4377,6 +4377,7 @@ AYN PLATFORM EC DRIVER M: Derek J. Clark L: platform-driver-x86@vger.kernel.org S: Maintained +F: Documentation/ABI/testing/sysfs-platform-ayn-ec F: drivers/platform/x86/ayn-ec.c AYANEO PLATFORM EC DRIVER From 142dcf8f006ffc94dc8fb83c6340595072512154 Mon Sep 17 00:00:00 2001 From: Kyle Gospodnetich Date: Tue, 28 Jul 2026 21:01:46 -0700 Subject: [PATCH 2758/2758] [NOT-FOR-UPSTREAM] platform/x86: ayn-ec: Report RGB LED under the Legion Go name This is deliberately an OGC-carried patch and must not be sent upstream. Squatting on another device's LED name is not acceptable in mainline; the real fix belongs in the Steam client, which should match the "*:rgb:joystick_rings" suffix rather than a per-device prefix. --- drivers/platform/x86/ayn-ec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/ayn-ec.c b/drivers/platform/x86/ayn-ec.c index 25f748d7db18e..12c8605d3a8ec 100644 --- a/drivers/platform/x86/ayn-ec.c +++ b/drivers/platform/x86/ayn-ec.c @@ -773,7 +773,7 @@ struct mc_subled ayn_led_mc_subled_info[] = { struct led_classdev_mc ayn_led_mc = { .led_cdev = { - .name = "ayn:rgb:joystick_rings", + .name = "go:rgb:joystick_rings", .brightness = 0, .max_brightness = 255, .brightness_set = ayn_led_mc_brightness_set,